summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--README25
-rw-r--r--dist/changes-1.1.02
-rw-r--r--dist/changes-1.2.079
-rw-r--r--examples/datavisualization/audiolevels/audiolevels.cpp2
-rw-r--r--examples/datavisualization/bars/doc/images/bars-example.pngbin188289 -> 238712 bytes
-rw-r--r--examples/datavisualization/bars/doc/src/bars.qdoc24
-rw-r--r--examples/datavisualization/bars/graphmodifier.cpp115
-rw-r--r--examples/datavisualization/bars/graphmodifier.h11
-rw-r--r--examples/datavisualization/bars/main.cpp14
-rw-r--r--examples/datavisualization/customproxy/doc/images/customproxy-example.pngbin125698 -> 173744 bytes
-rw-r--r--examples/datavisualization/customproxy/rainfallgraph.cpp5
-rw-r--r--examples/datavisualization/datavisualization.pro30
-rw-r--r--examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.pngbin0 -> 80103 bytes
-rw-r--r--examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc89
-rw-r--r--examples/datavisualization/qmlspectrogram/main.cpp42
-rw-r--r--examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml1560
-rw-r--r--examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml52
-rw-r--r--examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml293
-rw-r--r--examples/datavisualization/qmlspectrogram/qmlspectrogram.pro12
-rw-r--r--examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc7
-rw-r--r--examples/datavisualization/qmlsurfacelayers/layer_2.pngbin10553 -> 10563 bytes
-rw-r--r--examples/datavisualization/texturesurface/custominputhandler.cpp182
-rw-r--r--examples/datavisualization/texturesurface/custominputhandler.h91
-rw-r--r--examples/datavisualization/texturesurface/doc/images/texturesurface-example.pngbin0 -> 157373 bytes
-rw-r--r--examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc126
-rw-r--r--examples/datavisualization/texturesurface/highlightseries.cpp119
-rw-r--r--examples/datavisualization/texturesurface/highlightseries.h52
-rw-r--r--examples/datavisualization/texturesurface/license.txt77
-rw-r--r--examples/datavisualization/texturesurface/main.cpp101
-rw-r--r--examples/datavisualization/texturesurface/maptexture.jpgbin0 -> 352922 bytes
-rw-r--r--examples/datavisualization/texturesurface/surfacegraph.cpp90
-rw-r--r--examples/datavisualization/texturesurface/surfacegraph.h52
-rw-r--r--examples/datavisualization/texturesurface/texturedsurface.qrc6
-rw-r--r--examples/datavisualization/texturesurface/texturesurface.pro25
-rw-r--r--examples/datavisualization/texturesurface/topographicseries.cpp72
-rw-r--r--examples/datavisualization/texturesurface/topographicseries.h45
-rw-r--r--examples/datavisualization/texturesurface/topography.pngbin0 -> 395504 bytes
-rw-r--r--examples/datavisualization/volumetric/doc/images/volumetric-example.pngbin0 -> 177203 bytes
-rw-r--r--examples/datavisualization/volumetric/doc/src/volumetric.qdoc129
-rw-r--r--examples/datavisualization/volumetric/layer_ground.pngbin0 -> 63473 bytes
-rw-r--r--examples/datavisualization/volumetric/layer_magma.pngbin0 -> 17332 bytes
-rw-r--r--examples/datavisualization/volumetric/layer_water.pngbin0 -> 27124 bytes
-rw-r--r--examples/datavisualization/volumetric/main.cpp247
-rw-r--r--examples/datavisualization/volumetric/volumetric.cpp764
-rw-r--r--examples/datavisualization/volumetric/volumetric.h116
-rw-r--r--examples/datavisualization/volumetric/volumetric.pro17
-rw-r--r--examples/datavisualization/volumetric/volumetric.qrc7
-rw-r--r--qtdatavisualization.pro2
-rw-r--r--src/datavisualization/axis/axis.pri2
-rw-r--r--src/datavisualization/axis/qlogvalue3daxisformatter.cpp1
-rw-r--r--src/datavisualization/axis/qvalue3daxis.cpp24
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter.cpp40
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter.h4
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter_p.h8
-rw-r--r--src/datavisualization/common.pri7
-rw-r--r--src/datavisualization/data/abstractrenderitem_p.h6
-rw-r--r--src/datavisualization/data/customrenderitem.cpp68
-rw-r--r--src/datavisualization/data/customrenderitem_p.h109
-rw-r--r--src/datavisualization/data/data.pri9
-rw-r--r--src/datavisualization/data/labelitem.cpp6
-rw-r--r--src/datavisualization/data/qabstract3dseries.cpp3
-rw-r--r--src/datavisualization/data/qabstract3dseries.h1
-rw-r--r--src/datavisualization/data/qbar3dseries.cpp21
-rw-r--r--src/datavisualization/data/qcustom3ditem.cpp101
-rw-r--r--src/datavisualization/data/qcustom3ditem.h5
-rw-r--r--src/datavisualization/data/qcustom3ditem_p.h5
-rw-r--r--src/datavisualization/data/qcustom3dlabel.cpp1
-rw-r--r--src/datavisualization/data/qcustom3dvolume.cpp1278
-rw-r--r--src/datavisualization/data/qcustom3dvolume.h147
-rw-r--r--src/datavisualization/data/qcustom3dvolume_p.h108
-rw-r--r--src/datavisualization/data/qheightmapsurfacedataproxy.cpp16
-rw-r--r--src/datavisualization/data/qsurface3dseries.cpp66
-rw-r--r--src/datavisualization/data/qsurface3dseries.h9
-rw-r--r--src/datavisualization/data/qsurface3dseries_p.h3
-rw-r--r--src/datavisualization/data/qsurfacedataproxy.cpp4
-rw-r--r--src/datavisualization/datavisualization.pro8
-rw-r--r--src/datavisualization/doc/qtdatavisualization.qdocconf12
-rw-r--r--src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp8
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc151
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc8
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc21
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization.qdoc23
-rw-r--r--src/datavisualization/engine/abstract3dcontroller.cpp270
-rw-r--r--src/datavisualization/engine/abstract3dcontroller_p.h85
-rw-r--r--src/datavisualization/engine/abstract3drenderer.cpp1242
-rw-r--r--src/datavisualization/engine/abstract3drenderer_p.h119
-rw-r--r--src/datavisualization/engine/axisrendercache.cpp18
-rw-r--r--src/datavisualization/engine/axisrendercache_p.h2
-rw-r--r--src/datavisualization/engine/bars3dcontroller.cpp30
-rw-r--r--src/datavisualization/engine/bars3dcontroller_p.h7
-rw-r--r--src/datavisualization/engine/bars3drenderer.cpp869
-rw-r--r--src/datavisualization/engine/bars3drenderer_p.h28
-rw-r--r--src/datavisualization/engine/drawer.cpp68
-rw-r--r--src/datavisualization/engine/drawer_p.h4
-rw-r--r--src/datavisualization/engine/engine.pri2
-rw-r--r--src/datavisualization/engine/engine.qrc13
-rw-r--r--src/datavisualization/engine/q3dbars.cpp20
-rw-r--r--src/datavisualization/engine/q3dbars.h4
-rw-r--r--src/datavisualization/engine/q3dcamera.cpp233
-rw-r--r--src/datavisualization/engine/q3dcamera.h13
-rw-r--r--src/datavisualization/engine/q3dcamera_p.h19
-rw-r--r--src/datavisualization/engine/q3dlight.cpp9
-rw-r--r--src/datavisualization/engine/q3dobject.cpp9
-rw-r--r--src/datavisualization/engine/q3dscene.cpp123
-rw-r--r--src/datavisualization/engine/q3dscene.h5
-rw-r--r--src/datavisualization/engine/q3dscene_p.h7
-rw-r--r--src/datavisualization/engine/q3dsurface.cpp27
-rw-r--r--src/datavisualization/engine/q3dsurface.h4
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.cpp300
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.h41
-rw-r--r--src/datavisualization/engine/scatter3drenderer.cpp1661
-rw-r--r--src/datavisualization/engine/scatter3drenderer_p.h32
-rw-r--r--src/datavisualization/engine/scatterseriesrendercache.cpp4
-rw-r--r--src/datavisualization/engine/scatterseriesrendercache_p.h10
-rw-r--r--src/datavisualization/engine/selectionpointer.cpp33
-rw-r--r--src/datavisualization/engine/selectionpointer_p.h2
-rw-r--r--src/datavisualization/engine/seriesrendercache.cpp8
-rw-r--r--src/datavisualization/engine/seriesrendercache_p.h3
-rw-r--r--src/datavisualization/engine/shaders/3dsliceframes.frag15
-rw-r--r--src/datavisualization/engine/shaders/default.frag3
-rw-r--r--src/datavisualization/engine/shaders/defaultNoMatrices.vert26
-rw-r--r--src/datavisualization/engine/shaders/default_ES2.frag2
-rw-r--r--src/datavisualization/engine/shaders/point_ES2_UV.vert12
-rw-r--r--src/datavisualization/engine/shaders/position.vert10
-rw-r--r--src/datavisualization/engine/shaders/positionmap.frag11
-rw-r--r--src/datavisualization/engine/shaders/shadowNoMatrices.vert36
-rw-r--r--src/datavisualization/engine/shaders/shadowNoTex.frag2
-rw-r--r--src/datavisualization/engine/shaders/surface.frag4
-rw-r--r--src/datavisualization/engine/shaders/surfaceFlat.frag4
-rw-r--r--src/datavisualization/engine/shaders/surfaceFlat.vert3
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowFlat.frag10
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowFlat.vert16
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowNoTex.frag8
-rw-r--r--src/datavisualization/engine/shaders/surfaceTexturedFlat.frag39
-rw-r--r--src/datavisualization/engine/shaders/surfaceTexturedShadow.frag67
-rw-r--r--src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag72
-rw-r--r--src/datavisualization/engine/shaders/surface_ES2.frag4
-rw-r--r--src/datavisualization/engine/shaders/texture3d.frag155
-rw-r--r--src/datavisualization/engine/shaders/texture3d.vert36
-rw-r--r--src/datavisualization/engine/shaders/texture3dlowdef.frag109
-rw-r--r--src/datavisualization/engine/shaders/texture3dslice.frag155
-rw-r--r--src/datavisualization/engine/surface3dcontroller.cpp42
-rw-r--r--src/datavisualization/engine/surface3dcontroller_p.h20
-rw-r--r--src/datavisualization/engine/surface3drenderer.cpp1326
-rw-r--r--src/datavisualization/engine/surface3drenderer_p.h32
-rw-r--r--src/datavisualization/engine/surfaceseriesrendercache.cpp7
-rw-r--r--src/datavisualization/engine/surfaceseriesrendercache_p.h3
-rw-r--r--src/datavisualization/global/datavisualizationglobal_p.h2
-rw-r--r--src/datavisualization/global/global.pri2
-rw-r--r--src/datavisualization/global/qdatavisualizationglobal.h4
-rw-r--r--src/datavisualization/input/input.pri2
-rw-r--r--src/datavisualization/input/q3dinputhandler.cpp306
-rw-r--r--src/datavisualization/input/q3dinputhandler.h19
-rw-r--r--src/datavisualization/input/q3dinputhandler_p.h25
-rw-r--r--src/datavisualization/input/qtouch3dinputhandler.cpp158
-rw-r--r--src/datavisualization/input/qtouch3dinputhandler_p.h9
-rw-r--r--src/datavisualization/theme/q3dtheme.cpp6
-rw-r--r--src/datavisualization/theme/theme.pri2
-rw-r--r--src/datavisualization/utils/abstractobjecthelper.cpp1
-rw-r--r--src/datavisualization/utils/abstractobjecthelper_p.h4
-rw-r--r--src/datavisualization/utils/objecthelper.cpp5
-rw-r--r--src/datavisualization/utils/objecthelper_p.h2
-rw-r--r--src/datavisualization/utils/qutils.h12
-rw-r--r--src/datavisualization/utils/scatterobjectbufferhelper.cpp237
-rw-r--r--src/datavisualization/utils/scatterobjectbufferhelper_p.h13
-rw-r--r--src/datavisualization/utils/scatterpointbufferhelper.cpp109
-rw-r--r--src/datavisualization/utils/scatterpointbufferhelper_p.h13
-rw-r--r--src/datavisualization/utils/shaderhelper.cpp177
-rw-r--r--src/datavisualization/utils/shaderhelper_p.h112
-rw-r--r--src/datavisualization/utils/surfaceobject.cpp745
-rw-r--r--src/datavisualization/utils/surfaceobject_p.h52
-rw-r--r--src/datavisualization/utils/texturehelper.cpp244
-rw-r--r--src/datavisualization/utils/texturehelper_p.h14
-rw-r--r--src/datavisualization/utils/utils.cpp252
-rw-r--r--src/datavisualization/utils/utils.pri2
-rw-r--r--src/datavisualization/utils/utils_p.h15
-rw-r--r--src/datavisualizationqml2/abstractdeclarative.cpp202
-rw-r--r--src/datavisualizationqml2/abstractdeclarative_p.h40
-rw-r--r--src/datavisualizationqml2/datavisualizationqml2.pro1
-rw-r--r--src/datavisualizationqml2/datavisualizationqml2_plugin.cpp18
-rw-r--r--src/datavisualizationqml2/datavisualizationqml2_plugin.h7
-rw-r--r--src/datavisualizationqml2/declarativebars.cpp13
-rw-r--r--src/datavisualizationqml2/declarativebars_p.h5
-rw-r--r--src/datavisualizationqml2/declarativesurface.cpp12
-rw-r--r--src/datavisualizationqml2/declarativesurface_p.h4
-rw-r--r--src/datavisualizationqml2/designer/Bars3DSpecifics.qml86
-rw-r--r--src/datavisualizationqml2/designer/Scatter3DSpecifics.qml72
-rw-r--r--src/datavisualizationqml2/designer/Surface3DSpecifics.qml70
-rw-r--r--src/datavisualizationqml2/designer/default/Bars3D.qml2
-rw-r--r--src/datavisualizationqml2/designer/default/Scatter3D.qml2
-rw-r--r--src/datavisualizationqml2/designer/default/Surface3D.qml2
-rw-r--r--src/datavisualizationqml2/plugins.qmltypes334
-rw-r--r--src/src.pro5
-rw-r--r--tests/auto/auto.pro7
-rw-r--r--tests/auto/cpptest/cpptest.pro26
-rw-r--r--tests/auto/cpptest/q3daxis-category/q3daxis-category.pro8
-rw-r--r--tests/auto/cpptest/q3daxis-category/tst_axis.cpp133
-rw-r--r--tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro8
-rw-r--r--tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp102
-rw-r--r--tests/auto/cpptest/q3daxis-value/q3daxis-value.pro8
-rw-r--r--tests/auto/cpptest/q3daxis-value/tst_axis.cpp158
-rw-r--r--tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro8
-rw-r--r--tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp267
-rw-r--r--tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro8
-rw-r--r--tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp97
-rw-r--r--tests/auto/cpptest/q3dbars-series/q3dbars-series.pro8
-rw-r--r--tests/auto/cpptest/q3dbars-series/tst_series.cpp169
-rw-r--r--tests/auto/cpptest/q3dbars/q3dbars.pro8
-rw-r--r--tests/auto/cpptest/q3dbars/tst_bars.cpp401
-rw-r--r--tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro8
-rw-r--r--tests/auto/cpptest/q3dcustom-label/tst_custom.cpp158
-rw-r--r--tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro8
-rw-r--r--tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp204
-rw-r--r--tests/auto/cpptest/q3dcustom/q3dcustom.pro8
-rw-r--r--tests/auto/cpptest/q3dcustom/tst_custom.cpp128
-rw-r--r--tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro8
-rw-r--r--tests/auto/cpptest/q3dinput-touch/tst_input.cpp106
-rw-r--r--tests/auto/cpptest/q3dinput/q3dinput.pro8
-rw-r--r--tests/auto/cpptest/q3dinput/tst_input.cpp109
-rw-r--r--tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro8
-rw-r--r--tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp193
-rw-r--r--tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro8
-rw-r--r--tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp91
-rw-r--r--tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro8
-rw-r--r--tests/auto/cpptest/q3dscatter-series/tst_series.cpp110
-rw-r--r--tests/auto/cpptest/q3dscatter/q3dscatter.pro8
-rw-r--r--tests/auto/cpptest/q3dscatter/tst_scatter.cpp237
-rw-r--r--tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro8
-rw-r--r--tests/auto/cpptest/q3dscene-camera/tst_camera.cpp179
-rw-r--r--tests/auto/cpptest/q3dscene-light/q3dscene-light.pro8
-rw-r--r--tests/auto/cpptest/q3dscene-light/tst_light.cpp92
-rw-r--r--tests/auto/cpptest/q3dscene/q3dscene.pro8
-rw-r--r--tests/auto/cpptest/q3dscene/tst_scene.cpp170
-rw-r--r--tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpgbin0 -> 516 bytes
-rw-r--r--tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro11
-rw-r--r--tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc5
-rw-r--r--tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp156
-rw-r--r--tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro8
-rw-r--r--tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp283
-rw-r--r--tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro8
-rw-r--r--tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp98
-rw-r--r--tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro8
-rw-r--r--tests/auto/cpptest/q3dsurface-series/tst_series.cpp120
-rw-r--r--tests/auto/cpptest/q3dsurface/q3dsurface.pro8
-rw-r--r--tests/auto/cpptest/q3dsurface/tst_surface.cpp247
-rw-r--r--tests/auto/cpptest/q3dtheme/q3dtheme.pro8
-rw-r--r--tests/auto/cpptest/q3dtheme/tst_theme.cpp216
-rw-r--r--tests/auto/qmltest/axis3d/tst_category.qml135
-rw-r--r--tests/auto/qmltest/axis3d/tst_logvalue.qml91
-rw-r--r--tests/auto/qmltest/axis3d/tst_value.qml154
-rw-r--r--tests/auto/qmltest/bars3d/tst_bars.qml147
-rw-r--r--tests/auto/qmltest/bars3d/tst_barseries.qml215
-rw-r--r--tests/auto/qmltest/bars3d/tst_basic.qml240
-rw-r--r--tests/auto/qmltest/bars3d/tst_proxy.qml253
-rw-r--r--tests/auto/qmltest/custom3d/tst_customitem.qml106
-rw-r--r--tests/auto/qmltest/custom3d/tst_customlabel.qml134
-rw-r--r--tests/auto/qmltest/custom3d/tst_customvolume.qml182
-rw-r--r--tests/auto/qmltest/customitem.obj54
-rw-r--r--tests/auto/qmltest/customtexture.jpgbin0 -> 516 bytes
-rw-r--r--tests/auto/qmltest/input3d/tst_input.qml83
-rw-r--r--tests/auto/qmltest/input3d/tst_touch.qml83
-rw-r--r--tests/auto/qmltest/qmltest.pro35
-rw-r--r--tests/auto/qmltest/qmltest.qrc6
-rw-r--r--tests/auto/qmltest/scatter3d/tst_basic.qml203
-rw-r--r--tests/auto/qmltest/scatter3d/tst_proxy.qml134
-rw-r--r--tests/auto/qmltest/scatter3d/tst_scatter.qml60
-rw-r--r--tests/auto/qmltest/scatter3d/tst_scatterseries.qml233
-rw-r--r--tests/auto/qmltest/scene3d/tst_camera.qml135
-rw-r--r--tests/auto/qmltest/scene3d/tst_light.qml65
-rw-r--r--tests/auto/qmltest/scene3d/tst_scene.qml125
-rw-r--r--tests/auto/qmltest/surface3d/tst_basic.qml211
-rw-r--r--tests/auto/qmltest/surface3d/tst_heightproxy.qml116
-rw-r--r--tests/auto/qmltest/surface3d/tst_proxy.qml263
-rw-r--r--tests/auto/qmltest/surface3d/tst_surface.qml60
-rw-r--r--tests/auto/qmltest/surface3d/tst_surfaceseries.qml229
-rw-r--r--tests/auto/qmltest/theme3d/tst_colorgradient.qml89
-rw-r--r--tests/auto/qmltest/theme3d/tst_theme.qml266
-rw-r--r--tests/auto/qmltest/theme3d/tst_themecolor.qml66
-rw-r--r--tests/auto/qmltest/tst_qmltest.cpp20
-rw-r--r--tests/barstest/barstest.pro2
-rw-r--r--tests/barstest/barstest.qrc6
-rw-r--r--tests/barstest/chart.cpp160
-rw-r--r--tests/barstest/chart.h17
-rw-r--r--tests/barstest/main.cpp168
-rw-r--r--tests/barstest/shuttle.obj6349
-rw-r--r--tests/barstest/shuttle.pngbin0 -> 1361 bytes
-rw-r--r--tests/directional/main.cpp7
-rw-r--r--tests/directional/scatterdatamodifier.cpp9
-rw-r--r--tests/directional/scatterdatamodifier.h1
-rw-r--r--tests/qmlcamera/qml/qmlcamera/main.qml45
-rw-r--r--tests/qmldynamicdata/qml/qmldynamicdata/main.qml28
-rw-r--r--tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml122
-rw-r--r--tests/qmlperf/main.cpp47
-rw-r--r--tests/qmlperf/qml/qmlperf/main.qml189
-rw-r--r--tests/qmlperf/qml/qmlperf/script.js33
-rw-r--r--tests/qmlperf/qmlperf.pro12
-rw-r--r--tests/qmlperf/qmlperf.qrc6
-rw-r--r--tests/qmlvolume/datasource.cpp90
-rw-r--r--tests/qmlvolume/datasource.h38
-rw-r--r--tests/qmlvolume/main.cpp56
-rw-r--r--tests/qmlvolume/qml/qmlvolume/NewButton.qml52
-rw-r--r--tests/qmlvolume/qml/qmlvolume/main.qml164
-rw-r--r--tests/qmlvolume/qmlvolume.pro16
-rw-r--r--tests/qmlvolume/qmlvolume.qrc6
-rw-r--r--tests/scattertest/main.cpp96
-rw-r--r--tests/scattertest/scatterchart.cpp90
-rw-r--r--tests/scattertest/scatterchart.h11
-rw-r--r--tests/surfacetest/graphmodifier.cpp151
-rw-r--r--tests/surfacetest/graphmodifier.h16
-rw-r--r--tests/surfacetest/main.cpp151
-rw-r--r--tests/surfacetest/mapimage.pngbin0 -> 159540 bytes
-rw-r--r--tests/surfacetest/surfacetest.qrc1
-rw-r--r--tests/tests.pro43
-rw-r--r--tests/volumetrictest/cubeFilledFlat.obj54
-rw-r--r--tests/volumetrictest/logo.pngbin0 -> 2205 bytes
-rw-r--r--tests/volumetrictest/logo_no_padding.pngbin0 -> 2278 bytes
-rw-r--r--tests/volumetrictest/main.cpp168
-rw-r--r--tests/volumetrictest/volumetrictest.cpp716
-rw-r--r--tests/volumetrictest/volumetrictest.h80
-rw-r--r--tests/volumetrictest/volumetrictest.pro14
-rw-r--r--tests/volumetrictest/volumetrictest.qrc7
-rw-r--r--tools/blender/arrow.blendbin0 -> 497968 bytes
-rw-r--r--tools/blender/narrowArrow.blendbin0 -> 489112 bytes
324 files changed, 32714 insertions, 3104 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 6ab2606e..48e733ed 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,6 +1,6 @@
load(qt_build_config)
CONFIG += qt_example_installs
-MODULE_VERSION=1.1.1
+MODULE_VERSION=1.2.0
CMAKE_MODULE_TESTS=-
diff --git a/README b/README
index 78714891..667dcdaa 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
---------------------------
-Qt Data Visualization 1.1.1
+Qt Data Visualization 1.2.0
---------------------------
Qt Data Visualization module provides multiple graph types to visualize data in 3D space
@@ -8,7 +8,7 @@ both with C++ and Qt Quick 2.
System Requirements
===================
-- Qt 5.2 or newer
+- Qt 5.2.1 or newer
- OpenGL 2.1 or newer (recommended) or OpenGL ES2 (reduced feature set)
- Manipulating Qt Data Visualization graphs with QML Designer requires
Qt Creator 3.1 or newer
@@ -22,7 +22,7 @@ After running qmake, build the project with make:
(Linux) make
(Windows with MinGw) mingw32-make
(Windows with Visual Studio) nmake
- (OSX) make
+ (OS X) make
The above generates the default makefiles for your configuration, which is typically
the release build if you are using precompiled binary Qt distribution. To build both
@@ -42,7 +42,7 @@ For release builds:
qmake CONFIG+=debug_and_release
make release
-For both builds (Windows/Mac only):
+For both builds (Windows/OS X only):
qmake CONFIG+="debug_and_release build_all"
make
@@ -74,17 +74,18 @@ Please refer to the generated documentation for more information:
Known Issues
============
-- Android doesn't support both widgets and OpenGL simultaneously, so only
- the Qt Quick 2 API is usable in practice in Android.
+- Some platforms like Android and WinRT cannot handle multiple native windows properly,
+ so only the Qt Quick 2 versions of graphs are available in practice for those platforms.
- Shadows are not supported with OpenGL ES2 (including Angle builds in Windows).
- Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows).
+- QCustom3DVolume items are not supported with OpenGL ES2 (including Angle builds in Windows).
- Surfaces with non-straight rows and columns do not always render properly.
- Q3DLight class (and Light3D QML item) are currently not usable for anything.
-- Changing any of Q3DScene properties affecting subviewports currently has no effect.
-- The color style Q3DTheme::ColorStyleObjectGradient doesn't work for surface graphs.
+- Changing most of Q3DScene properties affecting subviewports currently has no effect.
- Widget based examples layout incorrectly in iOS.
- Reparenting a graph to an item in another QQuickWindow is not supported.
-- There is a low-impact binary break between 1.0 and 1.1. The break is due to a QML type
- registration conflict with QAbstractItemModel between QtDataVisualization and
- QtCommercial.Charts. Introducing the binary break makes it possible to use both
- Charts and Data Visualization in the same QML application.
+- Android builds of QML applications importing QtDataVisualization also require
+ "QT += datavisualization" in the pro file. This is because Qt Data Visualization QML plugin has
+ a dependency to Qt Data Visualization C++ library, which Qt Creator doesn't automatically add
+ to the deployment package.
+
diff --git a/dist/changes-1.1.0 b/dist/changes-1.1.0
index 7d27438b..d404cf09 100644
--- a/dist/changes-1.1.0
+++ b/dist/changes-1.1.0
@@ -73,4 +73,4 @@ Platform specific changes
- Fixed issue with graph not always updating before rotating the graph in iOS.
- Fixed shader linking error on some Android versions.
-- Fixed memory leaks in Mac and Android builds.
+- Fixed memory leaks in OS X and Android builds.
diff --git a/dist/changes-1.2.0 b/dist/changes-1.2.0
new file mode 100644
index 00000000..b23d83b1
--- /dev/null
+++ b/dist/changes-1.2.0
@@ -0,0 +1,79 @@
+Qt Data Visualization 1.2
+
+New features
+------------
+
+- Added support for volumetric custom objects (QCustom3DVolume) for rendering 3D voxel data.
+- Reflection support for bar graphs (floor only).
+- Polar horizontal axes supported for scatter and surface graphs.
+- Added flipHorizontalGrid property for surface to enable displaying grid in 2D orthographic
+ projections of the surface (e.g. 2D spectrogram graphs).
+- Added horizontalAspectRatio property for graphs to enable better control over graph dimensions.
+- Added an API for setting a custom texture for a surface series.
+- Added several properties to control the default input handler behavior.
+- Exposed default input handlers to QML API.
+- Camera can now be targeted at any point within axis ranges on the scatter and surface graphs.
+ On bar graphs, camera target is limited to any point on the graph floor.
+- Added possibility to scale custom items based on data ranges.
+- Added a property for specifying the locale for the graph, which affects how various labels
+ are formatted (e.g. which character is used for the decimal point).
+- Added a property for specifying the Y-value of the floor level on bar graphs.
+- Added a property to Q3DScene for querying the graph position at a screen position.
+- The default input handlers now zoom to cursor/pinch instead of zooming toward the center of the
+ graph. Added a property to restore the old zoom behavior.
+- Added properties to control the minimum and maximum allowable zoom level of the camera.
+- Added a method for getting the list of custom items added to the graph.
+- Added a property for specifying the graph background margin.
+
+Fixed issues
+------------
+
+General:
+- Label widths now update consistently when axis range changes.
+- Made selection texture creation more robust.
+- Grid lines and labels no longer change size if aspect ratio changes.
+- Q3DTheme::ColorStyleObjectGradient now works for surface graphs.
+- Removed the superfluous common.pri.
+- Fixed non-visible selected object drawing in static optimization mode.
+- Gradient color styles are now supported equally in both default and static optimization modes.
+- Specular highlight now works with rotated objects in static optimization mode.
+- Fixed a crash in static optimization mode when data is updated without resizing.
+- Fixed changing items in static optimization mode.
+- Fixed issues with static optimization mode when some items were outside axis ranges.
+- Slice mode grid lines should no longer vanish into the background when using high ambient light
+ value.
+- Reduced the size of the surface selection texture, allowing selection to work with larger
+ surfaces.
+- Fixed QAbstract3DGraph::renderToImage in OpenGL ES2 environments.
+- Fixed crash when attempting to enable slicing without row/column selection modes.
+- QCustom3DLabels now use the same shader as other labels, which means the specular highlight
+ no longer makes camera facing custom labels unreadable with some themes.
+- Made various selection queries thread safe.
+- Fixed selection query synchronization issue when using threaded renderer.
+- Font size is automatically reduced if the label gets too wide to fit the label texture.
+- Fixed the ordering of the subviews.
+- Fixed surface normals in cases where the surface values were not in the same order
+ (ascending or descending) along both axes.
+- Fixed a crash when removing and changing items on the same render frame.
+- Fixed an issue with grid line color on surface graphs.
+- Prevented selecting bars through the floor in bar graphs.
+- Fixed recurring GL_INVALID_VALUE OpenGL errors.
+- Improved the surface shadows.
+- Fixed the OpenGL context cleanup upon renderer destruction.
+- Fixed scatter item autosizing when adding a new series.
+- Fixed a crash related to selection render buffer reuse.
+- Fixed the flipped Z-coordinate for absolutely positioned custom items.
+- Fixed shadows when viewing the graph directly from above or below.
+
+New examples
+------------
+
+- Qmlspectrogram example added. It shows how to display 2D spectrogram using surface graph with
+ gradients and orthographic projection. Also demonstrates the use of polar axes.
+- Bars example now demonstrates zooming to selection, which leverages the new ability to
+ control the camera target.
+- Textured surface example added.
+- Volumetric example added. It shows how to use the new QCustom3DVolume object to visualize
+ volumetric data.
+- Reflection added to Bars and Customproxy examples.
+- Custom camera targeting added to Bars example.
diff --git a/examples/datavisualization/audiolevels/audiolevels.cpp b/examples/datavisualization/audiolevels/audiolevels.cpp
index 672e4984..c4a6b78c 100644
--- a/examples/datavisualization/audiolevels/audiolevels.cpp
+++ b/examples/datavisualization/audiolevels/audiolevels.cpp
@@ -74,7 +74,7 @@ AudioLevels::AudioLevels(Q3DBars *graph, QObject *parent)
m_audioInput = new QAudioInput(inputDevice, formatAudio, this);
#ifdef Q_OS_MAC
- // Mac seems to wait for entire buffer to fill before calling writeData, so use smaller buffer
+ // OS X seems to wait for entire buffer to fill before calling writeData, so use smaller buffer
m_audioInput->setBufferSize(256);
#else
m_audioInput->setBufferSize(1024);
diff --git a/examples/datavisualization/bars/doc/images/bars-example.png b/examples/datavisualization/bars/doc/images/bars-example.png
index fb79668d..c06fe2c1 100644
--- a/examples/datavisualization/bars/doc/images/bars-example.png
+++ b/examples/datavisualization/bars/doc/images/bars-example.png
Binary files differ
diff --git a/examples/datavisualization/bars/doc/src/bars.qdoc b/examples/datavisualization/bars/doc/src/bars.qdoc
index 9717fe8a..1117376f 100644
--- a/examples/datavisualization/bars/doc/src/bars.qdoc
+++ b/examples/datavisualization/bars/doc/src/bars.qdoc
@@ -188,6 +188,30 @@
You can use the same method with \c SelectionSlice and \c SelectionItem flags, as long as
you have either \c SelectionRow or \c SelectionColumn set as well.
+ \section1 Zooming to selection
+
+ As an example of adjusting camera target we have implemented an animation of zooming to
+ selection via a button press. Animation initializations are done in the constructor:
+
+ \snippet bars/graphmodifier.cpp 12
+
+ The function \c{GraphModifier::zoomToSelectedBar()} contains the rest of the functionality:
+
+ \snippet bars/graphmodifier.cpp 11
+
+ The QPropertyAnimation \c m_animationCameraTarget targets Q3DCamera::target property,
+ which takes a value normalized to the range (-1, 1). We figure out where the selected bar
+ is relative to axes, and use that as the end value for \c{m_animationCameraTarget}:
+
+ \snippet bars/graphmodifier.cpp 13
+ \dots
+ \snippet bars/graphmodifier.cpp 14
+
+ Likewise, we want to angle the camera so that it always points approximately to the center of
+ the graph at the end of the animation:
+
+ \snippet bars/graphmodifier.cpp 15
+
\section1 Example contents
*/
diff --git a/examples/datavisualization/bars/graphmodifier.cpp b/examples/datavisualization/bars/graphmodifier.cpp
index 9c280bfb..587bc1d6 100644
--- a/examples/datavisualization/bars/graphmodifier.cpp
+++ b/examples/datavisualization/bars/graphmodifier.cpp
@@ -26,6 +26,7 @@
#include <QtDataVisualization/q3dtheme.h>
#include <QtCore/QTime>
#include <QtWidgets/QComboBox>
+#include <QtCore/qmath.h>
using namespace QtDataVisualization;
@@ -106,6 +107,39 @@ GraphModifier::GraphModifier(Q3DBars *bargraph)
//! [9]
resetTemperatureData();
//! [9]
+
+ // Set up property animations for zooming to the selected bar
+ //! [12]
+ Q3DCamera *camera = m_graph->scene()->activeCamera();
+ m_defaultAngleX = camera->xRotation();
+ m_defaultAngleY = camera->yRotation();
+ m_defaultZoom = camera->zoomLevel();
+ m_defaultTarget = camera->target();
+
+ m_animationCameraX.setTargetObject(camera);
+ m_animationCameraY.setTargetObject(camera);
+ m_animationCameraZoom.setTargetObject(camera);
+ m_animationCameraTarget.setTargetObject(camera);
+
+ m_animationCameraX.setPropertyName("xRotation");
+ m_animationCameraY.setPropertyName("yRotation");
+ m_animationCameraZoom.setPropertyName("zoomLevel");
+ m_animationCameraTarget.setPropertyName("target");
+
+ int duration = 1700;
+ m_animationCameraX.setDuration(duration);
+ m_animationCameraY.setDuration(duration);
+ m_animationCameraZoom.setDuration(duration);
+ m_animationCameraTarget.setDuration(duration);
+
+ // The zoom always first zooms out above the graph and then zooms in
+ qreal zoomOutFraction = 0.3;
+ m_animationCameraX.setKeyValueAt(zoomOutFraction, QVariant::fromValue(0.0f));
+ m_animationCameraY.setKeyValueAt(zoomOutFraction, QVariant::fromValue(90.0f));
+ m_animationCameraZoom.setKeyValueAt(zoomOutFraction, QVariant::fromValue(50.0f));
+ m_animationCameraTarget.setKeyValueAt(zoomOutFraction,
+ QVariant::fromValue(QVector3D(0.0f, 0.0f, 0.0f)));
+ //! [12]
}
//! [0]
@@ -187,6 +221,14 @@ void GraphModifier::changeStyle(int style)
void GraphModifier::changePresetCamera()
{
+ m_animationCameraX.stop();
+ m_animationCameraY.stop();
+ m_animationCameraZoom.stop();
+ m_animationCameraTarget.stop();
+
+ // Restore camera target in case animation has changed it
+ m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
+
//! [10]
static int preset = Q3DCamera::CameraPresetFront;
@@ -263,6 +305,74 @@ void GraphModifier::setAxisTitleFixed(bool enabled)
m_yearAxis->setTitleFixed(enabled);
}
+//! [11]
+void GraphModifier::zoomToSelectedBar()
+{
+ m_animationCameraX.stop();
+ m_animationCameraY.stop();
+ m_animationCameraZoom.stop();
+ m_animationCameraTarget.stop();
+
+ Q3DCamera *camera = m_graph->scene()->activeCamera();
+ float currentX = camera->xRotation();
+ float currentY = camera->yRotation();
+ float currentZoom = camera->zoomLevel();
+ QVector3D currentTarget = camera->target();
+
+ m_animationCameraX.setStartValue(QVariant::fromValue(currentX));
+ m_animationCameraY.setStartValue(QVariant::fromValue(currentY));
+ m_animationCameraZoom.setStartValue(QVariant::fromValue(currentZoom));
+ m_animationCameraTarget.setStartValue(QVariant::fromValue(currentTarget));
+
+ QPoint selectedBar = m_graph->selectedSeries()
+ ? m_graph->selectedSeries()->selectedBar()
+ : QBar3DSeries::invalidSelectionPosition();
+
+ if (selectedBar != QBar3DSeries::invalidSelectionPosition()) {
+ // Normalize selected bar position within axis range to determine target coordinates
+ //! [13]
+ QVector3D endTarget;
+ float xMin = m_graph->columnAxis()->min();
+ float xRange = m_graph->columnAxis()->max() - xMin;
+ float zMin = m_graph->rowAxis()->min();
+ float zRange = m_graph->rowAxis()->max() - zMin;
+ endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f);
+ endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f);
+ //! [13]
+
+ // Rotate the camera so that it always points approximately to the graph center
+ //! [15]
+ qreal endAngleX = qAtan(qreal(endTarget.z() / endTarget.x())) / M_PI * -180.0 + 90.0;
+ if (endTarget.x() > 0.0f)
+ endAngleX -= 180.0f;
+ float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(selectedBar.x(),
+ selectedBar.y())->value();
+ float endAngleY = barValue >= 0.0f ? 30.0f : -30.0f;
+ if (m_graph->valueAxis()->reversed())
+ endAngleY *= -1.0f;
+ //! [15]
+
+ m_animationCameraX.setEndValue(QVariant::fromValue(float(endAngleX)));
+ m_animationCameraY.setEndValue(QVariant::fromValue(endAngleY));
+ m_animationCameraZoom.setEndValue(QVariant::fromValue(250));
+ //! [14]
+ m_animationCameraTarget.setEndValue(QVariant::fromValue(endTarget));
+ //! [14]
+ } else {
+ // No selected bar, so return to the default view
+ m_animationCameraX.setEndValue(QVariant::fromValue(m_defaultAngleX));
+ m_animationCameraY.setEndValue(QVariant::fromValue(m_defaultAngleY));
+ m_animationCameraZoom.setEndValue(QVariant::fromValue(m_defaultZoom));
+ m_animationCameraTarget.setEndValue(QVariant::fromValue(m_defaultTarget));
+ }
+
+ m_animationCameraX.start();
+ m_animationCameraY.start();
+ m_animationCameraZoom.start();
+ m_animationCameraTarget.start();
+}
+//! [11]
+
void GraphModifier::changeShadowQuality(int quality)
{
QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality);
@@ -310,3 +420,8 @@ void GraphModifier::setReverseValueAxis(int enabled)
{
m_graph->valueAxis()->setReversed(enabled);
}
+
+void GraphModifier::setReflection(bool enabled)
+{
+ m_graph->setReflection(enabled);
+}
diff --git a/examples/datavisualization/bars/graphmodifier.h b/examples/datavisualization/bars/graphmodifier.h
index 107ffbab..22b00923 100644
--- a/examples/datavisualization/bars/graphmodifier.h
+++ b/examples/datavisualization/bars/graphmodifier.h
@@ -27,6 +27,7 @@
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtCore/QPointer>
+#include <QtCore/QPropertyAnimation>
using namespace QtDataVisualization;
@@ -49,6 +50,7 @@ public:
void setSmoothBars(int smooth);
void setSeriesVisibility(int enabled);
void setReverseValueAxis(int enabled);
+ void setReflection(bool enabled);
public slots:
void changeRange(int range);
@@ -60,6 +62,7 @@ public slots:
void changeLabelRotation(int rotation);
void setAxisTitleVisibility(bool enabled);
void setAxisTitleFixed(bool enabled);
+ void zoomToSelectedBar();
signals:
void shadowQualityChanged(int quality);
@@ -86,6 +89,14 @@ private:
QBar3DSeries *m_secondarySeries;
QAbstract3DSeries::Mesh m_barMesh;
bool m_smooth;
+ QPropertyAnimation m_animationCameraX;
+ QPropertyAnimation m_animationCameraY;
+ QPropertyAnimation m_animationCameraZoom;
+ QPropertyAnimation m_animationCameraTarget;
+ float m_defaultAngleX;
+ float m_defaultAngleY;
+ float m_defaultZoom;
+ QVector3D m_defaultTarget;
};
#endif
diff --git a/examples/datavisualization/bars/main.cpp b/examples/datavisualization/bars/main.cpp
index 96abdc51..a7df14e0 100644
--- a/examples/datavisualization/bars/main.cpp
+++ b/examples/datavisualization/bars/main.cpp
@@ -72,6 +72,7 @@ int main(int argc, char **argv)
smoothCheckBox->setText(QStringLiteral("Smooth bars"));
smoothCheckBox->setChecked(false);
+
QComboBox *barStyleList = new QComboBox(widget);
barStyleList->addItem(QStringLiteral("Bar"), int(QAbstract3DSeries::MeshBar));
barStyleList->addItem(QStringLiteral("Pyramid"), int(QAbstract3DSeries::MeshPyramid));
@@ -84,6 +85,9 @@ int main(int argc, char **argv)
QPushButton *cameraButton = new QPushButton(widget);
cameraButton->setText(QStringLiteral("Change camera preset"));
+ QPushButton *zoomToSelectedButton = new QPushButton(widget);
+ zoomToSelectedButton->setText(QStringLiteral("Zoom to selected bar"));
+
QComboBox *selectionModeList = new QComboBox(widget);
selectionModeList->addItem(QStringLiteral("None"),
int(QAbstract3DGraph::SelectionNone));
@@ -136,6 +140,10 @@ int main(int argc, char **argv)
reverseValueAxisCheckBox->setText(QStringLiteral("Reverse value axis"));
reverseValueAxisCheckBox->setChecked(false);
+ QCheckBox *reflectionCheckBox = new QCheckBox(widget);
+ reflectionCheckBox->setText(QStringLiteral("Show reflections"));
+ reflectionCheckBox->setChecked(false);
+
//! [4]
QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget);
rotationSliderX->setTickInterval(30);
@@ -206,9 +214,11 @@ int main(int argc, char **argv)
//! [5]
vLayout->addWidget(labelButton, 0, Qt::AlignTop);
vLayout->addWidget(cameraButton, 0, Qt::AlignTop);
+ vLayout->addWidget(zoomToSelectedButton, 0, Qt::AlignTop);
vLayout->addWidget(backgroundCheckBox);
vLayout->addWidget(gridCheckBox);
vLayout->addWidget(smoothCheckBox);
+ vLayout->addWidget(reflectionCheckBox);
vLayout->addWidget(seriesCheckBox);
vLayout->addWidget(reverseValueAxisCheckBox);
vLayout->addWidget(axisTitlesVisibleCB);
@@ -243,6 +253,8 @@ int main(int argc, char **argv)
&GraphModifier::changeLabelBackground);
QObject::connect(cameraButton, &QPushButton::clicked, modifier,
&GraphModifier::changePresetCamera);
+ QObject::connect(zoomToSelectedButton, &QPushButton::clicked, modifier,
+ &GraphModifier::zoomToSelectedBar);
QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setBackgroundEnabled);
@@ -254,6 +266,8 @@ int main(int argc, char **argv)
&GraphModifier::setSeriesVisibility);
QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setReverseValueAxis);
+ QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setReflection);
QObject::connect(modifier, &GraphModifier::backgroundEnabledChanged,
backgroundCheckBox, &QCheckBox::setChecked);
diff --git a/examples/datavisualization/customproxy/doc/images/customproxy-example.png b/examples/datavisualization/customproxy/doc/images/customproxy-example.png
index 753b8951..4f82943a 100644
--- a/examples/datavisualization/customproxy/doc/images/customproxy-example.png
+++ b/examples/datavisualization/customproxy/doc/images/customproxy-example.png
Binary files differ
diff --git a/examples/datavisualization/customproxy/rainfallgraph.cpp b/examples/datavisualization/customproxy/rainfallgraph.cpp
index 024fc2a1..bcd67acc 100644
--- a/examples/datavisualization/customproxy/rainfallgraph.cpp
+++ b/examples/datavisualization/customproxy/rainfallgraph.cpp
@@ -49,7 +49,7 @@ RainfallGraph::RainfallGraph(Q3DBars *rainfall)
// Set up bar specifications; make the bars as wide as they are deep,
// and add a small space between the bars
m_graph->setBarThickness(1.0f);
- m_graph->setBarSpacing(QSizeF(0.2, 0.2));
+ m_graph->setBarSpacing(QSizeF(1.1, 1.1));
// Set axis labels and titles
QStringList months;
@@ -85,6 +85,9 @@ RainfallGraph::RainfallGraph(Q3DBars *rainfall)
// Set window title
m_graph->setTitle(QStringLiteral("Monthly rainfall in Northern Finland"));
+
+ // Set reflections on
+ m_graph->setReflection(true);
}
RainfallGraph::~RainfallGraph()
diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro
index 1f72820c..b6ece48d 100644
--- a/examples/datavisualization/datavisualization.pro
+++ b/examples/datavisualization/datavisualization.pro
@@ -1,14 +1,17 @@
TEMPLATE = subdirs
-SUBDIRS += qmlbars \
- qmlscatter \
- qmlsurface \
- qmlcustominput \
- qmllegend \
- qmlmultigraph \
- qmloscilloscope \
- qmlsurfacelayers \
- qmlaxisformatter \
- qmlaxisdrag
+qtHaveModule(quick) {
+ SUBDIRS += qmlbars \
+ qmlscatter \
+ qmlsurface \
+ qmlcustominput \
+ qmllegend \
+ qmlmultigraph \
+ qmloscilloscope \
+ qmlsurfacelayers \
+ qmlaxisformatter \
+ qmlaxisdrag \
+ qmlspectrogram
+}
!android:!ios {
SUBDIRS += bars \
@@ -19,7 +22,10 @@ SUBDIRS += qmlbars \
surface \
rotations \
draggableaxes \
- customitems
+ customitems \
+ texturesurface \
+ volumetric
+
+ qtHaveModule(multimedia): SUBDIRS += audiolevels
}
-qtHaveModule(multimedia):!android:!ios: SUBDIRS += audiolevels
diff --git a/examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.png b/examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.png
new file mode 100644
index 00000000..de376cd9
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.png
Binary files differ
diff --git a/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc
new file mode 100644
index 00000000..0aebdde0
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+/*!
+ \example qmlspectrogram
+ \title Qt Quick 2 Spectrogram Example
+ \ingroup qtdatavisualization_examples
+ \brief Showing spectrogram graph in a QML application.
+
+ The Qt Quick 2 Spectrogram example demonstrates how to show a polar and cartesian spectrograms
+ and how to utilize orthographic projection to show them in 2D.
+
+ \image qmlspectrogram-example.png
+
+ Spectrogram is simply a surface graph with a range gradient used to emphasize the different
+ values. Typically spectrograms are shown with two dimensional surfaces, which we simulate
+ with a top down orthographic view of the graph. To enforce the 2D effect, we disable the
+ graph rotation via mouse or touch when in the orthographic mode.
+
+ The focus in this example is on showing how to display spectrograms, so the basic
+ functionality is not explained. For more detailed QML example documentation,
+ see \l{Qt Quick 2 Scatter Example}.
+
+ \section1 Creating a spectrogram
+
+ To create a 2D spectrogram, we define a Surface3D item:
+
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 0
+
+ The key properties for enabling the 2D effect are
+ \l{AbstractGraph3D::orthoProjection}{orthoProjection} and
+ \l{Camera3D::cameraPreset}{scene.activeCamera.cameraPreset}. We remove the perspective by
+ enabling orthographic projection for the graph, and then we eliminate the Y-dimension by
+ viewing the graph directly from above:
+
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 1
+
+ Since this viewpoint causes the horizontal axis grid to be mostly obscured by the surface,
+ we also specify that the horizontal grid should be drawn on top of the graph:
+
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 2
+
+ \section1 Polar spectrogram
+
+ Depending on the data, it is sometimes more natural to use a polar graph instead of a cartesian
+ one. Qt Data Visualization supports this via \l{AbstractGraph3D::polar}{polar} property.
+ In this example we provide a button to switch between polar and cartesian modes:
+
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 3
+
+ In the polar mode, the X-axis is converted into the angular polar axis, and the Z-axis is
+ converted into the radial polar axis. The surface points are recalculated according to new axes.
+
+ The radial axis labels are drawn outside the graph by default, but in this example we want to
+ draw them right next to the 0 degree angular axis inside the graph, so we define only a tiny
+ offset for them:
+
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 4
+
+ To enforce the 2D effect, graph rotation via user input is disabled when in orthographic mode.
+ We do this by specifying a new input handler:
+
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 5
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 7
+ \dots 0
+ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 6
+ \dots 0
+
+ When the projection mode changes, we adjust the value of the
+ \l{InputHandler3D::rotationEnabled}{rotationEnabled} property of the \c{customInputHandler}
+ to control the rotation.
+
+ \section1 Example contents
+*/
diff --git a/examples/datavisualization/qmlspectrogram/main.cpp b/examples/datavisualization/qmlspectrogram/main.cpp
new file mode 100644
index 00000000..87665564
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/main.cpp
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtGui/QGuiApplication>
+#include <QtCore/QDir>
+#include <QtQuick/QQuickView>
+#include <QtQml/QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+
+ // The following are needed to make examples run without having to install the module
+ // in desktop environments.
+#ifdef Q_OS_WIN
+ QString extraImportPath(QStringLiteral("%1/../../../../%2"));
+#else
+ QString extraImportPath(QStringLiteral("%1/../../../%2"));
+#endif
+ engine.addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(),
+ QString::fromLatin1("qml")));
+ engine.load(QUrl(QStringLiteral("qrc:/qml/qml/qmlspectrogram/main.qml")));
+
+ return app.exec();
+}
diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml
new file mode 100644
index 00000000..fc54edf4
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml
@@ -0,0 +1,1560 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.1
+
+Item {
+ property alias model: dataModel
+
+ ListModel {
+ id: dataModel
+ ListElement{ radius: "0"; angle: "0"; value: "50"; }
+ ListElement{ radius: "0"; angle: "5"; value: "54.3578"; }
+ ListElement{ radius: "0"; angle: "10"; value: "58.6824"; }
+ ListElement{ radius: "0"; angle: "15"; value: "62.941"; }
+ ListElement{ radius: "0"; angle: "20"; value: "67.101"; }
+ ListElement{ radius: "0"; angle: "25"; value: "71.1309"; }
+ ListElement{ radius: "0"; angle: "30"; value: "75"; }
+ ListElement{ radius: "0"; angle: "35"; value: "78.6788"; }
+ ListElement{ radius: "0"; angle: "40"; value: "82.1394"; }
+ ListElement{ radius: "0"; angle: "45"; value: "85.3553"; }
+ ListElement{ radius: "0"; angle: "50"; value: "88.3022"; }
+ ListElement{ radius: "0"; angle: "55"; value: "90.9576"; }
+ ListElement{ radius: "0"; angle: "60"; value: "93.3013"; }
+ ListElement{ radius: "0"; angle: "65"; value: "95.3154"; }
+ ListElement{ radius: "0"; angle: "70"; value: "96.9846"; }
+ ListElement{ radius: "0"; angle: "75"; value: "98.2963"; }
+ ListElement{ radius: "0"; angle: "80"; value: "99.2404"; }
+ ListElement{ radius: "0"; angle: "85"; value: "99.8097"; }
+ ListElement{ radius: "0"; angle: "90"; value: "100"; }
+ ListElement{ radius: "0"; angle: "95"; value: "99.8097"; }
+ ListElement{ radius: "0"; angle: "100"; value: "99.2404"; }
+ ListElement{ radius: "0"; angle: "105"; value: "98.2963"; }
+ ListElement{ radius: "0"; angle: "110"; value: "96.9846"; }
+ ListElement{ radius: "0"; angle: "115"; value: "95.3154"; }
+ ListElement{ radius: "0"; angle: "120"; value: "93.3013"; }
+ ListElement{ radius: "0"; angle: "125"; value: "90.9576"; }
+ ListElement{ radius: "0"; angle: "130"; value: "88.3022"; }
+ ListElement{ radius: "0"; angle: "135"; value: "85.3553"; }
+ ListElement{ radius: "0"; angle: "140"; value: "82.1394"; }
+ ListElement{ radius: "0"; angle: "145"; value: "78.6788"; }
+ ListElement{ radius: "0"; angle: "150"; value: "75"; }
+ ListElement{ radius: "0"; angle: "155"; value: "71.1309"; }
+ ListElement{ radius: "0"; angle: "160"; value: "67.101"; }
+ ListElement{ radius: "0"; angle: "165"; value: "62.941"; }
+ ListElement{ radius: "0"; angle: "170"; value: "58.6824"; }
+ ListElement{ radius: "0"; angle: "175"; value: "54.3578"; }
+ ListElement{ radius: "0"; angle: "180"; value: "50"; }
+ ListElement{ radius: "0"; angle: "185"; value: "45.6422"; }
+ ListElement{ radius: "0"; angle: "190"; value: "41.3176"; }
+ ListElement{ radius: "0"; angle: "195"; value: "37.059"; }
+ ListElement{ radius: "0"; angle: "200"; value: "32.899"; }
+ ListElement{ radius: "0"; angle: "205"; value: "28.8691"; }
+ ListElement{ radius: "0"; angle: "210"; value: "25"; }
+ ListElement{ radius: "0"; angle: "215"; value: "21.3212"; }
+ ListElement{ radius: "0"; angle: "220"; value: "17.8606"; }
+ ListElement{ radius: "0"; angle: "225"; value: "14.6447"; }
+ ListElement{ radius: "0"; angle: "230"; value: "11.6978"; }
+ ListElement{ radius: "0"; angle: "235"; value: "9.0424"; }
+ ListElement{ radius: "0"; angle: "240"; value: "6.69873"; }
+ ListElement{ radius: "0"; angle: "245"; value: "4.68461"; }
+ ListElement{ radius: "0"; angle: "250"; value: "3.01537"; }
+ ListElement{ radius: "0"; angle: "255"; value: "1.70371"; }
+ ListElement{ radius: "0"; angle: "260"; value: "0.759612"; }
+ ListElement{ radius: "0"; angle: "265"; value: "0.190265"; }
+ ListElement{ radius: "0"; angle: "270"; value: "0"; }
+ ListElement{ radius: "0"; angle: "275"; value: "0.190265"; }
+ ListElement{ radius: "0"; angle: "280"; value: "0.759612"; }
+ ListElement{ radius: "0"; angle: "285"; value: "1.70371"; }
+ ListElement{ radius: "0"; angle: "290"; value: "3.01537"; }
+ ListElement{ radius: "0"; angle: "295"; value: "4.68461"; }
+ ListElement{ radius: "0"; angle: "300"; value: "6.69873"; }
+ ListElement{ radius: "0"; angle: "305"; value: "9.0424"; }
+ ListElement{ radius: "0"; angle: "310"; value: "11.6978"; }
+ ListElement{ radius: "0"; angle: "315"; value: "14.6447"; }
+ ListElement{ radius: "0"; angle: "320"; value: "17.8606"; }
+ ListElement{ radius: "0"; angle: "325"; value: "21.3212"; }
+ ListElement{ radius: "0"; angle: "330"; value: "25"; }
+ ListElement{ radius: "0"; angle: "335"; value: "28.8691"; }
+ ListElement{ radius: "0"; angle: "340"; value: "32.899"; }
+ ListElement{ radius: "0"; angle: "345"; value: "37.059"; }
+ ListElement{ radius: "0"; angle: "350"; value: "41.3176"; }
+ ListElement{ radius: "0"; angle: "355"; value: "45.6422"; }
+ ListElement{ radius: "0"; angle: "360"; value: "50"; }
+ ListElement{ radius: "5"; angle: "0"; value: "49.3844"; }
+ ListElement{ radius: "5"; angle: "5"; value: "53.7422"; }
+ ListElement{ radius: "5"; angle: "10"; value: "58.0668"; }
+ ListElement{ radius: "5"; angle: "15"; value: "62.3254"; }
+ ListElement{ radius: "5"; angle: "20"; value: "66.4854"; }
+ ListElement{ radius: "5"; angle: "25"; value: "70.5153"; }
+ ListElement{ radius: "5"; angle: "30"; value: "74.3844"; }
+ ListElement{ radius: "5"; angle: "35"; value: "78.0632"; }
+ ListElement{ radius: "5"; angle: "40"; value: "81.5238"; }
+ ListElement{ radius: "5"; angle: "45"; value: "84.7398"; }
+ ListElement{ radius: "5"; angle: "50"; value: "87.6866"; }
+ ListElement{ radius: "5"; angle: "55"; value: "90.342"; }
+ ListElement{ radius: "5"; angle: "60"; value: "92.6857"; }
+ ListElement{ radius: "5"; angle: "65"; value: "94.6998"; }
+ ListElement{ radius: "5"; angle: "70"; value: "96.369"; }
+ ListElement{ radius: "5"; angle: "75"; value: "97.6807"; }
+ ListElement{ radius: "5"; angle: "80"; value: "98.6248"; }
+ ListElement{ radius: "5"; angle: "85"; value: "99.1942"; }
+ ListElement{ radius: "5"; angle: "90"; value: "99.3844"; }
+ ListElement{ radius: "5"; angle: "95"; value: "99.1942"; }
+ ListElement{ radius: "5"; angle: "100"; value: "98.6248"; }
+ ListElement{ radius: "5"; angle: "105"; value: "97.6807"; }
+ ListElement{ radius: "5"; angle: "110"; value: "96.369"; }
+ ListElement{ radius: "5"; angle: "115"; value: "94.6998"; }
+ ListElement{ radius: "5"; angle: "120"; value: "92.6857"; }
+ ListElement{ radius: "5"; angle: "125"; value: "90.342"; }
+ ListElement{ radius: "5"; angle: "130"; value: "87.6866"; }
+ ListElement{ radius: "5"; angle: "135"; value: "84.7398"; }
+ ListElement{ radius: "5"; angle: "140"; value: "81.5238"; }
+ ListElement{ radius: "5"; angle: "145"; value: "78.0632"; }
+ ListElement{ radius: "5"; angle: "150"; value: "74.3844"; }
+ ListElement{ radius: "5"; angle: "155"; value: "70.5153"; }
+ ListElement{ radius: "5"; angle: "160"; value: "66.4854"; }
+ ListElement{ radius: "5"; angle: "165"; value: "62.3254"; }
+ ListElement{ radius: "5"; angle: "170"; value: "58.0668"; }
+ ListElement{ radius: "5"; angle: "175"; value: "53.7422"; }
+ ListElement{ radius: "5"; angle: "180"; value: "49.3844"; }
+ ListElement{ radius: "5"; angle: "185"; value: "45.0266"; }
+ ListElement{ radius: "5"; angle: "190"; value: "40.702"; }
+ ListElement{ radius: "5"; angle: "195"; value: "36.4435"; }
+ ListElement{ radius: "5"; angle: "200"; value: "32.2834"; }
+ ListElement{ radius: "5"; angle: "205"; value: "28.2535"; }
+ ListElement{ radius: "5"; angle: "210"; value: "24.3844"; }
+ ListElement{ radius: "5"; angle: "215"; value: "20.7056"; }
+ ListElement{ radius: "5"; angle: "220"; value: "17.245"; }
+ ListElement{ radius: "5"; angle: "225"; value: "14.0291"; }
+ ListElement{ radius: "5"; angle: "230"; value: "11.0822"; }
+ ListElement{ radius: "5"; angle: "235"; value: "8.42681"; }
+ ListElement{ radius: "5"; angle: "240"; value: "6.08315"; }
+ ListElement{ radius: "5"; angle: "245"; value: "4.06903"; }
+ ListElement{ radius: "5"; angle: "250"; value: "2.39979"; }
+ ListElement{ radius: "5"; angle: "255"; value: "1.08813"; }
+ ListElement{ radius: "5"; angle: "260"; value: "0.144029"; }
+ ListElement{ radius: "5"; angle: "265"; value: "-0.425318"; }
+ ListElement{ radius: "5"; angle: "270"; value: "-0.615583"; }
+ ListElement{ radius: "5"; angle: "275"; value: "-0.425318"; }
+ ListElement{ radius: "5"; angle: "280"; value: "0.144029"; }
+ ListElement{ radius: "5"; angle: "285"; value: "1.08813"; }
+ ListElement{ radius: "5"; angle: "290"; value: "2.39979"; }
+ ListElement{ radius: "5"; angle: "295"; value: "4.06903"; }
+ ListElement{ radius: "5"; angle: "300"; value: "6.08315"; }
+ ListElement{ radius: "5"; angle: "305"; value: "8.42681"; }
+ ListElement{ radius: "5"; angle: "310"; value: "11.0822"; }
+ ListElement{ radius: "5"; angle: "315"; value: "14.0291"; }
+ ListElement{ radius: "5"; angle: "320"; value: "17.245"; }
+ ListElement{ radius: "5"; angle: "325"; value: "20.7056"; }
+ ListElement{ radius: "5"; angle: "330"; value: "24.3844"; }
+ ListElement{ radius: "5"; angle: "335"; value: "28.2535"; }
+ ListElement{ radius: "5"; angle: "340"; value: "32.2834"; }
+ ListElement{ radius: "5"; angle: "345"; value: "36.4435"; }
+ ListElement{ radius: "5"; angle: "350"; value: "40.702"; }
+ ListElement{ radius: "5"; angle: "355"; value: "45.0266"; }
+ ListElement{ radius: "5"; angle: "360"; value: "49.3844"; }
+ ListElement{ radius: "10"; angle: "0"; value: "47.5528"; }
+ ListElement{ radius: "10"; angle: "5"; value: "51.9106"; }
+ ListElement{ radius: "10"; angle: "10"; value: "56.2352"; }
+ ListElement{ radius: "10"; angle: "15"; value: "60.4938"; }
+ ListElement{ radius: "10"; angle: "20"; value: "64.6538"; }
+ ListElement{ radius: "10"; angle: "25"; value: "68.6837"; }
+ ListElement{ radius: "10"; angle: "30"; value: "72.5528"; }
+ ListElement{ radius: "10"; angle: "35"; value: "76.2316"; }
+ ListElement{ radius: "10"; angle: "40"; value: "79.6922"; }
+ ListElement{ radius: "10"; angle: "45"; value: "82.9082"; }
+ ListElement{ radius: "10"; angle: "50"; value: "85.855"; }
+ ListElement{ radius: "10"; angle: "55"; value: "88.5104"; }
+ ListElement{ radius: "10"; angle: "60"; value: "90.8541"; }
+ ListElement{ radius: "10"; angle: "65"; value: "92.8682"; }
+ ListElement{ radius: "10"; angle: "70"; value: "94.5375"; }
+ ListElement{ radius: "10"; angle: "75"; value: "95.8491"; }
+ ListElement{ radius: "10"; angle: "80"; value: "96.7932"; }
+ ListElement{ radius: "10"; angle: "85"; value: "97.3626"; }
+ ListElement{ radius: "10"; angle: "90"; value: "97.5528"; }
+ ListElement{ radius: "10"; angle: "95"; value: "97.3626"; }
+ ListElement{ radius: "10"; angle: "100"; value: "96.7932"; }
+ ListElement{ radius: "10"; angle: "105"; value: "95.8491"; }
+ ListElement{ radius: "10"; angle: "110"; value: "94.5375"; }
+ ListElement{ radius: "10"; angle: "115"; value: "92.8682"; }
+ ListElement{ radius: "10"; angle: "120"; value: "90.8541"; }
+ ListElement{ radius: "10"; angle: "125"; value: "88.5104"; }
+ ListElement{ radius: "10"; angle: "130"; value: "85.855"; }
+ ListElement{ radius: "10"; angle: "135"; value: "82.9082"; }
+ ListElement{ radius: "10"; angle: "140"; value: "79.6922"; }
+ ListElement{ radius: "10"; angle: "145"; value: "76.2316"; }
+ ListElement{ radius: "10"; angle: "150"; value: "72.5528"; }
+ ListElement{ radius: "10"; angle: "155"; value: "68.6837"; }
+ ListElement{ radius: "10"; angle: "160"; value: "64.6538"; }
+ ListElement{ radius: "10"; angle: "165"; value: "60.4938"; }
+ ListElement{ radius: "10"; angle: "170"; value: "56.2352"; }
+ ListElement{ radius: "10"; angle: "175"; value: "51.9106"; }
+ ListElement{ radius: "10"; angle: "180"; value: "47.5528"; }
+ ListElement{ radius: "10"; angle: "185"; value: "43.195"; }
+ ListElement{ radius: "10"; angle: "190"; value: "38.8704"; }
+ ListElement{ radius: "10"; angle: "195"; value: "34.6119"; }
+ ListElement{ radius: "10"; angle: "200"; value: "30.4518"; }
+ ListElement{ radius: "10"; angle: "205"; value: "26.4219"; }
+ ListElement{ radius: "10"; angle: "210"; value: "22.5528"; }
+ ListElement{ radius: "10"; angle: "215"; value: "18.874"; }
+ ListElement{ radius: "10"; angle: "220"; value: "15.4134"; }
+ ListElement{ radius: "10"; angle: "225"; value: "12.1975"; }
+ ListElement{ radius: "10"; angle: "230"; value: "9.2506"; }
+ ListElement{ radius: "10"; angle: "235"; value: "6.59522"; }
+ ListElement{ radius: "10"; angle: "240"; value: "4.25156"; }
+ ListElement{ radius: "10"; angle: "245"; value: "2.23744"; }
+ ListElement{ radius: "10"; angle: "250"; value: "0.568195"; }
+ ListElement{ radius: "10"; angle: "255"; value: "-0.743465"; }
+ ListElement{ radius: "10"; angle: "260"; value: "-1.68756"; }
+ ListElement{ radius: "10"; angle: "265"; value: "-2.25691"; }
+ ListElement{ radius: "10"; angle: "270"; value: "-2.44717"; }
+ ListElement{ radius: "10"; angle: "275"; value: "-2.25691"; }
+ ListElement{ radius: "10"; angle: "280"; value: "-1.68756"; }
+ ListElement{ radius: "10"; angle: "285"; value: "-0.743465"; }
+ ListElement{ radius: "10"; angle: "290"; value: "0.568195"; }
+ ListElement{ radius: "10"; angle: "295"; value: "2.23744"; }
+ ListElement{ radius: "10"; angle: "300"; value: "4.25156"; }
+ ListElement{ radius: "10"; angle: "305"; value: "6.59522"; }
+ ListElement{ radius: "10"; angle: "310"; value: "9.2506"; }
+ ListElement{ radius: "10"; angle: "315"; value: "12.1975"; }
+ ListElement{ radius: "10"; angle: "320"; value: "15.4134"; }
+ ListElement{ radius: "10"; angle: "325"; value: "18.874"; }
+ ListElement{ radius: "10"; angle: "330"; value: "22.5528"; }
+ ListElement{ radius: "10"; angle: "335"; value: "26.4219"; }
+ ListElement{ radius: "10"; angle: "340"; value: "30.4518"; }
+ ListElement{ radius: "10"; angle: "345"; value: "34.6119"; }
+ ListElement{ radius: "10"; angle: "350"; value: "38.8704"; }
+ ListElement{ radius: "10"; angle: "355"; value: "43.195"; }
+ ListElement{ radius: "10"; angle: "360"; value: "47.5528"; }
+ ListElement{ radius: "15"; angle: "0"; value: "44.5503"; }
+ ListElement{ radius: "15"; angle: "5"; value: "48.9081"; }
+ ListElement{ radius: "15"; angle: "10"; value: "53.2327"; }
+ ListElement{ radius: "15"; angle: "15"; value: "57.4913"; }
+ ListElement{ radius: "15"; angle: "20"; value: "61.6513"; }
+ ListElement{ radius: "15"; angle: "25"; value: "65.6812"; }
+ ListElement{ radius: "15"; angle: "30"; value: "69.5503"; }
+ ListElement{ radius: "15"; angle: "35"; value: "73.2291"; }
+ ListElement{ radius: "15"; angle: "40"; value: "76.6897"; }
+ ListElement{ radius: "15"; angle: "45"; value: "79.9057"; }
+ ListElement{ radius: "15"; angle: "50"; value: "82.8525"; }
+ ListElement{ radius: "15"; angle: "55"; value: "85.5079"; }
+ ListElement{ radius: "15"; angle: "60"; value: "87.8516"; }
+ ListElement{ radius: "15"; angle: "65"; value: "89.8657"; }
+ ListElement{ radius: "15"; angle: "70"; value: "91.535"; }
+ ListElement{ radius: "15"; angle: "75"; value: "92.8466"; }
+ ListElement{ radius: "15"; angle: "80"; value: "93.7907"; }
+ ListElement{ radius: "15"; angle: "85"; value: "94.3601"; }
+ ListElement{ radius: "15"; angle: "90"; value: "94.5503"; }
+ ListElement{ radius: "15"; angle: "95"; value: "94.3601"; }
+ ListElement{ radius: "15"; angle: "100"; value: "93.7907"; }
+ ListElement{ radius: "15"; angle: "105"; value: "92.8466"; }
+ ListElement{ radius: "15"; angle: "110"; value: "91.535"; }
+ ListElement{ radius: "15"; angle: "115"; value: "89.8657"; }
+ ListElement{ radius: "15"; angle: "120"; value: "87.8516"; }
+ ListElement{ radius: "15"; angle: "125"; value: "85.5079"; }
+ ListElement{ radius: "15"; angle: "130"; value: "82.8525"; }
+ ListElement{ radius: "15"; angle: "135"; value: "79.9057"; }
+ ListElement{ radius: "15"; angle: "140"; value: "76.6897"; }
+ ListElement{ radius: "15"; angle: "145"; value: "73.2291"; }
+ ListElement{ radius: "15"; angle: "150"; value: "69.5503"; }
+ ListElement{ radius: "15"; angle: "155"; value: "65.6812"; }
+ ListElement{ radius: "15"; angle: "160"; value: "61.6513"; }
+ ListElement{ radius: "15"; angle: "165"; value: "57.4913"; }
+ ListElement{ radius: "15"; angle: "170"; value: "53.2327"; }
+ ListElement{ radius: "15"; angle: "175"; value: "48.9081"; }
+ ListElement{ radius: "15"; angle: "180"; value: "44.5503"; }
+ ListElement{ radius: "15"; angle: "185"; value: "40.1925"; }
+ ListElement{ radius: "15"; angle: "190"; value: "35.8679"; }
+ ListElement{ radius: "15"; angle: "195"; value: "31.6094"; }
+ ListElement{ radius: "15"; angle: "200"; value: "27.4493"; }
+ ListElement{ radius: "15"; angle: "205"; value: "23.4194"; }
+ ListElement{ radius: "15"; angle: "210"; value: "19.5503"; }
+ ListElement{ radius: "15"; angle: "215"; value: "15.8715"; }
+ ListElement{ radius: "15"; angle: "220"; value: "12.4109"; }
+ ListElement{ radius: "15"; angle: "225"; value: "9.19499"; }
+ ListElement{ radius: "15"; angle: "230"; value: "6.2481"; }
+ ListElement{ radius: "15"; angle: "235"; value: "3.59272"; }
+ ListElement{ radius: "15"; angle: "240"; value: "1.24906"; }
+ ListElement{ radius: "15"; angle: "245"; value: "-0.765063"; }
+ ListElement{ radius: "15"; angle: "250"; value: "-2.4343"; }
+ ListElement{ radius: "15"; angle: "255"; value: "-3.74597"; }
+ ListElement{ radius: "15"; angle: "260"; value: "-4.69006"; }
+ ListElement{ radius: "15"; angle: "265"; value: "-5.25941"; }
+ ListElement{ radius: "15"; angle: "270"; value: "-5.44967"; }
+ ListElement{ radius: "15"; angle: "275"; value: "-5.25941"; }
+ ListElement{ radius: "15"; angle: "280"; value: "-4.69006"; }
+ ListElement{ radius: "15"; angle: "285"; value: "-3.74597"; }
+ ListElement{ radius: "15"; angle: "290"; value: "-2.4343"; }
+ ListElement{ radius: "15"; angle: "295"; value: "-0.765063"; }
+ ListElement{ radius: "15"; angle: "300"; value: "1.24906"; }
+ ListElement{ radius: "15"; angle: "305"; value: "3.59272"; }
+ ListElement{ radius: "15"; angle: "310"; value: "6.2481"; }
+ ListElement{ radius: "15"; angle: "315"; value: "9.19499"; }
+ ListElement{ radius: "15"; angle: "320"; value: "12.4109"; }
+ ListElement{ radius: "15"; angle: "325"; value: "15.8715"; }
+ ListElement{ radius: "15"; angle: "330"; value: "19.5503"; }
+ ListElement{ radius: "15"; angle: "335"; value: "23.4194"; }
+ ListElement{ radius: "15"; angle: "340"; value: "27.4493"; }
+ ListElement{ radius: "15"; angle: "345"; value: "31.6094"; }
+ ListElement{ radius: "15"; angle: "350"; value: "35.8679"; }
+ ListElement{ radius: "15"; angle: "355"; value: "40.1925"; }
+ ListElement{ radius: "15"; angle: "360"; value: "44.5503"; }
+ ListElement{ radius: "20"; angle: "0"; value: "40.4508"; }
+ ListElement{ radius: "20"; angle: "5"; value: "44.8086"; }
+ ListElement{ radius: "20"; angle: "10"; value: "49.1333"; }
+ ListElement{ radius: "20"; angle: "15"; value: "53.3918"; }
+ ListElement{ radius: "20"; angle: "20"; value: "57.5519"; }
+ ListElement{ radius: "20"; angle: "25"; value: "61.5818"; }
+ ListElement{ radius: "20"; angle: "30"; value: "65.4508"; }
+ ListElement{ radius: "20"; angle: "35"; value: "69.1297"; }
+ ListElement{ radius: "20"; angle: "40"; value: "72.5902"; }
+ ListElement{ radius: "20"; angle: "45"; value: "75.8062"; }
+ ListElement{ radius: "20"; angle: "50"; value: "78.7531"; }
+ ListElement{ radius: "20"; angle: "55"; value: "81.4085"; }
+ ListElement{ radius: "20"; angle: "60"; value: "83.7521"; }
+ ListElement{ radius: "20"; angle: "65"; value: "85.7662"; }
+ ListElement{ radius: "20"; angle: "70"; value: "87.4355"; }
+ ListElement{ radius: "20"; angle: "75"; value: "88.7471"; }
+ ListElement{ radius: "20"; angle: "80"; value: "89.6912"; }
+ ListElement{ radius: "20"; angle: "85"; value: "90.2606"; }
+ ListElement{ radius: "20"; angle: "90"; value: "90.4508"; }
+ ListElement{ radius: "20"; angle: "95"; value: "90.2606"; }
+ ListElement{ radius: "20"; angle: "100"; value: "89.6912"; }
+ ListElement{ radius: "20"; angle: "105"; value: "88.7471"; }
+ ListElement{ radius: "20"; angle: "110"; value: "87.4355"; }
+ ListElement{ radius: "20"; angle: "115"; value: "85.7662"; }
+ ListElement{ radius: "20"; angle: "120"; value: "83.7521"; }
+ ListElement{ radius: "20"; angle: "125"; value: "81.4085"; }
+ ListElement{ radius: "20"; angle: "130"; value: "78.7531"; }
+ ListElement{ radius: "20"; angle: "135"; value: "75.8062"; }
+ ListElement{ radius: "20"; angle: "140"; value: "72.5902"; }
+ ListElement{ radius: "20"; angle: "145"; value: "69.1297"; }
+ ListElement{ radius: "20"; angle: "150"; value: "65.4508"; }
+ ListElement{ radius: "20"; angle: "155"; value: "61.5818"; }
+ ListElement{ radius: "20"; angle: "160"; value: "57.5519"; }
+ ListElement{ radius: "20"; angle: "165"; value: "53.3918"; }
+ ListElement{ radius: "20"; angle: "170"; value: "49.1333"; }
+ ListElement{ radius: "20"; angle: "175"; value: "44.8086"; }
+ ListElement{ radius: "20"; angle: "180"; value: "40.4508"; }
+ ListElement{ radius: "20"; angle: "185"; value: "36.0931"; }
+ ListElement{ radius: "20"; angle: "190"; value: "31.7684"; }
+ ListElement{ radius: "20"; angle: "195"; value: "27.5099"; }
+ ListElement{ radius: "20"; angle: "200"; value: "23.3498"; }
+ ListElement{ radius: "20"; angle: "205"; value: "19.3199"; }
+ ListElement{ radius: "20"; angle: "210"; value: "15.4508"; }
+ ListElement{ radius: "20"; angle: "215"; value: "11.772"; }
+ ListElement{ radius: "20"; angle: "220"; value: "8.31147"; }
+ ListElement{ radius: "20"; angle: "225"; value: "5.09551"; }
+ ListElement{ radius: "20"; angle: "230"; value: "2.14863"; }
+ ListElement{ radius: "20"; angle: "235"; value: "-0.506752"; }
+ ListElement{ radius: "20"; angle: "240"; value: "-2.85042"; }
+ ListElement{ radius: "20"; angle: "245"; value: "-4.86454"; }
+ ListElement{ radius: "20"; angle: "250"; value: "-6.53378"; }
+ ListElement{ radius: "20"; angle: "255"; value: "-7.84544"; }
+ ListElement{ radius: "20"; angle: "260"; value: "-8.78954"; }
+ ListElement{ radius: "20"; angle: "265"; value: "-9.35889"; }
+ ListElement{ radius: "20"; angle: "270"; value: "-9.54915"; }
+ ListElement{ radius: "20"; angle: "275"; value: "-9.35889"; }
+ ListElement{ radius: "20"; angle: "280"; value: "-8.78954"; }
+ ListElement{ radius: "20"; angle: "285"; value: "-7.84544"; }
+ ListElement{ radius: "20"; angle: "290"; value: "-6.53378"; }
+ ListElement{ radius: "20"; angle: "295"; value: "-4.86454"; }
+ ListElement{ radius: "20"; angle: "300"; value: "-2.85042"; }
+ ListElement{ radius: "20"; angle: "305"; value: "-0.506752"; }
+ ListElement{ radius: "20"; angle: "310"; value: "2.14863"; }
+ ListElement{ radius: "20"; angle: "315"; value: "5.09551"; }
+ ListElement{ radius: "20"; angle: "320"; value: "8.31147"; }
+ ListElement{ radius: "20"; angle: "325"; value: "11.772"; }
+ ListElement{ radius: "20"; angle: "330"; value: "15.4508"; }
+ ListElement{ radius: "20"; angle: "335"; value: "19.3199"; }
+ ListElement{ radius: "20"; angle: "340"; value: "23.3498"; }
+ ListElement{ radius: "20"; angle: "345"; value: "27.5099"; }
+ ListElement{ radius: "20"; angle: "350"; value: "31.7684"; }
+ ListElement{ radius: "20"; angle: "355"; value: "36.0931"; }
+ ListElement{ radius: "20"; angle: "360"; value: "40.4508"; }
+ ListElement{ radius: "25"; angle: "0"; value: "35.3553"; }
+ ListElement{ radius: "25"; angle: "5"; value: "39.7131"; }
+ ListElement{ radius: "25"; angle: "10"; value: "44.0377"; }
+ ListElement{ radius: "25"; angle: "15"; value: "48.2963"; }
+ ListElement{ radius: "25"; angle: "20"; value: "52.4563"; }
+ ListElement{ radius: "25"; angle: "25"; value: "56.4863"; }
+ ListElement{ radius: "25"; angle: "30"; value: "60.3553"; }
+ ListElement{ radius: "25"; angle: "35"; value: "64.0342"; }
+ ListElement{ radius: "25"; angle: "40"; value: "67.4947"; }
+ ListElement{ radius: "25"; angle: "45"; value: "70.7107"; }
+ ListElement{ radius: "25"; angle: "50"; value: "73.6576"; }
+ ListElement{ radius: "25"; angle: "55"; value: "76.3129"; }
+ ListElement{ radius: "25"; angle: "60"; value: "78.6566"; }
+ ListElement{ radius: "25"; angle: "65"; value: "80.6707"; }
+ ListElement{ radius: "25"; angle: "70"; value: "82.34"; }
+ ListElement{ radius: "25"; angle: "75"; value: "83.6516"; }
+ ListElement{ radius: "25"; angle: "80"; value: "84.5957"; }
+ ListElement{ radius: "25"; angle: "85"; value: "85.1651"; }
+ ListElement{ radius: "25"; angle: "90"; value: "85.3553"; }
+ ListElement{ radius: "25"; angle: "95"; value: "85.1651"; }
+ ListElement{ radius: "25"; angle: "100"; value: "84.5957"; }
+ ListElement{ radius: "25"; angle: "105"; value: "83.6516"; }
+ ListElement{ radius: "25"; angle: "110"; value: "82.34"; }
+ ListElement{ radius: "25"; angle: "115"; value: "80.6707"; }
+ ListElement{ radius: "25"; angle: "120"; value: "78.6566"; }
+ ListElement{ radius: "25"; angle: "125"; value: "76.3129"; }
+ ListElement{ radius: "25"; angle: "130"; value: "73.6576"; }
+ ListElement{ radius: "25"; angle: "135"; value: "70.7107"; }
+ ListElement{ radius: "25"; angle: "140"; value: "67.4947"; }
+ ListElement{ radius: "25"; angle: "145"; value: "64.0342"; }
+ ListElement{ radius: "25"; angle: "150"; value: "60.3553"; }
+ ListElement{ radius: "25"; angle: "155"; value: "56.4863"; }
+ ListElement{ radius: "25"; angle: "160"; value: "52.4563"; }
+ ListElement{ radius: "25"; angle: "165"; value: "48.2963"; }
+ ListElement{ radius: "25"; angle: "170"; value: "44.0377"; }
+ ListElement{ radius: "25"; angle: "175"; value: "39.7131"; }
+ ListElement{ radius: "25"; angle: "180"; value: "35.3553"; }
+ ListElement{ radius: "25"; angle: "185"; value: "30.9976"; }
+ ListElement{ radius: "25"; angle: "190"; value: "26.6729"; }
+ ListElement{ radius: "25"; angle: "195"; value: "22.4144"; }
+ ListElement{ radius: "25"; angle: "200"; value: "18.2543"; }
+ ListElement{ radius: "25"; angle: "205"; value: "14.2244"; }
+ ListElement{ radius: "25"; angle: "210"; value: "10.3553"; }
+ ListElement{ radius: "25"; angle: "215"; value: "6.67652"; }
+ ListElement{ radius: "25"; angle: "220"; value: "3.21596"; }
+ ListElement{ radius: "25"; angle: "225"; value: "5.55112e-15"; }
+ ListElement{ radius: "25"; angle: "230"; value: "-2.94688"; }
+ ListElement{ radius: "25"; angle: "235"; value: "-5.60226"; }
+ ListElement{ radius: "25"; angle: "240"; value: "-7.94593"; }
+ ListElement{ radius: "25"; angle: "245"; value: "-9.96005"; }
+ ListElement{ radius: "25"; angle: "250"; value: "-11.6293"; }
+ ListElement{ radius: "25"; angle: "255"; value: "-12.941"; }
+ ListElement{ radius: "25"; angle: "260"; value: "-13.885"; }
+ ListElement{ radius: "25"; angle: "265"; value: "-14.4544"; }
+ ListElement{ radius: "25"; angle: "270"; value: "-14.6447"; }
+ ListElement{ radius: "25"; angle: "275"; value: "-14.4544"; }
+ ListElement{ radius: "25"; angle: "280"; value: "-13.885"; }
+ ListElement{ radius: "25"; angle: "285"; value: "-12.941"; }
+ ListElement{ radius: "25"; angle: "290"; value: "-11.6293"; }
+ ListElement{ radius: "25"; angle: "295"; value: "-9.96005"; }
+ ListElement{ radius: "25"; angle: "300"; value: "-7.94593"; }
+ ListElement{ radius: "25"; angle: "305"; value: "-5.60226"; }
+ ListElement{ radius: "25"; angle: "310"; value: "-2.94688"; }
+ ListElement{ radius: "25"; angle: "315"; value: "-5.55112e-15"; }
+ ListElement{ radius: "25"; angle: "320"; value: "3.21596"; }
+ ListElement{ radius: "25"; angle: "325"; value: "6.67652"; }
+ ListElement{ radius: "25"; angle: "330"; value: "10.3553"; }
+ ListElement{ radius: "25"; angle: "335"; value: "14.2244"; }
+ ListElement{ radius: "25"; angle: "340"; value: "18.2543"; }
+ ListElement{ radius: "25"; angle: "345"; value: "22.4144"; }
+ ListElement{ radius: "25"; angle: "350"; value: "26.6729"; }
+ ListElement{ radius: "25"; angle: "355"; value: "30.9976"; }
+ ListElement{ radius: "25"; angle: "360"; value: "35.3553"; }
+ ListElement{ radius: "30"; angle: "0"; value: "29.3893"; }
+ ListElement{ radius: "30"; angle: "5"; value: "33.747"; }
+ ListElement{ radius: "30"; angle: "10"; value: "38.0717"; }
+ ListElement{ radius: "30"; angle: "15"; value: "42.3302"; }
+ ListElement{ radius: "30"; angle: "20"; value: "46.4903"; }
+ ListElement{ radius: "30"; angle: "25"; value: "50.5202"; }
+ ListElement{ radius: "30"; angle: "30"; value: "54.3893"; }
+ ListElement{ radius: "30"; angle: "35"; value: "58.0681"; }
+ ListElement{ radius: "30"; angle: "40"; value: "61.5286"; }
+ ListElement{ radius: "30"; angle: "45"; value: "64.7446"; }
+ ListElement{ radius: "30"; angle: "50"; value: "67.6915"; }
+ ListElement{ radius: "30"; angle: "55"; value: "70.3469"; }
+ ListElement{ radius: "30"; angle: "60"; value: "72.6905"; }
+ ListElement{ radius: "30"; angle: "65"; value: "74.7047"; }
+ ListElement{ radius: "30"; angle: "70"; value: "76.3739"; }
+ ListElement{ radius: "30"; angle: "75"; value: "77.6856"; }
+ ListElement{ radius: "30"; angle: "80"; value: "78.6297"; }
+ ListElement{ radius: "30"; angle: "85"; value: "79.199"; }
+ ListElement{ radius: "30"; angle: "90"; value: "79.3893"; }
+ ListElement{ radius: "30"; angle: "95"; value: "79.199"; }
+ ListElement{ radius: "30"; angle: "100"; value: "78.6297"; }
+ ListElement{ radius: "30"; angle: "105"; value: "77.6856"; }
+ ListElement{ radius: "30"; angle: "110"; value: "76.3739"; }
+ ListElement{ radius: "30"; angle: "115"; value: "74.7047"; }
+ ListElement{ radius: "30"; angle: "120"; value: "72.6905"; }
+ ListElement{ radius: "30"; angle: "125"; value: "70.3469"; }
+ ListElement{ radius: "30"; angle: "130"; value: "67.6915"; }
+ ListElement{ radius: "30"; angle: "135"; value: "64.7446"; }
+ ListElement{ radius: "30"; angle: "140"; value: "61.5286"; }
+ ListElement{ radius: "30"; angle: "145"; value: "58.0681"; }
+ ListElement{ radius: "30"; angle: "150"; value: "54.3893"; }
+ ListElement{ radius: "30"; angle: "155"; value: "50.5202"; }
+ ListElement{ radius: "30"; angle: "160"; value: "46.4903"; }
+ ListElement{ radius: "30"; angle: "165"; value: "42.3302"; }
+ ListElement{ radius: "30"; angle: "170"; value: "38.0717"; }
+ ListElement{ radius: "30"; angle: "175"; value: "33.747"; }
+ ListElement{ radius: "30"; angle: "180"; value: "29.3893"; }
+ ListElement{ radius: "30"; angle: "185"; value: "25.0315"; }
+ ListElement{ radius: "30"; angle: "190"; value: "20.7069"; }
+ ListElement{ radius: "30"; angle: "195"; value: "16.4483"; }
+ ListElement{ radius: "30"; angle: "200"; value: "12.2883"; }
+ ListElement{ radius: "30"; angle: "205"; value: "8.25835"; }
+ ListElement{ radius: "30"; angle: "210"; value: "4.38926"; }
+ ListElement{ radius: "30"; angle: "215"; value: "0.710441"; }
+ ListElement{ radius: "30"; angle: "220"; value: "-2.75012"; }
+ ListElement{ radius: "30"; angle: "225"; value: "-5.96608"; }
+ ListElement{ radius: "30"; angle: "230"; value: "-8.91296"; }
+ ListElement{ radius: "30"; angle: "235"; value: "-11.5683"; }
+ ListElement{ radius: "30"; angle: "240"; value: "-13.912"; }
+ ListElement{ radius: "30"; angle: "245"; value: "-15.9261"; }
+ ListElement{ radius: "30"; angle: "250"; value: "-17.5954"; }
+ ListElement{ radius: "30"; angle: "255"; value: "-18.907"; }
+ ListElement{ radius: "30"; angle: "260"; value: "-19.8511"; }
+ ListElement{ radius: "30"; angle: "265"; value: "-20.4205"; }
+ ListElement{ radius: "30"; angle: "270"; value: "-20.6107"; }
+ ListElement{ radius: "30"; angle: "275"; value: "-20.4205"; }
+ ListElement{ radius: "30"; angle: "280"; value: "-19.8511"; }
+ ListElement{ radius: "30"; angle: "285"; value: "-18.907"; }
+ ListElement{ radius: "30"; angle: "290"; value: "-17.5954"; }
+ ListElement{ radius: "30"; angle: "295"; value: "-15.9261"; }
+ ListElement{ radius: "30"; angle: "300"; value: "-13.912"; }
+ ListElement{ radius: "30"; angle: "305"; value: "-11.5683"; }
+ ListElement{ radius: "30"; angle: "310"; value: "-8.91296"; }
+ ListElement{ radius: "30"; angle: "315"; value: "-5.96608"; }
+ ListElement{ radius: "30"; angle: "320"; value: "-2.75012"; }
+ ListElement{ radius: "30"; angle: "325"; value: "0.710441"; }
+ ListElement{ radius: "30"; angle: "330"; value: "4.38926"; }
+ ListElement{ radius: "30"; angle: "335"; value: "8.25835"; }
+ ListElement{ radius: "30"; angle: "340"; value: "12.2883"; }
+ ListElement{ radius: "30"; angle: "345"; value: "16.4483"; }
+ ListElement{ radius: "30"; angle: "350"; value: "20.7069"; }
+ ListElement{ radius: "30"; angle: "355"; value: "25.0315"; }
+ ListElement{ radius: "30"; angle: "360"; value: "29.3893"; }
+ ListElement{ radius: "35"; angle: "0"; value: "22.6995"; }
+ ListElement{ radius: "35"; angle: "5"; value: "27.0573"; }
+ ListElement{ radius: "35"; angle: "10"; value: "31.3819"; }
+ ListElement{ radius: "35"; angle: "15"; value: "35.6405"; }
+ ListElement{ radius: "35"; angle: "20"; value: "39.8005"; }
+ ListElement{ radius: "35"; angle: "25"; value: "43.8304"; }
+ ListElement{ radius: "35"; angle: "30"; value: "47.6995"; }
+ ListElement{ radius: "35"; angle: "35"; value: "51.3783"; }
+ ListElement{ radius: "35"; angle: "40"; value: "54.8389"; }
+ ListElement{ radius: "35"; angle: "45"; value: "58.0549"; }
+ ListElement{ radius: "35"; angle: "50"; value: "61.0017"; }
+ ListElement{ radius: "35"; angle: "55"; value: "63.6571"; }
+ ListElement{ radius: "35"; angle: "60"; value: "66.0008"; }
+ ListElement{ radius: "35"; angle: "65"; value: "68.0149"; }
+ ListElement{ radius: "35"; angle: "70"; value: "69.6842"; }
+ ListElement{ radius: "35"; angle: "75"; value: "70.9958"; }
+ ListElement{ radius: "35"; angle: "80"; value: "71.9399"; }
+ ListElement{ radius: "35"; angle: "85"; value: "72.5093"; }
+ ListElement{ radius: "35"; angle: "90"; value: "72.6995"; }
+ ListElement{ radius: "35"; angle: "95"; value: "72.5093"; }
+ ListElement{ radius: "35"; angle: "100"; value: "71.9399"; }
+ ListElement{ radius: "35"; angle: "105"; value: "70.9958"; }
+ ListElement{ radius: "35"; angle: "110"; value: "69.6842"; }
+ ListElement{ radius: "35"; angle: "115"; value: "68.0149"; }
+ ListElement{ radius: "35"; angle: "120"; value: "66.0008"; }
+ ListElement{ radius: "35"; angle: "125"; value: "63.6571"; }
+ ListElement{ radius: "35"; angle: "130"; value: "61.0017"; }
+ ListElement{ radius: "35"; angle: "135"; value: "58.0549"; }
+ ListElement{ radius: "35"; angle: "140"; value: "54.8389"; }
+ ListElement{ radius: "35"; angle: "145"; value: "51.3783"; }
+ ListElement{ radius: "35"; angle: "150"; value: "47.6995"; }
+ ListElement{ radius: "35"; angle: "155"; value: "43.8304"; }
+ ListElement{ radius: "35"; angle: "160"; value: "39.8005"; }
+ ListElement{ radius: "35"; angle: "165"; value: "35.6405"; }
+ ListElement{ radius: "35"; angle: "170"; value: "31.3819"; }
+ ListElement{ radius: "35"; angle: "175"; value: "27.0573"; }
+ ListElement{ radius: "35"; angle: "180"; value: "22.6995"; }
+ ListElement{ radius: "35"; angle: "185"; value: "18.3417"; }
+ ListElement{ radius: "35"; angle: "190"; value: "14.0171"; }
+ ListElement{ radius: "35"; angle: "195"; value: "9.75857"; }
+ ListElement{ radius: "35"; angle: "200"; value: "5.59852"; }
+ ListElement{ radius: "35"; angle: "205"; value: "1.56861"; }
+ ListElement{ radius: "35"; angle: "210"; value: "-2.30048"; }
+ ListElement{ radius: "35"; angle: "215"; value: "-5.9793"; }
+ ListElement{ radius: "35"; angle: "220"; value: "-9.43986"; }
+ ListElement{ radius: "35"; angle: "225"; value: "-12.6558"; }
+ ListElement{ radius: "35"; angle: "230"; value: "-15.6027"; }
+ ListElement{ radius: "35"; angle: "235"; value: "-18.2581"; }
+ ListElement{ radius: "35"; angle: "240"; value: "-20.6017"; }
+ ListElement{ radius: "35"; angle: "245"; value: "-22.6159"; }
+ ListElement{ radius: "35"; angle: "250"; value: "-24.2851"; }
+ ListElement{ radius: "35"; angle: "255"; value: "-25.5968"; }
+ ListElement{ radius: "35"; angle: "260"; value: "-26.5409"; }
+ ListElement{ radius: "35"; angle: "265"; value: "-27.1102"; }
+ ListElement{ radius: "35"; angle: "270"; value: "-27.3005"; }
+ ListElement{ radius: "35"; angle: "275"; value: "-27.1102"; }
+ ListElement{ radius: "35"; angle: "280"; value: "-26.5409"; }
+ ListElement{ radius: "35"; angle: "285"; value: "-25.5968"; }
+ ListElement{ radius: "35"; angle: "290"; value: "-24.2851"; }
+ ListElement{ radius: "35"; angle: "295"; value: "-22.6159"; }
+ ListElement{ radius: "35"; angle: "300"; value: "-20.6017"; }
+ ListElement{ radius: "35"; angle: "305"; value: "-18.2581"; }
+ ListElement{ radius: "35"; angle: "310"; value: "-15.6027"; }
+ ListElement{ radius: "35"; angle: "315"; value: "-12.6558"; }
+ ListElement{ radius: "35"; angle: "320"; value: "-9.43986"; }
+ ListElement{ radius: "35"; angle: "325"; value: "-5.9793"; }
+ ListElement{ radius: "35"; angle: "330"; value: "-2.30048"; }
+ ListElement{ radius: "35"; angle: "335"; value: "1.56861"; }
+ ListElement{ radius: "35"; angle: "340"; value: "5.59852"; }
+ ListElement{ radius: "35"; angle: "345"; value: "9.75857"; }
+ ListElement{ radius: "35"; angle: "350"; value: "14.0171"; }
+ ListElement{ radius: "35"; angle: "355"; value: "18.3417"; }
+ ListElement{ radius: "35"; angle: "360"; value: "22.6995"; }
+ ListElement{ radius: "40"; angle: "0"; value: "15.4508"; }
+ ListElement{ radius: "40"; angle: "5"; value: "19.8086"; }
+ ListElement{ radius: "40"; angle: "10"; value: "24.1333"; }
+ ListElement{ radius: "40"; angle: "15"; value: "28.3918"; }
+ ListElement{ radius: "40"; angle: "20"; value: "32.5519"; }
+ ListElement{ radius: "40"; angle: "25"; value: "36.5818"; }
+ ListElement{ radius: "40"; angle: "30"; value: "40.4508"; }
+ ListElement{ radius: "40"; angle: "35"; value: "44.1297"; }
+ ListElement{ radius: "40"; angle: "40"; value: "47.5902"; }
+ ListElement{ radius: "40"; angle: "45"; value: "50.8062"; }
+ ListElement{ radius: "40"; angle: "50"; value: "53.7531"; }
+ ListElement{ radius: "40"; angle: "55"; value: "56.4085"; }
+ ListElement{ radius: "40"; angle: "60"; value: "58.7521"; }
+ ListElement{ radius: "40"; angle: "65"; value: "60.7662"; }
+ ListElement{ radius: "40"; angle: "70"; value: "62.4355"; }
+ ListElement{ radius: "40"; angle: "75"; value: "63.7471"; }
+ ListElement{ radius: "40"; angle: "80"; value: "64.6912"; }
+ ListElement{ radius: "40"; angle: "85"; value: "65.2606"; }
+ ListElement{ radius: "40"; angle: "90"; value: "65.4508"; }
+ ListElement{ radius: "40"; angle: "95"; value: "65.2606"; }
+ ListElement{ radius: "40"; angle: "100"; value: "64.6912"; }
+ ListElement{ radius: "40"; angle: "105"; value: "63.7471"; }
+ ListElement{ radius: "40"; angle: "110"; value: "62.4355"; }
+ ListElement{ radius: "40"; angle: "115"; value: "60.7662"; }
+ ListElement{ radius: "40"; angle: "120"; value: "58.7521"; }
+ ListElement{ radius: "40"; angle: "125"; value: "56.4085"; }
+ ListElement{ radius: "40"; angle: "130"; value: "53.7531"; }
+ ListElement{ radius: "40"; angle: "135"; value: "50.8062"; }
+ ListElement{ radius: "40"; angle: "140"; value: "47.5902"; }
+ ListElement{ radius: "40"; angle: "145"; value: "44.1297"; }
+ ListElement{ radius: "40"; angle: "150"; value: "40.4508"; }
+ ListElement{ radius: "40"; angle: "155"; value: "36.5818"; }
+ ListElement{ radius: "40"; angle: "160"; value: "32.5519"; }
+ ListElement{ radius: "40"; angle: "165"; value: "28.3918"; }
+ ListElement{ radius: "40"; angle: "170"; value: "24.1333"; }
+ ListElement{ radius: "40"; angle: "175"; value: "19.8086"; }
+ ListElement{ radius: "40"; angle: "180"; value: "15.4508"; }
+ ListElement{ radius: "40"; angle: "185"; value: "11.0931"; }
+ ListElement{ radius: "40"; angle: "190"; value: "6.76844"; }
+ ListElement{ radius: "40"; angle: "195"; value: "2.5099"; }
+ ListElement{ radius: "40"; angle: "200"; value: "-1.65016"; }
+ ListElement{ radius: "40"; angle: "205"; value: "-5.68006"; }
+ ListElement{ radius: "40"; angle: "210"; value: "-9.54915"; }
+ ListElement{ radius: "40"; angle: "215"; value: "-13.228"; }
+ ListElement{ radius: "40"; angle: "220"; value: "-16.6885"; }
+ ListElement{ radius: "40"; angle: "225"; value: "-19.9045"; }
+ ListElement{ radius: "40"; angle: "230"; value: "-22.8514"; }
+ ListElement{ radius: "40"; angle: "235"; value: "-25.5068"; }
+ ListElement{ radius: "40"; angle: "240"; value: "-27.8504"; }
+ ListElement{ radius: "40"; angle: "245"; value: "-29.8645"; }
+ ListElement{ radius: "40"; angle: "250"; value: "-31.5338"; }
+ ListElement{ radius: "40"; angle: "255"; value: "-32.8454"; }
+ ListElement{ radius: "40"; angle: "260"; value: "-33.7895"; }
+ ListElement{ radius: "40"; angle: "265"; value: "-34.3589"; }
+ ListElement{ radius: "40"; angle: "270"; value: "-34.5492"; }
+ ListElement{ radius: "40"; angle: "275"; value: "-34.3589"; }
+ ListElement{ radius: "40"; angle: "280"; value: "-33.7895"; }
+ ListElement{ radius: "40"; angle: "285"; value: "-32.8454"; }
+ ListElement{ radius: "40"; angle: "290"; value: "-31.5338"; }
+ ListElement{ radius: "40"; angle: "295"; value: "-29.8645"; }
+ ListElement{ radius: "40"; angle: "300"; value: "-27.8504"; }
+ ListElement{ radius: "40"; angle: "305"; value: "-25.5068"; }
+ ListElement{ radius: "40"; angle: "310"; value: "-22.8514"; }
+ ListElement{ radius: "40"; angle: "315"; value: "-19.9045"; }
+ ListElement{ radius: "40"; angle: "320"; value: "-16.6885"; }
+ ListElement{ radius: "40"; angle: "325"; value: "-13.228"; }
+ ListElement{ radius: "40"; angle: "330"; value: "-9.54915"; }
+ ListElement{ radius: "40"; angle: "335"; value: "-5.68006"; }
+ ListElement{ radius: "40"; angle: "340"; value: "-1.65016"; }
+ ListElement{ radius: "40"; angle: "345"; value: "2.5099"; }
+ ListElement{ radius: "40"; angle: "350"; value: "6.76844"; }
+ ListElement{ radius: "40"; angle: "355"; value: "11.0931"; }
+ ListElement{ radius: "40"; angle: "360"; value: "15.4508"; }
+ ListElement{ radius: "45"; angle: "0"; value: "7.82172"; }
+ ListElement{ radius: "45"; angle: "5"; value: "12.1795"; }
+ ListElement{ radius: "45"; angle: "10"; value: "16.5041"; }
+ ListElement{ radius: "45"; angle: "15"; value: "20.7627"; }
+ ListElement{ radius: "45"; angle: "20"; value: "24.9227"; }
+ ListElement{ radius: "45"; angle: "25"; value: "28.9526"; }
+ ListElement{ radius: "45"; angle: "30"; value: "32.8217"; }
+ ListElement{ radius: "45"; angle: "35"; value: "36.5005"; }
+ ListElement{ radius: "45"; angle: "40"; value: "39.9611"; }
+ ListElement{ radius: "45"; angle: "45"; value: "43.1771"; }
+ ListElement{ radius: "45"; angle: "50"; value: "46.1239"; }
+ ListElement{ radius: "45"; angle: "55"; value: "48.7793"; }
+ ListElement{ radius: "45"; angle: "60"; value: "51.123"; }
+ ListElement{ radius: "45"; angle: "65"; value: "53.1371"; }
+ ListElement{ radius: "45"; angle: "70"; value: "54.8064"; }
+ ListElement{ radius: "45"; angle: "75"; value: "56.118"; }
+ ListElement{ radius: "45"; angle: "80"; value: "57.0621"; }
+ ListElement{ radius: "45"; angle: "85"; value: "57.6315"; }
+ ListElement{ radius: "45"; angle: "90"; value: "57.8217"; }
+ ListElement{ radius: "45"; angle: "95"; value: "57.6315"; }
+ ListElement{ radius: "45"; angle: "100"; value: "57.0621"; }
+ ListElement{ radius: "45"; angle: "105"; value: "56.118"; }
+ ListElement{ radius: "45"; angle: "110"; value: "54.8064"; }
+ ListElement{ radius: "45"; angle: "115"; value: "53.1371"; }
+ ListElement{ radius: "45"; angle: "120"; value: "51.123"; }
+ ListElement{ radius: "45"; angle: "125"; value: "48.7793"; }
+ ListElement{ radius: "45"; angle: "130"; value: "46.1239"; }
+ ListElement{ radius: "45"; angle: "135"; value: "43.1771"; }
+ ListElement{ radius: "45"; angle: "140"; value: "39.9611"; }
+ ListElement{ radius: "45"; angle: "145"; value: "36.5005"; }
+ ListElement{ radius: "45"; angle: "150"; value: "32.8217"; }
+ ListElement{ radius: "45"; angle: "155"; value: "28.9526"; }
+ ListElement{ radius: "45"; angle: "160"; value: "24.9227"; }
+ ListElement{ radius: "45"; angle: "165"; value: "20.7627"; }
+ ListElement{ radius: "45"; angle: "170"; value: "16.5041"; }
+ ListElement{ radius: "45"; angle: "175"; value: "12.1795"; }
+ ListElement{ radius: "45"; angle: "180"; value: "7.82172"; }
+ ListElement{ radius: "45"; angle: "185"; value: "3.46394"; }
+ ListElement{ radius: "45"; angle: "190"; value: "-0.860686"; }
+ ListElement{ radius: "45"; angle: "195"; value: "-5.11923"; }
+ ListElement{ radius: "45"; angle: "200"; value: "-9.27928"; }
+ ListElement{ radius: "45"; angle: "205"; value: "-13.3092"; }
+ ListElement{ radius: "45"; angle: "210"; value: "-17.1783"; }
+ ListElement{ radius: "45"; angle: "215"; value: "-20.8571"; }
+ ListElement{ radius: "45"; angle: "220"; value: "-24.3177"; }
+ ListElement{ radius: "45"; angle: "225"; value: "-27.5336"; }
+ ListElement{ radius: "45"; angle: "230"; value: "-30.4805"; }
+ ListElement{ radius: "45"; angle: "235"; value: "-33.1359"; }
+ ListElement{ radius: "45"; angle: "240"; value: "-35.4795"; }
+ ListElement{ radius: "45"; angle: "245"; value: "-37.4937"; }
+ ListElement{ radius: "45"; angle: "250"; value: "-39.1629"; }
+ ListElement{ radius: "45"; angle: "255"; value: "-40.4746"; }
+ ListElement{ radius: "45"; angle: "260"; value: "-41.4187"; }
+ ListElement{ radius: "45"; angle: "265"; value: "-41.988"; }
+ ListElement{ radius: "45"; angle: "270"; value: "-42.1783"; }
+ ListElement{ radius: "45"; angle: "275"; value: "-41.988"; }
+ ListElement{ radius: "45"; angle: "280"; value: "-41.4187"; }
+ ListElement{ radius: "45"; angle: "285"; value: "-40.4746"; }
+ ListElement{ radius: "45"; angle: "290"; value: "-39.1629"; }
+ ListElement{ radius: "45"; angle: "295"; value: "-37.4937"; }
+ ListElement{ radius: "45"; angle: "300"; value: "-35.4795"; }
+ ListElement{ radius: "45"; angle: "305"; value: "-33.1359"; }
+ ListElement{ radius: "45"; angle: "310"; value: "-30.4805"; }
+ ListElement{ radius: "45"; angle: "315"; value: "-27.5336"; }
+ ListElement{ radius: "45"; angle: "320"; value: "-24.3177"; }
+ ListElement{ radius: "45"; angle: "325"; value: "-20.8571"; }
+ ListElement{ radius: "45"; angle: "330"; value: "-17.1783"; }
+ ListElement{ radius: "45"; angle: "335"; value: "-13.3092"; }
+ ListElement{ radius: "45"; angle: "340"; value: "-9.27928"; }
+ ListElement{ radius: "45"; angle: "345"; value: "-5.11923"; }
+ ListElement{ radius: "45"; angle: "350"; value: "-0.860686"; }
+ ListElement{ radius: "45"; angle: "355"; value: "3.46394"; }
+ ListElement{ radius: "45"; angle: "360"; value: "7.82172"; }
+ ListElement{ radius: "50"; angle: "0"; value: "3.06162e-15"; }
+ ListElement{ radius: "50"; angle: "5"; value: "4.35779"; }
+ ListElement{ radius: "50"; angle: "10"; value: "8.68241"; }
+ ListElement{ radius: "50"; angle: "15"; value: "12.941"; }
+ ListElement{ radius: "50"; angle: "20"; value: "17.101"; }
+ ListElement{ radius: "50"; angle: "25"; value: "21.1309"; }
+ ListElement{ radius: "50"; angle: "30"; value: "25"; }
+ ListElement{ radius: "50"; angle: "35"; value: "28.6788"; }
+ ListElement{ radius: "50"; angle: "40"; value: "32.1394"; }
+ ListElement{ radius: "50"; angle: "45"; value: "35.3553"; }
+ ListElement{ radius: "50"; angle: "50"; value: "38.3022"; }
+ ListElement{ radius: "50"; angle: "55"; value: "40.9576"; }
+ ListElement{ radius: "50"; angle: "60"; value: "43.3013"; }
+ ListElement{ radius: "50"; angle: "65"; value: "45.3154"; }
+ ListElement{ radius: "50"; angle: "70"; value: "46.9846"; }
+ ListElement{ radius: "50"; angle: "75"; value: "48.2963"; }
+ ListElement{ radius: "50"; angle: "80"; value: "49.2404"; }
+ ListElement{ radius: "50"; angle: "85"; value: "49.8097"; }
+ ListElement{ radius: "50"; angle: "90"; value: "50"; }
+ ListElement{ radius: "50"; angle: "95"; value: "49.8097"; }
+ ListElement{ radius: "50"; angle: "100"; value: "49.2404"; }
+ ListElement{ radius: "50"; angle: "105"; value: "48.2963"; }
+ ListElement{ radius: "50"; angle: "110"; value: "46.9846"; }
+ ListElement{ radius: "50"; angle: "115"; value: "45.3154"; }
+ ListElement{ radius: "50"; angle: "120"; value: "43.3013"; }
+ ListElement{ radius: "50"; angle: "125"; value: "40.9576"; }
+ ListElement{ radius: "50"; angle: "130"; value: "38.3022"; }
+ ListElement{ radius: "50"; angle: "135"; value: "35.3553"; }
+ ListElement{ radius: "50"; angle: "140"; value: "32.1394"; }
+ ListElement{ radius: "50"; angle: "145"; value: "28.6788"; }
+ ListElement{ radius: "50"; angle: "150"; value: "25"; }
+ ListElement{ radius: "50"; angle: "155"; value: "21.1309"; }
+ ListElement{ radius: "50"; angle: "160"; value: "17.101"; }
+ ListElement{ radius: "50"; angle: "165"; value: "12.941"; }
+ ListElement{ radius: "50"; angle: "170"; value: "8.68241"; }
+ ListElement{ radius: "50"; angle: "175"; value: "4.35779"; }
+ ListElement{ radius: "50"; angle: "180"; value: "9.18485e-15"; }
+ ListElement{ radius: "50"; angle: "185"; value: "-4.35779"; }
+ ListElement{ radius: "50"; angle: "190"; value: "-8.68241"; }
+ ListElement{ radius: "50"; angle: "195"; value: "-12.941"; }
+ ListElement{ radius: "50"; angle: "200"; value: "-17.101"; }
+ ListElement{ radius: "50"; angle: "205"; value: "-21.1309"; }
+ ListElement{ radius: "50"; angle: "210"; value: "-25"; }
+ ListElement{ radius: "50"; angle: "215"; value: "-28.6788"; }
+ ListElement{ radius: "50"; angle: "220"; value: "-32.1394"; }
+ ListElement{ radius: "50"; angle: "225"; value: "-35.3553"; }
+ ListElement{ radius: "50"; angle: "230"; value: "-38.3022"; }
+ ListElement{ radius: "50"; angle: "235"; value: "-40.9576"; }
+ ListElement{ radius: "50"; angle: "240"; value: "-43.3013"; }
+ ListElement{ radius: "50"; angle: "245"; value: "-45.3154"; }
+ ListElement{ radius: "50"; angle: "250"; value: "-46.9846"; }
+ ListElement{ radius: "50"; angle: "255"; value: "-48.2963"; }
+ ListElement{ radius: "50"; angle: "260"; value: "-49.2404"; }
+ ListElement{ radius: "50"; angle: "265"; value: "-49.8097"; }
+ ListElement{ radius: "50"; angle: "270"; value: "-50"; }
+ ListElement{ radius: "50"; angle: "275"; value: "-49.8097"; }
+ ListElement{ radius: "50"; angle: "280"; value: "-49.2404"; }
+ ListElement{ radius: "50"; angle: "285"; value: "-48.2963"; }
+ ListElement{ radius: "50"; angle: "290"; value: "-46.9846"; }
+ ListElement{ radius: "50"; angle: "295"; value: "-45.3154"; }
+ ListElement{ radius: "50"; angle: "300"; value: "-43.3013"; }
+ ListElement{ radius: "50"; angle: "305"; value: "-40.9576"; }
+ ListElement{ radius: "50"; angle: "310"; value: "-38.3022"; }
+ ListElement{ radius: "50"; angle: "315"; value: "-35.3553"; }
+ ListElement{ radius: "50"; angle: "320"; value: "-32.1394"; }
+ ListElement{ radius: "50"; angle: "325"; value: "-28.6788"; }
+ ListElement{ radius: "50"; angle: "330"; value: "-25"; }
+ ListElement{ radius: "50"; angle: "335"; value: "-21.1309"; }
+ ListElement{ radius: "50"; angle: "340"; value: "-17.101"; }
+ ListElement{ radius: "50"; angle: "345"; value: "-12.941"; }
+ ListElement{ radius: "50"; angle: "350"; value: "-8.68241"; }
+ ListElement{ radius: "50"; angle: "355"; value: "-4.35779"; }
+ ListElement{ radius: "50"; angle: "360"; value: "-9.18485e-15"; }
+ ListElement{ radius: "55"; angle: "0"; value: "-7.82172"; }
+ ListElement{ radius: "55"; angle: "5"; value: "-3.46394"; }
+ ListElement{ radius: "55"; angle: "10"; value: "0.860686"; }
+ ListElement{ radius: "55"; angle: "15"; value: "5.11923"; }
+ ListElement{ radius: "55"; angle: "20"; value: "9.27928"; }
+ ListElement{ radius: "55"; angle: "25"; value: "13.3092"; }
+ ListElement{ radius: "55"; angle: "30"; value: "17.1783"; }
+ ListElement{ radius: "55"; angle: "35"; value: "20.8571"; }
+ ListElement{ radius: "55"; angle: "40"; value: "24.3177"; }
+ ListElement{ radius: "55"; angle: "45"; value: "27.5336"; }
+ ListElement{ radius: "55"; angle: "50"; value: "30.4805"; }
+ ListElement{ radius: "55"; angle: "55"; value: "33.1359"; }
+ ListElement{ radius: "55"; angle: "60"; value: "35.4795"; }
+ ListElement{ radius: "55"; angle: "65"; value: "37.4937"; }
+ ListElement{ radius: "55"; angle: "70"; value: "39.1629"; }
+ ListElement{ radius: "55"; angle: "75"; value: "40.4746"; }
+ ListElement{ radius: "55"; angle: "80"; value: "41.4187"; }
+ ListElement{ radius: "55"; angle: "85"; value: "41.988"; }
+ ListElement{ radius: "55"; angle: "90"; value: "42.1783"; }
+ ListElement{ radius: "55"; angle: "95"; value: "41.988"; }
+ ListElement{ radius: "55"; angle: "100"; value: "41.4187"; }
+ ListElement{ radius: "55"; angle: "105"; value: "40.4746"; }
+ ListElement{ radius: "55"; angle: "110"; value: "39.1629"; }
+ ListElement{ radius: "55"; angle: "115"; value: "37.4937"; }
+ ListElement{ radius: "55"; angle: "120"; value: "35.4795"; }
+ ListElement{ radius: "55"; angle: "125"; value: "33.1359"; }
+ ListElement{ radius: "55"; angle: "130"; value: "30.4805"; }
+ ListElement{ radius: "55"; angle: "135"; value: "27.5336"; }
+ ListElement{ radius: "55"; angle: "140"; value: "24.3177"; }
+ ListElement{ radius: "55"; angle: "145"; value: "20.8571"; }
+ ListElement{ radius: "55"; angle: "150"; value: "17.1783"; }
+ ListElement{ radius: "55"; angle: "155"; value: "13.3092"; }
+ ListElement{ radius: "55"; angle: "160"; value: "9.27928"; }
+ ListElement{ radius: "55"; angle: "165"; value: "5.11923"; }
+ ListElement{ radius: "55"; angle: "170"; value: "0.860686"; }
+ ListElement{ radius: "55"; angle: "175"; value: "-3.46394"; }
+ ListElement{ radius: "55"; angle: "180"; value: "-7.82172"; }
+ ListElement{ radius: "55"; angle: "185"; value: "-12.1795"; }
+ ListElement{ radius: "55"; angle: "190"; value: "-16.5041"; }
+ ListElement{ radius: "55"; angle: "195"; value: "-20.7627"; }
+ ListElement{ radius: "55"; angle: "200"; value: "-24.9227"; }
+ ListElement{ radius: "55"; angle: "205"; value: "-28.9526"; }
+ ListElement{ radius: "55"; angle: "210"; value: "-32.8217"; }
+ ListElement{ radius: "55"; angle: "215"; value: "-36.5005"; }
+ ListElement{ radius: "55"; angle: "220"; value: "-39.9611"; }
+ ListElement{ radius: "55"; angle: "225"; value: "-43.1771"; }
+ ListElement{ radius: "55"; angle: "230"; value: "-46.1239"; }
+ ListElement{ radius: "55"; angle: "235"; value: "-48.7793"; }
+ ListElement{ radius: "55"; angle: "240"; value: "-51.123"; }
+ ListElement{ radius: "55"; angle: "245"; value: "-53.1371"; }
+ ListElement{ radius: "55"; angle: "250"; value: "-54.8064"; }
+ ListElement{ radius: "55"; angle: "255"; value: "-56.118"; }
+ ListElement{ radius: "55"; angle: "260"; value: "-57.0621"; }
+ ListElement{ radius: "55"; angle: "265"; value: "-57.6315"; }
+ ListElement{ radius: "55"; angle: "270"; value: "-57.8217"; }
+ ListElement{ radius: "55"; angle: "275"; value: "-57.6315"; }
+ ListElement{ radius: "55"; angle: "280"; value: "-57.0621"; }
+ ListElement{ radius: "55"; angle: "285"; value: "-56.118"; }
+ ListElement{ radius: "55"; angle: "290"; value: "-54.8064"; }
+ ListElement{ radius: "55"; angle: "295"; value: "-53.1371"; }
+ ListElement{ radius: "55"; angle: "300"; value: "-51.123"; }
+ ListElement{ radius: "55"; angle: "305"; value: "-48.7793"; }
+ ListElement{ radius: "55"; angle: "310"; value: "-46.1239"; }
+ ListElement{ radius: "55"; angle: "315"; value: "-43.1771"; }
+ ListElement{ radius: "55"; angle: "320"; value: "-39.9611"; }
+ ListElement{ radius: "55"; angle: "325"; value: "-36.5005"; }
+ ListElement{ radius: "55"; angle: "330"; value: "-32.8217"; }
+ ListElement{ radius: "55"; angle: "335"; value: "-28.9526"; }
+ ListElement{ radius: "55"; angle: "340"; value: "-24.9227"; }
+ ListElement{ radius: "55"; angle: "345"; value: "-20.7627"; }
+ ListElement{ radius: "55"; angle: "350"; value: "-16.5041"; }
+ ListElement{ radius: "55"; angle: "355"; value: "-12.1795"; }
+ ListElement{ radius: "55"; angle: "360"; value: "-7.82172"; }
+ ListElement{ radius: "60"; angle: "0"; value: "-15.4508"; }
+ ListElement{ radius: "60"; angle: "5"; value: "-11.0931"; }
+ ListElement{ radius: "60"; angle: "10"; value: "-6.76844"; }
+ ListElement{ radius: "60"; angle: "15"; value: "-2.5099"; }
+ ListElement{ radius: "60"; angle: "20"; value: "1.65016"; }
+ ListElement{ radius: "60"; angle: "25"; value: "5.68006"; }
+ ListElement{ radius: "60"; angle: "30"; value: "9.54915"; }
+ ListElement{ radius: "60"; angle: "35"; value: "13.228"; }
+ ListElement{ radius: "60"; angle: "40"; value: "16.6885"; }
+ ListElement{ radius: "60"; angle: "45"; value: "19.9045"; }
+ ListElement{ radius: "60"; angle: "50"; value: "22.8514"; }
+ ListElement{ radius: "60"; angle: "55"; value: "25.5068"; }
+ ListElement{ radius: "60"; angle: "60"; value: "27.8504"; }
+ ListElement{ radius: "60"; angle: "65"; value: "29.8645"; }
+ ListElement{ radius: "60"; angle: "70"; value: "31.5338"; }
+ ListElement{ radius: "60"; angle: "75"; value: "32.8454"; }
+ ListElement{ radius: "60"; angle: "80"; value: "33.7895"; }
+ ListElement{ radius: "60"; angle: "85"; value: "34.3589"; }
+ ListElement{ radius: "60"; angle: "90"; value: "34.5492"; }
+ ListElement{ radius: "60"; angle: "95"; value: "34.3589"; }
+ ListElement{ radius: "60"; angle: "100"; value: "33.7895"; }
+ ListElement{ radius: "60"; angle: "105"; value: "32.8454"; }
+ ListElement{ radius: "60"; angle: "110"; value: "31.5338"; }
+ ListElement{ radius: "60"; angle: "115"; value: "29.8645"; }
+ ListElement{ radius: "60"; angle: "120"; value: "27.8504"; }
+ ListElement{ radius: "60"; angle: "125"; value: "25.5068"; }
+ ListElement{ radius: "60"; angle: "130"; value: "22.8514"; }
+ ListElement{ radius: "60"; angle: "135"; value: "19.9045"; }
+ ListElement{ radius: "60"; angle: "140"; value: "16.6885"; }
+ ListElement{ radius: "60"; angle: "145"; value: "13.228"; }
+ ListElement{ radius: "60"; angle: "150"; value: "9.54915"; }
+ ListElement{ radius: "60"; angle: "155"; value: "5.68006"; }
+ ListElement{ radius: "60"; angle: "160"; value: "1.65016"; }
+ ListElement{ radius: "60"; angle: "165"; value: "-2.5099"; }
+ ListElement{ radius: "60"; angle: "170"; value: "-6.76844"; }
+ ListElement{ radius: "60"; angle: "175"; value: "-11.0931"; }
+ ListElement{ radius: "60"; angle: "180"; value: "-15.4508"; }
+ ListElement{ radius: "60"; angle: "185"; value: "-19.8086"; }
+ ListElement{ radius: "60"; angle: "190"; value: "-24.1333"; }
+ ListElement{ radius: "60"; angle: "195"; value: "-28.3918"; }
+ ListElement{ radius: "60"; angle: "200"; value: "-32.5519"; }
+ ListElement{ radius: "60"; angle: "205"; value: "-36.5818"; }
+ ListElement{ radius: "60"; angle: "210"; value: "-40.4508"; }
+ ListElement{ radius: "60"; angle: "215"; value: "-44.1297"; }
+ ListElement{ radius: "60"; angle: "220"; value: "-47.5902"; }
+ ListElement{ radius: "60"; angle: "225"; value: "-50.8062"; }
+ ListElement{ radius: "60"; angle: "230"; value: "-53.7531"; }
+ ListElement{ radius: "60"; angle: "235"; value: "-56.4085"; }
+ ListElement{ radius: "60"; angle: "240"; value: "-58.7521"; }
+ ListElement{ radius: "60"; angle: "245"; value: "-60.7662"; }
+ ListElement{ radius: "60"; angle: "250"; value: "-62.4355"; }
+ ListElement{ radius: "60"; angle: "255"; value: "-63.7471"; }
+ ListElement{ radius: "60"; angle: "260"; value: "-64.6912"; }
+ ListElement{ radius: "60"; angle: "265"; value: "-65.2606"; }
+ ListElement{ radius: "60"; angle: "270"; value: "-65.4508"; }
+ ListElement{ radius: "60"; angle: "275"; value: "-65.2606"; }
+ ListElement{ radius: "60"; angle: "280"; value: "-64.6912"; }
+ ListElement{ radius: "60"; angle: "285"; value: "-63.7471"; }
+ ListElement{ radius: "60"; angle: "290"; value: "-62.4355"; }
+ ListElement{ radius: "60"; angle: "295"; value: "-60.7662"; }
+ ListElement{ radius: "60"; angle: "300"; value: "-58.7521"; }
+ ListElement{ radius: "60"; angle: "305"; value: "-56.4085"; }
+ ListElement{ radius: "60"; angle: "310"; value: "-53.7531"; }
+ ListElement{ radius: "60"; angle: "315"; value: "-50.8062"; }
+ ListElement{ radius: "60"; angle: "320"; value: "-47.5902"; }
+ ListElement{ radius: "60"; angle: "325"; value: "-44.1297"; }
+ ListElement{ radius: "60"; angle: "330"; value: "-40.4508"; }
+ ListElement{ radius: "60"; angle: "335"; value: "-36.5818"; }
+ ListElement{ radius: "60"; angle: "340"; value: "-32.5519"; }
+ ListElement{ radius: "60"; angle: "345"; value: "-28.3918"; }
+ ListElement{ radius: "60"; angle: "350"; value: "-24.1333"; }
+ ListElement{ radius: "60"; angle: "355"; value: "-19.8086"; }
+ ListElement{ radius: "60"; angle: "360"; value: "-15.4508"; }
+ ListElement{ radius: "65"; angle: "0"; value: "-22.6995"; }
+ ListElement{ radius: "65"; angle: "5"; value: "-18.3417"; }
+ ListElement{ radius: "65"; angle: "10"; value: "-14.0171"; }
+ ListElement{ radius: "65"; angle: "15"; value: "-9.75857"; }
+ ListElement{ radius: "65"; angle: "20"; value: "-5.59852"; }
+ ListElement{ radius: "65"; angle: "25"; value: "-1.56861"; }
+ ListElement{ radius: "65"; angle: "30"; value: "2.30048"; }
+ ListElement{ radius: "65"; angle: "35"; value: "5.9793"; }
+ ListElement{ radius: "65"; angle: "40"; value: "9.43986"; }
+ ListElement{ radius: "65"; angle: "45"; value: "12.6558"; }
+ ListElement{ radius: "65"; angle: "50"; value: "15.6027"; }
+ ListElement{ radius: "65"; angle: "55"; value: "18.2581"; }
+ ListElement{ radius: "65"; angle: "60"; value: "20.6017"; }
+ ListElement{ radius: "65"; angle: "65"; value: "22.6159"; }
+ ListElement{ radius: "65"; angle: "70"; value: "24.2851"; }
+ ListElement{ radius: "65"; angle: "75"; value: "25.5968"; }
+ ListElement{ radius: "65"; angle: "80"; value: "26.5409"; }
+ ListElement{ radius: "65"; angle: "85"; value: "27.1102"; }
+ ListElement{ radius: "65"; angle: "90"; value: "27.3005"; }
+ ListElement{ radius: "65"; angle: "95"; value: "27.1102"; }
+ ListElement{ radius: "65"; angle: "100"; value: "26.5409"; }
+ ListElement{ radius: "65"; angle: "105"; value: "25.5968"; }
+ ListElement{ radius: "65"; angle: "110"; value: "24.2851"; }
+ ListElement{ radius: "65"; angle: "115"; value: "22.6159"; }
+ ListElement{ radius: "65"; angle: "120"; value: "20.6017"; }
+ ListElement{ radius: "65"; angle: "125"; value: "18.2581"; }
+ ListElement{ radius: "65"; angle: "130"; value: "15.6027"; }
+ ListElement{ radius: "65"; angle: "135"; value: "12.6558"; }
+ ListElement{ radius: "65"; angle: "140"; value: "9.43986"; }
+ ListElement{ radius: "65"; angle: "145"; value: "5.9793"; }
+ ListElement{ radius: "65"; angle: "150"; value: "2.30048"; }
+ ListElement{ radius: "65"; angle: "155"; value: "-1.56861"; }
+ ListElement{ radius: "65"; angle: "160"; value: "-5.59852"; }
+ ListElement{ radius: "65"; angle: "165"; value: "-9.75857"; }
+ ListElement{ radius: "65"; angle: "170"; value: "-14.0171"; }
+ ListElement{ radius: "65"; angle: "175"; value: "-18.3417"; }
+ ListElement{ radius: "65"; angle: "180"; value: "-22.6995"; }
+ ListElement{ radius: "65"; angle: "185"; value: "-27.0573"; }
+ ListElement{ radius: "65"; angle: "190"; value: "-31.3819"; }
+ ListElement{ radius: "65"; angle: "195"; value: "-35.6405"; }
+ ListElement{ radius: "65"; angle: "200"; value: "-39.8005"; }
+ ListElement{ radius: "65"; angle: "205"; value: "-43.8304"; }
+ ListElement{ radius: "65"; angle: "210"; value: "-47.6995"; }
+ ListElement{ radius: "65"; angle: "215"; value: "-51.3783"; }
+ ListElement{ radius: "65"; angle: "220"; value: "-54.8389"; }
+ ListElement{ radius: "65"; angle: "225"; value: "-58.0549"; }
+ ListElement{ radius: "65"; angle: "230"; value: "-61.0017"; }
+ ListElement{ radius: "65"; angle: "235"; value: "-63.6571"; }
+ ListElement{ radius: "65"; angle: "240"; value: "-66.0008"; }
+ ListElement{ radius: "65"; angle: "245"; value: "-68.0149"; }
+ ListElement{ radius: "65"; angle: "250"; value: "-69.6842"; }
+ ListElement{ radius: "65"; angle: "255"; value: "-70.9958"; }
+ ListElement{ radius: "65"; angle: "260"; value: "-71.9399"; }
+ ListElement{ radius: "65"; angle: "265"; value: "-72.5093"; }
+ ListElement{ radius: "65"; angle: "270"; value: "-72.6995"; }
+ ListElement{ radius: "65"; angle: "275"; value: "-72.5093"; }
+ ListElement{ radius: "65"; angle: "280"; value: "-71.9399"; }
+ ListElement{ radius: "65"; angle: "285"; value: "-70.9958"; }
+ ListElement{ radius: "65"; angle: "290"; value: "-69.6842"; }
+ ListElement{ radius: "65"; angle: "295"; value: "-68.0149"; }
+ ListElement{ radius: "65"; angle: "300"; value: "-66.0008"; }
+ ListElement{ radius: "65"; angle: "305"; value: "-63.6571"; }
+ ListElement{ radius: "65"; angle: "310"; value: "-61.0017"; }
+ ListElement{ radius: "65"; angle: "315"; value: "-58.0549"; }
+ ListElement{ radius: "65"; angle: "320"; value: "-54.8389"; }
+ ListElement{ radius: "65"; angle: "325"; value: "-51.3783"; }
+ ListElement{ radius: "65"; angle: "330"; value: "-47.6995"; }
+ ListElement{ radius: "65"; angle: "335"; value: "-43.8304"; }
+ ListElement{ radius: "65"; angle: "340"; value: "-39.8005"; }
+ ListElement{ radius: "65"; angle: "345"; value: "-35.6405"; }
+ ListElement{ radius: "65"; angle: "350"; value: "-31.3819"; }
+ ListElement{ radius: "65"; angle: "355"; value: "-27.0573"; }
+ ListElement{ radius: "65"; angle: "360"; value: "-22.6995"; }
+ ListElement{ radius: "70"; angle: "0"; value: "-29.3893"; }
+ ListElement{ radius: "70"; angle: "5"; value: "-25.0315"; }
+ ListElement{ radius: "70"; angle: "10"; value: "-20.7069"; }
+ ListElement{ radius: "70"; angle: "15"; value: "-16.4483"; }
+ ListElement{ radius: "70"; angle: "20"; value: "-12.2883"; }
+ ListElement{ radius: "70"; angle: "25"; value: "-8.25835"; }
+ ListElement{ radius: "70"; angle: "30"; value: "-4.38926"; }
+ ListElement{ radius: "70"; angle: "35"; value: "-0.710441"; }
+ ListElement{ radius: "70"; angle: "40"; value: "2.75012"; }
+ ListElement{ radius: "70"; angle: "45"; value: "5.96608"; }
+ ListElement{ radius: "70"; angle: "50"; value: "8.91296"; }
+ ListElement{ radius: "70"; angle: "55"; value: "11.5683"; }
+ ListElement{ radius: "70"; angle: "60"; value: "13.912"; }
+ ListElement{ radius: "70"; angle: "65"; value: "15.9261"; }
+ ListElement{ radius: "70"; angle: "70"; value: "17.5954"; }
+ ListElement{ radius: "70"; angle: "75"; value: "18.907"; }
+ ListElement{ radius: "70"; angle: "80"; value: "19.8511"; }
+ ListElement{ radius: "70"; angle: "85"; value: "20.4205"; }
+ ListElement{ radius: "70"; angle: "90"; value: "20.6107"; }
+ ListElement{ radius: "70"; angle: "95"; value: "20.4205"; }
+ ListElement{ radius: "70"; angle: "100"; value: "19.8511"; }
+ ListElement{ radius: "70"; angle: "105"; value: "18.907"; }
+ ListElement{ radius: "70"; angle: "110"; value: "17.5954"; }
+ ListElement{ radius: "70"; angle: "115"; value: "15.9261"; }
+ ListElement{ radius: "70"; angle: "120"; value: "13.912"; }
+ ListElement{ radius: "70"; angle: "125"; value: "11.5683"; }
+ ListElement{ radius: "70"; angle: "130"; value: "8.91296"; }
+ ListElement{ radius: "70"; angle: "135"; value: "5.96608"; }
+ ListElement{ radius: "70"; angle: "140"; value: "2.75012"; }
+ ListElement{ radius: "70"; angle: "145"; value: "-0.710441"; }
+ ListElement{ radius: "70"; angle: "150"; value: "-4.38926"; }
+ ListElement{ radius: "70"; angle: "155"; value: "-8.25835"; }
+ ListElement{ radius: "70"; angle: "160"; value: "-12.2883"; }
+ ListElement{ radius: "70"; angle: "165"; value: "-16.4483"; }
+ ListElement{ radius: "70"; angle: "170"; value: "-20.7069"; }
+ ListElement{ radius: "70"; angle: "175"; value: "-25.0315"; }
+ ListElement{ radius: "70"; angle: "180"; value: "-29.3893"; }
+ ListElement{ radius: "70"; angle: "185"; value: "-33.747"; }
+ ListElement{ radius: "70"; angle: "190"; value: "-38.0717"; }
+ ListElement{ radius: "70"; angle: "195"; value: "-42.3302"; }
+ ListElement{ radius: "70"; angle: "200"; value: "-46.4903"; }
+ ListElement{ radius: "70"; angle: "205"; value: "-50.5202"; }
+ ListElement{ radius: "70"; angle: "210"; value: "-54.3893"; }
+ ListElement{ radius: "70"; angle: "215"; value: "-58.0681"; }
+ ListElement{ radius: "70"; angle: "220"; value: "-61.5286"; }
+ ListElement{ radius: "70"; angle: "225"; value: "-64.7446"; }
+ ListElement{ radius: "70"; angle: "230"; value: "-67.6915"; }
+ ListElement{ radius: "70"; angle: "235"; value: "-70.3469"; }
+ ListElement{ radius: "70"; angle: "240"; value: "-72.6905"; }
+ ListElement{ radius: "70"; angle: "245"; value: "-74.7047"; }
+ ListElement{ radius: "70"; angle: "250"; value: "-76.3739"; }
+ ListElement{ radius: "70"; angle: "255"; value: "-77.6856"; }
+ ListElement{ radius: "70"; angle: "260"; value: "-78.6297"; }
+ ListElement{ radius: "70"; angle: "265"; value: "-79.199"; }
+ ListElement{ radius: "70"; angle: "270"; value: "-79.3893"; }
+ ListElement{ radius: "70"; angle: "275"; value: "-79.199"; }
+ ListElement{ radius: "70"; angle: "280"; value: "-78.6297"; }
+ ListElement{ radius: "70"; angle: "285"; value: "-77.6856"; }
+ ListElement{ radius: "70"; angle: "290"; value: "-76.3739"; }
+ ListElement{ radius: "70"; angle: "295"; value: "-74.7047"; }
+ ListElement{ radius: "70"; angle: "300"; value: "-72.6905"; }
+ ListElement{ radius: "70"; angle: "305"; value: "-70.3469"; }
+ ListElement{ radius: "70"; angle: "310"; value: "-67.6915"; }
+ ListElement{ radius: "70"; angle: "315"; value: "-64.7446"; }
+ ListElement{ radius: "70"; angle: "320"; value: "-61.5286"; }
+ ListElement{ radius: "70"; angle: "325"; value: "-58.0681"; }
+ ListElement{ radius: "70"; angle: "330"; value: "-54.3893"; }
+ ListElement{ radius: "70"; angle: "335"; value: "-50.5202"; }
+ ListElement{ radius: "70"; angle: "340"; value: "-46.4903"; }
+ ListElement{ radius: "70"; angle: "345"; value: "-42.3302"; }
+ ListElement{ radius: "70"; angle: "350"; value: "-38.0717"; }
+ ListElement{ radius: "70"; angle: "355"; value: "-33.747"; }
+ ListElement{ radius: "70"; angle: "360"; value: "-29.3893"; }
+ ListElement{ radius: "75"; angle: "0"; value: "-35.3553"; }
+ ListElement{ radius: "75"; angle: "5"; value: "-30.9976"; }
+ ListElement{ radius: "75"; angle: "10"; value: "-26.6729"; }
+ ListElement{ radius: "75"; angle: "15"; value: "-22.4144"; }
+ ListElement{ radius: "75"; angle: "20"; value: "-18.2543"; }
+ ListElement{ radius: "75"; angle: "25"; value: "-14.2244"; }
+ ListElement{ radius: "75"; angle: "30"; value: "-10.3553"; }
+ ListElement{ radius: "75"; angle: "35"; value: "-6.67652"; }
+ ListElement{ radius: "75"; angle: "40"; value: "-3.21596"; }
+ ListElement{ radius: "75"; angle: "45"; value: "5.55112e-15"; }
+ ListElement{ radius: "75"; angle: "50"; value: "2.94688"; }
+ ListElement{ radius: "75"; angle: "55"; value: "5.60226"; }
+ ListElement{ radius: "75"; angle: "60"; value: "7.94593"; }
+ ListElement{ radius: "75"; angle: "65"; value: "9.96005"; }
+ ListElement{ radius: "75"; angle: "70"; value: "11.6293"; }
+ ListElement{ radius: "75"; angle: "75"; value: "12.941"; }
+ ListElement{ radius: "75"; angle: "80"; value: "13.885"; }
+ ListElement{ radius: "75"; angle: "85"; value: "14.4544"; }
+ ListElement{ radius: "75"; angle: "90"; value: "14.6447"; }
+ ListElement{ radius: "75"; angle: "95"; value: "14.4544"; }
+ ListElement{ radius: "75"; angle: "100"; value: "13.885"; }
+ ListElement{ radius: "75"; angle: "105"; value: "12.941"; }
+ ListElement{ radius: "75"; angle: "110"; value: "11.6293"; }
+ ListElement{ radius: "75"; angle: "115"; value: "9.96005"; }
+ ListElement{ radius: "75"; angle: "120"; value: "7.94593"; }
+ ListElement{ radius: "75"; angle: "125"; value: "5.60226"; }
+ ListElement{ radius: "75"; angle: "130"; value: "2.94688"; }
+ ListElement{ radius: "75"; angle: "135"; value: "5.55112e-15"; }
+ ListElement{ radius: "75"; angle: "140"; value: "-3.21596"; }
+ ListElement{ radius: "75"; angle: "145"; value: "-6.67652"; }
+ ListElement{ radius: "75"; angle: "150"; value: "-10.3553"; }
+ ListElement{ radius: "75"; angle: "155"; value: "-14.2244"; }
+ ListElement{ radius: "75"; angle: "160"; value: "-18.2543"; }
+ ListElement{ radius: "75"; angle: "165"; value: "-22.4144"; }
+ ListElement{ radius: "75"; angle: "170"; value: "-26.6729"; }
+ ListElement{ radius: "75"; angle: "175"; value: "-30.9976"; }
+ ListElement{ radius: "75"; angle: "180"; value: "-35.3553"; }
+ ListElement{ radius: "75"; angle: "185"; value: "-39.7131"; }
+ ListElement{ radius: "75"; angle: "190"; value: "-44.0377"; }
+ ListElement{ radius: "75"; angle: "195"; value: "-48.2963"; }
+ ListElement{ radius: "75"; angle: "200"; value: "-52.4563"; }
+ ListElement{ radius: "75"; angle: "205"; value: "-56.4863"; }
+ ListElement{ radius: "75"; angle: "210"; value: "-60.3553"; }
+ ListElement{ radius: "75"; angle: "215"; value: "-64.0342"; }
+ ListElement{ radius: "75"; angle: "220"; value: "-67.4947"; }
+ ListElement{ radius: "75"; angle: "225"; value: "-70.7107"; }
+ ListElement{ radius: "75"; angle: "230"; value: "-73.6576"; }
+ ListElement{ radius: "75"; angle: "235"; value: "-76.3129"; }
+ ListElement{ radius: "75"; angle: "240"; value: "-78.6566"; }
+ ListElement{ radius: "75"; angle: "245"; value: "-80.6707"; }
+ ListElement{ radius: "75"; angle: "250"; value: "-82.34"; }
+ ListElement{ radius: "75"; angle: "255"; value: "-83.6516"; }
+ ListElement{ radius: "75"; angle: "260"; value: "-84.5957"; }
+ ListElement{ radius: "75"; angle: "265"; value: "-85.1651"; }
+ ListElement{ radius: "75"; angle: "270"; value: "-85.3553"; }
+ ListElement{ radius: "75"; angle: "275"; value: "-85.1651"; }
+ ListElement{ radius: "75"; angle: "280"; value: "-84.5957"; }
+ ListElement{ radius: "75"; angle: "285"; value: "-83.6516"; }
+ ListElement{ radius: "75"; angle: "290"; value: "-82.34"; }
+ ListElement{ radius: "75"; angle: "295"; value: "-80.6707"; }
+ ListElement{ radius: "75"; angle: "300"; value: "-78.6566"; }
+ ListElement{ radius: "75"; angle: "305"; value: "-76.3129"; }
+ ListElement{ radius: "75"; angle: "310"; value: "-73.6576"; }
+ ListElement{ radius: "75"; angle: "315"; value: "-70.7107"; }
+ ListElement{ radius: "75"; angle: "320"; value: "-67.4947"; }
+ ListElement{ radius: "75"; angle: "325"; value: "-64.0342"; }
+ ListElement{ radius: "75"; angle: "330"; value: "-60.3553"; }
+ ListElement{ radius: "75"; angle: "335"; value: "-56.4863"; }
+ ListElement{ radius: "75"; angle: "340"; value: "-52.4563"; }
+ ListElement{ radius: "75"; angle: "345"; value: "-48.2963"; }
+ ListElement{ radius: "75"; angle: "350"; value: "-44.0377"; }
+ ListElement{ radius: "75"; angle: "355"; value: "-39.7131"; }
+ ListElement{ radius: "75"; angle: "360"; value: "-35.3553"; }
+ ListElement{ radius: "80"; angle: "0"; value: "-40.4508"; }
+ ListElement{ radius: "80"; angle: "5"; value: "-36.0931"; }
+ ListElement{ radius: "80"; angle: "10"; value: "-31.7684"; }
+ ListElement{ radius: "80"; angle: "15"; value: "-27.5099"; }
+ ListElement{ radius: "80"; angle: "20"; value: "-23.3498"; }
+ ListElement{ radius: "80"; angle: "25"; value: "-19.3199"; }
+ ListElement{ radius: "80"; angle: "30"; value: "-15.4508"; }
+ ListElement{ radius: "80"; angle: "35"; value: "-11.772"; }
+ ListElement{ radius: "80"; angle: "40"; value: "-8.31147"; }
+ ListElement{ radius: "80"; angle: "45"; value: "-5.09551"; }
+ ListElement{ radius: "80"; angle: "50"; value: "-2.14863"; }
+ ListElement{ radius: "80"; angle: "55"; value: "0.506752"; }
+ ListElement{ radius: "80"; angle: "60"; value: "2.85042"; }
+ ListElement{ radius: "80"; angle: "65"; value: "4.86454"; }
+ ListElement{ radius: "80"; angle: "70"; value: "6.53378"; }
+ ListElement{ radius: "80"; angle: "75"; value: "7.84544"; }
+ ListElement{ radius: "80"; angle: "80"; value: "8.78954"; }
+ ListElement{ radius: "80"; angle: "85"; value: "9.35889"; }
+ ListElement{ radius: "80"; angle: "90"; value: "9.54915"; }
+ ListElement{ radius: "80"; angle: "95"; value: "9.35889"; }
+ ListElement{ radius: "80"; angle: "100"; value: "8.78954"; }
+ ListElement{ radius: "80"; angle: "105"; value: "7.84544"; }
+ ListElement{ radius: "80"; angle: "110"; value: "6.53378"; }
+ ListElement{ radius: "80"; angle: "115"; value: "4.86454"; }
+ ListElement{ radius: "80"; angle: "120"; value: "2.85042"; }
+ ListElement{ radius: "80"; angle: "125"; value: "0.506752"; }
+ ListElement{ radius: "80"; angle: "130"; value: "-2.14863"; }
+ ListElement{ radius: "80"; angle: "135"; value: "-5.09551"; }
+ ListElement{ radius: "80"; angle: "140"; value: "-8.31147"; }
+ ListElement{ radius: "80"; angle: "145"; value: "-11.772"; }
+ ListElement{ radius: "80"; angle: "150"; value: "-15.4508"; }
+ ListElement{ radius: "80"; angle: "155"; value: "-19.3199"; }
+ ListElement{ radius: "80"; angle: "160"; value: "-23.3498"; }
+ ListElement{ radius: "80"; angle: "165"; value: "-27.5099"; }
+ ListElement{ radius: "80"; angle: "170"; value: "-31.7684"; }
+ ListElement{ radius: "80"; angle: "175"; value: "-36.0931"; }
+ ListElement{ radius: "80"; angle: "180"; value: "-40.4508"; }
+ ListElement{ radius: "80"; angle: "185"; value: "-44.8086"; }
+ ListElement{ radius: "80"; angle: "190"; value: "-49.1333"; }
+ ListElement{ radius: "80"; angle: "195"; value: "-53.3918"; }
+ ListElement{ radius: "80"; angle: "200"; value: "-57.5519"; }
+ ListElement{ radius: "80"; angle: "205"; value: "-61.5818"; }
+ ListElement{ radius: "80"; angle: "210"; value: "-65.4508"; }
+ ListElement{ radius: "80"; angle: "215"; value: "-69.1297"; }
+ ListElement{ radius: "80"; angle: "220"; value: "-72.5902"; }
+ ListElement{ radius: "80"; angle: "225"; value: "-75.8062"; }
+ ListElement{ radius: "80"; angle: "230"; value: "-78.7531"; }
+ ListElement{ radius: "80"; angle: "235"; value: "-81.4085"; }
+ ListElement{ radius: "80"; angle: "240"; value: "-83.7521"; }
+ ListElement{ radius: "80"; angle: "245"; value: "-85.7662"; }
+ ListElement{ radius: "80"; angle: "250"; value: "-87.4355"; }
+ ListElement{ radius: "80"; angle: "255"; value: "-88.7471"; }
+ ListElement{ radius: "80"; angle: "260"; value: "-89.6912"; }
+ ListElement{ radius: "80"; angle: "265"; value: "-90.2606"; }
+ ListElement{ radius: "80"; angle: "270"; value: "-90.4508"; }
+ ListElement{ radius: "80"; angle: "275"; value: "-90.2606"; }
+ ListElement{ radius: "80"; angle: "280"; value: "-89.6912"; }
+ ListElement{ radius: "80"; angle: "285"; value: "-88.7471"; }
+ ListElement{ radius: "80"; angle: "290"; value: "-87.4355"; }
+ ListElement{ radius: "80"; angle: "295"; value: "-85.7662"; }
+ ListElement{ radius: "80"; angle: "300"; value: "-83.7521"; }
+ ListElement{ radius: "80"; angle: "305"; value: "-81.4085"; }
+ ListElement{ radius: "80"; angle: "310"; value: "-78.7531"; }
+ ListElement{ radius: "80"; angle: "315"; value: "-75.8062"; }
+ ListElement{ radius: "80"; angle: "320"; value: "-72.5902"; }
+ ListElement{ radius: "80"; angle: "325"; value: "-69.1297"; }
+ ListElement{ radius: "80"; angle: "330"; value: "-65.4508"; }
+ ListElement{ radius: "80"; angle: "335"; value: "-61.5818"; }
+ ListElement{ radius: "80"; angle: "340"; value: "-57.5519"; }
+ ListElement{ radius: "80"; angle: "345"; value: "-53.3918"; }
+ ListElement{ radius: "80"; angle: "350"; value: "-49.1333"; }
+ ListElement{ radius: "80"; angle: "355"; value: "-44.8086"; }
+ ListElement{ radius: "80"; angle: "360"; value: "-40.4508"; }
+ ListElement{ radius: "85"; angle: "0"; value: "-44.5503"; }
+ ListElement{ radius: "85"; angle: "5"; value: "-40.1925"; }
+ ListElement{ radius: "85"; angle: "10"; value: "-35.8679"; }
+ ListElement{ radius: "85"; angle: "15"; value: "-31.6094"; }
+ ListElement{ radius: "85"; angle: "20"; value: "-27.4493"; }
+ ListElement{ radius: "85"; angle: "25"; value: "-23.4194"; }
+ ListElement{ radius: "85"; angle: "30"; value: "-19.5503"; }
+ ListElement{ radius: "85"; angle: "35"; value: "-15.8715"; }
+ ListElement{ radius: "85"; angle: "40"; value: "-12.4109"; }
+ ListElement{ radius: "85"; angle: "45"; value: "-9.19499"; }
+ ListElement{ radius: "85"; angle: "50"; value: "-6.2481"; }
+ ListElement{ radius: "85"; angle: "55"; value: "-3.59272"; }
+ ListElement{ radius: "85"; angle: "60"; value: "-1.24906"; }
+ ListElement{ radius: "85"; angle: "65"; value: "0.765063"; }
+ ListElement{ radius: "85"; angle: "70"; value: "2.4343"; }
+ ListElement{ radius: "85"; angle: "75"; value: "3.74597"; }
+ ListElement{ radius: "85"; angle: "80"; value: "4.69006"; }
+ ListElement{ radius: "85"; angle: "85"; value: "5.25941"; }
+ ListElement{ radius: "85"; angle: "90"; value: "5.44967"; }
+ ListElement{ radius: "85"; angle: "95"; value: "5.25941"; }
+ ListElement{ radius: "85"; angle: "100"; value: "4.69006"; }
+ ListElement{ radius: "85"; angle: "105"; value: "3.74597"; }
+ ListElement{ radius: "85"; angle: "110"; value: "2.4343"; }
+ ListElement{ radius: "85"; angle: "115"; value: "0.765063"; }
+ ListElement{ radius: "85"; angle: "120"; value: "-1.24906"; }
+ ListElement{ radius: "85"; angle: "125"; value: "-3.59272"; }
+ ListElement{ radius: "85"; angle: "130"; value: "-6.2481"; }
+ ListElement{ radius: "85"; angle: "135"; value: "-9.19499"; }
+ ListElement{ radius: "85"; angle: "140"; value: "-12.4109"; }
+ ListElement{ radius: "85"; angle: "145"; value: "-15.8715"; }
+ ListElement{ radius: "85"; angle: "150"; value: "-19.5503"; }
+ ListElement{ radius: "85"; angle: "155"; value: "-23.4194"; }
+ ListElement{ radius: "85"; angle: "160"; value: "-27.4493"; }
+ ListElement{ radius: "85"; angle: "165"; value: "-31.6094"; }
+ ListElement{ radius: "85"; angle: "170"; value: "-35.8679"; }
+ ListElement{ radius: "85"; angle: "175"; value: "-40.1925"; }
+ ListElement{ radius: "85"; angle: "180"; value: "-44.5503"; }
+ ListElement{ radius: "85"; angle: "185"; value: "-48.9081"; }
+ ListElement{ radius: "85"; angle: "190"; value: "-53.2327"; }
+ ListElement{ radius: "85"; angle: "195"; value: "-57.4913"; }
+ ListElement{ radius: "85"; angle: "200"; value: "-61.6513"; }
+ ListElement{ radius: "85"; angle: "205"; value: "-65.6812"; }
+ ListElement{ radius: "85"; angle: "210"; value: "-69.5503"; }
+ ListElement{ radius: "85"; angle: "215"; value: "-73.2291"; }
+ ListElement{ radius: "85"; angle: "220"; value: "-76.6897"; }
+ ListElement{ radius: "85"; angle: "225"; value: "-79.9057"; }
+ ListElement{ radius: "85"; angle: "230"; value: "-82.8525"; }
+ ListElement{ radius: "85"; angle: "235"; value: "-85.5079"; }
+ ListElement{ radius: "85"; angle: "240"; value: "-87.8516"; }
+ ListElement{ radius: "85"; angle: "245"; value: "-89.8657"; }
+ ListElement{ radius: "85"; angle: "250"; value: "-91.535"; }
+ ListElement{ radius: "85"; angle: "255"; value: "-92.8466"; }
+ ListElement{ radius: "85"; angle: "260"; value: "-93.7907"; }
+ ListElement{ radius: "85"; angle: "265"; value: "-94.3601"; }
+ ListElement{ radius: "85"; angle: "270"; value: "-94.5503"; }
+ ListElement{ radius: "85"; angle: "275"; value: "-94.3601"; }
+ ListElement{ radius: "85"; angle: "280"; value: "-93.7907"; }
+ ListElement{ radius: "85"; angle: "285"; value: "-92.8466"; }
+ ListElement{ radius: "85"; angle: "290"; value: "-91.535"; }
+ ListElement{ radius: "85"; angle: "295"; value: "-89.8657"; }
+ ListElement{ radius: "85"; angle: "300"; value: "-87.8516"; }
+ ListElement{ radius: "85"; angle: "305"; value: "-85.5079"; }
+ ListElement{ radius: "85"; angle: "310"; value: "-82.8525"; }
+ ListElement{ radius: "85"; angle: "315"; value: "-79.9057"; }
+ ListElement{ radius: "85"; angle: "320"; value: "-76.6897"; }
+ ListElement{ radius: "85"; angle: "325"; value: "-73.2291"; }
+ ListElement{ radius: "85"; angle: "330"; value: "-69.5503"; }
+ ListElement{ radius: "85"; angle: "335"; value: "-65.6812"; }
+ ListElement{ radius: "85"; angle: "340"; value: "-61.6513"; }
+ ListElement{ radius: "85"; angle: "345"; value: "-57.4913"; }
+ ListElement{ radius: "85"; angle: "350"; value: "-53.2327"; }
+ ListElement{ radius: "85"; angle: "355"; value: "-48.9081"; }
+ ListElement{ radius: "85"; angle: "360"; value: "-44.5503"; }
+ ListElement{ radius: "90"; angle: "0"; value: "-47.5528"; }
+ ListElement{ radius: "90"; angle: "5"; value: "-43.195"; }
+ ListElement{ radius: "90"; angle: "10"; value: "-38.8704"; }
+ ListElement{ radius: "90"; angle: "15"; value: "-34.6119"; }
+ ListElement{ radius: "90"; angle: "20"; value: "-30.4518"; }
+ ListElement{ radius: "90"; angle: "25"; value: "-26.4219"; }
+ ListElement{ radius: "90"; angle: "30"; value: "-22.5528"; }
+ ListElement{ radius: "90"; angle: "35"; value: "-18.874"; }
+ ListElement{ radius: "90"; angle: "40"; value: "-15.4134"; }
+ ListElement{ radius: "90"; angle: "45"; value: "-12.1975"; }
+ ListElement{ radius: "90"; angle: "50"; value: "-9.2506"; }
+ ListElement{ radius: "90"; angle: "55"; value: "-6.59522"; }
+ ListElement{ radius: "90"; angle: "60"; value: "-4.25156"; }
+ ListElement{ radius: "90"; angle: "65"; value: "-2.23744"; }
+ ListElement{ radius: "90"; angle: "70"; value: "-0.568195"; }
+ ListElement{ radius: "90"; angle: "75"; value: "0.743465"; }
+ ListElement{ radius: "90"; angle: "80"; value: "1.68756"; }
+ ListElement{ radius: "90"; angle: "85"; value: "2.25691"; }
+ ListElement{ radius: "90"; angle: "90"; value: "2.44717"; }
+ ListElement{ radius: "90"; angle: "95"; value: "2.25691"; }
+ ListElement{ radius: "90"; angle: "100"; value: "1.68756"; }
+ ListElement{ radius: "90"; angle: "105"; value: "0.743465"; }
+ ListElement{ radius: "90"; angle: "110"; value: "-0.568195"; }
+ ListElement{ radius: "90"; angle: "115"; value: "-2.23744"; }
+ ListElement{ radius: "90"; angle: "120"; value: "-4.25156"; }
+ ListElement{ radius: "90"; angle: "125"; value: "-6.59522"; }
+ ListElement{ radius: "90"; angle: "130"; value: "-9.2506"; }
+ ListElement{ radius: "90"; angle: "135"; value: "-12.1975"; }
+ ListElement{ radius: "90"; angle: "140"; value: "-15.4134"; }
+ ListElement{ radius: "90"; angle: "145"; value: "-18.874"; }
+ ListElement{ radius: "90"; angle: "150"; value: "-22.5528"; }
+ ListElement{ radius: "90"; angle: "155"; value: "-26.4219"; }
+ ListElement{ radius: "90"; angle: "160"; value: "-30.4518"; }
+ ListElement{ radius: "90"; angle: "165"; value: "-34.6119"; }
+ ListElement{ radius: "90"; angle: "170"; value: "-38.8704"; }
+ ListElement{ radius: "90"; angle: "175"; value: "-43.195"; }
+ ListElement{ radius: "90"; angle: "180"; value: "-47.5528"; }
+ ListElement{ radius: "90"; angle: "185"; value: "-51.9106"; }
+ ListElement{ radius: "90"; angle: "190"; value: "-56.2352"; }
+ ListElement{ radius: "90"; angle: "195"; value: "-60.4938"; }
+ ListElement{ radius: "90"; angle: "200"; value: "-64.6538"; }
+ ListElement{ radius: "90"; angle: "205"; value: "-68.6837"; }
+ ListElement{ radius: "90"; angle: "210"; value: "-72.5528"; }
+ ListElement{ radius: "90"; angle: "215"; value: "-76.2316"; }
+ ListElement{ radius: "90"; angle: "220"; value: "-79.6922"; }
+ ListElement{ radius: "90"; angle: "225"; value: "-82.9082"; }
+ ListElement{ radius: "90"; angle: "230"; value: "-85.855"; }
+ ListElement{ radius: "90"; angle: "235"; value: "-88.5104"; }
+ ListElement{ radius: "90"; angle: "240"; value: "-90.8541"; }
+ ListElement{ radius: "90"; angle: "245"; value: "-92.8682"; }
+ ListElement{ radius: "90"; angle: "250"; value: "-94.5375"; }
+ ListElement{ radius: "90"; angle: "255"; value: "-95.8491"; }
+ ListElement{ radius: "90"; angle: "260"; value: "-96.7932"; }
+ ListElement{ radius: "90"; angle: "265"; value: "-97.3626"; }
+ ListElement{ radius: "90"; angle: "270"; value: "-97.5528"; }
+ ListElement{ radius: "90"; angle: "275"; value: "-97.3626"; }
+ ListElement{ radius: "90"; angle: "280"; value: "-96.7932"; }
+ ListElement{ radius: "90"; angle: "285"; value: "-95.8491"; }
+ ListElement{ radius: "90"; angle: "290"; value: "-94.5375"; }
+ ListElement{ radius: "90"; angle: "295"; value: "-92.8682"; }
+ ListElement{ radius: "90"; angle: "300"; value: "-90.8541"; }
+ ListElement{ radius: "90"; angle: "305"; value: "-88.5104"; }
+ ListElement{ radius: "90"; angle: "310"; value: "-85.855"; }
+ ListElement{ radius: "90"; angle: "315"; value: "-82.9082"; }
+ ListElement{ radius: "90"; angle: "320"; value: "-79.6922"; }
+ ListElement{ radius: "90"; angle: "325"; value: "-76.2316"; }
+ ListElement{ radius: "90"; angle: "330"; value: "-72.5528"; }
+ ListElement{ radius: "90"; angle: "335"; value: "-68.6837"; }
+ ListElement{ radius: "90"; angle: "340"; value: "-64.6538"; }
+ ListElement{ radius: "90"; angle: "345"; value: "-60.4938"; }
+ ListElement{ radius: "90"; angle: "350"; value: "-56.2352"; }
+ ListElement{ radius: "90"; angle: "355"; value: "-51.9106"; }
+ ListElement{ radius: "90"; angle: "360"; value: "-47.5528"; }
+ ListElement{ radius: "95"; angle: "0"; value: "-49.3844"; }
+ ListElement{ radius: "95"; angle: "5"; value: "-45.0266"; }
+ ListElement{ radius: "95"; angle: "10"; value: "-40.702"; }
+ ListElement{ radius: "95"; angle: "15"; value: "-36.4435"; }
+ ListElement{ radius: "95"; angle: "20"; value: "-32.2834"; }
+ ListElement{ radius: "95"; angle: "25"; value: "-28.2535"; }
+ ListElement{ radius: "95"; angle: "30"; value: "-24.3844"; }
+ ListElement{ radius: "95"; angle: "35"; value: "-20.7056"; }
+ ListElement{ radius: "95"; angle: "40"; value: "-17.245"; }
+ ListElement{ radius: "95"; angle: "45"; value: "-14.0291"; }
+ ListElement{ radius: "95"; angle: "50"; value: "-11.0822"; }
+ ListElement{ radius: "95"; angle: "55"; value: "-8.42681"; }
+ ListElement{ radius: "95"; angle: "60"; value: "-6.08315"; }
+ ListElement{ radius: "95"; angle: "65"; value: "-4.06903"; }
+ ListElement{ radius: "95"; angle: "70"; value: "-2.39979"; }
+ ListElement{ radius: "95"; angle: "75"; value: "-1.08813"; }
+ ListElement{ radius: "95"; angle: "80"; value: "-0.144029"; }
+ ListElement{ radius: "95"; angle: "85"; value: "0.425318"; }
+ ListElement{ radius: "95"; angle: "90"; value: "0.615583"; }
+ ListElement{ radius: "95"; angle: "95"; value: "0.425318"; }
+ ListElement{ radius: "95"; angle: "100"; value: "-0.144029"; }
+ ListElement{ radius: "95"; angle: "105"; value: "-1.08813"; }
+ ListElement{ radius: "95"; angle: "110"; value: "-2.39979"; }
+ ListElement{ radius: "95"; angle: "115"; value: "-4.06903"; }
+ ListElement{ radius: "95"; angle: "120"; value: "-6.08315"; }
+ ListElement{ radius: "95"; angle: "125"; value: "-8.42681"; }
+ ListElement{ radius: "95"; angle: "130"; value: "-11.0822"; }
+ ListElement{ radius: "95"; angle: "135"; value: "-14.0291"; }
+ ListElement{ radius: "95"; angle: "140"; value: "-17.245"; }
+ ListElement{ radius: "95"; angle: "145"; value: "-20.7056"; }
+ ListElement{ radius: "95"; angle: "150"; value: "-24.3844"; }
+ ListElement{ radius: "95"; angle: "155"; value: "-28.2535"; }
+ ListElement{ radius: "95"; angle: "160"; value: "-32.2834"; }
+ ListElement{ radius: "95"; angle: "165"; value: "-36.4435"; }
+ ListElement{ radius: "95"; angle: "170"; value: "-40.702"; }
+ ListElement{ radius: "95"; angle: "175"; value: "-45.0266"; }
+ ListElement{ radius: "95"; angle: "180"; value: "-49.3844"; }
+ ListElement{ radius: "95"; angle: "185"; value: "-53.7422"; }
+ ListElement{ radius: "95"; angle: "190"; value: "-58.0668"; }
+ ListElement{ radius: "95"; angle: "195"; value: "-62.3254"; }
+ ListElement{ radius: "95"; angle: "200"; value: "-66.4854"; }
+ ListElement{ radius: "95"; angle: "205"; value: "-70.5153"; }
+ ListElement{ radius: "95"; angle: "210"; value: "-74.3844"; }
+ ListElement{ radius: "95"; angle: "215"; value: "-78.0632"; }
+ ListElement{ radius: "95"; angle: "220"; value: "-81.5238"; }
+ ListElement{ radius: "95"; angle: "225"; value: "-84.7398"; }
+ ListElement{ radius: "95"; angle: "230"; value: "-87.6866"; }
+ ListElement{ radius: "95"; angle: "235"; value: "-90.342"; }
+ ListElement{ radius: "95"; angle: "240"; value: "-92.6857"; }
+ ListElement{ radius: "95"; angle: "245"; value: "-94.6998"; }
+ ListElement{ radius: "95"; angle: "250"; value: "-96.369"; }
+ ListElement{ radius: "95"; angle: "255"; value: "-97.6807"; }
+ ListElement{ radius: "95"; angle: "260"; value: "-98.6248"; }
+ ListElement{ radius: "95"; angle: "265"; value: "-99.1942"; }
+ ListElement{ radius: "95"; angle: "270"; value: "-99.3844"; }
+ ListElement{ radius: "95"; angle: "275"; value: "-99.1942"; }
+ ListElement{ radius: "95"; angle: "280"; value: "-98.6248"; }
+ ListElement{ radius: "95"; angle: "285"; value: "-97.6807"; }
+ ListElement{ radius: "95"; angle: "290"; value: "-96.369"; }
+ ListElement{ radius: "95"; angle: "295"; value: "-94.6998"; }
+ ListElement{ radius: "95"; angle: "300"; value: "-92.6857"; }
+ ListElement{ radius: "95"; angle: "305"; value: "-90.342"; }
+ ListElement{ radius: "95"; angle: "310"; value: "-87.6866"; }
+ ListElement{ radius: "95"; angle: "315"; value: "-84.7398"; }
+ ListElement{ radius: "95"; angle: "320"; value: "-81.5238"; }
+ ListElement{ radius: "95"; angle: "325"; value: "-78.0632"; }
+ ListElement{ radius: "95"; angle: "330"; value: "-74.3844"; }
+ ListElement{ radius: "95"; angle: "335"; value: "-70.5153"; }
+ ListElement{ radius: "95"; angle: "340"; value: "-66.4854"; }
+ ListElement{ radius: "95"; angle: "345"; value: "-62.3254"; }
+ ListElement{ radius: "95"; angle: "350"; value: "-58.0668"; }
+ ListElement{ radius: "95"; angle: "355"; value: "-53.7422"; }
+ ListElement{ radius: "95"; angle: "360"; value: "-49.3844"; }
+ ListElement{ radius: "100"; angle: "0"; value: "-50"; }
+ ListElement{ radius: "100"; angle: "5"; value: "-45.6422"; }
+ ListElement{ radius: "100"; angle: "10"; value: "-41.3176"; }
+ ListElement{ radius: "100"; angle: "15"; value: "-37.059"; }
+ ListElement{ radius: "100"; angle: "20"; value: "-32.899"; }
+ ListElement{ radius: "100"; angle: "25"; value: "-28.8691"; }
+ ListElement{ radius: "100"; angle: "30"; value: "-25"; }
+ ListElement{ radius: "100"; angle: "35"; value: "-21.3212"; }
+ ListElement{ radius: "100"; angle: "40"; value: "-17.8606"; }
+ ListElement{ radius: "100"; angle: "45"; value: "-14.6447"; }
+ ListElement{ radius: "100"; angle: "50"; value: "-11.6978"; }
+ ListElement{ radius: "100"; angle: "55"; value: "-9.0424"; }
+ ListElement{ radius: "100"; angle: "60"; value: "-6.69873"; }
+ ListElement{ radius: "100"; angle: "65"; value: "-4.68461"; }
+ ListElement{ radius: "100"; angle: "70"; value: "-3.01537"; }
+ ListElement{ radius: "100"; angle: "75"; value: "-1.70371"; }
+ ListElement{ radius: "100"; angle: "80"; value: "-0.759612"; }
+ ListElement{ radius: "100"; angle: "85"; value: "-0.190265"; }
+ ListElement{ radius: "100"; angle: "90"; value: "0"; }
+ ListElement{ radius: "100"; angle: "95"; value: "-0.190265"; }
+ ListElement{ radius: "100"; angle: "100"; value: "-0.759612"; }
+ ListElement{ radius: "100"; angle: "105"; value: "-1.70371"; }
+ ListElement{ radius: "100"; angle: "110"; value: "-3.01537"; }
+ ListElement{ radius: "100"; angle: "115"; value: "-4.68461"; }
+ ListElement{ radius: "100"; angle: "120"; value: "-6.69873"; }
+ ListElement{ radius: "100"; angle: "125"; value: "-9.0424"; }
+ ListElement{ radius: "100"; angle: "130"; value: "-11.6978"; }
+ ListElement{ radius: "100"; angle: "135"; value: "-14.6447"; }
+ ListElement{ radius: "100"; angle: "140"; value: "-17.8606"; }
+ ListElement{ radius: "100"; angle: "145"; value: "-21.3212"; }
+ ListElement{ radius: "100"; angle: "150"; value: "-25"; }
+ ListElement{ radius: "100"; angle: "155"; value: "-28.8691"; }
+ ListElement{ radius: "100"; angle: "160"; value: "-32.899"; }
+ ListElement{ radius: "100"; angle: "165"; value: "-37.059"; }
+ ListElement{ radius: "100"; angle: "170"; value: "-41.3176"; }
+ ListElement{ radius: "100"; angle: "175"; value: "-45.6422"; }
+ ListElement{ radius: "100"; angle: "180"; value: "-50"; }
+ ListElement{ radius: "100"; angle: "185"; value: "-54.3578"; }
+ ListElement{ radius: "100"; angle: "190"; value: "-58.6824"; }
+ ListElement{ radius: "100"; angle: "195"; value: "-62.941"; }
+ ListElement{ radius: "100"; angle: "200"; value: "-67.101"; }
+ ListElement{ radius: "100"; angle: "205"; value: "-71.1309"; }
+ ListElement{ radius: "100"; angle: "210"; value: "-75"; }
+ ListElement{ radius: "100"; angle: "215"; value: "-78.6788"; }
+ ListElement{ radius: "100"; angle: "220"; value: "-82.1394"; }
+ ListElement{ radius: "100"; angle: "225"; value: "-85.3553"; }
+ ListElement{ radius: "100"; angle: "230"; value: "-88.3022"; }
+ ListElement{ radius: "100"; angle: "235"; value: "-90.9576"; }
+ ListElement{ radius: "100"; angle: "240"; value: "-93.3013"; }
+ ListElement{ radius: "100"; angle: "245"; value: "-95.3154"; }
+ ListElement{ radius: "100"; angle: "250"; value: "-96.9846"; }
+ ListElement{ radius: "100"; angle: "255"; value: "-98.2963"; }
+ ListElement{ radius: "100"; angle: "260"; value: "-99.2404"; }
+ ListElement{ radius: "100"; angle: "265"; value: "-99.8097"; }
+ ListElement{ radius: "100"; angle: "270"; value: "-100"; }
+ ListElement{ radius: "100"; angle: "275"; value: "-99.8097"; }
+ ListElement{ radius: "100"; angle: "280"; value: "-99.2404"; }
+ ListElement{ radius: "100"; angle: "285"; value: "-98.2963"; }
+ ListElement{ radius: "100"; angle: "290"; value: "-96.9846"; }
+ ListElement{ radius: "100"; angle: "295"; value: "-95.3154"; }
+ ListElement{ radius: "100"; angle: "300"; value: "-93.3013"; }
+ ListElement{ radius: "100"; angle: "305"; value: "-90.9576"; }
+ ListElement{ radius: "100"; angle: "310"; value: "-88.3022"; }
+ ListElement{ radius: "100"; angle: "315"; value: "-85.3553"; }
+ ListElement{ radius: "100"; angle: "320"; value: "-82.1394"; }
+ ListElement{ radius: "100"; angle: "325"; value: "-78.6788"; }
+ ListElement{ radius: "100"; angle: "330"; value: "-75"; }
+ ListElement{ radius: "100"; angle: "335"; value: "-71.1309"; }
+ ListElement{ radius: "100"; angle: "340"; value: "-67.101"; }
+ ListElement{ radius: "100"; angle: "345"; value: "-62.941"; }
+ ListElement{ radius: "100"; angle: "350"; value: "-58.6824"; }
+ ListElement{ radius: "100"; angle: "355"; value: "-54.3578"; }
+ ListElement{ radius: "100"; angle: "360"; value: "-50"; }
+ }
+}
diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml
new file mode 100644
index 00000000..e4fb99d2
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Controls 1.0
+import QtQuick.Controls.Styles 1.0
+
+Item {
+ id: newbutton
+
+ property alias text: buttonText.text
+
+ signal clicked
+
+ implicitWidth: buttonText.implicitWidth + 5
+ implicitHeight: buttonText.implicitHeight + 10
+
+ Button {
+ id: buttonText
+ width: parent.width
+ height: parent.height
+
+ style: ButtonStyle {
+ label: Component {
+ Text {
+ text: buttonText.text
+ clip: true
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ anchors.fill: parent
+ }
+ }
+ }
+ onClicked: newbutton.clicked()
+ }
+}
diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml
new file mode 100644
index 00000000..4c77fd45
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml
@@ -0,0 +1,293 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Layouts 1.0
+import QtQuick.Window 2.1
+import QtDataVisualization 1.2
+import "."
+
+Window {
+ id: mainview
+ title: "Qt Quick 2 Spectrogram Example"
+ visible: true
+ width: 1024
+ height: 768
+ color: surfaceGraph.theme.windowColor
+
+ Data {
+ id: surfaceData
+ }
+
+ Item {
+ id: surfaceView
+ width: mainview.width
+ height: mainview.height
+ anchors.top: mainview.top
+ anchors.left: mainview.left
+
+ ColorGradient {
+ id: surfaceGradient
+ ColorGradientStop { position: 0.0; color: "black" }
+ ColorGradientStop { position: 0.2; color: "red" }
+ ColorGradientStop { position: 0.5; color: "blue" }
+ ColorGradientStop { position: 0.8; color: "yellow" }
+ ColorGradientStop { position: 1.0; color: "white" }
+ }
+
+ ValueAxis3D {
+ id: xAxis
+ segmentCount: 8
+ labelFormat: "%i\u00B0"
+ title: "Angle"
+ titleVisible: true
+ titleFixed: false
+ }
+
+ ValueAxis3D {
+ id: yAxis
+ segmentCount: 8
+ labelFormat: "%i \%"
+ title: "Value"
+ titleVisible: true
+ labelAutoRotation: 0
+ titleFixed: false
+ }
+
+ ValueAxis3D {
+ id: zAxis
+ segmentCount: 5
+ labelFormat: "%i nm"
+ title: "Radius"
+ titleVisible: true
+ titleFixed: false
+ }
+
+ Theme3D {
+ id: customTheme
+ type: Theme3D.ThemeQt
+ // Don't show specular spotlight as we don't want it to distort the colors
+ lightStrength: 0.0
+ ambientLightStrength: 1.0
+ backgroundEnabled: false
+ gridLineColor: "#AAAAAA"
+ windowColor: "#EEEEEE"
+ }
+
+
+ //! [5]
+ TouchInputHandler3D {
+ id: customInputHandler
+ rotationEnabled: false
+ }
+ //! [5]
+
+ //! [0]
+ //! [7]
+ Surface3D {
+ //! [7]
+ id: surfaceGraph
+ width: surfaceView.width
+ height: surfaceView.height
+
+ shadowQuality: AbstractGraph3D.ShadowQualityNone
+ selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndColumn
+ axisX: xAxis
+ axisY: yAxis
+ axisZ: zAxis
+
+ theme: customTheme
+ //! [6]
+ inputHandler: customInputHandler
+ //! [6]
+
+ // Remove the perspective and view the graph from top down to achieve 2D effect
+ //! [1]
+ orthoProjection: true
+ scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove
+ //! [1]
+
+ //! [2]
+ flipHorizontalGrid: true
+ //! [2]
+
+ //! [4]
+ radialLabelOffset: 0.01
+ //! [4]
+
+ horizontalAspectRatio: 1
+ scene.activeCamera.zoomLevel: 85
+
+ Surface3DSeries {
+ id: surfaceSeries
+ flatShadingEnabled: false
+ drawMode: Surface3DSeries.DrawSurface
+ baseGradient: surfaceGradient
+ colorStyle: Theme3D.ColorStyleRangeGradient
+ itemLabelFormat: "(@xLabel, @zLabel): @yLabel"
+
+ ItemModelSurfaceDataProxy {
+ itemModel: surfaceData.model
+ rowRole: "radius"
+ columnRole: "angle"
+ yPosRole: "value"
+ }
+ }
+ }
+ //! [0]
+ }
+
+ RowLayout {
+ id: buttonLayout
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ opacity: 0.5
+
+ //! [3]
+ NewButton {
+ id: polarToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Switch to polar"
+ onClicked: {
+ if (surfaceGraph.polar === false) {
+ surfaceGraph.polar = true
+ text = "Switch to cartesian"
+ } else {
+ surfaceGraph.polar = false
+ text = "Switch to polar"
+ }
+ }
+ }
+ //! [3]
+
+ NewButton {
+ id: orthoToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Switch to perspective"
+ onClicked: {
+ if (surfaceGraph.orthoProjection === true) {
+ surfaceGraph.orthoProjection = false;
+ xAxis.labelAutoRotation = 30
+ yAxis.labelAutoRotation = 30
+ zAxis.labelAutoRotation = 30
+ customInputHandler.rotationEnabled = true
+ text = "Switch to orthographic"
+ } else {
+ surfaceGraph.orthoProjection = true;
+ surfaceGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetDirectlyAbove
+ surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe;
+ xAxis.labelAutoRotation = 0
+ yAxis.labelAutoRotation = 0
+ zAxis.labelAutoRotation = 0
+ customInputHandler.rotationEnabled = false
+ text = "Switch to perspective"
+ }
+ }
+ }
+
+ NewButton {
+ id: flipGridToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Toggle axis grid on top"
+ onClicked: {
+ onClicked: {
+ if (surfaceGraph.flipHorizontalGrid === true) {
+ surfaceGraph.flipHorizontalGrid = false;
+ } else {
+ surfaceGraph.flipHorizontalGrid = true;
+ }
+ }
+ }
+ }
+
+ NewButton {
+ id: labelOffsetToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Toggle radial label position"
+ visible: surfaceGraph.polar
+ onClicked: {
+ if (surfaceGraph.radialLabelOffset >= 1.0) {
+ surfaceGraph.radialLabelOffset = 0.01
+ } else {
+ surfaceGraph.radialLabelOffset = 1.0
+ }
+ }
+ }
+
+ NewButton {
+ id: surfaceGridToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Toggle surface grid"
+ visible: !surfaceGraph.orthoProjection
+ onClicked: {
+ if (surfaceSeries.drawMode & Surface3DSeries.DrawWireframe) {
+ surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe;
+ } else {
+ surfaceSeries.drawMode |= Surface3DSeries.DrawWireframe;
+ }
+ }
+ }
+
+ }
+
+ Rectangle {
+ id: legend
+ anchors.margins: 20
+ anchors.bottom: parent.bottom
+ anchors.top: buttonLayout.bottom
+ anchors.right: parent.right
+ border.color: "black"
+ border.width: 1
+ width: 50
+ rotation: 180
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "black" }
+ GradientStop { position: 0.2; color: "red" }
+ GradientStop { position: 0.5; color: "blue" }
+ GradientStop { position: 0.8; color: "yellow" }
+ GradientStop { position: 1.0; color: "white" }
+ }
+ }
+
+ Text {
+ anchors.verticalCenter: legend.bottom
+ anchors.right: legend.left
+ anchors.margins: 2
+ text: surfaceGraph.axisY.min + "%"
+ }
+
+ Text {
+ anchors.verticalCenter: legend.verticalCenter
+ anchors.right: legend.left
+ anchors.margins: 2
+ text: (surfaceGraph.axisY.max + surfaceGraph.axisY.min) / 2 + "%"
+ }
+
+ Text {
+ anchors.verticalCenter: legend.top
+ anchors.right: legend.left
+ anchors.margins: 2
+ text: surfaceGraph.axisY.max + "%"
+ }
+}
diff --git a/examples/datavisualization/qmlspectrogram/qmlspectrogram.pro b/examples/datavisualization/qmlspectrogram/qmlspectrogram.pro
new file mode 100644
index 00000000..655fb0b8
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/qmlspectrogram.pro
@@ -0,0 +1,12 @@
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+# The .cpp file which was generated for your project. Feel free to hack it.
+SOURCES += main.cpp
+
+RESOURCES += qmlspectrogram.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/* \
+ qml/qmlspectrogram/*
diff --git a/examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc b/examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc
new file mode 100644
index 00000000..9f024404
--- /dev/null
+++ b/examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/qml">
+ <file>qml/qmlspectrogram/Data.qml</file>
+ <file>qml/qmlspectrogram/main.qml</file>
+ <file>qml/qmlspectrogram/NewButton.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/datavisualization/qmlsurfacelayers/layer_2.png b/examples/datavisualization/qmlsurfacelayers/layer_2.png
index 61631ae8..3af154e2 100644
--- a/examples/datavisualization/qmlsurfacelayers/layer_2.png
+++ b/examples/datavisualization/qmlsurfacelayers/layer_2.png
Binary files differ
diff --git a/examples/datavisualization/texturesurface/custominputhandler.cpp b/examples/datavisualization/texturesurface/custominputhandler.cpp
new file mode 100644
index 00000000..2510df54
--- /dev/null
+++ b/examples/datavisualization/texturesurface/custominputhandler.cpp
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "custominputhandler.h"
+
+#include <QtDataVisualization/Q3DCamera>
+#include <QtCore/qmath.h>
+
+CustomInputHandler::CustomInputHandler(QAbstract3DGraph *graph, QObject *parent) :
+ Q3DInputHandler(parent),
+ m_highlight(0),
+ m_mousePressed(false),
+ m_state(StateNormal),
+ m_axisX(0),
+ m_axisZ(0),
+ m_speedModifier(20.0f)
+{
+ // Connect to the item selection signal from graph
+ connect(graph, &QAbstract3DGraph::selectedElementChanged, this,
+ &CustomInputHandler::handleElementSelected);
+}
+
+void CustomInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ if (Qt::LeftButton == event->button()) {
+ m_highlight->setVisible(false);
+ m_mousePressed = true;
+ }
+ Q3DInputHandler::mousePressEvent(event, mousePos);
+}
+
+//! [1]
+void CustomInputHandler::wheelEvent(QWheelEvent *event)
+{
+ float delta = float(event->delta());
+
+ m_axisXMinValue += delta;
+ m_axisXMaxValue -= delta;
+ m_axisZMinValue += delta;
+ m_axisZMaxValue -= delta;
+ checkConstraints();
+
+ float y = (m_axisXMaxValue - m_axisXMinValue) * m_aspectRatio;
+
+ m_axisX->setRange(m_axisXMinValue, m_axisXMaxValue);
+ m_axisY->setRange(100.0f, y);
+ m_axisZ->setRange(m_axisZMinValue, m_axisZMaxValue);
+}
+//! [1]
+
+void CustomInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ // Check if we're trying to drag axis label
+ if (m_mousePressed && m_state != StateNormal) {
+ setPreviousInputPos(inputPosition());
+ setInputPosition(mousePos);
+ handleAxisDragging();
+ } else {
+ Q3DInputHandler::mouseMoveEvent(event, mousePos);
+ }
+}
+
+void CustomInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ Q3DInputHandler::mouseReleaseEvent(event, mousePos);
+ m_mousePressed = false;
+ m_state = StateNormal;
+}
+
+void CustomInputHandler::handleElementSelected(QAbstract3DGraph::ElementType type)
+{
+ switch (type) {
+ case QAbstract3DGraph::ElementAxisXLabel:
+ m_state = StateDraggingX;
+ break;
+ case QAbstract3DGraph::ElementAxisZLabel:
+ m_state = StateDraggingZ;
+ break;
+ default:
+ m_state = StateNormal;
+ break;
+ }
+}
+
+void CustomInputHandler::handleAxisDragging()
+{
+ float distance = 0.0f;
+
+ // Get scene orientation from active camera
+ float xRotation = scene()->activeCamera()->xRotation();
+
+ // Calculate directional drag multipliers based on rotation
+ float xMulX = qCos(qDegreesToRadians(xRotation));
+ float xMulY = qSin(qDegreesToRadians(xRotation));
+ float zMulX = qSin(qDegreesToRadians(xRotation));
+ float zMulY = qCos(qDegreesToRadians(xRotation));
+
+ // Get the drag amount
+ QPoint move = inputPosition() - previousInputPos();
+
+ // Adjust axes
+ switch (m_state) {
+//! [0]
+ case StateDraggingX:
+ distance = (move.x() * xMulX - move.y() * xMulY) * m_speedModifier;
+ m_axisXMinValue -= distance;
+ m_axisXMaxValue -= distance;
+ if (m_axisXMinValue < m_areaMinValue) {
+ float dist = m_axisXMaxValue - m_axisXMinValue;
+ m_axisXMinValue = m_areaMinValue;
+ m_axisXMaxValue = m_axisXMinValue + dist;
+ }
+ if (m_axisXMaxValue > m_areaMaxValue) {
+ float dist = m_axisXMaxValue - m_axisXMinValue;
+ m_axisXMaxValue = m_areaMaxValue;
+ m_axisXMinValue = m_axisXMaxValue - dist;
+ }
+ m_axisX->setRange(m_axisXMinValue, m_axisXMaxValue);
+ break;
+//! [0]
+ case StateDraggingZ:
+ distance = (move.x() * zMulX + move.y() * zMulY) * m_speedModifier;
+ m_axisZMinValue += distance;
+ m_axisZMaxValue += distance;
+ if (m_axisZMinValue < m_areaMinValue) {
+ float dist = m_axisZMaxValue - m_axisZMinValue;
+ m_axisZMinValue = m_areaMinValue;
+ m_axisZMaxValue = m_axisZMinValue + dist;
+ }
+ if (m_axisZMaxValue > m_areaMaxValue) {
+ float dist = m_axisZMaxValue - m_axisZMinValue;
+ m_axisZMaxValue = m_areaMaxValue;
+ m_axisZMinValue = m_axisZMaxValue - dist;
+ }
+ m_axisZ->setRange(m_axisZMinValue, m_axisZMaxValue);
+ break;
+ default:
+ break;
+ }
+}
+
+void CustomInputHandler::checkConstraints()
+{
+//! [2]
+ if (m_axisXMinValue < m_areaMinValue)
+ m_axisXMinValue = m_areaMinValue;
+ if (m_axisXMaxValue > m_areaMaxValue)
+ m_axisXMaxValue = m_areaMaxValue;
+ // Don't allow too much zoom in
+ if ((m_axisXMaxValue - m_axisXMinValue) < m_axisXMinRange) {
+ float adjust = (m_axisXMinRange - (m_axisXMaxValue - m_axisXMinValue)) / 2.0f;
+ m_axisXMinValue -= adjust;
+ m_axisXMaxValue += adjust;
+ }
+//! [2]
+
+ if (m_axisZMinValue < m_areaMinValue)
+ m_axisZMinValue = m_areaMinValue;
+ if (m_axisZMaxValue > m_areaMaxValue)
+ m_axisZMaxValue = m_areaMaxValue;
+ // Don't allow too much zoom in
+ if ((m_axisZMaxValue - m_axisZMinValue) < m_axisZMinRange) {
+ float adjust = (m_axisZMinRange - (m_axisZMaxValue - m_axisZMinValue)) / 2.0f;
+ m_axisZMinValue -= adjust;
+ m_axisZMaxValue += adjust;
+ }
+}
diff --git a/examples/datavisualization/texturesurface/custominputhandler.h b/examples/datavisualization/texturesurface/custominputhandler.h
new file mode 100644
index 00000000..8bef990e
--- /dev/null
+++ b/examples/datavisualization/texturesurface/custominputhandler.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef CUSTOMINPUTHANDLER_H
+#define CUSTOMINPUTHANDLER_H
+
+#include <QtDataVisualization/Q3DInputHandler>
+#include <QtDataVisualization/QAbstract3DGraph>
+#include <QtDataVisualization/QValue3DAxis>
+#include "highlightseries.h"
+
+using namespace QtDataVisualization;
+
+class CustomInputHandler : public Q3DInputHandler
+{
+ Q_OBJECT
+
+ enum InputState {
+ StateNormal = 0,
+ StateDraggingX,
+ StateDraggingZ,
+ StateDraggingY
+ };
+
+public:
+ explicit CustomInputHandler(QAbstract3DGraph *graph, QObject *parent = 0);
+
+ inline void setLimits(float min, float max, float minRange) {
+ m_areaMinValue = min;
+ m_areaMaxValue = max;
+ m_axisXMinValue = m_areaMinValue;
+ m_axisXMaxValue = m_areaMaxValue;
+ m_axisZMinValue = m_areaMinValue;
+ m_axisZMaxValue = m_areaMaxValue;
+ m_axisXMinRange = minRange;
+ m_axisZMinRange = minRange;
+ }
+ inline void setAxes(QValue3DAxis *axisX, QValue3DAxis *axisY, QValue3DAxis *axisZ) {
+ m_axisX = axisX;
+ m_axisY = axisY;
+ m_axisZ = axisZ;
+ }
+ inline void setAspectRatio(float ratio) { m_aspectRatio = ratio; }
+ inline void setHighlightSeries(HighlightSeries *series) { m_highlight = series; }
+ inline void setDragSpeedModifier(float modifier) { m_speedModifier = modifier; }
+
+ virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos);
+ virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos);
+ virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos);
+ virtual void wheelEvent(QWheelEvent *event);
+
+private:
+ void handleElementSelected(QAbstract3DGraph::ElementType type);
+ void handleAxisDragging();
+ void checkConstraints();
+
+private:
+ HighlightSeries *m_highlight;
+ bool m_mousePressed;
+ InputState m_state;
+ QValue3DAxis *m_axisX;
+ QValue3DAxis *m_axisY;
+ QValue3DAxis *m_axisZ;
+ float m_speedModifier;
+ float m_aspectRatio;
+ float m_axisXMinValue;
+ float m_axisXMaxValue;
+ float m_axisXMinRange;
+ float m_axisZMinValue;
+ float m_axisZMaxValue;
+ float m_axisZMinRange;
+ float m_areaMinValue;
+ float m_areaMaxValue;
+};
+
+#endif
diff --git a/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png b/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png
new file mode 100644
index 00000000..76819607
--- /dev/null
+++ b/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png
Binary files differ
diff --git a/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc
new file mode 100644
index 00000000..483b8110
--- /dev/null
+++ b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+/*!
+ \example texturesurface
+ \title Textured Surface Example
+ \ingroup qtdatavisualization_examples
+ \brief Using texture with Q3DSurface.
+ \since QtDataVisualization 1.2
+
+ The textured surface example shows how to add an image as a texture for a surface. The example
+ shows also how to:
+
+ \list
+ \li Create a surface series from an image
+ \li Use custom input handler to enable zooming and panning
+ \li Highlight an area of the surface
+ \endlist
+
+ \image texturesurface-example.png
+
+ \section1 Texture to a surface series
+
+ The image to be set as a texture to a surface can be set using QSurface3DSeries::setTextureFile().
+ In this example we have added a check box to control if the texture is set or not. The
+ following code extract is for reacting to the check box selections. The image in this
+ example is read from the resource file where it is as a JPG file. Setting an empty file
+ with the method clears the texture, and the surface uses the gradients or colors from the theme.
+
+ \snippet texturesurface/surfacegraph.cpp 0
+
+ \section1 Topographic surface series
+
+ The topographic data for this example is obtained from National Land Survey of Finland. It
+ provides a product called \c{Elevation Model 2 m}, which was suitable for our needs. We selected
+ Levi fell to be shown. The accuracy of the data was well beyond our needs and therefore it
+ is compressed and encoded into a PNG file. The height value from the original ASCII data is
+ encoded into RGB format using a multiplier, which you will see later on a code extract.
+ The multiplier is calculated simply by dividing the largest 24 bit value with the highest point
+ in Finland.
+
+ Qt Data Visualization has a special proxy for height map image files, but it converts
+ only one byte values. So to utilize the bigger accuracy on the data from National Land
+ Survey of Finland, we read the data from the PNG file and decode it into QSurface3DSeries.
+ The following code samples show how this is done.
+
+ First the encoding multiplier.
+ \snippet texturesurface/topographicseries.cpp 0
+
+ And then the actual decoding.
+ \snippet texturesurface/topographicseries.cpp 1
+
+ \section1 Use custom input handler to enable zooming and panning
+
+ For the panning the implementation is similar to the \l{Axis Range Dragging With Labels Example}.
+ The difference is that in this example we follow only dragging of X and Z axis and we don't
+ allow dragging the surface outside the graph. The control for this is very simple and done as
+ on the following example for the X axis.
+
+ \snippet texturesurface/custominputhandler.cpp 0
+
+ For the zooming we catch the \c wheelEvent and adjust the X and Y axis ranges according to delta
+ value on QWheelEvent. The Y axis is also adjusted so that the aspect ratio between Y axis and
+ XZ plane stays the same, and we don't get silly looking graph with height exaggerated too much.
+
+ \snippet texturesurface/custominputhandler.cpp 1
+
+ In this case we want to control the zoom level so that it won't get too near to or far from the
+ surface. For instance, if the value for the X axis gets below the allowed, i.e. zooming gets too
+ far, the value is set to the minimum allowed value. If the range is going to below the range
+ minimum, both ends of the axis are adjusted so that the range stays at the limit.
+
+ \snippet texturesurface/custominputhandler.cpp 2
+
+ \section1 Highlight an area of the surface
+
+ The main idea on creating a highlight on the surface is to create a copy of the series and add
+ a bit of offset to the y value. On this example the class \c HighlightSeries implements the
+ creation of the copy on its \c handlePositionChange method. Firstly the \c HighlightSeries
+ needs to get the pointer to the original series and then it starts to listen the
+ QSurface3DSeries::selectedPointChanged signal.
+
+ \snippet texturesurface/highlightseries.cpp 0
+
+ When the signal arrives, first thing is to check that the position is valid. Then the ranges
+ for the copied area are calculated and checked that they stay within the bounds. Finally
+ we simply fill the data array of the highlight series with the range from the data array of
+ topography series.
+
+ \snippet texturesurface/highlightseries.cpp 1
+
+ \section1 A gradient to the highlight series
+
+ Since the \c HighlightSeries is QSurface3DSeries, we can use all the decoration methods series can
+ have. In this example we added a gradient to emphasize the elevation. Because the suitable gradient
+ style depends on the range of the Y axis and we change the range when zooming, we need to adjust
+ the gradient color positions as the range change.
+
+ For the gradient color positions we define proportional values.
+
+ \snippet texturesurface/highlightseries.cpp 2
+
+ The gradient modification is done on \c handleGradientChange method and we connect it to react to
+ changes on Y axis.
+
+ \snippet texturesurface/surfacegraph.cpp 1
+
+ When a change on Y axis max value happens, we calculate the gradient color positions.
+
+ \snippet texturesurface/highlightseries.cpp 3
+*/
diff --git a/examples/datavisualization/texturesurface/highlightseries.cpp b/examples/datavisualization/texturesurface/highlightseries.cpp
new file mode 100644
index 00000000..13d1fba3
--- /dev/null
+++ b/examples/datavisualization/texturesurface/highlightseries.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "highlightseries.h"
+
+using namespace QtDataVisualization;
+
+//! [2]
+const float darkRedPos = 1.0f;
+const float redPos = 0.8f;
+const float yellowPos = 0.6f;
+const float greenPos = 0.4f;
+const float darkGreenPos = 0.2f;
+//! [2]
+
+HighlightSeries::HighlightSeries()
+ : m_width(100),
+ m_height(100)
+{
+ setDrawMode(QSurface3DSeries::DrawSurface);
+ setFlatShadingEnabled(true);
+ setVisible(false);
+}
+
+HighlightSeries::~HighlightSeries()
+{
+}
+
+//! [0]
+void HighlightSeries::setTopographicSeries(TopographicSeries *series)
+{
+ m_topographicSeries = series;
+ m_srcWidth = m_topographicSeries->dataProxy()->array()->at(0)->size();
+ m_srcHeight = m_topographicSeries->dataProxy()->array()->size();
+
+ QObject::connect(m_topographicSeries, &QSurface3DSeries::selectedPointChanged,
+ this, &HighlightSeries::handlePositionChange);
+}
+//! [0]
+
+//! [1]
+void HighlightSeries::handlePositionChange(const QPoint &position)
+{
+ m_position = position;
+
+ if (position == invalidSelectionPosition()) {
+ setVisible(false);
+
+ return;
+ }
+
+ int halfWidth = m_width / 2;
+ int halfHeight = m_height / 2;
+
+ int startX = position.y() - halfWidth;
+ if (startX < 0 )
+ startX = 0;
+ int endX = position.y() + halfWidth;
+ if (endX > (m_srcWidth - 1))
+ endX = m_srcWidth - 1;
+ int startZ = position.x() - halfHeight;
+ if (startZ < 0 )
+ startZ = 0;
+ int endZ = position.x() + halfHeight;
+ if (endZ > (m_srcHeight - 1))
+ endZ = m_srcHeight - 1;
+
+ QSurfaceDataProxy *srcProxy = m_topographicSeries->dataProxy();
+ const QSurfaceDataArray &srcArray = *srcProxy->array();
+
+ QSurfaceDataArray *dataArray = new QSurfaceDataArray;
+ dataArray->reserve(endZ - startZ);
+ for (int i = startZ; i < endZ; i++) {
+ QSurfaceDataRow *newRow = new QSurfaceDataRow(endX - startX);
+ QSurfaceDataRow *srcRow = srcArray.at(i);
+ for (int j = startX, p = 0; j < endX; j++, p++) {
+ QVector3D pos = srcRow->at(j).position();
+ (*newRow)[p].setPosition(QVector3D(pos.x(), pos.y() + 0.1f, pos.z()));
+ }
+ *dataArray << newRow;
+ }
+
+ dataProxy()->resetArray(dataArray);
+ setVisible(true);
+}
+//! [1]
+
+//! [3]
+void HighlightSeries::handleGradientChange(float value)
+{
+ float ratio = m_minHeight / value;
+
+ QLinearGradient gr;
+ gr.setColorAt(0.0f, Qt::black);
+ gr.setColorAt(darkGreenPos * ratio, Qt::darkGreen);
+ gr.setColorAt(greenPos * ratio, Qt::green);
+ gr.setColorAt(yellowPos * ratio, Qt::yellow);
+ gr.setColorAt(redPos * ratio, Qt::red);
+ gr.setColorAt(darkRedPos * ratio, Qt::darkRed);
+
+ setBaseGradient(gr);
+ setColorStyle(Q3DTheme::ColorStyleRangeGradient);
+}
+//! [3]
diff --git a/examples/datavisualization/texturesurface/highlightseries.h b/examples/datavisualization/texturesurface/highlightseries.h
new file mode 100644
index 00000000..aa1590e5
--- /dev/null
+++ b/examples/datavisualization/texturesurface/highlightseries.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef HIGHLIGHTSERIES_H
+#define HIGHLIGHTSERIES_H
+
+#include <QtDataVisualization/QSurface3DSeries>
+
+#include "topographicseries.h"
+
+using namespace QtDataVisualization;
+
+class HighlightSeries : public QSurface3DSeries
+{
+ Q_OBJECT
+public:
+ explicit HighlightSeries();
+ ~HighlightSeries();
+
+ void setTopographicSeries(TopographicSeries *series);
+ inline void setMinHeight(float height) { m_minHeight = height; }
+
+public slots:
+ void handlePositionChange(const QPoint &position);
+ void handleGradientChange(float value);
+
+private:
+ int m_width;
+ int m_height;
+ int m_srcWidth;
+ int m_srcHeight;
+ QPoint m_position;
+ TopographicSeries *m_topographicSeries;
+ float m_minHeight;
+};
+
+#endif // HIGHLIGHTSERIES_H
diff --git a/examples/datavisualization/texturesurface/license.txt b/examples/datavisualization/texturesurface/license.txt
new file mode 100644
index 00000000..749daf31
--- /dev/null
+++ b/examples/datavisualization/texturesurface/license.txt
@@ -0,0 +1,77 @@
+License information regarding the data obtained from National Land Survey of
+Finland http://www.maanmittauslaitos.fi/en
+- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
+ U4422C) 08/2014
+- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
+
+National Land Survey open data licence - version 1.0 - 1 May 2012
+
+1. General information
+
+The National Land Survey of Finland (hereinafter the Licensor), as the holder
+of the immaterial rights to the data, has granted on the terms mentioned below
+the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
+of it).
+
+The Licensee is a natural or legal person who makes use of the data covered by
+this licence. The Licensee accepts the terms of this licence by receiving the
+dataset(s) covered by the licence.
+
+This Licence agreement does not create a co-operation or business relationship
+between the Licensee and the Licensor.
+
+2. Terms of the licence
+
+2.1. Right of use
+
+This licence grants a worldwide, free of charge and irrevocable parallel right
+of use to open data. According to the terms of the licence, data received by
+the Licensee can be freely:
+ - copied, distributed and published,
+ - modified and utilised commercially and non-commercially,
+ - inserted into other products and
+ - used as a part of a software application or service.
+
+2.2. Duties and responsibilities of the Licensee
+
+Through reasonable means suitable to the distribution medium or method which is
+used in conjunction with a product containing data or a service utilising data
+covered by this licence or while distributing data, the Licensee shall:
+ - mention the name of the Licensor, the name of the dataset(s) and the time
+ when the National Land Survey has delivered the dataset(s) (e.g.: contains
+ data from the National Land Survey of Finland Topographic Database 06/2012)
+ - provide a copy of this licence or a link to it, as well as
+ - require third parties to provide the same information when granting rights
+ to copies of dataset(s) or products and services containing such data and
+ - remove the name of the Licensor from the product or service, if required to
+ do so by the Licensor.
+
+The terms of this licence do not allow the Licensee to state in conjunction
+with the use of dataset(s) that the Licensor supports or recommends such use.
+
+2.3. Duties and responsibilities of the Licensor
+
+The Licensor shall ensure that
+ - the Licensor has the right to grant rights to the dataset(s) in accordance
+ with this licence.
+
+The data has been licensed "as is" and the Licensor
+ - shall not be held responsible for any errors or omissions in the data,
+ disclaims any warranty for the validity or up to date status of the data and
+ shall be free from liability for direct or consequential damages arising
+ from the use of data provided by the Licensor,
+ - and is not obligated to ensure the continuous availability of the data, nor
+ to announce in advance the interruption or cessation of availability, and
+ the Licensor shall be free from liability for direct or consequential
+ damages arising from any such interruption or cessation.
+
+3. Jurisdiction
+
+Finnish law shall apply to this licence.
+
+4. Changes to this licence
+
+The Licensor may at any time change the terms of the licence or apply a
+different licence to the data. The terms of this licence shall, however, still
+apply to such data that has been received prior to the change of the terms of
+the licence or the licence itself.
diff --git a/examples/datavisualization/texturesurface/main.cpp b/examples/datavisualization/texturesurface/main.cpp
new file mode 100644
index 00000000..ed1a9be4
--- /dev/null
+++ b/examples/datavisualization/texturesurface/main.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "surfacegraph.h"
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QGroupBox>
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QLabel>
+#include <QtGui/QScreen>
+#include <QtGui/QPainter>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DSurface *graph = new Q3DSurface();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ QSize screenSize = graph->screen()->size();
+ container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.6));
+ container->setMaximumSize(screenSize);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget *widget = new QWidget;
+ QHBoxLayout *hLayout = new QHBoxLayout(widget);
+ QVBoxLayout *vLayout = new QVBoxLayout();
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayout);
+ vLayout->setAlignment(Qt::AlignTop);
+
+ widget->setWindowTitle(QStringLiteral("Textured surface example"));
+
+ QCheckBox *enableTexture = new QCheckBox(widget);
+ enableTexture->setText(QStringLiteral("Surface texture"));
+
+ int height = 400;
+ int width = 100;
+ int border = 10;
+ QLinearGradient gr(0, 0, 1, height - 2 * border);
+ gr.setColorAt(1.0f, Qt::black);
+ gr.setColorAt(0.8f, Qt::darkGreen);
+ gr.setColorAt(0.6f, Qt::green);
+ gr.setColorAt(0.4f, Qt::yellow);
+ gr.setColorAt(0.2f, Qt::red);
+ gr.setColorAt(0.0f, Qt::darkRed);
+
+ QPixmap pm(width, height);
+ pm.fill(Qt::transparent);
+ QPainter pmp(&pm);
+ pmp.setBrush(QBrush(gr));
+ pmp.setPen(Qt::NoPen);
+ pmp.drawRect(border, border, 35, height - 2 * border);
+ pmp.setPen(Qt::black);
+ int step = (height - 2 * border) / 5;
+ for (int i = 0; i < 6; i++) {
+ int yPos = i * step + border;
+ pmp.drawLine(border, yPos, 55, yPos);
+ pmp.drawText(60, yPos + 2, QString("%1 m").arg(550 - (i * 110)));
+ }
+
+ QLabel *label = new QLabel(widget);
+ label->setPixmap(pm);
+
+ QGroupBox *heightMapGroupBox = new QGroupBox(QStringLiteral("Height color map"));
+ QVBoxLayout *colorMapVBox = new QVBoxLayout;
+ colorMapVBox->addWidget(label);
+ heightMapGroupBox->setLayout(colorMapVBox);
+
+ vLayout->addWidget(enableTexture);
+ vLayout->addWidget(heightMapGroupBox);
+
+ widget->show();
+
+ SurfaceGraph *modifier = new SurfaceGraph(graph);
+
+ QObject::connect(enableTexture, &QCheckBox::stateChanged,
+ modifier, &SurfaceGraph::toggleSurfaceTexture);
+
+ enableTexture->setChecked(true);
+
+ return app.exec();
+}
diff --git a/examples/datavisualization/texturesurface/maptexture.jpg b/examples/datavisualization/texturesurface/maptexture.jpg
new file mode 100644
index 00000000..ae5d66eb
--- /dev/null
+++ b/examples/datavisualization/texturesurface/maptexture.jpg
Binary files differ
diff --git a/examples/datavisualization/texturesurface/surfacegraph.cpp b/examples/datavisualization/texturesurface/surfacegraph.cpp
new file mode 100644
index 00000000..e01a329d
--- /dev/null
+++ b/examples/datavisualization/texturesurface/surfacegraph.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "surfacegraph.h"
+#include "topographicseries.h"
+
+#include <QtDataVisualization/QValue3DAxis>
+#include <QtDataVisualization/Q3DTheme>
+
+using namespace QtDataVisualization;
+
+const float areaWidth = 8000.0f;
+const float areaHeight = 8000.0f;
+const float aspectRatio = 0.1389f;
+const float minRange = areaWidth * 0.49f;
+
+SurfaceGraph::SurfaceGraph(Q3DSurface *surface)
+ : m_graph(surface)
+{
+ m_graph->setAxisX(new QValue3DAxis);
+ m_graph->setAxisY(new QValue3DAxis);
+ m_graph->setAxisZ(new QValue3DAxis);
+ m_graph->axisX()->setLabelFormat("%i");
+ m_graph->axisZ()->setLabelFormat("%i");
+ m_graph->axisX()->setRange(0.0f, areaWidth);
+ m_graph->axisY()->setRange(100.0f, areaWidth * aspectRatio);
+ m_graph->axisZ()->setRange(0.0f, areaHeight);
+ m_graph->axisX()->setLabelAutoRotation(30);
+ m_graph->axisY()->setLabelAutoRotation(90);
+ m_graph->axisZ()->setLabelAutoRotation(30);
+ m_graph->activeTheme()->setType(Q3DTheme::ThemePrimaryColors);
+
+ QFont font = m_graph->activeTheme()->font();
+ font.setPointSize(20);
+ m_graph->activeTheme()->setFont(font);
+
+ m_topography = new TopographicSeries();
+ m_topography->setTopographyFile(":/maps/topography", areaWidth, areaHeight);
+ m_topography->setItemLabelFormat(QStringLiteral("@yLabel m"));
+
+ m_highlight = new HighlightSeries();
+ m_highlight->setTopographicSeries(m_topography);
+ m_highlight->setMinHeight(minRange * aspectRatio);
+ m_highlight->handleGradientChange(areaWidth * aspectRatio);
+//! [1]
+ QObject::connect(m_graph->axisY(), &QValue3DAxis::maxChanged,
+ m_highlight, &HighlightSeries::handleGradientChange);
+//! [1]
+
+ m_graph->addSeries(m_topography);
+ m_graph->addSeries(m_highlight);
+
+ m_inputHandler = new CustomInputHandler(m_graph);
+ m_inputHandler->setHighlightSeries(m_highlight);
+ m_inputHandler->setAxes(m_graph->axisX(), m_graph->axisY(), m_graph->axisZ());
+ m_inputHandler->setLimits(0.0f, areaWidth, minRange);
+ m_inputHandler->setAspectRatio(aspectRatio);
+
+ m_graph->setActiveInputHandler(m_inputHandler);
+}
+
+SurfaceGraph::~SurfaceGraph()
+{
+ delete m_graph;
+}
+
+//! [0]
+void SurfaceGraph::toggleSurfaceTexture(bool enable)
+{
+ if (enable)
+ m_topography->setTextureFile(":/maps/maptexture");
+ else
+ m_topography->setTextureFile("");
+}
+//! [0]
diff --git a/examples/datavisualization/texturesurface/surfacegraph.h b/examples/datavisualization/texturesurface/surfacegraph.h
new file mode 100644
index 00000000..c1d81595
--- /dev/null
+++ b/examples/datavisualization/texturesurface/surfacegraph.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef SURFACEGRAPH_H
+#define SURFACEGRAPH_H
+
+#include <QtDataVisualization/Q3DSurface>
+#include <QtDataVisualization/QSurface3DSeries>
+#include <QtWidgets/QSlider>
+#include "topographicseries.h"
+#include "highlightseries.h"
+
+#include "custominputhandler.h"
+
+using namespace QtDataVisualization;
+
+class SurfaceGraph : public QObject
+{
+ Q_OBJECT
+public:
+ explicit SurfaceGraph(Q3DSurface *surface);
+ ~SurfaceGraph();
+
+ void toggleSurfaceTexture(bool enable);
+
+private:
+ Q3DSurface *m_graph;
+
+ TopographicSeries *m_topography;
+ HighlightSeries *m_highlight;
+ int m_highlightWidth;
+ int m_highlightHeight;
+
+ CustomInputHandler *m_inputHandler;
+};
+
+#endif // SURFACEGRAPH_H
diff --git a/examples/datavisualization/texturesurface/texturedsurface.qrc b/examples/datavisualization/texturesurface/texturedsurface.qrc
new file mode 100644
index 00000000..94b96d24
--- /dev/null
+++ b/examples/datavisualization/texturesurface/texturedsurface.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/maps">
+ <file alias="topography">topography.png</file>
+ <file alias="maptexture">maptexture.jpg</file>
+ </qresource>
+</RCC>
diff --git a/examples/datavisualization/texturesurface/texturesurface.pro b/examples/datavisualization/texturesurface/texturesurface.pro
new file mode 100644
index 00000000..f24c3d17
--- /dev/null
+++ b/examples/datavisualization/texturesurface/texturesurface.pro
@@ -0,0 +1,25 @@
+android|ios {
+ error( "This example is not supported for android or ios." )
+}
+
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+SOURCES += main.cpp \
+ surfacegraph.cpp \
+ topographicseries.cpp \
+ highlightseries.cpp \
+ custominputhandler.cpp
+
+HEADERS += surfacegraph.h \
+ topographicseries.h \
+ highlightseries.h \
+ custominputhandler.h
+
+QT += widgets
+
+RESOURCES += texturedsurface.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
diff --git a/examples/datavisualization/texturesurface/topographicseries.cpp b/examples/datavisualization/texturesurface/topographicseries.cpp
new file mode 100644
index 00000000..530e56b4
--- /dev/null
+++ b/examples/datavisualization/texturesurface/topographicseries.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "topographicseries.h"
+
+using namespace QtDataVisualization;
+
+//! [0]
+// Value used to encode height data as RGB value on PNG file
+const float packingFactor = 11983.0f;
+//! [0]
+
+TopographicSeries::TopographicSeries()
+{
+ setDrawMode(QSurface3DSeries::DrawSurface);
+ setFlatShadingEnabled(true);
+}
+
+TopographicSeries::~TopographicSeries()
+{
+}
+
+void TopographicSeries::setTopographyFile(const QString file, float width, float height)
+{
+//! [1]
+ QImage heightMapImage(file);
+ uchar *bits = heightMapImage.bits();
+ int imageHeight = heightMapImage.height();
+ int imageWidth = heightMapImage.width();
+ int widthBits = imageWidth * 4;
+ float stepX = width / float(imageWidth);
+ float stepZ = height / float(imageHeight);
+
+ QSurfaceDataArray *dataArray = new QSurfaceDataArray;
+ dataArray->reserve(imageHeight);
+ for (int i = 0; i < imageHeight; i++) {
+ int p = i * widthBits;
+ float z = height - float(i) * stepZ;
+ QSurfaceDataRow *newRow = new QSurfaceDataRow(imageWidth);
+ for (int j = 0; j < imageWidth; j++) {
+ uchar aa = bits[p + 0];
+ uchar rr = bits[p + 1];
+ uchar gg = bits[p + 2];
+ uint color = uint((gg << 16) + (rr << 8) + aa);
+ float y = float(color) / packingFactor;
+ (*newRow)[j].setPosition(QVector3D(float(j) * stepX, y, z));
+ p = p + 4;
+ }
+ *dataArray << newRow;
+ }
+
+ dataProxy()->resetArray(dataArray);
+//! [1]
+
+ m_sampleCountX = float(imageWidth);
+ m_sampleCountZ = float(imageHeight);
+}
diff --git a/examples/datavisualization/texturesurface/topographicseries.h b/examples/datavisualization/texturesurface/topographicseries.h
new file mode 100644
index 00000000..06530c63
--- /dev/null
+++ b/examples/datavisualization/texturesurface/topographicseries.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef TOPOGRAPHICSERIES_H
+#define TOPOGRAPHICSERIES_H
+
+#include <QtDataVisualization/QSurface3DSeries>
+
+using namespace QtDataVisualization;
+
+class TopographicSeries : public QSurface3DSeries
+{
+ Q_OBJECT
+public:
+ explicit TopographicSeries();
+ ~TopographicSeries();
+
+ void setTopographyFile(const QString file, float width, float height);
+
+ float sampleCountX() { return m_sampleCountX; }
+ float sampleCountZ() { return m_sampleCountZ; }
+
+public slots:
+
+private:
+ float m_sampleCountX;
+ float m_sampleCountZ;
+};
+
+#endif // TOPOGRAPHICSERIES_H
diff --git a/examples/datavisualization/texturesurface/topography.png b/examples/datavisualization/texturesurface/topography.png
new file mode 100644
index 00000000..9349cdb3
--- /dev/null
+++ b/examples/datavisualization/texturesurface/topography.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/doc/images/volumetric-example.png b/examples/datavisualization/volumetric/doc/images/volumetric-example.png
new file mode 100644
index 00000000..277d4fe4
--- /dev/null
+++ b/examples/datavisualization/volumetric/doc/images/volumetric-example.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/doc/src/volumetric.qdoc b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc
new file mode 100644
index 00000000..39616670
--- /dev/null
+++ b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+/*!
+ \example volumetric
+ \title Volumetric rendering Example
+ \ingroup qtdatavisualization_examples
+ \brief Rendering volumetric objects.
+ \since QtDataVisualization 1.2
+
+ This example shows how to use QCustom3DVolume items to display volumetric data.
+
+ \image volumetric-example.png
+
+ \section1 Initializing volume item
+
+ The QCustom3DVolume items are special custom items (see QCustom3DItem), which can be used
+ to display volumetric data. The volume items are only supported with orthographic projection,
+ so first we make sure the graph is using it:
+
+ \snippet volumetric/volumetric.cpp 6
+
+ The following code shows how to create a volumetric item tied to the data ranges of the axes:
+
+ \snippet volumetric/volumetric.cpp 0
+
+ By setting the QCustom3DItem::scalingAbsolute property to \c{false}, we indicate that the
+ scaling of the volume should follow the changes in the data ranges. Next we define the
+ internal contents of the volume:
+
+ \snippet volumetric/volumetric.cpp 1
+
+ We use eight bit indexed color for our texture, as it is compact and makes it easy to adjust the
+ colors without needing to reset the whole texture. For the texture data we use the data we
+ created earlier based on some height maps.
+ Typically the data for volume items comes pregenerated in a form of a stack of images, so we are
+ not going to explain the data generation in detail. Please refer to the example code if you
+ are interested in the actual data generation process.
+
+ Since we are using eight bit indexed colors, we need a color table to map the eight bit color
+ indexes to actual colors. We use one we populated on our own, but in a typical use case you
+ would get the color table from the source images:
+
+ \snippet volumetric/volumetric.cpp 2
+
+ We want to optionally show slice frames around the volume, so we initialize their properties.
+ Initially, the frames will be hidden:
+
+ \snippet volumetric/volumetric.cpp 5
+
+ Finally we add the volume as a custom item to the graph to display it:
+
+ \snippet volumetric/volumetric.cpp 3
+
+ \section1 Slicing into the volume
+
+ Unless the volume is largely transparent, you can only see the surface of it, which is often
+ not very helpful. One way to inspect the internal structure of the volume is to view the slices
+ of the volume. QCustom3DVolume provides two ways to display the slices. The first is to show
+ the selected slices in place of the volume. For example, to specify a slice perpendicular to
+ the X-axis, you can use the following method:
+
+ \snippet volumetric/volumetric.cpp 7
+
+ To actually draw the slice specified above, the QCustom3DVolume::drawSlices property must be
+ also set:
+
+ \snippet volumetric/volumetric.cpp 8
+
+ The second way to view slices is to use QCustom3DVolume::renderSlice() method, which produces
+ a QImage from the specified slice. This image can then be displayed on another widget, such
+ as a QLabel here:
+
+ \snippet volumetric/volumetric.cpp 9
+
+ \section1 Adjusting volume transparency
+
+ Sometimes viewing just the slices doesn't give you a good understanding of the volume's internal
+ structure. QCustom3DVolume provides two properties that can be used to adjust the volume
+ transparency:
+
+ \snippet volumetric/volumetric.cpp 11
+ \dots
+ \snippet volumetric/volumetric.cpp 10
+
+ The QCustom3DVolume::alphaMultiplier is a general multiplier that is applied to the alpha value
+ of each voxel of the volume. It makes it possible to add uniform transparency to the already
+ somewhat transparent portions of the volume to reveal internal opaque details. This multiplier
+ doesn't affect colors that are fully opaque, unless the QCustom3DVolume::preserveOpacity
+ property is set to \c{false}.
+
+ An alternative way to adjust the transparency of the volume is adjust the alpha values of the
+ voxels directly. For eight bit indexed textures, this is done simply by modifying and
+ resetting the color table:
+
+ \snippet volumetric/volumetric.cpp 12
+
+ \section1 High definition vs. low definition shader
+
+ By default the volume rendering uses the high definition shader. It accounts for each
+ voxel of the volume with correct weight when ray-tracing the volume contents,
+ providing an accurate representation of even the finer details of the volume.
+ However, this is computationally very expensive, so the frame rate suffers.
+ If rendering speed is more important than pixel-perfect
+ accuracy of the volume contents, you can take the much faster low definition shader into use
+ by setting \c{false} for QCustom3DVolume::useHighDefShader property. The low definition shader
+ achieves the speed by making compromises on the accuracy, so it doesn't guarantee each voxel
+ of the volume will be sampled. This can lead to flickering and/or other rendering artifacts
+ on the finer details of the volume.
+
+ \snippet volumetric/volumetric.cpp 13
+
+ \section1 Example contents
+*/
diff --git a/examples/datavisualization/volumetric/layer_ground.png b/examples/datavisualization/volumetric/layer_ground.png
new file mode 100644
index 00000000..3f96a122
--- /dev/null
+++ b/examples/datavisualization/volumetric/layer_ground.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/layer_magma.png b/examples/datavisualization/volumetric/layer_magma.png
new file mode 100644
index 00000000..01434d35
--- /dev/null
+++ b/examples/datavisualization/volumetric/layer_magma.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/layer_water.png b/examples/datavisualization/volumetric/layer_water.png
new file mode 100644
index 00000000..4d57563e
--- /dev/null
+++ b/examples/datavisualization/volumetric/layer_water.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp
new file mode 100644
index 00000000..faf379ec
--- /dev/null
+++ b/examples/datavisualization/volumetric/main.cpp
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "volumetric.h"
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QRadioButton>
+#include <QtWidgets/QSlider>
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QGroupBox>
+#include <QtGui/QScreen>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DScatter *graph = new Q3DScatter();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ QSize screenSize = graph->screen()->size();
+ container->setMinimumSize(QSize(screenSize.width() / 3, screenSize.height() / 3));
+ container->setMaximumSize(screenSize);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget *widget = new QWidget();
+ QHBoxLayout *hLayout = new QHBoxLayout(widget);
+ QVBoxLayout *vLayout = new QVBoxLayout();
+ QVBoxLayout *vLayout2 = new QVBoxLayout();
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayout);
+ hLayout->addLayout(vLayout2);
+
+ widget->setWindowTitle(QStringLiteral("Volumetric object example - 3D terrain"));
+
+ QCheckBox *sliceXCheckBox = new QCheckBox(widget);
+ sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis"));
+ sliceXCheckBox->setChecked(false);
+ QCheckBox *sliceYCheckBox = new QCheckBox(widget);
+ sliceYCheckBox->setText(QStringLiteral("Slice volume on Y axis"));
+ sliceYCheckBox->setChecked(false);
+ QCheckBox *sliceZCheckBox = new QCheckBox(widget);
+ sliceZCheckBox->setText(QStringLiteral("Slice volume on Z axis"));
+ sliceZCheckBox->setChecked(false);
+
+ QSlider *sliceXSlider = new QSlider(Qt::Horizontal, widget);
+ sliceXSlider->setMinimum(0);
+ sliceXSlider->setMaximum(1024);
+ sliceXSlider->setValue(512);
+ sliceXSlider->setEnabled(true);
+ QSlider *sliceYSlider = new QSlider(Qt::Horizontal, widget);
+ sliceYSlider->setMinimum(0);
+ sliceYSlider->setMaximum(1024);
+ sliceYSlider->setValue(512);
+ sliceYSlider->setEnabled(true);
+ QSlider *sliceZSlider = new QSlider(Qt::Horizontal, widget);
+ sliceZSlider->setMinimum(0);
+ sliceZSlider->setMaximum(1024);
+ sliceZSlider->setValue(512);
+ sliceZSlider->setEnabled(true);
+
+ QCheckBox *fpsCheckBox = new QCheckBox(widget);
+ fpsCheckBox->setText(QStringLiteral("Show FPS"));
+ fpsCheckBox->setChecked(false);
+ QLabel *fpsLabel = new QLabel(QStringLiteral(""), widget);
+
+ QGroupBox *textureDetailGroupBox = new QGroupBox(QStringLiteral("Texture detail"));
+
+ QRadioButton *lowDetailRB = new QRadioButton(widget);
+ lowDetailRB->setText(QStringLiteral("Low (128x64x128)"));
+ lowDetailRB->setChecked(true);
+
+ QRadioButton *mediumDetailRB = new QRadioButton(widget);
+ mediumDetailRB->setText(QStringLiteral("Generating..."));
+ mediumDetailRB->setChecked(false);
+ mediumDetailRB->setEnabled(false);
+
+ QRadioButton *highDetailRB = new QRadioButton(widget);
+ highDetailRB->setText(QStringLiteral("Generating..."));
+ highDetailRB->setChecked(false);
+ highDetailRB->setEnabled(false);
+
+ QVBoxLayout *textureDetailVBox = new QVBoxLayout;
+ textureDetailVBox->addWidget(lowDetailRB);
+ textureDetailVBox->addWidget(mediumDetailRB);
+ textureDetailVBox->addWidget(highDetailRB);
+ textureDetailGroupBox->setLayout(textureDetailVBox);
+
+ QGroupBox *areaGroupBox = new QGroupBox(QStringLiteral("Show area"));
+
+ QRadioButton *areaAllRB = new QRadioButton(widget);
+ areaAllRB->setText(QStringLiteral("Whole region"));
+ areaAllRB->setChecked(true);
+
+ QRadioButton *areaMineRB = new QRadioButton(widget);
+ areaMineRB->setText(QStringLiteral("The mine"));
+ areaMineRB->setChecked(false);
+
+ QRadioButton *areaMountainRB = new QRadioButton(widget);
+ areaMountainRB->setText(QStringLiteral("The mountain"));
+ areaMountainRB->setChecked(false);
+
+ QVBoxLayout *areaVBox = new QVBoxLayout;
+ areaVBox->addWidget(areaAllRB);
+ areaVBox->addWidget(areaMineRB);
+ areaVBox->addWidget(areaMountainRB);
+ areaGroupBox->setLayout(areaVBox);
+
+ QCheckBox *colorTableCheckBox = new QCheckBox(widget);
+ colorTableCheckBox->setText(QStringLiteral("Alternate color table"));
+ colorTableCheckBox->setChecked(false);
+
+ QLabel *sliceImageXLabel = new QLabel(widget);
+ QLabel *sliceImageYLabel = new QLabel(widget);
+ QLabel *sliceImageZLabel = new QLabel(widget);
+ sliceImageXLabel->setMinimumSize(QSize(200, 100));
+ sliceImageYLabel->setMinimumSize(QSize(200, 200));
+ sliceImageZLabel->setMinimumSize(QSize(200, 100));
+ sliceImageXLabel->setMaximumSize(QSize(200, 100));
+ sliceImageYLabel->setMaximumSize(QSize(200, 200));
+ sliceImageZLabel->setMaximumSize(QSize(200, 100));
+ sliceImageXLabel->setFrameShape(QFrame::Box);
+ sliceImageYLabel->setFrameShape(QFrame::Box);
+ sliceImageZLabel->setFrameShape(QFrame::Box);
+ sliceImageXLabel->setScaledContents(true);
+ sliceImageYLabel->setScaledContents(true);
+ sliceImageZLabel->setScaledContents(true);
+
+ QSlider *alphaMultiplierSlider = new QSlider(Qt::Horizontal, widget);
+ alphaMultiplierSlider->setMinimum(0);
+ alphaMultiplierSlider->setMaximum(139);
+ alphaMultiplierSlider->setValue(100);
+ alphaMultiplierSlider->setEnabled(true);
+ QLabel *alphaMultiplierLabel = new QLabel(QStringLiteral("Alpha multiplier: 1.0"));
+
+ QCheckBox *preserveOpacityCheckBox = new QCheckBox(widget);
+ preserveOpacityCheckBox->setText(QStringLiteral("Preserve opacity"));
+ preserveOpacityCheckBox->setChecked(true);
+
+ QCheckBox *transparentGroundCheckBox = new QCheckBox(widget);
+ transparentGroundCheckBox->setText(QStringLiteral("Transparent ground"));
+ transparentGroundCheckBox->setChecked(false);
+
+ QCheckBox *useHighDefShaderCheckBox = new QCheckBox(widget);
+ useHighDefShaderCheckBox->setText(QStringLiteral("Use HD shader"));
+ useHighDefShaderCheckBox->setChecked(true);
+
+ QLabel *performanceNoteLabel =
+ new QLabel(QStringLiteral(
+ "Note: A high end graphics card is\nrecommended with the HD shader\nwhen the volume contains a lot of\ntransparent areas."));
+ performanceNoteLabel->setFrameShape(QFrame::Box);
+
+ QCheckBox *drawSliceFramesCheckBox = new QCheckBox(widget);
+ drawSliceFramesCheckBox->setText(QStringLiteral("Draw slice frames"));
+ drawSliceFramesCheckBox->setChecked(false);
+
+ vLayout->addWidget(sliceXCheckBox);
+ vLayout->addWidget(sliceXSlider);
+ vLayout->addWidget(sliceImageXLabel);
+ vLayout->addWidget(sliceYCheckBox);
+ vLayout->addWidget(sliceYSlider);
+ vLayout->addWidget(sliceImageYLabel);
+ vLayout->addWidget(sliceZCheckBox);
+ vLayout->addWidget(sliceZSlider);
+ vLayout->addWidget(sliceImageZLabel);
+ vLayout->addWidget(drawSliceFramesCheckBox, 1, Qt::AlignTop);
+ vLayout2->addWidget(fpsCheckBox);
+ vLayout2->addWidget(fpsLabel);
+ vLayout2->addWidget(textureDetailGroupBox);
+ vLayout2->addWidget(areaGroupBox);
+ vLayout2->addWidget(colorTableCheckBox);
+ vLayout2->addWidget(alphaMultiplierLabel);
+ vLayout2->addWidget(alphaMultiplierSlider);
+ vLayout2->addWidget(preserveOpacityCheckBox);
+ vLayout2->addWidget(transparentGroundCheckBox);
+ vLayout2->addWidget(useHighDefShaderCheckBox);
+ vLayout2->addWidget(performanceNoteLabel, 1, Qt::AlignTop);
+
+ VolumetricModifier *modifier = new VolumetricModifier(graph);
+ modifier->setFpsLabel(fpsLabel);
+ modifier->setMediumDetailRB(mediumDetailRB);
+ modifier->setHighDetailRB(highDetailRB);
+ modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider);
+ modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel);
+ modifier->setAlphaMultiplierLabel(alphaMultiplierLabel);
+ modifier->setTransparentGround(transparentGroundCheckBox->isChecked());
+
+ QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setFpsMeasurement);
+ QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceX);
+ QObject::connect(sliceYCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceY);
+ QObject::connect(sliceZCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceZ);
+ QObject::connect(sliceXSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceX);
+ QObject::connect(sliceYSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceY);
+ QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceZ);
+ QObject::connect(lowDetailRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleLowDetail);
+ QObject::connect(mediumDetailRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleMediumDetail);
+ QObject::connect(highDetailRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleHighDetail);
+ QObject::connect(colorTableCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::changeColorTable);
+ QObject::connect(preserveOpacityCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setPreserveOpacity);
+ QObject::connect(transparentGroundCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setTransparentGround);
+ QObject::connect(useHighDefShaderCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setUseHighDefShader);
+ QObject::connect(alphaMultiplierSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustAlphaMultiplier);
+ QObject::connect(areaAllRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleAreaAll);
+ QObject::connect(areaMineRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleAreaMine);
+ QObject::connect(areaMountainRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleAreaMountain);
+ QObject::connect(drawSliceFramesCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setDrawSliceFrames);
+
+ widget->show();
+ return app.exec();
+}
diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp
new file mode 100644
index 00000000..20338598
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.cpp
@@ -0,0 +1,764 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "volumetric.h"
+#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/q3dtheme.h>
+#include <QtDataVisualization/qcustom3dlabel.h>
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtDataVisualization/q3dinputhandler.h>
+#include <QtCore/qmath.h>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QRadioButton>
+#include <QtWidgets/QSlider>
+#include <QtCore/QDebug>
+#include <QtGui/QOpenGLContext>
+
+using namespace QtDataVisualization;
+
+const int lowDetailSize(128);
+const int mediumDetailSize(256);
+const int highDetailSize(512);
+const int colorTableSize(256);
+const int layerDataSize(512);
+const int mineShaftDiameter(1);
+
+const int airColorIndex(254);
+const int mineShaftColorIndex(255);
+const int layerColorThickness(60);
+const int heightToColorDiv(140);
+const int magmaColorsMin(0);
+const int magmaColorsMax(layerColorThickness);
+const int aboveWaterGroundColorsMin(magmaColorsMax + 1);
+const int aboveWaterGroundColorsMax(aboveWaterGroundColorsMin + layerColorThickness);
+const int underWaterGroundColorsMin(aboveWaterGroundColorsMax + 1);
+const int underWaterGroundColorsMax(underWaterGroundColorsMin + layerColorThickness);
+const int waterColorsMin(underWaterGroundColorsMax + 1);
+const int waterColorsMax(waterColorsMin + layerColorThickness);
+const int terrainTransparency(12);
+
+static bool isOpenGLES()
+{
+#if defined(QT_OPENGL_ES_2)
+ return true;
+#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
+ return false;
+#else
+ return QOpenGLContext::currentContext()->isOpenGLES();
+#endif
+}
+
+VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
+ : m_graph(scatter),
+ m_volumeItem(0),
+ m_sliceIndexX(lowDetailSize / 2),
+ m_sliceIndexY(lowDetailSize / 4),
+ m_sliceIndexZ(lowDetailSize / 2),
+ m_slicingX(false),
+ m_slicingY(false),
+ m_slicingZ(false),
+ m_mediumDetailRB(0),
+ m_highDetailRB(0),
+ m_lowDetailData(0),
+ m_mediumDetailData(0),
+ m_highDetailData(0),
+ m_mediumDetailIndex(0),
+ m_highDetailIndex(0),
+ m_mediumDetailShaftIndex(0),
+ m_highDetailShaftIndex(0),
+ m_sliceSliderX(0),
+ m_sliceSliderY(0),
+ m_sliceSliderZ(0),
+ m_usingPrimaryTable(true),
+ m_sliceLabelX(0),
+ m_sliceLabelY(0),
+ m_sliceLabelZ(0)
+{
+ m_graph->activeTheme()->setType(Q3DTheme::ThemeQt);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
+ //! [6]
+ m_graph->setOrthoProjection(true);
+ //! [6]
+ m_graph->activeTheme()->setBackgroundEnabled(false);
+
+ // Only allow zooming at the center and limit the zoom to 200% to avoid clipping issues
+ static_cast<Q3DInputHandler *>(m_graph->activeInputHandler())->setZoomAtTargetEnabled(false);
+ m_graph->scene()->activeCamera()->setMaxZoomLevel(200.0f);
+
+ toggleAreaAll(true);
+
+ if (!isOpenGLES()) {
+ m_lowDetailData = new QVector<uchar>(lowDetailSize * lowDetailSize * lowDetailSize / 2);
+ m_mediumDetailData = new QVector<uchar>(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2);
+ m_highDetailData = new QVector<uchar>(highDetailSize * highDetailSize * highDetailSize / 2);
+
+ initHeightMap(QStringLiteral(":/heightmaps/layer_ground.png"), m_groundLayer);
+ initHeightMap(QStringLiteral(":/heightmaps/layer_water.png"), m_waterLayer);
+ initHeightMap(QStringLiteral(":/heightmaps/layer_magma.png"), m_magmaLayer);
+
+ initMineShaftArray();
+
+ createVolume(lowDetailSize, 0, lowDetailSize, m_lowDetailData);
+ excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData);
+
+ //! [0]
+ m_volumeItem = new QCustom3DVolume;
+ // Adjust water level to zero with a minor tweak to y-coordinate position and scaling
+ m_volumeItem->setScaling(
+ QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(),
+ (m_graph->axisY()->max() - m_graph->axisY()->min()) * 0.91f,
+ m_graph->axisZ()->max() - m_graph->axisZ()->min()));
+ m_volumeItem->setPosition(
+ QVector3D((m_graph->axisX()->max() + m_graph->axisX()->min()) / 2.0f,
+ -0.045f * (m_graph->axisY()->max() - m_graph->axisY()->min()) +
+ (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f,
+ (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f));
+ m_volumeItem->setScalingAbsolute(false);
+ //! [0]
+ //! [1]
+ m_volumeItem->setTextureWidth(lowDetailSize);
+ m_volumeItem->setTextureHeight(lowDetailSize / 2);
+ m_volumeItem->setTextureDepth(lowDetailSize);
+ m_volumeItem->setTextureFormat(QImage::Format_Indexed8);
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_lowDetailData));
+ //! [1]
+
+ // Generate color tables.
+ m_colorTable1.resize(colorTableSize);
+ m_colorTable2.resize(colorTableSize);
+
+ for (int i = 0; i < colorTableSize - 2; i++) {
+ if (i < magmaColorsMax) {
+ m_colorTable1[i] = qRgba(130 - (i * 2), 0, 0, 255);
+ } else if (i < aboveWaterGroundColorsMax) {
+ m_colorTable1[i] = qRgba((i - magmaColorsMax) * 4,
+ ((i - magmaColorsMax) * 2) + 120,
+ (i - magmaColorsMax) * 5, terrainTransparency);
+ } else if (i < underWaterGroundColorsMax) {
+ m_colorTable1[i] = qRgba(((layerColorThickness - i - aboveWaterGroundColorsMax)) + 70,
+ ((layerColorThickness - i - aboveWaterGroundColorsMax) * 2) + 20,
+ ((layerColorThickness - i - aboveWaterGroundColorsMax)) + 50,
+ terrainTransparency);
+ } else if (i < waterColorsMax) {
+ m_colorTable1[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120,
+ terrainTransparency);
+ } else {
+ m_colorTable1[i] = qRgba(0, 0, 0, 0); // Not used
+ }
+ }
+ m_colorTable1[airColorIndex] = qRgba(0, 0, 0, 0);
+ m_colorTable1[mineShaftColorIndex] = qRgba(50, 50, 50, 255);
+
+ // The alternate color table just has gray gradients for all terrain except water
+ for (int i = 0; i < colorTableSize - 2; i++) {
+ if (i < magmaColorsMax) {
+ m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2), 255);
+ } else if (i < underWaterGroundColorsMax) {
+ m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2), terrainTransparency);
+ } else if (i < waterColorsMax) {
+ m_colorTable2[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120,
+ terrainTransparency);
+ } else {
+ m_colorTable2[i] = qRgba(0, 0, 0, 0); // Not used
+ }
+ }
+ m_colorTable2[airColorIndex] = qRgba(0, 0, 0, 0);
+ m_colorTable2[mineShaftColorIndex] = qRgba(255, 255, 0, 255);
+
+ //! [2]
+ m_volumeItem->setColorTable(m_colorTable1);
+ //! [2]
+
+ //! [5]
+ m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f));
+ m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f));
+ m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f));
+ m_volumeItem->setDrawSliceFrames(false);
+ //! [5]
+ handleSlicingChanges();
+
+ //! [3]
+ m_graph->addCustomItem(m_volumeItem);
+ //! [3]
+
+ m_timer.start(0);
+ } else {
+ // OpenGL ES2 doesn't support 3D textures, so show a warning label instead
+ QCustom3DLabel *warningLabel = new QCustom3DLabel(
+ "QCustom3DVolume is not supported with OpenGL ES2",
+ QFont(),
+ QVector3D(0.0f, 0.5f, 0.0f),
+ QVector3D(1.5f, 1.5f, 0.0f),
+ QQuaternion());
+ warningLabel->setPositionAbsolute(true);
+ warningLabel->setFacingCamera(true);
+ m_graph->addCustomItem(warningLabel);
+ }
+
+ QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
+ &VolumetricModifier::handleFpsChange);
+ QObject::connect(&m_timer, &QTimer::timeout, this,
+ &VolumetricModifier::handleTimeout);
+
+}
+
+VolumetricModifier::~VolumetricModifier()
+{
+ delete m_graph;
+}
+
+void VolumetricModifier::setFpsLabel(QLabel *fpsLabel)
+{
+ m_fpsLabel = fpsLabel;
+}
+
+void VolumetricModifier::setMediumDetailRB(QRadioButton *button)
+{
+ m_mediumDetailRB = button;
+}
+
+void VolumetricModifier::setHighDetailRB(QRadioButton *button)
+{
+ m_highDetailRB = button;
+}
+
+void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel)
+{
+ m_sliceLabelX = xLabel;
+ m_sliceLabelY = yLabel;
+ m_sliceLabelZ = zLabel;
+
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+}
+
+void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label)
+{
+ m_alphaMultiplierLabel = label;
+}
+
+void VolumetricModifier::sliceX(int enabled)
+{
+ m_slicingX = enabled;
+ handleSlicingChanges();
+}
+
+void VolumetricModifier::sliceY(int enabled)
+{
+ m_slicingY = enabled;
+ handleSlicingChanges();
+}
+
+void VolumetricModifier::sliceZ(int enabled)
+{
+ m_slicingZ = enabled;
+ handleSlicingChanges();
+}
+
+void VolumetricModifier::adjustSliceX(int value)
+{
+ if (m_volumeItem) {
+ m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth());
+ if (m_sliceIndexX == m_volumeItem->textureWidth())
+ m_sliceIndexX--;
+ if (m_volumeItem->sliceIndexX() != -1)
+ //! [7]
+ m_volumeItem->setSliceIndexX(m_sliceIndexX);
+ //! [7]
+ //! [9]
+ m_sliceLabelX->setPixmap(
+ QPixmap::fromImage(m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX)));
+ //! [9]
+ }
+}
+
+void VolumetricModifier::adjustSliceY(int value)
+{
+ if (m_volumeItem) {
+ m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight());
+ if (m_sliceIndexY == m_volumeItem->textureHeight())
+ m_sliceIndexY--;
+ if (m_volumeItem->sliceIndexY() != -1)
+ m_volumeItem->setSliceIndexY(m_sliceIndexY);
+ m_sliceLabelY->setPixmap(
+ QPixmap::fromImage(m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY)));
+ }
+}
+
+void VolumetricModifier::adjustSliceZ(int value)
+{
+ if (m_volumeItem) {
+ m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth());
+ if (m_sliceIndexZ == m_volumeItem->textureDepth())
+ m_sliceIndexZ--;
+ if (m_volumeItem->sliceIndexZ() != -1)
+ m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
+ m_sliceLabelZ->setPixmap(
+ QPixmap::fromImage(m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ)));
+ }
+}
+
+void VolumetricModifier::handleFpsChange(qreal fps)
+{
+ const QString fpsFormat = QStringLiteral("FPS: %1");
+ int fps10 = int(fps * 10.0);
+ m_fpsLabel->setText(fpsFormat.arg(qreal(fps10) / 10.0));
+}
+
+void VolumetricModifier::handleTimeout()
+{
+ if (!m_mediumDetailRB->isEnabled()) {
+ if (m_mediumDetailIndex != mediumDetailSize) {
+ m_mediumDetailIndex = createVolume(mediumDetailSize, m_mediumDetailIndex, 4,
+ m_mediumDetailData);
+ } else if (m_mediumDetailShaftIndex != m_mineShaftArray.size()) {
+ m_mediumDetailShaftIndex = excavateMineShaft(mediumDetailSize, m_mediumDetailShaftIndex,
+ 1, m_mediumDetailData );
+ } else {
+ m_mediumDetailRB->setEnabled(true);
+ QString label = QStringLiteral("Medium (%1x%2x%1)");
+ m_mediumDetailRB->setText(label.arg(mediumDetailSize).arg(mediumDetailSize / 2));
+ }
+ } else if (!m_highDetailRB->isEnabled()) {
+ if (m_highDetailIndex != highDetailSize) {
+ m_highDetailIndex = createVolume(highDetailSize, m_highDetailIndex, 1,
+ m_highDetailData);
+ } else if (m_highDetailShaftIndex != m_mineShaftArray.size()) {
+ m_highDetailShaftIndex = excavateMineShaft(highDetailSize, m_highDetailShaftIndex, 1,
+ m_highDetailData);
+ } else {
+ m_highDetailRB->setEnabled(true);
+ QString label = QStringLiteral("High (%1x%2x%1)");
+ m_highDetailRB->setText(label.arg(highDetailSize).arg(highDetailSize / 2));
+ m_timer.stop();
+ }
+ }
+}
+
+void VolumetricModifier::toggleLowDetail(bool enabled)
+{
+ if (enabled && m_volumeItem) {
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_lowDetailData));
+ m_volumeItem->setTextureDimensions(lowDetailSize, lowDetailSize / 2, lowDetailSize);
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::toggleMediumDetail(bool enabled)
+{
+ if (enabled && m_volumeItem) {
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_mediumDetailData));
+ m_volumeItem->setTextureDimensions(mediumDetailSize, mediumDetailSize / 2, mediumDetailSize);
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::toggleHighDetail(bool enabled)
+{
+ if (enabled && m_volumeItem) {
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_highDetailData));
+ m_volumeItem->setTextureDimensions(highDetailSize, highDetailSize / 2, highDetailSize);
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setFpsMeasurement(bool enabled)
+{
+ m_graph->setMeasureFps(enabled);
+ if (enabled)
+ m_fpsLabel->setText(QStringLiteral("Measuring..."));
+ else
+ m_fpsLabel->setText(QString());
+}
+
+void VolumetricModifier::setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ)
+{
+ m_sliceSliderX = sliderX;
+ m_sliceSliderY = sliderY;
+ m_sliceSliderZ = sliderZ;
+
+ // Set sliders to interesting values
+ m_sliceSliderX->setValue(715);
+ m_sliceSliderY->setValue(612);
+ m_sliceSliderZ->setValue(715);
+}
+
+void VolumetricModifier::changeColorTable(int enabled)
+{
+ if (m_volumeItem) {
+ if (enabled)
+ m_volumeItem->setColorTable(m_colorTable2);
+ else
+ m_volumeItem->setColorTable(m_colorTable1);
+
+ m_usingPrimaryTable = !enabled;
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setPreserveOpacity(bool enabled)
+{
+
+ if (m_volumeItem) {
+ //! [10]
+ m_volumeItem->setPreserveOpacity(enabled);
+ //! [10]
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setTransparentGround(bool enabled)
+{
+ if (m_volumeItem) {
+ //! [12]
+ int newAlpha = enabled ? terrainTransparency : 255;
+ for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) {
+ QRgb oldColor1 = m_colorTable1.at(i);
+ QRgb oldColor2 = m_colorTable2.at(i);
+ m_colorTable1[i] = qRgba(qRed(oldColor1), qGreen(oldColor1), qBlue(oldColor1), newAlpha);
+ m_colorTable2[i] = qRgba(qRed(oldColor2), qGreen(oldColor2), qBlue(oldColor2), newAlpha);
+ }
+ if (m_usingPrimaryTable)
+ m_volumeItem->setColorTable(m_colorTable1);
+ else
+ m_volumeItem->setColorTable(m_colorTable2);
+ //! [12]
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setUseHighDefShader(bool enabled)
+{
+ if (m_volumeItem) {
+ //! [13]
+ m_volumeItem->setUseHighDefShader(enabled);
+ //! [13]
+ }
+}
+
+void VolumetricModifier::adjustAlphaMultiplier(int value)
+{
+ if (m_volumeItem) {
+ float mult;
+ if (value > 100)
+ mult = float(value - 99) / 2.0f;
+ else
+ mult = float(value) / float(500 - value * 4);
+ //! [11]
+ m_volumeItem->setAlphaMultiplier(mult);
+ //! [11]
+ QString labelFormat = QStringLiteral("Alpha multiplier: %1");
+ m_alphaMultiplierLabel->setText(labelFormat.arg(
+ QString::number(m_volumeItem->alphaMultiplier(), 'f', 3)));
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::toggleAreaAll(bool enabled)
+{
+ if (enabled) {
+ m_graph->axisX()->setRange(0.0f, 1000.0f);
+ m_graph->axisY()->setRange(-600.0f, 600.0f);
+ m_graph->axisZ()->setRange(0.0f, 1000.0f);
+ m_graph->axisX()->setSegmentCount(5);
+ m_graph->axisY()->setSegmentCount(6);
+ m_graph->axisZ()->setSegmentCount(5);
+ }
+}
+
+void VolumetricModifier::toggleAreaMine(bool enabled)
+{
+ if (enabled) {
+ m_graph->axisX()->setRange(350.0f, 850.0f);
+ m_graph->axisY()->setRange(-500.0f, 100.0f);
+ m_graph->axisZ()->setRange(350.0f, 900.0f);
+ m_graph->axisX()->setSegmentCount(10);
+ m_graph->axisY()->setSegmentCount(6);
+ m_graph->axisZ()->setSegmentCount(11);
+ }
+}
+
+void VolumetricModifier::toggleAreaMountain(bool enabled)
+{
+ if (enabled) {
+ m_graph->axisX()->setRange(300.0f, 600.0f);
+ m_graph->axisY()->setRange(-100.0f, 400.0f);
+ m_graph->axisZ()->setRange(300.0f, 600.0f);
+ m_graph->axisX()->setSegmentCount(9);
+ m_graph->axisY()->setSegmentCount(5);
+ m_graph->axisZ()->setSegmentCount(9);
+ }
+}
+
+void VolumetricModifier::setDrawSliceFrames(int enabled)
+{
+ if (m_volumeItem)
+ m_volumeItem->setDrawSliceFrames(enabled);
+}
+
+void VolumetricModifier::initHeightMap(QString fileName, QVector<uchar> &layerData)
+{
+ QImage heightImage(fileName);
+
+ layerData.resize(layerDataSize * layerDataSize);
+ const uchar *bits = heightImage.bits();
+ int index = 0;
+ QVector<QRgb> colorTable = heightImage.colorTable();
+ for (int i = 0; i < layerDataSize; i++) {
+ for (int j = 0; j < layerDataSize; j++) {
+ layerData[index] = qRed(colorTable.at(bits[index]));
+ index++;
+ }
+ }
+}
+
+int VolumetricModifier::createVolume(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData)
+{
+ // Generate volume from layer data.
+ int index = startIndex * textureSize * textureSize / 2.0f;
+ int endIndex = startIndex + count;
+ if (endIndex > textureSize)
+ endIndex = textureSize;
+ QVector<uchar> magmaHeights(textureSize);
+ QVector<uchar> waterHeights(textureSize);
+ QVector<uchar> groundHeights(textureSize);
+ float multiplier = float(layerDataSize) / float(textureSize);
+ for (int i = startIndex; i < endIndex; i++) {
+ // Generate layer height arrays
+ for (int l = 0; l < textureSize; l++) {
+ int layerIndex = (int(i * multiplier) * layerDataSize + int(l * multiplier));
+ magmaHeights[l] = int(m_magmaLayer.at(layerIndex));
+ waterHeights[l] = int(m_waterLayer.at(layerIndex));
+ groundHeights[l] = int(m_groundLayer.at(layerIndex));
+ }
+ for (int j = 0; j < textureSize / 2; j++) {
+ for (int k = 0; k < textureSize; k++) {
+ int colorIndex;
+ int height((layerDataSize - (j * 2 * multiplier)) / 2);
+ if (height < magmaHeights.at(k)) {
+ // Magma layer
+ colorIndex = int((float(height) / heightToColorDiv)
+ * float(layerColorThickness)) + magmaColorsMin;
+ } else if (height < groundHeights.at(k) && height < waterHeights.at(k)) {
+ // Ground layer below water
+ colorIndex = int((float(waterHeights.at(k) - height) / heightToColorDiv)
+ * float(layerColorThickness)) + underWaterGroundColorsMin;
+ } else if (height < waterHeights.at(k)) {
+ // Water layer where water goes over ground
+ colorIndex = int((float(height - magmaHeights.at(k)) / heightToColorDiv)
+ * float(layerColorThickness)) + waterColorsMin;
+ } else if (height <= groundHeights.at(k)) {
+ // Ground above water
+ colorIndex = int((float(height - waterHeights.at(k)) / heightToColorDiv)
+ * float(layerColorThickness)) + aboveWaterGroundColorsMin;
+ } else {
+ // Rest is air
+ colorIndex = airColorIndex;
+ }
+
+ (*textureData)[index] = colorIndex;
+ index++;
+ }
+ }
+ }
+ return endIndex;
+}
+
+int VolumetricModifier::excavateMineShaft(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData)
+{
+ int endIndex = startIndex + count;
+ if (endIndex > m_mineShaftArray.size())
+ endIndex = m_mineShaftArray.size();
+ int shaftSize = mineShaftDiameter * textureSize / lowDetailSize;
+ for (int i = startIndex; i < endIndex; i++) {
+ QVector3D shaftStart(m_mineShaftArray.at(i).first);
+ QVector3D shaftEnd(m_mineShaftArray.at(i).second);
+ int shaftLen = (shaftEnd - shaftStart).length() * lowDetailSize;
+ int dataX = shaftStart.x() * textureSize - (shaftSize / 2);
+ int dataY = (shaftStart.y() * textureSize - (shaftSize / 2)) / 2;
+ int dataZ = shaftStart.z() * textureSize - (shaftSize / 2);
+ int dataIndex = dataX + (dataY * textureSize) + dataZ * (textureSize * textureSize / 2);
+ if (shaftStart.x() != shaftEnd.x()) {
+ for (int j = 0; j <= shaftLen; j++) {
+ excavateMineBlock(textureSize, dataIndex, shaftSize, textureData);
+ dataIndex += shaftSize;
+ }
+ } else if (shaftStart.y() != shaftEnd.y()) {
+ shaftLen /= 2; // Vertical shafts are half as long
+ for (int j = 0; j <= shaftLen; j++) {
+ excavateMineBlock(textureSize, dataIndex, shaftSize, textureData);
+ dataIndex += textureSize * shaftSize;
+ }
+ } else {
+ for (int j = 0; j <= shaftLen; j++) {
+ excavateMineBlock(textureSize, dataIndex, shaftSize, textureData);
+ dataIndex += (textureSize * textureSize / 2) * shaftSize;
+ }
+ }
+
+
+ }
+ return endIndex;
+}
+
+void VolumetricModifier::excavateMineBlock(int textureSize, int dataIndex, int size,
+ QVector<uchar> *textureData)
+{
+ for (int k = 0; k < size; k++) {
+ int curIndex = dataIndex + (k * textureSize * textureSize / 2);
+ for (int l = 0; l < size; l++) {
+ curIndex = dataIndex + (k * textureSize * textureSize / 2)
+ + (l * textureSize);
+ for (int m = 0; m < size; m++) {
+ if (textureData->at(curIndex) != airColorIndex)
+ (*textureData)[curIndex] = mineShaftColorIndex;
+ curIndex++;
+ }
+
+ }
+ }
+}
+
+void VolumetricModifier::handleSlicingChanges()
+{
+ if (m_volumeItem) {
+ if (m_slicingX || m_slicingY || m_slicingZ) {
+ // Only show slices of selected dimensions
+ //! [8]
+ m_volumeItem->setDrawSlices(true);
+ //! [8]
+ m_volumeItem->setSliceIndexX(m_slicingX ? m_sliceIndexX : -1);
+ m_volumeItem->setSliceIndexY(m_slicingY ? m_sliceIndexY : -1);
+ m_volumeItem->setSliceIndexZ(m_slicingZ ? m_sliceIndexZ : -1);
+ } else {
+ // Show slice frames for all dimenstions when not actually slicing
+ m_volumeItem->setDrawSlices(false);
+ m_volumeItem->setSliceIndexX(m_sliceIndexX);
+ m_volumeItem->setSliceIndexY(m_sliceIndexY);
+ m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
+ }
+ }
+}
+
+void VolumetricModifier::initMineShaftArray()
+{
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.1f, 0.7f),
+ QVector3D(0.7f, 0.8f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.7f, 0.5f),
+ QVector3D(0.7f, 0.7f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.4f, 0.7f, 0.7f),
+ QVector3D(0.7f, 0.7f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.4f, 0.7f, 0.7f),
+ QVector3D(0.4f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.45f, 0.7f, 0.7f),
+ QVector3D(0.45f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.7f, 0.7f),
+ QVector3D(0.5f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.55f, 0.7f, 0.7f),
+ QVector3D(0.55f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.7f),
+ QVector3D(0.6f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.65f, 0.7f, 0.7f),
+ QVector3D(0.65f, 0.7f, 0.8f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.6f, 0.7f),
+ QVector3D(0.7f, 0.6f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.6f, 0.7f),
+ QVector3D(0.5f, 0.6f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.55f, 0.6f, 0.7f),
+ QVector3D(0.55f, 0.6f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.7f),
+ QVector3D(0.6f, 0.6f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.65f, 0.6f, 0.7f),
+ QVector3D(0.65f, 0.6f, 0.8f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.6f, 0.4f),
+ QVector3D(0.7f, 0.6f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.45f),
+ QVector3D(0.8f, 0.6f, 0.45f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.5f),
+ QVector3D(0.8f, 0.6f, 0.5f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.55f),
+ QVector3D(0.8f, 0.6f, 0.55f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.6f),
+ QVector3D(0.8f, 0.6f, 0.6f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.65f),
+ QVector3D(0.8f, 0.6f, 0.65f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.7f),
+ QVector3D(0.8f, 0.6f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.7f, 0.4f),
+ QVector3D(0.7f, 0.7f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.45f),
+ QVector3D(0.8f, 0.7f, 0.45f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.5f),
+ QVector3D(0.8f, 0.7f, 0.5f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.55f),
+ QVector3D(0.8f, 0.7f, 0.55f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.6f),
+ QVector3D(0.8f, 0.7f, 0.6f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.65f),
+ QVector3D(0.8f, 0.7f, 0.65f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.7f),
+ QVector3D(0.8f, 0.7f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.8f, 0.5f),
+ QVector3D(0.7f, 0.8f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.55f),
+ QVector3D(0.8f, 0.8f, 0.55f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.6f),
+ QVector3D(0.8f, 0.8f, 0.6f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.65f),
+ QVector3D(0.8f, 0.8f, 0.65f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.7f),
+ QVector3D(0.8f, 0.8f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.1f, 0.4f),
+ QVector3D(0.7f, 0.7f, 0.4f));
+}
diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h
new file mode 100644
index 00000000..8d28b524
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef VOLUMETRICMODIFIER_H
+#define VOLUMETRICMODIFIER_H
+
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtDataVisualization/qcustom3dvolume.h>
+#include <QtCore/QTimer>
+#include <QtGui/QRgb>
+
+class QLabel;
+class QRadioButton;
+class QSlider;
+
+using namespace QtDataVisualization;
+
+class VolumetricModifier : public QObject
+{
+ Q_OBJECT
+public:
+ explicit VolumetricModifier(Q3DScatter *scatter);
+ ~VolumetricModifier();
+
+ void setFpsLabel(QLabel *fpsLabel);
+ void setMediumDetailRB(QRadioButton *button);
+ void setHighDetailRB(QRadioButton *button);
+ void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel);
+ void setAlphaMultiplierLabel(QLabel *label);
+
+public slots:
+ void sliceX(int enabled);
+ void sliceY(int enabled);
+ void sliceZ(int enabled);
+ void adjustSliceX(int value);
+ void adjustSliceY(int value);
+ void adjustSliceZ(int value);
+ void handleFpsChange(qreal fps);
+ void handleTimeout();
+ void toggleLowDetail(bool enabled);
+ void toggleMediumDetail(bool enabled);
+ void toggleHighDetail(bool enabled);
+ void setFpsMeasurement(bool enabled);
+ void setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ);
+ void changeColorTable(int enabled);
+ void setPreserveOpacity(bool enabled);
+ void setTransparentGround(bool enabled);
+ void setUseHighDefShader(bool enabled);
+ void adjustAlphaMultiplier(int value);
+ void toggleAreaAll(bool enabled);
+ void toggleAreaMine(bool enabled);
+ void toggleAreaMountain(bool enabled);
+ void setDrawSliceFrames(int enabled);
+
+private:
+
+ void initHeightMap(QString fileName, QVector<uchar> &layerData);
+ void initMineShaftArray();
+ int createVolume(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData);
+ int excavateMineShaft(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData);
+ void excavateMineBlock(int textureSize, int dataIndex, int size, QVector<uchar> *textureData);
+ void handleSlicingChanges();
+
+ Q3DScatter *m_graph;
+ QCustom3DVolume *m_volumeItem;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+ bool m_slicingX;
+ bool m_slicingY;
+ bool m_slicingZ;
+ QLabel *m_fpsLabel;
+ QRadioButton *m_mediumDetailRB;
+ QRadioButton *m_highDetailRB;
+ QVector<uchar> *m_lowDetailData;
+ QVector<uchar> *m_mediumDetailData;
+ QVector<uchar> *m_highDetailData;
+ QTimer m_timer;
+ int m_mediumDetailIndex;
+ int m_highDetailIndex;
+ int m_mediumDetailShaftIndex;
+ int m_highDetailShaftIndex;
+ QSlider *m_sliceSliderX;
+ QSlider *m_sliceSliderY;
+ QSlider *m_sliceSliderZ;
+ QVector<QRgb> m_colorTable1;
+ QVector<QRgb> m_colorTable2;
+ bool m_usingPrimaryTable;
+ QLabel *m_sliceLabelX;
+ QLabel *m_sliceLabelY;
+ QLabel *m_sliceLabelZ;
+ QLabel *m_alphaMultiplierLabel;
+ QVector<uchar> m_magmaLayer;
+ QVector<uchar> m_waterLayer;
+ QVector<uchar> m_groundLayer;
+ QVector<QPair<QVector3D, QVector3D> > m_mineShaftArray;
+};
+
+#endif
diff --git a/examples/datavisualization/volumetric/volumetric.pro b/examples/datavisualization/volumetric/volumetric.pro
new file mode 100644
index 00000000..fa355692
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.pro
@@ -0,0 +1,17 @@
+android|ios {
+ error( "This example is not supported for android or ios." )
+}
+
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+SOURCES += main.cpp volumetric.cpp
+HEADERS += volumetric.h
+
+QT += widgets
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
+
+RESOURCES += volumetric.qrc
diff --git a/examples/datavisualization/volumetric/volumetric.qrc b/examples/datavisualization/volumetric/volumetric.qrc
new file mode 100644
index 00000000..920fd1d2
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/heightmaps">
+ <file>layer_ground.png</file>
+ <file>layer_magma.png</file>
+ <file>layer_water.png</file>
+ </qresource>
+</RCC>
diff --git a/qtdatavisualization.pro b/qtdatavisualization.pro
index bb98435e..faadadbc 100644
--- a/qtdatavisualization.pro
+++ b/qtdatavisualization.pro
@@ -8,4 +8,4 @@ contains(QT_CONFIG, opengles1) {
error(QtDataVisualization does not support OpenGL ES 1!)
}
-OTHER_FILES += README dist/*
+OTHER_FILES += README dist/* .qmake.conf
diff --git a/src/datavisualization/axis/axis.pri b/src/datavisualization/axis/axis.pri
index 0173b597..4c142c91 100644
--- a/src/datavisualization/axis/axis.pri
+++ b/src/datavisualization/axis/axis.pri
@@ -16,3 +16,5 @@ SOURCES += \
$$PWD/qcategory3daxis.cpp \
$$PWD/qvalue3daxisformatter.cpp \
$$PWD/qlogvalue3daxisformatter.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
index 7367e7c5..85fd5c4f 100644
--- a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
+++ b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
@@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \since QtDataVisualization 1.1
* \ingroup datavisualization_qml
* \instantiates QLogValue3DAxisFormatter
+ * \inherits ValueAxis3DFormatter
* \brief LogValueAxis3DFormatter implements logarithmic value axis formatter.
*
* This type provides formatting rules for a logarithmic ValueAxis3D.
diff --git a/src/datavisualization/axis/qvalue3daxis.cpp b/src/datavisualization/axis/qvalue3daxis.cpp
index 8207174f..3b9c9e3d 100644
--- a/src/datavisualization/axis/qvalue3daxis.cpp
+++ b/src/datavisualization/axis/qvalue3daxis.cpp
@@ -18,6 +18,7 @@
#include "qvalue3daxis_p.h"
#include "qvalue3daxisformatter_p.h"
+#include "abstract3dcontroller_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -68,8 +69,16 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty string ValueAxis3D::labelFormat
*
- * Defines the label format to be used for the labels on this axis. Supported specifiers are:
+ * Defines the label format to be used for the labels on this axis. How the format is interpreted
+ * depends on the axis formatter and the locale in use. Using the default formatter and default
+ * locale (\c{"C"}), the formatting uses QString::sprintf(). Supported specifiers are:
* \c {d, i, o, x, X, f, F, e, E, g, G, c}. See QString::sprintf() for additional details.
+ * For other locales, the default formatter uses reduced set of printf format specifiers:
+ * \c {d, i, f, F, e, E, g, G}. In these cases, the only supported modifier is the precision
+ * modifier for the floating point and exponential formats. The decimal point and other locale
+ * dependent formatting is done according to the graph locale.
+ *
+ * \sa AbstractGraph3D::locale
*/
/*!
@@ -164,12 +173,20 @@ int QValue3DAxis::subSegmentCount() const
/*!
* \property QValue3DAxis::labelFormat
*
- * Defines the label \a format to be used for the labels on this axis. Supported specifiers are:
+ * Defines the label format to be used for the labels on this axis. How the format is interpreted
+ * depends on the axis formatter and the locale in use. Using the default formatter and default
+ * locale (\c{"C"}), the formatting uses QString::sprintf(). Supported specifiers are:
* \c {d, i, o, x, X, f, F, e, E, g, G, c}. See QString::sprintf() for additional details.
+ * For other locales, the default formatter uses reduced set of printf format specifiers:
+ * \c {d, i, f, F, e, E, g, G}. In these cases, the only supported modifier is the precision
+ * modifier for the floating point and exponential formats. The decimal point and other locale
+ * dependent formatting is done according to the graph locale.
*
* Usage example:
*
* \c {axis->setLabelFormat("%.2f mm");}
+ *
+ * \sa formatter, QAbstract3DGraph::locale
*/
void QValue3DAxis::setLabelFormat(const QString &format)
{
@@ -201,6 +218,9 @@ void QValue3DAxis::setFormatter(QValue3DAxisFormatter *formatter)
dptr()->m_formatter = formatter;
formatter->setParent(this);
formatter->d_ptr->setAxis(this);
+ Abstract3DController *controller = qobject_cast<Abstract3DController *>(parent());
+ if (controller)
+ formatter->setLocale(controller->locale());
emit formatterChanged(formatter);
emit dptr()->formatterDirty();
}
diff --git a/src/datavisualization/axis/qvalue3daxisformatter.cpp b/src/datavisualization/axis/qvalue3daxisformatter.cpp
index 56ca3b0f..f6b705a9 100644
--- a/src/datavisualization/axis/qvalue3daxisformatter.cpp
+++ b/src/datavisualization/axis/qvalue3daxisformatter.cpp
@@ -270,6 +270,28 @@ QStringList &QValue3DAxisFormatter::labelStrings() const
return d_ptr->m_labelStrings;
}
+/*!
+ * Sets the \a locale that this formatter uses.
+ * The graph automatically sets the formatter's locale to a graph's locale whenever the parent axis
+ * is set as an active axis of the graph, the axis formatter is set to an axis attached to
+ * the graph, or the graph's locale changes.
+ *
+ * \sa locale(), QAbstract3DGraph::locale
+ */
+void QValue3DAxisFormatter::setLocale(const QLocale &locale)
+{
+ d_ptr->m_cLocaleInUse = (locale == QLocale::c());
+ d_ptr->m_locale = locale;
+ markDirty(true);
+}
+/*!
+ * \return the current locale this formatter is using.
+ */
+QLocale QValue3DAxisFormatter::locale() const
+{
+ return d_ptr->m_locale;
+}
+
// QValue3DAxisFormatterPrivate
QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q)
: QObject(0),
@@ -281,7 +303,10 @@ QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter
m_axis(0),
m_preparsedParamType(Utils::ParamTypeUnknown),
m_allowNegatives(true),
- m_allowZero(true)
+ m_allowZero(true),
+ m_formatPrecision(6), // 6 and 'g' are defaults in Qt API for format precision and spec
+ m_formatSpec('g'),
+ m_cLocaleInUse(true)
{
}
@@ -363,12 +388,19 @@ QString QValue3DAxisFormatterPrivate::stringForValue(qreal value, const QString
{
if (m_previousLabelFormat.compare(format)) {
// Format string different than the previous one used, reparse it
- m_previousLabelFormat = format;
- m_preparsedParamType = Utils::findFormatParamType(format);
m_labelFormatArray = format.toUtf8();
+ m_previousLabelFormat = format;
+ m_preparsedParamType = Utils::preParseFormat(format, m_formatPreStr, m_formatPostStr,
+ m_formatPrecision, m_formatSpec);
}
- return Utils::formatLabel(m_labelFormatArray, m_preparsedParamType, value);
+ if (m_cLocaleInUse) {
+ return Utils::formatLabelSprintf(m_labelFormatArray, m_preparsedParamType, value);
+ } else {
+ return Utils::formatLabelLocalized(m_preparsedParamType, value, m_locale, m_formatPreStr,
+ m_formatPostStr, m_formatPrecision, m_formatSpec,
+ m_labelFormatArray);
+ }
}
float QValue3DAxisFormatterPrivate::positionAt(float value) const
diff --git a/src/datavisualization/axis/qvalue3daxisformatter.h b/src/datavisualization/axis/qvalue3daxisformatter.h
index c7b0bac5..82e49f21 100644
--- a/src/datavisualization/axis/qvalue3daxisformatter.h
+++ b/src/datavisualization/axis/qvalue3daxisformatter.h
@@ -24,6 +24,7 @@
#include <QtCore/QScopedPointer>
#include <QtCore/QVector>
#include <QtCore/QStringList>
+#include <QtCore/QLocale>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -60,6 +61,9 @@ protected:
QVector<float> &labelPositions() const;
QStringList &labelStrings() const;
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
QScopedPointer<QValue3DAxisFormatterPrivate> d_ptr;
private:
diff --git a/src/datavisualization/axis/qvalue3daxisformatter_p.h b/src/datavisualization/axis/qvalue3daxisformatter_p.h
index 2d1dc920..9571d001 100644
--- a/src/datavisualization/axis/qvalue3daxisformatter_p.h
+++ b/src/datavisualization/axis/qvalue3daxisformatter_p.h
@@ -32,6 +32,7 @@
#include "datavisualizationglobal_p.h"
#include "qvalue3daxisformatter.h"
#include "utils_p.h"
+#include <QtCore/QLocale>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -83,6 +84,13 @@ protected:
bool m_allowNegatives;
bool m_allowZero;
+ QLocale m_locale;
+ QString m_formatPreStr;
+ QString m_formatPostStr;
+ int m_formatPrecision;
+ char m_formatSpec;
+ bool m_cLocaleInUse;
+
friend class QValue3DAxisFormatter;
};
diff --git a/src/datavisualization/common.pri b/src/datavisualization/common.pri
deleted file mode 100644
index 9a497c2c..00000000
--- a/src/datavisualization/common.pri
+++ /dev/null
@@ -1,7 +0,0 @@
-INCLUDEPATH += $$PWD/engine \
- $$PWD/global \
- $$PWD/utils \
- $$PWD/axis \
- $$PWD/data \
- $$PWD/input \
- $$PWD/theme
diff --git a/src/datavisualization/data/abstractrenderitem_p.h b/src/datavisualization/data/abstractrenderitem_p.h
index 57977a3c..ccee2b00 100644
--- a/src/datavisualization/data/abstractrenderitem_p.h
+++ b/src/datavisualization/data/abstractrenderitem_p.h
@@ -48,10 +48,12 @@ public:
inline void setTranslation(const QVector3D &translation) { m_translation = translation; }
inline const QVector3D &translation() const {return m_translation; }
- inline QQuaternion rotation() const { return m_rotation; }
+ inline const QQuaternion &rotation() const { return m_rotation; }
inline void setRotation(const QQuaternion &rotation)
{
- if (m_rotation != rotation)
+ if (rotation.isNull())
+ m_rotation = identityQuaternion;
+ else
m_rotation = rotation;
}
diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp
index a1c70057..f56ca86e 100644
--- a/src/datavisualization/data/customrenderitem.cpp
+++ b/src/datavisualization/data/customrenderitem.cpp
@@ -23,9 +23,32 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
CustomRenderItem::CustomRenderItem()
: AbstractRenderItem(),
m_texture(0),
+ m_positionAbsolute(false),
+ m_scalingAbsolute(true),
m_object(0),
+ m_needBlend(true),
m_visible(true),
- m_renderer(0)
+ m_valid(true),
+ m_index(0),
+ m_shadowCasting(false),
+ m_isFacingCamera(false),
+ m_item(0),
+ m_renderer(0),
+ m_labelItem(false),
+ m_textureWidth(0),
+ m_textureHeight(0),
+ m_textureDepth(0),
+ m_isVolume(false),
+ m_textureFormat(QImage::Format_ARGB32),
+ m_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true),
+ m_useHighDefShader(true),
+ m_drawSlices(false),
+ m_drawSliceFrames(false)
+
{
}
@@ -39,4 +62,47 @@ void CustomRenderItem::setMesh(const QString &meshFile)
ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFile);
}
+void CustomRenderItem::setColorTable(const QVector<QRgb> &colors)
+{
+ m_colorTable.resize(256);
+ for (int i = 0; i < 256; i++) {
+ if (i < colors.size()) {
+ const QRgb &rgb = colors.at(i);
+ m_colorTable[i] = QVector4D(float(qRed(rgb)) / 255.0f,
+ float(qGreen(rgb)) / 255.0f,
+ float(qBlue(rgb)) / 255.0f,
+ float(qAlpha(rgb)) / 255.0f);
+ } else {
+ m_colorTable[i] = QVector4D(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ }
+}
+
+void CustomRenderItem::setMinBounds(const QVector3D &bounds)
+{
+ m_minBounds = bounds;
+ m_minBoundsNormal = m_minBounds;
+ m_minBoundsNormal.setY(-m_minBoundsNormal.y());
+ m_minBoundsNormal.setZ(-m_minBoundsNormal.z());
+ m_minBoundsNormal = 0.5f * (m_minBoundsNormal + oneVector);
+}
+
+void CustomRenderItem::setMaxBounds(const QVector3D &bounds)
+{
+ m_maxBounds = bounds;
+ m_maxBoundsNormal = m_maxBounds;
+ m_maxBoundsNormal.setY(-m_maxBoundsNormal.y());
+ m_maxBoundsNormal.setZ(-m_maxBoundsNormal.z());
+ m_maxBoundsNormal = 0.5f * (m_maxBoundsNormal + oneVector);
+}
+
+void CustomRenderItem::setSliceFrameColor(const QColor &color)
+{
+ const QRgb &rgb = color.rgba();
+ m_sliceFrameColor = QVector4D(float(qRed(rgb)) / 255.0f,
+ float(qGreen(rgb)) / 255.0f,
+ float(qBlue(rgb)) / 255.0f,
+ float(1.0f)); // Alpha not supported for frames
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h
index 4fb94276..ad868fac 100644
--- a/src/datavisualization/data/customrenderitem_p.h
+++ b/src/datavisualization/data/customrenderitem_p.h
@@ -31,6 +31,9 @@
#include "abstractrenderitem_p.h"
#include "objecthelper_p.h"
+#include <QtGui/QRgb>
+#include <QtGui/QImage>
+#include <QtGui/QColor>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -48,11 +51,17 @@ public:
void setMesh(const QString &meshFile);
inline ObjectHelper *mesh() const { return m_object; }
inline void setScaling(const QVector3D &scaling) { m_scaling = scaling; }
- inline QVector3D scaling() const { return m_scaling; }
+ inline const QVector3D &scaling() const { return m_scaling; }
+ inline void setOrigScaling(const QVector3D &scaling) { m_origScaling = scaling; }
+ inline const QVector3D &origScaling() const { return m_origScaling; }
inline void setPosition(const QVector3D &position) { m_position = position; }
- inline QVector3D position() const { return m_position; }
- inline void setPositionAbsolute(bool absolute) { m_absolute = absolute; }
- inline bool isPositionAbsolute() const { return m_absolute; }
+ inline const QVector3D &position() const { return m_position; }
+ inline void setOrigPosition(const QVector3D &position) { m_origPosition = position; }
+ inline const QVector3D &origPosition() const { return m_origPosition; }
+ inline void setPositionAbsolute(bool absolute) { m_positionAbsolute = absolute; }
+ inline bool isPositionAbsolute() const { return m_positionAbsolute; }
+ inline void setScalingAbsolute(bool absolute) { m_scalingAbsolute = absolute; }
+ inline bool isScalingAbsolute() const { return m_scalingAbsolute; }
inline void setBlendNeeded(bool blend) { m_needBlend = blend; }
inline bool isBlendNeeded() const { return m_needBlend; }
inline void setVisible(bool visible) { m_visible = visible; }
@@ -68,14 +77,78 @@ public:
inline void setFacingCamera(bool facing) { m_isFacingCamera = facing; }
inline bool isFacingCamera() const { return m_isFacingCamera; }
inline void setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; }
+ inline void setLabelItem(bool isLabel) { m_labelItem = isLabel; }
+ inline bool isLabel() const { return m_labelItem; }
+
+ // Volume specific
+ inline void setTextureWidth(int width) { m_textureWidth = width; setSliceIndexX(m_sliceIndexX); }
+ inline int textureWidth() const { return m_textureWidth; }
+ inline void setTextureHeight(int height) { m_textureHeight = height; setSliceIndexY(m_sliceIndexY); }
+ inline int textureHeight() const { return m_textureHeight; }
+ inline void setTextureDepth(int depth) { m_textureDepth = depth; setSliceIndexZ(m_sliceIndexZ); }
+ inline int textureDepth() const { return m_textureDepth; }
+ inline int textureSize() const { return m_textureWidth * m_textureHeight * m_textureDepth; }
+ inline void setColorTable(const QVector<QVector4D> &colors) { m_colorTable = colors; }
+ void setColorTable(const QVector<QRgb> &colors);
+ inline const QVector<QVector4D> &colorTable() const { return m_colorTable; }
+ inline void setVolume(bool volume) { m_isVolume = volume; }
+ inline bool isVolume() const { return m_isVolume; }
+ inline void setTextureFormat(QImage::Format format) { m_textureFormat = format; }
+ inline QImage::Format textureFormat() const { return m_textureFormat; }
+ inline void setSliceIndexX(int index)
+ {
+ m_sliceIndexX = index;
+ m_sliceFractions.setX((float(index) + 0.5f) / float(m_textureWidth) * 2.0 - 1.0);
+ }
+ inline void setSliceIndexY(int index)
+ {
+ m_sliceIndexY = index;
+ m_sliceFractions.setY((float(index) + 0.5f) / float(m_textureHeight) * 2.0 - 1.0);
+ }
+ inline void setSliceIndexZ(int index)
+ {
+ m_sliceIndexZ = index;
+ m_sliceFractions.setZ((float(index) + 0.5f) / float(m_textureDepth) * 2.0 - 1.0);
+ }
+ inline const QVector3D &sliceFractions() const { return m_sliceFractions; }
+ inline int sliceIndexX() const { return m_sliceIndexX; }
+ inline int sliceIndexY() const { return m_sliceIndexY; }
+ inline int sliceIndexZ() const { return m_sliceIndexZ; }
+ inline void setAlphaMultiplier(float mult) { m_alphaMultiplier = mult; }
+ inline float alphaMultiplier() const { return m_alphaMultiplier; }
+ inline void setPreserveOpacity(bool enable) { m_preserveOpacity = enable; }
+ inline bool preserveOpacity() const { return m_preserveOpacity; }
+ inline void setUseHighDefShader(bool enable) { m_useHighDefShader = enable; }
+ inline bool useHighDefShader() const {return m_useHighDefShader; }
+ void setMinBounds(const QVector3D &bounds);
+ inline const QVector3D &minBounds() const { return m_minBounds; }
+ void setMaxBounds(const QVector3D &bounds);
+ inline const QVector3D &maxBounds() const { return m_maxBounds; }
+ inline const QVector3D &minBoundsNormal() const { return m_minBoundsNormal; }
+ inline const QVector3D &maxBoundsNormal() const { return m_maxBoundsNormal; }
+ inline void setDrawSlices(bool enable) { m_drawSlices = enable; }
+ inline bool drawSlices() const {return m_drawSlices; }
+ inline void setDrawSliceFrames(bool enable) { m_drawSliceFrames = enable; }
+ inline bool drawSliceFrames() const {return m_drawSliceFrames; }
+ void setSliceFrameColor(const QColor &color);
+ inline const QVector4D &sliceFrameColor() const { return m_sliceFrameColor; }
+ inline void setSliceFrameWidths(const QVector3D &widths) { m_sliceFrameWidths = widths * 2.0f; }
+ inline const QVector3D &sliceFrameWidths() const { return m_sliceFrameWidths; }
+ inline void setSliceFrameGaps(const QVector3D &gaps) { m_sliceFrameGaps = gaps * 2.0f; }
+ inline const QVector3D &sliceFrameGaps() const { return m_sliceFrameGaps; }
+ inline void setSliceFrameThicknesses(const QVector3D &thicknesses) { m_sliceFrameThicknesses = thicknesses; }
+ inline const QVector3D &sliceFrameThicknesses() const { return m_sliceFrameThicknesses; }
private:
Q_DISABLE_COPY(CustomRenderItem)
GLuint m_texture;
QVector3D m_scaling;
+ QVector3D m_origScaling;
QVector3D m_position;
- bool m_absolute;
+ QVector3D m_origPosition;
+ bool m_positionAbsolute;
+ bool m_scalingAbsolute;
ObjectHelper *m_object; // shared reference
bool m_needBlend;
bool m_visible;
@@ -85,6 +158,32 @@ private:
bool m_isFacingCamera;
QCustom3DItem *m_item;
Abstract3DRenderer *m_renderer;
+ bool m_labelItem;
+
+ // Volume specific
+ int m_textureWidth;
+ int m_textureHeight;
+ int m_textureDepth;
+ QVector<QVector4D> m_colorTable;
+ bool m_isVolume;
+ QImage::Format m_textureFormat;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+ QVector3D m_sliceFractions;
+ float m_alphaMultiplier;
+ bool m_preserveOpacity;
+ bool m_useHighDefShader;
+ QVector3D m_minBounds;
+ QVector3D m_maxBounds;
+ QVector3D m_minBoundsNormal;
+ QVector3D m_maxBoundsNormal;
+ bool m_drawSlices;
+ bool m_drawSliceFrames;
+ QVector4D m_sliceFrameColor;
+ QVector3D m_sliceFrameWidths;
+ QVector3D m_sliceFrameGaps;
+ QVector3D m_sliceFrameThicknesses;
};
typedef QHash<QCustom3DItem *, CustomRenderItem *> CustomRenderItemArray;
diff --git a/src/datavisualization/data/data.pri b/src/datavisualization/data/data.pri
index 73f398bf..e2bbd6eb 100644
--- a/src/datavisualization/data/data.pri
+++ b/src/datavisualization/data/data.pri
@@ -41,7 +41,9 @@ HEADERS += \
$$PWD/qcustom3ditem.h \
$$PWD/qcustom3ditem_p.h \
$$PWD/qcustom3dlabel.h \
- $$PWD/qcustom3dlabel_p.h
+ $$PWD/qcustom3dlabel_p.h \
+ $$PWD/qcustom3dvolume.h \
+ $$PWD/qcustom3dvolume_p.h
SOURCES += \
$$PWD/labelitem.cpp \
@@ -69,4 +71,7 @@ SOURCES += \
$$PWD/qsurface3dseries.cpp \
$$PWD/customrenderitem.cpp \
$$PWD/qcustom3ditem.cpp \
- $$PWD/qcustom3dlabel.cpp
+ $$PWD/qcustom3dlabel.cpp \
+ $$PWD/qcustom3dvolume.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/data/labelitem.cpp b/src/datavisualization/data/labelitem.cpp
index 859b0550..ec8ba3fd 100644
--- a/src/datavisualization/data/labelitem.cpp
+++ b/src/datavisualization/data/labelitem.cpp
@@ -28,7 +28,7 @@ LabelItem::LabelItem()
LabelItem::~LabelItem()
{
- glDeleteTextures(1, &m_textureId);
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId);
}
void LabelItem::setSize(const QSize &size)
@@ -43,7 +43,7 @@ QSize LabelItem::size() const
void LabelItem::setTextureId(GLuint textureId)
{
- glDeleteTextures(1, &m_textureId);
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId);
m_textureId = textureId;
}
@@ -55,7 +55,7 @@ GLuint LabelItem::textureId() const
void LabelItem::clear()
{
if (m_textureId) {
- glDeleteTextures(1, &m_textureId);
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId);
m_textureId = 0;
}
m_size = QSize(0, 0);
diff --git a/src/datavisualization/data/qabstract3dseries.cpp b/src/datavisualization/data/qabstract3dseries.cpp
index f8fe6b4f..caa95062 100644
--- a/src/datavisualization/data/qabstract3dseries.cpp
+++ b/src/datavisualization/data/qabstract3dseries.cpp
@@ -657,6 +657,9 @@ QAbstract3DSeriesPrivate::QAbstract3DSeriesPrivate(QAbstract3DSeries *q,
m_mesh(QAbstract3DSeries::MeshCube),
m_meshSmooth(false),
m_colorStyle(Q3DTheme::ColorStyleUniform),
+ m_baseColor(Qt::black),
+ m_singleHighlightColor(Qt::black),
+ m_multiHighlightColor(Qt::black),
m_itemLabelDirty(true),
m_itemLabelVisible(true)
{
diff --git a/src/datavisualization/data/qabstract3dseries.h b/src/datavisualization/data/qabstract3dseries.h
index 87c4f3c1..c26c40d1 100644
--- a/src/datavisualization/data/qabstract3dseries.h
+++ b/src/datavisualization/data/qabstract3dseries.h
@@ -153,6 +153,7 @@ private:
friend class Bars3DController;
friend class Surface3DController;
friend class Surface3DRenderer;
+ friend class Scatter3DRenderer;
friend class Scatter3DController;
friend class QBar3DSeries;
friend class SeriesRenderCache;
diff --git a/src/datavisualization/data/qbar3dseries.cpp b/src/datavisualization/data/qbar3dseries.cpp
index 19c3bf79..0aa60b66 100644
--- a/src/datavisualization/data/qbar3dseries.cpp
+++ b/src/datavisualization/data/qbar3dseries.cpp
@@ -43,26 +43,27 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \row
* \li @valueTitle \li Title from value axis
* \row
- * \li @rowIdx \li Visible row index
+ * \li @rowIdx \li Visible row index. Localized using graph locale.
* \row
- * \li @colIdx \li Visible Column index
+ * \li @colIdx \li Visible Column index. Localized using graph locale.
* \row
* \li @rowLabel \li Label from row axis
* \row
* \li @colLabel \li Label from column axis
* \row
- * \li @valueLabel \li Item value formatted using the same format the value axis attached to the graph uses,
- * see \l{QValue3DAxis::setLabelFormat()} for more information.
+ * \li @valueLabel \li Item value formatted using the same format the value axis attached to
+ * the graph uses. See \l{QValue3DAxis::labelFormat} for more information.
* \row
* \li @seriesName \li Name of the series
* \row
- * \li %<format spec> \li Item value in specified format.
+ * \li %<format spec> \li Item value in specified format. Formatted using the same rules as
+ * \l{QValue3DAxis::labelFormat}.
* \endtable
*
* For example:
* \snippet doc_src_qtdatavisualization.cpp 1
*
- * \sa {Qt Data Visualization Data Handling}
+ * \sa {Qt Data Visualization Data Handling}, QAbstract3DGraph::locale
*/
/*!
@@ -334,6 +335,10 @@ void QBar3DSeriesPrivate::createItemLabel()
return;
}
+ QLocale locale(QLocale::c());
+ if (m_controller)
+ locale = m_controller->locale();
+
QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_controller->axisZ());
QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_controller->axisX());
QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_controller->axisY());
@@ -344,13 +349,13 @@ void QBar3DSeriesPrivate::createItemLabel()
int selBarPosRow = m_selectedBar.x();
int selBarPosCol = m_selectedBar.y();
- m_itemLabel.replace(rowIndexTag, QString::number(selBarPosRow));
+ m_itemLabel.replace(rowIndexTag, locale.toString(selBarPosRow));
if (categoryAxisZ->labels().size() > selBarPosRow)
m_itemLabel.replace(rowLabelTag, categoryAxisZ->labels().at(selBarPosRow));
else
m_itemLabel.replace(rowLabelTag, QString());
m_itemLabel.replace(rowTitleTag, categoryAxisZ->title());
- m_itemLabel.replace(colIndexTag, QString::number(selBarPosCol));
+ m_itemLabel.replace(colIndexTag, locale.toString(selBarPosCol));
if (categoryAxisX->labels().size() > selBarPosCol)
m_itemLabel.replace(colLabelTag, categoryAxisX->labels().at(selBarPosCol));
else
diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp
index efee3b15..b9825732 100644
--- a/src/datavisualization/data/qcustom3ditem.cpp
+++ b/src/datavisualization/data/qcustom3ditem.cpp
@@ -62,13 +62,15 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* Holds the item \a position as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}.
*
- * Item position is either in data coordinates or in absolute coordinates, depending on
+ * Item position is either in data coordinates or in absolute coordinates, depending on the
* positionAbsolute property. When using absolute coordinates, values between \c{-1.0...1.0} are
* within axis ranges.
*
- * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}.
+ * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false},
+ * unless the item is a Custom3DVolume that would be partially visible and scalingAbsolute is also
+ * \c{false}. In that case, the visible portion of the volume will be rendered.
*
- * \sa positionAbsolute
+ * \sa positionAbsolute, scalingAbsolute
*/
/*! \qmlproperty bool Custom3DItem::positionAbsolute
@@ -83,8 +85,33 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*! \qmlproperty vector3d Custom3DItem::scaling
*
* Holds the item \a scaling as a vector3d. Defaults to \c {vector3d(0.1, 0.1, 0.1)}.
- * The default value sets the item to 10% of the height of the graph, provided the item size is
- * normalized.
+ *
+ * Item scaling is either in data values or in absolute values, depending on the
+ * scalingAbsolute property. The default vector interpreted as absolute values sets the item to
+ * 10% of the height of the graph, provided the item mesh is normalized and the graph aspect ratios
+ * haven't been changed from the defaults.
+ *
+ * \sa scalingAbsolute
+ */
+
+/*! \qmlproperty bool Custom3DItem::scalingAbsolute
+ * \since QtDataVisualization 1.2
+ *
+ * This property dictates if item scaling is to be handled in data values or in absolute
+ * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same
+ * size, regardless of axis ranges. Items with data scaling will change their apparent size
+ * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored
+ * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling
+ * is calculated on the unrotated item. Similarly, for Custom3DVolume items, the range clipping
+ * is calculated on the unrotated item.
+ *
+ * \note: Only absolute scaling is supported for Custom3DLabel items or for custom items used in
+ * \l{AbstractGraph3D::polar}{polar} graphs.
+ *
+ * \note: The custom item's mesh must be normalized to range \c{[-1 ,1]}, or the data
+ * scaling will not be accurate.
+ *
+ * \sa scaling, positionAbsolute
*/
/*! \qmlproperty quaternion Custom3DItem::rotation
@@ -180,7 +207,9 @@ QString QCustom3DItem::meshFile() const
* positionAbsolute property. When using absolute coordinates, values between \c{-1.0...1.0} are
* within axis ranges.
*
- * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}.
+ * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false},
+ * unless the item is a QCustom3DVolume that would be partially visible and scalingAbsolute is also
+ * \c{false}. In that case, the visible portion of the volume will be rendered.
*
* \sa positionAbsolute
*/
@@ -211,7 +240,7 @@ void QCustom3DItem::setPositionAbsolute(bool positionAbsolute)
{
if (d_ptr->m_positionAbsolute != positionAbsolute) {
d_ptr->m_positionAbsolute = positionAbsolute;
- d_ptr->m_dirtyBits.positionAbsoluteDirty = true;
+ d_ptr->m_dirtyBits.positionDirty = true;
emit positionAbsoluteChanged(positionAbsolute);
emit d_ptr->needUpdate();
}
@@ -225,8 +254,13 @@ bool QCustom3DItem::isPositionAbsolute() const
/*! \property QCustom3DItem::scaling
*
* Holds the item \a scaling as a QVector3D. Defaults to \c {QVector3D(0.1, 0.1, 0.1)}.
- * The default value sets the item to 10% of the height of the graph, provided the item size is
- * normalized.
+ *
+ * Item scaling is either in data values or in absolute values, depending on the
+ * scalingAbsolute property. The default vector interpreted as absolute values sets the item to
+ * 10% of the height of the graph, provided the item mesh is normalized and the graph aspect ratios
+ * haven't been changed from the defaults.
+ *
+ * \sa scalingAbsolute
*/
void QCustom3DItem::setScaling(const QVector3D &scaling)
{
@@ -243,6 +277,42 @@ QVector3D QCustom3DItem::scaling() const
return d_ptr->m_scaling;
}
+/*! \property QCustom3DItem::scalingAbsolute
+ * \since QtDataVisualization 1.2
+ *
+ * This property dictates if item scaling is to be handled in data values or in absolute
+ * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same
+ * size, regardless of axis ranges. Items with data scaling will change their apparent size
+ * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored
+ * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling
+ * is calculated on the unrotated item. Similarly, for QCustom3DVolume items, the range clipping
+ * is calculated on the unrotated item.
+ *
+ * \note: Only absolute scaling is supported for QCustom3DLabel items or for custom items used in
+ * \l{QAbstract3DGraph::polar}{polar} graphs.
+ *
+ * \note: The custom item's mesh must be normalized to range \c{[-1 ,1]}, or the data
+ * scaling will not be accurate.
+ *
+ * \sa scaling, positionAbsolute
+ */
+void QCustom3DItem::setScalingAbsolute(bool scalingAbsolute)
+{
+ if (d_ptr->m_isLabelItem && !scalingAbsolute) {
+ qWarning() << __FUNCTION__ << "Data bounds are not supported for label items.";
+ } else if (d_ptr->m_scalingAbsolute != scalingAbsolute) {
+ d_ptr->m_scalingAbsolute = scalingAbsolute;
+ d_ptr->m_dirtyBits.scalingDirty = true;
+ emit scalingAbsoluteChanged(scalingAbsolute);
+ emit d_ptr->needUpdate();
+ }
+}
+
+bool QCustom3DItem::isScalingAbsolute() const
+{
+ return d_ptr->m_scalingAbsolute;
+}
+
/*! \property QCustom3DItem::rotation
*
* Holds the item \a rotation as a QQuaternion. Defaults to \c {QQuaternion(0.0, 0.0, 0.0, 0.0)}.
@@ -367,13 +437,16 @@ QString QCustom3DItem::textureFile() const
QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) :
q_ptr(q),
+ m_textureImage(QImage(1, 1, QImage::Format_ARGB32)),
m_position(QVector3D(0.0f, 0.0f, 0.0f)),
m_positionAbsolute(false),
m_scaling(QVector3D(0.1f, 0.1f, 0.1f)),
- m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)),
+ m_scalingAbsolute(true),
+ m_rotation(identityQuaternion),
m_visible(true),
m_shadowCasting(true),
- m_isLabelItem(false)
+ m_isLabelItem(false),
+ m_isVolumeItem(false)
{
}
@@ -381,14 +454,17 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &mesh
const QVector3D &position, const QVector3D &scaling,
const QQuaternion &rotation) :
q_ptr(q),
+ m_textureImage(QImage(1, 1, QImage::Format_ARGB32)),
m_meshFile(meshFile),
m_position(position),
m_positionAbsolute(false),
m_scaling(scaling),
+ m_scalingAbsolute(true),
m_rotation(rotation),
m_visible(true),
m_shadowCasting(true),
- m_isLabelItem(false)
+ m_isLabelItem(false),
+ m_isVolumeItem(false)
{
}
@@ -412,7 +488,6 @@ void QCustom3DItemPrivate::resetDirtyBits()
m_dirtyBits.textureDirty = false;
m_dirtyBits.meshDirty = false;
m_dirtyBits.positionDirty = false;
- m_dirtyBits.positionAbsoluteDirty = false;
m_dirtyBits.scalingDirty = false;
m_dirtyBits.rotationDirty = false;
m_dirtyBits.visibleDirty = false;
diff --git a/src/datavisualization/data/qcustom3ditem.h b/src/datavisualization/data/qcustom3ditem.h
index 2f7f37cf..5c880213 100644
--- a/src/datavisualization/data/qcustom3ditem.h
+++ b/src/datavisualization/data/qcustom3ditem.h
@@ -39,6 +39,7 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DItem : public QObject
Q_PROPERTY(QQuaternion rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
Q_PROPERTY(bool shadowCasting READ isShadowCasting WRITE setShadowCasting NOTIFY shadowCastingChanged)
+ Q_PROPERTY(bool scalingAbsolute READ isScalingAbsolute WRITE setScalingAbsolute NOTIFY scalingAbsoluteChanged REVISION 1)
public:
explicit QCustom3DItem(QObject *parent = 0);
@@ -62,6 +63,9 @@ public:
void setScaling(const QVector3D &scaling);
QVector3D scaling() const;
+ void setScalingAbsolute(bool scalingAbsolute);
+ bool isScalingAbsolute() const;
+
void setRotation(const QQuaternion &rotation);
QQuaternion rotation();
@@ -84,6 +88,7 @@ signals:
void rotationChanged(const QQuaternion &rotation);
void visibleChanged(bool visible);
void shadowCastingChanged(bool shadowCasting);
+ Q_REVISION(1) void scalingAbsoluteChanged(bool scalingAbsolute);
protected:
QCustom3DItem(QCustom3DItemPrivate *d, QObject *parent = 0);
diff --git a/src/datavisualization/data/qcustom3ditem_p.h b/src/datavisualization/data/qcustom3ditem_p.h
index c1ce5996..627bf53f 100644
--- a/src/datavisualization/data/qcustom3ditem_p.h
+++ b/src/datavisualization/data/qcustom3ditem_p.h
@@ -29,6 +29,7 @@
#ifndef QCUSTOM3DITEM_P_H
#define QCUSTOM3DITEM_P_H
+#include "datavisualizationglobal_p.h"
#include "qcustom3ditem.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -37,7 +38,6 @@ struct QCustomItemDirtyBitField {
bool textureDirty : 1;
bool meshDirty : 1;
bool positionDirty : 1;
- bool positionAbsoluteDirty : 1;
bool scalingDirty : 1;
bool rotationDirty : 1;
bool visibleDirty : 1;
@@ -47,7 +47,6 @@ struct QCustomItemDirtyBitField {
: textureDirty(false),
meshDirty(false),
positionDirty(false),
- positionAbsoluteDirty(false),
scalingDirty(false),
rotationDirty(false),
visibleDirty(false),
@@ -77,11 +76,13 @@ public:
QVector3D m_position;
bool m_positionAbsolute;
QVector3D m_scaling;
+ bool m_scalingAbsolute;
QQuaternion m_rotation;
bool m_visible;
bool m_shadowCasting;
bool m_isLabelItem;
+ bool m_isVolumeItem;
QCustomItemDirtyBitField m_dirtyBits;
diff --git a/src/datavisualization/data/qcustom3dlabel.cpp b/src/datavisualization/data/qcustom3dlabel.cpp
index 85a37e2d..bb5d71d0 100644
--- a/src/datavisualization/data/qcustom3dlabel.cpp
+++ b/src/datavisualization/data/qcustom3dlabel.cpp
@@ -44,6 +44,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \since QtDataVisualization 1.1
* \ingroup datavisualization_qml
* \instantiates QCustom3DLabel
+ * \inherits Custom3DItem
* \brief The Custom3DLabel type is for creating custom labels to be added to a graph.
*
* This type is for creating custom labels to be added to a graph. You can set text, font,
diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp
new file mode 100644
index 00000000..f8287a81
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume.cpp
@@ -0,0 +1,1278 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "qcustom3dvolume_p.h"
+#include "utils_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+/*!
+ * \class QCustom3DVolume
+ * \inmodule QtDataVisualization
+ * \brief The QCustom3DVolume class is for creating volume rendered objects to be added to a graph.
+ * \since QtDataVisualization 1.2
+ *
+ * This class is for creating volume rendered objects to be added to a graph. A volume rendered
+ * object is a box with a 3D texture. Three slice planes are supported for the volume, one along
+ * each main axis of the volume.
+ *
+ * Rendering volume objects is very performance intensive, especially when the volume is largely
+ * transparent, as the contents of the volume are ray-traced. The performance scales nearly linearly
+ * with the amount of pixels that the volume occupies on the screen, so showing the volume in a
+ * smaller view or limiting the zoom level of the graph are easy ways to improve performance.
+ * Similarly, the volume texture dimensions have a large impact on performance.
+ * If the frame rate is more important than pixel-perfect rendering of the volume contents, consider
+ * turning the high definition shader off by setting useHighDefShader property to \c{false}.
+ *
+ * \note Volumetric objects are only supported with orthographic projection.
+ *
+ * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments.
+ *
+ * \sa QAbstract3DGraph::addCustomItem(), QAbstract3DGraph::orthoProjection, useHighDefShader
+ */
+
+/*!
+ * \qmltype Custom3DVolume
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.2
+ * \ingroup datavisualization_qml
+ * \instantiates QCustom3DVolume
+ * \inherits Custom3DItem
+ * \brief The Custom3DVolume type is for creating volume rendered objects to be added to a graph.
+ *
+ * This class is for creating volume rendered objects to be added to a graph. A volume rendered
+ * object is a box with a 3D texture. Three slice planes are supported for the volume, one along
+ * each main axis of the volume.
+ *
+ * Rendering volume objects is very performance intensive, especially when the volume is largely
+ * transparent, as the contents of the volume are ray-traced. The performance scales nearly linearly
+ * with the amount of pixels that the volume occupies on the screen, so showing the volume in a
+ * smaller view or limiting the zoom level of the graph are easy ways to improve performance.
+ * Similarly, the volume texture dimensions have a large impact on performance.
+ * If the frame rate is more important than pixel-perfect rendering of the volume contents, consider
+ * turning the high definition shader off by setting useHighDefShader property to \c{false}.
+ *
+ * \note: Filling in the volume data would not typically be efficient or practical from pure QML,
+ * so properties directly related to that are not fully supported from QML.
+ * Make a hybrid QML/C++ application if you want to use volume objects with a QML UI.
+ *
+ * \note Volumetric objects are only supported with orthographic projection.
+ *
+ * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments.
+ *
+ * \sa AbstractGraph3D::orthoProjection, useHighDefShader
+ */
+
+/*! \qmlproperty int Custom3DVolume::textureWidth
+ *
+ * The width of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note: Changing this property from QML is not supported, as the texture data cannot be resized
+ * to match.
+ */
+
+/*! \qmlproperty int Custom3DVolume::textureHeight
+ *
+ * The height of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note: Changing this property from QML is not supported, as the texture data cannot be resized
+ * to match.
+ */
+
+/*! \qmlproperty int Custom3DVolume::textureDepth
+ *
+ * The depth of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note: Changing this property from QML is not supported, as the texture data cannot be resized
+ * to match.
+ */
+
+/*! \qmlproperty int Custom3DVolume::sliceIndexX
+ *
+ * The X dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames
+ */
+
+/*! \qmlproperty int Custom3DVolume::sliceIndexY
+ *
+ * The Y dimension index into the texture data indicating which horizontal slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames
+ */
+
+/*! \qmlproperty int Custom3DVolume::sliceIndexZ
+ *
+ * The Z dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty real Custom3DVolume::alphaMultiplier
+ *
+ * The alpha value of every texel of the volume texture is multiplied with this value at
+ * the render time. This can be used to introduce uniform transparency to the volume.
+ * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are
+ * affected, and fully opaque texels are not affected.
+ * The value must not be negative.
+ * Defaults to \c{1.0}.
+ *
+ * \sa preserveOpacity
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::preserveOpacity
+ *
+ * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have
+ * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all
+ * texels.
+ * Defaults to \c{true}.
+ *
+ * \sa alphaMultiplier
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::useHighDefShader
+ *
+ * If this property value is \c{true}, a high definition shader is used to render the volume.
+ * If it is \c{false}, a low definition shader is used.
+ *
+ * The high definition shader guarantees that every visible texel of the volume texture is sampled
+ * when the volume is rendered.
+ * The low definition shader renders only a rough approximation of the volume contents,
+ * but at much higher frame rate. The low definition shader doesn't guarantee every texel of the
+ * volume texture is sampled, so there may be flickering if the volume contains distinct thin
+ * features.
+ *
+ * \note This value doesn't affect the level of detail when rendering the slices of the volume.
+ *
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::drawSlices
+ *
+ * If this property value is \c{true}, the slices indicated by slice index properties
+ * will be drawn instead of the full volume.
+ * If it is \c{false}, the full volume will always be drawn.
+ * Defaults to \c{false}.
+ *
+ * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are
+ * rotated as well.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::drawSliceFrames
+ *
+ * If this property value is \c{true}, the frames of slices indicated by slice index properties
+ * will be drawn around the volume.
+ * If it is \c{false}, no slice frames will be drawn.
+ * Drawing slice frames is independent of drawing slices, so you can show the full volume and
+ * still draw the slice frames around it.
+ * Defaults to \c{false}.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices
+ */
+
+/*!
+ * \qmlproperty color Custom3DVolume::sliceFrameColor
+ *
+ * Indicates the color of the slice frame. Transparent slice frame color is not supported.
+ *
+ * Defaults to black.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty vector3d Custom3DVolume::sliceFrameWidths
+ *
+ * Indicates the widths of the slice frame. The width can be different on different dimensions,
+ * so you can for example omit drawing the frames on certain sides of the volume by setting the
+ * value for that dimension to zero. The values are fractions of the volume thickness in the same
+ * dimension. The values cannot be negative.
+ *
+ * Defaults to \c{vector3d(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty vector3d Custom3DVolume::sliceFrameGaps
+ *
+ * Indicates the amount of air gap left between the volume itself and the frame in each dimension.
+ * The gap can be different on different dimensions. The values are fractions of the volume
+ * thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{vector3d(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty vector3d Custom3DVolume::sliceFrameThicknesses
+ *
+ * Indicates the thickness of the slice frames for each dimension. The values are fractions of
+ * the volume thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{vector3d(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * Constructs QCustom3DVolume with given \a parent.
+ */
+QCustom3DVolume::QCustom3DVolume(QObject *parent) :
+ QCustom3DItem(new QCustom3DVolumePrivate(this), parent)
+{
+}
+
+/*!
+ * Constructs QCustom3DVolume with given \a position, \a scaling, \a rotation,
+ * \a textureWidth, \a textureHeight, \a textureDepth, \a textureData, \a textureFormat,
+ * \a colorTable, and optional \a parent.
+ *
+ * \sa textureData, setTextureFormat(), colorTable
+ */
+QCustom3DVolume::QCustom3DVolume(const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation, int textureWidth,
+ int textureHeight, int textureDepth,
+ QVector<uchar> *textureData, QImage::Format textureFormat,
+ const QVector<QRgb> &colorTable, QObject *parent) :
+ QCustom3DItem(new QCustom3DVolumePrivate(this, position, scaling, rotation, textureWidth,
+ textureHeight, textureDepth,
+ textureData, textureFormat, colorTable), parent)
+{
+}
+
+
+/*!
+ * Destroys QCustom3DVolume.
+ */
+QCustom3DVolume::~QCustom3DVolume()
+{
+}
+
+/*! \property QCustom3DVolume::textureWidth
+ *
+ * The width of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note The textureData may need to be resized or recreated if this value is changed.
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, textureHeight, textureDepth, setTextureFormat(), textureDataWidth()
+ */
+void QCustom3DVolume::setTextureWidth(int value)
+{
+ if (value >= 0) {
+ if (dptr()->m_textureWidth != value) {
+ dptr()->m_textureWidth = value;
+ dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true;
+ emit textureWidthChanged(value);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Cannot set negative value.";
+ }
+}
+
+int QCustom3DVolume::textureWidth() const
+{
+ return dptrc()->m_textureWidth;
+}
+
+/*! \property QCustom3DVolume::textureHeight
+ *
+ * The height of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note The textureData may need to be resized or recreated if this value is changed.
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, textureWidth, textureDepth, setTextureFormat()
+ */
+void QCustom3DVolume::setTextureHeight(int value)
+{
+ if (value >= 0) {
+ if (dptr()->m_textureHeight != value) {
+ dptr()->m_textureHeight = value;
+ dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true;
+ emit textureHeightChanged(value);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Cannot set negative value.";
+ }
+
+}
+
+int QCustom3DVolume::textureHeight() const
+{
+ return dptrc()->m_textureHeight;
+}
+
+/*! \property QCustom3DVolume::textureDepth
+ *
+ * The depth of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note The textureData may need to be resized or recreated if this value is changed.
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, textureWidth, textureHeight, setTextureFormat()
+ */
+void QCustom3DVolume::setTextureDepth(int value)
+{
+ if (value >= 0) {
+ if (dptr()->m_textureDepth != value) {
+ dptr()->m_textureDepth = value;
+ dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true;
+ emit textureDepthChanged(value);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Cannot set negative value.";
+ }
+}
+
+int QCustom3DVolume::textureDepth() const
+{
+ return dptrc()->m_textureDepth;
+}
+
+/*!
+ * A convenience function for setting all three texture dimensions
+ * (\a width, \a height, and \a depth) at once.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setTextureDimensions(int width, int height, int depth)
+{
+ setTextureWidth(width);
+ setTextureHeight(height);
+ setTextureDepth(depth);
+}
+
+/*!
+ * \return the actual texture data width. When the texture format is QImage::Format_Indexed8,
+ * this is textureWidth aligned to 32bit boundary. Otherwise this is four times textureWidth.
+ */
+int QCustom3DVolume::textureDataWidth() const
+{
+ int dataWidth = dptrc()->m_textureWidth;
+
+ if (dptrc()->m_textureFormat == QImage::Format_Indexed8)
+ dataWidth += dataWidth % 4;
+ else
+ dataWidth *= 4;
+
+ return dataWidth;
+}
+
+/*! \property QCustom3DVolume::sliceIndexX
+ *
+ * The X dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData, drawSlices, drawSliceFrames
+ */
+void QCustom3DVolume::setSliceIndexX(int value)
+{
+ if (dptr()->m_sliceIndexX != value) {
+ dptr()->m_sliceIndexX = value;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceIndexXChanged(value);
+ emit dptr()->needUpdate();
+ }
+}
+
+int QCustom3DVolume::sliceIndexX() const
+{
+ return dptrc()->m_sliceIndexX;
+}
+
+/*! \property QCustom3DVolume::sliceIndexY
+ *
+ * The Y dimension index into the texture data indicating which horizontal slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData, drawSlices, drawSliceFrames
+ */
+void QCustom3DVolume::setSliceIndexY(int value)
+{
+ if (dptr()->m_sliceIndexY != value) {
+ dptr()->m_sliceIndexY = value;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceIndexYChanged(value);
+ emit dptr()->needUpdate();
+ }
+}
+
+int QCustom3DVolume::sliceIndexY() const
+{
+ return dptrc()->m_sliceIndexY;
+}
+
+/*! \property QCustom3DVolume::sliceIndexZ
+ *
+ * The Z dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData, drawSlices, drawSliceFrames
+ */
+void QCustom3DVolume::setSliceIndexZ(int value)
+{
+ if (dptr()->m_sliceIndexZ != value) {
+ dptr()->m_sliceIndexZ = value;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceIndexZChanged(value);
+ emit dptr()->needUpdate();
+ }
+}
+
+int QCustom3DVolume::sliceIndexZ() const
+{
+ return dptrc()->m_sliceIndexZ;
+}
+
+/*!
+ * A convenience function for setting all three slice indices (\a x, \a y, and \a z) at once.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setSliceIndices(int x, int y, int z)
+{
+ setSliceIndexX(x);
+ setSliceIndexY(y);
+ setSliceIndexZ(z);
+}
+
+/*! \property QCustom3DVolume::colorTable
+ *
+ * The array containing the colors for indexed texture formats. If the texture format is not
+ * indexed, this array is not used and can be empty.
+ *
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, setTextureFormat(), QImage::colorTable()
+ */
+void QCustom3DVolume::setColorTable(const QVector<QRgb> &colors)
+{
+ if (dptr()->m_colorTable != colors) {
+ dptr()->m_colorTable = colors;
+ dptr()->m_dirtyBitsVolume.colorTableDirty = true;
+ emit colorTableChanged();
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector<QRgb> QCustom3DVolume::colorTable() const
+{
+ return dptrc()->m_colorTable;
+}
+
+/*! \property QCustom3DVolume::textureData
+ *
+ * The array containing the texture data in the format specified by textureFormat.
+ * The size of this array must be at least
+ * (\c{textureDataWidth * textureHeight * textureDepth * texture format color depth in bytes}).
+ *
+ * A 3D texture is defined by a stack of 2D subtextures. Each subtexture must be of identical size
+ * (\c{textureDataWidth * textureHeight}), and the depth of the stack is defined by textureDepth
+ * property. Each 2D texture data is identical to a QImage data with the same format, so
+ * QImage::bits() can be used to supply the data for each subtexture.
+ *
+ * Ownership of the new array transfers to QCustom3DVolume instance.
+ * If another array is set, the previous array is deleted.
+ * If the same array is set again, it is assumed that the array contents have been changed and the
+ * graph rendering is triggered.
+ *
+ * \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is
+ * QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need
+ * to be added to each X-line of the \a data. You can get the padded byte count with
+ * textureDataWidth() function. The padding bytes should indicate an fully transparent color
+ * to avoid rendering artifacts.
+ *
+ * Defaults to \c{0}.
+ *
+ * \sa colorTable, setTextureFormat(), setSubTextureData(), textureDataWidth()
+ */
+void QCustom3DVolume::setTextureData(QVector<uchar> *data)
+{
+ if (dptr()->m_textureData != data)
+ delete dptr()->m_textureData;
+
+ // Even if the pointer is same as previously, consider this property changed, as the values
+ // can be changed unbeknownst to us via the array pointer.
+ dptr()->m_textureData = data;
+ dptr()->m_dirtyBitsVolume.textureDataDirty = true;
+ emit textureDataChanged(data);
+ emit dptr()->needUpdate();
+}
+
+/*!
+ * This function creates a new texture data array from an array of \a images and sets it as
+ * textureData for this volume object. The texture dimensions are also set according to image
+ * and array dimensions. All of the images in the array must be the same size. If the images are not
+ * all in QImage::Format_Indexed8 format, all texture data will be converted into
+ * QImage::Format_ARGB32 format. If the images are in QImage::Format_Indexed8 format, the colorTable
+ * for the entire volume will be taken from the first image.
+ *
+ * \return pointer to the newly created array.
+ *
+ * \sa textureData, textureWidth, textureHeight, textureDepth, setTextureFormat()
+ */
+QVector<uchar> *QCustom3DVolume::createTextureData(const QVector<QImage *> &images)
+{
+ int imageCount = images.size();
+ if (imageCount) {
+ QImage *currentImage = images.at(0);
+ int imageWidth = currentImage->width();
+ int imageHeight = currentImage->height();
+ QImage::Format imageFormat = currentImage->format();
+ bool convert = false;
+ if (imageFormat != QImage::Format_Indexed8 && imageFormat != QImage::Format_ARGB32) {
+ convert = true;
+ imageFormat = QImage::Format_ARGB32;
+ } else {
+ for (int i = 0; i < imageCount; i++) {
+ currentImage = images.at(i);
+ if (imageWidth != currentImage->width() || imageHeight != currentImage->height()) {
+ qWarning() << __FUNCTION__ << "Not all images were of the same size.";
+ setTextureData(0);
+ setTextureWidth(0);
+ setTextureHeight(0);
+ setTextureDepth(0);
+ return 0;
+
+ }
+ if (currentImage->format() != imageFormat) {
+ convert = true;
+ imageFormat = QImage::Format_ARGB32;
+ break;
+ }
+ }
+ }
+ int colorBytes = (imageFormat == QImage::Format_Indexed8) ? 1 : 4;
+ int imageByteWidth = (imageFormat == QImage::Format_Indexed8)
+ ? currentImage->bytesPerLine() : imageWidth;
+ int frameSize = imageByteWidth * imageHeight * colorBytes;
+ QVector<uchar> *newTextureData = new QVector<uchar>;
+ newTextureData->resize(frameSize * imageCount);
+ uchar *texturePtr = newTextureData->data();
+ QImage convertedImage;
+
+ for (int i = 0; i < imageCount; i++) {
+ currentImage = images.at(i);
+ if (convert) {
+ convertedImage = currentImage->convertToFormat(imageFormat);
+ currentImage = &convertedImage;
+ }
+ memcpy(texturePtr, static_cast<void *>(currentImage->bits()), frameSize);
+ texturePtr += frameSize;
+ }
+
+ if (imageFormat == QImage::Format_Indexed8)
+ setColorTable(images.at(0)->colorTable());
+ setTextureData(newTextureData);
+ setTextureFormat(imageFormat);
+ setTextureWidth(imageWidth);
+ setTextureHeight(imageHeight);
+ setTextureDepth(imageCount);
+ } else {
+ setTextureData(0);
+ setTextureWidth(0);
+ setTextureHeight(0);
+ setTextureDepth(0);
+ }
+ return dptr()->m_textureData;
+}
+
+QVector<uchar> *QCustom3DVolume::textureData() const
+{
+ return dptrc()->m_textureData;
+}
+
+/*!
+ * This function allows setting a single 2D subtexture of the 3D texture along the specified
+ * \a axis of the volume.
+ * The \a index parameter specifies the subtexture to set.
+ * The texture \a data must be in the format specified by textureFormat property and have size of
+ * the cross-section of the volume texture along the specified axis multiplied by
+ * the texture format color depth in bytes.
+ * The \a data is expected to be ordered similarly to the data in images produced by renderSlice()
+ * method along the same axis.
+ *
+ * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis.
+ * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four,
+ * padding bytes need to be added to each X-line of the \a data in cases it is not already
+ * properly aligned. The padding bytes should indicate an fully transparent color to avoid
+ * rendering artifacts.
+ *
+ * \sa textureData, renderSlice()
+ */
+void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const uchar *data)
+{
+ if (data) {
+ int lineSize = textureDataWidth();
+ int frameSize = lineSize * dptr()->m_textureHeight;
+ int dataSize = dptr()->m_textureData->size();
+ int pixelWidth = (dptr()->m_textureFormat == QImage::Format_Indexed8) ? 1 : 4;
+ int targetIndex;
+ uchar *dataPtr = dptr()->m_textureData->data();
+ bool invalid = (index < 0);
+ if (axis == Qt::XAxis) {
+ targetIndex = index * pixelWidth;
+ if (index >= dptr()->m_textureWidth
+ || (frameSize * (dptr()->m_textureDepth - 1) + targetIndex) > dataSize) {
+ invalid = true;
+ }
+ } else if (axis == Qt::YAxis) {
+ targetIndex = (index * lineSize) + (frameSize * (dptr()->m_textureDepth - 1));
+ if (index >= dptr()->m_textureHeight || (targetIndex + lineSize > dataSize))
+ invalid = true;
+ } else {
+ targetIndex = index * frameSize;
+ if (index >= dptr()->m_textureDepth || ((targetIndex + frameSize) > dataSize))
+ invalid = true;
+ }
+
+ if (invalid) {
+ qWarning() << __FUNCTION__ << "Attempted to set invalid subtexture.";
+ } else {
+ const uchar *sourcePtr = data;
+ uchar *targetPtr = dataPtr + targetIndex;
+ if (axis == Qt::XAxis) {
+ int targetWidth = dptr()->m_textureDepth;
+ int targetHeight = dptr()->m_textureHeight;
+ for (int i = 0; i < targetHeight; i++) {
+ targetPtr = dataPtr + targetIndex + (lineSize * i);
+ for (int j = 0; j < targetWidth; j++) {
+ for (int k = 0; k < pixelWidth; k++)
+ *targetPtr++ = *sourcePtr++;
+ targetPtr += (frameSize - pixelWidth);
+ }
+ }
+ } else if (axis == Qt::YAxis) {
+ int targetHeight = dptr()->m_textureDepth;
+ for (int i = 0; i < targetHeight; i++){
+ for (int j = 0; j < lineSize; j++)
+ *targetPtr++ = *sourcePtr++;
+ targetPtr -= (frameSize + lineSize);
+ }
+ } else {
+ void *subTexPtr = dataPtr + targetIndex;
+ memcpy(subTexPtr, static_cast<const void *>(data), frameSize);
+ }
+ dptr()->m_dirtyBitsVolume.textureDataDirty = true;
+ emit textureDataChanged(dptr()->m_textureData);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Tried to set null data.";
+ }
+}
+
+/*!
+ * This function allows setting a single 2D subtexture of the 3D texture along the specified
+ * \a axis of the volume.
+ * The \a index parameter specifies the subtexture to set.
+ * The source \a image must be in the format specified by the textureFormat property if the
+ * textureFormat is indexed. If the textureFormat is QImage::Format_ARGB32, the image is converted
+ * to that format. The image must have the size of the cross-section of the volume texture along
+ * the specified axis. The orientation of the image should correspond to the orientation of
+ * the slice image produced by renderSlice() method along the same axis.
+ *
+ * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis.
+ * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four,
+ * padding bytes need to be added to each X-line of the \a image in cases it is not already
+ * properly aligned. The padding bytes should indicate an fully transparent color to avoid
+ * rendering artifacts. It is not guaranteed QImage will do this automatically.
+ *
+ * \sa textureData, renderSlice()
+ */
+void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const QImage &image)
+{
+ int sourceWidth = image.width();
+ int sourceHeight = image.height();
+ int targetWidth;
+ int targetHeight;
+ if (axis == Qt::XAxis) {
+ targetWidth = dptr()->m_textureDepth;
+ targetHeight = dptr()->m_textureHeight;
+ } else if (axis == Qt::YAxis) {
+ targetWidth = dptr()->m_textureWidth;
+ targetHeight = dptr()->m_textureDepth;
+ } else {
+ targetWidth = dptr()->m_textureWidth;
+ targetHeight = dptr()->m_textureHeight;
+ }
+
+ if (sourceWidth == targetWidth
+ && sourceHeight == targetHeight
+ && (image.format() == dptr()->m_textureFormat
+ || dptr()->m_textureFormat == QImage::Format_ARGB32)) {
+ QImage convertedImage;
+ if (dptr()->m_textureFormat == QImage::Format_ARGB32
+ && image.format() != QImage::Format_ARGB32) {
+ convertedImage = image.convertToFormat(QImage::Format_ARGB32);
+ } else {
+ convertedImage = image;
+ }
+ setSubTextureData(axis, index, convertedImage.bits());
+ } else {
+ qWarning() << __FUNCTION__ << "Invalid image size or format.";
+ }
+}
+
+// Note: textureFormat is not a Q_PROPERTY to work around an issue in meta object system that
+// doesn't allow QImage::format to be a property type. Qt 5.2.1 at least has this problem.
+
+/*!
+ * Sets the format of the textureData to \a format. Only two formats are supported currently:
+ * QImage::Format_Indexed8 and QImage::Format_ARGB32. If an indexed format is specified, colorTable
+ * must also be set.
+ * Defaults to QImage::Format_ARGB32.
+ *
+ * \sa colorTable, textureData
+ */
+void QCustom3DVolume::setTextureFormat(QImage::Format format)
+{
+ if (format == QImage::Format_ARGB32 || format == QImage::Format_Indexed8) {
+ if (dptr()->m_textureFormat != format) {
+ dptr()->m_textureFormat = format;
+ dptr()->m_dirtyBitsVolume.textureFormatDirty = true;
+ emit textureFormatChanged(format);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Attempted to set invalid texture format.";
+ }
+}
+
+/*!
+ * \return the format of the textureData.
+ *
+ * \sa setTextureFormat()
+ */
+QImage::Format QCustom3DVolume::textureFormat() const
+{
+ return dptrc()->m_textureFormat;
+}
+
+/*!
+ * \fn void QCustom3DVolume::textureFormatChanged(QImage::Format format)
+ *
+ * This signal is emitted when the textureData \a format changes.
+ *
+ * \sa setTextureFormat()
+ */
+
+/*!
+ * \property QCustom3DVolume::alphaMultiplier
+ *
+ * The alpha value of every texel of the volume texture is multiplied with this value at
+ * the render time. This can be used to introduce uniform transparency to the volume.
+ * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are
+ * affected, and fully opaque texels are not affected.
+ * The value must not be negative.
+ * Defaults to \c{1.0f}.
+ *
+ * \sa preserveOpacity, textureData
+ */
+void QCustom3DVolume::setAlphaMultiplier(float mult)
+{
+ if (mult >= 0.0f) {
+ if (dptr()->m_alphaMultiplier != mult) {
+ dptr()->m_alphaMultiplier = mult;
+ dptr()->m_dirtyBitsVolume.alphaDirty = true;
+ emit alphaMultiplierChanged(mult);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Attempted to set negative multiplier.";
+ }
+}
+
+float QCustom3DVolume::alphaMultiplier() const
+{
+ return dptrc()->m_alphaMultiplier;
+}
+
+/*!
+ * \property QCustom3DVolume::preserveOpacity
+ *
+ * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have
+ * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all
+ * texels.
+ * Defaults to \c{true}.
+ *
+ * \sa alphaMultiplier
+ */
+void QCustom3DVolume::setPreserveOpacity(bool enable)
+{
+ if (dptr()->m_preserveOpacity != enable) {
+ dptr()->m_preserveOpacity = enable;
+ dptr()->m_dirtyBitsVolume.alphaDirty = true;
+ emit preserveOpacityChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::preserveOpacity() const
+{
+ return dptrc()->m_preserveOpacity;
+}
+
+/*!
+ * \property QCustom3DVolume::useHighDefShader
+ *
+ * If this property value is \c{true}, a high definition shader is used to render the volume.
+ * If it is \c{false}, a low definition shader is used.
+ *
+ * The high definition shader guarantees that every visible texel of the volume texture is sampled
+ * when the volume is rendered.
+ * The low definition shader renders only a rough approximation of the volume contents,
+ * but at much higher frame rate. The low definition shader doesn't guarantee every texel of the
+ * volume texture is sampled, so there may be flickering if the volume contains distinct thin
+ * features.
+ *
+ * \note This value doesn't affect the level of detail when rendering the slices of the volume.
+ *
+ * Defaults to \c{true}.
+ *
+ * \sa renderSlice()
+ */
+void QCustom3DVolume::setUseHighDefShader(bool enable)
+{
+ if (dptr()->m_useHighDefShader != enable) {
+ dptr()->m_useHighDefShader = enable;
+ dptr()->m_dirtyBitsVolume.shaderDirty = true;
+ emit useHighDefShaderChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::useHighDefShader() const
+{
+ return dptrc()->m_useHighDefShader;
+}
+
+/*!
+ * \property QCustom3DVolume::drawSlices
+ *
+ * If this property value is \c{true}, the slices indicated by slice index properties
+ * will be drawn instead of the full volume.
+ * If it is \c{false}, the full volume will always be drawn.
+ * Defaults to \c{false}.
+ *
+ * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are
+ * rotated as well.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ
+ */
+void QCustom3DVolume::setDrawSlices(bool enable)
+{
+ if (dptr()->m_drawSlices != enable) {
+ dptr()->m_drawSlices = enable;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit drawSlicesChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::drawSlices() const
+{
+ return dptrc()->m_drawSlices;
+}
+
+/*!
+ * \property QCustom3DVolume::drawSliceFrames
+ *
+ * If this property value is \c{true}, the frames of slices indicated by slice index properties
+ * will be drawn around the volume.
+ * If it is \c{false}, no slice frames will be drawn.
+ * Drawing slice frames is independent of drawing slices, so you can show the full volume and
+ * still draw the slice frames around it. This is useful when using renderSlice() to display the
+ * slices outside the graph itself.
+ * Defaults to \c{false}.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices, renderSlice()
+ */
+void QCustom3DVolume::setDrawSliceFrames(bool enable)
+{
+ if (dptr()->m_drawSliceFrames != enable) {
+ dptr()->m_drawSliceFrames = enable;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit drawSliceFramesChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::drawSliceFrames() const
+{
+ return dptrc()->m_drawSliceFrames;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameColor
+ *
+ * Indicates the color of the slice frame. Transparent slice frame color is not supported.
+ *
+ * Defaults to black.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameColor(const QColor &color)
+{
+ if (dptr()->m_sliceFrameColor != color) {
+ dptr()->m_sliceFrameColor = color;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameColorChanged(color);
+ emit dptr()->needUpdate();
+ }
+}
+
+QColor QCustom3DVolume::sliceFrameColor() const
+{
+ return dptrc()->m_sliceFrameColor;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameWidths
+ *
+ * Indicates the widths of the slice frame. The width can be different on different dimensions,
+ * so you can for example omit drawing the frames on certain sides of the volume by setting the
+ * value for that dimension to zero. The values are fractions of the volume thickness in the same
+ * dimension. The values cannot be negative.
+ *
+ * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameWidths(const QVector3D &values)
+{
+ if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) {
+ qWarning() << __FUNCTION__ << "Attempted to set negative values.";
+ } else if (dptr()->m_sliceFrameWidths != values) {
+ dptr()->m_sliceFrameWidths = values;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameWidthsChanged(values);
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector3D QCustom3DVolume::sliceFrameWidths() const
+{
+ return dptrc()->m_sliceFrameWidths;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameGaps
+ *
+ * Indicates the amount of air gap left between the volume itself and the frame in each dimension.
+ * The gap can be different on different dimensions. The values are fractions of the volume
+ * thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameGaps(const QVector3D &values)
+{
+ if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) {
+ qWarning() << __FUNCTION__ << "Attempted to set negative values.";
+ } else if (dptr()->m_sliceFrameGaps != values) {
+ dptr()->m_sliceFrameGaps = values;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameGapsChanged(values);
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector3D QCustom3DVolume::sliceFrameGaps() const
+{
+ return dptrc()->m_sliceFrameGaps;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameThicknesses
+ *
+ * Indicates the thickness of the slice frames for each dimension. The values are fractions of
+ * the volume thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameThicknesses(const QVector3D &values)
+{
+ if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) {
+ qWarning() << __FUNCTION__ << "Attempted to set negative values.";
+ } else if (dptr()->m_sliceFrameThicknesses != values) {
+ dptr()->m_sliceFrameThicknesses = values;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameThicknessesChanged(values);
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector3D QCustom3DVolume::sliceFrameThicknesses() const
+{
+ return dptrc()->m_sliceFrameThicknesses;
+}
+
+/*!
+ * Renders the slice specified by \a index along \a axis into an image.
+ * The texture format of this object is used.
+ *
+ * \return the rendered image of the slice, or a null image if invalid index is specified.
+ *
+ * \sa setTextureFormat()
+ */
+QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index)
+{
+ return dptr()->renderSlice(axis, index);
+}
+
+/*!
+ * \internal
+ */
+QCustom3DVolumePrivate *QCustom3DVolume::dptr()
+{
+ return static_cast<QCustom3DVolumePrivate *>(d_ptr.data());
+}
+
+/*!
+ * \internal
+ */
+const QCustom3DVolumePrivate *QCustom3DVolume::dptrc() const
+{
+ return static_cast<const QCustom3DVolumePrivate *>(d_ptr.data());
+}
+
+QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) :
+ QCustom3DItemPrivate(q),
+ m_textureWidth(0),
+ m_textureHeight(0),
+ m_textureDepth(0),
+ m_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_textureFormat(QImage::Format_ARGB32),
+ m_textureData(0),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true),
+ m_useHighDefShader(true),
+ m_drawSlices(false),
+ m_drawSliceFrames(false),
+ m_sliceFrameColor(Qt::black),
+ m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f))
+{
+ m_isVolumeItem = true;
+ m_meshFile = QStringLiteral(":/defaultMeshes/barFull");
+}
+
+QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector3D &position,
+ const QVector3D &scaling,
+ const QQuaternion &rotation,
+ int textureWidth, int textureHeight,
+ int textureDepth, QVector<uchar> *textureData,
+ QImage::Format textureFormat,
+ const QVector<QRgb> &colorTable) :
+ QCustom3DItemPrivate(q, QStringLiteral(":/defaultMeshes/barFull"), position, scaling, rotation),
+ m_textureWidth(textureWidth),
+ m_textureHeight(textureHeight),
+ m_textureDepth(textureDepth),
+ m_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_textureFormat(textureFormat),
+ m_colorTable(colorTable),
+ m_textureData(textureData),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true),
+ m_useHighDefShader(true),
+ m_drawSlices(false),
+ m_drawSliceFrames(false),
+ m_sliceFrameColor(Qt::black),
+ m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f))
+{
+ m_isVolumeItem = true;
+ m_shadowCasting = false;
+
+ if (m_textureWidth < 0)
+ m_textureWidth = 0;
+ if (m_textureHeight < 0)
+ m_textureHeight = 0;
+ if (m_textureDepth < 0)
+ m_textureDepth = 0;
+
+ if (m_textureFormat != QImage::Format_Indexed8)
+ m_textureFormat = QImage::Format_ARGB32;
+
+}
+
+QCustom3DVolumePrivate::~QCustom3DVolumePrivate()
+{
+ delete m_textureData;
+}
+
+void QCustom3DVolumePrivate::resetDirtyBits()
+{
+ QCustom3DItemPrivate::resetDirtyBits();
+
+ m_dirtyBitsVolume.textureDimensionsDirty = false;
+ m_dirtyBitsVolume.slicesDirty = false;
+ m_dirtyBitsVolume.colorTableDirty = false;
+ m_dirtyBitsVolume.textureDataDirty = false;
+ m_dirtyBitsVolume.textureFormatDirty = false;
+ m_dirtyBitsVolume.alphaDirty = false;
+ m_dirtyBitsVolume.shaderDirty = false;
+}
+
+QImage QCustom3DVolumePrivate::renderSlice(Qt::Axis axis, int index)
+{
+ if (index < 0)
+ return QImage();
+
+ int x;
+ int y;
+ if (axis == Qt::XAxis) {
+ if (index >= m_textureWidth)
+ return QImage();
+ x = m_textureDepth;
+ y = m_textureHeight;
+ } else if (axis == Qt::YAxis) {
+ if (index >= m_textureHeight)
+ return QImage();
+ x = m_textureWidth;
+ y = m_textureDepth;
+ } else {
+ if (index >= m_textureDepth)
+ return QImage();
+ x = m_textureWidth;
+ y = m_textureHeight;
+ }
+
+ int padding = 0;
+ int pixelWidth = 4;
+ int dataWidth = qptr()->textureDataWidth();
+ if (m_textureFormat == QImage::Format_Indexed8) {
+ padding = x % 4;
+ pixelWidth = 1;
+ }
+ QVector<uchar> data((x + padding) * y * pixelWidth);
+ int frameSize = qptr()->textureDataWidth() * m_textureHeight;
+
+ int dataIndex = 0;
+ if (axis == Qt::XAxis) {
+ for (int i = 0; i < y; i++) {
+ const uchar *p = m_textureData->constData()
+ + (index * pixelWidth) + (dataWidth * i);
+ for (int j = 0; j < x; j++) {
+ for (int k = 0; k < pixelWidth; k++)
+ data[dataIndex++] = *(p + k);
+ p += frameSize;
+ }
+ }
+ } else if (axis == Qt::YAxis) {
+ for (int i = y - 1; i >= 0; i--) {
+ const uchar *p = m_textureData->constData() + (index * dataWidth)
+ + (frameSize * i);
+ for (int j = 0; j < (x * pixelWidth); j++) {
+ data[dataIndex++] = *p;
+ p++;
+ }
+ }
+ } else {
+ for (int i = 0; i < y; i++) {
+ const uchar *p = m_textureData->constData() + (index * frameSize) + (dataWidth * i);
+ for (int j = 0; j < (x * pixelWidth); j++) {
+ data[dataIndex++] = *p;
+ p++;
+ }
+ }
+ }
+
+ if (m_textureFormat != QImage::Format_Indexed8 && m_alphaMultiplier != 1.0f) {
+ for (int i = pixelWidth - 1; i < data.size(); i += pixelWidth)
+ data[i] = static_cast<uchar>(multipliedAlphaValue(data.at(i)));
+ }
+
+ QImage image(data.constData(), x, y, x * pixelWidth, m_textureFormat);
+ image.bits(); // Call bits() to detach the new image from local data
+ if (m_textureFormat == QImage::Format_Indexed8) {
+ QVector<QRgb> colorTable = m_colorTable;
+ if (m_alphaMultiplier != 1.0f) {
+ for (int i = 0; i < colorTable.size(); i++) {
+ QRgb curCol = colorTable.at(i);
+ int alpha = multipliedAlphaValue(qAlpha(curCol));
+ if (alpha != qAlpha(curCol))
+ colorTable[i] = qRgba(qRed(curCol), qGreen(curCol), qBlue(curCol), alpha);
+ }
+ }
+ image.setColorTable(colorTable);
+ }
+
+ return image;
+}
+
+int QCustom3DVolumePrivate::multipliedAlphaValue(int alpha)
+{
+ int modifiedAlpha = alpha;
+ if (!m_preserveOpacity || alpha != 255) {
+ modifiedAlpha = int(m_alphaMultiplier * float(alpha));
+ modifiedAlpha = qMin(modifiedAlpha, 255);
+ }
+ return modifiedAlpha;
+}
+
+QCustom3DVolume *QCustom3DVolumePrivate::qptr()
+{
+ return static_cast<QCustom3DVolume *>(q_ptr);
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h
new file mode 100644
index 00000000..a9dfda86
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef QCUSTOM3DVOLUME_H
+#define QCUSTOM3DVOLUME_H
+
+#include <QtDataVisualization/qdatavisualizationglobal.h>
+#include <QtDataVisualization/QCustom3DItem>
+#include <QtGui/QColor>
+#include <QtGui/QImage>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QCustom3DVolumePrivate;
+
+class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem
+{
+ Q_OBJECT
+ Q_PROPERTY(int textureWidth READ textureWidth WRITE setTextureWidth NOTIFY textureWidthChanged)
+ Q_PROPERTY(int textureHeight READ textureHeight WRITE setTextureHeight NOTIFY textureHeightChanged)
+ Q_PROPERTY(int textureDepth READ textureDepth WRITE setTextureDepth NOTIFY textureDepthChanged)
+ Q_PROPERTY(int sliceIndexX READ sliceIndexX WRITE setSliceIndexX NOTIFY sliceIndexXChanged)
+ Q_PROPERTY(int sliceIndexY READ sliceIndexY WRITE setSliceIndexY NOTIFY sliceIndexYChanged)
+ Q_PROPERTY(int sliceIndexZ READ sliceIndexZ WRITE setSliceIndexZ NOTIFY sliceIndexZChanged)
+ Q_PROPERTY(QVector<QRgb> colorTable READ colorTable WRITE setColorTable NOTIFY colorTableChanged)
+ Q_PROPERTY(QVector<uchar> *textureData READ textureData WRITE setTextureData NOTIFY textureDataChanged)
+ Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged)
+ Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged)
+ Q_PROPERTY(bool useHighDefShader READ useHighDefShader WRITE setUseHighDefShader NOTIFY useHighDefShaderChanged)
+ Q_PROPERTY(bool drawSlices READ drawSlices WRITE setDrawSlices NOTIFY drawSlicesChanged)
+ Q_PROPERTY(bool drawSliceFrames READ drawSliceFrames WRITE setDrawSliceFrames NOTIFY drawSliceFramesChanged)
+ Q_PROPERTY(QColor sliceFrameColor READ sliceFrameColor WRITE setSliceFrameColor NOTIFY sliceFrameColorChanged)
+ Q_PROPERTY(QVector3D sliceFrameWidths READ sliceFrameWidths WRITE setSliceFrameWidths NOTIFY sliceFrameWidthsChanged)
+ Q_PROPERTY(QVector3D sliceFrameGaps READ sliceFrameGaps WRITE setSliceFrameGaps NOTIFY sliceFrameGapsChanged)
+ Q_PROPERTY(QVector3D sliceFrameThicknesses READ sliceFrameThicknesses WRITE setSliceFrameThicknesses NOTIFY sliceFrameThicknessesChanged)
+
+public:
+
+ explicit QCustom3DVolume(QObject *parent = 0);
+ explicit QCustom3DVolume(const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation, int textureWidth,
+ int textureHeight, int textureDepth,
+ QVector<uchar> *textureData, QImage::Format textureFormat,
+ const QVector<QRgb> &colorTable, QObject *parent = 0);
+ virtual ~QCustom3DVolume();
+
+ void setTextureWidth(int value);
+ int textureWidth() const;
+ void setTextureHeight(int value);
+ int textureHeight() const;
+ void setTextureDepth(int value);
+ int textureDepth() const;
+ void setTextureDimensions(int width, int height, int depth);
+ int textureDataWidth() const;
+
+ void setSliceIndexX(int value);
+ int sliceIndexX() const;
+ void setSliceIndexY(int value);
+ int sliceIndexY() const;
+ void setSliceIndexZ(int value);
+ int sliceIndexZ() const;
+ void setSliceIndices(int x, int y, int z);
+
+ void setColorTable(const QVector<QRgb> &colors);
+ QVector<QRgb> colorTable() const;
+
+ void setTextureData(QVector<uchar> *data);
+ QVector<uchar> *createTextureData(const QVector<QImage *> &images);
+ QVector<uchar> *textureData() const;
+ void setSubTextureData(Qt::Axis axis, int index, const uchar *data);
+ void setSubTextureData(Qt::Axis axis, int index, const QImage &image);
+
+ void setTextureFormat(QImage::Format format);
+ QImage::Format textureFormat() const;
+
+ void setAlphaMultiplier(float mult);
+ float alphaMultiplier() const;
+ void setPreserveOpacity(bool enable);
+ bool preserveOpacity() const;
+
+ void setUseHighDefShader(bool enable);
+ bool useHighDefShader() const;
+
+ void setDrawSlices(bool enable);
+ bool drawSlices() const;
+ void setDrawSliceFrames(bool enable);
+ bool drawSliceFrames() const;
+
+ void setSliceFrameColor(const QColor &color);
+ QColor sliceFrameColor() const;
+ void setSliceFrameWidths(const QVector3D &values);
+ QVector3D sliceFrameWidths() const;
+ void setSliceFrameGaps(const QVector3D &values);
+ QVector3D sliceFrameGaps() const;
+ void setSliceFrameThicknesses(const QVector3D &values);
+ QVector3D sliceFrameThicknesses() const;
+
+ QImage renderSlice(Qt::Axis axis, int index);
+
+signals:
+ void textureWidthChanged(int value);
+ void textureHeightChanged(int value);
+ void textureDepthChanged(int value);
+ void sliceIndexXChanged(int value);
+ void sliceIndexYChanged(int value);
+ void sliceIndexZChanged(int value);
+ void colorTableChanged();
+ void textureDataChanged(QVector<uchar> *data);
+ void textureFormatChanged(QImage::Format format);
+ void alphaMultiplierChanged(float mult);
+ void preserveOpacityChanged(bool enabled);
+ void useHighDefShaderChanged(bool enabled);
+ void drawSlicesChanged(bool enabled);
+ void drawSliceFramesChanged(bool enabled);
+ void sliceFrameColorChanged(const QColor &color);
+ void sliceFrameWidthsChanged(const QVector3D &values);
+ void sliceFrameGapsChanged(const QVector3D &values);
+ void sliceFrameThicknessesChanged(const QVector3D &values);
+
+protected:
+ QCustom3DVolumePrivate *dptr();
+ const QCustom3DVolumePrivate *dptrc() const;
+
+private:
+ Q_DISABLE_COPY(QCustom3DVolume)
+
+ friend class Abstract3DRenderer;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h
new file mode 100644
index 00000000..642d2af5
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtDataVisualization API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef QCUSTOM3DVOLUME_P_H
+#define QCUSTOM3DVOLUME_P_H
+
+#include "qcustom3dvolume.h"
+#include "qcustom3ditem_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+struct QCustomVolumeDirtyBitField {
+ bool textureDimensionsDirty : 1;
+ bool slicesDirty : 1;
+ bool colorTableDirty : 1;
+ bool textureDataDirty : 1;
+ bool textureFormatDirty : 1;
+ bool alphaDirty : 1;
+ bool shaderDirty : 1;
+
+ QCustomVolumeDirtyBitField()
+ : textureDimensionsDirty(false),
+ slicesDirty(false),
+ colorTableDirty(false),
+ textureDataDirty(false),
+ textureFormatDirty(false),
+ alphaDirty(false),
+ shaderDirty(false)
+ {
+ }
+};
+
+class QCustom3DVolumePrivate : public QCustom3DItemPrivate
+{
+ Q_OBJECT
+
+public:
+ QCustom3DVolumePrivate(QCustom3DVolume *q);
+ QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation, int textureWidth,
+ int textureHeight, int textureDepth, QVector<uchar> *textureData,
+ QImage::Format textureFormat, const QVector<QRgb> &colorTable);
+ virtual ~QCustom3DVolumePrivate();
+
+ void resetDirtyBits();
+ QImage renderSlice(Qt::Axis axis, int index);
+
+ QCustom3DVolume *qptr();
+
+public:
+ int m_textureWidth;
+ int m_textureHeight;
+ int m_textureDepth;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+
+ QImage::Format m_textureFormat;
+ QVector<QRgb> m_colorTable;
+ QVector<uchar> *m_textureData;
+
+ float m_alphaMultiplier;
+ bool m_preserveOpacity;
+ bool m_useHighDefShader;
+
+ bool m_drawSlices;
+ bool m_drawSliceFrames;
+ QColor m_sliceFrameColor;
+ QVector3D m_sliceFrameWidths;
+ QVector3D m_sliceFrameGaps;
+ QVector3D m_sliceFrameThicknesses;
+
+ QCustomVolumeDirtyBitField m_dirtyBitsVolume;
+
+private:
+ int multipliedAlphaValue(int alpha);
+
+ friend class QCustom3DVolume;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
index d64046be..85aed432 100644
--- a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
+++ b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
@@ -84,7 +84,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::minXValue
*
- * The minimum X value for the generated surface points.
+ * The minimum X value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -92,7 +92,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::maxXValue
*
- * The maximum X value for the generated surface points.
+ * The maximum X value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -100,7 +100,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::minZValue
*
- * The minimum Z value for the generated surface points.
+ * The minimum Z value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -108,7 +108,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::maxZValue
*
- * The maximum Z value for the generated surface points.
+ * The maximum Z value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -228,7 +228,7 @@ void QHeightMapSurfaceDataProxy::setValueRanges(float minX, float maxX, float mi
/*!
* \property QHeightMapSurfaceDataProxy::minXValue
*
- * The minimum X value for the generated surface points.
+ * The minimum X value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -245,7 +245,7 @@ float QHeightMapSurfaceDataProxy::minXValue() const
/*!
* \property QHeightMapSurfaceDataProxy::maxXValue
*
- * The maximum X value for the generated surface points.
+ * The maximum X value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -262,7 +262,7 @@ float QHeightMapSurfaceDataProxy::maxXValue() const
/*!
* \property QHeightMapSurfaceDataProxy::minZValue
*
- * The minimum Z value for the generated surface points.
+ * The minimum Z value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -279,7 +279,7 @@ float QHeightMapSurfaceDataProxy::minZValue() const
/*!
* \property QHeightMapSurfaceDataProxy::maxZValue
*
- * The maximum Z value for the generated surface points.
+ * The maximum Z value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
diff --git a/src/datavisualization/data/qsurface3dseries.cpp b/src/datavisualization/data/qsurface3dseries.cpp
index 1107d721..7d4dacfe 100644
--- a/src/datavisualization/data/qsurface3dseries.cpp
+++ b/src/datavisualization/data/qsurface3dseries.cpp
@@ -141,6 +141,14 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty string Surface3DSeries::textureFile
+ *
+ * Holds the texture file name for the surface texture. To clear the texture, set empty
+ * file name.
+ */
+
+
+/*!
* \enum QSurface3DSeries::DrawFlag
*
* Drawing mode of the surface. Values of this enumeration can be combined with OR operator.
@@ -300,6 +308,57 @@ QSurface3DSeries::DrawFlags QSurface3DSeries::drawMode() const
}
/*!
+ * \property QSurface3DSeries::texture
+ *
+ * Set the \a texture as a QImage for the surface. To clear the texture, set empty
+ * QImage as texture.
+ */
+void QSurface3DSeries::setTexture(const QImage &texture)
+{
+ if (dptr()->m_texture != texture) {
+ dptr()->setTexture(texture);
+
+ emit textureChanged(texture);
+ dptr()->m_textureFile.clear();
+ }
+}
+
+QImage QSurface3DSeries::texture() const
+{
+ return dptrc()->m_texture;
+}
+
+/*!
+ * \property QSurface3DSeries::textureFile
+ *
+ * Holds the texture file name for the surface texture. To clear the texture, set empty
+ * file name.
+ */
+void QSurface3DSeries::setTextureFile(const QString &filename)
+{
+ if (dptr()->m_textureFile != filename) {
+ if (filename.isEmpty()) {
+ setTexture(QImage());
+ } else {
+ QImage image(filename);
+ if (image.isNull()) {
+ qWarning() << "Warning: Tried to set invalid image file as surface texture.";
+ return;
+ }
+ setTexture(image);
+ }
+
+ dptr()->m_textureFile = filename;
+ emit textureFileChanged(filename);
+ }
+}
+
+QString QSurface3DSeries::textureFile() const
+{
+ return dptrc()->m_textureFile;
+}
+
+/*!
* \internal
*/
QSurface3DSeriesPrivate *QSurface3DSeries::dptr()
@@ -445,4 +504,11 @@ void QSurface3DSeriesPrivate::setDrawMode(QSurface3DSeries::DrawFlags mode)
}
}
+void QSurface3DSeriesPrivate::setTexture(const QImage &texture)
+{
+ m_texture = texture;
+ if (static_cast<Surface3DController *>(m_controller))
+ static_cast<Surface3DController *>(m_controller)->updateSurfaceTexture(qptr());
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qsurface3dseries.h b/src/datavisualization/data/qsurface3dseries.h
index 64df7202..7051e583 100644
--- a/src/datavisualization/data/qsurface3dseries.h
+++ b/src/datavisualization/data/qsurface3dseries.h
@@ -35,6 +35,8 @@ class QT_DATAVISUALIZATION_EXPORT QSurface3DSeries : public QAbstract3DSeries
Q_PROPERTY(bool flatShadingEnabled READ isFlatShadingEnabled WRITE setFlatShadingEnabled NOTIFY flatShadingEnabledChanged)
Q_PROPERTY(bool flatShadingSupported READ isFlatShadingSupported NOTIFY flatShadingSupportedChanged)
Q_PROPERTY(DrawFlags drawMode READ drawMode WRITE setDrawMode NOTIFY drawModeChanged)
+ Q_PROPERTY(QImage texture READ texture WRITE setTexture NOTIFY textureChanged)
+ Q_PROPERTY(QString textureFile READ textureFile WRITE setTextureFile NOTIFY textureFileChanged)
public:
enum DrawFlag {
@@ -63,12 +65,19 @@ public:
bool isFlatShadingSupported() const;
+ void setTexture(const QImage &texture);
+ QImage texture() const;
+ void setTextureFile(const QString &filename);
+ QString textureFile() const;
+
signals:
void dataProxyChanged(QSurfaceDataProxy *proxy);
void selectedPointChanged(const QPoint &position);
void flatShadingEnabledChanged(bool enable);
void flatShadingSupportedChanged(bool enable);
void drawModeChanged(QSurface3DSeries::DrawFlags mode);
+ void textureChanged(const QImage &image);
+ void textureFileChanged(const QString &filename);
protected:
explicit QSurface3DSeries(QSurface3DSeriesPrivate *d, QObject *parent = 0);
diff --git a/src/datavisualization/data/qsurface3dseries_p.h b/src/datavisualization/data/qsurface3dseries_p.h
index d4cc2820..270a3ad1 100644
--- a/src/datavisualization/data/qsurface3dseries_p.h
+++ b/src/datavisualization/data/qsurface3dseries_p.h
@@ -48,6 +48,7 @@ public:
void setSelectedPoint(const QPoint &position);
void setFlatShadingEnabled(bool enabled);
void setDrawMode(QSurface3DSeries::DrawFlags mode);
+ void setTexture(const QImage &texture);
private:
QSurface3DSeries *qptr();
@@ -55,6 +56,8 @@ private:
QPoint m_selectedPoint;
bool m_flatShadingEnabled;
QSurface3DSeries::DrawFlags m_drawMode;
+ QImage m_texture;
+ QString m_textureFile;
private:
friend class QSurface3DSeries;
diff --git a/src/datavisualization/data/qsurfacedataproxy.cpp b/src/datavisualization/data/qsurfacedataproxy.cpp
index dbe7cc49..12b24465 100644
--- a/src/datavisualization/data/qsurfacedataproxy.cpp
+++ b/src/datavisualization/data/qsurfacedataproxy.cpp
@@ -53,6 +53,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \note Surfaces with less than two rows or columns are not considered valid surfaces and will
* not be rendered.
*
+ * \note On some environments, surfaces with a lot of visible vertices may not render, because
+ * they exceed the per-draw vertex count supported by the graphics driver.
+ * This is mostly an issue on 32bit and/or OpenGL ES2 platforms.
+ *
* \sa {Qt Data Visualization Data Handling}
*/
diff --git a/src/datavisualization/datavisualization.pro b/src/datavisualization/datavisualization.pro
index 44f0f60c..645a1733 100644
--- a/src/datavisualization/datavisualization.pro
+++ b/src/datavisualization/datavisualization.pro
@@ -1,5 +1,5 @@
# Target can't start with 'Qt' as it gets major version number inserted into it in that case,
-# which we don't want. Exception is mac bundles, where the target name must match the module name
+# which we don't want. Exception is OS X bundles, where the target name must match the module name
mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) {
TARGET = QtDataVisualization
} else {
@@ -7,7 +7,8 @@ mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) {
}
message($$QT_CONFIG)
-QT = core gui
+QT += core gui
+osx: QT += gui-private
DEFINES += QT_DATAVISUALIZATION_LIBRARY
# Fix exports in static builds for applications linking datavisualization module
@@ -19,9 +20,8 @@ QMAKE_DOCS = $$PWD/doc/qtdatavisualization.qdocconf
load(qt_module)
-include($$PWD/common.pri)
-include($$PWD/engine/engine.pri)
include($$PWD/global/global.pri)
+include($$PWD/engine/engine.pri)
include($$PWD/utils/utils.pri)
include($$PWD/theme/theme.pri)
include($$PWD/axis/axis.pri)
diff --git a/src/datavisualization/doc/qtdatavisualization.qdocconf b/src/datavisualization/doc/qtdatavisualization.qdocconf
index 6e9f09b4..4d63b526 100644
--- a/src/datavisualization/doc/qtdatavisualization.qdocconf
+++ b/src/datavisualization/doc/qtdatavisualization.qdocconf
@@ -6,7 +6,7 @@ include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf)
project = QtDataVisualization
description = Qt Data Visualization Reference Documentation
-version = 1.1.0
+version = 1.2.0
exampledirs += ../../../examples/datavisualization \
snippets
@@ -27,14 +27,14 @@ indexes += $QT_INSTALL_DOCS/qtcore/qtcore.index \
qhp.projects = QtDataVisualization
qhp.QtDataVisualization.file = qtdatavisualization.qhp
-qhp.QtDataVisualization.namespace = com.digia.qtdatavisualization.110
+qhp.QtDataVisualization.namespace = com.digia.qtdatavisualization.120
qhp.QtDataVisualization.virtualFolder = qtdatavisualization
qhp.QtDataVisualization.indexTitle = Qt Data Visualization
qhp.QtDataVisualization.indexRoot =
-qhp.QtDataVisualization.filterAttributes = qtdatavisualization 1.1.0 qtrefdoc
-qhp.QtDataVisualization.customFilters.Qt.name = QtDataVisualization 1.1.0
-qhp.QtDataVisualization.customFilters.Qt.filterAttributes = qtdatavisualization 1.1.0
+qhp.QtDataVisualization.filterAttributes = qtdatavisualization 1.2.0 qtrefdoc
+qhp.QtDataVisualization.customFilters.Qt.name = QtDataVisualization 1.2.0
+qhp.QtDataVisualization.customFilters.Qt.filterAttributes = qtdatavisualization 1.2.0
qhp.QtDataVisualization.subprojects = gettingstarted examples classes types
qhp.QtDataVisualization.subprojects.gettingstarted.title = Getting Started
qhp.QtDataVisualization.subprojects.gettingstarted.indexTitle = Qt Data Visualization Getting Started
@@ -50,7 +50,7 @@ qhp.QtDataVisualization.subprojects.classes.selectors = class
qhp.QtDataVisualization.subprojects.classes.sortPages = true
qhp.QtDataVisualization.subprojects.types.title = QML Types
qhp.QtDataVisualization.subprojects.types.indexTitle = Qt Data Visualization QML Types
-qhp.QtDataVisualization.subprojects.types.selectors = fake:qmlclass
+qhp.QtDataVisualization.subprojects.types.selectors = qmlclass
qhp.QtDataVisualization.subprojects.types.sortPages = true
navigation.landingpage = Qt Data Visualization
diff --git a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp
index 56bfc5ee..1ca3f597 100644
--- a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp
+++ b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp
@@ -17,12 +17,12 @@
****************************************************************************/
//! [0]
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
//! [0]
//! [1]
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Item {
width: 640
@@ -61,7 +61,7 @@ Item {
//! [2]
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Item {
width: 640
@@ -94,7 +94,7 @@ Item {
//! [3]
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Item {
width: 640
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
index 2cc3eece..b5a678e5 100644
--- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
@@ -153,6 +153,32 @@
*/
/*!
+ * \qmlproperty bool AbstractGraph3D::polar
+ * \since QtDataVisualization 1.2
+ *
+ * If \c {true}, the horizontal axes are changed into polar axes. The X axis becomes the
+ * angular axis and the Z axis becomes the radial axis.
+ * Polar mode is not available for bar graphs.
+ *
+ * Defaults to \c{false}.
+ *
+ * \sa orthoProjection, radialLabelOffset
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::radialLabelOffset
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies the normalized horizontal offset for the axis labels of the radial
+ * polar axis. The value 0.0 indicates the labels should be drawn next to the 0-angle angular
+ * axis grid line. The value 1.0 indicates the labels are drawn on their normal place at the edge
+ * of the graph background.
+ * This property is ignored if polar property value is \c{false}. Defaults to 1.0.
+ *
+ * \sa polar
+ */
+
+/*!
* \qmlmethod void AbstractGraph3D::clearSelection()
* Clears selection from all attached series.
*/
@@ -289,22 +315,123 @@
* \qmlproperty real AbstractGraph3D::aspectRatio
* \since QtDataVisualization 1.1
*
- * Aspect ratio of the graph data. This is the ratio of data scaling between horizontal and
- * vertical axes. Defaults to \c{2.0}.
+ * The aspect ratio is the ratio of the graph scaling between the longest axis on the horizontal
+ * plane and the Y-axis. Defaults to \c{2.0}.
*
* \note Has no effect on Bars3D.
+ *
+ * \sa horizontalAspectRatio
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::horizontalAspectRatio
+ * \since QtDataVisualization 1.2
+ *
+ * The horizontal aspect ratio is the ratio of the graph scaling between the X and Z axes.
+ * Value of 0.0 indicates automatic scaling according to axis ranges.
+ * Defaults to \c{0.0}.
+ *
+ * \note Has no effect on Bars3D, which handles scaling on the horizontal plane via
+ * \l{Bars3D::barThickness}{barThickness} and \l{Bars3D::barSpacing}{barSpacing} properties.
+ * Polar graphs also ignore this property.
+ *
+ * \sa aspectRatio, polar, Bars3D::barThickness, Bars3D::barSpacing
*/
/*!
* \qmlproperty AbstractGraph3D.OptimizationHints AbstractGraph3D::optimizationHints
- * \since Qt Data Visualization 1.1
- *
- * Defines if the rendering optimization is default or static. Default mode provides the full feature set at
- * reasonable performance. Static is a beta level feature and currently supports only a subset of the
- * features on the Scatter graph. Missing features are object gradient for mesh objects, both gradients
- * for points, and diffuse and specular color on rotations. At this point static is intended just for
- * introducing a new feature. It optimizes graph rendering and is ideal for large non-changing data
- * sets. It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it
- * with massive data sets is not advisable.
- * Defaults to \c{OptimizationDefault}
+ * \since QtDataVisualization 1.1
+ *
+ * Defines if the rendering optimization is default or static. Default mode provides the full
+ * feature set at reasonable performance. Static mode optimizes graph rendering and is ideal for
+ * large non-changing data sets. It is slower with dynamic data changes and item rotations.
+ * Selection is not optimized, so using it with massive data sets is not advisable.
+ * Static works only on the Scatter graph.
+ * Defaults to \c{OptimizationDefault}.
+ *
+ * \note On some environments, large graphs using static optimization may not render, because
+ * all of the items are rendered using a single draw call, and different graphics drivers have
+ * different maximum vertice counts per call that they support.
+ * This is mostly an issue on 32bit and/or OpenGL ES2 platforms.
+ * To work around this issue, choose an item mesh with low vertex count or use the point mesh.
+ *
+ * \sa Abstract3DSeries::mesh
+ */
+
+/*!
+ * \qmlproperty bool AbstractGraph3D::reflection
+ * \since QtDataVisualization 1.2
+ *
+ * Sets floor reflections on or off. Defaults to \c{false}.
+ *
+ * \note Affects only Bars3D.
+ *
+ * \note In Bars3D graphs holding both positive and negative values, reflections are not supported
+ * for custom items that intersect the floor plane. In that case, reflections should be turned off
+ * to avoid incorrect rendering.
+ *
+ * \sa reflectivity
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::reflectivity
+ * \since QtDataVisualization 1.2
+ *
+ * Adjusts floor reflectivity, larger number being more reflective. Valid range is \c{[0...1]}.
+ * Defaults to \c{0.5}.
+ *
+ * \note Affects only Bars3D.
+ *
+ * \sa reflection
+ */
+
+/*!
+ * \qmlproperty locale AbstractGraph3D::locale
+ * \since QtDataVisualization 1.2
+ *
+ * Sets the locale used for formatting various numeric labels.
+ * Defaults to \c{"C"} locale.
+ *
+ * \sa ValueAxis3D::labelFormat
+ */
+
+/*!
+ * \qmlproperty vector3d AbstractGraph3D::queriedGraphPosition
+ * \since QtDataVisualization 1.2
+ *
+ * This read-only property contains the latest graph position values along each axis queried using
+ * Scene3D::graphPositionQuery. The values are normalized to range \c{[-1, 1]}.
+ * If the queried position was outside the graph bounds, the values
+ * will not reflect the real position, but will instead be some undefined position outside
+ * the range \c{[-1, 1]}. The value will be undefined before any queries are made.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs only allow querying graph position at the graph floor level,
+ * so the Y-value is always zero for bar graphs and the valid queries can be only made at
+ * screen positions that contain the floor of the graph.
+ *
+ * \sa Scene3D::graphPositionQuery
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::margin
+ * \since QtDataVisualization 1.2
+ *
+ * This property contains the absolute value used for graph margin. The graph margin is the space
+ * left between the edge of the plottable graph area and the edge of the graph background.
+ * If the margin value is negative, the margins are determined automatically and can vary according
+ * to size of the items in the series and the type of the graph.
+ * The value is interpreted as a fraction of Y-axis range, provided the graph aspect ratios have
+ * not beed changed from the defaults.
+ * Defaults to \c{-1.0}.
+ *
+ * \note Having smaller than the automatically determined margin on scatter graph can cause
+ * the scatter items at the edges of the graph to overlap with the graph background.
+ *
+ * \note On scatter and surface graphs, if the margin is comparatively small to the axis label
+ * size, the positions of the edge labels of the axes are adjusted to avoid overlap with
+ * the edge labels of the neighboring axes.
*/
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc
index 6ee51742..0348652a 100644
--- a/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc
@@ -115,6 +115,14 @@
*/
/*!
+ * \qmlproperty real Bars3D::floorLevel
+ *
+ * The desired floor level for the bar graph in Y-axis data coordinates.
+ * The actual floor level cannot go below Y-axis minimum or above Y-axis maximum.
+ * Defaults to zero.
+ */
+
+/*!
* \qmlmethod void Bars3D::addSeries(Bar3DSeries series)
* Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes,
* so the rows and columns of all series must match for the visualized data to be meaningful.
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc
index 23a9a004..2b83b807 100644
--- a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc
@@ -73,11 +73,6 @@
*/
/*!
- \qmlproperty ColorGradient Surface3D::gradient
- The current surface gradient. Setting this property replaces the previous gradient.
- */
-
-/*!
* \qmlproperty list<Surface3DSeries> Surface3D::seriesList
* \default
* This property holds the series of the graph.
@@ -86,6 +81,22 @@
*/
/*!
+ * \qmlproperty bool Surface3D::flipHorizontalGrid
+ * \since QtDataVisualization 1.2
+ *
+ * In some use cases the horizontal axis grid is mostly covered by the surface, so it can be more
+ * useful to display the horizontal axis grid on top of the graph rather than on the bottom.
+ * A typical use case for this is showing 2D spectrograms using orthoGraphic projection with
+ * a top-down viewpoint.
+ *
+ * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background
+ * of the graph.
+ * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph
+ * from the horizontal background.
+ * Defaults to \c{false}.
+ */
+
+/*!
* \qmlmethod void Surface3D::addSeries(Surface3DSeries series)
* Adds the \a series to the graph.
*/
diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc
index af419814..b07074b1 100644
--- a/src/datavisualization/doc/src/qtdatavisualization.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc
@@ -37,7 +37,7 @@
*/
/*!
- \qmlmodule QtDataVisualization 1.1
+ \qmlmodule QtDataVisualization 1.2
\title Qt Data Visualization QML Types
\ingroup qmlmodules
@@ -98,7 +98,7 @@
\row
\li Windows (MSVC) \li nmake
\row
- \li OSX \li make
+ \li OS X \li make
\endtable
The above generates the default makefiles for your configuration, which is typically
@@ -127,7 +127,7 @@
make release
\endcode
- For both builds (Windows/Mac only):
+ For both builds (Windows/OS X only):
\code
qmake CONFIG+="debug_and_release build_all"
make
@@ -323,20 +323,21 @@
\title Qt Data Visualization Known Issues
\list
- \li Android doesn't support both widgets and OpenGL simultaneously, so only
- the Qt Quick 2 API is usable in practice in Android.
+ \li Some platforms like Android and WinRT cannot handle multiple native windows properly,
+ so only the Qt Quick 2 graphs are available in practice for those platforms.
\li Shadows are not supported with OpenGL ES2 (including Angle builds in Windows).
\li Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows).
+ \li QCustom3DVolume items are not supported with OpenGL ES2 (including Angle builds in
+ Windows).
\li Surfaces with non-straight rows and columns do not always render properly.
\li Q3DLight class (and Light3D QML item) are currently not usable for anything.
- \li Changing any of Q3DScene properties affecting subviewports currently has no effect.
- \li The color style Q3DTheme::ColorStyleObjectGradient doesn't work for surface graphs.
+ \li Changing most of Q3DScene properties affecting subviewports currently has no effect.
\li Widget based examples layout incorrectly in iOS.
\li Reparenting a graph to an item in another QQuickWindow is not supported.
- \li There is a low-impact binary break between 1.0 and 1.1. The break is due to a QML type
- registration conflict with QAbstractItemModel between QtDataVisualization and
- QtCommercial.Charts. Introducing the binary break makes it possible to use both
- Charts and Data Visualization in the same QML application.
+ \li Android builds of QML applications importing QtDataVisualization also require
+ "QT += datavisualization" in the pro file. This is because Qt Data Visualization
+ QML plugin has a dependency to Qt Data Visualization C++ library, which Qt Creator
+ doesn't automatically add to the deployment package.
\endlist
*/
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp
index 30434dca..275d0fe2 100644
--- a/src/datavisualization/engine/abstract3dcontroller.cpp
+++ b/src/datavisualization/engine/abstract3dcontroller.cpp
@@ -25,6 +25,7 @@
#include "thememanager_p.h"
#include "q3dtheme_p.h"
#include "qcustom3ditem_p.h"
+#include "utils_p.h"
#include <QtCore/QThread>
#include <QtGui/QOpenGLFramebufferObject>
@@ -37,8 +38,12 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
m_selectionMode(QAbstract3DGraph::SelectionItem),
m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium),
m_useOrthoProjection(false),
- m_aspectRatio(2.0f),
+ m_aspectRatio(2.0),
+ m_horizontalAspectRatio(0.0),
m_optimizationHints(QAbstract3DGraph::OptimizationDefault),
+ m_reflectionEnabled(false),
+ m_reflectivity(0.5),
+ m_locale(QLocale::c()),
m_scene(scene),
m_activeInputHandler(0),
m_axisX(0),
@@ -50,12 +55,19 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
m_isCustomItemDirty(true),
m_isSeriesVisualsDirty(true),
m_renderPending(false),
+ m_isPolar(false),
+ m_radialLabelOffset(1.0f),
m_measureFps(false),
m_numFrames(0),
- m_currentFps(0.0)
+ m_currentFps(0.0),
+ m_clickedType(QAbstract3DGraph::ElementNone),
+ m_selectedLabelIndex(-1),
+ m_selectedCustomItemIndex(-1),
+ m_margin(-1.0)
{
if (!m_scene)
m_scene = new Q3DScene;
+ m_scene->setParent(this);
// Set initial theme
Q3DTheme *defaultTheme = new Q3DTheme(Q3DTheme::ThemeQt);
@@ -72,10 +84,6 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
inputHandler = new QTouch3DInputHandler();
inputHandler->d_ptr->m_isDefaultHandler = true;
setActiveInputHandler(inputHandler);
- connect(inputHandler, &QAbstract3DInputHandler::inputViewChanged, this,
- &Abstract3DController::handleInputViewChanged);
- connect(inputHandler, &QAbstract3DInputHandler::positionChanged, this,
- &Abstract3DController::handleInputPositionChanged);
connect(m_scene->d_ptr.data(), &Q3DScenePrivate::needRender, this,
&Abstract3DController::emitNeedRender);
}
@@ -93,7 +101,7 @@ Abstract3DController::~Abstract3DController()
void Abstract3DController::destroyRenderer()
{
// Renderer can be in another thread, don't delete it directly in that case
- if (m_renderer && m_renderer->thread() != QThread::currentThread())
+ if (m_renderer && m_renderer->thread() && m_renderer->thread() != this->thread())
m_renderer->deleteLater();
else
delete m_renderer;
@@ -107,6 +115,13 @@ void Abstract3DController::destroyRenderer()
void Abstract3DController::setRenderer(Abstract3DRenderer *renderer)
{
m_renderer = renderer;
+
+ // If renderer is created in different thread than controller, make sure renderer gets
+ // destroyed before the render thread finishes.
+ if (renderer->thread() != this->thread()) {
+ QObject::connect(renderer->thread(), &QThread::finished, this,
+ &Abstract3DController::destroyRenderer, Qt::DirectConnection);
+ }
}
void Abstract3DController::addSeries(QAbstract3DSeries *series)
@@ -163,11 +178,14 @@ void Abstract3DController::synchDataToRenderer()
{
// Subclass implementations check for renderer validity already, so no need to check here.
- // If there is a pending click from renderer, handle that first.
- if (m_renderer->isClickPending()) {
+ m_renderPending = false;
+
+ // If there are pending queries, handle those first
+ if (m_renderer->isGraphPositionQueryResolved())
+ handlePendingGraphPositionQuery();
+
+ if (m_renderer->isClickQueryResolved())
handlePendingClick();
- m_renderer->clearClickPending();
- }
startRecordingRemovesAndInserts();
@@ -176,6 +194,16 @@ void Abstract3DController::synchDataToRenderer()
m_renderer->updateTheme(m_themeManager->activeTheme());
+ if (m_changeTracker.polarChanged) {
+ m_renderer->updatePolar(m_isPolar);
+ m_changeTracker.polarChanged = false;
+ }
+
+ if (m_changeTracker.radialLabelOffsetChanged) {
+ m_renderer->updateRadialLabelOffset(m_radialLabelOffset);
+ m_changeTracker.radialLabelOffsetChanged = false;
+ }
+
if (m_changeTracker.shadowQualityChanged) {
m_renderer->updateShadowQuality(m_shadowQuality);
m_changeTracker.shadowQualityChanged = false;
@@ -192,15 +220,31 @@ void Abstract3DController::synchDataToRenderer()
}
if (m_changeTracker.aspectRatioChanged) {
- m_renderer->updateAspectRatio(m_aspectRatio);
+ m_renderer->updateAspectRatio(float(m_aspectRatio));
m_changeTracker.aspectRatioChanged = false;
}
+ if (m_changeTracker.horizontalAspectRatioChanged) {
+ m_renderer->updateHorizontalAspectRatio(float(m_horizontalAspectRatio));
+ m_changeTracker.horizontalAspectRatioChanged = false;
+ }
+
if (m_changeTracker.optimizationHintChanged) {
m_renderer->updateOptimizationHint(m_optimizationHints);
m_changeTracker.optimizationHintChanged = false;
}
+ if (m_changeTracker.reflectionChanged) {
+ m_renderer->m_reflectionEnabled = m_reflectionEnabled;
+ m_changeTracker.reflectionChanged = false;
+ }
+
+ if (m_changeTracker.reflectivityChanged) {
+ // Invert value to match functionality to the property description
+ m_renderer->m_reflectivity = -(m_reflectivity - 1.0);
+ m_changeTracker.reflectivityChanged = false;
+ }
+
if (m_changeTracker.axisXFormatterChanged) {
m_changeTracker.axisXFormatterChanged = false;
if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
@@ -445,6 +489,11 @@ void Abstract3DController::synchDataToRenderer()
m_changeTracker.axisZTitleFixedChanged = false;
}
+ if (m_changeTracker.marginChanged) {
+ m_renderer->updateMargin(float(m_margin));
+ m_changeTracker.marginChanged = false;
+ }
+
if (m_changedSeriesList.size()) {
m_renderer->modifiedSeriesList(m_changedSeriesList);
m_changedSeriesList.clear();
@@ -475,8 +524,6 @@ void Abstract3DController::synchDataToRenderer()
void Abstract3DController::render(const GLuint defaultFboHandle)
{
- m_renderPending = false;
-
// If not initialized, do nothing.
if (!m_renderer)
return;
@@ -765,8 +812,9 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH
m_inputHandlers.removeAll(m_activeInputHandler);
delete m_activeInputHandler;
} else {
- // Disconnect the old input handler from the scene
+ // Disconnect the old input handler
m_activeInputHandler->setScene(0);
+ QObject::disconnect(m_activeInputHandler, 0, this, 0);
}
}
@@ -775,9 +823,16 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH
addInputHandler(inputHandler);
m_activeInputHandler = inputHandler;
- if (m_activeInputHandler)
+ if (m_activeInputHandler) {
m_activeInputHandler->setScene(m_scene);
+ // Connect the input handler
+ QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this,
+ &Abstract3DController::handleInputViewChanged);
+ QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this,
+ &Abstract3DController::handleInputPositionChanged);
+ }
+
// Notify change of input handler
emit activeInputHandlerChanged(m_activeInputHandler);
}
@@ -886,11 +941,7 @@ QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() co
bool Abstract3DController::shadowsSupported() const
{
-#if defined(QT_OPENGL_ES_2)
- return false;
-#else
- return true;
-#endif
+ return !isOpenGLES();
}
bool Abstract3DController::isSlicingActive() const
@@ -989,6 +1040,11 @@ void Abstract3DController::releaseCustomItem(QCustom3DItem *item)
}
}
+QList<QCustom3DItem *> Abstract3DController::customItems() const
+{
+ return m_customItems;
+}
+
void Abstract3DController::updateCustomItem()
{
m_isCustomItemDirty = true;
@@ -1303,6 +1359,11 @@ void Abstract3DController::markSeriesItemLabelsDirty()
m_seriesList.at(i)->d_ptr->markItemLabelDirty();
}
+bool Abstract3DController::isOpenGLES() const
+{
+ return Utils::isOpenGLES();
+}
+
void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation,
QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr)
{
@@ -1381,6 +1442,8 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient
handleAxisLabelFormatChangedBySender(valueAxis);
handleAxisReversedChangedBySender(valueAxis);
handleAxisFormatterDirtyBySender(valueAxis->dptr());
+
+ valueAxis->formatter()->setLocale(m_locale);
}
}
@@ -1427,13 +1490,37 @@ void Abstract3DController::emitNeedRender()
void Abstract3DController::handlePendingClick()
{
- QAbstract3DGraph::ElementType type = m_renderer->clickedType();
- emit elementSelected(type);
+ m_clickedType = m_renderer->clickedType();
+ m_selectedLabelIndex = m_renderer->m_selectedLabelIndex;
+ m_selectedCustomItemIndex = m_renderer->m_selectedCustomItemIndex;
+
+ // Invalidate query position to indicate the query has been handled, unless another
+ // point has been queried.
+ if (m_renderer->cachedClickQuery() == m_scene->selectionQueryPosition())
+ m_scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint());
+
+ m_renderer->clearClickQueryResolved();
+
+ emit elementSelected(m_clickedType);
+}
+
+void Abstract3DController::handlePendingGraphPositionQuery()
+{
+ m_queriedGraphPosition = m_renderer->queriedGraphPosition();
+
+ // Invalidate query position to indicate the query has been handled, unless another
+ // point has been queried.
+ if (m_renderer->cachedGraphPositionQuery() == m_scene->graphPositionQuery())
+ m_scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint());
+
+ m_renderer->clearGraphPositionQueryResolved();
+
+ emit queriedGraphPositionChanged(m_queriedGraphPosition);
}
int Abstract3DController::selectedLabelIndex() const
{
- int index = m_renderer->m_selectedLabelIndex;
+ int index = m_selectedLabelIndex;
QAbstract3DAxis *axis = selectedAxis();
if (axis && axis->labels().count() <= index)
index = -1;
@@ -1443,7 +1530,7 @@ int Abstract3DController::selectedLabelIndex() const
QAbstract3DAxis *Abstract3DController::selectedAxis() const
{
QAbstract3DAxis *axis = 0;
- QAbstract3DGraph::ElementType type = m_renderer->clickedType();
+ QAbstract3DGraph::ElementType type = m_clickedType;
switch (type) {
case QAbstract3DGraph::ElementAxisXLabel:
axis = axisX();
@@ -1464,7 +1551,7 @@ QAbstract3DAxis *Abstract3DController::selectedAxis() const
int Abstract3DController::selectedCustomItemIndex() const
{
- int index = m_renderer->m_selectedCustomItemIndex;
+ int index = m_selectedCustomItemIndex;
if (m_customItems.count() <= index)
index = -1;
return index;
@@ -1481,10 +1568,7 @@ QCustom3DItem *Abstract3DController::selectedCustomItem() const
QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const
{
- if (m_renderer)
- return m_renderer->clickedType();
- else
- return QAbstract3DGraph::ElementNone;
+ return m_clickedType;
}
void Abstract3DController::setOrthoProjection(bool enable)
@@ -1505,7 +1589,7 @@ bool Abstract3DController::isOrthoProjection() const
return m_useOrthoProjection;
}
-void Abstract3DController::setAspectRatio(float ratio)
+void Abstract3DController::setAspectRatio(qreal ratio)
{
if (m_aspectRatio != ratio) {
m_aspectRatio = ratio;
@@ -1516,9 +1600,131 @@ void Abstract3DController::setAspectRatio(float ratio)
}
}
-float Abstract3DController::aspectRatio()
+qreal Abstract3DController::aspectRatio()
{
return m_aspectRatio;
}
+void Abstract3DController::setHorizontalAspectRatio(qreal ratio)
+{
+ if (m_horizontalAspectRatio != ratio) {
+ m_horizontalAspectRatio = ratio;
+ m_changeTracker.horizontalAspectRatioChanged = true;
+ emit horizontalAspectRatioChanged(m_horizontalAspectRatio);
+ m_isDataDirty = true;
+ emitNeedRender();
+ }
+}
+
+qreal Abstract3DController::horizontalAspectRatio() const
+{
+ return m_horizontalAspectRatio;
+}
+
+void Abstract3DController::setReflection(bool enable)
+{
+ if (m_reflectionEnabled != enable) {
+ m_reflectionEnabled = enable;
+ m_changeTracker.reflectionChanged = true;
+ emit reflectionChanged(m_reflectionEnabled);
+ emitNeedRender();
+ }
+}
+
+bool Abstract3DController::reflection() const
+{
+ return m_reflectionEnabled;
+}
+
+void Abstract3DController::setReflectivity(qreal reflectivity)
+{
+ if (m_reflectivity != reflectivity) {
+ m_reflectivity = reflectivity;
+ m_changeTracker.reflectivityChanged = true;
+ emit reflectivityChanged(m_reflectivity);
+ emitNeedRender();
+ }
+}
+
+qreal Abstract3DController::reflectivity() const
+{
+ return m_reflectivity;
+}
+
+void Abstract3DController::setPolar(bool enable)
+{
+ if (enable != m_isPolar) {
+ m_isPolar = enable;
+ m_changeTracker.polarChanged = true;
+ m_isDataDirty = true;
+ emit polarChanged(m_isPolar);
+ emitNeedRender();
+ }
+}
+
+bool Abstract3DController::isPolar() const
+{
+ return m_isPolar;
+}
+
+void Abstract3DController::setRadialLabelOffset(float offset)
+{
+ if (m_radialLabelOffset != offset) {
+ m_radialLabelOffset = offset;
+ m_changeTracker.radialLabelOffsetChanged = true;
+ emit radialLabelOffsetChanged(m_radialLabelOffset);
+ emitNeedRender();
+ }
+}
+
+float Abstract3DController::radialLabelOffset() const
+{
+ return m_radialLabelOffset;
+}
+
+void Abstract3DController::setLocale(const QLocale &locale)
+{
+ if (m_locale != locale) {
+ m_locale = locale;
+
+ // Value axis formatters need to be updated
+ QValue3DAxis *axis = qobject_cast<QValue3DAxis *>(m_axisX);
+ if (axis)
+ axis->formatter()->setLocale(m_locale);
+ axis = qobject_cast<QValue3DAxis *>(m_axisY);
+ if (axis)
+ axis->formatter()->setLocale(m_locale);
+ axis = qobject_cast<QValue3DAxis *>(m_axisZ);
+ if (axis)
+ axis->formatter()->setLocale(m_locale);
+ emit localeChanged(m_locale);
+ }
+}
+
+QLocale Abstract3DController::locale() const
+{
+ return m_locale;
+}
+
+QVector3D Abstract3DController::queriedGraphPosition() const
+{
+ return m_queriedGraphPosition;
+}
+
+void Abstract3DController::setMargin(qreal margin)
+{
+ if (m_margin != margin) {
+ m_margin = margin;
+ m_changeTracker.marginChanged = true;
+ emit marginChanged(margin);
+ emitNeedRender();
+ }
+}
+
+qreal Abstract3DController::margin() const
+{
+ return m_margin;
+}
+
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h
index 0e4d1add..d5a1ac8f 100644
--- a/src/datavisualization/engine/abstract3dcontroller_p.h
+++ b/src/datavisualization/engine/abstract3dcontroller_p.h
@@ -38,6 +38,7 @@
#include "qcustom3ditem.h"
#include <QtGui/QLinearGradient>
#include <QtCore/QTime>
+#include <QtCore/QLocale>
class QOpenGLFramebufferObject;
@@ -84,12 +85,18 @@ struct Abstract3DChangeBitField {
bool axisYLabelAutoRotationChanged : 1;
bool axisZLabelAutoRotationChanged : 1;
bool aspectRatioChanged : 1;
+ bool horizontalAspectRatioChanged : 1;
bool axisXTitleVisibilityChanged : 1;
bool axisYTitleVisibilityChanged : 1;
bool axisZTitleVisibilityChanged : 1;
bool axisXTitleFixedChanged : 1;
bool axisYTitleFixedChanged : 1;
bool axisZTitleFixedChanged : 1;
+ bool polarChanged : 1;
+ bool radialLabelOffsetChanged : 1;
+ bool reflectionChanged : 1;
+ bool reflectivityChanged : 1;
+ bool marginChanged : 1;
Abstract3DChangeBitField() :
themeChanged(true),
@@ -128,12 +135,18 @@ struct Abstract3DChangeBitField {
axisYLabelAutoRotationChanged(true),
axisZLabelAutoRotationChanged(true),
aspectRatioChanged(true),
+ horizontalAspectRatioChanged(true),
axisXTitleVisibilityChanged(true),
axisYTitleVisibilityChanged(true),
axisZTitleVisibilityChanged(true),
axisXTitleFixedChanged(true),
axisYTitleFixedChanged(true),
- axisZTitleFixedChanged(true)
+ axisZTitleFixedChanged(true),
+ polarChanged(true),
+ radialLabelOffsetChanged(true),
+ reflectionChanged(true),
+ reflectivityChanged(true),
+ marginChanged(true)
{
}
};
@@ -156,8 +169,13 @@ private:
QAbstract3DGraph::SelectionFlags m_selectionMode;
QAbstract3DGraph::ShadowQuality m_shadowQuality;
bool m_useOrthoProjection;
- float m_aspectRatio;
+ qreal m_aspectRatio;
+ qreal m_horizontalAspectRatio;
QAbstract3DGraph::OptimizationHints m_optimizationHints;
+ bool m_reflectionEnabled;
+ qreal m_reflectivity;
+ QLocale m_locale;
+ QVector3D m_queriedGraphPosition;
protected:
Q3DScene *m_scene;
@@ -175,6 +193,8 @@ protected:
bool m_isCustomItemDirty;
bool m_isSeriesVisualsDirty;
bool m_renderPending;
+ bool m_isPolar;
+ float m_radialLabelOffset;
QList<QAbstract3DSeries *> m_seriesList;
@@ -187,13 +207,17 @@ protected:
QList<QCustom3DItem *> m_customItems;
+ QAbstract3DGraph::ElementType m_clickedType;
+ int m_selectedLabelIndex;
+ int m_selectedCustomItemIndex;
+ qreal m_margin;
+
explicit Abstract3DController(QRect initialViewport, Q3DScene *scene, QObject *parent = 0);
public:
virtual ~Abstract3DController();
inline bool isInitialized() { return (m_renderer != 0); }
- virtual void destroyRenderer();
virtual void synchDataToRenderer();
virtual void render(const GLuint defaultFboHandle = 0);
virtual void initializeOpenGL() = 0;
@@ -252,6 +276,7 @@ public:
void deleteCustomItem(QCustom3DItem *item);
void deleteCustomItem(const QVector3D &position);
void releaseCustomItem(QCustom3DItem *item);
+ QList<QCustom3DItem *> customItems() const;
int selectedLabelIndex() const;
QAbstract3DAxis *selectedAxis() const;
@@ -261,6 +286,35 @@ public:
void setOrthoProjection(bool enable);
bool isOrthoProjection() const;
+ void setMeasureFps(bool enable);
+ inline bool measureFps() const { return m_measureFps; }
+ inline qreal currentFps() const { return m_currentFps; }
+
+ QAbstract3DGraph::ElementType selectedElement() const;
+
+ void setAspectRatio(qreal ratio);
+ qreal aspectRatio();
+ void setHorizontalAspectRatio(qreal ratio);
+ qreal horizontalAspectRatio() const;
+
+ void setReflection(bool enable);
+ bool reflection() const;
+ void setReflectivity(qreal reflectivity);
+ qreal reflectivity() const;
+
+ void setPolar(bool enable);
+ bool isPolar() const;
+ void setRadialLabelOffset(float offset);
+ float radialLabelOffset() const;
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ QVector3D queriedGraphPosition() const;
+
+ void setMargin(qreal margin);
+ qreal margin() const;
+
void emitNeedRender();
virtual void clearSelection() = 0;
@@ -286,12 +340,16 @@ public:
virtual void handleAxisTitleVisibilityChangedBySender(QObject *sender);
virtual void handleAxisTitleFixedChangedBySender(QObject *sender);
virtual void handleSeriesVisibilityChangedBySender(QObject *sender);
- virtual void handlePendingClick() = 0;
+ virtual void handlePendingClick();
+ virtual void handlePendingGraphPositionQuery();
virtual void adjustAxisRanges() = 0;
void markSeriesItemLabelsDirty();
+ bool isOpenGLES() const;
public slots:
+ void destroyRenderer();
+
void handleAxisTitleChanged(const QString &title);
void handleAxisLabelsChanged();
void handleAxisRangeChanged(float min, float max);
@@ -320,17 +378,8 @@ public slots:
// Renderer callback handlers
void handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality);
- void setMeasureFps(bool enable);
- inline bool measureFps() const { return m_measureFps; }
- inline qreal currentFps() const { return m_currentFps; }
-
- QAbstract3DGraph::ElementType selectedElement() const;
-
void updateCustomItem();
- void setAspectRatio(float ratio);
- float aspectRatio();
-
signals:
void shadowQualityChanged(QAbstract3DGraph::ShadowQuality quality);
void activeInputHandlerChanged(QAbstract3DInputHandler *inputHandler);
@@ -344,8 +393,16 @@ signals:
void measureFpsChanged(bool enabled);
void currentFpsChanged(qreal fps);
void orthoProjectionChanged(bool enabled);
- void aspectRatioChanged(float ratio);
+ void aspectRatioChanged(qreal ratio);
+ void horizontalAspectRatioChanged(qreal ratio);
void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints);
+ void polarChanged(bool enabled);
+ void radialLabelOffsetChanged(float offset);
+ void reflectionChanged(bool enabled);
+ void reflectivityChanged(qreal reflectivity);
+ void localeChanged(const QLocale &locale);
+ void queriedGraphPositionChanged(const QVector3D &data);
+ void marginChanged(qreal margin);
protected:
virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation);
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp
index 04ede782..cfc691af 100644
--- a/src/datavisualization/engine/abstract3drenderer.cpp
+++ b/src/datavisualization/engine/abstract3drenderer.cpp
@@ -24,9 +24,24 @@
#include "shaderhelper_p.h"
#include "qcustom3ditem_p.h"
#include "qcustom3dlabel_p.h"
+#include "qcustom3dvolume_p.h"
+#include "scatter3drenderer_p.h"
+
+#include <QtCore/qmath.h>
+#include <QtGui/QWindow>
+#include <QtCore/QThread>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+// Defined in shaderhelper.cpp
+extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+
+const qreal doublePi(M_PI * 2.0);
+const int polarGridRoundness(64);
+const qreal polarGridAngle(doublePi / qreal(polarGridRoundness));
+const float polarGridAngleDegrees(float(360.0 / qreal(polarGridRoundness)));
+const qreal polarGridHalfAngle(polarGridAngle / 2.0);
+
Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
: QObject(0),
m_hasNegativeValues(false),
@@ -37,26 +52,83 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
m_cachedSelectionMode(QAbstract3DGraph::SelectionNone),
m_cachedOptimizationHint(QAbstract3DGraph::OptimizationDefault),
m_textureHelper(0),
+ m_depthTexture(0),
m_cachedScene(new Q3DScene()),
m_selectionDirty(true),
m_selectionState(SelectNone),
m_devicePixelRatio(1.0f),
m_selectionLabelDirty(true),
- m_clickPending(false),
+ m_clickResolved(false),
+ m_graphPositionQueryPending(false),
+ m_graphPositionQueryResolved(false),
m_clickedSeries(0),
m_clickedType(QAbstract3DGraph::ElementNone),
+ m_selectedLabelIndex(-1),
+ m_selectedCustomItemIndex(-1),
m_selectionLabelItem(0),
m_visibleSeriesCount(0),
m_customItemShader(0),
+ m_volumeTextureShader(0),
+ m_volumeTextureLowDefShader(0),
+ m_volumeTextureSliceShader(0),
+ m_volumeSliceFrameShader(0),
+ m_labelShader(0),
+ m_cursorPositionShader(0),
+ m_cursorPositionFrameBuffer(0),
+ m_cursorPositionTexture(0),
m_useOrthoProjection(false),
m_xFlipped(false),
m_yFlipped(false),
m_zFlipped(false),
+ m_yFlippedForGrid(false),
m_backgroundObj(0),
m_gridLineObj(0),
m_labelObj(0),
- m_graphAspectRatio(2.0f)
+ m_positionMapperObj(0),
+ m_graphAspectRatio(2.0f),
+ m_graphHorizontalAspectRatio(0.0f),
+ m_polarGraph(false),
+ m_radialLabelOffset(1.0f),
+ m_polarRadius(2.0f),
+ m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)),
+ m_yRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f)),
+ m_zRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f)),
+ m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f)),
+ m_yRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f)),
+ m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)),
+ m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)),
+ m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)),
+ m_requestedMargin(-1.0f),
+ m_vBackgroundMargin(0.1f),
+ m_hBackgroundMargin(0.1f),
+ m_scaleXWithBackground(0.0f),
+ m_scaleYWithBackground(0.0f),
+ m_scaleZWithBackground(0.0f),
+ m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target
+ m_reflectionEnabled(false),
+ m_reflectivity(0.5),
+#if !defined(QT_OPENGL_ES_2)
+ m_funcs_2_1(0),
+#endif
+ m_context(0),
+ m_dummySurfaceAtDelete(0),
+ m_isOpenGLES(true)
+
{
+ initializeOpenGLFunctions();
+ m_isOpenGLES = Utils::isOpenGLES();
+#if !defined(QT_OPENGL_ES_2)
+ if (!m_isOpenGLES) {
+ // Discard warnings about deprecated functions
+ QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
+
+ m_funcs_2_1 = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
+ m_funcs_2_1->initializeOpenGLFunctions();
+
+ // Restore original message handler
+ qInstallMessageHandler(handler);
+ }
+#endif
QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures);
QObject::connect(this, &Abstract3DRenderer::needRender, controller,
&Abstract3DController::needRender, Qt::QueuedConnection);
@@ -71,6 +143,12 @@ Abstract3DRenderer::~Abstract3DRenderer()
delete m_cachedTheme;
delete m_selectionLabelItem;
delete m_customItemShader;
+ delete m_volumeTextureShader;
+ delete m_volumeTextureLowDefShader;
+ delete m_volumeSliceFrameShader;
+ delete m_volumeTextureSliceShader;
+ delete m_labelShader;
+ delete m_cursorPositionShader;
foreach (SeriesRenderCache *cache, m_renderCacheList) {
cache->cleanup(m_textureHelper);
@@ -88,12 +166,29 @@ Abstract3DRenderer::~Abstract3DRenderer()
ObjectHelper::releaseObjectHelper(this, m_backgroundObj);
ObjectHelper::releaseObjectHelper(this, m_gridLineObj);
ObjectHelper::releaseObjectHelper(this, m_labelObj);
+ ObjectHelper::releaseObjectHelper(this, m_positionMapperObj);
+
+ if (m_textureHelper) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
+ m_textureHelper->deleteTexture(&m_cursorPositionTexture);
+
+ if (QOpenGLContext::currentContext())
+ m_textureHelper->glDeleteFramebuffers(1, &m_cursorPositionFrameBuffer);
+
+ delete m_textureHelper;
+ }
+
+ m_axisCacheX.clearLabels();
+ m_axisCacheY.clearLabels();
+ m_axisCacheZ.clearLabels();
- delete m_textureHelper;
+ restoreContextAfterDelete();
}
void Abstract3DRenderer::initializeOpenGL()
{
+ m_context = QOpenGLContext::currentContext();
+
// Set OpenGL features
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
@@ -101,9 +196,11 @@ void Abstract3DRenderer::initializeOpenGL()
glCullFace(GL_BACK);
#if !defined(QT_OPENGL_ES_2)
- glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
- glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
+ if (!m_isOpenGLES) {
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
+ }
#endif
m_textureHelper = new TextureHelper();
@@ -112,6 +209,15 @@ void Abstract3DRenderer::initializeOpenGL()
axisCacheForOrientation(QAbstract3DAxis::AxisOrientationX).setDrawer(m_drawer);
axisCacheForOrientation(QAbstract3DAxis::AxisOrientationY).setDrawer(m_drawer);
axisCacheForOrientation(QAbstract3DAxis::AxisOrientationZ).setDrawer(m_drawer);
+
+ initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
+ QStringLiteral(":/shaders/fragmentLabel"));
+
+ initCursorPositionShaders(QStringLiteral(":/shaders/vertexPosition"),
+ QStringLiteral(":/shaders/fragmentPositionMap"));
+
+ loadLabelMesh();
+ loadPositionMapperMesh();
}
void Abstract3DRenderer::render(const GLuint defaultFboHandle)
@@ -137,7 +243,7 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle)
glEnable(GL_SCISSOR_TEST);
QVector4D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor());
glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
}
@@ -146,28 +252,87 @@ void Abstract3DRenderer::updateSelectionState(SelectionState state)
m_selectionState = state;
}
-void Abstract3DRenderer::updateInputPosition(const QPoint &position)
+void Abstract3DRenderer::initGradientShaders(const QString &vertexShader,
+ const QString &fragmentShader)
{
- m_inputPosition = position;
+ // Do nothing by default
+ Q_UNUSED(vertexShader)
+ Q_UNUSED(fragmentShader)
}
-void Abstract3DRenderer::initGradientShaders(const QString &vertexShader,
- const QString &fragmentShader)
+void Abstract3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader)
{
// Do nothing by default
Q_UNUSED(vertexShader)
Q_UNUSED(fragmentShader)
+ Q_UNUSED(gradientVertexShader)
+ Q_UNUSED(gradientFragmentShader)
}
void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader,
const QString &fragmentShader)
{
- if (m_customItemShader)
- delete m_customItemShader;
+ delete m_customItemShader;
m_customItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_customItemShader->initialize();
}
+void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &fragmentLowDefShader,
+ const QString &sliceShader,
+ const QString &sliceFrameVertexShader,
+ const QString &sliceFrameShader)
+{
+
+ delete m_volumeTextureShader;
+ m_volumeTextureShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_volumeTextureShader->initialize();
+
+ delete m_volumeTextureLowDefShader;
+ m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, fragmentLowDefShader);
+ m_volumeTextureLowDefShader->initialize();
+
+ delete m_volumeTextureSliceShader;
+ m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader);
+ m_volumeTextureSliceShader->initialize();
+
+ delete m_volumeSliceFrameShader;
+ m_volumeSliceFrameShader = new ShaderHelper(this, sliceFrameVertexShader, sliceFrameShader);
+ m_volumeSliceFrameShader->initialize();
+}
+
+void Abstract3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
+{
+ delete m_labelShader;
+ m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_labelShader->initialize();
+}
+
+void Abstract3DRenderer::initCursorPositionShaders(const QString &vertexShader,
+ const QString &fragmentShader)
+{
+ // Init the shader
+ delete m_cursorPositionShader;
+ m_cursorPositionShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_cursorPositionShader->initialize();
+}
+
+void Abstract3DRenderer::initCursorPositionBuffer()
+{
+ m_textureHelper->deleteTexture(&m_cursorPositionTexture);
+
+ if (m_primarySubViewport.size().isEmpty())
+ return;
+
+ m_cursorPositionTexture =
+ m_textureHelper->createCursorPositionTexture(m_primarySubViewport.size(),
+ m_cursorPositionFrameBuffer);
+}
+
void Abstract3DRenderer::updateTheme(Q3DTheme *theme)
{
// Synchronize the controller theme with renderer
@@ -193,24 +358,22 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene)
handleResize();
}
- scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment);
- // Set light position (rotate light with activeCamera, a bit above it (as set in defaultLightPos))
- scene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos);
-
QPoint logicalPixelPosition = scene->selectionQueryPosition();
- updateInputPosition(QPoint(logicalPixelPosition.x() * m_devicePixelRatio,
- logicalPixelPosition.y() * m_devicePixelRatio));
+ m_inputPosition = QPoint(logicalPixelPosition.x() * m_devicePixelRatio,
+ logicalPixelPosition.y() * m_devicePixelRatio);
+
+ QPoint logicalGraphPosition = scene->graphPositionQuery();
+ m_graphPositionQuery = QPoint(logicalGraphPosition.x() * m_devicePixelRatio,
+ logicalGraphPosition.y() * m_devicePixelRatio);
// Synchronize the renderer scene to controller scene
scene->d_ptr->sync(*m_cachedScene->d_ptr);
+ updateCameraViewport();
+
if (Q3DScene::invalidSelectionPoint() == logicalPixelPosition) {
updateSelectionState(SelectNone);
} else {
- // Selections are one-shot, reset selection active to false before processing
- scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint());
- m_clickPending = true;
-
if (scene->isSlicingActive()) {
if (scene->isPointInPrimarySubView(logicalPixelPosition))
updateSelectionState(SelectOnOverview);
@@ -222,53 +385,109 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene)
updateSelectionState(SelectOnScene);
}
}
+
+ if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition)
+ m_graphPositionQueryPending = true;
+
+ // Queue up another render when we have a query that needs resolving.
+ // This is needed because QtQuick scene graph can sometimes do a sync without following it up
+ // with a render.
+ if (m_graphPositionQueryPending || m_selectionState != SelectNone)
+ emit needRender();
+}
+
+void Abstract3DRenderer::updateTextures()
+{
+ m_axisCacheX.updateTextures();
+ m_axisCacheY.updateTextures();
+ m_axisCacheZ.updateTextures();
}
void Abstract3DRenderer::reInitShaders()
{
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
- initShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadowNoTex"));
- initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadowNoTex"));
- initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadow"));
- } else {
- initGradientShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentColorOnY"));
- initShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragment"));
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && qobject_cast<Scatter3DRenderer *>(this)) {
+ initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadow"));
+ initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"),
+ QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ } else {
+ initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadow"));
+ } else {
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && qobject_cast<Scatter3DRenderer *>(this)) {
+ initGradientShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"),
+ QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertexNoMatrices"),
+ QStringLiteral(":/shaders/fragment"));
+ } else {
+ initGradientShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ }
+ initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"),
+ QStringLiteral(":/shaders/fragmentTexture3D"),
+ QStringLiteral(":/shaders/fragmentTexture3DLowDef"),
+ QStringLiteral(":/shaders/fragmentTexture3DSlice"),
+ QStringLiteral(":/shaders/vertexPosition"),
+ QStringLiteral(":/shaders/fragment3DSliceFrames"));
+ } else {
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && qobject_cast<Scatter3DRenderer *>(this)) {
+ initGradientShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+ initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentES2"),
+ QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexNoMatrices"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ } else {
+ initGradientShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ }
initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragment"));
+ QStringLiteral(":/shaders/fragmentES2"));
initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
- QStringLiteral(":/shaders/fragmentTexture"));
+ QStringLiteral(":/shaders/fragmentTextureES2"));
}
-#else
- initGradientShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentColorOnYES2"));
- initShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentES2"));
- initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentES2"));
- initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
- QStringLiteral(":/shaders/fragmentTextureES2"));
-#endif
}
void Abstract3DRenderer::handleShadowQualityChange()
{
reInitShaders();
-#if defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) {
+ if (m_isOpenGLES && m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) {
emit requestShadowQuality(QAbstract3DGraph::ShadowQualityNone);
qWarning("Shadows are not yet supported for OpenGL ES2");
m_cachedShadowQuality = QAbstract3DGraph::ShadowQualityNone;
}
-#endif
}
void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
@@ -280,16 +499,39 @@ void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mo
void Abstract3DRenderer::updateAspectRatio(float ratio)
{
m_graphAspectRatio = ratio;
- calculateZoomLevel();
- m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment);
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
- updateCustomItemPositions();
+}
+
+void Abstract3DRenderer::updateHorizontalAspectRatio(float ratio)
+{
+ m_graphHorizontalAspectRatio = ratio;
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+}
+
+void Abstract3DRenderer::updatePolar(bool enable)
+{
+ m_polarGraph = enable;
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+}
+
+void Abstract3DRenderer::updateRadialLabelOffset(float offset)
+{
+ m_radialLabelOffset = offset;
+}
+
+void Abstract3DRenderer::updateMargin(float margin)
+{
+ m_requestedMargin = margin;
}
void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
{
m_cachedOptimizationHint = hint;
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
}
void Abstract3DRenderer::handleResize()
@@ -303,10 +545,10 @@ void Abstract3DRenderer::handleResize()
// Re-init selection buffer
initSelectionBuffer();
-#if !defined(QT_OPENGL_ES_2)
// Re-init depth buffer
updateDepthBuffer();
-#endif
+
+ initCursorPositionBuffer();
}
void Abstract3DRenderer::calculateZoomLevel()
@@ -315,9 +557,9 @@ void Abstract3DRenderer::calculateZoomLevel()
GLfloat div;
GLfloat zoomAdjustment;
div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height());
- zoomAdjustment = 2.0f * defaultRatio
+ zoomAdjustment = defaultRatio
* ((m_primarySubViewport.width() / div)
- / (m_primarySubViewport.height() / div)) / m_graphAspectRatio;
+ / (m_primarySubViewport.height() / div));
m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f
}
@@ -348,8 +590,6 @@ void Abstract3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orient
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
-
- updateCustomItemPositions();
}
void Abstract3DRenderer::updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation,
@@ -378,8 +618,6 @@ void Abstract3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation ori
axisCacheForOrientation(orientation).setReversed(enable);
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
-
- updateCustomItemPositions();
}
void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation,
@@ -396,8 +634,6 @@ void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation or
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
-
- updateCustomItemPositions();
}
void Abstract3DRenderer::updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,
@@ -607,10 +843,9 @@ void Abstract3DRenderer::drawAxisTitleY(const QVector3D &sideLabelRotation,
QQuaternion titleRotation;
if (m_axisCacheY.isTitleFixed()) {
titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
- * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f);
+ * m_zRightAngleRotation;
} else {
- titleRotation = totalRotation
- * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f);
+ titleRotation = totalRotation * m_zRightAngleRotation;
}
dummyItem.setTranslation(titleTrans + titleOffsetVector);
@@ -628,17 +863,22 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation,
float labelsMaxWidth,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionMatrix,
- ShaderHelper *shader)
+ ShaderHelper *shader,
+ bool radial)
{
float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheX.titleItem().size().height();
- float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
+ float titleOffset;
+ if (radial)
+ titleOffset = -2.0f * (labelMargin + m_drawer->scaledFontSize());
+ else
+ titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
float zRotation = 0.0f;
float yRotation = 0.0f;
float xRotation = -90.0f + labelRotation.z();
float offsetRotation = labelRotation.z();
float extraRotation = -90.0f;
Qt::AlignmentFlag alignment = Qt::AlignTop;
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
alignment = Qt::AlignBottom;
zRotation = 180.0f;
if (m_zFlipped) {
@@ -678,6 +918,17 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation,
}
}
+ if (radial) {
+ if (m_zFlipped) {
+ titleOffset = -titleOffset;
+ } else {
+ if (m_yFlippedForGrid)
+ alignment = Qt::AlignTop;
+ else
+ alignment = Qt::AlignBottom;
+ }
+ }
+
if (offsetRotation == 180.0f || offsetRotation == -180.0f)
offsetRotation = 0.0f;
QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation);
@@ -718,7 +969,7 @@ void Abstract3DRenderer::drawAxisTitleZ(const QVector3D &labelRotation,
float xRotation = -90.0f;
float extraRotation = 90.0f;
Qt::AlignmentFlag alignment = Qt::AlignTop;
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
alignment = Qt::AlignBottom;
xRotation = -xRotation;
if (m_zFlipped) {
@@ -793,6 +1044,11 @@ void Abstract3DRenderer::loadLabelMesh()
QStringLiteral(":/defaultMeshes/plane"));
}
+void Abstract3DRenderer::loadPositionMapperMesh()
+{
+ ObjectHelper::resetObjectHelper(this, m_positionMapperObj,
+ QStringLiteral(":/defaultMeshes/barFull"));
+}
void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture)
{
@@ -846,11 +1102,16 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
newItem->setRenderer(this);
newItem->setItemPointer(item); // Store pointer for render item updates
newItem->setMesh(item->meshFile());
- QVector3D scaling = item->scaling();
+ newItem->setOrigPosition(item->position());
+ newItem->setOrigScaling(item->scaling());
+ newItem->setScalingAbsolute(item->isScalingAbsolute());
+ newItem->setPositionAbsolute(item->isPositionAbsolute());
QImage textureImage = item->d_ptr->textureImage();
bool facingCamera = false;
+ GLuint texture = 0;
if (item->d_ptr->m_isLabelItem) {
QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
+ newItem->setLabelItem(true);
float pointSize = labelItem->font().pointSizeF();
// Check do we have custom visuals or need to use theme
if (!labelItem->dptr()->m_customVisuals) {
@@ -864,22 +1125,52 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
}
// Calculate scaling based on text (texture size), font size and asked scaling
float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height());
+ QVector3D scaling = newItem->origScaling();
scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
+ newItem->setOrigScaling(scaling);
// Check if facing camera
facingCamera = labelItem->isFacingCamera();
+ } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) {
+ QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item);
+ newItem->setTextureWidth(volumeItem->textureWidth());
+ newItem->setTextureHeight(volumeItem->textureHeight());
+ newItem->setTextureDepth(volumeItem->textureDepth());
+ if (volumeItem->textureFormat() == QImage::Format_Indexed8)
+ newItem->setColorTable(volumeItem->colorTable());
+ newItem->setTextureFormat(volumeItem->textureFormat());
+ newItem->setVolume(true);
+ newItem->setBlendNeeded(true);
+ texture = m_textureHelper->create3DTexture(volumeItem->textureData(),
+ volumeItem->textureWidth(),
+ volumeItem->textureHeight(),
+ volumeItem->textureDepth(),
+ volumeItem->textureFormat());
+ newItem->setSliceIndexX(volumeItem->sliceIndexX());
+ newItem->setSliceIndexY(volumeItem->sliceIndexY());
+ newItem->setSliceIndexZ(volumeItem->sliceIndexZ());
+ newItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
+ newItem->setPreserveOpacity(volumeItem->preserveOpacity());
+ newItem->setUseHighDefShader(volumeItem->useHighDefShader());
+
+ newItem->setDrawSlices(volumeItem->drawSlices());
+ newItem->setDrawSliceFrames(volumeItem->drawSliceFrames());
+ newItem->setSliceFrameColor(volumeItem->sliceFrameColor());
+ newItem->setSliceFrameWidths(volumeItem->sliceFrameWidths());
+ newItem->setSliceFrameGaps(volumeItem->sliceFrameGaps());
+ newItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses());
}
- newItem->setScaling(scaling);
+ recalculateCustomItemScalingAndPos(newItem);
newItem->setRotation(item->rotation());
- newItem->setPosition(item->position());
- newItem->setPositionAbsolute(item->isPositionAbsolute());
- newItem->setBlendNeeded(textureImage.hasAlphaChannel());
- GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+
+ // In OpenGL ES we simply draw volumes as regular custom item placeholders.
+ if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES)
+ {
+ newItem->setBlendNeeded(textureImage.hasAlphaChannel());
+ texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+ }
newItem->setTexture(texture);
item->d_ptr->clearTextureImage();
- QVector3D translation = convertPositionToTranslation(item->position(),
- item->isPositionAbsolute());
- newItem->setTranslation(translation);
newItem->setVisible(item->isVisible());
newItem->setShadowCasting(item->isShadowCasting());
newItem->setFacingCamera(facingCamera);
@@ -887,6 +1178,74 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
return newItem;
}
+void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *item)
+{
+ if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute()
+ && !item->isPositionAbsolute()) {
+ QVector3D scale = item->origScaling() / 2.0f;
+ QVector3D pos = item->origPosition();
+ QVector3D minBounds(pos.x() - scale.x(),
+ pos.y() - scale.y(),
+ pos.z() + scale.z());
+ QVector3D maxBounds(pos.x() + scale.x(),
+ pos.y() + scale.y(),
+ pos.z() - scale.z());
+ QVector3D minCorner = convertPositionToTranslation(minBounds, false);
+ QVector3D maxCorner = convertPositionToTranslation(maxBounds, false);
+ scale = QVector3D(qAbs(maxCorner.x() - minCorner.x()),
+ qAbs(maxCorner.y() - minCorner.y()),
+ qAbs(maxCorner.z() - minCorner.z())) / 2.0f;
+ if (item->isVolume()) {
+ // Only volume items need to scale and reposition according to bounds
+ QVector3D minBoundsNormal = minCorner;
+ QVector3D maxBoundsNormal = maxCorner;
+ // getVisibleItemBounds returns bounds normalized for fragment shader [-1,1]
+ // Y and Z are also flipped.
+ getVisibleItemBounds(minBoundsNormal, maxBoundsNormal);
+ item->setMinBounds(minBoundsNormal);
+ item->setMaxBounds(maxBoundsNormal);
+ // For scaling calculations, we want [0,1] normalized values
+ minBoundsNormal = item->minBoundsNormal();
+ maxBoundsNormal = item->maxBoundsNormal();
+
+ // Rescale and reposition the item so that it doesn't go over the edges
+ QVector3D adjScaling =
+ QVector3D(scale.x() * (maxBoundsNormal.x() - minBoundsNormal.x()),
+ scale.y() * (maxBoundsNormal.y() - minBoundsNormal.y()),
+ scale.z() * (maxBoundsNormal.z() - minBoundsNormal.z()));
+
+ item->setScaling(adjScaling);
+
+ QVector3D adjPos = item->origPosition();
+ QVector3D dataExtents = QVector3D(maxBounds.x() - minBounds.x(),
+ maxBounds.y() - minBounds.y(),
+ maxBounds.z() - minBounds.z()) / 2.0f;
+ adjPos.setX(adjPos.x() + (dataExtents.x() * minBoundsNormal.x())
+ - (dataExtents.x() * (1.0f - maxBoundsNormal.x())));
+ adjPos.setY(adjPos.y() + (dataExtents.y() * minBoundsNormal.y())
+ - (dataExtents.y() * (1.0f - maxBoundsNormal.y())));
+ adjPos.setZ(adjPos.z() + (dataExtents.z() * minBoundsNormal.z())
+ - (dataExtents.z() * (1.0f - maxBoundsNormal.z())));
+ item->setPosition(adjPos);
+ } else {
+ // Only scale for non-volume items, and do not readjust position
+ item->setScaling(scale);
+ item->setPosition(item->origPosition());
+ }
+ } else {
+ item->setScaling(item->origScaling());
+ item->setPosition(item->origPosition());
+ if (item->isVolume()) {
+ // Y and Z need to be flipped as shader flips those axes
+ item->setMinBounds(QVector3D(-1.0f, 1.0f, 1.0f));
+ item->setMaxBounds(QVector3D(1.0f, -1.0f, -1.0f));
+ }
+ }
+ QVector3D translation = convertPositionToTranslation(item->position(),
+ item->isPositionAbsolute());
+ item->setTranslation(translation);
+}
+
void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
{
QCustom3DItem *item = renderItem->itemPointer();
@@ -894,8 +1253,17 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
renderItem->setMesh(item->meshFile());
item->d_ptr->m_dirtyBits.meshDirty = false;
}
+ if (item->d_ptr->m_dirtyBits.positionDirty) {
+ renderItem->setOrigPosition(item->position());
+ renderItem->setPositionAbsolute(item->isPositionAbsolute());
+ if (!item->d_ptr->m_dirtyBits.scalingDirty)
+ recalculateCustomItemScalingAndPos(renderItem);
+ item->d_ptr->m_dirtyBits.positionDirty = false;
+ }
if (item->d_ptr->m_dirtyBits.scalingDirty) {
QVector3D scaling = item->scaling();
+ renderItem->setOrigScaling(scaling);
+ renderItem->setScalingAbsolute(item->isScalingAbsolute());
// In case we have label item, we need to recreate texture for scaling adjustment
if (item->d_ptr->m_isLabelItem) {
QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
@@ -918,8 +1286,9 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
item->d_ptr->clearTextureImage();
+ renderItem->setOrigScaling(scaling);
}
- renderItem->setScaling(scaling);
+ recalculateCustomItemScalingAndPos(renderItem);
item->d_ptr->m_dirtyBits.scalingDirty = false;
}
if (item->d_ptr->m_dirtyBits.rotationDirty) {
@@ -939,24 +1308,16 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
m_cachedTheme->isLabelBorderEnabled());
textureImage = item->d_ptr->textureImage();
}
+ } else if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES) {
+ renderItem->setBlendNeeded(textureImage.hasAlphaChannel());
+ GLuint oldTexture = renderItem->texture();
+ m_textureHelper->deleteTexture(&oldTexture);
+ GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+ renderItem->setTexture(texture);
}
- renderItem->setBlendNeeded(textureImage.hasAlphaChannel());
- GLuint oldTexture = renderItem->texture();
- m_textureHelper->deleteTexture(&oldTexture);
- GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
- renderItem->setTexture(texture);
item->d_ptr->clearTextureImage();
item->d_ptr->m_dirtyBits.textureDirty = false;
}
- if (item->d_ptr->m_dirtyBits.positionDirty || item->d_ptr->m_dirtyBits.positionAbsoluteDirty) {
- renderItem->setPosition(item->position());
- renderItem->setPositionAbsolute(item->isPositionAbsolute());
- QVector3D translation = convertPositionToTranslation(item->position(),
- item->isPositionAbsolute());
- renderItem->setTranslation(translation);
- item->d_ptr->m_dirtyBits.positionDirty = false;
- item->d_ptr->m_dirtyBits.positionAbsoluteDirty = false;
- }
if (item->d_ptr->m_dirtyBits.visibleDirty) {
renderItem->setVisible(item->isVisible());
item->d_ptr->m_dirtyBits.visibleDirty = false;
@@ -971,130 +1332,649 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
renderItem->setFacingCamera(labelItem->isFacingCamera());
labelItem->dptr()->m_facingCameraDirty = false;
}
+ } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) {
+ QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item);
+ if (volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty) {
+ renderItem->setColorTable(volumeItem->colorTable());
+ volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty
+ || volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty
+ || volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty) {
+ GLuint oldTexture = renderItem->texture();
+ m_textureHelper->deleteTexture(&oldTexture);
+ GLuint texture = m_textureHelper->create3DTexture(volumeItem->textureData(),
+ volumeItem->textureWidth(),
+ volumeItem->textureHeight(),
+ volumeItem->textureDepth(),
+ volumeItem->textureFormat());
+ renderItem->setTexture(texture);
+ renderItem->setTextureWidth(volumeItem->textureWidth());
+ renderItem->setTextureHeight(volumeItem->textureHeight());
+ renderItem->setTextureDepth(volumeItem->textureDepth());
+ renderItem->setTextureFormat(volumeItem->textureFormat());
+ volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty = false;
+ volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty = false;
+ volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty) {
+ renderItem->setDrawSlices(volumeItem->drawSlices());
+ renderItem->setDrawSliceFrames(volumeItem->drawSliceFrames());
+ renderItem->setSliceFrameColor(volumeItem->sliceFrameColor());
+ renderItem->setSliceFrameWidths(volumeItem->sliceFrameWidths());
+ renderItem->setSliceFrameGaps(volumeItem->sliceFrameGaps());
+ renderItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses());
+ renderItem->setSliceIndexX(volumeItem->sliceIndexX());
+ renderItem->setSliceIndexY(volumeItem->sliceIndexY());
+ renderItem->setSliceIndexZ(volumeItem->sliceIndexZ());
+ volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) {
+ renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
+ renderItem->setPreserveOpacity(volumeItem->preserveOpacity());
+ volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty) {
+ renderItem->setUseHighDefShader(volumeItem->useHighDefShader());
+ volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty = false;
+ }
}
}
void Abstract3DRenderer::updateCustomItemPositions()
{
- foreach (CustomRenderItem *renderItem, m_customRenderCache) {
- QVector3D translation = convertPositionToTranslation(renderItem->position(),
- renderItem->isPositionAbsolute());
- renderItem->setTranslation(translation);
- }
+ foreach (CustomRenderItem *renderItem, m_customRenderCache)
+ recalculateCustomItemScalingAndPos(renderItem);
}
void Abstract3DRenderer::drawCustomItems(RenderingState state,
- ShaderHelper *shader,
+ ShaderHelper *regularShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
GLuint depthTexture,
- GLfloat shadowQuality)
+ GLfloat shadowQuality,
+ GLfloat reflection)
{
if (m_customRenderCache.isEmpty())
return;
+ ShaderHelper *shader = regularShader;
+ shader->bind();
+
if (RenderingNormal == state) {
- shader->bind();
shader->setUniformValue(shader->lightP(), m_cachedScene->activeLight()->position());
shader->setUniformValue(shader->ambientS(), m_cachedTheme->ambientLightStrength());
shader->setUniformValue(shader->lightColor(),
Utils::vectorFromColor(m_cachedTheme->lightColor()));
shader->setUniformValue(shader->view(), viewMatrix);
-
- glEnable(GL_TEXTURE_2D);
}
- // Draw custom items
- foreach (CustomRenderItem *item, m_customRenderCache) {
- // Check that the render item is visible, and skip drawing if not
- if (!item->isVisible())
- continue;
-
- // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is
- if (!item->isPositionAbsolute()
- && (item->position().x() < m_axisCacheX.min()
- || item->position().x() > m_axisCacheX.max()
- || item->position().z() < m_axisCacheZ.min()
- || item->position().z() > m_axisCacheZ.max()
- || item->position().y() < m_axisCacheY.min()
- || item->position().y() > m_axisCacheY.max())) {
- continue;
- }
+ // Draw custom items - first regular and then volumes
+ bool volumeDetected = false;
+ int loopCount = 0;
+ while (loopCount < 2) {
+ foreach (CustomRenderItem *item, m_customRenderCache) {
+ // Check that the render item is visible, and skip drawing if not
+ // Also check if reflected item is on the "wrong" side, and skip drawing if it is
+ if (!item->isVisible() || ((m_reflectionEnabled && reflection < 0.0f)
+ && (m_yFlipped == (item->translation().y() >= 0.0)))) {
+ continue;
+ }
+ if (loopCount == 0) {
+ if (item->isVolume()) {
+ volumeDetected = true;
+ continue;
+ }
+ } else {
+ if (!item->isVolume())
+ continue;
+ }
- QMatrix4x4 modelMatrix;
- QMatrix4x4 itModelMatrix;
- QMatrix4x4 MVPMatrix;
-
- QQuaternion rotation = item->rotation();
- // Check if the (label) item should be facing camera, and adjust rotation accordingly
- if (item->isFacingCamera()) {
- float camRotationX = m_cachedScene->activeCamera()->xRotation();
- float camRotationY = m_cachedScene->activeCamera()->yRotation();
- rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX)
- * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY);
+ // If the render item is in data coordinates and not within axis ranges, skip it
+ if (!item->isPositionAbsolute()
+ && (item->position().x() < m_axisCacheX.min()
+ || item->position().x() > m_axisCacheX.max()
+ || item->position().z() < m_axisCacheZ.min()
+ || item->position().z() > m_axisCacheZ.max()
+ || item->position().y() < m_axisCacheY.min()
+ || item->position().y() > m_axisCacheY.max())) {
+ continue;
+ }
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ QQuaternion rotation = item->rotation();
+ // Check if the (label) item should be facing camera, and adjust rotation accordingly
+ if (item->isFacingCamera()) {
+ float camRotationX = m_cachedScene->activeCamera()->xRotation();
+ float camRotationY = m_cachedScene->activeCamera()->yRotation();
+ rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX)
+ * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY);
+ }
+
+ if (m_reflectionEnabled) {
+ if (reflection < 0.0f) {
+ if (item->itemPointer()->d_ptr->m_isLabelItem)
+ continue;
+ else
+ glCullFace(GL_FRONT);
+ } else {
+ glCullFace(GL_BACK);
+ }
+ QVector3D trans = item->translation();
+ trans.setY(reflection * trans.y());
+ modelMatrix.translate(trans);
+ if (reflection < 0.0f) {
+ QQuaternion mirror = QQuaternion(rotation.scalar(),
+ -rotation.x(), rotation.y(), -rotation.z());
+ modelMatrix.rotate(mirror);
+ itModelMatrix.rotate(mirror);
+ } else {
+ modelMatrix.rotate(rotation);
+ itModelMatrix.rotate(rotation);
+ }
+ QVector3D scale = item->scaling();
+ scale.setY(reflection * scale.y());
+ modelMatrix.scale(scale);
+ } else {
+ modelMatrix.translate(item->translation());
+ modelMatrix.rotate(rotation);
+ modelMatrix.scale(item->scaling());
+ itModelMatrix.rotate(rotation);
+ }
+ if (!item->isFacingCamera())
+ itModelMatrix.scale(item->scaling());
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ if (RenderingNormal == state) {
+ // Normal render
+ ShaderHelper *prevShader = shader;
+ if (item->isVolume() && !m_isOpenGLES) {
+ if (item->drawSlices() &&
+ (item->sliceIndexX() >= 0
+ || item->sliceIndexY() >= 0
+ || item->sliceIndexZ() >= 0)) {
+ shader = m_volumeTextureSliceShader;
+ } else if (item->useHighDefShader()) {
+ shader = m_volumeTextureShader;
+ } else {
+ shader = m_volumeTextureLowDefShader;
+ }
+ } else if (item->isLabel()) {
+ shader = m_labelShader;
+ } else {
+ shader = regularShader;
+ }
+ if (shader != prevShader)
+ shader->bind();
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+
+ if (item->isBlendNeeded()) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if (!item->isVolume() && !m_isOpenGLES)
+ glDisable(GL_CULL_FACE);
+ } else {
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ }
+
+ if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone
+ && !item->isVolume()) {
+ // Set shadow shader bindings
+ shader->setUniformValue(shader->shadowQ(), shadowQuality);
+ shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix);
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f);
+ m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture);
+ } else {
+ // Set shadowless shader bindings
+ if (item->isVolume() && !m_isOpenGLES) {
+ QVector3D cameraPos = m_cachedScene->activeCamera()->position();
+ cameraPos = MVPMatrix.inverted().map(cameraPos);
+ // Adjust camera position according to min/max bounds
+ cameraPos = -(cameraPos
+ + ((oneVector - cameraPos) * item->minBoundsNormal())
+ - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal())));
+ shader->setUniformValue(shader->cameraPositionRelativeToModel(), cameraPos);
+ GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0;
+ if (color8Bit) {
+ shader->setUniformValueArray(shader->colorIndex(),
+ item->colorTable().constData(), 256);
+ }
+ shader->setUniformValue(shader->color8Bit(), color8Bit);
+ shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier());
+ shader->setUniformValue(shader->preserveOpacity(),
+ item->preserveOpacity() ? 1 : 0);
+
+ shader->setUniformValue(shader->minBounds(), item->minBounds());
+ shader->setUniformValue(shader->maxBounds(), item->maxBounds());
+
+ if (shader == m_volumeTextureSliceShader) {
+ shader->setUniformValue(shader->volumeSliceIndices(),
+ item->sliceFractions());
+ } else {
+ // Precalculate texture dimensions so we can optimize
+ // ray stepping to hit every texture layer.
+ QVector3D textureDimensions(1.0f / float(item->textureWidth()),
+ 1.0f / float(item->textureHeight()),
+ 1.0f / float(item->textureDepth()));
+
+ // Worst case scenario sample count
+ int sampleCount;
+ if (shader == m_volumeTextureLowDefShader) {
+ sampleCount = qMax(item->textureWidth(),
+ qMax(item->textureDepth(), item->textureHeight()));
+ // Further improve speed with big textures by simply dropping every
+ // other sample:
+ if (sampleCount > 256)
+ sampleCount /= 2;
+ } else {
+ sampleCount = item->textureWidth() + item->textureHeight()
+ + item->textureDepth();
+ }
+ shader->setUniformValue(shader->textureDimensions(), textureDimensions);
+ shader->setUniformValue(shader->sampleCount(), sampleCount);
+ }
+ if (item->drawSliceFrames()) {
+ // Set up the slice frame shader
+ glDisable(GL_CULL_FACE);
+ m_volumeSliceFrameShader->bind();
+ m_volumeSliceFrameShader->setUniformValue(
+ m_volumeSliceFrameShader->color(), item->sliceFrameColor());
+
+ // Draw individual slice frames.
+ if (item->sliceIndexX() >= 0)
+ drawVolumeSliceFrame(item, Qt::XAxis, projectionViewMatrix);
+ if (item->sliceIndexY() >= 0)
+ drawVolumeSliceFrame(item, Qt::YAxis, projectionViewMatrix);
+ if (item->sliceIndexZ() >= 0)
+ drawVolumeSliceFrame(item, Qt::ZAxis, projectionViewMatrix);
+
+ glEnable(GL_CULL_FACE);
+ shader->bind();
+ }
+ m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture());
+ } else {
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
+ m_drawer->drawObject(shader, item->mesh(), item->texture());
+ }
+ }
+ } else if (RenderingSelection == state) {
+ // Selection render
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ QVector4D itemColor = indexToSelectionColor(item->index());
+ itemColor.setW(customItemAlpha);
+ itemColor /= 255.0f;
+ shader->setUniformValue(shader->color(), itemColor);
+ m_drawer->drawObject(shader, item->mesh());
+ } else if (item->isShadowCasting()) {
+ // Depth render
+ shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix);
+ m_drawer->drawObject(shader, item->mesh());
+ }
}
+ loopCount++;
+ if (!volumeDetected)
+ loopCount++; // Skip second run if no volumes detected
+ }
+
+ if (RenderingNormal == state) {
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ }
+}
+
+void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis,
+ const QMatrix4x4 &projectionViewMatrix)
+{
+ QVector2D frameWidth;
+ QVector3D frameScaling;
+ QVector3D translation = item->translation();
+ QQuaternion rotation = item->rotation();
+ float fracTrans;
+ bool needRotate = !rotation.isIdentity();
+ QMatrix4x4 rotationMatrix;
+ if (needRotate)
+ rotationMatrix.rotate(rotation);
+
+ if (axis == Qt::XAxis) {
+ fracTrans = item->sliceFractions().x();
+ float range = item->maxBoundsNormal().x() - item->minBoundsNormal().x();
+ float minMult = item->minBoundsNormal().x() / range;
+ float maxMult = (1.0f - item->maxBoundsNormal().x()) / range;
+ fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
+ if (needRotate)
+ translation += rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f));
+ else
+ translation.setX(translation.x() + fracTrans * item->scaling().x());
+ frameScaling = QVector3D(item->scaling().z()
+ + (item->scaling().z() * item->sliceFrameGaps().z())
+ + (item->scaling().z() * item->sliceFrameWidths().z()),
+ item->scaling().y()
+ + (item->scaling().y() * item->sliceFrameGaps().y())
+ + (item->scaling().y() * item->sliceFrameWidths().y()),
+ item->scaling().x() * item->sliceFrameThicknesses().x());
+ frameWidth = QVector2D(item->scaling().z() * item->sliceFrameWidths().z(),
+ item->scaling().y() * item->sliceFrameWidths().y());
+ rotation *= m_yRightAngleRotation;
+ } else if (axis == Qt::YAxis) {
+ fracTrans = item->sliceFractions().y();
+ float range = item->maxBoundsNormal().y() - item->minBoundsNormal().y();
+ // Y axis is logically flipped, so we need to swam min and max bounds
+ float minMult = (1.0f - item->maxBoundsNormal().y()) / range;
+ float maxMult = item->minBoundsNormal().y() / range;
+ fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
+ if (needRotate)
+ translation -= rotationMatrix.map(QVector3D(0.0f, fracTrans * item->scaling().y(), 0.0f));
+ else
+ translation.setY(translation.y() - fracTrans * item->scaling().y());
+ frameScaling = QVector3D(item->scaling().x()
+ + (item->scaling().x() * item->sliceFrameGaps().x())
+ + (item->scaling().x() * item->sliceFrameWidths().x()),
+ item->scaling().z()
+ + (item->scaling().z() * item->sliceFrameGaps().z())
+ + (item->scaling().z() * item->sliceFrameWidths().z()),
+ item->scaling().y() * item->sliceFrameThicknesses().y());
+ frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(),
+ item->scaling().z() * item->sliceFrameWidths().z());
+ rotation *= m_xRightAngleRotation;
+ } else { // Z axis
+ fracTrans = item->sliceFractions().z();
+ float range = item->maxBoundsNormal().z() - item->minBoundsNormal().z();
+ // Z axis is logically flipped, so we need to swam min and max bounds
+ float minMult = (1.0f - item->maxBoundsNormal().z()) / range;
+ float maxMult = item->minBoundsNormal().z() / range;
+ fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
+ if (needRotate)
+ translation -= rotationMatrix.map(QVector3D(0.0f, 0.0f, fracTrans * item->scaling().z()));
+ else
+ translation.setZ(translation.z() - fracTrans * item->scaling().z());
+ frameScaling = QVector3D(item->scaling().x()
+ + (item->scaling().x() * item->sliceFrameGaps().x())
+ + (item->scaling().x() * item->sliceFrameWidths().x()),
+ item->scaling().y()
+ + (item->scaling().y() * item->sliceFrameGaps().y())
+ + (item->scaling().y() * item->sliceFrameWidths().y()),
+ item->scaling().z() * item->sliceFrameThicknesses().z());
+ frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(),
+ item->scaling().y() * item->sliceFrameWidths().y());
+ }
+
+ // If the slice is outside the shown area, don't show the frame
+ if (fracTrans < -1.0 || fracTrans > 1.0)
+ return;
+
+ // Shader needs the width of clear space in the middle.
+ frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x()));
+ frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y()));
- modelMatrix.translate(item->translation());
- modelMatrix.rotate(rotation);
- modelMatrix.scale(item->scaling());
- itModelMatrix.rotate(rotation);
- if (!item->isFacingCamera())
- itModelMatrix.scale(item->scaling());
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 mvpMatrix;
+
+ modelMatrix.translate(translation);
+ modelMatrix.rotate(rotation);
+ modelMatrix.scale(frameScaling);
+ mvpMatrix = projectionViewMatrix * modelMatrix;
+ m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->MVP(), mvpMatrix);
+ m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->sliceFrameWidth(),
+ frameWidth);
+
+ m_drawer->drawObject(m_volumeSliceFrameShader, item->mesh());
+
+}
+
+void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix,
+ const QVector3D &scaling,
+ GLuint defaultFboHandle)
+{
+ m_cursorPositionShader->bind();
+
+ // Set up mapper framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, m_cursorPositionFrameBuffer);
+ glViewport(0, 0,
+ m_primarySubViewport.width(),
+ m_primarySubViewport.height());
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_DITHER); // Dither may affect colors if enabled
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+
+ // Draw a cube scaled to the graph dimensions
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.scale(scaling);
+
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+ m_cursorPositionShader->setUniformValue(m_cursorPositionShader->MVP(), MVPMatrix);
+ m_drawer->drawObject(m_cursorPositionShader, m_positionMapperObj);
+
+ QVector4D dataColor = Utils::getSelection(m_graphPositionQuery,
+ m_primarySubViewport.height());
+ if (dataColor.w() > 0.0f) {
+ // If position is outside the graph, set the position well outside the graph boundaries
+ dataColor = QVector4D(-10000.0f, -10000.0f, -10000.0f, 0.0f);
+ } else {
+ // Normalize to range [0.0, 1.0]
+ dataColor /= 255.0f;
+ }
+
+ // Restore state
+ glEnable(GL_DITHER);
+ glCullFace(GL_BACK);
+ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
+ glViewport(m_primarySubViewport.x(),
+ m_primarySubViewport.y(),
+ m_primarySubViewport.width(),
+ m_primarySubViewport.height());
+
+ QVector3D normalizedValues = dataColor.toVector3D() * 2.0f;
+ normalizedValues -= oneVector;
+ m_queriedGraphPosition = QVector3D(normalizedValues.x(),
+ normalizedValues.y(),
+ normalizedValues.z());
+ m_graphPositionQueryResolved = true;
+ m_graphPositionQueryPending = false;
+}
+
+void Abstract3DRenderer::fixContextBeforeDelete()
+{
+ // Only need to fix context if the current context is null.
+ // Otherwise we expect it to be our shared context, so we can use it for cleanup.
+ if (!QOpenGLContext::currentContext() && !m_context.isNull()
+ && QThread::currentThread() == this->thread()) {
+ m_dummySurfaceAtDelete = new QWindow();
+ m_dummySurfaceAtDelete->setSurfaceType(QWindow::OpenGLSurface);
+ m_dummySurfaceAtDelete->setFormat(m_context->format());
+ m_dummySurfaceAtDelete->create();
+
+ m_context->makeCurrent(m_dummySurfaceAtDelete);
+ }
+}
+
+void Abstract3DRenderer::restoreContextAfterDelete()
+{
+ if (m_dummySurfaceAtDelete)
+ m_context->doneCurrent();
+
+ delete m_dummySurfaceAtDelete;
+ m_dummySurfaceAtDelete = 0;
+}
+
+void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const
+{
+ // x is angular, z is radial
+ qreal angle = m_axisCacheX.formatter()->positionAt(dataPos.x()) * doublePi;
+ qreal radius = m_axisCacheZ.formatter()->positionAt(dataPos.z());
+
+ // Convert angle & radius to X and Z coords
+ x = float(radius * qSin(angle)) * m_polarRadius;
+ z = -float(radius * qCos(angle)) * m_polarRadius;
+}
+
+void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &depthMatrix)
+{
+ static QVector<QQuaternion> lineRotations;
+ if (!lineRotations.size()) {
+ lineRotations.resize(polarGridRoundness);
+ for (int j = 0; j < polarGridRoundness; j++) {
+ lineRotations[j] = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f,
+ polarGridAngleDegrees * float(j));
+ }
+ }
+ int gridLineCount = m_axisCacheZ.gridLineCount();
+ const QVector<float> &gridPositions = m_axisCacheZ.formatter()->gridPositions();
+ const QVector<float> &subGridPositions = m_axisCacheZ.formatter()->subGridPositions();
+ int mainSize = gridPositions.size();
+ QVector3D translateVector(0.0f, yFloorLinePos, 0.0f);
+ QQuaternion finalRotation = m_xRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ finalRotation *= m_xFlipRotation;
+
+ for (int i = 0; i < gridLineCount; i++) {
+ float gridPosition = (i >= mainSize)
+ ? subGridPositions.at(i - mainSize) : gridPositions.at(i);
+ float radiusFraction = m_polarRadius * gridPosition;
+ QVector3D gridLineScaler(radiusFraction * float(qSin(polarGridHalfAngle)),
+ gridLineWidth, gridLineWidth);
+ translateVector.setZ(gridPosition * m_polarRadius);
+ for (int j = 0; j < polarGridRoundness; j++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ modelMatrix.rotate(lineRotations.at(j));
+ itModelMatrix.rotate(lineRotations.at(j));
+ modelMatrix.translate(translateVector);
+ modelMatrix.scale(gridLineScaler);
+ itModelMatrix.scale(gridLineScaler);
+ modelMatrix.rotate(finalRotation);
+ itModelMatrix.rotate(finalRotation);
+ QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix;
- if (RenderingNormal == state) {
- // Normal render
shader->setUniformValue(shader->model(), modelMatrix);
- shader->setUniformValue(shader->MVP(), MVPMatrix);
shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
-
- if (item->isBlendNeeded()) {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDisable(GL_CULL_FACE);
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix;
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj);
+ }
} else {
- glDisable(GL_BLEND);
- glEnable(GL_CULL_FACE);
+ m_drawer->drawLine(shader);
}
+ }
+ }
+}
-#if !defined(QT_OPENGL_ES_2)
+void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &depthMatrix)
+{
+ float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f);
+ QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio);
+ int gridLineCount = m_axisCacheX.gridLineCount();
+ const QVector<float> &gridPositions = m_axisCacheX.formatter()->gridPositions();
+ const QVector<float> &subGridPositions = m_axisCacheX.formatter()->subGridPositions();
+ int mainSize = gridPositions.size();
+ QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio);
+ QQuaternion finalRotation;
+ if (m_isOpenGLES)
+ finalRotation = m_yRightAngleRotationNeg;
+ else
+ finalRotation = m_xRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ finalRotation *= m_xFlipRotation;
+ for (int i = 0; i < gridLineCount; i++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ float gridPosition = (i >= mainSize)
+ ? subGridPositions.at(i - mainSize) : gridPositions.at(i);
+ QQuaternion lineRotation = QQuaternion::fromAxisAndAngle(upVector, gridPosition * 360.0f);
+ modelMatrix.rotate(lineRotation);
+ itModelMatrix.rotate(lineRotation);
+ modelMatrix.translate(translateVector);
+ modelMatrix.scale(gridLineScaler);
+ itModelMatrix.scale(gridLineScaler);
+ modelMatrix.rotate(finalRotation);
+ itModelMatrix.rotate(finalRotation);
+ QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(shader);
+ } else {
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
// Set shadow shader bindings
- shader->setUniformValue(shader->shadowQ(), shadowQuality);
- shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix);
- shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f);
- m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture);
- } else
-#else
- Q_UNUSED(depthTexture)
- Q_UNUSED(shadowQuality)
-#endif
- {
- // Set shadowless shader bindings
- shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
- m_drawer->drawObject(shader, item->mesh(), item->texture());
+ QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix;
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj);
}
- } else if (RenderingSelection == state) {
- // Selection render
- shader->setUniformValue(shader->MVP(), MVPMatrix);
- QVector4D itemColor = indexToSelectionColor(item->index());
- itemColor.setW(customItemAlpha);
- itemColor /= 255.0f;
- shader->setUniformValue(shader->color(), itemColor);
- m_drawer->drawObject(shader, item->mesh());
- } else if (item->isShadowCasting()) {
- // Depth render
- shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix);
- m_drawer->drawObject(shader, item->mesh());
}
}
+}
- if (RenderingNormal == state) {
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
- glEnable(GL_CULL_FACE);
+float Abstract3DRenderer::calculatePolarBackgroundMargin()
+{
+ // Check each extents of each angular label
+ // Calculate angular position
+ QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
+ float actualLabelHeight = m_drawer->scaledFontSize() * 2.0f; // All labels are same height
+ float maxNeededMargin = 0.0f;
+
+ // Axis title needs to be accounted for
+ if (m_axisCacheX.isTitleVisible())
+ maxNeededMargin = 2.0f * actualLabelHeight + 3.0f * labelMargin;
+
+ for (int label = 0; label < labelPositions.size(); label++) {
+ QSize labelSize = m_axisCacheX.labelItems().at(label)->size();
+ float actualLabelWidth = actualLabelHeight / labelSize.height() * labelSize.width();
+ float labelPosition = labelPositions.at(label);
+ qreal angle = labelPosition * M_PI * 2.0;
+ float x = qAbs((m_polarRadius + labelMargin) * float(qSin(angle)))
+ + actualLabelWidth - m_polarRadius + labelMargin;
+ float z = qAbs(-(m_polarRadius + labelMargin) * float(qCos(angle)))
+ + actualLabelHeight - m_polarRadius + labelMargin;
+ float neededMargin = qMax(x, z);
+ maxNeededMargin = qMax(maxNeededMargin, neededMargin);
}
+
+ return maxNeededMargin;
+}
+
+void Abstract3DRenderer::updateCameraViewport()
+{
+ QVector3D adjustedTarget = m_cachedScene->activeCamera()->target();
+ fixCameraTarget(adjustedTarget);
+ if (m_oldCameraTarget != adjustedTarget) {
+ QVector3D cameraBase = cameraDistanceVector + adjustedTarget;
+
+ m_cachedScene->activeCamera()->d_ptr->setBaseOrientation(cameraBase,
+ adjustedTarget,
+ upVector);
+ m_oldCameraTarget = adjustedTarget;
+ }
+ m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment);
+ // Set light position (i.e rotate light with activeCamera, a bit above it)
+ m_cachedScene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos);
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h
index 0dfc7367..1e38023d 100644
--- a/src/datavisualization/engine/abstract3drenderer_p.h
+++ b/src/datavisualization/engine/abstract3drenderer_p.h
@@ -30,13 +30,17 @@
#define ABSTRACT3DRENDERER_P_H
#include <QtGui/QOpenGLFunctions>
-
+#if !defined(QT_OPENGL_ES_2)
+# include <QtGui/QOpenGLFunctions_2_1>
+#endif
#include "datavisualizationglobal_p.h"
#include "abstract3dcontroller_p.h"
#include "axisrendercache_p.h"
#include "seriesrendercache_p.h"
#include "customrenderitem_p.h"
+class QSurface;
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class TextureHelper;
@@ -77,21 +81,33 @@ public:
virtual void updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode);
virtual void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint);
virtual void updateScene(Q3DScene *scene);
- virtual void updateTextures() = 0;
+ virtual void updateTextures();
virtual void initSelectionBuffer() = 0;
virtual void updateSelectionState(SelectionState state);
- virtual void updateInputPosition(const QPoint &position);
-#if !defined(QT_OPENGL_ES_2)
virtual void updateDepthBuffer() = 0;
-#endif
virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) = 0;
virtual void initShaders(const QString &vertexShader, const QString &fragmentShader) = 0;
virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader);
+ virtual void initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader);
virtual void initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader) = 0;
virtual void initCustomItemShaders(const QString &vertexShader,
const QString &fragmentShader);
+ virtual void initVolumeTextureShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &fragmentLowDefShader,
+ const QString &sliceShader,
+ const QString &sliceFrameVertexShader,
+ const QString &sliceFrameShader);
+ virtual void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
+ virtual void initCursorPositionShaders(const QString &vertexShader,
+ const QString &fragmentShader);
+ virtual void initCursorPositionBuffer();
+
virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation,
QAbstract3DAxis::AxisType type);
virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation,
@@ -112,7 +128,7 @@ public:
virtual void updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,
float angle);
virtual void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
- bool visible);
+ bool visible);
virtual void updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation,
bool fixed);
virtual void modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList);
@@ -123,6 +139,10 @@ public:
virtual void updateCustomItem(CustomRenderItem *renderItem);
virtual void updateAspectRatio(float ratio);
+ virtual void updateHorizontalAspectRatio(float ratio);
+ virtual void updatePolar(bool enable);
+ virtual void updateRadialLabelOffset(float offset);
+ virtual void updateMargin(float margin);
virtual QVector3D convertPositionToTranslation(const QVector3D &position,
bool isAbsolute) = 0;
@@ -130,21 +150,28 @@ public:
void generateBaseColorTexture(const QColor &color, GLuint *texture);
void fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture);
- inline bool isClickPending() { return m_clickPending; }
- inline void clearClickPending() { m_clickPending = false; }
+ inline bool isClickQueryResolved() const { return m_clickResolved; }
+ inline void clearClickQueryResolved() { m_clickResolved = false; }
+ inline QPoint cachedClickQuery() const { return m_cachedScene->selectionQueryPosition(); }
inline QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; }
inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; }
+ inline bool isGraphPositionQueryResolved() const { return m_graphPositionQueryResolved; }
+ inline void clearGraphPositionQueryResolved() { m_graphPositionQueryResolved = false; }
+ inline QVector3D queriedGraphPosition() const { return m_queriedGraphPosition; }
+ inline QPoint cachedGraphPositionQuery() const { return m_cachedScene->graphPositionQuery(); }
LabelItem &selectionLabelItem();
void setSelectionLabel(const QString &label);
QString &selectionLabel();
- void drawCustomItems(RenderingState state, ShaderHelper *shader,
+ void drawCustomItems(RenderingState state, ShaderHelper *regularShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
- GLuint depthTexture, GLfloat shadowQuality);
+ GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection = 1.0f);
+
QVector4D indexToSelectionColor(GLint index);
+ void calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const;
signals:
void needRender(); // Emit this if something in renderer causes need for another render pass.
@@ -177,7 +204,7 @@ protected:
const QQuaternion &totalRotation, AbstractRenderItem &dummyItem,
const Q3DCamera *activeCamera, float labelsMaxWidth,
const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
- ShaderHelper *shader);
+ ShaderHelper *shader, bool radial = false);
void drawAxisTitleZ(const QVector3D &labelRotation, const QVector3D &labelTrans,
const QQuaternion &totalRotation, AbstractRenderItem &dummyItem,
const Q3DCamera *activeCamera, float labelsMaxWidth,
@@ -186,6 +213,26 @@ protected:
void loadGridLineMesh();
void loadLabelMesh();
+ void loadPositionMapperMesh();
+
+ void drawRadialGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix);
+ void drawAngularGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix);
+
+ float calculatePolarBackgroundMargin();
+ virtual void fixCameraTarget(QVector3D &target) = 0;
+ void updateCameraViewport();
+
+ void recalculateCustomItemScalingAndPos(CustomRenderItem *item);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0;
+ void drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis,
+ const QMatrix4x4 &projectionViewMatrix);
+ void queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, const QVector3D &scaling,
+ GLuint defaultFboHandle);
+
+ void fixContextBeforeDelete();
+ void restoreContextAfterDelete();
bool m_hasNegativeValues;
Q3DTheme *m_cachedTheme;
@@ -201,6 +248,7 @@ protected:
AxisRenderCache m_axisCacheY;
AxisRenderCache m_axisCacheZ;
TextureHelper *m_textureHelper;
+ GLuint m_depthTexture;
Q3DScene *m_cachedScene;
bool m_selectionDirty;
@@ -212,28 +260,75 @@ protected:
QRect m_secondarySubViewport;
float m_devicePixelRatio;
bool m_selectionLabelDirty;
- bool m_clickPending;
+ bool m_clickResolved;
+ bool m_graphPositionQueryPending;
+ bool m_graphPositionQueryResolved;
QAbstract3DSeries *m_clickedSeries;
QAbstract3DGraph::ElementType m_clickedType;
int m_selectedLabelIndex;
int m_selectedCustomItemIndex;
+ QVector3D m_queriedGraphPosition;
+ QPoint m_graphPositionQuery;
QString m_selectionLabel;
LabelItem *m_selectionLabelItem;
int m_visibleSeriesCount;
ShaderHelper *m_customItemShader;
+ ShaderHelper *m_volumeTextureShader;
+ ShaderHelper *m_volumeTextureLowDefShader;
+ ShaderHelper *m_volumeTextureSliceShader;
+ ShaderHelper *m_volumeSliceFrameShader;
+ ShaderHelper *m_labelShader;
+ ShaderHelper *m_cursorPositionShader;
+ GLuint m_cursorPositionFrameBuffer;
+ GLuint m_cursorPositionTexture;
bool m_useOrthoProjection;
bool m_xFlipped;
bool m_yFlipped;
bool m_zFlipped;
+ bool m_yFlippedForGrid;
ObjectHelper *m_backgroundObj; // Shared reference
ObjectHelper *m_gridLineObj; // Shared reference
ObjectHelper *m_labelObj; // Shared reference
+ ObjectHelper *m_positionMapperObj; // Shared reference
float m_graphAspectRatio;
+ float m_graphHorizontalAspectRatio;
+ bool m_polarGraph;
+ float m_radialLabelOffset;
+ float m_polarRadius;
+
+ QQuaternion m_xRightAngleRotation;
+ QQuaternion m_yRightAngleRotation;
+ QQuaternion m_zRightAngleRotation;
+ QQuaternion m_xRightAngleRotationNeg;
+ QQuaternion m_yRightAngleRotationNeg;
+ QQuaternion m_zRightAngleRotationNeg;
+ QQuaternion m_xFlipRotation;
+ QQuaternion m_zFlipRotation;
+
+ float m_requestedMargin;
+ float m_vBackgroundMargin;
+ float m_hBackgroundMargin;
+ float m_scaleXWithBackground;
+ float m_scaleYWithBackground;
+ float m_scaleZWithBackground;
+
+ QVector3D m_oldCameraTarget;
+
+ bool m_reflectionEnabled;
+ qreal m_reflectivity;
+
+ QLocale m_locale;
+#if !defined(QT_OPENGL_ES_2)
+ QOpenGLFunctions_2_1 *m_funcs_2_1; // Not owned
+#endif
+ QPointer<QOpenGLContext> m_context; // Not owned
+ QWindow *m_dummySurfaceAtDelete;
+ bool m_isOpenGLES;
private:
friend class Abstract3DController;
diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp
index 0b7a5c7a..02e3b7f6 100644
--- a/src/datavisualization/engine/axisrendercache.cpp
+++ b/src/datavisualization/engine/axisrendercache.cpp
@@ -46,6 +46,7 @@ AxisRenderCache::~AxisRenderCache()
{
foreach (LabelItem *label, m_labelItems)
delete label;
+ m_titleItem.clear();
delete m_formatter;
}
@@ -54,10 +55,8 @@ void AxisRenderCache::setDrawer(Drawer *drawer)
{
m_drawer = drawer;
m_font = m_drawer->font();
- if (m_drawer) {
- QObject::connect(m_drawer, &Drawer::drawerChanged, this, &AxisRenderCache::updateTextures);
+ if (m_drawer)
updateTextures();
- }
}
void AxisRenderCache::setType(QAbstract3DAxis::AxisType type)
@@ -106,10 +105,12 @@ void AxisRenderCache::setLabels(const QStringList &labels)
if (i >= oldSize)
m_labelItems.append(new LabelItem);
if (m_drawer) {
- if (labels.at(i).isEmpty())
+ if (labels.at(i).isEmpty()) {
m_labelItems[i]->clear();
- else if (i >= oldSize || labels.at(i) != m_labels.at(i))
+ } else if (i >= oldSize || labels.at(i) != m_labels.at(i)
+ || m_labelItems[i]->size().width() != widest) {
m_drawer->generateLabelItem(*m_labelItems[i], labels.at(i), widest);
+ }
}
}
m_labels = labels;
@@ -175,6 +176,13 @@ void AxisRenderCache::updateTextures()
}
}
+void AxisRenderCache::clearLabels()
+{
+ m_titleItem.clear();
+ for (int i = 0; i < m_labels.size(); i++)
+ m_labelItems[i]->clear();
+}
+
int AxisRenderCache::maxLabelWidth(const QStringList &labels) const
{
int labelWidth = 0;
diff --git a/src/datavisualization/engine/axisrendercache_p.h b/src/datavisualization/engine/axisrendercache_p.h
index 90321740..e32c590e 100644
--- a/src/datavisualization/engine/axisrendercache_p.h
+++ b/src/datavisualization/engine/axisrendercache_p.h
@@ -107,8 +107,8 @@ public:
inline bool isTitleFixed() const { return m_titleFixed; }
inline void setTitleFixed(bool fixed) { m_titleFixed = fixed; }
-public slots:
void updateTextures();
+ void clearLabels();
private:
int maxLabelWidth(const QStringList &labels) const;
diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp
index 3a240c24..2092dd60 100644
--- a/src/datavisualization/engine/bars3dcontroller.cpp
+++ b/src/datavisualization/engine/bars3dcontroller.cpp
@@ -36,6 +36,7 @@ Bars3DController::Bars3DController(QRect boundRect, Q3DScene *scene)
m_isBarSpecRelative(true),
m_barThicknessRatio(1.0f),
m_barSpacing(QSizeF(1.0, 1.0)),
+ m_floorLevel(0.0f),
m_renderer(0)
{
// Setting a null axis creates a new default axis according to orientation and graph type.
@@ -83,6 +84,12 @@ void Bars3DController::synchDataToRenderer()
needSceneUpdate = true;
}
+ // Floor level update requires data update, so do before abstract sync
+ if (m_changeTracker.floorLevelChanged) {
+ m_renderer->updateFloorLevel(m_floorLevel);
+ m_changeTracker.floorLevelChanged = false;
+ }
+
Abstract3DController::synchDataToRenderer();
// Notify changes to renderer
@@ -114,12 +121,10 @@ void Bars3DController::synchDataToRenderer()
m_changeTracker.selectedBarChanged = false;
}
- if (needSceneUpdate) {
- // Since scene is updated before axis updates are handled,
- // do another render pass for scene update
- m_scene->d_ptr->m_sceneDirty = true;
- emitNeedRender();
- }
+ // Since scene is updated before axis updates are handled, do another render pass to
+ // properly update controller side camera limits.
+ if (needSceneUpdate)
+ m_scene->d_ptr->markDirty();
}
void Bars3DController::handleArrayReset()
@@ -487,6 +492,19 @@ bool Bars3DController::isBarSpecRelative()
return m_isBarSpecRelative;
}
+void Bars3DController::setFloorLevel(float level)
+{
+ m_floorLevel = level;
+ m_isDataDirty = true;
+ m_changeTracker.floorLevelChanged = true;
+ emitNeedRender();
+}
+
+float Bars3DController::floorLevel() const
+{
+ return m_floorLevel;
+}
+
void Bars3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
{
if (mode.testFlag(QAbstract3DGraph::SelectionSlice)
diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h
index 4f0e1f20..ac62f37d 100644
--- a/src/datavisualization/engine/bars3dcontroller_p.h
+++ b/src/datavisualization/engine/bars3dcontroller_p.h
@@ -43,13 +43,15 @@ struct Bars3DChangeBitField {
bool selectedBarChanged : 1;
bool rowsChanged : 1;
bool itemChanged : 1;
+ bool floorLevelChanged : 1;
Bars3DChangeBitField() :
multiSeriesScalingChanged(true),
barSpecsChanged(true),
selectedBarChanged(true),
rowsChanged(false),
- itemChanged(false)
+ itemChanged(false),
+ floorLevelChanged(false)
{
}
};
@@ -84,6 +86,7 @@ private:
bool m_isBarSpecRelative;
GLfloat m_barThicknessRatio;
QSizeF m_barSpacing;
+ float m_floorLevel;
// Rendering
Bars3DRenderer *m_renderer;
@@ -107,6 +110,8 @@ public:
GLfloat barThickness();
QSizeF barSpacing();
bool isBarSpecRelative();
+ void setFloorLevel(float level);
+ float floorLevel() const;
inline QBar3DSeries *selectedSeries() const { return m_selectedBarSeries; }
diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp
index 689f3f5d..b6a191a9 100644
--- a/src/datavisualization/engine/bars3drenderer.cpp
+++ b/src/datavisualization/engine/bars3drenderer.cpp
@@ -31,7 +31,6 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-const GLfloat gridLineWidth = 0.005f;
const bool sliceGridLabels = true;
Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
@@ -48,9 +47,7 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_depthShader(0),
m_selectionShader(0),
m_backgroundShader(0),
- m_labelShader(0),
m_bgrTexture(0),
- m_depthTexture(0),
m_selectionTexture(0),
m_depthFrameBuffer(0),
m_selectionFrameBuffer(0),
@@ -67,7 +64,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_scaleFactor(0),
m_maxSceneSize(40.0f),
m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()),
- m_resetCameraBaseOrientation(true),
m_selectedBarPos(Bars3DController::invalidSelectionPosition()),
m_selectedSeriesCache(0),
m_noZeroInRange(false),
@@ -79,17 +75,22 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_keepSeriesUniform(false),
m_haveUniformColorSeries(false),
m_haveGradientSeries(false),
- m_zeroPosition(0.0f)
+ m_zeroPosition(0.0f),
+ m_xScaleFactor(1.0f),
+ m_zScaleFactor(1.0f),
+ m_floorLevel(0.0f),
+ m_actualFloorLevel(0.0f)
{
m_axisCacheY.setScale(2.0f);
m_axisCacheY.setTranslate(-1.0f);
- initializeOpenGLFunctions();
initializeOpenGL();
}
Bars3DRenderer::~Bars3DRenderer()
{
+ fixContextBeforeDelete();
+
if (QOpenGLContext::currentContext()) {
m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
@@ -103,7 +104,6 @@ Bars3DRenderer::~Bars3DRenderer()
delete m_depthShader;
delete m_selectionShader;
delete m_backgroundShader;
- delete m_labelShader;
}
void Bars3DRenderer::initializeOpenGL()
@@ -111,13 +111,9 @@ void Bars3DRenderer::initializeOpenGL()
Abstract3DRenderer::initializeOpenGL();
// Initialize shaders
- initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
- QStringLiteral(":/shaders/fragmentLabel"));
-#if !defined(QT_OPENGL_ES_2)
// Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
initDepthShader();
-#endif
// Init selection shader
initSelectionShader();
@@ -125,13 +121,57 @@ void Bars3DRenderer::initializeOpenGL()
// Load grid line mesh
loadGridLineMesh();
- // Load label mesh
- loadLabelMesh();
-
// Load background mesh (we need to be initialized first)
loadBackgroundMesh();
}
+void Bars3DRenderer::fixCameraTarget(QVector3D &target)
+{
+ target.setX(target.x() * m_xScaleFactor);
+ target.setY(0.0f);
+ target.setZ(target.z() * -m_zScaleFactor);
+}
+
+void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_xScaleFactor)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -1.0f + m_backgroundAdjustment)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_zScaleFactor)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_xScaleFactor)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > 1.0f + m_backgroundAdjustment)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_zScaleFactor)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Bars3DRenderer::updateData()
{
int minRow = m_axisCacheZ.min();
@@ -163,11 +203,11 @@ void Bars3DRenderer::updateData()
GLfloat sceneRatio = qMin(GLfloat(newColumns) / GLfloat(newRows),
GLfloat(newRows) / GLfloat(newColumns));
m_maxSceneSize = 2.0f * qSqrt(sceneRatio * newColumns * newRows);
- // Calculate here and at setting bar specs
- calculateSceneScalingFactors();
}
- m_zeroPosition = m_axisCacheY.formatter()->positionAt(0.0f);
+ calculateSceneScalingFactors();
+
+ m_zeroPosition = m_axisCacheY.formatter()->positionAt(m_actualFloorLevel);
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
@@ -390,14 +430,6 @@ void Bars3DRenderer::updateScene(Q3DScene *scene)
}
}
- if (m_resetCameraBaseOrientation) {
- // Set initial camera position. Also update if height adjustment has changed.
- scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector,
- zeroVector,
- upVector);
- m_resetCameraBaseOrientation = false;
- }
-
Abstract3DRenderer::updateScene(scene);
updateSlicingActive(scene->isSlicingActive());
@@ -418,10 +450,17 @@ void Bars3DRenderer::render(GLuint defaultFboHandle)
void Bars3DRenderer::drawSlicedScene()
{
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)
+ == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
+ qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
+ " QAbstract3DGraph::SelectionColumn must be set before calling"
+ " setSlicingActive(true).");
+ return;
+ }
+
GLfloat barPosX = 0;
QVector3D lightPos;
QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
- static QQuaternion ninetyDegreeRotation = QQuaternion::fromAxisAndAngle(upVector, 90.0f);
// Specify viewport
glViewport(m_secondarySubViewport.x(),
@@ -482,11 +521,12 @@ void Bars3DRenderer::drawSlicedScene()
// Draw grid lines
if (m_cachedTheme->isGridEnabled()) {
glDisable(GL_DEPTH_TEST);
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
+
// Bind line shader
lineShader->bind();
@@ -496,7 +536,8 @@ void Bars3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 7.0f);
lineShader->setUniformValue(lineShader->lightS(), 0.0f);
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
@@ -524,11 +565,10 @@ void Bars3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
// Check if we have a line at zero position already
if (gridPos == (barPosYAdjustment + zeroPosAdjustment))
@@ -553,18 +593,16 @@ void Bars3DRenderer::drawSlicedScene()
m_cachedTheme->labelTextColor()));
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
}
}
if (sliceGridLabels) {
// Bind label shader
m_labelShader->bind();
- glEnable(GL_TEXTURE_2D);
glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -587,7 +625,6 @@ void Bars3DRenderer::drawSlicedScene()
}
labelNbr++;
}
- glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
@@ -606,14 +643,16 @@ void Bars3DRenderer::drawSlicedScene()
m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
m_barShader->setUniformValue(m_barShader->lightS(), 0.15f);
m_barShader->setUniformValue(m_barShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 7.0f);
m_barShader->setUniformValue(m_barShader->lightColor(), lightColor);
m_barGradientShader->bind();
m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos);
m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix);
m_barGradientShader->setUniformValue(m_barGradientShader->lightS(), 0.15f);
m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 7.0f);
m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f);
m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor);
@@ -700,7 +739,7 @@ void Bars3DRenderer::drawSlicedScene()
barPosX = item.translation().x();
} else {
barPosX = -(item.translation().z()); // flip z; frontmost bar to the left
- barRotation *= ninetyDegreeRotation;
+ barRotation *= m_yRightAngleRotation;
}
modelMatrix.translate(barPosX, barPosY, 0.0f);
@@ -760,7 +799,6 @@ void Bars3DRenderer::drawSlicedScene()
// Draw labels
m_labelShader->bind();
glDisable(GL_DEPTH_TEST);
- glEnable(GL_TEXTURE_2D);
glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -778,6 +816,12 @@ void Bars3DRenderer::drawSlicedScene()
int labelCount = m_sliceCache->labelItems().size();
for (int labelNo = 0; labelNo < labelCount; labelNo++) {
+ // Check for invalid usage (no selection when setting slicing active)
+ if (!firstVisualSliceArray) {
+ qWarning("No slice data found. Make sure there is a valid selection.");
+ continue;
+ }
+
// Get labels from first series only
const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo);
m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
@@ -889,7 +933,6 @@ void Bars3DRenderer::drawSlicedScene()
m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
false, false, Drawer::LabelMid, Qt::AlignBottom);
- glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
@@ -912,8 +955,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
GLfloat colPos = 0;
GLfloat rowPos = 0;
- QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
-
const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
glViewport(m_primarySubViewport.x(),
@@ -990,15 +1031,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
- bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
-
- GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor;
- GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor;
-
BarRenderItem *selectedBar(0);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Render scene into a depth texture for using with shadow mapping
// Enable drawing to depth framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
@@ -1052,6 +1087,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
shadowOffset = -0.015f;
}
+ if (m_cachedTheme->isBackgroundEnabled() && m_reflectionEnabled
+ && ((m_yFlipped && item.height() > 0.0)
+ || (!m_yFlipped && item.height() < 0.0))) {
+ continue;
+ }
+
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
@@ -1097,8 +1138,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -1112,12 +1154,22 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_primarySubViewport.width(),
m_primarySubViewport.height());
}
-#endif
+
+ // Do position mapping when necessary
+ if (m_graphPositionQueryPending) {
+ QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor);
+ queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
+
+ // Y is always at floor level
+ m_queriedGraphPosition.setY(0.0f);
+ emit needRender();
+ }
// Skip selection mode drawing if we're slicing or have no selection mode
if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
&& m_selectionState == SelectOnScene
- && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) {
+ && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
+ && m_selectionTexture) {
// Bind selection shader
m_selectionShader->bind();
@@ -1181,18 +1233,20 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
glCullFace(GL_BACK);
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
+ viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
- drawLabels(true, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor,
- columnScaleFactor);
+ drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix, false, true);
glEnable(GL_DITHER);
// Read color under cursor
- QVector4D clickedColor = Utils::getSelection(m_inputPosition,
- m_viewport.height());
+ QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height());
m_clickedPosition = selectionColorToArrayPosition(clickedColor);
m_clickedSeries = selectionColorToSeries(clickedColor);
+ m_clickResolved = true;
emit needRender();
@@ -1204,8 +1258,133 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_primarySubViewport.height());
}
- // Enable texturing
- glEnable(GL_TEXTURE_2D);
+ if (m_reflectionEnabled) {
+ //
+ // Draw reflections
+ //
+ glDisable(GL_DEPTH_TEST);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glEnable(GL_STENCIL_TEST);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
+
+ // Draw background stencil
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+
+ glStencilFunc(GL_EQUAL, 1, 0xffffffff);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ // Set light
+ QVector3D reflectionLightPos = lightPos;
+ reflectionLightPos.setY(-(lightPos.y()));
+ m_cachedScene->activeLight()->setPosition(reflectionLightPos);
+
+ // Draw bar reflections
+ (void)drawBars(&selectedBar, depthProjectionViewMatrix,
+ projectionViewMatrix, viewMatrix,
+ startRow, stopRow, stepRow,
+ startBar, stopBar, stepBar, -1.0f);
+
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader,
+ viewMatrix, projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader, -1.0f);
+
+ // Reset light
+ m_cachedScene->activeLight()->setPosition(lightPos);
+
+ glDisable(GL_STENCIL_TEST);
+
+ glCullFace(GL_BACK);
+ }
+
+ //
+ // Draw the real scene
+ //
+ // Draw background
+ if (m_reflectionEnabled) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix, true);
+ glDisable(GL_BLEND);
+ } else {
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix);
+ }
+
+ // Draw bars
+ bool barSelectionFound = drawBars(&selectedBar, depthProjectionViewMatrix,
+ projectionViewMatrix, viewMatrix,
+ startRow, stopRow, stepRow,
+ startBar, stopBar, stepBar);
+
+ // Draw grid lines
+ drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix);
+
+ // Draw custom items
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+
+ // Draw labels
+ drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
+
+ // Handle selected bar label generation
+ if (barSelectionFound) {
+ // Print value of selected bar
+ glDisable(GL_DEPTH_TEST);
+ // Draw the selection label
+ LabelItem &labelItem = selectionLabelItem();
+ if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId()
+ || m_selectionLabelDirty) {
+ QString labelText = selectionLabel();
+ if (labelText.isNull() || m_selectionLabelDirty) {
+ labelText = m_selectedSeriesCache->itemLabel();
+ setSelectionLabel(labelText);
+ m_selectionLabelDirty = false;
+ }
+ m_drawer->generateLabelItem(labelItem, labelText);
+ m_selectedBar = selectedBar;
+ }
+
+ Drawer::LabelPosition position =
+ m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow;
+
+ m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix,
+ zeroVector, identityQuaternion, selectedBar->height(),
+ m_cachedSelectionMode, m_labelShader,
+ m_labelObj, activeCamera, true, false, position);
+
+ // Reset label update flag; they should have been updated when we get here
+ m_updateLabels = false;
+
+ glEnable(GL_DEPTH_TEST);
+ } else {
+ m_selectedBar = 0;
+ }
+
+ glDisable(GL_BLEND);
+
+ // Release shader
+ glUseProgram(0);
+ m_selectionDirty = false;
+}
+
+bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar,
+ const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
+ GLint startRow, GLint stopRow, GLint stepRow,
+ GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection)
+{
+ QVector3D lightPos = m_cachedScene->activeLight()->position();
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+
+ bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
ShaderHelper *barShader = 0;
GLuint gradientTexture = 0;
@@ -1251,7 +1430,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_sliceTitleItem = 0;
}
- // Draw bars
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.5f, 1.0f);
@@ -1302,12 +1480,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
previousColorStyle = colorStyle;
-
for (int row = startRow; row != stopRow; row += stepRow) {
BarRenderItemRow &renderRow = renderArray[row];
for (int bar = startBar; bar != stopBar; bar += stepBar) {
BarRenderItem &item = renderRow[bar];
- if (item.height() < 0)
+ float adjustedHeight = reflection * item.height();
+ if (adjustedHeight < 0)
glCullFace(GL_FRONT);
else
glCullFace(GL_BACK);
@@ -1316,13 +1494,13 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 itModelMatrix;
QMatrix4x4 MVPMatrix;
- colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
- rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
+ GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
+ GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
- item.height(),
+ adjustedHeight,
(m_columnDepth - rowPos) / m_scaleFactor);
- modelScaler.setY(item.height());
+ modelScaler.setY(adjustedHeight);
if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
QQuaternion totalRotation = seriesRotation * item.rotation();
modelMatrix.rotate(totalRotation);
@@ -1357,8 +1535,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
// We have no ownership, don't delete the previous one
if (!m_cachedIsSlicingActivated
&& m_selectedSeriesCache == cache) {
- selectedBar = &item;
- selectedBar->setPosition(QPoint(row, bar));
+ *selectedBar = &item;
+ (*selectedBar)->setPosition(QPoint(row, bar));
item.setTranslation(modelMatrix.column(3).toVector3D());
barSelectionFound = true;
}
@@ -1438,8 +1616,15 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
- // Skip drawing of 0-height bars
- if (item.height() != 0) {
+ if (item.height() == 0) {
+ continue;
+ } else if ((m_reflectionEnabled
+ && (reflection == 1.0f
+ || (reflection != 1.0f
+ && ((m_yFlipped && item.height() < 0.0)
+ || (!m_yFlipped && item.height() > 0.0)))))
+ || !m_reflectionEnabled) {
+ // Skip drawing of 0-height bars and reflections of bars on the "wrong side"
// Set shader bindings
barShader->setUniformValue(barShader->model(), modelMatrix);
barShader->setUniformValue(barShader->nModel(),
@@ -1452,8 +1637,10 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
qAbs(item.height()) / m_gradientFraction);
}
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (((m_reflectionEnabled && reflection == 1.0f
+ && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
+ || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
+ && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
barShader->setUniformValue(barShader->shadowQ(),
@@ -1465,13 +1652,15 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw the object
m_drawer->drawObject(barShader, barObj, gradientTexture,
m_depthTexture);
- } else
-#else
- Q_UNUSED(shadowLightStrength);
-#endif
- {
+ } else {
// Set shadowless shader bindings
- barShader->setUniformValue(barShader->lightS(), lightStrength);
+ if (m_reflectionEnabled && reflection != 1.0f
+ && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ barShader->setUniformValue(barShader->lightS(),
+ adjustedLightStrength);
+ } else {
+ barShader->setUniformValue(barShader->lightS(), lightStrength);
+ }
// Draw the object
m_drawer->drawObject(barShader, barObj, gradientTexture);
@@ -1481,122 +1670,145 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
}
-
glDisable(GL_POLYGON_OFFSET_FILL);
// Reset culling
glCullFace(GL_BACK);
- // Bind background shader
- m_backgroundShader->bind();
+ return barSelectionFound;
+}
+void Bars3DRenderer::drawBackground(GLfloat backgroundRotation,
+ const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &viewMatrix, bool reflectingDraw,
+ bool drawingSelectionBuffer)
+{
// Draw background
if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
+ QVector3D lightPos = m_cachedScene->activeLight()->position();
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+ GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
+ ShaderHelper *shader = 0;
+
+ // Bind background shader
+ if (drawingSelectionBuffer)
+ shader = m_selectionShader; // Use single color shader when drawing to selection buffer
+ else
+ shader = m_backgroundShader;
+ shader->bind();
+
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor);
- modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f);
+ QVector3D backgroundScaler(m_scaleXWithBackground, m_scaleYWithBackground,
+ m_scaleZWithBackground);
+ QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
+ if (m_reflectionEnabled)
+ backgroundColor.setW(backgroundColor.w() * m_reflectivity);
+ // Set shader bindings
+ shader->setUniformValue(shader->lightP(), lightPos);
+ shader->setUniformValue(shader->view(), viewMatrix);
+ if (drawingSelectionBuffer) {
+ // Use selectionSkipColor for background when drawing to selection buffer
+ shader->setUniformValue(shader->color(), selectionSkipColor);
+ } else {
+ shader->setUniformValue(shader->color(), backgroundColor);
+ }
+ shader->setUniformValue(shader->ambientS(),
+ m_cachedTheme->ambientLightStrength() * 2.0f);
+ shader->setUniformValue(shader->lightColor(), lightColor);
+
+ // Draw floor
modelMatrix.scale(backgroundScaler);
- itModelMatrix.scale(backgroundScaler);
- modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
- itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
+
+ if (m_yFlipped)
+ modelMatrix.rotate(m_xRightAngleRotation);
+ else
+ modelMatrix.rotate(m_xRightAngleRotationNeg);
+
+ itModelMatrix = modelMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
-
- // Set shader bindings
- m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos);
- m_backgroundShader->setUniformValue(m_backgroundShader->view(), viewMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->nModel(),
- itModelMatrix.inverted().transposed());
- m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->color(), backgroundColor);
- m_backgroundShader->setUniformValue(m_backgroundShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.0f);
- m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
+ // Set changed shader bindings
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
- m_shadowQualityToShader);
- m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
- adjustedLightStrength);
-
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
// Draw the object
- m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
- } else
-#endif
- {
- // Set shadowless shader bindings
- m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
- m_cachedTheme->lightStrength());
-
+ m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
+ } else {
// Draw the object
- m_drawer->drawObject(m_backgroundShader, m_backgroundObj);
+ m_drawer->drawObject(shader, m_gridLineObj);
}
- // Draw floor
+ // Draw walls
modelMatrix = QMatrix4x4();
itModelMatrix = QMatrix4x4();
+ modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f);
modelMatrix.scale(backgroundScaler);
-
- if (m_yFlipped)
- modelMatrix.rotate(90.0f, 1.0f, 0.0f, 0.0f);
- else
- modelMatrix.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
-
- itModelMatrix = modelMatrix;
+ itModelMatrix.scale(backgroundScaler);
+ modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
+ itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
+
// Set changed shader bindings
- m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->nModel(),
- itModelMatrix.inverted().transposed());
- m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix);
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader);
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
+ shader->setUniformValue(shader->lightS(), adjustedLightStrength);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(m_backgroundShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
- // Draw the object
- m_drawer->drawObject(m_backgroundShader, m_gridLineObj);
+ // Draw the object
+ m_drawer->drawObject(shader, m_backgroundObj, 0, m_depthTexture);
+ } else {
+ // Set shadowless shader bindings
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
+
+ // Draw the object
+ m_drawer->drawObject(shader, m_backgroundObj);
+ }
}
}
+}
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
- // Draw grid lines
+void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &viewMatrix)
+{
if (m_cachedTheme->isGridEnabled()) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
+
QQuaternion lineRotation;
+ QVector3D lightPos = m_cachedScene->activeLight()->position();
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+
// Bind bar shader
lineShader->bind();
@@ -1607,15 +1819,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->color(), barColor);
lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadowed shader bindings
lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 20.0f);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 2.5f);
@@ -1625,12 +1834,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
if (m_yFlipped)
yFloorLinePosition = -yFloorLinePosition;
- QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth);
+ QVector3D gridLineScaler(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
if (m_yFlipped)
- lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f);
+ lineRotation = m_xRightAngleRotation;
else
- lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f);
+ lineRotation = m_xRightAngleRotationNeg;
// Floor lines: rows
for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) {
@@ -1638,7 +1847,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- rowPos = row * m_cachedBarSpacing.height();
+ GLfloat rowPos = row * m_cachedBarSpacing.height();
modelMatrix.translate(0.0f, yFloorLinePosition,
(m_columnDepth - rowPos) / m_scaleFactor);
modelMatrix.scale(gridLineScaler);
@@ -1654,33 +1863,32 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Floor lines: columns
-#if defined(QT_OPENGL_ES_2)
- lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
-#endif
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor);
+ if (m_isOpenGLES)
+ lineRotation = m_yRightAngleRotation;
+ gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- colPos = bar * m_cachedBarSpacing.width();
+ GLfloat colPos = bar * m_cachedBarSpacing.width();
modelMatrix.translate((m_rowWidth - colPos) / m_scaleFactor,
yFloorLinePosition, 0.0f);
modelMatrix.scale(gridLineScaler);
@@ -1696,31 +1904,31 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
if (m_axisCacheY.segmentCount() > 0) {
// Wall lines: back wall
int gridLineCount = m_axisCacheY.gridLineCount();
- GLfloat zWallLinePosition = -columnScaleFactor + gridLineOffset;
+ GLfloat zWallLinePosition = -m_scaleZWithBackground + gridLineOffset;
if (m_zFlipped)
zWallLinePosition = -zWallLinePosition;
- gridLineScaler = QVector3D(rowScaleFactor, gridLineWidth, gridLineWidth);
+ gridLineScaler = QVector3D(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
@@ -1732,8 +1940,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
}
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1744,33 +1952,33 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Wall lines: side wall
- GLfloat xWallLinePosition = -rowScaleFactor + gridLineOffset;
+ GLfloat xWallLinePosition = -m_scaleXWithBackground + gridLineOffset;
if (m_xFlipped)
xWallLinePosition = -xWallLinePosition;
if (m_xFlipped)
- lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f);
+ lineRotation = m_yRightAngleRotationNeg;
else
- lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
+ lineRotation = m_yRightAngleRotation;
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor);
+ gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
@@ -1792,76 +2000,27 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
}
-
- Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
-
- drawLabels(false, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor,
- columnScaleFactor);
-
- // Handle selected bar label generation
- if (barSelectionFound) {
- // Print value of selected bar
- glDisable(GL_DEPTH_TEST);
- // Draw the selection label
- LabelItem &labelItem = selectionLabelItem();
- if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId()
- || m_selectionLabelDirty) {
- QString labelText = selectionLabel();
- if (labelText.isNull() || m_selectionLabelDirty) {
- labelText = m_selectedSeriesCache->itemLabel();
- setSelectionLabel(labelText);
- m_selectionLabelDirty = false;
- }
- m_drawer->generateLabelItem(labelItem, labelText);
- m_selectedBar = selectedBar;
- }
-
- Drawer::LabelPosition position =
- m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow;
-
- m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix,
- zeroVector, identityQuaternion, selectedBar->height(),
- m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, true, false, position);
-
- // Reset label update flag; they should have been updated when we get here
- m_updateLabels = false;
-
- glEnable(GL_DEPTH_TEST);
- } else {
- m_selectedBar = 0;
- }
-
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
-
- // Release shader
- glUseProgram(0);
- m_selectionDirty = false;
}
void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
- const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
- GLfloat rowScaleFactor, GLfloat columnScaleFactor) {
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix) {
ShaderHelper *shader = 0;
GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
@@ -1873,7 +2032,6 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer
shader = m_labelShader;
shader->bind();
- glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -1894,8 +2052,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer
int labelCount = m_axisCacheY.labelCount();
GLfloat labelMarginXTrans = labelMargin;
GLfloat labelMarginZTrans = labelMargin;
- GLfloat labelXTrans = rowScaleFactor;
- GLfloat labelZTrans = columnScaleFactor;
+ GLfloat labelXTrans = m_scaleXWithBackground;
+ GLfloat labelZTrans = m_scaleZWithBackground;
QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
@@ -1999,10 +2157,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
GLfloat labelYAdjustment = 0.005f;
- GLfloat scaledRowWidth = rowScaleFactor;
- GLfloat scaledColumnDepth = columnScaleFactor;
- GLfloat colPosValue = scaledRowWidth + labelMargin;
- GLfloat rowPosValue = scaledColumnDepth + labelMargin;
+ GLfloat colPosValue = m_scaleXWithBackground + labelMargin;
+ GLfloat rowPosValue = m_scaleZWithBackground + labelMargin;
GLfloat rowPos = 0.0f;
GLfloat colPos = 0.0f;
Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
@@ -2296,14 +2452,8 @@ void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientatio
{
Abstract3DRenderer::updateAxisRange(orientation, min, max);
- if (orientation == QAbstract3DAxis::AxisOrientationY) {
- // Check if we have negative values
- if (min < 0)
- m_hasNegativeValues = true;
- else if (min >= 0)
- m_hasNegativeValues = false;
+ if (orientation == QAbstract3DAxis::AxisOrientationY)
calculateHeightAdjustment();
- }
}
void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable)
@@ -2384,10 +2534,12 @@ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality
handleShadowQualityChange();
-#if !defined(QT_OPENGL_ES_2)
// Re-init depth buffer
updateDepthBuffer();
-#endif
+
+ // Redraw to handle both reflections and shadows on background
+ if (m_reflectionEnabled)
+ needRender();
}
void Bars3DRenderer::loadBackgroundMesh()
@@ -2398,6 +2550,8 @@ void Bars3DRenderer::loadBackgroundMesh()
void Bars3DRenderer::updateTextures()
{
+ Abstract3DRenderer::updateTextures();
+
// Drawer has changed; this flag needs to be checked when checking if we need to update labels
m_updateLabels = true;
}
@@ -2420,30 +2574,60 @@ void Bars3DRenderer::calculateSceneScalingFactors()
m_maxDimension = qMax(m_rowWidth, m_columnDepth);
m_scaleFactor = qMin((m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)),
(m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
+
+ // Single bar scaling
m_scaleX = m_cachedBarThickness.width() / m_scaleFactor;
m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor;
+
+ // Whole graph scale factors
+ m_xScaleFactor = m_rowWidth / m_scaleFactor;
+ m_zScaleFactor = m_columnDepth / m_scaleFactor;
+
+ if (m_requestedMargin < 0.0f) {
+ m_hBackgroundMargin = 0.0f;
+ m_vBackgroundMargin = 0.0f;
+ } else {
+ m_hBackgroundMargin = m_requestedMargin;
+ m_vBackgroundMargin = m_requestedMargin;
+ }
+
+ m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin;
+ m_scaleYWithBackground = 1.0f + m_vBackgroundMargin;
+ m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin;
+
+ updateCameraViewport();
+ updateCustomItemPositions();
}
void Bars3DRenderer::calculateHeightAdjustment()
{
+ float min = m_axisCacheY.min();
+ float max = m_axisCacheY.max();
GLfloat newAdjustment = 1.0f;
- GLfloat maxAbs = qFabs(m_axisCacheY.max());
-
- if (m_axisCacheY.max() < 0.0f) {
- m_heightNormalizer = GLfloat(qFabs(m_axisCacheY.min()) - qFabs(m_axisCacheY.max()));
- maxAbs = qFabs(m_axisCacheY.max()) - qFabs(m_axisCacheY.min());
+ m_actualFloorLevel = qBound(min, m_floorLevel, max);
+ GLfloat maxAbs = qFabs(max - m_actualFloorLevel);
+
+ // Check if we have negative values
+ if (min < m_actualFloorLevel)
+ m_hasNegativeValues = true;
+ else if (min >= m_actualFloorLevel)
+ m_hasNegativeValues = false;
+
+ if (max < m_actualFloorLevel) {
+ m_heightNormalizer = GLfloat(qFabs(min) - qFabs(max));
+ maxAbs = qFabs(max) - qFabs(min);
} else {
- m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min());
+ m_heightNormalizer = GLfloat(max - min);
}
// Height fractions are used in gradient calculations and are therefore doubled
// Note that if max or min is exactly zero, we still consider it outside the range
- if (m_axisCacheY.max() <= 0.0f || m_axisCacheY.min() >= 0.0f) {
+ if (max <= m_actualFloorLevel || min >= m_actualFloorLevel) {
m_noZeroInRange = true;
m_gradientFraction = 2.0f;
} else {
m_noZeroInRange = false;
- GLfloat minAbs = qFabs(m_axisCacheY.min());
+ GLfloat minAbs = qFabs(min - m_actualFloorLevel);
m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f;
}
@@ -2551,13 +2735,13 @@ void Bars3DRenderer::updateSlicingActive(bool isSlicing)
m_cachedIsSlicingActivated = isSlicing;
- if (!m_cachedIsSlicingActivated)
- initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize
+ if (!m_cachedIsSlicingActivated) {
+ // We need to re-init selection buffer in case there has been a resize
+ initSelectionBuffer();
+ initCursorPositionBuffer();
+ }
-#if !defined(QT_OPENGL_ES_2)
updateDepthBuffer(); // Re-init depth buffer as well
-#endif
-
m_selectionDirty = true;
}
@@ -2598,32 +2782,35 @@ void Bars3DRenderer::initSelectionBuffer()
m_selectionDepthBuffer);
}
-#if !defined(QT_OPENGL_ES_2)
void Bars3DRenderer::initDepthShader()
{
- if (m_depthShader)
- delete m_depthShader;
- m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
- QStringLiteral(":/shaders/fragmentDepth"));
- m_depthShader->initialize();
+ if (!m_isOpenGLES) {
+ if (m_depthShader)
+ delete m_depthShader;
+ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
+ QStringLiteral(":/shaders/fragmentDepth"));
+ m_depthShader->initialize();
+ }
}
void Bars3DRenderer::updateDepthBuffer()
{
- m_textureHelper->deleteTexture(&m_depthTexture);
+ if (!m_isOpenGLES) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
- if (m_primarySubViewport.size().isEmpty())
- return;
+ if (m_primarySubViewport.size().isEmpty())
+ return;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
- m_depthFrameBuffer,
- m_shadowQualityMultiplier);
- if (!m_depthTexture)
- lowerShadowQuality();
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_depthTexture =
+ m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
+ m_depthFrameBuffer,
+ m_shadowQualityMultiplier);
+ if (!m_depthTexture)
+ lowerShadowQuality();
+ }
}
}
-#endif
void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader)
@@ -2634,14 +2821,6 @@ void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader,
m_backgroundShader->initialize();
}
-void Bars3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
-{
- if (m_labelShader)
- delete m_labelShader;
- m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
- m_labelShader->initialize();
-}
-
QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute)
{
float xTrans = 0.0f;
@@ -2649,15 +2828,15 @@ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position
float zTrans = 0.0f;
if (!isAbsolute) {
// Convert row and column to translation on graph
- xTrans = (((position.x() + 0.5f) * m_cachedBarSpacing.width()) - m_rowWidth)
- / m_scaleFactor;
- zTrans = (m_columnDepth - ((position.z() + 0.5f) * m_cachedBarSpacing.height()))
- / m_scaleFactor;
+ xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width())
+ - m_rowWidth) / m_scaleFactor;
+ zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f)
+ * m_cachedBarSpacing.height())) / m_scaleFactor;
yTrans = m_axisCacheY.positionAt(position.y());
} else {
- xTrans = position.x() * m_scaleX;
+ xTrans = position.x() * m_xScaleFactor;
yTrans = position.y() + m_backgroundAdjustment;
- zTrans = position.z() * m_scaleZ;
+ zTrans = position.z() * -m_zScaleFactor;
}
return QVector3D(xTrans, yTrans, zTrans);
}
@@ -2667,4 +2846,18 @@ void Bars3DRenderer::updateAspectRatio(float ratio)
Q_UNUSED(ratio)
}
+void Bars3DRenderer::updateFloorLevel(float level)
+{
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+ m_floorLevel = level;
+ calculateHeightAdjustment();
+}
+
+void Bars3DRenderer::updateMargin(float margin)
+{
+ Abstract3DRenderer::updateMargin(margin);
+ calculateSceneScalingFactors();
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h
index 3a0ab3b8..094b8fd9 100644
--- a/src/datavisualization/engine/bars3drenderer_p.h
+++ b/src/datavisualization/engine/bars3drenderer_p.h
@@ -66,9 +66,7 @@ private:
ShaderHelper *m_depthShader;
ShaderHelper *m_selectionShader;
ShaderHelper *m_backgroundShader;
- ShaderHelper *m_labelShader;
GLuint m_bgrTexture;
- GLuint m_depthTexture;
GLuint m_selectionTexture;
GLuint m_depthFrameBuffer;
GLuint m_selectionFrameBuffer;
@@ -86,7 +84,6 @@ private:
GLfloat m_scaleFactor;
GLfloat m_maxSceneSize;
QPoint m_visualSelectedBarPos;
- bool m_resetCameraBaseOrientation;
QPoint m_selectedBarPos;
BarSeriesRenderCache *m_selectedSeriesCache;
BarRenderItem m_dummyBarRenderItem;
@@ -100,6 +97,10 @@ private:
bool m_haveUniformColorSeries;
bool m_haveGradientSeries;
float m_zeroPosition;
+ float m_xScaleFactor;
+ float m_zScaleFactor;
+ float m_floorLevel;
+ float m_actualFloorLevel;
public:
explicit Bars3DRenderer(Bars3DController *controller);
@@ -116,9 +117,13 @@ public:
QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
void updateAspectRatio(float ratio);
+ void updateFloorLevel(float level);
+ void updateMargin(float margin);
protected:
virtual void initializeOpenGL();
+ virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
public slots:
void updateMultiSeriesScaling(bool uniform);
@@ -146,18 +151,25 @@ private:
void drawSlicedScene();
void drawScene(GLuint defaultFboHandle);
void drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
- const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
- GLfloat rowScaleFactor, GLfloat columnScaleFactor);
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix);
+
+ bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
+ GLint startRow, GLint stopRow, GLint stepRow,
+ GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection = 1.0f);
+ void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
+ bool reflectingDraw = false, bool drawingSelectionBuffer = false);
+ void drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &viewMatrix);
void loadBackgroundMesh();
void initSelectionShader();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
- void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
void initSelectionBuffer();
-#if !defined(QT_OPENGL_ES_2)
void initDepthShader();
void updateDepthBuffer();
-#endif
void calculateSceneScalingFactors();
void calculateHeightAdjustment();
Abstract3DController::SelectionType isSelected(int row, int bar,
diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp
index b8726840..d4352702 100644
--- a/src/datavisualization/engine/drawer.cpp
+++ b/src/datavisualization/engine/drawer.cpp
@@ -69,10 +69,9 @@ Drawer::~Drawer()
void Drawer::initializeOpenGL()
{
- if (!m_textureHelper) {
- initializeOpenGLFunctions();
+ initializeOpenGLFunctions();
+ if (!m_textureHelper)
m_textureHelper = new TextureHelper();
- }
}
void Drawer::setTheme(Q3DTheme *theme)
@@ -93,8 +92,11 @@ QFont Drawer::font() const
}
void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId,
- GLuint depthTextureId)
+ GLuint depthTextureId, GLuint textureId3D)
{
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(textureId3D)
+#endif
if (textureId) {
// Activate texture
glActiveTexture(GL_TEXTURE0);
@@ -108,6 +110,14 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
glBindTexture(GL_TEXTURE_2D, depthTextureId);
shader->setUniformValue(shader->shadow(), 1);
}
+#if !defined(QT_OPENGL_ES_2)
+ if (textureId3D) {
+ // Activate texture
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_3D, textureId3D);
+ shader->setUniformValue(shader->texture(), 2);
+ }
+#endif
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
@@ -115,14 +125,18 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// 2nd attribute buffer : normals
- glEnableVertexAttribArray(shader->normalAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf());
- glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ if (shader->normalAtt() >= 0) {
+ glEnableVertexAttribArray(shader->normalAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf());
+ glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ }
// 3rd attribute buffer : UVs
- glEnableVertexAttribArray(shader->uvAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
- glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ if (shader->uvAtt() >= 0) {
+ glEnableVertexAttribArray(shader->uvAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
+ glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ }
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
@@ -134,11 +148,19 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDisableVertexAttribArray(shader->uvAtt());
- glDisableVertexAttribArray(shader->normalAtt());
+ if (shader->uvAtt() >= 0)
+ glDisableVertexAttribArray(shader->uvAtt());
+ if (shader->normalAtt() >= 0)
+ glDisableVertexAttribArray(shader->normalAtt());
glDisableVertexAttribArray(shader->posAtt());
// Release textures
+#if !defined(QT_OPENGL_ES_2)
+ if (textureId3D) {
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_3D, 0);
+ }
+#endif
if (depthTextureId) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
@@ -206,13 +228,27 @@ void Drawer::drawPoint(ShaderHelper *shader)
glDisableVertexAttribArray(shader->posAtt());
}
-void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object)
+void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId)
{
+ if (textureId) {
+ // Activate texture
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ shader->setUniformValue(shader->texture(), 0);
+ }
+
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->pointBuf());
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ // 2nd attribute buffer : UVs
+ if (textureId) {
+ glEnableVertexAttribArray(shader->uvAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
+ glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ }
+
// Draw the points
glDrawArrays(GL_POINTS, 0, object->indexCount());
@@ -220,6 +256,12 @@ void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->posAtt());
+
+ if (textureId) {
+ glDisableVertexAttribArray(shader->uvAtt());
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
}
void Drawer::drawLine(ShaderHelper *shader)
diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h
index ffcea315..0c96724c 100644
--- a/src/datavisualization/engine/drawer_p.h
+++ b/src/datavisualization/engine/drawer_p.h
@@ -75,11 +75,11 @@ public:
inline GLfloat scaledFontSize() const { return m_scaledFontSize; }
void drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId = 0,
- GLuint depthTextureId = 0);
+ GLuint depthTextureId = 0, GLuint textureId3D = 0);
void drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object);
void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object);
void drawPoint(ShaderHelper *shader);
- void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object);
+ void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId);
void drawLine(ShaderHelper *shader);
void drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem,
const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix,
diff --git a/src/datavisualization/engine/engine.pri b/src/datavisualization/engine/engine.pri
index 96fa7fa9..60703e96 100644
--- a/src/datavisualization/engine/engine.pri
+++ b/src/datavisualization/engine/engine.pri
@@ -57,3 +57,5 @@ SOURCES += $$PWD/qabstract3dgraph.cpp \
RESOURCES += engine/engine.qrc
OTHER_FILES += $$PWD/meshes/* $$PWD/shaders/*
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc
index 673b6ee0..0ef169c9 100644
--- a/src/datavisualization/engine/engine.qrc
+++ b/src/datavisualization/engine/engine.qrc
@@ -57,5 +57,18 @@
<file alias="fragmentTexture">shaders/texture.frag</file>
<file alias="fragmentTextureES2">shaders/texture_ES2.frag</file>
<file alias="vertexTexture">shaders/texture.vert</file>
+ <file alias="fragmentTexturedSurfaceShadowFlat">shaders/surfaceTexturedShadowFlat.frag</file>
+ <file alias="fragmentSurfaceTexturedFlat">shaders/surfaceTexturedFlat.frag</file>
+ <file alias="fragmentTexture3D">shaders/texture3d.frag</file>
+ <file alias="vertexTexture3D">shaders/texture3d.vert</file>
+ <file alias="fragmentTexture3DSlice">shaders/texture3dslice.frag</file>
+ <file alias="vertexShadowNoMatrices">shaders/shadowNoMatrices.vert</file>
+ <file alias="vertexNoMatrices">shaders/defaultNoMatrices.vert</file>
+ <file alias="fragmentTexture3DLowDef">shaders/texture3dlowdef.frag</file>
+ <file alias="vertexPointES2_UV">shaders/point_ES2_UV.vert</file>
+ <file alias="fragment3DSliceFrames">shaders/3dsliceframes.frag</file>
+ <file alias="vertexPosition">shaders/position.vert</file>
+ <file alias="fragmentPositionMap">shaders/positionmap.frag</file>
+ <file alias="fragmentTexturedSurfaceShadow">shaders/surfaceTexturedShadow.frag</file>
</qresource>
</RCC>
diff --git a/src/datavisualization/engine/q3dbars.cpp b/src/datavisualization/engine/q3dbars.cpp
index bb7aca89..d42c11b7 100644
--- a/src/datavisualization/engine/q3dbars.cpp
+++ b/src/datavisualization/engine/q3dbars.cpp
@@ -331,6 +331,26 @@ QBar3DSeries *Q3DBars::selectedSeries() const
}
/*!
+ * \property Q3DBars::floorLevel
+ *
+ * The desired floor level for the bar graph in Y-axis data coordinates.
+ * The actual floor level cannot go below Y-axis minimum or above Y-axis maximum.
+ * Defaults to zero.
+ */
+void Q3DBars::setFloorLevel(float level)
+{
+ if (level != floorLevel()) {
+ dptr()->m_shared->setFloorLevel(level);
+ emit floorLevelChanged(level);
+ }
+}
+
+float Q3DBars::floorLevel() const
+{
+ return dptrc()->m_shared->floorLevel();
+}
+
+/*!
* Adds \a axis to the graph. The axes added via addAxis are not yet taken to use,
* addAxis is simply used to give the ownership of the \a axis to the graph.
* The \a axis must not be null or added to another graph.
diff --git a/src/datavisualization/engine/q3dbars.h b/src/datavisualization/engine/q3dbars.h
index 7f9c981f..df1c846e 100644
--- a/src/datavisualization/engine/q3dbars.h
+++ b/src/datavisualization/engine/q3dbars.h
@@ -40,6 +40,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DBars : public QAbstract3DGraph
Q_PROPERTY(QValue3DAxis *valueAxis READ valueAxis WRITE setValueAxis NOTIFY valueAxisChanged)
Q_PROPERTY(QBar3DSeries *primarySeries READ primarySeries WRITE setPrimarySeries NOTIFY primarySeriesChanged)
Q_PROPERTY(QBar3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged)
+ Q_PROPERTY(float floorLevel READ floorLevel WRITE setFloorLevel NOTIFY floorLevelChanged)
public:
explicit Q3DBars(const QSurfaceFormat *format = 0, QWindow *parent = 0);
@@ -75,6 +76,8 @@ public:
QList<QAbstract3DAxis *> axes() const;
QBar3DSeries *selectedSeries() const;
+ void setFloorLevel(float level);
+ float floorLevel() const;
signals:
void multiSeriesUniformChanged(bool uniform);
@@ -86,6 +89,7 @@ signals:
void valueAxisChanged(QValue3DAxis *axis);
void primarySeriesChanged(QBar3DSeries *series);
void selectedSeriesChanged(QBar3DSeries *series);
+ void floorLevelChanged(float level);
private:
Q3DBarsPrivate *dptr();
diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp
index 06fd570c..897c3779 100644
--- a/src/datavisualization/engine/q3dcamera.cpp
+++ b/src/datavisualization/engine/q3dcamera.cpp
@@ -110,8 +110,36 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty float Camera3D::zoomLevel
*
- * This property contains the the camera zoom level in percentage. 100.0 means there is no zoom
- * in or out set in the camera.
+ * This property contains the the camera zoom level in percentage. The default value of \c{100.0}
+ * means there is no zoom in or out set in the camera.
+ * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties.
+ *
+ * \sa minZoomLevel, maxZoomLevel
+ */
+
+/*!
+ * \qmlproperty float Camera3D::minZoomLevel
+ *
+ * This property contains the the minimum allowed camera zoom level.
+ * If the new minimum level is more than the existing maximum level, the maximum level is
+ * adjusted to the new minimum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * The minZoomLevel cannot be set below \c{1.0}.
+ * Defaults to \c{10.0}.
+ *
+ * \sa zoomLevel, maxZoomLevel
+ */
+
+/*!
+ * \qmlproperty float Camera3D::maxZoomLevel
+ *
+ * This property contains the the maximum allowed camera zoom level.
+ * If the new maximum level is less than the existing minimum level, the minimum level is
+ * adjusted to the new maximum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * Defaults to \c{500.0f}.
+ *
+ * \sa zoomLevel, minZoomLevel
*/
/*!
@@ -137,6 +165,19 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty vector3d Camera3D::target
+ * \since QtDataVisualization 1.2
+ *
+ * Holds the camera \a target as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}.
+ *
+ * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate
+ * the edges of the corresponding axis range. Any values outside this range are clamped to the edge.
+ *
+ * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on
+ * the horizontal background.
+ */
+
+/*!
* Constructs a new 3D camera with position set to origin, up direction facing towards the Y-axis
* and looking at origin by default. An optional \a parent parameter can be given and is then passed
* to QObject constructor.
@@ -160,22 +201,11 @@ Q3DCamera::~Q3DCamera()
*/
void Q3DCamera::copyValuesFrom(const Q3DObject &source)
{
- Q3DObject::copyValuesFrom(source);
+ // Note: Do not copy values from parent, as we are handling the position internally
const Q3DCamera &sourceCamera = static_cast<const Q3DCamera &>(source);
- d_ptr->m_target.setX(sourceCamera.d_ptr->m_target.x());
- d_ptr->m_target.setY(sourceCamera.d_ptr->m_target.y());
- d_ptr->m_target.setZ(sourceCamera.d_ptr->m_target.z());
-
- d_ptr->m_up.setX(sourceCamera.d_ptr->m_up.x());
- d_ptr->m_up.setY(sourceCamera.d_ptr->m_up.y());
- d_ptr->m_up.setZ(sourceCamera.d_ptr->m_up.z());
-
- float *values = new float[16];
- sourceCamera.d_ptr->m_viewMatrix.copyDataTo(values);
- d_ptr->m_viewMatrix = QMatrix4x4(values);
- delete[] values;
+ d_ptr->m_requestedTarget = sourceCamera.d_ptr->m_requestedTarget;
d_ptr->m_xRotation = sourceCamera.d_ptr->m_xRotation;
d_ptr->m_yRotation = sourceCamera.d_ptr->m_yRotation;
@@ -189,6 +219,8 @@ void Q3DCamera::copyValuesFrom(const Q3DObject &source)
d_ptr->m_wrapYRotation = sourceCamera.d_ptr->m_wrapYRotation;
d_ptr->m_zoomLevel = sourceCamera.d_ptr->m_zoomLevel;
+ d_ptr->m_minZoomLevel = sourceCamera.d_ptr->m_minZoomLevel;
+ d_ptr->m_maxZoomLevel = sourceCamera.d_ptr->m_maxZoomLevel;
d_ptr->m_activePreset = sourceCamera.d_ptr->m_activePreset;
}
@@ -389,6 +421,9 @@ void Q3DCamera::setCameraPreset(CameraPreset preset)
break;
}
+ // All presets target the center of the graph
+ setTarget(zeroVector);
+
if (d_ptr->m_activePreset != preset) {
d_ptr->m_activePreset = preset;
setDirty(true);
@@ -399,8 +434,11 @@ void Q3DCamera::setCameraPreset(CameraPreset preset)
/*!
* \property Q3DCamera::zoomLevel
*
- * This property contains the the camera zoom level in percentage. \c 100.0f means there is no zoom
- * in or out set in the camera.
+ * This property contains the the camera zoom level in percentage. The default value of \c{100.0f}
+ * means there is no zoom in or out set in the camera.
+ * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties.
+ *
+ * \sa minZoomLevel, maxZoomLevel
*/
float Q3DCamera::zoomLevel() const
{
@@ -409,10 +447,73 @@ float Q3DCamera::zoomLevel() const
void Q3DCamera::setZoomLevel(float zoomLevel)
{
- if (d_ptr->m_zoomLevel != zoomLevel) {
- d_ptr->m_zoomLevel = zoomLevel;
+ float newZoomLevel = qBound(d_ptr->m_minZoomLevel, zoomLevel, d_ptr->m_maxZoomLevel);
+
+ if (d_ptr->m_zoomLevel != newZoomLevel) {
+ d_ptr->m_zoomLevel = newZoomLevel;
+ setDirty(true);
+ emit zoomLevelChanged(newZoomLevel);
+ }
+}
+
+/*!
+ * \property Q3DCamera::minZoomLevel
+ *
+ * This property contains the the minimum allowed camera zoom level.
+ * If the new minimum level is more than the existing maximum level, the maximum level is
+ * adjusted to the new minimum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * The minZoomLevel cannot be set below \c{1.0f}.
+ * Defaults to \c{10.0f}.
+ *
+ * \sa zoomLevel, maxZoomLevel
+ */
+float Q3DCamera::minZoomLevel() const
+{
+ return d_ptr->m_minZoomLevel;
+}
+
+void Q3DCamera::setMinZoomLevel(float zoomLevel)
+{
+ // Don't allow minimum to be below one, as that can cause zoom to break.
+ float newMinLevel = qMax(zoomLevel, 1.0f);
+ if (d_ptr->m_minZoomLevel != newMinLevel) {
+ d_ptr->m_minZoomLevel = newMinLevel;
+ if (d_ptr->m_maxZoomLevel < newMinLevel)
+ setMaxZoomLevel(newMinLevel);
+ setZoomLevel(d_ptr->m_zoomLevel);
setDirty(true);
- emit zoomLevelChanged(zoomLevel);
+ emit minZoomLevelChanged(newMinLevel);
+ }
+}
+
+/*!
+ * \property Q3DCamera::maxZoomLevel
+ *
+ * This property contains the the maximum allowed camera zoom level.
+ * If the new maximum level is less than the existing minimum level, the minimum level is
+ * adjusted to the new maximum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * Defaults to \c{500.0f}.
+ *
+ * \sa zoomLevel, minZoomLevel
+ */
+float Q3DCamera::maxZoomLevel() const
+{
+ return d_ptr->m_maxZoomLevel;
+}
+
+void Q3DCamera::setMaxZoomLevel(float zoomLevel)
+{
+ // Don't allow maximum to be below one, as that can cause zoom to break.
+ float newMaxLevel = qMax(zoomLevel, 1.0f);
+ if (d_ptr->m_maxZoomLevel != newMaxLevel) {
+ d_ptr->m_maxZoomLevel = newMaxLevel;
+ if (d_ptr->m_minZoomLevel > newMaxLevel)
+ setMinZoomLevel(newMaxLevel);
+ setZoomLevel(d_ptr->m_zoomLevel);
+ setDirty(true);
+ emit maxZoomLevelChanged(newMaxLevel);
}
}
@@ -459,16 +560,61 @@ void Q3DCamera::setWrapYRotation(bool isEnabled)
/*!
* Utility function that sets the camera rotations and distance.\a horizontal and \a vertical
* define the camera rotations to be used.
- * Optional \a zoom parameter can be given to set the zoom percentage of the camera in range of
- * \c{10.0f - 500.0f}.
+ * Optional \a zoom parameter can be given to set the zoom percentage of the camera within
+ * the bounds defined by minZoomLevel and maxZoomLevel properties.
*/
void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom)
{
- setZoomLevel(qBound(10.0f, zoom, 500.0f));
+ setZoomLevel(zoom);
setXRotation(horizontal);
setYRotation(vertical);
}
+/*!
+ * \property Q3DCamera::target
+ * \since QtDataVisualization 1.2
+ *
+ * Holds the camera \a target as a QVector3D. Defaults to \c {QVector3D(0.0, 0.0, 0.0)}.
+ *
+ * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate
+ * the edges of the corresponding axis range. Any values outside this range are clamped to the edge.
+ *
+ * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on
+ * the horizontal background.
+ */
+QVector3D Q3DCamera::target() const
+{
+ return d_ptr->m_requestedTarget;
+}
+
+void Q3DCamera::setTarget(const QVector3D &target)
+{
+ QVector3D newTarget = target;
+
+ if (newTarget.x() < -1.0f)
+ newTarget.setX(-1.0f);
+ else if (newTarget.x() > 1.0f)
+ newTarget.setX(1.0f);
+
+ if (newTarget.y() < -1.0f)
+ newTarget.setY(-1.0f);
+ else if (newTarget.y() > 1.0f)
+ newTarget.setY(1.0f);
+
+ if (newTarget.z() < -1.0f)
+ newTarget.setZ(-1.0f);
+ else if (newTarget.z() > 1.0f)
+ newTarget.setZ(1.0f);
+
+ if (d_ptr->m_requestedTarget != newTarget) {
+ if (d_ptr->m_activePreset != CameraPresetNone)
+ d_ptr->m_activePreset = CameraPresetNone;
+ d_ptr->m_requestedTarget = newTarget;
+ setDirty(true);
+ emit targetChanged(newTarget);
+ }
+}
+
Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) :
q_ptr(q),
m_isViewMatrixUpdateActive(true),
@@ -479,6 +625,8 @@ Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) :
m_maxXRotation(180.0f),
m_maxYRotation(90.0f),
m_zoomLevel(100.0f),
+ m_minZoomLevel(10.0f),
+ m_maxZoomLevel(500.0f),
m_wrapXRotation(true),
m_wrapYRotation(false),
m_activePreset(Q3DCamera::CameraPresetNone)
@@ -645,9 +793,9 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment)
QMatrix4x4 viewMatrix;
// Apply to view matrix
- viewMatrix.lookAt(q_ptr->position(), m_target, m_up);
+ viewMatrix.lookAt(q_ptr->position(), m_actualTarget, m_up);
// Compensate for translation (if d_ptr->m_target is off origin)
- viewMatrix.translate(m_target.x(), m_target.y(), m_target.z());
+ viewMatrix.translate(m_actualTarget.x(), m_actualTarget.y(), m_actualTarget.z());
// Apply rotations
// Handle x and z rotation when y -angle is other than 0
viewMatrix.rotate(m_xRotation, 0, qCos(qDegreesToRadians(m_yRotation)),
@@ -657,7 +805,7 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment)
// handle zoom by scaling
viewMatrix.scale(zoom / 100.0f);
// Compensate for translation (if d_ptr->m_target is off origin)
- viewMatrix.translate(-m_target.x(), -m_target.y(), -m_target.z());
+ viewMatrix.translate(-m_actualTarget.x(), -m_actualTarget.y(), -m_actualTarget.z());
setViewMatrix(viewMatrix);
}
@@ -713,9 +861,9 @@ void Q3DCameraPrivate::setBaseOrientation(const QVector3D &basePosition,
const QVector3D &target,
const QVector3D &baseUp)
{
- if (q_ptr->position() != basePosition || m_target != target || m_up != baseUp) {
+ if (q_ptr->position() != basePosition || m_actualTarget != target || m_up != baseUp) {
q_ptr->setPosition(basePosition);
- m_target = target;
+ m_actualTarget = target;
m_up = baseUp;
q_ptr->setDirty(true);
}
@@ -737,20 +885,33 @@ QVector3D Q3DCameraPrivate::calculatePositionRelativeToCamera(const QVector3D &r
float distanceModifier) const
{
// Move the position with camera
- GLfloat radiusFactor = cameraDistance * (1.5f + distanceModifier);
- GLfloat xAngle;
- GLfloat yAngle;
+ const float radiusFactor = cameraDistance * (1.5f + distanceModifier);
+ float xAngle;
+ float yAngle;
+
if (!fixedRotation) {
xAngle = qDegreesToRadians(m_xRotation);
- yAngle = qDegreesToRadians(m_yRotation);
+ float yRotation = m_yRotation;
+ // Light must not be paraller to eye vector, so fudge the y rotation a bit.
+ // Note: This needs redoing if we ever allow arbitrary light positioning.
+ const float yMargin = 0.1f; // Smaller margins cause weird shadow artifacts on tops of bars
+ const float absYRotation = qAbs(yRotation);
+ if (absYRotation < 90.0f + yMargin && absYRotation > 90.0f - yMargin) {
+ if (yRotation < 0.0f)
+ yRotation = -90.0f + yMargin;
+ else
+ yRotation = 90.0f - yMargin;
+ }
+ yAngle = qDegreesToRadians(yRotation);
} else {
xAngle = qDegreesToRadians(fixedRotation);
yAngle = 0;
}
- GLfloat radius = (radiusFactor + relativePosition.y()); // set radius to match the highest height of the position
- GLfloat zPos = radius * qCos(xAngle) * qCos(yAngle);
- GLfloat xPos = radius * qSin(xAngle) * qCos(yAngle);
- GLfloat yPos = (radiusFactor + relativePosition.y()) * qSin(yAngle);
+ // Set radius to match the highest height of the position
+ const float radius = (radiusFactor + relativePosition.y());
+ const float zPos = radius * qCos(xAngle) * qCos(yAngle);
+ const float xPos = radius * qSin(xAngle) * qCos(yAngle);
+ const float yPos = radius * qSin(yAngle);
// Keep in the set position in relation to camera
return QVector3D(-xPos + relativePosition.x(),
diff --git a/src/datavisualization/engine/q3dcamera.h b/src/datavisualization/engine/q3dcamera.h
index e9ad6dd2..5b539ccf 100644
--- a/src/datavisualization/engine/q3dcamera.h
+++ b/src/datavisualization/engine/q3dcamera.h
@@ -35,6 +35,9 @@ class QT_DATAVISUALIZATION_EXPORT Q3DCamera : public Q3DObject
Q_PROPERTY(CameraPreset cameraPreset READ cameraPreset WRITE setCameraPreset NOTIFY cameraPresetChanged)
Q_PROPERTY(bool wrapXRotation READ wrapXRotation WRITE setWrapXRotation NOTIFY wrapXRotationChanged)
Q_PROPERTY(bool wrapYRotation READ wrapYRotation WRITE setWrapYRotation NOTIFY wrapYRotationChanged)
+ Q_PROPERTY(QVector3D target READ target WRITE setTarget NOTIFY targetChanged REVISION 1)
+ Q_PROPERTY(float minZoomLevel READ minZoomLevel WRITE setMinZoomLevel NOTIFY minZoomLevelChanged REVISION 1)
+ Q_PROPERTY(float maxZoomLevel READ maxZoomLevel WRITE setMaxZoomLevel NOTIFY maxZoomLevelChanged REVISION 1)
public:
enum CameraPreset {
@@ -86,9 +89,16 @@ public:
float zoomLevel() const;
void setZoomLevel(float zoomLevel);
+ float minZoomLevel() const;
+ void setMinZoomLevel(float zoomLevel);
+ float maxZoomLevel() const;
+ void setMaxZoomLevel(float zoomLevel);
void setCameraPosition(float horizontal, float vertical, float zoom = 100.0f);
+ QVector3D target() const;
+ void setTarget(const QVector3D &target);
+
signals:
void xRotationChanged(float rotation);
void yRotationChanged(float rotation);
@@ -96,6 +106,9 @@ signals:
void cameraPresetChanged(Q3DCamera::CameraPreset preset);
void wrapXRotationChanged(bool isEnabled);
void wrapYRotationChanged(bool isEnabled);
+ Q_REVISION(1) void targetChanged(const QVector3D &target);
+ Q_REVISION(1) void minZoomLevelChanged(float zoomLevel);
+ Q_REVISION(1) void maxZoomLevelChanged(float zoomLevel);
private:
QScopedPointer<Q3DCameraPrivate> d_ptr;
diff --git a/src/datavisualization/engine/q3dcamera_p.h b/src/datavisualization/engine/q3dcamera_p.h
index 01e7a508..b7826a5b 100644
--- a/src/datavisualization/engine/q3dcamera_p.h
+++ b/src/datavisualization/engine/q3dcamera_p.h
@@ -84,22 +84,25 @@ signals:
public:
Q3DCamera *q_ptr;
- QVector3D m_target;
+ QVector3D m_actualTarget;
QVector3D m_up;
QMatrix4x4 m_viewMatrix;
bool m_isViewMatrixUpdateActive;
- GLfloat m_xRotation;
- GLfloat m_yRotation;
- GLfloat m_minXRotation;
- GLfloat m_minYRotation;
- GLfloat m_maxXRotation;
- GLfloat m_maxYRotation;
- GLfloat m_zoomLevel;
+ float m_xRotation;
+ float m_yRotation;
+ float m_minXRotation;
+ float m_minYRotation;
+ float m_maxXRotation;
+ float m_maxYRotation;
+ float m_zoomLevel;
+ float m_minZoomLevel;
+ float m_maxZoomLevel;
bool m_wrapXRotation;
bool m_wrapYRotation;
Q3DCamera::CameraPreset m_activePreset;
+ QVector3D m_requestedTarget;
friend class Bars3DRenderer;
friend class Surface3DRenderer;
diff --git a/src/datavisualization/engine/q3dlight.cpp b/src/datavisualization/engine/q3dlight.cpp
index 03f094cb..92b7bd07 100644
--- a/src/datavisualization/engine/q3dlight.cpp
+++ b/src/datavisualization/engine/q3dlight.cpp
@@ -68,13 +68,8 @@ Q3DLightPrivate::~Q3DLightPrivate()
void Q3DLightPrivate::sync(Q3DLight &other)
{
- // Copies changed values from this light to the other light. If the other light had same changes,
- // those changes are discarded.
- if (q_ptr->isDirty()) {
- other.copyValuesFrom(*q_ptr);
- q_ptr->setDirty(false);
- other.setDirty(false);
- }
+ Q_UNUSED(other);
+ // Do nothing. Only value light has to sync is the position, which we handle internally.
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/q3dobject.cpp b/src/datavisualization/engine/q3dobject.cpp
index 56edb098..6b442ef8 100644
--- a/src/datavisualization/engine/q3dobject.cpp
+++ b/src/datavisualization/engine/q3dobject.cpp
@@ -53,9 +53,7 @@ Q3DObject::~Q3DObject()
*/
void Q3DObject::copyValuesFrom(const Q3DObject &source)
{
- d_ptr->m_position.setX(source.d_ptr->m_position.x());
- d_ptr->m_position.setY(source.d_ptr->m_position.y());
- d_ptr->m_position.setZ(source.d_ptr->m_position.z());
+ d_ptr->m_position = source.d_ptr->m_position;
setDirty(true);
}
@@ -74,6 +72,9 @@ Q3DScene *Q3DObject::parentScene()
* \property Q3DObject::position
*
* This property contains the 3D position of the object.
+ *
+ * \note Currently setting this property has no effect, as the positions of Q3DObjects in the
+ * scene are handled internally.
*/
QVector3D Q3DObject::position() const
{
@@ -97,7 +98,7 @@ void Q3DObject::setDirty(bool dirty)
{
d_ptr->m_isDirty = dirty;
if (parentScene())
- parentScene()->d_ptr->m_sceneDirty = true;
+ parentScene()->d_ptr->markDirty();
}
/*!
diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp
index 9464bc8d..e4015c2b 100644
--- a/src/datavisualization/engine/q3dscene.cpp
+++ b/src/datavisualization/engine/q3dscene.cpp
@@ -96,9 +96,33 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty point Scene3D::graphPositionQuery
+ *
+ * This property contains the coordinates for the user input that should be processed
+ * by the scene as a graph position query. If this is set to value other than
+ * invalidSelectionPoint, the graph tries to match a graph position to the given \a point
+ * within the primary viewport.
+ * After the rendering pass this property is returned to its default state of
+ * invalidSelectionPoint. The queried graph position can be read from
+ * AbstractGraph3D::queriedGraphPosition property after the next render pass.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs allow graph position queries only at the graph floor level.
+ *
+ * \sa AbstractGraph3D::queriedGraphPosition
+ */
+
+/*!
* \qmlproperty bool Scene3D::slicingActive
*
- * This property contains whether 2D slicing view is currently active or not.
+ * This property contains whether 2D slicing view is currently active or not. If setting it, you
+ * must make sure AbstractGraph3D::selectionMode has either
+ * \l{QAbstract3DGraph::SelectionRow}{AbstractGraph3D.SelectionRow} or
+ * \l{QAbstract3DGraph::SelectionColumn}{AbstractGraph3D.SelectionColumn} flag set, and there is a
+ * valid selection.
* \note Not all visualizations support the 2D slicing view.
*/
@@ -135,7 +159,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* A constant property providing an invalid point for selection.
*/
-
/*!
* Constructs a basic scene with one light and one camera in it. An
* optional \a parent parameter can be given and is then passed to QObject constructor.
@@ -195,33 +218,35 @@ void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport)
/*!
* Returns whether the given \a point resides inside the primary subview or not.
* \return \c true if the point is inside the primary subview.
+ * \note If subviews are superimposed, and the given \a point resides inside both, result is
+ * \c true only when the primary subview is on top.
*/
bool Q3DScene::isPointInPrimarySubView(const QPoint &point)
{
int x = point.x();
int y = point.y();
- int areaMinX = d_ptr->m_primarySubViewport.x();
- int areaMaxX = d_ptr->m_primarySubViewport.x() + d_ptr->m_primarySubViewport.width();
- int areaMinY = d_ptr->m_primarySubViewport.y();
- int areaMaxY = d_ptr->m_primarySubViewport.y() + d_ptr->m_primarySubViewport.height();
-
- return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY );
+ bool isInSecondary = d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y);
+ if (!isInSecondary || (isInSecondary && !d_ptr->m_isSecondarySubviewOnTop))
+ return d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y);
+ else
+ return false;
}
/*!
* Returns whether the given \a point resides inside the secondary subview or not.
* \return \c true if the point is inside the secondary subview.
+ * \note If subviews are superimposed, and the given \a point resides inside both, result is
+ * \c true only when the secondary subview is on top.
*/
bool Q3DScene::isPointInSecondarySubView(const QPoint &point)
{
int x = point.x();
int y = point.y();
- int areaMinX = d_ptr->m_secondarySubViewport.x();
- int areaMaxX = d_ptr->m_secondarySubViewport.x() + d_ptr->m_secondarySubViewport.width();
- int areaMinY = d_ptr->m_secondarySubViewport.y();
- int areaMaxY = d_ptr->m_secondarySubViewport.y() + d_ptr->m_secondarySubViewport.height();
-
- return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY );
+ bool isInPrimary = d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y);
+ if (!isInPrimary || (isInPrimary && d_ptr->m_isSecondarySubviewOnTop))
+ return d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y);
+ else
+ return false;
}
/*!
@@ -254,7 +279,7 @@ void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport)
* \property Q3DScene::selectionQueryPosition
*
* This property contains the coordinates for the user input that should be processed
- * by the scene as selection. If this is set to value other than invalidSelectionPoint() the
+ * by the scene as a selection. If this is set to value other than invalidSelectionPoint(), the
* graph tries to select a data item, axis label, or a custom item at the given \a point within
* the primary viewport.
* After the rendering pass the property is returned to its default state of
@@ -289,9 +314,47 @@ QPoint Q3DScene::invalidSelectionPoint()
}
/*!
+ * \property Q3DScene::graphPositionQuery
+ *
+ * This property contains the coordinates for the user input that should be processed
+ * by the scene as a graph position query. If this is set to value other than
+ * invalidSelectionPoint(), the graph tries to match a graph position to the given \a point
+ * within the primary viewport.
+ * After the rendering pass this property is returned to its default state of
+ * invalidSelectionPoint(). The queried graph position can be read from
+ * QAbstract3DGraph::queriedGraphPosition property after the next render pass.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs allow graph position queries only at the graph floor level.
+ *
+ * \sa QAbstract3DGraph::queriedGraphPosition
+ */
+void Q3DScene::setGraphPositionQuery(const QPoint &point)
+{
+ if (point != d_ptr->m_graphPositionQueryPosition) {
+ d_ptr->m_graphPositionQueryPosition = point;
+ d_ptr->m_changeTracker.graphPositionQueryPositionChanged = true;
+ d_ptr->m_sceneDirty = true;
+
+ emit graphPositionQueryChanged(point);
+ emit d_ptr->needRender();
+ }
+}
+
+QPoint Q3DScene::graphPositionQuery() const
+{
+ return d_ptr->m_graphPositionQueryPosition;
+}
+
+/*!
* \property Q3DScene::slicingActive
*
- * This property contains whether 2D slicing view is currently active or not.
+ * This property contains whether 2D slicing view is currently active or not. If setting it, you
+ * must make sure QAbstract3DGraph::selectionMode has either QAbstract3DGraph::SelectionRow or
+ * QAbstract3DGraph::SelectionColumn flag set, and there is a valid selection.
* \note Not all visualizations support the 2D slicing view.
*/
bool Q3DScene::isSlicingActive() const
@@ -306,6 +369,10 @@ void Q3DScene::setSlicingActive(bool isSlicing)
d_ptr->m_changeTracker.slicingActivatedChanged = true;
d_ptr->m_sceneDirty = true;
+ // Set secondary subview behind primary to achieve default functionality (= clicking on
+ // primary disables slice)
+ setSecondarySubviewOnTop(!isSlicing);
+
d_ptr->calculateSubViewports();
emit slicingActiveChanged(isSlicing);
emit d_ptr->needRender();
@@ -447,7 +514,9 @@ Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) :
m_light(),
m_isUnderSideCameraEnabled(false),
m_isSlicingActive(false),
- m_selectionQueryPosition(Q3DScene::invalidSelectionPoint())
+ m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()),
+ m_graphPositionQueryPosition(Q3DScene::invalidSelectionPoint()),
+ m_sceneDirty(true)
{
}
@@ -491,6 +560,11 @@ void Q3DScenePrivate::sync(Q3DScenePrivate &other)
m_changeTracker.selectionQueryPositionChanged = false;
other.m_changeTracker.selectionQueryPositionChanged = false;
}
+ if (m_changeTracker.graphPositionQueryPositionChanged) {
+ other.q_ptr->setGraphPositionQuery(q_ptr->graphPositionQuery());
+ m_changeTracker.graphPositionQueryPositionChanged = false;
+ other.m_changeTracker.graphPositionQueryPositionChanged = false;
+ }
if (m_changeTracker.cameraChanged) {
m_camera->setDirty(true);
m_changeTracker.cameraChanged = false;
@@ -649,4 +723,19 @@ void Q3DScenePrivate::setLightPositionRelativeToCamera(const QVector3D &relative
distanceModifier));
}
+void Q3DScenePrivate::markDirty()
+{
+ m_sceneDirty = true;
+ emit needRender();
+}
+
+bool Q3DScenePrivate::isInArea(const QRect &area, int x, int y) const
+{
+ int areaMinX = area.x();
+ int areaMaxX = area.x() + area.width();
+ int areaMinY = area.y();
+ int areaMaxY = area.y() + area.height();
+ return ( x >= areaMinX && x <= areaMaxX && y >= areaMinY && y <= areaMaxY );
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h
index 1699b125..a46b4d7b 100644
--- a/src/datavisualization/engine/q3dscene.h
+++ b/src/datavisualization/engine/q3dscene.h
@@ -41,6 +41,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DScene : public QObject
Q_PROPERTY(Q3DCamera* activeCamera READ activeCamera WRITE setActiveCamera NOTIFY activeCameraChanged)
Q_PROPERTY(Q3DLight* activeLight READ activeLight WRITE setActiveLight NOTIFY activeLightChanged)
Q_PROPERTY(float devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged)
+ Q_PROPERTY(QPoint graphPositionQuery READ graphPositionQuery WRITE setGraphPositionQuery NOTIFY graphPositionQueryChanged REVISION 1)
public:
Q3DScene(QObject *parent = 0);
@@ -60,6 +61,9 @@ public:
QPoint selectionQueryPosition() const;
static QPoint invalidSelectionPoint();
+ void setGraphPositionQuery(const QPoint &point);
+ QPoint graphPositionQuery() const;
+
void setSlicingActive(bool isSlicing);
bool isSlicingActive() const;
@@ -85,6 +89,7 @@ signals:
void activeLightChanged(Q3DLight *light);
void devicePixelRatioChanged(float pixelRatio);
void selectionQueryPositionChanged(const QPoint &position);
+ Q_REVISION(1) void graphPositionQueryChanged(const QPoint &position);
private:
QScopedPointer<Q3DScenePrivate> d_ptr;
diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h
index 2c69e5e0..d0f6e735 100644
--- a/src/datavisualization/engine/q3dscene_p.h
+++ b/src/datavisualization/engine/q3dscene_p.h
@@ -47,6 +47,7 @@ struct Q3DSceneChangeBitField {
bool slicingActivatedChanged : 1;
bool devicePixelRatioChanged : 1;
bool selectionQueryPositionChanged : 1;
+ bool graphPositionQueryPositionChanged : 1;
bool windowSizeChanged : 1;
Q3DSceneChangeBitField()
@@ -59,6 +60,7 @@ struct Q3DSceneChangeBitField {
slicingActivatedChanged(true),
devicePixelRatioChanged(true),
selectionQueryPositionChanged(false),
+ graphPositionQueryPositionChanged(false),
windowSizeChanged(true)
{
}
@@ -89,6 +91,10 @@ public:
float fixedRotation = 0.0f,
float distanceModifier = 0.0f);
+ void markDirty();
+
+ bool isInArea(const QRect &area, int x, int y) const;
+
signals:
void needRender();
@@ -106,6 +112,7 @@ public:
bool m_isUnderSideCameraEnabled;
bool m_isSlicingActive;
QPoint m_selectionQueryPosition;
+ QPoint m_graphPositionQueryPosition;
QSize m_windowSize;
QRect m_glViewport;
QRect m_glPrimarySubViewport;
diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp
index c5ce29d7..ce3e7f4c 100644
--- a/src/datavisualization/engine/q3dsurface.cpp
+++ b/src/datavisualization/engine/q3dsurface.cpp
@@ -98,6 +98,8 @@ Q3DSurface::Q3DSurface(const QSurfaceFormat *format, QWindow *parent)
dptr()->m_shared->initializeOpenGL();
QObject::connect(dptr()->m_shared, &Surface3DController::selectedSeriesChanged,
this, &Q3DSurface::selectedSeriesChanged);
+ QObject::connect(dptr()->m_shared, &Surface3DController::flipHorizontalGridChanged,
+ this, &Q3DSurface::flipHorizontalGridChanged);
}
/*!
@@ -230,6 +232,31 @@ QSurface3DSeries *Q3DSurface::selectedSeries() const
}
/*!
+ * \property Q3DSurface::flipHorizontalGrid
+ * \since QtDataVisualization 1.2
+ *
+ * In some use cases the horizontal axis grid is mostly covered by the surface, so it can be more
+ * useful to display the horizontal axis grid on top of the graph rather than on the bottom.
+ * A typical use case for this is showing 2D spectrograms using orthoGraphic projection with
+ * a top-down viewpoint.
+ *
+ * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background
+ * of the graph.
+ * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph
+ * from the horizontal background.
+ * Defaults to \c{false}.
+ */
+void Q3DSurface::setFlipHorizontalGrid(bool flip)
+{
+ dptr()->m_shared->setFlipHorizontalGrid(flip);
+}
+
+bool Q3DSurface::flipHorizontalGrid() const
+{
+ return dptrc()->m_shared->flipHorizontalGrid();
+}
+
+/*!
* Adds \a axis to the graph. The axes added via addAxis are not yet taken to use,
* addAxis is simply used to give the ownership of the \a axis to the graph.
* The \a axis must not be null or added to another graph.
diff --git a/src/datavisualization/engine/q3dsurface.h b/src/datavisualization/engine/q3dsurface.h
index 9868c844..86740519 100644
--- a/src/datavisualization/engine/q3dsurface.h
+++ b/src/datavisualization/engine/q3dsurface.h
@@ -34,6 +34,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DSurface : public QAbstract3DGraph
Q_PROPERTY(QValue3DAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged)
Q_PROPERTY(QValue3DAxis *axisZ READ axisZ WRITE setAxisZ NOTIFY axisZChanged)
Q_PROPERTY(QSurface3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged)
+ Q_PROPERTY(bool flipHorizontalGrid READ flipHorizontalGrid WRITE setFlipHorizontalGrid NOTIFY flipHorizontalGridChanged)
public:
explicit Q3DSurface(const QSurfaceFormat *format = 0, QWindow *parent = 0);
@@ -55,12 +56,15 @@ public:
QList<QValue3DAxis *> axes() const;
QSurface3DSeries *selectedSeries() const;
+ void setFlipHorizontalGrid(bool flip);
+ bool flipHorizontalGrid() const;
signals:
void axisXChanged(QValue3DAxis *axis);
void axisYChanged(QValue3DAxis *axis);
void axisZChanged(QValue3DAxis *axis);
void selectedSeriesChanged(QSurface3DSeries *series);
+ void flipHorizontalGridChanged(bool flip);
private:
Q3DSurfacePrivate *dptr();
diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp
index b8fa92e8..e51d9ce4 100644
--- a/src/datavisualization/engine/qabstract3dgraph.cpp
+++ b/src/datavisualization/engine/qabstract3dgraph.cpp
@@ -22,6 +22,7 @@
#include "qabstract3dinputhandler_p.h"
#include "q3dscene_p.h"
#include "qutils.h"
+#include "utils_p.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QOpenGLContext>
@@ -29,6 +30,9 @@
#include <QtGui/QPainter>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtGui/QOffscreenSurface>
+#if defined(Q_OS_OSX)
+#include <qpa/qplatformnativeinterface.h>
+#endif
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -148,7 +152,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
\value OptimizationDefault
Provides the full feature set at a reasonable performance.
\value OptimizationStatic
- Beta level feature. Optimizes the rendering of static data sets at the expense of some features.
+ Optimizes the rendering of static data sets at the expense of some features.
*/
/*!
@@ -169,11 +173,7 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor
if (format) {
surfaceFormat = *format;
// Make sure renderable type is correct
-#if !defined(QT_OPENGL_ES_2)
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL);
-#else
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
-#endif
+ surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType);
} else {
surfaceFormat = qDefaultSurfaceFormat();
}
@@ -197,17 +197,24 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor
qDebug() << "GLSL version:" << (const char *)shaderVersion;
#endif
-#if !defined(QT_OPENGL_ES_2)
- // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not.
- QStringList splitversionstr =
- QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' '));
- if (splitversionstr[0].toFloat() < 1.2)
- qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers.");
-#else
- Q_UNUSED(shaderVersion)
-#endif
+ if (!Utils::isOpenGLES()) {
+ // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not.
+ QStringList splitversionstr =
+ QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' '));
+ if (splitversionstr[0].toFloat() < 1.2)
+ qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers.");
+ }
d_ptr->renderLater();
+
+#if defined(Q_OS_OSX)
+ // Enable touch events for Mac touchpads
+ typedef void * (*EnableTouch)(QWindow*, bool);
+ EnableTouch enableTouch =
+ (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow");
+ if (enableTouch)
+ enableTouch(this, true);
+#endif
}
/*!
@@ -400,7 +407,7 @@ void QAbstract3DGraph::clearSelection()
* \return index to the added item if add was successful, -1 if trying to add a null item, and
* index of the item if trying to add an already added item.
*
- * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt()
+ * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt(), customItems()
*
* \since QtDataVisualization 1.1
*/
@@ -455,6 +462,16 @@ void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item)
}
/*!
+ * \return list of all added custom items.
+ * \since QtDataVisualization 1.2
+ * \sa addCustomItem()
+ */
+QList<QCustom3DItem *> QAbstract3DGraph::customItems() const
+{
+ return d_ptr->m_visualController->customItems();
+}
+
+/*!
* Can be used to query the index of the selected label after receiving \c selectedElementChanged
* signal with any label type. Selection is valid until the next \c selectedElementChanged signal.
*
@@ -545,6 +562,8 @@ QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const
* \since QtDataVisualization 1.1
*
* \return rendered image.
+ *
+ * \note OpenGL ES2 does not support anitialiasing, so \a msaaSamples is always forced to \c{0}.
*/
QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize)
{
@@ -591,8 +610,15 @@ qreal QAbstract3DGraph::currentFps() const
* \property QAbstract3DGraph::orthoProjection
* \since QtDataVisualization 1.1
*
- * If \c {true}, orthographic projection will be used for displaying the graph. Defaults to \c{false}.
+ * If \c {true}, orthographic projection will be used for displaying the graph.
+ * \note Orthographic projection can be used to create 2D graphs by replacing the default input
+ * handler with one that doesn't allow rotating the graph and setting the camera to view the graph
+ * directly from the side or from the top. Also, axis labels typically need to be rotated when
+ * viewing the graph from the sides.
+ * Defaults to \c{false}.
* \note Shadows will be disabled when set to \c{true}.
+ *
+ * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset
*/
void QAbstract3DGraph::setOrthoProjection(bool enable)
{
@@ -608,14 +634,16 @@ bool QAbstract3DGraph::isOrthoProjection() const
* \property QAbstract3DGraph::aspectRatio
* \since QtDataVisualization 1.1
*
- * Aspect ratio of the graph data. This is the ratio of data scaling between horizontal and
- * vertical axes. Defaults to \c{2.0}.
+ * The aspect ratio is the ratio of the graph scaling between the longest axis on the horizontal
+ * plane and the Y-axis. Defaults to \c{2.0}.
*
* \note Has no effect on Q3DBars.
+ *
+ * \sa horizontalAspectRatio
*/
void QAbstract3DGraph::setAspectRatio(qreal ratio)
{
- d_ptr->m_visualController->setAspectRatio(float(ratio));
+ d_ptr->m_visualController->setAspectRatio(ratio);
}
qreal QAbstract3DGraph::aspectRatio() const
@@ -626,14 +654,20 @@ qreal QAbstract3DGraph::aspectRatio() const
/*!
* \property QAbstract3DGraph::optimizationHints
*
- * Defines if the rendering optimization is default or static. Default mode provides the full feature set at
- * reasonable performance. Static is a beta level feature and currently supports only a subset of the
- * features on the Scatter graph. Missing features are object gradient for mesh objects, both gradients
- * for points, and diffuse and specular color on rotations. At this point static is intended just for
- * introducing a new feature. It optimizes graph rendering and is ideal for large non-changing data
- * sets. It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it
- * with massive data sets is not advisable.
+ * Defines if the rendering optimization is default or static. Default mode provides the full
+ * feature set at reasonable performance. Static mode optimizes graph rendering and is ideal for
+ * large non-changing data sets. It is slower with dynamic data changes and item rotations.
+ * Selection is not optimized, so using it with massive data sets is not advisable.
+ * Static works only on the Scatter graph.
* Defaults to \c{OptimizationDefault}.
+ *
+ * \note On some environments, large graphs using static optimization may not render, because
+ * all of the items are rendered using a single draw call, and different graphics drivers have
+ * different maximum vertice counts per call that they support.
+ * This is mostly an issue on 32bit and/or OpenGL ES2 platforms.
+ * To work around this issue, choose an item mesh with low vertex count or use the point mesh.
+ *
+ * \sa QAbstract3DSeries::mesh
*/
void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints)
{
@@ -646,6 +680,195 @@ QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const
}
/*!
+ * \property QAbstract3DGraph::polar
+ * \since QtDataVisualization 1.2
+ *
+ * If \c {true}, the horizontal axes are changed into polar axes. The X axis becomes the
+ * angular axis and the Z axis becomes the radial axis.
+ * Polar mode is not available for bar graphs.
+ *
+ * Defaults to \c{false}.
+ *
+ * \sa orthoProjection, radialLabelOffset
+ */
+void QAbstract3DGraph::setPolar(bool enable)
+{
+ d_ptr->m_visualController->setPolar(enable);
+}
+
+bool QAbstract3DGraph::isPolar() const
+{
+ return d_ptr->m_visualController->isPolar();
+}
+
+/*!
+ * \property QAbstract3DGraph::radialLabelOffset
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies the normalized horizontal offset for the axis labels of the radial
+ * polar axis. The value 0.0 indicates the labels should be drawn next to the 0-angle angular
+ * axis grid line. The value 1.0 indicates the labels are drawn on their normal place at the edge
+ * of the graph background.
+ * This property is ignored if polar property value is \c{false}. Defaults to 1.0.
+ *
+ * \sa polar
+ */
+void QAbstract3DGraph::setRadialLabelOffset(float offset)
+{
+ d_ptr->m_visualController->setRadialLabelOffset(offset);
+}
+
+float QAbstract3DGraph::radialLabelOffset() const
+{
+ return d_ptr->m_visualController->radialLabelOffset();
+}
+
+/*!
+ * \property QAbstract3DGraph::horizontalAspectRatio
+ * \since QtDataVisualization 1.2
+ *
+ * The horizontal aspect ratio is the ratio of the graph scaling between the X and Z axes.
+ * Value of 0.0 indicates automatic scaling according to axis ranges.
+ * Defaults to \c{0.0}.
+ *
+ * \note Has no effect on Q3DBars, which handles scaling on the horizontal plane via
+ * \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties.
+ * Polar graphs also ignore this property.
+ *
+ * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing
+ */
+void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio)
+{
+ d_ptr->m_visualController->setHorizontalAspectRatio(ratio);
+}
+
+qreal QAbstract3DGraph::horizontalAspectRatio() const
+{
+ return d_ptr->m_visualController->horizontalAspectRatio();
+}
+
+/*!
+ * \property QAbstract3DGraph::reflection
+ * \since QtDataVisualization 1.2
+ *
+ * Sets floor reflections on or off. Defaults to \c{false}.
+ *
+ * \note Affects only Q3DBars.
+ *
+ * \note In Q3DBars graphs holding both positive and negative values, reflections are not supported
+ * for custom items that intersect the floor plane. In that case, reflections should be turned off
+ * to avoid incorrect rendering.
+ *
+ * \note If using custom surface format, stencil buffer needs to be defined
+ * (QSurfaceFormat::setStencilBufferSize()) for reflections to work.
+ *
+ * \sa reflectivity
+ */
+void QAbstract3DGraph::setReflection(bool enable)
+{
+ d_ptr->m_visualController->setReflection(enable);
+}
+
+bool QAbstract3DGraph::isReflection() const
+{
+ return d_ptr->m_visualController->reflection();
+}
+
+/*!
+ * \property QAbstract3DGraph::reflectivity
+ * \since QtDataVisualization 1.2
+ *
+ * Adjusts floor reflectivity, larger number being more reflective. Valid range is \c{[0...1]}.
+ * Defaults to \c{0.5}.
+ *
+ * \note Affects only Q3DBars.
+ *
+ * \sa reflection
+ */
+void QAbstract3DGraph::setReflectivity(qreal reflectivity)
+{
+ d_ptr->m_visualController->setReflectivity(reflectivity);
+}
+
+qreal QAbstract3DGraph::reflectivity() const
+{
+ return d_ptr->m_visualController->reflectivity();
+}
+
+/*!
+ * \property QAbstract3DGraph::locale
+ * \since QtDataVisualization 1.2
+ *
+ * Sets the locale used for formatting various numeric labels.
+ * Defaults to \c{"C"} locale.
+ *
+ * \sa QValue3DAxis::labelFormat
+ */
+void QAbstract3DGraph::setLocale(const QLocale &locale)
+{
+ d_ptr->m_visualController->setLocale(locale);
+}
+
+QLocale QAbstract3DGraph::locale() const
+{
+ return d_ptr->m_visualController->locale();
+}
+
+/*!
+ * \property QAbstract3DGraph::queriedGraphPosition
+ * \since QtDataVisualization 1.2
+ *
+ * This read-only property contains the latest graph position values along each axis queried using
+ * Q3DScene::graphPositionQuery. The values are normalized to range \c{[-1, 1]}.
+ * If the queried position was outside the graph bounds, the values
+ * will not reflect the real position, but will instead be some undefined position outside
+ * the range \c{[-1, 1]}. The value will be undefined before any queries are made.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs only allow querying graph position at the graph floor level,
+ * so the Y-value is always zero for bar graphs and the valid queries can be only made at
+ * screen positions that contain the floor of the graph.
+ *
+ * \sa Q3DScene::graphPositionQuery
+ */
+QVector3D QAbstract3DGraph::queriedGraphPosition() const
+{
+ return d_ptr->m_visualController->queriedGraphPosition();
+}
+
+/*!
+ * \property QAbstract3DGraph::margin
+ * \since QtDataVisualization 1.2
+ *
+ * This property contains the absolute value used for graph margin. The graph margin is the space
+ * left between the edge of the plottable graph area and the edge of the graph background.
+ * If the margin value is negative, the margins are determined automatically and can vary according
+ * to size of the items in the series and the type of the graph.
+ * The value is interpreted as a fraction of Y-axis range, provided the graph aspect ratios have
+ * not beed changed from the defaults.
+ * Defaults to \c{-1.0}.
+ *
+ * \note Having smaller than the automatically determined margin on scatter graph can cause
+ * the scatter items at the edges of the graph to overlap with the graph background.
+ *
+ * \note On scatter and surface graphs, if the margin is comparatively small to the axis label
+ * size, the positions of the edge labels of the axes are adjusted to avoid overlap with
+ * the edge labels of the neighboring axes.
+ */
+void QAbstract3DGraph::setMargin(qreal margin)
+{
+ d_ptr->m_visualController->setMargin(margin);
+}
+
+qreal QAbstract3DGraph::margin() const
+{
+ return d_ptr->m_visualController->margin();
+}
+
+/*!
* \internal
*/
bool QAbstract3DGraph::event(QEvent *event)
@@ -795,6 +1018,23 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll
QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr,
&QAbstract3DGraph::aspectRatioChanged);
+ QObject::connect(m_visualController, &Abstract3DController::polarChanged, q_ptr,
+ &QAbstract3DGraph::polarChanged);
+ QObject::connect(m_visualController, &Abstract3DController::radialLabelOffsetChanged, q_ptr,
+ &QAbstract3DGraph::radialLabelOffsetChanged);
+ QObject::connect(m_visualController, &Abstract3DController::horizontalAspectRatioChanged, q_ptr,
+ &QAbstract3DGraph::horizontalAspectRatioChanged);
+
+ QObject::connect(m_visualController, &Abstract3DController::reflectionChanged, q_ptr,
+ &QAbstract3DGraph::reflectionChanged);
+ QObject::connect(m_visualController, &Abstract3DController::reflectivityChanged, q_ptr,
+ &QAbstract3DGraph::reflectivityChanged);
+ QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr,
+ &QAbstract3DGraph::localeChanged);
+ QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr,
+ &QAbstract3DGraph::queriedGraphPositionChanged);
+ QObject::connect(m_visualController, &Abstract3DController::marginChanged, q_ptr,
+ &QAbstract3DGraph::marginChanged);
}
void QAbstract3DGraphPrivate::handleDevicePixelRatioChange()
@@ -849,8 +1089,10 @@ QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imag
// Render the wanted frame offscreen
m_context->makeCurrent(m_offscreenSurface);
fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
- fboFormat.setInternalTextureFormat(GL_RGB);
- fboFormat.setSamples(msaaSamples);
+ if (!Utils::isOpenGLES()) {
+ fboFormat.setInternalTextureFormat(GL_RGB);
+ fboFormat.setSamples(msaaSamples);
+ }
fbo = new QOpenGLFramebufferObject(imageSize, fboFormat);
if (fbo->isValid()) {
QRect originalViewport = m_visualController->m_scene->viewport();
diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h
index 59f61aae..a0c26a42 100644
--- a/src/datavisualization/engine/qabstract3dgraph.h
+++ b/src/datavisualization/engine/qabstract3dgraph.h
@@ -25,6 +25,7 @@
#include <QtDataVisualization/qabstract3dinputhandler.h>
#include <QtGui/QWindow>
#include <QtGui/QOpenGLFunctions>
+#include <QtCore/QLocale>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -50,6 +51,14 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q
Q_PROPERTY(ElementType selectedElement READ selectedElement NOTIFY selectedElementChanged)
Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged)
Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged)
+ Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged)
+ Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged)
+ Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged)
+ Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged)
+ Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged)
+ Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged)
+ Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged)
+ Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged)
protected:
explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format,
@@ -126,6 +135,7 @@ public:
void removeCustomItem(QCustom3DItem *item);
void removeCustomItemAt(const QVector3D &position);
void releaseCustomItem(QCustom3DItem *item);
+ QList<QCustom3DItem *> customItems() const;
int selectedLabelIndex() const;
QAbstract3DAxis *selectedAxis() const;
@@ -150,6 +160,29 @@ public:
void setOptimizationHints(OptimizationHints hints);
OptimizationHints optimizationHints() const;
+ void setPolar(bool enable);
+ bool isPolar() const;
+
+ void setRadialLabelOffset(float offset);
+ float radialLabelOffset() const;
+
+ void setHorizontalAspectRatio(qreal ratio);
+ qreal horizontalAspectRatio() const;
+
+ void setReflection(bool enable);
+ bool isReflection() const;
+
+ void setReflectivity(qreal reflectivity);
+ qreal reflectivity() const;
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ QVector3D queriedGraphPosition() const;
+
+ void setMargin(qreal margin);
+ qreal margin() const;
+
protected:
bool event(QEvent *event);
void resizeEvent(QResizeEvent *event);
@@ -173,6 +206,14 @@ signals:
void orthoProjectionChanged(bool enabled);
void aspectRatioChanged(qreal ratio);
void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints);
+ void polarChanged(bool enabled);
+ void radialLabelOffsetChanged(float offset);
+ void horizontalAspectRatioChanged(qreal ratio);
+ void reflectionChanged(bool enabled);
+ void reflectivityChanged(qreal reflectivity);
+ void localeChanged(const QLocale &locale);
+ void queriedGraphPositionChanged(const QVector3D &data);
+ void marginChanged(qreal margin);
private:
Q_DISABLE_COPY(QAbstract3DGraph)
diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp
index dd50188b..f6367153 100644
--- a/src/datavisualization/engine/scatter3drenderer.cpp
+++ b/src/datavisualization/engine/scatter3drenderer.cpp
@@ -33,12 +33,9 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-//#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values
-
const GLfloat defaultMinSize = 0.01f;
const GLfloat defaultMaxSize = 0.1f;
const GLfloat itemScaler = 3.0f;
-const GLfloat gridLineWidth = 0.005f;
Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
: Abstract3DRenderer(controller),
@@ -46,30 +43,27 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
m_updateLabels(false),
m_dotShader(0),
m_dotGradientShader(0),
- #if defined(QT_OPENGL_ES_2)
+ m_staticSelectedItemGradientShader(0),
+ m_staticSelectedItemShader(0),
m_pointShader(0),
- #endif
m_depthShader(0),
m_selectionShader(0),
m_backgroundShader(0),
- m_labelShader(0),
+ m_staticGradientPointShader(0),
m_bgrTexture(0),
- m_depthTexture(0),
m_selectionTexture(0),
m_depthFrameBuffer(0),
m_selectionFrameBuffer(0),
m_selectionDepthBuffer(0),
m_shadowQualityToShader(100.0f),
m_shadowQualityMultiplier(3),
- m_heightNormalizer(1.0f),
- m_scaleFactor(0),
+ m_scaleX(0.0f),
+ m_scaleY(0.0f),
+ m_scaleZ(0.0f),
m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()),
m_selectedSeriesCache(0),
m_oldSelectedSeriesCache(0),
- m_areaSize(QSizeF(0.0, 0.0)),
m_dotSizeScale(1.0f),
- m_hasHeightAdjustmentChanged(true),
- m_backgroundMargin(defaultMaxSize),
m_maxItemSize(0.0f),
m_clickedIndex(Scatter3DController::invalidSelectionIndex()),
m_havePointSeries(false),
@@ -77,15 +71,13 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
m_haveUniformColorMeshSeries(false),
m_haveGradientMeshSeries(false)
{
- m_axisCacheY.setScale(2.0f);
- m_axisCacheY.setTranslate(-1.0f);
-
- initializeOpenGLFunctions();
initializeOpenGL();
}
Scatter3DRenderer::~Scatter3DRenderer()
{
+ fixContextBeforeDelete();
+
if (QOpenGLContext::currentContext()) {
m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
@@ -94,11 +86,13 @@ Scatter3DRenderer::~Scatter3DRenderer()
m_textureHelper->deleteTexture(&m_bgrTexture);
}
delete m_dotShader;
+ delete m_staticSelectedItemGradientShader;
+ delete m_staticSelectedItemShader;
delete m_dotGradientShader;
delete m_depthShader;
delete m_selectionShader;
delete m_backgroundShader;
- delete m_labelShader;
+ delete m_staticGradientPointShader;
}
void Scatter3DRenderer::initializeOpenGL()
@@ -106,28 +100,17 @@ void Scatter3DRenderer::initializeOpenGL()
Abstract3DRenderer::initializeOpenGL();
// Initialize shaders
- initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
- QStringLiteral(":/shaders/fragmentLabel"));
-#if !defined(QT_OPENGL_ES_2)
- // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
- initDepthShader();
-#else
- // Init point shader
- initPointShader();
-#endif
+ if (!m_isOpenGLES) {
+ initDepthShader(); // For shadows
+ loadGridLineMesh();
+ } else {
+ initPointShader();
+ }
// Init selection shader
initSelectionShader();
-#if !defined(QT_OPENGL_ES_2)
- // Load grid line mesh
- loadGridLineMesh();
-#endif
-
- // Load label mesh
- loadLabelMesh();
-
// Set view port
glViewport(m_primarySubViewport.x(),
m_primarySubViewport.y(),
@@ -138,6 +121,53 @@ void Scatter3DRenderer::initializeOpenGL()
loadBackgroundMesh();
}
+void Scatter3DRenderer::fixCameraTarget(QVector3D &target)
+{
+ target.setX(target.x() * m_scaleX);
+ target.setY(target.y() * m_scaleY);
+ target.setZ(target.z() * -m_scaleZ);
+}
+
+void Scatter3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_scaleX)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -m_scaleY)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_scaleZ)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_scaleX)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > m_scaleY)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_scaleZ)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Scatter3DRenderer::updateData()
{
calculateSceneScalingFactors();
@@ -145,24 +175,31 @@ void Scatter3DRenderer::updateData()
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
- if (cache->isVisible() && cache->dataDirty()) {
+ if (cache->isVisible()) {
const QScatter3DSeries *currentSeries = cache->series();
ScatterRenderItemArray &renderArray = cache->renderArray();
QScatterDataProxy *dataProxy = currentSeries->dataProxy();
const QScatterDataArray &dataArray = *dataProxy->array();
int dataSize = dataArray.size();
totalDataSize += dataSize;
- if (dataSize != renderArray.size())
- renderArray.resize(dataSize);
+ if (cache->dataDirty()) {
+ if (dataSize != renderArray.size())
+ renderArray.resize(dataSize);
+
+ for (int i = 0; i < dataSize; i++)
+ updateRenderItem(dataArray.at(i), renderArray[i]);
- for (int i = 0; i < dataSize; i++)
- updateRenderItem(dataArray.at(i), renderArray[i]);
- cache->setDataDirty(false);
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic))
+ cache->setStaticBufferDirty(true);
+
+ cache->setDataDirty(false);
+ }
}
}
if (totalDataSize) {
- m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))),
+ m_dotSizeScale = GLfloat(qBound(defaultMinSize,
+ 2.0f / float(qSqrt(qreal(totalDataSize))),
defaultMaxSize));
}
@@ -179,6 +216,7 @@ void Scatter3DRenderer::updateData()
points = new ScatterPointBufferHelper();
cache->setBufferPoints(points);
}
+ points->setScaleY(m_scaleY);
points->load(cache);
} else {
ScatterObjectBufferHelper *object = cache->bufferObject();
@@ -187,7 +225,9 @@ void Scatter3DRenderer::updateData()
cache->setBufferObject(object);
}
if (renderArraySize != cache->oldArraySize()
- || cache->object()->objectFile() != cache->oldMeshFileName()) {
+ || cache->object()->objectFile() != cache->oldMeshFileName()
+ || cache->staticBufferDirty()) {
+ object->setScaleY(m_scaleY);
object->fullLoad(cache, m_dotSizeScale);
cache->setOldArraySize(renderArraySize);
cache->setOldMeshFileName(cache->object()->objectFile());
@@ -195,6 +235,8 @@ void Scatter3DRenderer::updateData()
object->update(cache, m_dotSizeScale);
}
}
+
+ cache->setStaticBufferDirty(false);
}
}
}
@@ -205,9 +247,28 @@ void Scatter3DRenderer::updateData()
void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
{
+ int seriesCount = seriesList.size();
+
+ // Check OptimizationStatic specific issues before populate marks changeTracker done
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) {
+ for (int i = 0; i < seriesCount; i++) {
+ QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]);
+ if (scatterSeries->isVisible()) {
+ QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker;
+ ScatterSeriesRenderCache *cache =
+ static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(scatterSeries));
+ if (cache) {
+ if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged)
+ cache->setStaticObjectUVDirty(true);
+ if (cache->itemSize() != scatterSeries->itemSize())
+ cache->setStaticBufferDirty(true);
+ }
+ }
+ }
+ }
+
Abstract3DRenderer::updateSeries(seriesList);
- int seriesCount = seriesList.size();
float maxItemSize = 0.0f;
float itemSize = 0.0f;
bool noSelection = true;
@@ -243,13 +304,28 @@ void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis
else
m_haveGradientMeshSeries = true;
}
+
+ if (cache->staticBufferDirty()) {
+ if (cache->mesh() != QAbstract3DSeries::MeshPoint) {
+ ScatterObjectBufferHelper *object = cache->bufferObject();
+ object->update(cache, m_dotSizeScale);
+ }
+ cache->setStaticBufferDirty(false);
+ }
+ if (cache->staticObjectUVDirty()) {
+ if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
+ ScatterPointBufferHelper *object = cache->bufferPoints();
+ object->updateUVs(cache);
+ } else {
+ ScatterObjectBufferHelper *object = cache->bufferObject();
+ object->updateUVs(cache);
+ }
+ cache->setStaticObjectUVDirty(false);
+ }
}
}
m_maxItemSize = maxItemSize;
- if (maxItemSize > defaultMaxSize)
- m_backgroundMargin = maxItemSize / itemScaler;
- else
- m_backgroundMargin = defaultMaxSize;
+ calculateSceneScalingFactors();
if (noSelection) {
if (!selectionLabel().isEmpty())
@@ -268,6 +344,8 @@ void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeIte
ScatterSeriesRenderCache *cache = 0;
const QScatter3DSeries *prevSeries = 0;
const QScatterDataArray *dataArray = 0;
+ const bool optimizationStatic = m_cachedOptimizationHint.testFlag(
+ QAbstract3DGraph::OptimizationStatic);
foreach (Scatter3DController::ChangeItem item, items) {
QScatter3DSeries *currentSeries = item.series;
@@ -282,7 +360,43 @@ void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeIte
}
if (cache->isVisible()) {
const int index = item.index;
- updateRenderItem(dataArray->at(index), cache->renderArray()[index]);
+ if (index >= cache->renderArray().size())
+ continue; // Items removed from array for same render
+ bool oldVisibility;
+ ScatterRenderItem &item = cache->renderArray()[index];
+ if (optimizationStatic)
+ oldVisibility = item.isVisible();
+ updateRenderItem(dataArray->at(index), item);
+ if (optimizationStatic) {
+ if (!cache->visibilityChanged() && oldVisibility != item.isVisible())
+ cache->setVisibilityChanged(true);
+ cache->updateIndices().append(index);
+ }
+ }
+ }
+ if (optimizationStatic) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
+ if (cache->isVisible() && cache->updateIndices().size()) {
+ if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
+ cache->bufferPoints()->update(cache);
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ cache->bufferPoints()->updateUVs(cache);
+ } else {
+ if (cache->visibilityChanged()) {
+ // If any change changes item visibility, full load is needed to
+ // resize the buffers.
+ cache->updateIndices().clear();
+ cache->bufferObject()->fullLoad(cache, m_dotSizeScale);
+ } else {
+ cache->bufferObject()->update(cache, m_dotSizeScale);
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ cache->bufferObject()->updateUVs(cache);
+ }
+ }
+ cache->updateIndices().clear();
+ }
+ cache->setVisibilityChanged(false);
}
}
}
@@ -291,14 +405,46 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene)
{
scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
- if (m_hasHeightAdjustmentChanged) {
- // Set initial camera position. Also update if height adjustment has changed.
- scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector,
- upVector);
- m_hasHeightAdjustmentChanged = false;
+ Abstract3DRenderer::updateScene(scene);
+}
+
+void Scatter3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels)
+{
+ Abstract3DRenderer::updateAxisLabels(orientation, labels);
+
+ // Angular axis label dimensions affect the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Scatter3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible)
+{
+ Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible);
+
+ // Angular axis title existence affects the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
+{
+ Abstract3DRenderer::updateOptimizationHint(hint);
+
+ Abstract3DRenderer::reInitShaders();
+
+ if (m_isOpenGLES && hint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && !m_staticGradientPointShader) {
+ initStaticPointShaders(QStringLiteral(":/shaders/vertexPointES2_UV"),
+ QStringLiteral(":/shaders/fragmentLabel"));
}
+}
- Abstract3DRenderer::updateScene(scene);
+void Scatter3DRenderer::updateMargin(float margin)
+{
+ Abstract3DRenderer::updateMargin(margin);
+ calculateSceneScalingFactors();
}
void Scatter3DRenderer::resetClickedStatus()
@@ -374,6 +520,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
m_yFlipped = true;
else
m_yFlipped = false;
+ m_yFlippedForGrid = m_yFlipped; // Polar axis grid drawing in abstract needs this
// Calculate background rotation
if (!m_zFlipped && !m_xFlipped)
@@ -389,169 +536,182 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QVector3D lightPos = m_cachedScene->activeLight()->position();
// Introduce regardless of shadow quality to simplify logic
- QMatrix4x4 depthViewMatrix;
- QMatrix4x4 depthProjectionMatrix;
QMatrix4x4 depthProjectionViewMatrix;
+ ShaderHelper *pointSelectionShader;
+ if (!m_isOpenGLES) {
#if !defined(QT_OPENGL_ES_2)
- if (m_havePointSeries) {
- glEnable(GL_POINT_SMOOTH);
- glEnable(GL_PROGRAM_POINT_SIZE);
- }
-
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Render scene into a depth texture for using with shadow mapping
- // Bind depth shader
- m_depthShader->bind();
-
- // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
- glViewport(0, 0,
- m_primarySubViewport.width() * m_shadowQualityMultiplier,
- m_primarySubViewport.height() * m_shadowQualityMultiplier);
-
- // Enable drawing to framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
- glClear(GL_DEPTH_BUFFER_BIT);
-
- // Set front face culling to reduce self-shadowing issues
- glCullFace(GL_FRONT);
-
- // Get the depth view matrix
- // It may be possible to hack lightPos here if we want to make some tweaks to shadow
- QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
- zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment);
- depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
- // Set the depth projection matrix
- depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f);
- depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
-
- // Draw dots to depth buffer
- foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
- if (baseCache->isVisible()) {
- ScatterSeriesRenderCache *cache =
- static_cast<ScatterSeriesRenderCache *>(baseCache);
- ObjectHelper *dotObj = cache->object();
- QQuaternion seriesRotation(cache->meshRotation());
- const ScatterRenderItemArray &renderArray = cache->renderArray();
- const int renderArraySize = renderArray.size();
- bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
- float itemSize = cache->itemSize() / itemScaler;
- if (itemSize == 0.0f)
- itemSize = m_dotSizeScale;
- if (drawingPoints) {
- // Scale points based on shadow quality for shadows, not by zoom level
- glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier);
- }
- QVector3D modelScaler(itemSize, itemSize, itemSize);
+ if (m_havePointSeries) {
+ glEnable(GL_POINT_SMOOTH);
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ }
- if (!optimizationDefault
- && ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
- || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
- continue;
- }
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Render scene into a depth texture for using with shadow mapping
+ // Bind depth shader
+ m_depthShader->bind();
+
+ // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
+ glViewport(0, 0,
+ m_primarySubViewport.width() * m_shadowQualityMultiplier,
+ m_primarySubViewport.height() * m_shadowQualityMultiplier);
+
+ // Enable drawing to framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ // Set front face culling to reduce self-shadowing issues
+ glCullFace(GL_FRONT);
+
+ QMatrix4x4 depthViewMatrix;
+ QMatrix4x4 depthProjectionMatrix;
+
+ // Get the depth view matrix
+ // It may be possible to hack lightPos here if we want to make some tweaks to shadow
+ QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
+ zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment);
+ depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
+ // Set the depth projection matrix
+ depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f);
+ depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
+
+ // Draw dots to depth buffer
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ ScatterSeriesRenderCache *cache =
+ static_cast<ScatterSeriesRenderCache *>(baseCache);
+ ObjectHelper *dotObj = cache->object();
+ QQuaternion seriesRotation(cache->meshRotation());
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int renderArraySize = renderArray.size();
+ bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
+ float itemSize = cache->itemSize() / itemScaler;
+ if (itemSize == 0.0f)
+ itemSize = m_dotSizeScale;
+ if (drawingPoints) {
+ // Scale points based on shadow quality for shadows, not by zoom level
+ m_funcs_2_1->glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier);
+ }
+ QVector3D modelScaler(itemSize, itemSize, itemSize);
- int loopCount = 1;
- if (optimizationDefault)
- loopCount = renderArraySize;
- for (int dot = 0; dot < loopCount; dot++) {
- const ScatterRenderItem &item = renderArray.at(dot);
- if (!item.isVisible() && optimizationDefault)
+ if (!optimizationDefault
+ && ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
+ || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
continue;
-
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
-
- if (optimizationDefault) {
- modelMatrix.translate(item.translation());
- if (!drawingPoints) {
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
- modelMatrix.rotate(seriesRotation * item.rotation());
- modelMatrix.scale(modelScaler);
- }
}
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ int loopCount = 1;
+ if (optimizationDefault)
+ loopCount = renderArraySize;
+ for (int dot = 0; dot < loopCount; dot++) {
+ const ScatterRenderItem &item = renderArray.at(dot);
+ if (!item.isVisible() && optimizationDefault)
+ continue;
- m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
- if (drawingPoints) {
- if (optimizationDefault)
- m_drawer->drawPoint(m_depthShader);
- else
- m_drawer->drawPoints(m_depthShader, cache->bufferPoints());
- } else {
if (optimizationDefault) {
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_depthShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf());
- glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
-
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf());
+ modelMatrix.translate(item.translation());
+ if (!drawingPoints) {
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
+ modelMatrix.rotate(seriesRotation * item.rotation());
+ modelMatrix.scale(modelScaler);
+ }
+ }
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT,
- (void *)0);
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
- // Free buffers
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+ m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
- glDisableVertexAttribArray(m_depthShader->posAtt());
+ if (drawingPoints) {
+ if (optimizationDefault)
+ m_drawer->drawPoint(m_depthShader);
+ else
+ m_drawer->drawPoints(m_depthShader, cache->bufferPoints(), 0);
} else {
- ScatterObjectBufferHelper *object = cache->bufferObject();
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_depthShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
- glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
-
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
-
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, object->indexCount(),
- object->indicesType(), (void *)0);
-
- // Free buffers
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-
- glDisableVertexAttribArray(m_depthShader->posAtt());
+ if (optimizationDefault) {
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_depthShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
+ (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT,
+ (void *)0);
+
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(m_depthShader->posAtt());
+ } else {
+ ScatterObjectBufferHelper *object = cache->bufferObject();
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_depthShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
+ (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, object->indexCount(),
+ object->indicesType(), (void *)0);
+
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(m_depthShader->posAtt());
+ }
}
}
}
}
- }
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
- // Disable drawing to framebuffer (= enable drawing to screen)
- glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
+ // Disable drawing to framebuffer (= enable drawing to screen)
+ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
- // Reset culling to normal
- glCullFace(GL_BACK);
+ // Reset culling to normal
+ glCullFace(GL_BACK);
- // Revert to original viewport
- glViewport(m_primarySubViewport.x(),
- m_primarySubViewport.y(),
- m_primarySubViewport.width(),
- m_primarySubViewport.height());
+ // Revert to original viewport
+ glViewport(m_primarySubViewport.x(),
+ m_primarySubViewport.y(),
+ m_primarySubViewport.width(),
+ m_primarySubViewport.height());
+ }
+#endif
+ pointSelectionShader = m_selectionShader;
+ } else {
+ pointSelectionShader = m_pointShader;
}
- ShaderHelper *pointSelectionShader = m_selectionShader;
-#else
- ShaderHelper *pointSelectionShader = m_pointShader;
-#endif
ShaderHelper *selectionShader = m_selectionShader;
+ // Do position mapping when necessary
+ if (m_graphPositionQueryPending) {
+ QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ);
+ queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
+ emit needRender();
+ }
+
// Skip selection mode drawing if we have no selection mode
if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
&& SelectOnScene == m_selectionState
- && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) {
+ && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
+ && m_selectionTexture) {
// Draw dots to selection buffer
glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
glViewport(0, 0,
@@ -578,8 +738,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (itemSize == 0.0f)
itemSize = m_dotSizeScale;
#if !defined(QT_OPENGL_ES_2)
- if (drawingPoints)
- glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom
+ if (drawingPoints && !m_isOpenGLES)
+ m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel());
#endif
QVector3D modelScaler(itemSize, itemSize, itemSize);
@@ -627,9 +787,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
}
}
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
+ viewMatrix, projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
@@ -639,6 +800,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QVector4D clickedColor = Utils::getSelection(m_inputPosition,
m_viewport.height());
selectionColorToSeriesAndIndex(clickedColor, m_clickedIndex, m_clickedSeries);
+ m_clickResolved = true;
emit needRender();
@@ -683,13 +845,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
previousMeshColorStyle = Q3DTheme::ColorStyleRangeGradient;
m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f);
}
- glEnable(GL_TEXTURE_2D);
} else {
dotShader = pointSelectionShader;
- previousDrawingPoints = true;
- dotShader->bind();
}
+ float rangeGradientYScaler = 0.5f / m_scaleY;
+
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
if (baseCache->isVisible()) {
ScatterSeriesRenderCache *cache =
@@ -710,14 +871,16 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (itemSize == 0.0f)
itemSize = m_dotSizeScale;
#if !defined(QT_OPENGL_ES_2)
- if (drawingPoints)
- glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom
+ if (drawingPoints && !m_isOpenGLES)
+ m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel());
#endif
QVector3D modelScaler(itemSize, itemSize, itemSize);
+ int gradientImageHeight = cache->gradientImage().height();
+ int maxGradientPositition = gradientImageHeight - 1;
if (!optimizationDefault
&& ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
- || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
+ || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
continue;
}
@@ -725,10 +888,18 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (drawingPoints != previousDrawingPoints
|| (!drawingPoints &&
(colorStyleIsUniform != (previousMeshColorStyle
- == Q3DTheme::ColorStyleUniform)))) {
+ == Q3DTheme::ColorStyleUniform)))
+ || (!optimizationDefault && drawingPoints)) {
previousDrawingPoints = drawingPoints;
if (drawingPoints) {
- dotShader = pointSelectionShader;
+ if (!optimizationDefault && rangeGradientPoints) {
+ if (m_isOpenGLES)
+ dotShader = m_staticGradientPointShader;
+ else
+ dotShader = m_labelShader;
+ } else {
+ dotShader = pointSelectionShader;
+ }
} else {
if (colorStyleIsUniform)
dotShader = m_dotShader;
@@ -740,13 +911,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (!drawingPoints && !colorStyleIsUniform && previousMeshColorStyle != colorStyle) {
if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
- m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientMin(), 0.0f);
- m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(),
- 0.5f);
+ dotShader->setUniformValue(dotShader->gradientMin(), 0.0f);
+ dotShader->setUniformValue(dotShader->gradientHeight(),
+ 0.5f);
} else {
// Each dot is of uniform color according to its Y-coordinate
- m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(),
- 0.0f);
+ dotShader->setUniformValue(dotShader->gradientHeight(),
+ 0.0f);
}
}
@@ -760,6 +931,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
int loopCount = 1;
if (optimizationDefault)
loopCount = renderArraySize;
+
for (int i = 0; i < loopCount; i++) {
ScatterRenderItem &item = renderArray[i];
if (!item.isVisible() && optimizationDefault)
@@ -791,7 +963,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (rangeGradientPoints) {
// Drawing points with range gradient
// Get color from gradient based on items y position converted to percent
- int position = int(item.translation().y() * 50.0f) + 50;
+ int position = ((item.translation().y() + m_scaleY) * rangeGradientYScaler) * gradientImageHeight;
+ position = qMin(maxGradientPositition, position); // clamp to edge
dotColor = Utils::vectorFromColor(
cache->gradientImage().pixel(0, position));
} else {
@@ -801,6 +974,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
gradientTexture = cache->baseGradientTexture();
}
+ if (!optimizationDefault && rangeGradientPoints)
+ gradientTexture = cache->baseGradientTexture();
+
GLfloat lightStrength = m_cachedTheme->lightStrength();
if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) {
if (useColor)
@@ -808,13 +984,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
else
gradientTexture = cache->singleHighlightGradientTexture();
lightStrength = m_cachedTheme->highlightLightStrength();
- // Insert data to ScatterRenderItem
- // We don't have ownership, so don't delete the previous one
+ // Save the reference to the item to be used in label drawing
selectedItem = &item;
dotSelectionFound = true;
// Save selected item size (adjusted with font size) for selection label
// positioning
- selectedItemSize = itemSize + (m_cachedTheme->font().pointSizeF() / 500.0f);
+ selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
}
if (!drawingPoints) {
@@ -829,10 +1004,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
dotShader->setUniformValue(dotShader->color(), dotColor);
} else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
dotShader->setUniformValue(dotShader->gradientMin(),
- (item.translation().y() + 1.0f) / 2.0f);
+ (item.translation().y() + m_scaleY)
+ * rangeGradientYScaler);
}
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
if (!drawingPoints) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
@@ -853,11 +1028,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (optimizationDefault)
m_drawer->drawPoint(dotShader);
else
- m_drawer->drawPoints(dotShader, cache->bufferPoints());
+ m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture);
}
- } else
-#endif
- {
+ } else {
if (!drawingPoints) {
// Set shadowless shader bindings
dotShader->setUniformValue(dotShader->lightS(), lightStrength);
@@ -871,100 +1044,139 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (optimizationDefault)
m_drawer->drawPoint(dotShader);
else
- m_drawer->drawPoints(dotShader, cache->bufferPoints());
+ m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture);
}
}
}
+
// Draw the selected item on static optimization
if (!optimizationDefault && selectedSeries
&& m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) {
ScatterRenderItem &item = renderArray[m_selectedItemIndex];
- ObjectHelper *dotObj = cache->object();
+ if (item.isVisible()) {
+ ShaderHelper *selectionShader;
+ if (drawingPoints) {
+ selectionShader = pointSelectionShader;
+ } else {
+ if (colorStyleIsUniform)
+ selectionShader = m_staticSelectedItemShader;
+ else
+ selectionShader = m_staticSelectedItemGradientShader;
+ }
+ selectionShader->bind();
- QMatrix4x4 modelMatrix;
- QMatrix4x4 itModelMatrix;
+ ObjectHelper *dotObj = cache->object();
- modelMatrix.translate(item.translation());
- if (!drawingPoints) {
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
- QQuaternion totalRotation = seriesRotation * item.rotation();
- modelMatrix.rotate(totalRotation);
- itModelMatrix.rotate(totalRotation);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(item.translation());
+ if (!drawingPoints) {
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
+ QQuaternion totalRotation = seriesRotation * item.rotation();
+ modelMatrix.rotate(totalRotation);
+ itModelMatrix.rotate(totalRotation);
+ }
+ modelMatrix.scale(modelScaler);
+ itModelMatrix.scale(modelScaler);
+
+ selectionShader->setUniformValue(selectionShader->lightP(),
+ lightPos);
+ selectionShader->setUniformValue(selectionShader->view(),
+ viewMatrix);
+ selectionShader->setUniformValue(selectionShader->ambientS(),
+ m_cachedTheme->ambientLightStrength());
+ selectionShader->setUniformValue(selectionShader->lightColor(),
+ lightColor);
}
- modelMatrix.scale(modelScaler);
- itModelMatrix.scale(modelScaler);
- }
- QMatrix4x4 MVPMatrix;
+ QMatrix4x4 MVPMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- if (useColor)
- dotColor = cache->singleHighlightColor();
- else
- gradientTexture = cache->singleHighlightGradientTexture();
- GLfloat lightStrength = m_cachedTheme->highlightLightStrength();
- // Save the reference to the item to be used on label drawing
- selectedItem = &item;
- dotSelectionFound = true;
- // Save selected item size (adjusted with font size) for selection label
- // positioning
- selectedItemSize = itemSize + (m_cachedTheme->font().pointSizeF() / 500.0f);
-
- if (!drawingPoints) {
- // Set shader bindings
- dotShader->setUniformValue(dotShader->model(), modelMatrix);
- dotShader->setUniformValue(dotShader->nModel(),
- itModelMatrix.inverted().transposed());
- }
+ if (useColor)
+ dotColor = cache->singleHighlightColor();
+ else
+ gradientTexture = cache->singleHighlightGradientTexture();
+ GLfloat lightStrength = m_cachedTheme->highlightLightStrength();
+ // Save the reference to the item to be used in label drawing
+ selectedItem = &item;
+ dotSelectionFound = true;
+ // Save selected item size (adjusted with font size) for selection label
+ // positioning
+ selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
- dotShader->setUniformValue(dotShader->MVP(), MVPMatrix);
- if (useColor) {
- dotShader->setUniformValue(dotShader->color(), dotColor);
- } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
- dotShader->setUniformValue(dotShader->gradientMin(),
- (item.translation().y() + 1.0f) / 2.0f);
- }
+ if (!drawingPoints) {
+ // Set shader bindings
+ selectionShader->setUniformValue(selectionShader->model(), modelMatrix);
+ selectionShader->setUniformValue(selectionShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ if (!colorStyleIsUniform) {
+ if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
+ selectionShader->setUniformValue(selectionShader->gradientMin(),
+ 0.0f);
+ selectionShader->setUniformValue(selectionShader->gradientHeight(),
+ 0.5f);
+ } else {
+ // Each dot is of uniform color according to its Y-coordinate
+ selectionShader->setUniformValue(selectionShader->gradientHeight(),
+ 0.0f);
+ selectionShader->setUniformValue(selectionShader->gradientMin(),
+ (item.translation().y() + m_scaleY)
+ * rangeGradientYScaler);
+ }
+ }
+ }
- if (!drawingPoints) {
- glEnable(GL_POLYGON_OFFSET_FILL);
- glPolygonOffset(-1.0f, 1.0f);
- }
+ selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix);
+ if (useColor)
+ selectionShader->setUniformValue(selectionShader->color(), dotColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
if (!drawingPoints) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix);
- dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f);
-
- // Draw the object
- m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawPoint(dotShader);
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(-1.0f, 1.0f);
}
- } else
-#endif
- {
- if (!drawingPoints) {
- // Set shadowless shader bindings
- dotShader->setUniformValue(dotShader->lightS(), lightStrength);
- // Draw the object
- m_drawer->drawObject(dotShader, dotObj, gradientTexture);
+
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone
+ && !m_isOpenGLES) {
+ if (!drawingPoints) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ selectionShader->setUniformValue(selectionShader->shadowQ(),
+ m_shadowQualityToShader);
+ selectionShader->setUniformValue(selectionShader->depth(),
+ depthMVPMatrix);
+ selectionShader->setUniformValue(selectionShader->lightS(),
+ lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(selectionShader, dotObj, gradientTexture,
+ m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawPoint(selectionShader);
+ }
} else {
- // Draw the object
- m_drawer->drawPoint(dotShader);
+ if (!drawingPoints) {
+ // Set shadowless shader bindings
+ selectionShader->setUniformValue(selectionShader->lightS(),
+ lightStrength);
+ // Draw the object
+ m_drawer->drawObject(selectionShader, dotObj, gradientTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawPoint(selectionShader);
+ }
}
- }
- if (!drawingPoints)
- glDisable(GL_POLYGON_OFFSET_FILL);
+ if (!drawingPoints)
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ }
+ dotShader->bind();
}
}
}
@@ -987,25 +1199,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor
- + m_backgroundMargin;
- GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > xScale)
- xScale = m_maxItemSize;
- if (m_maxItemSize > zScale)
- zScale = m_maxItemSize;
- QVector3D bgScale(xScale, 1.0f + m_backgroundMargin, zScale);
-#else // ..and this if we want uniform scaling based on largest dimension
- QVector3D bgScale((m_graphAspectRatio + m_backgroundMargin),
- 1.0f + m_backgroundMargin,
- (m_graphAspectRatio + m_backgroundMargin));
-#endif
+ QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground,
+ m_scaleZWithBackground);
modelMatrix.scale(bgScale);
// If we're viewing from below, background object must be flipped
if (m_yFlipped) {
- modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0);
+ modelMatrix.rotate(m_xFlipRotation);
modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f);
} else {
modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
@@ -1031,8 +1230,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
m_cachedTheme->ambientLightStrength() * 2.0f);
m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
@@ -1043,9 +1241,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
// Draw the object
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
m_cachedTheme->lightStrength());
@@ -1055,16 +1251,17 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
}
}
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
// Draw grid lines
+ QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
+ QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
+ QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
+
if (m_cachedTheme->isGridEnabled()) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
// Bind line shader
lineShader->bind();
@@ -1076,15 +1273,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadowed shader bindings
lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 20.0f);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 2.5f);
@@ -1094,242 +1288,213 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QQuaternion lineXRotation;
if (m_xFlipped)
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f);
+ lineYRotation = m_yRightAngleRotationNeg;
else
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
+ lineYRotation = m_yRightAngleRotation;
- if (m_yFlipped)
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f);
+ if (m_yFlippedForGrid)
+ lineXRotation = m_xRightAngleRotation;
else
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f);
+ lineXRotation = m_xRightAngleRotationNeg;
- GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset;
- if (m_yFlipped)
+ GLfloat yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset;
+ if (m_yFlippedForGrid)
yFloorLinePosition = -yFloorLinePosition;
// Rows (= Z)
if (m_axisCacheZ.segmentCount() > 0) {
// Floor lines
int gridLineCount = m_axisCacheZ.gridLineCount();
+ if (m_polarGraph) {
+ drawRadialGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > xScale)
- xScale = m_maxItemSize;
- QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth);
-#else // ..and this if we want uniform scaling based on largest dimension
- QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin),
- gridLineWidth, gridLineWidth);
-#endif
+ modelMatrix.translate(0.0f, yFloorLinePosition,
+ m_axisCacheZ.gridLinePosition(line));
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(0.0f, yFloorLinePosition,
- m_axisCacheZ.gridLinePosition(line));
+ modelMatrix.scale(gridLineScaleX);
+ itModelMatrix.scale(gridLineScaleX);
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- // Set shadow shader bindings
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
+ } else {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
- // Side wall lines
- gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth);
-#ifndef USE_UNIFORM_SCALING
- GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineXTrans)
- lineXTrans = m_maxItemSize - gridLineOffset;
-#else
- GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
-#endif
- if (!m_xFlipped)
- lineXTrans = -lineXTrans;
+ // Side wall lines
+ GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
+ if (!m_xFlipped)
+ lineXTrans = -lineXTrans;
- modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
-#if !defined(QT_OPENGL_ES_2)
- modelMatrix.rotate(lineYRotation);
- itModelMatrix.rotate(lineYRotation);
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else {
+ modelMatrix.rotate(lineYRotation);
+ itModelMatrix.rotate(lineYRotation);
+ }
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
// Columns (= X)
if (m_axisCacheX.segmentCount() > 0) {
-#if defined(QT_OPENGL_ES_2)
- lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
-#endif
+ if (m_isOpenGLES)
+ lineXRotation = m_yRightAngleRotation;
// Floor lines
int gridLineCount = m_axisCacheX.gridLineCount();
-#ifndef USE_UNIFORM_SCALING
- GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > zScale)
- zScale = m_maxItemSize;
- QVector3D gridLineScaler(gridLineWidth, gridLineWidth, zScale);
-#else
- QVector3D gridLineScaler(gridLineWidth, gridLineWidth,
- m_graphAspectRatio + m_backgroundMargin);
-#endif
-
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
- 0.0f);
+ if (m_polarGraph) {
+ drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
+ 0.0f);
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
+ modelMatrix.scale(gridLineScaleZ);
+ itModelMatrix.scale(gridLineScaleZ);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
-
- // Back wall lines
-#ifndef USE_UNIFORM_SCALING
- GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineZTrans)
- lineZTrans = m_maxItemSize - gridLineOffset;
-#else
- GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
-#endif
- if (!m_zFlipped)
- lineZTrans = -lineZTrans;
- gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth);
+ // Back wall lines
+ GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
+ if (!m_zFlipped)
+ lineZTrans = -lineZTrans;
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
-#if !defined(QT_OPENGL_ES_2)
- if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- }
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else {
+ if (m_zFlipped) {
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
+ }
+ }
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
@@ -1338,21 +1503,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
// Back wall
int gridLineCount = m_axisCacheY.gridLineCount();
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineZTrans)
- lineZTrans = m_maxItemSize - gridLineOffset;
- GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > xScale)
- xScale = m_maxItemSize;
- QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth);
-#else // ..and this if we want uniform scaling based on largest dimension
- GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
- QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin),
- gridLineWidth, gridLineWidth);
-#endif
+ GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
+
if (!m_zFlipped)
lineZTrans = -lineZTrans;
@@ -1363,12 +1515,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans);
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.scale(gridLineScaleX);
+ itModelMatrix.scale(gridLineScaleX);
if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
}
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1379,38 +1531,25 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Side wall
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineXTrans)
- lineXTrans = m_maxItemSize - gridLineOffset;
- GLfloat zScale = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor + m_backgroundMargin;
- if (m_maxItemSize > zScale)
- zScale = m_maxItemSize;
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, zScale);
-#else // ..and this if we want uniform scaling based on largest dimension
- GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth,
- m_graphAspectRatio + m_backgroundMargin);
-#endif
+ GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
+
if (!m_xFlipped)
lineXTrans = -lineXTrans;
@@ -1421,8 +1560,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f);
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.scale(gridLineScaleZ);
+ itModelMatrix.scale(gridLineScaleZ);
modelMatrix.rotate(lineYRotation);
itModelMatrix.rotate(lineYRotation);
@@ -1435,20 +1574,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
}
@@ -1489,7 +1628,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
glEnable(GL_DEPTH_TEST);
}
- glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
// Release shader
@@ -1512,7 +1650,6 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
shader = m_labelShader;
shader->bind();
- glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -1532,15 +1669,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
// Z Labels
if (m_axisCacheZ.segmentCount() > 0) {
int labelCount = m_axisCacheZ.labelCount();
-#ifndef USE_UNIFORM_SCALING
- GLfloat labelXTrans = (m_graphAspectRatio * m_areaSize.width())
- / m_scaleFactor + labelMargin + m_backgroundMargin;
- if (m_maxItemSize > labelXTrans)
- labelXTrans = m_maxItemSize + labelMargin;
-#else
- GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin;
-#endif
- GLfloat labelYTrans = -1.0f - m_backgroundMargin;
+ float labelXTrans = m_scaleXWithBackground + labelMargin;
+ float labelYTrans = -m_scaleYWithBackground;
+ if (m_polarGraph) {
+ labelXTrans *= m_radialLabelOffset;
+ // YTrans up only if over background
+ if (m_radialLabelOffset < 1.0f)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ }
Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_xFlipped)
@@ -1550,7 +1686,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
if (labelAutoAngle == 0.0f) {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped)
labelRotation.setY(180.0f);
else
@@ -1562,7 +1698,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
} else {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
@@ -1621,13 +1757,35 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
- labelTrans.setZ(m_axisCacheZ.labelPosition(label));
-
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
-
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
// Draw the label here
+ if (m_polarGraph) {
+ float direction = m_zFlipped ? -1.0f : 1.0f;
+ labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label)
+ * -m_polarRadius
+ + m_drawer->scaledFontSize() + gridLineWidth) * direction);
+ } else {
+ labelTrans.setZ(m_axisCacheZ.labelPosition(label));
+ }
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.z())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleZWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setZ(labelTrans.z() - labelOverlap);
+ else
+ labelTrans.setZ(labelTrans.z() + labelOverlap);
+ }
+ }
m_dummyRenderItem.setTranslation(labelTrans);
- const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
if (drawSelection) {
QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
@@ -1642,7 +1800,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
}
if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
- labelTrans.setZ(0.0f);
+ if (m_polarGraph) {
+ float titleZ = -m_polarRadius / 2.0f;
+ if (m_zFlipped)
+ titleZ = -titleZ;
+ labelTrans.setZ(titleZ);
+ } else {
+ labelTrans.setZ(0.0f);
+ }
drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
@@ -1656,15 +1821,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheX.labelCount();
-#ifndef USE_UNIFORM_SCALING
- GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor + labelMargin + m_backgroundMargin;
- if (m_maxItemSize > labelZTrans)
- labelZTrans = m_maxItemSize + labelMargin;
-#else
- GLfloat labelZTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin;
-#endif
- GLfloat labelYTrans = -1.0f - m_backgroundMargin;
+ float labelZTrans = 0.0f;
+ float labelYTrans = -m_scaleYWithBackground;
+ if (m_polarGraph)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ else
+ labelZTrans = m_scaleZWithBackground + labelMargin;
+
Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_zFlipped)
@@ -1675,7 +1838,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
if (m_xFlipped)
labelRotation.setY(-90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_xFlipped)
labelRotation.setY(-90.0f);
else
@@ -1687,7 +1850,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation.setY(-90.0f);
else
labelRotation.setY(90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
@@ -1735,6 +1898,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
+ if (m_polarGraph) {
+ if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped))
+ || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) {
+ totalRotation *= m_zRightAngleRotation;
+ } else {
+ totalRotation *= m_zRightAngleRotationNeg;
+ }
+ }
QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans);
if (m_xFlipped) {
startIndex = labelCount - 1;
@@ -1746,14 +1917,64 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
indexStep = 1;
}
float offsetValue = 0.0f;
- for (int label = startIndex; label != endIndex; label = label + indexStep) {
- labelTrans.setX(m_axisCacheX.labelPosition(label));
+ bool showLastLabel = false;
+ QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
+ int lastLabelPosIndex = labelPositions.size() - 1;
+ if (labelPositions.size()
+ && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) {
+ // Avoid overlapping first and last label if they would get on same position
+ showLastLabel = true;
+ }
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
-
// Draw the label here
- m_dummyRenderItem.setTranslation(labelTrans);
+ if (m_polarGraph) {
+ // Calculate angular position
+ if (label == lastLabelPosIndex && !showLastLabel)
+ continue;
+ float labelPosition = labelPositions.at(label);
+ qreal angle = labelPosition * M_PI * 2.0;
+ labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle)));
+ labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(angle)));
+ // Alignment depends on label angular position, as well as flips
+ Qt::AlignmentFlag vAlignment = Qt::AlignCenter;
+ Qt::AlignmentFlag hAlignment = Qt::AlignCenter;
+ const float centerMargin = 0.005f;
+ if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom;
+ else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop;
+
+ if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
+ else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight;
+ if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter)
+ vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop;
+ alignment = Qt::AlignmentFlag(vAlignment | hAlignment);
+ } else {
+ labelTrans.setX(m_axisCacheX.labelPosition(label));
+ }
const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label);
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.x())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleXWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setX(labelTrans.x() + labelOverlap);
+ else
+ labelTrans.setX(labelTrans.x() - labelOverlap);
+ }
+ }
+ m_dummyRenderItem.setTranslation(labelTrans);
if (drawSelection) {
QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
@@ -1769,8 +1990,20 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
if (!drawSelection && m_axisCacheX.isTitleVisible()) {
labelTrans.setX(0.0f);
+ bool radial = false;
+ if (m_polarGraph) {
+ if (m_xFlipped == m_zFlipped)
+ totalRotation *= m_zRightAngleRotation;
+ else
+ totalRotation *= m_zRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f);
+ labelTrans.setZ(-m_polarRadius);
+ radial = true;
+ }
drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
- activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader,
+ radial);
}
}
@@ -1782,22 +2015,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheY.labelCount();
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat labelXTrans = (m_graphAspectRatio* m_areaSize.width())
- / m_scaleFactor + m_backgroundMargin;
- GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor + m_backgroundMargin;
- if (m_maxItemSize > labelXTrans)
- labelXTrans = m_maxItemSize;
- if (m_maxItemSize > labelZTrans)
- labelZTrans = m_maxItemSize;
-#else // ..and this if we want uniform scaling based on largest dimension
- GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin;
- GLfloat labelZTrans = labelXTrans;
-#endif
+
+ float labelXTrans = m_scaleXWithBackground;
+ float labelZTrans = m_scaleZWithBackground;
+
// Back & side wall
- GLfloat labelMarginXTrans = labelMargin;
- GLfloat labelMarginZTrans = labelMargin;
+ float labelMarginXTrans = labelMargin;
+ float labelMarginZTrans = labelMargin;
QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
Qt::AlignmentFlag backAlignment =
@@ -1854,7 +2078,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label);
- const GLfloat labelYTrans = m_axisCacheY.labelPosition(label);
+ float labelYTrans = m_axisCacheY.labelPosition(label);
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
@@ -1864,6 +2088,21 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
shader->setUniformValue(shader->color(), labelColor);
}
+ if (label == startIndex) {
+ // If the margin is small, adjust the position of the edge label to avoid
+ // overlapping with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelYTrans)
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleYWithBackground + labelMargin;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelYTrans += labelOverlap;
+ else
+ labelYTrans -= labelOverlap;
+ }
+ }
+
// Back wall
labelTransBack.setY(labelYTrans);
m_dummyRenderItem.setTranslation(labelTransBack);
@@ -1958,10 +2197,8 @@ void Scatter3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual
handleShadowQualityChange();
-#if !defined(QT_OPENGL_ES_2)
// Re-init depth buffer
updateDepthBuffer();
-#endif
}
void Scatter3DRenderer::loadBackgroundMesh()
@@ -1972,8 +2209,13 @@ void Scatter3DRenderer::loadBackgroundMesh()
void Scatter3DRenderer::updateTextures()
{
+ Abstract3DRenderer::updateTextures();
+
// Drawer has changed; this flag needs to be checked when checking if we need to update labels
m_updateLabels = true;
+
+ if (m_polarGraph)
+ calculateSceneScalingFactors();
}
void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
@@ -1991,35 +2233,83 @@ void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item)
{
// We need to normalize translations
const QVector3D &pos = item.position();
- float xTrans = m_axisCacheX.positionAt(pos.x());
+ float xTrans;
float yTrans = m_axisCacheY.positionAt(pos.y());
- float zTrans = m_axisCacheZ.positionAt(pos.z());
+ float zTrans;
+ if (m_polarGraph) {
+ calculatePolarXZ(pos, xTrans, zTrans);
+ } else {
+ xTrans = m_axisCacheX.positionAt(pos.x());
+ zTrans = m_axisCacheZ.positionAt(pos.z());
+ }
item.setTranslation(QVector3D(xTrans, yTrans, zTrans));
}
void Scatter3DRenderer::calculateSceneScalingFactors()
{
- m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f;
- m_areaSize.setHeight((m_axisCacheZ.max() - m_axisCacheZ.min()) / 2.0f);
- m_areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min()) / 2.0f);
- m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height());
-
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor;
- m_axisCacheX.setScale(factorScaler * m_areaSize.width());
- m_axisCacheZ.setScale(-factorScaler * m_areaSize.height());
-#else // ..and this if we want uniform scaling based on largest dimension
- m_axisCacheX.setScale(2.0f * m_graphAspectRatio);
- m_axisCacheZ.setScale(-m_axisCacheX.scale());
-#endif
- m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f);
- m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f);
+ if (m_requestedMargin < 0.0f) {
+ if (m_maxItemSize > defaultMaxSize)
+ m_hBackgroundMargin = m_maxItemSize / itemScaler;
+ else
+ m_hBackgroundMargin = defaultMaxSize;
+ m_vBackgroundMargin = m_hBackgroundMargin;
+ } else {
+ m_hBackgroundMargin = m_requestedMargin;
+ m_vBackgroundMargin = m_requestedMargin;
+ }
+ if (m_polarGraph) {
+ float polarMargin = calculatePolarBackgroundMargin();
+ m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin);
+ }
+
+ float horizontalAspectRatio;
+ if (m_polarGraph)
+ horizontalAspectRatio = 1.0f;
+ else
+ horizontalAspectRatio = m_graphHorizontalAspectRatio;
+
+ QSizeF areaSize;
+ if (horizontalAspectRatio == 0.0f) {
+ areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
+ areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
+ } else {
+ areaSize.setHeight(1.0f);
+ areaSize.setWidth(horizontalAspectRatio);
+ }
+
+ float horizontalMaxDimension;
+ if (m_graphAspectRatio > 2.0f) {
+ horizontalMaxDimension = 2.0f;
+ m_scaleY = 2.0f / m_graphAspectRatio;
+ } else {
+ horizontalMaxDimension = m_graphAspectRatio;
+ m_scaleY = 1.0f;
+ }
+ if (m_polarGraph)
+ m_polarRadius = horizontalMaxDimension;
+
+ float scaleFactor = qMax(areaSize.width(), areaSize.height());
+ m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
+ m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
+
+ m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin;
+ m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin;
+ m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin;
+
+ m_axisCacheX.setScale(m_scaleX * 2.0f);
+ m_axisCacheY.setScale(m_scaleY * 2.0f);
+ m_axisCacheZ.setScale(-m_scaleZ * 2.0f);
+ m_axisCacheX.setTranslate(-m_scaleX);
+ m_axisCacheY.setTranslate(-m_scaleY);
+ m_axisCacheZ.setTranslate(m_scaleZ);
+
+ updateCameraViewport();
+ updateCustomItemPositions();
}
void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
{
- if (m_dotShader)
- delete m_dotShader;
+ delete m_dotShader;
m_dotShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_dotShader->initialize();
}
@@ -2027,16 +2317,30 @@ void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &
void Scatter3DRenderer::initGradientShaders(const QString &vertexShader,
const QString &fragmentShader)
{
- if (m_dotGradientShader)
- delete m_dotGradientShader;
+ delete m_dotGradientShader;
m_dotGradientShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_dotGradientShader->initialize();
+
+}
+
+void Scatter3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader)
+{
+ delete m_staticSelectedItemShader;
+ m_staticSelectedItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_staticSelectedItemShader->initialize();
+
+ delete m_staticSelectedItemGradientShader;
+ m_staticSelectedItemGradientShader = new ShaderHelper(this, gradientVertexShader,
+ gradientFragmentShader);
+ m_staticSelectedItemGradientShader->initialize();
}
void Scatter3DRenderer::initSelectionShader()
{
- if (m_selectionShader)
- delete m_selectionShader;
+ delete m_selectionShader;
m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
QStringLiteral(":/shaders/fragmentPlainColor"));
m_selectionShader->initialize();
@@ -2054,41 +2358,45 @@ void Scatter3DRenderer::initSelectionBuffer()
m_selectionDepthBuffer);
}
-#if !defined(QT_OPENGL_ES_2)
void Scatter3DRenderer::initDepthShader()
{
- if (m_depthShader)
- delete m_depthShader;
- m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
- QStringLiteral(":/shaders/fragmentDepth"));
- m_depthShader->initialize();
+ if (!m_isOpenGLES) {
+ if (m_depthShader)
+ delete m_depthShader;
+ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
+ QStringLiteral(":/shaders/fragmentDepth"));
+ m_depthShader->initialize();
+ }
}
void Scatter3DRenderer::updateDepthBuffer()
{
- m_textureHelper->deleteTexture(&m_depthTexture);
+ if (!m_isOpenGLES) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
- if (m_primarySubViewport.size().isEmpty())
- return;
+ if (m_primarySubViewport.size().isEmpty())
+ return;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
- m_depthFrameBuffer,
- m_shadowQualityMultiplier);
- if (!m_depthTexture)
- lowerShadowQuality();
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
+ m_depthFrameBuffer,
+ m_shadowQualityMultiplier);
+ if (!m_depthTexture)
+ lowerShadowQuality();
+ }
}
}
-#else
+
void Scatter3DRenderer::initPointShader()
{
- if (m_pointShader)
- delete m_pointShader;
- m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"),
- QStringLiteral(":/shaders/fragmentPlainColor"));
- m_pointShader->initialize();
+ if (m_isOpenGLES) {
+ if (m_pointShader)
+ delete m_pointShader;
+ m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"),
+ QStringLiteral(":/shaders/fragmentPlainColor"));
+ m_pointShader->initialize();
+ }
}
-#endif
void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader)
@@ -2099,12 +2407,13 @@ void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader,
m_backgroundShader->initialize();
}
-void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
+void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader,
+ const QString &fragmentShader)
{
- if (m_labelShader)
- delete m_labelShader;
- m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
- m_labelShader->initialize();
+ if (m_staticGradientPointShader)
+ delete m_staticGradientPointShader;
+ m_staticGradientPointShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_staticGradientPointShader->initialize();
}
void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color,
@@ -2190,13 +2499,17 @@ QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &posit
float yTrans = 0.0f;
float zTrans = 0.0f;
if (!isAbsolute) {
- xTrans = m_axisCacheX.positionAt(position.x());
+ if (m_polarGraph) {
+ calculatePolarXZ(position, xTrans, zTrans);
+ } else {
+ xTrans = m_axisCacheX.positionAt(position.x());
+ zTrans = m_axisCacheZ.positionAt(position.z());
+ }
yTrans = m_axisCacheY.positionAt(position.y());
- zTrans = m_axisCacheZ.positionAt(position.z());
} else {
- xTrans = position.x() * m_axisCacheX.scale() / 2.0f;
- yTrans = position.y();
- zTrans = position.z() * m_axisCacheZ.scale() / 2.0f;
+ xTrans = position.x() * m_scaleX;
+ yTrans = position.y() * m_scaleY;
+ zTrans = position.z() * -m_scaleZ;
}
return QVector3D(xTrans, yTrans, zTrans);
}
diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h
index 7f213179..b45b31a2 100644
--- a/src/datavisualization/engine/scatter3drenderer_p.h
+++ b/src/datavisualization/engine/scatter3drenderer_p.h
@@ -53,31 +53,28 @@ private:
bool m_updateLabels;
ShaderHelper *m_dotShader;
ShaderHelper *m_dotGradientShader;
-#if defined(QT_OPENGL_ES_2)
+ ShaderHelper *m_staticSelectedItemGradientShader;
+ ShaderHelper *m_staticSelectedItemShader;
ShaderHelper *m_pointShader;
-#endif
ShaderHelper *m_depthShader;
ShaderHelper *m_selectionShader;
ShaderHelper *m_backgroundShader;
- ShaderHelper *m_labelShader;
+ ShaderHelper *m_staticGradientPointShader;
GLuint m_bgrTexture;
- GLuint m_depthTexture;
GLuint m_selectionTexture;
GLuint m_depthFrameBuffer;
GLuint m_selectionFrameBuffer;
GLuint m_selectionDepthBuffer;
GLfloat m_shadowQualityToShader;
GLint m_shadowQualityMultiplier;
- GLfloat m_heightNormalizer;
- GLfloat m_scaleFactor;
+ float m_scaleX;
+ float m_scaleY;
+ float m_scaleZ;
int m_selectedItemIndex;
ScatterSeriesRenderCache *m_selectedSeriesCache;
ScatterSeriesRenderCache *m_oldSelectedSeriesCache;
- QSizeF m_areaSize;
GLfloat m_dotSizeScale;
- bool m_hasHeightAdjustmentChanged;
ScatterRenderItem m_dummyRenderItem;
- GLfloat m_backgroundMargin;
GLfloat m_maxItemSize;
int m_clickedIndex;
bool m_havePointSeries;
@@ -94,6 +91,12 @@ public:
SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
void updateItems(const QVector<Scatter3DController::ChangeItem> &items);
void updateScene(Q3DScene *scene);
+ void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels);
+ void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible);
+ void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint);
+ void updateMargin(float margin);
QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
@@ -107,10 +110,16 @@ public slots:
protected:
virtual void initializeOpenGL();
+ virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
private:
virtual void initShaders(const QString &vertexShader, const QString &fragmentShader);
virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader);
+ virtual void initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader);
virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality);
virtual void updateTextures();
virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh);
@@ -122,14 +131,11 @@ private:
void loadBackgroundMesh();
void initSelectionShader();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
- void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
+ void initStaticPointShaders(const QString &vertexShader, const QString &fragmentShader);
void initSelectionBuffer();
-#if !defined(QT_OPENGL_ES_2)
void initDepthShader();
void updateDepthBuffer();
-#else
void initPointShader();
-#endif
void calculateTranslation(ScatterRenderItem &item);
void calculateSceneScalingFactors();
diff --git a/src/datavisualization/engine/scatterseriesrendercache.cpp b/src/datavisualization/engine/scatterseriesrendercache.cpp
index e8888d19..4930dde1 100644
--- a/src/datavisualization/engine/scatterseriesrendercache.cpp
+++ b/src/datavisualization/engine/scatterseriesrendercache.cpp
@@ -27,10 +27,12 @@ ScatterSeriesRenderCache::ScatterSeriesRenderCache(QAbstract3DSeries *series,
: SeriesRenderCache(series, renderer),
m_itemSize(0.0f),
m_selectionIndexOffset(0),
+ m_staticBufferDirty(false),
m_oldRenderArraySize(0),
m_oldMeshFileName(QString()),
m_scatterBufferObj(0),
- m_scatterBufferPoints(0)
+ m_scatterBufferPoints(0),
+ m_visibilityChanged(false)
{
}
diff --git a/src/datavisualization/engine/scatterseriesrendercache_p.h b/src/datavisualization/engine/scatterseriesrendercache_p.h
index 490e21fb..9c6e8e8f 100644
--- a/src/datavisualization/engine/scatterseriesrendercache_p.h
+++ b/src/datavisualization/engine/scatterseriesrendercache_p.h
@@ -53,6 +53,8 @@ public:
inline float itemSize() const { return m_itemSize; }
inline void setSelectionIndexOffset(int offset) { m_selectionIndexOffset = offset; }
inline int selectionIndexOffset() const { return m_selectionIndexOffset; }
+ inline void setStaticBufferDirty(bool state) { m_staticBufferDirty = state; }
+ inline bool staticBufferDirty() const { return m_staticBufferDirty; }
inline int oldArraySize() const { return m_oldRenderArraySize; }
inline void setOldArraySize(int size) { m_oldRenderArraySize = size; }
inline const QString &oldMeshFileName() const { return m_oldMeshFileName; }
@@ -61,15 +63,23 @@ public:
inline ScatterObjectBufferHelper *bufferObject() const { return m_scatterBufferObj; }
inline void setBufferPoints(ScatterPointBufferHelper *object) { m_scatterBufferPoints = object; }
inline ScatterPointBufferHelper *bufferPoints() const { return m_scatterBufferPoints; }
+ inline QVector<int> &updateIndices() { return m_updateIndices; }
+ inline QVector<int> &bufferIndices() { return m_bufferIndices; }
+ inline void setVisibilityChanged(bool changed) { m_visibilityChanged = changed; }
+ inline bool visibilityChanged() const { return m_visibilityChanged; }
protected:
ScatterRenderItemArray m_renderArray;
float m_itemSize;
int m_selectionIndexOffset; // Temporarily cached value for selection color calculations
+ bool m_staticBufferDirty;
int m_oldRenderArraySize; // Used to detect if full buffer change needed
QString m_oldMeshFileName; // Used to detect if full buffer change needed
ScatterObjectBufferHelper *m_scatterBufferObj;
ScatterPointBufferHelper *m_scatterBufferPoints;
+ QVector<int> m_updateIndices; // Used as temporary cache during item updates
+ QVector<int> m_bufferIndices; // Cache for mapping renderarray to mesh buffer
+ bool m_visibilityChanged; // Used to detect if full buffer change needed
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp
index 183d3f8e..b57ec511 100644
--- a/src/datavisualization/engine/selectionpointer.cpp
+++ b/src/datavisualization/engine/selectionpointer.cpp
@@ -122,9 +122,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho)
MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
- // Enable texturing
- glEnable(GL_TEXTURE_2D);
-
QVector3D lightPos = m_cachedScene->activeLight()->position();
//
@@ -186,9 +183,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho)
// Release shader
glUseProgram(0);
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
// Disable transparency
glDisable(GL_BLEND);
@@ -217,10 +211,12 @@ void SelectionPointer::setRotation(const QQuaternion &rotation)
m_rotation = rotation;
}
-void SelectionPointer::setLabel(const QString &label)
+void SelectionPointer::setLabel(const QString &label, bool themeChange)
{
- m_label = label;
- m_drawer->generateLabelItem(m_labelItem, m_label);
+ if (themeChange || m_label != label) {
+ m_label = label;
+ m_drawer->generateLabelItem(m_labelItem, m_label);
+ }
}
void SelectionPointer::setPointerObject(ObjectHelper *object)
@@ -236,7 +232,7 @@ void SelectionPointer::setLabelObject(ObjectHelper *object)
void SelectionPointer::handleDrawerChange()
{
m_cachedTheme = m_drawer->theme();
- setLabel(m_label);
+ setLabel(m_label, true);
}
void SelectionPointer::updateBoundingRect(const QRect &rect)
@@ -256,15 +252,16 @@ void SelectionPointer::initShaders()
// The shader for the small point ball
if (m_pointShader)
delete m_pointShader;
-#if !defined(QT_OPENGL_ES_2)
- m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragment"));
-#else
- m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentES2"));
-#endif
- m_pointShader->initialize();
+ if (Utils::isOpenGLES()) {
+ m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ } else {
+ m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+
+ m_pointShader->initialize();
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/selectionpointer_p.h b/src/datavisualization/engine/selectionpointer_p.h
index 7dc28024..08382b9f 100644
--- a/src/datavisualization/engine/selectionpointer_p.h
+++ b/src/datavisualization/engine/selectionpointer_p.h
@@ -49,7 +49,7 @@ public:
void render(GLuint defaultFboHandle = 0, bool useOrtho = false);
void setPosition(const QVector3D &position);
- void setLabel(const QString &label);
+ void setLabel(const QString &label, bool themeChange = false);
void setPointerObject(ObjectHelper *object);
void setLabelObject(ObjectHelper *object);
void handleDrawerChange();
diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp
index dc4b9db3..77af31c7 100644
--- a/src/datavisualization/engine/seriesrendercache.cpp
+++ b/src/datavisualization/engine/seriesrendercache.cpp
@@ -37,7 +37,8 @@ SeriesRenderCache::SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRender
m_valid(false),
m_visible(false),
m_renderer(renderer),
- m_objectDirty(true)
+ m_objectDirty(true),
+ m_staticObjectUVDirty(false)
{
}
@@ -91,9 +92,8 @@ void SeriesRenderCache::populate(bool newSeries)
meshFileName = QStringLiteral(":/defaultMeshes/arrow");
break;
case QAbstract3DSeries::MeshPoint:
-#if defined(QT_OPENGL_ES_2)
- qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2");
-#endif
+ if (Utils::isOpenGLES())
+ qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2");
break;
default:
// Default to cube
diff --git a/src/datavisualization/engine/seriesrendercache_p.h b/src/datavisualization/engine/seriesrendercache_p.h
index 96b61b87..5047d671 100644
--- a/src/datavisualization/engine/seriesrendercache_p.h
+++ b/src/datavisualization/engine/seriesrendercache_p.h
@@ -71,6 +71,8 @@ public:
inline bool isVisible() const { return m_visible; }
inline void setDataDirty(bool state) { m_objectDirty = state; }
inline bool dataDirty() const { return m_objectDirty; }
+ inline void setStaticObjectUVDirty(bool state) { m_staticObjectUVDirty = state; }
+ inline bool staticObjectUVDirty() { return m_staticObjectUVDirty; }
protected:
QAbstract3DSeries *m_series;
@@ -94,6 +96,7 @@ protected:
bool m_visible;
Abstract3DRenderer *m_renderer;
bool m_objectDirty;
+ bool m_staticObjectUVDirty;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/shaders/3dsliceframes.frag b/src/datavisualization/engine/shaders/3dsliceframes.frag
new file mode 100644
index 00000000..44c080dc
--- /dev/null
+++ b/src/datavisualization/engine/shaders/3dsliceframes.frag
@@ -0,0 +1,15 @@
+#version 120
+
+uniform highp vec4 color_mdl;
+uniform highp vec2 sliceFrameWidth;
+
+varying highp vec3 pos;
+
+void main() {
+ highp vec2 absPos = min(vec2(1.0, 1.0), abs(pos.xy));
+ if (absPos.x > sliceFrameWidth.x || absPos.y > sliceFrameWidth.y)
+ gl_FragColor = color_mdl;
+ else
+ discard;
+}
+
diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag
index d16055a3..0276ed95 100644
--- a/src/datavisualization/engine/shaders/default.frag
+++ b/src/datavisualization/engine/shaders/default.frag
@@ -32,6 +32,7 @@ void main() {
materialAmbientColor +
materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance +
materialSpecularColor * lightStrength * pow(cosAlpha, 5) / distance;
- gl_FragColor.a = 1.0;
+ gl_FragColor.a = color_mdl.a;
+ gl_FragColor = clamp(gl_FragColor, 0.0, 1.0);
}
diff --git a/src/datavisualization/engine/shaders/defaultNoMatrices.vert b/src/datavisualization/engine/shaders/defaultNoMatrices.vert
new file mode 100644
index 00000000..cef10c19
--- /dev/null
+++ b/src/datavisualization/engine/shaders/defaultNoMatrices.vert
@@ -0,0 +1,26 @@
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+attribute highp vec3 vertexNormal_mdl;
+
+uniform highp mat4 MVP;
+uniform highp mat4 V;
+uniform highp vec3 lightPosition_wrld;
+
+varying highp vec3 lightPosition_wrld_frag;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec2 coords_mdl;
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ coords_mdl = vertexPosition_mdl.xy;
+ position_wrld = vertexPosition_mdl;
+ vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz;
+ eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr;
+ vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
+ lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
+ normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz;
+ lightPosition_wrld_frag = lightPosition_wrld;
+}
diff --git a/src/datavisualization/engine/shaders/default_ES2.frag b/src/datavisualization/engine/shaders/default_ES2.frag
index 73d66d5b..60fa3c43 100644
--- a/src/datavisualization/engine/shaders/default_ES2.frag
+++ b/src/datavisualization/engine/shaders/default_ES2.frag
@@ -34,6 +34,6 @@ void main() {
materialAmbientColor +
materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance +
materialSpecularColor * lightStrength * (cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha) / distance;
- gl_FragColor.a = 1.0;
+ gl_FragColor.a = color_mdl.a;
}
diff --git a/src/datavisualization/engine/shaders/point_ES2_UV.vert b/src/datavisualization/engine/shaders/point_ES2_UV.vert
new file mode 100644
index 00000000..f181db4f
--- /dev/null
+++ b/src/datavisualization/engine/shaders/point_ES2_UV.vert
@@ -0,0 +1,12 @@
+uniform highp mat4 MVP;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+
+varying highp vec2 UV;
+
+void main() {
+ gl_PointSize = 5.0;
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ UV = vertexUV;
+}
diff --git a/src/datavisualization/engine/shaders/position.vert b/src/datavisualization/engine/shaders/position.vert
new file mode 100644
index 00000000..34849eae
--- /dev/null
+++ b/src/datavisualization/engine/shaders/position.vert
@@ -0,0 +1,10 @@
+uniform highp mat4 MVP;
+
+attribute highp vec3 vertexPosition_mdl;
+
+varying highp vec3 pos;
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ pos = vertexPosition_mdl;
+}
diff --git a/src/datavisualization/engine/shaders/positionmap.frag b/src/datavisualization/engine/shaders/positionmap.frag
new file mode 100644
index 00000000..9a277ab8
--- /dev/null
+++ b/src/datavisualization/engine/shaders/positionmap.frag
@@ -0,0 +1,11 @@
+varying highp vec3 pos;
+
+void main() {
+ // This shader encodes the axis position into the vertex color, assuming the object
+ // is a cube filling the entire data area of the graph
+ gl_FragColor = vec4((pos.x + 1.0) / 2.0,
+ (pos.y + 1.0) / 2.0,
+ (-pos.z + 1.0) / 2.0,
+ 0.0);
+}
+
diff --git a/src/datavisualization/engine/shaders/shadowNoMatrices.vert b/src/datavisualization/engine/shaders/shadowNoMatrices.vert
new file mode 100644
index 00000000..31748503
--- /dev/null
+++ b/src/datavisualization/engine/shaders/shadowNoMatrices.vert
@@ -0,0 +1,36 @@
+#version 120
+
+uniform highp mat4 MVP;
+uniform highp mat4 V;
+uniform highp mat4 M;
+uniform highp mat4 depthMVP;
+uniform highp vec3 lightPosition_wrld;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec3 vertexNormal_mdl;
+attribute highp vec2 vertexUV;
+
+varying highp vec2 UV;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec4 shadowCoord;
+varying highp vec2 coords_mdl;
+
+const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0,
+ 0.0, 0.5, 0.0, 0.0,
+ 0.0, 0.0, 0.5, 0.0,
+ 0.5, 0.5, 0.5, 1.0);
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ coords_mdl = vertexPosition_mdl.xy;
+ shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0);
+ position_wrld = vertexPosition_mdl;
+ vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz;
+ eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr;
+ lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz;
+ normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz;
+ UV = vertexUV;
+}
diff --git a/src/datavisualization/engine/shaders/shadowNoTex.frag b/src/datavisualization/engine/shaders/shadowNoTex.frag
index b2e7adfc..84e2f209 100644
--- a/src/datavisualization/engine/shaders/shadowNoTex.frag
+++ b/src/datavisualization/engine/shaders/shadowNoTex.frag
@@ -61,6 +61,6 @@ void main() {
(materialAmbientColor +
materialDiffuseColor * lightStrength * cosTheta +
materialSpecularColor * lightStrength * pow(cosAlpha, 10));
- gl_FragColor.a = 1.0;
+ gl_FragColor.a = color_mdl.a;
gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0);
}
diff --git a/src/datavisualization/engine/shaders/surface.frag b/src/datavisualization/engine/shaders/surface.frag
index f17dd73e..238e5fed 100644
--- a/src/datavisualization/engine/shaders/surface.frag
+++ b/src/datavisualization/engine/shaders/surface.frag
@@ -11,9 +11,11 @@ uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
diff --git a/src/datavisualization/engine/shaders/surfaceFlat.frag b/src/datavisualization/engine/shaders/surfaceFlat.frag
index 748fb3dd..1a0bbdeb 100644
--- a/src/datavisualization/engine/shaders/surfaceFlat.frag
+++ b/src/datavisualization/engine/shaders/surfaceFlat.frag
@@ -13,9 +13,11 @@ uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
diff --git a/src/datavisualization/engine/shaders/surfaceFlat.vert b/src/datavisualization/engine/shaders/surfaceFlat.vert
index 102bea78..0f953f4c 100644
--- a/src/datavisualization/engine/shaders/surfaceFlat.vert
+++ b/src/datavisualization/engine/shaders/surfaceFlat.vert
@@ -4,6 +4,7 @@
attribute highp vec3 vertexPosition_mdl;
attribute highp vec3 vertexNormal_mdl;
+attribute highp vec2 vertexUV;
uniform highp mat4 MVP;
uniform highp mat4 V;
@@ -11,6 +12,7 @@ uniform highp mat4 M;
uniform highp mat4 itM;
uniform highp vec3 lightPosition_wrld;
+varying highp vec2 UV;
varying highp vec3 position_wrld;
flat varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
@@ -26,4 +28,5 @@ void main() {
vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ UV = vertexUV;
}
diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
index 0613a40c..7eaba7f5 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
+++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
@@ -2,20 +2,22 @@
#extension GL_EXT_gpu_shader4 : require
-varying highp vec3 coords_mdl;
+varying highp vec2 coords_mdl;
varying highp vec3 position_wrld;
flat varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
+varying highp vec4 shadowCoord;
uniform highp sampler2DShadow shadowMap;
uniform sampler2D textureSampler;
-varying highp vec4 shadowCoord;
uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp float shadowQuality;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.94558609, -0.76890725),
@@ -35,7 +37,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.14383161, -0.14100790));
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
@@ -49,7 +51,7 @@ void main() {
highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
highp float bias = 0.005 * tan(acos(cosTheta));
- bias = clamp(bias, 0.0, 0.01);
+ bias = clamp(bias, 0.001, 0.01);
vec4 shadCoords = shadowCoord;
shadCoords.z -= bias;
diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
index 8da7b196..98fdde3f 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
+++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
@@ -2,9 +2,6 @@
#extension GL_EXT_gpu_shader4 : require
-attribute highp vec3 vertexPosition_mdl;
-attribute highp vec3 vertexNormal_mdl;
-
uniform highp mat4 MVP;
uniform highp mat4 V;
uniform highp mat4 M;
@@ -12,12 +9,17 @@ uniform highp mat4 itM;
uniform highp mat4 depthMVP;
uniform highp vec3 lightPosition_wrld;
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec3 vertexNormal_mdl;
+attribute highp vec2 vertexUV;
+
+varying highp vec2 UV;
varying highp vec3 position_wrld;
flat varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
varying highp vec4 shadowCoord;
-varying highp vec3 coords_mdl;
+varying highp vec2 coords_mdl;
const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
@@ -26,12 +28,12 @@ const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0,
void main() {
gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
- coords_mdl = vertexPosition_mdl;
+ coords_mdl = vertexPosition_mdl.xy;
shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0);
position_wrld = vec4(M * vec4(vertexPosition_mdl, 1.0)).xyz;
vec3 vertexPosition_cmr = vec4(V * M * vec4(vertexPosition_mdl, 1.0)).xyz;
eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr;
- vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
- lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
+ lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz;
normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ UV = vertexUV;
}
diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
index 1acf8f69..985214be 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
+++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
@@ -5,15 +5,17 @@ varying highp vec3 position_wrld;
varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
+varying highp vec4 shadowCoord;
uniform highp sampler2DShadow shadowMap;
uniform sampler2D textureSampler;
-varying highp vec4 shadowCoord;
uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp float shadowQuality;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.94558609, -0.76890725),
@@ -33,7 +35,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.14383161, -0.14100790));
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
@@ -47,7 +49,7 @@ void main() {
highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
highp float bias = 0.005 * tan(acos(cosTheta));
- bias = clamp(bias, 0.0, 0.01);
+ bias = clamp(bias, 0.001, 0.01);
vec4 shadCoords = shadowCoord;
shadCoords.z -= bias;
diff --git a/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag
new file mode 100644
index 00000000..7c654e0c
--- /dev/null
+++ b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag
@@ -0,0 +1,39 @@
+#version 120
+
+#extension GL_EXT_gpu_shader4 : require
+
+varying highp vec3 coords_mdl;
+varying highp vec3 position_wrld;
+flat varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec2 UV;
+
+uniform sampler2D textureSampler;
+uniform highp vec3 lightPosition_wrld;
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp vec4 lightColor;
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
+
+ highp float distance = length(lightPosition_wrld - position_wrld);
+
+ highp vec3 n = normalize(normal_cmr);
+ highp vec3 l = normalize(lightDirection_cmr);
+ highp float cosTheta = clamp(dot(n, l), 0.0, 1.0);
+
+ highp vec3 E = normalize(eyeDirection_cmr);
+ highp vec3 R = reflect(-l, n);
+ highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
+
+ gl_FragColor.rgb =
+ materialAmbientColor +
+ materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance +
+ materialSpecularColor * lightStrength * pow(cosAlpha, 10) / distance;
+ gl_FragColor.a = 1.0;
+}
+
diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag
new file mode 100644
index 00000000..a4259565
--- /dev/null
+++ b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag
@@ -0,0 +1,67 @@
+#version 120
+
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp float shadowQuality;
+uniform highp sampler2D textureSampler;
+uniform highp sampler2DShadow shadowMap;
+uniform highp vec4 lightColor;
+
+varying highp vec4 shadowCoord;
+varying highp vec2 UV;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+
+highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
+ vec2(0.94558609, -0.76890725),
+ vec2(-0.094184101, -0.92938870),
+ vec2(0.34495938, 0.29387760),
+ vec2(-0.91588581, 0.45771432),
+ vec2(-0.81544232, -0.87912464),
+ vec2(-0.38277543, 0.27676845),
+ vec2(0.97484398, 0.75648379),
+ vec2(0.44323325, -0.97511554),
+ vec2(0.53742981, -0.47373420),
+ vec2(-0.26496911, -0.41893023),
+ vec2(0.79197514, 0.19090188),
+ vec2(-0.24188840, 0.99706507),
+ vec2(-0.81409955, 0.91437590),
+ vec2(0.19984126, 0.78641367),
+ vec2(0.14383161, -0.14100790));
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb * 0.2;
+
+ highp vec3 n = normalize(normal_cmr);
+ highp vec3 l = normalize(lightDirection_cmr);
+ highp float cosTheta = clamp(dot(n, l), 0.0, 1.0);
+
+ highp vec3 E = normalize(eyeDirection_cmr);
+ highp vec3 R = reflect(-l, n);
+ highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
+
+ highp float bias = 0.005 * tan(acos(cosTheta));
+ bias = clamp(bias, 0.001, 0.01);
+
+ vec4 shadCoords = shadowCoord;
+ shadCoords.z -= bias;
+
+ highp float visibility = 0.6;
+ for (int i = 0; i < 15; i++) {
+ vec4 shadCoordsPD = shadCoords;
+ shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality;
+ shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality;
+ visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r;
+ }
+
+ gl_FragColor.rgb =
+ (materialAmbientColor +
+ materialDiffuseColor * lightStrength * cosTheta +
+ materialSpecularColor * lightStrength * pow(cosAlpha, 10));
+ gl_FragColor.a = texture2D(textureSampler, UV).a;
+ gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0);
+}
diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag
new file mode 100644
index 00000000..496f4777
--- /dev/null
+++ b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag
@@ -0,0 +1,72 @@
+#version 120
+
+#extension GL_EXT_gpu_shader4 : require
+
+varying highp vec3 coords_mdl;
+varying highp vec3 position_wrld;
+flat varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec2 UV;
+
+uniform highp sampler2DShadow shadowMap;
+uniform sampler2D textureSampler;
+varying highp vec4 shadowCoord;
+uniform highp vec3 lightPosition_wrld;
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp float shadowQuality;
+uniform highp vec4 lightColor;
+
+highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
+ vec2(0.94558609, -0.76890725),
+ vec2(-0.094184101, -0.92938870),
+ vec2(0.34495938, 0.29387760),
+ vec2(-0.91588581, 0.45771432),
+ vec2(-0.81544232, -0.87912464),
+ vec2(-0.38277543, 0.27676845),
+ vec2(0.97484398, 0.75648379),
+ vec2(0.44323325, -0.97511554),
+ vec2(0.53742981, -0.47373420),
+ vec2(-0.26496911, -0.41893023),
+ vec2(0.79197514, 0.19090188),
+ vec2(-0.24188840, 0.99706507),
+ vec2(-0.81409955, 0.91437590),
+ vec2(0.19984126, 0.78641367),
+ vec2(0.14383161, -0.14100790));
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
+
+ highp vec3 n = normalize(normal_cmr);
+ highp vec3 l = normalize(lightDirection_cmr);
+ highp float cosTheta = clamp(dot(n, l), 0.0, 1.0);
+
+ highp vec3 E = normalize(eyeDirection_cmr);
+ highp vec3 R = reflect(-l, n);
+ highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
+
+ highp float bias = 0.005 * tan(acos(cosTheta));
+ bias = clamp(bias, 0.001, 0.01);
+
+ vec4 shadCoords = shadowCoord;
+ shadCoords.z -= bias;
+
+ highp float visibility = 0.6;
+ for (int i = 0; i < 15; i++) {
+ vec4 shadCoordsPD = shadCoords;
+ shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality;
+ shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality;
+ visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r;
+ }
+
+ gl_FragColor.rgb =
+ (materialAmbientColor +
+ materialDiffuseColor * lightStrength * cosTheta +
+ materialSpecularColor * lightStrength * pow(cosAlpha, 10));
+ gl_FragColor.a = 1.0;
+ gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0);
+}
+
diff --git a/src/datavisualization/engine/shaders/surface_ES2.frag b/src/datavisualization/engine/shaders/surface_ES2.frag
index 58d13834..1d1bdc3e 100644
--- a/src/datavisualization/engine/shaders/surface_ES2.frag
+++ b/src/datavisualization/engine/shaders/surface_ES2.frag
@@ -10,9 +10,11 @@ uniform sampler2D textureSampler;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag
new file mode 100644
index 00000000..3f9c42ff
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3d.frag
@@ -0,0 +1,155 @@
+#version 120
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+uniform highp vec3 textureDimensions;
+uniform highp int sampleCount; // This is the maximum sample count
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+
+// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha.
+// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over
+// entire volume, regardless of texture dimensions
+const highp float alphaThicknesses = 32.0;
+
+void main() {
+ vec3 rayStart = pos;
+
+ highp vec3 startBounds = minBounds;
+ highp vec3 endBounds = maxBounds;
+ if (rayDir.x < 0.0) {
+ startBounds.x = maxBounds.x;
+ endBounds.x = minBounds.x;
+ }
+ if (rayDir.y > 0.0) {
+ startBounds.y = maxBounds.y;
+ endBounds.y = minBounds.y;
+ }
+ if (rayDir.z > 0.0) {
+ startBounds.z = maxBounds.z;
+ endBounds.z = minBounds.z;
+ }
+
+ // Calculate ray intersection endpoint
+ highp vec3 rayStop;
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 t = (endBounds - rayStart) * invRayDir;
+ highp float endT = min(t.x, min(t.y, t.z));
+ rayStop = rayStart + endT * rayDir;
+ if (endT <= 0.0)
+ discard;
+
+ // Convert intersections to texture coords
+ rayStart = 0.5 * (rayStart + 1.0);
+ rayStop = 0.5 * (rayStop + 1.0);
+
+ highp vec3 ray = rayStop - rayStart;
+
+ highp vec3 absRay = abs(ray);
+ highp vec3 invAbsRay = 1.0 / absRay;
+ highp float fullDist = length(ray);
+ highp vec3 curPos = rayStart;
+
+ highp vec4 curColor = vec4(0, 0, 0, 0);
+ highp float curAlpha = 0.0;
+ highp float curLen = 0.0;
+ highp vec3 curRgb = vec3(0, 0, 0);
+
+ highp vec4 destColor = vec4(0, 0, 0, 0);
+ highp float totalOpacity = 1.0;
+
+ highp float extraAlphaMultiplier = fullDist * alphaThicknesses * alphaMultiplier;
+
+ // nextEdges vector indicates the next edges of the texel boundaries along each axis that
+ // the ray is about to cross. The first edges are offset by a fraction of a texel to
+ // avoid artifacts from rounding errors later.
+ highp vec3 nextEdges = vec3(floor(curPos.x / textureDimensions.x) * textureDimensions.x,
+ floor(curPos.y / textureDimensions.y) * textureDimensions.y,
+ floor(curPos.z / textureDimensions.z) * textureDimensions.z);
+
+ highp vec3 textureSteps = textureDimensions;
+ highp vec3 textureOffset = textureDimensions * 0.001;
+ if (ray.x > 0) {
+ nextEdges.x += textureDimensions.x + textureOffset.x;
+ } else {
+ nextEdges.x -= textureOffset.x;
+ textureSteps.x = -textureDimensions.x;
+ }
+ if (ray.y > 0) {
+ nextEdges.y += textureDimensions.y + textureOffset.y;
+ } else {
+ nextEdges.y -= textureOffset.y;
+ textureSteps.y = -textureDimensions.y;
+ }
+ if (ray.z > 0) {
+ nextEdges.z += textureDimensions.z + textureOffset.z;
+ } else {
+ nextEdges.z -= textureOffset.z;
+ textureSteps.z = -textureDimensions.z;
+ }
+
+ // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1
+ for (int i = 0; i < sampleCount; i++) {
+ curColor = texture3D(textureSampler, curPos);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+
+ // Find which dimension has least to go to figure out the next step distance
+ highp vec3 delta = abs(nextEdges - curPos);
+ highp vec3 modDelta = delta * invAbsRay;
+ highp float minDelta = min(modDelta.x, min(modDelta.y, modDelta.z));
+ highp float stepSize;
+ if (minDelta == modDelta.x) {
+ nextEdges.x += textureSteps.x;
+ stepSize = delta.x * invAbsRay.x;
+ }
+ if (minDelta == modDelta.y) {
+ nextEdges.y += textureSteps.y;
+ stepSize = delta.y * invAbsRay.y;
+ }
+ if (minDelta == modDelta.z) {
+ nextEdges.z += textureSteps.z;
+ stepSize = delta.z * invAbsRay.z;
+ }
+
+ curPos += stepSize * ray;
+ curLen += stepSize;
+
+ if (curColor.a >= 0.0) {
+ if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0))
+ curAlpha = 1.0;
+ else
+ curAlpha = curColor.a * extraAlphaMultiplier * stepSize;
+ highp float nextOpacity = totalOpacity - curAlpha;
+ // If opacity goes beyond full opacity, we need to adjust current alpha according
+ // to the fraction of the distance the material is visible, so that we don't get
+ // box artifacts around texels.
+ if (nextOpacity < 0.0) {
+ curAlpha *= totalOpacity / curAlpha;
+ nextOpacity = 0.0;
+ }
+ curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity);
+ totalOpacity = nextOpacity;
+ destColor.rgb += curRgb;
+ }
+
+ if (curLen >= 1.0 || totalOpacity <= 0.0)
+ break;
+ }
+
+ if (totalOpacity == 1.0)
+ discard;
+
+ // Brighten up the final color if there is some transparency left
+ if (totalOpacity >= 0.0 && totalOpacity < 1.0)
+ destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity);
+
+ destColor.a = (1.0 - totalOpacity);
+ gl_FragColor = clamp(destColor, 0.0, 1.0);
+}
diff --git a/src/datavisualization/engine/shaders/texture3d.vert b/src/datavisualization/engine/shaders/texture3d.vert
new file mode 100644
index 00000000..ef3f1b25
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3d.vert
@@ -0,0 +1,36 @@
+uniform highp mat4 MVP;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+uniform highp vec3 cameraPositionRelativeToModel;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+attribute highp vec3 vertexNormal_mdl;
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+
+ highp vec3 minBoundsNorm = minBounds;
+ highp vec3 maxBoundsNorm = maxBounds;
+
+ // Y and Z are flipped in bounds to be directly usable in texture calculations,
+ // so flip them back to normal for position calculations
+ minBoundsNorm.yz = -minBoundsNorm.yz;
+ maxBoundsNorm.yz = -maxBoundsNorm.yz;
+
+ minBoundsNorm = 0.5 * (minBoundsNorm + 1.0);
+ maxBoundsNorm = 0.5 * (maxBoundsNorm + 1.0);
+
+ pos = vertexPosition_mdl
+ + ((1.0 - vertexPosition_mdl) * minBoundsNorm)
+ - ((1.0 + vertexPosition_mdl) * (1.0 - maxBoundsNorm));
+
+ rayDir = -(cameraPositionRelativeToModel - pos);
+
+ // Flip Y and Z so QImage bits work directly for texture and first image is in the front
+ rayDir.yz = -rayDir.yz;
+ pos.yz = -pos.yz;
+}
diff --git a/src/datavisualization/engine/shaders/texture3dlowdef.frag b/src/datavisualization/engine/shaders/texture3dlowdef.frag
new file mode 100644
index 00000000..ed0d41ce
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3dlowdef.frag
@@ -0,0 +1,109 @@
+#version 120
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+uniform highp vec3 textureDimensions;
+uniform highp int sampleCount; // This is the maximum sample count
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+
+// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha.
+// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over
+// entire volume, regardless of texture dimensions
+const highp float alphaThicknesses = 32.0;
+const highp float SQRT3 = 1.73205081;
+
+void main() {
+ vec3 rayStart = pos;
+ highp vec3 startBounds = minBounds;
+ highp vec3 endBounds = maxBounds;
+ if (rayDir.x < 0.0) {
+ startBounds.x = maxBounds.x;
+ endBounds.x = minBounds.x;
+ }
+ if (rayDir.y > 0.0) {
+ startBounds.y = maxBounds.y;
+ endBounds.y = minBounds.y;
+ }
+ if (rayDir.z > 0.0) {
+ startBounds.z = maxBounds.z;
+ endBounds.z = minBounds.z;
+ }
+
+ // Calculate ray intersection endpoint
+ highp vec3 rayStop;
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 t = (endBounds - rayStart) * invRayDir;
+ highp float endT = min(t.x, min(t.y, t.z));
+ if (endT <= 0.0)
+ discard;
+ rayStop = rayStart + endT * rayDir;
+
+ // Convert intersections to texture coords
+ rayStart = 0.5 * (rayStart + 1.0);
+ rayStop = 0.5 * (rayStop + 1.0);
+
+ highp vec3 ray = rayStop - rayStart;
+
+ highp float fullDist = length(ray);
+ highp float stepSize = SQRT3 / sampleCount;
+ highp vec3 step = (SQRT3 * normalize(ray)) / sampleCount;
+
+ rayStart += (step * 0.001);
+
+ highp vec3 curPos = rayStart;
+ highp float curLen = 0.0;
+ highp vec4 curColor = vec4(0, 0, 0, 0);
+ highp float curAlpha = 0.0;
+ highp vec3 curRgb = vec3(0, 0, 0);
+ highp vec4 destColor = vec4(0, 0, 0, 0);
+ highp float totalOpacity = 1.0;
+
+ highp float extraAlphaMultiplier = stepSize * alphaThicknesses * alphaMultiplier;
+
+ // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1
+ for (int i = 0; i < sampleCount; i++) {
+ curColor = texture3D(textureSampler, curPos);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+
+ if (curColor.a >= 0.0) {
+ if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0))
+ curAlpha = 1.0;
+ else
+ curAlpha = curColor.a * extraAlphaMultiplier;
+ highp float nextOpacity = totalOpacity - curAlpha;
+ // If opacity goes beyond full opacity, we need to adjust current alpha according
+ // to the fraction of the distance the material is visible, so that we don't get
+ // box artifacts around texels.
+ if (nextOpacity < 0.0) {
+ curAlpha *= totalOpacity / curAlpha;
+ nextOpacity = 0.0;
+ }
+
+ curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity);
+ destColor.rgb += curRgb;
+ totalOpacity = nextOpacity;
+ }
+ curPos += step;
+ curLen += stepSize;
+ if (curLen >= fullDist || totalOpacity <= 0.0)
+ break;
+ }
+
+ if (totalOpacity == 1.0)
+ discard;
+
+ // Brighten up the final color if there is some transparency left
+ if (totalOpacity >= 0.0 && totalOpacity < 1.0)
+ destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity);
+
+ destColor.a = (1.0 - totalOpacity);
+ gl_FragColor = clamp(destColor, 0.0, 1.0);
+}
diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag
new file mode 100644
index 00000000..c555af98
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3dslice.frag
@@ -0,0 +1,155 @@
+#version 120
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec3 volumeSliceIndices;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+
+const highp vec3 xPlaneNormal = vec3(1.0, 0, 0);
+const highp vec3 yPlaneNormal = vec3(0, 1.0, 0);
+const highp vec3 zPlaneNormal = vec3(0, 0, 1.0);
+
+void main() {
+ // Find out where ray intersects the slice planes
+ vec3 normRayDir = normalize(rayDir);
+ highp vec3 rayStart = pos;
+ highp float minT = 2.0f;
+ if (normRayDir.x != 0.0 && normRayDir.y != 0.0 && normRayDir.z != 0.0) {
+ highp vec3 boxBounds = vec3(1.0, 1.0, 1.0);
+ highp vec3 invRayDir = 1.0 / normRayDir;
+ if (normRayDir.x < 0)
+ boxBounds.x = -1.0;
+ if (normRayDir.y < 0)
+ boxBounds.y = -1.0;
+ if (normRayDir.z < 0)
+ boxBounds.z = -1.0;
+ highp vec3 t = (boxBounds - rayStart) * invRayDir;
+ minT = min(t.x, min(t.y, t.z));
+ }
+
+ highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0);
+ highp vec3 yPoint = vec3(0, volumeSliceIndices.y, 0);
+ highp vec3 zPoint = vec3(0, 0, volumeSliceIndices.z);
+ highp float firstD = minT + 1.0;
+ highp float secondD = firstD;
+ highp float thirdD = firstD;
+ if (volumeSliceIndices.x >= -1.0) {
+ highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(normRayDir, xPlaneNormal);
+ if (dx >= 0.0 && dx <= minT)
+ firstD = min(dx, firstD);
+ }
+ if (volumeSliceIndices.y >= -1.0) {
+ highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(normRayDir, yPlaneNormal);
+ if (dy >= 0.0 && dy <= minT) {
+ if (dy < firstD) {
+ secondD = firstD;
+ firstD = dy;
+ } else {
+ secondD = dy;
+ }
+ }
+ }
+ if (volumeSliceIndices.z >= -1.0) {
+ highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(normRayDir, zPlaneNormal);
+ if (dz >= 0.0) {
+ if (dz < firstD && dz <= minT) {
+ thirdD = secondD;
+ secondD = firstD;
+ firstD = dz;
+ } else if (dz < secondD){
+ thirdD = secondD;
+ secondD = dz;
+ } else {
+ thirdD = dz;
+ }
+ }
+ }
+
+ highp vec4 destColor = vec4(0.0, 0.0, 0.0, 0.0);
+ highp vec4 curColor = vec4(0.0, 0.0, 0.0, 0.0);
+ highp float totalAlpha = 0.0;
+ highp vec3 curRgb = vec3(0, 0, 0);
+ highp float curAlpha = 0.0;
+
+ // Convert intersection to texture coords
+
+ if (firstD <= minT) {
+ highp vec3 texelVec = rayStart + normRayDir * firstD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+
+ if (curColor.a > 0.0) {
+ curAlpha = curColor.a;
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ destColor.rgb = curColor.rgb * curAlpha;
+ totalAlpha = curAlpha;
+ }
+ }
+ if (secondD <= minT && totalAlpha < 1.0) {
+ texelVec = rayStart + normRayDir * secondD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+ if (curColor.a > 0.0) {
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += curAlpha;
+ }
+ }
+ if (thirdD <= minT && totalAlpha < 1.0) {
+ texelVec = rayStart + normRayDir * thirdD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (curColor.a > 0.0) {
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += curAlpha;
+ }
+ }
+ }
+ }
+ }
+
+ if (totalAlpha == 0.0)
+ discard;
+
+ // Brighten up the final color if there is some transparency left
+ if (totalAlpha > 0.0 && totalAlpha < 1.0)
+ destColor *= 1.0 / totalAlpha;
+
+ destColor.a = totalAlpha;
+ gl_FragColor = clamp(destColor, 0.0, 1.0);
+}
+
diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp
index c03bafd8..a6c086da 100644
--- a/src/datavisualization/engine/surface3dcontroller.cpp
+++ b/src/datavisualization/engine/surface3dcontroller.cpp
@@ -29,7 +29,8 @@ Surface3DController::Surface3DController(QRect rect, Q3DScene *scene)
m_renderer(0),
m_selectedPoint(invalidSelectionPosition()),
m_selectedSeries(0),
- m_flatShadingSupported(true)
+ m_flatShadingSupported(true),
+ m_flipHorizontalGrid(false)
{
// Setting a null axis creates a new default axis according to orientation and graph type.
// Note: these cannot be set in the Abstract3DController constructor, as they will call virtual
@@ -79,6 +80,17 @@ void Surface3DController::synchDataToRenderer()
m_renderer->updateSelectedPoint(m_selectedPoint, m_selectedSeries);
m_changeTracker.selectedPointChanged = false;
}
+
+ if (m_changeTracker.flipHorizontalGridChanged) {
+ m_renderer->updateFlipHorizontalGrid(m_flipHorizontalGrid);
+ m_changeTracker.flipHorizontalGridChanged = false;
+ }
+
+ if (m_changeTracker.surfaceTextureChanged) {
+ m_renderer->updateSurfaceTextures(m_changedTextures);
+ m_changeTracker.surfaceTextureChanged = false;
+ m_changedTextures.clear();
+ }
}
void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation(
@@ -140,6 +152,9 @@ void Surface3DController::addSeries(QAbstract3DSeries *series)
QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
if (surfaceSeries->selectedPoint() != invalidSelectionPosition())
setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries, false);
+
+ if (!surfaceSeries->texture().isNull())
+ updateSurfaceTexture(surfaceSeries);
}
void Surface3DController::removeSeries(QAbstract3DSeries *series)
@@ -168,6 +183,21 @@ QList<QSurface3DSeries *> Surface3DController::surfaceSeriesList()
return surfaceSeriesList;
}
+void Surface3DController::setFlipHorizontalGrid(bool flip)
+{
+ if (m_flipHorizontalGrid != flip) {
+ m_flipHorizontalGrid = flip;
+ m_changeTracker.flipHorizontalGridChanged = true;
+ emit flipHorizontalGridChanged(flip);
+ emitNeedRender();
+ }
+}
+
+bool Surface3DController::flipHorizontalGrid() const
+{
+ return m_flipHorizontalGrid;
+}
+
void Surface3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
{
// Currently surface only supports row and column modes when also slicing
@@ -429,6 +459,16 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count)
emitNeedRender();
}
+void Surface3DController::updateSurfaceTexture(QSurface3DSeries *series)
+{
+ m_changeTracker.surfaceTextureChanged = true;
+
+ if (!m_changedTextures.contains(series))
+ m_changedTextures.append(series);
+
+ emitNeedRender();
+}
+
void Surface3DController::adjustAxisRanges()
{
QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h
index 2be74f35..8bcdea91 100644
--- a/src/datavisualization/engine/surface3dcontroller_p.h
+++ b/src/datavisualization/engine/surface3dcontroller_p.h
@@ -38,14 +38,18 @@ class Surface3DRenderer;
class QSurface3DSeries;
struct Surface3DChangeBitField {
- bool selectedPointChanged : 1;
- bool rowsChanged : 1;
- bool itemChanged : 1;
+ bool selectedPointChanged : 1;
+ bool rowsChanged : 1;
+ bool itemChanged : 1;
+ bool flipHorizontalGridChanged : 1;
+ bool surfaceTextureChanged : 1;
Surface3DChangeBitField() :
selectedPointChanged(true),
rowsChanged(false),
- itemChanged(false)
+ itemChanged(false),
+ flipHorizontalGridChanged(true),
+ surfaceTextureChanged(true)
{
}
};
@@ -73,6 +77,8 @@ private:
bool m_flatShadingSupported;
QVector<ChangeItem> m_changedItems;
QVector<ChangeRow> m_changedRows;
+ bool m_flipHorizontalGrid;
+ QVector<QSurface3DSeries *> m_changedTextures;
public:
explicit Surface3DController(QRect rect, Q3DScene *scene = 0);
@@ -101,6 +107,11 @@ public:
virtual void removeSeries(QAbstract3DSeries *series);
virtual QList<QSurface3DSeries *> surfaceSeriesList();
+ void setFlipHorizontalGrid(bool flip);
+ bool flipHorizontalGrid() const;
+
+ void updateSurfaceTexture(QSurface3DSeries *series);
+
public slots:
void handleArrayReset();
void handleRowsAdded(int startIndex, int count);
@@ -113,6 +124,7 @@ public slots:
signals:
void selectedSeriesChanged(QSurface3DSeries *series);
+ void flipHorizontalGridChanged(bool flip);
private:
Q_DISABLE_COPY(Surface3DController)
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp
index 38c8d8fe..37d6b463 100644
--- a/src/datavisualization/engine/surface3drenderer.cpp
+++ b/src/datavisualization/engine/surface3drenderer.cpp
@@ -30,10 +30,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
//#define SHOW_DEPTH_TEXTURE_SCENE
-// Margin for background (1.10 make it 10% larger to avoid
-// selection ball being drawn inside background)
-const GLfloat backgroundMargin = 1.1f;
-const GLfloat gridLineWidth = 0.005f;
const GLfloat sliceZScale = 0.1f;
const GLfloat sliceUnits = 2.5f;
const uint greenMultiplier = 256;
@@ -47,19 +43,16 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
m_backgroundShader(0),
m_surfaceFlatShader(0),
m_surfaceSmoothShader(0),
+ m_surfaceTexturedSmoothShader(0),
+ m_surfaceTexturedFlatShader(0),
m_surfaceGridShader(0),
m_surfaceSliceFlatShader(0),
m_surfaceSliceSmoothShader(0),
m_selectionShader(0),
- m_labelShader(0),
m_heightNormalizer(0.0f),
- m_scaleFactor(0.0f),
m_scaleX(0.0f),
+ m_scaleY(0.0f),
m_scaleZ(0.0f),
- m_scaleXWithBackground(0.0f),
- m_scaleZWithBackground(0.0f),
- m_depthTexture(0),
- m_depthModelTexture(0),
m_depthFrameBuffer(0),
m_selectionFrameBuffer(0),
m_selectionDepthBuffer(0),
@@ -68,16 +61,12 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
m_flatSupported(true),
m_selectionActive(false),
m_shadowQualityMultiplier(3),
- m_hasHeightAdjustmentChanged(true),
m_selectedPoint(Surface3DController::invalidSelectionPosition()),
m_selectedSeries(0),
m_clickedPosition(Surface3DController::invalidSelectionPosition()),
m_selectionTexturesDirty(false),
m_noShadowTexture(0)
{
- m_axisCacheY.setScale(2.0f);
- m_axisCacheY.setTranslate(-1.0f);
-
// Check if flat feature is supported
ShaderHelper tester(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
QStringLiteral(":/shaders/fragmentSurfaceFlat"));
@@ -90,12 +79,13 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
" Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.";
}
- initializeOpenGLFunctions();
initializeOpenGL();
}
Surface3DRenderer::~Surface3DRenderer()
{
+ fixContextBeforeDelete();
+
if (QOpenGLContext::currentContext()) {
m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer);
m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
@@ -103,7 +93,6 @@ Surface3DRenderer::~Surface3DRenderer()
m_textureHelper->deleteTexture(&m_noShadowTexture);
m_textureHelper->deleteTexture(&m_depthTexture);
- m_textureHelper->deleteTexture(&m_depthModelTexture);
m_textureHelper->deleteTexture(&m_selectionResultTexture);
}
delete m_depthShader;
@@ -111,10 +100,11 @@ Surface3DRenderer::~Surface3DRenderer()
delete m_selectionShader;
delete m_surfaceFlatShader;
delete m_surfaceSmoothShader;
+ delete m_surfaceTexturedSmoothShader;
+ delete m_surfaceTexturedFlatShader;
delete m_surfaceGridShader;
delete m_surfaceSliceFlatShader;
delete m_surfaceSliceSmoothShader;
- delete m_labelShader;
}
void Surface3DRenderer::initializeOpenGL()
@@ -123,25 +113,15 @@ void Surface3DRenderer::initializeOpenGL()
// Initialize shaders
initSurfaceShaders();
- initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
- QStringLiteral(":/shaders/fragmentLabel"));
-#if !defined(QT_OPENGL_ES_2)
- // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
- initDepthShader();
-#endif
+ if (!m_isOpenGLES) {
+ initDepthShader(); // For shadows
+ loadGridLineMesh();
+ }
// Init selection shader
initSelectionShaders();
-#if !(defined QT_OPENGL_ES_2)
- // Load grid line mesh
- loadGridLineMesh();
-#endif
-
- // Load label mesh
- loadLabelMesh();
-
// Resize in case we've missed resize events
// Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here
handleResize();
@@ -155,6 +135,53 @@ void Surface3DRenderer::initializeOpenGL()
m_noShadowTexture = m_textureHelper->create2DTexture(image, false, true, false, true);
}
+void Surface3DRenderer::fixCameraTarget(QVector3D &target)
+{
+ target.setX(target.x() * m_scaleX);
+ target.setY(target.y() * m_scaleY);
+ target.setZ(target.z() * -m_scaleZ);
+}
+
+void Surface3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_scaleX)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -m_scaleY)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_scaleZ)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_scaleX)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > m_scaleY)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_scaleZ)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Surface3DRenderer::updateData()
{
calculateSceneScalingFactors();
@@ -264,6 +291,33 @@ void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis
}
}
+void Surface3DRenderer::updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList)
+{
+ foreach (QSurface3DSeries *series, seriesList) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(series));
+ if (cache) {
+ GLuint oldTexture = cache->surfaceTexture();
+ m_textureHelper->deleteTexture(&oldTexture);
+ cache->setSurfaceTexture(0);
+
+ const QSurface3DSeries *currentSeries = cache->series();
+ QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
+ const QSurfaceDataArray &array = *dataProxy->array();
+
+ if (!series->texture().isNull()) {
+ cache->setSurfaceTexture(m_textureHelper->create2DTexture(
+ series->texture(), true, true, true));
+
+ if (cache->isFlatShadingEnabled())
+ cache->surfaceObject()->coarseUVs(array, cache->dataArray());
+ else
+ cache->surfaceObject()->smoothUVs(array, cache->dataArray());
+ }
+ }
+ }
+}
+
SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series)
{
m_selectionTexturesDirty = true;
@@ -301,10 +355,13 @@ void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow>
srcArray->at(row)->at(j + sampleSpace.x());
}
- if (cache->isFlatShadingEnabled())
- cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y());
- else
- cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y());
+ if (cache->isFlatShadingEnabled()) {
+ cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y(),
+ m_polarGraph);
+ } else {
+ cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y(),
+ m_polarGraph);
+ }
}
if (updateBuffers)
cache->surfaceObject()->uploadBuffers();
@@ -343,9 +400,9 @@ void Surface3DRenderer::updateItems(const QVector<Surface3DController::ChangeIte
(*(dstArray.at(y)))[x] = srcArray->at(point.x())->at(point.y());
if (cache->isFlatShadingEnabled())
- cache->surfaceObject()->updateCoarseItem(dstArray, y, x);
+ cache->surfaceObject()->updateCoarseItem(dstArray, y, x, m_polarGraph);
else
- cache->surfaceObject()->updateSmoothItem(dstArray, y, x);
+ cache->surfaceObject()->updateSmoothItem(dstArray, y, x, m_polarGraph);
}
if (updateBuffers)
cache->surfaceObject()->uploadBuffers();
@@ -538,10 +595,12 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const
QRect sliceRect(0, 0, sliceRow->size(), 2);
if (sliceRow->size() > 0) {
- if (cache->isFlatShadingEnabled())
- cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, flipZX);
- else
- cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, flipZX);
+ if (cache->isFlatShadingEnabled()) {
+ cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, false, flipZX);
+ } else {
+ cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, false,
+ flipZX);
+ }
}
}
@@ -664,15 +723,6 @@ QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array)
void Surface3DRenderer::updateScene(Q3DScene *scene)
{
- // Set initial camera position
- // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later
- if (m_hasHeightAdjustmentChanged) {
- scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector,
- upVector);
- // For now this is used just to make things once. Proper use will come
- m_hasHeightAdjustmentChanged = false;
- }
-
Abstract3DRenderer::updateScene(scene);
if (m_selectionActive
@@ -716,6 +766,14 @@ void Surface3DRenderer::render(GLuint defaultFboHandle)
void Surface3DRenderer::drawSlicedScene()
{
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)
+ == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
+ qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
+ " QAbstract3DGraph::SelectionColumn must be set before calling"
+ " setSlicingActive(true).");
+ return;
+ }
+
QVector3D lightPos;
QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
@@ -751,6 +809,19 @@ void Surface3DRenderer::drawSlicedScene()
AxisRenderCache &sliceCache = rowMode ? m_axisCacheX : m_axisCacheZ;
GLfloat scaleXBackground = 0.0f;
+ if (rowMode) {
+ // Don't use the regular margin for polar, as the graph is not going to be to scale anyway,
+ // and polar graphs often have quite a bit of margin, resulting in ugly slices.
+ if (m_polarGraph)
+ scaleXBackground = m_scaleX + 0.1f;
+ else
+ scaleXBackground = m_scaleXWithBackground;
+ } else {
+ if (m_polarGraph)
+ scaleXBackground = m_scaleZ + 0.1f;
+ else
+ scaleXBackground = m_scaleZWithBackground;
+ }
// Disable culling to avoid ugly conditionals with reversed axes and data
glDisable(GL_CULL_FACE);
@@ -767,11 +838,6 @@ void Surface3DRenderer::drawSlicedScene()
drawGrid = true;
}
- if (rowMode)
- scaleXBackground = m_scaleXWithBackground;
- else
- scaleXBackground = m_scaleZWithBackground;
-
QMatrix4x4 MVPMatrix;
QMatrix4x4 modelMatrix;
QMatrix4x4 itModelMatrix;
@@ -790,9 +856,27 @@ void Surface3DRenderer::drawSlicedScene()
surfaceShader->bind();
- GLuint colorTexture = cache->baseUniformTexture();;
- if (cache->colorStyle() != Q3DTheme::ColorStyleUniform)
+ GLuint colorTexture = cache->baseUniformTexture();
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
+ colorTexture = cache->baseUniformTexture();
+ surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.0f);
+ surfaceShader->setUniformValue(surfaceShader->gradientHeight(), 0.0f);
+ } else {
colorTexture = cache->baseGradientTexture();
+ if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
+ float objMin = cache->surfaceObject()->minYValue();
+ float objMax = cache->surfaceObject()->maxYValue();
+ float objRange = objMax - objMin;
+ surfaceShader->setUniformValue(surfaceShader->gradientMin(),
+ -(objMin / objRange));
+ surfaceShader->setUniformValue(surfaceShader->gradientHeight(),
+ 1.0f / objRange);
+ } else {
+ surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.5f);
+ surfaceShader->setUniformValue(surfaceShader->gradientHeight(),
+ 1.0f / (m_scaleY * 2.0f));
+ }
+ }
// Set shader bindings
surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos);
@@ -801,9 +885,10 @@ void Surface3DRenderer::drawSlicedScene()
surfaceShader->setUniformValue(surfaceShader->nModel(),
itModelMatrix.inverted().transposed());
surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix);
- surfaceShader->setUniformValue(surfaceShader->lightS(), 0.15f);
+ surfaceShader->setUniformValue(surfaceShader->lightS(), 0.0f);
surfaceShader->setUniformValue(surfaceShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 10.0f);
surfaceShader->setUniformValue(surfaceShader->lightColor(), lightColor);
m_drawer->drawObject(surfaceShader, cache->sliceSurfaceObject(), colorTexture);
@@ -830,19 +915,16 @@ void Surface3DRenderer::drawSlicedScene()
}
}
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// Grid lines
- if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ if (m_cachedTheme->isGridEnabled()) {
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
// Bind line shader
lineShader->bind();
@@ -853,7 +935,8 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 10.0f);
lineShader->setUniformValue(lineShader->lightS(), 0.0f);
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
@@ -881,16 +964,15 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
}
}
// Vertical lines
- QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth);
+ QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
gridLineCount = sliceCache.gridLineCount();
for (int line = 0; line < gridLineCount; line++) {
@@ -901,10 +983,11 @@ void Surface3DRenderer::drawSlicedScene()
modelMatrix.translate(sliceCache.gridLinePosition(line), 0.0f, -1.0f);
modelMatrix.scale(gridLineScaleY);
itModelMatrix.scale(gridLineScaleY);
-#if (defined QT_OPENGL_ES_2)
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ }
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -915,17 +998,15 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
}
}
// Draw labels
m_labelShader->bind();
- glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -959,12 +1040,15 @@ void Surface3DRenderer::drawSlicedScene()
labelNbr = 0;
positionComp.setY(-0.1f);
- labelTrans.setY(-backgroundMargin);
+ labelTrans.setY(-m_scaleYWithBackground);
labelCount = sliceCache.labelCount();
for (int label = 0; label < labelCount; label++) {
if (countLabelItems > labelNbr) {
// Draw the label here
- labelTrans.setX(sliceCache.labelPosition(label));
+ if (rowMode)
+ labelTrans.setX(sliceCache.labelPosition(label));
+ else
+ labelTrans.setX(-sliceCache.labelPosition(label));
m_dummyRenderItem.setTranslation(labelTrans);
@@ -998,7 +1082,6 @@ void Surface3DRenderer::drawSlicedScene()
m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
false, false, Drawer::LabelMid, Qt::AlignBottom);
- glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
@@ -1048,6 +1131,21 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
else
m_xFlipped = true;
+ m_yFlippedForGrid = m_yFlipped;
+ if (m_flipHorizontalGrid) {
+ if (!m_useOrthoProjection) {
+ // Need to determine if camera is below graph top
+ float distanceToCenter = activeCamera->position().length()
+ / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f;
+ qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI;
+ float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter;
+ m_yFlippedForGrid = cameraYPos < (m_scaleYWithBackground - m_oldCameraTarget.y());
+ } else if (m_useOrthoProjection && activeCamera->yRotation() == 0.0f) {
+ // With ortho we only need to flip at angle zero, to fix label autorotation angles
+ m_yFlippedForGrid = !m_yFlipped;
+ }
+ }
+
// calculate background rotation based on view matrix rotation
if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0)
backgroundRotation = 270.0f;
@@ -1065,9 +1163,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 depthProjectionViewMatrix;
// Draw depth buffer
-#if !defined(QT_OPENGL_ES_2)
GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone &&
+ if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone &&
(!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty())) {
// Render scene into a depth texture for using with shadow mapping
// Enable drawing to depth framebuffer
@@ -1097,6 +1194,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
/ (GLfloat)m_primarySubViewport.height(), 3.0f, 100.0f);
depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
+ // Surface is not closed, so don't cull anything
glDisable(GL_CULL_FACE);
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
@@ -1104,45 +1202,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
SurfaceObject *object = cache->surfaceObject();
if (object->indexCount() && cache->surfaceVisible() && cache->isVisible()
&& cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
-
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
- cache->setMVPMatrix(MVPMatrix);
- m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
-
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_depthShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
- glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
-
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
-
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, object->indexCount(),
- object->indicesType(), (void *)0);
- }
- }
-
- glEnable(GL_CULL_FACE);
- glCullFace(GL_FRONT);
-
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
- m_depthModelTexture, 0);
- glClear(GL_DEPTH_BUFFER_BIT);
-
- foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
- SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
- SurfaceObject *object = cache->surfaceObject();
- if (object->indexCount() && cache->surfaceVisible() && cache->isVisible()
- && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) {
- m_depthShader->setUniformValue(m_depthShader->MVP(), cache->MVPMatrix());
+ // No translation nor scaling for surfaces, therefore no modelMatrix
+ // Use directly projectionViewMatrix
+ m_depthShader->setUniformValue(m_depthShader->MVP(), depthProjectionViewMatrix);
// 1st attribute buffer : vertices
glEnableVertexAttribArray(m_depthShader->posAtt());
@@ -1165,9 +1227,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glDisableVertexAttribArray(m_depthShader->posAtt());
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+
Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -1182,15 +1248,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
-#endif
- // Enable texturing
- glEnable(GL_TEXTURE_2D);
+
+ // Do position mapping when necessary
+ if (m_graphPositionQueryPending) {
+ QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ);
+ queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
+ emit needRender();
+ }
// Draw selection buffer
if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty()
|| !m_customRenderCache.isEmpty())
&& m_selectionState == SelectOnScene
- && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
+ && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
+ && m_selectionResultTexture) {
m_selectionShader->bind();
glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
glViewport(0,
@@ -1208,18 +1279,17 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->surfaceObject()->indexCount() && cache->renderable()) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
+ m_selectionShader->setUniformValue(m_selectionShader->MVP(), projectionViewMatrix);
- MVPMatrix = projectionViewMatrix * modelMatrix;
- m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
+ cache->surfaceObject()->activateSurfaceTexture(false);
m_drawer->drawObject(m_selectionShader, cache->surfaceObject(),
cache->selectionTexture());
}
}
m_surfaceGridShader->bind();
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader,
+ viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
@@ -1237,6 +1307,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
+ uint(clickedColor.w()) * alphaMultiplier;
m_clickedPosition = selectionIdToSurfacePoint(selectionId);
+ m_clickResolved = true;
emit needRender();
@@ -1249,7 +1320,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw the surface
if (!m_renderCacheList.isEmpty()) {
- // For surface we can see climpses from underneath
+ // For surface we can see glimpses from underneath
glDisable(GL_CULL_FACE);
bool drawGrid = false;
@@ -1261,9 +1332,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 itModelMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix;
#else
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ MVPMatrix = projectionViewMatrix;
#endif
cache->setMVPMatrix(MVPMatrix);
@@ -1279,8 +1350,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
if (cache->surfaceVisible()) {
ShaderHelper *shader = m_surfaceFlatShader;
- if (!cache->isFlatShadingEnabled())
+ if (cache->surfaceTexture())
+ shader = m_surfaceTexturedFlatShader;
+ if (!cache->isFlatShadingEnabled()) {
shader = m_surfaceSmoothShader;
+ if (cache->surfaceTexture())
+ shader = m_surfaceTexturedSmoothShader;
+ }
shader->bind();
// Set shader bindings
@@ -1294,14 +1370,35 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
m_cachedTheme->ambientLightStrength());
shader->setUniformValue(shader->lightColor(), lightColor);
- GLuint gradientTexture;
- if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
- gradientTexture = cache->baseUniformTexture();
- else
- gradientTexture = cache->baseGradientTexture();
+ // Set the surface texturing
+ cache->surfaceObject()->activateSurfaceTexture(false);
+ GLuint texture;
+ if (cache->surfaceTexture()) {
+ texture = cache->surfaceTexture();
+ cache->surfaceObject()->activateSurfaceTexture(true);
+ } else {
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
+ texture = cache->baseUniformTexture();
+ shader->setUniformValue(shader->gradientMin(), 0.0f);
+ shader->setUniformValue(shader->gradientHeight(), 0.0f);
+ } else {
+ texture = cache->baseGradientTexture();
+ if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
+ float objMin = cache->surfaceObject()->minYValue();
+ float objMax = cache->surfaceObject()->maxYValue();
+ float objRange = objMax - objMin;
+ shader->setUniformValue(shader->gradientMin(), -(objMin / objRange));
+ shader->setUniformValue(shader->gradientHeight(), 1.0f / objRange);
+ } else {
+ shader->setUniformValue(shader->gradientMin(), 0.5f);
+ shader->setUniformValue(shader->gradientHeight(),
+ 1.0f / (m_scaleY * 2.0f));
+ }
+ }
+ }
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (!m_isOpenGLES &&
+ m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader);
@@ -1309,17 +1406,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
shader->setUniformValue(shader->lightS(), adjustedLightStrength);
// Draw the objects
- m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture,
- m_depthModelTexture);
- } else
-#endif
- {
+ m_drawer->drawObject(shader, cache->surfaceObject(), texture,
+ m_depthTexture);
+ } else {
// Set shadowless shader bindings
- shader->setUniformValue(shader->lightS(),
- m_cachedTheme->lightStrength());
-
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
// Draw the objects
- m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture);
+ m_drawer->drawObject(shader, cache->surfaceObject(), texture);
}
}
}
@@ -1359,12 +1452,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- QVector3D bgScale(m_scaleXWithBackground, backgroundMargin, m_scaleZWithBackground);
+ QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground, m_scaleZWithBackground);
modelMatrix.scale(bgScale);
// If we're viewing from below, background object must be flipped
if (m_yFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f);
} else {
modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
@@ -1392,8 +1485,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
m_cachedTheme->ambientLightStrength() * 2.0f);
m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
@@ -1406,11 +1498,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_noShadowTexture);
else
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
- } else
-#else
- Q_UNUSED(noShadows);
-#endif
- {
+ } else {
// Set shadowless shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
m_cachedTheme->lightStrength());
@@ -1423,14 +1511,14 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw grid lines
QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
- QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth);
+ QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
- if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ if (m_cachedTheme->isGridEnabled()) {
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
// Bind line shader
lineShader->bind();
@@ -1442,15 +1530,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadowed shader bindings
lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 20.0f);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 2.5f);
@@ -1460,205 +1545,211 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QQuaternion lineXRotation;
if (m_xFlipped)
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f);
+ lineYRotation = m_yRightAngleRotationNeg;
else
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
+ lineYRotation = m_yRightAngleRotation;
- if (m_yFlipped)
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f);
+ if (m_yFlippedForGrid)
+ lineXRotation = m_xRightAngleRotation;
else
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f);
+ lineXRotation = m_xRightAngleRotationNeg;
- GLfloat yFloorLinePosition = -backgroundMargin + gridLineOffset;
- if (m_yFlipped)
+ float yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset;
+ if (m_yFlipped != m_flipHorizontalGrid)
yFloorLinePosition = -yFloorLinePosition;
// Rows (= Z)
if (m_axisCacheZ.segmentCount() > 0) {
- // Floor lines
int gridLineCount = m_axisCacheZ.gridLineCount();
-
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(0.0f, yFloorLinePosition,
- m_axisCacheZ.gridLinePosition(line));
-
- modelMatrix.scale(gridLineScaleX);
- itModelMatrix.scale(gridLineScaleX);
-
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
-
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Floor lines
+ if (m_polarGraph) {
+ drawRadialGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(0.0f, yFloorLinePosition,
+ m_axisCacheZ.gridLinePosition(line));
+
+ modelMatrix.scale(gridLineScaleX);
+ itModelMatrix.scale(gridLineScaleX);
+
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
+
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
+ // Side wall lines
+ GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
- // Side wall lines
- GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
+ if (!m_xFlipped)
+ lineXTrans = -lineXTrans;
- if (!m_xFlipped)
- lineXTrans = -lineXTrans;
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
+ modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
- modelMatrix.scale(gridLineScaleY);
- itModelMatrix.scale(gridLineScaleY);
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
-#if !defined(QT_OPENGL_ES_2)
- modelMatrix.rotate(lineYRotation);
- itModelMatrix.rotate(lineYRotation);
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
-
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else {
+ modelMatrix.rotate(lineYRotation);
+ itModelMatrix.rotate(lineYRotation);
+ }
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
// Columns (= X)
if (m_axisCacheX.segmentCount() > 0) {
-#if defined(QT_OPENGL_ES_2)
- lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
-#endif
+ if (m_isOpenGLES)
+ lineXRotation = m_yRightAngleRotation;
+
// Floor lines
int gridLineCount = m_axisCacheX.gridLineCount();
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
- 0.0f);
-
- modelMatrix.scale(gridLineScaleZ);
- itModelMatrix.scale(gridLineScaleZ);
-
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
-
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_polarGraph) {
+ drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
+ 0.0f);
+
+ modelMatrix.scale(gridLineScaleZ);
+ itModelMatrix.scale(gridLineScaleZ);
+
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
+
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
- // Back wall lines
- GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
+ // Back wall lines
+ GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
- if (!m_zFlipped)
- lineZTrans = -lineZTrans;
-
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
+ if (!m_zFlipped)
+ lineZTrans = -lineZTrans;
- modelMatrix.scale(gridLineScaleY);
- itModelMatrix.scale(gridLineScaleY);
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- }
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else if (m_zFlipped) {
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
+ }
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
@@ -1683,8 +1774,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.scale(gridLineScaleX);
if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
}
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1695,20 +1786,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Side wall
@@ -1738,20 +1829,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
}
@@ -1811,7 +1902,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
} else {
shader = m_labelShader;
shader->bind();
- glEnable(GL_TEXTURE_2D);
+
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -1832,8 +1923,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
QVector3D positionZComp(0.0f, 0.0f, 0.0f);
if (m_axisCacheZ.segmentCount() > 0) {
int labelCount = m_axisCacheZ.labelCount();
- GLfloat labelXTrans = m_scaleXWithBackground + labelMargin;
- GLfloat labelYTrans = -backgroundMargin;
+ float labelXTrans = m_scaleXWithBackground + labelMargin;
+ float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground;
+ if (m_polarGraph) {
+ labelXTrans *= m_radialLabelOffset;
+ // YTrans up only if over background
+ if (m_radialLabelOffset < 1.0f)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ }
Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_xFlipped)
@@ -1843,7 +1940,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
if (labelAutoAngle == 0.0f) {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped)
labelRotation.setY(180.0f);
else
@@ -1855,7 +1952,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
} else {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
@@ -1920,10 +2017,34 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
// Draw the label here
- labelTrans.setZ(m_axisCacheZ.labelPosition(label));
+ if (m_polarGraph) {
+ float direction = m_zFlipped ? -1.0f : 1.0f;
+ labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label)
+ * -m_polarRadius
+ + m_drawer->scaledFontSize() + gridLineWidth) * direction);
+ } else {
+ labelTrans.setZ(m_axisCacheZ.labelPosition(label));
+ }
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.z())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleZWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setZ(labelTrans.z() - labelOverlap);
+ else
+ labelTrans.setZ(labelTrans.z() + labelOverlap);
+ }
+ }
m_dummyRenderItem.setTranslation(labelTrans);
- const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
if (drawSelection) {
QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
@@ -1938,7 +2059,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
}
if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
- labelTrans.setZ(0.0f);
+ if (m_polarGraph) {
+ float titleZ = -m_polarRadius / 2.0f;
+ if (m_zFlipped)
+ titleZ = -titleZ;
+ labelTrans.setZ(titleZ);
+ } else {
+ labelTrans.setZ(0.0f);
+ }
drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
@@ -1952,8 +2080,13 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheX.labelCount();
- GLfloat labelZTrans = m_scaleZWithBackground + labelMargin;
- GLfloat labelYTrans = -backgroundMargin;
+ float labelZTrans = 0.0f;
+ float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground;
+ if (m_polarGraph)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ else
+ labelZTrans = m_scaleZWithBackground + labelMargin;
+
Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_zFlipped)
@@ -1964,7 +2097,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
if (m_xFlipped)
labelRotation.setY(-90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_xFlipped)
labelRotation.setY(-90.0f);
else
@@ -1976,7 +2109,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation.setY(-90.0f);
else
labelRotation.setY(90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
@@ -2022,7 +2155,16 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
}
}
+
QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
+ if (m_polarGraph) {
+ if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped))
+ || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) {
+ totalRotation *= m_zRightAngleRotation;
+ } else {
+ totalRotation *= m_zRightAngleRotationNeg;
+ }
+ }
QVector3D labelTrans = QVector3D(0.0f,
labelYTrans,
@@ -2038,12 +2180,64 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
indexStep = 1;
}
float offsetValue = 0.0f;
+ bool showLastLabel = false;
+ QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
+ int lastLabelPosIndex = labelPositions.size() - 1;
+ if (labelPositions.size()
+ && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) {
+ // Avoid overlapping first and last label if they would get on same position
+ showLastLabel = true;
+ }
+
for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
// Draw the label here
- labelTrans.setX(m_axisCacheX.labelPosition(label));
- m_dummyRenderItem.setTranslation(labelTrans);
+ if (m_polarGraph) {
+ // Calculate angular position
+ if (label == lastLabelPosIndex && !showLastLabel)
+ continue;
+ float labelPosition = labelPositions.at(label);
+ qreal angle = labelPosition * M_PI * 2.0;
+ labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle)));
+ labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(angle)));
+ // Alignment depends on label angular position, as well as flips
+ Qt::AlignmentFlag vAlignment = Qt::AlignCenter;
+ Qt::AlignmentFlag hAlignment = Qt::AlignCenter;
+ const float centerMargin = 0.005f;
+ if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom;
+ else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop;
+
+ if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
+ else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight;
+ if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter)
+ vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop;
+ alignment = Qt::AlignmentFlag(vAlignment | hAlignment);
+ } else {
+ labelTrans.setX(m_axisCacheX.labelPosition(label));
+ }
const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label);
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.x())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleXWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setX(labelTrans.x() + labelOverlap);
+ else
+ labelTrans.setX(labelTrans.x() - labelOverlap);
+ }
+ }
+ m_dummyRenderItem.setTranslation(labelTrans);
if (drawSelection) {
QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
@@ -2059,8 +2253,20 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
if (!drawSelection && m_axisCacheX.isTitleVisible()) {
labelTrans.setX(0.0f);
+ bool radial = false;
+ if (m_polarGraph) {
+ if (m_xFlipped == m_zFlipped)
+ totalRotation *= m_zRightAngleRotation;
+ else
+ totalRotation *= m_zRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f);
+ labelTrans.setZ(-m_polarRadius);
+ radial = true;
+ }
drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
- activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader,
+ radial);
}
}
// Y Labels
@@ -2072,12 +2278,12 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheY.labelCount();
- GLfloat labelXTrans = m_scaleXWithBackground;
- GLfloat labelZTrans = m_scaleZWithBackground;
+ float labelXTrans = m_scaleXWithBackground;
+ float labelZTrans = m_scaleZWithBackground;
// Back & side wall
- GLfloat labelMarginXTrans = labelMargin;
- GLfloat labelMarginZTrans = labelMargin;
+ float labelMarginXTrans = labelMargin;
+ float labelMarginZTrans = labelMargin;
QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
Qt::AlignmentFlag backAlignment =
@@ -2134,7 +2340,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label);
- const GLfloat labelYTrans = m_axisCacheY.labelPosition(label);
+ float labelYTrans = m_axisCacheY.labelPosition(label);
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
@@ -2144,6 +2350,21 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
shader->setUniformValue(shader->color(), labelColor);
}
+ if (label == startIndex) {
+ // If the margin is small, adjust the position of the edge label to avoid
+ // overlapping with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelYTrans)
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleYWithBackground + labelMargin;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelYTrans += labelOverlap;
+ else
+ labelYTrans -= labelOverlap;
+ }
+ }
+
// Back wall
labelTransBack.setY(labelYTrans);
m_dummyRenderItem.setTranslation(labelTransBack);
@@ -2174,10 +2395,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
glDisable(GL_POLYGON_OFFSET_FILL);
- if (!drawSelection) {
- glDisable(GL_TEXTURE_2D);
+ if (!drawSelection)
glDisable(GL_BLEND);
- }
}
void Surface3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
@@ -2205,11 +2424,12 @@ void Surface3DRenderer::updateSelectionTextures()
void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache,
uint &lastSelectionId)
{
- // Create the selection ID image. Each grid corner gets 2x2 pixel area of
- // ID color so that each vertex (data point) has 4x4 pixel area of ID color
+ // Create the selection ID image. Each grid corner gets 1 pixel area of
+ // ID color so that each vertex (data point) has 2x2 pixel area of ID color,
+ // except the vertices on the edges.
const QRect &sampleSpace = cache->sampleSpace();
- int idImageWidth = (sampleSpace.width() - 1) * 4;
- int idImageHeight = (sampleSpace.height() - 1) * 4;
+ int idImageWidth = (sampleSpace.width() - 1) * 2;
+ int idImageHeight = (sampleSpace.height() - 1) * 2;
if (idImageHeight <= 0 || idImageWidth <= 0) {
cache->setSelectionIdRange(-1, -1);
@@ -2221,21 +2441,21 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache,
uint idStart = lastSelectionId;
uchar *bits = new uchar[idImageWidth * idImageHeight * 4 * sizeof(uchar)];
- for (int i = 0; i < idImageHeight; i += 4) {
- for (int j = 0; j < idImageWidth; j += 4) {
+ for (int i = 0; i < idImageHeight; i += 2) {
+ for (int j = 0; j < idImageWidth; j += 2) {
int p = (i * idImageWidth + j) * 4;
uchar r, g, b, a;
idToRGBA(lastSelectionId, &r, &g, &b, &a);
- fillIdCorner(&bits[p], r, g, b, a, stride);
+ fillIdCorner(&bits[p], r, g, b, a);
idToRGBA(lastSelectionId + 1, &r, &g, &b, &a);
- fillIdCorner(&bits[p + 8], r, g, b, a, stride);
+ fillIdCorner(&bits[p + 4], r, g, b, a);
idToRGBA(lastSelectionId + sampleSpace.width(), &r, &g, &b, &a);
- fillIdCorner(&bits[p + 2 * stride], r, g, b, a, stride);
+ fillIdCorner(&bits[p + stride], r, g, b, a);
idToRGBA(lastSelectionId + sampleSpace.width() + 1, &r, &g, &b, &a);
- fillIdCorner(&bits[p + 2 * stride + 8], r, g, b, a, stride);
+ fillIdCorner(&bits[p + stride + 4], r, g, b, a);
lastSelectionId++;
}
@@ -2263,24 +2483,12 @@ void Surface3DRenderer::initSelectionBuffer()
m_selectionDepthBuffer);
}
-void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride)
+void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a)
{
p[0] = r;
p[1] = g;
p[2] = b;
p[3] = a;
- p[4] = r;
- p[5] = g;
- p[6] = b;
- p[7] = a;
- p[stride + 0] = r;
- p[stride + 1] = g;
- p[stride + 2] = b;
- p[stride + 3] = a;
- p[stride + 4] = r;
- p[stride + 5] = g;
- p[stride + 6] = b;
- p[stride + 7] = a;
}
void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a)
@@ -2293,21 +2501,66 @@ void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a
void Surface3DRenderer::calculateSceneScalingFactors()
{
+ // Margin for background (the default 0.10 makes it 10% larger to avoid
+ // selection ball being drawn inside background)
+ if (m_requestedMargin < 0.0f) {
+ m_hBackgroundMargin = 0.1f;
+ m_vBackgroundMargin = 0.1f;
+ } else {
+ m_hBackgroundMargin = m_requestedMargin;
+ m_vBackgroundMargin = m_requestedMargin;
+ }
+ if (m_polarGraph) {
+ float polarMargin = calculatePolarBackgroundMargin();
+ m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin);
+ }
+
// Calculate scene scaling and translation factors
m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min());
- m_areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
- m_areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
- m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height());
- m_scaleX = m_graphAspectRatio * m_areaSize.width() / m_scaleFactor;
- m_scaleZ = m_graphAspectRatio * m_areaSize.height() / m_scaleFactor;
- m_scaleXWithBackground = m_scaleX + backgroundMargin - 1.0f;
- m_scaleZWithBackground = m_scaleZ + backgroundMargin - 1.0f;
-
- float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor;
- m_axisCacheX.setScale(factorScaler * m_areaSize.width());
- m_axisCacheZ.setScale(-factorScaler * m_areaSize.height());
- m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f);
- m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f);
+
+ float horizontalAspectRatio;
+ if (m_polarGraph)
+ horizontalAspectRatio = 1.0f;
+ else
+ horizontalAspectRatio = m_graphHorizontalAspectRatio;
+
+ QSizeF areaSize;
+ if (horizontalAspectRatio == 0.0f) {
+ areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
+ areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
+ } else {
+ areaSize.setHeight(1.0f);
+ areaSize.setWidth(horizontalAspectRatio);
+ }
+
+ float horizontalMaxDimension;
+ if (m_graphAspectRatio > 2.0f) {
+ horizontalMaxDimension = 2.0f;
+ m_scaleY = 2.0f / m_graphAspectRatio;
+ } else {
+ horizontalMaxDimension = m_graphAspectRatio;
+ m_scaleY = 1.0f;
+ }
+ if (m_polarGraph)
+ m_polarRadius = horizontalMaxDimension;
+
+ float scaleFactor = qMax(areaSize.width(), areaSize.height());
+ m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
+ m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
+
+ m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin;
+ m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin;
+ m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin;
+
+ m_axisCacheX.setScale(m_scaleX * 2.0f);
+ m_axisCacheY.setScale(m_scaleY * 2.0f);
+ m_axisCacheZ.setScale(-m_scaleZ * 2.0f);
+ m_axisCacheX.setTranslate(-m_scaleX);
+ m_axisCacheY.setTranslate(-m_scaleY);
+ m_axisCacheZ.setTranslate(m_scaleZ);
+
+ updateCameraViewport();
+ updateCustomItemPositions();
}
void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache)
@@ -2326,10 +2579,20 @@ void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dime
QSurfaceDataArray &dataArray = cache->dataArray();
const QRect &sampleSpace = cache->sampleSpace();
- if (cache->isFlatShadingEnabled())
- cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged);
- else
- cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged);
+ const QSurface3DSeries *currentSeries = cache->series();
+ QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
+ const QSurfaceDataArray &array = *dataProxy->array();
+
+ if (cache->isFlatShadingEnabled()) {
+ cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged, m_polarGraph);
+ if (cache->surfaceTexture())
+ cache->surfaceObject()->coarseUVs(array, dataArray);
+ } else {
+ cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged,
+ m_polarGraph);
+ if (cache->surfaceTexture())
+ cache->surfaceObject()->smoothUVs(array, dataArray);
+ }
}
void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series)
@@ -2339,6 +2602,11 @@ void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSe
m_selectionDirty = true;
}
+void Surface3DRenderer::updateFlipHorizontalGrid(bool flip)
+{
+ m_flipHorizontalGrid = flip;
+}
+
void Surface3DRenderer::resetClickedStatus()
{
m_clickedPosition = Surface3DController::invalidSelectionPosition();
@@ -2539,14 +2807,15 @@ void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual
handleShadowQualityChange();
-#if !defined(QT_OPENGL_ES_2)
updateDepthBuffer();
-#endif
}
void Surface3DRenderer::updateTextures()
{
- // Do nothing, but required as it is pure virtual on parent
+ Abstract3DRenderer::updateTextures();
+
+ if (m_polarGraph)
+ calculateSceneScalingFactors();
}
void Surface3DRenderer::updateSlicingActive(bool isSlicing)
@@ -2556,12 +2825,13 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing)
m_cachedIsSlicingActivated = isSlicing;
- if (!m_cachedIsSlicingActivated)
- initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize
+ if (!m_cachedIsSlicingActivated) {
+ // We need to re-init selection buffer in case there has been a resize
+ initSelectionBuffer();
+ initCursorPositionBuffer();
+ }
-#if !defined(QT_OPENGL_ES_2)
updateDepthBuffer(); // Re-init depth buffer as well
-#endif
m_selectionDirty = true;
@@ -2577,55 +2847,68 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &
Q_UNUSED(vertexShader);
Q_UNUSED(fragmentShader);
- // draw the shader for the surface according to smooth status, shadow and uniform color
- if (m_surfaceFlatShader)
- delete m_surfaceFlatShader;
- if (m_surfaceSmoothShader)
- delete m_surfaceSmoothShader;
- if (m_surfaceSliceFlatShader)
- delete m_surfaceSliceFlatShader;
- if (m_surfaceSliceSmoothShader)
- delete m_surfaceSliceSmoothShader;
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex"));
- } else {
- m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurface"));
- }
- m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurface"));
- if (m_flatSupported) {
+ delete m_surfaceFlatShader;
+ delete m_surfaceSmoothShader;
+ delete m_surfaceTexturedSmoothShader;
+ delete m_surfaceTexturedFlatShader;
+ delete m_surfaceSliceFlatShader;
+ delete m_surfaceSliceSmoothShader;
+
+ if (!m_isOpenGLES) {
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceShadowFlat"));
+ m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex"));
+ m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentTexturedSurfaceShadow"));
+ } else {
+ m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurface"));
+ m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ }
+ m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurface"));
+ if (m_flatSupported) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceShadowFlat"));
+ m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
+ QStringLiteral(":/shaders/fragmentTexturedSurfaceShadowFlat"));
+ } else {
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceFlat"));
+ m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceTexturedFlat"));
+ }
+ m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceFlat"));
} else {
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceFlat"));
+ m_surfaceFlatShader = 0;
+ m_surfaceSliceFlatShader = 0;
+ m_surfaceTexturedFlatShader = 0;
}
- m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceFlat"));
} else {
- m_surfaceFlatShader = 0;
- m_surfaceSliceFlatShader = 0;
+ m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
+ m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+ m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+ m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
+ m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
}
-#else
- m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
- m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
- m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
-#endif
+
m_surfaceSmoothShader->initialize();
m_surfaceSliceSmoothShader->initialize();
+ m_surfaceTexturedSmoothShader->initialize();
if (m_flatSupported) {
m_surfaceFlatShader->initialize();
m_surfaceSliceFlatShader->initialize();
+ m_surfaceTexturedFlatShader->initialize();
}
}
@@ -2660,45 +2943,33 @@ void Surface3DRenderer::initSurfaceShaders()
handleShadowQualityChange();
}
-void Surface3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
-{
- if (m_labelShader)
- delete m_labelShader;
- m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
- m_labelShader->initialize();
-}
-
-#if !defined(QT_OPENGL_ES_2)
void Surface3DRenderer::initDepthShader()
{
- if (m_depthShader)
+ if (!m_isOpenGLES) {
delete m_depthShader;
- m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
- QStringLiteral(":/shaders/fragmentDepth"));
- m_depthShader->initialize();
+ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
+ QStringLiteral(":/shaders/fragmentDepth"));
+ m_depthShader->initialize();
+ }
}
void Surface3DRenderer::updateDepthBuffer()
{
- m_textureHelper->deleteTexture(&m_depthTexture);
- m_textureHelper->deleteTexture(&m_depthModelTexture);
+ if (!m_isOpenGLES) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
- if (m_primarySubViewport.size().isEmpty())
- return;
+ if (m_primarySubViewport.size().isEmpty())
+ return;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
- m_depthFrameBuffer,
- m_shadowQualityMultiplier);
- m_textureHelper->fillDepthTexture(m_depthTexture, m_primarySubViewport.size(),
- m_shadowQualityMultiplier, 1.0f);
- m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(),
- m_shadowQualityMultiplier);
- if (!m_depthTexture || !m_depthModelTexture)
- lowerShadowQuality();
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
+ m_depthFrameBuffer,
+ m_shadowQualityMultiplier);
+ if (!m_depthTexture)
+ lowerShadowQuality();
+ }
}
}
-#endif
QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &position,
bool isAbsolute)
@@ -2707,15 +2978,44 @@ QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &posit
float yTrans = 0.0f;
float zTrans = 0.0f;
if (!isAbsolute) {
- xTrans = m_axisCacheX.positionAt(position.x());
+ if (m_polarGraph) {
+ calculatePolarXZ(position, xTrans, zTrans);
+ } else {
+ xTrans = m_axisCacheX.positionAt(position.x());
+ zTrans = m_axisCacheZ.positionAt(position.z());
+ }
yTrans = m_axisCacheY.positionAt(position.y());
- zTrans = m_axisCacheZ.positionAt(position.z());
} else {
xTrans = position.x() * m_scaleX;
- yTrans = position.y();
- zTrans = position.z() * m_scaleZ;
+ yTrans = position.y() * m_scaleY;
+ zTrans = position.z() * -m_scaleZ;
}
return QVector3D(xTrans, yTrans, zTrans);
}
+void Surface3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels)
+{
+ Abstract3DRenderer::updateAxisLabels(orientation, labels);
+
+ // Angular axis label dimensions affect the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Surface3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, bool visible)
+{
+ Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible);
+
+ // Angular axis title existence affects the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Surface3DRenderer::updateMargin(float margin)
+{
+ Abstract3DRenderer::updateMargin(margin);
+ calculateSceneScalingFactors();
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h
index efa8ff7e..57b6f9e6 100644
--- a/src/datavisualization/engine/surface3drenderer_p.h
+++ b/src/datavisualization/engine/surface3drenderer_p.h
@@ -51,19 +51,16 @@ private:
ShaderHelper *m_backgroundShader;
ShaderHelper *m_surfaceFlatShader;
ShaderHelper *m_surfaceSmoothShader;
+ ShaderHelper *m_surfaceTexturedSmoothShader;
+ ShaderHelper *m_surfaceTexturedFlatShader;
ShaderHelper *m_surfaceGridShader;
ShaderHelper *m_surfaceSliceFlatShader;
ShaderHelper *m_surfaceSliceSmoothShader;
ShaderHelper *m_selectionShader;
- ShaderHelper *m_labelShader;
- GLfloat m_heightNormalizer;
- GLfloat m_scaleFactor;
- GLfloat m_scaleX;
- GLfloat m_scaleZ;
- GLfloat m_scaleXWithBackground;
- GLfloat m_scaleZWithBackground;
- GLuint m_depthTexture;
- GLuint m_depthModelTexture;
+ float m_heightNormalizer;
+ float m_scaleX;
+ float m_scaleY;
+ float m_scaleZ;
GLuint m_depthFrameBuffer;
GLuint m_selectionFrameBuffer;
GLuint m_selectionDepthBuffer;
@@ -73,13 +70,12 @@ private:
bool m_selectionActive;
AbstractRenderItem m_dummyRenderItem;
GLint m_shadowQualityMultiplier;
- QSizeF m_areaSize;
- bool m_hasHeightAdjustmentChanged;
QPoint m_selectedPoint;
QSurface3DSeries *m_selectedSeries;
QPoint m_clickedPosition;
bool m_selectionTexturesDirty;
GLuint m_noShadowTexture;
+ bool m_flipHorizontalGrid;
public:
explicit Surface3DRenderer(Surface3DController *controller);
@@ -87,6 +83,7 @@ public:
void updateData();
void updateSeries(const QList<QAbstract3DSeries *> &seriesList);
+ void updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList);
SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
void cleanCache(SeriesRenderCache *cache);
void updateSelectionMode(QAbstract3DGraph::SelectionFlags mode);
@@ -95,14 +92,22 @@ public:
void updateScene(Q3DScene *scene);
void updateSlicingActive(bool isSlicing);
void updateSelectedPoint(const QPoint &position, QSurface3DSeries *series);
+ void updateFlipHorizontalGrid(bool flip);
inline QPoint clickedPosition() const { return m_clickedPosition; }
void resetClickedStatus();
QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
+ void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels);
+ void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible);
+ void updateMargin(float margin);
void render(GLuint defaultFboHandle = 0);
protected:
void initializeOpenGL();
+ virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
signals:
void flatShadingSupportedChanged(bool supported);
@@ -128,7 +133,6 @@ private:
void calculateSceneScalingFactors();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
- void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
void initSelectionShaders();
void initSurfaceShaders();
void initSelectionBuffer();
@@ -136,13 +140,11 @@ private:
void updateSelectionTextures();
void createSelectionTexture(SurfaceSeriesRenderCache *cache, uint &lastSelectionId);
void idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a);
- void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride);
+ void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a);
void surfacePointSelected(const QPoint &point);
void updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point, bool label);
QPoint selectionIdToSurfacePoint(uint id);
-#if !defined(QT_OPENGL_ES_2)
void updateDepthBuffer();
-#endif
void emitSelectedPointChanged(QPoint position);
Q_DISABLE_COPY(Surface3DRenderer)
diff --git a/src/datavisualization/engine/surfaceseriesrendercache.cpp b/src/datavisualization/engine/surfaceseriesrendercache.cpp
index 1cce5288..f1ad752a 100644
--- a/src/datavisualization/engine/surfaceseriesrendercache.cpp
+++ b/src/datavisualization/engine/surfaceseriesrendercache.cpp
@@ -39,7 +39,8 @@ SurfaceSeriesRenderCache::SurfaceSeriesRenderCache(QAbstract3DSeries *series,
m_sliceSelectionPointer(0),
m_mainSelectionPointer(0),
m_slicePointerActive(false),
- m_mainPointerActive(false)
+ m_mainPointerActive(false),
+ m_surfaceTexture(0)
{
}
@@ -62,8 +63,10 @@ void SurfaceSeriesRenderCache::populate(bool newSeries)
void SurfaceSeriesRenderCache::cleanup(TextureHelper *texHelper)
{
- if (QOpenGLContext::currentContext())
+ if (QOpenGLContext::currentContext()) {
texHelper->deleteTexture(&m_selectionTexture);
+ texHelper->deleteTexture(&m_surfaceTexture);
+ }
delete m_surfaceObj;
delete m_sliceSurfaceObj;
diff --git a/src/datavisualization/engine/surfaceseriesrendercache_p.h b/src/datavisualization/engine/surfaceseriesrendercache_p.h
index b6254a75..4d04149f 100644
--- a/src/datavisualization/engine/surfaceseriesrendercache_p.h
+++ b/src/datavisualization/engine/surfaceseriesrendercache_p.h
@@ -85,6 +85,8 @@ public:
inline bool slicePointerActive() const { return m_slicePointerActive; }
inline void setMainPointerActivity(bool activity) { m_mainPointerActive = activity; }
inline bool mainPointerActive() const { return m_mainPointerActive; }
+ inline void setSurfaceTexture(GLuint texture) { m_surfaceTexture = texture; }
+ inline GLuint surfaceTexture() const { return m_surfaceTexture; }
protected:
bool m_surfaceVisible;
@@ -105,6 +107,7 @@ protected:
SelectionPointer *m_mainSelectionPointer;
bool m_slicePointerActive;
bool m_mainPointerActive;
+ GLuint m_surfaceTexture;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/global/datavisualizationglobal_p.h b/src/datavisualization/global/datavisualizationglobal_p.h
index abdac998..16bf7c6d 100644
--- a/src/datavisualization/global/datavisualizationglobal_p.h
+++ b/src/datavisualization/global/datavisualizationglobal_p.h
@@ -52,6 +52,7 @@ static const float gridLineOffset = 0.0035f; // Offset for lifting grid lines of
// y position is added to the minimum height (or can be thought to be that much above or below the camera)
static const QVector3D defaultLightPos = QVector3D(0.0f, 0.5f, 0.0f);
static const QVector3D zeroVector = QVector3D(0.0f, 0.0f, 0.0f);
+static const QVector3D oneVector = QVector3D(1.0f, 1.0f, 1.0f);
static const QVector3D upVector = QVector3D(0.0f, 1.0f, 0.0f);
static const QVector3D cameraDistanceVector = QVector3D(0.0f, 0.0f, cameraDistance);
static const QQuaternion identityQuaternion;
@@ -69,6 +70,7 @@ static const GLfloat gradientTextureWidth = 2.0f;
static const GLfloat uniformTextureHeight = 64.0f;
static const GLfloat uniformTextureWidth = 2.0f;
static const GLfloat labelMargin = 0.05f;
+static const GLfloat gridLineWidth = 0.005f;
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/global/global.pri b/src/datavisualization/global/global.pri
index 00a3eb65..a43ffb20 100644
--- a/src/datavisualization/global/global.pri
+++ b/src/datavisualization/global/global.pri
@@ -1,3 +1,5 @@
HEADERS += \
$$PWD/qdatavisualizationglobal.h \
$$PWD/datavisualizationglobal_p.h
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/global/qdatavisualizationglobal.h b/src/datavisualization/global/qdatavisualizationglobal.h
index c637f408..a7f96361 100644
--- a/src/datavisualization/global/qdatavisualizationglobal.h
+++ b/src/datavisualization/global/qdatavisualizationglobal.h
@@ -21,11 +21,11 @@
#include <QtCore/qglobal.h>
-#define QT_DATAVISUALIZATION_VERSION_STR "1.1.1"
+#define QT_DATAVISUALIZATION_VERSION_STR "1.2.0"
/*
QT_DATAVISUALIZATION_VERSION is (major << 16) + (minor << 8) + patch.
*/
-#define QT_DATAVISUALIZATION_VERSION 0x010101
+#define QT_DATAVISUALIZATION_VERSION 0x010200
/*
can be used like #if (QT_DATAVISUALIZATION_VERSION >= QT_DATAVISUALIZATION_VERSION_CHECK(1, 0, 0))
*/
diff --git a/src/datavisualization/input/input.pri b/src/datavisualization/input/input.pri
index 5a4c4a76..f6b9fd6a 100644
--- a/src/datavisualization/input/input.pri
+++ b/src/datavisualization/input/input.pri
@@ -10,3 +10,5 @@ SOURCES += \
$$PWD/qabstract3dinputhandler.cpp \
$$PWD/q3dinputhandler.cpp \
$$PWD/qtouch3dinputhandler.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/input/q3dinputhandler.cpp b/src/datavisualization/input/q3dinputhandler.cpp
index f0044096..43cee84e 100644
--- a/src/datavisualization/input/q3dinputhandler.cpp
+++ b/src/datavisualization/input/q3dinputhandler.cpp
@@ -18,19 +18,20 @@
#include "datavisualizationglobal_p.h"
#include "q3dinputhandler_p.h"
+#include "abstract3dcontroller_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-const int minZoomLevel = 10;
-const int halfSizeZoomLevel = 50;
-const int oneToOneZoomLevel = 100;
-const int maxZoomLevel = 500;
+static const int halfSizeZoomLevel = 50;
+static const int oneToOneZoomLevel = 100;
+static const int driftTowardCenterLevel = 175;
+static const float wheelZoomDrift = 0.1f;
-const int nearZoomRangeDivider = 12;
-const int midZoomRangeDivider = 60;
-const int farZoomRangeDivider = 120;
+static const int nearZoomRangeDivider = 12;
+static const int midZoomRangeDivider = 60;
+static const int farZoomRangeDivider = 120;
-const float rotationSpeed = 100.0f;
+static const float rotationSpeed = 100.0f;
/*!
* \class Q3DInputHandler
@@ -55,12 +56,61 @@ const float rotationSpeed = 100.0f;
* \l {QAbstract3DGraph::selectionMode}{selection mode}.
* \row
* \li Mouse wheel
- * \li Zoom in/out within default range (10...500%).
+ * \li Zoom in/out within the allowable zoom range set for Q3DCamera.
* \row
* \li Left click on the primary view when the secondary view is visible
* \li Closes the secondary view.
* \note Secondary view is available only for Q3DBars and Q3DSurface graphs.
* \endtable
+ *
+ * Rotation, zoom, and selection can each be individually disabled using
+ * corresponding properties of this class.
+ */
+
+/*!
+ * \qmltype InputHandler3D
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.2
+ * \ingroup datavisualization_qml
+ * \instantiates Q3DInputHandler
+ * \brief Basic wheel mouse based input handler.
+ *
+ * InputHandler3D is the basic input handler for wheel mouse type of input devices.
+ *
+ * See Q3DInputHandler documentation for more details.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::rotationEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph rotation.
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::zoomEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph zooming.
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::selectionEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows selection from the graph.
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::zoomAtTargetEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if zooming changes the camera target to the position of the input
+ * at the time of the zoom.
+ * Defaults to \c{true}.
*/
/*!
@@ -92,29 +142,35 @@ void Q3DInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos
Q_UNUSED(mousePos);
#else
if (Qt::LeftButton == event->button()) {
- if (scene()->isSlicingActive()) {
- if (scene()->isPointInPrimarySubView(mousePos))
+ if (isSelectionEnabled()) {
+ if (scene()->isSlicingActive()) {
+ if (scene()->isPointInPrimarySubView(mousePos))
+ setInputView(InputViewOnPrimary);
+ else if (scene()->isPointInSecondarySubView(mousePos))
+ setInputView(InputViewOnSecondary);
+ else
+ setInputView(InputViewNone);
+ } else {
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ setInputPosition(mousePos);
+ scene()->setSelectionQueryPosition(mousePos);
setInputView(InputViewOnPrimary);
- else if (scene()->isPointInSecondarySubView(mousePos))
- setInputView(InputViewOnSecondary);
- else
- setInputView(InputViewNone);
- } else {
- // update mouse positions to prevent jumping when releasing or repressing a button
- setInputPosition(mousePos);
- scene()->setSelectionQueryPosition(mousePos);
- setInputView(InputViewOnPrimary);
- d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ }
}
} else if (Qt::MiddleButton == event->button()) {
- // reset rotations
- setInputPosition(QPoint(0, 0));
+ if (isRotationEnabled()) {
+ // reset rotations
+ setInputPosition(QPoint(0, 0));
+ }
} else if (Qt::RightButton == event->button()) {
- // disable rotating when in slice view
- if (!scene()->isSlicingActive())
- d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
- // update mouse positions to prevent jumping when releasing or repressing a button
- setInputPosition(mousePos);
+ if (isRotationEnabled()) {
+ // disable rotating when in slice view
+ if (!scene()->isSlicingActive())
+ d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ setInputPosition(mousePos);
+ }
}
#endif
}
@@ -148,7 +204,8 @@ void Q3DInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
#if defined(Q_OS_IOS)
Q_UNUSED(mousePos);
#else
- if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState) {
+ if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState
+ && isRotationEnabled()) {
// Calculate mouse movement since last frame
float xRotation = scene()->activeCamera()->xRotation();
float yRotation = scene()->activeCamera()->yRotation();
@@ -174,34 +231,191 @@ void Q3DInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
*/
void Q3DInputHandler::wheelEvent(QWheelEvent *event)
{
- // disable zooming if in slice view
- if (scene()->isSlicingActive())
- return;
+ if (isZoomEnabled()) {
+ // disable zooming if in slice view
+ if (scene()->isSlicingActive())
+ return;
- // Adjust zoom level based on what zoom range we're in.
- int zoomLevel = scene()->activeCamera()->zoomLevel();
- if (zoomLevel > oneToOneZoomLevel)
- zoomLevel += event->angleDelta().y() / nearZoomRangeDivider;
- else if (zoomLevel > halfSizeZoomLevel)
- zoomLevel += event->angleDelta().y() / midZoomRangeDivider;
- else
- zoomLevel += event->angleDelta().y() / farZoomRangeDivider;
- if (zoomLevel > maxZoomLevel)
- zoomLevel = maxZoomLevel;
- else if (zoomLevel < minZoomLevel)
- zoomLevel = minZoomLevel;
+ // Adjust zoom level based on what zoom range we're in.
+ Q3DCamera *camera = scene()->activeCamera();
+ int zoomLevel = int(camera->zoomLevel());
+ const int minZoomLevel = int(camera->minZoomLevel());
+ const int maxZoomLevel = int(camera->maxZoomLevel());
+ if (zoomLevel > oneToOneZoomLevel)
+ zoomLevel += event->angleDelta().y() / nearZoomRangeDivider;
+ else if (zoomLevel > halfSizeZoomLevel)
+ zoomLevel += event->angleDelta().y() / midZoomRangeDivider;
+ else
+ zoomLevel += event->angleDelta().y() / farZoomRangeDivider;
+ zoomLevel = qBound(minZoomLevel, zoomLevel, maxZoomLevel);
- scene()->activeCamera()->setZoomLevel(zoomLevel);
+ if (isZoomAtTargetEnabled()) {
+ scene()->setGraphPositionQuery(event->pos());
+ d_ptr->m_zoomAtTargetPending = true;
+ // If zoom at target is enabled, we don't want to zoom yet, as that causes
+ // jitter. Instead, we zoom next frame, when we apply the camera position.
+ d_ptr->m_requestedZoomLevel = zoomLevel;
+ d_ptr->m_driftMultiplier = wheelZoomDrift;
+ } else {
+ camera->setZoomLevel(zoomLevel);
+ }
+ }
+}
+
+/*!
+ * \property Q3DInputHandler::rotationEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph rotation.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setRotationEnabled(bool enable)
+{
+ if (d_ptr->m_rotationEnabled != enable) {
+ d_ptr->m_rotationEnabled = enable;
+ emit rotationEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isRotationEnabled() const
+{
+ return d_ptr->m_rotationEnabled;
+}
+
+/*!
+ * \property Q3DInputHandler::zoomEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph zooming.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setZoomEnabled(bool enable)
+{
+ if (d_ptr->m_zoomEnabled != enable) {
+ d_ptr->m_zoomEnabled = enable;
+ emit zoomEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isZoomEnabled() const
+{
+ return d_ptr->m_zoomEnabled;
+}
+
+/*!
+ * \property Q3DInputHandler::selectionEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows selection from the graph.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setSelectionEnabled(bool enable)
+{
+ if (d_ptr->m_selectionEnabled != enable) {
+ d_ptr->m_selectionEnabled = enable;
+ emit selectionEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isSelectionEnabled() const
+{
+ return d_ptr->m_selectionEnabled;
+}
+
+/*!
+ * \property Q3DInputHandler::zoomAtTargetEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if zooming should change the camera target so that the zoomed point
+ * of the graph stays at the same location after the zoom.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setZoomAtTargetEnabled(bool enable)
+{
+ if (d_ptr->m_zoomAtTargetEnabled != enable) {
+ d_ptr->m_zoomAtTargetEnabled = enable;
+ emit zoomAtTargetEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isZoomAtTargetEnabled() const
+{
+ return d_ptr->m_zoomAtTargetEnabled;
}
Q3DInputHandlerPrivate::Q3DInputHandlerPrivate(Q3DInputHandler *q)
: q_ptr(q),
- m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone)
+ m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone),
+ m_rotationEnabled(true),
+ m_zoomEnabled(true),
+ m_selectionEnabled(true),
+ m_zoomAtTargetEnabled(true),
+ m_zoomAtTargetPending(false),
+ m_controller(0),
+ m_requestedZoomLevel(0.0f),
+ m_driftMultiplier(0.0f)
{
+ QObject::connect(q, &QAbstract3DInputHandler::sceneChanged,
+ this, &Q3DInputHandlerPrivate::handleSceneChange);
}
Q3DInputHandlerPrivate::~Q3DInputHandlerPrivate()
{
}
+void Q3DInputHandlerPrivate::handleSceneChange(Q3DScene *scene)
+{
+ if (scene) {
+ if (m_controller) {
+ QObject::disconnect(m_controller, &Abstract3DController::queriedGraphPositionChanged,
+ this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange);
+ }
+
+ m_controller = qobject_cast<Abstract3DController *>(scene->parent());
+
+ if (m_controller) {
+ QObject::connect(m_controller, &Abstract3DController::queriedGraphPositionChanged,
+ this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange);
+ }
+ }
+}
+
+void Q3DInputHandlerPrivate::handleQueriedGraphPositionChange()
+{
+ if (m_zoomAtTargetPending) {
+ // Check if the zoom point is on graph
+ QVector3D newTarget = m_controller->queriedGraphPosition();
+ float currentZoom = m_requestedZoomLevel;
+ float previousZoom = q_ptr->scene()->activeCamera()->zoomLevel();
+ q_ptr->scene()->activeCamera()->setZoomLevel(currentZoom);
+ float diffAdj = 0.0f;
+
+ // If zooming in/out outside the graph, or zooming out after certain point,
+ // move towards the center.
+ if ((qAbs(newTarget.x()) > 1.0f
+ || qAbs(newTarget.y()) > 1.0f
+ || qAbs(newTarget.z()) > 1.0f)
+ || (previousZoom > currentZoom && currentZoom <= driftTowardCenterLevel)) {
+ newTarget = zeroVector;
+ // Add some extra correction so that we actually reach the center eventually
+ diffAdj = m_driftMultiplier;
+ if (previousZoom > currentZoom)
+ diffAdj *= 2.0f; // Correct towards center little more when zooming out
+ }
+
+ float zoomFraction = 1.0f - (previousZoom / currentZoom);
+
+ // Adjust camera towards the zoom point, attempting to keep the cursor at same graph point
+ QVector3D oldTarget = q_ptr->scene()->activeCamera()->target();
+ QVector3D origDiff = newTarget - oldTarget;
+ QVector3D diff = origDiff * zoomFraction + (origDiff.normalized() * diffAdj);
+ if (diff.length() > origDiff.length())
+ diff = origDiff;
+ q_ptr->scene()->activeCamera()->setTarget(oldTarget + diff);
+
+ if (q_ptr->scene()->selectionQueryPosition() == Q3DScene::invalidSelectionPoint())
+ m_zoomAtTargetPending = false;
+ }
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/input/q3dinputhandler.h b/src/datavisualization/input/q3dinputhandler.h
index 118bd829..5423b4ea 100644
--- a/src/datavisualization/input/q3dinputhandler.h
+++ b/src/datavisualization/input/q3dinputhandler.h
@@ -28,17 +28,36 @@ class Q3DInputHandlerPrivate;
class QT_DATAVISUALIZATION_EXPORT Q3DInputHandler : public QAbstract3DInputHandler
{
Q_OBJECT
+ Q_PROPERTY(bool rotationEnabled READ isRotationEnabled WRITE setRotationEnabled NOTIFY rotationEnabledChanged)
+ Q_PROPERTY(bool zoomEnabled READ isZoomEnabled WRITE setZoomEnabled NOTIFY zoomEnabledChanged)
+ Q_PROPERTY(bool selectionEnabled READ isSelectionEnabled WRITE setSelectionEnabled NOTIFY selectionEnabledChanged)
+ Q_PROPERTY(bool zoomAtTargetEnabled READ isZoomAtTargetEnabled WRITE setZoomAtTargetEnabled NOTIFY zoomAtTargetEnabledChanged)
public:
explicit Q3DInputHandler(QObject *parent = 0);
virtual ~Q3DInputHandler();
+ void setRotationEnabled(bool enable);
+ bool isRotationEnabled() const;
+ void setZoomEnabled(bool enable);
+ bool isZoomEnabled() const;
+ void setSelectionEnabled(bool enable);
+ bool isSelectionEnabled() const;
+ void setZoomAtTargetEnabled(bool enable);
+ bool isZoomAtTargetEnabled() const;
+
// Input event listeners
virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos);
virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos);
virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos);
virtual void wheelEvent(QWheelEvent *event);
+signals:
+ void rotationEnabledChanged(bool enable);
+ void zoomEnabledChanged(bool enable);
+ void selectionEnabledChanged(bool enable);
+ void zoomAtTargetEnabledChanged(bool enable);
+
private:
Q_DISABLE_COPY(Q3DInputHandler)
diff --git a/src/datavisualization/input/q3dinputhandler_p.h b/src/datavisualization/input/q3dinputhandler_p.h
index a9b27307..79b1c8dd 100644
--- a/src/datavisualization/input/q3dinputhandler_p.h
+++ b/src/datavisualization/input/q3dinputhandler_p.h
@@ -34,15 +34,36 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-class Q3DInputHandlerPrivate
+class Abstract3DController;
+
+class Q3DInputHandlerPrivate : public QObject
{
+ Q_OBJECT
public:
Q3DInputHandlerPrivate(Q3DInputHandler *q);
~Q3DInputHandlerPrivate();
-public:
+public slots:
+ void handleSceneChange(Q3DScene *scene);
+ void handleQueriedGraphPositionChange();
+
+private:
Q3DInputHandler *q_ptr;
+protected:
QAbstract3DInputHandlerPrivate::InputState m_inputState;
+
+ bool m_rotationEnabled;
+ bool m_zoomEnabled;
+ bool m_selectionEnabled;
+ bool m_zoomAtTargetEnabled;
+ bool m_zoomAtTargetPending;
+
+ Abstract3DController *m_controller; // Not owned
+
+ float m_requestedZoomLevel;
+ float m_driftMultiplier;
+
+ friend class Q3DInputHandler;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp
index 30f31d4a..357a6f3e 100644
--- a/src/datavisualization/input/qtouch3dinputhandler.cpp
+++ b/src/datavisualization/input/qtouch3dinputhandler.cpp
@@ -22,17 +22,16 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-const float maxTapAndHoldJitter = 20.0f;
-const int maxPinchJitter = 10;
+static const float maxTapAndHoldJitter = 20.0f;
+static const int maxPinchJitter = 10;
#if defined (Q_OS_ANDROID) || defined(Q_OS_IOS)
-const int maxSelectionJitter = 10;
+static const int maxSelectionJitter = 10;
#else
-const int maxSelectionJitter = 5;
+static const int maxSelectionJitter = 5;
#endif
-const int tapAndHoldTime = 250;
-const float rotationSpeed = 200.0f;
-const int minZoomLevel = 10;
-const int maxZoomLevel = 500;
+static const int tapAndHoldTime = 250;
+static const float rotationSpeed = 200.0f;
+static const float touchZoomDrift = 0.02f;
/*!
* \class QTouch3DInputHandler
@@ -60,12 +59,29 @@ const int maxZoomLevel = 500;
* \li Same as tap.
* \row
* \li Pinch
- * \li Zoom in/out within default range (10...500%).
+ * \li Zoom in/out within the allowable zoom range set for Q3DCamera.
* \row
* \li Tap on the primary view when the secondary view is visible
* \li Closes the secondary view.
* \note Secondary view is available only for Q3DBars and Q3DSurface graphs.
* \endtable
+ *
+ * Rotation, zoom, and selection can each be individually disabled using
+ * corresponding Q3DInputHandler properties.
+ */
+
+/*!
+ * \qmltype TouchInputHandler3D
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.2
+ * \ingroup datavisualization_qml
+ * \instantiates QTouch3DInputHandler
+ * \inherits InputHandler3D
+ * \brief Basic touch display based input handler.
+ *
+ * TouchInputHandler3D is the basic input handler for touch screen devices.
+ *
+ * See QTouch3DInputHandler documentation for more details.
*/
/*!
@@ -97,36 +113,46 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event)
if (!scene()->isSlicingActive() && points.count() == 2) {
d_ptr->m_holdTimer->stop();
QPointF distance = points.at(0).pos() - points.at(1).pos();
- d_ptr->handlePinchZoom(distance.manhattanLength());
+ QPoint midPoint = ((points.at(0).pos() + points.at(1).pos()) / 2.0).toPoint();
+ d_ptr->handlePinchZoom(distance.manhattanLength(), midPoint);
} else if (points.count() == 1) {
QPointF pointerPos = points.at(0).pos();
if (event->type() == QEvent::TouchBegin) {
// Flush input state
d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
if (scene()->isSlicingActive()) {
- if (scene()->isPointInPrimarySubView(pointerPos.toPoint()))
- setInputView(InputViewOnPrimary);
- else if (scene()->isPointInSecondarySubView(pointerPos.toPoint()))
- setInputView(InputViewOnSecondary);
- else
- setInputView(InputViewNone);
+ if (isSelectionEnabled()) {
+ if (scene()->isPointInPrimarySubView(pointerPos.toPoint()))
+ setInputView(InputViewOnPrimary);
+ else if (scene()->isPointInSecondarySubView(pointerPos.toPoint()))
+ setInputView(InputViewOnSecondary);
+ else
+ setInputView(InputViewNone);
+ }
} else {
// Handle possible tap-and-hold selection
- d_ptr->m_startHoldPos = pointerPos;
- d_ptr->m_touchHoldPos = d_ptr->m_startHoldPos;
- d_ptr->m_holdTimer->start();
- setInputView(InputViewOnPrimary);
+ if (isSelectionEnabled()) {
+ d_ptr->m_startHoldPos = pointerPos;
+ d_ptr->m_touchHoldPos = d_ptr->m_startHoldPos;
+ d_ptr->m_holdTimer->start();
+ setInputView(InputViewOnPrimary);
+ }
// Start rotating
- d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
- setInputPosition(pointerPos.toPoint());
+ if (isRotationEnabled()) {
+ d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
+ setInputPosition(pointerPos.toPoint());
+ setInputView(InputViewOnPrimary);
+ }
}
} else if (event->type() == QEvent::TouchEnd) {
setInputView(InputViewNone);
d_ptr->m_holdTimer->stop();
// Handle possible selection
if (!scene()->isSlicingActive()
- && QAbstract3DInputHandlerPrivate::InputStatePinching != d_ptr->m_inputState)
+ && QAbstract3DInputHandlerPrivate::InputStatePinching
+ != d_ptr->m_inputState) {
d_ptr->handleSelection(pointerPos);
+ }
} else if (event->type() == QEvent::TouchUpdate) {
if (!scene()->isSlicingActive()) {
d_ptr->m_touchHoldPos = pointerPos;
@@ -140,7 +166,8 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event)
}
QTouch3DInputHandlerPrivate::QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q)
- : q_ptr(q),
+ : Q3DInputHandlerPrivate(q),
+ q_ptr(q),
m_holdTimer(0),
m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone)
{
@@ -156,54 +183,71 @@ QTouch3DInputHandlerPrivate::~QTouch3DInputHandlerPrivate()
delete m_holdTimer;
}
-void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance)
+void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance, const QPoint &pos)
{
- int newDistance = distance;
- int prevDist = q_ptr->prevDistance();
- if (prevDist > 0 && qAbs(prevDist - newDistance) < maxPinchJitter)
- return;
- m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching;
- Q3DCamera *camera = q_ptr->scene()->activeCamera();
- int zoomLevel = camera->zoomLevel();
- float zoomRate = qSqrt(qSqrt(zoomLevel));
- if (newDistance > prevDist)
- zoomLevel += zoomRate;
- else
- zoomLevel -= zoomRate;
- if (zoomLevel > maxZoomLevel)
- zoomLevel = maxZoomLevel;
- else if (zoomLevel < minZoomLevel)
- zoomLevel = minZoomLevel;
- camera->setZoomLevel(zoomLevel);
- q_ptr->setPrevDistance(newDistance);
+ if (q_ptr->isZoomEnabled()) {
+ int newDistance = distance;
+ int prevDist = q_ptr->prevDistance();
+ if (prevDist > 0 && qAbs(prevDist - newDistance) < maxPinchJitter)
+ return;
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching;
+ Q3DCamera *camera = q_ptr->scene()->activeCamera();
+ int zoomLevel = int(camera->zoomLevel());
+ const int minZoomLevel = int(camera->minZoomLevel());
+ const int maxZoomLevel = int(camera->maxZoomLevel());
+ float zoomRate = qSqrt(qSqrt(zoomLevel));
+ if (newDistance > prevDist)
+ zoomLevel += zoomRate;
+ else
+ zoomLevel -= zoomRate;
+ zoomLevel = qBound(minZoomLevel, zoomLevel, maxZoomLevel);
+
+ if (q_ptr->isZoomAtTargetEnabled()) {
+ q_ptr->scene()->setGraphPositionQuery(pos);
+ m_zoomAtTargetPending = true;
+ // If zoom at target is enabled, we don't want to zoom yet, as that causes
+ // jitter. Instead, we zoom next frame, when we apply the camera position.
+ m_requestedZoomLevel = zoomLevel;
+ m_driftMultiplier = touchZoomDrift;
+ } else {
+ camera->setZoomLevel(zoomLevel);
+ }
+
+ q_ptr->setPrevDistance(newDistance);
+ }
}
void QTouch3DInputHandlerPrivate::handleTapAndHold()
{
- QPointF distance = m_startHoldPos - m_touchHoldPos;
- if (distance.manhattanLength() < maxTapAndHoldJitter) {
- q_ptr->setInputPosition(m_touchHoldPos.toPoint());
- q_ptr->scene()->setSelectionQueryPosition(m_touchHoldPos.toPoint());
- m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ if (q_ptr->isSelectionEnabled()) {
+ QPointF distance = m_startHoldPos - m_touchHoldPos;
+ if (distance.manhattanLength() < maxTapAndHoldJitter) {
+ q_ptr->setInputPosition(m_touchHoldPos.toPoint());
+ q_ptr->scene()->setSelectionQueryPosition(m_touchHoldPos.toPoint());
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ }
}
}
void QTouch3DInputHandlerPrivate::handleSelection(const QPointF &position)
{
- QPointF distance = m_startHoldPos - position;
- if (distance.manhattanLength() < maxSelectionJitter) {
- m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
- q_ptr->scene()->setSelectionQueryPosition(position.toPoint());
- } else {
- m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
- q_ptr->setInputView(QAbstract3DInputHandler::InputViewNone);
+ if (q_ptr->isSelectionEnabled()) {
+ QPointF distance = m_startHoldPos - position;
+ if (distance.manhattanLength() < maxSelectionJitter) {
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ q_ptr->scene()->setSelectionQueryPosition(position.toPoint());
+ } else {
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
+ q_ptr->setInputView(QAbstract3DInputHandler::InputViewNone);
+ }
+ q_ptr->setPreviousInputPos(position.toPoint());
}
- q_ptr->setPreviousInputPos(position.toPoint());
}
void QTouch3DInputHandlerPrivate::handleRotation(const QPointF &position)
{
- if (QAbstract3DInputHandlerPrivate::InputStateRotating == m_inputState) {
+ if (q_ptr->isRotationEnabled()
+ && QAbstract3DInputHandlerPrivate::InputStateRotating == m_inputState) {
Q3DScene *scene = q_ptr->scene();
Q3DCamera *camera = scene->activeCamera();
float xRotation = camera->xRotation();
diff --git a/src/datavisualization/input/qtouch3dinputhandler_p.h b/src/datavisualization/input/qtouch3dinputhandler_p.h
index 613b5f28..b01904ca 100644
--- a/src/datavisualization/input/qtouch3dinputhandler_p.h
+++ b/src/datavisualization/input/qtouch3dinputhandler_p.h
@@ -19,7 +19,7 @@
#ifndef QTOUCH3DINPUTHANDLER_P_H
#define QTOUCH3DINPUTHANDLER_P_H
-#include "qabstract3dinputhandler_p.h"
+#include "q3dinputhandler_p.h"
#include "qtouch3dinputhandler.h"
class QTimer;
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QAbstract3DInputHandler;
-class QTouch3DInputHandlerPrivate : public QObject
+class QTouch3DInputHandlerPrivate : public Q3DInputHandlerPrivate
{
Q_OBJECT
@@ -36,13 +36,14 @@ public:
QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q);
~QTouch3DInputHandlerPrivate();
- void handlePinchZoom(float distance);
+ void handlePinchZoom(float distance, const QPoint &pos);
void handleTapAndHold();
void handleSelection(const QPointF &position);
void handleRotation(const QPointF &position);
-public:
+private:
QTouch3DInputHandler *q_ptr;
+public:
QTimer *m_holdTimer;
QAbstract3DInputHandlerPrivate::InputState m_inputState;
QPointF m_startHoldPos;
diff --git a/src/datavisualization/theme/q3dtheme.cpp b/src/datavisualization/theme/q3dtheme.cpp
index 97ff8f81..a1dfe8b3 100644
--- a/src/datavisualization/theme/q3dtheme.cpp
+++ b/src/datavisualization/theme/q3dtheme.cpp
@@ -323,7 +323,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty real Theme3D::lightStrength
*
- * Specular light strength for the whole graph. Value must be between 0.0 and 1.0.
+ * Specular light strength for the whole graph. Value must be between 0.0 and 10.0.
*/
/*!
@@ -335,7 +335,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty real Theme3D::highlightLightStrength
*
- * Specular light strength for highlighted objects. Value must be between 0.0 and 1.0.
+ * Specular light strength for highlighted objects. Value must be between 0.0 and 10.0.
*/
/*!
@@ -679,7 +679,7 @@ QLinearGradient Q3DTheme::multiHighlightGradient() const
/*!
* \property Q3DTheme::lightStrength
*
- * Specular light strength for the whole graph. Value must be 0.0f and 10.0f.
+ * Specular light strength for the whole graph. Value must be between 0.0f and 10.0f.
*/
void Q3DTheme::setLightStrength(float strength)
{
diff --git a/src/datavisualization/theme/theme.pri b/src/datavisualization/theme/theme.pri
index 1de3035a..cf7b434c 100644
--- a/src/datavisualization/theme/theme.pri
+++ b/src/datavisualization/theme/theme.pri
@@ -6,3 +6,5 @@ HEADERS += \
SOURCES += \
$$PWD/q3dtheme.cpp \
$$PWD/thememanager.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/utils/abstractobjecthelper.cpp b/src/datavisualization/utils/abstractobjecthelper.cpp
index c350d096..40b3a45e 100644
--- a/src/datavisualization/utils/abstractobjecthelper.cpp
+++ b/src/datavisualization/utils/abstractobjecthelper.cpp
@@ -28,6 +28,7 @@ AbstractObjectHelper::AbstractObjectHelper()
m_indexCount(0),
m_meshDataLoaded(false)
{
+ initializeOpenGLFunctions();
}
AbstractObjectHelper::~AbstractObjectHelper()
diff --git a/src/datavisualization/utils/abstractobjecthelper_p.h b/src/datavisualization/utils/abstractobjecthelper_p.h
index c1618909..99f85dab 100644
--- a/src/datavisualization/utils/abstractobjecthelper_p.h
+++ b/src/datavisualization/utils/abstractobjecthelper_p.h
@@ -38,11 +38,11 @@ class AbstractObjectHelper: protected QOpenGLFunctions
protected:
AbstractObjectHelper();
public:
- ~AbstractObjectHelper();
+ virtual ~AbstractObjectHelper();
GLuint vertexBuf();
GLuint normalBuf();
- GLuint uvBuf();
+ virtual GLuint uvBuf();
GLuint elementBuf();
GLuint indexCount();
GLuint indicesType();
diff --git a/src/datavisualization/utils/objecthelper.cpp b/src/datavisualization/utils/objecthelper.cpp
index a66e0f7e..4240d6f5 100644
--- a/src/datavisualization/utils/objecthelper.cpp
+++ b/src/datavisualization/utils/objecthelper.cpp
@@ -111,7 +111,6 @@ ObjectHelper *ObjectHelper::getObjectHelper(const Abstract3DRenderer *cacheId,
void ObjectHelper::load()
{
- initializeOpenGLFunctions();
if (m_meshDataLoaded) {
// Delete old data
glDeleteBuffers(1, &m_vertexbuffer);
@@ -122,6 +121,10 @@ void ObjectHelper::load()
m_indexedVertices.clear();
m_indexedUVs.clear();
m_indexedNormals.clear();
+ m_vertexbuffer = 0;
+ m_uvbuffer = 0;
+ m_normalbuffer = 0;
+ m_elementbuffer = 0;
}
QVector<QVector3D> vertices;
QVector<QVector2D> uvs;
diff --git a/src/datavisualization/utils/objecthelper_p.h b/src/datavisualization/utils/objecthelper_p.h
index c84f53bd..b00e5d8d 100644
--- a/src/datavisualization/utils/objecthelper_p.h
+++ b/src/datavisualization/utils/objecthelper_p.h
@@ -41,7 +41,7 @@ class ObjectHelper : public AbstractObjectHelper
private:
ObjectHelper(const QString &objectFile);
public:
- ~ObjectHelper();
+ virtual ~ObjectHelper();
static void resetObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj,
const QString &meshFile);
diff --git a/src/datavisualization/utils/qutils.h b/src/datavisualization/utils/qutils.h
index 43375a9c..d4acfc99 100644
--- a/src/datavisualization/utils/qutils.h
+++ b/src/datavisualization/utils/qutils.h
@@ -28,17 +28,21 @@ inline static QSurfaceFormat qDefaultSurfaceFormat(bool antialias = true)
QSurfaceFormat surfaceFormat;
surfaceFormat.setDepthBufferSize(24);
+ surfaceFormat.setStencilBufferSize(8);
surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
-#if !defined(QT_OPENGL_ES_2)
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL);
-#else
+ surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType);
+#if defined(QT_OPENGL_ES_2)
// Antialias not supported for ES
antialias = false;
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
+ surfaceFormat.setRedBufferSize(8);
+ surfaceFormat.setBlueBufferSize(8);
+ surfaceFormat.setGreenBufferSize(8);
#endif
if (antialias)
surfaceFormat.setSamples(8);
+ else
+ surfaceFormat.setSamples(0);
return surfaceFormat;
}
diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp
index d68b9df4..44c84ae0 100644
--- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp
+++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp
@@ -20,12 +20,14 @@
#include "objecthelper_p.h"
#include <QtGui/QVector2D>
#include <QtGui/QMatrix4x4>
+#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
const GLfloat itemScaler = 3.0f;
ScatterObjectBufferHelper::ScatterObjectBufferHelper()
+ : m_scaleY(0.0f)
{
m_indicesType = GL_UNSIGNED_INT;
}
@@ -36,34 +38,40 @@ ScatterObjectBufferHelper::~ScatterObjectBufferHelper()
void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale)
{
- initializeOpenGLFunctions();
-
m_meshDataLoaded = false;
m_indexCount = 0;
ObjectHelper *dotObj = cache->object();
- ScatterRenderItemArray &renderArray = cache->renderArray();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
const uint renderArraySize = renderArray.size();
+
if (renderArraySize == 0)
return; // No use to go forward
- uint itemCount = renderArraySize;
+
+ uint itemCount = 0;
QQuaternion seriesRotation(cache->meshRotation());
+
if (m_meshDataLoaded) {
// Delete old data
glDeleteBuffers(1, &m_vertexbuffer);
glDeleteBuffers(1, &m_uvbuffer);
glDeleteBuffers(1, &m_normalbuffer);
glDeleteBuffers(1, &m_elementbuffer);
+ m_vertexbuffer = 0;
+ m_uvbuffer = 0;
+ m_normalbuffer = 0;
+ m_elementbuffer = 0;
}
+
// Index vertices
const QVector<unsigned short> indices = dotObj->indices();
const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
const QVector<QVector2D> indexed_uvs = dotObj->indexedUVs();
const QVector<QVector3D> indexed_normals = dotObj->indexedNormals();
- int indicesCount = indices.count();
- int verticeCount = indexed_vertices.count();
- int uvsCount = indexed_uvs.count();
- int normalsCount = indexed_normals.count();
+ const int indicesCount = indices.count();
+ const int verticeCount = indexed_vertices.count();
+ const int uvsCount = indexed_uvs.count();
+ const int normalsCount = indexed_normals.count();
float itemSize = cache->itemSize() / itemScaler;
if (itemSize == 0.0f)
@@ -89,47 +97,58 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal
buffered_indices.resize(indicesCount * renderArraySize);
buffered_vertices.resize(verticeCount * renderArraySize);
- buffered_uvs.resize(uvsCount * renderArraySize);
buffered_normals.resize(normalsCount * renderArraySize);
- uint pos = 0;
+ buffered_uvs.resize(uvsCount * renderArraySize);
+
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ createRangeGradientUVs(cache, buffered_uvs);
+ else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient)
+ createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
+
+ QVector2D dummyUV(0.0f, 0.0f);
+
+ cache->bufferIndices().resize(renderArraySize);
for (uint i = 0; i < renderArraySize; i++) {
- ScatterRenderItem &item = renderArray[i];
- if (!item.isVisible()) {
- itemCount--;
+ const ScatterRenderItem &item = renderArray.at(i);
+ if (!item.isVisible())
continue;
- }
+ else
+ cache->bufferIndices()[i] = itemCount;
- int offset = pos * verticeCount;
+ int offset = itemCount * verticeCount;
if (item.rotation().isIdentity()) {
- for (int j = 0; j < verticeCount; j++)
+ for (int j = 0; j < verticeCount; j++) {
buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
+ buffered_normals[j + offset] = indexed_normals[j];
+ }
} else {
QMatrix4x4 matrix;
- matrix.rotate(seriesRotation * item.rotation());
- modelMatrix = matrix.transposed();
- modelMatrix.scale(modelScaler);
+ QQuaternion totalRotation = seriesRotation * item.rotation();
+ matrix.rotate(totalRotation);
+ matrix.scale(modelScaler);
+ QMatrix4x4 itModelMatrix = matrix.inverted();
+ modelMatrix = matrix.transposed(); // Because of row-column major difference
- for (int j = 0; j < verticeCount; j++)
+ for (int j = 0; j < verticeCount; j++) {
buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
+ item.translation();
+ buffered_normals[j + offset] = indexed_normals[j] * itModelMatrix;
+ }
}
- offset = pos * normalsCount;
- for (int j = 0; j < normalsCount; j++)
- buffered_normals[j + offset] = indexed_normals[j];
-
- offset = pos * uvsCount;
- for (int j = 0; j < uvsCount; j++)
- buffered_uvs[j + offset] = indexed_uvs[j];
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
+ offset = itemCount * uvsCount;
+ for (int j = 0; j < uvsCount; j++)
+ buffered_uvs[j + offset] = dummyUV;
+ }
- int offsetVertice = i * verticeCount;
- offset = pos * indicesCount;
- for (int j = 0; j < indicesCount; j++) {
+ int offsetVertice = itemCount * verticeCount;
+ offset = itemCount * indicesCount;
+ for (int j = 0; j < indicesCount; j++)
buffered_indices[j + offset] = GLuint(indices[j] + offsetVertice);
- }
- pos++;
+ itemCount++;
}
m_indexCount = indicesCount * itemCount;
@@ -164,15 +183,128 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal
}
}
-void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale)
+void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache)
+{
+ ObjectHelper *dotObj = cache->object();
+ const int uvsCount = dotObj->indexedUVs().count();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
+
+ if (!updateSize)
+ return;
+
+ QVector<QVector2D> buffered_uvs;
+ buffered_uvs.resize(uvsCount * updateSize);
+
+ uint itemCount = 0;
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) {
+ itemCount = createRangeGradientUVs(cache, buffered_uvs);
+ } else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
+ const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
+ itemCount = createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
+ int itemSize = uvsCount * sizeof(QVector2D);
+ if (cache->updateIndices().size()) {
+ int pos = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = cache->updateIndices().at(i);
+ if (renderArray.at(index).isVisible()) {
+ int dataPos = cache->bufferIndices().at(index);
+ glBufferSubData(GL_ARRAY_BUFFER, itemSize * dataPos, itemSize,
+ &buffered_uvs.at(uvsCount * pos++));
+ }
+ }
+ } else {
+ glBufferData(GL_ARRAY_BUFFER, itemSize * itemCount, &buffered_uvs.at(0), GL_STATIC_DRAW);
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs)
{
- initializeOpenGLFunctions();
+ ObjectHelper *dotObj = cache->object();
+ const int uvsCount = dotObj->indexedUVs().count();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
+ const float yAdjustment = 0.1f;
+ const float flippedYAdjustment = 0.9f;
+
+ QVector2D uv;
+ uv.setX(0.0f);
+ uint pos = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
+ if (!item.isVisible())
+ continue;
+
+ float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY;
+ // Avoid values near gradient texel boundary, as this causes artifacts
+ // with some graphics cards.
+ const float floorY = float(qFloor(y * gradientTextureHeight));
+ const float diff = (y * gradientTextureHeight) - floorY;
+ if (diff < yAdjustment)
+ y += yAdjustment / gradientTextureHeight;
+ else if (diff > flippedYAdjustment)
+ y -= yAdjustment / gradientTextureHeight;
+ uv.setY(y);
+
+ int offset = pos * uvsCount;
+ for (int j = 0; j < uvsCount; j++)
+ buffered_uvs[j + offset] = uv;
+
+ pos++;
+ }
+
+ return pos;
+}
+
+uint ScatterObjectBufferHelper::createObjectGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs,
+ const QVector<QVector3D> &indexed_vertices)
+{
ObjectHelper *dotObj = cache->object();
- ScatterRenderItemArray &renderArray = cache->renderArray();
- const int renderArraySize = renderArray.size();
+ const int uvsCount = dotObj->indexedUVs().count();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const uint renderArraySize = renderArray.size();
+
+ QVector2D uv;
+ uv.setX(0.0f);
+ uint pos = 0;
+ for (uint i = 0; i < renderArraySize; i++) {
+ const ScatterRenderItem &item = renderArray.at(i);
+ if (!item.isVisible())
+ continue;
+
+ int offset = pos * uvsCount;
+ for (int j = 0; j < uvsCount; j++) {
+ uv.setY((indexed_vertices.at(j).y() + 1.0f) / 2.0f);
+ buffered_uvs[j + offset] = uv;
+ }
+
+ pos++;
+ }
+
+ return pos;
+}
+
+void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale)
+{
+ ObjectHelper *dotObj = cache->object();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
QQuaternion seriesRotation(cache->meshRotation());
+ if (!updateSize)
+ return;
+
// Index vertices
const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
int verticeCount = indexed_vertices.count();
@@ -195,14 +327,16 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do
scaled_vertices[i] = indexed_vertices[i] * modelMatrix;
QVector<QVector3D> buffered_vertices;
+ buffered_vertices.resize(verticeCount * updateSize);
- buffered_vertices.resize(verticeCount * renderArraySize);
- for (int i = 0; i < renderArraySize; i++) {
- ScatterRenderItem &item = renderArray[i];
+ int itemCount = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
if (!item.isVisible())
continue;
- const int offset = i * verticeCount;
+ const int offset = itemCount * verticeCount;
if (item.rotation().isIdentity()) {
for (int j = 0; j < verticeCount; j++)
buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
@@ -216,15 +350,28 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do
buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
+ item.translation();
}
+ itemCount++;
}
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
- glBufferData(GL_ARRAY_BUFFER, buffered_vertices.size() * sizeof(QVector3D),
- &buffered_vertices.at(0),
- GL_DYNAMIC_DRAW);
-
+ int sizeOfItem = verticeCount * sizeof(QVector3D);
+ if (updateAll) {
+ if (itemCount) {
+ glBufferData(GL_ARRAY_BUFFER, itemCount * sizeOfItem,
+ &buffered_vertices.at(0), GL_STATIC_DRAW);
+ }
+ } else {
+ itemCount = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ if (renderArray.at(index).isVisible()) {
+ glBufferSubData(GL_ARRAY_BUFFER, cache->bufferIndices().at(index) * sizeOfItem,
+ sizeOfItem, &buffered_vertices.at(itemCount * verticeCount));
+ itemCount++;
+ }
+ }
+ }
glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
diff --git a/src/datavisualization/utils/scatterobjectbufferhelper_p.h b/src/datavisualization/utils/scatterobjectbufferhelper_p.h
index 952c3d7d..08a42900 100644
--- a/src/datavisualization/utils/scatterobjectbufferhelper_p.h
+++ b/src/datavisualization/utils/scatterobjectbufferhelper_p.h
@@ -39,10 +39,21 @@ class ScatterObjectBufferHelper : public AbstractObjectHelper
{
public:
ScatterObjectBufferHelper();
- ~ScatterObjectBufferHelper();
+ virtual ~ScatterObjectBufferHelper();
void fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale);
void update(ScatterSeriesRenderCache *cache, qreal dotScale);
+ void updateUVs(ScatterSeriesRenderCache *cache);
+ void setScaleY(float scale) { m_scaleY = scale; }
+
+private:
+ uint createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs);
+ uint createObjectGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs,
+ const QVector<QVector3D> &indexed_vertices);
+
+ float m_scaleY;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp
index b14d84ad..22e76f92 100644
--- a/src/datavisualization/utils/scatterpointbufferhelper.cpp
+++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp
@@ -17,6 +17,7 @@
****************************************************************************/
#include "scatterpointbufferhelper_p.h"
+#include <QtGui/QVector2D>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -24,8 +25,7 @@ const QVector3D hiddenPos(-1000.0f, -1000.0f, -1000.0f);
ScatterPointBufferHelper::ScatterPointBufferHelper()
: m_pointbuffer(0),
- m_oldRemoveIndex(0),
- m_oldRemove(false)
+ m_oldRemoveIndex(-1)
{
m_indicesType = GL_UNSIGNED_INT;
}
@@ -47,7 +47,8 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex)
{
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
- if (m_oldRemove && m_oldRemoveIndex < pointIndex) {
+ // Pop the previous point if it is still pushed
+ if (m_oldRemoveIndex >= 0) {
glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D),
sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex));
}
@@ -59,26 +60,22 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex)
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_oldRemoveIndex = pointIndex;
- m_oldRemove = true;
}
void ScatterPointBufferHelper::popPoint()
{
- if (m_oldRemove) {
+ if (m_oldRemoveIndex >= 0) {
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D),
sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex));
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
- m_oldRemoveIndex = 0;
- m_oldRemove = false;
+ m_oldRemoveIndex = -1;
}
void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache)
{
- initializeOpenGLFunctions();
-
ScatterRenderItemArray &renderArray = cache->renderArray();
const int renderArraySize = renderArray.size();
m_indexCount = 0;
@@ -86,13 +83,16 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache)
if (m_meshDataLoaded) {
// Delete old data
glDeleteBuffers(1, &m_pointbuffer);
+ glDeleteBuffers(1, &m_uvbuffer);
m_bufferedPoints.clear();
+ m_pointbuffer = 0;
+ m_uvbuffer = 0;
}
bool itemsVisible = false;
m_bufferedPoints.resize(renderArraySize);
for (int i = 0; i < renderArraySize; i++) {
- ScatterRenderItem &item = renderArray[i];
+ const ScatterRenderItem &item = renderArray.at(i);
if (!item.isVisible()) {
m_bufferedPoints[i] = hiddenPos;
} else {
@@ -101,19 +101,108 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache)
}
}
+ QVector<QVector2D> buffered_uvs;
if (itemsVisible)
m_indexCount = renderArraySize;
if (m_indexCount > 0) {
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ createRangeGradientUVs(cache, buffered_uvs);
+
glGenBuffers(1, &m_pointbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
glBufferData(GL_ARRAY_BUFFER, m_bufferedPoints.size() * sizeof(QVector3D),
&m_bufferedPoints.at(0),
GL_DYNAMIC_DRAW);
+
+ if (buffered_uvs.size()) {
+ glGenBuffers(1, &m_uvbuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
+ glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D),
+ &buffered_uvs.at(0), GL_STATIC_DRAW);
+ }
+
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
}
+void ScatterPointBufferHelper::update(ScatterSeriesRenderCache *cache)
+{
+ // It may be that the buffer hasn't yet been initialized, in case the entire series was
+ // hidden items. No need to update in that case.
+ if (m_indexCount > 0) {
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int updateSize = cache->updateIndices().size();
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
+ for (int i = 0; i < updateSize; i++) {
+ int index = cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
+ if (!item.isVisible())
+ m_bufferedPoints[index] = hiddenPos;
+ else
+ m_bufferedPoints[index] = item.translation();
+
+ if (index != m_oldRemoveIndex) {
+ glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector3D),
+ sizeof(QVector3D), &m_bufferedPoints.at(index));
+ }
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+}
+
+void ScatterPointBufferHelper::updateUVs(ScatterSeriesRenderCache *cache)
+{
+ // It may be that the buffer hasn't yet been initialized, in case the entire series was
+ // hidden items. No need to update in that case.
+ if (m_indexCount > 0) {
+ QVector<QVector2D> buffered_uvs;
+ createRangeGradientUVs(cache, buffered_uvs);
+
+ if (buffered_uvs.size()) {
+ if (!m_uvbuffer)
+ glGenBuffers(1, &m_uvbuffer);
+
+ int updateSize = cache->updateIndices().size();
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
+ if (updateSize) {
+ for (int i = 0; i < updateSize; i++) {
+ int index = cache->updateIndices().at(i);
+ glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector2D),
+ sizeof(QVector2D), &buffered_uvs.at(i));
+
+ }
+ } else {
+ glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D),
+ &buffered_uvs.at(0), GL_STATIC_DRAW);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+ }
+}
+
+void ScatterPointBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs)
+{
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
+ buffered_uvs.resize(updateSize);
+
+ QVector2D uv;
+ uv.setX(0.0f);
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
+
+ float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY;
+ uv.setY(y);
+ buffered_uvs[i] = uv;
+ }
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/scatterpointbufferhelper_p.h b/src/datavisualization/utils/scatterpointbufferhelper_p.h
index b3adcfa8..8b34542d 100644
--- a/src/datavisualization/utils/scatterpointbufferhelper_p.h
+++ b/src/datavisualization/utils/scatterpointbufferhelper_p.h
@@ -39,21 +39,28 @@ class ScatterPointBufferHelper : public AbstractObjectHelper
{
public:
ScatterPointBufferHelper();
- ~ScatterPointBufferHelper();
+ virtual ~ScatterPointBufferHelper();
GLuint pointBuf();
void pushPoint(uint pointIndex);
void popPoint();
void load(ScatterSeriesRenderCache *cache);
+ void update(ScatterSeriesRenderCache *cache);
+ void setScaleY(float scale) { m_scaleY = scale; }
+ void updateUVs(ScatterSeriesRenderCache *cache);
public:
GLuint m_pointbuffer;
private:
+ void createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs);
+
+private:
QVector<QVector3D> m_bufferedPoints;
- uint m_oldRemoveIndex;
- bool m_oldRemove;
+ int m_oldRemoveIndex;
+ float m_scaleY;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp
index 7fb237c6..bbd7fe0e 100644
--- a/src/datavisualization/utils/shaderhelper.cpp
+++ b/src/datavisualization/utils/shaderhelper.cpp
@@ -40,7 +40,37 @@ ShaderHelper::ShaderHelper(QObject *parent,
m_vertexShaderFile(vertexShader),
m_fragmentShaderFile(fragmentShader),
m_textureFile(texture),
- m_depthTextureFile(depthTexture)
+ m_depthTextureFile(depthTexture),
+ m_positionAttr(0),
+ m_uvAttr(0),
+ m_normalAttr(0),
+ m_colorUniform(0),
+ m_viewMatrixUniform(0),
+ m_modelMatrixUniform(0),
+ m_invTransModelMatrixUniform(0),
+ m_depthMatrixUniform(0),
+ m_mvpMatrixUniform(0),
+ m_lightPositionUniform(0),
+ m_lightStrengthUniform(0),
+ m_ambientStrengthUniform(0),
+ m_shadowQualityUniform(0),
+ m_textureUniform(0),
+ m_shadowUniform(0),
+ m_gradientMinUniform(0),
+ m_gradientHeightUniform(0),
+ m_lightColorUniform(0),
+ m_volumeSliceIndicesUniform(0),
+ m_colorIndexUniform(0),
+ m_cameraPositionRelativeToModelUniform(0),
+ m_color8BitUniform(0),
+ m_textureDimensionsUniform(0),
+ m_sampleCountUniform(0),
+ m_alphaMultiplierUniform(0),
+ m_preserveOpacityUniform(0),
+ m_minBoundsUniform(0),
+ m_maxBoundsUniform(0),
+ m_sliceFrameWidthUniform(0),
+ m_initialized(false)
{
}
@@ -93,6 +123,17 @@ void ShaderHelper::initialize()
m_gradientMinUniform = m_program->uniformLocation("gradMin");
m_gradientHeightUniform = m_program->uniformLocation("gradHeight");
m_lightColorUniform = m_program->uniformLocation("lightColor");
+ m_volumeSliceIndicesUniform = m_program->uniformLocation("volumeSliceIndices");
+ m_colorIndexUniform = m_program->uniformLocation("colorIndex");
+ m_cameraPositionRelativeToModelUniform = m_program->uniformLocation("cameraPositionRelativeToModel");
+ m_color8BitUniform = m_program->uniformLocation("color8Bit");
+ m_textureDimensionsUniform = m_program->uniformLocation("textureDimensions");
+ m_sampleCountUniform = m_program->uniformLocation("sampleCount");
+ m_alphaMultiplierUniform = m_program->uniformLocation("alphaMultiplier");
+ m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity");
+ m_minBoundsUniform = m_program->uniformLocation("minBounds");
+ m_maxBoundsUniform = m_program->uniformLocation("maxBounds");
+ m_sliceFrameWidthUniform = m_program->uniformLocation("sliceFrameWidth");
m_initialized = true;
}
@@ -125,151 +166,239 @@ void ShaderHelper::release()
m_program->release();
}
-void ShaderHelper::setUniformValue(GLuint uniform, const QVector3D &value)
+void ShaderHelper::setUniformValue(GLint uniform, const QVector2D &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, const QVector4D &value)
+void ShaderHelper::setUniformValue(GLint uniform, const QVector3D &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, const QMatrix4x4 &value)
+void ShaderHelper::setUniformValue(GLint uniform, const QVector4D &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, GLfloat value)
+void ShaderHelper::setUniformValue(GLint uniform, const QMatrix4x4 &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, GLint value)
+void ShaderHelper::setUniformValue(GLint uniform, GLfloat value)
{
m_program->setUniformValue(uniform, value);
}
-GLuint ShaderHelper::MVP()
+void ShaderHelper::setUniformValue(GLint uniform, GLint value)
+{
+ m_program->setUniformValue(uniform, value);
+}
+
+void ShaderHelper::setUniformValueArray(GLint uniform, const QVector4D *values, int count)
+{
+ m_program->setUniformValueArray(uniform, values, count);
+}
+
+GLint ShaderHelper::MVP()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_mvpMatrixUniform;
}
-GLuint ShaderHelper::view()
+GLint ShaderHelper::view()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_viewMatrixUniform;
}
-GLuint ShaderHelper::model()
+GLint ShaderHelper::model()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_modelMatrixUniform;
}
-GLuint ShaderHelper::nModel()
+GLint ShaderHelper::nModel()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_invTransModelMatrixUniform;
}
-GLuint ShaderHelper::depth()
+GLint ShaderHelper::depth()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_depthMatrixUniform;
}
-GLuint ShaderHelper::lightP()
+GLint ShaderHelper::lightP()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_lightPositionUniform;
}
-GLuint ShaderHelper::lightS()
+GLint ShaderHelper::lightS()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_lightStrengthUniform;
}
-GLuint ShaderHelper::ambientS()
+GLint ShaderHelper::ambientS()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_ambientStrengthUniform;
}
-GLuint ShaderHelper::shadowQ()
+GLint ShaderHelper::shadowQ()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_shadowQualityUniform;
}
-GLuint ShaderHelper::color()
+GLint ShaderHelper::color()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_colorUniform;
}
-GLuint ShaderHelper::texture()
+GLint ShaderHelper::texture()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_textureUniform;
}
-GLuint ShaderHelper::shadow()
+GLint ShaderHelper::shadow()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_shadowUniform;
}
-GLuint ShaderHelper::gradientMin()
+GLint ShaderHelper::gradientMin()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_gradientMinUniform;
}
-GLuint ShaderHelper::gradientHeight()
+GLint ShaderHelper::gradientHeight()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_gradientHeightUniform;
}
-GLuint ShaderHelper::lightColor()
+GLint ShaderHelper::lightColor()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_lightColorUniform;
}
-GLuint ShaderHelper::posAtt()
+GLint ShaderHelper::volumeSliceIndices()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_volumeSliceIndicesUniform;
+}
+
+GLint ShaderHelper::colorIndex()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_colorIndexUniform;
+}
+
+GLint ShaderHelper::cameraPositionRelativeToModel()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_cameraPositionRelativeToModelUniform;
+}
+
+GLint ShaderHelper::color8Bit()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_color8BitUniform;
+}
+
+GLint ShaderHelper::textureDimensions()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_textureDimensionsUniform;
+}
+
+GLint ShaderHelper::sampleCount()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_sampleCountUniform;
+}
+
+GLint ShaderHelper::alphaMultiplier()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_alphaMultiplierUniform;
+}
+
+GLint ShaderHelper::preserveOpacity()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_preserveOpacityUniform;
+}
+
+GLint ShaderHelper::maxBounds()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_maxBoundsUniform;
+}
+
+GLint ShaderHelper::minBounds()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_minBoundsUniform;
+}
+
+GLint ShaderHelper::sliceFrameWidth()
+{
+
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_sliceFrameWidthUniform;
+}
+
+GLint ShaderHelper::posAtt()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_positionAttr;
}
-GLuint ShaderHelper::uvAtt()
+GLint ShaderHelper::uvAtt()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_uvAttr;
}
-GLuint ShaderHelper::normalAtt()
+GLint ShaderHelper::normalAtt()
{
if (!m_initialized)
qFatal("Shader not initialized");
diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h
index fdef0dff..812cba18 100644
--- a/src/datavisualization/utils/shaderhelper_p.h
+++ b/src/datavisualization/utils/shaderhelper_p.h
@@ -52,31 +52,44 @@ class ShaderHelper
bool testCompile();
void bind();
void release();
- void setUniformValue(GLuint uniform, const QVector3D &value);
- void setUniformValue(GLuint uniform, const QVector4D &value);
- void setUniformValue(GLuint uniform, const QMatrix4x4 &value);
- void setUniformValue(GLuint uniform, GLfloat value);
- void setUniformValue(GLuint uniform, GLint value);
-
- GLuint MVP();
- GLuint view();
- GLuint model();
- GLuint nModel();
- GLuint depth();
- GLuint lightP();
- GLuint lightS();
- GLuint ambientS();
- GLuint shadowQ();
- GLuint color();
- GLuint texture();
- GLuint shadow();
- GLuint gradientMin();
- GLuint gradientHeight();
- GLuint lightColor();
-
- GLuint posAtt();
- GLuint uvAtt();
- GLuint normalAtt();
+ void setUniformValue(GLint uniform, const QVector2D &value);
+ void setUniformValue(GLint uniform, const QVector3D &value);
+ void setUniformValue(GLint uniform, const QVector4D &value);
+ void setUniformValue(GLint uniform, const QMatrix4x4 &value);
+ void setUniformValue(GLint uniform, GLfloat value);
+ void setUniformValue(GLint uniform, GLint value);
+ void setUniformValueArray(GLint uniform, const QVector4D *values, int count);
+
+ GLint MVP();
+ GLint view();
+ GLint model();
+ GLint nModel();
+ GLint depth();
+ GLint lightP();
+ GLint lightS();
+ GLint ambientS();
+ GLint shadowQ();
+ GLint color();
+ GLint texture();
+ GLint shadow();
+ GLint gradientMin();
+ GLint gradientHeight();
+ GLint lightColor();
+ GLint volumeSliceIndices();
+ GLint colorIndex();
+ GLint cameraPositionRelativeToModel();
+ GLint color8Bit();
+ GLint textureDimensions();
+ GLint sampleCount();
+ GLint alphaMultiplier();
+ GLint preserveOpacity();
+ GLint maxBounds();
+ GLint minBounds();
+ GLint sliceFrameWidth();
+
+ GLint posAtt();
+ GLint uvAtt();
+ GLint normalAtt();
private:
QObject *m_caller;
@@ -88,25 +101,36 @@ class ShaderHelper
QString m_textureFile;
QString m_depthTextureFile;
- GLuint m_positionAttr;
- GLuint m_uvAttr;
- GLuint m_normalAttr;
-
- GLuint m_colorUniform;
- GLuint m_viewMatrixUniform;
- GLuint m_modelMatrixUniform;
- GLuint m_invTransModelMatrixUniform;
- GLuint m_depthMatrixUniform;
- GLuint m_mvpMatrixUniform;
- GLuint m_lightPositionUniform;
- GLuint m_lightStrengthUniform;
- GLuint m_ambientStrengthUniform;
- GLuint m_shadowQualityUniform;
- GLuint m_textureUniform;
- GLuint m_shadowUniform;
- GLuint m_gradientMinUniform;
- GLuint m_gradientHeightUniform;
- GLuint m_lightColorUniform;
+ GLint m_positionAttr;
+ GLint m_uvAttr;
+ GLint m_normalAttr;
+
+ GLint m_colorUniform;
+ GLint m_viewMatrixUniform;
+ GLint m_modelMatrixUniform;
+ GLint m_invTransModelMatrixUniform;
+ GLint m_depthMatrixUniform;
+ GLint m_mvpMatrixUniform;
+ GLint m_lightPositionUniform;
+ GLint m_lightStrengthUniform;
+ GLint m_ambientStrengthUniform;
+ GLint m_shadowQualityUniform;
+ GLint m_textureUniform;
+ GLint m_shadowUniform;
+ GLint m_gradientMinUniform;
+ GLint m_gradientHeightUniform;
+ GLint m_lightColorUniform;
+ GLint m_volumeSliceIndicesUniform;
+ GLint m_colorIndexUniform;
+ GLint m_cameraPositionRelativeToModelUniform;
+ GLint m_color8BitUniform;
+ GLint m_textureDimensionsUniform;
+ GLint m_sampleCountUniform;
+ GLint m_alphaMultiplierUniform;
+ GLint m_preserveOpacityUniform;
+ GLint m_minBoundsUniform;
+ GLint m_maxBoundsUniform;
+ GLint m_sliceFrameWidthUniform;
GLboolean m_initialized;
};
diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp
index d999ba90..b93030b1 100644
--- a/src/datavisualization/utils/surfaceobject.cpp
+++ b/src/datavisualization/utils/surfaceobject.cpp
@@ -30,26 +30,31 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer)
m_gridIndexCount(0),
m_axisCacheX(renderer->m_axisCacheX),
m_axisCacheY(renderer->m_axisCacheY),
- m_axisCacheZ(renderer->m_axisCacheZ)
-
+ m_axisCacheZ(renderer->m_axisCacheZ),
+ m_renderer(renderer),
+ m_returnTextureBuffer(false),
+ m_dataDimension(0),
+ m_oldDataDimension(-1)
{
m_indicesType = GL_UNSIGNED_INT;
- initializeOpenGLFunctions();
glGenBuffers(1, &m_vertexbuffer);
glGenBuffers(1, &m_normalbuffer);
glGenBuffers(1, &m_uvbuffer);
glGenBuffers(1, &m_elementbuffer);
glGenBuffers(1, &m_gridElementbuffer);
+ glGenBuffers(1, &m_uvTextureBuffer);
}
SurfaceObject::~SurfaceObject()
{
- if (QOpenGLContext::currentContext())
+ if (QOpenGLContext::currentContext()) {
glDeleteBuffers(1, &m_gridElementbuffer);
+ glDeleteBuffers(1, &m_uvTextureBuffer);
+ }
}
void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ)
+ bool changeGeometry, bool polar, bool flipXZ)
{
m_columns = space.width();
m_rows = space.height();
@@ -59,6 +64,12 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
m_surfaceType = SurfaceSmooth;
+ checkDirections(dataArray);
+ bool indicesDirty = false;
+ if (m_dataDimension != m_oldDataDimension)
+ indicesDirty = true;
+ m_oldDataDimension = m_dataDimension;
+
// Create/populate vertix table
if (changeGeometry)
m_vertices.resize(totalSize);
@@ -68,17 +79,14 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
uvs.resize(totalSize);
int totalIndex = 0;
- AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX;
- AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ;
+ // Init min and max to ridiculous values
+ m_minY = 10000000.0;
+ m_maxY = -10000000.0f;
for (int i = 0; i < m_rows; i++) {
const QSurfaceDataRow &p = *dataArray.at(i);
for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = p.at(j);
- float normalizedX = xCache.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = zCache.positionAt(data.z());
- m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ);
+ getNormalizedVertex(p.at(j), m_vertices[totalIndex], polar, flipXZ);
if (changeGeometry)
uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY);
totalIndex++;
@@ -95,161 +103,305 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
// Create normals
int rowLimit = m_rows - 1;
int colLimit = m_columns - 1;
- int rowColLimit = rowLimit * m_columns;
- int totalLimit = totalSize - 1;
if (changeGeometry)
m_normals.resize(totalSize);
totalIndex = 0;
- const bool flipNormal = checkFlipNormal(dataArray);
- for (int row = 0; row < rowColLimit; row += m_columns) {
- for (int j = 0; j < colLimit; j++) {
- m_normals[totalIndex++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(row + m_columns + j),
- flipNormal);
- }
- int p = row + colLimit;
- m_normals[totalIndex++] = normal(m_vertices.at(p),
- m_vertices.at(p + m_columns),
- m_vertices.at(p - 1),
- flipNormal);
- }
- for (int j = rowColLimit; j < totalLimit; j++) {
- m_normals[totalIndex++] = normal(m_vertices.at(j),
- m_vertices.at(j - m_columns),
- m_vertices.at(j + 1),
- flipNormal);
+
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == XDescending)) {
+ for (int row = 0; row < rowLimit; row++)
+ createSmoothNormalBodyLine(totalIndex, row * m_columns);
+ createSmoothNormalUpperLine(totalIndex);
+ } else { // BothDescending || ZDescending
+ createSmoothNormalUpperLine(totalIndex);
+ for (int row = 1; row < m_rows; row++)
+ createSmoothNormalBodyLine(totalIndex, row * m_columns);
}
- m_normals[totalIndex++] = normal(m_vertices.at(totalLimit),
- m_vertices.at(totalLimit - 1),
- m_vertices.at(totalLimit - m_columns),
- flipNormal);
// Create indices table
- if (changeGeometry)
+ if (changeGeometry || indicesDirty)
createSmoothIndices(0, 0, colLimit, rowLimit);
// Create line element indices
if (changeGeometry)
createSmoothGridlineIndices(0, 0, colLimit, rowLimit);
- createBuffers(m_vertices, uvs, m_normals, 0, changeGeometry);
+ createBuffers(m_vertices, uvs, m_normals, 0);
}
-void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex)
+void SurfaceObject::createSmoothNormalBodyLine(int &totalIndex, int column)
+{
+ int colLimit = m_columns - 1;
+
+ if (m_dataDimension == BothAscending) {
+ int end = colLimit + column;
+ for (int j = column; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + 1),
+ m_vertices.at(j + m_columns));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(end),
+ m_vertices.at(end + m_columns),
+ m_vertices.at(end - 1));
+ } else if (m_dataDimension == XDescending) {
+ m_normals[totalIndex++] = normal(m_vertices.at(column),
+ m_vertices.at(column + m_columns),
+ m_vertices.at(column + 1));
+ int end = column + m_columns;
+ for (int j = column + 1; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - 1),
+ m_vertices.at(j + m_columns));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ int end = colLimit + column;
+ for (int j = column; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + 1),
+ m_vertices.at(j - m_columns));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(end),
+ m_vertices.at(end - m_columns),
+ m_vertices.at(end - 1));
+ } else { // BothDescending
+ m_normals[totalIndex++] = normal(m_vertices.at(column),
+ m_vertices.at(column - m_columns),
+ m_vertices.at(column + 1));
+ int end = column + m_columns;
+ for (int j = column + 1; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - 1),
+ m_vertices.at(j - m_columns));
+ }
+ }
+}
+
+void SurfaceObject::createSmoothNormalUpperLine(int &totalIndex)
+{
+ if (m_dataDimension == BothAscending) {
+ int lineEnd = m_rows * m_columns - 1;
+ for (int j = (m_rows - 1) * m_columns; j < lineEnd; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - m_columns),
+ m_vertices.at(j + 1));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(lineEnd),
+ m_vertices.at(lineEnd - 1),
+ m_vertices.at(lineEnd - m_columns));
+ } else if (m_dataDimension == XDescending) {
+ int lineStart = (m_rows - 1) * m_columns;
+ int lineEnd = m_rows * m_columns;
+ m_normals[totalIndex++] = normal(m_vertices.at(lineStart),
+ m_vertices.at(lineStart + 1),
+ m_vertices.at(lineStart - m_columns));
+ for (int j = lineStart + 1; j < lineEnd; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - m_columns),
+ m_vertices.at(j - 1));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ int colLimit = m_columns - 1;
+ for (int j = 0; j < colLimit; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + m_columns),
+ m_vertices.at(j + 1));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(colLimit),
+ m_vertices.at(colLimit - 1),
+ m_vertices.at(colLimit + m_columns));
+ } else { // BothDescending
+ m_normals[totalIndex++] = normal(m_vertices.at(0),
+ m_vertices.at(1),
+ m_vertices.at(m_columns));
+ for (int j = 1; j < m_columns; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + m_columns),
+ m_vertices.at(j - 1));
+ }
+ }
+}
+
+QVector3D SurfaceObject::createSmoothNormalBodyLineItem(int x, int y)
+{
+ int p = y * m_columns + x;
+ if (m_dataDimension == BothAscending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p + 1),
+ m_vertices.at(p + m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p - 1));
+ }
+ } else if (m_dataDimension == XDescending) {
+ if (x == 0) {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p + m_columns));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p + 1),
+ m_vertices.at(p - m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p - 1));
+ }
+ } else { // BothDescending
+ if (x == 0) {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p - m_columns));
+ }
+ }
+}
+
+QVector3D SurfaceObject::createSmoothNormalUpperLineItem(int x, int y)
+{
+ int p = y * m_columns + x;
+ if (m_dataDimension == BothAscending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p - m_columns));
+ }
+ } else if (m_dataDimension == XDescending) {
+ if (x == 0) {
+ return normal(m_vertices.at(p), m_vertices.at(p + 1),
+ m_vertices.at(p - m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p - 1));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p + m_columns));
+ }
+ } else { // BothDescending
+ if (x == 0) {
+ return normal(m_vertices.at(0), m_vertices.at(1),
+ m_vertices.at(m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p - 1));
+ }
+ }
+}
+
+void SurfaceObject::smoothUVs(const QSurfaceDataArray &dataArray,
+ const QSurfaceDataArray &modelArray)
+{
+ int columns = dataArray.at(0)->size();
+ int rows = dataArray.size();
+ float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x();
+ float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z();
+ float xMin = dataArray.at(0)->at(0).x();
+ float zMin = dataArray.at(0)->at(0).z();
+ const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending);
+ const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending);
+
+ QVector<QVector2D> uvs;
+ uvs.resize(m_rows * m_columns);
+ int index = 0;
+ for (int i = 0; i < m_rows; i++) {
+ float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer;
+ if (zDescending)
+ y = 1.0f - y;
+ const QSurfaceDataRow &p = *modelArray.at(i);
+ for (int j = 0; j < m_columns; j++) {
+ float x = (p.at(j).x() - xMin) / xRangeNormalizer;
+ if (xDescending)
+ x = 1.0f - x;
+ uvs[index] = QVector2D(x, y);
+ index++;
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer);
+ glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D),
+ &uvs.at(0), GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ m_returnTextureBuffer = true;
+}
+
+void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar)
{
// Update vertices
int p = rowIndex * m_columns;
const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex);
- for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = dataRow.at(j);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ);
- }
+ for (int j = 0; j < m_columns; j++)
+ getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false);
// Create normals
- int colLimit = m_columns - 1;
+ bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending);
int startRow = rowIndex;
- if (startRow > 0)
+ if ((startRow > 0) && upwards)
startRow--;
+ int endRow = rowIndex;
+ if (!upwards && (rowIndex < m_rows - 1))
+ endRow++;
+ if ((endRow == m_rows - 1) && upwards)
+ endRow--;
int totalIndex = startRow * m_columns;
- int rowLimit = (rowIndex + 1) * m_columns;
- if (rowIndex == m_rows - 1)
- rowLimit = rowIndex * m_columns; // The rowIndex is top most row, special handling
- const bool flipNormal = checkFlipNormal(dataArray);
- for (int row = totalIndex; row < rowLimit; row += m_columns) {
- for (int j = 0; j < colLimit; j++) {
- // One right and one up
- m_normals[totalIndex++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(row + m_columns + j),
- flipNormal);
- }
- int p = row + colLimit;
- // One up and one left
- m_normals[totalIndex++] = normal(m_vertices.at(p),
- m_vertices.at(p + m_columns),
- m_vertices.at(p - 1),
- flipNormal);
+ if ((startRow == 0) && !upwards) {
+ createSmoothNormalUpperLine(totalIndex);
+ startRow++;
}
- if (rowIndex == m_rows - 1) {
- // Top most line, nothing above, must have different handling.
- // Take from one down and one right. Read till second-to-last
- rowLimit = (rowIndex + 1) * m_columns - 1;
- for (int j = rowIndex * m_columns; j < rowLimit; j++) {
- m_normals[totalIndex++] = normal(m_vertices.at(j),
- m_vertices.at(j - m_columns),
- m_vertices.at(j + 1),
- flipNormal);
- }
- // Top left corner. Take from one left and one down
- m_normals[totalIndex++] = normal(m_vertices.at(rowLimit),
- m_vertices.at(rowLimit - 1),
- m_vertices.at(rowLimit - m_columns),
- flipNormal);
- }
+ for (int row = startRow; row <= endRow; row++)
+ createSmoothNormalBodyLine(totalIndex, row * m_columns);
+
+ if ((rowIndex == m_rows - 1) && upwards)
+ createSmoothNormalUpperLine(totalIndex);
}
-void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column)
+void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column,
+ bool polar)
{
// Update a vertice
- const QSurfaceDataItem &data = dataArray.at(row)->at(column);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[row * m_columns + column] = QVector3D(normalizedX, normalizedY, normalizedZ);
+ getNormalizedVertex(dataArray.at(row)->at(column),
+ m_vertices[row * m_columns + column], polar, false);
// Create normals
+ bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending);
+ bool rightwards = (m_dataDimension == BothAscending) || (m_dataDimension == ZDescending);
int startRow = row;
- if (startRow > 0)
- startRow--; // Change the normal for previous row also
+ if ((startRow > 0) && upwards)
+ startRow--;
+ int endRow = row;
+ if (!upwards && (row < m_rows - 1))
+ endRow++;
+ if ((endRow == m_rows - 1) && upwards)
+ endRow--;
int startCol = column;
- if (startCol > 0)
+ if ((startCol > 0) && rightwards)
startCol--;
- int rightCol = m_columns - 1;
- int topRow = m_rows - 1;
+ int endCol = column;
+ if ((endCol < m_columns - 1) && !rightwards)
+ endCol++;
- const bool flipNormal = checkFlipNormal(dataArray);
- for (int i = startRow; i <= row; i++) {
- for (int j = startCol; j <= column; j++) {
+ for (int i = startRow; i <= endRow; i++) {
+ for (int j = startCol; j <= endCol; j++) {
int p = i * m_columns + j;
- if (i < topRow) {
- if (j < rightCol) {
- // One right and one up
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + 1),
- m_vertices.at(p + m_columns),
- flipNormal);
- } else {
- // Last item, nothing on the right. One up and one left
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + m_columns),
- m_vertices.at(p - 1),
- flipNormal);
- }
- } else {
- // Top most line, nothing above, must have different handling.
- if (j < rightCol) {
- // Take from one down and one right. Read till second-to-last
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p - m_columns),
- m_vertices.at(p + 1),
- flipNormal);
- } else {
- // Top left corner. Take from one left and one down
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p - 1),
- m_vertices.at(p - m_columns),
- flipNormal);
- }
- }
- }
+ if ((i == 0) && !upwards)
+ m_normals[p] = createSmoothNormalUpperLineItem(j, i);
+ else if ((i == m_rows - 1) && upwards)
+ m_normals[p] = createSmoothNormalUpperLineItem(j, i);
+ else
+ m_normals[p] = createSmoothNormalBodyLineItem(j, i);
+ }
}
}
@@ -271,15 +423,38 @@ void SurfaceObject::createSmoothIndices(int x, int y, int endX, int endY)
int rowEnd = endY * m_columns;
for (int row = y * m_columns; row < rowEnd; row += m_columns) {
for (int j = x; j < endX; j++) {
- // Left triangle
- indices[p++] = row + j + 1;
- indices[p++] = row + m_columns + j;
- indices[p++] = row + j;
-
- // Right triangle
- indices[p++] = row + m_columns + j + 1;
- indices[p++] = row + m_columns + j;
- indices[p++] = row + j + 1;
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) {
+ // Left triangle
+ indices[p++] = row + j + 1;
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + j + 1;
+ } else if (m_dataDimension == XDescending) {
+ // Right triangle
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j;
+
+ // Left triangle
+ indices[p++] = row + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j + 1;
+ } else {
+ // Left triangle
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = row + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j + 1;
+
+ }
}
}
@@ -331,7 +506,7 @@ void SurfaceObject::createSmoothGridlineIndices(int x, int y, int endX, int endY
}
void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ)
+ bool changeGeometry, bool polar, bool flipXZ)
{
m_columns = space.width();
m_rows = space.height();
@@ -339,6 +514,12 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
GLfloat uvX = 1.0f / GLfloat(m_columns - 1);
GLfloat uvY = 1.0f / GLfloat(m_rows - 1);
+ checkDirections(dataArray);
+ bool indicesDirty = false;
+ if (m_dataDimension != m_oldDataDimension)
+ indicesDirty = true;
+ m_oldDataDimension = m_dataDimension;
+
m_surfaceType = SurfaceFlat;
// Create vertix table
@@ -355,17 +536,14 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
int doubleColumns = m_columns * 2 - 2;
int rowColLimit = rowLimit * doubleColumns;
- AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX;
- AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ;
+ // Init min and max to ridiculous values
+ m_minY = 10000000.0;
+ m_maxY = -10000000.0f;
for (int i = 0; i < m_rows; i++) {
const QSurfaceDataRow &row = *dataArray.at(i);
for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = row.at(j);
- float normalizedX = xCache.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = zCache.positionAt(data.z());
- m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ);
+ getNormalizedVertex(row.at(j), m_vertices[totalIndex], polar, flipXZ);
if (changeGeometry)
uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY);
@@ -389,42 +567,23 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
// Create normals & indices table
GLint *indices = 0;
- int p = 0;
- if (changeGeometry) {
+ if (changeGeometry || indicesDirty) {
int normalCount = 2 * colLimit * rowLimit;
m_indexCount = 3 * normalCount;
indices = new GLint[m_indexCount];
m_normals.resize(normalCount);
}
+ int p = 0;
totalIndex = 0;
- const bool flipNormal = checkFlipNormal(dataArray);
for (int row = 0, upperRow = doubleColumns;
row < rowColLimit;
row += doubleColumns, upperRow += doubleColumns) {
for (int j = 0; j < doubleColumns; j += 2) {
- // Normal for the left triangle
- m_normals[totalIndex++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
-
- // Normal for the right triangle
- m_normals[totalIndex++] = normal(m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
-
- if (changeGeometry) {
- // Left triangle
- indices[p++] = row + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j;
+ createNormals(totalIndex, row, upperRow, j);
- // Right triangle
- indices[p++] = upperRow + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j + 1;
+ if (changeGeometry || indicesDirty) {
+ createCoarseIndices(indices, p, row, upperRow, j);
}
}
}
@@ -433,12 +592,54 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
if (changeGeometry)
createCoarseGridlineIndices(0, 0, colLimit, rowLimit);
- createBuffers(m_vertices, uvs, m_normals, indices, changeGeometry);
+ createBuffers(m_vertices, uvs, m_normals, indices);
delete[] indices;
}
-void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex)
+void SurfaceObject::coarseUVs(const QSurfaceDataArray &dataArray,
+ const QSurfaceDataArray &modelArray)
+{
+ int columns = dataArray.at(0)->size();
+ int rows = dataArray.size();
+ float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x();
+ float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z();
+ float xMin = dataArray.at(0)->at(0).x();
+ float zMin = dataArray.at(0)->at(0).z();
+ const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending);
+ const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending);
+
+ QVector<QVector2D> uvs;
+ uvs.resize(m_rows * m_columns * 2);
+ int index = 0;
+ int colLimit = m_columns - 1;
+ for (int i = 0; i < m_rows; i++) {
+ float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer;
+ if (zDescending)
+ y = 1.0f - y;
+ const QSurfaceDataRow &p = *modelArray.at(i);
+ for (int j = 0; j < m_columns; j++) {
+ float x = (p.at(j).x() - xMin) / xRangeNormalizer;
+ if (xDescending)
+ x = 1.0f - x;
+ uvs[index] = QVector2D(x, y);
+ index++;
+ if (j > 0 && j < colLimit) {
+ uvs[index] = uvs[index - 1];
+ index++;
+ }
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer);
+ glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D),
+ &uvs.at(0), GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ m_returnTextureBuffer = true;
+}
+
+void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar)
{
int colLimit = m_columns - 1;
int doubleColumns = m_columns * 2 - 2;
@@ -447,12 +648,7 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI
const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex);
for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = dataRow.at(j);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ);
-
+ getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false);
if (j > 0 && j < colLimit) {
m_vertices[p] = m_vertices[p - 1];
p++;
@@ -466,39 +662,23 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI
int rowLimit = (rowIndex + 1) * doubleColumns;
if (rowIndex == m_rows - 1)
rowLimit = rowIndex * doubleColumns; //Topmost row, no normals
- const bool flipNormal = checkFlipNormal(dataArray);
for (int row = p, upperRow = p + doubleColumns;
row < rowLimit;
row += doubleColumns, upperRow += doubleColumns) {
- for (int j = 0; j < doubleColumns; j += 2) {
- // Normal for the left triangle
- m_normals[p++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
-
- // Normal for the right triangle
- m_normals[p++] = normal(m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
- }
+ for (int j = 0; j < doubleColumns; j += 2)
+ createNormals(p, row, upperRow, j);
}
}
-void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column)
+void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column,
+ bool polar)
{
int colLimit = m_columns - 1;
int doubleColumns = m_columns * 2 - 2;
// Update a vertice
int p = row * doubleColumns + column * 2 - (column > 0);
- const QSurfaceDataItem &data = dataArray.at(row)->at(column);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[p] = QVector3D(normalizedX, normalizedY, normalizedZ);
- p++;
+ getNormalizedVertex(dataArray.at(row)->at(column), m_vertices[p++], polar, false);
if (column > 0 && column < colLimit)
m_vertices[p] = m_vertices[p - 1];
@@ -515,27 +695,15 @@ void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row
if (column == m_columns - 1)
column--;
- const bool flipNormal = checkFlipNormal(dataArray);
for (int i = startRow; i <= row; i++) {
for (int j = startCol; j <= column; j++) {
p = i * doubleColumns + j * 2;
- // Normal for the left triangle
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + 1),
- m_vertices.at(p + doubleColumns),
- flipNormal);
- p++;
-
- // Normal for the right triangle
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + doubleColumns),
- m_vertices.at(p + doubleColumns - 1),
- flipNormal);
+ createNormals(p, i * doubleColumns, (i + 1) * doubleColumns, j * 2);
}
}
}
-void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows)
+void SurfaceObject::createCoarseSubSection(int x, int y, int columns, int rows)
{
if (columns > m_columns)
columns = m_columns;
@@ -557,17 +725,8 @@ void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows)
for (int row = y * doubleColumns, upperRow = (y + 1) * doubleColumns;
row < rowColLimit;
row += doubleColumns, upperRow += doubleColumns) {
- for (int j = 2 * x; j < doubleColumnsLimit; j += 2) {
- // Left triangle
- indices[p++] = row + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j;
-
- // Right triangle
- indices[p++] = upperRow + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j + 1;
- }
+ for (int j = 2 * x; j < doubleColumnsLimit; j += 2)
+ createCoarseIndices(indices, p, row, upperRow, j);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
@@ -631,12 +790,11 @@ void SurfaceObject::createCoarseGridlineIndices(int x, int y, int endX, int endY
void SurfaceObject::uploadBuffers()
{
QVector<QVector2D> uvs; // Empty dummy
- createBuffers(m_vertices, uvs, m_normals, 0, false);
+ createBuffers(m_vertices, uvs, m_normals, 0);
}
void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs,
- const QVector<QVector3D> &normals, const GLint *indices,
- bool changeGeometry)
+ const QVector<QVector3D> &normals, const GLint *indices)
{
// Move to buffers
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
@@ -647,30 +805,62 @@ void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVec
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(QVector3D),
&normals.at(0), GL_DYNAMIC_DRAW);
- if (changeGeometry) {
+ if (uvs.size()) {
glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D),
&uvs.at(0), GL_STATIC_DRAW);
+ }
- if (indices) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint),
- indices, GL_STATIC_DRAW);
- }
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ if (indices) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint),
+ indices, GL_STATIC_DRAW);
}
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
-bool SurfaceObject::checkFlipNormal(const QSurfaceDataArray &array)
+void SurfaceObject::checkDirections(const QSurfaceDataArray &array)
+{
+ m_dataDimension = BothAscending;
+
+ if (array.at(0)->at(0).x() > array.at(0)->at(array.at(0)->size() - 1).x())
+ m_dataDimension |= XDescending;
+ if (m_axisCacheX.reversed())
+ m_dataDimension ^= XDescending;
+
+ if (array.at(0)->at(0).z() > array.at(array.size() - 1)->at(0).z())
+ m_dataDimension |= ZDescending;
+ if (m_axisCacheZ.reversed())
+ m_dataDimension ^= ZDescending;
+}
+
+void SurfaceObject::getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex,
+ bool polar, bool flipXZ)
{
- const bool ascendingX = array.at(0)->at(0).x() < array.at(0)->at(array.at(0)->size() - 1).x();
- const bool ascendingZ = array.at(0)->at(0).z() < array.at(array.size() - 1)->at(0).z();
- return ascendingX != ascendingZ;
+ float normalizedX;
+ float normalizedZ;
+ if (polar) {
+ // Slice don't use polar, so don't care about flip
+ m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ);
+ } else {
+ if (flipXZ) {
+ normalizedX = m_axisCacheZ.positionAt(data.x());
+ normalizedZ = m_axisCacheX.positionAt(data.z());
+ } else {
+ normalizedX = m_axisCacheX.positionAt(data.x());
+ normalizedZ = m_axisCacheZ.positionAt(data.z());
+ }
+ }
+ float normalizedY = m_axisCacheY.positionAt(data.y());
+ m_minY = qMin(normalizedY, m_minY);
+ m_maxY = qMax(normalizedY, m_maxY);
+ vertex.setX(normalizedX);
+ vertex.setY(normalizedY);
+ vertex.setZ(normalizedZ);
}
GLuint SurfaceObject::gridElementBuf()
@@ -680,6 +870,17 @@ GLuint SurfaceObject::gridElementBuf()
return m_gridElementbuffer;
}
+GLuint SurfaceObject::uvBuf()
+{
+ if (!m_meshDataLoaded)
+ qFatal("No loaded object");
+
+ if (m_returnTextureBuffer)
+ return m_uvTextureBuffer;
+ else
+ return m_uvbuffer;
+}
+
GLuint SurfaceObject::gridIndexCount()
{
return m_gridIndexCount;
@@ -707,14 +908,74 @@ void SurfaceObject::clear()
m_normals.clear();
}
-QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c,
- bool flipNormal)
+void SurfaceObject::createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j)
+{
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) {
+ // Left triangle
+ indices[p++] = row + j + 1;
+ indices[p++] = upperRow + j;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = upperRow + j;
+ indices[p++] = row + j + 1;
+ } else if (m_dataDimension == XDescending) {
+ indices[p++] = upperRow + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j;
+
+ indices[p++] = row + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j + 1;
+ } else {
+ // Left triangle
+ indices[p++] = upperRow + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = row + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j + 1;
+ }
+}
+
+void SurfaceObject::createNormals(int &p, int row, int upperRow, int j)
+{
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) {
+ m_normals[p++] = normal(m_vertices.at(row + j),
+ m_vertices.at(row + j + 1),
+ m_vertices.at(upperRow + j));
+
+ m_normals[p++] = normal(m_vertices.at(row + j + 1),
+ m_vertices.at(upperRow + j + 1),
+ m_vertices.at(upperRow + j));
+ } else if (m_dataDimension == XDescending) {
+ m_normals[p++] = normal(m_vertices.at(row + j),
+ m_vertices.at(upperRow + j),
+ m_vertices.at(upperRow + j + 1));
+
+ m_normals[p++] = normal(m_vertices.at(row + j + 1),
+ m_vertices.at(row + j),
+ m_vertices.at(upperRow + j + 1));
+ } else {
+ m_normals[p++] = normal(m_vertices.at(row + j),
+ m_vertices.at(upperRow + j),
+ m_vertices.at(upperRow + j + 1));
+
+ m_normals[p++] = normal(m_vertices.at(row + j + 1),
+ m_vertices.at(row + j),
+ m_vertices.at(upperRow + j + 1));
+ }
+}
+
+QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c)
{
QVector3D v1 = b - a;
QVector3D v2 = c - a;
QVector3D normal = QVector3D::crossProduct(v1, v2);
- if (flipNormal)
- normal *= -1;
+
return normal;
}
diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h
index 9c18dcb2..e7b61310 100644
--- a/src/datavisualization/utils/surfaceobject_p.h
+++ b/src/datavisualization/utils/surfaceobject_p.h
@@ -49,34 +49,55 @@ public:
Undefined
};
+ enum DataDimension {
+ BothAscending = 0,
+ XDescending = 1,
+ ZDescending = 2,
+ BothDescending = XDescending | ZDescending
+ };
+ Q_DECLARE_FLAGS(DataDimensions, DataDimension)
+
public:
SurfaceObject(Surface3DRenderer *renderer);
- ~SurfaceObject();
+ virtual ~SurfaceObject();
void setUpData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ = false);
+ bool changeGeometry, bool polar, bool flipXZ = false);
void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ = false);
- void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex);
- void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow);
- void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column);
- void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column);
+ bool changeGeometry, bool polar, bool flipXZ = false);
+ void smoothUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray);
+ void coarseUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray);
+ void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar);
+ void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow, bool polar);
+ void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar);
+ void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar);
void createSmoothIndices(int x, int y, int endX, int endY);
- void createCoarseIndices(int x, int y, int columns, int rows);
+ void createCoarseSubSection(int x, int y, int columns, int rows);
void createSmoothGridlineIndices(int x, int y, int endX, int endY);
void createCoarseGridlineIndices(int x, int y, int endX, int endY);
void uploadBuffers();
GLuint gridElementBuf();
+ GLuint uvBuf();
GLuint gridIndexCount();
QVector3D vertexAt(int column, int row);
void clear();
+ float minYValue() const { return m_minY; }
+ float maxYValue() const { return m_maxY; }
+ inline void activateSurfaceTexture(bool value) { m_returnTextureBuffer = value; }
private:
- QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, bool flipNormal);
+ void createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j);
+ void createNormals(int &p, int row, int upperRow, int j);
+ void createSmoothNormalBodyLine(int &totalIndex, int column);
+ void createSmoothNormalUpperLine(int &totalIndex);
+ QVector3D createSmoothNormalBodyLineItem(int x, int y);
+ QVector3D createSmoothNormalUpperLineItem(int x, int y);
+ QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c);
void createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs,
- const QVector<QVector3D> &normals, const GLint *indices,
- bool changeGeometry);
- bool checkFlipNormal(const QSurfaceDataArray &array);
+ const QVector<QVector3D> &normals, const GLint *indices);
+ void checkDirections(const QSurfaceDataArray &array);
+ inline void getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, bool polar,
+ bool flipXZ);
private:
SurfaceType m_surfaceType;
@@ -90,6 +111,13 @@ private:
AxisRenderCache &m_axisCacheX;
AxisRenderCache &m_axisCacheY;
AxisRenderCache &m_axisCacheZ;
+ Surface3DRenderer *m_renderer;
+ float m_minY;
+ float m_maxY;
+ GLuint m_uvTextureBuffer;
+ bool m_returnTextureBuffer;
+ SurfaceObject::DataDimensions m_dataDimension;
+ SurfaceObject::DataDimensions m_oldDataDimension;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp
index 2a2a89dd..3944fb0c 100644
--- a/src/datavisualization/utils/texturehelper.cpp
+++ b/src/datavisualization/utils/texturehelper.cpp
@@ -21,12 +21,29 @@
#include <QtGui/QImage>
#include <QtGui/QPainter>
+#include <QtCore/QTime>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+// Defined in shaderhelper.cpp
+extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+
TextureHelper::TextureHelper()
{
initializeOpenGLFunctions();
+#if !defined(QT_OPENGL_ES_2)
+ if (!Utils::isOpenGLES()) {
+ // Discard warnings about deprecated functions
+ QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
+
+ m_openGlFunctions_2_1 =
+ QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
+ m_openGlFunctions_2_1->initializeOpenGLFunctions();
+
+ // Restore original message handler
+ qInstallMessageHandler(handler);
+ }
+#endif
}
TextureHelper::~TextureHelper()
@@ -41,17 +58,16 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt
QImage texImage = image;
-#if defined(QT_OPENGL_ES_2)
- GLuint temp;
- GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width(), temp);
- GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height(), temp);
- if (smoothScale) {
- texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- } else {
- texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio);
+ if (!Utils::isOpenGLES()) {
+ GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width());
+ GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height());
+ if (smoothScale) {
+ texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio,
+ Qt::SmoothTransformation);
+ } else {
+ texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio);
+ }
}
-#endif
GLuint textureId;
glGenTextures(1, &textureId);
@@ -73,6 +89,53 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt
if (clampY)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
+
+ return textureId;
+}
+
+GLuint TextureHelper::create3DTexture(const QVector<uchar> *data, int width, int height, int depth,
+ QImage::Format dataFormat)
+{
+ if (Utils::isOpenGLES() || !width || !height || !depth)
+ return 0;
+
+ GLuint textureId = 0;
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(dataFormat)
+ Q_UNUSED(data)
+#else
+ glEnable(GL_TEXTURE_3D);
+
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_3D, textureId);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ GLenum status = glGetError();
+ // glGetError docs advise to call glGetError in loop to clear all error flags
+ while (status)
+ status = glGetError();
+
+ GLint internalFormat = 4;
+ GLint format = GL_BGRA;
+ if (dataFormat == QImage::Format_Indexed8) {
+ internalFormat = 1;
+ format = GL_RED;
+ // Align width to 32bits
+ width = width + width % 4;
+ }
+ m_openGlFunctions_2_1->glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth, 0,
+ format, GL_UNSIGNED_BYTE, data->constData());
+ status = glGetError();
+ if (status)
+ qWarning() << __FUNCTION__ << "3D texture creation failed:" << status;
+
+ glBindTexture(GL_TEXTURE_3D, 0);
+ glDisable(GL_TEXTURE_3D);
+#endif
return textureId;
}
@@ -113,14 +176,27 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf
glBindTexture(GL_TEXTURE_2D, 0);
// Create render buffer
- if (!depthBuffer)
- glGenRenderbuffers(1, &depthBuffer);
+ if (depthBuffer)
+ glDeleteRenderbuffers(1, &depthBuffer);
+
+ glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
-#if !defined(QT_OPENGL_ES_2)
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
-#else
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
-#endif
+ GLenum status = glGetError();
+ // glGetError docs advise to call glGetError in loop to clear all error flags
+ while (status)
+ status = glGetError();
+ if (Utils::isOpenGLES())
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
+ else
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
+
+ status = glGetError();
+ if (status) {
+ qCritical() << "Selection texture render buffer creation failed:" << status;
+ glDeleteTextures(1, &textureid);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ return 0;
+ }
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// Create frame buffer
@@ -134,10 +210,11 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
// Verify that the frame buffer is complete
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
- qCritical() << "Frame buffer creation failed" << status;
- return 0;
+ qCritical() << "Selection texture frame buffer creation failed:" << status;
+ glDeleteTextures(1, &textureid);
+ textureid = 0;
}
// Restore the default framebuffer
@@ -146,6 +223,31 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf
return textureid;
}
+GLuint TextureHelper::createCursorPositionTexture(const QSize &size, GLuint &frameBuffer)
+{
+ GLuint textureid;
+ glGenTextures(1, &textureid);
+ glBindTexture(GL_TEXTURE_2D, textureid);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glGenFramebuffers(1, &frameBuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ textureid, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ qCritical() << "Cursor position mapper frame buffer creation failed:" << status;
+ glDeleteTextures(1, &textureid);
+ textureid = 0;
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return textureid;
+}
+
GLuint TextureHelper::createUniformTexture(const QColor &color)
{
QImage image(QSize(int(uniformTextureWidth), int(uniformTextureHeight)),
@@ -170,76 +272,64 @@ GLuint TextureHelper::createGradientTexture(const QLinearGradient &gradient)
return create2DTexture(image, false, true, false, true);
}
-#if !defined(QT_OPENGL_ES_2)
GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint textureSize)
{
- GLuint depthtextureid;
-
- // Create depth texture for the shadow mapping
- glGenTextures(1, &depthtextureid);
- glBindTexture(GL_TEXTURE_2D, depthtextureid);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize,
- size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
+ GLuint depthtextureid = 0;
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(size)
+ Q_UNUSED(textureSize)
+#else
+ if (!Utils::isOpenGLES()) {
+ // Create depth texture for the shadow mapping
+ glGenTextures(1, &depthtextureid);
+ glBindTexture(GL_TEXTURE_2D, depthtextureid);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize,
+ size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+#endif
return depthtextureid;
}
-#endif
-#if !defined(QT_OPENGL_ES_2)
GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer,
GLuint textureSize)
{
GLuint depthtextureid = createDepthTexture(size, textureSize);
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(frameBuffer)
+#else
+ if (!Utils::isOpenGLES()) {
+ // Create frame buffer
+ if (!frameBuffer)
+ glGenFramebuffers(1, &frameBuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
+
+ // Attach texture to depth attachment
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0);
+
+ m_openGlFunctions_2_1->glDrawBuffer(GL_NONE);
+ m_openGlFunctions_2_1->glReadBuffer(GL_NONE);
+
+ // Verify that the frame buffer is complete
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ qCritical() << "Depth texture frame buffer creation failed" << status;
+ glDeleteTextures(1, &depthtextureid);
+ depthtextureid = 0;
+ }
- // Create frame buffer
- if (!frameBuffer)
- glGenFramebuffers(1, &frameBuffer);
- glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
-
- // Attach texture to depth attachment
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0);
-
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
-
- // Verify that the frame buffer is complete
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- qCritical() << "Frame buffer creation failed" << status;
- return 0;
+ // Restore the default framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-
- // Restore the default framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- return depthtextureid;
-}
#endif
-
-#if !defined(QT_OPENGL_ES_2)
-void TextureHelper::fillDepthTexture(GLuint texture,const QSize &size, GLuint textureSize,
- GLfloat value)
-{
- int nItems = size.width() * textureSize * size.height() * textureSize;
- GLfloat *bits = new GLfloat[nItems];
- for (int i = 0; i < nItems; i++)
- bits[i] = value;
-
- glBindTexture(GL_TEXTURE_2D, texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize,
- size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, bits);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- delete[] bits;
+ return depthtextureid;
}
-#endif
void TextureHelper::deleteTexture(GLuint *texture)
{
diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h
index aec137a4..6c0aa3de 100644
--- a/src/datavisualization/utils/texturehelper_p.h
+++ b/src/datavisualization/utils/texturehelper_p.h
@@ -32,6 +32,10 @@
#include "datavisualizationglobal_p.h"
#include <QtGui/QRgb>
#include <QtGui/QLinearGradient>
+#if !defined(QT_OPENGL_ES_2)
+// 3D Textures are not supported by ES set
+# include <QtGui/QOpenGLFunctions_2_1>
+#endif
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -44,17 +48,17 @@ class TextureHelper : protected QOpenGLFunctions
// Ownership of created texture is transferred to caller
GLuint create2DTexture(const QImage &image, bool useTrilinearFiltering = false,
bool convert = true, bool smoothScale = true, bool clampY = false);
+ GLuint create3DTexture(const QVector<uchar> *data, int width, int height, int depth,
+ QImage::Format dataFormat);
GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false);
// Returns selection texture and inserts generated framebuffers to framebuffer parameters
GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer);
+ GLuint createCursorPositionTexture(const QSize &size, GLuint &frameBuffer);
GLuint createUniformTexture(const QColor &color);
GLuint createGradientTexture(const QLinearGradient &gradient);
-#if !defined(QT_OPENGL_ES_2)
GLuint createDepthTexture(const QSize &size, GLuint textureSize);
// Returns depth texture and inserts generated framebuffer to parameter
GLuint createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize);
- void fillDepthTexture(GLuint texture, const QSize &size, GLuint textureSize, GLfloat value);
-#endif
void deleteTexture(GLuint *texture);
private:
@@ -62,9 +66,13 @@ class TextureHelper : protected QOpenGLFunctions
void convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage, GLenum texture_format);
QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format);
+#if !defined(QT_OPENGL_ES_2)
+ QOpenGLFunctions_2_1 *m_openGlFunctions_2_1; // Not owned
+#endif
friend class Bars3DRenderer;
friend class Surface3DRenderer;
friend class Scatter3DRenderer;
+ friend class Abstract3DRenderer;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp
index 2368d86a..cb64eb8f 100644
--- a/src/datavisualization/utils/utils.cpp
+++ b/src/datavisualization/utils/utils.cpp
@@ -17,6 +17,7 @@
****************************************************************************/
#include "utils_p.h"
+#include "qutils.h"
#include <QtGui/QPainter>
@@ -25,11 +26,12 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
#define NUM_IN_POWER(y, x) for (;y<x;y<<=1)
#define MIN_POWER 2
-GLuint Utils::getNearestPowerOfTwo(GLuint value, GLuint &padding)
+static GLint maxTextureSize = 0; // Safe, as all instances have the same texture size
+
+GLuint Utils::getNearestPowerOfTwo(GLuint value)
{
GLuint powOfTwoValue = MIN_POWER;
NUM_IN_POWER(powOfTwoValue, value);
- padding = powOfTwoValue - value;
return powOfTwoValue;
}
@@ -54,35 +56,76 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo
const QColor &txtColor, bool labelBackground,
bool borders, int maxLabelWidth)
{
+ if (maxTextureSize == 0) {
+ QOpenGLContext::currentContext()->functions()->glGetIntegerv(
+ GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ }
GLuint paddingWidth = 20;
GLuint paddingHeight = 20;
+ GLuint prePadding = 20;
+ GLint targetWidth = maxTextureSize;
+
// Calculate text dimensions
QFont valueFont = font;
valueFont.setPointSize(textureFontSize);
QFontMetrics valueFM(valueFont);
int valueStrWidth = valueFM.width(text);
- if (maxLabelWidth && labelBackground)
+
+ // ES2 needs to use maxLabelWidth always (when given) because of the power of 2 -issue.
+ if (maxLabelWidth && (labelBackground || Utils::isOpenGLES()))
valueStrWidth = maxLabelWidth;
int valueStrHeight = valueFM.height();
valueStrWidth += paddingWidth / 2; // Fix clipping problem with skewed fonts (italic or italic-style)
QSize labelSize;
+ qreal fontRatio = 1.0;
-#if defined(QT_OPENGL_ES_2)
- // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly.
- // Add some padding before converting to power of two to avoid too tight fit
- GLuint prePadding = 5;
- // ES2 needs to use this always (when given) because of the power of 2 -issue.
- if (maxLabelWidth)
- valueStrWidth = maxLabelWidth + paddingWidth / 2;
- labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding);
- labelSize.setWidth(getNearestPowerOfTwo(labelSize.width(), paddingWidth));
- labelSize.setHeight(getNearestPowerOfTwo(labelSize.height(), paddingHeight));
-#else
- if (!labelBackground)
- labelSize = QSize(valueStrWidth, valueStrHeight);
- else
- labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2);
-#endif
+ if (Utils::isOpenGLES()) {
+ // Test if text with slighly smaller font would fit into one step smaller texture
+ // ie. if the text is just exceeded the smaller texture boundary, it would
+ // make a label with large empty space
+ uint testWidth = getNearestPowerOfTwo(valueStrWidth + prePadding) >> 1;
+ int diffToFit = (valueStrWidth + prePadding) - testWidth;
+ int maxSqueeze = int((valueStrWidth + prePadding) * 0.1f);
+ if (diffToFit < maxSqueeze && maxTextureSize > GLint(testWidth))
+ targetWidth = testWidth;
+ }
+
+ bool sizeOk = false;
+ int currentFontSize = textureFontSize;
+ do {
+ if (Utils::isOpenGLES()) {
+ // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly.
+ // Add some padding before converting to power of two to avoid too tight fit
+ labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding);
+ labelSize.setWidth(getNearestPowerOfTwo(labelSize.width()));
+ labelSize.setHeight(getNearestPowerOfTwo(labelSize.height()));
+ } else {
+ if (!labelBackground)
+ labelSize = QSize(valueStrWidth, valueStrHeight);
+ else
+ labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2);
+ }
+
+ if (!maxTextureSize || (labelSize.width() <= maxTextureSize
+ && (labelSize.width() <= targetWidth || !Utils::isOpenGLES()))) {
+ // Make sure the label is not too wide
+ sizeOk = true;
+ } else if (--currentFontSize == 4) {
+ qCritical() << "Label" << text << "is too long to be generated.";
+ return QImage();
+ } else {
+ fontRatio = (qreal)currentFontSize / (qreal)textureFontSize;
+ // Reduce font size and try again
+ valueFont.setPointSize(currentFontSize);
+ QFontMetrics currentValueFM(valueFont);
+ if (maxLabelWidth && (labelBackground || Utils::isOpenGLES()))
+ valueStrWidth = maxLabelWidth * fontRatio;
+ else
+ valueStrWidth = currentValueFM.width(text);
+ valueStrHeight = currentValueFM.height();
+ valueStrWidth += paddingWidth / 2;
+ }
+ } while (!sizeOk);
// Create image
QImage image = QImage(labelSize, QImage::Format_ARGB32);
@@ -96,27 +139,30 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo
painter.setFont(valueFont);
if (!labelBackground) {
painter.setPen(txtColor);
-#if defined(QT_OPENGL_ES_2)
- painter.drawText((labelSize.width() - valueStrWidth) / 2.0f,
- (labelSize.height() - valueStrHeight) / 2.0f,
- valueStrWidth, valueStrHeight,
- Qt::AlignCenter | Qt::AlignVCenter,
- text);
-#else
- painter.drawText(0, 0,
- valueStrWidth, valueStrHeight,
- Qt::AlignCenter | Qt::AlignVCenter,
- text);
-#endif
+ if (Utils::isOpenGLES()) {
+ painter.drawText((labelSize.width() - valueStrWidth) / 2.0f,
+ (labelSize.height() - valueStrHeight) / 2.0f,
+ valueStrWidth, valueStrHeight,
+ Qt::AlignCenter | Qt::AlignVCenter,
+ text);
+ } else {
+ painter.drawText(0, 0,
+ valueStrWidth, valueStrHeight,
+ Qt::AlignCenter | Qt::AlignVCenter,
+ text);
+ }
} else {
painter.setBrush(QBrush(bgrColor));
+ qreal radius = 10.0 * fontRatio;
if (borders) {
- painter.setPen(QPen(QBrush(txtColor), 5, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
- painter.drawRoundedRect(5, 5, labelSize.width() - 10, labelSize.height() - 10,
- 10.0, 10.0);
+ painter.setPen(QPen(QBrush(txtColor), 5.0 * fontRatio,
+ Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
+ painter.drawRoundedRect(5, 5,
+ labelSize.width() - 10, labelSize.height() - 10,
+ radius, radius);
} else {
painter.setPen(bgrColor);
- painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), 10.0, 10.0);
+ painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), radius, radius);
}
painter.setPen(txtColor);
painter.drawText((labelSize.width() - valueStrWidth) / 2.0f,
@@ -133,62 +179,74 @@ QVector4D Utils::getSelection(QPoint mousepos, int height)
// This is the only one that works with OpenGL ES 2.0, so we're forced to use it
// Item count will be limited to 256*256*256
GLubyte pixel[4] = {255, 255, 255, 255};
- glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
- (void *)pixel);
+ QOpenGLContext::currentContext()->functions()->glReadPixels(mousepos.x(), height - mousepos.y(),
+ 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ (void *)pixel);
QVector4D selectedColor(pixel[0], pixel[1], pixel[2], pixel[3]);
return selectedColor;
}
-QImage Utils::getGradientImage(const QLinearGradient &gradient)
+QImage Utils::getGradientImage(QLinearGradient &gradient)
{
- QImage image(QSize(1, 101), QImage::Format_RGB32);
+ QImage image(QSize(gradientTextureWidth, gradientTextureHeight), QImage::Format_RGB32);
+ gradient.setFinalStop(qreal(gradientTextureWidth), qreal(gradientTextureHeight));
+ gradient.setStart(0.0, 0.0);
+
QPainter pmp(&image);
pmp.setBrush(QBrush(gradient));
pmp.setPen(Qt::NoPen);
- pmp.drawRect(0, 0, 1, 101);
+ pmp.drawRect(0, 0, int(gradientTextureWidth), int(gradientTextureHeight));
return image;
}
-Utils::ParamType Utils::mapFormatCharToParamType(const QChar &formatChar)
+Utils::ParamType Utils::preParseFormat(const QString &format, QString &preStr, QString &postStr,
+ int &precision, char &formatSpec)
{
- ParamType retVal = ParamTypeUnknown;
- if (formatChar == QLatin1Char('d')
- || formatChar == QLatin1Char('i')
- || formatChar == QLatin1Char('c')) {
- retVal = ParamTypeInt;
- } else if (formatChar == QLatin1Char('u')
- || formatChar == QLatin1Char('o')
- || formatChar == QLatin1Char('x')
- || formatChar == QLatin1Char('X')) {
- retVal = ParamTypeUInt;
- } else if (formatChar == QLatin1Char('f')
- || formatChar == QLatin1Char('F')
- || formatChar == QLatin1Char('e')
- || formatChar == QLatin1Char('E')
- || formatChar == QLatin1Char('g')
- || formatChar == QLatin1Char('G')) {
- retVal = ParamTypeReal;
+ static QRegExp formatMatcher(QStringLiteral("^([^%]*)%([\\-\\+#\\s\\d\\.lhjztL]*)([dicuoxfegXFEG])(.*)$"));
+ static QRegExp precisionMatcher(QStringLiteral("\\.(\\d+)"));
+
+ Utils::ParamType retVal;
+
+ if (formatMatcher.indexIn(format, 0) != -1) {
+ preStr = formatMatcher.cap(1);
+ // Six and 'g' are defaults in Qt API
+ precision = 6;
+ if (!formatMatcher.cap(2).isEmpty()) {
+ if (precisionMatcher.indexIn(formatMatcher.cap(2), 0) != -1)
+ precision = precisionMatcher.cap(1).toInt();
+ }
+ if (formatMatcher.cap(3).isEmpty())
+ formatSpec = 'g';
+ else
+ formatSpec = formatMatcher.cap(3).at(0).toLatin1();
+ postStr = formatMatcher.cap(4);
+ retVal = mapFormatCharToParamType(formatSpec);
+ } else {
+ retVal = ParamTypeUnknown;
+ // The out parameters are irrelevant in unknown case
}
return retVal;
}
-Utils::ParamType Utils::findFormatParamType(const QString &format)
+Utils::ParamType Utils::mapFormatCharToParamType(char formatSpec)
{
- static QRegExp formatMatcher(QStringLiteral("%[\\-\\+#\\s\\d\\.lhjztL]*([dicuoxfegXFEG])"));
-
- if (formatMatcher.indexIn(format, 0) != -1) {
- QString capStr = formatMatcher.cap(1);
- if (capStr.isEmpty())
- return ParamTypeUnknown;
- else
- return mapFormatCharToParamType(capStr.at(0));
+ ParamType retVal = ParamTypeUnknown;
+ if (formatSpec == 'd' || formatSpec == 'i' || formatSpec == 'c') {
+ retVal = ParamTypeInt;
+ } else if (formatSpec == 'u' || formatSpec == 'o'
+ || formatSpec == 'x'|| formatSpec == 'X') {
+ retVal = ParamTypeUInt;
+ } else if (formatSpec == 'f' || formatSpec == 'F'
+ || formatSpec == 'e' || formatSpec == 'E'
+ || formatSpec == 'g' || formatSpec == 'G') {
+ retVal = ParamTypeReal;
}
- return ParamTypeUnknown;
+ return retVal;
}
-QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal value)
+QString Utils::formatLabelSprintf(const QByteArray &format, Utils::ParamType paramType, qreal value)
{
switch (paramType) {
case ParamTypeInt:
@@ -198,7 +256,24 @@ QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal
case ParamTypeReal:
return QString().sprintf(format, value);
default:
- return QString::fromUtf8(format); // To detect errors
+ // Return format string to detect errors. Bars selection label logic also depends on this.
+ return QString::fromUtf8(format);
+ }
+}
+
+QString Utils::formatLabelLocalized(Utils::ParamType paramType, qreal value,
+ const QLocale &locale, const QString &preStr, const QString &postStr,
+ int precision, char formatSpec, const QByteArray &format)
+{
+ switch (paramType) {
+ case ParamTypeInt:
+ case ParamTypeUInt:
+ return preStr + locale.toString(qint64(value)) + postStr;
+ case ParamTypeReal:
+ return preStr + locale.toString(value, formatSpec, precision) + postStr;
+ default:
+ // Return format string to detect errors. Bars selection label logic also depends on this.
+ return QString::fromUtf8(format);
}
}
@@ -238,4 +313,41 @@ QQuaternion Utils::calculateRotation(const QVector3D &xyzRotations)
return totalRotation;
}
+bool Utils::isOpenGLES()
+{
+#if defined(QT_OPENGL_ES_2)
+ return true;
+#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
+ return false;
+#else
+ static bool resolved = false;
+ static bool isES = false;
+ if (!resolved) {
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QWindow *dummySurface = 0;
+ if (!ctx) {
+ QSurfaceFormat surfaceFormat = qDefaultSurfaceFormat();
+ dummySurface = new QWindow();
+ dummySurface->setSurfaceType(QWindow::OpenGLSurface);
+ dummySurface->setFormat(surfaceFormat);
+ dummySurface->create();
+ ctx = new QOpenGLContext;
+ ctx->setFormat(surfaceFormat);
+ ctx->create();
+ ctx->makeCurrent(dummySurface);
+ }
+
+ isES = ctx->isOpenGLES();
+ resolved = true;
+
+ if (dummySurface) {
+ ctx->doneCurrent();
+ delete ctx;
+ delete dummySurface;
+ }
+ }
+ return isES;
+#endif
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/utils.pri b/src/datavisualization/utils/utils.pri
index 30bfb156..904407b8 100644
--- a/src/datavisualization/utils/utils.pri
+++ b/src/datavisualization/utils/utils.pri
@@ -22,3 +22,5 @@ SOURCES += $$PWD/meshloader.cpp \
$$PWD/surfaceobject.cpp \
$$PWD/scatterobjectbufferhelper.cpp \
$$PWD/scatterpointbufferhelper.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h
index d7187c16..1a46c731 100644
--- a/src/datavisualization/utils/utils_p.h
+++ b/src/datavisualization/utils/utils_p.h
@@ -45,7 +45,7 @@ public:
ParamTypeReal
};
- static GLuint getNearestPowerOfTwo(GLuint value, GLuint &padding);
+ static GLuint getNearestPowerOfTwo(GLuint value);
static QVector4D vectorFromColor(const QColor &color);
static QColor colorFromVector(const QVector3D &colorVector);
static QColor colorFromVector(const QVector4D &colorVector);
@@ -57,17 +57,22 @@ public:
bool borders = false,
int maxLabelWidth = 0);
static QVector4D getSelection(QPoint mousepos, int height);
- static QImage getGradientImage(const QLinearGradient &gradient);
+ static QImage getGradientImage(QLinearGradient &gradient);
- static ParamType findFormatParamType(const QString &format);
- static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value);
+ static ParamType preParseFormat(const QString &format, QString &preStr, QString &postStr,
+ int &precision, char &formatSpec);
+ static QString formatLabelSprintf(const QByteArray &format, ParamType paramType, qreal value);
+ static QString formatLabelLocalized(ParamType paramType, qreal value,
+ const QLocale &locale, const QString &preStr, const QString &postStr,
+ int precision, char formatSpec, const QByteArray &format);
static QString defaultLabelFormat();
static float wrapValue(float value, float min, float max);
static QQuaternion calculateRotation(const QVector3D &xyzRotations);
+ static bool isOpenGLES();
private:
- static ParamType mapFormatCharToParamType(const QChar &formatChar);
+ static ParamType mapFormatCharToParamType(char formatSpec);
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp
index f7ccbf21..51c254ec 100644
--- a/src/datavisualizationqml2/abstractdeclarative.cpp
+++ b/src/datavisualizationqml2/abstractdeclarative.cpp
@@ -19,11 +19,13 @@
#include "abstractdeclarative_p.h"
#include "declarativetheme_p.h"
#include "declarativerendernode_p.h"
-
#include <QtGui/QGuiApplication>
#if defined(Q_OS_IOS)
#include <QtCore/QTimer>
#endif
+#if defined(Q_OS_OSX)
+#include <qpa/qplatformnativeinterface.h>
+#endif
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -36,11 +38,7 @@ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) :
m_controller(0),
m_contextWindow(0),
m_renderMode(RenderIndirect),
- #if defined(QT_OPENGL_ES_2)
m_samples(0),
- #else
- m_samples(4),
- #endif
m_windowSamples(0),
m_initialisedSize(0, 0),
#ifdef USE_SHARED_CONTEXT
@@ -53,7 +51,6 @@ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) :
m_contextThread(0)
{
connect(this, &QQuickItem::windowChanged, this, &AbstractDeclarative::handleWindowChanged);
- setAntialiasing(m_samples > 0);
// Set contents to false in case we are in qml designer to make component look nice
m_runningInDesigner = QGuiApplication::applicationDisplayName() == "Qml2Puppet";
@@ -62,24 +59,7 @@ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) :
AbstractDeclarative::~AbstractDeclarative()
{
-#ifdef USE_SHARED_CONTEXT
- // Context can be in another thread, don't delete it directly in that case
- if (m_contextThread && m_contextThread != m_mainThread) {
- if (m_context)
- m_context->deleteLater();
- m_context = 0;
- } else {
- delete m_context;
- }
-#else
- if (m_contextThread && m_contextThread != m_mainThread) {
- if (m_stateStore)
- m_stateStore->deleteLater();
- m_stateStore = 0;
- } else {
- delete m_stateStore;
- }
-#endif
+ destroyContext();
disconnect(this, 0, this, 0);
checkWindowList(0);
@@ -293,6 +273,10 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller)
Q_ASSERT(controller);
m_controller = controller;
+ if (!m_controller->isOpenGLES())
+ m_samples = 4;
+ setAntialiasing(m_samples > 0);
+
// Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D.
DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D;
defaultTheme->d_ptr->setDefaultTheme(true);
@@ -329,6 +313,22 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller)
&AbstractDeclarative::aspectRatioChanged);
QObject::connect(m_controller.data(), &Abstract3DController::optimizationHintsChanged, this,
&AbstractDeclarative::handleOptimizationHintChange);
+ QObject::connect(m_controller.data(), &Abstract3DController::polarChanged, this,
+ &AbstractDeclarative::polarChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::radialLabelOffsetChanged, this,
+ &AbstractDeclarative::radialLabelOffsetChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::horizontalAspectRatioChanged, this,
+ &AbstractDeclarative::horizontalAspectRatioChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::reflectionChanged, this,
+ &AbstractDeclarative::reflectionChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::reflectivityChanged, this,
+ &AbstractDeclarative::reflectivityChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::localeChanged, this,
+ &AbstractDeclarative::localeChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::queriedGraphPositionChanged, this,
+ &AbstractDeclarative::queriedGraphPositionChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::marginChanged, this,
+ &AbstractDeclarative::marginChanged);
}
void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window)
@@ -347,7 +347,6 @@ void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window)
m_contextThread = QThread::currentThread();
m_contextWindow = window;
m_qtContext = currentContext;
-
m_context = new QOpenGLContext();
m_context->setFormat(m_qtContext->format());
m_context->setShareContext(m_qtContext);
@@ -355,6 +354,10 @@ void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window)
m_context->makeCurrent(window);
m_controller->initializeOpenGL();
+
+ // Make sure context gets deleted.
+ QObject::connect(m_contextThread, &QThread::finished, this,
+ &AbstractDeclarative::destroyContext, Qt::DirectConnection);
} else {
m_context->makeCurrent(window);
}
@@ -376,6 +379,10 @@ void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window)
m_stateStore->storeGLState();
m_controller->initializeOpenGL();
+
+ // Make sure state store gets deleted.
+ QObject::connect(m_contextThread, &QThread::finished, this,
+ &AbstractDeclarative::destroyContext, Qt::DirectConnection);
} else {
m_stateStore->storeGLState();
}
@@ -416,17 +423,15 @@ void AbstractDeclarative::setMsaaSamples(int samples)
if (m_renderMode != RenderIndirect) {
qWarning("Multisampling cannot be adjusted in this render mode");
} else {
-#if defined(QT_OPENGL_ES_2)
- if (samples > 0)
- qWarning("Multisampling is not supported in OpenGL ES2");
-#else
- if (m_samples != samples) {
+ if (m_controller->isOpenGLES()) {
+ if (samples > 0)
+ qWarning("Multisampling is not supported in OpenGL ES2");
+ } else if (m_samples != samples) {
m_samples = samples;
setAntialiasing(m_samples > 0);
emit msaaSamplesChanged(samples);
update();
}
-#endif
}
}
@@ -437,6 +442,18 @@ void AbstractDeclarative::handleWindowChanged(QQuickWindow *window)
if (!window)
return;
+#if defined(Q_OS_OSX)
+ bool previousVisibility = window->isVisible();
+ // Enable touch events for Mac touchpads
+ window->setVisible(true);
+ typedef void * (*EnableTouch)(QWindow*, bool);
+ EnableTouch enableTouch =
+ (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow");
+ if (enableTouch)
+ enableTouch(window, true);
+ window->setVisible(previousVisibility);
+#endif
+
connect(window, &QObject::destroyed, this, &AbstractDeclarative::windowDestroyed);
int oldWindowSamples = m_windowSamples;
@@ -556,24 +573,25 @@ void AbstractDeclarative::render()
// Clear the background once per window as that is not done by default
QQuickWindow *win = window();
activateOpenGLContext(win);
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
if (m_renderMode == RenderDirectToBackground && !clearList.contains(win)) {
clearList.append(win);
QColor clearColor = win->color();
- glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
+ funcs->glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), 1.0f);
+ funcs->glClear(GL_COLOR_BUFFER_BIT);
}
if (isVisible()) {
- glDepthMask(GL_TRUE);
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- glDisable(GL_BLEND);
+ funcs->glDepthMask(GL_TRUE);
+ funcs->glEnable(GL_DEPTH_TEST);
+ funcs->glDepthFunc(GL_LESS);
+ funcs->glEnable(GL_CULL_FACE);
+ funcs->glCullFace(GL_BACK);
+ funcs->glDisable(GL_BLEND);
m_controller->render();
- glEnable(GL_BLEND);
+ funcs->glEnable(GL_BLEND);
}
doneOpenGLContext(win);
}
@@ -704,7 +722,7 @@ AbstractDeclarative::ElementType AbstractDeclarative::selectedElement() const
void AbstractDeclarative::setAspectRatio(qreal ratio)
{
- m_controller->setAspectRatio(float(ratio));
+ m_controller->setAspectRatio(ratio);
}
qreal AbstractDeclarative::aspectRatio() const
@@ -724,6 +742,81 @@ AbstractDeclarative::OptimizationHints AbstractDeclarative::optimizationHints()
return OptimizationHints(intmode);
}
+void AbstractDeclarative::setPolar(bool enable)
+{
+ m_controller->setPolar(enable);
+}
+
+bool AbstractDeclarative::isPolar() const
+{
+ return m_controller->isPolar();
+}
+
+void AbstractDeclarative::setRadialLabelOffset(float offset)
+{
+ m_controller->setRadialLabelOffset(offset);
+}
+
+float AbstractDeclarative::radialLabelOffset() const
+{
+ return m_controller->radialLabelOffset();
+}
+
+void AbstractDeclarative::setHorizontalAspectRatio(qreal ratio)
+{
+ m_controller->setHorizontalAspectRatio(ratio);
+}
+
+qreal AbstractDeclarative::horizontalAspectRatio() const
+{
+ return m_controller->horizontalAspectRatio();
+}
+
+void AbstractDeclarative::setReflection(bool enable)
+{
+ m_controller->setReflection(enable);
+}
+
+bool AbstractDeclarative::isReflection() const
+{
+ return m_controller->reflection();
+}
+
+void AbstractDeclarative::setReflectivity(qreal reflectivity)
+{
+ m_controller->setReflectivity(reflectivity);
+}
+
+qreal AbstractDeclarative::reflectivity() const
+{
+ return m_controller->reflectivity();
+}
+
+void AbstractDeclarative::setLocale(const QLocale &locale)
+{
+ m_controller->setLocale(locale);
+}
+
+QLocale AbstractDeclarative::locale() const
+{
+ return m_controller->locale();
+}
+
+QVector3D AbstractDeclarative::queriedGraphPosition() const
+{
+ return m_controller->queriedGraphPosition();
+}
+
+void AbstractDeclarative::setMargin(qreal margin)
+{
+ m_controller->setMargin(margin);
+}
+
+qreal AbstractDeclarative::margin() const
+{
+ return m_controller->margin();
+}
+
void AbstractDeclarative::windowDestroyed(QObject *obj)
{
// Remove destroyed window from window lists
@@ -736,4 +829,31 @@ void AbstractDeclarative::windowDestroyed(QObject *obj)
windowClearList.remove(win);
}
+void AbstractDeclarative::destroyContext()
+{
+#ifdef USE_SHARED_CONTEXT
+ // Context can be in another thread, don't delete it directly in that case
+ if (m_contextThread && m_contextThread != m_mainThread) {
+ if (m_context)
+ m_context->deleteLater();
+ } else {
+ delete m_context;
+ }
+ m_context = 0;
+#else
+ if (m_contextThread && m_contextThread != m_mainThread) {
+ if (m_stateStore)
+ m_stateStore->deleteLater();
+ } else {
+ delete m_stateStore;
+ }
+ m_stateStore = 0;
+#endif
+ if (m_contextThread) {
+ QObject::disconnect(m_contextThread, &QThread::finished, this,
+ &AbstractDeclarative::destroyContext);
+ m_contextThread = 0;
+ }
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h
index dfcd9537..0c32a4a5 100644
--- a/src/datavisualizationqml2/abstractdeclarative_p.h
+++ b/src/datavisualizationqml2/abstractdeclarative_p.h
@@ -72,6 +72,14 @@ class AbstractDeclarative : public QQuickItem
Q_PROPERTY(ElementType selectedElement READ selectedElement NOTIFY selectedElementChanged REVISION 1)
Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged REVISION 1)
Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged REVISION 1)
+ Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged REVISION 2)
+ Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged REVISION 2)
+ Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged REVISION 2)
+ Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged REVISION 2)
+ Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged REVISION 2)
+ Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged REVISION 2)
+ Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged REVISION 2)
+ Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged REVISION 2)
public:
enum SelectionFlag {
@@ -193,11 +201,35 @@ public:
void setOptimizationHints(OptimizationHints hints);
OptimizationHints optimizationHints() const;
+ void setPolar(bool enable);
+ bool isPolar() const;
+
+ void setRadialLabelOffset(float offset);
+ float radialLabelOffset() const;
+
+ void setHorizontalAspectRatio(qreal ratio);
+ qreal horizontalAspectRatio() const;
+
+ void setReflection(bool enable);
+ bool isReflection() const;
+
+ void setReflectivity(qreal reflectivity);
+ qreal reflectivity() const;
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ QVector3D queriedGraphPosition() const;
+
+ void setMargin(qreal margin);
+ qreal margin() const;
+
public slots:
virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0;
virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0;
virtual void handleAxisZChanged(QAbstract3DAxis *axis) = 0;
void windowDestroyed(QObject *obj);
+ void destroyContext();
protected:
virtual void mouseDoubleClickEvent(QMouseEvent *event);
@@ -230,6 +262,14 @@ signals:
Q_REVISION(1) void orthoProjectionChanged(bool enabled);
Q_REVISION(1) void aspectRatioChanged(qreal ratio);
Q_REVISION(1) void optimizationHintsChanged(AbstractDeclarative::OptimizationHints hints);
+ Q_REVISION(2) void polarChanged(bool enabled);
+ Q_REVISION(2) void radialLabelOffsetChanged(float offset);
+ Q_REVISION(2) void horizontalAspectRatioChanged(qreal ratio);
+ Q_REVISION(2) void reflectionChanged(bool enabled);
+ Q_REVISION(2) void reflectivityChanged(qreal reflectivity);
+ Q_REVISION(2) void localeChanged(const QLocale &locale);
+ Q_REVISION(2) void queriedGraphPositionChanged(const QVector3D &data);
+ Q_REVISION(2) void marginChanged(qreal margin);
private:
QPointer<Abstract3DController> m_controller;
diff --git a/src/datavisualizationqml2/datavisualizationqml2.pro b/src/datavisualizationqml2/datavisualizationqml2.pro
index 7c65d69e..87376e70 100644
--- a/src/datavisualizationqml2/datavisualizationqml2.pro
+++ b/src/datavisualizationqml2/datavisualizationqml2.pro
@@ -1,5 +1,6 @@
TARGET = datavisualizationqml2
QT += qml quick datavisualization
+osx: QT += gui-private
TARGETPATH = QtDataVisualization
IMPORT_VERSION = $$MODULE_VERSION
diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp
index 09780dc5..5aaebf03 100644
--- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp
+++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp
@@ -93,6 +93,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri)
QLatin1String("Trying to create uncreatable: Abstract3DSeries."));
qmlRegisterUncreatableType<AbstractDeclarative, 1>(uri, 1, 1, "AbstractGraph3D",
QLatin1String("Trying to create uncreatable: AbstractGraph3D."));
+
qmlRegisterType<QValue3DAxis, 1>(uri, 1, 1, "ValueAxis3D");
qmlRegisterType<QItemModelBarDataProxy, 1>(uri, 1, 1, "ItemModelBarDataProxy");
qmlRegisterType<QItemModelSurfaceDataProxy, 1>(uri, 1, 1, "ItemModelSurfaceDataProxy");
@@ -106,6 +107,23 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri)
// New metatypes
qRegisterMetaType<QAbstract3DGraph::ElementType>("QAbstract3DGraph::ElementType");
+
+ // QtDataVisualization 1.2
+
+ // New revisions
+ qmlRegisterUncreatableType<AbstractDeclarative, 2>(uri, 1, 2, "AbstractGraph3D",
+ QLatin1String("Trying to create uncreatable: AbstractGraph3D."));
+ qmlRegisterUncreatableType<Declarative3DScene, 1>(uri, 1, 2, "Scene3D",
+ QLatin1String("Trying to create uncreatable: Scene3D."));
+ qmlRegisterType<DeclarativeSurface, 1>(uri, 1, 2, "Surface3D");
+ qmlRegisterType<Q3DCamera, 1>(uri, 1, 2, "Camera3D");
+ qmlRegisterType<QCustom3DItem, 1>(uri, 1, 2, "Custom3DItem");
+ qmlRegisterType<DeclarativeBars, 1>(uri, 1, 2, "Bars3D");
+
+ // New types
+ qmlRegisterType<Q3DInputHandler>(uri, 1, 2, "InputHandler3D");
+ qmlRegisterType<QTouch3DInputHandler>(uri, 1, 2, "TouchInputHandler3D");
+ qmlRegisterType<QCustom3DVolume>(uri, 1, 2, "Custom3DVolume");
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.h b/src/datavisualizationqml2/datavisualizationqml2_plugin.h
index 21ef85b8..8ece1c15 100644
--- a/src/datavisualizationqml2/datavisualizationqml2_plugin.h
+++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.h
@@ -42,11 +42,13 @@
#include "declarativeseries_p.h"
#include "q3dtheme.h"
#include "declarativetheme_p.h"
-#include "qabstract3dinputhandler.h"
+#include "q3dinputhandler.h"
+#include "qtouch3dinputhandler.h"
#include "declarativecolor_p.h"
#include "declarativescene_p.h"
#include "qcustom3ditem.h"
#include "qcustom3dlabel.h"
+#include "qcustom3dvolume.h"
#include <QtQml/QQmlExtensionPlugin>
@@ -97,9 +99,12 @@ QML_DECLARE_TYPE(Q3DTheme)
QML_DECLARE_TYPE(DeclarativeTheme3D)
QML_DECLARE_TYPE(QAbstract3DInputHandler)
+QML_DECLARE_TYPE(Q3DInputHandler)
+QML_DECLARE_TYPE(QTouch3DInputHandler)
QML_DECLARE_TYPE(QCustom3DItem)
QML_DECLARE_TYPE(QCustom3DLabel)
+QML_DECLARE_TYPE(QCustom3DVolume)
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/declarativebars.cpp b/src/datavisualizationqml2/declarativebars.cpp
index 9670a7db..61e6aafb 100644
--- a/src/datavisualizationqml2/declarativebars.cpp
+++ b/src/datavisualizationqml2/declarativebars.cpp
@@ -129,6 +129,19 @@ QBar3DSeries *DeclarativeBars::selectedSeries() const
return m_barsController->selectedSeries();
}
+void DeclarativeBars::setFloorLevel(float level)
+{
+ if (level != floorLevel()) {
+ m_barsController->setFloorLevel(level);
+ emit floorLevelChanged(level);
+ }
+}
+
+float DeclarativeBars::floorLevel() const
+{
+ return m_barsController->floorLevel();
+}
+
QQmlListProperty<QBar3DSeries> DeclarativeBars::seriesList()
{
return QQmlListProperty<QBar3DSeries>(this, this,
diff --git a/src/datavisualizationqml2/declarativebars_p.h b/src/datavisualizationqml2/declarativebars_p.h
index 52690813..05d66cdc 100644
--- a/src/datavisualizationqml2/declarativebars_p.h
+++ b/src/datavisualizationqml2/declarativebars_p.h
@@ -48,6 +48,7 @@ class DeclarativeBars : public AbstractDeclarative
Q_PROPERTY(QQmlListProperty<QBar3DSeries> seriesList READ seriesList)
Q_PROPERTY(QBar3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged)
Q_PROPERTY(QBar3DSeries *primarySeries READ primarySeries WRITE setPrimarySeries NOTIFY primarySeriesChanged)
+ Q_PROPERTY(float floorLevel READ floorLevel WRITE setFloorLevel NOTIFY floorLevelChanged REVISION 1)
Q_CLASSINFO("DefaultProperty", "seriesList")
public:
@@ -85,6 +86,9 @@ public:
QBar3DSeries *primarySeries() const;
QBar3DSeries *selectedSeries() const;
+ void setFloorLevel(float level);
+ float floorLevel() const;
+
public slots:
void handleAxisXChanged(QAbstract3DAxis *axis);
void handleAxisYChanged(QAbstract3DAxis *axis);
@@ -101,6 +105,7 @@ signals:
void meshFileNameChanged(QString filename);
void primarySeriesChanged(QBar3DSeries *series);
void selectedSeriesChanged(QBar3DSeries *series);
+ Q_REVISION(1) void floorLevelChanged(float level);
private:
Bars3DController *m_barsController;
diff --git a/src/datavisualizationqml2/declarativesurface.cpp b/src/datavisualizationqml2/declarativesurface.cpp
index 3075d207..ec520459 100644
--- a/src/datavisualizationqml2/declarativesurface.cpp
+++ b/src/datavisualizationqml2/declarativesurface.cpp
@@ -32,6 +32,8 @@ DeclarativeSurface::DeclarativeSurface(QQuickItem *parent)
QObject::connect(m_surfaceController, &Surface3DController::selectedSeriesChanged,
this, &DeclarativeSurface::selectedSeriesChanged);
+ QObject::connect(m_surfaceController, &Surface3DController::flipHorizontalGridChanged,
+ this, &DeclarativeSurface::flipHorizontalGridChanged);
}
DeclarativeSurface::~DeclarativeSurface()
@@ -74,6 +76,16 @@ QSurface3DSeries *DeclarativeSurface::selectedSeries() const
return m_surfaceController->selectedSeries();
}
+void DeclarativeSurface::setFlipHorizontalGrid(bool flip)
+{
+ m_surfaceController->setFlipHorizontalGrid(flip);
+}
+
+bool DeclarativeSurface::flipHorizontalGrid() const
+{
+ return m_surfaceController->flipHorizontalGrid();
+}
+
QQmlListProperty<QSurface3DSeries> DeclarativeSurface::seriesList()
{
return QQmlListProperty<QSurface3DSeries>(this, this,
diff --git a/src/datavisualizationqml2/declarativesurface_p.h b/src/datavisualizationqml2/declarativesurface_p.h
index 6fe800ba..ff6e4d70 100644
--- a/src/datavisualizationqml2/declarativesurface_p.h
+++ b/src/datavisualizationqml2/declarativesurface_p.h
@@ -44,6 +44,7 @@ class DeclarativeSurface : public AbstractDeclarative
Q_PROPERTY(QValue3DAxis *axisZ READ axisZ WRITE setAxisZ NOTIFY axisZChanged)
Q_PROPERTY(QSurface3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged)
Q_PROPERTY(QQmlListProperty<QSurface3DSeries> seriesList READ seriesList)
+ Q_PROPERTY(bool flipHorizontalGrid READ flipHorizontalGrid WRITE setFlipHorizontalGrid NOTIFY flipHorizontalGridChanged REVISION 1)
Q_CLASSINFO("DefaultProperty", "seriesList")
public:
@@ -66,6 +67,8 @@ public:
Q_INVOKABLE void removeSeries(QSurface3DSeries *series);
QSurface3DSeries *selectedSeries() const;
+ void setFlipHorizontalGrid(bool flip);
+ bool flipHorizontalGrid() const;
public slots:
void handleAxisXChanged(QAbstract3DAxis *axis);
@@ -77,6 +80,7 @@ signals:
void axisYChanged(QValue3DAxis *axis);
void axisZChanged(QValue3DAxis *axis);
void selectedSeriesChanged(QSurface3DSeries *series);
+ Q_REVISION(1) void flipHorizontalGridChanged(bool flip);
private:
Surface3DController *m_surfaceController;
diff --git a/src/datavisualizationqml2/designer/Bars3DSpecifics.qml b/src/datavisualizationqml2/designer/Bars3DSpecifics.qml
index cb5fb4a0..bd32d383 100644
--- a/src/datavisualizationqml2/designer/Bars3DSpecifics.qml
+++ b/src/datavisualizationqml2/designer/Bars3DSpecifics.qml
@@ -288,6 +288,92 @@ Column {
Layout.fillWidth: true
}
}
+ Label {
+ text: qsTr("aspectRatio")
+ toolTip: qsTr("Aspect Ratio")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.aspectRatio
+ minimumValue: 0.01
+ maximumValue: 100.0
+ stepSize: 0.01
+ decimals: 2
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("floorLevel")
+ toolTip: qsTr("Floor Level")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ LineEdit {
+ backendValue: backendValues.floorLevel
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("horizontalAspectRatio")
+ toolTip: qsTr("Horizontal Aspect Ratio")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.horizontalAspectRatio
+ minimumValue: 0.0
+ maximumValue: 100.0
+ stepSize: 0.01
+ decimals: 2
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("reflection")
+ toolTip: qsTr("Reflection")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ id: reflectionCheckbox
+ backendValue: backendValues.reflection
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("reflectivity")
+ toolTip: qsTr("Reflectivity")
+ Layout.fillWidth: true
+ visible: reflectionCheckbox.checked
+ }
+ SecondColumnLayout {
+ visible: reflectionCheckbox.checked
+ SpinBox {
+ backendValue: backendValues.reflectivity
+ minimumValue: 0.0
+ maximumValue: 1.0
+ stepSize: 0.01
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("margin")
+ toolTip: qsTr("Graph Margin")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.margin
+ minimumValue: -1.0
+ maximumValue: 100.0
+ stepSize: 0.1
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
// Kept for debugging
Label { }
diff --git a/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml b/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml
index 1e2556ec..131f71fd 100644
--- a/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml
+++ b/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml
@@ -121,6 +121,78 @@ Column {
Layout.fillWidth: true
}
}
+ Label {
+ text: qsTr("optimizationHints")
+ toolTip: qsTr("Optimization Hints")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ ComboBox {
+ backendValue: backendValues.optimizationHints
+ model: ["OptimizationDefault", "OptimizationStatic"]
+ Layout.fillWidth: true
+ scope: "AbstractGraph3D"
+ }
+ }
+ Label {
+ text: qsTr("polar")
+ toolTip: qsTr("Use Polar Coordinates")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ id: polarCheckbox
+ backendValue: backendValues.polar
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("radialLabelOffset")
+ toolTip: qsTr("Radial Label Offset")
+ Layout.fillWidth: true
+ visible: polarCheckbox.checked
+ }
+ SecondColumnLayout {
+ visible: polarCheckbox.checked
+ SpinBox {
+ backendValue: backendValues.radialLabelOffset
+ minimumValue: 0.0
+ maximumValue: 1.0
+ stepSize: 0.01
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("horizontalAspectRatio")
+ toolTip: qsTr("Horizontal Aspect Ratio")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.horizontalAspectRatio
+ minimumValue: 0.0
+ maximumValue: 100.0
+ stepSize: 0.01
+ decimals: 2
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("margin")
+ toolTip: qsTr("Graph Margin")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.margin
+ minimumValue: -1.0
+ maximumValue: 100.0
+ stepSize: 0.1
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
}
}
}
diff --git a/src/datavisualizationqml2/designer/Surface3DSpecifics.qml b/src/datavisualizationqml2/designer/Surface3DSpecifics.qml
index 65a65d37..a834f677 100644
--- a/src/datavisualizationqml2/designer/Surface3DSpecifics.qml
+++ b/src/datavisualizationqml2/designer/Surface3DSpecifics.qml
@@ -241,6 +241,76 @@ Column {
Layout.fillWidth: true
}
}
+ Label {
+ text: qsTr("flipHorizontalGrid")
+ toolTip: qsTr("Flip Horizontal Grid")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ backendValue: backendValues.flipHorizontalGrid
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("polar")
+ toolTip: qsTr("Use Polar Coordinates")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ id: polarCheckbox
+ backendValue: backendValues.polar
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("radialLabelOffset")
+ toolTip: qsTr("Radial Label Offset")
+ Layout.fillWidth: true
+ visible: polarCheckbox.checked
+ }
+ SecondColumnLayout {
+ visible: polarCheckbox.checked
+ SpinBox {
+ backendValue: backendValues.radialLabelOffset
+ minimumValue: 0.0
+ maximumValue: 1.0
+ stepSize: 0.01
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("horizontalAspectRatio")
+ toolTip: qsTr("Horizontal Aspect Ratio")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.horizontalAspectRatio
+ minimumValue: 0.0
+ maximumValue: 100.0
+ stepSize: 0.01
+ decimals: 2
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("margin")
+ toolTip: qsTr("Graph Margin")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.margin
+ minimumValue: -1.0
+ maximumValue: 100.0
+ stepSize: 0.1
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
// Kept for debugging
Label { }
diff --git a/src/datavisualizationqml2/designer/default/Bars3D.qml b/src/datavisualizationqml2/designer/default/Bars3D.qml
index 10fefe53..c85c0e94 100644
--- a/src/datavisualizationqml2/designer/default/Bars3D.qml
+++ b/src/datavisualizationqml2/designer/default/Bars3D.qml
@@ -17,7 +17,7 @@
****************************************************************************/
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Bars3D {
width: 300
diff --git a/src/datavisualizationqml2/designer/default/Scatter3D.qml b/src/datavisualizationqml2/designer/default/Scatter3D.qml
index b08d4e24..0bd6cac2 100644
--- a/src/datavisualizationqml2/designer/default/Scatter3D.qml
+++ b/src/datavisualizationqml2/designer/default/Scatter3D.qml
@@ -17,7 +17,7 @@
****************************************************************************/
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Scatter3D {
width: 300
diff --git a/src/datavisualizationqml2/designer/default/Surface3D.qml b/src/datavisualizationqml2/designer/default/Surface3D.qml
index 77ee476e..f9de62a1 100644
--- a/src/datavisualizationqml2/designer/default/Surface3D.qml
+++ b/src/datavisualizationqml2/designer/default/Surface3D.qml
@@ -17,7 +17,7 @@
****************************************************************************/
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Surface3D {
width: 300
diff --git a/src/datavisualizationqml2/plugins.qmltypes b/src/datavisualizationqml2/plugins.qmltypes
index 6a580536..956100ed 100644
--- a/src/datavisualizationqml2/plugins.qmltypes
+++ b/src/datavisualizationqml2/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.1
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtDataVisualization 1.1'
+// 'qmlplugindump.exe -nonrelocatable QtDataVisualization 1.2'
Module {
Component {
@@ -13,10 +13,11 @@ Module {
prototype: "QQuickItem"
exports: [
"QtDataVisualization/AbstractGraph3D 1.0",
- "QtDataVisualization/AbstractGraph3D 1.1"
+ "QtDataVisualization/AbstractGraph3D 1.1",
+ "QtDataVisualization/AbstractGraph3D 1.2"
]
isCreatable: false
- exportMetaObjectRevisions: [0, 1]
+ exportMetaObjectRevisions: [0, 1, 2]
Enum {
name: "SelectionFlag"
values: {
@@ -113,6 +114,14 @@ Module {
Property { name: "selectedElement"; revision: 1; type: "ElementType"; isReadonly: true }
Property { name: "aspectRatio"; revision: 1; type: "double" }
Property { name: "optimizationHints"; revision: 1; type: "OptimizationHints" }
+ Property { name: "polar"; revision: 2; type: "bool" }
+ Property { name: "radialLabelOffset"; revision: 2; type: "float" }
+ Property { name: "horizontalAspectRatio"; revision: 2; type: "double" }
+ Property { name: "reflection"; revision: 2; type: "bool" }
+ Property { name: "reflectivity"; revision: 2; type: "double" }
+ Property { name: "locale"; revision: 2; type: "QLocale" }
+ Property { name: "queriedGraphPosition"; revision: 2; type: "QVector3D"; isReadonly: true }
+ Property { name: "margin"; revision: 2; type: "double" }
Signal {
name: "selectionModeChanged"
Parameter { name: "mode"; type: "AbstractDeclarative::SelectionFlags" }
@@ -175,6 +184,46 @@ Module {
revision: 1
Parameter { name: "hints"; type: "AbstractDeclarative::OptimizationHints" }
}
+ Signal {
+ name: "polarChanged"
+ revision: 2
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "radialLabelOffsetChanged"
+ revision: 2
+ Parameter { name: "offset"; type: "float" }
+ }
+ Signal {
+ name: "horizontalAspectRatioChanged"
+ revision: 2
+ Parameter { name: "ratio"; type: "double" }
+ }
+ Signal {
+ name: "reflectionChanged"
+ revision: 2
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "reflectivityChanged"
+ revision: 2
+ Parameter { name: "reflectivity"; type: "double" }
+ }
+ Signal {
+ name: "localeChanged"
+ revision: 2
+ Parameter { name: "locale"; type: "QLocale" }
+ }
+ Signal {
+ name: "queriedGraphPositionChanged"
+ revision: 2
+ Parameter { name: "data"; type: "QVector3D" }
+ }
+ Signal {
+ name: "marginChanged"
+ revision: 2
+ Parameter { name: "margin"; type: "double" }
+ }
Method {
name: "handleAxisXChanged"
Parameter { name: "axis"; type: "QAbstract3DAxis"; isPointer: true }
@@ -191,6 +240,7 @@ Module {
name: "windowDestroyed"
Parameter { name: "obj"; type: "QObject"; isPointer: true }
}
+ Method { name: "destroyContext" }
Method { name: "clearSelection" }
Method {
name: "addCustomItem"
@@ -247,9 +297,12 @@ Module {
Component {
name: "QtDataVisualization::Declarative3DScene"
prototype: "QtDataVisualization::Q3DScene"
- exports: ["QtDataVisualization/Scene3D 1.0"]
+ exports: [
+ "QtDataVisualization/Scene3D 1.0",
+ "QtDataVisualization/Scene3D 1.2"
+ ]
isCreatable: false
- exportMetaObjectRevisions: [0]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "selectionQueryPosition"; type: "QPointF" }
Property { name: "invalidSelectionPoint"; type: "QPoint"; isReadonly: true }
Signal {
@@ -293,18 +346,22 @@ Module {
name: "QtDataVisualization::DeclarativeBars"
defaultProperty: "seriesList"
prototype: "QtDataVisualization::AbstractDeclarative"
- exports: ["QtDataVisualization/Bars3D 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/Bars3D 1.0",
+ "QtDataVisualization/Bars3D 1.2"
+ ]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "rowAxis"; type: "QCategory3DAxis"; isPointer: true }
Property { name: "valueAxis"; type: "QValue3DAxis"; isPointer: true }
Property { name: "columnAxis"; type: "QCategory3DAxis"; isPointer: true }
Property { name: "multiSeriesUniform"; type: "bool" }
- Property { name: "barThickness"; type: "double" }
+ Property { name: "barThickness"; type: "float" }
Property { name: "barSpacing"; type: "QSizeF" }
Property { name: "barSpacingRelative"; type: "bool" }
Property { name: "seriesList"; type: "QBar3DSeries"; isList: true; isReadonly: true }
Property { name: "selectedSeries"; type: "QBar3DSeries"; isReadonly: true; isPointer: true }
Property { name: "primarySeries"; type: "QBar3DSeries"; isPointer: true }
+ Property { name: "floorLevel"; revision: 1; type: "float" }
Signal {
name: "rowAxisChanged"
Parameter { name: "axis"; type: "QCategory3DAxis"; isPointer: true }
@@ -323,7 +380,7 @@ Module {
}
Signal {
name: "barThicknessChanged"
- Parameter { name: "thicknessRatio"; type: "double" }
+ Parameter { name: "thicknessRatio"; type: "float" }
}
Signal {
name: "barSpacingChanged"
@@ -345,6 +402,11 @@ Module {
name: "selectedSeriesChanged"
Parameter { name: "series"; type: "QBar3DSeries"; isPointer: true }
}
+ Signal {
+ name: "floorLevelChanged"
+ revision: 1
+ Parameter { name: "level"; type: "float" }
+ }
Method {
name: "handleAxisXChanged"
Parameter { name: "axis"; type: "QAbstract3DAxis"; isPointer: true }
@@ -461,13 +523,17 @@ Module {
name: "QtDataVisualization::DeclarativeSurface"
defaultProperty: "seriesList"
prototype: "QtDataVisualization::AbstractDeclarative"
- exports: ["QtDataVisualization/Surface3D 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/Surface3D 1.0",
+ "QtDataVisualization/Surface3D 1.2"
+ ]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "axisX"; type: "QValue3DAxis"; isPointer: true }
Property { name: "axisY"; type: "QValue3DAxis"; isPointer: true }
Property { name: "axisZ"; type: "QValue3DAxis"; isPointer: true }
Property { name: "selectedSeries"; type: "QSurface3DSeries"; isReadonly: true; isPointer: true }
Property { name: "seriesList"; type: "QSurface3DSeries"; isList: true; isReadonly: true }
+ Property { name: "flipHorizontalGrid"; revision: 1; type: "bool" }
Signal {
name: "axisXChanged"
Parameter { name: "axis"; type: "QValue3DAxis"; isPointer: true }
@@ -484,6 +550,11 @@ Module {
name: "selectedSeriesChanged"
Parameter { name: "series"; type: "QSurface3DSeries"; isPointer: true }
}
+ Signal {
+ name: "flipHorizontalGridChanged"
+ revision: 1
+ Parameter { name: "flip"; type: "bool" }
+ }
Method {
name: "handleAxisXChanged"
Parameter { name: "axis"; type: "QAbstract3DAxis"; isPointer: true }
@@ -560,8 +631,11 @@ Module {
Component {
name: "QtDataVisualization::Q3DCamera"
prototype: "QtDataVisualization::Q3DObject"
- exports: ["QtDataVisualization/Camera3D 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/Camera3D 1.0",
+ "QtDataVisualization/Camera3D 1.2"
+ ]
+ exportMetaObjectRevisions: [0, 1]
Enum {
name: "CameraPreset"
values: {
@@ -592,23 +666,26 @@ Module {
"CameraPresetDirectlyBelow": 23
}
}
- Property { name: "xRotation"; type: "double" }
- Property { name: "yRotation"; type: "double" }
- Property { name: "zoomLevel"; type: "double" }
+ Property { name: "xRotation"; type: "float" }
+ Property { name: "yRotation"; type: "float" }
+ Property { name: "zoomLevel"; type: "float" }
Property { name: "cameraPreset"; type: "CameraPreset" }
Property { name: "wrapXRotation"; type: "bool" }
Property { name: "wrapYRotation"; type: "bool" }
+ Property { name: "target"; revision: 1; type: "QVector3D" }
+ Property { name: "minZoomLevel"; revision: 1; type: "float" }
+ Property { name: "maxZoomLevel"; revision: 1; type: "float" }
Signal {
name: "xRotationChanged"
- Parameter { name: "rotation"; type: "double" }
+ Parameter { name: "rotation"; type: "float" }
}
Signal {
name: "yRotationChanged"
- Parameter { name: "rotation"; type: "double" }
+ Parameter { name: "rotation"; type: "float" }
}
Signal {
name: "zoomLevelChanged"
- Parameter { name: "zoomLevel"; type: "double" }
+ Parameter { name: "zoomLevel"; type: "float" }
}
Signal {
name: "cameraPresetChanged"
@@ -622,10 +699,47 @@ Module {
name: "wrapYRotationChanged"
Parameter { name: "isEnabled"; type: "bool" }
}
+ Signal {
+ name: "targetChanged"
+ revision: 1
+ Parameter { name: "target"; type: "QVector3D" }
+ }
+ Signal {
+ name: "minZoomLevelChanged"
+ revision: 1
+ Parameter { name: "zoomLevel"; type: "float" }
+ }
+ Signal {
+ name: "maxZoomLevelChanged"
+ revision: 1
+ Parameter { name: "zoomLevel"; type: "float" }
+ }
}
Component {
name: "QtDataVisualization::Q3DInputHandler"
prototype: "QtDataVisualization::QAbstract3DInputHandler"
+ exports: ["QtDataVisualization/InputHandler3D 1.2"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "rotationEnabled"; type: "bool" }
+ Property { name: "zoomEnabled"; type: "bool" }
+ Property { name: "selectionEnabled"; type: "bool" }
+ Property { name: "zoomAtTargetEnabled"; type: "bool" }
+ Signal {
+ name: "rotationEnabledChanged"
+ Parameter { name: "enable"; type: "bool" }
+ }
+ Signal {
+ name: "zoomEnabledChanged"
+ Parameter { name: "enable"; type: "bool" }
+ }
+ Signal {
+ name: "selectionEnabledChanged"
+ Parameter { name: "enable"; type: "bool" }
+ }
+ Signal {
+ name: "zoomAtTargetEnabledChanged"
+ Parameter { name: "enable"; type: "bool" }
+ }
}
Component {
name: "QtDataVisualization::Q3DLight"
@@ -654,7 +768,8 @@ Module {
Property { name: "slicingActive"; type: "bool" }
Property { name: "activeCamera"; type: "Q3DCamera"; isPointer: true }
Property { name: "activeLight"; type: "Q3DLight"; isPointer: true }
- Property { name: "devicePixelRatio"; type: "double" }
+ Property { name: "devicePixelRatio"; type: "float" }
+ Property { name: "graphPositionQuery"; revision: 1; type: "QPoint" }
Signal {
name: "viewportChanged"
Parameter { name: "viewport"; type: "QRect" }
@@ -685,12 +800,17 @@ Module {
}
Signal {
name: "devicePixelRatioChanged"
- Parameter { name: "pixelRatio"; type: "double" }
+ Parameter { name: "pixelRatio"; type: "float" }
}
Signal {
name: "selectionQueryPositionChanged"
Parameter { name: "position"; type: "QPoint" }
}
+ Signal {
+ name: "graphPositionQueryChanged"
+ revision: 1
+ Parameter { name: "position"; type: "QPoint" }
+ }
}
Component {
name: "QtDataVisualization::Q3DTheme"
@@ -733,9 +853,9 @@ Module {
Property { name: "baseGradients"; type: "QList<QLinearGradient>" }
Property { name: "singleHighlightGradient"; type: "QLinearGradient" }
Property { name: "multiHighlightGradient"; type: "QLinearGradient" }
- Property { name: "lightStrength"; type: "double" }
- Property { name: "ambientLightStrength"; type: "double" }
- Property { name: "highlightLightStrength"; type: "double" }
+ Property { name: "lightStrength"; type: "float" }
+ Property { name: "ambientLightStrength"; type: "float" }
+ Property { name: "highlightLightStrength"; type: "float" }
Property { name: "labelBorderEnabled"; type: "bool" }
Property { name: "font"; type: "QFont" }
Property { name: "backgroundEnabled"; type: "bool" }
@@ -796,15 +916,15 @@ Module {
}
Signal {
name: "lightStrengthChanged"
- Parameter { name: "strength"; type: "double" }
+ Parameter { name: "strength"; type: "float" }
}
Signal {
name: "ambientLightStrengthChanged"
- Parameter { name: "strength"; type: "double" }
+ Parameter { name: "strength"; type: "float" }
}
Signal {
name: "highlightLightStrengthChanged"
- Parameter { name: "strength"; type: "double" }
+ Parameter { name: "strength"; type: "float" }
}
Signal {
name: "labelBorderEnabledChanged"
@@ -861,10 +981,10 @@ Module {
Property { name: "labels"; type: "QStringList" }
Property { name: "orientation"; type: "AxisOrientation"; isReadonly: true }
Property { name: "type"; type: "AxisType"; isReadonly: true }
- Property { name: "min"; type: "double" }
- Property { name: "max"; type: "double" }
+ Property { name: "min"; type: "float" }
+ Property { name: "max"; type: "float" }
Property { name: "autoAdjustRange"; type: "bool" }
- Property { name: "labelAutoRotation"; revision: 1; type: "double" }
+ Property { name: "labelAutoRotation"; revision: 1; type: "float" }
Property { name: "titleVisible"; revision: 1; type: "bool" }
Property { name: "titleFixed"; revision: 1; type: "bool" }
Signal {
@@ -877,16 +997,16 @@ Module {
}
Signal {
name: "minChanged"
- Parameter { name: "value"; type: "double" }
+ Parameter { name: "value"; type: "float" }
}
Signal {
name: "maxChanged"
- Parameter { name: "value"; type: "double" }
+ Parameter { name: "value"; type: "float" }
}
Signal {
name: "rangeChanged"
- Parameter { name: "min"; type: "double" }
- Parameter { name: "max"; type: "double" }
+ Parameter { name: "min"; type: "float" }
+ Parameter { name: "max"; type: "float" }
}
Signal {
name: "autoAdjustRangeChanged"
@@ -895,7 +1015,7 @@ Module {
Signal {
name: "labelAutoRotationChanged"
revision: 1
- Parameter { name: "angle"; type: "double" }
+ Parameter { name: "angle"; type: "float" }
}
Signal {
name: "titleVisibilityChanged"
@@ -1059,7 +1179,7 @@ Module {
Method {
name: "setMeshAxisAndAngle"
Parameter { name: "axis"; type: "QVector3D" }
- Parameter { name: "angle"; type: "double" }
+ Parameter { name: "angle"; type: "float" }
}
}
Component {
@@ -1087,7 +1207,7 @@ Module {
exportMetaObjectRevisions: [0]
Property { name: "dataProxy"; type: "QBarDataProxy"; isPointer: true }
Property { name: "selectedBar"; type: "QPoint" }
- Property { name: "meshAngle"; type: "double" }
+ Property { name: "meshAngle"; type: "float" }
Signal {
name: "dataProxyChanged"
Parameter { name: "proxy"; type: "QBarDataProxy"; isPointer: true }
@@ -1098,7 +1218,7 @@ Module {
}
Signal {
name: "meshAngleChanged"
- Parameter { name: "angle"; type: "double" }
+ Parameter { name: "angle"; type: "float" }
}
}
Component {
@@ -1156,8 +1276,11 @@ Module {
Component {
name: "QtDataVisualization::QCustom3DItem"
prototype: "QObject"
- exports: ["QtDataVisualization/Custom3DItem 1.1"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/Custom3DItem 1.1",
+ "QtDataVisualization/Custom3DItem 1.2"
+ ]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "meshFile"; type: "string" }
Property { name: "textureFile"; type: "string" }
Property { name: "position"; type: "QVector3D" }
@@ -1166,6 +1289,7 @@ Module {
Property { name: "rotation"; type: "QQuaternion" }
Property { name: "visible"; type: "bool" }
Property { name: "shadowCasting"; type: "bool" }
+ Property { name: "scalingAbsolute"; revision: 1; type: "bool" }
Signal {
name: "meshFileChanged"
Parameter { name: "meshFile"; type: "string" }
@@ -1198,10 +1322,15 @@ Module {
name: "shadowCastingChanged"
Parameter { name: "shadowCasting"; type: "bool" }
}
+ Signal {
+ name: "scalingAbsoluteChanged"
+ revision: 1
+ Parameter { name: "scalingAbsolute"; type: "bool" }
+ }
Method {
name: "setRotationAxisAndAngle"
Parameter { name: "axis"; type: "QVector3D" }
- Parameter { name: "angle"; type: "double" }
+ Parameter { name: "angle"; type: "float" }
}
}
Component {
@@ -1246,16 +1375,107 @@ Module {
}
}
Component {
+ name: "QtDataVisualization::QCustom3DVolume"
+ prototype: "QtDataVisualization::QCustom3DItem"
+ exports: ["QtDataVisualization/Custom3DVolume 1.2"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "textureWidth"; type: "int" }
+ Property { name: "textureHeight"; type: "int" }
+ Property { name: "textureDepth"; type: "int" }
+ Property { name: "sliceIndexX"; type: "int" }
+ Property { name: "sliceIndexY"; type: "int" }
+ Property { name: "sliceIndexZ"; type: "int" }
+ Property { name: "colorTable"; type: "QVector<QRgb>" }
+ Property { name: "textureData"; type: "QVector<uchar>"; isPointer: true }
+ Property { name: "alphaMultiplier"; type: "float" }
+ Property { name: "preserveOpacity"; type: "bool" }
+ Property { name: "useHighDefShader"; type: "bool" }
+ Property { name: "drawSlices"; type: "bool" }
+ Property { name: "drawSliceFrames"; type: "bool" }
+ Property { name: "sliceFrameColor"; type: "QColor" }
+ Property { name: "sliceFrameWidths"; type: "QVector3D" }
+ Property { name: "sliceFrameGaps"; type: "QVector3D" }
+ Property { name: "sliceFrameThicknesses"; type: "QVector3D" }
+ Signal {
+ name: "textureWidthChanged"
+ Parameter { name: "value"; type: "int" }
+ }
+ Signal {
+ name: "textureHeightChanged"
+ Parameter { name: "value"; type: "int" }
+ }
+ Signal {
+ name: "textureDepthChanged"
+ Parameter { name: "value"; type: "int" }
+ }
+ Signal {
+ name: "sliceIndexXChanged"
+ Parameter { name: "value"; type: "int" }
+ }
+ Signal {
+ name: "sliceIndexYChanged"
+ Parameter { name: "value"; type: "int" }
+ }
+ Signal {
+ name: "sliceIndexZChanged"
+ Parameter { name: "value"; type: "int" }
+ }
+ Signal {
+ name: "textureDataChanged"
+ Parameter { name: "data"; type: "QVector<uchar>"; isPointer: true }
+ }
+ Signal {
+ name: "textureFormatChanged"
+ Parameter { name: "format"; type: "QImage::Format" }
+ }
+ Signal {
+ name: "alphaMultiplierChanged"
+ Parameter { name: "mult"; type: "float" }
+ }
+ Signal {
+ name: "preserveOpacityChanged"
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "useHighDefShaderChanged"
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "drawSlicesChanged"
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "drawSliceFramesChanged"
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "sliceFrameColorChanged"
+ Parameter { name: "color"; type: "QColor" }
+ }
+ Signal {
+ name: "sliceFrameWidthsChanged"
+ Parameter { name: "values"; type: "QVector3D" }
+ }
+ Signal {
+ name: "sliceFrameGapsChanged"
+ Parameter { name: "values"; type: "QVector3D" }
+ }
+ Signal {
+ name: "sliceFrameThicknessesChanged"
+ Parameter { name: "values"; type: "QVector3D" }
+ }
+ }
+ Component {
name: "QtDataVisualization::QHeightMapSurfaceDataProxy"
prototype: "QtDataVisualization::QSurfaceDataProxy"
exports: ["QtDataVisualization/HeightMapSurfaceDataProxy 1.0"]
exportMetaObjectRevisions: [0]
Property { name: "heightMap"; type: "QImage" }
Property { name: "heightMapFile"; type: "string" }
- Property { name: "minXValue"; type: "double" }
- Property { name: "maxXValue"; type: "double" }
- Property { name: "minZValue"; type: "double" }
- Property { name: "maxZValue"; type: "double" }
+ Property { name: "minXValue"; type: "float" }
+ Property { name: "maxXValue"; type: "float" }
+ Property { name: "minZValue"; type: "float" }
+ Property { name: "maxZValue"; type: "float" }
Signal {
name: "heightMapChanged"
Parameter { name: "image"; type: "QImage" }
@@ -1266,19 +1486,19 @@ Module {
}
Signal {
name: "minXValueChanged"
- Parameter { name: "value"; type: "double" }
+ Parameter { name: "value"; type: "float" }
}
Signal {
name: "maxXValueChanged"
- Parameter { name: "value"; type: "double" }
+ Parameter { name: "value"; type: "float" }
}
Signal {
name: "minZValueChanged"
- Parameter { name: "value"; type: "double" }
+ Parameter { name: "value"; type: "float" }
}
Signal {
name: "maxZValueChanged"
- Parameter { name: "value"; type: "double" }
+ Parameter { name: "value"; type: "float" }
}
}
Component {
@@ -1657,7 +1877,7 @@ Module {
exportMetaObjectRevisions: [0]
Property { name: "dataProxy"; type: "QScatterDataProxy"; isPointer: true }
Property { name: "selectedItem"; type: "int" }
- Property { name: "itemSize"; type: "double" }
+ Property { name: "itemSize"; type: "float" }
Signal {
name: "dataProxyChanged"
Parameter { name: "proxy"; type: "QScatterDataProxy"; isPointer: true }
@@ -1668,7 +1888,7 @@ Module {
}
Signal {
name: "itemSizeChanged"
- Parameter { name: "size"; type: "double" }
+ Parameter { name: "size"; type: "float" }
}
}
Component {
@@ -1736,6 +1956,8 @@ Module {
Property { name: "flatShadingEnabled"; type: "bool" }
Property { name: "flatShadingSupported"; type: "bool"; isReadonly: true }
Property { name: "drawMode"; type: "DrawFlags" }
+ Property { name: "texture"; type: "QImage" }
+ Property { name: "textureFile"; type: "string" }
Signal {
name: "dataProxyChanged"
Parameter { name: "proxy"; type: "QSurfaceDataProxy"; isPointer: true }
@@ -1756,6 +1978,14 @@ Module {
name: "drawModeChanged"
Parameter { name: "mode"; type: "QSurface3DSeries::DrawFlags" }
}
+ Signal {
+ name: "textureChanged"
+ Parameter { name: "image"; type: "QImage" }
+ }
+ Signal {
+ name: "textureFileChanged"
+ Parameter { name: "filename"; type: "string" }
+ }
}
Component {
name: "QtDataVisualization::QSurfaceDataProxy"
@@ -1808,6 +2038,8 @@ Module {
Component {
name: "QtDataVisualization::QTouch3DInputHandler"
prototype: "QtDataVisualization::Q3DInputHandler"
+ exports: ["QtDataVisualization/TouchInputHandler3D 1.2"]
+ exportMetaObjectRevisions: [0]
}
Component {
name: "QtDataVisualization::QValue3DAxis"
diff --git a/src/src.pro b/src/src.pro
index 33e3c009..3547745d 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -1,4 +1,5 @@
TEMPLATE = subdirs
CONFIG += ordered
-SUBDIRS += datavisualization \
- datavisualizationqml2
+SUBDIRS += datavisualization
+
+qtHaveModule(quick): SUBDIRS += datavisualizationqml2
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
new file mode 100644
index 00000000..df2943a4
--- /dev/null
+++ b/tests/auto/auto.pro
@@ -0,0 +1,7 @@
+TEMPLATE = subdirs
+
+SUBDIRS += cpptest
+
+qtHaveModule(quick): SUBDIRS += qmltest
+
+installed_cmake.depends = cmake
diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro
new file mode 100644
index 00000000..abd8f38e
--- /dev/null
+++ b/tests/auto/cpptest/cpptest.pro
@@ -0,0 +1,26 @@
+TEMPLATE = subdirs
+SUBDIRS = q3dbars \
+ q3dbars-proxy \
+ q3dbars-modelproxy \
+ q3dbars-series \
+ q3dscatter \
+ q3dscatter-proxy \
+ q3dscatter-modelproxy \
+ q3dscatter-series \
+ q3dsurface \
+ q3dsurface-proxy \
+ q3dsurface-modelproxy \
+ q3dsurface-heightproxy \
+ q3dsurface-series \
+ q3daxis-category \
+ q3daxis-logvalue \
+ q3daxis-value \
+ q3dscene \
+ q3dscene-camera \
+ q3dscene-light \
+ q3dtheme \
+ q3dinput \
+ q3dinput-touch \
+ q3dcustom \
+ q3dcustom-label \
+ q3dcustom-volume
diff --git a/tests/auto/cpptest/q3daxis-category/q3daxis-category.pro b/tests/auto/cpptest/q3daxis-category/q3daxis-category.pro
new file mode 100644
index 00000000..74415397
--- /dev/null
+++ b/tests/auto/cpptest/q3daxis-category/q3daxis-category.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_axis.cpp
diff --git a/tests/auto/cpptest/q3daxis-category/tst_axis.cpp b/tests/auto/cpptest/q3daxis-category/tst_axis.cpp
new file mode 100644
index 00000000..800a0953
--- /dev/null
+++ b/tests/auto/cpptest/q3daxis-category/tst_axis.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QCategory3DAxis>
+
+using namespace QtDataVisualization;
+
+class tst_axis: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QCategory3DAxis *m_axis;
+};
+
+void tst_axis::initTestCase()
+{
+}
+
+void tst_axis::cleanupTestCase()
+{
+}
+
+void tst_axis::init()
+{
+ m_axis = new QCategory3DAxis();
+}
+
+void tst_axis::cleanup()
+{
+ delete m_axis;
+}
+
+void tst_axis::construct()
+{
+ QCategory3DAxis *axis = new QCategory3DAxis();
+ QVERIFY(axis);
+ delete axis;
+}
+
+void tst_axis::initialProperties()
+{
+ QVERIFY(m_axis);
+
+ QCOMPARE(m_axis->labels().length(), 0);
+
+ // Common (from QAbstract3DAxis)
+ QCOMPARE(m_axis->isAutoAdjustRange(), true);
+ QCOMPARE(m_axis->labelAutoRotation(), 0.0f);
+ QCOMPARE(m_axis->max(), 10.0f);
+ QCOMPARE(m_axis->min(), 0.0f);
+ QCOMPARE(m_axis->orientation(), QAbstract3DAxis::AxisOrientationNone);
+ QCOMPARE(m_axis->title(), QString(""));
+ QCOMPARE(m_axis->isTitleFixed(), true);
+ QCOMPARE(m_axis->isTitleVisible(), false);
+ QCOMPARE(m_axis->type(), QAbstract3DAxis::AxisTypeCategory);
+}
+
+void tst_axis::initializeProperties()
+{
+ QVERIFY(m_axis);
+
+ m_axis->setLabels(QStringList() << "first" << "second");
+
+ QCOMPARE(m_axis->labels().length(), 2);
+ QCOMPARE(m_axis->labels().at(1), QString("second"));
+
+ // Common (from QAbstract3DAxis)
+ m_axis->setAutoAdjustRange(false);
+ m_axis->setLabelAutoRotation(15.0f);
+ m_axis->setMax(25.0f);
+ m_axis->setMin(5.0f);
+ m_axis->setTitle("title");
+ m_axis->setTitleFixed(false);
+ m_axis->setTitleVisible(true);
+
+ QCOMPARE(m_axis->isAutoAdjustRange(), false);
+ QCOMPARE(m_axis->labelAutoRotation(), 15.0f);
+ QCOMPARE(m_axis->max(), 25.0f);
+ QCOMPARE(m_axis->min(), 5.0f);
+ QCOMPARE(m_axis->title(), QString("title"));
+ QCOMPARE(m_axis->isTitleFixed(), false);
+ QCOMPARE(m_axis->isTitleVisible(), true);
+}
+
+void tst_axis::invalidProperties()
+{
+ m_axis->setLabelAutoRotation(-15.0f);
+ QCOMPARE(m_axis->labelAutoRotation(), 0.0f);
+
+ m_axis->setLabelAutoRotation(100.0f);
+ QCOMPARE(m_axis->labelAutoRotation(), 90.0f);
+
+ m_axis->setMax(-10.0f);
+ QCOMPARE(m_axis->max(), 0.0f);
+ QCOMPARE(m_axis->min(), 0.0f);
+
+ m_axis->setMin(10.0f);
+ QCOMPARE(m_axis->max(), 11.0f);
+ QCOMPARE(m_axis->min(), 10.0f);
+}
+
+QTEST_MAIN(tst_axis)
+#include "tst_axis.moc"
diff --git a/tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro b/tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro
new file mode 100644
index 00000000..74415397
--- /dev/null
+++ b/tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_axis.cpp
diff --git a/tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp b/tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp
new file mode 100644
index 00000000..adbedb24
--- /dev/null
+++ b/tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QLogValue3DAxisFormatter>
+
+using namespace QtDataVisualization;
+
+class tst_axis: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QLogValue3DAxisFormatter *m_formatter;
+};
+
+void tst_axis::initTestCase()
+{
+}
+
+void tst_axis::cleanupTestCase()
+{
+}
+
+void tst_axis::init()
+{
+ m_formatter = new QLogValue3DAxisFormatter();
+}
+
+void tst_axis::cleanup()
+{
+ delete m_formatter;
+}
+
+void tst_axis::construct()
+{
+ QLogValue3DAxisFormatter *formatter = new QLogValue3DAxisFormatter();
+ QVERIFY(formatter);
+ delete formatter;
+}
+
+void tst_axis::initialProperties()
+{
+ QVERIFY(m_formatter);
+
+ QCOMPARE(m_formatter->autoSubGrid(), true);
+ QCOMPARE(m_formatter->base(), 10.0);
+ QCOMPARE(m_formatter->showEdgeLabels(), true);
+}
+
+void tst_axis::initializeProperties()
+{
+ QVERIFY(m_formatter);
+
+ m_formatter->setAutoSubGrid(false);
+ m_formatter->setBase(0.1);
+ m_formatter->setShowEdgeLabels(false);
+
+ QCOMPARE(m_formatter->autoSubGrid(), false);
+ QCOMPARE(m_formatter->base(), 0.1);
+ QCOMPARE(m_formatter->showEdgeLabels(), false);
+}
+
+void tst_axis::invalidProperties()
+{
+ m_formatter->setBase(-1.0);
+ QCOMPARE(m_formatter->base(), 10.0);
+
+ m_formatter->setBase(1.0);
+ QCOMPARE(m_formatter->base(), 10.0);
+}
+
+QTEST_MAIN(tst_axis)
+#include "tst_axis.moc"
diff --git a/tests/auto/cpptest/q3daxis-value/q3daxis-value.pro b/tests/auto/cpptest/q3daxis-value/q3daxis-value.pro
new file mode 100644
index 00000000..74415397
--- /dev/null
+++ b/tests/auto/cpptest/q3daxis-value/q3daxis-value.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_axis.cpp
diff --git a/tests/auto/cpptest/q3daxis-value/tst_axis.cpp b/tests/auto/cpptest/q3daxis-value/tst_axis.cpp
new file mode 100644
index 00000000..92029f13
--- /dev/null
+++ b/tests/auto/cpptest/q3daxis-value/tst_axis.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QValue3DAxis>
+
+using namespace QtDataVisualization;
+
+class tst_axis: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QValue3DAxis *m_axis;
+};
+
+void tst_axis::initTestCase()
+{
+}
+
+void tst_axis::cleanupTestCase()
+{
+}
+
+void tst_axis::init()
+{
+ m_axis = new QValue3DAxis();
+}
+
+void tst_axis::cleanup()
+{
+ delete m_axis;
+}
+
+void tst_axis::construct()
+{
+ QValue3DAxis *axis = new QValue3DAxis();
+ QVERIFY(axis);
+ delete axis;
+}
+
+void tst_axis::initialProperties()
+{
+ QVERIFY(m_axis);
+
+ QCOMPARE(m_axis->labelFormat(), QString("%.2f"));
+ QCOMPARE(m_axis->reversed(), false);
+ QCOMPARE(m_axis->segmentCount(), 5);
+ QCOMPARE(m_axis->subSegmentCount(), 1);
+
+ // Common (from QAbstract3DAxis)
+ QCOMPARE(m_axis->isAutoAdjustRange(), true);
+ QCOMPARE(m_axis->labelAutoRotation(), 0.0f);
+ QCOMPARE(m_axis->labels().length(), 6);
+ QCOMPARE(m_axis->labels().at(0), QString("0.00"));
+ QCOMPARE(m_axis->labels().at(1), QString("2.00"));
+ QCOMPARE(m_axis->labels().at(2), QString("4.00"));
+ QCOMPARE(m_axis->labels().at(3), QString("6.00"));
+ QCOMPARE(m_axis->labels().at(4), QString("8.00"));
+ QCOMPARE(m_axis->labels().at(5), QString("10.00"));
+ QCOMPARE(m_axis->max(), 10.0f);
+ QCOMPARE(m_axis->min(), 0.0f);
+ QCOMPARE(m_axis->orientation(), QAbstract3DAxis::AxisOrientationNone);
+ QCOMPARE(m_axis->title(), QString(""));
+ QCOMPARE(m_axis->isTitleFixed(), true);
+ QCOMPARE(m_axis->isTitleVisible(), false);
+ QCOMPARE(m_axis->type(), QAbstract3DAxis::AxisTypeValue);
+}
+
+void tst_axis::initializeProperties()
+{
+ QVERIFY(m_axis);
+
+ m_axis->setLabelFormat("%.0fm");
+ m_axis->setReversed(true);
+ m_axis->setSegmentCount(2);
+ m_axis->setSubSegmentCount(5);
+
+ QCOMPARE(m_axis->labelFormat(), QString("%.0fm"));
+ QCOMPARE(m_axis->reversed(), true);
+ QCOMPARE(m_axis->segmentCount(), 2);
+ QCOMPARE(m_axis->subSegmentCount(), 5);
+
+ // Common (from QAbstract3DAxis)
+ m_axis->setAutoAdjustRange(false);
+ m_axis->setLabelAutoRotation(15.0f);
+ m_axis->setMax(25.0f);
+ m_axis->setMin(5.0f);
+ m_axis->setTitle("title");
+ m_axis->setTitleFixed(false);
+ m_axis->setTitleVisible(true);
+
+ QCOMPARE(m_axis->isAutoAdjustRange(), false);
+ QCOMPARE(m_axis->labelAutoRotation(), 15.0f);
+ QCOMPARE(m_axis->labels().length(), 3);
+ QCOMPARE(m_axis->labels().at(0), QString("5m"));
+ QCOMPARE(m_axis->labels().at(1), QString("15m"));
+ QCOMPARE(m_axis->labels().at(2), QString("25m"));
+ QCOMPARE(m_axis->max(), 25.0f);
+ QCOMPARE(m_axis->min(), 5.0f);
+ QCOMPARE(m_axis->title(), QString("title"));
+ QCOMPARE(m_axis->isTitleFixed(), false);
+ QCOMPARE(m_axis->isTitleVisible(), true);
+}
+
+void tst_axis::invalidProperties()
+{
+ m_axis->setSegmentCount(-1);
+ QCOMPARE(m_axis->segmentCount(), 1);
+
+ m_axis->setSubSegmentCount(-1);
+ QCOMPARE(m_axis->subSegmentCount(), 1);
+
+ m_axis->setLabelAutoRotation(-15.0f);
+ QCOMPARE(m_axis->labelAutoRotation(), 0.0f);
+
+ m_axis->setLabelAutoRotation(100.0f);
+ QCOMPARE(m_axis->labelAutoRotation(), 90.0f);
+
+ m_axis->setMax(-10.0f);
+ QCOMPARE(m_axis->max(), -10.0f);
+ QCOMPARE(m_axis->min(), -11.0f);
+
+ m_axis->setMin(10.0f);
+ QCOMPARE(m_axis->max(), 11.0f);
+ QCOMPARE(m_axis->min(), 10.0f);
+}
+
+QTEST_MAIN(tst_axis)
+#include "tst_axis.moc"
diff --git a/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro b/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro
new file mode 100644
index 00000000..c383ec25
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization widgets
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_proxy.cpp
diff --git a/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp
new file mode 100644
index 00000000..c65e151b
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QItemModelBarDataProxy>
+#include <QtDataVisualization/Q3DBars>
+#include <QtWidgets/QTableWidget>
+
+using namespace QtDataVisualization;
+
+class tst_proxy: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+ void multiMatch();
+
+private:
+ QItemModelBarDataProxy *m_proxy;
+};
+
+void tst_proxy::initTestCase()
+{
+}
+
+void tst_proxy::cleanupTestCase()
+{
+}
+
+void tst_proxy::init()
+{
+ m_proxy = new QItemModelBarDataProxy();
+}
+
+void tst_proxy::cleanup()
+{
+ delete m_proxy;
+}
+
+void tst_proxy::construct()
+{
+ QItemModelBarDataProxy *proxy = new QItemModelBarDataProxy();
+ QVERIFY(proxy);
+ delete proxy;
+
+ QTableWidget *table = new QTableWidget();
+
+ proxy = new QItemModelBarDataProxy(table->model());
+ QVERIFY(proxy);
+ delete proxy;
+
+ proxy = new QItemModelBarDataProxy(table->model(), "val");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString(""));
+ QCOMPARE(proxy->columnRole(), QString(""));
+ QCOMPARE(proxy->valueRole(), QString("val"));
+ QCOMPARE(proxy->rotationRole(), QString(""));
+ QCOMPARE(proxy->rowCategories().length(), 0);
+ QCOMPARE(proxy->columnCategories().length(), 0);
+ delete proxy;
+
+ proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("col"));
+ QCOMPARE(proxy->valueRole(), QString("val"));
+ QCOMPARE(proxy->rotationRole(), QString(""));
+ QCOMPARE(proxy->rowCategories().length(), 0);
+ QCOMPARE(proxy->columnCategories().length(), 0);
+ delete proxy;
+
+ proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val", "rot");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("col"));
+ QCOMPARE(proxy->valueRole(), QString("val"));
+ QCOMPARE(proxy->rotationRole(), QString("rot"));
+ QCOMPARE(proxy->rowCategories().length(), 0);
+ QCOMPARE(proxy->columnCategories().length(), 0);
+ delete proxy;
+
+ proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val",
+ QStringList() << "rowCat", QStringList() << "colCat");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("col"));
+ QCOMPARE(proxy->valueRole(), QString("val"));
+ QCOMPARE(proxy->rotationRole(), QString(""));
+ QCOMPARE(proxy->rowCategories().length(), 1);
+ QCOMPARE(proxy->columnCategories().length(), 1);
+ delete proxy;
+
+ proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val", "rot",
+ QStringList() << "rowCat", QStringList() << "colCat");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("col"));
+ QCOMPARE(proxy->valueRole(), QString("val"));
+ QCOMPARE(proxy->rotationRole(), QString("rot"));
+ QCOMPARE(proxy->rowCategories().length(), 1);
+ QCOMPARE(proxy->columnCategories().length(), 1);
+ delete proxy;
+}
+
+void tst_proxy::initialProperties()
+{
+ QVERIFY(m_proxy);
+
+ QCOMPARE(m_proxy->autoColumnCategories(), true);
+ QCOMPARE(m_proxy->autoRowCategories(), true);
+ QCOMPARE(m_proxy->columnCategories(), QStringList());
+ QCOMPARE(m_proxy->columnRole(), QString());
+ QCOMPARE(m_proxy->columnRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->columnRoleReplace(), QString());
+ QVERIFY(!m_proxy->itemModel());
+ QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelBarDataProxy::MMBLast);
+ QCOMPARE(m_proxy->rotationRole(), QString());
+ QCOMPARE(m_proxy->rotationRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->rotationRoleReplace(), QString());
+ QCOMPARE(m_proxy->rowCategories(), QStringList());
+ QCOMPARE(m_proxy->rowRole(), QString());
+ QCOMPARE(m_proxy->rowRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->rowRoleReplace(), QString());
+ QCOMPARE(m_proxy->useModelCategories(), false);
+ QCOMPARE(m_proxy->valueRole(), QString());
+ QCOMPARE(m_proxy->valueRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->valueRoleReplace(), QString());
+
+ QCOMPARE(m_proxy->columnLabels().count(), 0);
+ QCOMPARE(m_proxy->rowCount(), 0);
+ QCOMPARE(m_proxy->rowLabels().count(), 0);
+ QVERIFY(!m_proxy->series());
+
+ QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeBar);
+}
+
+void tst_proxy::initializeProperties()
+{
+ QVERIFY(m_proxy);
+
+ QTableWidget *table = new QTableWidget();
+
+ m_proxy->setAutoColumnCategories(false);
+ m_proxy->setAutoRowCategories(false);
+ m_proxy->setColumnCategories(QStringList() << "col1" << "col2");
+ m_proxy->setColumnRole("column");
+ m_proxy->setColumnRolePattern(QRegExp("/^.*-(\\d\\d)$/"));
+ m_proxy->setColumnRoleReplace("\\\\1");
+ m_proxy->setItemModel(table->model());
+ m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBAverage);
+ m_proxy->setRotationRole("rotation");
+ m_proxy->setRotationRolePattern(QRegExp("/-/"));
+ m_proxy->setRotationRoleReplace("\\\\1");
+ m_proxy->setRowCategories(QStringList() << "row1" << "row2");
+ m_proxy->setRowRole("row");
+ m_proxy->setRowRolePattern(QRegExp("/^(\\d\\d\\d\\d).*$/"));
+ m_proxy->setRowRoleReplace("\\\\1");
+ m_proxy->setUseModelCategories(true);
+ m_proxy->setValueRole("value");
+ m_proxy->setValueRolePattern(QRegExp("/-/"));
+ m_proxy->setValueRoleReplace("\\\\1");
+
+ QCOMPARE(m_proxy->autoColumnCategories(), false);
+ QCOMPARE(m_proxy->autoRowCategories(), false);
+ QCOMPARE(m_proxy->columnCategories().count(), 2);
+ QCOMPARE(m_proxy->columnRole(), QString("column"));
+ QCOMPARE(m_proxy->columnRolePattern(), QRegExp("/^.*-(\\d\\d)$/"));
+ QCOMPARE(m_proxy->columnRoleReplace(), QString("\\\\1"));
+ QVERIFY(m_proxy->itemModel());
+ QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelBarDataProxy::MMBAverage);
+ QCOMPARE(m_proxy->rotationRole(), QString("rotation"));
+ QCOMPARE(m_proxy->rotationRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->rotationRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->rowCategories().count(), 2);
+ QCOMPARE(m_proxy->rowRole(), QString("row"));
+ QCOMPARE(m_proxy->rowRolePattern(), QRegExp("/^(\\d\\d\\d\\d).*$/"));
+ QCOMPARE(m_proxy->rowRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->useModelCategories(), true);
+ QCOMPARE(m_proxy->valueRole(), QString("value"));
+ QCOMPARE(m_proxy->valueRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->valueRoleReplace(), QString("\\\\1"));
+}
+
+void tst_proxy::multiMatch()
+{
+ Q3DBars *graph = new Q3DBars();
+
+ QTableWidget *table = new QTableWidget();
+ QStringList rows;
+ rows << "row 1" << "row 2" << "row 3";
+ QStringList columns;
+ columns << "col 1";
+ const char *values[1][3] = {{"0/0/3.5/30", "0/0/5.0/30", "0/0/6.5/30"}};
+
+ table->setRowCount(1);
+ table->setColumnCount(3);
+
+ for (int col = 0; col < columns.size(); col++) {
+ for (int row = 0; row < rows.size(); row++) {
+ QModelIndex index = table->model()->index(col, row);
+ table->model()->setData(index, values[col][row]);
+ }
+ }
+
+ m_proxy->setItemModel(table->model());
+ m_proxy->setRowRole(table->model()->roleNames().value(Qt::DisplayRole));
+ m_proxy->setColumnRole(table->model()->roleNames().value(Qt::DisplayRole));
+ m_proxy->setRowRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setRowRoleReplace(QStringLiteral("\\2"));
+ m_proxy->setValueRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setValueRoleReplace(QStringLiteral("\\3"));
+ m_proxy->setColumnRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setColumnRoleReplace(QStringLiteral("\\1"));
+
+ QBar3DSeries *series = new QBar3DSeries(m_proxy);
+
+ graph->addSeries(series);
+
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->valueAxis()->max(), 6.5f);
+ m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBFirst);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->valueAxis()->max(), 3.5f);
+ m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBLast);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->valueAxis()->max(), 6.5f);
+ m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBAverage);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->valueAxis()->max(), 5.0f);
+ m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBCumulative);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->valueAxis()->max(), 15.0f);
+
+ QCOMPARE(m_proxy->columnLabels().count(), 1);
+ QCOMPARE(m_proxy->rowCount(), 1);
+ QCOMPARE(m_proxy->rowLabels().count(), 1);
+ QVERIFY(m_proxy->series());
+}
+
+QTEST_MAIN(tst_proxy)
+#include "tst_proxy.moc"
diff --git a/tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro b/tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro
new file mode 100644
index 00000000..b0b5d361
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_proxy.cpp
diff --git a/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp
new file mode 100644
index 00000000..8d64ed54
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QBarDataProxy>
+
+using namespace QtDataVisualization;
+
+class tst_proxy: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ QBarDataProxy *m_proxy;
+};
+
+void tst_proxy::initTestCase()
+{
+}
+
+void tst_proxy::cleanupTestCase()
+{
+}
+
+void tst_proxy::init()
+{
+ m_proxy = new QBarDataProxy();
+}
+
+void tst_proxy::cleanup()
+{
+ delete m_proxy;
+}
+
+void tst_proxy::construct()
+{
+ QBarDataProxy *proxy = new QBarDataProxy();
+ QVERIFY(proxy);
+ delete proxy;
+}
+
+void tst_proxy::initialProperties()
+{
+ QVERIFY(m_proxy);
+
+ QCOMPARE(m_proxy->columnLabels().count(), 0);
+ QCOMPARE(m_proxy->rowCount(), 0);
+ QCOMPARE(m_proxy->rowLabels().count(), 0);
+ QVERIFY(!m_proxy->series());
+
+ QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeBar);
+}
+
+void tst_proxy::initializeProperties()
+{
+ QVERIFY(m_proxy);
+
+ m_proxy->setColumnLabels(QStringList() << "1" << "2" << "3");
+ QBarDataRow *data = new QBarDataRow;
+ *data << 1.0f << 3.0f << 7.5f;
+ m_proxy->addRow(data);
+ m_proxy->setRowLabels(QStringList() << "1");
+
+ QCOMPARE(m_proxy->columnLabels().count(), 3);
+ QCOMPARE(m_proxy->rowCount(), 1);
+ QCOMPARE(m_proxy->rowLabels().count(), 1);
+}
+
+QTEST_MAIN(tst_proxy)
+#include "tst_proxy.moc"
diff --git a/tests/auto/cpptest/q3dbars-series/q3dbars-series.pro b/tests/auto/cpptest/q3dbars-series/q3dbars-series.pro
new file mode 100644
index 00000000..481653ef
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars-series/q3dbars-series.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_series.cpp
diff --git a/tests/auto/cpptest/q3dbars-series/tst_series.cpp b/tests/auto/cpptest/q3dbars-series/tst_series.cpp
new file mode 100644
index 00000000..e2a40ae7
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars-series/tst_series.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QBar3DSeries>
+
+using namespace QtDataVisualization;
+
+class tst_series: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QBar3DSeries *m_series;
+};
+
+void tst_series::initTestCase()
+{
+}
+
+void tst_series::cleanupTestCase()
+{
+}
+
+void tst_series::init()
+{
+ m_series = new QBar3DSeries();
+}
+
+void tst_series::cleanup()
+{
+ delete m_series;
+}
+
+void tst_series::construct()
+{
+ QBar3DSeries *series = new QBar3DSeries();
+ QVERIFY(series);
+ delete series;
+
+ QBarDataProxy *proxy = new QBarDataProxy();
+
+ series = new QBar3DSeries(proxy);
+ QVERIFY(series);
+ QCOMPARE(series->dataProxy(), proxy);
+ delete series;
+}
+
+void tst_series::initialProperties()
+{
+ QVERIFY(m_series);
+
+ QVERIFY(m_series->dataProxy());
+ QCOMPARE(m_series->meshAngle(), 0.0f);
+ QCOMPARE(m_series->selectedBar(), m_series->invalidSelectionPosition());
+
+ // Common properties
+ QCOMPARE(m_series->baseColor(), QColor(Qt::black));
+ QCOMPARE(m_series->baseGradient(), QLinearGradient());
+ QCOMPARE(m_series->colorStyle(), Q3DTheme::ColorStyleUniform);
+ QCOMPARE(m_series->itemLabel(), QString(""));
+ QCOMPARE(m_series->itemLabelFormat(), QString("@valueLabel"));
+ QCOMPARE(m_series->isItemLabelVisible(), true);
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshBevelBar);
+ QCOMPARE(m_series->meshRotation(), QQuaternion(1, 0, 0, 0));
+ QCOMPARE(m_series->isMeshSmooth(), false);
+ QCOMPARE(m_series->multiHighlightColor(), QColor(Qt::black));
+ QCOMPARE(m_series->multiHighlightGradient(), QLinearGradient());
+ QCOMPARE(m_series->name(), QString(""));
+ QCOMPARE(m_series->singleHighlightColor(), QColor(Qt::black));
+ QCOMPARE(m_series->singleHighlightGradient(), QLinearGradient());
+ QCOMPARE(m_series->type(), QAbstract3DSeries::SeriesTypeBar);
+ QCOMPARE(m_series->userDefinedMesh(), QString(""));
+ QCOMPARE(m_series->isVisible(), true);
+}
+
+void tst_series::initializeProperties()
+{
+ QVERIFY(m_series);
+
+ m_series->setDataProxy(new QBarDataProxy());
+ m_series->setMeshAngle(15.0f);
+ m_series->setSelectedBar(QPoint(0, 0));
+
+ QCOMPARE(m_series->meshAngle(), 15.0f);
+ QCOMPARE(m_series->selectedBar(), QPoint(0, 0));
+
+ QLinearGradient gradient1;
+ gradient1.setColorAt(0.0, Qt::red);
+ gradient1.setColorAt(1.0, Qt::blue);
+ QLinearGradient gradient2;
+ gradient2.setColorAt(0.0, Qt::yellow);
+ gradient2.setColorAt(1.0, Qt::green);
+ QLinearGradient gradient3;
+ gradient3.setColorAt(0.0, Qt::white);
+ gradient3.setColorAt(1.0, Qt::gray);
+
+ // Common properties
+ m_series->setBaseColor(QColor(Qt::blue));
+ m_series->setBaseGradient(gradient1);
+ m_series->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
+ m_series->setItemLabelFormat("%f");
+ m_series->setItemLabelVisible(false);
+ m_series->setMesh(QAbstract3DSeries::MeshCone);
+ m_series->setMeshSmooth(true);
+ m_series->setMultiHighlightColor(QColor(Qt::green));
+ m_series->setMultiHighlightGradient(gradient2);
+ m_series->setName("name");
+ m_series->setSingleHighlightColor(QColor(Qt::red));
+ m_series->setSingleHighlightGradient(gradient3);
+ m_series->setUserDefinedMesh(":/customitem.obj");
+ m_series->setVisible(false);
+
+ QCOMPARE(m_series->baseColor(), QColor(Qt::blue));
+ QCOMPARE(m_series->baseGradient(), gradient1);
+ QCOMPARE(m_series->baseGradient().stops().at(0).second, QColor(Qt::red));
+ QCOMPARE(m_series->colorStyle(), Q3DTheme::ColorStyleRangeGradient);
+ QCOMPARE(m_series->itemLabelFormat(), QString("%f"));
+ QCOMPARE(m_series->isItemLabelVisible(), false);
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshCone);
+ QCOMPARE(m_series->isMeshSmooth(), true);
+ QCOMPARE(m_series->multiHighlightColor(), QColor(Qt::green));
+ QCOMPARE(m_series->multiHighlightGradient(), gradient2);
+ QCOMPARE(m_series->multiHighlightGradient().stops().at(0).second, QColor(Qt::yellow));
+ QCOMPARE(m_series->name(), QString("name"));
+ QCOMPARE(m_series->singleHighlightColor(), QColor(Qt::red));
+ QCOMPARE(m_series->singleHighlightGradient(), gradient3);
+ QCOMPARE(m_series->singleHighlightGradient().stops().at(0).second, QColor(Qt::white));
+ QCOMPARE(m_series->userDefinedMesh(), QString(":/customitem.obj"));
+ QCOMPARE(m_series->isVisible(), false);
+}
+
+void tst_series::invalidProperties()
+{
+ m_series->setMesh(QAbstract3DSeries::MeshMinimal);
+
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshBevelBar);
+}
+
+QTEST_MAIN(tst_series)
+#include "tst_series.moc"
diff --git a/tests/auto/cpptest/q3dbars/q3dbars.pro b/tests/auto/cpptest/q3dbars/q3dbars.pro
new file mode 100644
index 00000000..a7f7c809
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars/q3dbars.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_bars.cpp
diff --git a/tests/auto/cpptest/q3dbars/tst_bars.cpp b/tests/auto/cpptest/q3dbars/tst_bars.cpp
new file mode 100644
index 00000000..6910e4fa
--- /dev/null
+++ b/tests/auto/cpptest/q3dbars/tst_bars.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DBars>
+#include <QtDataVisualization/QCustom3DItem>
+#include <QtDataVisualization/Q3DInputHandler>
+#include <QtDataVisualization/QTouch3DInputHandler>
+
+using namespace QtDataVisualization;
+
+class tst_bars: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+ void addSeries();
+ void addMultipleSeries();
+ void selectSeries();
+ void removeSeries();
+ void removeMultipleSeries();
+
+ // The following tests are not required for scatter or surface, as they are handled identically
+ void addInputHandler();
+ void removeInputHandler();
+
+ void addTheme();
+ void removeTheme();
+
+ void addCustomItem();
+ void removeCustomItem();
+
+ void renderToImage();
+
+private:
+ Q3DBars *m_graph;
+};
+
+QBar3DSeries *newSeries()
+{
+ QBar3DSeries *series = new QBar3DSeries;
+ QBarDataRow *data = new QBarDataRow;
+ *data << -1.0f << 3.0f << 7.5f << 5.0f << 2.2f;
+ series->dataProxy()->addRow(data);
+ return series;
+}
+
+void tst_bars::initTestCase()
+{
+}
+
+void tst_bars::cleanupTestCase()
+{
+}
+
+void tst_bars::init()
+{
+ m_graph = new Q3DBars();
+}
+
+void tst_bars::cleanup()
+{
+ delete m_graph;
+}
+
+void tst_bars::construct()
+{
+ Q3DBars *graph = new Q3DBars();
+ QVERIFY(graph);
+ delete graph;
+
+ graph = new Q3DBars(new QSurfaceFormat());
+ QVERIFY(graph);
+ delete graph;
+}
+
+void tst_bars::initialProperties()
+{
+ QVERIFY(m_graph);
+ QCOMPARE(m_graph->isMultiSeriesUniform(), false);
+ QCOMPARE(m_graph->barThickness(), 1.0);
+ QCOMPARE(m_graph->barSpacing(), QSizeF(1.0f, 1.0f));
+ QCOMPARE(m_graph->isBarSpacingRelative(), true);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+ QVERIFY(!m_graph->selectedSeries());
+ QVERIFY(!m_graph->primarySeries());
+ QCOMPARE(m_graph->floorLevel(), 0.0);
+ QCOMPARE(m_graph->columnAxis()->orientation(), QAbstract3DAxis::AxisOrientationX);
+ QCOMPARE(m_graph->valueAxis()->orientation(), QAbstract3DAxis::AxisOrientationY);
+ QCOMPARE(m_graph->rowAxis()->orientation(), QAbstract3DAxis::AxisOrientationZ);
+
+ // Common properties
+ QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt);
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium);
+ QVERIFY(m_graph->scene());
+ QCOMPARE(m_graph->measureFps(), false);
+ QCOMPARE(m_graph->isOrthoProjection(), false);
+ QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone);
+ QCOMPARE(m_graph->aspectRatio(), 2.0);
+ QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationDefault);
+ QCOMPARE(m_graph->isPolar(), false);
+ QCOMPARE(m_graph->radialLabelOffset(), 1.0);
+ QCOMPARE(m_graph->horizontalAspectRatio(), 0.0);
+ QCOMPARE(m_graph->isReflection(), false);
+ QCOMPARE(m_graph->reflectivity(), 0.5);
+ QCOMPARE(m_graph->locale(), QLocale("C"));
+ QCOMPARE(m_graph->queriedGraphPosition(), QVector3D(0, 0, 0));
+ QCOMPARE(m_graph->margin(), -1.0);
+}
+
+void tst_bars::initializeProperties()
+{
+ QVERIFY(m_graph);
+
+ m_graph->setMultiSeriesUniform(true);
+ m_graph->setBarThickness(0.2f);
+ m_graph->setBarSpacing(QSizeF(0.1f, 0.1f));
+ m_graph->setBarSpacingRelative(false);
+ m_graph->setFloorLevel(1.0f);
+
+ QCOMPARE(m_graph->isMultiSeriesUniform(), true);
+ QCOMPARE(m_graph->barThickness(), 0.2f);
+ QCOMPARE(m_graph->barSpacing(), QSizeF(0.1f, 0.1f));
+ QCOMPARE(m_graph->isBarSpacingRelative(), false);
+ QCOMPARE(m_graph->floorLevel(), 1.0f);
+
+ Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia);
+ m_graph->setActiveTheme(theme);
+ m_graph->setSelectionMode(QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftHigh);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualitySoftHigh);
+ m_graph->setMeasureFps(true);
+ m_graph->setOrthoProjection(true);
+ m_graph->setAspectRatio(1.0);
+ m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic);
+ m_graph->setPolar(true);
+ m_graph->setRadialLabelOffset(0.1f);
+ m_graph->setHorizontalAspectRatio(1.0);
+ m_graph->setReflection(true);
+ m_graph->setReflectivity(0.1);
+ m_graph->setLocale(QLocale("FI"));
+ m_graph->setMargin(1.0);
+
+ QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeDigia);
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityNone); // Ortho disables shadows
+ QCOMPARE(m_graph->measureFps(), true);
+ QCOMPARE(m_graph->isOrthoProjection(), true);
+ QCOMPARE(m_graph->aspectRatio(), 1.0);
+ QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationStatic);
+ QCOMPARE(m_graph->isPolar(), true);
+ QCOMPARE(m_graph->radialLabelOffset(), 0.1f);
+ QCOMPARE(m_graph->horizontalAspectRatio(), 1.0);
+ QCOMPARE(m_graph->isReflection(), true);
+ QCOMPARE(m_graph->reflectivity(), 0.1);
+ QCOMPARE(m_graph->locale(), QLocale("FI"));
+ QCOMPARE(m_graph->margin(), 1.0);
+}
+
+void tst_bars::invalidProperties()
+{
+ m_graph->setSelectionMode(QAbstract3DGraph::SelectionColumn | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice);
+ m_graph->setAspectRatio(-1.0);
+ m_graph->setHorizontalAspectRatio(-1.0);
+ m_graph->setReflectivity(-1.0);
+ m_graph->setLocale(QLocale("XX"));
+
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem);
+ QCOMPARE(m_graph->aspectRatio(), -1.0/*2.0*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->horizontalAspectRatio(), -1.0/*0.0*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->reflectivity(), -1.0/*0.5*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->locale(), QLocale("C"));
+}
+
+void tst_bars::addSeries()
+{
+ QBar3DSeries *series = newSeries();
+
+ m_graph->addSeries(series);
+
+ QCOMPARE(m_graph->seriesList().length(), 1);
+ QVERIFY(!m_graph->selectedSeries());
+ QCOMPARE(m_graph->primarySeries(), series);
+}
+
+void tst_bars::addMultipleSeries()
+{
+ QBar3DSeries *series = newSeries();
+ QBar3DSeries *series2 = newSeries();
+ QBar3DSeries *series3 = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->addSeries(series2);
+ m_graph->addSeries(series3);
+
+ QCOMPARE(m_graph->seriesList().length(), 3);
+ QCOMPARE(m_graph->primarySeries(), series);
+
+ m_graph->setPrimarySeries(series2);
+
+ QCOMPARE(m_graph->primarySeries(), series2);
+}
+
+void tst_bars::selectSeries()
+{
+ QBar3DSeries *series = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->primarySeries()->setSelectedBar(QPoint(0, 0));
+
+ QCOMPARE(m_graph->seriesList().length(), 1);
+ QCOMPARE(m_graph->selectedSeries(), series);
+
+ m_graph->clearSelection();
+ QVERIFY(!m_graph->selectedSeries());
+}
+
+void tst_bars::removeSeries()
+{
+ QBar3DSeries *series = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->removeSeries(series);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+}
+
+void tst_bars::removeMultipleSeries()
+{
+ QBar3DSeries *series = newSeries();
+ QBar3DSeries *series2 = newSeries();
+ QBar3DSeries *series3 = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->addSeries(series2);
+ m_graph->addSeries(series3);
+
+ m_graph->primarySeries()->setSelectedBar(QPoint(0, 0));
+ QCOMPARE(m_graph->selectedSeries(), series);
+
+ m_graph->removeSeries(series);
+ QCOMPARE(m_graph->seriesList().length(), 2);
+ QCOMPARE(m_graph->primarySeries(), series2);
+ QVERIFY(!m_graph->selectedSeries());
+
+ m_graph->removeSeries(series2);
+ QCOMPARE(m_graph->seriesList().length(), 1);
+ QCOMPARE(m_graph->primarySeries(), series3);
+
+ m_graph->removeSeries(series3);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+}
+
+// The following tests are not required for scatter or surface, as they are handled identically
+void tst_bars::addInputHandler()
+{
+ Q3DInputHandler *handler = new Q3DInputHandler();
+ QTouch3DInputHandler *handler2 = new QTouch3DInputHandler();
+ QAbstract3DInputHandler *initialHandler = m_graph->activeInputHandler();
+
+ m_graph->addInputHandler(handler);
+ m_graph->addInputHandler(handler2);
+
+ QCOMPARE(m_graph->inputHandlers().length(), 3); // Default, as it is still active, plus added ones
+ QCOMPARE(m_graph->activeInputHandler(), initialHandler);
+ m_graph->setActiveInputHandler(handler2);
+ QCOMPARE(m_graph->activeInputHandler(), handler2);
+
+ m_graph->setActiveInputHandler(NULL);
+ QVERIFY(!m_graph->activeInputHandler());
+ QCOMPARE(m_graph->inputHandlers().length(), 2);
+}
+
+void tst_bars::removeInputHandler()
+{
+ Q3DInputHandler *handler = new Q3DInputHandler();
+ QTouch3DInputHandler *handler2 = new QTouch3DInputHandler();
+
+ m_graph->addInputHandler(handler);
+ m_graph->addInputHandler(handler2);
+
+ m_graph->setActiveInputHandler(handler2);
+ QCOMPARE(m_graph->inputHandlers().length(), 2); // Default handler removed by previous call
+ QCOMPARE(m_graph->activeInputHandler(), handler2);
+ m_graph->releaseInputHandler(handler2);
+ QCOMPARE(m_graph->inputHandlers().length(), 1);
+ m_graph->releaseInputHandler(handler);
+ QCOMPARE(m_graph->inputHandlers().length(), 0);
+
+ delete handler2;
+ delete handler;
+}
+
+void tst_bars::addTheme()
+{
+ Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia);
+ Q3DTheme *theme2 = new Q3DTheme();
+ Q3DTheme *initialTheme = m_graph->activeTheme();
+ m_graph->addTheme(theme);
+ m_graph->addTheme(theme2);
+
+ QCOMPARE(m_graph->themes().length(), 3); // Default, plus added ones
+ QCOMPARE(m_graph->activeTheme(), initialTheme);
+ m_graph->setActiveTheme(theme2);
+ QCOMPARE(m_graph->activeTheme(), theme2);
+}
+
+void tst_bars::removeTheme()
+{
+ Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia);
+ Q3DTheme *theme2 = new Q3DTheme();
+ m_graph->addTheme(theme);
+ m_graph->addTheme(theme2);
+
+ m_graph->setActiveTheme(theme2);
+ QCOMPARE(m_graph->activeTheme(), theme2);
+ m_graph->releaseTheme(theme2);
+ QCOMPARE(m_graph->themes().length(), 2);
+ m_graph->releaseTheme(theme);
+ QCOMPARE(m_graph->themes().length(), 1); // Default theme remains
+
+ delete theme2;
+ delete theme;
+}
+
+void tst_bars::addCustomItem()
+{
+ QCustom3DItem *item = new QCustom3DItem();
+ QCustom3DItem *item2 = new QCustom3DItem();
+
+ m_graph->addCustomItem(item);
+ QCOMPARE(m_graph->customItems().length(), 1);
+ m_graph->addCustomItem(item2);
+ QCOMPARE(m_graph->customItems().length(), 2);
+}
+
+void tst_bars::removeCustomItem()
+{
+ QCustom3DItem *item = new QCustom3DItem();
+ QCustom3DItem *item2 = new QCustom3DItem();
+ QCustom3DItem *item3 = new QCustom3DItem();
+ item3->setPosition(QVector3D(1, 1, 1));
+
+ m_graph->addCustomItem(item);
+ m_graph->addCustomItem(item2);
+ m_graph->addCustomItem(item3);
+
+ m_graph->releaseCustomItem(item);
+ QCOMPARE(m_graph->customItems().length(), 2);
+ m_graph->removeCustomItem(item2);
+ QCOMPARE(m_graph->customItems().length(), 1);
+ m_graph->addCustomItem(item);
+ m_graph->removeCustomItemAt(QVector3D(1, 1, 1));
+ QCOMPARE(m_graph->customItems().length(), 1);
+ m_graph->removeCustomItems();
+ QCOMPARE(m_graph->customItems().length(), 0);
+}
+
+void tst_bars::renderToImage()
+{
+ m_graph->addSeries(newSeries());
+
+ QImage image = m_graph->renderToImage();
+ QCOMPARE(image.size(), m_graph->size());
+
+ image = m_graph->renderToImage(8);
+ QCOMPARE(image.size(), m_graph->size());
+
+ image = m_graph->renderToImage(4, QSize(300, 300));
+ QCOMPARE(image.size(), QSize(300, 300));
+}
+
+QTEST_MAIN(tst_bars)
+#include "tst_bars.moc"
diff --git a/tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro b/tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro
new file mode 100644
index 00000000..af584baa
--- /dev/null
+++ b/tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_custom.cpp
diff --git a/tests/auto/cpptest/q3dcustom-label/tst_custom.cpp b/tests/auto/cpptest/q3dcustom-label/tst_custom.cpp
new file mode 100644
index 00000000..40bd5eac
--- /dev/null
+++ b/tests/auto/cpptest/q3dcustom-label/tst_custom.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QCustom3DLabel>
+
+using namespace QtDataVisualization;
+
+class tst_custom: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QCustom3DLabel *m_custom;
+};
+
+void tst_custom::initTestCase()
+{
+}
+
+void tst_custom::cleanupTestCase()
+{
+}
+
+void tst_custom::init()
+{
+ m_custom = new QCustom3DLabel();
+}
+
+void tst_custom::cleanup()
+{
+ delete m_custom;
+}
+
+void tst_custom::construct()
+{
+ QCustom3DLabel *custom = new QCustom3DLabel();
+ QVERIFY(custom);
+ delete custom;
+
+ custom = new QCustom3DLabel("label", QFont("Times New Roman", 10.0), QVector3D(1.0, 1.0, 1.0),
+ QVector3D(1.0, 1.0, 1.0), QQuaternion(1.0, 1.0, 10.0, 100.0));
+ QVERIFY(custom);
+ QCOMPARE(custom->backgroundColor(), QColor(Qt::gray));
+ QCOMPARE(custom->isBackgroundEnabled(), true);
+ QCOMPARE(custom->isBorderEnabled(), true);
+ QCOMPARE(custom->isFacingCamera(), false);
+ QCOMPARE(custom->font(), QFont("Times New Roman", 10));
+ QCOMPARE(custom->text(), QString("label"));
+ QCOMPARE(custom->textColor(), QColor(Qt::white));
+ QCOMPARE(custom->meshFile(), QString(":/defaultMeshes/plane"));
+ QCOMPARE(custom->position(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(custom->isPositionAbsolute(), false);
+ QCOMPARE(custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0));
+ QCOMPARE(custom->scaling(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(custom->isScalingAbsolute(), true);
+ QCOMPARE(custom->isShadowCasting(), false);
+ QCOMPARE(custom->textureFile(), QString());
+ QCOMPARE(custom->isVisible(), true);
+ delete custom;
+}
+
+void tst_custom::initialProperties()
+{
+ QVERIFY(m_custom);
+
+ QCOMPARE(m_custom->backgroundColor(), QColor(Qt::gray));
+ QCOMPARE(m_custom->isBackgroundEnabled(), true);
+ QCOMPARE(m_custom->isBorderEnabled(), true);
+ QCOMPARE(m_custom->isFacingCamera(), false);
+ QCOMPARE(m_custom->font(), QFont("Arial", 20));
+ QCOMPARE(m_custom->text(), QString());
+ QCOMPARE(m_custom->textColor(), QColor(Qt::white));
+
+ // Common (from QCustom3DItem)
+ QCOMPARE(m_custom->meshFile(), QString(":/defaultMeshes/plane"));
+ QCOMPARE(m_custom->position(), QVector3D());
+ QCOMPARE(m_custom->isPositionAbsolute(), false);
+ QCOMPARE(m_custom->rotation(), QQuaternion());
+ QCOMPARE(m_custom->scaling(), QVector3D(0.1f, 0.1f, 0.1f));
+ QCOMPARE(m_custom->isScalingAbsolute(), true);
+ QCOMPARE(m_custom->isShadowCasting(), false);
+ QCOMPARE(m_custom->textureFile(), QString());
+ QCOMPARE(m_custom->isVisible(), true);
+}
+
+void tst_custom::initializeProperties()
+{
+ QVERIFY(m_custom);
+
+ m_custom->setBackgroundColor(QColor(Qt::red));
+ m_custom->setBackgroundEnabled(false);
+ m_custom->setBorderEnabled(false);
+ m_custom->setFacingCamera(true);
+ m_custom->setFont(QFont("Times New Roman", 10));
+ m_custom->setText(QString("This is a Custom Label"));
+ m_custom->setTextColor(QColor(Qt::blue));
+
+ QCOMPARE(m_custom->backgroundColor(), QColor(Qt::red));
+ QCOMPARE(m_custom->isBackgroundEnabled(), false);
+ QCOMPARE(m_custom->isBorderEnabled(), false);
+ QCOMPARE(m_custom->isFacingCamera(), true);
+ QCOMPARE(m_custom->font(), QFont("Times New Roman", 10));
+ QCOMPARE(m_custom->text(), QString("This is a Custom Label"));
+ QCOMPARE(m_custom->textColor(), QColor(Qt::blue));
+
+ // Common (from QCustom3DItem)
+ m_custom->setPosition(QVector3D(1.0, 1.0, 1.0));
+ m_custom->setPositionAbsolute(true);
+ m_custom->setRotation(QQuaternion(1.0, 1.0, 10.0, 100.0));
+ m_custom->setScaling(QVector3D(1.0, 1.0, 1.0));
+ m_custom->setShadowCasting(true);
+ m_custom->setVisible(false);
+
+ QCOMPARE(m_custom->position(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(m_custom->isPositionAbsolute(), true);
+ QCOMPARE(m_custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0));
+ QCOMPARE(m_custom->scaling(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(m_custom->isShadowCasting(), true);
+ QCOMPARE(m_custom->isVisible(), false);
+}
+
+void tst_custom::invalidProperties()
+{
+ m_custom->setScalingAbsolute(false);
+ QCOMPARE(m_custom->isScalingAbsolute(), true);
+}
+
+QTEST_MAIN(tst_custom)
+#include "tst_custom.moc"
diff --git a/tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro b/tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro
new file mode 100644
index 00000000..af584baa
--- /dev/null
+++ b/tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_custom.cpp
diff --git a/tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp b/tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp
new file mode 100644
index 00000000..372e8ecf
--- /dev/null
+++ b/tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QCustom3DVolume>
+
+using namespace QtDataVisualization;
+
+class tst_custom: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QCustom3DVolume *m_custom;
+};
+
+void tst_custom::initTestCase()
+{
+}
+
+void tst_custom::cleanupTestCase()
+{
+}
+
+void tst_custom::init()
+{
+ m_custom = new QCustom3DVolume();
+}
+
+void tst_custom::cleanup()
+{
+ delete m_custom;
+}
+
+void tst_custom::construct()
+{
+ QCustom3DVolume *custom = new QCustom3DVolume();
+ QVERIFY(custom);
+ delete custom;
+
+ QVector<uchar> *tdata = new QVector<uchar>(1000);
+
+ QVector<QRgb> table;
+ table << QRgb(0xff00ff) << QRgb(0x00ff00);
+
+ custom = new QCustom3DVolume(QVector3D(1.0, 1.0, 1.0), QVector3D(1.0, 1.0, 1.0),
+ QQuaternion(1.0, 1.0, 10.0, 100.0), 10, 10, 10,
+ tdata, QImage::Format_ARGB32, table);
+ QVERIFY(custom);
+ QCOMPARE(custom->alphaMultiplier(), 1.0f);
+ QCOMPARE(custom->drawSliceFrames(), false);
+ QCOMPARE(custom->drawSliceFrames(), false);
+ QCOMPARE(custom->preserveOpacity(), true);
+ QCOMPARE(custom->sliceFrameColor(), QColor(Qt::black));
+ QCOMPARE(custom->sliceFrameGaps(), QVector3D(0.01f, 0.01f, 0.01f));
+ QCOMPARE(custom->sliceFrameThicknesses(), QVector3D(0.01f, 0.01f, 0.01f));
+ QCOMPARE(custom->sliceFrameWidths(), QVector3D(0.01f, 0.01f, 0.01f));
+ QCOMPARE(custom->sliceIndexX(), -1);
+ QCOMPARE(custom->sliceIndexY(), -1);
+ QCOMPARE(custom->sliceIndexZ(), -1);
+ QCOMPARE(custom->useHighDefShader(), true);
+ QCOMPARE(custom->textureData()->length(), 1000);
+ QCOMPARE(custom->textureDataWidth(), 40);
+ QCOMPARE(custom->textureFormat(), QImage::Format_ARGB32);
+ QCOMPARE(custom->textureHeight(), 10);
+ QCOMPARE(custom->textureWidth(), 10);
+ QCOMPARE(custom->textureDepth(), 10);
+ QCOMPARE(custom->meshFile(), QString(":/defaultMeshes/barFull"));
+ QCOMPARE(custom->position(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(custom->isPositionAbsolute(), false);
+ QCOMPARE(custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0));
+ QCOMPARE(custom->scaling(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(custom->isScalingAbsolute(), true);
+ QCOMPARE(custom->isShadowCasting(), false);
+ QCOMPARE(custom->textureFile(), QString());
+ QCOMPARE(custom->isVisible(), true);
+ delete custom;
+}
+
+void tst_custom::initialProperties()
+{
+ QVERIFY(m_custom);
+
+ QCOMPARE(m_custom->alphaMultiplier(), 1.0f);
+ QCOMPARE(m_custom->drawSliceFrames(), false);
+ QCOMPARE(m_custom->drawSliceFrames(), false);
+ QCOMPARE(m_custom->preserveOpacity(), true);
+ QCOMPARE(m_custom->sliceFrameColor(), QColor(Qt::black));
+ QCOMPARE(m_custom->sliceFrameGaps(), QVector3D(0.01f, 0.01f, 0.01f));
+ QCOMPARE(m_custom->sliceFrameThicknesses(), QVector3D(0.01f, 0.01f, 0.01f));
+ QCOMPARE(m_custom->sliceFrameWidths(), QVector3D(0.01f, 0.01f, 0.01f));
+ QCOMPARE(m_custom->sliceIndexX(), -1);
+ QCOMPARE(m_custom->sliceIndexY(), -1);
+ QCOMPARE(m_custom->sliceIndexZ(), -1);
+ QCOMPARE(m_custom->useHighDefShader(), true);
+
+ // Common (from QCustom3DVolume)
+ QCOMPARE(m_custom->meshFile(), QString(":/defaultMeshes/barFull"));
+ QCOMPARE(m_custom->position(), QVector3D());
+ QCOMPARE(m_custom->isPositionAbsolute(), false);
+ QCOMPARE(m_custom->rotation(), QQuaternion());
+ QCOMPARE(m_custom->scaling(), QVector3D(0.1f, 0.1f, 0.1f));
+ QCOMPARE(m_custom->isScalingAbsolute(), true);
+ QCOMPARE(m_custom->isShadowCasting(), true);
+ QCOMPARE(m_custom->textureFile(), QString());
+ QCOMPARE(m_custom->isVisible(), true);
+}
+
+void tst_custom::initializeProperties()
+{
+ QVERIFY(m_custom);
+
+ m_custom->setAlphaMultiplier(0.1f);
+ m_custom->setDrawSliceFrames(true);
+ m_custom->setDrawSliceFrames(true);
+ m_custom->setPreserveOpacity(false);
+ m_custom->setSliceFrameColor(QColor(Qt::red));
+ m_custom->setSliceFrameGaps(QVector3D(2.0f, 2.0f, 2.0f));
+ m_custom->setSliceFrameThicknesses(QVector3D(2.0f, 2.0f, 2.0f));
+ m_custom->setSliceFrameWidths(QVector3D(2.0f, 2.0f, 2.0f));
+ m_custom->setSliceIndexX(0);
+ m_custom->setSliceIndexY(0);
+ m_custom->setSliceIndexZ(0);
+ m_custom->setUseHighDefShader(false);
+
+ QCOMPARE(m_custom->alphaMultiplier(), 0.1f);
+ QCOMPARE(m_custom->drawSliceFrames(), true);
+ QCOMPARE(m_custom->drawSliceFrames(), true);
+ QCOMPARE(m_custom->preserveOpacity(), false);
+ QCOMPARE(m_custom->sliceFrameColor(), QColor(Qt::red));
+ QCOMPARE(m_custom->sliceFrameGaps(), QVector3D(2.0f, 2.0f, 2.0f));
+ QCOMPARE(m_custom->sliceFrameThicknesses(), QVector3D(2.0f, 2.0f, 2.0f));
+ QCOMPARE(m_custom->sliceFrameWidths(), QVector3D(2.0f, 2.0f, 2.0f));
+ QCOMPARE(m_custom->sliceIndexX(), 0);
+ QCOMPARE(m_custom->sliceIndexY(), 0);
+ QCOMPARE(m_custom->sliceIndexZ(), 0);
+ QCOMPARE(m_custom->useHighDefShader(), false);
+
+ // Common (from QCustom3DVolume)
+ m_custom->setPosition(QVector3D(1.0, 1.0, 1.0));
+ m_custom->setPositionAbsolute(true);
+ m_custom->setRotation(QQuaternion(1.0, 1.0, 10.0, 100.0));
+ m_custom->setScaling(QVector3D(1.0, 1.0, 1.0));
+ m_custom->setScalingAbsolute(false);
+ m_custom->setShadowCasting(false);
+ m_custom->setVisible(false);
+
+ QCOMPARE(m_custom->position(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(m_custom->isPositionAbsolute(), true);
+ QCOMPARE(m_custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0));
+ QCOMPARE(m_custom->scaling(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(m_custom->isScalingAbsolute(), false);
+ QCOMPARE(m_custom->isShadowCasting(), false);
+ QCOMPARE(m_custom->isVisible(), false);
+}
+
+void tst_custom::invalidProperties()
+{
+ m_custom->setAlphaMultiplier(-1.0f);
+ QCOMPARE(m_custom->alphaMultiplier(), 1.0f);
+
+ m_custom->setSliceFrameGaps(QVector3D(-0.1f, -0.1f, -0.1f));
+ QCOMPARE(m_custom->sliceFrameGaps(), QVector3D(0.01f, 0.01f, 0.01f));
+
+ m_custom->setSliceFrameThicknesses(QVector3D(-0.1f, -0.1f, -0.1f));
+ QCOMPARE(m_custom->sliceFrameThicknesses(), QVector3D(0.01f, 0.01f, 0.01f));
+
+ m_custom->setSliceFrameWidths(QVector3D(-0.1f, -0.1f, -0.1f));
+ QCOMPARE(m_custom->sliceFrameWidths(), QVector3D(0.01f, 0.01f, 0.01f));
+
+ m_custom->setTextureFormat(QImage::Format_ARGB8555_Premultiplied);
+ QCOMPARE(m_custom->textureFormat(), QImage::Format_ARGB32);
+}
+
+QTEST_MAIN(tst_custom)
+#include "tst_custom.moc"
diff --git a/tests/auto/cpptest/q3dcustom/q3dcustom.pro b/tests/auto/cpptest/q3dcustom/q3dcustom.pro
new file mode 100644
index 00000000..af584baa
--- /dev/null
+++ b/tests/auto/cpptest/q3dcustom/q3dcustom.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_custom.cpp
diff --git a/tests/auto/cpptest/q3dcustom/tst_custom.cpp b/tests/auto/cpptest/q3dcustom/tst_custom.cpp
new file mode 100644
index 00000000..abc088f9
--- /dev/null
+++ b/tests/auto/cpptest/q3dcustom/tst_custom.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QCustom3DItem>
+
+using namespace QtDataVisualization;
+
+class tst_custom: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ QCustom3DItem *m_custom;
+};
+
+void tst_custom::initTestCase()
+{
+}
+
+void tst_custom::cleanupTestCase()
+{
+}
+
+void tst_custom::init()
+{
+ m_custom = new QCustom3DItem();
+}
+
+void tst_custom::cleanup()
+{
+ delete m_custom;
+}
+
+void tst_custom::construct()
+{
+ QCustom3DItem *custom = new QCustom3DItem();
+ QVERIFY(custom);
+ delete custom;
+
+ custom = new QCustom3DItem(":/customitem.obj", QVector3D(1.0, 1.0, 1.0),
+ QVector3D(1.0, 1.0, 1.0), QQuaternion(1.0, 1.0, 10.0, 100.0),
+ QImage(":/customtexture.jpg"));
+ QVERIFY(custom);
+ QCOMPARE(custom->meshFile(), QString(":/customitem.obj"));
+ QCOMPARE(custom->position(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(custom->isPositionAbsolute(), false);
+ QCOMPARE(custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0));
+ QCOMPARE(custom->scaling(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(custom->isScalingAbsolute(), true);
+ QCOMPARE(custom->isShadowCasting(), true);
+ QCOMPARE(custom->textureFile(), QString());
+ QCOMPARE(custom->isVisible(), true);
+ delete custom;
+}
+
+void tst_custom::initialProperties()
+{
+ QVERIFY(m_custom);
+
+ QCOMPARE(m_custom->meshFile(), QString());
+ QCOMPARE(m_custom->position(), QVector3D());
+ QCOMPARE(m_custom->isPositionAbsolute(), false);
+ QCOMPARE(m_custom->rotation(), QQuaternion());
+ QCOMPARE(m_custom->scaling(), QVector3D(0.1f, 0.1f, 0.1f));
+ QCOMPARE(m_custom->isScalingAbsolute(), true);
+ QCOMPARE(m_custom->isShadowCasting(), true);
+ QCOMPARE(m_custom->textureFile(), QString());
+ QCOMPARE(m_custom->isVisible(), true);
+}
+
+void tst_custom::initializeProperties()
+{
+ QVERIFY(m_custom);
+
+ m_custom->setMeshFile(":/customitem.obj");
+ m_custom->setPosition(QVector3D(1.0, 1.0, 1.0));
+ m_custom->setPositionAbsolute(true);
+ m_custom->setRotation(QQuaternion(1.0, 1.0, 10.0, 100.0));
+ m_custom->setScaling(QVector3D(1.0, 1.0, 1.0));
+ m_custom->setScalingAbsolute(false);
+ m_custom->setShadowCasting(false);
+ m_custom->setTextureFile(":/customtexture.jpg");
+ m_custom->setVisible(false);
+
+ QCOMPARE(m_custom->meshFile(), QString(":/customitem.obj"));
+ QCOMPARE(m_custom->position(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(m_custom->isPositionAbsolute(), true);
+ QCOMPARE(m_custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0));
+ QCOMPARE(m_custom->scaling(), QVector3D(1.0, 1.0, 1.0));
+ QCOMPARE(m_custom->isScalingAbsolute(), false);
+ QCOMPARE(m_custom->isShadowCasting(), false);
+ QCOMPARE(m_custom->textureFile(), QString(":/customtexture.jpg"));
+ QCOMPARE(m_custom->isVisible(), false);
+
+ m_custom->setTextureImage(QImage(QSize(10, 10), QImage::Format_ARGB32));
+ QCOMPARE(m_custom->textureFile(), QString());
+}
+
+QTEST_MAIN(tst_custom)
+#include "tst_custom.moc"
diff --git a/tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro b/tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro
new file mode 100644
index 00000000..2de48158
--- /dev/null
+++ b/tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_input.cpp
diff --git a/tests/auto/cpptest/q3dinput-touch/tst_input.cpp b/tests/auto/cpptest/q3dinput-touch/tst_input.cpp
new file mode 100644
index 00000000..53d760ae
--- /dev/null
+++ b/tests/auto/cpptest/q3dinput-touch/tst_input.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QTouch3DInputHandler>
+
+using namespace QtDataVisualization;
+
+class tst_input: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ QTouch3DInputHandler *m_input;
+};
+
+void tst_input::initTestCase()
+{
+}
+
+void tst_input::cleanupTestCase()
+{
+}
+
+void tst_input::init()
+{
+ m_input = new QTouch3DInputHandler();
+}
+
+void tst_input::cleanup()
+{
+ delete m_input;
+}
+
+void tst_input::construct()
+{
+ QTouch3DInputHandler *input = new QTouch3DInputHandler();
+ QVERIFY(input);
+ delete input;
+}
+
+void tst_input::initialProperties()
+{
+ QVERIFY(m_input);
+
+ // Common (from Q3DInputHandler and QAbstract3DInputHandler)
+ QCOMPARE(m_input->isRotationEnabled(), true);
+ QCOMPARE(m_input->isSelectionEnabled(), true);
+ QCOMPARE(m_input->isZoomAtTargetEnabled(), true);
+ QCOMPARE(m_input->isZoomEnabled(), true);
+ QCOMPARE(m_input->inputPosition(), QPoint(0, 0));
+ QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewNone);
+ QVERIFY(!m_input->scene());
+}
+
+void tst_input::initializeProperties()
+{
+ QVERIFY(m_input);
+
+ // Common (from Q3DInputHandler and QAbstract3DInputHandler)
+ m_input->setRotationEnabled(false);
+ m_input->setSelectionEnabled(false);
+ m_input->setZoomAtTargetEnabled(false);
+ m_input->setZoomEnabled(false);
+ m_input->setInputPosition(QPoint(100, 100));
+ m_input->setInputView(QAbstract3DInputHandler::InputViewOnPrimary);
+
+ QCOMPARE(m_input->isRotationEnabled(), false);
+ QCOMPARE(m_input->isSelectionEnabled(), false);
+ QCOMPARE(m_input->isZoomAtTargetEnabled(), false);
+ QCOMPARE(m_input->isZoomEnabled(), false);
+ QCOMPARE(m_input->inputPosition(), QPoint(100, 100));
+ QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewOnPrimary);
+}
+
+// TODO: QTRD-3380 (mouse/touch events)
+
+QTEST_MAIN(tst_input)
+#include "tst_input.moc"
diff --git a/tests/auto/cpptest/q3dinput/q3dinput.pro b/tests/auto/cpptest/q3dinput/q3dinput.pro
new file mode 100644
index 00000000..2de48158
--- /dev/null
+++ b/tests/auto/cpptest/q3dinput/q3dinput.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_input.cpp
diff --git a/tests/auto/cpptest/q3dinput/tst_input.cpp b/tests/auto/cpptest/q3dinput/tst_input.cpp
new file mode 100644
index 00000000..68b2225c
--- /dev/null
+++ b/tests/auto/cpptest/q3dinput/tst_input.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DInputHandler>
+
+using namespace QtDataVisualization;
+
+class tst_input: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ Q3DInputHandler *m_input;
+};
+
+void tst_input::initTestCase()
+{
+}
+
+void tst_input::cleanupTestCase()
+{
+}
+
+void tst_input::init()
+{
+ m_input = new Q3DInputHandler();
+}
+
+void tst_input::cleanup()
+{
+ delete m_input;
+}
+
+void tst_input::construct()
+{
+ Q3DInputHandler *input = new Q3DInputHandler();
+ QVERIFY(input);
+ delete input;
+}
+
+void tst_input::initialProperties()
+{
+ QVERIFY(m_input);
+
+ QCOMPARE(m_input->isRotationEnabled(), true);
+ QCOMPARE(m_input->isSelectionEnabled(), true);
+ QCOMPARE(m_input->isZoomAtTargetEnabled(), true);
+ QCOMPARE(m_input->isZoomEnabled(), true);
+
+ // Common (from QAbstract3DInputHandler)
+ QCOMPARE(m_input->inputPosition(), QPoint(0, 0));
+ QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewNone);
+ QVERIFY(!m_input->scene());
+}
+
+void tst_input::initializeProperties()
+{
+ QVERIFY(m_input);
+
+ m_input->setRotationEnabled(false);
+ m_input->setSelectionEnabled(false);
+ m_input->setZoomAtTargetEnabled(false);
+ m_input->setZoomEnabled(false);
+
+ QCOMPARE(m_input->isRotationEnabled(), false);
+ QCOMPARE(m_input->isSelectionEnabled(), false);
+ QCOMPARE(m_input->isZoomAtTargetEnabled(), false);
+ QCOMPARE(m_input->isZoomEnabled(), false);
+
+ // Common (from QAbstract3DInputHandler)
+ m_input->setInputPosition(QPoint(100, 100));
+ m_input->setInputView(QAbstract3DInputHandler::InputViewOnPrimary);
+
+ QCOMPARE(m_input->inputPosition(), QPoint(100, 100));
+ QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewOnPrimary);
+}
+
+// TODO: QTRD-3380 (mouse events)
+
+QTEST_MAIN(tst_input)
+#include "tst_input.moc"
diff --git a/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro b/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro
new file mode 100644
index 00000000..c383ec25
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization widgets
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_proxy.cpp
diff --git a/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp
new file mode 100644
index 00000000..9d5cea90
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QItemModelScatterDataProxy>
+#include <QtDataVisualization/Q3DScatter>
+#include <QtWidgets/QTableWidget>
+
+using namespace QtDataVisualization;
+
+class tst_proxy: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+ void addModel();
+
+private:
+ QItemModelScatterDataProxy *m_proxy;
+};
+
+void tst_proxy::initTestCase()
+{
+}
+
+void tst_proxy::cleanupTestCase()
+{
+}
+
+void tst_proxy::init()
+{
+ m_proxy = new QItemModelScatterDataProxy();
+}
+
+void tst_proxy::cleanup()
+{
+ delete m_proxy;
+}
+
+void tst_proxy::construct()
+{
+ QItemModelScatterDataProxy *proxy = new QItemModelScatterDataProxy();
+ QVERIFY(proxy);
+ delete proxy;
+
+ QTableWidget *table = new QTableWidget();
+
+ proxy = new QItemModelScatterDataProxy(table->model());
+ QVERIFY(proxy);
+ delete proxy;
+
+ proxy = new QItemModelScatterDataProxy(table->model(), "x", "y", "z");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->xPosRole(), QString("x"));
+ QCOMPARE(proxy->yPosRole(), QString("y"));
+ QCOMPARE(proxy->zPosRole(), QString("z"));
+ QCOMPARE(proxy->rotationRole(), QString(""));
+ delete proxy;
+
+ proxy = new QItemModelScatterDataProxy(table->model(), "x", "y", "z", "rot");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->xPosRole(), QString("x"));
+ QCOMPARE(proxy->yPosRole(), QString("y"));
+ QCOMPARE(proxy->zPosRole(), QString("z"));
+ QCOMPARE(proxy->rotationRole(), QString("rot"));
+ delete proxy;
+}
+
+void tst_proxy::initialProperties()
+{
+ QVERIFY(m_proxy);
+
+ QVERIFY(!m_proxy->itemModel());
+ QCOMPARE(m_proxy->rotationRole(), QString());
+ QCOMPARE(m_proxy->rotationRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->rotationRoleReplace(), QString());
+ QCOMPARE(m_proxy->xPosRole(), QString());
+ QCOMPARE(m_proxy->xPosRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->xPosRoleReplace(), QString());
+ QCOMPARE(m_proxy->yPosRole(), QString());
+ QCOMPARE(m_proxy->yPosRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->yPosRoleReplace(), QString());
+ QCOMPARE(m_proxy->zPosRole(), QString());
+ QCOMPARE(m_proxy->zPosRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->zPosRoleReplace(), QString());
+
+ QCOMPARE(m_proxy->itemCount(), 0);
+ QVERIFY(!m_proxy->series());
+
+ QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeScatter);
+}
+
+void tst_proxy::initializeProperties()
+{
+ QVERIFY(m_proxy);
+
+ QTableWidget *table = new QTableWidget();
+
+ m_proxy->setItemModel(table->model());
+ m_proxy->setRotationRole("rotation");
+ m_proxy->setRotationRolePattern(QRegExp("/-/"));
+ m_proxy->setRotationRoleReplace("\\\\1");
+ m_proxy->setXPosRole("X");
+ m_proxy->setXPosRolePattern(QRegExp("/-/"));
+ m_proxy->setXPosRoleReplace("\\\\1");
+ m_proxy->setYPosRole("Y");
+ m_proxy->setYPosRolePattern(QRegExp("/-/"));
+ m_proxy->setYPosRoleReplace("\\\\1");
+ m_proxy->setZPosRole("Z");
+ m_proxy->setZPosRolePattern(QRegExp("/-/"));
+ m_proxy->setZPosRoleReplace("\\\\1");
+
+ QVERIFY(m_proxy->itemModel());
+ QCOMPARE(m_proxy->rotationRole(), QString("rotation"));
+ QCOMPARE(m_proxy->rotationRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->rotationRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->xPosRole(), QString("X"));
+ QCOMPARE(m_proxy->xPosRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->xPosRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->yPosRole(), QString("Y"));
+ QCOMPARE(m_proxy->yPosRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->yPosRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->zPosRole(), QString("Z"));
+ QCOMPARE(m_proxy->zPosRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->zPosRoleReplace(), QString("\\\\1"));
+}
+
+void tst_proxy::addModel()
+{
+ QTableWidget *table = new QTableWidget();
+ QStringList rows;
+ rows << "row 1";
+ QStringList columns;
+ columns << "col 1";
+ const char *values[1][2] = {{"0/0/5.5/30", "0/0/10.5/30"}};
+
+ table->setRowCount(2);
+ table->setColumnCount(1);
+
+ for (int col = 0; col < columns.size(); col++) {
+ for (int row = 0; row < rows.size(); row++) {
+ QModelIndex index = table->model()->index(col, row);
+ table->model()->setData(index, values[col][row]);
+ }
+ }
+
+ m_proxy->setItemModel(table->model());
+ m_proxy->setXPosRole(table->model()->roleNames().value(Qt::DisplayRole));
+ m_proxy->setZPosRole(table->model()->roleNames().value(Qt::DisplayRole));
+ m_proxy->setXPosRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setXPosRoleReplace(QStringLiteral("\\2"));
+ m_proxy->setYPosRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setYPosRoleReplace(QStringLiteral("\\3"));
+ m_proxy->setZPosRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setZPosRoleReplace(QStringLiteral("\\1"));
+
+ QScatter3DSeries *series = new QScatter3DSeries(m_proxy);
+ Q_UNUSED(series)
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_proxy->itemCount(), 2);
+ QVERIFY(m_proxy->series());
+}
+
+QTEST_MAIN(tst_proxy)
+#include "tst_proxy.moc"
diff --git a/tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro b/tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro
new file mode 100644
index 00000000..b0b5d361
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_proxy.cpp
diff --git a/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp
new file mode 100644
index 00000000..436350dc
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QScatterDataProxy>
+
+using namespace QtDataVisualization;
+
+class tst_proxy: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ QScatterDataProxy *m_proxy;
+};
+
+void tst_proxy::initTestCase()
+{
+}
+
+void tst_proxy::cleanupTestCase()
+{
+}
+
+void tst_proxy::init()
+{
+ m_proxy = new QScatterDataProxy();
+}
+
+void tst_proxy::cleanup()
+{
+ delete m_proxy;
+}
+
+void tst_proxy::construct()
+{
+ QScatterDataProxy *proxy = new QScatterDataProxy();
+ QVERIFY(proxy);
+ delete proxy;
+}
+
+void tst_proxy::initialProperties()
+{
+ QVERIFY(m_proxy);
+
+ QCOMPARE(m_proxy->itemCount(), 0);
+ QVERIFY(!m_proxy->series());
+
+ QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeScatter);
+}
+
+void tst_proxy::initializeProperties()
+{
+ QVERIFY(m_proxy);
+
+ QScatterDataArray data;
+ data << QVector3D(0.5f, 0.5f, 0.5f) << QVector3D(-0.3f, -0.5f, -0.4f);
+ m_proxy->addItems(data);
+
+ QCOMPARE(m_proxy->itemCount(), 2);
+}
+
+QTEST_MAIN(tst_proxy)
+#include "tst_proxy.moc"
diff --git a/tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro b/tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro
new file mode 100644
index 00000000..481653ef
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_series.cpp
diff --git a/tests/auto/cpptest/q3dscatter-series/tst_series.cpp b/tests/auto/cpptest/q3dscatter-series/tst_series.cpp
new file mode 100644
index 00000000..df290579
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter-series/tst_series.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QScatter3DSeries>
+
+using namespace QtDataVisualization;
+
+class tst_series: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ QScatter3DSeries *m_series;
+};
+
+void tst_series::initTestCase()
+{
+}
+
+void tst_series::cleanupTestCase()
+{
+}
+
+void tst_series::init()
+{
+ m_series = new QScatter3DSeries();
+}
+
+void tst_series::cleanup()
+{
+ delete m_series;
+}
+
+void tst_series::construct()
+{
+ QScatter3DSeries *series = new QScatter3DSeries();
+ QVERIFY(series);
+ delete series;
+
+ QScatterDataProxy *proxy = new QScatterDataProxy();
+
+ series = new QScatter3DSeries(proxy);
+ QVERIFY(series);
+ QCOMPARE(series->dataProxy(), proxy);
+ delete series;
+}
+
+void tst_series::initialProperties()
+{
+ QVERIFY(m_series);
+
+ QVERIFY(m_series->dataProxy());
+ QCOMPARE(m_series->itemSize(), 0.0f);
+ QCOMPARE(m_series->selectedItem(), m_series->invalidSelectionIndex());
+
+ // Common properties. The ones identical between different series are tested in QBar3DSeries tests
+ QCOMPARE(m_series->itemLabelFormat(), QString("@xLabel, @yLabel, @zLabel"));
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshSphere);
+ QCOMPARE(m_series->type(), QAbstract3DSeries::SeriesTypeScatter);
+}
+
+void tst_series::initializeProperties()
+{
+ QVERIFY(m_series);
+
+ m_series->setDataProxy(new QScatterDataProxy());
+ m_series->setItemSize(0.5f);
+ m_series->setSelectedItem(0);
+
+ QCOMPARE(m_series->itemSize(), 0.5f);
+ QCOMPARE(m_series->selectedItem(), 0);
+
+ // Common properties. The ones identical between different series are tested in QBar3DSeries tests
+ m_series->setMesh(QAbstract3DSeries::MeshPoint);
+ m_series->setMeshRotation(QQuaternion(1, 1, 10, 20));
+
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshPoint);
+ QCOMPARE(m_series->meshRotation(), QQuaternion(1, 1, 10, 20));
+}
+
+QTEST_MAIN(tst_series)
+#include "tst_series.moc"
diff --git a/tests/auto/cpptest/q3dscatter/q3dscatter.pro b/tests/auto/cpptest/q3dscatter/q3dscatter.pro
new file mode 100644
index 00000000..9f356ebc
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter/q3dscatter.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_scatter.cpp
diff --git a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp
new file mode 100644
index 00000000..5a3b6550
--- /dev/null
+++ b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DScatter>
+
+using namespace QtDataVisualization;
+
+class tst_scatter: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+ void addSeries();
+ void addMultipleSeries();
+ void selectSeries();
+ void removeSeries();
+ void removeMultipleSeries();
+
+private:
+ Q3DScatter *m_graph;
+};
+
+QScatter3DSeries *newSeries()
+{
+ QScatter3DSeries *series = new QScatter3DSeries;
+ QScatterDataArray data;
+ data << QVector3D(0.5f, 0.5f, 0.5f) << QVector3D(-0.3f, -0.5f, -0.4f) << QVector3D(0.0f, -0.3f, 0.2f);
+ series->dataProxy()->addItems(data);
+ return series;
+}
+
+void tst_scatter::initTestCase()
+{
+}
+
+void tst_scatter::cleanupTestCase()
+{
+}
+
+void tst_scatter::init()
+{
+ m_graph = new Q3DScatter();
+}
+
+void tst_scatter::cleanup()
+{
+ delete m_graph;
+}
+
+void tst_scatter::construct()
+{
+ Q3DScatter *graph = new Q3DScatter();
+ QVERIFY(graph);
+ delete graph;
+
+ graph = new Q3DScatter(new QSurfaceFormat());
+ QVERIFY(graph);
+ delete graph;
+}
+
+void tst_scatter::initialProperties()
+{
+ QVERIFY(m_graph);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+ QVERIFY(!m_graph->selectedSeries());
+ QCOMPARE(m_graph->axisX()->orientation(), QAbstract3DAxis::AxisOrientationX);
+ QCOMPARE(m_graph->axisY()->orientation(), QAbstract3DAxis::AxisOrientationY);
+ QCOMPARE(m_graph->axisZ()->orientation(), QAbstract3DAxis::AxisOrientationZ);
+
+ // Common properties
+ QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt);
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium);
+ QVERIFY(m_graph->scene());
+ QCOMPARE(m_graph->measureFps(), false);
+ QCOMPARE(m_graph->isOrthoProjection(), false);
+ QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone);
+ QCOMPARE(m_graph->aspectRatio(), 2.0);
+ QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationDefault);
+ QCOMPARE(m_graph->isPolar(), false);
+ QCOMPARE(m_graph->radialLabelOffset(), 1.0);
+ QCOMPARE(m_graph->horizontalAspectRatio(), 0.0);
+ QCOMPARE(m_graph->isReflection(), false);
+ QCOMPARE(m_graph->reflectivity(), 0.5);
+ QCOMPARE(m_graph->locale(), QLocale("C"));
+ QCOMPARE(m_graph->queriedGraphPosition(), QVector3D(0, 0, 0));
+ QCOMPARE(m_graph->margin(), -1.0);
+}
+
+void tst_scatter::initializeProperties()
+{
+ Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia);
+ m_graph->setActiveTheme(theme);
+ m_graph->setSelectionMode(QAbstract3DGraph::SelectionNone);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftHigh);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualitySoftHigh);
+ m_graph->setMeasureFps(true);
+ m_graph->setOrthoProjection(true);
+ m_graph->setAspectRatio(1.0);
+ m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic);
+ m_graph->setPolar(true);
+ m_graph->setRadialLabelOffset(0.1f);
+ m_graph->setHorizontalAspectRatio(1.0);
+ m_graph->setReflection(true);
+ m_graph->setReflectivity(0.1);
+ m_graph->setLocale(QLocale("FI"));
+ m_graph->setMargin(1.0);
+
+ QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeDigia);
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionNone);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityNone); // Ortho disables shadows
+ QCOMPARE(m_graph->measureFps(), true);
+ QCOMPARE(m_graph->isOrthoProjection(), true);
+ QCOMPARE(m_graph->aspectRatio(), 1.0);
+ QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationStatic);
+ QCOMPARE(m_graph->isPolar(), true);
+ QCOMPARE(m_graph->radialLabelOffset(), 0.1f);
+ QCOMPARE(m_graph->horizontalAspectRatio(), 1.0);
+ QCOMPARE(m_graph->isReflection(), true);
+ QCOMPARE(m_graph->reflectivity(), 0.1);
+ QCOMPARE(m_graph->locale(), QLocale("FI"));
+ QCOMPARE(m_graph->margin(), 1.0);
+}
+
+void tst_scatter::invalidProperties()
+{
+ m_graph->setSelectionMode(QAbstract3DGraph::SelectionColumn | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice);
+ m_graph->setAspectRatio(-1.0);
+ m_graph->setHorizontalAspectRatio(-1.0);
+ m_graph->setReflectivity(-1.0);
+ m_graph->setLocale(QLocale("XX"));
+
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem);
+ QCOMPARE(m_graph->aspectRatio(), -1.0/*2.0*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->horizontalAspectRatio(), -1.0/*0.0*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->reflectivity(), -1.0/*0.5*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->locale(), QLocale("C"));
+}
+
+void tst_scatter::addSeries()
+{
+ m_graph->addSeries(newSeries());
+
+ QCOMPARE(m_graph->seriesList().length(), 1);
+ QVERIFY(!m_graph->selectedSeries());
+}
+
+void tst_scatter::addMultipleSeries()
+{
+ QScatter3DSeries *series = newSeries();
+ QScatter3DSeries *series2 = newSeries();
+ QScatter3DSeries *series3 = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->addSeries(series2);
+ m_graph->addSeries(series3);
+
+ QCOMPARE(m_graph->seriesList().length(), 3);
+}
+
+void tst_scatter::selectSeries()
+{
+ QScatter3DSeries *series = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->seriesList()[0]->setSelectedItem(1);
+
+ QCOMPARE(m_graph->seriesList().length(), 1);
+ QCOMPARE(m_graph->selectedSeries(), series);
+
+ m_graph->clearSelection();
+ QVERIFY(!m_graph->selectedSeries());
+}
+
+void tst_scatter::removeSeries()
+{
+ QScatter3DSeries *series = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->removeSeries(series);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+}
+
+void tst_scatter::removeMultipleSeries()
+{
+ QScatter3DSeries *series = newSeries();
+ QScatter3DSeries *series2 = newSeries();
+ QScatter3DSeries *series3 = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->addSeries(series2);
+ m_graph->addSeries(series3);
+
+ m_graph->seriesList()[0]->setSelectedItem(1);
+ QCOMPARE(m_graph->selectedSeries(), series);
+
+ m_graph->removeSeries(series);
+ QCOMPARE(m_graph->seriesList().length(), 2);
+ QVERIFY(!m_graph->selectedSeries());
+
+ m_graph->removeSeries(series2);
+ QCOMPARE(m_graph->seriesList().length(), 1);
+
+ m_graph->removeSeries(series3);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+}
+
+QTEST_MAIN(tst_scatter)
+#include "tst_scatter.moc"
diff --git a/tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro b/tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro
new file mode 100644
index 00000000..c575a55e
--- /dev/null
+++ b/tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_camera.cpp
diff --git a/tests/auto/cpptest/q3dscene-camera/tst_camera.cpp b/tests/auto/cpptest/q3dscene-camera/tst_camera.cpp
new file mode 100644
index 00000000..ee321b22
--- /dev/null
+++ b/tests/auto/cpptest/q3dscene-camera/tst_camera.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DCamera>
+
+using namespace QtDataVisualization;
+
+class tst_camera: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+ void changePresets();
+
+private:
+ Q3DCamera *m_camera;
+};
+
+void tst_camera::initTestCase()
+{
+}
+
+void tst_camera::cleanupTestCase()
+{
+}
+
+void tst_camera::init()
+{
+ m_camera = new Q3DCamera();
+}
+
+void tst_camera::cleanup()
+{
+ delete m_camera;
+}
+
+void tst_camera::construct()
+{
+ Q3DCamera *camera = new Q3DCamera();
+ QVERIFY(camera);
+ delete camera;
+}
+
+void tst_camera::initialProperties()
+{
+ QVERIFY(m_camera);
+
+ QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetNone);
+ QCOMPARE(m_camera->maxZoomLevel(), 500.0f);
+ QCOMPARE(m_camera->minZoomLevel(), 10.0f);
+ QCOMPARE(m_camera->target(), QVector3D(0.0, 0.0, 0.0));
+ QCOMPARE(m_camera->wrapXRotation(), true);
+ QCOMPARE(m_camera->wrapYRotation(), false);
+ QCOMPARE(m_camera->xRotation(), 0.0f);
+ QCOMPARE(m_camera->yRotation(), 0.0f);
+ QCOMPARE(m_camera->zoomLevel(), 100.0f);
+
+ // Common (from Q3DObject)
+ QVERIFY(!m_camera->parentScene());
+ QCOMPARE(m_camera->position(), QVector3D(0, 0, 0));
+}
+
+void tst_camera::initializeProperties()
+{
+ QVERIFY(m_camera);
+
+ m_camera->setMaxZoomLevel(1000.0f);
+ m_camera->setMinZoomLevel(100.0f);
+ m_camera->setTarget(QVector3D(1.0, -1.0, 1.0));
+ m_camera->setWrapXRotation(false);
+ m_camera->setWrapYRotation(true);
+ m_camera->setXRotation(30.0f);
+ m_camera->setYRotation(30.0f);
+ m_camera->setZoomLevel(500.0f);
+
+ QCOMPARE(m_camera->maxZoomLevel(), 1000.0f);
+ QCOMPARE(m_camera->minZoomLevel(), 100.0f);
+ QCOMPARE(m_camera->target(), QVector3D(1.0, -1.0, 1.0));
+ QCOMPARE(m_camera->wrapXRotation(), false);
+ QCOMPARE(m_camera->wrapYRotation(), true);
+ QCOMPARE(m_camera->xRotation(), 30.0f);
+ QCOMPARE(m_camera->yRotation(), 30.0f);
+ QCOMPARE(m_camera->zoomLevel(), 500.0f);
+
+ m_camera->setPosition(QVector3D(1.0, 1.0, 1.0));
+
+ // Common (from Q3DObject)
+ QCOMPARE(m_camera->position(), QVector3D(1.0, 1.0, 1.0));
+}
+
+void tst_camera::invalidProperties()
+{
+ m_camera->setTarget(QVector3D(-1.5, -1.5, -1.5));
+ QCOMPARE(m_camera->target(), QVector3D(-1.0, -1.0, -1.0));
+
+ m_camera->setTarget(QVector3D(1.5, 1.5, 1.5));
+ QCOMPARE(m_camera->target(), QVector3D(1.0, 1.0, 1.0));
+
+ m_camera->setMinZoomLevel(0.1f);
+ QCOMPARE(m_camera->minZoomLevel(), 1.0f);
+}
+
+void tst_camera::changePresets()
+{
+ m_camera->setCameraPreset(Q3DCamera::CameraPresetBehind); // Will be overridden by the the following sets
+ m_camera->setMaxZoomLevel(1000.0f);
+ m_camera->setMinZoomLevel(100.0f);
+ m_camera->setTarget(QVector3D(1.0, -1.0, 1.0));
+ m_camera->setWrapXRotation(false);
+ m_camera->setWrapYRotation(true);
+ m_camera->setXRotation(30.0f);
+ m_camera->setYRotation(30.0f);
+ m_camera->setZoomLevel(500.0f);
+
+ QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetNone);
+ QCOMPARE(m_camera->maxZoomLevel(), 1000.0f);
+ QCOMPARE(m_camera->minZoomLevel(), 100.0f);
+ QCOMPARE(m_camera->target(), QVector3D(1.0, -1.0, 1.0));
+ QCOMPARE(m_camera->wrapXRotation(), false);
+ QCOMPARE(m_camera->wrapYRotation(), true);
+ QCOMPARE(m_camera->xRotation(), 30.0f);
+ QCOMPARE(m_camera->yRotation(), 30.0f);
+ QCOMPARE(m_camera->zoomLevel(), 500.0f);
+
+ m_camera->setCameraPreset(Q3DCamera::CameraPresetBehind); // Sets target and rotations
+
+ QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetBehind);
+ QCOMPARE(m_camera->maxZoomLevel(), 1000.0f);
+ QCOMPARE(m_camera->minZoomLevel(), 100.0f);
+ QCOMPARE(m_camera->target(), QVector3D(0.0, 0.0, 0.0));
+ QCOMPARE(m_camera->wrapXRotation(), false);
+ QCOMPARE(m_camera->wrapYRotation(), true);
+ QCOMPARE(m_camera->xRotation(), 180.0f);
+ QCOMPARE(m_camera->yRotation(), 22.5f);
+ QCOMPARE(m_camera->zoomLevel(), 500.0f);
+
+ m_camera->setCameraPosition(10.0f, 15.0f, 125.0f); // Overrides preset
+
+ QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetNone);
+ QCOMPARE(m_camera->maxZoomLevel(), 1000.0f);
+ QCOMPARE(m_camera->minZoomLevel(), 100.0f);
+ QCOMPARE(m_camera->target(), QVector3D(0.0, 0.0, 0.0));
+ QCOMPARE(m_camera->wrapXRotation(), false);
+ QCOMPARE(m_camera->wrapYRotation(), true);
+ QCOMPARE(m_camera->xRotation(), 10.0f);
+ QCOMPARE(m_camera->yRotation(), 15.0f);
+ QCOMPARE(m_camera->zoomLevel(), 125.0f);
+}
+
+QTEST_MAIN(tst_camera)
+#include "tst_camera.moc"
diff --git a/tests/auto/cpptest/q3dscene-light/q3dscene-light.pro b/tests/auto/cpptest/q3dscene-light/q3dscene-light.pro
new file mode 100644
index 00000000..21a3c934
--- /dev/null
+++ b/tests/auto/cpptest/q3dscene-light/q3dscene-light.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_light.cpp
diff --git a/tests/auto/cpptest/q3dscene-light/tst_light.cpp b/tests/auto/cpptest/q3dscene-light/tst_light.cpp
new file mode 100644
index 00000000..4568b01e
--- /dev/null
+++ b/tests/auto/cpptest/q3dscene-light/tst_light.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DLight>
+
+using namespace QtDataVisualization;
+
+class tst_light: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ Q3DLight *m_light;
+};
+
+void tst_light::initTestCase()
+{
+}
+
+void tst_light::cleanupTestCase()
+{
+}
+
+void tst_light::init()
+{
+ m_light = new Q3DLight();
+}
+
+void tst_light::cleanup()
+{
+ delete m_light;
+}
+
+void tst_light::construct()
+{
+ Q3DLight *light = new Q3DLight();
+ QVERIFY(light);
+ delete light;
+}
+
+void tst_light::initialProperties()
+{
+ QVERIFY(m_light);
+
+ // TODO: Has no adjustable properties yet.
+ // Keeping this as a placeholder for future implementations (QTRD-2406)
+
+ // Common (from Q3DObject)
+ QVERIFY(!m_light->parentScene());
+ QCOMPARE(m_light->position(), QVector3D(0, 0, 0));
+}
+
+void tst_light::initializeProperties()
+{
+ QVERIFY(m_light);
+
+ m_light->setPosition(QVector3D(1.0, 1.0, 1.0));
+
+ // Common (from Q3DObject)
+ QCOMPARE(m_light->position(), QVector3D(1.0, 1.0, 1.0));
+}
+
+QTEST_MAIN(tst_light)
+#include "tst_light.moc"
diff --git a/tests/auto/cpptest/q3dscene/q3dscene.pro b/tests/auto/cpptest/q3dscene/q3dscene.pro
new file mode 100644
index 00000000..b9be69c0
--- /dev/null
+++ b/tests/auto/cpptest/q3dscene/q3dscene.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_scene.cpp
diff --git a/tests/auto/cpptest/q3dscene/tst_scene.cpp b/tests/auto/cpptest/q3dscene/tst_scene.cpp
new file mode 100644
index 00000000..7d1ecad3
--- /dev/null
+++ b/tests/auto/cpptest/q3dscene/tst_scene.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DScene>
+#include <QtDataVisualization/Q3DBars>
+
+using namespace QtDataVisualization;
+
+class tst_scene: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+ void subViews();
+
+private:
+ Q3DScene *m_scene;
+};
+
+void tst_scene::initTestCase()
+{
+}
+
+void tst_scene::cleanupTestCase()
+{
+}
+
+void tst_scene::init()
+{
+ m_scene = new Q3DScene();
+}
+
+void tst_scene::cleanup()
+{
+ delete m_scene;
+}
+
+void tst_scene::construct()
+{
+ Q3DScene *scene = new Q3DScene();
+ QVERIFY(scene);
+ delete scene;
+}
+
+void tst_scene::initialProperties()
+{
+ QVERIFY(m_scene);
+
+ QVERIFY(m_scene->activeCamera());
+ QVERIFY(m_scene->activeLight());
+ QCOMPARE(m_scene->devicePixelRatio(), 1.0f);
+ QCOMPARE(m_scene->graphPositionQuery(), m_scene->invalidSelectionPoint());
+ QCOMPARE(m_scene->primarySubViewport(), QRect(0, 0, 0, 0));
+ QCOMPARE(m_scene->secondarySubViewport(), QRect(0, 0, 0, 0));
+ QCOMPARE(m_scene->isSecondarySubviewOnTop(), true);
+ QCOMPARE(m_scene->selectionQueryPosition(), m_scene->invalidSelectionPoint());
+ QCOMPARE(m_scene->isSlicingActive(), false);
+ QCOMPARE(m_scene->viewport(), QRect(0, 0, 0, 0));
+}
+
+void tst_scene::initializeProperties()
+{
+ QVERIFY(m_scene);
+
+ Q3DCamera *camera1 = new Q3DCamera();
+ Q3DLight *light1 = new Q3DLight();
+
+ m_scene->setActiveCamera(camera1);
+ m_scene->setActiveLight(light1);
+ m_scene->setDevicePixelRatio(2.0f);
+ m_scene->setGraphPositionQuery(QPoint(0, 0));
+ m_scene->setPrimarySubViewport(QRect(0, 0, 50, 50));
+ m_scene->setSecondarySubViewport(QRect(50, 50, 100, 100));
+ m_scene->setSecondarySubviewOnTop(false);
+ m_scene->setSelectionQueryPosition(QPoint(0, 0));
+ m_scene->setSlicingActive(true);
+
+ QCOMPARE(m_scene->activeCamera(), camera1);
+ QCOMPARE(m_scene->activeLight(), light1);
+ QCOMPARE(m_scene->devicePixelRatio(), 2.0f);
+ QCOMPARE(m_scene->graphPositionQuery(), QPoint(0, 0)); // TODO: When doing signal checks, add tests to check that queries return something (asynchronously)
+ // TODO: subviewports are not set (QTRD-2435)
+ //QCOMPARE(m_scene->primarySubViewport(), QRect(0, 0, 50, 50));
+ //QCOMPARE(m_scene->secondarySubViewport(), QRect(50, 50, 100, 100));
+ QCOMPARE(m_scene->isSecondarySubviewOnTop(), false);
+ QCOMPARE(m_scene->selectionQueryPosition(), QPoint(0, 0)); // TODO: When doing signal checks, add tests to check that queries return something (asynchronously)
+ QCOMPARE(m_scene->isSlicingActive(), true);
+ // TODO: viewport is not set by subviewports (QTRD-2435)
+ //QCOMPARE(m_scene->viewport(), QRect(0, 0, 100, 100));
+}
+
+void tst_scene::invalidProperties()
+{
+ m_scene->setPrimarySubViewport(QRect(0, 0, -50, -50));
+ m_scene->setSecondarySubViewport(QRect(-50, -50, -100, -100));
+ QCOMPARE(m_scene->primarySubViewport(), QRect(0, 0, 0, 0));
+ QCOMPARE(m_scene->secondarySubViewport(), QRect(0, 0, 0, 0));
+}
+
+void tst_scene::subViews()
+{
+ Q3DBars *graph = new Q3DBars();
+ graph->setPosition(QPoint(0, 0));
+ graph->setWidth(200);
+ graph->setHeight(200);
+
+ Q3DScene *scene = graph->scene();
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(scene->viewport(), QRect(0, 0, 200, 200));
+ QCOMPARE(scene->primarySubViewport(), QRect(0, 0, 200, 200));
+ QCOMPARE(scene->secondarySubViewport(), QRect(0, 0, 0, 0));
+
+ QCOMPARE(scene->isSecondarySubviewOnTop(), true);
+ QCOMPARE(scene->isPointInPrimarySubView(QPoint(100, 100)), true);
+ QCOMPARE(scene->isPointInPrimarySubView(QPoint(201, 201)), false);
+ QCOMPARE(scene->isPointInSecondarySubView(QPoint(100, 100)), false);
+
+ scene->setSlicingActive(true);
+
+ QCOMPARE(scene->isSecondarySubviewOnTop(), false);
+ QCOMPARE(scene->primarySubViewport(), QRect(0, 0, 40, 40));
+ QCOMPARE(scene->secondarySubViewport(), QRect(0, 0, 200, 200));
+ QCOMPARE(scene->isPointInPrimarySubView(QPoint(100, 100)), false);
+ QCOMPARE(scene->isPointInPrimarySubView(QPoint(30, 30)), true);
+ QCOMPARE(scene->isPointInSecondarySubView(QPoint(100, 100)), true);
+ QCOMPARE(scene->isPointInSecondarySubView(QPoint(30, 30)), false);
+
+ scene->setSecondarySubviewOnTop(true);
+
+ QCOMPARE(scene->isSecondarySubviewOnTop(), true);
+ QCOMPARE(scene->primarySubViewport(), QRect(0, 0, 40, 40));
+ QCOMPARE(scene->secondarySubViewport(), QRect(0, 0, 200, 200));
+ QCOMPARE(scene->isPointInPrimarySubView(QPoint(100, 100)), false);
+ QCOMPARE(scene->isPointInPrimarySubView(QPoint(30, 30)), false);
+ QCOMPARE(scene->isPointInSecondarySubView(QPoint(100, 100)), true);
+ QCOMPARE(scene->isPointInSecondarySubView(QPoint(30, 30)), true);
+}
+
+QTEST_MAIN(tst_scene)
+#include "tst_scene.moc"
diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpg b/tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpg
new file mode 100644
index 00000000..2580f5bd
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpg
Binary files differ
diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro
new file mode 100644
index 00000000..56a964d0
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro
@@ -0,0 +1,11 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_proxy.cpp
+
+RESOURCES += \
+ q3dsurface-heightproxy.qrc
diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc
new file mode 100644
index 00000000..b83c7ef9
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>customtexture.jpg</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp
new file mode 100644
index 00000000..20ed1aeb
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QHeightMapSurfaceDataProxy>
+
+using namespace QtDataVisualization;
+
+class tst_proxy: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QHeightMapSurfaceDataProxy *m_proxy;
+};
+
+void tst_proxy::initTestCase()
+{
+}
+
+void tst_proxy::cleanupTestCase()
+{
+}
+
+void tst_proxy::init()
+{
+ m_proxy = new QHeightMapSurfaceDataProxy();
+}
+
+void tst_proxy::cleanup()
+{
+ delete m_proxy;
+}
+
+void tst_proxy::construct()
+{
+ QHeightMapSurfaceDataProxy *proxy = new QHeightMapSurfaceDataProxy();
+ QVERIFY(proxy);
+ delete proxy;
+
+ proxy = new QHeightMapSurfaceDataProxy(QImage(QSize(10, 10), QImage::Format_ARGB32));
+ QVERIFY(proxy);
+ QCoreApplication::processEvents();
+ QCOMPARE(proxy->columnCount(), 10);
+ QCOMPARE(proxy->rowCount(), 10);
+ delete proxy;
+
+ proxy = new QHeightMapSurfaceDataProxy(":/customtexture.jpg");
+ QVERIFY(proxy);
+ QCoreApplication::processEvents();
+ QCOMPARE(proxy->columnCount(), 24);
+ QCOMPARE(proxy->rowCount(), 24);
+ delete proxy;
+}
+
+void tst_proxy::initialProperties()
+{
+ QVERIFY(m_proxy);
+
+ QCOMPARE(m_proxy->heightMap(), QImage());
+ QCOMPARE(m_proxy->heightMapFile(), QString(""));
+ QCOMPARE(m_proxy->maxXValue(), 10.0f);
+ QCOMPARE(m_proxy->maxZValue(), 10.0f);
+ QCOMPARE(m_proxy->minXValue(), 0.0f);
+ QCOMPARE(m_proxy->minZValue(), 0.0f);
+
+ QCOMPARE(m_proxy->columnCount(), 0);
+ QCOMPARE(m_proxy->rowCount(), 0);
+ QVERIFY(!m_proxy->series());
+
+ QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeSurface);
+}
+
+void tst_proxy::initializeProperties()
+{
+ QVERIFY(m_proxy);
+
+ m_proxy->setHeightMapFile(":/customtexture.jpg");
+ m_proxy->setMaxXValue(11.0f);
+ m_proxy->setMaxZValue(11.0f);
+ m_proxy->setMinXValue(-10.0f);
+ m_proxy->setMinZValue(-10.0f);
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_proxy->heightMapFile(), QString(":/customtexture.jpg"));
+ QCOMPARE(m_proxy->maxXValue(), 11.0f);
+ QCOMPARE(m_proxy->maxZValue(), 11.0f);
+ QCOMPARE(m_proxy->minXValue(), -10.0f);
+ QCOMPARE(m_proxy->minZValue(), -10.0f);
+
+ QCOMPARE(m_proxy->columnCount(), 24);
+ QCOMPARE(m_proxy->rowCount(), 24);
+
+ m_proxy->setHeightMapFile("");
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_proxy->columnCount(), 0);
+ QCOMPARE(m_proxy->rowCount(), 0);
+
+ m_proxy->setHeightMap(QImage(":/customtexture.jpg"));
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_proxy->columnCount(), 24);
+ QCOMPARE(m_proxy->rowCount(), 24);
+}
+
+void tst_proxy::invalidProperties()
+{
+ m_proxy->setMaxXValue(-10.0f);
+ m_proxy->setMaxZValue(-10.0f);
+ QCOMPARE(m_proxy->maxXValue(), -10.0f);
+ QCOMPARE(m_proxy->maxZValue(), -10.0f);
+ QCOMPARE(m_proxy->minXValue(), -11.0f);
+ QCOMPARE(m_proxy->minZValue(), -11.0f);
+
+ m_proxy->setMinXValue(10.0f);
+ m_proxy->setMinZValue(10.0f);
+ QCOMPARE(m_proxy->maxXValue(), 11.0f);
+ QCOMPARE(m_proxy->maxZValue(), 11.0f);
+ QCOMPARE(m_proxy->minXValue(), 10.0f);
+ QCOMPARE(m_proxy->minZValue(), 10.0f);
+}
+
+QTEST_MAIN(tst_proxy)
+#include "tst_proxy.moc"
diff --git a/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro b/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro
new file mode 100644
index 00000000..c383ec25
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization widgets
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_proxy.cpp
diff --git a/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp
new file mode 100644
index 00000000..6bef9478
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp
@@ -0,0 +1,283 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QItemModelSurfaceDataProxy>
+#include <QtDataVisualization/Q3DSurface>
+#include <QtWidgets/QTableWidget>
+
+using namespace QtDataVisualization;
+
+class tst_proxy: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+ void multiMatch();
+
+private:
+ QItemModelSurfaceDataProxy *m_proxy;
+};
+
+void tst_proxy::initTestCase()
+{
+}
+
+void tst_proxy::cleanupTestCase()
+{
+}
+
+void tst_proxy::init()
+{
+ m_proxy = new QItemModelSurfaceDataProxy();
+}
+
+void tst_proxy::cleanup()
+{
+ delete m_proxy;
+}
+
+
+void tst_proxy::construct()
+{
+ QItemModelSurfaceDataProxy *proxy = new QItemModelSurfaceDataProxy();
+ QVERIFY(proxy);
+ delete proxy;
+
+ QTableWidget *table = new QTableWidget();
+
+ proxy = new QItemModelSurfaceDataProxy(table->model());
+ QVERIFY(proxy);
+ delete proxy;
+
+ proxy = new QItemModelSurfaceDataProxy(table->model(), "y");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString(""));
+ QCOMPARE(proxy->columnRole(), QString(""));
+ QCOMPARE(proxy->xPosRole(), QString(""));
+ QCOMPARE(proxy->yPosRole(), QString("y"));
+ QCOMPARE(proxy->zPosRole(), QString(""));
+ QCOMPARE(proxy->rowCategories().length(), 0);
+ QCOMPARE(proxy->columnCategories().length(), 0);
+ delete proxy;
+
+ proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "y");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("column"));
+ QCOMPARE(proxy->xPosRole(), QString("column"));
+ QCOMPARE(proxy->yPosRole(), QString("y"));
+ QCOMPARE(proxy->zPosRole(), QString("row"));
+ QCOMPARE(proxy->rowCategories().length(), 0);
+ QCOMPARE(proxy->columnCategories().length(), 0);
+ delete proxy;
+
+ proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "x", "y", "z");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("column"));
+ QCOMPARE(proxy->xPosRole(), QString("x"));
+ QCOMPARE(proxy->yPosRole(), QString("y"));
+ QCOMPARE(proxy->zPosRole(), QString("z"));
+ QCOMPARE(proxy->rowCategories().length(), 0);
+ QCOMPARE(proxy->columnCategories().length(), 0);
+ delete proxy;
+
+ proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "y",
+ QStringList() << "rowCat", QStringList() << "colCat");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("column"));
+ QCOMPARE(proxy->xPosRole(), QString("column"));
+ QCOMPARE(proxy->yPosRole(), QString("y"));
+ QCOMPARE(proxy->zPosRole(), QString("row"));
+ QCOMPARE(proxy->rowCategories().length(), 1);
+ QCOMPARE(proxy->columnCategories().length(), 1);
+ delete proxy;
+
+ proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "x", "y", "z",
+ QStringList() << "rowCat", QStringList() << "colCat");
+ QVERIFY(proxy);
+ QCOMPARE(proxy->rowRole(), QString("row"));
+ QCOMPARE(proxy->columnRole(), QString("column"));
+ QCOMPARE(proxy->xPosRole(), QString("x"));
+ QCOMPARE(proxy->yPosRole(), QString("y"));
+ QCOMPARE(proxy->zPosRole(), QString("z"));
+ QCOMPARE(proxy->rowCategories().length(), 1);
+ QCOMPARE(proxy->columnCategories().length(), 1);
+ delete proxy;
+}
+
+void tst_proxy::initialProperties()
+{
+ QVERIFY(m_proxy);
+
+ QCOMPARE(m_proxy->autoColumnCategories(), true);
+ QCOMPARE(m_proxy->autoRowCategories(), true);
+ QCOMPARE(m_proxy->columnCategories(), QStringList());
+ QCOMPARE(m_proxy->columnRole(), QString());
+ QCOMPARE(m_proxy->columnRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->columnRoleReplace(), QString());
+ QVERIFY(!m_proxy->itemModel());
+ QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelSurfaceDataProxy::MMBLast);
+ QCOMPARE(m_proxy->rowCategories(), QStringList());
+ QCOMPARE(m_proxy->rowRole(), QString());
+ QCOMPARE(m_proxy->rowRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->rowRoleReplace(), QString());
+ QCOMPARE(m_proxy->useModelCategories(), false);
+ QCOMPARE(m_proxy->xPosRole(), QString());
+ QCOMPARE(m_proxy->xPosRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->xPosRoleReplace(), QString());
+ QCOMPARE(m_proxy->yPosRole(), QString());
+ QCOMPARE(m_proxy->yPosRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->yPosRoleReplace(), QString());
+ QCOMPARE(m_proxy->zPosRole(), QString());
+ QCOMPARE(m_proxy->zPosRolePattern(), QRegExp());
+ QCOMPARE(m_proxy->zPosRoleReplace(), QString());
+
+ QCOMPARE(m_proxy->columnCount(), 0);
+ QCOMPARE(m_proxy->rowCount(), 0);
+ QVERIFY(!m_proxy->series());
+
+ QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeSurface);
+}
+
+void tst_proxy::initializeProperties()
+{
+ QVERIFY(m_proxy);
+
+ QTableWidget *table = new QTableWidget();
+
+ m_proxy->setAutoColumnCategories(false);
+ m_proxy->setAutoRowCategories(false);
+ m_proxy->setColumnCategories(QStringList() << "col1" << "col2");
+ m_proxy->setColumnRole("column");
+ m_proxy->setColumnRolePattern(QRegExp("/^.*-(\\d\\d)$/"));
+ m_proxy->setColumnRoleReplace("\\\\1");
+ m_proxy->setItemModel(table->model());
+ m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBAverage);
+ m_proxy->setRowCategories(QStringList() << "row1" << "row2");
+ m_proxy->setRowRole("row");
+ m_proxy->setRowRolePattern(QRegExp("/^(\\d\\d\\d\\d).*$/"));
+ m_proxy->setRowRoleReplace("\\\\1");
+ m_proxy->setUseModelCategories(true);
+ m_proxy->setXPosRole("X");
+ m_proxy->setXPosRolePattern(QRegExp("/-/"));
+ m_proxy->setXPosRoleReplace("\\\\1");
+ m_proxy->setYPosRole("Y");
+ m_proxy->setYPosRolePattern(QRegExp("/-/"));
+ m_proxy->setYPosRoleReplace("\\\\1");
+ m_proxy->setZPosRole("Z");
+ m_proxy->setZPosRolePattern(QRegExp("/-/"));
+ m_proxy->setZPosRoleReplace("\\\\1");
+
+ QCOMPARE(m_proxy->autoColumnCategories(), false);
+ QCOMPARE(m_proxy->autoRowCategories(), false);
+ QCOMPARE(m_proxy->columnCategories().count(), 2);
+ QCOMPARE(m_proxy->columnRole(), QString("column"));
+ QCOMPARE(m_proxy->columnRolePattern(), QRegExp("/^.*-(\\d\\d)$/"));
+ QCOMPARE(m_proxy->columnRoleReplace(), QString("\\\\1"));
+ QVERIFY(m_proxy->itemModel());
+ QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelSurfaceDataProxy::MMBAverage);
+ QCOMPARE(m_proxy->rowCategories().count(), 2);
+ QCOMPARE(m_proxy->rowRole(), QString("row"));
+ QCOMPARE(m_proxy->rowRolePattern(), QRegExp("/^(\\d\\d\\d\\d).*$/"));
+ QCOMPARE(m_proxy->rowRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->useModelCategories(), true);
+ QCOMPARE(m_proxy->xPosRole(), QString("X"));
+ QCOMPARE(m_proxy->xPosRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->xPosRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->yPosRole(), QString("Y"));
+ QCOMPARE(m_proxy->yPosRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->yPosRoleReplace(), QString("\\\\1"));
+ QCOMPARE(m_proxy->zPosRole(), QString("Z"));
+ QCOMPARE(m_proxy->zPosRolePattern(), QRegExp("/-/"));
+ QCOMPARE(m_proxy->zPosRoleReplace(), QString("\\\\1"));
+}
+
+void tst_proxy::multiMatch()
+{
+ Q3DSurface *graph = new Q3DSurface();
+
+ QTableWidget *table = new QTableWidget();
+ QStringList rows;
+ rows << "row 1" << "row 2";
+ QStringList columns;
+ columns << "col 1" << "col 2" << "col 3" << "col 4";
+ const char *values[4][2] = {{"0/0/5.5/30", "0/0/10.5/30"},
+ {"0/1/5.5/30", "0/1/0.5/30"},
+ {"1/0/5.5/30", "1/0/0.5/30"},
+ {"1/1/0.0/30", "1/1/0.0/30"}};
+
+ table->setRowCount(2);
+ table->setColumnCount(4);
+
+ for (int col = 0; col < columns.size(); col++) {
+ for (int row = 0; row < rows.size(); row++) {
+ QModelIndex index = table->model()->index(col, row);
+ table->model()->setData(index, values[col][row]);
+ }
+ }
+
+ m_proxy->setItemModel(table->model());
+ m_proxy->setRowRole(table->model()->roleNames().value(Qt::DisplayRole));
+ m_proxy->setColumnRole(table->model()->roleNames().value(Qt::DisplayRole));
+ m_proxy->setRowRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setRowRoleReplace(QStringLiteral("\\2"));
+ m_proxy->setYPosRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setYPosRoleReplace(QStringLiteral("\\3"));
+ m_proxy->setColumnRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ m_proxy->setColumnRoleReplace(QStringLiteral("\\1"));
+
+ QSurface3DSeries *series = new QSurface3DSeries(m_proxy);
+
+ graph->addSeries(series);
+
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->axisY()->max(), 10.5f);
+ m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBFirst);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->axisY()->max(), 5.5f);
+ m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBLast);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->axisY()->max(), 10.5f);
+ m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBAverage);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->axisY()->max(), 8.0f);
+ m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBCumulativeY);
+ QCoreApplication::processEvents();
+ QCOMPARE(graph->axisY()->max(), 16.0f);
+
+ QCOMPARE(m_proxy->columnCount(), 2);
+ QCOMPARE(m_proxy->rowCount(), 3);
+ QVERIFY(m_proxy->series());
+}
+
+QTEST_MAIN(tst_proxy)
+#include "tst_proxy.moc"
diff --git a/tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro b/tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro
new file mode 100644
index 00000000..b0b5d361
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_proxy.cpp
diff --git a/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp
new file mode 100644
index 00000000..4274899d
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QSurfaceDataProxy>
+
+using namespace QtDataVisualization;
+
+class tst_proxy: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+
+private:
+ QSurfaceDataProxy *m_proxy;
+};
+
+void tst_proxy::initTestCase()
+{
+}
+
+void tst_proxy::cleanupTestCase()
+{
+}
+
+void tst_proxy::init()
+{
+ m_proxy = new QSurfaceDataProxy();
+}
+
+void tst_proxy::cleanup()
+{
+ delete m_proxy;
+}
+
+void tst_proxy::construct()
+{
+ QSurfaceDataProxy *proxy = new QSurfaceDataProxy();
+ QVERIFY(proxy);
+ delete proxy;
+}
+
+void tst_proxy::initialProperties()
+{
+ QVERIFY(m_proxy);
+
+ QCOMPARE(m_proxy->columnCount(), 0);
+ QCOMPARE(m_proxy->rowCount(), 0);
+ QVERIFY(!m_proxy->series());
+
+ QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeSurface);
+}
+
+void tst_proxy::initializeProperties()
+{
+ QVERIFY(m_proxy);
+
+ QSurfaceDataArray *data = new QSurfaceDataArray;
+ QSurfaceDataRow *dataRow1 = new QSurfaceDataRow;
+ QSurfaceDataRow *dataRow2 = new QSurfaceDataRow;
+ *dataRow1 << QVector3D(0.0f, 0.1f, 0.5f) << QVector3D(1.0f, 0.5f, 0.5f);
+ *dataRow2 << QVector3D(0.0f, 1.8f, 1.0f) << QVector3D(1.0f, 1.2f, 1.0f);
+ *data << dataRow1 << dataRow2;
+
+ m_proxy->resetArray(data);
+
+ QCOMPARE(m_proxy->columnCount(), 2);
+ QCOMPARE(m_proxy->rowCount(), 2);
+}
+
+QTEST_MAIN(tst_proxy)
+#include "tst_proxy.moc"
diff --git a/tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro b/tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro
new file mode 100644
index 00000000..481653ef
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_series.cpp
diff --git a/tests/auto/cpptest/q3dsurface-series/tst_series.cpp b/tests/auto/cpptest/q3dsurface-series/tst_series.cpp
new file mode 100644
index 00000000..50eed686
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface-series/tst_series.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/QSurface3DSeries>
+
+using namespace QtDataVisualization;
+
+class tst_series: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ QSurface3DSeries *m_series;
+};
+
+void tst_series::initTestCase()
+{
+}
+
+void tst_series::cleanupTestCase()
+{
+}
+
+void tst_series::init()
+{
+ m_series = new QSurface3DSeries();
+}
+
+void tst_series::cleanup()
+{
+ delete m_series;
+}
+
+void tst_series::construct()
+{
+ QSurface3DSeries *series = new QSurface3DSeries();
+ QVERIFY(series);
+ delete series;
+
+ QSurfaceDataProxy *proxy = new QSurfaceDataProxy();
+
+ series = new QSurface3DSeries(proxy);
+ QVERIFY(series);
+ QCOMPARE(series->dataProxy(), proxy);
+ delete series;
+}
+
+void tst_series::initialProperties()
+{
+ QVERIFY(m_series);
+
+ QVERIFY(m_series->dataProxy());
+ QCOMPARE(m_series->drawMode(), QSurface3DSeries::DrawSurfaceAndWireframe);
+ QCOMPARE(m_series->isFlatShadingEnabled(), true);
+ QCOMPARE(m_series->isFlatShadingSupported(), true);
+ QCOMPARE(m_series->selectedPoint(), m_series->invalidSelectionPosition());
+
+ // Common properties. The ones identical between different series are tested in QBar3DSeries tests
+ QCOMPARE(m_series->itemLabelFormat(), QString("@xLabel, @yLabel, @zLabel"));
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshSphere);
+ QCOMPARE(m_series->type(), QAbstract3DSeries::SeriesTypeSurface);
+}
+
+void tst_series::initializeProperties()
+{
+ QVERIFY(m_series);
+
+ m_series->setDataProxy(new QSurfaceDataProxy());
+ m_series->setDrawMode(QSurface3DSeries::DrawWireframe);
+ m_series->setFlatShadingEnabled(false);
+ m_series->setSelectedPoint(QPoint(0, 0));
+
+ QCOMPARE(m_series->drawMode(), QSurface3DSeries::DrawWireframe);
+ QCOMPARE(m_series->isFlatShadingEnabled(), false);
+ QCOMPARE(m_series->selectedPoint(), QPoint(0, 0));
+
+ // Common properties. The ones identical between different series are tested in QBar3DSeries tests
+ m_series->setMesh(QAbstract3DSeries::MeshPyramid);
+
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshPyramid);
+}
+
+void tst_series::invalidProperties()
+{
+ m_series->setMesh(QAbstract3DSeries::MeshPoint);
+
+ QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshSphere);
+}
+
+QTEST_MAIN(tst_series)
+#include "tst_series.moc"
diff --git a/tests/auto/cpptest/q3dsurface/q3dsurface.pro b/tests/auto/cpptest/q3dsurface/q3dsurface.pro
new file mode 100644
index 00000000..b7a6bf08
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface/q3dsurface.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_surface.cpp
diff --git a/tests/auto/cpptest/q3dsurface/tst_surface.cpp b/tests/auto/cpptest/q3dsurface/tst_surface.cpp
new file mode 100644
index 00000000..0ae0a326
--- /dev/null
+++ b/tests/auto/cpptest/q3dsurface/tst_surface.cpp
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DSurface>
+
+using namespace QtDataVisualization;
+
+class tst_surface: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+ void addSeries();
+ void addMultipleSeries();
+ void selectSeries();
+ void removeSeries();
+ void removeMultipleSeries();
+
+private:
+ Q3DSurface *m_graph;
+};
+
+QSurface3DSeries *newSeries()
+{
+ QSurface3DSeries *series = new QSurface3DSeries;
+ QSurfaceDataArray *data = new QSurfaceDataArray;
+ QSurfaceDataRow *dataRow1 = new QSurfaceDataRow;
+ QSurfaceDataRow *dataRow2 = new QSurfaceDataRow;
+ *dataRow1 << QVector3D(0.0f, 0.1f, 0.5f) << QVector3D(1.0f, 0.5f, 0.5f);
+ *dataRow2 << QVector3D(0.0f, 1.8f, 1.0f) << QVector3D(1.0f, 1.2f, 1.0f);
+ *data << dataRow1 << dataRow2;
+ series->dataProxy()->resetArray(data);
+
+ return series;
+}
+
+void tst_surface::initTestCase()
+{
+}
+
+void tst_surface::cleanupTestCase()
+{
+}
+
+void tst_surface::init()
+{
+ m_graph = new Q3DSurface();
+}
+
+void tst_surface::cleanup()
+{
+ delete m_graph;
+}
+
+void tst_surface::construct()
+{
+ Q3DSurface *graph = new Q3DSurface();
+ QVERIFY(graph);
+ delete graph;
+
+ graph = new Q3DSurface(new QSurfaceFormat());
+ QVERIFY(graph);
+ delete graph;
+}
+
+void tst_surface::initialProperties()
+{
+ QVERIFY(m_graph);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+ QVERIFY(!m_graph->selectedSeries());
+ QCOMPARE(m_graph->flipHorizontalGrid(), false);
+ QCOMPARE(m_graph->axisX()->orientation(), QAbstract3DAxis::AxisOrientationX);
+ QCOMPARE(m_graph->axisY()->orientation(), QAbstract3DAxis::AxisOrientationY);
+ QCOMPARE(m_graph->axisZ()->orientation(), QAbstract3DAxis::AxisOrientationZ);
+
+ // Common properties
+ QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt);
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium);
+ QVERIFY(m_graph->scene());
+ QCOMPARE(m_graph->measureFps(), false);
+ QCOMPARE(m_graph->isOrthoProjection(), false);
+ QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone);
+ QCOMPARE(m_graph->aspectRatio(), 2.0);
+ QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationDefault);
+ QCOMPARE(m_graph->isPolar(), false);
+ QCOMPARE(m_graph->radialLabelOffset(), 1.0);
+ QCOMPARE(m_graph->horizontalAspectRatio(), 0.0);
+ QCOMPARE(m_graph->isReflection(), false);
+ QCOMPARE(m_graph->reflectivity(), 0.5);
+ QCOMPARE(m_graph->locale(), QLocale("C"));
+ QCOMPARE(m_graph->queriedGraphPosition(), QVector3D(0, 0, 0));
+ QCOMPARE(m_graph->margin(), -1.0);
+}
+
+void tst_surface::initializeProperties()
+{
+ m_graph->setFlipHorizontalGrid(true);
+
+ QCOMPARE(m_graph->flipHorizontalGrid(), true);
+
+ Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia);
+ m_graph->setActiveTheme(theme);
+ m_graph->setSelectionMode(QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftHigh);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualitySoftHigh);
+ m_graph->setMeasureFps(true);
+ m_graph->setOrthoProjection(true);
+ m_graph->setAspectRatio(1.0);
+ m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic);
+ m_graph->setPolar(true);
+ m_graph->setRadialLabelOffset(0.1f);
+ m_graph->setHorizontalAspectRatio(1.0);
+ m_graph->setReflection(true);
+ m_graph->setReflectivity(0.1);
+ m_graph->setLocale(QLocale("FI"));
+ m_graph->setMargin(1.0);
+
+ QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeDigia);
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice);
+ QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityNone); // Ortho disables shadows
+ QCOMPARE(m_graph->measureFps(), true);
+ QCOMPARE(m_graph->isOrthoProjection(), true);
+ QCOMPARE(m_graph->aspectRatio(), 1.0);
+ QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationStatic);
+ QCOMPARE(m_graph->isPolar(), true);
+ QCOMPARE(m_graph->radialLabelOffset(), 0.1f);
+ QCOMPARE(m_graph->horizontalAspectRatio(), 1.0);
+ QCOMPARE(m_graph->isReflection(), true);
+ QCOMPARE(m_graph->reflectivity(), 0.1);
+ QCOMPARE(m_graph->locale(), QLocale("FI"));
+ QCOMPARE(m_graph->margin(), 1.0);
+}
+
+void tst_surface::invalidProperties()
+{
+ m_graph->setSelectionMode(QAbstract3DGraph::SelectionColumn | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice);
+ m_graph->setAspectRatio(-1.0);
+ m_graph->setHorizontalAspectRatio(-1.0);
+ m_graph->setReflectivity(-1.0);
+ m_graph->setLocale(QLocale("XX"));
+
+ QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem);
+ QCOMPARE(m_graph->aspectRatio(), -1.0/*2.0*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->horizontalAspectRatio(), -1.0/*0.0*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->reflectivity(), -1.0/*0.5*/); // TODO: Fix once QTRD-3367 is done
+ QCOMPARE(m_graph->locale(), QLocale("C"));
+}
+
+void tst_surface::addSeries()
+{
+ m_graph->addSeries(newSeries());
+
+ QCOMPARE(m_graph->seriesList().length(), 1);
+ QVERIFY(!m_graph->selectedSeries());
+}
+
+void tst_surface::addMultipleSeries()
+{
+ QSurface3DSeries *series = newSeries();
+ QSurface3DSeries *series2 = newSeries();
+ QSurface3DSeries *series3 = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->addSeries(series2);
+ m_graph->addSeries(series3);
+
+ QCOMPARE(m_graph->seriesList().length(), 3);
+}
+
+void tst_surface::selectSeries()
+{
+ QSurface3DSeries *series = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->seriesList()[0]->setSelectedPoint(QPoint(0, 0));
+
+ QCOMPARE(m_graph->seriesList().length(), 1);
+ QCOMPARE(m_graph->selectedSeries(), series);
+
+ m_graph->clearSelection();
+ QVERIFY(!m_graph->selectedSeries());
+}
+
+void tst_surface::removeSeries()
+{
+ QSurface3DSeries *series = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->removeSeries(series);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+}
+
+void tst_surface::removeMultipleSeries()
+{
+ QSurface3DSeries *series = newSeries();
+ QSurface3DSeries *series2 = newSeries();
+ QSurface3DSeries *series3 = newSeries();
+
+ m_graph->addSeries(series);
+ m_graph->addSeries(series2);
+ m_graph->addSeries(series3);
+
+ m_graph->seriesList()[0]->setSelectedPoint(QPoint(0, 0));
+ QCOMPARE(m_graph->selectedSeries(), series);
+
+ m_graph->removeSeries(series);
+ QCOMPARE(m_graph->seriesList().length(), 2);
+ QVERIFY(!m_graph->selectedSeries());
+
+ m_graph->removeSeries(series2);
+ QCOMPARE(m_graph->seriesList().length(), 1);
+
+ m_graph->removeSeries(series3);
+ QCOMPARE(m_graph->seriesList().length(), 0);
+}
+
+QTEST_MAIN(tst_surface)
+#include "tst_surface.moc"
diff --git a/tests/auto/cpptest/q3dtheme/q3dtheme.pro b/tests/auto/cpptest/q3dtheme/q3dtheme.pro
new file mode 100644
index 00000000..30a4802c
--- /dev/null
+++ b/tests/auto/cpptest/q3dtheme/q3dtheme.pro
@@ -0,0 +1,8 @@
+QT += testlib datavisualization
+
+TARGET = tst_cpptest
+CONFIG += console testcase
+
+TEMPLATE = app
+
+SOURCES += tst_theme.cpp
diff --git a/tests/auto/cpptest/q3dtheme/tst_theme.cpp b/tests/auto/cpptest/q3dtheme/tst_theme.cpp
new file mode 100644
index 00000000..35945aef
--- /dev/null
+++ b/tests/auto/cpptest/q3dtheme/tst_theme.cpp
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDataVisualization/Q3DTheme>
+
+using namespace QtDataVisualization;
+
+class tst_theme: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void construct();
+
+ void initialProperties();
+ void initializeProperties();
+ void invalidProperties();
+
+private:
+ Q3DTheme *m_theme;
+};
+
+void tst_theme::initTestCase()
+{
+}
+
+void tst_theme::cleanupTestCase()
+{
+}
+
+void tst_theme::init()
+{
+ m_theme = new Q3DTheme();
+}
+
+void tst_theme::cleanup()
+{
+ delete m_theme;
+}
+
+void tst_theme::construct()
+{
+ Q3DTheme *theme = new Q3DTheme();
+ QVERIFY(theme);
+ delete theme;
+
+ theme = new Q3DTheme(Q3DTheme::ThemeEbony);
+ QVERIFY(theme);
+ QCOMPARE(theme->ambientLightStrength(), 0.5f);
+ QCOMPARE(theme->backgroundColor(), QColor(Qt::black));
+ QCOMPARE(theme->isBackgroundEnabled(), true);
+ QCOMPARE(theme->baseColors().length(), 5);
+ QCOMPARE(theme->baseColors().at(0), QColor(Qt::white));
+ QCOMPARE(theme->baseColors().at(4), QColor(QRgb(0x6b6b6b)));
+ QCOMPARE(theme->baseGradients().length(), 5);
+ QCOMPARE(theme->baseGradients().at(0).stops().at(1).second, QColor(Qt::white));
+ QCOMPARE(theme->baseGradients().at(4).stops().at(1).second, QColor(QRgb(0x6b6b6b)));
+ QCOMPARE(theme->colorStyle(), Q3DTheme::ColorStyleUniform);
+ QCOMPARE(theme->font(), QFont("Arial"));
+ QCOMPARE(theme->isGridEnabled(), true);
+ QCOMPARE(theme->gridLineColor(), QColor(QRgb(0x35322f)));
+ QCOMPARE(theme->highlightLightStrength(), 5.0f);
+ QCOMPARE(theme->labelBackgroundColor(), QColor(0x00, 0x00, 0x00, 0xcd));
+ QCOMPARE(theme->isLabelBackgroundEnabled(), true);
+ QCOMPARE(theme->isLabelBorderEnabled(), false);
+ QCOMPARE(theme->labelTextColor(), QColor(QRgb(0xaeadac)));
+ QCOMPARE(theme->lightColor(), QColor(Qt::white));
+ QCOMPARE(theme->lightStrength(), 5.0f);
+ QCOMPARE(theme->multiHighlightColor(), QColor(QRgb(0xd72222)));
+ QCOMPARE(theme->multiHighlightGradient().stops().at(1).second, QColor(QRgb(0xd72222)));
+ QCOMPARE(theme->singleHighlightColor(), QColor(QRgb(0xf5dc0d)));
+ QCOMPARE(theme->singleHighlightGradient().stops().at(1).second, QColor(QRgb(0xf5dc0d)));
+ QCOMPARE(theme->type(), Q3DTheme::ThemeEbony);
+ QCOMPARE(theme->windowColor(), QColor(Qt::black));
+ delete theme;
+}
+
+void tst_theme::initialProperties()
+{
+ QVERIFY(m_theme);
+
+ QCOMPARE(m_theme->ambientLightStrength(), 0.25f);
+ QCOMPARE(m_theme->backgroundColor(), QColor(Qt::black));
+ QCOMPARE(m_theme->isBackgroundEnabled(), true);
+ QCOMPARE(m_theme->baseColors().length(), 1);
+ QCOMPARE(m_theme->baseColors().at(0), QColor(Qt::black));
+ QCOMPARE(m_theme->baseGradients().length(), 1);
+ QCOMPARE(m_theme->baseGradients().at(0).stops().at(0).second, QColor(Qt::black));
+ QCOMPARE(m_theme->baseGradients().at(0).stops().at(1).second, QColor(Qt::white));
+ QCOMPARE(m_theme->colorStyle(), Q3DTheme::ColorStyleUniform);
+ QCOMPARE(m_theme->font(), QFont());
+ QCOMPARE(m_theme->isGridEnabled(), true);
+ QCOMPARE(m_theme->gridLineColor(), QColor(Qt::white));
+ QCOMPARE(m_theme->highlightLightStrength(), 7.5f);
+ QCOMPARE(m_theme->labelBackgroundColor(), QColor(Qt::gray));
+ QCOMPARE(m_theme->isLabelBackgroundEnabled(), true);
+ QCOMPARE(m_theme->isLabelBorderEnabled(), true);
+ QCOMPARE(m_theme->labelTextColor(), QColor(Qt::white));
+ QCOMPARE(m_theme->lightColor(), QColor(Qt::white));
+ QCOMPARE(m_theme->lightStrength(), 5.0f);
+ QCOMPARE(m_theme->multiHighlightColor(), QColor(Qt::blue));
+ QCOMPARE(m_theme->multiHighlightGradient().stops(), QLinearGradient().stops());
+ QCOMPARE(m_theme->singleHighlightColor(), QColor(Qt::red));
+ QCOMPARE(m_theme->singleHighlightGradient().stops(), QLinearGradient().stops());
+ QCOMPARE(m_theme->type(), Q3DTheme::ThemeUserDefined);
+ QCOMPARE(m_theme->windowColor(), QColor(Qt::black));
+}
+
+void tst_theme::initializeProperties()
+{
+ QVERIFY(m_theme);
+
+ QLinearGradient gradient1;
+ QLinearGradient gradient2;
+ QLinearGradient gradient3;
+ QLinearGradient gradient4;
+
+ QList<QColor> basecolors;
+ basecolors << QColor(Qt::red) << QColor(Qt::blue);
+
+ QList<QLinearGradient> basegradients;
+ basegradients << gradient1 << gradient2;
+
+ m_theme->setType(Q3DTheme::ThemeQt); // We'll override default values with the following setters
+ m_theme->setAmbientLightStrength(0.3f);
+ m_theme->setBackgroundColor(QColor(Qt::red));
+ m_theme->setBackgroundEnabled(false);
+ m_theme->setBaseColors(basecolors);
+ m_theme->setBaseGradients(basegradients);
+ m_theme->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
+ m_theme->setFont(QFont("Arial"));
+ m_theme->setGridEnabled(false);
+ m_theme->setGridLineColor(QColor(Qt::green));
+ m_theme->setHighlightLightStrength(5.0f);
+ m_theme->setLabelBackgroundColor(QColor(Qt::gray));
+ m_theme->setLabelBackgroundEnabled(false);
+ m_theme->setLabelBorderEnabled(false);
+ m_theme->setLabelTextColor(QColor(Qt::cyan));
+ m_theme->setLightColor(QColor(Qt::yellow));
+ m_theme->setLightStrength(2.5f);
+ m_theme->setMultiHighlightColor(QColor(Qt::darkBlue));
+ m_theme->setMultiHighlightGradient(gradient3);
+ m_theme->setSingleHighlightColor(QColor(Qt::darkRed));
+ m_theme->setSingleHighlightGradient(gradient4);
+ m_theme->setWindowColor(QColor(Qt::darkYellow));
+
+ QCOMPARE(m_theme->ambientLightStrength(), 0.3f);
+ QCOMPARE(m_theme->backgroundColor(), QColor(Qt::red));
+ QCOMPARE(m_theme->isBackgroundEnabled(), false);
+ QCOMPARE(m_theme->baseColors().length(), 2);
+ QCOMPARE(m_theme->baseColors().at(0), QColor(Qt::red));
+ QCOMPARE(m_theme->baseColors().at(1), QColor(Qt::blue));
+ QCOMPARE(m_theme->baseGradients().length(), 2);
+ QCOMPARE(m_theme->baseGradients().at(0), gradient1);
+ QCOMPARE(m_theme->baseGradients().at(0), gradient2);
+ QCOMPARE(m_theme->colorStyle(), Q3DTheme::ColorStyleRangeGradient);
+ QCOMPARE(m_theme->font(), QFont("Arial"));
+ QCOMPARE(m_theme->isGridEnabled(), false);
+ QCOMPARE(m_theme->gridLineColor(), QColor(Qt::green));
+ QCOMPARE(m_theme->highlightLightStrength(), 5.0f);
+ QCOMPARE(m_theme->labelBackgroundColor(), QColor(Qt::gray));
+ QCOMPARE(m_theme->isLabelBackgroundEnabled(), false);
+ QCOMPARE(m_theme->isLabelBorderEnabled(), false);
+ QCOMPARE(m_theme->labelTextColor(), QColor(Qt::cyan));
+ QCOMPARE(m_theme->lightColor(), QColor(Qt::yellow));
+ QCOMPARE(m_theme->lightStrength(), 2.5f);
+ QCOMPARE(m_theme->multiHighlightColor(), QColor(Qt::darkBlue));
+ QCOMPARE(m_theme->multiHighlightGradient(), gradient3);
+ QCOMPARE(m_theme->singleHighlightColor(), QColor(Qt::darkRed));
+ QCOMPARE(m_theme->singleHighlightGradient(), gradient4);
+ QCOMPARE(m_theme->type(), Q3DTheme::ThemeQt);
+ QCOMPARE(m_theme->windowColor(), QColor(Qt::darkYellow));
+}
+
+void tst_theme::invalidProperties()
+{
+ m_theme->setAmbientLightStrength(-1.0f);
+ QCOMPARE(m_theme->ambientLightStrength(), 0.25f);
+ m_theme->setAmbientLightStrength(1.1f);
+ QCOMPARE(m_theme->ambientLightStrength(), 0.25f);
+
+ m_theme->setHighlightLightStrength(-1.0f);
+ QCOMPARE(m_theme->highlightLightStrength(), 7.5f);
+ m_theme->setHighlightLightStrength(10.1f);
+ QCOMPARE(m_theme->highlightLightStrength(), 7.5f);
+
+ m_theme->setLightStrength(-1.0f);
+ QCOMPARE(m_theme->lightStrength(), 5.0f);
+ m_theme->setLightStrength(10.1f);
+ QCOMPARE(m_theme->lightStrength(), 5.0f);
+}
+
+QTEST_MAIN(tst_theme)
+#include "tst_theme.moc"
diff --git a/tests/auto/qmltest/axis3d/tst_category.qml b/tests/auto/qmltest/axis3d/tst_category.qml
new file mode 100644
index 00000000..318fa011
--- /dev/null
+++ b/tests/auto/qmltest/axis3d/tst_category.qml
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ CategoryAxis3D {
+ id: initial
+ }
+
+ CategoryAxis3D {
+ id: initialized
+ labels: ["first", "second"]
+
+ autoAdjustRange: false
+ labelAutoRotation: 10.0
+ max: 20
+ min: 10
+ title: "initialized"
+ titleFixed: false
+ titleVisible: true
+ }
+
+ CategoryAxis3D {
+ id: change
+ }
+
+ CategoryAxis3D {
+ id: invalid
+ }
+
+ TestCase {
+ name: "CategoryAxis3D Initial"
+
+ function test_initial() {
+ compare(initial.labels.length, 0)
+
+ compare(initial.autoAdjustRange, true)
+ compare(initial.labelAutoRotation, 0.0)
+ compare(initial.max, 10)
+ compare(initial.min, 0)
+ compare(initial.orientation, AbstractAxis3D.AxisOrientationNone)
+ compare(initial.title, "")
+ compare(initial.titleFixed, true)
+ compare(initial.titleVisible, false)
+ compare(initial.type, AbstractAxis3D.AxisTypeCategory)
+ }
+ }
+
+ TestCase {
+ name: "CategoryAxis3D Initialized"
+
+ function test_initialized() {
+ compare(initialized.labels.length, 2)
+ compare(initialized.labels[0], "first")
+ compare(initialized.labels[1], "second")
+
+ compare(initialized.autoAdjustRange, false)
+ compare(initialized.labelAutoRotation, 10.0)
+ compare(initialized.max, 20)
+ compare(initialized.min, 10)
+ compare(initialized.title, "initialized")
+ compare(initialized.titleFixed, false)
+ compare(initialized.titleVisible, true)
+ }
+ }
+
+ TestCase {
+ name: "CategoryAxis3D Change"
+
+ function test_change() {
+ change.labels = ["first"]
+ compare(change.labels.length, 1)
+ compare(change.labels[0], "first")
+ change.labels = ["first", "second"]
+ compare(change.labels.length, 2)
+ compare(change.labels[0], "first")
+ compare(change.labels[1], "second")
+ change.labels[1] = "another"
+ compare(change.labels[1], "another")
+
+ change.autoAdjustRange = false
+ change.labelAutoRotation = 10.0
+ change.max = 20
+ change.min = 10
+ change.title = "initialized"
+ change.titleFixed = false
+ change.titleVisible = true
+
+ compare(change.autoAdjustRange, false)
+ compare(change.labelAutoRotation, 10.0)
+ compare(change.max, 20)
+ compare(change.min, 10)
+ compare(change.title, "initialized")
+ compare(change.titleFixed, false)
+ compare(change.titleVisible, true)
+ }
+ }
+
+ TestCase {
+ name: "CategoryAxis3D Invalid"
+
+ function test_invalid() {
+ invalid.labelAutoRotation = -10
+ compare(invalid.labelAutoRotation, 0.0)
+ invalid.labelAutoRotation = 100
+ compare(invalid.labelAutoRotation, 90.0)
+ invalid.max = -10
+ compare(invalid.min, 0)
+ invalid.min = 10
+ compare(invalid.max, 11)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/axis3d/tst_logvalue.qml b/tests/auto/qmltest/axis3d/tst_logvalue.qml
new file mode 100644
index 00000000..89228ad1
--- /dev/null
+++ b/tests/auto/qmltest/axis3d/tst_logvalue.qml
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ LogValueAxis3DFormatter {
+ id: initial
+ }
+
+ LogValueAxis3DFormatter {
+ id: initialized
+ autoSubGrid: false
+ base: 0.1
+ showEdgeLabels: false
+ }
+
+ LogValueAxis3DFormatter {
+ id: change
+ }
+
+ LogValueAxis3DFormatter {
+ id: invalid
+ }
+
+ TestCase {
+ name: "LogValueAxis3DFormatter Initial"
+
+ function test_initial() {
+ compare(initial.autoSubGrid, true)
+ compare(initial.base, 10)
+ compare(initial.showEdgeLabels, true)
+ }
+ }
+
+ TestCase {
+ name: "LogValueAxis3DFormatter Initialized"
+
+ function test_initialized() {
+ compare(initialized.autoSubGrid, false)
+ compare(initialized.base, 0.1)
+ compare(initialized.showEdgeLabels, false)
+ }
+ }
+
+ TestCase {
+ name: "LogValueAxis3DFormatter Change"
+
+ function test_change() {
+ change.autoSubGrid = false
+ change.base = 0.1
+ change.showEdgeLabels = false
+
+ compare(change.autoSubGrid, false)
+ compare(change.base, 0.1)
+ compare(change.showEdgeLabels, false)
+ }
+ }
+
+ TestCase {
+ name: "LogValueAxis3DFormatter Invalid"
+
+ function test_invalid() {
+ invalid.base = 1
+ compare(invalid.base, 10)
+ invalid.base = -1
+ compare(invalid.base, 10)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/axis3d/tst_value.qml b/tests/auto/qmltest/axis3d/tst_value.qml
new file mode 100644
index 00000000..54189c8b
--- /dev/null
+++ b/tests/auto/qmltest/axis3d/tst_value.qml
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ ValueAxis3D {
+ id: initial
+ }
+
+ ValueAxis3D {
+ id: initialized
+ formatter: ValueAxis3DFormatter { objectName: "formatter1" }
+ labelFormat: "%f"
+ reversed: true
+ segmentCount: 10
+ subSegmentCount: 5
+
+ autoAdjustRange: false
+ labelAutoRotation: 10.0
+ max: 20
+ min: -10
+ title: "initialized"
+ titleFixed: false
+ titleVisible: true
+ }
+
+ ValueAxis3D {
+ id: change
+ }
+
+ ValueAxis3D {
+ id: invalid
+ }
+
+ TestCase {
+ name: "ValueAxis3D Initial"
+
+ function test_initial() {
+ verify(initial.formatter)
+ compare(initial.labelFormat, "%.2f")
+ compare(initial.reversed, false)
+ compare(initial.segmentCount, 5)
+ compare(initial.subSegmentCount, 1)
+
+ compare(initial.autoAdjustRange, true)
+ compare(initial.labelAutoRotation, 0.0)
+ compare(initial.max, 10)
+ compare(initial.min, 0)
+ compare(initial.orientation, AbstractAxis3D.AxisOrientationNone)
+ compare(initial.title, "")
+ compare(initial.titleFixed, true)
+ compare(initial.titleVisible, false)
+ compare(initial.type, AbstractAxis3D.AxisTypeValue)
+ }
+ }
+
+ TestCase {
+ name: "ValueAxis3D Initialized"
+
+ function test_initialized() {
+ compare(initialized.formatter.objectName, "formatter1")
+ compare(initialized.labelFormat, "%f")
+ compare(initialized.reversed, true)
+ compare(initialized.segmentCount, 10)
+ compare(initialized.subSegmentCount, 5)
+
+ compare(initialized.autoAdjustRange, false)
+ compare(initialized.labelAutoRotation, 10.0)
+ compare(initialized.max, 20)
+ compare(initialized.min, -10)
+ compare(initialized.title, "initialized")
+ compare(initialized.titleFixed, false)
+ compare(initialized.titleVisible, true)
+ }
+ }
+
+ TestCase {
+ name: "ValueAxis3D Change"
+
+ ValueAxis3DFormatter { id: formatter1 }
+
+ function test_change() {
+ change.formatter = formatter1
+ change.labelFormat = "%f"
+ change.reversed = true
+ change.segmentCount = 10
+ change.subSegmentCount = 5
+
+ compare(change.formatter, formatter1)
+ compare(change.labelFormat, "%f")
+ compare(change.reversed, true)
+ compare(change.segmentCount, 10)
+ compare(change.subSegmentCount, 5)
+
+ change.autoAdjustRange = false
+ change.labelAutoRotation = 10.0
+ change.max = 20
+ change.min = -10
+ change.title = "initialized"
+ change.titleFixed = false
+ change.titleVisible = true
+
+ compare(change.autoAdjustRange, false)
+ compare(change.labelAutoRotation, 10.0)
+ compare(change.max, 20)
+ compare(change.min, -10)
+ compare(change.title, "initialized")
+ compare(change.titleFixed, false)
+ compare(change.titleVisible, true)
+ }
+ }
+
+ TestCase {
+ name: "ValueAxis3D Invalid"
+
+ function test_invalid() {
+ invalid.segmentCount = -1
+ compare(invalid.segmentCount, 1)
+ invalid.subSegmentCount = -1
+ compare(invalid.subSegmentCount, 1)
+
+ invalid.labelAutoRotation = -10
+ compare(invalid.labelAutoRotation, 0.0)
+ invalid.labelAutoRotation = 100
+ compare(invalid.labelAutoRotation, 90.0)
+ invalid.max = -10
+ compare(invalid.min, -11)
+ invalid.min = 10
+ compare(invalid.max, 11)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/bars3d/tst_bars.qml b/tests/auto/qmltest/bars3d/tst_bars.qml
new file mode 100644
index 00000000..a64aaf1a
--- /dev/null
+++ b/tests/auto/qmltest/bars3d/tst_bars.qml
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Bars3D {
+ id: series
+ anchors.fill: parent
+ }
+
+ TestCase {
+ name: "Bars3D Series"
+
+ Bar3DSeries { id: series1 }
+ Bar3DSeries { id: series2 }
+
+ function test_1_add_series() {
+ series.seriesList = [series1, series2]
+ compare(series.seriesList.length, 2)
+ }
+
+ function test_2_remove_series() {
+ series.seriesList = [series1]
+ compare(series.seriesList.length, 1)
+ }
+
+ function test_3_remove_series() {
+ series.seriesList = []
+ compare(series.seriesList.length, 0)
+ }
+
+ function test_4_primary_series() {
+ series.seriesList = [series1, series2]
+ compare(series.primarySeries, series1)
+ series.primarySeries = series2
+ compare(series.primarySeries, series2)
+ }
+
+ function test_5_selected_series() {
+ series.seriesList[0].selectedBar = Qt.point(0, 0)
+ compare(series.selectedSeries, series1)
+ }
+ }
+
+ // The following tests are not required for scatter or surface, as they are handled identically
+ Bars3D {
+ id: theme
+ anchors.fill: parent
+ }
+
+ Bars3D {
+ id: input
+ anchors.fill: parent
+ }
+
+ Custom3DItem { id: item1; meshFile: ":/customitem.obj" }
+ Custom3DItem { id: item2; meshFile: ":/customitem.obj" }
+ Custom3DItem { id: item3; meshFile: ":/customitem.obj" }
+ Custom3DItem { id: item4; meshFile: ":/customitem.obj"; position: Qt.vector3d(0.0, 1.0, 0.0) }
+
+ Bars3D {
+ id: custom
+ anchors.fill: parent
+ customItemList: [item1, item2]
+ }
+
+ TestCase {
+ name: "Bars3D Theme"
+ when: windowShown
+
+ Theme3D { id: newTheme }
+
+ function test_1_add_theme() {
+ theme.theme = newTheme
+ compare(theme.theme, newTheme)
+ }
+
+ function test_2_change_theme() {
+ newTheme.type = Theme3D.ThemePrimaryColors
+ compare(theme.theme.type, Theme3D.ThemePrimaryColors)
+ }
+ }
+
+ TestCase {
+ name: "Bars3D Input"
+ when: windowShown
+
+ function test_1_remove_input() {
+ input.inputHandler = null
+ compare(input.inputHandler, null)
+ }
+ }
+
+ TestCase {
+ name: "Bars3D Custom"
+ when: windowShown
+
+ function test_1_custom_items() {
+ compare(custom.customItemList.length, 2)
+ }
+
+ function test_2_add_custom_items() {
+ custom.addCustomItem(item3)
+ compare(custom.customItemList.length, 3)
+ custom.addCustomItem(item4)
+ compare(custom.customItemList.length, 4)
+ }
+
+ function test_3_change_custom_items() {
+ item1.position = Qt.vector3d(1.0, 1.0, 1.0)
+ compare(custom.customItemList[0].position, Qt.vector3d(1.0, 1.0, 1.0))
+ }
+
+ function test_4_remove_custom_items() {
+ custom.removeCustomItemAt(Qt.vector3d(0.0, 1.0, 0.0))
+ compare(custom.customItemList.length, 3)
+ custom.releaseCustomItem(item1)
+ compare(custom.customItemList[0], item2)
+ custom.releaseCustomItem(item2)
+ compare(custom.customItemList.length, 1)
+ custom.removeCustomItems()
+ compare(custom.customItemList.length, 0)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/bars3d/tst_barseries.qml b/tests/auto/qmltest/bars3d/tst_barseries.qml
new file mode 100644
index 00000000..7e303ab0
--- /dev/null
+++ b/tests/auto/qmltest/bars3d/tst_barseries.qml
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Bar3DSeries {
+ id: initial
+ }
+
+ ColorGradient {
+ id: gradient1;
+ stops: [
+ ColorGradientStop { color: "red"; position: 0 },
+ ColorGradientStop { color: "blue"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient2;
+ stops: [
+ ColorGradientStop { color: "green"; position: 0 },
+ ColorGradientStop { color: "red"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient3;
+ stops: [
+ ColorGradientStop { color: "gray"; position: 0 },
+ ColorGradientStop { color: "darkgray"; position: 1 }
+ ]
+ }
+
+ Bar3DSeries {
+ id: initialized
+ dataProxy: ItemModelBarDataProxy {
+ itemModel: ListModel {
+ ListElement{ year: "2012"; city: "Oulu"; expenses: "4200"; }
+ ListElement{ year: "2012"; city: "Rauma"; expenses: "2100"; }
+ }
+ rowRole: "city"
+ columnRole: "year"
+ valueRole: "expenses"
+ }
+ meshAngle: 15.0
+ selectedBar: Qt.point(0, 0)
+
+ baseColor: "blue"
+ baseGradient: gradient1
+ colorStyle: Theme3D.ColorStyleObjectGradient
+ itemLabelFormat: "%f"
+ itemLabelVisible: false
+ mesh: Abstract3DSeries.MeshCone
+ meshSmooth: true
+ multiHighlightColor: "green"
+ multiHighlightGradient: gradient2
+ name: "series1"
+ singleHighlightColor: "red"
+ singleHighlightGradient: gradient3
+ userDefinedMesh: ":/customitem.obj"
+ visible: false
+ }
+
+ ItemModelBarDataProxy {
+ id: proxy1
+ itemModel: ListModel {
+ ListElement{ year: "2012"; city: "Oulu"; expenses: "4200"; }
+ ListElement{ year: "2012"; city: "Rauma"; expenses: "2100"; }
+ ListElement{ year: "2012"; city: "Helsinki"; expenses: "7040"; }
+ }
+ rowRole: "city"
+ columnRole: "year"
+ valueRole: "expenses"
+ }
+
+ Bar3DSeries {
+ id: change
+ }
+
+ TestCase {
+ name: "Bar3DSeries Initial"
+
+ function test_1_initial() {
+ compare(initial.dataProxy.rowCount, 0)
+ compare(initial.invalidSelectionPosition, Qt.point(-1, -1))
+ compare(initial.meshAngle, 0)
+ compare(initial.selectedBar, Qt.point(-1, -1))
+ }
+
+ function test_2_initial_common() {
+ // Common properties
+ compare(initial.baseColor, "#000000")
+ compare(initial.baseGradient, null)
+ compare(initial.colorStyle, Theme3D.ColorStyleUniform)
+ compare(initial.itemLabel, "")
+ compare(initial.itemLabelFormat, "@valueLabel")
+ compare(initial.itemLabelVisible, true)
+ compare(initial.mesh, Abstract3DSeries.MeshBevelBar)
+ compare(initial.meshRotation, Qt.quaternion(1, 0, 0, 0))
+ compare(initial.meshSmooth, false)
+ compare(initial.multiHighlightColor, "#000000")
+ compare(initial.multiHighlightGradient, null)
+ compare(initial.name, "")
+ compare(initial.singleHighlightColor, "#000000")
+ compare(initial.singleHighlightGradient, null)
+ compare(initial.type, Abstract3DSeries.SeriesTypeBar)
+ compare(initial.userDefinedMesh, "")
+ compare(initial.visible, true)
+ }
+ }
+
+ TestCase {
+ name: "Bar3DSeries Initialized"
+
+ function test_1_initialized() {
+ compare(initialized.dataProxy.rowCount, 2)
+ fuzzyCompare(initialized.meshAngle, 15.0, 0.01)
+ compare(initialized.selectedBar, Qt.point(0, 0))
+ }
+
+ function test_2_initialized_common() {
+ // Common properties
+ compare(initialized.baseColor, "#0000ff")
+ compare(initialized.baseGradient, gradient1)
+ compare(initialized.colorStyle, Theme3D.ColorStyleObjectGradient)
+ compare(initialized.itemLabelFormat, "%f")
+ compare(initialized.itemLabelVisible, false)
+ compare(initialized.mesh, Abstract3DSeries.MeshCone)
+ compare(initialized.meshSmooth, true)
+ compare(initialized.multiHighlightColor, "#008000")
+ compare(initialized.multiHighlightGradient, gradient2)
+ compare(initialized.name, "series1")
+ compare(initialized.singleHighlightColor, "#ff0000")
+ compare(initialized.singleHighlightGradient, gradient3)
+ compare(initialized.userDefinedMesh, ":/customitem.obj")
+ compare(initialized.visible, false)
+ }
+ }
+
+ TestCase {
+ name: "Bar3DSeries Change"
+
+ function test_1_change() {
+ change.dataProxy = proxy1
+ change.meshAngle = 15.0
+ change.selectedBar = Qt.point(0, 0)
+ }
+
+ function test_2_test_change() {
+ // This test has a dependency to the previous one due to asynchronous item model resolving
+ compare(change.dataProxy.rowCount, 3)
+ fuzzyCompare(change.meshAngle, 15.0, 0.01)
+ compare(change.selectedBar, Qt.point(0, 0))
+ }
+
+ function test_3_change_common() {
+ change.baseColor = "blue"
+ change.baseGradient = gradient1
+ change.colorStyle = Theme3D.ColorStyleObjectGradient
+ change.itemLabelFormat = "%f"
+ change.itemLabelVisible = false
+ change.mesh = Abstract3DSeries.MeshCone
+ change.meshSmooth = true
+ change.multiHighlightColor = "green"
+ change.multiHighlightGradient = gradient2
+ change.name = "series1"
+ change.singleHighlightColor = "red"
+ change.singleHighlightGradient = gradient3
+ change.userDefinedMesh = ":/customitem.obj"
+ change.visible = false
+
+ compare(change.baseColor, "#0000ff")
+ compare(change.baseGradient, gradient1)
+ compare(change.colorStyle, Theme3D.ColorStyleObjectGradient)
+ compare(change.itemLabelFormat, "%f")
+ compare(change.itemLabelVisible, false)
+ compare(change.mesh, Abstract3DSeries.MeshCone)
+ compare(change.meshSmooth, true)
+ compare(change.multiHighlightColor, "#008000")
+ compare(change.multiHighlightGradient, gradient2)
+ compare(change.name, "series1")
+ compare(change.singleHighlightColor, "#ff0000")
+ compare(change.singleHighlightGradient, gradient3)
+ compare(change.userDefinedMesh, ":/customitem.obj")
+ compare(change.visible, false)
+ }
+
+ function test_4_change_gradient_stop() {
+ gradient1.stops[0].color = "yellow"
+ compare(change.baseGradient.stops[0].color, "#ffff00")
+ }
+ }
+}
diff --git a/tests/auto/qmltest/bars3d/tst_basic.qml b/tests/auto/qmltest/bars3d/tst_basic.qml
new file mode 100644
index 00000000..bf27ae8c
--- /dev/null
+++ b/tests/auto/qmltest/bars3d/tst_basic.qml
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Bars3D {
+ id: empty
+ }
+
+ Bars3D {
+ id: basic
+ anchors.fill: parent
+ multiSeriesUniform: true
+ barThickness: 0.1
+ barSpacing.width: 0.1
+ barSpacing.height: 0.1
+ barSpacingRelative: false
+ floorLevel: 1.0
+ }
+
+ Bars3D {
+ id: common
+ anchors.fill: parent
+ }
+
+ Bars3D {
+ id: common_init
+ anchors.fill: parent
+ selectionMode: AbstractGraph3D.SelectionNone
+ shadowQuality: AbstractGraph3D.ShadowQualityLow
+ msaaSamples: 2
+ theme: Theme3D { }
+ renderingMode: AbstractGraph3D.RenderIndirect
+ measureFps: true
+ orthoProjection: false
+ aspectRatio: 3.0
+ optimizationHints: AbstractGraph3D.OptimizationStatic
+ polar: false
+ radialLabelOffset: 2
+ horizontalAspectRatio: 0.2
+ reflection: true
+ reflectivity: 0.1
+ locale: Qt.locale("UK")
+ margin: 0.2
+ }
+
+ TestCase {
+ name: "Bars3D Empty"
+
+ function test_empty() {
+ compare(empty.width, 0, "width")
+ compare(empty.height, 0, "height")
+ compare(empty.multiSeriesUniform, false, "multiSeriesUniform")
+ compare(empty.barThickness, 1.0, "barThickness")
+ compare(empty.barSpacing, Qt.size(0.2, 0.2), "barSpacing")
+ compare(empty.barSpacingRelative, true, "barSpacingRelative")
+ compare(empty.seriesList.length, 0, "seriesList")
+ compare(empty.selectedSeries, null, "selectedSeries")
+ compare(empty.primarySeries, null, "primarySeries")
+ compare(empty.floorLevel, 0.0, "floorLevel")
+ compare(empty.columnAxis.orientation, AbstractAxis3D.AxisOrientationX)
+ compare(empty.rowAxis.orientation, AbstractAxis3D.AxisOrientationZ)
+ compare(empty.valueAxis.orientation, AbstractAxis3D.AxisOrientationY)
+ compare(empty.columnAxis.type, AbstractAxis3D.AxisTypeCategory)
+ compare(empty.rowAxis.type, AbstractAxis3D.AxisTypeCategory)
+ compare(empty.valueAxis.type, AbstractAxis3D.AxisTypeValue)
+ }
+ }
+
+ TestCase {
+ name: "Bars3D Basic"
+ when: windowShown
+
+ function test_basic() {
+ compare(basic.width, 150, "width")
+ compare(basic.height, 150, "height")
+ compare(basic.multiSeriesUniform, true, "multiSeriesUniform")
+ compare(basic.barThickness, 0.1, "barThickness")
+ compare(basic.barSpacing, Qt.size(0.1, 0.1), "barSpacing")
+ compare(basic.barSpacingRelative, false, "barSpacingRelative")
+ compare(basic.floorLevel, 1.0, "floorLevel")
+ }
+
+ function test_change_basic() {
+ basic.multiSeriesUniform = false
+ basic.barThickness = 0.5
+ basic.barSpacing = Qt.size(1.0, 0.0)
+ basic.barSpacingRelative = true
+ basic.floorLevel = 0.2
+ compare(basic.multiSeriesUniform, false, "multiSeriesUniform")
+ compare(basic.barThickness, 0.5, "barThickness")
+ compare(basic.barSpacing, Qt.size(1.0, 0.0), "barSpacing")
+ compare(basic.barSpacingRelative, true, "barSpacingRelative")
+ compare(basic.floorLevel, 0.2, "floorLevel")
+ }
+
+ function test_change_invalid_basic() {
+ basic.barThickness = -1
+ basic.barSpacing = Qt.size(-1.0, -1.0)
+ compare(basic.barThickness, -1/*0.5*/, "barThickness") // TODO: Fix once QTRD-3367 is done
+ compare(basic.barSpacing, Qt.size(1.0, 0.0), "barSpacing")
+ }
+ }
+
+ TestCase {
+ name: "Bars3D Common"
+ when: windowShown
+
+ function test_1_common() {
+ compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode")
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality")
+ if (common.shadowsSupported === true)
+ compare(common.msaaSamples, 4, "msaaSamples")
+ else
+ compare(common.msaaSamples, 0, "msaaSamples")
+ compare(common.theme.type, Theme3D.ThemeQt, "theme")
+ compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode")
+ compare(common.measureFps, false, "measureFps")
+ compare(common.customItemList.length, 0, "customItemList")
+ compare(common.orthoProjection, false, "orthoProjection")
+ compare(common.selectedElement, AbstractGraph3D.ElementNone, "selectedElement")
+ compare(common.aspectRatio, 2.0, "aspectRatio")
+ compare(common.optimizationHints, AbstractGraph3D.OptimizationDefault, "optimizationHints")
+ compare(common.polar, false, "polar")
+ compare(common.radialLabelOffset, 1, "radialLabelOffset")
+ compare(common.horizontalAspectRatio, 0, "horizontalAspectRatio")
+ compare(common.reflection, false, "reflection")
+ compare(common.reflectivity, 0.5, "reflectivity")
+ compare(common.locale, Qt.locale("C"), "locale")
+ compare(common.queriedGraphPosition, Qt.vector3d(0, 0, 0), "queriedGraphPosition")
+ compare(common.margin, -1, "margin")
+ }
+
+ function test_2_change_common() {
+ common.selectionMode = AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice
+ common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality")
+ common.msaaSamples = 8
+ if (common.shadowsSupported === true)
+ compare(common.msaaSamples, 8, "msaaSamples")
+ else
+ compare(common.msaaSamples, 0, "msaaSamples")
+ common.theme.type = Theme3D.ThemeRetro
+ common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear
+ common.measureFps = true
+ common.orthoProjection = true
+ common.aspectRatio = 1.0
+ common.optimizationHints = AbstractGraph3D.OptimizationStatic
+ common.polar = true
+ common.radialLabelOffset = 2
+ common.horizontalAspectRatio = 1
+ common.reflection = true
+ common.reflectivity = 1.0
+ common.locale = Qt.locale("FI")
+ common.margin = 1.0
+ compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode")
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") // Ortho disables shadows
+ compare(common.msaaSamples, 0, "msaaSamples") // Rendering mode changes this to zero
+ compare(common.theme.type, Theme3D.ThemeRetro, "theme")
+ compare(common.renderingMode, AbstractGraph3D.RenderDirectToBackground_NoClear, "renderingMode")
+ compare(common.measureFps, true, "measureFps")
+ compare(common.orthoProjection, true, "orthoProjection")
+ compare(common.aspectRatio, 1.0, "aspectRatio")
+ compare(common.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints")
+ compare(common.polar, true, "polar")
+ compare(common.radialLabelOffset, 2, "radialLabelOffset")
+ compare(common.horizontalAspectRatio, 1, "horizontalAspectRatio")
+ compare(common.reflection, true, "reflection")
+ compare(common.reflectivity, 1.0, "reflectivity")
+ compare(common.locale, Qt.locale("FI"), "locale")
+ compare(common.margin, 1.0, "margin")
+ }
+
+ function test_3_change_invalid_common() {
+ common.selectionMode = AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionColumn | AbstractGraph3D.SelectionSlice
+ common.theme.type = -2
+ common.renderingMode = -1
+ common.measureFps = false
+ common.orthoProjection = false
+ common.aspectRatio = -1.0
+ common.polar = false
+ common.horizontalAspectRatio = -2
+ common.reflection = false
+ common.reflectivity = -1.0
+ compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode")
+ compare(common.theme.type, -2/*Theme3D.ThemeRetro*/, "theme") // TODO: Fix once QTRD-3367 is done
+ compare(common.renderingMode, -1/*AbstractGraph3D.RenderDirectToBackground_NoClear*/, "renderingMode") // TODO: Fix once QTRD-3367 is done
+ compare(common.aspectRatio, -1.0/*1.0*/, "aspectRatio") // TODO: Fix once QTRD-3367 is done
+ compare(common.horizontalAspectRatio, -2/*1*/, "horizontalAspectRatio") // TODO: Fix once QTRD-3367 is done
+ compare(common.reflectivity, -1.0/*1.0*/, "reflectivity") // TODO: Fix once QTRD-3367 is done
+ }
+
+ function test_4_common_initialized() {
+ compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode")
+ if (common_init.shadowsSupported === true) {
+ compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality")
+ compare(common_init.msaaSamples, 2, "msaaSamples")
+ } else {
+ compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality")
+ compare(common_init.msaaSamples, 0, "msaaSamples")
+ }
+ compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme")
+ compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode")
+ compare(common_init.measureFps, true, "measureFps")
+ compare(common_init.customItemList.length, 0, "customItemList")
+ compare(common_init.orthoProjection, false, "orthoProjection")
+ compare(common_init.aspectRatio, 3.0, "aspectRatio")
+ compare(common_init.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints")
+ compare(common_init.polar, false, "polar")
+ compare(common_init.radialLabelOffset, 2, "radialLabelOffset")
+ compare(common_init.horizontalAspectRatio, 0.2, "horizontalAspectRatio")
+ compare(common_init.reflection, true, "reflection")
+ compare(common_init.reflectivity, 0.1, "reflectivity")
+ compare(common_init.locale, Qt.locale("UK"), "locale")
+ compare(common_init.margin, 0.2, "margin")
+ }
+ }
+}
diff --git a/tests/auto/qmltest/bars3d/tst_proxy.qml b/tests/auto/qmltest/bars3d/tst_proxy.qml
new file mode 100644
index 00000000..8d91c055
--- /dev/null
+++ b/tests/auto/qmltest/bars3d/tst_proxy.qml
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ ItemModelBarDataProxy {
+ id: initial
+ }
+
+ ItemModelBarDataProxy {
+ id: initialized
+
+ autoColumnCategories: false
+ autoRowCategories: false
+ columnCategories: ["colcat1", "colcat2"]
+ columnRole: "col"
+ columnRolePattern: /^.*-(\d\d)$/
+ columnRoleReplace: "\\1"
+ itemModel: ListModel { objectName: "model1" }
+ multiMatchBehavior: ItemModelBarDataProxy.MMBAverage
+ rotationRole: "rot"
+ rotationRolePattern: /-/
+ rotationRoleReplace: "\\1"
+ rowCategories: ["rowcat1", "rowcat2"]
+ rowRole: "row"
+ rowRolePattern: /^(\d\d\d\d).*$/
+ rowRoleReplace: "\\1"
+ valueRole: "val"
+ valueRolePattern: /-/
+ valueRoleReplace: "\\1"
+
+ columnLabels: ["col1", "col2"]
+ rowLabels: ["row1", "row2"]
+ }
+
+ ItemModelBarDataProxy {
+ id: change
+ }
+
+ TestCase {
+ name: "ItemModelBarDataProxy Initial"
+
+ function test_initial() {
+ compare(initial.autoColumnCategories, true)
+ compare(initial.autoRowCategories, true)
+ compare(initial.columnCategories, [])
+ compare(initial.columnRole, "")
+ verify(initial.columnRolePattern)
+ compare(initial.columnRoleReplace, "")
+ verify(!initial.itemModel)
+ compare(initial.multiMatchBehavior, ItemModelBarDataProxy.MMBLast)
+ compare(initial.rotationRole, "")
+ verify(initial.rotationRolePattern)
+ compare(initial.rotationRoleReplace, "")
+ compare(initial.rowCategories, [])
+ compare(initial.rowRole, "")
+ verify(initial.rowRolePattern)
+ compare(initial.rowRoleReplace, "")
+ compare(initial.useModelCategories, false)
+ compare(initial.valueRole, "")
+ verify(initial.valueRolePattern)
+ compare(initial.valueRoleReplace, "")
+
+ compare(initial.columnLabels.length, 0)
+ compare(initial.rowCount, 0)
+ compare(initial.rowLabels.length, 0)
+ verify(!initial.series)
+
+ compare(initial.type, AbstractDataProxy.DataTypeBar)
+ }
+ }
+
+ TestCase {
+ name: "ItemModelBarDataProxy Initialized"
+
+ function test_initialized() {
+ compare(initialized.autoColumnCategories, false)
+ compare(initialized.autoRowCategories, false)
+ compare(initialized.columnCategories.length, 2)
+ compare(initialized.columnCategories[0], "colcat1")
+ compare(initialized.columnCategories[1], "colcat2")
+ compare(initialized.columnRole, "col")
+ compare(initialized.columnRolePattern, /^.*-(\d\d)$/)
+ compare(initialized.columnRoleReplace, "\\1")
+ compare(initialized.itemModel.objectName, "model1")
+ compare(initialized.multiMatchBehavior, ItemModelBarDataProxy.MMBAverage)
+ compare(initialized.rotationRole, "rot")
+ compare(initialized.rotationRolePattern, /-/)
+ compare(initialized.rotationRoleReplace, "\\1")
+ compare(initialized.rowCategories.length, 2)
+ compare(initialized.rowCategories[0], "rowcat1")
+ compare(initialized.rowCategories[1], "rowcat2")
+ compare(initialized.rowRole, "row")
+ compare(initialized.rowRolePattern, /^(\d\d\d\d).*$/)
+ compare(initialized.rowRoleReplace, "\\1")
+ compare(initialized.valueRole, "val")
+ compare(initialized.valueRolePattern, /-/)
+ compare(initialized.valueRoleReplace, "\\1")
+
+ compare(initialized.columnLabels.length, 2)
+ compare(initialized.rowCount, 2)
+ compare(initialized.rowLabels.length, 2)
+ }
+ }
+
+ TestCase {
+ name: "ItemModelBarDataProxy Change"
+
+ ListModel { id: model1; objectName: "model1" }
+
+ function test_1_change() {
+ change.autoColumnCategories = false
+ change.autoRowCategories = false
+ change.columnCategories = ["colcat1", "colcat2"]
+ change.columnRole = "col"
+ change.columnRolePattern = /^.*-(\d\d)$/
+ change.columnRoleReplace = "\\1"
+ change.itemModel = model1
+ change.multiMatchBehavior = ItemModelBarDataProxy.MMBAverage
+ change.rotationRole = "rot"
+ change.rotationRolePattern = /-/
+ change.rotationRoleReplace = "\\1"
+ change.rowCategories = ["rowcat1", "rowcat2"]
+ change.rowRole = "row"
+ change.rowRolePattern = /^(\d\d\d\d).*$/
+ change.rowRoleReplace = "\\1"
+ change.useModelCategories = true // Overwrites columnLabels and rowLabels
+ change.valueRole = "val"
+ change.valueRolePattern = /-/
+ change.valueRoleReplace = "\\1"
+
+ change.columnLabels = ["col1", "col2"]
+ change.rowLabels = ["row1", "row2"]
+ }
+
+ function test_2_test_change() {
+ // This test has a dependency to the previous one due to asynchronous item model resolving
+ compare(change.autoColumnCategories, false)
+ compare(change.autoRowCategories, false)
+ compare(change.columnCategories.length, 2)
+ compare(change.columnCategories[0], "colcat1")
+ compare(change.columnCategories[1], "colcat2")
+ compare(change.columnRole, "col")
+ compare(change.columnRolePattern, /^.*-(\d\d)$/)
+ compare(change.columnRoleReplace, "\\1")
+ compare(change.itemModel.objectName, "model1")
+ compare(change.multiMatchBehavior, ItemModelBarDataProxy.MMBAverage)
+ compare(change.rotationRole, "rot")
+ compare(change.rotationRolePattern, /-/)
+ compare(change.rotationRoleReplace, "\\1")
+ compare(change.rowCategories.length, 2)
+ compare(change.rowCategories[0], "rowcat1")
+ compare(change.rowCategories[1], "rowcat2")
+ compare(change.rowRole, "row")
+ compare(change.rowRolePattern, /^(\d\d\d\d).*$/)
+ compare(change.rowRoleReplace, "\\1")
+ compare(change.useModelCategories, true)
+ compare(change.valueRole, "val")
+ compare(change.valueRolePattern, /-/)
+ compare(change.valueRoleReplace, "\\1")
+
+ compare(change.columnLabels.length, 1)
+ compare(change.rowCount, 0)
+ compare(change.rowLabels.length, 0)
+ }
+ }
+
+ TestCase {
+ name: "ItemModelBarDataProxy MultiMatchBehaviour"
+
+ Bars3D {
+ id: bars1
+
+ Bar3DSeries {
+ ItemModelBarDataProxy {
+ id: barProxy
+ itemModel: ListModel {
+ ListElement{ coords: "0,0"; data: "5"; }
+ ListElement{ coords: "0,0"; data: "15"; }
+ }
+ rowRole: "coords"
+ columnRole: "coords"
+ valueRole: "data"
+ rowRolePattern: /(\d),\d/
+ columnRolePattern: /(\d),(\d)/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\2"
+ }
+ }
+ }
+
+ function test_0_async_dummy() {
+ }
+
+ function test_1_test_multimatch() {
+ compare(bars1.valueAxis.max, 15)
+ }
+
+ function test_2_multimatch() {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBFirst
+ }
+
+ function test_3_test_multimatch() {
+ compare(bars1.valueAxis.max, 5)
+ }
+
+ function test_4_multimatch() {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBLast
+ }
+
+ function test_5_test_multimatch() {
+ compare(bars1.valueAxis.max, 15)
+ }
+
+ function test_6_multimatch() {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBAverage
+ }
+
+ function test_7_test_multimatch() {
+ compare(bars1.valueAxis.max, 10)
+ }
+
+ function test_8_multimatch() {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBCumulative
+ }
+
+ function test_9_test_multimatch() {
+ compare(bars1.valueAxis.max, 20)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/custom3d/tst_customitem.qml b/tests/auto/qmltest/custom3d/tst_customitem.qml
new file mode 100644
index 00000000..ee3d10fc
--- /dev/null
+++ b/tests/auto/qmltest/custom3d/tst_customitem.qml
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ Custom3DItem {
+ id: initial
+ }
+
+ Custom3DItem {
+ id: initialized
+ meshFile: ":\customitem.obj"
+ position: Qt.vector3d(1.0, 0.5, 1.0)
+ positionAbsolute: true
+ rotation: Qt.quaternion(1, 0.5, 0, 0)
+ scaling: Qt.vector3d(0.2, 0.2, 0.2)
+ scalingAbsolute: false
+ shadowCasting: false
+ textureFile: ":\customtexture.jpg"
+ visible: false
+ }
+
+ Custom3DItem {
+ id: change
+ }
+
+ TestCase {
+ name: "Custom3DItem Initial"
+
+ function test_initial() {
+ compare(initial.meshFile, "")
+ compare(initial.position, Qt.vector3d(0.0, 0.0, 0.0))
+ compare(initial.positionAbsolute, false)
+ compare(initial.rotation, Qt.quaternion(0, 0, 0, 0))
+ compare(initial.scaling, Qt.vector3d(0.1, 0.1, 0.1))
+ compare(initial.scalingAbsolute, true)
+ compare(initial.shadowCasting, true)
+ compare(initial.textureFile, "")
+ compare(initial.visible, true)
+ }
+ }
+
+ TestCase {
+ name: "Custom3DItem Initialized"
+
+ function test_initialized() {
+ compare(initialized.meshFile, ":\customitem.obj")
+ compare(initialized.position, Qt.vector3d(1.0, 0.5, 1.0))
+ compare(initialized.positionAbsolute, true)
+ compare(initialized.rotation, Qt.quaternion(1, 0.5, 0, 0))
+ compare(initialized.scaling, Qt.vector3d(0.2, 0.2, 0.2))
+ compare(initialized.scalingAbsolute, false)
+ compare(initialized.shadowCasting, false)
+ compare(initialized.textureFile, ":\customtexture.jpg")
+ compare(initialized.visible, false)
+ }
+ }
+
+ TestCase {
+ name: "Custom3DItem Change"
+
+ function test_change() {
+ change.meshFile = ":\customitem.obj"
+ change.position = Qt.vector3d(1.0, 0.5, 1.0)
+ change.positionAbsolute = true
+ change.rotation = Qt.quaternion(1, 0.5, 0, 0)
+ change.scaling = Qt.vector3d(0.2, 0.2, 0.2)
+ change.scalingAbsolute = false
+ change.shadowCasting = false
+ change.textureFile = ":\customtexture.jpg"
+ change.visible = false
+
+ compare(change.meshFile, ":\customitem.obj")
+ compare(change.position, Qt.vector3d(1.0, 0.5, 1.0))
+ compare(change.positionAbsolute, true)
+ compare(change.rotation, Qt.quaternion(1, 0.5, 0, 0))
+ compare(change.scaling, Qt.vector3d(0.2, 0.2, 0.2))
+ compare(change.scalingAbsolute, false)
+ compare(change.shadowCasting, false)
+ compare(change.textureFile, ":\customtexture.jpg")
+ compare(change.visible, false)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/custom3d/tst_customlabel.qml b/tests/auto/qmltest/custom3d/tst_customlabel.qml
new file mode 100644
index 00000000..acac1b63
--- /dev/null
+++ b/tests/auto/qmltest/custom3d/tst_customlabel.qml
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ Custom3DLabel {
+ id: initial
+ }
+
+ Custom3DLabel {
+ id: initialized
+ backgroundColor: "red"
+ backgroundEnabled: false
+ borderEnabled: false
+ facingCamera: true
+ font.family: "Times New Roman"
+ text: "test label"
+ textColor: "blue"
+
+ position: Qt.vector3d(1.0, 0.5, 1.0)
+ positionAbsolute: true
+ rotation: Qt.quaternion(1, 0.5, 0, 0)
+ scaling: Qt.vector3d(0.2, 0.2, 0.2)
+ shadowCasting: true
+ visible: false
+ }
+
+ Custom3DLabel {
+ id: change
+ }
+
+ TestCase {
+ name: "Custom3DLabel Initial"
+
+ function test_initial() {
+ compare(initial.backgroundColor, "#a0a0a4")
+ compare(initial.backgroundEnabled, true)
+ compare(initial.borderEnabled, true)
+ compare(initial.facingCamera, false)
+ compare(initial.font.family, "Arial")
+ compare(initial.text, "")
+ compare(initial.textColor, "#ffffff")
+
+ compare(initial.meshFile, ":/defaultMeshes/plane")
+ compare(initial.position, Qt.vector3d(0.0, 0.0, 0.0))
+ compare(initial.positionAbsolute, false)
+ compare(initial.rotation, Qt.quaternion(0, 0, 0, 0))
+ compare(initial.scaling, Qt.vector3d(0.1, 0.1, 0.1))
+ compare(initial.scalingAbsolute, true)
+ compare(initial.shadowCasting, false)
+ compare(initial.textureFile, "")
+ compare(initial.visible, true)
+ }
+ }
+
+ TestCase {
+ name: "Custom3DLabel Initialized"
+
+ function test_initialized() {
+ compare(initialized.backgroundColor, "#ff0000")
+ compare(initialized.backgroundEnabled, false)
+ compare(initialized.borderEnabled, false)
+ compare(initialized.facingCamera, true)
+ compare(initialized.font.family, "Times New Roman")
+ compare(initialized.text, "test label")
+ compare(initialized.textColor, "#0000ff")
+
+ compare(initialized.position, Qt.vector3d(1.0, 0.5, 1.0))
+ compare(initialized.positionAbsolute, true)
+ compare(initialized.rotation, Qt.quaternion(1, 0.5, 0, 0))
+ compare(initialized.scaling, Qt.vector3d(0.2, 0.2, 0.2))
+ compare(initialized.shadowCasting, true)
+ compare(initialized.visible, false)
+ }
+ }
+
+ TestCase {
+ name: "Custom3DLabel Change"
+
+ function test_change() {
+ change.backgroundColor = "red"
+ change.backgroundEnabled = false
+ change.borderEnabled = false
+ change.facingCamera = true
+ change.font.family = "Times New Roman"
+ change.text = "test label"
+ change.textColor = "blue"
+
+ change.position = Qt.vector3d(1.0, 0.5, 1.0)
+ change.positionAbsolute = true
+ change.rotation = Qt.quaternion(1, 0.5, 0, 0)
+ change.scaling = Qt.vector3d(0.2, 0.2, 0.2)
+ change.shadowCasting = true
+ change.visible = false
+
+ compare(change.backgroundColor, "#ff0000")
+ compare(change.backgroundEnabled, false)
+ compare(change.borderEnabled, false)
+ compare(change.facingCamera, true)
+ compare(change.font.family, "Times New Roman")
+ compare(change.text, "test label")
+ compare(change.textColor, "#0000ff")
+
+ compare(change.position, Qt.vector3d(1.0, 0.5, 1.0))
+ compare(change.positionAbsolute, true)
+ compare(change.rotation, Qt.quaternion(1, 0.5, 0, 0))
+ compare(change.scaling, Qt.vector3d(0.2, 0.2, 0.2))
+ compare(change.shadowCasting, true)
+ compare(change.visible, false)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/custom3d/tst_customvolume.qml b/tests/auto/qmltest/custom3d/tst_customvolume.qml
new file mode 100644
index 00000000..08c15013
--- /dev/null
+++ b/tests/auto/qmltest/custom3d/tst_customvolume.qml
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ Custom3DVolume {
+ id: initial
+ }
+
+ Custom3DVolume {
+ id: initialized
+ alphaMultiplier: 0.1
+ drawSliceFrames: true
+ drawSlices: true
+ preserveOpacity: false
+ sliceFrameColor: "red"
+ sliceFrameGaps: Qt.vector3d(2.0, 2.0, 2.0)
+ sliceFrameThicknesses: Qt.vector3d(2.0, 2.0, 2.0)
+ sliceFrameWidths: Qt.vector3d(2.0, 2.0, 2.0)
+ sliceIndexX: 0
+ sliceIndexY: 0
+ sliceIndexZ: 0
+ useHighDefShader: false
+
+ position: Qt.vector3d(1.0, 0.5, 1.0)
+ positionAbsolute: true
+ rotation: Qt.quaternion(1, 0.5, 0, 0)
+ scaling: Qt.vector3d(0.2, 0.2, 0.2)
+ scalingAbsolute: false
+ shadowCasting: false
+ visible: false
+ }
+
+ Custom3DVolume {
+ id: change
+ }
+
+ Custom3DVolume {
+ id: invalid
+ }
+
+ TestCase {
+ name: "Custom3DVolume Initial"
+
+ function test_initial() {
+ compare(initial.alphaMultiplier, 1.0)
+ compare(initial.drawSliceFrames, false)
+ compare(initial.drawSlices, false)
+ compare(initial.preserveOpacity, true)
+ compare(initial.sliceFrameColor, "#000000")
+ compare(initial.sliceFrameGaps, Qt.vector3d(0.01, 0.01, 0.01))
+ compare(initial.sliceFrameThicknesses, Qt.vector3d(0.01, 0.01, 0.01))
+ compare(initial.sliceFrameWidths, Qt.vector3d(0.01, 0.01, 0.01))
+ compare(initial.sliceIndexX, -1)
+ compare(initial.sliceIndexY, -1)
+ compare(initial.sliceIndexZ, -1)
+ compare(initial.useHighDefShader, true)
+
+ compare(initial.meshFile, ":/defaultMeshes/barFull")
+ compare(initial.position, Qt.vector3d(0.0, 0.0, 0.0))
+ compare(initial.positionAbsolute, false)
+ compare(initial.rotation, Qt.quaternion(0, 0, 0, 0))
+ compare(initial.scaling, Qt.vector3d(0.1, 0.1, 0.1))
+ compare(initial.scalingAbsolute, true)
+ compare(initial.shadowCasting, true)
+ compare(initial.textureFile, "")
+ compare(initial.visible, true)
+ }
+ }
+
+ TestCase {
+ name: "Custom3DVolume Initialized"
+
+ function test_initialized() {
+ compare(initialized.alphaMultiplier, 0.1)
+ compare(initialized.drawSliceFrames, true)
+ compare(initialized.drawSlices, true)
+ compare(initialized.preserveOpacity, false)
+ compare(initialized.sliceFrameColor, "#ff0000")
+ compare(initialized.sliceFrameGaps, Qt.vector3d(2.0, 2.0, 2.0))
+ compare(initialized.sliceFrameThicknesses, Qt.vector3d(2.0, 2.0, 2.0))
+ compare(initialized.sliceFrameWidths, Qt.vector3d(2.0, 2.0, 2.0))
+ compare(initialized.sliceIndexX, 0)
+ compare(initialized.sliceIndexY, 0)
+ compare(initialized.sliceIndexZ, 0)
+ compare(initialized.useHighDefShader, false)
+
+ compare(initialized.position, Qt.vector3d(1.0, 0.5, 1.0))
+ compare(initialized.positionAbsolute, true)
+ compare(initialized.rotation, Qt.quaternion(1, 0.5, 0, 0))
+ compare(initialized.scaling, Qt.vector3d(0.2, 0.2, 0.2))
+ compare(initialized.scalingAbsolute, false)
+ compare(initialized.shadowCasting, false)
+ compare(initialized.visible, false)
+ }
+ }
+
+ TestCase {
+ name: "Custom3DVolume Change"
+
+ function test_change() {
+ change.alphaMultiplier = 0.1
+ change.drawSliceFrames = true
+ change.drawSlices = true
+ change.preserveOpacity = false
+ change.sliceFrameColor = "red"
+ change.sliceFrameGaps = Qt.vector3d(2.0, 2.0, 2.0)
+ change.sliceFrameThicknesses = Qt.vector3d(2.0, 2.0, 2.0)
+ change.sliceFrameWidths = Qt.vector3d(2.0, 2.0, 2.0)
+ change.sliceIndexX = 0
+ change.sliceIndexY = 0
+ change.sliceIndexZ = 0
+ change.useHighDefShader = false
+
+ change.position = Qt.vector3d(1.0, 0.5, 1.0)
+ change.positionAbsolute = true
+ change.rotation = Qt.quaternion(1, 0.5, 0, 0)
+ change.scaling = Qt.vector3d(0.2, 0.2, 0.2)
+ change.scalingAbsolute = false
+ change.shadowCasting = false
+ change.visible = false
+
+ compare(change.alphaMultiplier, 0.1)
+ compare(change.drawSliceFrames, true)
+ compare(change.drawSlices, true)
+ compare(change.preserveOpacity, false)
+ compare(change.sliceFrameColor, "#ff0000")
+ compare(change.sliceFrameGaps, Qt.vector3d(2.0, 2.0, 2.0))
+ compare(change.sliceFrameThicknesses, Qt.vector3d(2.0, 2.0, 2.0))
+ compare(change.sliceFrameWidths, Qt.vector3d(2.0, 2.0, 2.0))
+ compare(change.sliceIndexX, 0)
+ compare(change.sliceIndexY, 0)
+ compare(change.sliceIndexZ, 0)
+ compare(change.useHighDefShader, false)
+
+ compare(change.position, Qt.vector3d(1.0, 0.5, 1.0))
+ compare(change.positionAbsolute, true)
+ compare(change.rotation, Qt.quaternion(1, 0.5, 0, 0))
+ compare(change.scaling, Qt.vector3d(0.2, 0.2, 0.2))
+ compare(change.scalingAbsolute, false)
+ compare(change.shadowCasting, false)
+ compare(change.visible, false)
+ }
+ }
+
+ TestCase {
+ name: "Custom3DVolume Invalid"
+
+ function test_invalid() {
+ invalid.alphaMultiplier = -1.0
+ compare(invalid.alphaMultiplier, 1.0)
+ invalid.sliceFrameGaps = Qt.vector3d(-0.1, -0.1, -0.1)
+ compare(invalid.sliceFrameGaps, Qt.vector3d(0.01, 0.01, 0.01))
+ invalid.sliceFrameThicknesses = Qt.vector3d(-0.1, -0.1, -0.1)
+ compare(invalid.sliceFrameThicknesses, Qt.vector3d(0.01, 0.01, 0.01))
+ invalid.sliceFrameWidths = Qt.vector3d(-0.1, -0.1, -0.1)
+ compare(invalid.sliceFrameWidths, Qt.vector3d(0.01, 0.01, 0.01))
+ }
+ }
+}
diff --git a/tests/auto/qmltest/customitem.obj b/tests/auto/qmltest/customitem.obj
new file mode 100644
index 00000000..108cf7ac
--- /dev/null
+++ b/tests/auto/qmltest/customitem.obj
@@ -0,0 +1,54 @@
+# Blender v2.66 (sub 0) OBJ File: 'cube_filled.blend'
+# www.blender.org
+o Cube
+v -1.000000 -1.000000 1.000000
+v -1.000000 -1.000000 -1.000000
+v 1.000000 -1.000000 -1.000000
+v 1.000000 -1.000000 1.000000
+v -1.000000 1.000000 1.000000
+v -1.000000 1.000000 -1.000000
+v 1.000000 1.000000 -1.000000
+v 1.000000 1.000000 1.000000
+vt 0.666667 0.332314
+vt 0.334353 0.333333
+vt 0.665647 0.000000
+vt 0.001020 0.333333
+vt 0.000000 0.001020
+vt 0.333333 0.332314
+vt 0.333333 0.665647
+vt 0.001019 0.666667
+vt 0.000000 0.334353
+vt 0.334353 0.666667
+vt 0.333333 0.334353
+vt 0.665647 0.333333
+vt 0.333333 0.667686
+vt 0.665647 0.666667
+vt 0.666667 0.998980
+vt 0.667686 0.333333
+vt 0.666667 0.001019
+vt 0.998980 0.000000
+vt 0.333333 0.001019
+vt 0.332314 0.000000
+vt 0.332314 0.333333
+vt 0.666667 0.665647
+vt 0.334353 1.000000
+vt 1.000000 0.332314
+vn -1.000000 0.000000 0.000000
+vn 0.000000 0.000000 -1.000000
+vn 1.000000 -0.000000 0.000000
+vn 0.000000 0.000000 1.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 -1.000000 -0.000000
+s off
+f 5/1/1 6/2/1 1/3/1
+f 6/4/2 7/5/2 2/6/2
+f 7/7/3 8/8/3 4/9/3
+f 8/10/4 5/11/4 1/12/4
+f 8/13/5 7/14/5 6/15/5
+f 2/16/6 3/17/6 4/18/6
+f 6/2/1 2/19/1 1/3/1
+f 7/5/2 3/20/2 2/6/2
+f 3/21/3 7/7/3 4/9/3
+f 4/22/4 8/10/4 1/12/4
+f 5/23/5 8/13/5 6/15/5
+f 1/24/6 2/16/6 4/18/6
diff --git a/tests/auto/qmltest/customtexture.jpg b/tests/auto/qmltest/customtexture.jpg
new file mode 100644
index 00000000..2580f5bd
--- /dev/null
+++ b/tests/auto/qmltest/customtexture.jpg
Binary files differ
diff --git a/tests/auto/qmltest/input3d/tst_input.qml b/tests/auto/qmltest/input3d/tst_input.qml
new file mode 100644
index 00000000..fefb585e
--- /dev/null
+++ b/tests/auto/qmltest/input3d/tst_input.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ InputHandler3D {
+ id: initial
+ }
+
+ InputHandler3D {
+ id: initialized
+ rotationEnabled: false
+ selectionEnabled: false
+ zoomAtTargetEnabled: false
+ zoomEnabled: false
+ }
+
+ InputHandler3D {
+ id: change
+ }
+
+ TestCase {
+ name: "InputHandler3D Initial"
+
+ function test_initial() {
+ compare(initial.rotationEnabled, true)
+ compare(initial.selectionEnabled, true)
+ compare(initial.zoomAtTargetEnabled, true)
+ compare(initial.zoomEnabled, true)
+ }
+ }
+
+ TestCase {
+ name: "InputHandler3D Initialized"
+
+ function test_initialized() {
+ compare(initialized.rotationEnabled, false)
+ compare(initialized.selectionEnabled, false)
+ compare(initialized.zoomAtTargetEnabled, false)
+ compare(initialized.zoomEnabled, false)
+ }
+ }
+
+ TestCase {
+ name: "InputHandler3D Change"
+
+ function test_change() {
+ change.rotationEnabled = false
+ change.selectionEnabled = false
+ change.zoomAtTargetEnabled = false
+ change.zoomEnabled = false
+
+ compare(change.rotationEnabled, false)
+ compare(change.selectionEnabled, false)
+ compare(change.zoomAtTargetEnabled, false)
+ compare(change.zoomEnabled, false)
+ }
+
+ // TODO: QTRD-3380 (mouse events)
+ }
+}
diff --git a/tests/auto/qmltest/input3d/tst_touch.qml b/tests/auto/qmltest/input3d/tst_touch.qml
new file mode 100644
index 00000000..626da68f
--- /dev/null
+++ b/tests/auto/qmltest/input3d/tst_touch.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ TouchInputHandler3D {
+ id: initial
+ }
+
+ TouchInputHandler3D {
+ id: initialized
+ rotationEnabled: false
+ selectionEnabled: false
+ zoomAtTargetEnabled: false
+ zoomEnabled: false
+ }
+
+ TouchInputHandler3D {
+ id: change
+ }
+
+ TestCase {
+ name: "TouchInputHandler3D Initial"
+
+ function test_initial() {
+ compare(initial.rotationEnabled, true)
+ compare(initial.selectionEnabled, true)
+ compare(initial.zoomAtTargetEnabled, true)
+ compare(initial.zoomEnabled, true)
+ }
+ }
+
+ TestCase {
+ name: "TouchInputHandler3D Initialized"
+
+ function test_initialized() {
+ compare(initialized.rotationEnabled, false)
+ compare(initialized.selectionEnabled, false)
+ compare(initialized.zoomAtTargetEnabled, false)
+ compare(initialized.zoomEnabled, false)
+ }
+ }
+
+ TestCase {
+ name: "TouchInputHandler3D Change"
+
+ function test_change() {
+ change.rotationEnabled = false
+ change.selectionEnabled = false
+ change.zoomAtTargetEnabled = false
+ change.zoomEnabled = false
+
+ compare(change.rotationEnabled, false)
+ compare(change.selectionEnabled, false)
+ compare(change.zoomAtTargetEnabled, false)
+ compare(change.zoomEnabled, false)
+
+ // TODO: QTRD-3380 (mouse events)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro
new file mode 100644
index 00000000..cbb9b8b8
--- /dev/null
+++ b/tests/auto/qmltest/qmltest.pro
@@ -0,0 +1,35 @@
+TEMPLATE = app
+TARGET = tst_qmltest
+CONFIG += qmltestcase
+CONFIG += console
+SOURCES += tst_qmltest.cpp
+OTHER_FILES += bars3d\tst_basic.qml \
+ bars3d\tst_bars.qml \
+ bars3d\tst_barseries.qml \
+ bars3d\tst_proxy.qml \
+ scatter3d\tst_basic.qml \
+ scatter3d\tst_scatter.qml \
+ scatter3d\tst_scatterseries.qml \
+ scatter3d\tst_proxy.qml \
+ surface3d\tst_basic.qml \
+ surface3d\tst_surface.qml \
+ surface3d\tst_surfaceseries.qml \
+ surface3d\tst_proxy.qml \
+ surface3d\tst_heightproxy.qml \
+ theme3d\tst_theme.qml \
+ theme3d\tst_colorgradient.qml \
+ theme3d\tst_themecolor.qml \
+ custom3d\tst_customitem.qml \
+ custom3d\tst_customlabel.qml \
+ custom3d\tst_customvolume.qml \
+ scene3d\tst_scene.qml \
+ scene3d\tst_camera.qml \
+ scene3d\tst_light.qml \
+ input3d\tst_input.qml \
+ input3d\tst_touch.qml \
+ axis3d\tst_category.qml \
+ axis3d\tst_value.qml \
+ axis3d\tst_logvalue.qml \
+
+RESOURCES += \
+ qmltest.qrc
diff --git a/tests/auto/qmltest/qmltest.qrc b/tests/auto/qmltest/qmltest.qrc
new file mode 100644
index 00000000..61f19086
--- /dev/null
+++ b/tests/auto/qmltest/qmltest.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>customitem.obj</file>
+ <file>customtexture.jpg</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/qmltest/scatter3d/tst_basic.qml b/tests/auto/qmltest/scatter3d/tst_basic.qml
new file mode 100644
index 00000000..b7221701
--- /dev/null
+++ b/tests/auto/qmltest/scatter3d/tst_basic.qml
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Scatter3D {
+ id: empty
+ }
+
+ Scatter3D {
+ id: basic
+ anchors.fill: parent
+ }
+
+ Scatter3D {
+ id: common
+ anchors.fill: parent
+ }
+
+ Scatter3D {
+ id: common_init
+ anchors.fill: parent
+ selectionMode: AbstractGraph3D.SelectionNone
+ shadowQuality: AbstractGraph3D.ShadowQualityLow
+ msaaSamples: 2
+ theme: Theme3D { }
+ renderingMode: AbstractGraph3D.RenderIndirect
+ measureFps: true
+ orthoProjection: false
+ aspectRatio: 3.0
+ optimizationHints: AbstractGraph3D.OptimizationStatic
+ polar: false
+ radialLabelOffset: 2
+ horizontalAspectRatio: 0.2
+ reflection: true
+ reflectivity: 0.1
+ locale: Qt.locale("UK")
+ margin: 0.2
+ }
+
+ TestCase {
+ name: "Scatter3D Empty"
+
+ function test_empty() {
+ compare(empty.width, 0, "width")
+ compare(empty.height, 0, "height")
+ compare(empty.seriesList.length, 0, "seriesList")
+ compare(empty.selectedSeries, null, "selectedSeries")
+ compare(empty.axisX.orientation, AbstractAxis3D.AxisOrientationX)
+ compare(empty.axisZ.orientation, AbstractAxis3D.AxisOrientationZ)
+ compare(empty.axisY.orientation, AbstractAxis3D.AxisOrientationY)
+ compare(empty.axisX.type, AbstractAxis3D.AxisTypeValue)
+ compare(empty.axisZ.type, AbstractAxis3D.AxisTypeValue)
+ compare(empty.axisY.type, AbstractAxis3D.AxisTypeValue)
+ }
+ }
+
+ TestCase {
+ name: "Scatter3D Basic"
+ when: windowShown
+
+ function test_basic() {
+ compare(basic.width, 150, "width")
+ compare(basic.height, 150, "height")
+ }
+ }
+
+ TestCase {
+ name: "Scatter3D Common"
+ when: windowShown
+
+ function test_1_common() {
+ compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode")
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality")
+ if (common.shadowsSupported === true)
+ compare(common.msaaSamples, 4, "msaaSamples")
+ else
+ compare(common.msaaSamples, 0, "msaaSamples")
+ compare(common.theme.type, Theme3D.ThemeQt, "theme")
+ compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode")
+ compare(common.measureFps, false, "measureFps")
+ compare(common.customItemList.length, 0, "customItemList")
+ compare(common.orthoProjection, false, "orthoProjection")
+ compare(common.selectedElement, AbstractGraph3D.ElementNone, "selectedElement")
+ compare(common.aspectRatio, 2.0, "aspectRatio")
+ compare(common.optimizationHints, AbstractGraph3D.OptimizationDefault, "optimizationHints")
+ compare(common.polar, false, "polar")
+ compare(common.radialLabelOffset, 1, "radialLabelOffset")
+ compare(common.horizontalAspectRatio, 0, "horizontalAspectRatio")
+ compare(common.reflection, false, "reflection")
+ compare(common.reflectivity, 0.5, "reflectivity")
+ compare(common.locale, Qt.locale("C"), "locale")
+ compare(common.queriedGraphPosition, Qt.vector3d(0, 0, 0), "queriedGraphPosition")
+ compare(common.margin, -1, "margin")
+ }
+
+ function test_2_change_common() {
+ common.selectionMode = AbstractGraph3D.SelectionNone
+ common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality")
+ common.msaaSamples = 8
+ if (common.shadowsSupported === true)
+ compare(common.msaaSamples, 8, "msaaSamples")
+ else
+ compare(common.msaaSamples, 0, "msaaSamples")
+ common.theme.type = Theme3D.ThemeRetro
+ common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear
+ common.measureFps = true
+ common.orthoProjection = true
+ common.aspectRatio = 1.0
+ common.optimizationHints = AbstractGraph3D.OptimizationStatic
+ common.polar = true
+ common.radialLabelOffset = 2
+ common.horizontalAspectRatio = 1
+ common.reflection = true
+ common.reflectivity = 1.0
+ common.locale = Qt.locale("FI")
+ common.margin = 1.0
+ compare(common.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode")
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") // Ortho disables shadows
+ compare(common.msaaSamples, 0, "msaaSamples") // Rendering mode changes this to zero
+ compare(common.theme.type, Theme3D.ThemeRetro, "theme")
+ compare(common.renderingMode, AbstractGraph3D.RenderDirectToBackground_NoClear, "renderingMode")
+ compare(common.measureFps, true, "measureFps")
+ compare(common.orthoProjection, true, "orthoProjection")
+ compare(common.aspectRatio, 1.0, "aspectRatio")
+ compare(common.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints")
+ compare(common.polar, true, "polar")
+ compare(common.radialLabelOffset, 2, "radialLabelOffset")
+ compare(common.horizontalAspectRatio, 1, "horizontalAspectRatio")
+ compare(common.reflection, true, "reflection")
+ compare(common.reflectivity, 1.0, "reflectivity")
+ compare(common.locale, Qt.locale("FI"), "locale")
+ compare(common.margin, 1.0, "margin")
+ }
+
+ function test_3_change_invalid_common() {
+ common.selectionMode = AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionColumn | AbstractGraph3D.SelectionSlice
+ common.theme.type = -2
+ common.renderingMode = -1
+ common.measureFps = false
+ common.orthoProjection = false
+ common.aspectRatio = -1.0
+ common.polar = false
+ common.horizontalAspectRatio = -2
+ common.reflection = false
+ common.reflectivity = -1.0
+ compare(common.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode")
+ compare(common.theme.type, -2/*Theme3D.ThemeRetro*/, "theme") // TODO: Fix once QTRD-3367 is done
+ compare(common.renderingMode, -1/*AbstractGraph3D.RenderDirectToBackground_NoClear*/, "renderingMode") // TODO: Fix once QTRD-3367 is done
+ compare(common.aspectRatio, -1.0/*1.0*/, "aspectRatio") // TODO: Fix once QTRD-3367 is done
+ compare(common.horizontalAspectRatio, -2/*1*/, "horizontalAspectRatio") // TODO: Fix once QTRD-3367 is done
+ compare(common.reflectivity, -1.0/*1.0*/, "reflectivity") // TODO: Fix once QTRD-3367 is done
+ }
+
+ function test_4_common_initialized() {
+ compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode")
+ if (common_init.shadowsSupported === true) {
+ compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality")
+ compare(common_init.msaaSamples, 2, "msaaSamples")
+ } else {
+ compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality")
+ compare(common_init.msaaSamples, 0, "msaaSamples")
+ }
+ compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme")
+ compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode")
+ compare(common_init.measureFps, true, "measureFps")
+ compare(common_init.customItemList.length, 0, "customItemList")
+ compare(common_init.orthoProjection, false, "orthoProjection")
+ compare(common_init.aspectRatio, 3.0, "aspectRatio")
+ compare(common_init.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints")
+ compare(common_init.polar, false, "polar")
+ compare(common_init.radialLabelOffset, 2, "radialLabelOffset")
+ compare(common_init.horizontalAspectRatio, 0.2, "horizontalAspectRatio")
+ compare(common_init.reflection, true, "reflection")
+ compare(common_init.reflectivity, 0.1, "reflectivity")
+ compare(common_init.locale, Qt.locale("UK"), "locale")
+ compare(common_init.margin, 0.2, "margin")
+ }
+ }
+}
diff --git a/tests/auto/qmltest/scatter3d/tst_proxy.qml b/tests/auto/qmltest/scatter3d/tst_proxy.qml
new file mode 100644
index 00000000..e6478f15
--- /dev/null
+++ b/tests/auto/qmltest/scatter3d/tst_proxy.qml
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ ItemModelScatterDataProxy {
+ id: initial
+ }
+
+ ItemModelScatterDataProxy {
+ id: initialized
+
+ itemModel: ListModel { objectName: "model1" }
+ rotationRole: "rot"
+ rotationRolePattern: /-/
+ rotationRoleReplace: "\\1"
+ xPosRole: "x"
+ xPosRolePattern: /^.*-(\d\d)$/
+ xPosRoleReplace: "\\1"
+ yPosRole: "y"
+ yPosRolePattern: /^(\d\d\d\d).*$/
+ yPosRoleReplace: "\\1"
+ zPosRole: "z"
+ zPosRolePattern: /-/
+ zPosRoleReplace: "\\1"
+ }
+
+ ItemModelScatterDataProxy {
+ id: change
+ }
+
+ TestCase {
+ name: "ItemModelScatterDataProxy Initial"
+
+ function test_initial() {
+ verify(!initial.itemModel)
+ compare(initial.rotationRole, "")
+ verify(initial.rotationRolePattern)
+ compare(initial.rotationRoleReplace, "")
+ compare(initial.xPosRole, "")
+ verify(initial.xPosRolePattern)
+ compare(initial.xPosRoleReplace, "")
+ compare(initial.yPosRole, "")
+ verify(initial.yPosRolePattern)
+ compare(initial.yPosRoleReplace, "")
+ compare(initial.zPosRole, "")
+ verify(initial.zPosRolePattern)
+ compare(initial.zPosRoleReplace, "")
+
+ compare(initial.itemCount, 0)
+ verify(!initial.series)
+
+ compare(initial.type, AbstractDataProxy.DataTypeScatter)
+ }
+ }
+
+ TestCase {
+ name: "ItemModelScatterDataProxy Initialized"
+
+ function test_initialized() {
+ compare(initialized.itemModel.objectName, "model1")
+ compare(initialized.rotationRole, "rot")
+ compare(initialized.rotationRolePattern, /-/)
+ compare(initialized.rotationRoleReplace, "\\1")
+ compare(initialized.xPosRole, "x")
+ compare(initialized.xPosRolePattern, /^.*-(\d\d)$/)
+ compare(initialized.xPosRoleReplace, "\\1")
+ compare(initialized.yPosRole, "y")
+ compare(initialized.yPosRolePattern, /^(\d\d\d\d).*$/)
+ compare(initialized.yPosRoleReplace, "\\1")
+ compare(initialized.zPosRole, "z")
+ compare(initialized.zPosRolePattern, /-/)
+ compare(initialized.zPosRoleReplace, "\\1")
+ }
+ }
+
+ TestCase {
+ name: "ItemModelScatterDataProxy Change"
+
+ ListModel { id: model1; objectName: "model1" }
+
+ function test_change() {
+ change.itemModel = model1
+ change.rotationRole = "rot"
+ change.rotationRolePattern = /-/
+ change.rotationRoleReplace = "\\1"
+ change.xPosRole = "x"
+ change.xPosRolePattern = /^.*-(\d\d)$/
+ change.xPosRoleReplace = "\\1"
+ change.yPosRole = "y"
+ change.yPosRolePattern = /^(\d\d\d\d).*$/
+ change.yPosRoleReplace = "\\1"
+ change.zPosRole = "z"
+ change.zPosRolePattern = /-/
+ change.zPosRoleReplace = "\\1"
+
+ compare(change.itemModel.objectName, "model1")
+ compare(change.rotationRole, "rot")
+ compare(change.rotationRolePattern, /-/)
+ compare(change.rotationRoleReplace, "\\1")
+ compare(change.xPosRole, "x")
+ compare(change.xPosRolePattern, /^.*-(\d\d)$/)
+ compare(change.xPosRoleReplace, "\\1")
+ compare(change.yPosRole, "y")
+ compare(change.yPosRolePattern, /^(\d\d\d\d).*$/)
+ compare(change.yPosRoleReplace, "\\1")
+ compare(change.zPosRole, "z")
+ compare(change.zPosRolePattern, /-/)
+ compare(change.zPosRoleReplace, "\\1")
+ }
+ }
+}
diff --git a/tests/auto/qmltest/scatter3d/tst_scatter.qml b/tests/auto/qmltest/scatter3d/tst_scatter.qml
new file mode 100644
index 00000000..b070b5f0
--- /dev/null
+++ b/tests/auto/qmltest/scatter3d/tst_scatter.qml
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Scatter3D {
+ id: series
+ anchors.fill: parent
+ }
+
+ TestCase {
+ name: "Scatter3D Series"
+
+ Scatter3DSeries { id: series1 }
+ Scatter3DSeries { id: series2 }
+
+ function test_1_add_series() {
+ series.seriesList = [series1, series2]
+ compare(series.seriesList.length, 2)
+ }
+
+ function test_2_remove_series() {
+ series.seriesList = [series1]
+ compare(series.seriesList.length, 1)
+ }
+
+ function test_3_remove_series() {
+ series.seriesList = []
+ compare(series.seriesList.length, 0)
+ }
+
+ function test_4_selected_series() {
+ series.seriesList = [series1, series2]
+ series.seriesList[0].selectedItem = 0
+ compare(series.selectedSeries, series1)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/scatter3d/tst_scatterseries.qml b/tests/auto/qmltest/scatter3d/tst_scatterseries.qml
new file mode 100644
index 00000000..4df58303
--- /dev/null
+++ b/tests/auto/qmltest/scatter3d/tst_scatterseries.qml
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Scatter3DSeries {
+ id: initial
+ }
+
+ ColorGradient {
+ id: gradient1;
+ stops: [
+ ColorGradientStop { color: "red"; position: 0 },
+ ColorGradientStop { color: "blue"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient2;
+ stops: [
+ ColorGradientStop { color: "green"; position: 0 },
+ ColorGradientStop { color: "red"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient3;
+ stops: [
+ ColorGradientStop { color: "gray"; position: 0 },
+ ColorGradientStop { color: "darkgray"; position: 1 }
+ ]
+ }
+
+ Scatter3DSeries {
+ id: initialized
+ dataProxy: ItemModelScatterDataProxy {
+ itemModel: ListModel {
+ ListElement{ xPos: "2.754"; yPos: "1.455"; zPos: "3.362"; }
+ ListElement{ xPos: "3.164"; yPos: "2.022"; zPos: "4.348"; }
+ }
+ xPosRole: "xPos"
+ yPosRole: "yPos"
+ zPosRole: "zPos"
+ }
+ itemSize: 0.5
+ selectedItem: 0
+
+ baseColor: "blue"
+ baseGradient: gradient1
+ colorStyle: Theme3D.ColorStyleObjectGradient
+ itemLabelFormat: "%f"
+ itemLabelVisible: false
+ mesh: Abstract3DSeries.MeshMinimal
+ meshRotation: Qt.quaternion(1, 1, 1, 1)
+ meshSmooth: true
+ multiHighlightColor: "green"
+ multiHighlightGradient: gradient2
+ name: "series1"
+ singleHighlightColor: "red"
+ singleHighlightGradient: gradient3
+ userDefinedMesh: ":/customitem.obj"
+ visible: false
+ }
+
+ ItemModelScatterDataProxy {
+ id: proxy1
+ itemModel: ListModel {
+ ListElement{ xPos: "2.754"; yPos: "1.455"; zPos: "3.362"; }
+ ListElement{ xPos: "3.164"; yPos: "2.022"; zPos: "4.348"; }
+ ListElement{ xPos: "4.564"; yPos: "1.865"; zPos: "1.346"; }
+ }
+ xPosRole: "xPos"
+ yPosRole: "yPos"
+ zPosRole: "zPos"
+ }
+
+ Scatter3DSeries {
+ id: change
+ }
+
+ Scatter3DSeries {
+ id: invalid
+ }
+
+ TestCase {
+ name: "Scatter3DSeries Initial"
+
+ function test_1_initial() {
+ compare(initial.dataProxy.itemCount, 0)
+ compare(initial.invalidSelectionIndex, -1)
+ compare(initial.itemSize, 0.0)
+ compare(initial.selectedItem, -1)
+ }
+
+ function test_2_initial_common() {
+ // Common properties
+ compare(initial.baseColor, "#000000")
+ compare(initial.baseGradient, null)
+ compare(initial.colorStyle, Theme3D.ColorStyleUniform)
+ compare(initial.itemLabel, "")
+ compare(initial.itemLabelFormat, "@xLabel, @yLabel, @zLabel")
+ compare(initial.itemLabelVisible, true)
+ compare(initial.mesh, Abstract3DSeries.MeshSphere)
+ compare(initial.meshRotation, Qt.quaternion(1, 0, 0, 0))
+ compare(initial.meshSmooth, false)
+ compare(initial.multiHighlightColor, "#000000")
+ compare(initial.multiHighlightGradient, null)
+ compare(initial.name, "")
+ compare(initial.singleHighlightColor, "#000000")
+ compare(initial.singleHighlightGradient, null)
+ compare(initial.type, Abstract3DSeries.SeriesTypeScatter)
+ compare(initial.userDefinedMesh, "")
+ compare(initial.visible, true)
+ }
+ }
+
+ TestCase {
+ name: "Scatter3DSeries Initialized"
+
+ function test_1_initialized() {
+ compare(initialized.dataProxy.itemCount, 2)
+ compare(initialized.itemSize, 0.5)
+ compare(initialized.selectedItem, 0)
+ }
+
+ function test_2_initialized_common() {
+ // Common properties
+ compare(initialized.baseColor, "#0000ff")
+ compare(initialized.baseGradient, gradient1)
+ compare(initialized.colorStyle, Theme3D.ColorStyleObjectGradient)
+ compare(initialized.itemLabelFormat, "%f")
+ compare(initialized.itemLabelVisible, false)
+ compare(initialized.mesh, Abstract3DSeries.MeshMinimal)
+ compare(initialized.meshRotation, Qt.quaternion(1, 1, 1, 1))
+ compare(initialized.meshSmooth, true)
+ compare(initialized.multiHighlightColor, "#008000")
+ compare(initialized.multiHighlightGradient, gradient2)
+ compare(initialized.name, "series1")
+ compare(initialized.singleHighlightColor, "#ff0000")
+ compare(initialized.singleHighlightGradient, gradient3)
+ compare(initialized.userDefinedMesh, ":/customitem.obj")
+ compare(initialized.visible, false)
+ }
+ }
+
+ TestCase {
+ name: "Scatter3DSeries Change"
+
+ function test_1_change() {
+ change.dataProxy = proxy1
+ change.itemSize = 0.5
+ change.selectedItem = 0
+ }
+
+ function test_2_test_change() {
+ // This test has a dependency to the previous one due to asynchronous item model resolving
+ compare(change.dataProxy.itemCount, 3)
+ compare(change.itemSize, 0.5)
+ compare(change.selectedItem, 0)
+ }
+
+ function test_3_change_common() {
+ change.baseColor = "blue"
+ change.baseGradient = gradient1
+ change.colorStyle = Theme3D.ColorStyleObjectGradient
+ change.itemLabelFormat = "%f"
+ change.itemLabelVisible = false
+ change.mesh = Abstract3DSeries.MeshMinimal
+ change.meshRotation = Qt.quaternion(1, 1, 1, 1)
+ change.meshSmooth = true
+ change.multiHighlightColor = "green"
+ change.multiHighlightGradient = gradient2
+ change.name = "series1"
+ change.singleHighlightColor = "red"
+ change.singleHighlightGradient = gradient3
+ change.userDefinedMesh = ":/customitem.obj"
+ change.visible = false
+
+ compare(change.baseColor, "#0000ff")
+ compare(change.baseGradient, gradient1)
+ compare(change.colorStyle, Theme3D.ColorStyleObjectGradient)
+ compare(change.itemLabelFormat, "%f")
+ compare(change.itemLabelVisible, false)
+ compare(change.mesh, Abstract3DSeries.MeshMinimal)
+ compare(change.meshRotation, Qt.quaternion(1, 1, 1, 1))
+ compare(change.meshSmooth, true)
+ compare(change.multiHighlightColor, "#008000")
+ compare(change.multiHighlightGradient, gradient2)
+ compare(change.name, "series1")
+ compare(change.singleHighlightColor, "#ff0000")
+ compare(change.singleHighlightGradient, gradient3)
+ compare(change.userDefinedMesh, ":/customitem.obj")
+ compare(change.visible, false)
+ }
+
+ function test_4_change_gradient_stop() {
+ gradient1.stops[0].color = "yellow"
+ compare(change.baseGradient.stops[0].color, "#ffff00")
+ }
+ }
+ TestCase {
+ name: "Scatter3DSeries Invalid"
+
+ function test_invalid() {
+ invalid.itemSize = -1.0
+ compare(invalid.itemSize, 0.0)
+ invalid.itemSize = 1.1
+ compare(invalid.itemSize, 0.0)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/scene3d/tst_camera.qml b/tests/auto/qmltest/scene3d/tst_camera.qml
new file mode 100644
index 00000000..07adf633
--- /dev/null
+++ b/tests/auto/qmltest/scene3d/tst_camera.qml
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ Camera3D {
+ id: initial
+ }
+
+ Camera3D {
+ id: initialized
+ maxZoomLevel: 1000.0
+ minZoomLevel: 100.0
+ target: Qt.vector3d(1.0, -1.0, 1.0)
+ wrapXRotation: false
+ wrapYRotation: true
+ xRotation: 30.0
+ yRotation: 30.0
+ zoomLevel: 500.0
+ }
+
+ Camera3D {
+ id: change
+ }
+
+ Camera3D {
+ id: invalid
+ }
+
+ TestCase {
+ name: "Camera3D Initial"
+
+ function test_initial() {
+ compare(initial.cameraPreset, Camera3D.CameraPresetNone)
+ compare(initial.maxZoomLevel, 500.0)
+ compare(initial.minZoomLevel, 10.0)
+ compare(initial.target, Qt.vector3d(0.0, 0.0, 0.0))
+ compare(initial.wrapXRotation, true)
+ compare(initial.wrapYRotation, false)
+ compare(initial.xRotation, 0.0)
+ compare(initial.yRotation, 0.0)
+ compare(initial.zoomLevel, 100.0)
+ }
+ }
+
+ TestCase {
+ name: "Camera3D Initialized"
+
+ function test_initialized() {
+ compare(initialized.maxZoomLevel, 1000.0)
+ compare(initialized.minZoomLevel, 100.0)
+ compare(initialized.target, Qt.vector3d(1.0, -1.0, 1.0))
+ compare(initialized.wrapXRotation, false)
+ compare(initialized.wrapYRotation, true)
+ compare(initialized.xRotation, 30.0)
+ compare(initialized.yRotation, 30.0)
+ compare(initialized.zoomLevel, 500.0)
+ }
+ }
+
+ TestCase {
+ name: "Camera3D Change"
+
+ function test_1_change() {
+ change.cameraPreset = Camera3D.CameraPresetBehind // Will be overridden by the the following sets
+ change.maxZoomLevel = 1000.0
+ change.minZoomLevel = 100.0
+ change.target = Qt.vector3d(1.0, -1.0, 1.0)
+ change.wrapXRotation = false
+ change.wrapYRotation = true
+ change.xRotation = 30.0
+ change.yRotation = 30.0
+ change.zoomLevel = 500.0
+
+ compare(change.cameraPreset, Camera3D.CameraPresetNone)
+ compare(change.maxZoomLevel, 1000.0)
+ compare(change.minZoomLevel, 100.0)
+ compare(change.target, Qt.vector3d(1.0, -1.0, 1.0))
+ compare(change.wrapXRotation, false)
+ compare(change.wrapYRotation, true)
+ compare(change.xRotation, 30.0)
+ compare(change.yRotation, 30.0)
+ compare(change.zoomLevel, 500.0)
+ }
+
+ function test_2_change_preset() {
+ change.cameraPreset = Camera3D.CameraPresetBehind // Sets target and rotations
+
+ compare(change.cameraPreset, Camera3D.CameraPresetBehind)
+ compare(change.maxZoomLevel, 1000.0)
+ compare(change.minZoomLevel, 100.0)
+ compare(change.target, Qt.vector3d(0.0, 0.0, 0.0))
+ compare(change.wrapXRotation, false)
+ compare(change.wrapYRotation, true)
+ compare(change.xRotation, 180.0)
+ compare(change.yRotation, 22.5)
+ compare(change.zoomLevel, 500.0)
+ }
+ }
+
+ TestCase {
+ name: "Camera3D Invalid"
+
+ function test_invalid() {
+ invalid.target = Qt.vector3d(-1.5, -1.5, -1.5)
+ compare(invalid.target, Qt.vector3d(-1.0, -1.0, -1.0))
+ invalid.target = Qt.vector3d(1.5, 1.5, 1.5)
+ compare(invalid.target, Qt.vector3d(1.0, 1.0, 1.0))
+ invalid.minZoomLevel = 0.1
+ compare(invalid.minZoomLevel, 1.0)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/scene3d/tst_light.qml b/tests/auto/qmltest/scene3d/tst_light.qml
new file mode 100644
index 00000000..d9fa282b
--- /dev/null
+++ b/tests/auto/qmltest/scene3d/tst_light.qml
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ // TODO: Has no adjustable properties yet.
+ // Keeping this as a placeholder for future implementations (QTRD-2406)
+ /*
+ Light3D {
+ id: initial
+ }
+
+ Light3D {
+ id: initialized
+ }
+
+
+ Light3D {
+ id: change
+ }
+
+ TestCase {
+ name: "Light3D Initial"
+
+ function test_initial() {
+ }
+ }
+
+ TestCase {
+ name: "Light3D Initialized"
+
+ function test_initialized() {
+ }
+ }
+
+ TestCase {
+ name: "Light3D Change"
+
+ function test_change() {
+ }
+ }
+ */
+}
diff --git a/tests/auto/qmltest/scene3d/tst_scene.qml b/tests/auto/qmltest/scene3d/tst_scene.qml
new file mode 100644
index 00000000..d53042ca
--- /dev/null
+++ b/tests/auto/qmltest/scene3d/tst_scene.qml
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ // Scene3D is uncreatable, so it needs to be accessed via a graph
+ Bars3D {
+ id: initial
+ }
+
+ Bars3D {
+ id: initialized
+ scene.activeCamera: Camera3D { zoomLevel: 200 }
+ scene.devicePixelRatio: 2.0
+ //scene.graphPositionQuery: Qt.point(0, 0) // TODO: Unusable until QTBUG-40043 is fixed
+ scene.primarySubViewport: Qt.rect(0, 0, 50, 50)
+ scene.secondarySubViewport: Qt.rect(50, 50, 100, 100)
+ scene.secondarySubviewOnTop: false
+ scene.selectionQueryPosition: Qt.point(0, 0)
+ scene.slicingActive: true
+ }
+
+ Bars3D {
+ id: change
+ }
+
+ Bars3D {
+ id: invalid
+ }
+
+ TestCase {
+ name: "Scene3D Initial"
+
+ function test_initial() {
+ verify(initial.scene.activeCamera)
+ verify(initial.scene.activeLight)
+ compare(initial.scene.devicePixelRatio, 1.0)
+ compare(initial.scene.graphPositionQuery, Qt.point(-1, -1))
+ compare(initial.scene.invalidSelectionPoint, Qt.point(-1, -1))
+ compare(initial.scene.primarySubViewport, Qt.rect(0, 0, 0, 0))
+ compare(initial.scene.secondarySubViewport, Qt.rect(0, 0, 0, 0))
+ compare(initial.scene.secondarySubviewOnTop, true)
+ compare(initial.scene.selectionQueryPosition, Qt.point(-1, -1))
+ compare(initial.scene.slicingActive, false)
+ compare(initial.scene.viewport, Qt.rect(0, 0, 0, 0))
+ }
+ }
+
+ TestCase {
+ name: "Scene3D Initialized"
+
+ function test_initialized() {
+ compare(initialized.scene.activeCamera.zoomLevel, 200)
+ compare(initialized.scene.devicePixelRatio, 2.0)
+ //compare(initialized.scene.graphPositionQuery, Qt.point(0, 0)) // TODO: Unusable until QTBUG-40043 is fixed
+ compare(initialized.scene.primarySubViewport, Qt.rect(0, 0, 50, 50))
+ compare(initialized.scene.secondarySubViewport, Qt.rect(50, 50, 100, 100))
+ compare(initialized.scene.secondarySubviewOnTop, false)
+ compare(initialized.scene.selectionQueryPosition, Qt.point(0, 0))
+ compare(initialized.scene.slicingActive, true)
+ compare(initialized.scene.viewport, Qt.rect(0, 0, 100, 100))
+ }
+ }
+
+ TestCase {
+ name: "Scene3D Change"
+
+ Camera3D {
+ id: camera1
+ zoomLevel: 200
+ }
+
+ function test_change() {
+ change.scene.activeCamera = camera1
+ change.scene.devicePixelRatio = 2.0
+ change.scene.graphPositionQuery = Qt.point(0, 0)
+ change.scene.primarySubViewport = Qt.rect(0, 0, 50, 50)
+ change.scene.secondarySubViewport = Qt.rect(50, 50, 100, 100)
+ change.scene.secondarySubviewOnTop = false
+ change.scene.selectionQueryPosition = Qt.point(0, 0) // TODO: When doing signal checks, add tests to check that queries return something (asynchronously)
+ change.scene.slicingActive = true
+
+ compare(change.scene.activeCamera.zoomLevel, 200)
+ compare(change.scene.devicePixelRatio, 2.0)
+ compare(change.scene.graphPositionQuery, Qt.point(0, 0))
+ compare(change.scene.primarySubViewport, Qt.rect(0, 0, 50, 50))
+ compare(change.scene.secondarySubViewport, Qt.rect(50, 50, 100, 100))
+ compare(change.scene.secondarySubviewOnTop, false)
+ compare(change.scene.selectionQueryPosition, Qt.point(0, 0))
+ compare(change.scene.slicingActive, true)
+ compare(change.scene.viewport, Qt.rect(0, 0, 100, 100))
+ }
+ }
+
+ TestCase {
+ name: "Scene3D Invalid"
+
+ function test_invalid() {
+ invalid.scene.primarySubViewport = Qt.rect(0, 0, -50, -50)
+ compare(invalid.scene.primarySubViewport, Qt.rect(0, 0, 0, 0))
+ }
+ }
+}
diff --git a/tests/auto/qmltest/surface3d/tst_basic.qml b/tests/auto/qmltest/surface3d/tst_basic.qml
new file mode 100644
index 00000000..dfcc4542
--- /dev/null
+++ b/tests/auto/qmltest/surface3d/tst_basic.qml
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Surface3D {
+ id: empty
+ }
+
+ Surface3D {
+ id: basic
+ anchors.fill: parent
+ flipHorizontalGrid: true
+ }
+
+ Surface3D {
+ id: common
+ anchors.fill: parent
+ }
+
+ Surface3D {
+ id: common_init
+ anchors.fill: parent
+ selectionMode: AbstractGraph3D.SelectionNone
+ shadowQuality: AbstractGraph3D.ShadowQualityLow
+ msaaSamples: 2
+ theme: Theme3D { }
+ renderingMode: AbstractGraph3D.RenderIndirect
+ measureFps: true
+ orthoProjection: false
+ aspectRatio: 3.0
+ optimizationHints: AbstractGraph3D.OptimizationStatic
+ polar: false
+ radialLabelOffset: 2
+ horizontalAspectRatio: 0.2
+ reflection: true
+ reflectivity: 0.1
+ locale: Qt.locale("UK")
+ margin: 0.2
+ }
+
+ TestCase {
+ name: "Surface3D Empty"
+
+ function test_empty() {
+ compare(empty.width, 0, "width")
+ compare(empty.height, 0, "height")
+ compare(empty.seriesList.length, 0, "seriesList")
+ compare(empty.selectedSeries, null, "selectedSeries")
+ compare(empty.flipHorizontalGrid, false, "flipHorizontalGrid")
+ compare(empty.axisX.orientation, AbstractAxis3D.AxisOrientationX)
+ compare(empty.axisZ.orientation, AbstractAxis3D.AxisOrientationZ)
+ compare(empty.axisY.orientation, AbstractAxis3D.AxisOrientationY)
+ compare(empty.axisX.type, AbstractAxis3D.AxisTypeValue)
+ compare(empty.axisZ.type, AbstractAxis3D.AxisTypeValue)
+ compare(empty.axisY.type, AbstractAxis3D.AxisTypeValue)
+ }
+ }
+
+ TestCase {
+ name: "Surface3D Basic"
+ when: windowShown
+
+ function test_basic() {
+ compare(basic.width, 150, "width")
+ compare(basic.height, 150, "height")
+ compare(basic.flipHorizontalGrid, true, "flipHorizontalGrid")
+ }
+
+ function test_change_basic() {
+ basic.flipHorizontalGrid = false
+ compare(basic.flipHorizontalGrid, false, "flipHorizontalGrid")
+ }
+ }
+
+ TestCase {
+ name: "Surface3D Common"
+ when: windowShown
+
+ function test_1_common() {
+ compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode")
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality")
+ if (common.shadowsSupported === true)
+ compare(common.msaaSamples, 4, "msaaSamples")
+ else
+ compare(common.msaaSamples, 0, "msaaSamples")
+ compare(common.theme.type, Theme3D.ThemeQt, "theme")
+ compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode")
+ compare(common.measureFps, false, "measureFps")
+ compare(common.customItemList.length, 0, "customItemList")
+ compare(common.orthoProjection, false, "orthoProjection")
+ compare(common.selectedElement, AbstractGraph3D.ElementNone, "selectedElement")
+ compare(common.aspectRatio, 2.0, "aspectRatio")
+ compare(common.optimizationHints, AbstractGraph3D.OptimizationDefault, "optimizationHints")
+ compare(common.polar, false, "polar")
+ compare(common.radialLabelOffset, 1, "radialLabelOffset")
+ compare(common.horizontalAspectRatio, 0, "horizontalAspectRatio")
+ compare(common.reflection, false, "reflection")
+ compare(common.reflectivity, 0.5, "reflectivity")
+ compare(common.locale, Qt.locale("C"), "locale")
+ compare(common.queriedGraphPosition, Qt.vector3d(0, 0, 0), "queriedGraphPosition")
+ compare(common.margin, -1, "margin")
+ }
+
+ function test_2_change_common() {
+ common.selectionMode = AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice
+ common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality")
+ common.msaaSamples = 8
+ if (common.shadowsSupported === true)
+ compare(common.msaaSamples, 8, "msaaSamples")
+ else
+ compare(common.msaaSamples, 0, "msaaSamples")
+ common.theme.type = Theme3D.ThemeRetro
+ common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear
+ common.measureFps = true
+ common.orthoProjection = true
+ common.aspectRatio = 1.0
+ common.optimizationHints = AbstractGraph3D.OptimizationStatic
+ common.polar = true
+ common.radialLabelOffset = 2
+ common.horizontalAspectRatio = 1
+ common.reflection = true
+ common.reflectivity = 1.0
+ common.locale = Qt.locale("FI")
+ common.margin = 1.0
+ compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode")
+ compare(common.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") // Ortho disables shadows
+ compare(common.msaaSamples, 0, "msaaSamples") // Rendering mode changes this to zero
+ compare(common.theme.type, Theme3D.ThemeRetro, "theme")
+ compare(common.renderingMode, AbstractGraph3D.RenderDirectToBackground_NoClear, "renderingMode")
+ compare(common.measureFps, true, "measureFps")
+ compare(common.orthoProjection, true, "orthoProjection")
+ compare(common.aspectRatio, 1.0, "aspectRatio")
+ compare(common.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints")
+ compare(common.polar, true, "polar")
+ compare(common.radialLabelOffset, 2, "radialLabelOffset")
+ compare(common.horizontalAspectRatio, 1, "horizontalAspectRatio")
+ compare(common.reflection, true, "reflection")
+ compare(common.reflectivity, 1.0, "reflectivity")
+ compare(common.locale, Qt.locale("FI"), "locale")
+ compare(common.margin, 1.0, "margin")
+ }
+
+ function test_3_change_invalid_common() {
+ common.selectionMode = AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionColumn | AbstractGraph3D.SelectionSlice
+ common.theme.type = -2
+ common.renderingMode = -1
+ common.measureFps = false
+ common.orthoProjection = false
+ common.aspectRatio = -1.0
+ common.polar = false
+ common.horizontalAspectRatio = -2
+ common.reflection = false
+ common.reflectivity = -1.0
+ compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode")
+ compare(common.theme.type, -2/*Theme3D.ThemeRetro*/, "theme") // TODO: Fix once QTRD-3367 is done
+ compare(common.renderingMode, -1/*AbstractGraph3D.RenderDirectToBackground_NoClear*/, "renderingMode") // TODO: Fix once QTRD-3367 is done
+ compare(common.aspectRatio, -1.0/*1.0*/, "aspectRatio") // TODO: Fix once QTRD-3367 is done
+ compare(common.horizontalAspectRatio, -2/*1*/, "horizontalAspectRatio") // TODO: Fix once QTRD-3367 is done
+ compare(common.reflectivity, -1.0/*1.0*/, "reflectivity") // TODO: Fix once QTRD-3367 is done
+ }
+
+ function test_4_common_initialized() {
+ compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode")
+ if (common_init.shadowsSupported === true) {
+ compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality")
+ compare(common_init.msaaSamples, 2, "msaaSamples")
+ } else {
+ compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality")
+ compare(common_init.msaaSamples, 0, "msaaSamples")
+ }
+ compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme")
+ compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode")
+ compare(common_init.measureFps, true, "measureFps")
+ compare(common_init.customItemList.length, 0, "customItemList")
+ compare(common_init.orthoProjection, false, "orthoProjection")
+ compare(common_init.aspectRatio, 3.0, "aspectRatio")
+ compare(common_init.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints")
+ compare(common_init.polar, false, "polar")
+ compare(common_init.radialLabelOffset, 2, "radialLabelOffset")
+ compare(common_init.horizontalAspectRatio, 0.2, "horizontalAspectRatio")
+ compare(common_init.reflection, true, "reflection")
+ compare(common_init.reflectivity, 0.1, "reflectivity")
+ compare(common_init.locale, Qt.locale("UK"), "locale")
+ compare(common_init.margin, 0.2, "margin")
+ }
+ }
+}
diff --git a/tests/auto/qmltest/surface3d/tst_heightproxy.qml b/tests/auto/qmltest/surface3d/tst_heightproxy.qml
new file mode 100644
index 00000000..29772451
--- /dev/null
+++ b/tests/auto/qmltest/surface3d/tst_heightproxy.qml
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ HeightMapSurfaceDataProxy {
+ id: initial
+ }
+
+ HeightMapSurfaceDataProxy {
+ id: initialized
+ heightMapFile: ":/customtexture.jpg"
+ maxXValue: 10.0
+ maxZValue: 10.0
+ minXValue: -10.0
+ minZValue: -10.0
+ }
+
+ HeightMapSurfaceDataProxy {
+ id: change
+ }
+
+ HeightMapSurfaceDataProxy {
+ id: invalid
+ }
+
+ TestCase {
+ name: "HeightMapSurfaceDataProxy Initial"
+
+ function test_initial() {
+ compare(initial.heightMapFile, "")
+ compare(initial.maxXValue, 10.0)
+ compare(initial.maxZValue, 10.0)
+ compare(initial.minXValue, 0)
+ compare(initial.minZValue, 0)
+
+ compare(initial.columnCount, 0)
+ compare(initial.rowCount, 0)
+ verify(!initial.series)
+
+ compare(initial.type, AbstractDataProxy.DataTypeSurface)
+ }
+ }
+
+ TestCase {
+ name: "HeightMapSurfaceDataProxy Initialized"
+
+ function test_initialized() {
+ compare(initialized.heightMapFile, ":/customtexture.jpg")
+ compare(initialized.maxXValue, 10.0)
+ compare(initialized.maxZValue, 10.0)
+ compare(initialized.minXValue, -10.0)
+ compare(initialized.minZValue, -10.0)
+
+ compare(initialized.columnCount, 24)
+ compare(initialized.rowCount, 24)
+ }
+ }
+
+ TestCase {
+ name: "HeightMapSurfaceDataProxy Change"
+
+ function test_1_change() {
+ change.heightMapFile = ":/customtexture.jpg"
+ change.maxXValue = 10.0
+ change.maxZValue = 10.0
+ change.minXValue = -10.0
+ change.minZValue = -10.0
+ }
+
+ function test_2_test_change() {
+ // This test has a dependency to the previous one due to asynchronous item model resolving
+ compare(change.heightMapFile, ":/customtexture.jpg")
+ compare(change.maxXValue, 10.0)
+ compare(change.maxZValue, 10.0)
+ compare(change.minXValue, -10.0)
+ compare(change.minZValue, -10.0)
+
+ compare(change.columnCount, 24)
+ compare(change.rowCount, 24)
+ }
+ }
+
+ TestCase {
+ name: "HeightMapSurfaceDataProxy Invalid"
+
+ function test_invalid() {
+ invalid.maxXValue = -10
+ compare(invalid.minXValue, -11)
+ invalid.minZValue = 20
+ compare(invalid.maxZValue, 21)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/surface3d/tst_proxy.qml b/tests/auto/qmltest/surface3d/tst_proxy.qml
new file mode 100644
index 00000000..8f353153
--- /dev/null
+++ b/tests/auto/qmltest/surface3d/tst_proxy.qml
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ ItemModelSurfaceDataProxy {
+ id: initial
+ }
+
+ ItemModelSurfaceDataProxy {
+ id: initialized
+
+ autoColumnCategories: false
+ autoRowCategories: false
+ columnCategories: ["colcat1", "colcat2"]
+ columnRole: "col"
+ columnRolePattern: /^.*-(\d\d)$/
+ columnRoleReplace: "\\1"
+ itemModel: ListModel { objectName: "model1" }
+ multiMatchBehavior: ItemModelSurfaceDataProxy.MMBAverage
+ rowCategories: ["rowcat1", "rowcat2"]
+ rowRole: "row"
+ rowRolePattern: /^(\d\d\d\d).*$/
+ rowRoleReplace: "\\1"
+ xPosRole: "x"
+ xPosRolePattern: /^.*-(\d\d)$/
+ xPosRoleReplace: "\\1"
+ yPosRole: "y"
+ yPosRolePattern: /^(\d\d\d\d).*$/
+ yPosRoleReplace: "\\1"
+ zPosRole: "z"
+ zPosRolePattern: /-/
+ zPosRoleReplace: "\\1"
+ }
+
+ ItemModelSurfaceDataProxy {
+ id: change
+ }
+
+ TestCase {
+ name: "ItemModelSurfaceDataProxy Initial"
+
+ function test_initial() {
+ compare(initial.autoColumnCategories, true)
+ compare(initial.autoRowCategories, true)
+ compare(initial.columnCategories, [])
+ compare(initial.columnRole, "")
+ verify(initial.columnRolePattern)
+ compare(initial.columnRoleReplace, "")
+ verify(!initial.itemModel)
+ compare(initial.multiMatchBehavior, ItemModelSurfaceDataProxy.MMBLast)
+ compare(initial.rowCategories, [])
+ compare(initial.rowRole, "")
+ verify(initial.rowRolePattern)
+ compare(initial.rowRoleReplace, "")
+ compare(initial.useModelCategories, false)
+ compare(initial.xPosRole, "")
+ verify(initial.xPosRolePattern)
+ compare(initial.xPosRoleReplace, "")
+ compare(initial.yPosRole, "")
+ verify(initial.yPosRolePattern)
+ compare(initial.yPosRoleReplace, "")
+ compare(initial.zPosRole, "")
+ verify(initial.zPosRolePattern)
+ compare(initial.zPosRoleReplace, "")
+
+ compare(initial.columnCount, 0)
+ compare(initial.rowCount, 0)
+ verify(!initial.series)
+
+ compare(initial.type, AbstractDataProxy.DataTypeSurface)
+ }
+ }
+
+ TestCase {
+ name: "ItemModelSurfaceDataProxy Initialized"
+
+ function test_initialized() {
+ compare(initialized.autoColumnCategories, false)
+ compare(initialized.autoRowCategories, false)
+ compare(initialized.columnCategories.length, 2)
+ compare(initialized.columnCategories[0], "colcat1")
+ compare(initialized.columnCategories[1], "colcat2")
+ compare(initialized.columnRole, "col")
+ compare(initialized.columnRolePattern, /^.*-(\d\d)$/)
+ compare(initialized.columnRoleReplace, "\\1")
+ compare(initialized.itemModel.objectName, "model1")
+ compare(initialized.multiMatchBehavior, ItemModelSurfaceDataProxy.MMBAverage)
+ compare(initialized.rowCategories.length, 2)
+ compare(initialized.rowCategories[0], "rowcat1")
+ compare(initialized.rowCategories[1], "rowcat2")
+ compare(initialized.rowRole, "row")
+ compare(initialized.rowRolePattern, /^(\d\d\d\d).*$/)
+ compare(initialized.rowRoleReplace, "\\1")
+ compare(initialized.xPosRole, "x")
+ compare(initialized.xPosRolePattern, /^.*-(\d\d)$/)
+ compare(initialized.xPosRoleReplace, "\\1")
+ compare(initialized.yPosRole, "y")
+ compare(initialized.yPosRolePattern, /^(\d\d\d\d).*$/)
+ compare(initialized.yPosRoleReplace, "\\1")
+ compare(initialized.zPosRole, "z")
+ compare(initialized.zPosRolePattern, /-/)
+ compare(initialized.zPosRoleReplace, "\\1")
+
+ compare(initialized.columnCount, 2)
+ compare(initialized.rowCount, 2)
+ }
+ }
+
+ TestCase {
+ name: "ItemModelSurfaceDataProxy Change"
+
+ ListModel { id: model1; objectName: "model1" }
+
+ function test_1_change() {
+ change.autoColumnCategories = false
+ change.autoRowCategories = false
+ change.columnCategories = ["colcat1", "colcat2"]
+ change.columnRole = "col"
+ change.columnRolePattern = /^.*-(\d\d)$/
+ change.columnRoleReplace = "\\1"
+ change.itemModel = model1
+ change.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBAverage
+ change.rowCategories = ["rowcat1", "rowcat2"]
+ change.rowRole = "row"
+ change.rowRolePattern = /^(\d\d\d\d).*$/
+ change.rowRoleReplace = "\\1"
+ change.useModelCategories = true // Overwrites columnLabels and rowLabels
+ change.xPosRole = "x"
+ change.xPosRolePattern = /^.*-(\d\d)$/
+ change.xPosRoleReplace = "\\1"
+ change.yPosRole = "y"
+ change.yPosRolePattern = /^(\d\d\d\d).*$/
+ change.yPosRoleReplace = "\\1"
+ change.zPosRole = "z"
+ change.zPosRolePattern = /-/
+ change.zPosRoleReplace = "\\1"
+ }
+
+ function test_2_test_change() {
+ // This test has a dependency to the previous one due to asynchronous item model resolving
+ compare(change.autoColumnCategories, false)
+ compare(change.autoRowCategories, false)
+ compare(change.columnCategories.length, 2)
+ compare(change.columnCategories[0], "colcat1")
+ compare(change.columnCategories[1], "colcat2")
+ compare(change.columnRole, "col")
+ compare(change.columnRolePattern, /^.*-(\d\d)$/)
+ compare(change.columnRoleReplace, "\\1")
+ compare(change.itemModel.objectName, "model1")
+ compare(change.multiMatchBehavior, ItemModelSurfaceDataProxy.MMBAverage)
+ compare(change.rowCategories.length, 2)
+ compare(change.rowCategories[0], "rowcat1")
+ compare(change.rowCategories[1], "rowcat2")
+ compare(change.rowRole, "row")
+ compare(change.rowRolePattern, /^(\d\d\d\d).*$/)
+ compare(change.rowRoleReplace, "\\1")
+ compare(change.useModelCategories, true)
+ compare(change.xPosRole, "x")
+ compare(change.xPosRolePattern, /^.*-(\d\d)$/)
+ compare(change.xPosRoleReplace, "\\1")
+ compare(change.yPosRole, "y")
+ compare(change.yPosRolePattern, /^(\d\d\d\d).*$/)
+ compare(change.yPosRoleReplace, "\\1")
+ compare(change.zPosRole, "z")
+ compare(change.zPosRolePattern, /-/)
+ compare(change.zPosRoleReplace, "\\1")
+
+ compare(change.columnCount, 0)
+ compare(change.rowCount, 0)
+ }
+ }
+
+ TestCase {
+ name: "ItemModelSurfaceDataProxy MultiMatchBehaviour"
+
+ Surface3D {
+ id: surface1
+ Surface3DSeries {
+ ItemModelSurfaceDataProxy {
+ id: surfaceProxy
+ itemModel: ListModel {
+ ListElement{ coords: "0,0"; data: "5"; }
+ ListElement{ coords: "0,0"; data: "15"; }
+ ListElement{ coords: "0,1"; data: "5"; }
+ ListElement{ coords: "0,1"; data: "15"; }
+ ListElement{ coords: "1,0"; data: "5"; }
+ ListElement{ coords: "1,0"; data: "15"; }
+ ListElement{ coords: "1,1"; data: "0"; }
+ }
+ rowRole: "coords"
+ columnRole: "coords"
+ yPosRole: "data"
+ rowRolePattern: /(\d),\d/
+ columnRolePattern: /(\d),(\d)/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\2"
+ }
+ }
+ }
+
+ function test_0_async_dummy() {
+ }
+
+ function test_1_test_multimatch() {
+ compare(surface1.axisY.max, 15)
+ }
+
+ function test_2_multimatch() {
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBFirst
+ }
+
+ function test_3_test_multimatch() {
+ compare(surface1.axisY.max, 5)
+ }
+
+ function test_4_multimatch() {
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBLast
+ }
+
+ function test_5_test_multimatch() {
+ compare(surface1.axisY.max, 15)
+ }
+
+ function test_6_multimatch() {
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBAverage
+ }
+
+ function test_7_test_multimatch() {
+ compare(surface1.axisY.max, 10)
+ }
+
+ function test_8_multimatch() {
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBCumulativeY
+ }
+
+ function test_9_test_multimatch() {
+ compare(surface1.axisY.max, 20)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/surface3d/tst_surface.qml b/tests/auto/qmltest/surface3d/tst_surface.qml
new file mode 100644
index 00000000..31c86da2
--- /dev/null
+++ b/tests/auto/qmltest/surface3d/tst_surface.qml
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Surface3D {
+ id: series
+ anchors.fill: parent
+ }
+
+ TestCase {
+ name: "Surface3D Series"
+
+ Surface3DSeries { id: series1 }
+ Surface3DSeries { id: series2 }
+
+ function test_1_add_series() {
+ series.seriesList = [series1, series2]
+ compare(series.seriesList.length, 2)
+ }
+
+ function test_2_remove_series() {
+ series.seriesList = [series1]
+ compare(series.seriesList.length, 1)
+ }
+
+ function test_3_remove_series() {
+ series.seriesList = []
+ compare(series.seriesList.length, 0)
+ }
+
+ function test_4_selected_series() {
+ series.seriesList = [series1, series2]
+ series.seriesList[0].selectedPoint = Qt.point(0, 0)
+ compare(series.selectedSeries, series1)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/surface3d/tst_surfaceseries.qml b/tests/auto/qmltest/surface3d/tst_surfaceseries.qml
new file mode 100644
index 00000000..dff2e4a8
--- /dev/null
+++ b/tests/auto/qmltest/surface3d/tst_surfaceseries.qml
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ height: 150
+ width: 150
+
+ Surface3DSeries {
+ id: initial
+ }
+
+ ColorGradient {
+ id: gradient1;
+ stops: [
+ ColorGradientStop { color: "red"; position: 0 },
+ ColorGradientStop { color: "blue"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient2;
+ stops: [
+ ColorGradientStop { color: "green"; position: 0 },
+ ColorGradientStop { color: "red"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient3;
+ stops: [
+ ColorGradientStop { color: "gray"; position: 0 },
+ ColorGradientStop { color: "darkgray"; position: 1 }
+ ]
+ }
+
+ Surface3DSeries {
+ id: initialized
+ dataProxy: ItemModelSurfaceDataProxy {
+ itemModel: ListModel {
+ ListElement{ longitude: "20"; latitude: "10"; pop_density: "4.75"; }
+ ListElement{ longitude: "21"; latitude: "10"; pop_density: "3.00"; }
+ }
+ rowRole: "longitude"
+ columnRole: "latitude"
+ yPosRole: "pop_density"
+ }
+ drawMode: Surface3DSeries.DrawSurface
+ flatShadingEnabled: false
+ selectedPoint: Qt.point(0, 0)
+ textureFile: ":\customtexture.jpg"
+
+ baseColor: "blue"
+ baseGradient: gradient1
+ colorStyle: Theme3D.ColorStyleObjectGradient
+ itemLabelFormat: "%f"
+ itemLabelVisible: false
+ mesh: Abstract3DSeries.MeshCube
+ meshRotation: Qt.quaternion(1, 1, 1, 1)
+ meshSmooth: true
+ multiHighlightColor: "green"
+ multiHighlightGradient: gradient2
+ name: "series1"
+ singleHighlightColor: "red"
+ singleHighlightGradient: gradient3
+ userDefinedMesh: ":/customitem.obj"
+ visible: false
+ }
+
+ ItemModelSurfaceDataProxy {
+ id: proxy1
+ itemModel: ListModel {
+ ListElement{ longitude: "20"; latitude: "10"; pop_density: "4.75"; }
+ ListElement{ longitude: "21"; latitude: "10"; pop_density: "3.00"; }
+ ListElement{ longitude: "22"; latitude: "10"; pop_density: "1.24"; }
+ }
+ rowRole: "longitude"
+ columnRole: "latitude"
+ yPosRole: "pop_density"
+ }
+
+ Surface3DSeries {
+ id: change
+ }
+
+ TestCase {
+ name: "Surface3DSeries Initial"
+
+ function test_1_initial() {
+ compare(initial.dataProxy.rowCount, 0)
+ compare(initial.invalidSelectionPosition, Qt.point(-1, -1))
+ compare(initial.drawMode, Surface3DSeries.DrawSurfaceAndWireframe)
+ compare(initial.flatShadingEnabled, true)
+ compare(initial.flatShadingSupported, true)
+ compare(initial.selectedPoint, Qt.point(-1, -1))
+ }
+
+ function test_2_initial_common() {
+ // Common properties
+ compare(initial.baseColor, "#000000")
+ compare(initial.baseGradient, null)
+ compare(initial.colorStyle, Theme3D.ColorStyleUniform)
+ compare(initial.itemLabel, "")
+ compare(initial.itemLabelFormat, "@xLabel, @yLabel, @zLabel")
+ compare(initial.itemLabelVisible, true)
+ compare(initial.mesh, Abstract3DSeries.MeshSphere)
+ compare(initial.meshRotation, Qt.quaternion(1, 0, 0, 0))
+ compare(initial.meshSmooth, false)
+ compare(initial.multiHighlightColor, "#000000")
+ compare(initial.multiHighlightGradient, null)
+ compare(initial.name, "")
+ compare(initial.singleHighlightColor, "#000000")
+ compare(initial.singleHighlightGradient, null)
+ compare(initial.type, Abstract3DSeries.SeriesTypeSurface)
+ compare(initial.userDefinedMesh, "")
+ compare(initial.visible, true)
+ }
+ }
+
+ TestCase {
+ name: "Surface3DSeries Initialized"
+
+ function test_1_initialized() {
+ compare(initialized.dataProxy.rowCount, 2)
+ compare(initialized.drawMode, Surface3DSeries.DrawSurface)
+ compare(initialized.flatShadingEnabled, false)
+ compare(initialized.selectedPoint, Qt.point(0, 0))
+ compare(initialized.textureFile, ":\customtexture.jpg")
+ }
+
+ function test_2_initialized_common() {
+ // Common properties
+ compare(initialized.baseColor, "#0000ff")
+ compare(initialized.baseGradient, gradient1)
+ compare(initialized.colorStyle, Theme3D.ColorStyleObjectGradient)
+ compare(initialized.itemLabelFormat, "%f")
+ compare(initialized.itemLabelVisible, false)
+ compare(initialized.mesh, Abstract3DSeries.MeshCube)
+ compare(initialized.meshRotation, Qt.quaternion(1, 1, 1, 1))
+ compare(initialized.meshSmooth, true)
+ compare(initialized.multiHighlightColor, "#008000")
+ compare(initialized.multiHighlightGradient, gradient2)
+ compare(initialized.name, "series1")
+ compare(initialized.singleHighlightColor, "#ff0000")
+ compare(initialized.singleHighlightGradient, gradient3)
+ compare(initialized.userDefinedMesh, ":/customitem.obj")
+ compare(initialized.visible, false)
+ }
+ }
+
+ TestCase {
+ name: "Surface3DSeries Change"
+
+ function test_1_change() {
+ change.dataProxy = proxy1
+ change.drawMode = Surface3DSeries.DrawSurface
+ change.flatShadingEnabled = false
+ change.selectedPoint = Qt.point(0, 0)
+ change.textureFile = ":\customtexture.jpg"
+ }
+
+ function test_2_test_change() {
+ // This test has a dependency to the previous one due to asynchronous item model resolving
+ compare(change.dataProxy.rowCount, 3)
+ compare(change.drawMode, Surface3DSeries.DrawSurface)
+ compare(change.flatShadingEnabled, false)
+ compare(change.selectedPoint, Qt.point(0, 0))
+ compare(change.textureFile, ":\customtexture.jpg")
+ }
+
+ function test_3_change_common() {
+ change.baseColor = "blue"
+ change.baseGradient = gradient1
+ change.colorStyle = Theme3D.ColorStyleObjectGradient
+ change.itemLabelFormat = "%f"
+ change.itemLabelVisible = false
+ change.mesh = Abstract3DSeries.MeshCube
+ change.meshRotation = Qt.quaternion(1, 1, 1, 1)
+ change.meshSmooth = true
+ change.multiHighlightColor = "green"
+ change.multiHighlightGradient = gradient2
+ change.name = "series1"
+ change.singleHighlightColor = "red"
+ change.singleHighlightGradient = gradient3
+ change.userDefinedMesh = ":/customitem.obj"
+ change.visible = false
+
+ compare(change.baseColor, "#0000ff")
+ compare(change.baseGradient, gradient1)
+ compare(change.colorStyle, Theme3D.ColorStyleObjectGradient)
+ compare(change.itemLabelFormat, "%f")
+ compare(change.itemLabelVisible, false)
+ compare(change.mesh, Abstract3DSeries.MeshCube)
+ compare(change.meshRotation, Qt.quaternion(1, 1, 1, 1))
+ compare(change.meshSmooth, true)
+ compare(change.multiHighlightColor, "#008000")
+ compare(change.multiHighlightGradient, gradient2)
+ compare(change.name, "series1")
+ compare(change.singleHighlightColor, "#ff0000")
+ compare(change.singleHighlightGradient, gradient3)
+ compare(change.userDefinedMesh, ":/customitem.obj")
+ compare(change.visible, false)
+ }
+
+ function test_4_change_gradient_stop() {
+ gradient1.stops[0].color = "yellow"
+ compare(change.baseGradient.stops[0].color, "#ffff00")
+ }
+ }
+}
diff --git a/tests/auto/qmltest/theme3d/tst_colorgradient.qml b/tests/auto/qmltest/theme3d/tst_colorgradient.qml
new file mode 100644
index 00000000..395b6672
--- /dev/null
+++ b/tests/auto/qmltest/theme3d/tst_colorgradient.qml
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ ColorGradient {
+ id: initial
+ }
+
+ ColorGradient {
+ id: initialized
+ stops: [
+ ColorGradientStop { color: "blue"; position: 0 },
+ ColorGradientStop { color: "white"; position: 0.5 },
+ ColorGradientStop { color: "red"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: change
+ }
+
+ TestCase {
+ name: "ColorGradient Initial"
+
+ function test_initial() {
+ compare(initial.stops.length, 0)
+ }
+ }
+
+ TestCase {
+ name: "ColorGradient Initialized"
+
+ function test_initialized() {
+ compare(initialized.stops.length, 3)
+ compare(initialized.stops[0].color, "#0000ff")
+ compare(initialized.stops[1].color, "#ffffff")
+ compare(initialized.stops[2].color, "#ff0000")
+ }
+ }
+
+ TestCase {
+ name: "ColorGradient Change"
+
+ ColorGradientStop { id: stop1; color: "blue"; position: 0 }
+ ColorGradientStop { id: stop2; color: "red"; position: 1.0 }
+ ColorGradientStop { id: stop3; color: "white"; position: 0.5 }
+
+ function test_change() {
+ change.stops = [stop1]
+ compare(change.stops.length, 1)
+ change.stops = [stop1, stop2]
+ compare(change.stops.length, 2)
+ compare(change.stops[0].color, "#0000ff")
+ change.stops[0].color = "red"
+ compare(change.stops[0].color, "#ff0000")
+ compare(change.stops[1].color, "#ff0000")
+ change.stops = [stop1, stop2, stop3]
+ compare(change.stops[2].color, "#ffffff")
+ compare(change.stops.length, 3)
+ stop2.position = 0.25
+ stop3.position = 1.0
+ compare(change.stops[1].position, 0.25)
+ compare(change.stops[2].position, 1.0)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/theme3d/tst_theme.qml b/tests/auto/qmltest/theme3d/tst_theme.qml
new file mode 100644
index 00000000..3e42b300
--- /dev/null
+++ b/tests/auto/qmltest/theme3d/tst_theme.qml
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ Theme3D {
+ id: initial
+ }
+
+ ColorGradient {
+ id: gradient1
+ stops: [
+ ColorGradientStop { color: "red"; position: 0 },
+ ColorGradientStop { color: "blue"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient2
+ stops: [
+ ColorGradientStop { color: "green"; position: 0 },
+ ColorGradientStop { color: "red"; position: 1 }
+ ]
+ }
+
+ ColorGradient {
+ id: gradient3
+ stops: [
+ ColorGradientStop { color: "gray"; position: 0 },
+ ColorGradientStop { color: "darkgray"; position: 1 }
+ ]
+ }
+
+ ThemeColor {
+ id: color1
+ color: "red"
+ }
+
+ ThemeColor {
+ id: color2
+ color: "blue"
+ }
+
+ Theme3D {
+ id: initialized
+ ambientLightStrength: 0.3
+ backgroundColor: "#ff0000"
+ backgroundEnabled: false
+ baseColors: [color1, color2]
+ baseGradients: [gradient1, gradient2]
+ colorStyle: Theme3D.ColorStyleRangeGradient
+ font.family: "Arial"
+ gridEnabled: false
+ gridLineColor: "#00ff00"
+ highlightLightStrength: 5.0
+ labelBackgroundColor: "#ff00ff"
+ labelBackgroundEnabled: false
+ labelBorderEnabled: false
+ labelTextColor: "#00ffff"
+ lightColor: "#ffff00"
+ lightStrength: 2.5
+ multiHighlightColor: "#ff00ff"
+ multiHighlightGradient: gradient3
+ singleHighlightColor: "#ff0000"
+ singleHighlightGradient: gradient3
+ type: Theme3D.ThemeQt // Default values will be overwritten by initialized values
+ windowColor: "#fff00f"
+ }
+
+ Theme3D {
+ id: change
+ }
+
+ Theme3D {
+ id: invalid
+ }
+
+ TestCase {
+ name: "Theme3D Initial"
+
+ Text { id: dummy }
+
+ function test_initial() {
+ compare(initial.ambientLightStrength, 0.25)
+ compare(initial.backgroundColor, "#000000")
+ compare(initial.backgroundEnabled, true)
+ compare(initial.baseColors.length, 1)
+ compare(initial.baseColors[0].color, "#000000")
+ compare(initial.baseGradients.length, 1)
+ compare(initial.baseGradients[0].stops[0].color, "#000000")
+ compare(initial.baseGradients[0].stops[1].color, "#ffffff")
+ compare(initial.colorStyle, Theme3D.ColorStyleUniform)
+ // Initial font needs to be tested like this, as different platforms have different default font (QFont())
+ compare(initial.font.family, dummy.font.family)
+ compare(initial.gridEnabled, true)
+ compare(initial.gridLineColor, "#ffffff")
+ compare(initial.highlightLightStrength, 7.5)
+ compare(initial.labelBackgroundColor, "#a0a0a4")
+ compare(initial.labelBackgroundEnabled, true)
+ compare(initial.labelBorderEnabled, true)
+ compare(initial.labelTextColor, "#ffffff")
+ compare(initial.lightColor, "#ffffff")
+ compare(initial.lightStrength, 5)
+ compare(initial.multiHighlightColor, "#0000ff")
+ compare(initial.multiHighlightGradient, null)
+ compare(initial.singleHighlightColor, "#ff0000")
+ compare(initial.singleHighlightGradient, null)
+ compare(initial.type, Theme3D.ThemeUserDefined)
+ compare(initial.windowColor, "#000000")
+ }
+ }
+
+ TestCase {
+ name: "Theme3D Initialized"
+
+ function test_initialized() {
+ compare(initialized.ambientLightStrength, 0.3)
+ compare(initialized.backgroundColor, "#ff0000")
+ compare(initialized.backgroundEnabled, false)
+ compare(initialized.baseColors.length, 2)
+ compare(initialized.baseColors[0].color, "#ff0000")
+ compare(initialized.baseColors[1].color, "#0000ff")
+ compare(initialized.baseGradients.length, 2)
+ compare(initialized.baseGradients[0], gradient1)
+ compare(initialized.baseGradients[1], gradient2)
+ compare(initialized.colorStyle, Theme3D.ColorStyleRangeGradient)
+ compare(initialized.font.family, "Arial")
+ compare(initialized.gridEnabled, false)
+ compare(initialized.gridLineColor, "#00ff00")
+ compare(initialized.highlightLightStrength, 5.0)
+ compare(initialized.labelBackgroundColor, "#ff00ff")
+ compare(initialized.labelBackgroundEnabled, false)
+ compare(initialized.labelBorderEnabled, false)
+ compare(initialized.labelTextColor, "#00ffff")
+ compare(initialized.lightColor, "#ffff00")
+ compare(initialized.lightStrength, 2.5)
+ compare(initialized.multiHighlightColor, "#ff00ff")
+ compare(initialized.multiHighlightGradient, gradient3)
+ compare(initialized.singleHighlightColor, "#ff0000")
+ compare(initialized.singleHighlightGradient, gradient3)
+ compare(initialized.type, Theme3D.ThemeQt)
+ compare(initialized.windowColor, "#fff00f")
+ }
+ }
+
+ TestCase {
+ name: "Theme3D Change"
+
+ ThemeColor {
+ id: color3
+ color: "red"
+ }
+
+ ColorGradient {
+ id: gradient4
+ stops: [
+ ColorGradientStop { color: "red"; position: 0 },
+ ColorGradientStop { color: "blue"; position: 1 }
+ ]
+ }
+
+ function test_1_change() {
+ change.type = Theme3D.ThemeStoneMoss // Default values will be overwritten by the following sets
+ change.ambientLightStrength = 0.3
+ change.backgroundColor = "#ff0000"
+ change.backgroundEnabled = false
+ change.baseColors = [color3, color2]
+ change.baseGradients = [gradient4, gradient2]
+ change.colorStyle = Theme3D.ColorStyleObjectGradient
+ change.font.family = "Arial"
+ change.gridEnabled = false
+ change.gridLineColor = "#00ff00"
+ change.highlightLightStrength = 5.0
+ change.labelBackgroundColor = "#ff00ff"
+ change.labelBackgroundEnabled = false
+ change.labelBorderEnabled = false
+ change.labelTextColor = "#00ffff"
+ change.lightColor = "#ffff00"
+ change.lightStrength = 2.5
+ change.multiHighlightColor = "#ff00ff"
+ change.multiHighlightGradient = gradient3
+ change.singleHighlightColor = "#ff0000"
+ change.singleHighlightGradient = gradient3
+ change.windowColor = "#fff00f"
+
+ compare(change.ambientLightStrength, 0.3)
+ compare(change.backgroundColor, "#ff0000")
+ compare(change.backgroundEnabled, false)
+ compare(change.baseColors.length, 2)
+ compare(change.baseColors[0].color, "#ff0000")
+ compare(change.baseColors[1].color, "#0000ff")
+ compare(change.baseGradients.length, 2)
+ compare(change.baseGradients[0], gradient4)
+ compare(change.baseGradients[1], gradient2)
+ compare(change.colorStyle, Theme3D.ColorStyleObjectGradient)
+ compare(change.font.family, "Arial")
+ compare(change.gridEnabled, false)
+ compare(change.gridLineColor, "#00ff00")
+ compare(change.highlightLightStrength, 5.0)
+ compare(change.labelBackgroundColor, "#ff00ff")
+ compare(change.labelBackgroundEnabled, false)
+ compare(change.labelBorderEnabled, false)
+ compare(change.labelTextColor, "#00ffff")
+ compare(change.lightColor, "#ffff00")
+ compare(change.lightStrength, 2.5)
+ compare(change.multiHighlightColor, "#ff00ff")
+ compare(change.multiHighlightGradient, gradient3)
+ compare(change.singleHighlightColor, "#ff0000")
+ compare(change.singleHighlightGradient, gradient3)
+ compare(change.type, Theme3D.ThemeStoneMoss)
+ compare(change.windowColor, "#fff00f")
+ }
+
+ function test_2_change_color() {
+ color3.color = "white"
+ compare(change.baseColors[0].color, "#ffffff")
+ }
+
+ function test_3_change_gradient() {
+ gradient4.stops[0].color = "black"
+ compare(change.baseGradients[0].stops[0].color, "#000000")
+ }
+ }
+
+
+ TestCase {
+ name: "Theme3D Invalid"
+
+ function test_invalid() {
+ invalid.ambientLightStrength = -1.0
+ compare(invalid.ambientLightStrength, 0.25)
+ invalid.ambientLightStrength = 1.1
+ compare(invalid.ambientLightStrength, 0.25)
+ invalid.highlightLightStrength = -1.0
+ compare(invalid.highlightLightStrength, 7.5)
+ invalid.highlightLightStrength = 10.1
+ compare(invalid.highlightLightStrength, 7.5)
+ invalid.lightStrength = -1.0
+ compare(invalid.lightStrength, 5.0)
+ invalid.lightStrength = 10.1
+ compare(invalid.lightStrength, 5.0)
+ }
+ }
+}
diff --git a/tests/auto/qmltest/theme3d/tst_themecolor.qml b/tests/auto/qmltest/theme3d/tst_themecolor.qml
new file mode 100644
index 00000000..421a5775
--- /dev/null
+++ b/tests/auto/qmltest/theme3d/tst_themecolor.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtDataVisualization 1.2
+import QtTest 1.0
+
+Item {
+ id: top
+ width: 150
+ height: 150
+
+ ThemeColor {
+ id: initial
+ }
+
+ ThemeColor {
+ id: initialized
+ color: "red"
+ }
+
+ ThemeColor {
+ id: change
+ }
+
+ TestCase {
+ name: "ThemeColor Initial"
+
+ function test_initial() {
+ compare(initial.color, "#000000")
+ }
+ }
+
+ TestCase {
+ name: "ThemeColor Initialized"
+
+ function test_initialized() {
+ compare(initialized.color, "#ff0000")
+ }
+ }
+
+ TestCase {
+ name: "ThemeColor Change"
+
+ function test_change() {
+ change.color = "blue"
+
+ compare(change.color, "#0000ff")
+ }
+ }
+}
diff --git a/tests/auto/qmltest/tst_qmltest.cpp b/tests/auto/qmltest/tst_qmltest.cpp
new file mode 100644
index 00000000..569d9150
--- /dev/null
+++ b/tests/auto/qmltest/tst_qmltest.cpp
@@ -0,0 +1,20 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(qmltest)
diff --git a/tests/barstest/barstest.pro b/tests/barstest/barstest.pro
index f213ea9e..56e24ef2 100644
--- a/tests/barstest/barstest.pro
+++ b/tests/barstest/barstest.pro
@@ -5,4 +5,6 @@
SOURCES += main.cpp chart.cpp custominputhandler.cpp
HEADERS += chart.h custominputhandler.h
+RESOURCES += barstest.qrc
+
QT += widgets
diff --git a/tests/barstest/barstest.qrc b/tests/barstest/barstest.qrc
new file mode 100644
index 00000000..f7237eb7
--- /dev/null
+++ b/tests/barstest/barstest.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>shuttle.obj</file>
+ <file>shuttle.png</file>
+ </qresource>
+</RCC>
diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp
index d3c38d85..e468700e 100644
--- a/tests/barstest/chart.cpp
+++ b/tests/barstest/chart.cpp
@@ -26,6 +26,7 @@
#include <QtDataVisualization/q3dcamera.h>
#include <QtDataVisualization/q3dtheme.h>
#include <QtDataVisualization/q3dinputhandler.h>
+#include <QtDataVisualization/qcustom3ditem.h>
#include <QtCore/QTime>
#include <QtCore/qmath.h>
@@ -203,7 +204,7 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog)
m_graph->activeTheme()->setFont(QFont("Times Roman", 20));
// Release and store the default input handler.
- m_defaultInputHandler = m_graph->activeInputHandler();
+ m_defaultInputHandler = static_cast<Q3DInputHandler *>(m_graph->activeInputHandler());
m_graph->releaseInputHandler(m_defaultInputHandler);
m_graph->setActiveInputHandler(m_defaultInputHandler);
@@ -241,7 +242,6 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog)
QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
&GraphModifier::handleFpsChange);
-
resetTemperatureData();
}
@@ -334,6 +334,9 @@ void GraphModifier::releaseSeries()
void GraphModifier::flipViews()
{
m_graph->scene()->setSecondarySubviewOnTop(!m_graph->scene()->isSecondarySubviewOnTop());
+ qDebug() << "secondary subview on top:" << m_graph->scene()->isSecondarySubviewOnTop();
+ qDebug() << "point (50, 50) in primary subview:" << m_graph->scene()->isPointInPrimarySubView(QPoint(50, 50));
+ qDebug() << "point (50, 50) in secondary subview:" << m_graph->scene()->isPointInSecondarySubView(QPoint(50, 50));
}
void GraphModifier::createMassiveArray()
@@ -626,33 +629,33 @@ void GraphModifier::changeTheme()
m_graph->setActiveTheme(m_builtinTheme);
switch (theme) {
- case Q3DTheme::ThemeQt:
- qDebug() << __FUNCTION__ << "ThemeQt";
- break;
- case Q3DTheme::ThemePrimaryColors:
- qDebug() << __FUNCTION__ << "ThemePrimaryColors";
- break;
- case Q3DTheme::ThemeDigia:
- qDebug() << __FUNCTION__ << "ThemeDigia";
- break;
- case Q3DTheme::ThemeStoneMoss:
- qDebug() << __FUNCTION__ << "ThemeStoneMoss";
- break;
- case Q3DTheme::ThemeArmyBlue:
- qDebug() << __FUNCTION__ << "ThemeArmyBlue";
- break;
- case Q3DTheme::ThemeRetro:
- qDebug() << __FUNCTION__ << "ThemeRetro";
- break;
- case Q3DTheme::ThemeEbony:
- qDebug() << __FUNCTION__ << "ThemeEbony";
- break;
- case Q3DTheme::ThemeIsabelle:
- qDebug() << __FUNCTION__ << "ThemeIsabelle";
- break;
- default:
- qDebug() << __FUNCTION__ << "Unknown theme";
- break;
+ case Q3DTheme::ThemeQt:
+ qDebug() << __FUNCTION__ << "ThemeQt";
+ break;
+ case Q3DTheme::ThemePrimaryColors:
+ qDebug() << __FUNCTION__ << "ThemePrimaryColors";
+ break;
+ case Q3DTheme::ThemeDigia:
+ qDebug() << __FUNCTION__ << "ThemeDigia";
+ break;
+ case Q3DTheme::ThemeStoneMoss:
+ qDebug() << __FUNCTION__ << "ThemeStoneMoss";
+ break;
+ case Q3DTheme::ThemeArmyBlue:
+ qDebug() << __FUNCTION__ << "ThemeArmyBlue";
+ break;
+ case Q3DTheme::ThemeRetro:
+ qDebug() << __FUNCTION__ << "ThemeRetro";
+ break;
+ case Q3DTheme::ThemeEbony:
+ qDebug() << __FUNCTION__ << "ThemeEbony";
+ break;
+ case Q3DTheme::ThemeIsabelle:
+ qDebug() << __FUNCTION__ << "ThemeIsabelle";
+ break;
+ default:
+ qDebug() << __FUNCTION__ << "Unknown theme";
+ break;
}
if (++theme > Q3DTheme::ThemeIsabelle)
@@ -1128,7 +1131,7 @@ void GraphModifier::changeValueAxisFormat(const QString & text)
void GraphModifier::changeLogBase(const QString &text)
{
QLogValue3DAxisFormatter *formatter =
- qobject_cast<QLogValue3DAxisFormatter *>(m_graph->valueAxis()->formatter());
+ qobject_cast<QLogValue3DAxisFormatter *>(m_graph->valueAxis()->formatter());
if (formatter)
formatter->setBase(qreal(text.toDouble()));
}
@@ -1406,6 +1409,26 @@ void GraphModifier::reverseValueAxis(int enabled)
m_graph->valueAxis()->setReversed(enabled);
}
+void GraphModifier::setInputHandlerRotationEnabled(int enabled)
+{
+ m_defaultInputHandler->setRotationEnabled(enabled);
+}
+
+void GraphModifier::setInputHandlerZoomEnabled(int enabled)
+{
+ m_defaultInputHandler->setZoomEnabled(enabled);
+}
+
+void GraphModifier::setInputHandlerSelectionEnabled(int enabled)
+{
+ m_defaultInputHandler->setSelectionEnabled(enabled);
+}
+
+void GraphModifier::setInputHandlerZoomAtTargetEnabled(int enabled)
+{
+ m_defaultInputHandler->setZoomAtTargetEnabled(enabled);
+}
+
void GraphModifier::changeValueAxisSegments(int value)
{
qDebug() << __FUNCTION__ << value;
@@ -1480,6 +1503,42 @@ void GraphModifier::handleFpsChange(qreal fps)
m_fpsLabel->setText(fpsPrefix + QString::number(qRound(fps)));
}
+void GraphModifier::setCameraTargetX(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setX(float(value) / 100.0f);
+ m_graph->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void GraphModifier::setCameraTargetY(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setY(float(value) / 100.0f);
+ m_graph->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void GraphModifier::setCameraTargetZ(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setZ(float(value) / 100.0f);
+ m_graph->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void GraphModifier::setFloorLevel(int value)
+{
+ m_graph->setFloorLevel(float(value));
+ qDebug() << "Floor level:" << value;
+}
+
+void GraphModifier::setGraphMargin(int value)
+{
+ m_graph->setMargin(qreal(value) / 100.0);
+ qDebug() << "Setting margin:" << m_graph->margin() << value;
+}
+
void GraphModifier::populateFlatSeries(QBar3DSeries *series, int rows, int columns, float value)
{
QBarDataArray *dataArray = new QBarDataArray;
@@ -1673,3 +1732,44 @@ void GraphModifier::toggleMultiseriesScaling()
{
m_graph->setMultiSeriesUniform(!m_graph->isMultiSeriesUniform());
}
+
+void GraphModifier::setReflection(bool enabled)
+{
+ m_graph->setReflection(enabled);
+}
+
+void GraphModifier::setReflectivity(int value)
+{
+ qreal reflectivity = (qreal)value / 100.0;
+ m_graph->setReflectivity(reflectivity);
+}
+
+void GraphModifier::toggleCustomItem()
+{
+ static int counter = 0;
+ int state = ++counter % 3;
+
+ QVector3D positionOne = QVector3D(6.0f, -15.0f, 3.0f);
+ QVector3D positionTwo = QVector3D(2.0f, 18.0f, 3.0f);
+
+ if (state == 0) {
+ m_graph->removeCustomItemAt(positionTwo);
+ } else if (state == 1) {
+ QCustom3DItem *item = new QCustom3DItem();
+ item->setMeshFile(":/shuttle.obj");
+ item->setPosition(positionOne);
+ item->setScaling(QVector3D(0.1f, 0.1f, 0.1f));
+ item->setRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rand()));
+ item->setTextureImage(QImage(":/shuttle.png"));
+ m_graph->addCustomItem(item);
+ } else {
+ m_graph->removeCustomItemAt(positionOne);
+ QCustom3DItem *item = new QCustom3DItem();
+ item->setMeshFile(":/shuttle.obj");
+ item->setPosition(positionTwo);
+ item->setScaling(QVector3D(0.1f, 0.1f, 0.1f));
+ item->setRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rand()));
+ item->setTextureImage(QImage(":/shuttle.png"));
+ m_graph->addCustomItem(item);
+ }
+}
diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h
index 47d29c25..9713d06c 100644
--- a/tests/barstest/chart.h
+++ b/tests/barstest/chart.h
@@ -20,7 +20,7 @@
#define CHARTMODIFIER_H
#include <QtDataVisualization/q3dbars.h>
-#include <QtDataVisualization/qabstract3dinputhandler.h>
+#include <QtDataVisualization/q3dinputhandler.h>
#include <QtDataVisualization/qbar3dseries.h>
#include <QtDataVisualization/q3dtheme.h>
#include <QFont>
@@ -94,6 +94,13 @@ public:
void addRemoveSeries();
void testItemAndRowChanges();
void reverseValueAxis(int enabled);
+ void setInputHandlerRotationEnabled(int enabled);
+ void setInputHandlerZoomEnabled(int enabled);
+ void setInputHandlerSelectionEnabled(int enabled);
+ void setInputHandlerZoomAtTargetEnabled(int enabled);
+ void setReflection(bool enabled);
+ void setReflectivity(int value);
+ void toggleCustomItem();
public slots:
void flipViews();
@@ -115,6 +122,11 @@ public slots:
void triggerRotation();
void handleValueAxisLabelsChanged();
void handleFpsChange(qreal fps);
+ void setCameraTargetX(int value);
+ void setCameraTargetY(int value);
+ void setCameraTargetZ(int value);
+ void setFloorLevel(int value);
+ void setGraphMargin(int value);
signals:
void shadowQualityChanged(int quality);
@@ -159,7 +171,7 @@ private:
QValue3DAxis *m_currentAxis;
bool m_negativeValuesOn;
bool m_useNullInputHandler;
- QAbstract3DInputHandler *m_defaultInputHandler;
+ Q3DInputHandler *m_defaultInputHandler;
Q3DTheme *m_ownTheme;
Q3DTheme *m_builtinTheme;
QTimer m_insertRemoveTimer;
@@ -169,6 +181,7 @@ private:
QTimer m_rotationTimer;
QLabel *m_fpsLabel;
QBar3DSeries *m_extraSeries;
+ QVector3D m_cameraTarget;
};
#endif
diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp
index 5ecf63a4..af033990 100644
--- a/tests/barstest/main.cpp
+++ b/tests/barstest/main.cpp
@@ -34,6 +34,18 @@
#include <QColorDialog>
#include <QLineEdit>
#include <QSpinBox>
+#include <QtGui/QOpenGLContext>
+
+static bool isOpenGLES()
+{
+#if defined(QT_OPENGL_ES_2)
+ return true;
+#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
+ return false;
+#else
+ return QOpenGLContext::currentContext()->isOpenGLES();
+#endif
+}
int main(int argc, char **argv)
{
@@ -43,13 +55,13 @@ int main(int argc, char **argv)
QHBoxLayout *hLayout = new QHBoxLayout(widget);
QVBoxLayout *vLayout = new QVBoxLayout();
QVBoxLayout *vLayout2 = new QVBoxLayout();
+ QVBoxLayout *vLayout3 = new QVBoxLayout();
// For testing custom surface format
QSurfaceFormat surfaceFormat;
surfaceFormat.setDepthBufferSize(24);
-#if !defined(QT_OPENGL_ES_2)
- surfaceFormat.setSamples(8);
-#endif
+ if (!isOpenGLES())
+ surfaceFormat.setSamples(8);
Q3DBars *widgetchart = new Q3DBars(&surfaceFormat);
QSize screenSize = widgetchart->screen()->size();
@@ -65,6 +77,7 @@ int main(int argc, char **argv)
hLayout->addWidget(container, 1);
hLayout->addLayout(vLayout);
hLayout->addLayout(vLayout2);
+ hLayout->addLayout(vLayout3);
QPushButton *addSeriesButton = new QPushButton(widget);
addSeriesButton->setText(QStringLiteral("Add / Remove a series"));
@@ -220,6 +233,22 @@ int main(int argc, char **argv)
staticCheckBox->setText("Use dynamic data");
staticCheckBox->setChecked(false);
+ QCheckBox *inputHandlerRotationCheckBox = new QCheckBox(widget);
+ inputHandlerRotationCheckBox->setText("IH: Allow rotation");
+ inputHandlerRotationCheckBox->setChecked(true);
+
+ QCheckBox *inputHandlerZoomCheckBox = new QCheckBox(widget);
+ inputHandlerZoomCheckBox->setText("IH: Allow zoom");
+ inputHandlerZoomCheckBox->setChecked(true);
+
+ QCheckBox *inputHandlerSelectionCheckBox = new QCheckBox(widget);
+ inputHandlerSelectionCheckBox->setText("IH: Allow selection");
+ inputHandlerSelectionCheckBox->setChecked(true);
+
+ QCheckBox *inputHandlerZoomAtTargetCheckBox = new QCheckBox(widget);
+ inputHandlerZoomAtTargetCheckBox->setText("IH: setZoomAtTarget");
+ inputHandlerZoomAtTargetCheckBox->setChecked(true);
+
QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget);
rotationSliderX->setTickInterval(1);
rotationSliderX->setMinimum(-180);
@@ -239,6 +268,23 @@ int main(int argc, char **argv)
ratioSlider->setValue(30);
ratioSlider->setMaximum(100);
+ QCheckBox *reflectionCheckBox = new QCheckBox(widget);
+ reflectionCheckBox->setText(QStringLiteral("Show reflections"));
+ reflectionCheckBox->setChecked(false);
+
+ QSlider *reflectivitySlider = new QSlider(Qt::Horizontal, widget);
+ reflectivitySlider->setMinimum(0);
+ reflectivitySlider->setValue(50);
+ reflectivitySlider->setMaximum(100);
+
+ QSlider *floorLevelSlider = new QSlider(Qt::Horizontal, widget);
+ floorLevelSlider->setMinimum(-50);
+ floorLevelSlider->setValue(0);
+ floorLevelSlider->setMaximum(50);
+
+ QPushButton *toggleCustomItemButton = new QPushButton(widget);
+ toggleCustomItemButton->setText(QStringLiteral("Toggle Custom Item"));
+
QSlider *spacingSliderX = new QSlider(Qt::Horizontal, widget);
spacingSliderX->setTickInterval(1);
spacingSliderX->setMinimum(0);
@@ -317,6 +363,27 @@ int main(int argc, char **argv)
valueAxisSegmentsSpin->setMaximum(100);
valueAxisSegmentsSpin->setValue(10);
+ QSlider *cameraTargetSliderX = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderX->setTickInterval(1);
+ cameraTargetSliderX->setMinimum(-100);
+ cameraTargetSliderX->setValue(0);
+ cameraTargetSliderX->setMaximum(100);
+ QSlider *cameraTargetSliderY = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderY->setTickInterval(1);
+ cameraTargetSliderY->setMinimum(-100);
+ cameraTargetSliderY->setValue(0);
+ cameraTargetSliderY->setMaximum(100);
+ QSlider *cameraTargetSliderZ = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderZ->setTickInterval(1);
+ cameraTargetSliderZ->setMinimum(-100);
+ cameraTargetSliderZ->setValue(0);
+ cameraTargetSliderZ->setMaximum(100);
+
+ QSlider *marginSlider = new QSlider(Qt::Horizontal, widget);
+ marginSlider->setMinimum(-1);
+ marginSlider->setValue(-1);
+ marginSlider->setMaximum(100);
+
vLayout->addWidget(addSeriesButton, 0, Qt::AlignTop);
vLayout->addWidget(addDataButton, 0, Qt::AlignTop);
vLayout->addWidget(addMultiDataButton, 0, Qt::AlignTop);
@@ -340,15 +407,15 @@ int main(int argc, char **argv)
vLayout->addWidget(insertRemoveTestButton, 0, Qt::AlignTop);
vLayout->addWidget(releaseAxesButton, 0, Qt::AlignTop);
vLayout->addWidget(releaseProxiesButton, 1, Qt::AlignTop);
- vLayout->addWidget(flipViewsButton, 0, Qt::AlignTop);
- vLayout->addWidget(changeColorStyleButton, 0, Qt::AlignTop);
- vLayout->addWidget(ownThemeButton, 0, Qt::AlignTop);
- vLayout->addWidget(primarySeriesTestsButton, 0, Qt::AlignTop);
- vLayout->addWidget(toggleRotationButton, 0, Qt::AlignTop);
- vLayout->addWidget(gradientBtoYPB, 0, Qt::AlignTop);
- vLayout->addWidget(logAxisButton, 0, Qt::AlignTop);
- vLayout->addWidget(testItemAndRowChangesButton, 1, Qt::AlignTop);
+ vLayout2->addWidget(flipViewsButton, 0, Qt::AlignTop);
+ vLayout2->addWidget(changeColorStyleButton, 0, Qt::AlignTop);
+ vLayout2->addWidget(ownThemeButton, 0, Qt::AlignTop);
+ vLayout2->addWidget(primarySeriesTestsButton, 0, Qt::AlignTop);
+ vLayout2->addWidget(toggleRotationButton, 0, Qt::AlignTop);
+ vLayout2->addWidget(gradientBtoYPB, 0, Qt::AlignTop);
+ vLayout2->addWidget(logAxisButton, 0, Qt::AlignTop);
+ vLayout2->addWidget(testItemAndRowChangesButton, 0, Qt::AlignTop);
vLayout2->addWidget(staticCheckBox, 0, Qt::AlignTop);
vLayout2->addWidget(rotationCheckBox, 0, Qt::AlignTop);
vLayout2->addWidget(rotationSliderX, 0, Qt::AlignTop);
@@ -365,25 +432,40 @@ int main(int argc, char **argv)
vLayout2->addWidget(minSliderX, 0, Qt::AlignTop);
vLayout2->addWidget(minSliderZ, 0, Qt::AlignTop);
vLayout2->addWidget(minSliderY, 0, Qt::AlignTop);
- vLayout2->addWidget(maxSliderY, 0, Qt::AlignTop);
- vLayout2->addWidget(fpsLabel, 0, Qt::AlignTop);
- vLayout2->addWidget(fpsCheckBox, 0, Qt::AlignTop);
- vLayout2->addWidget(reverseValueAxisCheckBox, 0, Qt::AlignTop);
- vLayout2->addWidget(backgroundCheckBox, 0, Qt::AlignTop);
- vLayout2->addWidget(gridCheckBox, 0, Qt::AlignTop);
- vLayout2->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")), 0, Qt::AlignTop);
- vLayout2->addWidget(shadowQuality, 0, Qt::AlignTop);
- vLayout2->addWidget(new QLabel(QStringLiteral("Change font")), 0, Qt::AlignTop);
- vLayout2->addWidget(fontList, 0, Qt::AlignTop);
- vLayout2->addWidget(new QLabel(QStringLiteral("Adjust font size")), 0, Qt::AlignTop);
- vLayout2->addWidget(fontSizeSlider, 0, Qt::AlignTop);
- vLayout2->addWidget(new QLabel(QStringLiteral("Value axis format")), 0, Qt::AlignTop);
- vLayout2->addWidget(valueAxisFormatEdit, 0, Qt::AlignTop);
- vLayout2->addWidget(new QLabel(QStringLiteral("Log axis base")), 0, Qt::AlignTop);
- vLayout2->addWidget(logBaseEdit, 0, Qt::AlignTop);
- vLayout2->addWidget(new QLabel(QStringLiteral("Value axis segments")), 0, Qt::AlignTop);
- vLayout2->addWidget(valueAxisSegmentsSpin, 0, Qt::AlignTop);
- // TODO: Add example for setMeshFileName
+ vLayout2->addWidget(maxSliderY, 1, Qt::AlignTop);
+
+ vLayout3->addWidget(fpsLabel, 0, Qt::AlignTop);
+ vLayout3->addWidget(fpsCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(reverseValueAxisCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(backgroundCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(gridCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(inputHandlerRotationCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(inputHandlerZoomCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(inputHandlerSelectionCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(inputHandlerZoomAtTargetCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")), 0, Qt::AlignTop);
+ vLayout3->addWidget(shadowQuality, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Change font")), 0, Qt::AlignTop);
+ vLayout3->addWidget(fontList, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Adjust font size")), 0, Qt::AlignTop);
+ vLayout3->addWidget(fontSizeSlider, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Value axis format")), 0, Qt::AlignTop);
+ vLayout3->addWidget(valueAxisFormatEdit, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Log axis base")), 0, Qt::AlignTop);
+ vLayout3->addWidget(logBaseEdit, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Value axis segments")), 0, Qt::AlignTop);
+ vLayout3->addWidget(valueAxisSegmentsSpin, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Camera target")), 0, Qt::AlignTop);
+ vLayout3->addWidget(cameraTargetSliderX, 0, Qt::AlignTop);
+ vLayout3->addWidget(cameraTargetSliderY, 0, Qt::AlignTop);
+ vLayout3->addWidget(cameraTargetSliderZ, 0, Qt::AlignTop);
+ vLayout3->addWidget(reflectionCheckBox, 0, Qt::AlignTop);
+ vLayout3->addWidget(reflectivitySlider, 0, Qt::AlignTop);
+ vLayout3->addWidget(toggleCustomItemButton, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Adjust floor level")), 0, Qt::AlignTop);
+ vLayout3->addWidget(floorLevelSlider, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Adjust margin")), 0, Qt::AlignTop);
+ vLayout3->addWidget(marginSlider, 1, Qt::AlignTop);
widget->show();
@@ -411,6 +493,12 @@ int main(int argc, char **argv)
&GraphModifier::setMinY);
QObject::connect(maxSliderY, &QSlider::valueChanged, modifier,
&GraphModifier::setMaxY);
+ QObject::connect(cameraTargetSliderX, &QSlider::valueChanged, modifier,
+ &GraphModifier::setCameraTargetX);
+ QObject::connect(cameraTargetSliderY, &QSlider::valueChanged, modifier,
+ &GraphModifier::setCameraTargetY);
+ QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier,
+ &GraphModifier::setCameraTargetZ);
QObject::connect(shadowQuality, SIGNAL(currentIndexChanged(int)), modifier,
SLOT(changeShadowQuality(int)));
@@ -488,7 +576,14 @@ int main(int argc, char **argv)
&GraphModifier::setBackgroundEnabled);
QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setGridEnabled);
-
+ QObject::connect(inputHandlerRotationCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setInputHandlerRotationEnabled);
+ QObject::connect(inputHandlerZoomCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setInputHandlerZoomEnabled);
+ QObject::connect(inputHandlerSelectionCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setInputHandlerSelectionEnabled);
+ QObject::connect(inputHandlerZoomAtTargetCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setInputHandlerZoomAtTargetEnabled);
QObject::connect(rotationCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setUseNullInputHandler);
@@ -501,6 +596,17 @@ int main(int argc, char **argv)
QObject::connect(rotationCheckBox, &QCheckBox::stateChanged, rotationSliderY,
&QSlider::setValue);
+ QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setReflection);
+ QObject::connect(reflectivitySlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::setReflectivity);
+ QObject::connect(floorLevelSlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::setFloorLevel);
+ QObject::connect(marginSlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::setGraphMargin);
+ QObject::connect(toggleCustomItemButton, &QPushButton::clicked, modifier,
+ &GraphModifier::toggleCustomItem);
+
QObject::connect(staticCheckBox, &QCheckBox::stateChanged, addDataButton,
&QPushButton::setEnabled);
QObject::connect(staticCheckBox, &QCheckBox::stateChanged, addMultiDataButton,
diff --git a/tests/barstest/shuttle.obj b/tests/barstest/shuttle.obj
new file mode 100644
index 00000000..e872228d
--- /dev/null
+++ b/tests/barstest/shuttle.obj
@@ -0,0 +1,6349 @@
+# Blender v2.66 (sub 0) OBJ File: ''
+# www.blender.org
+v -0.290924 -0.416888 -3.733797
+v -0.033253 -0.399965 -3.916942
+v 0.098892 -0.335648 -4.014968
+v 0.110144 -0.419894 -4.016005
+v -0.014627 -0.351984 -3.919619
+v -0.003375 -0.436229 -3.920655
+v 0.077510 -0.338845 -3.987319
+v 0.088762 -0.423090 -3.988356
+v 0.268419 -0.311615 -4.127978
+v 0.279672 -0.395861 -4.129015
+v -0.216945 -0.357152 -3.636585
+v -0.442480 -0.421494 -3.440589
+v 0.040557 -0.350366 -3.819523
+v -0.366883 -0.356351 -3.352865
+v -0.059636 -0.414591 -3.440213
+v 0.195164 -0.388461 -3.624492
+v -0.508658 -0.419445 -3.066802
+v -0.205255 -0.426666 -3.173799
+v -0.446409 -0.327120 -2.985436
+v -0.301084 -0.444031 -2.817294
+v 0.151449 -0.262081 -3.165398
+v 0.405315 -0.264481 -3.348743
+v 0.818570 -0.244023 -3.649532
+v 0.829822 -0.328268 -3.650568
+v 0.438945 -0.298561 -3.337861
+v 0.450197 -0.382806 -3.338897
+v 0.010751 -0.257630 -2.926187
+v 0.476024 -0.294278 -3.283418
+v 0.487276 -0.378524 -3.284455
+v 0.893196 -0.234640 -3.602006
+v 0.904448 -0.318886 -3.603042
+v -0.575146 -0.405155 -2.358083
+v -0.526846 -0.291701 -2.296914
+v -0.123567 -0.205514 -2.586394
+v -0.401501 -0.443667 -2.166967
+v 0.438519 -0.375468 -2.805563
+v 0.688118 -0.342410 -2.991890
+v 0.304996 -0.393955 -2.597466
+v -0.651696 -0.393273 -1.762192
+v -0.615966 -0.265853 -1.716287
+v -0.263638 -0.153966 -1.992210
+v 0.143186 -0.430595 -2.279088
+v -0.512839 -0.438185 -1.613670
+v -0.710028 -0.388098 -1.448538
+v -0.679965 -0.261349 -1.410043
+v -0.813314 -0.388657 -1.206769
+v 0.751287 -0.156495 -2.401505
+v 0.998622 -0.165467 -2.585768
+v -0.031714 -0.447371 -1.755604
+v -0.583894 -0.434779 -1.325088
+v -0.408965 -0.117462 -1.482493
+v -0.790573 -0.268054 -1.169135
+v 0.625851 -0.150158 -2.234187
+v -1.051885 -0.397310 -0.885646
+v -1.031232 -0.284379 -0.851625
+v -0.707410 -0.437077 -1.088477
+v -1.334166 -0.408930 -0.570468
+v 0.404917 -0.087146 -1.938298
+v -0.498689 -0.116097 -1.212783
+v -1.314745 -0.307881 -0.538558
+v -0.959224 -0.446984 -0.774490
+v 1.798825 0.967755 -3.015114
+v 2.046406 0.986555 -3.209870
+v 1.795686 1.006585 -3.015357
+v 2.043267 1.025386 -3.210112
+v 1.806945 0.929656 -3.008470
+v 2.054526 0.948456 -3.203225
+v 1.797665 1.044451 -3.009186
+v 2.045245 1.063251 -3.203941
+v -0.218636 -0.452499 -1.294639
+v -0.644958 -0.132219 -0.977335
+v 1.804675 1.079696 -2.996872
+v 2.052256 1.098496 -3.191627
+v -1.248682 -0.457011 -0.466167
+v 1.840314 0.837931 -2.974904
+v 2.087895 0.856731 -3.169660
+v 1.646588 -0.144702 -2.733368
+v 1.657840 -0.228948 -2.734405
+v 1.737599 -0.131659 -2.805524
+v 1.748852 -0.215905 -2.806561
+v 1.819258 1.148577 -2.971684
+v 1.033279 0.836881 -2.319959
+v 1.031777 0.970096 -2.298396
+v 1.060381 0.758531 -2.296534
+v -0.899523 -0.158764 -0.676483
+v 0.945538 0.814187 -2.200939
+v 0.170080 -0.031614 -1.496071
+v 1.043881 1.032762 -2.270612
+v 0.966443 0.743790 -2.180778
+v 0.952423 0.936921 -2.181507
+v 1.858652 1.239977 -2.912781
+v -0.317238 -0.452926 -1.060344
+v -1.192492 -0.196285 -0.374663
+v 1.856681 0.583228 -2.822182
+v 2.018811 -0.094129 -2.886386
+v 0.967854 0.995533 -2.156932
+v 1.272977 -0.200327 -2.268122
+v 1.284228 -0.284573 -2.269158
+v 1.076506 1.117039 -2.211863
+v -0.484562 -0.458987 -0.834904
+v 1.848687 0.788896 -2.757349
+v 2.078178 -0.164712 -2.848704
+v 1.967059 0.229003 -2.794663
+v 1.967832 0.643840 -2.831535
+v 2.215413 0.662641 -3.026290
+v 0.936685 0.816251 -2.042840
+v 2.039912 0.250063 -2.839813
+v 1.003257 1.074974 -2.104949
+v 1.164321 0.598239 -2.181823
+v 0.950019 0.768391 -2.028775
+v 0.943810 0.900014 -2.031005
+v 2.521226 0.390336 -3.213021
+v 1.936351 1.129229 -2.824698
+v 2.183932 1.148030 -3.019454
+v 2.568967 0.404915 -3.250601
+v 1.055366 0.603082 -2.081103
+v 0.869869 0.465058 -1.923758
+v 0.936491 -0.173930 -1.917023
+v 2.705161 0.067033 -3.318110
+v 2.096944 0.264363 -2.850901
+v 1.957182 0.386704 -2.747152
+v 2.029313 0.077472 -2.774992
+v 0.955724 0.940103 -2.015164
+v 1.928438 1.372002 -2.811322
+v -0.765050 -0.471609 -0.536370
+v 2.030367 0.402476 -2.793896
+v 2.100080 0.103611 -2.820803
+v 2.514476 0.522913 -3.173254
+v 2.574949 0.263657 -3.196595
+v 2.562289 0.536088 -3.211256
+v 2.622122 0.279579 -3.234349
+v 2.598488 0.409527 -3.214962
+v -0.064938 0.006053 -1.100592
+v 2.088244 0.403277 -2.809050
+v 2.151782 0.130884 -2.833575
+v 2.199769 1.159790 -2.964559
+v 2.193588 1.233527 -2.965299
+v 2.221596 1.161935 -2.979915
+v 2.215481 1.234891 -2.980646
+v 2.136538 0.271664 -2.829406
+v 1.137830 1.220917 -2.137315
+v 0.872108 0.654248 -1.875077
+v 0.961190 -0.247342 -1.861737
+v 2.739614 0.051854 -3.275776
+v -2.850674 0.163164 1.082772
+v -2.774223 -0.444295 1.078949
+v -2.508742 -0.433973 0.871016
+v -2.566129 0.205717 0.857016
+v 0.982043 0.994775 -1.981001
+v -1.069794 -0.481435 -0.242789
+v 2.592936 0.518564 -3.182256
+v 2.642673 0.305340 -3.201453
+v -2.926099 0.145051 1.155134
+v -2.869277 -0.451273 1.165592
+v 1.009718 0.673075 -1.961110
+v 2.235091 1.170649 -2.961918
+v 1.045386 1.162465 -2.033586
+v 2.230007 1.231294 -2.962527
+v 2.325998 0.322675 -2.951759
+v 1.972627 1.185781 -2.753756
+v 2.129131 0.389939 -2.793774
+v 2.183229 0.158016 -2.814654
+v 2.227766 1.099078 -2.934829
+v 1.117407 -0.306283 -1.939579
+v 1.981589 1.144577 -2.746340
+v 1.973910 1.228192 -2.748031
+v 2.249297 1.101867 -2.950499
+v 2.210880 1.300532 -2.936848
+v 1.886303 0.951739 -2.651532
+v 2.165588 -0.207364 -2.762840
+v 1.360371 -0.269775 -2.128519
+v 2.232590 1.301186 -2.952497
+v 2.160973 0.274542 -2.798066
+v 0.937019 0.410104 -1.852872
+v 0.960798 0.265444 -1.858128
+v 2.041237 1.191461 -2.784518
+v 2.048705 1.157124 -2.778339
+v 2.042305 1.226803 -2.779748
+v 2.258118 1.120717 -2.937466
+v 2.320933 0.422137 -2.921925
+v 2.366302 0.227636 -2.939436
+v 1.976683 1.431691 -2.744228
+v 2.244230 1.286403 -2.939127
+v -3.086442 -0.474834 1.379359
+v 1.999431 1.110853 -2.726914
+v 1.985242 1.265353 -2.730038
+v -3.023720 -0.458462 1.334241
+v -3.024019 0.109883 1.282283
+v -2.844489 0.344693 1.122684
+v -2.737176 -0.511595 1.117650
+v -2.469550 -0.506414 0.908517
+v -2.551327 0.395732 0.889431
+v 1.000046 -0.329019 -1.811242
+v 2.154800 0.373104 -2.768372
+v 2.199881 0.179835 -2.785773
+v -2.914064 0.322285 1.189905
+v -2.834171 -0.518740 1.204847
+v 1.179604 1.267508 -2.087104
+v 2.051748 1.257770 -2.764754
+v 2.063573 1.129020 -2.762150
+v 2.080589 1.195258 -2.775463
+v -0.196284 0.002337 -0.889996
+v 1.013546 1.060481 -1.927888
+v -3.072142 -0.445996 1.396327
+v 2.603875 0.637948 -3.126857
+v 2.707508 0.193661 -3.166857
+v 2.085482 1.172761 -2.771414
+v 2.081289 1.218414 -2.772337
+v 1.075555 1.201415 -1.986324
+v 2.002330 0.508317 -2.645192
+v 2.127263 -0.027287 -2.693413
+v 2.556508 0.625865 -3.087951
+v 2.661251 0.176820 -3.128379
+v 2.074001 0.520013 -2.695353
+v 2.194747 0.002364 -2.741958
+v 1.000951 0.830916 -1.880131
+v 2.095224 1.154349 -2.760807
+v 2.087476 1.238703 -2.762513
+v 2.023438 1.089743 -2.698434
+v 2.004898 1.291606 -2.702516
+v -3.122983 -0.461461 1.459483
+v -3.158215 -0.041663 1.448402
+v -2.497997 0.004146 0.929450
+v -2.509563 0.149734 0.925099
+v 1.003036 0.858653 -1.877204
+v 2.270079 1.067661 -2.884072
+v 1.005703 0.814744 -1.874867
+v 2.291162 1.070783 -2.900280
+v 2.128013 0.510403 -2.719236
+v 2.238064 0.038604 -2.761713
+v 2.068128 1.279648 -2.741818
+v 2.083579 1.111428 -2.738417
+v 2.247011 1.342852 -2.886831
+v 2.268338 1.343057 -2.903010
+v -2.993610 -0.524459 1.373357
+v -2.994892 0.277606 1.300703
+v 2.627505 0.603237 -3.112099
+v 2.713651 0.233920 -3.145348
+v 2.292918 1.094878 -2.895721
+v 1.006938 0.871768 -1.872413
+v 2.105035 1.197866 -2.757530
+v 2.093582 1.187722 -2.747088
+v 2.092232 1.203828 -2.747250
+v 2.273946 1.321208 -2.897990
+v 0.912103 0.803812 -1.788658
+v 1.019079 -0.292614 -1.771378
+v 2.804159 0.046327 -3.194257
+v 2.107537 1.186366 -2.755460
+v 2.105394 1.209702 -2.755932
+v 2.245164 0.292374 -2.778011
+v 0.747334 -0.381459 -1.545246
+v 2.108331 1.142823 -2.745258
+v 2.098208 1.253036 -2.747486
+v 2.099697 1.174463 -2.740594
+v 2.096009 1.218463 -2.741035
+v 2.112516 1.176954 -2.750038
+v 2.108556 1.220074 -2.750911
+v 1.015678 0.889476 -1.861661
+v 2.162992 0.481149 -2.717304
+v 2.256692 0.079446 -2.753470
+v 1.035607 1.090416 -1.893256
+v 2.119216 1.171063 -2.742090
+v 2.114042 1.227400 -2.743229
+v -3.036469 -0.488219 1.432582
+v 1.026486 0.781917 -1.851175
+v 2.241119 0.356951 -2.758555
+v 2.270656 0.230325 -2.769956
+v -0.400720 -0.022989 -0.663568
+v 2.108939 1.167601 -2.729508
+v 2.352467 0.499374 -2.857929
+v 2.431048 0.162488 -2.888259
+v 2.103901 1.227706 -2.730110
+v 2.078094 0.869479 -2.669583
+v 2.325675 0.888279 -2.864338
+v 2.049954 1.084459 -2.665236
+v 2.029886 1.302955 -2.669655
+v 2.105674 1.107026 -2.710752
+v 2.088951 1.289105 -2.714434
+v 2.107442 1.198835 -2.720560
+v 2.122808 1.139938 -2.727132
+v 2.111851 1.259233 -2.729544
+v 2.126617 1.169588 -2.732825
+v 2.121016 1.230568 -2.734058
+v 2.126010 1.200245 -2.735167
+v 2.126854 1.200309 -2.735831
+v -3.255541 -0.463337 1.614298
+v -3.256979 -0.068785 1.579187
+v -3.090151 -0.525402 1.492390
+v -3.140838 0.071873 1.477060
+v 2.183017 0.449113 -2.704647
+v 2.261101 0.114361 -2.734786
+v 1.025626 0.910940 -1.843900
+v 2.118830 1.168976 -2.716800
+v 2.113792 1.229081 -2.717403
+v 2.133590 1.172756 -2.723654
+v 2.128416 1.229093 -2.724793
+v -2.405642 -0.551479 0.981773
+v -2.505313 0.545503 0.958747
+v 1.032741 0.920593 -1.832253
+v 2.139077 1.180082 -2.715972
+v 2.135116 1.223202 -2.716844
+v 2.126722 1.178219 -2.705876
+v 2.123034 1.222219 -2.706317
+v 2.136451 1.146135 -2.709190
+v 2.126328 1.256348 -2.711419
+v 2.142239 1.190453 -2.710951
+v 2.140096 1.213790 -2.711423
+v 2.330166 1.100055 -2.847870
+v 2.130499 1.192853 -2.699662
+v 2.129149 1.208959 -2.699824
+v 2.142597 1.202290 -2.709353
+v 2.311194 1.326385 -2.850139
+v -0.678415 -0.058034 -0.389918
+v 2.126498 1.116483 -2.683367
+v 2.111047 1.284703 -2.686769
+v 2.335972 1.077011 -2.842714
+v 2.315368 1.073955 -2.825890
+v 2.297923 0.301154 -2.738834
+v 2.313149 1.349285 -2.845444
+v -3.224716 -0.523658 1.644234
+v -3.228977 0.037748 1.596002
+v 2.259607 0.406750 -2.716804
+v 2.310765 0.187427 -2.736550
+v 2.292301 1.349146 -2.828649
+v 1.051190 0.836263 -1.810548
+v 2.074942 1.095809 -2.632374
+v 2.056402 1.297672 -2.636457
+v 1.051411 0.836718 -1.810222
+v 1.051764 0.833314 -1.810103
+v 2.147183 1.160469 -2.694164
+v 2.139435 1.244822 -2.695870
+v 1.051937 0.833724 -1.809843
+v 1.051832 0.835286 -1.809826
+v 1.052011 0.834070 -1.809716
+v 1.052027 0.833906 -1.809712
+v 1.051999 0.834825 -1.809658
+v 1.051961 0.835391 -1.809651
+v 1.052122 0.834297 -1.809553
+v 2.289008 0.296555 -2.724424
+v 1.946327 1.084103 -2.529010
+v 2.264997 -0.237532 -2.656099
+v -3.146048 -0.468059 1.586396
+v 2.295856 0.334163 -2.728890
+v 2.310954 0.269436 -2.734717
+v 2.287534 0.325511 -2.715739
+v 2.300741 0.268886 -2.720837
+v 2.153370 1.180757 -2.684340
+v 2.149177 1.226410 -2.685263
+v -2.995660 -0.411552 1.480706
+v 1.055648 0.923143 -1.800017
+v 2.154070 1.203913 -2.681214
+v -0.987628 -0.106173 -0.106669
+v -3.134648 -0.466717 1.601017
+v 2.142878 1.138361 -2.660432
+v 2.131053 1.267111 -2.663036
+v 2.305306 0.359619 -2.707547
+v 2.331457 0.247507 -2.717641
+v 2.296714 0.347997 -2.697108
+v 2.319591 0.249920 -2.705938
+v 2.094598 1.122062 -2.604852
+v 2.080408 1.276562 -2.607976
+v 0.470274 -0.415629 -1.195124
+v 2.682583 0.683203 -3.020018
+v 2.802250 0.170184 -3.066206
+v 2.692932 0.640855 -3.023288
+v 2.792405 0.214405 -3.061682
+v 2.636060 0.671604 -2.979968
+v 2.757006 0.153092 -3.026651
+v 2.359883 1.134861 -2.806733
+v 2.152321 1.169328 -2.645438
+v 2.145921 1.239007 -2.646847
+v 2.345995 1.300547 -2.808394
+v 2.153390 1.204670 -2.640668
+v 2.412149 0.533689 -2.776916
+v 2.502886 0.144687 -2.811939
+v 0.970224 0.925229 -1.686331
+v 1.091354 -0.325301 -1.665935
+v 2.881699 0.045075 -3.095805
+v 2.371721 1.118882 -2.793227
+v 2.351501 1.116275 -2.775872
+v -3.112347 -0.464090 1.629621
+v 2.105930 1.159222 -2.586859
+v 2.098250 1.242837 -2.588550
+v 2.355013 1.318201 -2.795225
+v 2.334614 1.317729 -2.777893
+v 2.314089 0.357987 -2.673523
+v 2.340505 0.244738 -2.683719
+v 1.104303 1.101391 -1.798523
+v 2.090404 0.561258 -2.516102
+v 2.159124 0.571179 -2.570591
+v 2.234664 -0.057203 -2.571783
+v 2.205596 0.557038 -2.605525
+v 2.229047 0.520855 -2.620486
+v 2.238063 0.482200 -2.623966
+v 2.298549 -0.026551 -2.624406
+v 2.332671 0.012252 -2.654572
+v 2.295672 0.428428 -2.663943
+v 2.328226 0.095662 -2.658767
+v 2.337243 0.057009 -2.662247
+v 2.319328 0.300003 -2.670594
+v 2.323741 0.370700 -2.680527
+v 2.354745 0.175176 -2.686744
+v 2.343450 0.307384 -2.690079
+v 2.353938 0.241245 -2.692182
+v 1.085896 0.905799 -1.761274
+v 2.107213 1.201633 -2.581135
+v 1.172914 1.208536 -1.853034
+v -3.524472 -0.463065 1.962955
+v -3.542270 -0.167984 1.949735
+v 2.374104 1.189970 -2.783334
+v 2.369021 1.250615 -2.783942
+v 1.303689 1.278820 -1.940955
+v -2.749923 0.604098 1.287890
+v -2.601352 -0.583194 1.281085
+v -2.331104 -0.584296 1.070484
+v -2.444929 0.666788 1.044342
+v 2.335002 0.352805 -2.651304
+v 2.357880 0.254727 -2.660134
+v -2.812832 0.575628 1.346300
+v -2.702155 -0.590712 1.367115
+v 2.388829 1.185178 -2.765078
+v 2.382714 1.258133 -2.765809
+v 2.346222 0.364438 -2.655067
+v 2.372374 0.252327 -2.665161
+v 2.368793 1.183281 -2.747422
+v 2.362612 1.257019 -2.748161
+v -3.502416 -0.514638 1.987533
+v -3.528153 -0.092777 1.968860
+v -2.930290 -0.491180 1.547024
+v 2.066685 0.851305 -2.470213
+v -3.080119 -0.460294 1.670958
+v 2.353852 0.333838 -2.636405
+v 2.367060 0.277213 -2.641503
+v 2.140192 0.869385 -2.515599
+v -2.873451 -0.595312 1.528083
+v -2.875654 0.517249 1.427634
+v 2.123721 1.449962 -2.555544
+v 2.366726 0.342510 -2.637991
+v 2.381824 0.277783 -2.643818
+v 2.365586 0.306170 -2.632818
+v 0.180044 -0.433739 -0.860263
+v 2.390810 0.196854 -2.633883
+v 2.339653 0.416178 -2.614137
+v 2.379757 0.310792 -2.633873
+v -2.972414 -0.594417 1.632153
+v -3.043199 0.236465 1.611041
+v 2.197619 0.881876 -2.526829
+v 1.155619 0.140929 -1.644002
+v 2.061041 1.010198 -2.426111
+v 2.124709 0.698580 -2.447134
+v 2.629037 1.066507 -2.869623
+v 2.771686 0.621339 -2.939622
+v 2.857832 0.252023 -2.972872
+v 2.676305 1.087418 -2.904726
+v 2.483987 0.515887 -2.700598
+v 2.562568 0.179001 -2.730927
+v 2.134736 1.022953 -2.472975
+v 2.196270 0.721780 -2.493294
+v -3.115630 -0.588979 1.772415
+v -3.122642 0.192029 1.706162
+v 2.305190 0.463503 -2.547947
+v 2.383274 0.128751 -2.578085
+v 2.026136 1.180204 -2.395136
+v 2.372061 -0.253901 -2.533144
+v 0.043832 -0.439397 -0.700086
+v -2.331525 -0.123197 1.132486
+v 2.610039 1.166097 -2.837585
+v 2.673477 0.969915 -2.869029
+v 2.777325 0.659725 -2.919367
+v 2.880958 0.215439 -2.959367
+v 2.657509 1.185952 -2.873027
+v 2.720273 0.991850 -2.904138
+v 2.379762 0.373280 -2.580731
+v 2.409298 0.246654 -2.592131
+v 2.731814 0.647876 -2.878239
+v 2.836558 0.198831 -2.918667
+v -3.039373 -0.455495 1.723220
+v 2.192646 1.021841 -2.487980
+v 2.248729 0.747345 -2.506499
+v 2.309599 0.498417 -2.529263
+v 2.403300 0.096714 -2.565429
+v 2.237351 0.888547 -2.505383
+v -0.183302 -0.450597 -0.489896
+v 2.405254 0.311230 -2.572676
+v 1.043932 1.013192 -1.572568
+v 1.174857 -0.343976 -1.550015
+v 2.968846 0.048153 -2.984724
+v 2.704206 1.090705 -2.868940
+v 1.193313 1.053879 -1.685613
+v 2.300203 0.530684 -2.498384
+v 2.410253 0.058885 -2.540860
+v -2.870028 -0.379944 1.617982
+v -0.502937 -0.466927 -0.212460
+v 2.262925 0.542265 -2.453038
+v 2.383671 0.024616 -2.499642
+v 2.688581 1.172611 -2.842590
+v 2.740755 1.011263 -2.868450
+v 2.233117 1.007717 -2.472307
+v 2.280868 0.774003 -2.488075
+v -2.681424 0.688036 1.389168
+v -2.372814 0.754289 1.142473
+v -2.249194 -0.603433 1.170773
+v 2.433319 0.963376 -2.621027
+v -2.742923 0.657642 1.444079
+v 2.197806 0.531341 -2.394472
+v 2.322739 -0.004263 -2.442693
+v -3.420844 -0.571247 2.087621
+v -3.456852 0.016609 2.061712
+v 2.261785 0.891425 -2.474043
+v -0.828435 -0.477984 0.061043
+v 2.548733 0.450740 -2.649420
+v 2.594102 0.256238 -2.666930
+v 2.619376 1.253520 -2.777794
+v 2.736592 0.891025 -2.835893
+v 2.666746 1.272449 -2.813868
+v 2.782720 0.913795 -2.871353
+v -2.802757 0.594778 1.513184
+v 2.842664 0.549920 -2.883518
+v 2.892401 0.336695 -2.902714
+v 2.419066 1.038090 -2.596991
+v 2.466659 0.890910 -2.620581
+v 2.366409 0.398028 -2.496960
+v 2.411491 0.204759 -2.514360
+v -3.768498 -0.285858 2.351028
+v -3.754395 -0.436849 2.353898
+v -2.979417 0.290761 1.695520
+v 2.258257 0.990733 -2.446479
+v 2.298049 0.795972 -2.459619
+v -3.744207 -0.468465 2.368351
+v -3.763825 -0.250991 2.363676
+v -2.991889 -0.449902 1.784124
+v 2.589037 0.355701 -2.637096
+v -3.058914 0.242857 1.779885
+v 2.405318 0.303321 -2.484666
+v 2.886850 0.445733 -2.870009
+v 2.109286 1.132686 -2.326646
+v 2.219562 0.592947 -2.363060
+v 2.696261 1.244513 -2.793414
+v 2.792664 0.946380 -2.841198
+v 1.301631 1.132067 -1.690738
+v 2.383062 0.419848 -2.468079
+v 2.437160 0.187925 -2.488959
+v 2.181364 1.141334 -2.376844
+v 2.287943 0.619687 -2.412037
+v 2.862712 0.573808 -2.851874
+v 2.922544 0.317299 -2.874968
+v 2.818116 0.561038 -2.810024
+v 2.878590 0.301782 -2.833364
+v 2.426071 1.103677 -2.552133
+v 2.514010 0.831724 -2.595722
+v 2.235144 1.129736 -2.400364
+v 2.332283 0.654296 -2.432440
+v 2.346390 0.907367 -2.454136
+v -3.411176 0.052851 2.120724
+v 2.429752 0.306199 -2.453326
+v 2.269301 1.099582 -2.397708
+v 2.352008 0.694778 -2.425019
+v 2.386485 0.438404 -2.426523
+v 2.450023 0.166012 -2.451047
+v 2.915866 0.448472 -2.835623
+v 2.344078 0.972432 -2.436077
+v 2.370150 0.844828 -2.444686
+v 2.871840 0.434359 -2.793598
+v -2.786263 -0.483209 1.702896
+v 1.459973 1.193063 -1.759240
+v -3.703011 -0.503725 2.422886
+v -3.730082 -0.199896 2.416091
+v 2.655625 1.315469 -2.699349
+v 2.808775 0.841848 -2.775261
+v 2.702611 1.333742 -2.736255
+v 2.854139 0.865139 -2.811362
+v 2.288410 1.067288 -2.384314
+v 2.357333 0.729951 -2.407073
+v 2.357592 0.441017 -2.374193
+v 2.427305 0.142152 -2.401100
+v 1.807099 1.188967 -2.008747
+v -3.783568 -0.334437 2.490638
+v -3.776477 -0.407414 2.491810
+v 2.122243 1.235844 -2.255761
+v 2.482100 -0.255753 -2.399353
+v 2.726073 1.295463 -2.728897
+v 2.852031 0.905933 -2.791331
+v 2.295756 0.426582 -2.312892
+v 2.367887 0.117350 -2.340733
+v -2.939741 -0.443760 1.851008
+v -3.781106 -0.318610 2.496646
+v -3.771103 -0.424172 2.498541
+v 2.441322 0.304925 -2.409196
+v 2.453266 1.150154 -2.493283
+v 2.568163 0.794829 -2.550234
+v 1.130007 1.063856 -1.452342
+v 1.265938 -0.347821 -1.428686
+v 3.061789 0.055427 -2.865868
+v -2.602579 0.736106 1.497157
+v -2.292120 0.804178 1.248855
+v -2.434953 -0.604050 1.489534
+v -2.163494 -0.608053 1.278256
+v -2.664054 0.704644 1.549387
+v -2.539160 -0.611934 1.572914
+v 2.399283 0.915534 -2.415008
+v -3.707198 -0.182800 2.448233
+v 2.363834 1.022590 -2.395347
+v 2.408991 0.801573 -2.410258
+v -2.722515 -0.616791 1.721958
+v -2.725142 0.639161 1.608672
+v -2.829179 -0.615824 1.810674
+v -2.909245 0.322942 1.786891
+v 2.392228 0.914706 -2.400276
+v 2.398101 0.948794 -2.405777
+v 2.411428 0.883567 -2.410178
+v -2.983486 -0.609514 1.936485
+v -2.991769 0.272910 1.861910
+v 2.388079 0.936458 -2.393279
+v 2.401934 0.893610 -2.400146
+v 1.266405 1.148829 -1.537915
+v 1.276485 1.330618 -1.562468
+v 1.301959 1.399716 -1.588675
+v 1.829089 1.191557 -1.980541
+v 2.385908 3.043540 -2.584738
+v 2.432531 3.084452 -2.624845
+v 2.484531 3.138930 -2.670390
+v 2.555058 3.180480 -2.729192
+v 2.587869 1.699016 -2.618729
+v 2.897153 3.208163 -2.998451
+v 2.950947 3.055532 -3.026375
+v 2.946595 3.189529 -3.035288
+v 2.959600 3.134547 -3.040378
+v 2.417759 0.294566 -2.355182
+v -3.764574 -0.295328 2.521762
+v -3.750702 -0.443027 2.524510
+v 2.358011 0.275051 -2.293223
+v -3.320640 -0.590025 2.214050
+v -3.361390 0.074490 2.184798
+v 2.390117 0.955552 -2.380219
+v 2.415719 0.876379 -2.392909
+v 2.408200 0.974432 -2.384957
+v 2.431282 0.861455 -2.392579
+v -3.753472 -0.287501 2.537197
+v 2.398036 0.969083 -2.363085
+v 2.431485 0.865638 -2.379665
+v -3.651074 -0.516133 2.490283
+v -3.681587 -0.172421 2.482510
+v 2.311849 1.347830 -2.326247
+v -3.741101 -0.282712 2.553676
+v -3.725441 -0.449869 2.556817
+v 2.175858 1.240742 -2.187508
+v 1.176857 1.068079 -1.392566
+v 1.312589 -0.342230 -1.368881
+v -2.560177 0.739860 1.551322
+v -2.121265 -0.602981 1.332435
+v -2.249778 0.807875 1.303072
+v -2.622366 0.708349 1.602724
+v -2.686267 0.642614 1.658664
+v -2.872984 0.326449 1.833357
+v -2.958486 0.276123 1.904730
+v -3.335886 0.076990 2.217503
+v -3.668144 -0.171074 2.499706
+v -3.734634 -0.282056 2.561952
+v -3.734566 -0.369723 2.570150
+v -2.150712 -0.249451 1.324240
+v 1.267079 0.010084 -1.364303
+v 1.704978 -0.191159 -1.687236
+v 2.198494 1.185947 -2.198470
+v 2.267581 1.192809 -2.252964
+v 2.325830 0.562710 -2.240516
+v 2.313725 1.176653 -2.287457
+v 2.336207 1.139528 -2.301576
+v 2.434129 0.098705 -2.282343
+v 2.390649 0.590464 -2.293602
+v 2.344165 1.100576 -2.304204
+v 2.425891 0.627661 -2.324495
+v 2.423750 0.711053 -2.330483
+v 2.431708 0.672101 -2.333111
+v 2.400364 1.044399 -2.342861
+v 2.410625 0.974989 -2.344487
+v 2.420496 0.915260 -2.346699
+v 2.426872 0.985581 -2.358127
+v 2.452506 0.789191 -2.360079
+v 2.446831 0.863022 -2.362433
+v 2.444856 0.921557 -2.366269
+v 2.453526 0.855127 -2.366929
+v 2.496511 1.170443 -2.429398
+v 2.620876 0.785842 -2.491042
+v 2.713268 1.342514 -2.614195
+v 2.759644 1.360499 -2.652004
+v 2.773482 1.317705 -2.658864
+v 2.879037 0.829869 -2.696361
+v 2.909818 0.896081 -2.726441
+v 2.923656 0.853286 -2.733300
+v 2.434469 0.098745 -2.281906
+v -2.120000 -0.602832 1.334057
+v 1.313986 -0.342065 -1.367090
+v 2.435135 0.098824 -2.281052
+v 2.435757 0.098897 -2.280254
+v 2.436307 0.098962 -2.279548
+v 2.436763 0.099016 -2.278964
+v 2.436999 0.099043 -2.278662
+v -2.885212 -0.437337 1.920949
+v -2.717870 -0.356682 1.785125
+v -3.728000 -0.281169 2.570479
+v -3.712341 -0.448326 2.573620
+v -0.735546 -0.053125 0.219066
+v -0.406216 0.000394 -0.041418
+v -0.100273 0.040739 -0.281944
+v 0.172417 0.073341 -0.495491
+v 2.335518 1.350617 -2.295889
+v 0.354290 0.083209 -0.634777
+v -3.623852 -0.512926 2.525198
+v -3.654365 -0.169215 2.517424
+v 2.425971 0.972373 -2.327255
+v 2.459421 0.868929 -2.343834
+v 0.696977 0.054076 -0.892441
+v 2.948120 3.214165 -2.933081
+v 2.606057 3.186486 -2.663780
+v 2.997989 3.195582 -2.969370
+v 2.536222 3.145018 -2.604090
+v 3.012064 3.140727 -2.973088
+v 2.485188 3.090654 -2.557307
+v 1.046458 0.007453 -1.152257
+v -3.714744 -0.282939 2.586870
+v 2.439279 3.049827 -2.516283
+v 3.004908 3.061887 -2.957164
+v 2.449116 0.979252 -2.332477
+v 2.472199 0.866275 -2.340099
+v 1.368491 -0.052711 -1.393705
+v 1.717894 -0.070445 -1.662515
+v 1.478573 -0.058668 -1.476762
+v 2.441737 0.961632 -2.314012
+v 2.467338 0.882459 -2.326701
+v -3.309670 0.080582 2.251135
+v -3.268919 -0.583933 2.280387
+v 2.410137 0.281190 -2.226363
+v -3.701911 -0.287948 2.602134
+v -3.688039 -0.435647 2.604882
+v 2.482991 0.302250 -2.271514
+v 2.455522 0.944402 -2.306774
+v 2.469378 0.901553 -2.313642
+v -2.915858 -0.601549 2.023225
+v -2.924141 0.280876 1.948650
+v 2.468971 0.957141 -2.314878
+v 2.482299 0.891914 -2.319279
+v 2.465229 0.923305 -2.306645
+v -2.755682 -0.607167 1.904943
+v -2.835748 0.331599 1.881160
+v -2.646201 0.648459 1.709923
+v -2.643573 -0.607493 1.823210
+v 2.443879 1.032018 -2.292681
+v 2.489036 0.811001 -2.307592
+v -3.626724 -0.173321 2.551450
+v 2.481117 0.925173 -2.310048
+v -2.579668 0.714584 1.657621
+v -2.454774 -0.601995 1.681149
+v -2.516832 0.746206 1.607137
+v -2.349206 -0.593950 1.599513
+v -2.206373 0.814278 1.358835
+v -2.077747 -0.597953 1.388236
+v 2.667233 1.708364 -2.516935
+v 1.224686 1.075007 -1.330905
+v 1.360617 -0.336670 -1.307250
+v 3.156468 0.066579 -2.744432
+v 2.549224 1.161456 -2.370206
+v 2.664121 0.806131 -2.427157
+v 2.540023 0.316550 -2.282602
+v 1.385180 1.409518 -1.481935
+v -3.679715 -0.306668 2.626691
+v -3.669712 -0.412230 2.628586
+v 1.360989 1.340571 -1.454082
+v 2.400261 0.438891 -2.178853
+v 2.472391 0.129659 -2.206693
+v 2.831269 1.307854 -2.593973
+v 2.957226 0.918323 -2.656406
+v 2.230447 1.248588 -2.116977
+v 2.590304 -0.243009 -2.260568
+v 1.354370 1.159190 -1.425091
+v 1.917054 1.201918 -1.867717
+v -3.673127 -0.321429 2.632291
+v -3.666035 -0.394406 2.633464
+v -2.830682 -0.430915 1.990889
+v 2.473447 0.454663 -2.225597
+v 2.543159 0.155798 -2.252504
+v 2.410583 1.081677 -2.227614
+v 2.479506 0.744341 -2.250373
+v 3.001074 0.449623 -2.628313
+v 2.829160 1.348647 -2.573941
+v 2.980689 0.880044 -2.649048
+v 2.783530 1.330534 -2.535297
+v 2.936681 0.856913 -2.611208
+v 3.051332 0.464469 -2.662340
+v -3.572802 -0.488389 2.589894
+v -3.599872 -0.184558 2.583099
+v 1.591093 1.208507 -1.591064
+v 2.482720 0.988763 -2.258252
+v 2.508792 0.861158 -2.266861
+v 2.531323 0.455464 -2.240752
+v 2.594861 0.183071 -2.265276
+v 2.415908 1.116850 -2.209669
+v 2.498616 0.712046 -2.236978
+v 2.579618 0.323851 -2.261107
+v -2.645182 -0.466535 1.885712
+v 1.939045 1.204508 -1.839511
+v -3.258276 0.070860 2.316835
+v 2.506480 0.926224 -2.248802
+v 2.992383 0.581606 -2.586979
+v 3.052857 0.322350 -2.610320
+v 2.407334 1.150018 -2.179512
+v 2.504473 0.674577 -2.211588
+v 3.042733 0.595054 -2.621444
+v 3.102565 0.338545 -2.644537
+v 2.603378 1.124561 -2.324717
+v 2.691316 0.852608 -2.368305
+v 3.078396 0.468329 -2.624718
+v 2.370287 1.163586 -2.134528
+v 2.476867 0.641940 -2.169721
+v 2.572210 0.442126 -2.225475
+v 2.626308 0.210203 -2.246356
+v 1.491168 1.154391 -1.447635
+v 2.890636 1.267406 -2.544106
+v 2.987040 0.969274 -2.591890
+v 2.788228 0.379194 -2.381967
+v 2.304762 1.155709 -2.075926
+v 2.415038 0.615971 -2.112340
+v 2.604052 0.326729 -2.229767
+v -2.858987 0.266405 2.036315
+v -3.533523 -0.443650 2.638577
+v -3.553141 -0.226176 2.633901
+v 2.469866 1.015657 -2.175067
+v 2.509659 0.820896 -2.188207
+v -2.762138 0.316353 1.974206
+v -2.778535 -0.424773 2.057773
+v 3.071249 0.576878 -2.590723
+v 3.120985 0.363654 -2.609920
+v 2.781708 0.478212 -2.350957
+v 2.827077 0.283710 -2.368468
+v -3.539008 -0.258828 2.645375
+v -3.524905 -0.409819 2.648245
+v 2.597879 0.425291 -2.200073
+v 2.642961 0.232022 -2.217474
+v 2.650729 1.065375 -2.299858
+v 2.698322 0.918196 -2.323448
+v -2.569382 0.622266 1.812512
+v 2.900579 1.299991 -2.513951
+v 3.016554 0.941337 -2.571435
+v 2.855713 1.281357 -2.474664
+v 2.972929 0.918862 -2.532765
+v 2.506130 0.920204 -2.160643
+v -3.173446 -0.542107 2.404937
+v -3.209453 0.045748 2.379028
+v 2.445409 0.560504 -2.076893
+v 2.570342 0.024900 -2.125114
+v -2.493455 0.687025 1.764050
+v 2.684069 0.992910 -2.299412
+v -2.427931 0.717893 1.714301
+v -2.119321 0.784146 1.467606
+v -1.995701 -0.573576 1.495906
+v 2.487048 1.037626 -2.146613
+v 2.534799 0.803912 -2.162379
+v 2.942544 1.202524 -2.516853
+v 2.994718 1.041175 -2.542714
+v 2.517081 0.572200 -2.127054
+v 2.637826 0.054551 -2.173658
+v 2.571093 0.562590 -2.150938
+v 2.681143 0.090791 -2.193414
+v 1.466306 1.086034 -1.335468
+v 2.979094 1.123082 -2.516365
+v -0.568604 -0.447372 0.394570
+v 1.323830 1.046159 -1.213567
+v 1.454755 -0.311009 -1.191014
+v 3.248744 0.081120 -2.625723
+v 2.688243 0.344561 -2.209711
+v -2.593169 -0.347277 1.974947
+v 3.029114 0.682935 -2.497393
+v 3.133857 0.233890 -2.537821
+v 2.530565 0.923082 -2.129303
+v -0.224107 -0.434065 0.145862
+v 3.079074 0.695309 -2.532807
+v 3.182707 0.251022 -2.572807
+v 2.606071 0.533336 -2.149005
+v 2.699770 0.131633 -2.185170
+v 2.490887 1.056969 -2.105453
+v 2.546971 0.782472 -2.123972
+v 2.684199 0.409138 -2.190257
+v 2.713735 0.282512 -2.201657
+v 2.963027 1.221936 -2.481166
+v 3.025790 1.027834 -2.512276
+v 2.918829 1.202466 -2.441528
+v 2.982266 1.006286 -2.472972
+v -2.731051 -0.419180 2.118678
+v 2.346020 1.217882 -1.984849
+v 2.691945 -0.216224 -2.122858
+v 2.626097 0.501300 -2.136348
+v 2.704180 0.166548 -2.166486
+v 2.809263 0.554231 -2.283747
+v 2.887846 0.217345 -2.314077
+v -2.799153 0.230131 2.121073
+v -2.792140 -0.550877 2.187326
+v 0.122037 -0.414552 -0.095656
+v 3.101457 0.660216 -2.517043
+v 3.187603 0.290900 -2.550293
+v 2.461960 1.061494 -2.053272
+v 2.523494 0.760321 -2.073591
+v 3.006995 1.126368 -2.480578
+v 2.963268 1.105875 -2.440934
+v 2.399616 1.050076 -1.991851
+v 2.463283 0.738458 -2.012874
+v 2.541997 0.922437 -2.085123
+v -2.620849 -0.553009 2.083075
+v -2.691633 0.277873 2.061963
+v 0.380138 -0.399580 -0.262083
+v 2.741003 0.353341 -2.170536
+v 2.702686 0.458937 -2.148505
+v 2.753845 0.239614 -2.168251
+v 2.735783 0.349782 -2.158101
+v 2.738935 0.386350 -2.160591
+v 2.754033 0.321623 -2.166418
+v 2.501059 1.494406 -2.071566
+v -2.498046 0.561724 1.911958
+v -2.495843 -0.550836 2.012409
+v 2.518039 0.913888 -2.030968
+v 2.733885 0.378609 -2.149074
+v 2.747093 0.321983 -2.154171
+v 2.457638 0.897352 -1.968773
+v -2.690304 -0.414381 2.170940
+v -3.127855 -0.045628 2.482288
+v -3.102117 -0.467490 2.500962
+v 2.770798 1.227046 -2.232150
+v 2.748385 0.411806 -2.139249
+v 2.774536 0.299695 -2.149342
+v 2.764617 1.300784 -2.232889
+v 2.792626 1.229192 -2.247504
+v -2.409183 0.623172 1.864025
+v -2.298506 -0.543169 1.884841
+v 2.786510 1.302148 -2.248235
+v 2.741907 0.400740 -2.129507
+v 2.764784 0.302663 -2.138337
+v -2.529794 -0.443952 2.062569
+v -2.339763 0.652407 1.813967
+v -2.191192 -0.534884 1.807161
+v -1.920944 -0.535987 1.596561
+v -2.034769 0.715099 1.570418
+v -1.970078 -0.080625 1.596081
+v 1.720073 1.327862 -1.406896
+v 0.572047 -0.387414 -0.352526
+v 2.806120 1.237906 -2.229508
+v 2.801037 1.298551 -2.230116
+v -3.088440 -0.411708 2.522214
+v -3.106239 -0.116627 2.508994
+v 1.609069 1.259907 -1.293617
+v 2.543657 1.253039 -2.021345
+v 1.525799 0.957611 -1.197050
+v 2.757700 0.410246 -2.104644
+v 2.784117 0.296996 -2.114841
+v 2.533483 0.613445 -1.947803
+v 2.602202 0.623366 -2.002292
+v 2.677744 -0.005017 -2.003484
+v 2.648675 0.609224 -2.037226
+v 2.672127 0.573042 -2.052187
+v 2.681143 0.534388 -2.055668
+v 2.741627 0.025636 -2.056106
+v 2.775750 0.064439 -2.086273
+v 2.738751 0.480616 -2.095645
+v 2.771306 0.147849 -2.090468
+v 2.780322 0.109196 -2.093948
+v 2.762407 0.352190 -2.102295
+v 2.766821 0.422887 -2.112228
+v 2.797824 0.227363 -2.118445
+v 2.786530 0.359571 -2.121780
+v 2.797017 0.293432 -2.123883
+v 1.548968 1.153765 -1.228190
+v 2.798795 1.166335 -2.202419
+v 2.552618 1.211834 -2.013930
+v 2.544939 1.295449 -2.015621
+v 2.863513 0.586883 -2.198347
+v 2.954250 0.197881 -2.233369
+v 2.820327 1.169125 -2.218088
+v 2.781909 1.367789 -2.204438
+v 2.803619 1.368443 -2.220086
+v 1.423109 0.978571 -1.105456
+v 1.544240 -0.271959 -1.085059
+v 3.334585 0.098417 -2.514929
+v 1.554716 0.187936 -1.132116
+v 2.612265 1.258718 -2.052108
+v 3.101424 0.726458 -2.383560
+v 3.222370 0.207946 -2.430242
+v -2.658075 -0.410585 2.212276
+v 3.150617 0.738370 -2.420182
+v 3.270283 0.225352 -2.466369
+v 3.160928 0.696012 -2.423422
+v 3.260400 0.269561 -2.461816
+v 2.619734 1.224381 -2.045928
+v 2.613334 1.294060 -2.047338
+v 2.829147 1.187974 -2.205056
+v 2.815259 1.353660 -2.206717
+v 2.570461 1.178110 -1.994503
+v 2.556271 1.332609 -1.997628
+v 2.777033 0.404579 -2.081148
+v 2.799910 0.306502 -2.089978
+v 2.789302 0.416625 -2.086768
+v 2.815452 0.304514 -2.096862
+v 2.634603 1.196277 -2.029740
+v 2.622778 1.325027 -2.032343
+v 2.651619 1.262515 -2.043053
+v 1.554142 0.981857 -1.160642
+v 2.656512 1.240018 -2.039004
+v 2.652319 1.285671 -2.039927
+v -2.635774 -0.407958 2.240880
+v 2.794724 0.385259 -2.065313
+v 2.807933 0.328634 -2.070411
+v 2.809805 0.394697 -2.069692
+v 2.824903 0.329970 -2.075520
+v 2.806035 0.357461 -2.061383
+v -2.489068 -0.351828 2.132329
+v 2.463909 1.145066 -1.865152
+v 2.782581 -0.176570 -1.992240
+v 1.570340 0.895334 -1.144881
+v 1.570297 0.896443 -1.144827
+v 1.570363 0.895879 -1.144798
+v 1.570504 0.894974 -1.144707
+v 1.570489 0.895138 -1.144709
+v 1.570444 0.896371 -1.144649
+v 1.570624 0.894816 -1.144569
+v 2.666254 1.221606 -2.028398
+v 2.658506 1.305960 -2.030103
+v 1.570869 0.894455 -1.144293
+v 1.570594 0.897869 -1.144312
+v 2.594467 1.157000 -1.966023
+v 2.575927 1.358863 -1.970106
+v 1.570894 0.897475 -1.143969
+v 2.833890 0.249041 -2.065584
+v 2.782731 0.468365 -2.045837
+v -2.701300 -0.462008 2.315575
+v -2.705561 0.099398 2.267342
+v 2.841108 1.134918 -2.151662
+v -0.479044 -0.046272 0.545593
+v 2.862191 1.138039 -2.167869
+v 2.822836 0.362979 -2.065575
+v 2.654608 1.178685 -2.006007
+v 2.639158 1.346905 -2.009409
+v -2.624374 -0.406615 2.255502
+v 2.818040 1.410109 -2.154421
+v 2.839368 1.410315 -2.170599
+v 2.863947 1.162135 -2.163311
+v 2.676065 1.265123 -2.025119
+v 2.664612 1.254980 -2.014677
+v 2.663261 1.271085 -2.014839
+v 0.971816 -0.356390 -0.546463
+v 2.844975 1.388465 -2.165580
+v 2.678566 1.253623 -2.023050
+v 2.676423 1.276959 -2.023522
+v 2.679361 1.210080 -2.012848
+v 2.669238 1.320294 -2.015076
+v 2.670727 1.241720 -2.008183
+v 2.667038 1.285720 -2.008625
+v 2.683546 1.244211 -2.017628
+v 2.679586 1.287331 -2.018500
+v 1.579791 0.985026 -1.130600
+v -1.956412 0.610154 1.662774
+v -1.856740 -0.486828 1.685802
+v 2.690246 1.238319 -2.009680
+v 2.685071 1.294657 -2.010819
+v 2.679968 1.234858 -1.997098
+v 2.674930 1.294963 -1.997700
+v -0.132362 0.006287 0.310615
+v 1.590120 0.977428 -1.119874
+v 2.748270 0.515690 -1.979648
+v 2.826352 0.180938 -2.009786
+v -2.571993 0.138873 2.206668
+v -2.521306 -0.458402 2.221998
+v -2.685403 -0.396185 2.345565
+v -2.686840 -0.001633 2.310453
+v 2.620983 1.151716 -1.932826
+v 2.600915 1.370211 -1.937245
+v 2.676704 1.174282 -1.978342
+v 2.659981 1.356362 -1.982024
+v 2.678470 1.266092 -1.988150
+v 2.693838 1.207195 -1.994723
+v 2.682881 1.326489 -1.997135
+v 2.697646 1.236845 -2.000415
+v 2.692045 1.297825 -2.001648
+v 2.697040 1.267502 -2.002758
+v 2.651196 0.936979 -1.934514
+v 2.898778 0.955779 -2.129270
+v 2.929918 0.567420 -2.117638
+v 3.008498 0.230534 -2.147968
+v 2.689859 1.236233 -1.984390
+v 2.684821 1.296338 -1.984993
+v 2.822841 0.425467 -2.012432
+v 2.852378 0.298841 -2.023833
+v 1.608781 0.850501 -1.104316
+v 2.704620 1.240012 -1.991243
+v 2.699446 1.296350 -1.992383
+v 1.624309 1.159754 -1.138181
+v 2.752678 0.550604 -1.960964
+v 2.846378 0.148901 -1.997130
+v 1.606690 0.959087 -1.103622
+v 2.710106 1.247339 -1.983562
+v 2.706145 1.290459 -1.984434
+v 2.697752 1.245476 -1.973465
+v 2.694063 1.289476 -1.973907
+v 2.707481 1.213392 -1.976781
+v 2.697358 1.323606 -1.979010
+v -2.445962 -0.418610 2.191836
+v 3.233725 0.674674 -2.334944
+v 3.319870 0.305357 -2.368193
+v 2.848333 0.363417 -2.004377
+v 2.713269 1.257710 -1.978540
+v 2.711125 1.281046 -1.979012
+v 1.518181 0.875199 -1.011295
+v 1.625158 -0.221229 -0.994015
+v 3.410238 0.117713 -2.416893
+v 2.901195 1.167312 -2.115460
+v 2.701529 1.260111 -1.967251
+v 2.700178 1.276216 -1.967413
+v 2.713626 1.269547 -1.976943
+v 1.616466 0.943560 -1.090626
+v 2.882224 1.393643 -2.117728
+v -2.383910 0.349570 2.084356
+v -2.382627 -0.452496 2.157011
+v 2.697528 1.183740 -1.950957
+v 2.682078 1.351960 -1.954359
+v 2.907001 1.144267 -2.110304
+v 2.886398 1.141212 -2.093480
+v 2.743282 0.582871 -1.930085
+v 2.853333 0.111072 -1.972562
+v 1.623579 0.887518 -1.082372
+v 0.198902 0.047648 0.105888
+v 1.621282 0.931471 -1.084234
+v 2.884178 1.416542 -2.113034
+v 2.863331 1.416404 -2.096239
+v -2.503361 -0.388481 2.254219
+v -2.538594 0.031317 2.243138
+v -1.878374 0.077127 1.724185
+v -1.889940 0.222714 1.719833
+v 2.645971 1.163066 -1.899965
+v 2.627431 1.364929 -1.904047
+v 2.718213 1.227726 -1.961754
+v 2.710466 1.312080 -1.963460
+v 1.626047 0.904541 -1.078374
+v 3.189935 0.700514 -2.275981
+v 3.294679 0.251469 -2.316409
+v 3.238191 0.712702 -2.313742
+v 3.341825 0.268415 -2.353742
+v 2.706004 0.594452 -1.884739
+v 2.826750 0.076803 -1.931343
+v 2.640884 0.583528 -1.826173
+v 2.765818 0.047924 -1.874394
+v 1.714162 1.276631 -1.167239
+v 2.724400 1.248014 -1.951930
+v 2.720207 1.293667 -1.952853
+v 1.654904 1.136021 -1.105274
+v 2.725100 1.271170 -1.948805
+v 2.713907 1.205618 -1.928023
+v 2.702083 1.334368 -1.930626
+v 1.831320 1.344269 -1.251206
+v -2.260945 0.399211 2.027604
+v -2.181051 -0.441814 2.042546
+v 2.809488 0.450216 -1.928661
+v 2.854570 0.256946 -1.946061
+v -2.423567 -0.369548 2.230060
+v -2.180836 0.422861 1.973894
+v -2.073524 -0.433428 1.968859
+v -1.805897 -0.428247 1.759726
+v -1.887675 0.473899 1.740641
+v -2.358199 -0.380076 2.187846
+v -2.358499 0.188271 2.135889
+v 2.990686 0.501054 -2.063247
+v 3.036054 0.306554 -2.080758
+v 2.665627 1.189319 -1.872442
+v 2.651437 1.343818 -1.875567
+v 2.648270 1.510792 -1.882843
+v 2.930912 1.202118 -2.074323
+v 2.723351 1.236585 -1.913028
+v 2.716951 1.306264 -1.914438
+v 2.917024 1.367804 -2.075984
+v 1.384653 -0.306314 -0.725204
+v 2.724420 1.271928 -1.908258
+v 1.621186 0.490687 -0.975352
+v 1.644965 0.346027 -0.980608
+v 0.465380 0.080433 -0.036070
+v 2.848397 0.355508 -1.916368
+v -2.408185 -0.394890 2.251162
+v 2.942750 1.186138 -2.060817
+v 2.578963 1.033323 -1.763117
+v 2.858250 -0.125781 -1.874424
+v 2.922529 1.183532 -2.043463
+v 2.676959 1.226479 -1.854450
+v 2.669279 1.310094 -1.856141
+v 2.926042 1.385458 -2.062815
+v 2.826142 0.472035 -1.899780
+v 2.880239 0.240111 -1.920660
+v 2.905643 1.384987 -2.045482
+v 3.029535 0.405571 -2.049749
+v 3.300344 0.601920 -2.275317
+v 3.350079 0.388695 -2.294513
+v 2.678242 1.268890 -1.848725
+v 2.945134 1.257227 -2.050923
+v 1.755731 1.246131 -1.122488
+v 1.720327 0.756772 -1.049674
+v 2.940050 1.317871 -2.051532
+v -2.214680 0.228844 2.067608
+v -2.157859 -0.367480 2.078067
+v 1.704158 1.079827 -1.054807
+v -2.127781 0.248308 2.009962
+v -2.051331 -0.359151 2.006140
+v -1.843236 0.290860 1.784206
+v -1.785848 -0.348829 1.798208
+v 1.604892 0.740558 -0.935201
+v 1.693974 -0.161033 -0.921860
+v 3.472397 0.138163 -2.335899
+v 1.873536 1.307570 -1.193691
+v 2.872832 0.358386 -1.885027
+v 2.959859 1.252435 -2.032667
+v 2.953743 1.325390 -2.033399
+v 3.342932 0.497244 -2.260519
+v 2.939822 1.250538 -2.015012
+v 2.829564 0.490591 -1.858224
+v 2.893102 0.218199 -1.882748
+v 2.933641 1.324275 -2.015751
+v 1.701154 -0.246228 -0.905127
+v -0.334995 -0.394882 0.699940
+v 3.318333 0.625178 -2.242012
+v 3.378166 0.368670 -2.265106
+v 3.270936 0.612055 -2.203483
+v 3.331410 0.352798 -2.226824
+v 2.870384 0.194339 -1.832802
+v 2.800672 0.493204 -1.805894
+v 2.706330 1.463624 -1.813588
+v 1.735438 1.031940 -1.015094
+v 2.096574 -0.183066 -1.184359
+v 2.738836 0.478769 -1.744593
+v 2.810966 0.169537 -1.772433
+v 1.857375 -0.219119 -0.990222
+v 2.884402 0.357112 -1.840897
+v 3.369567 0.499255 -2.224210
+v 0.704277 0.096803 -0.109143
+v 3.322719 0.484781 -2.185490
+v 1.668063 0.559071 -0.899985
+v 1.734685 -0.079917 -0.893249
+v 3.503355 0.161047 -2.294336
+v 1.853604 0.697100 -1.057274
+v 0.023839 -0.378671 0.476160
+v 2.742703 1.224204 -1.790460
+v 2.990285 1.243004 -1.985215
+v 1.751320 0.995124 -0.995284
+v 1.762367 0.864072 -0.986848
+v 1.978204 0.694100 -1.137927
+v 1.821889 1.171394 -1.054962
+v 2.860838 0.346753 -1.786883
+v 1.768391 0.914212 -0.976082
+v 2.800102 0.741867 -1.764054
+v 3.047683 0.760667 -1.958810
+v 2.801089 0.327237 -1.724923
+v 2.686153 0.887537 -1.683203
+v 2.915644 -0.066074 -1.774558
+v 1.939461 1.218681 -1.105028
+v 0.380965 -0.356962 0.277840
+v 2.157984 -0.096145 -1.130609
+v 2.171539 -0.180007 -1.133476
+v 1.870303 1.101825 -0.999439
+v 2.768902 0.690672 -1.652155
+v 2.931033 0.013315 -1.716360
+v 2.798614 1.350688 -1.707175
+v 1.895380 1.047986 -0.972059
+v -0.263286 -0.086843 0.817096
+v 1.915261 0.855545 -0.963812
+v 1.995518 1.144847 -1.050030
+v 1.923295 0.929349 -0.946858
+v 0.639411 -0.340042 0.173318
+v 2.048910 0.874962 -1.028633
+v 2.024876 1.087065 -1.024636
+v 0.098227 -0.041243 0.603403
+v 1.153992 0.084424 -0.229250
+v 2.057853 0.957558 -1.005829
+v 2.853774 1.270425 -1.644800
+v 2.691514 -0.001160 -1.386453
+v 2.705070 -0.085024 -1.389320
+v 2.781930 0.015918 -1.458515
+v 2.795485 -0.067945 -1.461382
+v 2.889620 0.961520 -1.629051
+v 3.137201 0.980321 -1.823807
+v 2.880562 1.206416 -1.616926
+v 3.128144 1.225217 -1.811681
+v 2.893827 1.173559 -1.603234
+v 3.141408 1.192360 -1.797990
+v -0.165990 -0.329480 0.922774
+v 2.905606 1.059058 -1.599313
+v 3.153188 1.077859 -1.794069
+v 2.902644 1.136966 -1.595558
+v 3.150226 1.155766 -1.790314
+v 2.906628 1.098234 -1.594232
+v 3.154210 1.117035 -1.788988
+v 0.450756 -0.003152 0.428413
+v 0.893967 -0.321301 0.137348
+v 0.203261 -0.310043 0.717218
+v -0.125580 -0.167821 0.986626
+v 0.712336 0.026704 0.345760
+v -0.090811 -0.262476 1.024542
+v 1.652963 0.059929 -0.335038
+v 0.245682 -0.133976 0.786321
+v 0.568430 -0.286725 0.550539
+v 0.283133 -0.240046 0.827356
+v 2.000346 0.011962 -0.463845
+v 0.611741 -0.102875 0.629863
+v 0.998399 0.048451 0.327453
+v 0.827128 -0.268380 0.491359
+v 1.390944 -0.279641 0.074489
+v 2.438757 0.004155 -0.738633
+v 0.652016 -0.215986 0.675288
+v 2.198852 0.014020 -0.544254
+v 0.870584 -0.078559 0.583979
+v 0.910831 -0.196982 0.637049
+v 1.127571 -0.244822 0.495289
+v 1.185795 -0.053488 0.599517
+v 1.535993 0.058149 0.320863
+v 1.947252 -0.218027 0.037437
+v 1.232465 -0.171199 0.659405
+v 2.293159 -0.159573 -0.040558
+v 2.769338 -0.097283 -0.322594
+v 1.695832 -0.196471 0.528473
+v 2.530709 -0.129037 -0.121828
+v 2.157723 0.063260 0.342121
+v 1.776927 -0.020207 0.662777
+v 1.833764 -0.121263 0.736990
+v 3.351091 0.073201 -0.443243
+v 3.364646 -0.010661 -0.446110
+v 2.948324 -0.003291 -0.110055
+v 2.961879 -0.087154 -0.112922
+v 2.992015 0.002089 -0.060870
+v 3.005570 -0.081773 -0.063737
+v 2.525032 0.038738 0.306061
+v 3.379430 0.074900 -0.358922
+v 3.392986 -0.008964 -0.361790
+v 3.036570 0.045434 0.026135
+v 2.796513 0.049478 0.227779
+v 2.357571 -0.130808 0.595337
+v 2.473462 0.016867 0.762129
+v 2.725617 -0.081249 0.592239
+v 2.544662 -0.059737 0.852031
+v 3.261550 -0.027300 0.308389
+v 3.022995 -0.051504 0.513873
+v 2.851826 0.022984 0.782891
+v 3.407963 0.046255 0.499554
+v 2.923753 -0.024800 0.883847
+v 3.168214 0.041578 0.705848
+v 3.711371 0.106264 0.292980
+v 3.724926 0.022401 0.290113
+v 3.531985 0.072015 0.446671
+v 3.545540 -0.011849 0.443804
+v 3.445588 0.055512 0.520889
+v 3.459143 -0.028351 0.518022
+v 3.563503 0.077601 0.432271
+v 3.577058 -0.006262 0.429404
+v 3.488101 0.014784 0.599486
+v 3.249111 0.000073 0.806963
+vt 0.592295 0.226200
+vt 0.597451 0.733993
+vt 0.582117 0.735152
+vt 0.578489 0.226600
+vt 0.583926 0.737917
+vt 0.580317 0.227579
+vt 0.553525 0.740642
+vt 0.440791 0.740508
+vt 0.450875 0.228401
+vt 0.552974 0.228523
+vt 0.390668 0.737686
+vt 0.405291 0.227370
+vt 0.388859 0.734922
+vt 0.403463 0.226391
+vt 0.371983 0.733724
+vt 0.388098 0.225956
+vt 0.619772 0.907170
+vt 0.592331 0.999817
+vt 0.601330 0.999666
+vt 0.610146 0.909999
+vt 0.574598 0.999907
+vt 0.590330 0.911915
+vt 0.553091 0.999969
+vt 0.566105 0.913419
+vt 0.528748 1.000000
+vt 0.538531 0.914447
+vt 0.502634 1.000000
+vt 0.478247 0.914918
+vt 0.475889 0.999968
+vt 0.508812 0.914955
+vt 0.449684 0.999906
+vt 0.448173 0.914340
+vt 0.425163 0.999816
+vt 0.419902 0.913245
+vt 0.403398 0.999703
+vt 0.394672 0.911681
+vt 0.385339 0.999570
+vt 0.373584 0.909717
+vt 0.375862 0.999397
+vt 0.362094 0.906863
+vt 0.605465 0.740602
+vt 0.597205 0.739049
+vt 0.491046 0.731144
+vt 0.490016 0.901745
+vt 0.489949 0.901745
+vt 0.579978 0.737974
+vt 0.489821 0.901745
+vt 0.558870 0.737116
+vt 0.489665 0.901745
+vt 0.534804 0.736510
+vt 0.489489 0.901745
+vt 0.508831 0.736184
+vt 0.489301 0.901744
+vt 0.482087 0.736152
+vt 0.489205 0.901744
+vt 0.455740 0.736416
+vt 0.430942 0.736963
+vt 0.408777 0.737770
+vt 0.390214 0.738801
+vt 0.379997 0.740333
+vt 0.353656 0.886514
+vt 0.378904 0.730193
+vt 0.361705 0.880889
+vt 0.395023 0.727499
+vt 0.380234 0.876591
+vt 0.415254 0.725377
+vt 0.403440 0.873201
+vt 0.438713 0.723920
+vt 0.430309 0.870869
+vt 0.464376 0.723192
+vt 0.459668 0.869695
+vt 0.477566 0.723232
+vt 0.474742 0.869751
+vt 0.491120 0.723224
+vt 0.490232 0.869731
+vt 0.517777 0.724015
+vt 0.520667 0.870977
+vt 0.543181 0.725530
+vt 0.549643 0.873375
+vt 0.566223 0.727704
+vt 0.575892 0.876824
+vt 0.585895 0.730440
+vt 0.598267 0.881171
+vt 0.611334 0.886821
+vt 0.514919 0.001808
+vt 0.498936 0.000000
+vt 0.513858 0.001846
+vt 0.529721 0.013570
+vt 0.531965 0.013618
+vt 0.556734 0.062512
+vt 0.561107 0.062168
+vt 0.573935 0.111992
+vt 0.579743 0.111218
+vt 0.580242 0.134938
+vt 0.586574 0.134186
+vt 0.586077 0.152110
+vt 0.592909 0.152041
+vt 0.591813 0.178477
+vt 0.599157 0.177963
+vt 0.593139 0.193428
+vt 0.600618 0.192699
+vt 0.592908 0.232476
+vt 0.600324 0.231459
+vt 0.496437 0.231529
+vt 0.508654 0.001888
+vt 0.518859 0.013502
+vt 0.535963 0.062914
+vt 0.546664 0.112914
+vt 0.550579 0.135830
+vt 0.554180 0.152166
+vt 0.557666 0.179076
+vt 0.558422 0.194288
+vt 0.558268 0.233688
+vt 0.501777 0.001902
+vt 0.504546 0.013460
+vt 0.508707 0.063079
+vt 0.510973 0.113312
+vt 0.511780 0.136212
+vt 0.512489 0.152160
+vt 0.513077 0.179318
+vt 0.513104 0.194651
+vt 0.512986 0.234213
+vt 0.498076 0.001898
+vt 0.496856 0.013450
+vt 0.494097 0.063062
+vt 0.491870 0.113289
+vt 0.491019 0.136187
+vt 0.490190 0.152133
+vt 0.489240 0.179289
+vt 0.488883 0.194622
+vt 0.488765 0.234184
+vt 0.490954 0.001867
+vt 0.482078 0.013458
+vt 0.466079 0.062831
+vt 0.455287 0.112805
+vt 0.451272 0.135711
+vt 0.447516 0.152039
+vt 0.443646 0.178940
+vt 0.442563 0.194150
+vt 0.442409 0.233550
+vt 0.485218 0.001812
+vt 0.470208 0.013499
+vt 0.443661 0.062377
+vt 0.426084 0.111816
+vt 0.419558 0.134746
+vt 0.413491 0.151904
+vt 0.407324 0.178257
+vt 0.405675 0.193204
+vt 0.405444 0.232252
+vt 0.483722 0.001771
+vt 0.467140 0.013541
+vt 0.437940 0.062021
+vt 0.418694 0.111026
+vt 0.411548 0.133977
+vt 0.404917 0.151817
+vt 0.398200 0.177723
+vt 0.396421 0.192455
+vt 0.396126 0.231215
+vt 0.482807 0.001091
+vt 0.465246 0.012218
+vt 0.434231 0.060860
+vt 0.413722 0.112734
+vt 0.406278 0.130880
+vt 0.397753 0.154535
+vt 0.390718 0.172336
+vt 0.388811 0.184259
+vt 0.483894 0.000866
+vt 0.467479 0.011696
+vt 0.438359 0.060674
+vt 0.419010 0.113922
+vt 0.412060 0.130257
+vt 0.403381 0.155615
+vt 0.396772 0.170695
+vt 0.394947 0.181718
+vt 0.394122 0.224712
+vt 0.489100 0.000562
+vt 0.478265 0.010987
+vt 0.458692 0.060437
+vt 0.445446 0.115578
+vt 0.440842 0.129428
+vt 0.433493 0.157124
+vt 0.429012 0.168469
+vt 0.427688 0.178256
+vt 0.426709 0.223032
+vt 0.492401 0.000471
+vt 0.485112 0.010775
+vt 0.471652 0.060373
+vt 0.462347 0.116094
+vt 0.459225 0.129185
+vt 0.452993 0.157597
+vt 0.449873 0.167802
+vt 0.448880 0.177211
+vt 0.447853 0.222532
+vt 0.495979 0.000427
+vt 0.492542 0.010672
+vt 0.485745 0.060349
+vt 0.480752 0.116370
+vt 0.479235 0.129074
+vt 0.474359 0.157853
+vt 0.472721 0.167476
+vt 0.472093 0.176692
+vt 0.471042 0.222291
+vt 0.497803 0.000430
+vt 0.496333 0.010680
+vt 0.492949 0.060359
+vt 0.490171 0.116373
+vt 0.489471 0.129091
+vt 0.485358 0.157859
+vt 0.484479 0.167501
+vt 0.484040 0.176724
+vt 0.482990 0.222314
+vt 0.499680 0.000431
+vt 0.500232 0.010681
+vt 0.500355 0.060366
+vt 0.499854 0.116392
+vt 0.499996 0.129099
+vt 0.496658 0.157879
+vt 0.496558 0.167505
+vt 0.496314 0.176721
+vt 0.495264 0.222320
+vt 0.503340 0.000484
+vt 0.507844 0.010802
+vt 0.514842 0.060424
+vt 0.518821 0.116161
+vt 0.520601 0.129259
+vt 0.518915 0.157676
+vt 0.520341 0.167887
+vt 0.520484 0.177297
+vt 0.519458 0.222618
+vt 0.506801 0.000583
+vt 0.515045 0.011031
+vt 0.528575 0.060520
+vt 0.536823 0.115687
+vt 0.540149 0.129546
+vt 0.540157 0.157251
+vt 0.543032 0.168605
+vt 0.543547 0.178394
+vt 0.542568 0.223170
+vt 0.512534 0.000900
+vt 0.526992 0.011767
+vt 0.551432 0.060809
+vt 0.566860 0.114099
+vt 0.572744 0.130449
+vt 0.575967 0.155821
+vt 0.581260 0.170915
+vt 0.582411 0.181941
+vt 0.581586 0.224936
+vt 0.514004 0.001128
+vt 0.530070 0.012295
+vt 0.557398 0.061007
+vt 0.574770 0.112926
+vt 0.581304 0.131089
+vt 0.585745 0.154759
+vt 0.591675 0.172575
+vt 0.593008 0.184503
+vt 0.500194 0.123038
+vt 0.538682 0.145632
+vt 0.499355 0.147686
+vt 0.567425 0.437987
+vt 0.495309 0.437689
+vt 0.572058 0.485965
+vt 0.494594 0.485885
+vt 0.579203 0.530428
+vt 0.494130 0.530571
+vt 0.588071 0.564595
+vt 0.493832 0.570346
+vt 0.603460 0.591429
+vt 0.494064 0.596672
+vt 0.634825 0.640783
+vt 0.495158 0.646017
+vt 0.673396 0.693624
+vt 0.496679 0.696159
+vt 0.692678 0.734348
+vt 0.497801 0.742533
+vt 0.702762 0.758051
+vt 0.498113 0.758399
+vt 0.701606 0.793564
+vt 0.498079 0.793255
+vt 0.493311 0.794017
+vt 0.530328 0.123074
+vt 0.571363 0.144013
+vt 0.627653 0.438535
+vt 0.636786 0.486372
+vt 0.650275 0.530678
+vt 0.666763 0.560227
+vt 0.694688 0.587480
+vt 0.750987 0.636789
+vt 0.820060 0.691789
+vt 0.854194 0.727752
+vt 0.872335 0.757923
+vt 0.870198 0.793953
+vt 0.555251 0.123104
+vt 0.591748 0.143109
+vt 0.665577 0.439237
+vt 0.677585 0.487036
+vt 0.695058 0.531278
+vt 0.716302 0.557996
+vt 0.751975 0.585509
+vt 0.823559 0.634725
+vt 0.911312 0.690971
+vt 0.954422 0.723886
+vt 0.977512 0.758039
+vt 0.974704 0.794354
+vt 0.570653 0.123123
+vt 0.596282 0.143062
+vt 0.674482 0.439878
+vt 0.687203 0.487720
+vt 0.705580 0.531920
+vt 0.727870 0.558106
+vt 0.765162 0.585722
+vt 0.839794 0.634825
+vt 0.931229 0.691232
+vt 0.975934 0.723388
+vt 1.000000 0.758335
+vt 0.996949 0.794648
+vt 0.404437 0.142964
+vt 0.426515 0.122951
+vt 0.573874 0.123126
+vt 0.323231 0.439478
+vt 0.310002 0.487319
+vt 0.291306 0.531610
+vt 0.269108 0.558029
+vt 0.232259 0.585436
+vt 0.158605 0.634392
+vt 0.068392 0.690386
+vt 0.024123 0.722737
+vt 0.000000 0.757160
+vt 0.002277 0.793454
+vt 0.408288 0.143022
+vt 0.329678 0.438833
+vt 0.316870 0.486617
+vt 0.298892 0.530832
+vt 0.277591 0.557845
+vt 0.242362 0.585244
+vt 0.172141 0.634290
+vt 0.086185 0.690163
+vt 0.044209 0.723323
+vt 0.021216 0.756939
+vt 0.023503 0.793219
+vt 0.429735 0.122954
+vt 0.428009 0.143973
+vt 0.365185 0.438218
+vt 0.354927 0.486047
+vt 0.340715 0.530335
+vt 0.323958 0.560190
+vt 0.296482 0.587348
+vt 0.241975 0.636524
+vt 0.175315 0.691197
+vt 0.142962 0.727426
+vt 0.125096 0.757073
+vt 0.126941 0.793066
+vt 0.445138 0.122973
+vt 0.460221 0.145670
+vt 0.423771 0.437812
+vt 0.417791 0.485792
+vt 0.409775 0.530252
+vt 0.400446 0.564744
+vt 0.385513 0.591511
+vt 0.356231 0.640793
+vt 0.320514 0.693380
+vt 0.303405 0.734406
+vt 0.293784 0.757604
+vt 0.294807 0.793079
+vt 0.470061 0.123002
+vt 0.584118 0.143846
+vt 0.652512 0.440323
+vt 0.663585 0.488333
+vt 0.679561 0.533226
+vt 0.698964 0.560878
+vt 0.731472 0.587869
+vt 0.796494 0.637100
+vt 0.876085 0.692426
+vt 0.914822 0.726015
+vt 0.935707 0.758515
+vt 0.932883 0.794633
+vt 0.564354 0.123115
+vt 0.557453 0.145374
+vt 0.603941 0.440767
+vt 0.611404 0.489119
+vt 0.622180 0.535513
+vt 0.635332 0.566338
+vt 0.657480 0.591977
+vt 0.701732 0.641506
+vt 0.755840 0.694578
+vt 0.781940 0.731416
+vt 0.796061 0.758682
+vt 0.793893 0.794463
+vt 0.543740 0.123090
+vt 0.520898 0.147383
+vt 0.537164 0.441133
+vt 0.539680 0.489942
+vt 0.543358 0.538385
+vt 0.547975 0.573543
+vt 0.555980 0.597337
+vt 0.571895 0.647283
+vt 0.591287 0.697315
+vt 0.600266 0.738659
+vt 0.605208 0.758809
+vt 0.604010 0.794167
+vt 0.515597 0.123057
+vt 0.480790 0.147467
+vt 0.463733 0.441064
+vt 0.460823 0.489897
+vt 0.456750 0.538466
+vt 0.452065 0.573899
+vt 0.444570 0.597554
+vt 0.429485 0.647493
+vt 0.410904 0.697284
+vt 0.401280 0.738907
+vt 0.396151 0.758578
+vt 0.396067 0.793912
+vt 0.484791 0.123020
+vt 0.444068 0.145371
+vt 0.396343 0.440538
+vt 0.388469 0.488901
+vt 0.377334 0.535405
+vt 0.364191 0.566484
+vt 0.342521 0.591951
+vt 0.299133 0.641406
+vt 0.245883 0.694154
+vt 0.219396 0.731230
+vt 0.205038 0.757996
+vt 0.206019 0.793754
+vt 0.456648 0.122986
+vt 0.417060 0.143778
+vt 0.346645 0.439977
+vt 0.335120 0.487990
+vt 0.318813 0.532980
+vt 0.299476 0.560871
+vt 0.267422 0.587665
+vt 0.203316 0.636772
+vt 0.124729 0.691714
+vt 0.085988 0.725511
+vt 0.064911 0.757495
+vt 0.066727 0.793592
+vt 0.436035 0.122962
+vt 0.495260 0.736170
+vt 0.500696 0.234197
+vt 0.536496 0.234045
+vt 0.577350 0.233157
+vt 0.495655 0.736171
+vt 0.501053 0.234197
+vt 0.464891 0.233960
+vt 0.422300 0.232972
+vt 0.408534 0.223769
+vt 0.563583 0.223955
+vt 0.367604 0.795692
+vt 0.344548 0.863566
+vt 0.343493 0.862334
+vt 0.368659 0.796923
+vt 0.344327 0.876900
+vt 0.343272 0.875668
+vt 0.145143 0.856433
+vt 0.144088 0.855201
+vt 0.144487 0.796170
+vt 0.143432 0.794939
+vt 0.132013 0.795411
+vt 0.130679 0.853014
+vt 0.129623 0.851782
+vt 0.133068 0.796642
+vt 0.006462 0.836828
+vt 0.005407 0.835597
+vt 0.003936 0.813497
+vt 0.002882 0.812266
+vt 0.005457 0.809612
+vt 0.004402 0.808380
+vt 0.004946 0.796489
+vt 0.003891 0.795258
+vt 0.854563 0.797672
+vt 0.853823 0.852194
+vt 0.854872 0.853763
+vt 0.853514 0.796103
+vt 0.978631 0.836386
+vt 0.979680 0.837955
+vt 0.982018 0.813180
+vt 0.983067 0.814749
+vt 0.980641 0.809310
+vt 0.981690 0.810879
+vt 0.981636 0.796256
+vt 0.982685 0.797825
+vt 0.618972 0.797391
+vt 0.639573 0.862183
+vt 0.640622 0.863752
+vt 0.617923 0.795822
+vt 0.639302 0.875450
+vt 0.640350 0.877019
+vt 0.839233 0.855561
+vt 0.840281 0.857130
+vt 0.842114 0.795606
+vt 0.843162 0.797175
+vt 0.341429 0.892018
+vt 0.354422 0.771751
+vt 0.371180 0.771906
+vt 0.361304 0.757630
+vt 0.375583 0.756290
+vt 0.376061 0.747099
+vt 0.386704 0.746114
+vt 0.396022 0.741918
+vt 0.399519 0.741536
+vt 0.406939 0.741656
+vt 0.407021 0.741651
+vt 0.362783 0.891001
+vt 0.382518 0.772090
+vt 0.385281 0.755640
+vt 0.393806 0.745632
+vt 0.401875 0.741347
+vt 0.407053 0.741648
+vt 0.377126 0.890562
+vt 0.415783 0.773053
+vt 0.413937 0.755087
+vt 0.414119 0.745184
+vt 0.408737 0.741153
+vt 0.407022 0.741639
+vt 0.418673 0.890604
+vt 0.457357 0.774594
+vt 0.449912 0.755475
+vt 0.439090 0.745395
+vt 0.417274 0.741201
+vt 0.406885 0.741627
+vt 0.367889 0.778452
+vt 0.364200 0.897177
+vt 0.470167 0.891713
+vt 0.370122 0.761446
+vt 0.381736 0.748731
+vt 0.398612 0.742335
+vt 0.406817 0.741630
+vt 0.341083 0.776828
+vt 0.346977 0.760159
+vt 0.366096 0.747919
+vt 0.393148 0.742062
+vt 0.406985 0.741639
+vt 0.330950 0.895375
+vt 0.334983 0.775889
+vt 0.341985 0.759566
+vt 0.362746 0.747620
+vt 0.391920 0.741975
+vt 0.407026 0.741643
+vt 0.322792 0.894560
+vt 0.337783 0.774019
+vt 0.345386 0.758625
+vt 0.365129 0.747299
+vt 0.392543 0.741914
+vt 0.407019 0.741650
+vt 0.324143 0.893295
+vt 0.342886 0.773061
+vt 0.350401 0.758202
+vt 0.368571 0.747202
+vt 0.393616 0.741910
+vt 0.406994 0.741653
+vt 0.329152 0.892735
+vt 0.606943 0.892335
+vt 0.578997 0.772154
+vt 0.598183 0.772042
+vt 0.576236 0.756530
+vt 0.592545 0.757906
+vt 0.567870 0.746331
+vt 0.580039 0.747342
+vt 0.558973 0.741727
+vt 0.562967 0.742117
+vt 0.553477 0.741826
+vt 0.553572 0.741831
+vt 0.582516 0.891263
+vt 0.566609 0.772310
+vt 0.565670 0.755855
+vt 0.560098 0.745830
+vt 0.556401 0.741531
+vt 0.553436 0.741823
+vt 0.566831 0.890789
+vt 0.533400 0.773194
+vt 0.537139 0.755234
+vt 0.539725 0.745333
+vt 0.549548 0.741322
+vt 0.553438 0.741813
+vt 0.525261 0.890731
+vt 0.494395 0.774638
+vt 0.503451 0.755539
+vt 0.516203 0.745487
+vt 0.541534 0.741350
+vt 0.553540 0.741802
+vt 0.599293 0.897457
+vt 0.597789 0.778726
+vt 0.476853 0.891721
+vt 0.595602 0.761715
+vt 0.582464 0.748970
+vt 0.563094 0.742531
+vt 0.553620 0.741805
+vt 0.620315 0.777161
+vt 0.614992 0.760479
+vt 0.595561 0.748192
+vt 0.567681 0.742270
+vt 0.553478 0.741814
+vt 0.627351 0.895729
+vt 0.624397 0.776234
+vt 0.618175 0.759896
+vt 0.597681 0.747900
+vt 0.568493 0.742185
+vt 0.553449 0.741818
+vt 0.633134 0.894930
+vt 0.618307 0.774354
+vt 0.611745 0.758943
+vt 0.593228 0.747572
+vt 0.567181 0.742122
+vt 0.553475 0.741825
+vt 0.628051 0.893657
+vt 0.611697 0.773381
+vt 0.605318 0.758506
+vt 0.588819 0.747464
+vt 0.565791 0.742115
+vt 0.553508 0.741827
+vt 0.621374 0.893083
+vt 0.467142 0.741480
+vt 0.460043 0.823727
+vt 0.466255 0.823734
+vt 0.491990 0.741510
+vt 0.491102 0.823764
+vt 0.497314 0.823771
+vt 0.459387 0.936839
+vt 0.481806 0.936865
+vt 0.442062 0.996371
+vt 0.457305 0.996390
+vt 0.441061 0.998017
+vt 0.455880 0.998035
+vt 0.440376 0.996388
+vt 0.454893 0.996405
+vt 0.440171 0.989270
+vt 0.454568 0.989287
+vt 0.440732 0.939254
+vt 0.455138 0.939271
+vt 0.441300 0.928768
+vt 0.455901 0.928785
+vt 0.442019 0.920921
+vt 0.456893 0.920939
+vt 0.442564 0.913924
+vt 0.457640 0.913942
+vt 0.463957 0.747884
+vt 0.487465 0.747912
+vt 0.464844 0.743833
+vt 0.488714 0.743862
+vt 0.549439 0.921434
+vt 0.548851 0.918488
+vt 0.544157 0.921323
+vt 0.551271 0.945406
+vt 0.533127 0.945025
+vt 0.552776 0.983628
+vt 0.528590 0.983120
+vt 0.552915 0.990499
+vt 0.528985 0.989997
+vt 0.552488 0.989209
+vt 0.532597 0.988792
+vt 0.540100 0.920799
+vt 0.519194 0.943227
+vt 0.510019 0.980723
+vt 0.510611 0.987626
+vt 0.517323 0.986820
+vt 0.538358 0.920003
+vt 0.513207 0.940494
+vt 0.502039 0.977080
+vt 0.502715 0.984021
+vt 0.510760 0.983824
+vt 0.539395 0.919149
+vt 0.516770 0.937557
+vt 0.506788 0.973165
+vt 0.507415 0.980148
+vt 0.514666 0.980605
+vt 0.542934 0.918463
+vt 0.528929 0.935204
+vt 0.522994 0.970029
+vt 0.523449 0.977045
+vt 0.527995 0.978025
+vt 0.548028 0.918132
+vt 0.546424 0.934066
+vt 0.546314 0.968512
+vt 0.546522 0.975543
+vt 0.547174 0.976777
+vt 0.553310 0.918243
+vt 0.564569 0.934446
+vt 0.570500 0.969019
+vt 0.570451 0.976045
+vt 0.567066 0.977194
+vt 0.557366 0.918766
+vt 0.578501 0.936245
+vt 0.589071 0.971416
+vt 0.588825 0.978417
+vt 0.582340 0.979166
+vt 0.559110 0.919562
+vt 0.584488 0.938978
+vt 0.597051 0.975059
+vt 0.596721 0.982022
+vt 0.588903 0.982162
+vt 0.558072 0.920417
+vt 0.580925 0.941915
+vt 0.592302 0.978974
+vt 0.592022 0.985895
+vt 0.584996 0.985382
+vt 0.554533 0.921102
+vt 0.568767 0.944267
+vt 0.576096 0.982110
+vt 0.575988 0.988998
+vt 0.571668 0.987961
+vt 0.424145 0.921277
+vt 0.423693 0.918338
+vt 0.418870 0.920933
+vt 0.423822 0.945229
+vt 0.405702 0.944048
+vt 0.421388 0.983437
+vt 0.397235 0.981863
+vt 0.420772 0.990308
+vt 0.396875 0.988751
+vt 0.420346 0.989024
+vt 0.400481 0.987729
+vt 0.414819 0.920239
+vt 0.391787 0.941664
+vt 0.378687 0.978685
+vt 0.378524 0.985607
+vt 0.385227 0.985116
+vt 0.413078 0.919381
+vt 0.385807 0.938716
+vt 0.370716 0.974756
+vt 0.370638 0.981719
+vt 0.378671 0.981884
+vt 0.414113 0.918588
+vt 0.389363 0.935994
+vt 0.375457 0.971128
+vt 0.375328 0.978129
+vt 0.382570 0.978900
+vt 0.417648 0.918074
+vt 0.401504 0.934227
+vt 0.391639 0.968773
+vt 0.391339 0.975799
+vt 0.395879 0.976963
+vt 0.422734 0.917975
+vt 0.418975 0.933889
+vt 0.414927 0.968321
+vt 0.414380 0.975353
+vt 0.415032 0.976592
+vt 0.428009 0.918319
+vt 0.437095 0.935070
+vt 0.439080 0.969896
+vt 0.438277 0.976910
+vt 0.434897 0.977887
+vt 0.432060 0.919013
+vt 0.451010 0.937454
+vt 0.457627 0.973073
+vt 0.456628 0.980054
+vt 0.450151 0.980500
+vt 0.433801 0.919872
+vt 0.456990 0.940402
+vt 0.465599 0.977003
+vt 0.464515 0.983942
+vt 0.456707 0.983732
+vt 0.432766 0.920664
+vt 0.453434 0.943124
+vt 0.460858 0.980631
+vt 0.459824 0.987532
+vt 0.452808 0.986716
+vt 0.429231 0.921178
+vt 0.441293 0.944891
+vt 0.444676 0.982987
+vt 0.443813 0.989862
+vt 0.439499 0.988653
+vt 0.489178 0.904955
+vt 0.478920 0.903724
+vt 0.488659 0.905860
+vt 0.513457 0.924223
+vt 0.511674 0.927332
+vt 0.523738 0.959386
+vt 0.521362 0.963531
+vt 0.522949 0.966121
+vt 0.520598 0.970220
+vt 0.515067 0.966111
+vt 0.513113 0.969519
+vt 0.486649 0.906626
+vt 0.504771 0.929961
+vt 0.512160 0.967035
+vt 0.511494 0.973688
+vt 0.505545 0.972402
+vt 0.483455 0.907135
+vt 0.493798 0.931710
+vt 0.497535 0.969367
+vt 0.497023 0.975995
+vt 0.493517 0.974319
+vt 0.479562 0.907310
+vt 0.480427 0.932313
+vt 0.479712 0.970170
+vt 0.479389 0.976790
+vt 0.478859 0.974980
+vt 0.475564 0.907126
+vt 0.466693 0.931678
+vt 0.461405 0.969323
+vt 0.461277 0.975952
+vt 0.463802 0.974284
+vt 0.472068 0.906608
+vt 0.454687 0.929901
+vt 0.445402 0.966955
+vt 0.445442 0.973609
+vt 0.450640 0.972336
+vt 0.469608 0.905838
+vt 0.446236 0.927254
+vt 0.434137 0.963426
+vt 0.434297 0.970118
+vt 0.441375 0.969434
+vt 0.468558 0.904931
+vt 0.442627 0.924138
+vt 0.429327 0.959274
+vt 0.429538 0.966009
+vt 0.437419 0.966018
+vt 0.469077 0.904026
+vt 0.444410 0.921030
+vt 0.431703 0.955130
+vt 0.431889 0.961909
+vt 0.439374 0.962610
+vt 0.471086 0.903260
+vt 0.451313 0.918400
+vt 0.440904 0.951625
+vt 0.440993 0.958442
+vt 0.446941 0.959728
+vt 0.474281 0.902751
+vt 0.462285 0.916651
+vt 0.455530 0.949294
+vt 0.455463 0.956135
+vt 0.458970 0.957810
+vt 0.478174 0.902575
+vt 0.475656 0.916048
+vt 0.473353 0.948490
+vt 0.473097 0.955340
+vt 0.473628 0.957150
+vt 0.482172 0.902760
+vt 0.489391 0.916684
+vt 0.491660 0.949337
+vt 0.491210 0.956177
+vt 0.488685 0.957846
+vt 0.485667 0.903278
+vt 0.501397 0.918460
+vt 0.507663 0.951705
+vt 0.507044 0.958521
+vt 0.501847 0.959794
+vt 0.488128 0.904048
+vt 0.509848 0.921108
+vt 0.518928 0.955234
+vt 0.518190 0.962012
+vt 0.511111 0.962696
+vt 0.490402 0.906636
+vt 0.478826 0.907307
+vt 0.488446 0.905821
+vt 0.501508 0.904625
+vt 0.497682 0.903031
+vt 0.513572 0.898392
+vt 0.507734 0.895959
+vt 0.520474 0.898400
+vt 0.513468 0.895480
+vt 0.527756 0.896328
+vt 0.519527 0.892898
+vt 0.532641 0.890349
+vt 0.523612 0.886586
+vt 0.534751 0.880504
+vt 0.525409 0.876611
+vt 0.483918 0.905221
+vt 0.488823 0.901856
+vt 0.494212 0.894166
+vt 0.497242 0.893329
+vt 0.500470 0.890372
+vt 0.502703 0.883814
+vt 0.503775 0.873743
+vt 0.478030 0.904996
+vt 0.477305 0.901416
+vt 0.476632 0.893495
+vt 0.476145 0.892524
+vt 0.475691 0.889426
+vt 0.475516 0.882776
+vt 0.475646 0.872669
+vt 0.472360 0.905207
+vt 0.466213 0.901829
+vt 0.459702 0.894125
+vt 0.455830 0.893280
+vt 0.451831 0.890314
+vt 0.449337 0.883750
+vt 0.448558 0.873677
+vt 0.468427 0.905797
+vt 0.458520 0.902984
+vt 0.447960 0.895887
+vt 0.441739 0.895394
+vt 0.435282 0.892798
+vt 0.431179 0.886476
+vt 0.429771 0.876497
+vt 0.467286 0.906609
+vt 0.456287 0.904571
+vt 0.444552 0.898310
+vt 0.437650 0.898301
+vt 0.430478 0.896212
+vt 0.425909 0.890222
+vt 0.424318 0.880373
+vt 0.469241 0.907423
+vt 0.460112 0.906165
+vt 0.450390 0.900743
+vt 0.444656 0.901221
+vt 0.438707 0.899641
+vt 0.434938 0.893984
+vt 0.433660 0.884266
+vt 0.473770 0.908024
+vt 0.468971 0.907340
+vt 0.463912 0.902535
+vt 0.460882 0.903372
+vt 0.457764 0.902168
+vt 0.455847 0.896756
+vt 0.455294 0.887134
+vt 0.479658 0.908248
+vt 0.480490 0.907779
+vt 0.481492 0.903207
+vt 0.481979 0.904178
+vt 0.482542 0.903114
+vt 0.483033 0.897794
+vt 0.483423 0.888208
+vt 0.485328 0.908037
+vt 0.491581 0.907367
+vt 0.498422 0.902577
+vt 0.502294 0.903422
+vt 0.506403 0.902226
+vt 0.509212 0.896820
+vt 0.510510 0.887200
+vt 0.489260 0.907447
+vt 0.499275 0.906212
+vt 0.510164 0.900814
+vt 0.516385 0.901307
+vt 0.522952 0.899742
+vt 0.527370 0.894095
+vt 0.529298 0.884380
+vt 0.435164 0.921226
+vt 0.423586 0.921892
+vt 0.433212 0.920281
+vt 0.446278 0.919232
+vt 0.442459 0.917382
+vt 0.458366 0.913050
+vt 0.452538 0.910227
+vt 0.465268 0.913058
+vt 0.458275 0.909671
+vt 0.472557 0.911003
+vt 0.464343 0.907025
+vt 0.477464 0.905073
+vt 0.468453 0.900708
+vt 0.479612 0.895309
+vt 0.470288 0.890792
+vt 0.428687 0.919585
+vt 0.433606 0.916021
+vt 0.439025 0.908150
+vt 0.442059 0.907178
+vt 0.445298 0.904097
+vt 0.447556 0.897496
+vt 0.448667 0.887469
+vt 0.422799 0.919325
+vt 0.422089 0.915513
+vt 0.421447 0.907374
+vt 0.420965 0.906248
+vt 0.420524 0.903004
+vt 0.420375 0.896296
+vt 0.420542 0.886228
+vt 0.417129 0.919572
+vt 0.410995 0.915994
+vt 0.404515 0.908109
+vt 0.400647 0.907129
+vt 0.396659 0.904039
+vt 0.394191 0.897432
+vt 0.393450 0.887403
+vt 0.413194 0.920257
+vt 0.403297 0.917335
+vt 0.392764 0.910156
+vt 0.386546 0.909585
+vt 0.380099 0.906924
+vt 0.376020 0.900598
+vt 0.374650 0.890678
+vt 0.412048 0.921199
+vt 0.401056 0.919178
+vt 0.389345 0.912967
+vt 0.382443 0.912959
+vt 0.375279 0.910887
+vt 0.370733 0.904945
+vt 0.369179 0.895177
+vt 0.414000 0.922144
+vt 0.404875 0.921027
+vt 0.395173 0.915790
+vt 0.389436 0.916346
+vt 0.383493 0.914865
+vt 0.379745 0.909310
+vt 0.378503 0.899693
+vt 0.418526 0.922840
+vt 0.413728 0.922388
+vt 0.408686 0.917867
+vt 0.405652 0.918839
+vt 0.402538 0.917793
+vt 0.400641 0.912523
+vt 0.400124 0.903017
+vt 0.424413 0.923099
+vt 0.425245 0.922896
+vt 0.426264 0.918643
+vt 0.426745 0.919770
+vt 0.427312 0.918886
+vt 0.427823 0.913722
+vt 0.428249 0.904258
+vt 0.430084 0.922854
+vt 0.436339 0.922415
+vt 0.443196 0.917909
+vt 0.447065 0.918889
+vt 0.451177 0.917851
+vt 0.454007 0.912586
+vt 0.455341 0.903083
+vt 0.434019 0.922168
+vt 0.444037 0.921074
+vt 0.454947 0.915862
+vt 0.461165 0.916432
+vt 0.467738 0.914966
+vt 0.472177 0.909421
+vt 0.474141 0.899807
+vt 0.560322 0.921376
+vt 0.548743 0.922041
+vt 0.558370 0.920430
+vt 0.571436 0.919381
+vt 0.567617 0.917532
+vt 0.583524 0.913199
+vt 0.577696 0.910376
+vt 0.590426 0.913208
+vt 0.583432 0.909820
+vt 0.597715 0.911152
+vt 0.589501 0.907174
+vt 0.602622 0.905222
+vt 0.593610 0.900857
+vt 0.604770 0.895458
+vt 0.595446 0.890942
+vt 0.553844 0.919735
+vt 0.558764 0.916170
+vt 0.564183 0.908299
+vt 0.567217 0.907327
+vt 0.570456 0.904246
+vt 0.572714 0.897645
+vt 0.573824 0.887618
+vt 0.547957 0.919475
+vt 0.547247 0.915662
+vt 0.546605 0.907524
+vt 0.546123 0.906397
+vt 0.545682 0.903153
+vt 0.545532 0.896446
+vt 0.545700 0.886378
+vt 0.542286 0.919721
+vt 0.536153 0.916144
+vt 0.529672 0.908258
+vt 0.525804 0.907278
+vt 0.521817 0.904188
+vt 0.519348 0.897581
+vt 0.518608 0.887552
+vt 0.538351 0.920406
+vt 0.528455 0.917485
+vt 0.517922 0.910305
+vt 0.511704 0.909735
+vt 0.505256 0.907073
+vt 0.501178 0.900747
+vt 0.499808 0.890828
+vt 0.537206 0.921348
+vt 0.526215 0.919327
+vt 0.514503 0.913117
+vt 0.507601 0.913109
+vt 0.500437 0.911036
+vt 0.495891 0.905095
+vt 0.494337 0.895326
+vt 0.539158 0.922293
+vt 0.530033 0.921176
+vt 0.520331 0.915940
+vt 0.514594 0.916496
+vt 0.508651 0.915014
+vt 0.504902 0.909460
+vt 0.503661 0.899843
+vt 0.543684 0.922989
+vt 0.538886 0.922537
+vt 0.533844 0.918017
+vt 0.530810 0.918989
+vt 0.527696 0.917942
+vt 0.525799 0.912672
+vt 0.525282 0.903166
+vt 0.549571 0.923249
+vt 0.550403 0.923045
+vt 0.551422 0.918792
+vt 0.551903 0.919919
+vt 0.552470 0.919035
+vt 0.552980 0.913871
+vt 0.553407 0.904407
+vt 0.555241 0.923003
+vt 0.561497 0.922564
+vt 0.568354 0.918058
+vt 0.572222 0.919038
+vt 0.576335 0.918000
+vt 0.579165 0.912735
+vt 0.580499 0.903232
+vt 0.559177 0.922317
+vt 0.569195 0.921223
+vt 0.580104 0.916011
+vt 0.586323 0.916581
+vt 0.592896 0.915115
+vt 0.597334 0.909570
+vt 0.599299 0.899957
+vt 0.559044 0.903560
+vt 0.555560 0.904061
+vt 0.557240 0.903502
+vt 0.562383 0.902441
+vt 0.558853 0.902327
+vt 0.565998 0.899330
+vt 0.560611 0.899155
+vt 0.568167 0.890833
+vt 0.561703 0.890623
+vt 0.555180 0.903480
+vt 0.554825 0.902283
+vt 0.554462 0.899089
+vt 0.554324 0.890544
+vt 0.553180 0.903497
+vt 0.550910 0.902317
+vt 0.548487 0.899141
+vt 0.547155 0.890606
+vt 0.551541 0.903552
+vt 0.547705 0.902423
+vt 0.543596 0.899303
+vt 0.541285 0.890801
+vt 0.550515 0.903635
+vt 0.545699 0.902586
+vt 0.540533 0.899551
+vt 0.537609 0.891098
+vt 0.550258 0.903734
+vt 0.545195 0.902780
+vt 0.539764 0.899847
+vt 0.536687 0.891453
+vt 0.550808 0.903834
+vt 0.546272 0.902975
+vt 0.541408 0.900146
+vt 0.538658 0.891812
+vt 0.552082 0.903920
+vt 0.548764 0.903143
+vt 0.545212 0.900402
+vt 0.543224 0.892119
+vt 0.553886 0.903978
+vt 0.552293 0.903257
+vt 0.550598 0.900576
+vt 0.549688 0.892329
+vt 0.555946 0.904000
+vt 0.556322 0.903301
+vt 0.556747 0.900643
+vt 0.557066 0.892408
+vt 0.557947 0.903983
+vt 0.560237 0.903267
+vt 0.562722 0.900591
+vt 0.564236 0.892346
+vt 0.559585 0.903929
+vt 0.563441 0.903161
+vt 0.567613 0.900428
+vt 0.570106 0.892151
+vt 0.560611 0.903845
+vt 0.565448 0.902998
+vt 0.570676 0.900181
+vt 0.573781 0.891854
+vt 0.560868 0.903746
+vt 0.565951 0.902804
+vt 0.571445 0.899885
+vt 0.574704 0.891499
+vt 0.560318 0.903646
+vt 0.564875 0.902609
+vt 0.569802 0.899586
+vt 0.572732 0.891140
+vt 0.397743 0.903368
+vt 0.394259 0.903868
+vt 0.395939 0.903310
+vt 0.401082 0.902248
+vt 0.397553 0.902134
+vt 0.404697 0.899137
+vt 0.399311 0.898963
+vt 0.406867 0.890641
+vt 0.400403 0.890431
+vt 0.393880 0.903288
+vt 0.393524 0.902090
+vt 0.393162 0.898897
+vt 0.393024 0.890351
+vt 0.391879 0.903305
+vt 0.389610 0.902124
+vt 0.387187 0.898948
+vt 0.385854 0.890414
+vt 0.390241 0.903359
+vt 0.386405 0.902231
+vt 0.382296 0.899111
+vt 0.379985 0.890608
+vt 0.389215 0.903442
+vt 0.384398 0.902393
+vt 0.379233 0.899359
+vt 0.376309 0.890906
+vt 0.388958 0.903541
+vt 0.383895 0.902587
+vt 0.378464 0.899655
+vt 0.375387 0.891261
+vt 0.389508 0.903641
+vt 0.384971 0.902783
+vt 0.380107 0.899953
+vt 0.377358 0.891619
+vt 0.390782 0.903727
+vt 0.387464 0.902951
+vt 0.383912 0.900209
+vt 0.381923 0.891927
+vt 0.392586 0.903786
+vt 0.390993 0.903065
+vt 0.389298 0.900384
+vt 0.388387 0.892136
+vt 0.394645 0.903808
+vt 0.395022 0.903108
+vt 0.395447 0.900450
+vt 0.395766 0.892216
+vt 0.396646 0.903790
+vt 0.398936 0.903074
+vt 0.401422 0.900398
+vt 0.402936 0.892153
+vt 0.398285 0.903736
+vt 0.402141 0.902968
+vt 0.406313 0.900236
+vt 0.408806 0.891959
+vt 0.399310 0.903653
+vt 0.404148 0.902806
+vt 0.409376 0.899988
+vt 0.412481 0.891661
+vt 0.399568 0.903554
+vt 0.404651 0.902612
+vt 0.410145 0.899692
+vt 0.413404 0.891306
+vt 0.399017 0.903454
+vt 0.403575 0.902416
+vt 0.408502 0.899393
+vt 0.411432 0.890948
+vt 0.560050 0.895619
+vt 0.598903 0.933649
+vt 0.559659 0.931810
+vt 0.626960 0.931921
+vt 0.632743 0.931121
+vt 0.633550 0.930793
+vt 0.633940 0.894602
+vt 0.632944 0.930462
+vt 0.633334 0.894271
+vt 0.630952 0.930143
+vt 0.631342 0.893951
+vt 0.627661 0.929849
+vt 0.589170 0.929516
+vt 0.589561 0.893325
+vt 0.398164 0.895426
+vt 0.363809 0.933369
+vt 0.397773 0.931617
+vt 0.330560 0.931567
+vt 0.322401 0.930751
+vt 0.321016 0.894228
+vt 0.320625 0.930420
+vt 0.320649 0.893898
+vt 0.320258 0.930089
+vt 0.321706 0.893582
+vt 0.321316 0.929773
+vt 0.323752 0.929486
+vt 0.361788 0.893053
+vt 0.361398 0.929244
+vt 0.559656 0.902097
+vt 0.555589 0.901346
+vt 0.557317 0.902165
+vt 0.573965 0.927557
+vt 0.563255 0.927865
+vt 0.573734 0.930613
+vt 0.563137 0.930918
+vt 0.570621 0.930463
+vt 0.561812 0.930717
+vt 0.554520 0.902163
+vt 0.550449 0.927858
+vt 0.550467 0.930911
+vt 0.551280 0.930711
+vt 0.552015 0.902093
+vt 0.538979 0.927538
+vt 0.539119 0.930594
+vt 0.541846 0.930447
+vt 0.550473 0.901973
+vt 0.531919 0.926990
+vt 0.532133 0.930052
+vt 0.536040 0.929997
+vt 0.550307 0.901836
+vt 0.531159 0.926362
+vt 0.531382 0.929430
+vt 0.535415 0.929480
+vt 0.551562 0.901718
+vt 0.536905 0.925821
+vt 0.537067 0.928895
+vt 0.540141 0.929035
+vt 0.553901 0.901651
+vt 0.547615 0.925513
+vt 0.547664 0.928591
+vt 0.548949 0.928782
+vt 0.556698 0.901652
+vt 0.560422 0.925520
+vt 0.560334 0.928598
+vt 0.559482 0.928788
+vt 0.559203 0.901723
+vt 0.571891 0.925841
+vt 0.571682 0.928915
+vt 0.568915 0.929052
+vt 0.560745 0.901842
+vt 0.578951 0.926388
+vt 0.578668 0.929457
+vt 0.574722 0.929502
+vt 0.560911 0.901979
+vt 0.579711 0.927017
+vt 0.579419 0.930079
+vt 0.575346 0.930019
+vt 0.398356 0.901905
+vt 0.394289 0.901154
+vt 0.396017 0.901972
+vt 0.412665 0.927365
+vt 0.401954 0.927673
+vt 0.412434 0.930421
+vt 0.401837 0.930726
+vt 0.409321 0.930271
+vt 0.400512 0.930524
+vt 0.394258 0.903992
+vt 0.393220 0.901971
+vt 0.389148 0.927666
+vt 0.389167 0.930719
+vt 0.389979 0.930518
+vt 0.390714 0.901901
+vt 0.377679 0.927345
+vt 0.377819 0.930402
+vt 0.380546 0.930255
+vt 0.389172 0.901781
+vt 0.370618 0.926798
+vt 0.370833 0.929860
+vt 0.374739 0.929804
+vt 0.389006 0.901644
+vt 0.369859 0.926169
+vt 0.370082 0.929238
+vt 0.374115 0.929287
+vt 0.390261 0.901526
+vt 0.375605 0.925629
+vt 0.375767 0.928703
+vt 0.378840 0.928843
+vt 0.392601 0.901458
+vt 0.386315 0.925321
+vt 0.386364 0.928398
+vt 0.387649 0.928590
+vt 0.395398 0.901460
+vt 0.399121 0.925328
+vt 0.399034 0.928406
+vt 0.398181 0.928596
+vt 0.397903 0.901530
+vt 0.410591 0.925648
+vt 0.410382 0.928723
+vt 0.407615 0.928859
+vt 0.399445 0.901650
+vt 0.417651 0.926196
+vt 0.417368 0.929265
+vt 0.413422 0.929310
+vt 0.399611 0.901787
+vt 0.418410 0.926824
+vt 0.418119 0.929886
+vt 0.414046 0.929826
+vn 0.011353 0.401349 0.915830
+vn 0.382275 0.411969 -0.827082
+vn -0.579608 0.385968 -0.717673
+vn -0.581500 0.379101 -0.719810
+vn -0.578936 0.391400 -0.715262
+vn -0.587512 0.353526 -0.727866
+vn -0.281625 0.919126 -0.275399
+vn 0.057253 0.984527 0.165563
+vn 0.071383 0.980926 0.180639
+vn -0.296457 0.908780 -0.293558
+vn 0.474380 0.596789 0.647114
+vn 0.491165 0.565630 0.662404
+vn 0.513932 0.515061 0.685965
+vn 0.517106 0.508774 0.688223
+vn 0.275857 0.401013 -0.873531
+vn -0.191260 0.288827 -0.938047
+vn -0.394971 -0.383221 0.834925
+vn 0.387310 -0.917112 0.094089
+vn 0.623280 0.340129 0.704123
+vn 0.167821 -0.764855 0.621906
+vn 0.346049 -0.937437 -0.037507
+vn 0.028108 -0.899045 0.436933
+vn 0.351848 -0.931425 -0.092898
+vn -0.021699 -0.935636 0.352214
+vn 0.354595 -0.925047 -0.135960
+vn -0.066134 -0.957091 0.282022
+vn 0.350780 -0.921049 -0.169012
+vn 0.518418 -0.764580 -0.382855
+vn 0.338420 -0.920835 -0.193701
+vn 0.202460 -0.979156 -0.015046
+vn 0.316080 -0.924802 -0.211646
+vn 0.010743 -0.996918 -0.077364
+vn 0.282449 -0.932432 -0.225257
+vn -0.263527 -0.963652 0.043672
+vn 0.235603 -0.942106 -0.238563
+vn -0.338664 -0.940489 -0.026948
+vn 0.130589 -0.939299 -0.317209
+vn -0.497787 -0.838588 -0.221198
+vn -0.419324 0.243233 -0.874599
+vn -0.876553 -0.425245 0.225349
+vn 0.628407 0.047426 -0.776421
+vn 0.780297 -0.115146 -0.614673
+vn 0.783563 0.069918 -0.617328
+vn -0.748405 -0.260353 0.609973
+vn -0.749504 -0.261116 0.608264
+vn 0.753014 -0.055422 -0.655629
+vn -0.749474 -0.261025 0.608386
+vn 0.758538 -0.024628 -0.651143
+vn -0.749535 -0.261055 0.608264
+vn 0.767571 -0.000092 -0.640919
+vn -0.749565 -0.261055 0.608234
+vn 0.611438 -0.665090 -0.428663
+vn -0.751213 -0.254250 0.609088
+vn 0.407697 -0.867550 -0.284768
+vn -0.752129 -0.255257 0.607532
+vn 0.356670 -0.868801 -0.343394
+vn 0.296243 -0.863887 -0.407300
+vn 0.220618 -0.847133 -0.483383
+vn 0.021393 -0.758263 -0.651570
+vn -0.141331 -0.391461 -0.909268
+vn -0.187475 -0.241737 0.952055
+vn 0.837397 0.084780 -0.539933
+vn -0.023103 -0.413434 0.910215
+vn 0.824702 0.075686 -0.560442
+vn -0.095370 -0.585253 0.805200
+vn 0.812677 0.068239 -0.578692
+vn -0.154118 -0.691580 0.705618
+vn 0.800623 0.065004 -0.595599
+vn -0.233772 -0.776574 0.584979
+vn 0.788965 0.064425 -0.611011
+vn -0.333689 -0.831782 0.443556
+vn 0.785150 0.052644 -0.617023
+vn -0.415937 -0.817103 0.399091
+vn 0.779382 0.063265 -0.623310
+vn -0.437086 -0.843745 0.311441
+vn 0.767327 0.061159 -0.638295
+vn -0.553789 -0.813898 0.175604
+vn 0.753685 0.061495 -0.654317
+vn -0.658071 -0.750481 0.060671
+vn 0.738456 0.065859 -0.671041
+vn -0.748985 -0.661794 -0.031587
+vn 0.720969 0.071505 -0.689230
+vn -0.847560 -0.510117 -0.146214
+vn -0.934355 -0.298257 -0.194861
+vn -0.388806 -0.092868 0.916593
+vn -0.783807 -0.067476 0.617298
+vn -0.417402 -0.324046 0.848964
+vn 0.045289 -0.536576 0.842616
+vn 0.101627 -0.139500 0.984985
+vn 0.268532 -0.648549 0.712180
+vn 0.342967 -0.179662 0.921964
+vn 0.327982 -0.690237 0.644917
+vn 0.425520 -0.197241 0.883145
+vn 0.333811 -0.702963 0.627979
+vn 0.428297 -0.209754 0.878933
+vn 0.353496 -0.704642 0.615192
+vn 0.454268 -0.208472 0.866115
+vn 0.400616 -0.719047 0.567858
+vn 0.518052 -0.221412 0.826167
+vn 0.455519 -0.674062 0.581439
+vn 0.540757 -0.377819 0.751518
+vn -0.056063 -0.389721 0.919187
+vn -0.071047 -0.408887 0.909787
+vn -0.799554 -0.060579 0.597491
+vn -0.499863 -0.502060 0.705710
+vn -0.068178 -0.820673 0.567278
+vn 0.131443 -0.908658 0.396252
+vn 0.175726 -0.927091 0.331004
+vn 0.185553 -0.931791 0.311930
+vn 0.202643 -0.933988 0.294198
+vn 0.229926 -0.937773 0.260079
+vn 0.249245 -0.927580 0.278268
+vn -0.605335 -0.408643 0.683035
+vn -0.580706 -0.562334 0.588610
+vn -0.185675 -0.914670 0.358959
+vn 0.015992 -0.980255 0.196997
+vn 0.065188 -0.986755 0.148350
+vn 0.079196 -0.987762 0.134129
+vn 0.098361 -0.988250 0.116916
+vn 0.122410 -0.988006 0.093783
+vn 0.182073 -0.973327 0.139500
+vn -0.653920 -0.466628 0.595477
+vn -0.661611 -0.570849 0.486160
+vn -0.310678 -0.929106 0.200537
+vn -0.097995 -0.993866 0.051149
+vn -0.037843 -0.999146 0.014679
+vn -0.021607 -0.999725 0.005341
+vn 0.000305 -0.999939 -0.008972
+vn 0.045381 -0.998901 0.010651
+vn 0.056917 0.996734 0.057009
+vn -0.761834 -0.063967 0.644581
+vn -0.760979 -0.529954 0.374187
+vn -0.492935 -0.869564 0.028474
+vn -0.268410 -0.956328 -0.115513
+vn -0.190405 -0.971160 -0.143376
+vn -0.172277 -0.974181 -0.145756
+vn -0.149602 -0.976074 -0.157720
+vn -0.080416 -0.988525 -0.127750
+vn 0.157567 0.963073 0.218177
+vn -0.774438 -0.080844 0.627430
+vn -0.895108 -0.376080 0.239387
+vn -0.758873 -0.626179 -0.178747
+vn -0.565905 -0.744591 -0.353954
+vn -0.478896 -0.784600 -0.393689
+vn -0.462539 -0.795160 -0.392071
+vn -0.446303 -0.797143 -0.406629
+vn -0.383801 -0.811182 -0.441176
+vn -0.569842 -0.402722 -0.716269
+vn -0.867214 -0.482040 -0.124546
+vn -0.972625 -0.160802 0.167699
+vn -0.913907 -0.254952 -0.315836
+vn -0.789666 -0.307993 -0.530595
+vn -0.731681 -0.333262 -0.594592
+vn -0.719108 -0.343425 -0.604053
+vn -0.711142 -0.341990 -0.614215
+vn -0.645924 -0.361064 -0.672597
+vn -0.589465 -0.359386 -0.723411
+vn -0.558916 -0.355724 -0.749016
+vn -0.984649 -0.017029 0.173559
+vn -0.950407 0.063387 -0.304422
+vn -0.841395 0.103824 -0.530290
+vn -0.806238 0.117710 -0.579699
+vn -0.796869 0.065188 -0.600574
+vn -0.815058 0.082797 -0.573412
+vn -0.747459 0.052217 -0.662221
+vn -0.662618 0.054933 -0.746910
+vn -0.946440 0.184942 0.264534
+vn -0.901151 0.405866 -0.152165
+vn -0.791284 0.484329 -0.373119
+vn -0.758507 0.497940 -0.420301
+vn -0.832942 0.412763 -0.368511
+vn -0.853053 0.386303 -0.350780
+vn -0.759667 0.372906 -0.532762
+vn -0.660024 0.386639 -0.644063
+vn -0.246498 0.354472 -0.901975
+vn -0.869778 0.307108 0.386151
+vn -0.768059 0.638356 0.050264
+vn -0.649892 0.745659 -0.146855
+vn -0.625477 0.757378 -0.187414
+vn -0.755150 0.653676 -0.049348
+vn -0.807489 0.588000 -0.046693
+vn -0.708548 0.652181 -0.269387
+vn -0.584002 0.694327 -0.420484
+vn -0.127415 0.687521 -0.714866
+vn -0.815638 0.347392 0.462600
+vn -0.668355 0.719535 0.188513
+vn -0.535874 0.844081 0.017426
+vn -0.510453 0.859676 -0.017945
+vn -0.661184 0.738060 0.134465
+vn -0.726676 0.668844 0.156682
+vn -0.613208 0.788659 -0.044343
+vn -0.469069 0.859127 -0.204505
+vn -0.005982 0.836024 -0.548601
+vn -0.769311 0.368267 0.521989
+vn -0.581652 0.755638 0.301035
+vn -0.433180 0.887845 0.155126
+vn -0.404614 0.905606 0.127018
+vn -0.570086 0.773553 0.276742
+vn -0.631184 0.706412 0.320200
+vn -0.491775 0.855464 0.162236
+vn -0.328074 0.944578 0.010163
+vn 0.136052 0.927732 -0.347453
+vn -0.751610 0.359294 0.553117
+vn -0.543962 0.760521 0.354472
+vn -0.385083 0.896725 0.218116
+vn -0.352306 0.916196 0.190802
+vn -0.517808 0.788629 0.331462
+vn -0.585437 0.710044 0.391247
+vn -0.428938 0.866573 0.255043
+vn -0.254402 0.960753 0.110477
+vn 0.239692 0.931913 -0.272164
+vn -0.726463 0.373699 0.576678
+vn -0.501083 0.765160 0.404187
+vn -0.335337 0.899319 0.280557
+vn -0.301431 0.917936 0.257851
+vn -0.474563 0.785058 0.398083
+vn -0.523942 0.719474 0.455824
+vn -0.352763 0.870785 0.342387
+vn -0.173925 0.962493 0.208197
+vn -0.035707 0.993988 0.103244
+vn -0.678487 0.364696 0.637684
+vn -0.410749 0.749992 0.518418
+vn -0.224158 0.880764 0.417066
+vn -0.182409 0.898740 0.398663
+vn -0.354961 0.774987 0.522843
+vn -0.385052 0.709708 0.589892
+vn -0.180914 0.837123 0.516190
+vn -0.016968 0.917447 0.397443
+vn -0.462996 0.684591 0.562975
+vn -0.612934 0.339244 0.713553
+vn -0.294046 0.693960 0.657216
+vn -0.084872 0.811884 0.577593
+vn -0.034639 0.826319 0.562120
+vn -0.180486 0.720573 0.669454
+vn -0.199011 0.655660 0.728324
+vn 0.022217 0.734397 0.678335
+vn 0.145665 0.812983 0.563738
+vn -0.714560 0.223823 0.662770
+vn -0.504288 0.240455 0.829341
+vn -0.113254 0.500290 0.858394
+vn 0.119724 0.592853 0.796319
+vn 0.180761 0.608966 0.772301
+vn 0.118778 0.528459 0.840571
+vn 0.082675 0.500137 0.861965
+vn 0.284463 0.498032 0.819147
+vn 0.422620 0.516312 0.744835
+vn 0.035096 0.318186 0.947356
+vn -0.410077 0.052370 0.910520
+vn 0.049562 0.183081 0.981842
+vn 0.291269 0.239875 0.926054
+vn 0.358043 0.252937 0.898770
+vn 0.362987 0.210150 0.907773
+vn 0.343822 0.221992 0.912381
+vn 0.442213 0.193243 0.875820
+vn 0.550554 0.199133 0.810663
+vn -0.318186 0.933988 0.162420
+vn -0.200354 0.971496 0.126591
+vn -0.223548 0.970855 0.086062
+vn -0.066347 0.990966 0.116398
+vn -0.139744 0.989990 0.019105
+vn -0.061495 0.991150 0.117496
+vn -0.134465 0.990814 0.013977
+vn -0.061037 0.991882 0.111362
+vn -0.123630 0.992309 0.003357
+vn -0.057009 0.994201 0.090915
+vn -0.100101 0.994812 -0.016938
+vn -0.012268 0.998962 0.043367
+vn -0.037477 0.996979 -0.067965
+vn 0.029817 0.999237 -0.024323
+vn 0.018677 0.993408 -0.112918
+vn 0.065371 0.994415 -0.082797
+vn 0.053926 0.989196 -0.136113
+vn 0.033235 0.996521 -0.076144
+vn 0.028626 0.992889 -0.115299
+vn -0.016511 0.998688 -0.048128
+vn -0.013733 0.996429 -0.083041
+vn 0.529496 0.712210 -0.460829
+vn 0.523026 0.708579 -0.473617
+vn 0.789056 -0.011536 -0.614185
+vn -0.302988 0.938353 0.166265
+vn -0.265389 0.938994 0.218726
+vn 0.008972 0.974334 0.224860
+vn 0.017914 0.971709 0.235389
+vn 0.009888 0.967406 0.252937
+vn -0.025452 0.968108 0.249153
+vn -0.026399 0.977172 0.210669
+vn -0.000336 0.993500 0.113742
+vn 0.030061 0.999298 0.021210
+vn 0.023957 0.999512 -0.018830
+vn -0.017853 0.999817 0.001221
+vn 0.536332 0.721305 -0.438215
+vn -0.298685 0.932981 0.200690
+vn -0.114658 -0.798791 -0.590564
+vn -0.370708 -0.526780 -0.764885
+vn -0.291025 -0.616688 -0.731407
+vn -0.143254 -0.625172 -0.767205
+vn 0.027223 -0.640553 -0.767418
+vn 0.111332 -0.662038 -0.741111
+vn 0.135563 -0.740410 -0.658315
+vn 0.095309 -0.862575 -0.496811
+vn 0.031892 -0.950316 -0.309610
+vn -0.117801 -0.895688 -0.428724
+vn 0.703726 0.085910 -0.705222
+vn 0.140812 -0.988342 -0.057833
+vn -0.176916 0.112217 -0.977783
+vn -0.463820 -0.000244 -0.885922
+vn -0.396466 0.495773 -0.772637
+vn -0.251259 0.629933 -0.734855
+vn -0.082247 0.613941 -0.785028
+vn 0.062685 0.432813 -0.899289
+vn 0.114200 0.360729 -0.925626
+vn 0.067263 0.509629 -0.857723
+vn -0.147404 0.666036 -0.731162
+vn -0.333659 0.761101 -0.556200
+vn -0.153478 0.913327 -0.377148
+vn -0.370403 -0.745903 -0.553514
+vn -0.117374 0.990661 0.069308
+vn -0.045228 -0.997101 0.061098
+vn -0.457808 -0.775109 -0.435408
+vn -0.716849 -0.364360 -0.594409
+vn -0.842647 0.257424 -0.472915
+vn -0.742363 -0.635609 -0.211829
+vn -0.978973 -0.126621 -0.159734
+vn -0.920560 0.370373 -0.124027
+vn -0.685263 -0.714591 -0.140477
+vn -0.822535 0.452986 -0.343791
+vn -0.424726 0.841395 -0.334086
+vn 0.104862 -0.938810 0.328043
+vn -0.146855 0.961974 -0.230232
+vn -0.592578 0.640126 -0.488907
+vn -0.615009 0.631336 -0.472365
+vn -0.672750 0.632954 -0.383038
+vn -0.732994 0.635304 -0.243019
+vn -0.733726 0.657796 -0.169988
+vn -0.650349 0.747856 -0.133000
+vn -0.509659 0.849849 -0.133976
+vn -0.374187 0.917051 -0.137638
+vn -0.261879 0.943449 -0.203101
+vn -0.750603 0.280526 0.598193
+vn -0.264351 0.961669 0.072787
+vn -0.292032 0.955870 -0.031159
+vn -0.329691 0.920103 -0.211402
+vn -0.339793 0.915555 -0.215125
+vn -0.354900 0.915159 -0.190985
+vn -0.364147 0.919675 -0.146916
+vn -0.327921 0.934049 -0.141392
+vn -0.236305 0.962859 -0.130497
+vn -0.132908 0.981079 -0.140751
+vn -0.095737 0.987732 -0.123142
+vn -0.095767 0.990448 -0.099002
+vn 0.488937 0.717917 -0.495499
+vn -0.306192 0.943205 0.128666
+vn -0.249184 0.966887 0.054659
+vn -0.214362 0.973754 -0.076144
+vn -0.213904 0.973327 -0.082614
+vn -0.208258 0.974456 -0.083956
+vn -0.190619 0.978057 -0.083712
+vn -0.134281 0.983673 -0.119694
+vn -0.065127 0.987915 -0.140446
+vn -0.005676 0.987548 -0.157170
+vn -0.010773 0.991577 -0.128971
+vn -0.047334 0.995025 -0.087466
+vn 0.511277 0.710990 -0.482742
+vn -0.317698 0.934355 0.161168
+vn 0.115726 -0.988556 0.096622
+vn 0.137333 -0.978088 0.156377
+vn 0.149754 -0.969787 0.192450
+vn 0.113804 -0.975402 0.188665
+vn 0.085177 -0.975860 0.201056
+vn 0.090030 -0.983673 0.155797
+vn 0.118778 -0.991150 0.058962
+vn 0.183233 -0.982940 -0.013886
+vn 0.138249 -0.988586 0.059420
+vn 0.134892 -0.989990 0.041383
+vn 0.729453 -0.456862 -0.509049
+vn -0.047090 -0.987243 0.151891
+vn 0.017121 -0.989441 0.143895
+vn 0.119480 -0.983428 0.136174
+vn 0.126865 -0.983886 0.125919
+vn 0.118809 -0.985748 0.119053
+vn 0.100009 -0.988037 0.117252
+vn 0.092471 -0.989349 0.112217
+vn 0.103183 -0.991180 0.082858
+vn 0.137211 -0.989776 0.038453
+vn 0.148564 -0.988800 0.012665
+vn 0.133518 -0.990966 0.011872
+vn 0.619343 -0.669668 -0.409772
+vn -0.082308 -0.983306 0.162206
+vn -0.013428 -0.993072 0.116581
+vn 0.076205 -0.994476 0.071780
+vn 0.089877 -0.994110 0.060427
+vn 0.092868 -0.994140 0.054903
+vn 0.089145 -0.994568 0.053194
+vn 0.092318 -0.994537 0.048616
+vn 0.110874 -0.993408 0.028413
+vn 0.147404 -0.989044 -0.005463
+vn 0.164617 -0.986053 -0.024293
+vn 0.148503 -0.988769 -0.015656
+vn 0.615223 -0.670400 -0.414716
+vn -0.095187 -0.981842 0.163976
+vn -0.030366 -0.994903 0.096072
+vn 0.037080 -0.999023 0.022736
+vn 0.052431 -0.998535 0.012757
+vn 0.059267 -0.998169 0.011414
+vn 0.060518 -0.998016 0.015534
+vn 0.066469 -0.997681 0.014222
+vn 0.091372 -0.995788 0.001312
+vn 0.133305 -0.990692 -0.027131
+vn 0.154118 -0.987274 -0.039003
+vn 0.143284 -0.989319 -0.026276
+vn 0.612751 -0.670492 -0.418226
+vn -0.094028 -0.983001 0.157659
+vn -0.057375 -0.996307 0.063570
+vn -0.016144 -0.999237 -0.034730
+vn -0.002991 -0.999207 -0.039277
+vn 0.003479 -0.999542 -0.030030
+vn 0.002014 -0.999908 -0.012177
+vn 0.004822 -0.999969 -0.002655
+vn 0.037202 -0.999268 -0.006897
+vn 0.092563 -0.995239 -0.029878
+vn 0.117161 -0.992523 -0.033509
+vn 0.112613 -0.993439 -0.018281
+vn 0.609699 -0.669759 -0.423841
+vn -0.078555 -0.987610 0.135685
+vn -0.137303 -0.990387 -0.015900
+vn -0.142460 -0.977447 -0.155675
+vn -0.135044 -0.979888 -0.146733
+vn -0.135533 -0.984924 -0.107364
+vn -0.147191 -0.987518 -0.055879
+vn -0.153020 -0.987945 -0.022248
+vn -0.097934 -0.995117 -0.011658
+vn -0.015992 -0.999634 -0.021455
+vn 0.047945 -0.998474 -0.026307
+vn 0.066897 -0.997559 -0.018983
+vn 0.600665 -0.671346 -0.434065
+vn -0.027772 -0.995697 0.088137
+vn 0.090670 -0.995666 0.019135
+vn 0.090579 -0.995697 0.019044
+vn 0.188879 -0.970733 0.148198
+vn 0.334513 -0.875454 0.348735
+vn 0.092196 -0.995514 0.021088
+vn 0.092257 -0.995483 0.021180
+vn -0.010163 -0.994171 -0.107089
+vn -0.175970 -0.935575 -0.306040
+vn -0.539750 0.570238 -0.619221
+vn 0.411664 0.682363 0.604022
+vn -0.004852 -0.012940 0.999878
+vn 0.696249 0.084201 0.712821
+vn -0.004883 -0.012940 0.999878
+vn 0.990326 0.131718 0.043519
+vn 0.050478 0.019013 -0.998535
+vn -0.983367 -0.129765 -0.126957
+vn -0.108615 -0.026734 0.993713
+vn 0.991089 0.132176 0.014832
+vn 0.062136 0.020569 -0.997833
+vn -0.673421 -0.080905 -0.734764
+vn -0.673421 -0.080905 -0.734794
+vn -0.689810 -0.083254 -0.719169
+vn -0.689810 -0.083285 -0.719169
+vn -0.981964 -0.129429 -0.137730
+vn -0.979644 -0.153935 -0.128636
+vn 0.215613 0.068148 -0.974090
+vn 0.972839 0.151250 0.175115
+vn 0.549547 0.060335 0.833247
+vn 0.530747 0.056856 0.845607
+vn -0.095462 -0.049409 0.994201
+vn -0.961516 -0.147496 -0.231666
+vn -0.523148 -0.055483 -0.850398
+vn 0.187872 0.063875 -0.980102
+vn 0.970794 0.150517 0.186682
+vn -0.106113 -0.051088 0.993011
+vn -0.567309 0.638081 -0.520554
+vn -0.672536 0.613269 -0.414167
+vn -0.600604 0.726829 -0.333079
+vn -0.865627 0.485733 -0.121220
+vn -0.821436 0.567125 -0.059572
+vn -0.932096 0.276009 0.234443
+vn -0.919126 0.294443 0.261635
+vn -0.874447 0.076205 0.479049
+vn -0.883908 0.053224 0.464583
+vn -0.603076 0.791742 0.096896
+vn -0.825739 -0.022523 0.563585
+vn -0.512680 0.740440 -0.434584
+vn -0.416974 0.906156 -0.070406
+vn -0.663503 0.732475 0.152409
+vn -0.827845 0.397473 0.395764
+vn -0.848079 0.094943 0.521256
+vn -0.826167 -0.037660 0.562120
+vn -0.333415 0.927396 -0.169500
+vn -0.134648 0.947172 0.290994
+vn -0.400525 0.795190 0.455214
+vn -0.672384 0.449507 0.588031
+vn -0.789911 0.101962 0.604633
+vn -0.920225 -0.098300 0.378765
+vn -0.052156 0.977294 0.205359
+vn 0.416059 0.251076 0.873959
+vn 0.175665 0.154698 0.972198
+vn -0.109897 -0.090121 0.989837
+vn -0.221870 -0.384716 0.895932
+vn 0.825556 -0.543962 0.150029
+vn 0.010590 -0.999908 0.005737
+vn 0.058046 -0.998260 -0.008972
+vn 0.495163 0.283273 0.821284
+vn -0.225318 -0.960570 0.162877
+vn -0.348491 -0.769860 0.534623
+vn -0.290658 -0.581988 0.759453
+vn 0.808496 -0.539659 0.234657
+vn -0.564837 -0.555742 -0.609973
+vn -0.793847 -0.511612 -0.328593
+vn -0.928495 -0.333506 0.163152
+vn -0.862911 -0.147649 0.483230
+vn 0.801141 0.079134 -0.593188
+vn -0.487472 -0.583026 -0.649922
+vn -0.719382 -0.151708 -0.677816
+vn -0.918424 -0.140019 -0.369945
+vn -0.987701 -0.086550 0.129978
+vn -0.884121 -0.044404 0.465102
+vn 0.804407 0.067415 -0.590197
+vn -0.601794 -0.283303 -0.746666
+vn -0.756035 0.248848 -0.605335
+vn -0.935301 0.212775 -0.282632
+vn -0.976867 0.133824 0.166753
+vn -0.877590 0.049898 0.476791
+vn 0.810999 0.054262 -0.582507
+vn -0.590533 0.500595 -0.632954
+vn -0.717277 0.486373 -0.498917
+vn -0.895657 0.404645 -0.184393
+vn -0.943510 0.251595 0.215583
+vn -0.863338 0.100070 0.494583
+vn 0.816828 0.033052 -0.575884
+vn -0.622181 0.495407 -0.606128
+vn 0.305063 0.740837 0.598376
+vn 0.108097 0.810297 0.575915
+vn 0.178533 0.713523 0.677450
+vn -0.196326 0.640767 0.742180
+vn -0.140599 0.571123 0.808710
+vn -0.506272 0.343059 0.791162
+vn -0.481643 0.329081 0.812220
+vn -0.671957 0.078188 0.736412
+vn -0.685629 0.098453 0.721244
+vn -0.746391 -0.013153 0.665365
+vn -0.312754 0.825800 0.469253
+vn 0.226600 0.827509 0.513627
+vn -0.115696 0.941649 0.316019
+vn -0.376385 0.766289 0.520676
+vn -0.622211 0.421674 0.659536
+vn -0.721458 0.109836 0.683645
+vn -0.743767 -0.027955 0.667837
+vn -0.001251 0.966521 0.256508
+vn -0.398022 0.916166 -0.046754
+vn -0.608875 0.770653 0.187964
+vn -0.773766 0.437574 0.458022
+vn -0.788141 0.102176 0.606922
+vn -0.584918 -0.058779 0.808954
+vn -0.297617 0.948363 -0.109470
+vn -0.762291 0.112308 -0.637410
+vn -0.908078 0.027039 -0.417859
+vn -0.974181 -0.191931 -0.118686
+vn -0.885250 -0.462874 0.045076
+vn 0.108097 -0.628437 -0.770287
+vn 0.111484 -0.991974 0.059542
+vn 0.085665 -0.991058 0.102023
+vn -0.694693 0.143132 -0.704886
+vn -0.127873 -0.949065 0.287851
+vn -0.533952 -0.791681 0.296762
+vn -0.753136 -0.636464 0.166173
+vn -0.002838 -0.644154 -0.764885
+vn 0.497726 -0.430586 0.752861
+vn 0.165593 -0.398602 0.902036
+vn -0.357982 -0.266305 0.894925
+vn -0.666982 -0.124607 0.734550
+vn 0.763817 0.074709 -0.641072
+vn 0.557848 -0.459914 0.690817
+vn 0.489090 -0.009369 0.872158
+vn 0.141697 -0.015137 0.989776
+vn -0.362529 -0.012940 0.931852
+vn -0.663869 -0.018494 0.747581
+vn 0.762780 0.062502 -0.643605
+vn 0.596301 -0.142186 0.790033
+vn 0.374584 0.382000 0.844813
+vn 0.022004 0.325541 0.945250
+vn -0.414838 0.200018 0.887631
+vn -0.681906 0.072970 0.727775
+vn 0.758141 0.048006 -0.650288
+vn 0.419935 0.619617 0.663076
+vn 0.260475 0.601550 0.755150
+vn -0.079928 0.500717 0.861873
+vn -0.464095 0.308054 0.830470
+vn -0.699973 0.119327 0.704093
+vn 0.755120 0.025727 -0.655049
+vn 0.386670 0.614246 0.687857
+vn -0.797174 -0.603107 -0.026521
+vn -0.244301 -0.755242 -0.608173
+vn 0.250954 -0.962004 -0.107303
+vn -0.112033 -0.515458 0.849513
+vn 0.600024 -0.644642 0.473647
+vn 0.108371 -0.565691 -0.817438
+vn 0.871578 -0.463698 0.159001
+vn 0.153172 -0.253609 -0.955077
+vn 0.984649 -0.136814 0.108341
+vn 0.192694 -0.028748 -0.980804
+vn 0.995270 0.083193 0.049776
+vn 0.059358 0.501267 -0.863247
+vn 0.796503 0.598743 0.084140
+vn -0.371776 0.692984 -0.617664
+vn 0.433210 0.798486 0.417951
+vn -0.580859 0.677633 -0.450972
+vn 0.224006 0.783013 0.580218
+vn -0.743370 0.583178 -0.327494
+vn 0.075320 0.691855 0.718070
+vn -0.787133 0.522355 -0.327952
+vn 0.071963 0.637623 0.766930
+vn -0.818659 0.513230 -0.257607
+vn -0.003662 0.622639 0.782464
+vn -0.911527 0.376415 -0.165441
+vn -0.102359 0.487228 0.867244
+vn -0.980560 0.178625 -0.081088
+vn -0.182897 0.289376 0.939573
+vn -0.523942 -0.729637 0.439375
+vn -0.776849 -0.229896 0.586200
+vn -0.699820 -0.676443 0.229469
+vn -0.117740 -0.979553 0.162999
+vn -0.408979 -0.893521 -0.185186
+vn 0.164525 -0.985473 -0.041627
+vn -0.152776 -0.891842 -0.425672
+vn 0.689962 -0.561754 -0.456435
+vn 0.454360 -0.492691 -0.742149
+vn 0.777703 0.233924 -0.583453
+vn -0.864254 -0.495743 0.085330
+vn -0.681845 -0.595752 -0.424390
+vn -0.450972 -0.565142 -0.690786
+vn 0.231727 -0.250313 -0.940001
+vn -0.972015 -0.229987 0.047243
+vn -0.859523 -0.156987 -0.486312
+vn -0.646535 -0.082919 -0.758324
+vn 0.085025 0.109104 -0.990356
+vn -0.990478 0.048311 0.128666
+vn -0.887539 0.300302 -0.349345
+vn -0.679220 0.417493 -0.603565
+vn 0.060488 0.482131 -0.873989
+vn -0.915403 0.260872 0.306497
+vn -0.761162 0.646199 -0.054811
+vn -0.543809 0.792535 -0.275918
+vn 0.161473 0.760338 -0.629109
+vn -0.769860 0.352367 0.532090
+vn -0.521287 0.792932 0.315378
+vn -0.284402 0.949736 0.130772
+vn 0.353923 0.876370 -0.326548
+vn -0.592029 0.299966 0.747978
+vn -0.230628 0.706900 0.668630
+vn 0.032563 0.856044 0.515854
+vn 0.588946 0.807154 -0.040101
+vn -0.427168 0.113987 0.896939
+vn 0.038881 0.404797 0.913541
+vn 0.329691 0.528428 0.782311
+vn 0.810205 0.564165 0.158971
+vn 0.777703 0.233955 -0.583422
+vn -0.320933 -0.157964 0.933805
+vn 0.213782 -0.038301 0.976104
+vn 0.524552 0.045381 0.850154
+vn 0.956145 0.204260 0.209815
+vn -0.304483 -0.437086 0.846278
+vn 0.242317 -0.495590 0.834040
+vn 0.557573 -0.454939 0.694327
+vn 0.981292 -0.168584 0.092654
+vn -0.379955 -0.644246 0.663717
+vn 0.119327 -0.837092 0.533830
+vn 0.423261 -0.829066 0.365337
+vn 0.881680 -0.446150 -0.153417
+vn -0.488205 -0.725425 0.485153
+vn -0.733879 -0.224799 0.640980
+vn -0.678182 -0.676565 0.286843
+vn -0.099948 -0.977447 0.185858
+vn -0.414655 -0.898648 -0.143132
+vn 0.167852 -0.985076 -0.037324
+vn -0.175298 -0.899380 -0.400403
+vn 0.661214 -0.565142 -0.493301
+vn 0.406384 -0.501968 -0.763451
+vn 0.734886 0.228889 -0.638356
+vn -0.853175 -0.499100 0.151402
+vn -0.705008 -0.606250 -0.367931
+vn -0.492904 -0.578600 -0.649770
+vn 0.169286 -0.264016 -0.949522
+vn -0.965056 -0.234626 0.116550
+vn -0.889401 -0.169530 -0.424482
+vn -0.695791 -0.098636 -0.711417
+vn 0.017121 0.093722 -0.995422
+vn -0.979827 0.044832 0.194708
+vn -0.911100 0.289743 -0.293100
+vn -0.721244 0.404004 -0.562609
+vn -0.001923 0.468429 -0.883480
+vn 0.734916 0.228889 -0.638356
+vn -0.894284 0.260659 0.363689
+vn -0.767388 0.641011 -0.013215
+vn -0.566454 0.784967 -0.250771
+vn 0.113529 0.751061 -0.650380
+vn -0.734428 0.356548 0.577441
+vn -0.504013 0.794977 0.337565
+vn -0.281198 0.950102 0.134922
+vn 0.325205 0.873012 -0.363384
+vn -0.542070 0.308603 0.781579
+vn -0.189550 0.716208 0.671590
+vn 0.061708 0.864345 0.499069
+vn 0.579547 0.809656 -0.092471
+vn 0.734886 0.228858 -0.638356
+vn -0.366283 0.125919 0.921934
+vn 0.097873 0.419568 0.902402
+vn 0.378338 0.542711 0.749840
+vn 0.815332 0.571123 0.094913
+vn -0.255989 -0.144780 0.955748
+vn 0.279550 -0.021516 0.959868
+vn 0.580523 0.061892 0.811853
+vn 0.966765 0.212897 0.141392
+vn -0.243629 -0.425153 0.871670
+vn 0.301462 -0.480850 0.823328
+vn 0.606250 -0.440687 0.661977
+vn 0.986419 -0.161626 0.028596
+vn -0.329936 -0.635670 0.697867
+vn 0.160710 -0.827784 0.537492
+vn 0.452467 -0.820734 0.348704
+vn 0.872219 -0.443617 -0.205817
+vn -0.279611 -0.239448 0.929746
+vn -0.728965 -0.331675 0.598804
+vn -0.241798 -0.437696 0.865963
+vn 0.251595 -0.073305 0.965026
+vn 0.314890 -0.405164 0.858272
+vn 0.554064 0.045930 0.831172
+vn 0.623554 -0.318369 0.713981
+vn 0.945769 0.279214 0.165960
+vn 0.996704 0.012146 0.080050
+vn 0.728965 0.331675 -0.598804
+vn 0.728965 0.331675 -0.598773
+vn -0.260384 -0.611774 0.746940
+vn 0.283761 -0.696493 0.659017
+vn 0.589404 -0.638203 0.495224
+vn 0.971679 -0.222236 -0.080233
+vn -0.332560 -0.735099 0.590747
+vn 0.162969 -0.902921 0.397626
+vn 0.456801 -0.864834 0.208258
+vn 0.874447 -0.388348 -0.290597
+vn -0.447310 -0.788965 0.421186
+vn -0.029084 -0.993042 0.113865
+vn 0.245918 -0.963744 -0.103214
+vn 0.719901 -0.460891 -0.518937
+vn -0.587207 -0.765099 0.264107
+vn -0.263222 -0.953124 -0.149022
+vn -0.011078 -0.919919 -0.391858
+vn 0.531480 -0.428755 -0.730522
+vn 0.728965 0.331706 -0.598804
+vn -0.730918 -0.667196 0.143406
+vn -0.503769 -0.789239 -0.351085
+vn -0.275155 -0.740013 -0.613666
+vn 0.337901 -0.296884 -0.893094
+vn -0.856594 -0.510117 0.077425
+vn -0.714072 -0.526353 -0.461501
+vn -0.506058 -0.451430 -0.734886
+vn 0.168676 -0.085330 -0.981964
+vn -0.945067 -0.317820 0.076235
+vn -0.862178 -0.204505 -0.463485
+vn -0.668630 -0.098056 -0.737083
+vn 0.049501 0.173650 -0.983551
+vn -0.982879 -0.119541 0.140019
+vn -0.925474 0.127354 -0.356700
+vn -0.738121 0.266243 -0.619861
+vn -0.001404 0.440687 -0.897641
+vn -0.964293 0.054506 0.259072
+vn -0.894345 0.418653 -0.157476
+vn -0.703970 0.586077 -0.401135
+vn 0.023591 0.675130 -0.737297
+vn 0.728935 0.331706 -0.598804
+vn -0.892117 0.177862 0.415265
+vn -0.773553 0.625111 0.103916
+vn -0.571368 0.812708 -0.114170
+vn 0.120792 0.841243 -0.526933
+vn -0.777367 0.231697 0.584796
+vn -0.581469 0.715232 0.387677
+vn -0.360485 0.911618 0.197333
+vn 0.275369 0.913755 -0.298593
+vn -0.637471 0.207831 0.741874
+vn -0.347331 0.675314 0.650594
+vn -0.103458 0.867794 0.485977
+vn 0.463790 0.881649 -0.087039
+vn -0.493759 0.109928 0.862606
+vn -0.106815 0.511429 0.852626
+vn 0.160588 0.687887 0.707785
+vn 0.657338 0.749779 0.075533
+vn -0.368084 -0.047121 0.928587
+vn 0.103488 0.248543 0.963042
+vn 0.391491 0.399304 0.829005
+vn 0.826594 0.538224 0.164373
+vn 0.869930 0.207892 -0.447157
+vn 0.764183 0.197546 -0.613941
+vn 0.834101 0.302194 -0.461409
+vn 0.965789 0.207770 -0.155034
+vn 0.877438 0.440321 -0.190191
+vn 0.906308 0.209906 -0.366710
+vn 0.854885 0.345256 -0.387158
+vn 0.889004 0.209113 -0.407300
+vn 0.845332 0.324046 -0.424696
+vn 0.972869 0.189856 0.131993
+vn 0.842891 0.532029 0.080264
+vn 0.805780 0.125584 0.578692
+vn 0.631306 0.584796 0.509323
+vn 0.701254 0.095614 0.706442
+vn 0.520554 0.571215 0.634571
+vn 0.775536 0.367412 -0.513291
+vn 0.733024 0.601154 -0.318217
+vn 0.770806 0.438887 -0.461684
+vn 0.773949 0.403546 -0.487960
+vn 0.630360 0.768731 -0.108066
+vn 0.346110 0.902432 0.256508
+vn 0.225166 0.900174 0.372753
+vn 0.709952 0.386090 -0.588977
+vn 0.571215 0.647175 -0.504776
+vn 0.676626 0.465712 -0.570299
+vn 0.693960 0.426313 -0.580187
+vn 0.392254 0.836482 -0.382641
+vn 0.026582 0.993347 -0.111942
+vn -0.105747 0.994324 -0.008850
+vn 0.654866 0.353191 -0.668111
+vn 0.435377 0.566088 -0.699942
+vn 0.597552 0.418500 -0.683920
+vn 0.626820 0.386212 -0.676656
+vn 0.192389 0.717124 -0.669820
+vn -0.241646 0.833186 -0.497330
+vn -0.383557 0.828486 -0.408002
+vn 0.625080 0.277566 -0.729514
+vn 0.361919 0.379589 -0.851405
+vn 0.554796 0.309915 -0.772057
+vn 0.590503 0.294046 -0.751518
+vn 0.084262 0.442701 -0.892666
+vn -0.386700 0.464888 -0.796411
+vn -0.533799 0.447035 -0.717765
+vn 0.628559 0.179479 -0.756737
+vn 0.370525 0.137669 -0.918546
+vn 0.559801 0.169073 -0.811151
+vn 0.594775 0.174444 -0.784722
+vn 0.096927 0.086673 -0.991485
+vn -0.369732 -0.012848 -0.929014
+vn -0.516221 -0.047761 -0.855098
+vn 0.664388 0.085177 -0.742485
+vn 0.458876 -0.094852 -0.883389
+vn 0.611225 0.033723 -0.790704
+vn 0.638417 0.059511 -0.767357
+vn 0.226936 -0.255470 -0.939787
+vn -0.195257 -0.472060 -0.859645
+vn -0.335521 -0.523392 -0.783227
+vn 0.722953 0.019959 -0.690573
+vn 0.603290 -0.255684 -0.755394
+vn 0.695303 -0.059877 -0.716178
+vn 0.709830 -0.019990 -0.704062
+vn 0.439467 -0.492141 -0.751396
+vn 0.089908 -0.789697 -0.606830
+vn -0.040132 -0.852351 -0.521409
+vn 0.788568 0.001282 -0.614917
+vn 0.765099 -0.301706 -0.568804
+vn 0.789483 -0.086673 -0.607562
+vn 0.789788 -0.042726 -0.611835
+vn 0.677572 -0.559893 -0.476852
+vn 0.409467 -0.880612 -0.238350
+vn 0.290780 -0.946501 -0.139775
+vn 0.843623 0.034150 -0.535783
+vn 0.900937 -0.220618 -0.373638
+vn 0.868557 -0.039460 -0.493973
+vn 0.856929 -0.002655 -0.515366
+vn 0.877438 -0.440565 -0.189642
+vn 0.677694 -0.720481 0.147008
+vn 0.568590 -0.780633 0.259346
+vn 0.873409 0.109806 -0.474380
+vn 0.974395 -0.034120 -0.222205
+vn 0.911313 0.069063 -0.405805
+vn 0.893246 0.089511 -0.440504
+vn 0.985534 -0.166112 0.033174
+vn 0.822748 -0.352153 0.446089
+vn 0.718833 -0.399182 0.569109
+vn 0.862636 0.241218 -0.444533
+vn 0.756767 0.231544 -0.611286
+vn 0.824213 0.334788 -0.456679
+vn 0.959288 0.237495 -0.152715
+vn 0.864498 0.468215 -0.182653
+vn 0.899197 0.242531 -0.364147
+vn 0.844020 0.376812 -0.381573
+vn 0.881771 0.242103 -0.404706
+vn 0.834925 0.356151 -0.419507
+vn 0.967681 0.213660 0.133854
+vn 0.828181 0.553178 0.089785
+vn 0.803674 0.135289 0.579455
+vn 0.616474 0.590899 0.520310
+vn 0.700339 0.099704 0.706778
+vn 0.506485 0.571612 0.645497
+vn 0.763726 0.399457 -0.507035
+vn 0.715354 0.627735 -0.306864
+vn 0.757195 0.469680 -0.453902
+vn 0.761193 0.435011 -0.480911
+vn 0.608722 0.787896 -0.092959
+vn 0.321940 0.905911 0.275033
+vn 0.201453 0.897855 0.391461
+vn 0.697409 0.417951 -0.582141
+vn 0.551836 0.673269 -0.492080
+vn 0.662008 0.496200 -0.561693
+vn 0.680380 0.457503 -0.572466
+vn 0.368084 0.854915 -0.365490
+vn -0.000977 0.995849 -0.090670
+vn -0.133000 0.991028 0.012665
+vn 0.643055 0.385266 -0.661824
+vn 0.417737 0.592669 -0.688620
+vn 0.583941 0.449263 -0.676107
+vn 0.614093 0.417676 -0.669637
+vn 0.170751 0.736290 -0.654714
+vn -0.265786 0.836665 -0.478835
+vn -0.407270 0.826136 -0.389294
+vn 0.615162 0.310160 -0.724784
+vn 0.348979 0.407514 -0.843867
+vn 0.543901 0.341472 -0.766472
+vn 0.580096 0.326151 -0.746361
+vn 0.069582 0.463820 -0.883175
+vn -0.401532 0.470992 -0.785394
+vn -0.547868 0.447432 -0.706809
+vn 0.621265 0.212806 -0.754112
+vn 0.364025 0.167394 -0.916196
+vn 0.552660 0.201697 -0.808588
+vn 0.587542 0.207465 -0.782128
+vn 0.091739 0.110477 -0.989624
+vn -0.371838 -0.003143 -0.928251
+vn -0.517106 -0.043641 -0.854762
+vn 0.659688 0.119236 -0.741966
+vn 0.458815 -0.063295 -0.886258
+vn 0.607837 0.067385 -0.791162
+vn 0.634388 0.093417 -0.767327
+vn 0.231208 -0.229011 -0.945555
+vn -0.184667 -0.458785 -0.869106
+vn -0.323252 -0.515549 -0.793512
+vn 0.720176 0.054567 -0.691610
+vn 0.607959 -0.222816 -0.762047
+vn 0.694662 -0.025422 -0.718863
+vn 0.708121 0.014557 -0.705924
+vn 0.450667 -0.463729 -0.762749
+vn 0.109836 -0.773766 -0.623829
+vn -0.018220 -0.841792 -0.539476
+vn 0.786493 0.036073 -0.616504
+vn 0.771477 -0.268349 -0.576830
+vn 0.789850 -0.051943 -0.611042
+vn 0.788934 -0.007935 -0.614368
+vn 0.691305 -0.530747 -0.490249
+vn 0.432783 -0.863735 -0.258095
+vn 0.316233 -0.934965 -0.160680
+vn 0.840877 0.068789 -0.536790
+vn 0.905576 -0.187750 -0.380291
+vn 0.867916 -0.005036 -0.496628
+vn 0.855251 0.031892 -0.517228
+vn 0.888638 -0.412152 -0.200995
+vn 0.697592 -0.704550 0.130039
+vn 0.590503 -0.770074 0.241279
+vn 0.868740 0.143864 -0.473861
+vn 0.974334 -0.002594 -0.225043
+vn 0.907956 0.102756 -0.406262
+vn 0.889218 0.123417 -0.440474
+vn 0.989807 -0.139653 0.027406
+vn 0.833369 -0.338878 0.436598
+vn 0.731101 -0.391369 0.558794
+vn 0.862636 0.241218 -0.444502
+vn 0.899197 0.242500 -0.364147
+vn 0.506485 0.571612 0.645527
+vn 0.763726 0.399487 -0.507035
+vn 0.757195 0.469680 -0.453871
+vn 0.697439 0.417951 -0.582141
+vn -0.133000 0.991028 0.012696
+vn 0.583911 0.449263 -0.676107
+vn 0.348979 0.407483 -0.843867
+vn 0.543931 0.341472 -0.766472
+vn -0.401532 0.470992 -0.785424
+vn 0.091708 0.110477 -0.989624
+vn 0.659719 0.119236 -0.741966
+vn 0.694662 -0.025452 -0.718863
+vn 0.109836 -0.773797 -0.623829
+vn 0.786493 0.036103 -0.616504
+vn 0.771477 -0.268349 -0.576861
+vn 0.840877 0.068789 -0.536821
+vn 0.855220 0.031892 -0.517228
+vn 0.907956 0.102725 -0.406262
+vn 0.731101 -0.391369 0.558824
+vn 0.841060 0.204260 -0.500870
+vn 0.784539 0.059572 -0.617145
+vn 0.798791 0.241432 -0.550981
+vn 0.865413 0.414045 -0.282052
+vn 0.760613 0.506211 -0.406415
+vn 0.723441 0.675710 0.141453
+vn 0.536637 0.839961 -0.080142
+vn 0.548235 0.746971 0.376080
+vn 0.335978 0.933622 0.124271
+vn 0.752129 0.250740 -0.609424
+vn 0.644826 0.529283 -0.551347
+vn 0.330332 0.881069 -0.338450
+vn 0.101566 0.980316 -0.169195
+vn 0.708152 0.230750 -0.667257
+vn 0.535752 0.479720 -0.694845
+vn 0.135899 0.792749 -0.594165
+vn -0.119327 0.879971 -0.459731
+vn 0.673544 0.184545 -0.715720
+vn 0.449904 0.365093 -0.814997
+vn -0.017029 0.588488 -0.808313
+vn -0.293130 0.647877 -0.703055
+vn 0.653584 0.119144 -0.747368
+vn 0.400433 0.202857 -0.893582
+vn -0.105197 0.299326 -0.948302
+vn -0.393292 0.319315 -0.862148
+vn 0.651326 0.044496 -0.757439
+vn 0.394848 0.017670 -0.918546
+vn -0.115177 -0.030641 -0.992859
+vn -0.404645 -0.055605 -0.912748
+vn 0.667104 -0.028016 -0.744407
+vn 0.433973 -0.162206 -0.886196
+vn -0.045442 -0.351268 -0.935148
+vn -0.325419 -0.419904 -0.847194
+vn 0.698508 -0.087374 -0.710196
+vn 0.511856 -0.309458 -0.801355
+vn 0.093356 -0.613666 -0.783990
+vn -0.167669 -0.718070 -0.675436
+vn 0.740776 -0.124516 -0.660085
+vn 0.616688 -0.401624 -0.677023
+vn 0.280160 -0.777917 -0.562395
+vn 0.044557 -0.904721 -0.423658
+vn 0.787439 -0.133824 -0.601642
+vn 0.732444 -0.424696 -0.532060
+vn 0.486465 -0.819056 -0.304086
+vn 0.278970 -0.951415 -0.130131
+vn 0.831416 -0.113834 -0.543809
+vn 0.841578 -0.375134 -0.388592
+vn 0.680929 -0.730735 -0.048372
+vn 0.499893 -0.851070 0.160375
+vn 0.866024 -0.067629 -0.495376
+vn 0.927396 -0.260506 -0.268410
+vn 0.833857 -0.526444 0.165746
+vn 0.673696 -0.618976 0.403699
+vn 0.885983 -0.002228 -0.463698
+vn 0.976867 -0.098239 -0.189856
+vn 0.922025 -0.237312 0.305765
+vn 0.773888 -0.290414 0.562761
+vn 0.888241 0.072390 -0.453627
+vn 0.982452 0.086886 -0.164861
+vn 0.932005 0.092654 0.350291
+vn 0.785211 0.084506 0.613392
+vn 0.872463 0.144902 -0.466659
+vn 0.943327 0.266793 -0.197241
+vn 0.862270 0.413282 0.292611
+vn 0.705985 0.448805 0.547838
+vn 0.841060 0.204291 -0.500870
+vn 0.784570 0.059572 -0.617145
+vn 0.760613 0.506211 -0.406384
+vn 0.723441 0.675680 0.141423
+vn 0.336009 0.933622 0.124271
+vn 0.535722 0.479720 -0.694845
+vn 0.135899 0.792749 -0.594134
+vn -0.016999 0.588488 -0.808313
+vn -0.293100 0.647877 -0.703055
+vn 0.400433 0.202857 -0.893551
+vn 0.651326 0.044496 -0.757469
+vn 0.394818 0.017670 -0.918577
+vn -0.045442 -0.351238 -0.935148
+vn 0.093387 -0.613666 -0.783990
+vn 0.280160 -0.777947 -0.562395
+vn 0.486496 -0.819056 -0.304056
+vn 0.279000 -0.951415 -0.130131
+vn 0.841578 -0.375134 -0.388562
+vn 0.499924 -0.851070 0.160375
+vn 0.866024 -0.067629 -0.495346
+vn 0.976867 -0.098270 -0.189856
+vn 0.922056 -0.237281 0.305765
+vn 0.982452 0.086886 -0.164830
+vn 0.932035 0.092685 0.350291
+vn 0.785241 0.084506 0.613392
+vn 0.872463 0.144932 -0.466659
+vn 0.943327 0.266793 -0.197211
+vn 0.862300 0.413282 0.292611
+vn 0.705985 0.448805 0.547807
+vn -0.602557 -0.161321 -0.781579
+vn 0.115879 -0.991913 0.051546
+vn 0.569872 -0.461409 0.679922
+vn 0.613330 -0.220466 0.758415
+vn 0.618824 -0.013581 0.785363
+vn 0.597156 0.195227 0.777978
+vn 0.547441 0.400739 0.734611
+vn 0.291025 0.843501 0.451399
+vn -0.326334 0.885983 -0.329356
+vn 0.618976 -0.017457 0.785180
+vn 0.066836 -0.997681 -0.011322
+vn -0.473830 -0.584338 -0.658773
+vn -0.560137 -0.358684 -0.746696
+vn -0.603107 -0.157506 -0.781915
+vn -0.619861 0.051881 -0.782983
+vn -0.608539 0.264595 -0.748070
+vn -0.438063 0.757622 -0.483779
+vn 0.158361 0.943083 0.292367
+vn -0.298898 -0.455184 0.838710
+vn -0.785272 -0.055574 0.616627
+vn -0.454390 -0.633290 0.626423
+vn 0.329417 -0.651906 0.682974
+vn 0.086978 -0.932493 0.350475
+vn 0.897397 -0.441145 0.003815
+vn 0.716025 -0.653005 -0.246620
+vn -0.653432 -0.658773 0.372845
+vn -0.224281 -0.973388 -0.046602
+vn 0.481399 -0.685232 -0.546495
+vn -0.843684 -0.517258 0.143559
+vn -0.521531 -0.751701 -0.403577
+vn 0.255623 -0.517869 -0.816340
+vn -0.969604 -0.244484 0.005127
+vn -0.717399 -0.325297 -0.616016
+vn 0.106540 -0.194739 -0.975036
+vn -0.996765 0.080172 -0.004120
+vn -0.758629 0.180090 -0.626087
+vn 0.074801 0.186163 -0.979644
+vn -0.922208 0.369335 0.114292
+vn -0.642232 0.628254 -0.439070
+vn 0.161504 0.522233 -0.837336
+vn -0.766472 0.551256 0.329508
+vn -0.400708 0.910184 -0.104678
+vn 0.342265 0.734306 -0.586200
+vn -0.566881 0.576373 0.588549
+vn -0.091281 0.950774 0.296030
+vn 0.575640 0.766411 -0.284951
+vn 0.784570 0.059542 -0.617145
+vn -0.376293 0.430647 0.820307
+vn 0.205054 0.727439 0.654775
+vn 0.800806 0.598712 -0.014435
+vn -0.250587 0.154088 0.955718
+vn 0.401807 0.299661 0.865291
+vn 0.950530 0.275399 0.143498
+vn -0.224036 -0.170202 0.959593
+vn 0.444899 -0.205390 0.871700
+vn 0.983520 -0.105411 0.146764
+vn -0.298898 -0.455153 0.838710
+vn -0.454360 -0.633290 0.626453
+vn 0.897427 -0.441115 0.003815
+vn 0.716056 -0.653005 -0.246651
+vn 0.478042 0.346324 -0.807154
+vn 0.590289 0.472304 -0.654561
+vn 0.786279 0.049104 -0.615894
+vn -0.653401 -0.658773 0.372845
+vn 0.732505 0.487350 -0.475265
+vn 0.866543 0.386975 -0.315104
+vn 0.106510 -0.194739 -0.975036
+vn 0.956420 0.196387 -0.216041
+vn -0.996765 0.080203 -0.004120
+vn -0.758660 0.180090 -0.626057
+vn 0.074770 0.186193 -0.979644
+vn 0.977935 -0.034547 -0.205969
+vn -0.922208 0.369366 0.114322
+vn 0.161473 0.522233 -0.837367
+vn 0.925565 -0.243690 -0.289712
+vn -0.766472 0.551256 0.329539
+vn 0.342235 0.734306 -0.586200
+vn 0.813379 -0.373730 -0.445723
+vn -0.566851 0.576373 0.588549
+vn 0.671163 -0.388348 -0.631397
+vn -0.376263 0.430647 0.820307
+vn 0.205023 0.727439 0.654775
+vn 0.800806 0.598743 -0.014435
+vn 0.537034 -0.283303 -0.794519
+vn 0.950530 0.275369 0.143498
+vn 0.447127 -0.088626 -0.890042
+vn 0.444899 -0.205390 0.871670
+vn 0.983520 -0.105411 0.146733
+vn 0.425611 0.141789 -0.893704
+s 1
+f 1204/1/1 1236/2/2 1176/3/3
+f 1132/4/4 1176/3/3 1177/5/5
+f 1131/6/6 1177/5/5 980/7/7
+f 980/7/7 448/8/8 466/9/9
+f 940/10/10 980/7/7 466/9/9
+f 466/9/9 448/8/8 175/11/11
+f 223/12/12 175/11/11 174/13/13
+f 224/14/14 174/13/13 117/15/15
+f 1204/1/1 1176/3/3 1132/4/4
+f 1132/4/4 1177/5/5 1131/6/6
+f 1131/6/6 980/7/7 940/10/10
+f 466/9/9 175/11/11 223/12/12
+f 223/12/12 174/13/13 224/14/14
+f 224/14/14 117/15/15 148/16/16
+f 1260/17/17 1208/18/18 1238/19/19
+f 1253/20/20 1109/21/21 1208/18/18
+f 1183/22/22 979/23/23 1109/21/21
+f 1013/24/24 868/25/25 979/23/23
+f 889/26/26 760/27/27 868/25/25
+f 760/27/27 580/28/28 593/29/29
+f 773/30/30 580/28/28 760/27/27
+f 580/28/28 487/31/31 593/29/29
+f 464/32/32 378/33/33 487/31/31
+f 341/34/34 247/35/35 378/33/33
+f 170/36/36 144/37/37 247/35/35
+f 102/38/38 119/39/39 144/37/37
+f 119/39/39 1260/17/17 1238/19/19
+f 95/40/40 1260/17/17 119/39/39
+f 1260/17/17 1253/20/20 1208/18/18
+f 1253/20/20 1183/22/22 1109/21/21
+f 1183/22/22 1013/24/24 979/23/23
+f 1013/24/24 889/26/26 868/25/25
+f 889/26/26 773/30/30 760/27/27
+f 580/28/28 464/32/32 487/31/31
+f 464/32/32 341/34/34 378/33/33
+f 341/34/34 170/36/36 247/35/35
+f 170/36/36 102/38/38 144/37/37
+f 102/38/38 95/40/40 119/39/39
+f 1237/41/41 1207/42/42 661/43/43
+f 1237/41/41 1253/20/20 1260/17/17
+f 697/44/44 696/45/45 1253/20/20
+f 1260/17/17 697/44/44 1253/20/20
+f 1207/42/42 1108/46/46 661/43/43
+f 1207/42/42 1183/22/22 1253/20/20
+f 696/45/45 695/47/47 1183/22/22
+f 1253/20/20 696/45/45 1183/22/22
+f 1108/46/46 978/48/48 661/43/43
+f 1108/46/46 1013/24/24 1183/22/22
+f 695/47/47 694/49/49 1013/24/24
+f 1183/22/22 695/47/47 1013/24/24
+f 978/48/48 867/50/50 661/43/43
+f 978/48/48 889/26/26 1013/24/24
+f 694/49/49 693/51/51 889/26/26
+f 1013/24/24 694/49/49 889/26/26
+f 867/50/50 759/52/52 661/43/43
+f 867/50/50 773/30/30 889/26/26
+f 693/51/51 690/53/53 773/30/30
+f 889/26/26 693/51/51 773/30/30
+f 759/52/52 592/54/54 661/43/43
+f 773/30/30 592/54/54 580/28/28
+f 759/52/52 592/54/54 773/30/30
+f 690/53/53 668/55/55 580/28/28
+f 773/30/30 580/28/28 690/53/53
+f 592/54/54 486/56/56 661/43/43
+f 592/54/54 464/32/32 580/28/28
+f 580/28/28 464/32/32 668/55/55
+f 486/56/56 377/57/57 661/43/43
+f 486/56/56 341/34/34 464/32/32
+f 464/32/32 668/55/55 341/34/34
+f 377/57/57 246/58/58 661/43/43
+f 377/57/57 170/36/36 341/34/34
+f 341/34/34 668/55/55 170/36/36
+f 246/58/58 143/59/59 661/43/43
+f 246/58/58 102/38/38 170/36/36
+f 170/36/36 668/55/55 102/38/38
+f 143/59/59 118/60/60 661/43/43
+f 143/59/59 95/40/40 102/38/38
+f 102/38/38 668/55/55 95/40/40
+f 118/60/60 117/15/15 661/43/43
+f 118/60/60 95/40/40 94/61/61
+f 95/40/40 668/55/55 94/61/61
+f 117/15/15 142/62/62 661/43/43
+f 117/15/15 94/61/61 101/63/63
+f 94/61/61 668/55/55 101/63/63
+f 142/62/62 245/64/64 661/43/43
+f 142/62/62 101/63/63 169/65/65
+f 101/63/63 668/55/55 169/65/65
+f 245/64/64 376/66/66 661/43/43
+f 245/64/64 169/65/65 340/67/67
+f 169/65/65 668/55/55 340/67/67
+f 376/66/66 485/68/68 661/43/43
+f 376/66/66 340/67/67 463/69/69
+f 340/67/67 668/55/55 463/69/69
+f 485/68/68 591/70/70 661/43/43
+f 485/68/68 463/69/69 579/71/71
+f 463/69/69 668/55/55 579/71/71
+f 591/70/70 647/72/72 661/43/43
+f 591/70/70 579/71/71 646/73/73
+f 579/71/71 668/55/55 646/73/73
+f 647/72/72 758/74/74 661/43/43
+f 647/72/72 646/73/73 772/75/75
+f 668/55/55 690/53/53 772/75/75
+f 646/73/73 668/55/55 772/75/75
+f 758/74/74 866/76/76 661/43/43
+f 758/74/74 772/75/75 888/77/77
+f 690/53/53 693/51/51 888/77/77
+f 772/75/75 690/53/53 888/77/77
+f 866/76/76 977/78/78 661/43/43
+f 866/76/76 888/77/77 1012/79/79
+f 693/51/51 694/49/49 1012/79/79
+f 888/77/77 693/51/51 1012/79/79
+f 977/78/78 1107/80/80 661/43/43
+f 977/78/78 1012/79/79 1182/81/81
+f 694/49/49 695/47/47 1182/81/81
+f 1012/79/79 694/49/49 1182/81/81
+f 1107/80/80 1206/82/82 661/43/43
+f 1107/80/80 1182/81/81 1252/83/83
+f 695/47/47 696/45/45 1252/83/83
+f 1182/81/81 695/47/47 1252/83/83
+f 1206/82/82 1236/2/2 661/43/43
+f 1206/82/82 1252/83/83 1259/84/84
+f 696/45/45 697/44/44 1259/84/84
+f 1252/83/83 696/45/45 1259/84/84
+f 1236/2/2 1237/41/41 661/43/43
+f 1236/2/2 1259/84/84 1260/17/17
+f 1259/84/84 697/44/44 1260/17/17
+f 1237/41/41 1253/20/20 1207/42/42
+f 1207/42/42 1183/22/22 1108/46/46
+f 1108/46/46 1013/24/24 978/48/48
+f 978/48/48 889/26/26 867/50/50
+f 867/50/50 773/30/30 759/52/52
+f 592/54/54 486/56/56 464/32/32
+f 486/56/56 377/57/57 341/34/34
+f 377/57/57 246/58/58 170/36/36
+f 246/58/58 143/59/59 102/38/38
+f 143/59/59 118/60/60 95/40/40
+f 118/60/60 117/15/15 94/61/61
+f 117/15/15 101/63/63 142/62/62
+f 142/62/62 169/65/65 245/64/64
+f 245/64/64 340/67/67 376/66/66
+f 376/66/66 463/69/69 485/68/68
+f 485/68/68 579/71/71 591/70/70
+f 591/70/70 646/73/73 647/72/72
+f 647/72/72 772/75/75 758/74/74
+f 758/74/74 888/77/77 866/76/76
+f 866/76/76 1012/79/79 977/78/78
+f 977/78/78 1182/81/81 1107/80/80
+f 1107/80/80 1252/83/83 1206/82/82
+f 1206/82/82 1259/84/84 1236/2/2
+f 1236/2/2 1260/17/17 1237/41/41
+f 777/85/85 659/86/86 766/87/87
+f 777/85/85 824/88/88 835/89/89
+f 835/89/89 924/90/90 945/91/91
+f 945/91/91 1030/92/92 1068/93/93
+f 1068/93/93 1067/94/94 1129/95/95
+f 1129/95/95 1117/96/96 1163/97/97
+f 1163/97/97 1155/98/98 1200/99/99
+f 1200/99/99 1160/100/100 1203/101/101
+f 1203/101/101 1161/102/102 1205/103/103
+f 1205/103/103 660/104/104 1161/102/102
+f 766/87/87 659/86/86 734/105/105
+f 766/87/87 789/106/106 824/88/88
+f 824/88/88 846/107/107 924/90/90
+f 924/90/90 895/108/108 1030/92/92
+f 1030/92/92 906/109/109 1067/94/94
+f 1067/94/94 917/110/110 1117/96/96
+f 1117/96/96 931/111/111 1155/98/98
+f 1155/98/98 937/112/112 1160/100/100
+f 1160/100/100 1161/102/102 938/113/113
+f 1161/102/102 660/104/104 938/113/113
+f 734/105/105 659/86/86 701/114/114
+f 734/105/105 708/115/115 789/106/106
+f 789/106/106 731/116/116 846/107/107
+f 846/107/107 738/117/117 895/108/108
+f 895/108/108 743/118/118 906/109/109
+f 906/109/109 746/119/119 917/110/110
+f 917/110/110 752/120/120 931/111/111
+f 931/111/111 754/121/121 937/112/112
+f 937/112/112 938/113/113 756/122/122
+f 938/113/113 660/104/104 756/122/122
+f 701/114/114 659/86/86 645/123/123
+f 708/115/115 645/123/123 641/124/124
+f 701/114/114 645/123/123 708/115/115
+f 731/116/116 641/124/124 632/125/125
+f 708/115/115 641/124/124 731/116/116
+f 738/117/117 632/125/125 611/126/126
+f 731/116/116 632/125/125 738/117/117
+f 743/118/118 611/126/126 606/127/127
+f 738/117/117 611/126/126 743/118/118
+f 746/119/119 606/127/127 604/128/128
+f 743/118/118 606/127/127 746/119/119
+f 752/120/120 604/128/128 599/129/129
+f 746/119/119 604/128/128 752/120/120
+f 754/121/121 599/129/129 596/130/130
+f 752/120/120 599/129/129 754/121/121
+f 756/122/122 597/131/131 596/130/130
+f 754/121/121 756/122/122 596/130/130
+f 756/122/122 660/104/104 597/131/131
+f 645/123/123 659/86/86 630/132/132
+f 645/123/123 566/133/133 641/124/124
+f 641/124/124 507/134/134 632/125/125
+f 632/125/125 459/135/135 611/126/126
+f 611/126/126 445/136/136 606/127/127
+f 606/127/127 435/137/137 604/128/128
+f 604/128/128 420/138/138 599/129/129
+f 599/129/129 596/130/130 414/139/139
+f 596/130/130 597/131/131 415/140/140
+f 597/131/131 660/104/104 415/140/140
+f 630/132/132 659/86/86 587/141/141
+f 630/132/132 529/142/142 566/133/133
+f 566/133/133 427/143/143 507/134/134
+f 507/134/134 320/144/144 459/135/135
+f 459/135/135 288/145/145 445/136/136
+f 445/136/136 235/146/146 435/137/137
+f 435/137/137 197/147/147 420/138/138
+f 420/138/138 414/139/139 190/148/148
+f 414/139/139 415/140/140 191/149/149
+f 415/140/140 660/104/104 191/149/149
+f 587/141/141 659/86/86 578/150/150
+f 587/141/141 525/151/151 529/142/142
+f 529/142/142 408/152/152 427/143/143
+f 427/143/143 286/153/153 320/144/144
+f 320/144/144 221/154/154 288/145/145
+f 288/145/145 187/155/155 235/146/146
+f 235/146/146 154/156/156 197/147/147
+f 197/147/147 146/157/157 190/148/148
+f 190/148/148 147/158/158 191/149/149
+f 191/149/149 660/104/104 147/158/158
+f 578/150/150 659/86/86 577/159/159
+f 578/150/150 524/160/160 525/151/151
+f 525/151/151 409/161/161 408/152/152
+f 408/152/152 287/162/162 286/153/153
+f 286/153/153 222/163/163 221/154/154
+f 221/154/154 188/164/164 187/155/155
+f 187/155/155 153/165/165 154/156/156
+f 154/156/156 145/166/166 146/157/157
+f 147/158/158 145/166/166 148/16/16
+f 146/157/157 145/166/166 147/158/158
+f 147/158/158 148/16/16 660/104/104
+f 577/159/159 659/86/86 586/167/167
+f 577/159/159 530/168/168 524/160/160
+f 524/160/160 428/169/169 409/161/161
+f 409/161/161 321/170/170 287/162/162
+f 287/162/162 289/171/171 222/163/163
+f 222/163/163 236/172/172 188/164/164
+f 188/164/164 196/173/173 153/165/165
+f 153/165/165 189/174/174 145/166/166
+f 145/166/166 192/175/175 148/16/16
+f 148/16/16 192/175/175 660/104/104
+f 586/167/167 659/86/86 629/176/176
+f 586/167/167 567/177/177 530/168/168
+f 530/168/168 508/178/178 428/169/169
+f 428/169/169 460/179/179 321/170/170
+f 321/170/170 446/180/180 289/171/171
+f 289/171/171 436/181/181 236/172/172
+f 236/172/172 419/182/182 196/173/173
+f 196/173/173 413/183/183 189/174/174
+f 189/174/174 416/184/184 192/175/175
+f 192/175/175 416/184/184 660/104/104
+f 629/176/176 659/86/86 638/185/185
+f 629/176/176 601/186/186 567/177/177
+f 567/177/177 554/187/187 508/178/178
+f 508/178/178 533/188/188 460/179/179
+f 460/179/179 526/189/189 446/180/180
+f 446/180/180 517/190/190 436/181/181
+f 436/181/181 504/191/191 419/182/182
+f 419/182/182 500/192/192 413/183/183
+f 413/183/183 501/193/193 416/184/184
+f 416/184/184 501/193/193 660/104/104
+f 638/185/185 659/86/86 644/194/194
+f 638/185/185 642/195/195 601/186/186
+f 601/186/186 633/196/196 554/187/187
+f 554/187/187 612/197/197 533/188/188
+f 533/188/188 607/198/198 526/189/189
+f 526/189/189 605/199/199 517/190/190
+f 517/190/190 598/200/200 504/191/191
+f 504/191/191 594/201/201 500/192/192
+f 500/192/192 595/202/202 501/193/193
+f 501/193/193 595/202/202 660/104/104
+f 644/194/194 659/86/86 658/203/203
+f 642/195/195 658/203/203 657/204/204
+f 644/194/194 658/203/203 642/195/195
+f 633/196/196 657/204/204 656/205/205
+f 642/195/195 657/204/204 633/196/196
+f 612/197/197 656/205/205 655/206/206
+f 633/196/196 656/205/205 612/197/197
+f 607/198/198 655/206/206 654/207/207
+f 612/197/197 655/206/206 607/198/198
+f 607/198/198 653/208/208 605/199/199
+f 605/199/199 652/209/209 598/200/200
+f 594/201/201 652/209/209 649/210/210
+f 598/200/200 652/209/209 594/201/201
+f 594/201/201 651/211/211 595/202/202
+f 595/202/202 651/211/211 660/104/104
+f 658/203/203 659/86/86 700/212/212
+f 657/204/204 700/212/212 709/213/213
+f 658/203/203 700/212/212 657/204/204
+f 656/205/205 709/213/213 730/214/214
+f 657/204/204 709/213/213 656/205/205
+f 655/206/206 730/214/214 739/215/215
+f 656/205/205 730/214/214 655/206/206
+f 654/207/207 739/215/215 744/216/216
+f 655/206/206 739/215/215 654/207/207
+f 654/207/207 745/217/217 653/208/208
+f 653/208/208 751/218/218 652/209/209
+f 649/210/210 751/218/218 753/219/219
+f 652/209/209 751/218/218 649/210/210
+f 649/210/210 755/220/220 651/211/211
+f 651/211/211 755/220/220 660/104/104
+f 700/212/212 659/86/86 720/221/221
+f 700/212/212 749/222/222 709/213/213
+f 709/213/213 801/223/223 730/214/214
+f 730/214/214 823/224/224 739/215/215
+f 739/215/215 828/225/225 744/216/216
+f 744/216/216 840/226/226 745/217/217
+f 745/217/217 850/227/227 751/218/218
+f 751/218/218 852/228/228 753/219/219
+f 753/219/219 853/229/229 755/220/220
+f 755/220/220 660/104/104 853/229/229
+f 720/221/221 659/86/86 733/230/230
+f 720/221/221 790/231/231 749/222/222
+f 749/222/222 847/232/232 801/223/223
+f 801/223/223 894/233/233 823/224/224
+f 823/224/224 907/234/234 828/225/225
+f 828/225/225 916/235/235 840/226/226
+f 840/226/226 930/236/236 850/227/227
+f 850/227/227 936/237/237 852/228/228
+f 852/228/228 853/229/229 939/238/238
+f 853/229/229 660/104/104 939/238/238
+f 733/230/230 659/86/86 765/239/239
+f 733/230/230 825/240/240 790/231/231
+f 790/231/231 923/241/241 847/232/232
+f 847/232/232 1031/242/242 894/233/233
+f 894/233/233 1066/243/243 907/234/234
+f 907/234/234 1116/244/244 916/235/235
+f 916/235/235 1154/245/245 930/236/236
+f 930/236/236 1159/246/246 936/237/237
+f 936/237/237 939/238/238 1162/247/247
+f 939/238/238 660/104/104 1162/247/247
+f 765/239/239 659/86/86 776/248/248
+f 765/239/239 834/249/249 825/240/240
+f 825/240/240 946/250/250 923/241/241
+f 923/241/241 1069/251/251 1031/242/242
+f 1031/242/242 1130/252/252 1066/243/243
+f 1066/243/243 1164/253/253 1116/244/244
+f 1116/244/244 1199/254/254 1154/245/245
+f 1154/245/245 1202/255/255 1159/246/246
+f 1159/246/246 1204/1/1 1162/247/247
+f 1162/247/247 660/104/104 1204/1/1
+f 776/248/248 659/86/86 777/85/85
+f 776/248/248 835/89/89 834/249/249
+f 834/249/249 945/91/91 946/250/250
+f 946/250/250 1068/93/93 1069/251/251
+f 1069/251/251 1129/95/95 1130/252/252
+f 1130/252/252 1163/97/97 1164/253/253
+f 1164/253/253 1200/99/99 1199/254/254
+f 1199/254/254 1203/101/101 1202/255/255
+f 1204/1/1 1205/103/103 1203/101/101
+f 1202/255/255 1203/101/101 1204/1/1
+f 1204/1/1 660/104/104 1205/103/103
+f 777/85/85 766/87/87 824/88/88
+f 835/89/89 824/88/88 924/90/90
+f 945/91/91 924/90/90 1030/92/92
+f 1068/93/93 1030/92/92 1067/94/94
+f 1129/95/95 1067/94/94 1117/96/96
+f 1163/97/97 1117/96/96 1155/98/98
+f 1200/99/99 1155/98/98 1160/100/100
+f 1203/101/101 1160/100/100 1161/102/102
+f 766/87/87 734/105/105 789/106/106
+f 824/88/88 789/106/106 846/107/107
+f 924/90/90 846/107/107 895/108/108
+f 1030/92/92 895/108/108 906/109/109
+f 1067/94/94 906/109/109 917/110/110
+f 1117/96/96 917/110/110 931/111/111
+f 1155/98/98 931/111/111 937/112/112
+f 1160/100/100 937/112/112 938/113/113
+f 734/105/105 701/114/114 708/115/115
+f 789/106/106 708/115/115 731/116/116
+f 846/107/107 731/116/116 738/117/117
+f 895/108/108 738/117/117 743/118/118
+f 906/109/109 743/118/118 746/119/119
+f 917/110/110 746/119/119 752/120/120
+f 931/111/111 752/120/120 754/121/121
+f 937/112/112 754/121/121 756/122/122
+f 645/123/123 630/132/132 566/133/133
+f 641/124/124 566/133/133 507/134/134
+f 632/125/125 507/134/134 459/135/135
+f 611/126/126 459/135/135 445/136/136
+f 606/127/127 445/136/136 435/137/137
+f 604/128/128 435/137/137 420/138/138
+f 599/129/129 420/138/138 414/139/139
+f 596/130/130 415/140/140 414/139/139
+f 630/132/132 587/141/141 529/142/142
+f 566/133/133 529/142/142 427/143/143
+f 507/134/134 427/143/143 320/144/144
+f 459/135/135 320/144/144 288/145/145
+f 445/136/136 288/145/145 235/146/146
+f 435/137/137 235/146/146 197/147/147
+f 420/138/138 197/147/147 190/148/148
+f 414/139/139 191/149/149 190/148/148
+f 587/141/141 578/150/150 525/151/151
+f 529/142/142 525/151/151 408/152/152
+f 427/143/143 408/152/152 286/153/153
+f 320/144/144 286/153/153 221/154/154
+f 288/145/145 221/154/154 187/155/155
+f 235/146/146 187/155/155 154/156/156
+f 197/147/147 154/156/156 146/157/157
+f 190/148/148 146/157/157 147/158/158
+f 578/150/150 577/159/159 524/160/160
+f 525/151/151 524/160/160 409/161/161
+f 408/152/152 409/161/161 287/162/162
+f 286/153/153 287/162/162 222/163/163
+f 221/154/154 222/163/163 188/164/164
+f 187/155/155 188/164/164 153/165/165
+f 154/156/156 153/165/165 145/166/166
+f 577/159/159 586/167/167 530/168/168
+f 524/160/160 530/168/168 428/169/169
+f 409/161/161 428/169/169 321/170/170
+f 287/162/162 321/170/170 289/171/171
+f 222/163/163 289/171/171 236/172/172
+f 188/164/164 236/172/172 196/173/173
+f 153/165/165 196/173/173 189/174/174
+f 145/166/166 189/174/174 192/175/175
+f 586/167/167 629/176/176 567/177/177
+f 530/168/168 567/177/177 508/178/178
+f 428/169/169 508/178/178 460/179/179
+f 321/170/170 460/179/179 446/180/180
+f 289/171/171 446/180/180 436/181/181
+f 236/172/172 436/181/181 419/182/182
+f 196/173/173 419/182/182 413/183/183
+f 189/174/174 413/183/183 416/184/184
+f 629/176/176 638/185/185 601/186/186
+f 567/177/177 601/186/186 554/187/187
+f 508/178/178 554/187/187 533/188/188
+f 460/179/179 533/188/188 526/189/189
+f 446/180/180 526/189/189 517/190/190
+f 436/181/181 517/190/190 504/191/191
+f 419/182/182 504/191/191 500/192/192
+f 413/183/183 500/192/192 501/193/193
+f 638/185/185 644/194/194 642/195/195
+f 601/186/186 642/195/195 633/196/196
+f 554/187/187 633/196/196 612/197/197
+f 533/188/188 612/197/197 607/198/198
+f 526/189/189 607/198/198 605/199/199
+f 517/190/190 605/199/199 598/200/200
+f 504/191/191 598/200/200 594/201/201
+f 500/192/192 594/201/201 595/202/202
+f 607/198/198 654/207/207 653/208/208
+f 605/199/199 653/208/208 652/209/209
+f 594/201/201 649/210/210 651/211/211
+f 654/207/207 744/216/216 745/217/217
+f 653/208/208 745/217/217 751/218/218
+f 649/210/210 753/219/219 755/220/220
+f 700/212/212 720/221/221 749/222/222
+f 709/213/213 749/222/222 801/223/223
+f 730/214/214 801/223/223 823/224/224
+f 739/215/215 823/224/224 828/225/225
+f 744/216/216 828/225/225 840/226/226
+f 745/217/217 840/226/226 850/227/227
+f 751/218/218 850/227/227 852/228/228
+f 753/219/219 852/228/228 853/229/229
+f 720/221/221 733/230/230 790/231/231
+f 749/222/222 790/231/231 847/232/232
+f 801/223/223 847/232/232 894/233/233
+f 823/224/224 894/233/233 907/234/234
+f 828/225/225 907/234/234 916/235/235
+f 840/226/226 916/235/235 930/236/236
+f 850/227/227 930/236/236 936/237/237
+f 852/228/228 936/237/237 939/238/238
+f 733/230/230 765/239/239 825/240/240
+f 790/231/231 825/240/240 923/241/241
+f 847/232/232 923/241/241 1031/242/242
+f 894/233/233 1031/242/242 1066/243/243
+f 907/234/234 1066/243/243 1116/244/244
+f 916/235/235 1116/244/244 1154/245/245
+f 930/236/236 1154/245/245 1159/246/246
+f 936/237/237 1159/246/246 1162/247/247
+f 765/239/239 776/248/248 834/249/249
+f 825/240/240 834/249/249 946/250/250
+f 923/241/241 946/250/250 1069/251/251
+f 1031/242/242 1069/251/251 1130/252/252
+f 1066/243/243 1130/252/252 1164/253/253
+f 1116/244/244 1164/253/253 1199/254/254
+f 1154/245/245 1199/254/254 1202/255/255
+f 1159/246/246 1202/255/255 1204/1/1
+f 776/248/248 777/85/85 835/89/89
+f 834/249/249 835/89/89 945/91/91
+f 946/250/250 945/91/91 1068/93/93
+f 1069/251/251 1068/93/93 1129/95/95
+f 1130/252/252 1129/95/95 1163/97/97
+f 1164/253/253 1163/97/97 1200/99/99
+f 1199/254/254 1200/99/99 1203/101/101
+f 698/256/256 870/257/257 699/258/258
+f 699/258/258 1033/259/259 702/260/260
+f 702/260/260 1062/261/261 703/262/262
+f 703/262/262 1125/263/263 704/264/264
+f 704/264/264 1178/265/265 705/266/266
+f 705/266/266 1234/267/267 707/268/268
+f 707/268/268 1271/269/269 712/270/270
+f 712/270/270 1297/271/271 719/272/272
+f 719/272/272 1301/273/273 725/274/274
+f 725/274/274 1308/275/275 727/276/276
+f 727/276/276 1306/277/277 726/278/278
+f 726/278/278 1306/277/277 662/279/279
+f 829/280/280 1011/281/281 870/257/257
+f 870/257/257 1263/282/282 1033/259/259
+f 1033/259/259 1270/283/283 1062/261/261
+f 1062/261/261 1291/284/284 1125/263/263
+f 1125/263/263 1295/285/285 1178/265/265
+f 1178/265/265 1303/286/286 1234/267/267
+f 1234/267/267 1313/287/287 1271/269/269
+f 1271/269/269 1320/288/288 1297/271/271
+f 1297/271/271 1329/289/289 1301/273/273
+f 1301/273/273 1333/290/290 1308/275/275
+f 1308/275/275 1332/291/291 1306/277/277
+f 1306/277/277 1332/291/291 662/279/279
+f 922/292/292 1158/293/293 1011/281/281
+f 1011/281/281 1294/294/294 1263/282/282
+f 1263/282/282 1298/295/295 1270/283/283
+f 1270/283/283 1302/296/296 1291/284/284
+f 1291/284/284 1309/297/297 1295/285/285
+f 1295/285/285 1312/298/298 1303/286/286
+f 1303/286/286 1321/299/299 1313/287/287
+f 1313/287/287 1335/300/300 1320/288/288
+f 1320/288/288 1340/301/301 1329/289/289
+f 1329/289/289 1343/302/302 1333/290/290
+f 1333/290/290 1341/303/303 1332/291/291
+f 1332/291/291 1341/303/303 662/279/279
+f 1005/304/304 1158/293/293 1180/305/305
+f 1158/293/293 1294/294/294 1296/306/306
+f 1294/294/294 1298/295/295 1300/307/307
+f 1298/295/295 1302/296/296 1307/308/308
+f 1302/296/296 1309/297/297 1310/309/309
+f 1309/297/297 1312/298/298 1315/310/310
+f 1312/298/298 1321/299/299 1322/311/311
+f 1321/299/299 1335/300/300 1337/312/312
+f 1335/300/300 1340/301/301 1342/313/313
+f 1340/301/301 1343/302/302 1353/314/314
+f 1343/302/302 1341/303/303 1352/315/315
+f 1341/303/303 1352/315/315 662/279/279
+f 1180/305/305 184/316/316 342/317/317
+f 1038/318/318 1180/305/305 342/317/317
+f 1296/306/306 184/316/316 57/319/319
+f 1180/305/305 1296/306/306 184/316/316
+f 1300/307/307 57/319/319 54/320/320
+f 1296/306/306 1300/307/307 57/319/319
+f 1307/308/308 46/321/321 54/320/320
+f 1300/307/307 1307/308/308 54/320/320
+f 1310/309/309 44/322/322 46/321/321
+f 1307/308/308 1310/309/309 46/321/321
+f 1315/310/310 44/322/322 39/323/323
+f 1310/309/309 1315/310/310 44/322/322
+f 1322/311/311 32/324/324 39/323/323
+f 1315/310/310 1322/311/311 39/323/323
+f 1337/312/312 17/325/325 32/324/324
+f 1322/311/311 1337/312/312 32/324/324
+f 1337/312/312 1342/313/313 12/326/326
+f 1353/314/314 1/327/327 12/326/326
+f 1342/313/313 1353/314/314 12/326/326
+f 1/327/327 1352/315/315 2/328/328
+f 1/327/327 1353/314/314 1352/315/315
+f 1352/315/315 662/279/279 2/328/328
+f 342/317/317 184/316/316 204/329/329
+f 184/316/316 60/330/330 57/319/319
+f 57/319/319 55/331/331 54/320/320
+f 54/320/320 52/332/332 46/321/321
+f 46/321/321 45/333/333 44/322/322
+f 44/322/322 40/334/334 39/323/323
+f 39/323/323 33/335/335 32/324/324
+f 32/324/324 19/336/336 17/325/325
+f 17/325/325 14/337/337 12/326/326
+f 12/326/326 11/338/338 1/327/327
+f 1/327/327 2/328/328 13/339/339
+f 2/328/328 662/279/279 13/339/339
+f 353/340/340 349/341/341 204/329/329
+f 204/329/329 93/342/342 60/330/330
+f 60/330/330 85/343/343 55/331/331
+f 55/331/331 71/344/344 52/332/332
+f 52/332/332 59/345/345 45/333/333
+f 45/333/333 51/346/346 40/334/334
+f 40/334/334 41/347/347 33/335/335
+f 33/335/335 34/348/348 19/336/336
+f 19/336/336 27/349/349 14/337/337
+f 14/337/337 21/350/350 11/338/338
+f 11/338/338 22/351/351 13/339/339
+f 13/339/339 22/351/351 662/279/279
+f 431/352/352 492/353/353 349/341/341
+f 349/341/341 352/354/354 93/342/342
+f 93/342/342 313/355/355 85/343/343
+f 85/343/343 268/356/356 71/344/344
+f 71/344/344 202/357/357 59/345/345
+f 59/345/345 133/358/358 51/346/346
+f 51/346/346 87/359/359 41/347/347
+f 41/347/347 58/360/360 34/348/348
+f 34/348/348 53/361/361 27/349/349
+f 27/349/349 47/362/362 21/350/350
+f 21/350/350 48/363/363 22/351/351
+f 22/351/351 48/363/363 662/279/279
+f 531/364/364 699/258/258 492/353/353
+f 492/353/353 702/260/260 352/354/354
+f 352/354/354 703/262/262 313/355/355
+f 313/355/355 704/264/264 268/356/356
+f 268/356/356 705/266/266 202/357/357
+f 202/357/357 707/268/268 133/358/358
+f 133/358/358 712/270/270 87/359/359
+f 87/359/359 719/272/272 58/360/360
+f 58/360/360 725/274/274 53/361/361
+f 53/361/361 727/276/276 47/362/362
+f 47/362/362 726/278/278 48/363/363
+f 48/363/363 726/278/278 662/279/279
+f 698/256/256 829/280/280 870/257/257
+f 699/258/258 870/257/257 1033/259/259
+f 702/260/260 1033/259/259 1062/261/261
+f 703/262/262 1062/261/261 1125/263/263
+f 704/264/264 1125/263/263 1178/265/265
+f 705/266/266 1178/265/265 1234/267/267
+f 707/268/268 1234/267/267 1271/269/269
+f 712/270/270 1271/269/269 1297/271/271
+f 719/272/272 1297/271/271 1301/273/273
+f 725/274/274 1301/273/273 1308/275/275
+f 727/276/276 1308/275/275 1306/277/277
+f 829/280/280 922/292/292 1011/281/281
+f 870/257/257 1011/281/281 1263/282/282
+f 1033/259/259 1263/282/282 1270/283/283
+f 1062/261/261 1270/283/283 1291/284/284
+f 1125/263/263 1291/284/284 1295/285/285
+f 1178/265/265 1295/285/285 1303/286/286
+f 1234/267/267 1303/286/286 1313/287/287
+f 1271/269/269 1313/287/287 1320/288/288
+f 1297/271/271 1320/288/288 1329/289/289
+f 1301/273/273 1329/289/289 1333/290/290
+f 1308/275/275 1333/290/290 1332/291/291
+f 922/292/292 1158/293/293 1005/304/304
+f 1011/281/281 1294/294/294 1158/293/293
+f 1263/282/282 1298/295/295 1294/294/294
+f 1270/283/283 1302/296/296 1298/295/295
+f 1291/284/284 1309/297/297 1302/296/296
+f 1295/285/285 1312/298/298 1309/297/297
+f 1303/286/286 1321/299/299 1312/298/298
+f 1313/287/287 1335/300/300 1321/299/299
+f 1320/288/288 1340/301/301 1335/300/300
+f 1329/289/289 1343/302/302 1340/301/301
+f 1333/290/290 1343/302/302 1341/303/303
+f 1005/304/304 1180/305/305 1038/318/318
+f 1158/293/293 1296/306/306 1180/305/305
+f 1294/294/294 1300/307/307 1296/306/306
+f 1298/295/295 1307/308/308 1300/307/307
+f 1302/296/296 1310/309/309 1307/308/308
+f 1309/297/297 1315/310/310 1310/309/309
+f 1312/298/298 1322/311/311 1315/310/310
+f 1321/299/299 1337/312/312 1322/311/311
+f 1335/300/300 1342/313/313 1337/312/312
+f 1340/301/301 1353/314/314 1342/313/313
+f 1343/302/302 1352/315/315 1353/314/314
+f 1337/312/312 17/325/325 12/326/326
+f 342/317/317 353/340/340 204/329/329
+f 184/316/316 204/329/329 60/330/330
+f 57/319/319 60/330/330 55/331/331
+f 54/320/320 55/331/331 52/332/332
+f 46/321/321 52/332/332 45/333/333
+f 44/322/322 45/333/333 40/334/334
+f 39/323/323 40/334/334 33/335/335
+f 32/324/324 33/335/335 19/336/336
+f 17/325/325 19/336/336 14/337/337
+f 12/326/326 14/337/337 11/338/338
+f 1/327/327 11/338/338 13/339/339
+f 353/340/340 431/352/352 349/341/341
+f 204/329/329 349/341/341 93/342/342
+f 60/330/330 93/342/342 85/343/343
+f 55/331/331 85/343/343 71/344/344
+f 52/332/332 71/344/344 59/345/345
+f 45/333/333 59/345/345 51/346/346
+f 40/334/334 51/346/346 41/347/347
+f 33/335/335 41/347/347 34/348/348
+f 19/336/336 34/348/348 27/349/349
+f 14/337/337 27/349/349 21/350/350
+f 11/338/338 21/350/350 22/351/351
+f 431/352/352 531/364/364 492/353/353
+f 349/341/341 492/353/353 352/354/354
+f 93/342/342 352/354/354 313/355/355
+f 85/343/343 313/355/355 268/356/356
+f 71/344/344 268/356/356 202/357/357
+f 59/345/345 202/357/357 133/358/358
+f 51/346/346 133/358/358 87/359/359
+f 41/347/347 87/359/359 58/360/360
+f 34/348/348 58/360/360 53/361/361
+f 27/349/349 53/361/361 47/362/362
+f 21/350/350 47/362/362 48/363/363
+f 531/364/364 698/256/256 699/258/258
+f 492/353/353 699/258/258 702/260/260
+f 352/354/354 702/260/260 703/262/262
+f 313/355/355 703/262/262 704/264/264
+f 268/356/356 704/264/264 705/266/266
+f 202/357/357 705/266/266 707/268/268
+f 133/358/358 707/268/268 712/270/270
+f 87/359/359 712/270/270 719/272/272
+f 58/360/360 719/272/272 725/274/274
+f 53/361/361 725/274/274 727/276/276
+f 47/362/362 727/276/276 726/278/278
+f 1038/318/318 1180/305/305 1101/365/365
+f 1180/305/305 1296/306/306 1284/366/366
+f 1296/306/306 1300/307/307 1293/367/367
+f 1300/307/307 1307/308/308 1299/368/368
+f 1307/308/308 1310/309/309 1304/369/369
+f 1310/309/309 1315/310/310 1311/370/370
+f 1315/310/310 1322/311/311 1318/371/371
+f 1322/311/311 1337/312/312 1334/372/372
+f 1337/312/312 1342/313/313 1336/373/373
+f 1342/313/313 1353/314/314 1339/374/374
+f 1353/314/314 1352/315/315 1338/375/375
+f 1352/315/315 1338/375/375 662/279/279
+f 984/376/376 935/377/377 1101/365/365
+f 1101/365/365 1219/378/378 1284/366/366
+f 1284/366/366 1240/379/379 1293/367/367
+f 1293/367/367 1255/380/380 1299/368/368
+f 1299/368/368 1267/381/381 1304/369/369
+f 1304/369/369 1292/382/382 1311/370/370
+f 1311/370/370 1305/383/383 1318/371/371
+f 1318/371/371 1314/384/384 1334/372/372
+f 1334/372/372 1316/385/385 1336/373/373
+f 1336/373/373 1319/386/386 1339/374/374
+f 1339/374/374 1317/387/387 1338/375/375
+f 1338/375/375 1317/387/387 662/279/279
+f 887/388/388 799/389/389 935/377/377
+f 935/377/377 865/390/390 1219/378/378
+f 1219/378/378 874/391/391 1240/379/379
+f 1240/379/379 896/392/392 1255/380/380
+f 1255/380/380 908/393/393 1267/381/381
+f 1267/381/381 942/394/394 1292/382/382
+f 1292/382/382 1045/395/395 1305/383/383
+f 1305/383/383 1174/396/396 1314/384/384
+f 1314/384/384 1218/397/397 1316/385/385
+f 1316/385/385 1231/398/398 1319/386/386
+f 1319/386/386 1228/399/399 1317/387/387
+f 1317/387/387 1228/399/399 662/279/279
+f 778/400/400 564/401/401 799/389/389
+f 799/389/389 510/402/402 865/390/390
+f 874/391/391 510/402/402 493/403/403
+f 865/390/390 510/402/402 874/391/391
+f 896/392/392 493/403/403 483/404/404
+f 874/391/391 493/403/403 896/392/392
+f 908/393/393 483/404/404 465/405/405
+f 896/392/392 483/404/404 908/393/393
+f 942/394/394 465/405/405 441/406/406
+f 908/393/393 465/405/405 942/394/394
+f 1045/395/395 441/406/406 362/407/407
+f 942/394/394 441/406/406 1045/395/395
+f 1045/395/395 251/408/408 1174/396/396
+f 1174/396/396 193/409/409 1218/397/397
+f 1218/397/397 164/410/410 1231/398/398
+f 1228/399/399 164/410/410 171/411/411
+f 1231/398/398 164/410/410 1228/399/399
+f 1228/399/399 171/411/411 662/279/279
+f 585/412/412 429/413/413 564/401/401
+f 564/401/401 150/414/414 510/402/402
+f 510/402/402 125/415/415 493/403/403
+f 493/403/403 100/416/416 483/404/404
+f 483/404/404 92/417/417 465/405/405
+f 465/405/405 70/418/418 441/406/406
+f 441/406/406 49/419/419 362/407/407
+f 362/407/407 42/420/420 251/408/408
+f 251/408/408 38/421/421 193/409/409
+f 193/409/409 36/422/422 164/410/410
+f 164/410/410 37/423/423 171/411/411
+f 171/411/411 37/423/423 662/279/279
+f 477/424/424 264/425/425 429/413/413
+f 429/413/413 74/426/426 150/414/414
+f 150/414/414 61/427/427 125/415/415
+f 125/415/415 56/428/428 100/416/416
+f 100/416/416 50/429/429 92/417/417
+f 92/417/417 43/430/430 70/418/418
+f 70/418/418 35/431/431 49/419/419
+f 49/419/419 20/432/432 42/420/420
+f 42/420/420 18/433/433 38/421/421
+f 38/421/421 15/434/434 36/422/422
+f 36/422/422 16/435/435 37/423/423
+f 37/423/423 16/435/435 662/279/279
+f 381/436/436 184/316/316 264/425/425
+f 264/425/425 57/319/319 74/426/426
+f 74/426/426 54/320/320 61/427/427
+f 61/427/427 46/321/321 56/428/428
+f 56/428/428 44/322/322 50/429/429
+f 50/429/429 39/323/323 43/430/430
+f 43/430/430 32/324/324 35/431/431
+f 35/431/431 17/325/325 20/432/432
+f 20/432/432 12/326/326 18/433/433
+f 1/327/327 15/434/434 18/433/433
+f 15/434/434 2/328/328 16/435/435
+f 16/435/435 2/328/328 662/279/279
+f 1038/318/318 984/376/376 1101/365/365
+f 1180/305/305 1101/365/365 1284/366/366
+f 1296/306/306 1284/366/366 1293/367/367
+f 1300/307/307 1293/367/367 1299/368/368
+f 1307/308/308 1299/368/368 1304/369/369
+f 1310/309/309 1304/369/369 1311/370/370
+f 1315/310/310 1311/370/370 1318/371/371
+f 1322/311/311 1318/371/371 1334/372/372
+f 1337/312/312 1334/372/372 1336/373/373
+f 1342/313/313 1336/373/373 1339/374/374
+f 1353/314/314 1339/374/374 1338/375/375
+f 984/376/376 887/388/388 935/377/377
+f 1101/365/365 935/377/377 1219/378/378
+f 1284/366/366 1219/378/378 1240/379/379
+f 1293/367/367 1240/379/379 1255/380/380
+f 1299/368/368 1255/380/380 1267/381/381
+f 1304/369/369 1267/381/381 1292/382/382
+f 1311/370/370 1292/382/382 1305/383/383
+f 1318/371/371 1305/383/383 1314/384/384
+f 1334/372/372 1314/384/384 1316/385/385
+f 1336/373/373 1316/385/385 1319/386/386
+f 1339/374/374 1319/386/386 1317/387/387
+f 887/388/388 778/400/400 799/389/389
+f 935/377/377 799/389/389 865/390/390
+f 1219/378/378 865/390/390 874/391/391
+f 1240/379/379 874/391/391 896/392/392
+f 1255/380/380 896/392/392 908/393/393
+f 1267/381/381 908/393/393 942/394/394
+f 1292/382/382 942/394/394 1045/395/395
+f 1305/383/383 1045/395/395 1174/396/396
+f 1314/384/384 1174/396/396 1218/397/397
+f 1316/385/385 1218/397/397 1231/398/398
+f 1319/386/386 1231/398/398 1228/399/399
+f 778/400/400 585/412/412 564/401/401
+f 799/389/389 564/401/401 510/402/402
+f 1045/395/395 362/407/407 251/408/408
+f 1174/396/396 251/408/408 193/409/409
+f 1218/397/397 193/409/409 164/410/410
+f 585/412/412 477/424/424 429/413/413
+f 564/401/401 429/413/413 150/414/414
+f 510/402/402 150/414/414 125/415/415
+f 493/403/403 125/415/415 100/416/416
+f 483/404/404 100/416/416 92/417/417
+f 465/405/405 92/417/417 70/418/418
+f 441/406/406 70/418/418 49/419/419
+f 362/407/407 49/419/419 42/420/420
+f 251/408/408 42/420/420 38/421/421
+f 193/409/409 38/421/421 36/422/422
+f 164/410/410 36/422/422 37/423/423
+f 477/424/424 381/436/436 264/425/425
+f 429/413/413 264/425/425 74/426/426
+f 150/414/414 74/426/426 61/427/427
+f 125/415/415 61/427/427 56/428/428
+f 100/416/416 56/428/428 50/429/429
+f 92/417/417 50/429/429 43/430/430
+f 70/418/418 43/430/430 35/431/431
+f 49/419/419 35/431/431 20/432/432
+f 42/420/420 20/432/432 18/433/433
+f 38/421/421 18/433/433 15/434/434
+f 36/422/422 15/434/434 16/435/435
+f 381/436/436 342/317/317 184/316/316
+f 264/425/425 184/316/316 57/319/319
+f 74/426/426 57/319/319 54/320/320
+f 61/427/427 54/320/320 46/321/321
+f 56/428/428 46/321/321 44/322/322
+f 50/429/429 44/322/322 39/323/323
+f 43/430/430 39/323/323 32/324/324
+f 35/431/431 32/324/324 17/325/325
+f 20/432/432 17/325/325 12/326/326
+f 18/433/433 12/326/326 1/327/327
+f 1/327/327 2/328/328 15/434/434
+f 648/437/437 759/52/52 756/122/122
+f 650/438/438 648/437/437 756/122/122
+f 756/122/122 759/52/52 867/50/50
+f 854/439/439 867/50/50 978/48/48
+f 938/113/113 978/48/48 1108/46/46
+f 1057/440/440 1108/46/46 1207/42/42
+f 1161/102/102 1207/42/42 1237/41/41
+f 1205/103/103 1237/41/41 1236/2/2
+f 756/122/122 867/50/50 854/439/439
+f 854/439/439 978/48/48 938/113/113
+f 938/113/113 1108/46/46 1057/440/440
+f 1057/440/440 1207/42/42 1161/102/102
+f 1161/102/102 1237/41/41 1205/103/103
+f 1205/103/103 1236/2/2 1204/1/1
+f 692/441/441 597/131/131 592/54/54
+f 691/442/442 597/131/131 692/441/441
+f 597/131/131 486/56/56 592/54/54
+f 502/443/443 377/57/57 486/56/56
+f 415/140/140 246/58/58 377/57/57
+f 297/444/444 143/59/59 246/58/58
+f 191/149/149 118/60/60 143/59/59
+f 147/158/158 117/15/15 118/60/60
+f 597/131/131 502/443/443 486/56/56
+f 502/443/443 415/140/140 377/57/57
+f 415/140/140 297/444/444 246/58/58
+f 297/444/444 191/149/149 143/59/59
+f 191/149/149 147/158/158 118/60/60
+f 147/158/158 148/16/16 117/15/15
+f 148/16/16 142/62/62 117/15/15
+f 192/175/175 245/64/64 142/62/62
+f 298/445/445 376/66/66 245/64/64
+f 416/184/184 485/68/68 376/66/66
+f 501/193/193 591/70/70 485/68/68
+f 595/202/202 647/72/72 591/70/70
+f 148/16/16 192/175/175 142/62/62
+f 192/175/175 298/445/445 245/64/64
+f 298/445/445 416/184/184 376/66/66
+f 416/184/184 501/193/193 485/68/68
+f 501/193/193 595/202/202 591/70/70
+f 595/202/202 651/211/211 647/72/72
+f 651/211/211 758/74/74 647/72/72
+f 755/220/220 866/76/76 758/74/74
+f 853/229/229 977/78/78 866/76/76
+f 939/238/238 1107/80/80 977/78/78
+f 1056/446/446 1206/82/82 1107/80/80
+f 1162/247/247 1236/2/2 1206/82/82
+f 651/211/211 755/220/220 758/74/74
+f 755/220/220 853/229/229 866/76/76
+f 853/229/229 939/238/238 977/78/78
+f 939/238/238 1056/446/446 1107/80/80
+f 1056/446/446 1162/247/247 1206/82/82
+f 1162/247/247 1204/1/1 1236/2/2
+f 97/447/447 78/448/448 77/449/448
+f 98/450/449 78/448/448 97/447/447
+f 77/449/448 80/451/450 79/452/450
+f 78/448/448 80/451/450 77/449/448
+f 79/452/450 31/453/451 30/454/451
+f 80/451/450 31/453/451 79/452/450
+f 30/454/451 29/455/452 28/456/452
+f 31/453/451 29/455/452 30/454/451
+f 28/456/452 98/450/449 97/447/447
+f 29/455/452 98/450/449 28/456/452
+f 25/457/453 24/458/454 23/459/454
+f 26/460/453 24/458/454 25/457/453
+f 23/459/454 10/461/455 9/462/455
+f 24/458/454 10/461/455 23/459/454
+f 9/462/455 4/463/456 3/464/457
+f 10/461/455 4/463/456 9/462/455
+f 3/464/457 8/465/458 7/466/459
+f 4/463/456 8/465/458 3/464/457
+f 7/466/459 6/467/460 5/468/460
+f 8/465/458 6/467/460 7/466/459
+f 5/468/460 26/460/453 25/457/453
+f 6/467/460 26/460/453 5/468/460
+f 1328/469/461 1330/470/462 1331/471/462
+f 1327/472/461 1330/470/462 1328/469/461
+f 1331/471/462 1344/473/463 1345/474/463
+f 1330/470/462 1344/473/463 1331/471/462
+f 1345/474/463 1350/475/464 1351/476/464
+f 1344/473/463 1350/475/464 1345/474/463
+f 1351/476/464 1346/477/465 1347/478/465
+f 1350/475/464 1346/477/465 1351/476/464
+f 1347/478/465 1348/479/466 1349/480/466
+f 1346/477/465 1348/479/466 1347/478/465
+f 1349/480/466 1327/472/461 1328/469/461
+f 1348/479/466 1327/472/461 1349/480/466
+f 1257/481/467 1274/482/468 1275/483/468
+f 1256/484/467 1274/482/468 1257/481/467
+f 1275/483/468 1276/485/469 1277/486/469
+f 1274/482/468 1276/485/469 1275/483/468
+f 1277/486/469 1323/487/470 1324/488/470
+f 1276/485/469 1323/487/470 1277/486/469
+f 1324/488/470 1325/489/471 1326/490/471
+f 1323/487/470 1325/489/471 1324/488/470
+f 1326/490/471 1256/484/467 1257/481/467
+f 1325/489/471 1256/484/467 1326/490/471
+f 91/491/472 99/492/473 141/493/474
+f 99/492/473 108/494/475 157/495/476
+f 108/494/475 149/496/477 203/497/478
+f 149/496/477 258/498/479 292/499/480
+f 258/498/479 329/500/481 335/501/482
+f 124/502/483 141/493/474 198/503/484
+f 141/493/474 157/495/476 209/504/485
+f 157/495/476 203/497/478 261/505/486
+f 203/497/478 292/499/480 299/506/487
+f 335/501/482 338/507/488 299/506/487
+f 292/499/480 335/501/482 299/506/487
+f 182/508/489 198/503/484 412/509/490
+f 198/503/484 209/504/485 407/510/491
+f 209/504/485 261/505/486 388/511/492
+f 261/505/486 299/506/487 350/512/493
+f 299/506/487 338/507/488 337/513/494
+f 437/514/495 412/509/490 565/515/496
+f 412/509/490 407/510/491 540/516/497
+f 407/510/491 388/511/492 489/517/498
+f 350/512/493 405/518/499 489/517/498
+f 388/511/492 350/512/493 489/517/498
+f 350/512/493 337/513/494 328/519/500
+f 565/515/496 109/520/501 104/521/502
+f 643/522/503 565/515/496 104/521/502
+f 540/516/497 116/523/504 109/520/501
+f 565/515/496 540/516/497 109/520/501
+f 489/517/498 155/524/505 116/523/504
+f 540/516/497 489/517/498 116/523/504
+f 405/518/499 265/525/506 155/524/505
+f 489/517/498 405/518/499 155/524/505
+f 328/519/500 325/526/507 265/525/506
+f 405/518/499 328/519/500 265/525/506
+f 104/521/502 109/520/501 84/527/508
+f 109/520/501 116/523/504 89/528/509
+f 116/523/504 155/524/505 110/529/510
+f 155/524/505 265/525/506 227/530/511
+f 325/526/507 333/531/512 227/530/511
+f 265/525/506 325/526/507 227/530/511
+f 75/532/513 84/527/508 82/533/514
+f 84/527/508 89/528/509 86/534/515
+f 89/528/509 110/529/510 106/535/516
+f 110/529/510 227/530/511 216/536/517
+f 227/530/511 333/531/512 336/537/518
+f 66/538/519 82/533/514 83/539/520
+f 82/533/514 86/534/515 90/540/521
+f 86/534/515 106/535/516 111/541/522
+f 106/535/516 216/536/517 225/542/523
+f 216/536/517 336/537/518 334/543/524
+f 72/544/525 83/539/520 88/545/526
+f 83/539/520 90/540/521 96/546/527
+f 90/540/521 111/541/522 123/547/528
+f 111/541/522 225/542/523 240/548/529
+f 225/542/523 334/543/524 332/549/530
+f 81/550/531 88/545/526 99/492/473
+f 88/545/526 96/546/527 108/494/475
+f 96/546/527 123/547/528 149/496/477
+f 123/547/528 240/548/529 258/498/479
+f 240/548/529 332/549/530 329/500/481
+f 91/491/472 141/493/474 124/502/483
+f 99/492/473 157/495/476 141/493/474
+f 108/494/475 203/497/478 157/495/476
+f 149/496/477 292/499/480 203/497/478
+f 258/498/479 335/501/482 292/499/480
+f 124/502/483 198/503/484 182/508/489
+f 141/493/474 209/504/485 198/503/484
+f 157/495/476 261/505/486 209/504/485
+f 203/497/478 299/506/487 261/505/486
+f 182/508/489 412/509/490 437/514/495
+f 198/503/484 407/510/491 412/509/490
+f 209/504/485 388/511/492 407/510/491
+f 261/505/486 350/512/493 388/511/492
+f 299/506/487 337/513/494 350/512/493
+f 437/514/495 565/515/496 643/522/503
+f 412/509/490 540/516/497 565/515/496
+f 407/510/491 489/517/498 540/516/497
+f 350/512/493 328/519/500 405/518/499
+f 104/521/502 84/527/508 75/532/513
+f 109/520/501 89/528/509 84/527/508
+f 116/523/504 110/529/510 89/528/509
+f 155/524/505 227/530/511 110/529/510
+f 75/532/513 82/533/514 66/538/519
+f 84/527/508 86/534/515 82/533/514
+f 89/528/509 106/535/516 86/534/515
+f 110/529/510 216/536/517 106/535/516
+f 227/530/511 336/537/518 216/536/517
+f 66/538/519 83/539/520 72/544/525
+f 82/533/514 90/540/521 83/539/520
+f 86/534/515 111/541/522 90/540/521
+f 106/535/516 225/542/523 111/541/522
+f 216/536/517 334/543/524 225/542/523
+f 72/544/525 88/545/526 81/550/531
+f 83/539/520 96/546/527 88/545/526
+f 90/540/521 123/547/528 96/546/527
+f 111/541/522 240/548/529 123/547/528
+f 225/542/523 332/549/530 240/548/529
+f 81/550/531 99/492/473 91/491/472
+f 88/545/526 108/494/475 99/492/473
+f 96/546/527 149/496/477 108/494/475
+f 123/547/528 258/498/479 149/496/477
+f 240/548/529 329/500/481 258/498/479
+f 1261/551/532 1209/552/533 1254/553/534
+f 1254/553/534 1196/554/535 1246/555/536
+f 1246/555/536 1149/556/537 1201/557/538
+f 1201/557/538 1063/558/539 1094/559/540
+f 1094/559/540 1017/560/541 1023/561/542
+f 1226/562/543 1153/563/544 1209/552/533
+f 1209/552/533 1146/564/545 1196/554/535
+f 1196/554/535 1091/565/546 1149/556/537
+f 1149/556/537 1055/566/547 1063/558/539
+f 1017/560/541 1055/566/547 1014/567/548
+f 1063/558/539 1055/566/547 1017/560/541
+f 1169/568/549 941/569/550 1153/563/544
+f 1153/563/544 947/570/551 1146/564/545
+f 1146/564/545 968/571/552 1091/565/546
+f 1091/565/546 1002/572/553 1055/566/547
+f 1055/566/547 1015/573/554 1014/567/548
+f 915/574/555 791/575/556 941/569/550
+f 941/569/550 816/576/557 947/570/551
+f 947/570/551 863/577/558 968/571/552
+f 1002/572/553 863/577/558 949/578/559
+f 968/571/552 863/577/558 1002/572/553
+f 1002/572/553 1024/579/560 1015/573/554
+f 791/575/556 1249/580/561 1245/581/562
+f 706/582/563 1249/580/561 791/575/556
+f 816/576/557 1245/581/562 1239/583/564
+f 791/575/556 1245/581/562 816/576/557
+f 863/577/558 1239/583/564 1197/584/565
+f 816/576/557 1239/583/564 863/577/558
+f 949/578/559 1197/584/565 1088/585/566
+f 863/577/558 1197/584/565 949/578/559
+f 1024/579/560 1088/585/566 1027/586/567
+f 949/578/559 1088/585/566 1024/579/560
+f 1249/580/561 1268/587/568 1245/581/562
+f 1245/581/562 1264/588/569 1239/583/564
+f 1239/583/564 1244/589/570 1197/584/565
+f 1197/584/565 1124/590/571 1088/585/566
+f 1027/586/567 1124/590/571 1019/591/572
+f 1088/585/566 1124/590/571 1027/586/567
+f 1278/592/573 1272/593/574 1268/587/568
+f 1268/587/568 1266/594/575 1264/588/569
+f 1264/588/569 1248/595/576 1244/589/570
+f 1244/589/570 1137/596/577 1124/590/571
+f 1124/590/571 1016/597/578 1019/591/572
+f 1285/598/579 1269/599/580 1272/593/574
+f 1272/593/574 1262/600/581 1266/594/575
+f 1266/594/575 1243/601/582 1248/595/576
+f 1248/595/576 1126/602/583 1137/596/577
+f 1137/596/577 1018/603/584 1016/597/578
+f 1280/604/585 1265/605/586 1269/599/580
+f 1269/599/580 1258/606/587 1262/600/581
+f 1262/600/581 1227/607/588 1243/601/582
+f 1243/601/582 1114/608/589 1126/602/583
+f 1126/602/583 1020/609/590 1018/603/584
+f 1273/610/591 1254/553/534 1265/605/586
+f 1265/605/586 1246/555/536 1258/606/587
+f 1258/606/587 1201/557/538 1227/607/588
+f 1227/607/588 1094/559/540 1114/608/589
+f 1114/608/589 1023/561/542 1020/609/590
+f 1261/551/532 1226/562/543 1209/552/533
+f 1254/553/534 1209/552/533 1196/554/535
+f 1246/555/536 1196/554/535 1149/556/537
+f 1201/557/538 1149/556/537 1063/558/539
+f 1094/559/540 1063/558/539 1017/560/541
+f 1226/562/543 1169/568/549 1153/563/544
+f 1209/552/533 1153/563/544 1146/564/545
+f 1196/554/535 1146/564/545 1091/565/546
+f 1149/556/537 1091/565/546 1055/566/547
+f 1169/568/549 915/574/555 941/569/550
+f 1153/563/544 941/569/550 947/570/551
+f 1146/564/545 947/570/551 968/571/552
+f 1091/565/546 968/571/552 1002/572/553
+f 1055/566/547 1002/572/553 1015/573/554
+f 915/574/555 706/582/563 791/575/556
+f 941/569/550 791/575/556 816/576/557
+f 947/570/551 816/576/557 863/577/558
+f 1002/572/553 949/578/559 1024/579/560
+f 1249/580/561 1278/592/573 1268/587/568
+f 1245/581/562 1268/587/568 1264/588/569
+f 1239/583/564 1264/588/569 1244/589/570
+f 1197/584/565 1244/589/570 1124/590/571
+f 1278/592/573 1285/598/579 1272/593/574
+f 1268/587/568 1272/593/574 1266/594/575
+f 1264/588/569 1266/594/575 1248/595/576
+f 1244/589/570 1248/595/576 1137/596/577
+f 1124/590/571 1137/596/577 1016/597/578
+f 1285/598/579 1280/604/585 1269/599/580
+f 1272/593/574 1269/599/580 1262/600/581
+f 1266/594/575 1262/600/581 1243/601/582
+f 1248/595/576 1243/601/582 1126/602/583
+f 1137/596/577 1126/602/583 1018/603/584
+f 1280/604/585 1273/610/591 1265/605/586
+f 1269/599/580 1265/605/586 1258/606/587
+f 1262/600/581 1258/606/587 1227/607/588
+f 1243/601/582 1227/607/588 1114/608/589
+f 1126/602/583 1114/608/589 1020/609/590
+f 1273/610/591 1261/551/532 1254/553/534
+f 1265/605/586 1254/553/534 1246/555/536
+f 1258/606/587 1246/555/536 1201/557/538
+f 1227/607/588 1201/557/538 1094/559/540
+f 1114/608/589 1094/559/540 1023/561/542
+f 615/611/592 576/612/593 618/613/594
+f 774/614/595 618/613/594 775/615/594
+f 615/611/592 618/613/594 774/614/595
+f 774/614/595 775/615/594 800/616/596
+f 618/613/594 576/612/593 623/617/597
+f 775/615/594 623/617/597 757/618/598
+f 618/613/594 623/617/597 775/615/594
+f 775/615/594 757/618/598 800/616/596
+f 623/617/597 576/612/593 625/619/599
+f 757/618/598 625/619/599 722/620/600
+f 623/617/597 625/619/599 757/618/598
+f 757/618/598 722/620/600 800/616/596
+f 625/619/599 576/612/593 627/621/601
+f 722/620/600 627/621/601 717/622/602
+f 625/619/599 627/621/601 722/620/600
+f 722/620/600 717/622/602 800/616/596
+f 627/621/601 576/612/593 626/623/603
+f 717/622/602 626/623/603 715/624/604
+f 627/621/601 626/623/603 717/622/602
+f 717/622/602 715/624/604 800/616/596
+f 626/623/603 576/612/593 624/625/605
+f 715/624/604 624/625/605 713/626/606
+f 626/623/603 624/625/605 715/624/604
+f 715/624/604 713/626/606 800/616/596
+f 624/625/605 576/612/593 622/627/607
+f 713/626/606 622/627/607 714/628/608
+f 624/625/605 622/627/607 713/626/606
+f 713/626/606 714/628/608 800/616/596
+f 622/627/607 576/612/593 621/629/609
+f 714/628/608 621/629/609 716/630/610
+f 622/627/607 621/629/609 714/628/608
+f 714/628/608 716/630/610 800/616/596
+f 621/629/609 576/612/593 620/631/611
+f 716/630/610 620/631/611 718/632/612
+f 621/629/609 620/631/611 716/630/610
+f 716/630/610 718/632/612 800/616/596
+f 620/631/611 576/612/593 619/633/613
+f 718/632/612 619/633/613 721/634/614
+f 620/631/611 619/633/613 718/632/612
+f 718/632/612 721/634/614 800/616/596
+f 619/633/613 576/612/593 617/635/615
+f 721/634/614 617/635/615 764/636/616
+f 619/633/613 617/635/615 721/634/614
+f 721/634/614 764/636/616 800/616/596
+f 617/635/615 576/612/593 616/637/617
+f 764/636/616 616/637/617 767/638/618
+f 617/635/615 616/637/617 764/636/616
+f 764/636/616 767/638/618 800/616/596
+f 616/637/617 576/612/593 615/611/592
+f 767/638/618 615/611/592 774/614/595
+f 616/637/617 615/611/592 767/638/618
+f 767/638/618 774/614/595 800/616/596
+f 951/639/619 963/640/620 934/641/621
+f 973/642/622 934/641/621 893/643/623
+f 951/639/619 934/641/621 973/642/622
+f 983/644/624 893/643/623 872/645/625
+f 973/642/622 893/643/623 983/644/624
+f 986/646/626 872/645/625 876/647/627
+f 983/644/624 872/645/625 986/646/626
+f 988/648/628 876/647/627 898/649/628
+f 986/646/626 876/647/627 988/648/628
+f 934/641/621 963/640/620 920/650/629
+f 893/643/623 920/650/629 833/651/630
+f 934/641/621 920/650/629 893/643/623
+f 872/645/625 833/651/630 804/652/631
+f 893/643/623 833/651/630 872/645/625
+f 876/647/627 804/652/631 808/653/632
+f 872/645/625 804/652/631 876/647/627
+f 898/649/628 808/653/632 831/654/628
+f 876/647/627 808/653/632 898/649/628
+f 920/650/629 963/640/620 912/655/633
+f 833/651/630 912/655/633 819/656/634
+f 920/650/629 912/655/633 833/651/630
+f 804/652/631 819/656/634 783/657/635
+f 833/651/630 819/656/634 804/652/631
+f 808/653/632 783/657/635 788/658/636
+f 804/652/631 783/657/635 808/653/632
+f 831/654/628 788/658/636 811/659/628
+f 808/653/632 788/658/636 831/654/628
+f 912/655/633 963/640/620 919/660/637
+f 819/656/634 919/660/637 832/661/638
+f 912/655/633 919/660/637 819/656/634
+f 783/657/635 832/661/638 803/662/639
+f 819/656/634 832/661/638 783/657/635
+f 788/658/636 803/662/639 807/663/640
+f 783/657/635 803/662/639 788/658/636
+f 811/659/628 807/663/640 830/664/628
+f 788/658/636 807/663/640 811/659/628
+f 919/660/637 963/640/620 933/665/641
+f 832/661/638 933/665/641 892/666/642
+f 919/660/637 933/665/641 832/661/638
+f 803/662/639 892/666/642 871/667/643
+f 832/661/638 892/666/642 803/662/639
+f 807/663/640 871/667/643 875/668/644
+f 803/662/639 871/667/643 807/663/640
+f 830/664/628 875/668/644 897/669/628
+f 807/663/640 875/668/644 830/664/628
+f 933/665/641 963/640/620 950/670/645
+f 892/666/642 950/670/645 972/671/646
+f 933/665/641 950/670/645 892/666/642
+f 871/667/643 972/671/646 982/672/647
+f 892/666/642 972/671/646 871/667/643
+f 875/668/644 982/672/647 985/673/648
+f 871/667/643 982/672/647 875/668/644
+f 897/669/628 985/673/648 987/674/628
+f 875/668/644 985/673/648 897/669/628
+f 950/670/645 963/640/620 995/675/649
+f 972/671/646 995/675/649 1082/676/650
+f 950/670/645 995/675/649 972/671/646
+f 982/672/647 1082/676/650 1138/677/651
+f 972/671/646 1082/676/650 982/672/647
+f 985/673/648 1138/677/651 1140/678/652
+f 982/672/647 1138/677/651 985/673/648
+f 987/674/628 1140/678/652 1102/679/628
+f 985/673/648 1140/678/652 987/674/628
+f 995/675/649 963/640/620 1006/680/653
+f 1082/676/650 1006/680/653 1165/681/654
+f 995/675/649 1006/680/653 1082/676/650
+f 1138/677/651 1165/681/654 1222/682/655
+f 1082/676/650 1165/681/654 1138/677/651
+f 1140/678/652 1222/682/655 1220/683/656
+f 1138/677/651 1222/682/655 1140/678/652
+f 1102/679/628 1220/683/656 1192/684/657
+f 1140/678/652 1220/683/656 1102/679/628
+f 1006/680/653 963/640/620 1010/685/658
+f 1165/681/654 1010/685/658 1191/686/659
+f 1006/680/653 1010/685/658 1165/681/654
+f 1222/682/655 1191/686/659 1235/687/660
+f 1165/681/654 1191/686/659 1222/682/655
+f 1220/683/656 1235/687/660 1233/688/661
+f 1222/682/655 1235/687/660 1220/683/656
+f 1192/684/657 1233/688/661 1213/689/628
+f 1220/683/656 1233/688/661 1192/684/657
+f 1010/685/658 963/640/620 1007/690/662
+f 1191/686/659 1007/690/662 1166/691/663
+f 1010/685/658 1007/690/662 1191/686/659
+f 1235/687/660 1166/691/663 1223/692/664
+f 1191/686/659 1166/691/663 1235/687/660
+f 1233/688/661 1223/692/664 1221/693/665
+f 1235/687/660 1223/692/664 1233/688/661
+f 1213/689/628 1221/693/665 1193/694/628
+f 1233/688/661 1221/693/665 1213/689/628
+f 1007/690/662 963/640/620 996/695/666
+f 1166/691/663 996/695/666 1083/696/667
+f 1007/690/662 996/695/666 1166/691/663
+f 1223/692/664 1083/696/667 1139/697/668
+f 1166/691/663 1083/696/667 1223/692/664
+f 1221/693/665 1139/697/668 1141/698/669
+f 1223/692/664 1139/697/668 1221/693/665
+f 1193/694/628 1141/698/669 1103/699/628
+f 1221/693/665 1141/698/669 1193/694/628
+f 996/695/666 963/640/620 951/639/619
+f 1083/696/667 951/639/619 973/642/622
+f 996/695/666 951/639/619 1083/696/667
+f 1139/697/668 973/642/622 983/644/624
+f 1083/696/667 973/642/622 1139/697/668
+f 1141/698/669 983/644/624 986/646/626
+f 1139/697/668 983/644/624 1141/698/669
+f 1103/699/628 986/646/626 988/648/628
+f 1141/698/669 986/646/626 1103/699/628
+f 387/700/670 400/701/671 359/702/672
+f 375/703/673 359/702/672 271/704/674
+f 387/700/670 359/702/672 375/703/673
+f 368/705/675 271/704/674 213/706/676
+f 375/703/673 271/704/674 368/705/675
+f 364/707/677 213/706/676 206/708/678
+f 368/705/675 213/706/676 364/707/677
+f 366/709/679 206/708/678 238/710/679
+f 364/707/677 206/708/678 366/709/679
+f 359/702/672 400/701/671 346/711/680
+f 271/704/674 346/711/680 181/712/681
+f 359/702/672 346/711/680 271/704/674
+f 213/706/676 181/712/681 129/713/682
+f 271/704/674 181/712/681 213/706/676
+f 206/708/678 129/713/682 131/714/683
+f 213/706/676 129/713/682 206/708/678
+f 238/710/679 131/714/683 152/715/679
+f 206/708/678 131/714/683 238/710/679
+f 346/711/680 400/701/671 339/716/684
+f 181/712/681 339/716/684 159/717/685
+f 346/711/680 339/716/684 181/712/681
+f 129/713/682 159/717/685 112/718/686
+f 181/712/681 159/717/685 129/713/682
+f 131/714/683 112/718/686 115/719/687
+f 129/713/682 112/718/686 131/714/683
+f 152/715/679 115/719/687 132/720/679
+f 131/714/683 115/719/687 152/715/679
+f 339/716/684 400/701/671 345/721/688
+f 159/717/685 345/721/688 180/722/689
+f 339/716/684 345/721/688 159/717/685
+f 112/718/686 180/722/689 128/723/690
+f 159/717/685 180/722/689 112/718/686
+f 115/719/687 128/723/690 130/724/691
+f 112/718/686 128/723/690 115/719/687
+f 132/720/679 130/724/691 151/725/692
+f 115/719/687 130/724/691 132/720/679
+f 345/721/688 400/701/671 358/726/693
+f 180/722/689 358/726/693 270/727/694
+f 345/721/688 358/726/693 180/722/689
+f 128/723/690 270/727/694 212/728/695
+f 180/722/689 270/727/694 128/723/690
+f 130/724/691 212/728/695 205/729/696
+f 128/723/690 212/728/695 130/724/691
+f 151/725/692 205/729/696 237/730/679
+f 130/724/691 205/729/696 151/725/692
+f 358/726/693 400/701/671 386/731/697
+f 270/727/694 386/731/697 374/732/698
+f 358/726/693 386/731/697 270/727/694
+f 212/728/695 374/732/698 367/733/699
+f 270/727/694 374/732/698 212/728/695
+f 205/729/696 367/733/699 363/734/700
+f 212/728/695 367/733/699 205/729/696
+f 237/730/679 363/734/700 365/735/679
+f 205/729/696 363/734/700 237/730/679
+f 386/731/697 400/701/671 417/736/701
+f 374/732/698 417/736/701 455/737/702
+f 386/731/697 417/736/701 374/732/698
+f 367/733/699 455/737/702 475/738/703
+f 374/732/698 455/737/702 367/733/699
+f 363/734/700 475/738/703 469/739/704
+f 367/733/699 475/738/703 363/734/700
+f 365/735/679 469/739/704 452/740/705
+f 363/734/700 469/739/704 365/735/679
+f 417/736/701 400/701/671 432/741/706
+f 455/737/702 432/741/706 511/742/707
+f 417/736/701 432/741/706 455/737/702
+f 475/738/703 511/742/707 547/743/708
+f 455/737/702 511/742/707 475/738/703
+f 469/739/704 547/743/708 545/744/709
+f 475/738/703 547/743/708 469/739/704
+f 452/740/705 545/744/709 518/745/679
+f 469/739/704 545/744/709 452/740/705
+f 432/741/706 400/701/671 440/746/710
+f 511/742/707 440/746/710 532/747/711
+f 432/741/706 440/746/710 511/742/707
+f 547/743/708 532/747/711 563/748/712
+f 511/742/707 532/747/711 547/743/708
+f 545/744/709 563/748/712 560/749/713
+f 547/743/708 563/748/712 545/744/709
+f 518/745/679 560/749/713 535/750/679
+f 545/744/709 560/749/713 518/745/679
+f 440/746/710 400/701/671 433/751/714
+f 532/747/711 433/751/714 512/752/715
+f 440/746/710 433/751/714 532/747/711
+f 563/748/712 512/752/715 548/753/716
+f 532/747/711 512/752/715 563/748/712
+f 560/749/713 548/753/716 546/754/717
+f 563/748/712 548/753/716 560/749/713
+f 535/750/679 546/754/717 519/755/679
+f 560/749/713 546/754/717 535/750/679
+f 433/751/714 400/701/671 418/756/718
+f 512/752/715 418/756/718 456/757/719
+f 433/751/714 418/756/718 512/752/715
+f 548/753/716 456/757/719 476/758/720
+f 512/752/715 456/757/719 548/753/716
+f 546/754/717 476/758/720 470/759/721
+f 548/753/716 476/758/720 546/754/717
+f 519/755/679 470/759/721 453/760/679
+f 546/754/717 470/759/721 519/755/679
+f 418/756/718 400/701/671 387/700/670
+f 456/757/719 387/700/670 375/703/673
+f 418/756/718 387/700/670 456/757/719
+f 476/758/720 375/703/673 368/705/675
+f 456/757/719 375/703/673 476/758/720
+f 470/759/721 368/705/675 364/707/677
+f 476/758/720 368/705/675 470/759/721
+f 453/760/679 364/707/677 366/709/679
+f 470/759/721 364/707/677 453/760/679
+f 742/761/722 676/762/723 737/763/724
+f 851/764/725 737/763/724 839/765/726
+f 742/761/722 737/763/724 851/764/725
+f 902/766/727 839/765/726 886/767/728
+f 851/764/725 839/765/726 902/766/727
+f 901/768/729 886/767/728 884/769/730
+f 902/766/727 886/767/728 901/768/729
+f 864/770/731 884/769/730 858/771/732
+f 901/768/729 884/769/730 864/770/731
+f 737/763/724 676/762/723 729/772/733
+f 839/765/726 729/772/733 810/773/734
+f 737/763/724 729/772/733 839/765/726
+f 886/767/728 810/773/734 844/774/735
+f 839/765/726 810/773/734 886/767/728
+f 884/769/730 844/774/735 842/775/736
+f 886/767/728 844/774/735 884/769/730
+f 858/771/732 842/775/736 818/776/731
+f 884/769/730 842/775/736 858/771/732
+f 729/772/733 676/762/723 711/777/737
+f 810/773/734 711/777/737 762/778/738
+f 729/772/733 711/777/737 810/773/734
+f 844/774/735 762/778/738 787/779/739
+f 810/773/734 762/778/738 844/774/735
+f 842/775/736 787/779/739 785/780/740
+f 844/774/735 787/779/739 842/775/736
+f 818/776/731 785/780/740 771/781/731
+f 842/775/736 785/780/740 818/776/731
+f 711/777/737 676/762/723 679/782/741
+f 762/778/738 679/782/741 683/783/742
+f 711/777/737 679/782/741 762/778/738
+f 787/779/739 683/783/742 687/784/743
+f 762/778/738 683/783/742 787/779/739
+f 785/780/740 687/784/743 689/785/744
+f 787/779/739 687/784/743 785/780/740
+f 771/781/731 689/785/744 688/786/731
+f 785/780/740 689/785/744 771/781/731
+f 679/782/741 676/762/723 640/787/745
+f 683/783/742 640/787/745 590/788/746
+f 679/782/741 640/787/745 683/783/742
+f 687/784/743 590/788/746 569/789/747
+f 683/783/742 590/788/746 687/784/743
+f 689/785/744 569/789/747 571/790/748
+f 687/784/743 569/789/747 689/785/744
+f 688/786/731 571/790/748 582/791/749
+f 689/785/744 571/790/748 688/786/731
+f 640/787/745 676/762/723 635/792/750
+f 590/788/746 635/792/750 550/793/751
+f 640/787/745 635/792/750 590/788/746
+f 569/789/747 550/793/751 514/794/752
+f 590/788/746 550/793/751 569/789/747
+f 571/790/748 514/794/752 516/795/753
+f 569/789/747 514/794/752 571/790/748
+f 582/791/749 516/795/753 539/796/731
+f 571/790/748 516/795/753 582/791/749
+f 635/792/750 676/762/723 614/797/754
+f 550/793/751 614/797/754 521/798/755
+f 635/792/750 614/797/754 550/793/751
+f 514/794/752 521/798/755 468/799/756
+f 550/793/751 521/798/755 514/794/752
+f 516/795/753 468/799/756 472/800/757
+f 514/794/752 468/799/756 516/795/753
+f 539/796/731 472/800/757 497/801/731
+f 516/795/753 472/800/757 539/796/731
+f 614/797/754 676/762/723 608/802/758
+f 521/798/755 608/802/758 503/803/759
+f 614/797/754 608/802/758 521/798/755
+f 468/799/756 503/803/759 451/804/760
+f 521/798/755 503/803/759 468/799/756
+f 472/800/757 451/804/760 454/805/761
+f 468/799/756 451/804/760 472/800/757
+f 497/801/731 454/805/761 488/806/731
+f 472/800/757 454/805/761 497/801/731
+f 608/802/758 676/762/723 613/807/762
+f 503/803/759 613/807/762 520/808/763
+f 608/802/758 613/807/762 503/803/759
+f 451/804/760 520/808/763 467/809/764
+f 503/803/759 520/808/763 451/804/760
+f 454/805/761 467/809/764 471/810/765
+f 451/804/760 467/809/764 454/805/761
+f 488/806/731 471/810/765 496/811/731
+f 454/805/761 471/810/765 488/806/731
+f 613/807/762 676/762/723 634/812/766
+f 520/808/763 634/812/766 549/813/767
+f 613/807/762 634/812/766 520/808/763
+f 467/809/764 549/813/767 513/814/768
+f 520/808/763 549/813/767 467/809/764
+f 471/810/765 513/814/768 515/815/769
+f 467/809/764 513/814/768 471/810/765
+f 496/811/731 515/815/769 538/816/770
+f 471/810/765 515/815/769 496/811/731
+f 634/812/766 676/762/723 639/817/771
+f 549/813/767 639/817/771 589/818/772
+f 634/812/766 639/817/771 549/813/767
+f 513/814/768 589/818/772 568/819/773
+f 549/813/767 589/818/772 513/814/768
+f 515/815/769 568/819/773 570/820/774
+f 513/814/768 568/819/773 515/815/769
+f 538/816/770 570/820/774 581/821/749
+f 515/815/769 570/820/774 538/816/770
+f 639/817/771 676/762/723 675/822/775
+f 589/818/772 675/822/775 682/823/776
+f 639/817/771 675/822/775 589/818/772
+f 568/819/773 682/823/776 684/824/777
+f 589/818/772 682/823/776 568/819/773
+f 570/820/774 684/824/777 685/825/778
+f 568/819/773 684/824/777 570/820/774
+f 581/821/749 685/825/778 686/826/749
+f 570/820/774 685/825/778 581/821/749
+f 675/822/775 676/762/723 710/827/779
+f 682/823/776 710/827/779 761/828/780
+f 675/822/775 710/827/779 682/823/776
+f 684/824/777 761/828/780 786/829/781
+f 682/823/776 761/828/780 684/824/777
+f 685/825/778 786/829/781 784/830/782
+f 684/824/777 786/829/781 685/825/778
+f 686/826/749 784/830/782 770/831/749
+f 685/825/778 784/830/782 686/826/749
+f 710/827/779 676/762/723 728/832/783
+f 761/828/780 728/832/783 809/833/784
+f 710/827/779 728/832/783 761/828/780
+f 786/829/781 809/833/784 843/834/785
+f 761/828/780 809/833/784 786/829/781
+f 784/830/782 843/834/785 841/835/786
+f 786/829/781 843/834/785 784/830/782
+f 770/831/749 841/835/786 817/836/749
+f 784/830/782 841/835/786 770/831/749
+f 728/832/783 676/762/723 736/837/787
+f 809/833/784 736/837/787 838/838/788
+f 728/832/783 736/837/787 809/833/784
+f 843/834/785 838/838/788 885/839/789
+f 809/833/784 838/838/788 843/834/785
+f 841/835/786 885/839/789 883/840/790
+f 843/834/785 885/839/789 841/835/786
+f 817/836/749 883/840/790 857/841/731
+f 841/835/786 883/840/790 817/836/749
+f 736/837/787 676/762/723 742/761/722
+f 838/838/788 742/761/722 851/764/725
+f 736/837/787 742/761/722 838/838/788
+f 885/839/789 851/764/725 902/766/727
+f 838/838/788 851/764/725 885/839/789
+f 883/840/790 902/766/727 901/768/729
+f 885/839/789 902/766/727 883/840/790
+f 857/841/731 901/768/729 864/770/731
+f 883/840/790 901/768/729 857/841/731
+f 750/842/791 680/843/792 740/844/793
+f 802/845/794 740/844/793 792/846/795
+f 750/842/791 740/844/793 802/845/794
+f 845/847/796 792/846/795 826/848/797
+f 802/845/794 792/846/795 845/847/796
+f 873/849/798 826/848/797 855/850/799
+f 845/847/796 826/848/797 873/849/798
+f 905/851/800 855/850/799 879/852/801
+f 873/849/798 855/850/799 905/851/800
+f 918/853/802 879/852/801 899/854/803
+f 905/851/800 879/852/801 918/853/802
+f 921/855/804 899/854/803 903/856/805
+f 918/853/802 899/854/803 921/855/804
+f 740/844/793 680/843/792 723/857/806
+f 792/846/795 723/857/806 747/858/807
+f 740/844/793 723/857/806 792/846/795
+f 826/848/797 747/858/807 781/859/808
+f 792/846/795 747/858/807 826/848/797
+f 855/850/799 781/859/808 796/860/809
+f 826/848/797 781/859/808 855/850/799
+f 879/852/801 796/860/809 805/861/810
+f 855/850/799 796/860/809 879/852/801
+f 899/854/803 805/861/810 812/862/811
+f 879/852/801 805/861/810 899/854/803
+f 903/856/805 812/862/811 820/863/812
+f 899/854/803 812/862/811 903/856/805
+f 723/857/806 680/843/792 677/864/813
+f 747/858/807 677/864/813 674/865/814
+f 723/857/806 677/864/813 747/858/807
+f 781/859/808 674/865/814 670/866/815
+f 747/858/807 674/865/814 781/859/808
+f 796/860/809 670/866/815 667/867/816
+f 781/859/808 670/866/815 796/860/809
+f 805/861/810 667/867/816 666/868/817
+f 796/860/809 667/867/816 805/861/810
+f 812/862/811 666/868/817 664/869/818
+f 805/861/810 666/868/817 812/862/811
+f 820/863/812 664/869/818 663/870/819
+f 812/862/811 664/869/818 820/863/812
+f 677/864/813 680/843/792 636/871/820
+f 674/865/814 636/871/820 602/872/821
+f 677/864/813 636/871/820 674/865/814
+f 670/866/815 602/872/821 572/873/822
+f 674/865/814 602/872/821 670/866/815
+f 667/867/816 572/873/822 556/874/823
+f 670/866/815 572/873/822 667/867/816
+f 666/868/817 556/874/823 551/875/824
+f 667/867/816 556/874/823 666/868/817
+f 664/869/818 551/875/824 543/876/825
+f 666/868/817 551/875/824 664/869/818
+f 663/870/819 543/876/825 536/877/826
+f 664/869/818 543/876/825 663/870/819
+f 636/871/820 680/843/792 609/878/827
+f 602/872/821 609/878/827 561/879/828
+f 636/871/820 609/878/827 602/872/821
+f 572/873/822 561/879/828 527/880/829
+f 602/872/821 561/879/828 572/873/822
+f 556/874/823 527/880/829 498/881/830
+f 572/873/822 527/880/829 556/874/823
+f 551/875/824 498/881/830 478/882/831
+f 556/874/823 498/881/830 551/875/824
+f 543/876/825 478/882/831 457/883/832
+f 551/875/824 478/882/831 543/876/825
+f 536/877/826 457/883/832 449/884/833
+f 543/876/825 457/883/832 536/877/826
+f 609/878/827 680/843/792 600/885/834
+f 561/879/828 600/885/834 553/886/835
+f 609/878/827 600/885/834 561/879/828
+f 527/880/829 553/886/835 509/887/836
+f 561/879/828 553/886/835 527/880/829
+f 498/881/830 509/887/836 482/888/837
+f 527/880/829 509/887/836 498/881/830
+f 478/882/831 482/888/837 447/889/838
+f 498/881/830 482/888/837 478/882/831
+f 457/883/832 447/889/838 434/890/839
+f 478/882/831 447/889/838 457/883/832
+f 449/884/833 434/890/839 430/891/840
+f 457/883/832 434/890/839 449/884/833
+f 600/885/834 680/843/792 610/892/841
+f 553/886/835 610/892/841 562/893/842
+f 600/885/834 610/892/841 553/886/835
+f 509/887/836 562/893/842 528/894/843
+f 553/886/835 562/893/842 509/887/836
+f 482/888/837 528/894/843 499/895/844
+f 509/887/836 528/894/843 482/888/837
+f 447/889/838 499/895/844 479/896/845
+f 482/888/837 499/895/844 447/889/838
+f 434/890/839 479/896/845 458/897/846
+f 447/889/838 479/896/845 434/890/839
+f 430/891/840 458/897/846 450/898/847
+f 434/890/839 458/897/846 430/891/840
+f 610/892/841 680/843/792 637/899/848
+f 562/893/842 637/899/848 603/900/849
+f 610/892/841 637/899/848 562/893/842
+f 528/894/843 603/900/849 573/901/850
+f 562/893/842 603/900/849 528/894/843
+f 499/895/844 573/901/850 557/902/851
+f 528/894/843 573/901/850 499/895/844
+f 479/896/845 557/902/851 552/903/852
+f 499/895/844 557/902/851 479/896/845
+f 458/897/846 552/903/852 544/904/853
+f 479/896/845 552/903/852 458/897/846
+f 450/898/847 544/904/853 537/905/854
+f 458/897/846 544/904/853 450/898/847
+f 637/899/848 680/843/792 681/906/855
+f 603/900/849 681/906/855 678/907/856
+f 637/899/848 681/906/855 603/900/849
+f 573/901/850 678/907/856 672/908/857
+f 603/900/849 678/907/856 573/901/850
+f 557/902/851 672/908/857 673/909/858
+f 573/901/850 672/908/857 557/902/851
+f 552/903/852 673/909/858 671/910/859
+f 557/902/851 673/909/858 552/903/852
+f 544/904/853 671/910/859 669/911/860
+f 552/903/852 671/910/859 544/904/853
+f 537/905/854 669/911/860 665/912/861
+f 544/904/853 669/911/860 537/905/854
+f 681/906/855 680/843/792 724/913/862
+f 678/907/856 724/913/862 748/914/863
+f 681/906/855 724/913/862 678/907/856
+f 672/908/857 748/914/863 782/915/864
+f 678/907/856 748/914/863 672/908/857
+f 673/909/858 782/915/864 797/916/865
+f 672/908/857 782/915/864 673/909/858
+f 671/910/859 797/916/865 806/917/866
+f 673/909/858 797/916/865 671/910/859
+f 669/911/860 806/917/866 813/918/867
+f 671/910/859 806/917/866 669/911/860
+f 665/912/861 813/918/867 821/919/868
+f 669/911/860 813/918/867 665/912/861
+f 724/913/862 680/843/792 741/920/869
+f 748/914/863 741/920/869 793/921/870
+f 724/913/862 741/920/869 748/914/863
+f 782/915/864 793/921/870 827/922/871
+f 748/914/863 793/921/870 782/915/864
+f 797/916/865 827/922/871 856/923/872
+f 782/915/864 827/922/871 797/916/865
+f 806/917/866 856/923/872 880/924/873
+f 797/916/865 856/923/872 806/917/866
+f 813/918/867 880/924/873 900/925/874
+f 806/917/866 880/924/873 813/918/867
+f 821/919/868 900/925/874 904/926/875
+f 813/918/867 900/925/874 821/919/868
+f 741/920/869 680/843/792 750/842/791
+f 793/921/870 750/842/791 802/845/794
+f 741/920/869 750/842/791 793/921/870
+f 827/922/871 802/845/794 845/847/796
+f 793/921/870 802/845/794 827/922/871
+f 856/923/872 845/847/796 873/849/798
+f 827/922/871 845/847/796 856/923/872
+f 880/924/873 873/849/798 905/851/800
+f 856/923/872 873/849/798 880/924/873
+f 900/925/874 905/851/800 918/853/802
+f 880/924/873 905/851/800 900/925/874
+f 904/926/875 918/853/802 921/855/804
+f 900/925/874 918/853/802 904/926/875
+f 444/927/876 403/928/877 438/929/878
+f 484/930/879 438/929/878 473/931/880
+f 444/927/876 438/929/878 484/930/879
+f 534/932/881 473/931/880 522/933/882
+f 484/930/879 473/931/880 534/932/881
+f 555/934/883 522/933/882 541/935/884
+f 534/932/881 522/933/882 555/934/883
+f 588/936/885 541/935/884 558/937/886
+f 555/934/883 541/935/884 588/936/885
+f 628/938/887 558/937/886 574/939/888
+f 588/936/885 558/937/886 628/938/887
+f 631/940/889 574/939/888 583/941/890
+f 628/938/887 574/939/888 631/940/889
+f 438/929/878 403/928/877 423/942/891
+f 473/931/880 423/942/891 443/943/892
+f 438/929/878 423/942/891 473/931/880
+f 522/933/882 443/943/892 461/944/893
+f 473/931/880 443/943/892 522/933/882
+f 541/935/884 461/944/893 480/945/894
+f 522/933/882 461/944/893 541/935/884
+f 558/937/886 480/945/894 490/946/895
+f 541/935/884 480/945/894 558/937/886
+f 574/939/888 490/946/895 494/947/896
+f 558/937/886 490/946/895 574/939/888
+f 583/941/890 494/947/896 505/948/897
+f 574/939/888 494/947/896 583/941/890
+f 423/942/891 403/928/877 401/949/898
+f 443/943/892 401/949/898 397/950/899
+f 423/942/891 401/949/898 443/943/892
+f 461/944/893 397/950/899 394/951/900
+f 443/943/892 397/950/899 461/944/893
+f 480/945/894 394/951/900 393/952/901
+f 461/944/893 394/951/900 480/945/894
+f 490/946/895 393/952/901 392/953/902
+f 480/945/894 393/952/901 490/946/895
+f 494/947/896 392/953/902 390/954/903
+f 490/946/895 392/953/902 494/947/896
+f 505/948/897 390/954/903 389/955/904
+f 494/947/896 390/954/903 505/948/897
+f 401/949/898 403/928/877 356/956/905
+f 397/950/899 356/956/905 322/957/906
+f 401/949/898 356/956/905 397/950/899
+f 394/951/900 322/957/906 290/958/907
+f 397/950/899 322/957/906 394/951/900
+f 393/952/901 290/958/907 259/959/908
+f 394/951/900 290/958/907 393/952/901
+f 392/953/902 259/959/908 229/960/909
+f 393/952/901 259/959/908 392/953/902
+f 390/954/903 229/960/909 214/961/910
+f 392/953/902 229/960/909 390/954/903
+f 389/955/904 214/961/910 210/962/911
+f 390/954/903 214/961/910 389/955/904
+f 356/956/905 403/928/877 343/963/912
+f 322/957/906 343/963/912 266/964/913
+f 356/956/905 343/963/912 322/957/906
+f 290/958/907 266/964/913 194/965/914
+f 322/957/906 266/964/913 290/958/907
+f 259/959/908 194/965/914 161/966/915
+f 290/958/907 194/965/914 259/959/908
+f 229/960/909 161/966/915 134/967/916
+f 259/959/908 161/966/915 229/960/909
+f 214/961/910 134/967/916 126/968/917
+f 229/960/909 134/967/916 214/961/910
+f 210/962/911 126/968/917 121/969/918
+f 214/961/910 126/968/917 210/962/911
+f 343/963/912 403/928/877 318/970/919
+f 266/964/913 318/970/919 250/971/920
+f 343/963/912 318/970/919 266/964/913
+f 194/965/914 250/971/920 173/972/921
+f 266/964/913 250/971/920 194/965/914
+f 161/966/915 173/972/921 140/973/922
+f 194/965/914 173/972/921 161/966/915
+f 134/967/916 140/973/922 120/974/923
+f 161/966/915 140/973/922 134/967/916
+f 126/968/917 120/974/923 107/975/924
+f 134/967/916 120/974/923 126/968/917
+f 121/969/918 107/975/924 103/976/925
+f 126/968/917 107/975/924 121/969/918
+f 318/970/919 403/928/877 344/977/926
+f 250/971/920 344/977/926 267/978/927
+f 318/970/919 344/977/926 250/971/920
+f 173/972/921 267/978/927 195/979/928
+f 250/971/920 267/978/927 173/972/921
+f 140/973/922 195/979/928 162/980/929
+f 173/972/921 195/979/928 140/973/922
+f 120/974/923 162/980/929 135/981/930
+f 140/973/922 162/980/929 120/974/923
+f 107/975/924 135/981/930 127/982/931
+f 120/974/923 135/981/930 107/975/924
+f 103/976/925 127/982/931 122/983/932
+f 107/975/924 127/982/931 103/976/925
+f 344/977/926 403/928/877 357/984/933
+f 267/978/927 357/984/933 323/985/934
+f 344/977/926 357/984/933 267/978/927
+f 195/979/928 323/985/934 291/986/935
+f 267/978/927 323/985/934 195/979/928
+f 162/980/929 291/986/935 260/987/936
+f 195/979/928 291/986/935 162/980/929
+f 135/981/930 260/987/936 230/988/937
+f 162/980/929 260/987/936 135/981/930
+f 127/982/931 230/988/937 215/989/938
+f 135/981/930 230/988/937 127/982/931
+f 122/983/932 215/989/938 211/990/939
+f 127/982/931 215/989/938 122/983/932
+f 357/984/933 403/928/877 404/991/940
+f 323/985/934 404/991/940 402/992/941
+f 357/984/933 404/991/940 323/985/934
+f 291/986/935 402/992/941 398/993/942
+f 323/985/934 402/992/941 291/986/935
+f 260/987/936 398/993/942 399/994/943
+f 291/986/935 398/993/942 260/987/936
+f 230/988/937 399/994/943 396/995/944
+f 260/987/936 399/994/943 230/988/937
+f 215/989/938 396/995/944 395/996/945
+f 230/988/937 396/995/944 215/989/938
+f 211/990/939 395/996/945 391/997/946
+f 215/989/938 395/996/945 211/990/939
+f 404/991/940 403/928/877 424/998/947
+f 402/992/941 424/998/947 442/999/948
+f 404/991/940 424/998/947 402/992/941
+f 398/993/942 442/999/948 462/1000/949
+f 402/992/941 442/999/948 398/993/942
+f 399/994/943 462/1000/949 481/1001/950
+f 398/993/942 462/1000/949 399/994/943
+f 396/995/944 481/1001/950 491/1002/951
+f 399/994/943 481/1001/950 396/995/944
+f 395/996/945 491/1002/951 495/1003/952
+f 396/995/944 491/1002/951 395/996/945
+f 391/997/946 495/1003/952 506/1004/953
+f 395/996/945 495/1003/952 391/997/946
+f 424/998/947 403/928/877 439/1005/954
+f 442/999/948 439/1005/954 474/1006/955
+f 424/998/947 439/1005/954 442/999/948
+f 462/1000/949 474/1006/955 523/1007/956
+f 442/999/948 474/1006/955 462/1000/949
+f 481/1001/950 523/1007/956 542/1008/957
+f 462/1000/949 523/1007/956 481/1001/950
+f 491/1002/951 542/1008/957 559/1009/958
+f 481/1001/950 542/1008/957 491/1002/951
+f 495/1003/952 559/1009/958 575/1010/959
+f 491/1002/951 559/1009/958 495/1003/952
+f 506/1004/953 575/1010/959 584/1011/960
+f 495/1003/952 575/1010/959 506/1004/953
+f 439/1005/954 403/928/877 444/927/876
+f 474/1006/955 444/927/876 484/930/879
+f 439/1005/954 444/927/876 474/1006/955
+f 523/1007/956 484/930/879 534/932/881
+f 474/1006/955 484/930/879 523/1007/956
+f 542/1008/957 534/932/881 555/934/883
+f 523/1007/956 534/932/881 542/1008/957
+f 559/1009/958 555/934/883 588/936/885
+f 542/1008/957 555/934/883 559/1009/958
+f 575/1010/959 588/936/885 628/938/887
+f 559/1009/958 588/936/885 575/1010/959
+f 584/1011/960 628/938/887 631/940/889
+f 575/1010/959 628/938/887 584/1011/960
+f 1035/1012/961 966/1013/877 1008/1014/878
+f 1104/1015/879 1008/1014/878 1086/1016/880
+f 1035/1012/961 1008/1014/878 1104/1015/879
+f 1179/1017/962 1086/1016/880 1156/1018/882
+f 1104/1015/879 1086/1016/880 1179/1017/962
+f 1210/1019/883 1156/1018/882 1188/1020/884
+f 1179/1017/962 1156/1018/882 1210/1019/883
+f 1232/1021/885 1188/1020/884 1215/1022/886
+f 1210/1019/883 1188/1020/884 1232/1021/885
+f 1247/1023/887 1215/1022/886 1225/1024/888
+f 1232/1021/885 1215/1022/886 1247/1023/887
+f 1251/1025/889 1225/1024/888 1229/1026/963
+f 1247/1023/887 1225/1024/888 1251/1025/889
+f 1008/1014/878 966/1013/877 997/1027/964
+f 1086/1016/880 997/1027/964 1029/1028/892
+f 1008/1014/878 997/1027/964 1086/1016/880
+f 1156/1018/882 1029/1028/892 1064/1029/965
+f 1086/1016/880 1029/1028/892 1156/1018/882
+f 1188/1020/884 1064/1029/965 1092/1030/894
+f 1156/1018/882 1064/1029/965 1188/1020/884
+f 1215/1022/886 1092/1030/894 1122/1031/895
+f 1188/1020/884 1092/1030/894 1215/1022/886
+f 1225/1024/888 1122/1031/895 1142/1032/896
+f 1215/1022/886 1122/1031/895 1225/1024/888
+f 1229/1026/963 1142/1032/896 1144/1033/897
+f 1225/1024/888 1142/1032/896 1229/1026/963
+f 997/1027/964 966/1013/877 964/1034/966
+f 1029/1028/892 964/1034/966 960/1035/899
+f 997/1027/964 964/1034/966 1029/1028/892
+f 1064/1029/965 960/1035/899 957/1036/900
+f 1029/1028/892 960/1035/899 1064/1029/965
+f 1092/1030/894 957/1036/900 956/1037/901
+f 1064/1029/965 957/1036/900 1092/1030/894
+f 1122/1031/895 956/1037/901 955/1038/902
+f 1092/1030/894 956/1037/901 1122/1031/895
+f 1142/1032/896 955/1038/902 953/1039/903
+f 1122/1031/895 955/1038/902 1142/1032/896
+f 1144/1033/897 953/1039/903 952/1040/967
+f 1142/1032/896 953/1039/903 1144/1033/897
+f 964/1034/966 966/1013/877 926/1041/905
+f 960/1035/899 926/1041/905 910/1042/906
+f 964/1034/966 926/1041/905 960/1035/899
+f 957/1036/900 910/1042/906 890/1043/968
+f 960/1035/899 910/1042/906 957/1036/900
+f 956/1037/901 890/1043/968 877/1044/908
+f 957/1036/900 890/1043/968 956/1037/901
+f 955/1038/902 877/1044/908 861/1045/909
+f 956/1037/901 877/1044/908 955/1038/902
+f 953/1039/903 861/1045/909 859/1046/910
+f 955/1038/902 861/1045/909 953/1039/903
+f 952/1040/967 859/1046/910 848/1047/911
+f 953/1039/903 859/1046/910 952/1040/967
+f 926/1041/905 966/1013/877 913/1048/912
+f 910/1042/906 913/1048/912 881/1049/969
+f 926/1041/905 913/1048/912 910/1042/906
+f 890/1043/968 881/1049/969 836/1050/970
+f 910/1042/906 881/1049/969 890/1043/968
+f 877/1044/908 836/1050/970 814/1051/915
+f 890/1043/968 836/1050/970 877/1044/908
+f 861/1045/909 814/1051/915 794/1052/916
+f 877/1044/908 814/1051/915 861/1045/909
+f 859/1046/910 794/1052/916 779/1053/971
+f 861/1045/909 794/1052/916 859/1046/910
+f 848/1047/911 779/1053/971 768/1054/918
+f 859/1046/910 779/1053/971 848/1047/911
+f 913/1048/912 966/1013/877 909/1055/919
+f 881/1049/969 909/1055/919 869/1056/920
+f 913/1048/912 909/1055/919 881/1049/969
+f 836/1050/970 869/1056/920 822/1057/921
+f 881/1049/969 869/1056/920 836/1050/970
+f 814/1051/915 822/1057/921 798/1058/922
+f 836/1050/970 822/1057/921 814/1051/915
+f 794/1052/916 798/1058/922 763/1059/972
+f 814/1051/915 798/1058/922 794/1052/916
+f 779/1053/971 763/1059/972 735/1060/924
+f 794/1052/916 763/1059/972 779/1053/971
+f 768/1054/918 735/1060/924 732/1061/925
+f 779/1053/971 735/1060/924 768/1054/918
+f 909/1055/919 966/1013/877 914/1062/973
+f 869/1056/920 914/1062/973 882/1063/927
+f 909/1055/919 914/1062/973 869/1056/920
+f 822/1057/921 882/1063/927 837/1064/928
+f 869/1056/920 882/1063/927 822/1057/921
+f 798/1058/922 837/1064/928 815/1065/929
+f 822/1057/921 837/1064/928 798/1058/922
+f 763/1059/972 815/1065/929 795/1066/930
+f 798/1058/922 815/1065/929 763/1059/972
+f 735/1060/924 795/1066/930 780/1067/931
+f 763/1059/972 795/1066/930 735/1060/924
+f 732/1061/925 780/1067/931 769/1068/932
+f 735/1060/924 780/1067/931 732/1061/925
+f 914/1062/973 966/1013/877 927/1069/933
+f 882/1063/927 927/1069/933 911/1070/934
+f 914/1062/973 927/1069/933 882/1063/927
+f 837/1064/928 911/1070/934 891/1071/974
+f 882/1063/927 911/1070/934 837/1064/928
+f 815/1065/929 891/1071/974 878/1072/936
+f 837/1064/928 891/1071/974 815/1065/929
+f 795/1066/930 878/1072/936 862/1073/937
+f 815/1065/929 878/1072/936 795/1066/930
+f 780/1067/931 862/1073/937 860/1074/975
+f 795/1066/930 862/1073/937 780/1067/931
+f 769/1068/932 860/1074/975 849/1075/939
+f 780/1067/931 860/1074/975 769/1068/932
+f 927/1069/933 966/1013/877 967/1076/976
+f 911/1070/934 967/1076/976 965/1077/977
+f 927/1069/933 967/1076/976 911/1070/934
+f 891/1071/974 965/1077/977 961/1078/942
+f 911/1070/934 965/1077/977 891/1071/974
+f 878/1072/936 961/1078/942 962/1079/943
+f 891/1071/974 961/1078/942 878/1072/936
+f 862/1073/937 962/1079/943 959/1080/944
+f 878/1072/936 962/1079/943 862/1073/937
+f 860/1074/975 959/1080/944 958/1081/945
+f 862/1073/937 959/1080/944 860/1074/975
+f 849/1075/939 958/1081/945 954/1082/946
+f 860/1074/975 958/1081/945 849/1075/939
+f 967/1076/976 966/1013/877 998/1083/978
+f 965/1077/977 998/1083/978 1028/1084/948
+f 967/1076/976 998/1083/978 965/1077/977
+f 961/1078/942 1028/1084/948 1065/1085/949
+f 965/1077/977 1028/1084/948 961/1078/942
+f 962/1079/943 1065/1085/949 1093/1086/979
+f 961/1078/942 1065/1085/949 962/1079/943
+f 959/1080/944 1093/1086/979 1123/1087/951
+f 962/1079/943 1093/1086/979 959/1080/944
+f 958/1081/945 1123/1087/951 1143/1088/952
+f 959/1080/944 1123/1087/951 958/1081/945
+f 954/1082/946 1143/1088/952 1145/1089/953
+f 958/1081/945 1143/1088/952 954/1082/946
+f 998/1083/978 966/1013/877 1009/1090/954
+f 1028/1084/948 1009/1090/954 1087/1091/955
+f 998/1083/978 1009/1090/954 1028/1084/948
+f 1065/1085/949 1087/1091/955 1157/1092/980
+f 1028/1084/948 1087/1091/955 1065/1085/949
+f 1093/1086/979 1157/1092/980 1189/1093/957
+f 1065/1085/949 1157/1092/980 1093/1086/979
+f 1123/1087/951 1189/1093/957 1216/1094/958
+f 1093/1086/979 1189/1093/957 1123/1087/951
+f 1143/1088/952 1216/1094/958 1224/1095/959
+f 1123/1087/951 1216/1094/958 1143/1088/952
+f 1145/1089/953 1224/1095/959 1230/1096/981
+f 1143/1088/952 1224/1095/959 1145/1089/953
+f 1009/1090/954 966/1013/877 1035/1012/961
+f 1087/1091/955 1035/1012/961 1104/1015/879
+f 1009/1090/954 1035/1012/961 1087/1091/955
+f 1157/1092/980 1104/1015/879 1179/1017/962
+f 1087/1091/955 1104/1015/879 1157/1092/980
+f 1189/1093/957 1179/1017/962 1210/1019/883
+f 1157/1092/980 1179/1017/962 1189/1093/957
+f 1216/1094/958 1210/1019/883 1232/1021/885
+f 1189/1093/957 1210/1019/883 1216/1094/958
+f 1224/1095/959 1232/1021/885 1247/1023/887
+f 1216/1094/958 1232/1021/885 1224/1095/959
+f 1230/1096/981 1247/1023/887 1251/1025/889
+f 1224/1095/959 1247/1023/887 1230/1096/981
+f 1096/1097/982 1079/1098/983 1090/1099/984
+f 1136/1100/985 1090/1099/984 1100/1101/986
+f 1096/1097/982 1090/1099/984 1136/1100/985
+f 1152/1102/987 1100/1101/986 1119/1103/988
+f 1136/1100/985 1100/1101/986 1152/1102/987
+f 1168/1104/989 1119/1103/988 1134/1105/990
+f 1152/1102/987 1119/1103/988 1168/1104/989
+f 1090/1099/984 1079/1098/983 1078/1106/991
+f 1100/1101/986 1078/1106/991 1076/1107/992
+f 1090/1099/984 1078/1106/991 1100/1101/986
+f 1119/1103/988 1076/1107/992 1073/1108/993
+f 1100/1101/986 1076/1107/992 1119/1103/988
+f 1134/1105/990 1073/1108/993 1071/1109/994
+f 1119/1103/988 1073/1108/993 1134/1105/990
+f 1078/1106/991 1079/1098/983 1059/1110/995
+f 1076/1107/992 1059/1110/995 1050/1111/996
+f 1078/1106/991 1059/1110/995 1076/1107/992
+f 1073/1108/993 1050/1111/996 1037/1112/997
+f 1076/1107/992 1050/1111/996 1073/1108/993
+f 1071/1109/994 1037/1112/997 1026/1113/998
+f 1073/1108/993 1037/1112/997 1071/1109/994
+f 1059/1110/995 1079/1098/983 1054/1114/999
+f 1050/1111/996 1054/1114/999 1022/1115/1000
+f 1059/1110/995 1054/1114/999 1050/1111/996
+f 1037/1112/997 1022/1115/1000 1000/1116/1001
+f 1050/1111/996 1022/1115/1000 1037/1112/997
+f 1026/1113/998 1000/1116/1001 994/1117/1002
+f 1037/1112/997 1000/1116/1001 1026/1113/998
+f 1054/1114/999 1079/1098/983 1048/1118/1003
+f 1022/1115/1000 1048/1118/1003 1004/1119/1004
+f 1054/1114/999 1048/1118/1003 1022/1115/1000
+f 1000/1116/1001 1004/1119/1004 990/1120/1005
+f 1022/1115/1000 1004/1119/1004 1000/1116/1001
+f 994/1117/1002 990/1120/1005 971/1121/1006
+f 1000/1116/1001 990/1120/1005 994/1117/1002
+f 1048/1118/1003 1079/1098/983 1042/1122/1007
+f 1004/1119/1004 1042/1122/1007 1001/1123/1008
+f 1048/1118/1003 1042/1122/1007 1004/1119/1004
+f 990/1120/1005 1001/1123/1008 981/1124/1009
+f 1004/1119/1004 1001/1123/1008 990/1120/1005
+f 971/1121/1006 981/1124/1009 948/1125/1010
+f 990/1120/1005 981/1124/1009 971/1121/1006
+f 1042/1122/1007 1079/1098/983 1047/1126/1011
+f 1001/1123/1008 1047/1126/1011 1003/1127/1012
+f 1042/1122/1007 1047/1126/1011 1001/1123/1008
+f 981/1124/1009 1003/1127/1012 989/1128/1013
+f 1001/1123/1008 1003/1127/1012 981/1124/1009
+f 948/1125/1010 989/1128/1013 970/1129/1014
+f 981/1124/1009 989/1128/1013 948/1125/1010
+f 1047/1126/1011 1079/1098/983 1053/1130/1015
+f 1003/1127/1012 1053/1130/1015 1021/1131/1016
+f 1047/1126/1011 1053/1130/1015 1003/1127/1012
+f 989/1128/1013 1021/1131/1016 999/1132/1017
+f 1003/1127/1012 1021/1131/1016 989/1128/1013
+f 970/1129/1014 999/1132/1017 993/1133/1018
+f 989/1128/1013 999/1132/1017 970/1129/1014
+f 1053/1130/1015 1079/1098/983 1058/1134/1019
+f 1021/1131/1016 1058/1134/1019 1049/1135/1020
+f 1053/1130/1015 1058/1134/1019 1021/1131/1016
+f 999/1132/1017 1049/1135/1020 1036/1136/1021
+f 1021/1131/1016 1049/1135/1020 999/1132/1017
+f 993/1133/1018 1036/1136/1021 1025/1137/1022
+f 999/1132/1017 1036/1136/1021 993/1133/1018
+f 1058/1134/1019 1079/1098/983 1077/1138/1023
+f 1049/1135/1020 1077/1138/1023 1075/1139/1024
+f 1058/1134/1019 1077/1138/1023 1049/1135/1020
+f 1036/1136/1021 1075/1139/1024 1072/1140/1025
+f 1049/1135/1020 1075/1139/1024 1036/1136/1021
+f 1025/1137/1022 1072/1140/1025 1070/1141/1026
+f 1036/1136/1021 1072/1140/1025 1025/1137/1022
+f 1077/1138/1023 1079/1098/983 1089/1142/1027
+f 1075/1139/1024 1089/1142/1027 1099/1143/1028
+f 1077/1138/1023 1089/1142/1027 1075/1139/1024
+f 1072/1140/1025 1099/1143/1028 1118/1144/1029
+f 1075/1139/1024 1099/1143/1028 1072/1140/1025
+f 1070/1141/1026 1118/1144/1029 1133/1145/1030
+f 1072/1140/1025 1118/1144/1029 1070/1141/1026
+f 1089/1142/1027 1079/1098/983 1095/1146/1031
+f 1099/1143/1028 1095/1146/1031 1135/1147/1032
+f 1089/1142/1027 1095/1146/1031 1099/1143/1028
+f 1118/1144/1029 1135/1147/1032 1151/1148/1033
+f 1099/1143/1028 1135/1147/1032 1118/1144/1029
+f 1133/1145/1030 1151/1148/1033 1167/1149/1034
+f 1118/1144/1029 1151/1148/1033 1133/1145/1030
+f 1095/1146/1031 1079/1098/983 1105/1150/1035
+f 1135/1147/1032 1105/1150/1035 1147/1151/1036
+f 1095/1146/1031 1105/1150/1035 1135/1147/1032
+f 1151/1148/1033 1147/1151/1036 1171/1152/1037
+f 1135/1147/1032 1147/1151/1036 1151/1148/1033
+f 1167/1149/1034 1171/1152/1037 1185/1153/1038
+f 1151/1148/1033 1171/1152/1037 1167/1149/1034
+f 1105/1150/1035 1079/1098/983 1113/1154/1039
+f 1147/1151/1036 1113/1154/1039 1150/1155/1040
+f 1105/1150/1035 1113/1154/1039 1147/1151/1036
+f 1171/1152/1037 1150/1155/1040 1175/1156/1041
+f 1147/1151/1036 1150/1155/1040 1171/1152/1037
+f 1185/1153/1038 1175/1156/1041 1194/1157/1042
+f 1171/1152/1037 1175/1156/1041 1185/1153/1038
+f 1113/1154/1039 1079/1098/983 1106/1158/1043
+f 1150/1155/1040 1106/1158/1043 1148/1159/1044
+f 1113/1154/1039 1106/1158/1043 1150/1155/1040
+f 1175/1156/1041 1148/1159/1044 1172/1160/1045
+f 1150/1155/1040 1148/1159/1044 1175/1156/1041
+f 1194/1157/1042 1172/1160/1045 1186/1161/1046
+f 1175/1156/1041 1172/1160/1045 1194/1157/1042
+f 1106/1158/1043 1079/1098/983 1096/1097/982
+f 1148/1159/1044 1096/1097/982 1136/1100/985
+f 1106/1158/1043 1096/1097/982 1148/1159/1044
+f 1172/1160/1045 1136/1100/985 1152/1102/987
+f 1148/1159/1044 1136/1100/985 1172/1160/1045
+f 1186/1161/1046 1152/1102/987 1168/1104/989
+f 1172/1160/1045 1152/1102/987 1186/1161/1046
+f 301/1162/1047 284/1163/1048 296/1164/984
+f 331/1165/985 296/1164/984 305/1166/1049
+f 301/1162/1047 296/1164/984 331/1165/985
+f 355/1167/1050 305/1166/1049 315/1168/988
+f 331/1165/985 305/1166/1049 355/1167/1050
+f 361/1169/989 315/1168/988 327/1170/1051
+f 355/1167/1050 315/1168/988 361/1169/989
+f 296/1164/984 284/1163/1048 283/1171/991
+f 305/1166/1049 283/1171/991 281/1172/992
+f 296/1164/984 283/1171/991 305/1166/1049
+f 315/1168/988 281/1172/992 278/1173/993
+f 305/1166/1049 281/1172/992 315/1168/988
+f 327/1170/1051 278/1173/993 276/1174/994
+f 315/1168/988 278/1173/993 327/1170/1051
+f 283/1171/991 284/1163/1048 263/1175/995
+f 281/1172/992 263/1175/995 253/1176/1052
+f 283/1171/991 263/1175/995 281/1172/992
+f 278/1173/993 253/1176/1052 231/1177/1053
+f 281/1172/992 253/1176/1052 278/1173/993
+f 276/1174/994 231/1177/1053 220/1178/998
+f 278/1173/993 231/1177/1053 276/1174/994
+f 263/1175/995 284/1163/1048 257/1179/999
+f 253/1176/1052 257/1179/999 218/1180/1000
+f 263/1175/995 257/1179/999 253/1176/1052
+f 231/1177/1053 218/1180/1000 199/1181/1054
+f 253/1176/1052 218/1180/1000 231/1177/1053
+f 220/1178/998 199/1181/1054 186/1182/1055
+f 231/1177/1053 199/1181/1054 220/1178/998
+f 257/1179/999 284/1163/1048 249/1183/1003
+f 218/1180/1000 249/1183/1003 208/1184/1056
+f 257/1179/999 249/1183/1003 218/1180/1000
+f 199/1181/1054 208/1184/1056 178/1185/1005
+f 218/1180/1000 208/1184/1056 199/1181/1054
+f 186/1182/1055 178/1185/1005 166/1186/1006
+f 199/1181/1054 178/1185/1005 186/1182/1055
+f 249/1183/1003 284/1163/1048 241/1187/1057
+f 208/1184/1056 241/1187/1057 201/1188/1058
+f 249/1183/1003 241/1187/1057 208/1184/1056
+f 178/1185/1005 201/1188/1058 176/1189/1009
+f 208/1184/1056 201/1188/1058 178/1185/1005
+f 166/1186/1006 176/1189/1009 160/1190/1010
+f 178/1185/1005 176/1189/1009 166/1186/1006
+f 241/1187/1057 284/1163/1048 248/1191/1011
+f 201/1188/1058 248/1191/1011 207/1192/1012
+f 241/1187/1057 248/1191/1011 201/1188/1058
+f 176/1189/1009 207/1192/1012 177/1193/1059
+f 201/1188/1058 207/1192/1012 176/1189/1009
+f 160/1190/1010 177/1193/1059 165/1194/1014
+f 176/1189/1009 177/1193/1059 160/1190/1010
+f 248/1191/1011 284/1163/1048 256/1195/1015
+f 207/1192/1012 256/1195/1015 217/1196/1016
+f 248/1191/1011 256/1195/1015 207/1192/1012
+f 177/1193/1059 217/1196/1016 200/1197/1060
+f 207/1192/1012 217/1196/1016 177/1193/1059
+f 165/1194/1014 200/1197/1060 185/1198/1018
+f 177/1193/1059 200/1197/1060 165/1194/1014
+f 256/1195/1015 284/1163/1048 262/1199/1019
+f 217/1196/1016 262/1199/1019 252/1200/1020
+f 256/1195/1015 262/1199/1019 217/1196/1016
+f 200/1197/1060 252/1200/1020 232/1201/1061
+f 217/1196/1016 252/1200/1020 200/1197/1060
+f 185/1198/1018 232/1201/1061 219/1202/1022
+f 200/1197/1060 232/1201/1061 185/1198/1018
+f 262/1199/1019 284/1163/1048 282/1203/1023
+f 252/1200/1020 282/1203/1023 280/1204/1024
+f 262/1199/1019 282/1203/1023 252/1200/1020
+f 232/1201/1061 280/1204/1024 277/1205/1062
+f 252/1200/1020 280/1204/1024 232/1201/1061
+f 219/1202/1022 277/1205/1062 275/1206/1063
+f 232/1201/1061 277/1205/1062 219/1202/1022
+f 282/1203/1023 284/1163/1048 295/1207/1027
+f 280/1204/1024 295/1207/1027 304/1208/1064
+f 282/1203/1023 295/1207/1027 280/1204/1024
+f 277/1205/1062 304/1208/1064 314/1209/1029
+f 280/1204/1024 304/1208/1064 277/1205/1062
+f 275/1206/1063 314/1209/1029 326/1210/1065
+f 277/1205/1062 314/1209/1029 275/1206/1063
+f 295/1207/1027 284/1163/1048 300/1211/1066
+f 304/1208/1064 300/1211/1066 330/1212/1032
+f 295/1207/1027 300/1211/1066 304/1208/1064
+f 314/1209/1029 330/1212/1032 354/1213/1033
+f 304/1208/1064 330/1212/1032 314/1209/1029
+f 326/1210/1065 354/1213/1033 360/1214/1034
+f 314/1209/1029 354/1213/1033 326/1210/1065
+f 300/1211/1066 284/1163/1048 306/1215/1035
+f 330/1212/1032 306/1215/1035 347/1216/1067
+f 300/1211/1066 306/1215/1035 330/1212/1032
+f 354/1213/1033 347/1216/1067 370/1217/1068
+f 330/1212/1032 347/1216/1067 354/1213/1033
+f 360/1214/1034 370/1217/1068 382/1218/1038
+f 354/1213/1033 370/1217/1068 360/1214/1034
+f 306/1215/1035 284/1163/1048 311/1219/1039
+f 347/1216/1067 311/1219/1039 351/1220/1069
+f 306/1215/1035 311/1219/1039 347/1216/1067
+f 370/1217/1068 351/1220/1069 373/1221/1070
+f 347/1216/1067 351/1220/1069 370/1217/1068
+f 382/1218/1038 373/1221/1070 406/1222/1071
+f 370/1217/1068 373/1221/1070 382/1218/1038
+f 311/1219/1039 284/1163/1048 307/1223/1072
+f 351/1220/1069 307/1223/1072 348/1224/1073
+f 311/1219/1039 307/1223/1072 351/1220/1069
+f 373/1221/1070 348/1224/1073 371/1225/1074
+f 351/1220/1069 348/1224/1073 373/1221/1070
+f 406/1222/1071 371/1225/1074 383/1226/1075
+f 373/1221/1070 371/1225/1074 406/1222/1071
+f 307/1223/1072 284/1163/1048 301/1162/1047
+f 348/1224/1073 301/1162/1047 331/1165/985
+f 307/1223/1072 301/1162/1047 348/1224/1073
+f 371/1225/1074 331/1165/985 355/1167/1050
+f 348/1224/1073 331/1165/985 371/1225/1074
+f 383/1226/1075 355/1167/1050 361/1169/989
+f 371/1225/1074 355/1167/1050 383/1226/1075
+f 1080/1227/1076 1250/1228/1077 1249/580/561
+f 1081/1229/1076 1250/1228/1077 1080/1227/1076
+f 1249/580/561 1279/1230/1078 1278/592/573
+f 1250/1228/1077 1279/1230/1078 1249/580/561
+f 1278/592/573 1286/1231/1079 1285/598/579
+f 1279/1230/1078 1286/1231/1079 1278/592/573
+f 1285/598/579 1290/1232/1080 1289/1233/1080
+f 1286/1231/1079 1290/1232/1080 1285/598/579
+f 1289/1233/1080 1288/1234/1081 1287/1235/1081
+f 1290/1232/1080 1288/1234/1081 1289/1233/1080
+f 1287/1235/1081 1283/1236/1082 1282/1237/1082
+f 1288/1234/1081 1283/1236/1082 1287/1235/1081
+f 1282/1237/1082 1281/1238/1083 1280/604/585
+f 1283/1236/1082 1281/1238/1083 1282/1237/1082
+f 1280/604/585 1242/1239/1084 1241/1240/1084
+f 1281/1238/1083 1242/1239/1084 1280/604/585
+f 1241/1240/1084 1081/1229/1076 1080/1227/1076
+f 1242/1239/1084 1081/1229/1076 1241/1240/1084
+f 273/1241/1085 104/521/502 105/1242/1086
+f 274/1243/1085 273/1241/1085 105/1242/1086
+f 104/521/502 75/532/513 76/1244/1087
+f 105/1242/1086 104/521/502 76/1244/1087
+f 75/532/513 66/538/519 67/1245/1088
+f 76/1244/1087 75/532/513 67/1245/1088
+f 66/538/519 62/1246/1089 63/1247/1089
+f 67/1245/1088 66/538/519 63/1247/1089
+f 62/1246/1089 64/1248/1090 65/1249/1090
+f 63/1247/1089 62/1246/1089 65/1249/1090
+f 64/1248/1090 68/1250/1091 69/1251/1091
+f 65/1249/1090 64/1248/1090 69/1251/1091
+f 68/1250/1091 72/544/525 73/1252/1092
+f 69/1251/1091 68/1250/1091 73/1252/1092
+f 72/544/525 113/1253/1093 114/1254/1093
+f 73/1252/1092 72/544/525 114/1254/1093
+f 113/1253/1093 273/1241/1085 274/1243/1085
+f 114/1254/1093 113/1253/1093 274/1243/1085
+f 1097/1255/1094 1074/1256/1095 1084/1257/1096
+f 1184/1258/1097 1084/1257/1096 1121/1259/1098
+f 1097/1255/1094 1084/1257/1096 1184/1258/1097
+f 1181/1260/1099 1121/1259/1098 1120/1261/1100
+f 1184/1258/1097 1121/1259/1098 1181/1260/1099
+f 1170/1262/1048 1120/1261/1100 1110/1263/1048
+f 1181/1260/1099 1120/1261/1100 1170/1262/1048
+f 1084/1257/1096 1074/1256/1095 1060/1264/1101
+f 1121/1259/1098 1060/1264/1101 1032/1265/1102
+f 1084/1257/1096 1060/1264/1101 1121/1259/1098
+f 1120/1261/1100 1032/1265/1102 1034/1266/1103
+f 1121/1259/1098 1032/1265/1102 1120/1261/1100
+f 1110/1263/1048 1034/1266/1103 1041/1267/1048
+f 1120/1261/1100 1034/1266/1103 1110/1263/1048
+f 1060/1264/1101 1074/1256/1095 1051/1268/1104
+f 1032/1265/1102 1051/1268/1104 969/1269/1105
+f 1060/1264/1101 1051/1268/1104 1032/1265/1102
+f 1034/1266/1103 969/1269/1105 974/1270/1106
+f 1032/1265/1102 969/1269/1105 1034/1266/1103
+f 1041/1267/1048 974/1270/1106 991/1271/1048
+f 1034/1266/1103 974/1270/1106 1041/1267/1048
+f 1051/1268/1104 1074/1256/1095 1043/1272/1107
+f 969/1269/1105 1043/1272/1107 925/1273/1108
+f 1051/1268/1104 1043/1272/1107 969/1269/1105
+f 974/1270/1106 925/1273/1108 929/1274/1109
+f 969/1269/1105 925/1273/1108 974/1270/1106
+f 991/1271/1048 929/1274/1109 943/1275/1048
+f 974/1270/1106 929/1274/1109 991/1271/1048
+f 1043/1272/1107 1074/1256/1095 1044/1276/1110
+f 925/1273/1108 1044/1276/1110 928/1277/1111
+f 1043/1272/1107 1044/1276/1110 925/1273/1108
+f 929/1274/1109 928/1277/1111 932/1278/1112
+f 925/1273/1108 928/1277/1111 929/1274/1109
+f 943/1275/1048 932/1278/1112 944/1279/983
+f 929/1274/1109 932/1278/1112 943/1275/1048
+f 1044/1276/1110 1074/1256/1095 1052/1280/1113
+f 928/1277/1111 1052/1280/1113 975/1281/1114
+f 1044/1276/1110 1052/1280/1113 928/1277/1111
+f 932/1278/1112 975/1281/1114 976/1282/1115
+f 928/1277/1111 975/1281/1114 932/1278/1112
+f 944/1279/983 976/1282/1115 992/1283/983
+f 932/1278/1112 976/1282/1115 944/1279/983
+f 1052/1280/1113 1074/1256/1095 1061/1284/1116
+f 975/1281/1114 1061/1284/1116 1039/1285/1117
+f 1052/1280/1113 1061/1284/1116 975/1281/1114
+f 976/1282/1115 1039/1285/1117 1040/1286/1118
+f 975/1281/1114 1039/1285/1117 976/1282/1115
+f 992/1283/983 1040/1286/1118 1046/1287/1048
+f 976/1282/1115 1040/1286/1118 992/1283/983
+f 1061/1284/1116 1074/1256/1095 1085/1288/1119
+f 1039/1285/1117 1085/1288/1119 1128/1289/1120
+f 1061/1284/1116 1085/1288/1119 1039/1285/1117
+f 1040/1286/1118 1128/1289/1120 1127/1290/1121
+f 1039/1285/1117 1128/1289/1120 1040/1286/1118
+f 1046/1287/1048 1127/1290/1121 1115/1291/1122
+f 1040/1286/1118 1127/1290/1121 1046/1287/1048
+f 1085/1288/1119 1074/1256/1095 1098/1292/1123
+f 1128/1289/1120 1098/1292/1123 1190/1293/1124
+f 1085/1288/1119 1098/1292/1123 1128/1289/1120
+f 1127/1290/1121 1190/1293/1124 1187/1294/1125
+f 1128/1289/1120 1190/1293/1124 1127/1290/1121
+f 1115/1291/1122 1187/1294/1125 1173/1295/1048
+f 1127/1290/1121 1187/1294/1125 1115/1291/1122
+f 1098/1292/1123 1074/1256/1095 1112/1296/1126
+f 1190/1293/1124 1112/1296/1126 1217/1297/1127
+f 1098/1292/1123 1112/1296/1126 1190/1293/1124
+f 1187/1294/1125 1217/1297/1127 1212/1298/1128
+f 1190/1293/1124 1217/1297/1127 1187/1294/1125
+f 1173/1295/1048 1212/1298/1128 1198/1299/1048
+f 1187/1294/1125 1212/1298/1128 1173/1295/1048
+f 1112/1296/1126 1074/1256/1095 1111/1300/1129
+f 1217/1297/1127 1111/1300/1129 1214/1301/1130
+f 1112/1296/1126 1111/1300/1129 1217/1297/1127
+f 1212/1298/1128 1214/1301/1130 1211/1302/1131
+f 1217/1297/1127 1214/1301/1130 1212/1298/1128
+f 1198/1299/1048 1211/1302/1131 1195/1303/1048
+f 1212/1298/1128 1211/1302/1131 1198/1299/1048
+f 1111/1300/1129 1074/1256/1095 1097/1255/1094
+f 1214/1301/1130 1097/1255/1094 1184/1258/1097
+f 1111/1300/1129 1097/1255/1094 1214/1301/1130
+f 1211/1302/1131 1184/1258/1097 1181/1260/1099
+f 1214/1301/1130 1184/1258/1097 1211/1302/1131
+f 1195/1303/1048 1181/1260/1099 1170/1262/1048
+f 1211/1302/1131 1181/1260/1099 1195/1303/1048
+f 302/1304/1132 279/1305/1095 293/1306/1133
+f 380/1307/1097 293/1306/1133 317/1308/1098
+f 302/1304/1132 293/1306/1133 380/1307/1097
+f 379/1309/1134 317/1308/1098 316/1310/1135
+f 380/1307/1097 317/1308/1098 379/1309/1134
+f 369/1311/1136 316/1310/1135 308/1312/1137
+f 379/1309/1134 316/1310/1135 369/1311/1136
+f 369/1311/1136 308/1312/1137 285/1313/1138
+f 293/1306/1133 279/1305/1095 269/1314/1139
+f 317/1308/1098 269/1314/1139 226/1315/1102
+f 293/1306/1133 269/1314/1139 317/1308/1098
+f 316/1310/1135 226/1315/1102 228/1316/1103
+f 317/1308/1098 226/1315/1102 316/1310/1135
+f 308/1312/1137 228/1316/1103 239/1317/1140
+f 316/1310/1135 228/1316/1103 308/1312/1137
+f 308/1312/1137 239/1317/1140 285/1313/1138
+f 269/1314/1139 279/1305/1095 254/1318/1104
+f 226/1315/1102 254/1318/1104 163/1319/1105
+f 269/1314/1139 254/1318/1104 226/1315/1102
+f 228/1316/1103 163/1319/1105 167/1320/1106
+f 226/1315/1102 163/1319/1105 228/1316/1103
+f 239/1317/1140 167/1320/1106 179/1321/1141
+f 228/1316/1103 167/1320/1106 239/1317/1140
+f 239/1317/1140 179/1321/1141 285/1313/1138
+f 254/1318/1104 279/1305/1095 242/1322/1107
+f 163/1319/1105 242/1322/1107 136/1323/1108
+f 254/1318/1104 242/1322/1107 163/1319/1105
+f 167/1320/1106 136/1323/1108 138/1324/1142
+f 163/1319/1105 136/1323/1108 167/1320/1106
+f 179/1321/1141 138/1324/1142 156/1325/1143
+f 167/1320/1106 138/1324/1142 179/1321/1141
+f 179/1321/1141 156/1325/1143 285/1313/1138
+f 242/1322/1107 279/1305/1095 243/1326/1144
+f 136/1323/1108 243/1326/1144 137/1327/1145
+f 242/1322/1107 243/1326/1144 136/1323/1108
+f 138/1324/1142 137/1327/1145 139/1328/1146
+f 136/1323/1108 137/1327/1145 138/1324/1142
+f 156/1325/1143 139/1328/1146 158/1329/1147
+f 138/1324/1142 139/1328/1146 156/1325/1143
+f 156/1325/1143 158/1329/1147 285/1313/1138
+f 243/1326/1144 279/1305/1095 255/1330/1148
+f 137/1327/1145 255/1330/1148 168/1331/1114
+f 243/1326/1144 255/1330/1148 137/1327/1145
+f 139/1328/1146 168/1331/1114 172/1332/1149
+f 137/1327/1145 168/1331/1114 139/1328/1146
+f 158/1329/1147 172/1332/1149 183/1333/1150
+f 139/1328/1146 172/1332/1149 158/1329/1147
+f 158/1329/1147 183/1333/1150 285/1313/1138
+f 255/1330/1148 279/1305/1095 272/1334/1151
+f 168/1331/1114 272/1334/1151 233/1335/1117
+f 255/1330/1148 272/1334/1151 168/1331/1114
+f 172/1332/1149 233/1335/1117 234/1336/1152
+f 168/1331/1114 233/1335/1117 172/1332/1149
+f 183/1333/1150 234/1336/1152 244/1337/1153
+f 172/1332/1149 234/1336/1152 183/1333/1150
+f 183/1333/1150 244/1337/1153 285/1313/1138
+f 272/1334/1151 279/1305/1095 294/1338/1154
+f 233/1335/1117 294/1338/1154 324/1339/1120
+f 272/1334/1151 294/1338/1154 233/1335/1117
+f 234/1336/1152 324/1339/1120 319/1340/1121
+f 233/1335/1117 324/1339/1120 234/1336/1152
+f 244/1337/1153 319/1340/1121 312/1341/1155
+f 234/1336/1152 319/1340/1121 244/1337/1153
+f 244/1337/1153 312/1341/1155 285/1313/1138
+f 294/1338/1154 279/1305/1095 303/1342/1156
+f 324/1339/1120 303/1342/1156 385/1343/1157
+f 294/1338/1154 303/1342/1156 324/1339/1120
+f 319/1340/1121 385/1343/1157 384/1344/1158
+f 324/1339/1120 385/1343/1157 319/1340/1121
+f 312/1341/1155 384/1344/1158 372/1345/1159
+f 319/1340/1121 384/1344/1158 312/1341/1155
+f 312/1341/1155 372/1345/1159 285/1313/1138
+f 303/1342/1156 279/1305/1095 310/1346/1126
+f 385/1343/1157 310/1346/1126 426/1347/1127
+f 303/1342/1156 310/1346/1126 385/1343/1157
+f 384/1344/1158 426/1347/1127 422/1348/1160
+f 385/1343/1157 426/1347/1127 384/1344/1158
+f 372/1345/1159 422/1348/1160 411/1349/1161
+f 384/1344/1158 422/1348/1160 372/1345/1159
+f 372/1345/1159 411/1349/1161 285/1313/1138
+f 310/1346/1126 279/1305/1095 309/1350/1129
+f 426/1347/1127 309/1350/1129 425/1351/1162
+f 310/1346/1126 309/1350/1129 426/1347/1127
+f 422/1348/1160 425/1351/1162 421/1352/1163
+f 426/1347/1127 425/1351/1162 422/1348/1160
+f 411/1349/1161 421/1352/1163 410/1353/1164
+f 422/1348/1160 421/1352/1163 411/1349/1161
+f 411/1349/1161 410/1353/1164 285/1313/1138
+f 309/1350/1129 279/1305/1095 302/1304/1132
+f 425/1351/1162 302/1304/1132 380/1307/1097
+f 309/1350/1129 302/1304/1132 425/1351/1162
+f 421/1352/1163 380/1307/1097 379/1309/1134
+f 425/1351/1162 380/1307/1097 421/1352/1163
+f 410/1353/1164 379/1309/1134 369/1311/1136
+f 421/1352/1163 379/1309/1134 410/1353/1164
+f 410/1353/1164 369/1311/1136 285/1313/1138
diff --git a/tests/barstest/shuttle.png b/tests/barstest/shuttle.png
new file mode 100644
index 00000000..52d6244c
--- /dev/null
+++ b/tests/barstest/shuttle.png
Binary files differ
diff --git a/tests/directional/main.cpp b/tests/directional/main.cpp
index 2b077b97..551ab141 100644
--- a/tests/directional/main.cpp
+++ b/tests/directional/main.cpp
@@ -80,6 +80,9 @@ int main(int argc, char **argv)
backgroundCheckBox->setText(QStringLiteral("Show background"));
backgroundCheckBox->setChecked(true);
+ QCheckBox *optimizationCheckBox = new QCheckBox(widget);
+ optimizationCheckBox->setText(QStringLiteral("Optimization static"));
+
QCheckBox *gridCheckBox = new QCheckBox(widget);
gridCheckBox->setText(QStringLiteral("Show grid"));
gridCheckBox->setChecked(true);
@@ -100,6 +103,7 @@ int main(int argc, char **argv)
vLayout->addWidget(labelButton, 0, Qt::AlignTop);
vLayout->addWidget(cameraButton, 0, Qt::AlignTop);
vLayout->addWidget(toggleRotationButton, 0, Qt::AlignTop);
+ vLayout->addWidget(optimizationCheckBox);
vLayout->addWidget(backgroundCheckBox);
vLayout->addWidget(gridCheckBox);
vLayout->addWidget(new QLabel(QStringLiteral("Change dot style")));
@@ -127,6 +131,8 @@ int main(int argc, char **argv)
QObject::connect(modifier, &ScatterDataModifier::backgroundEnabledChanged,
backgroundCheckBox, &QCheckBox::setChecked);
+ QObject::connect(optimizationCheckBox, &QCheckBox::stateChanged,
+ modifier, &ScatterDataModifier::enableOptimization);
QObject::connect(modifier, &ScatterDataModifier::gridEnabledChanged,
gridCheckBox, &QCheckBox::setChecked);
QObject::connect(itemStyleList, SIGNAL(currentIndexChanged(int)), modifier,
@@ -150,6 +156,7 @@ int main(int argc, char **argv)
&QFontComboBox::setCurrentFont);
itemStyleList->setCurrentIndex(0);
+ optimizationCheckBox->setChecked(true);
widget->show();
return app.exec();
diff --git a/tests/directional/scatterdatamodifier.cpp b/tests/directional/scatterdatamodifier.cpp
index 1422cebb..2d6672b6 100644
--- a/tests/directional/scatterdatamodifier.cpp
+++ b/tests/directional/scatterdatamodifier.cpp
@@ -119,7 +119,14 @@ void ScatterDataModifier::addData()
m_graph->seriesList().at(0)->dataProxy()->resetArray(dataArray);
}
-//! [8]
+void ScatterDataModifier::enableOptimization(int enabled)
+{
+ if (enabled)
+ m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic);
+ else
+ m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationDefault);
+}
+
void ScatterDataModifier::changeStyle(int style)
{
QComboBox *comboBox = qobject_cast<QComboBox *>(sender());
diff --git a/tests/directional/scatterdatamodifier.h b/tests/directional/scatterdatamodifier.h
index b87fa89f..20e59cdc 100644
--- a/tests/directional/scatterdatamodifier.h
+++ b/tests/directional/scatterdatamodifier.h
@@ -39,6 +39,7 @@ public:
void changeLabelStyle();
void changeFont(const QFont &font);
void changeFontSize(int fontsize);
+ void enableOptimization(int enabled);
void setBackgroundEnabled(int enabled);
void setGridEnabled(int enabled);
void toggleRotation();
diff --git a/tests/qmlcamera/qml/qmlcamera/main.qml b/tests/qmlcamera/qml/qmlcamera/main.qml
index 444e175c..be4feca3 100644
--- a/tests/qmlcamera/qml/qmlcamera/main.qml
+++ b/tests/qmlcamera/qml/qmlcamera/main.qml
@@ -18,7 +18,7 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
import "."
Rectangle {
@@ -35,6 +35,15 @@ Rectangle {
id: chartAxes
}
+ Camera3D {
+ id: customCamera
+ wrapXRotation: false
+ xRotation: camControlArea.xValue
+ yRotation: camControlArea.yValue
+ zoomLevel: zoomSlider.value
+ target: Qt.vector3d(0.5, 0.5, 0.5)
+ }
+
Item {
id: dataView
width: parent.width - camControlArea.width
@@ -60,21 +69,20 @@ Rectangle {
columnAxis: chartAxes.column
valueAxis: chartAxes.expenses
- // Bind UI controls to the camera
- scene.activeCamera.wrapXRotation: false
- scene.activeCamera.xRotation: camControlArea.xValue
- scene.activeCamera.yRotation: camControlArea.yValue
- scene.activeCamera.zoomLevel: zoomSlider.value
+ scene.activeCamera: customCamera
inputHandler: null
customItemList: [shuttleItem, labelItem]
+ orthoProjection: true
+
+ floorLevel: 10
}
Custom3DItem {
id: shuttleItem
meshFile: ":/items/shuttle.obj"
textureFile: ":/items/shuttle.png"
- position: Qt.vector3d(5.0,29.0,3.0)
+ position: Qt.vector3d(2.0,29.0,2.0)
scaling: Qt.vector3d(0.2,0.2,0.2)
}
@@ -82,7 +90,7 @@ Rectangle {
id: labelItem
facingCamera: true
positionAbsolute: true
- position: Qt.vector3d(0.0,1.5,0.0)
+ position: Qt.vector3d(-1.0,1.5,-1.0)
scaling: Qt.vector3d(1.0,1.0,1.0)
text: "Qt Shuttle"
}
@@ -162,6 +170,11 @@ Rectangle {
currentAngle += 5
chartData.series.meshAngle = currentAngle
shuttleItem.setRotationAxisAndAngle(Qt.vector3d(0.0, 1.0, 1.0), currentAngle)
+ console.log("label pos:", labelItem.position)
+ labelItem.position.x += 0.1
+ labelItem.position.z += 0.1
+ customCamera.target.x -= 0.1
+ customCamera.target.z -= 0.1
}
}
@@ -203,4 +216,20 @@ Rectangle {
}
}
}
+
+ Button {
+ id: reflectionToggle
+ anchors.bottom: shuttleAdd.top
+ width: camControlArea.width
+ text: "Show reflections"
+ onClicked: {
+ if (testChart.reflection === true) {
+ text = "Show reflections"
+ testChart.reflection = false
+ } else {
+ text = "Hide reflections"
+ testChart.reflection = true
+ }
+ }
+ }
}
diff --git a/tests/qmldynamicdata/qml/qmldynamicdata/main.qml b/tests/qmldynamicdata/qml/qmldynamicdata/main.qml
index 0ec9d277..29c51fb3 100644
--- a/tests/qmldynamicdata/qml/qmldynamicdata/main.qml
+++ b/tests/qmldynamicdata/qml/qmldynamicdata/main.qml
@@ -71,18 +71,16 @@ Rectangle {
isIncreasing = false;
}
} else {
- // TODO: Once QTRD-2645 is fixed, change this to remove from
- // random index to add coverage.
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
- graphModel.remove(2);
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
+ graphModel.remove(Math.random() * (graphModel.count - 1));
if (graphModel.count == 2) {
scatterGraph.theme.type = Theme3D.ThemeDigia;
isIncreasing = true;
@@ -118,6 +116,12 @@ Rectangle {
shadowQuality: AbstractGraph3D.ShadowQualitySoftMedium
scene.activeCamera.yRotation: 30.0
inputHandler: null
+ axisX.min: 0
+ axisY.min: 0
+ axisZ.min: 0
+ axisX.max: 1
+ axisY.max: 1
+ axisZ.max: 1
Scatter3DSeries {
id: scatterSeries
diff --git a/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml b/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml
index 7dfe0bec..da382e3d 100644
--- a/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml
+++ b/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml
@@ -31,19 +31,51 @@ Rectangle {
id: data
}
- Window {
- id: firstWindow
- x: 100
- y: 100
- width: 500
- height: 500
- visible: true
- Rectangle {
- id: firstRect
- color: "red"
- anchors.fill: parent
- }
- }
+ property QtObject surfaceWindowObject;
+ property string surfaceWindowStr:
+ "\n
+ import QtQuick 2.1\n
+ import QtQuick.Window 2.1\n
+ import QtQuick.Layouts 1.0\n
+ import QtDataVisualization 1.0\n
+ import \".\"\n
+ Window {\n
+ Data {\n
+ id: data\n
+ }\n
+ id: firstWindow\n
+ x: 100\n
+ y: 100\n
+ width: 500\n
+ height: 500\n
+ visible: true\n
+ Rectangle {\n
+ id: firstRect\n
+ color: \"red\"\n
+ anchors.fill: parent\n
+ Surface3D {\n
+ id: surfaceGraph\n
+ anchors.fill: parent\n
+ anchors.margins: parent.border.width\n
+ theme: Theme3D {\n
+ type: Theme3D.ThemePrimaryColors\n
+ font.pointSize: 60\n
+ }\n
+ scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh\n
+ Surface3DSeries {\n
+ itemLabelFormat: \"Pop density at (@xLabel N, @zLabel E): @yLabel\"\n
+ ItemModelSurfaceDataProxy {\n
+ itemModel: data.myData\n
+ rowRole: \"row\"\n
+ columnRole: \"col\"\n
+ xPosRole: \"latitude\"\n
+ zPosRole: \"longitude\"\n
+ yPosRole: \"pop_density\"\n
+ }\n
+ }\n
+ }\n
+ }\n
+ }"
Window {
id: secondWindow
@@ -59,18 +91,12 @@ Rectangle {
}
}
- states: [
- State {
- name: "firstWindow"
- ParentChange { target: surfaceGraph; parent: firstRect; x: 0; y: 0 }
- },
- State {
- name: "secondWindow"
- ParentChange { target: surfaceGraph; parent: secondRect; x: 0; y: 0 }
- }
- ]
+ function destroyWindow() {
+ if (surfaceWindowObject != null)
+ surfaceWindowObject.destroy()
+ }
- state: "firstWindow"
+ Component.onDestruction: destroyWindow()
//! [0]
GridLayout {
@@ -86,32 +112,17 @@ Rectangle {
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
- border.color: surfaceGraph.theme.gridLineColor
border.width: 2
+ }
- Surface3D {
- id: surfaceGraph
- anchors.fill: parent
- anchors.margins: parent.border.width
- theme: Theme3D {
- type: Theme3D.ThemePrimaryColors
- font.pointSize: 60
- }
- scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh
-
- Surface3DSeries {
- itemLabelFormat: "Pop density at (@xLabel N, @zLabel E): @yLabel"
- ItemModelSurfaceDataProxy {
- itemModel: data.myData
- // The surface data points are not neatly lined up in rows and columns,
- // so we define explicit row and column roles.
- rowRole: "row"
- columnRole: "col"
- xPosRole: "latitude"
- zPosRole: "longitude"
- yPosRole: "pop_density"
- }
- }
+ Timer {
+ id: windowToggleTimer
+ interval: 1000
+ running: false
+ repeat: false
+ onTriggered: {
+ destroyWindow()
+ surfaceWindowObject = Qt.createQmlObject(surfaceWindowStr, mainView)
}
}
@@ -131,13 +142,10 @@ Rectangle {
Layout.minimumWidth: parent.width / 2
Layout.fillHeight: true
Layout.fillWidth: true
- text: "Move graph between windows"
+ text: "(re)construct surface window in a loop"
onClicked: {
- if (mainView.state === "firstWindow") {
- mainView.state = "secondWindow"
- } else {
- mainView.state = "firstWindow"
- }
+ windowToggleTimer.running = true
+ windowToggleTimer.repeat = true
}
}
@@ -231,14 +239,11 @@ Rectangle {
function clearSelections() {
barGraph.clearSelection()
scatterGraph.clearSelection()
- surfaceGraph.clearSelection()
}
function resetCameras() {
- surfaceGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetIsometricLeftHigh
scatterGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetIsometricLeftHigh
barGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetIsometricLeftHigh
- surfaceGraph.scene.activeCamera.zoomLevel = 100.0
scatterGraph.scene.activeCamera.zoomLevel = 100.0
barGraph.scene.activeCamera.zoomLevel = 100.0
}
@@ -246,12 +251,9 @@ Rectangle {
function toggleMeshStyle() {
if (barGraph.seriesList[0].meshSmooth === true) {
barGraph.seriesList[0].meshSmooth = false
- if (surfaceGraph.seriesList[0].flatShadingSupported)
- surfaceGraph.seriesList[0].flatShadingEnabled = true
scatterGraph.seriesList[0].meshSmooth = false
} else {
barGraph.seriesList[0].meshSmooth = true
- surfaceGraph.seriesList[0].flatShadingEnabled = false
scatterGraph.seriesList[0].meshSmooth = true
}
}
diff --git a/tests/qmlperf/main.cpp b/tests/qmlperf/main.cpp
new file mode 100644
index 00000000..7d35b2ed
--- /dev/null
+++ b/tests/qmlperf/main.cpp
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include <QtGui/QGuiApplication>
+#include <QtCore/QDir>
+#include <QtQuick/QQuickView>
+#include <QtQml/QQmlEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQuickView viewer;
+
+ // The following are needed to make examples run without having to install the module
+ // in desktop environments.
+#ifdef Q_OS_WIN
+ QString extraImportPath(QStringLiteral("%1/../../../%2"));
+#else
+ QString extraImportPath(QStringLiteral("%1/../../%2"));
+#endif
+ viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(),
+ QString::fromLatin1("qml")));
+ QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close);
+
+ viewer.setTitle(QStringLiteral("QML Performance"));
+ viewer.setSource(QUrl("qrc:/qml/qmlperf/main.qml"));
+ viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+ viewer.show();
+
+ return app.exec();
+}
diff --git a/tests/qmlperf/qml/qmlperf/main.qml b/tests/qmlperf/qml/qmlperf/main.qml
new file mode 100644
index 00000000..35f8df5d
--- /dev/null
+++ b/tests/qmlperf/qml/qmlperf/main.qml
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 1.0
+import QtDataVisualization 1.1
+import "script.js" as Script
+import "."
+
+Rectangle {
+ id: mainview
+ width: 1280
+ height: 1024
+
+ property var itemCount: 1000.0
+ property var addItems: 1000.0
+
+ Button {
+ id: changeButton
+ width: 350
+ height: 50
+ anchors.left: parent.left
+ enabled: true
+ text: "Change"
+ onClicked: {
+ console.log("changeButton clicked");
+ if (graphView.state == "meshsphere") {
+ graphView.state = "meshcube"
+ } else if (graphView.state == "meshcube") {
+ graphView.state = "meshpyramid"
+ } else if (graphView.state == "meshpyramid") {
+ graphView.state = "meshpoint"
+ } else if (graphView.state == "meshpoint") {
+ graphView.state = "meshsphere"
+ }
+ }
+ }
+
+ Text {
+ id: fpsText
+ text: "Reading"
+ width: 300
+ height: 50
+ anchors.left: changeButton.right
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ id: optimization
+ width: 300
+ height: 50
+ anchors.left: fpsText.right
+ enabled: true
+ text: scatterPlot.optimizationHints === AbstractGraph3D.OptimizationDefault ? "To Static" : "To Default"
+ onClicked: {
+ console.log("Optimization");
+ if (scatterPlot.optimizationHints === AbstractGraph3D.OptimizationDefault) {
+ scatterPlot.optimizationHints = AbstractGraph3D.OptimizationStatic;
+ optimization.text = "To Default";
+ } else {
+ scatterPlot.optimizationHints = AbstractGraph3D.OptimizationDefault;
+ optimization.text = "To Static";
+ }
+ }
+ }
+
+ Button {
+ id: itemAdd
+ width: 300
+ height: 50
+ anchors.left: optimization.right
+ enabled: true
+ text: "Add"
+ onClicked: {
+ itemCount = itemCount + addItems;
+ Script.createData(addItems);
+ }
+ }
+
+ Item {
+ id: graphView
+ width: mainview.width
+ height: mainview.height
+ anchors.top: changeButton.bottom
+ anchors.left: mainview.left
+ state: "meshsphere"
+
+ ListModel {
+ id: dataModel
+ Component.onCompleted: Script.createData(itemCount)
+ }
+
+ Scatter3D {
+ id: scatterPlot
+ width: graphView.width
+ height: graphView.height
+ shadowQuality: AbstractGraph3D.ShadowQualityNone
+ optimizationHints: AbstractGraph3D.OptimizationDefault
+ scene.activeCamera.yRotation: 45.0
+ measureFps: true
+ onCurrentFpsChanged: {
+ fpsText.text = itemCount + " : " + scatterPlot.currentFps.toFixed(1);
+ }
+
+// theme: Theme3D {
+// type: Theme3D.ThemeRetro
+// colorStyle: Theme3D.ColorStyleRangeGradient
+// baseGradients: customGradient
+
+// ColorGradient {
+// id: customGradient
+// ColorGradientStop { position: 1.0; color: "red" }
+// ColorGradientStop { position: 0.0; color: "blue" }
+// }
+// }
+
+ Scatter3DSeries {
+ id: scatterSeries
+ mesh: Abstract3DSeries.MeshSphere
+ ItemModelScatterDataProxy {
+ itemModel: dataModel
+ xPosRole: "x"
+ yPosRole: "y"
+ zPosRole: "z"
+ }
+ }
+ }
+
+ states: [
+ State {
+ name: "meshsphere"
+ StateChangeScript {
+ name: "doSphere"
+ script: {
+ console.log("Do the sphere");
+ scatterSeries.mesh = Abstract3DSeries.MeshSphere;
+ }
+ }
+ },
+ State {
+ name: "meshcube"
+ StateChangeScript {
+ name: "doCube"
+ script: {
+ console.log("Do the cube");
+ scatterSeries.mesh = Abstract3DSeries.MeshCube;
+ }
+ }
+ },
+ State {
+ name: "meshpyramid"
+ StateChangeScript {
+ name: "doPyramid"
+ script: {
+ console.log("Do the pyramid");
+ scatterSeries.mesh = Abstract3DSeries.MeshPyramid;
+ }
+ }
+ },
+ State {
+ name: "meshpoint"
+ StateChangeScript {
+ name: "doPoint"
+ script: {
+ console.log("Do the point");
+ scatterSeries.mesh = Abstract3DSeries.MeshPoint;
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/qmlperf/qml/qmlperf/script.js b/tests/qmlperf/qml/qmlperf/script.js
new file mode 100644
index 00000000..dc271e8d
--- /dev/null
+++ b/tests/qmlperf/qml/qmlperf/script.js
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+//function createData(base) {
+// for (var z = 0; z < 30; z++) {
+// for (var x = 0; x < 30; x++) {
+// var angle = (((z - 16) * (x - 16)) / 144.0) * 1.57;
+// var y = Math.sin(angle + base);
+// dataModel.append({"z": z, "x": x, "y": y});
+// }
+// }
+//}
+
+function createData(base) {
+ for (var i = 0; i < base; i++) {
+ dataModel.append({"z": Math.random(), "x": Math.random(), "y": Math.random()});
+ }
+}
diff --git a/tests/qmlperf/qmlperf.pro b/tests/qmlperf/qmlperf.pro
new file mode 100644
index 00000000..6560f55c
--- /dev/null
+++ b/tests/qmlperf/qmlperf.pro
@@ -0,0 +1,12 @@
+!include( ../tests.pri ) {
+ error( "Couldn't find the tests.pri file!" )
+}
+
+# The .cpp file which was generated for your project. Feel free to hack it.
+SOURCES += main.cpp
+
+RESOURCES += qmlperf.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/* \
+ qml/qmlperf/*
diff --git a/tests/qmlperf/qmlperf.qrc b/tests/qmlperf/qmlperf.qrc
new file mode 100644
index 00000000..e50815c5
--- /dev/null
+++ b/tests/qmlperf/qmlperf.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/qmlperf/main.qml</file>
+ <file>qml/qmlperf/script.js</file>
+ </qresource>
+</RCC>
diff --git a/tests/qmlvolume/datasource.cpp b/tests/qmlvolume/datasource.cpp
new file mode 100644
index 00000000..e1fe5385
--- /dev/null
+++ b/tests/qmlvolume/datasource.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "datasource.h"
+#include <QtCore/qmath.h>
+#include <QtGui/QRgb>
+#include <QtGui/QVector3D>
+
+using namespace QtDataVisualization;
+
+Q_DECLARE_METATYPE(QCustom3DVolume *)
+
+DataSource::DataSource(QObject *parent) :
+ QObject(parent)
+{
+ qRegisterMetaType<QCustom3DVolume *>();
+}
+
+DataSource::~DataSource()
+{
+}
+
+void DataSource::fillVolume(QCustom3DVolume *volumeItem)
+{
+ // Generate example texture data for an half-ellipsoid with a section missing.
+ // This can take a while if the dimensions are large, so we support incremental data generation.
+
+ int index = 0;
+ int textureSize = 256;
+ QVector3D midPoint(float(textureSize) / 2.0f,
+ float(textureSize) / 2.0f,
+ float(textureSize) / 2.0f);
+
+ QVector<uchar> *textureData = new QVector<uchar>(textureSize * textureSize * textureSize / 2);
+ for (int i = 0; i < textureSize; i++) {
+ for (int j = 0; j < textureSize / 2; j++) {
+ for (int k = 0; k < textureSize; k++) {
+ int colorIndex = 0;
+ // Take a section out of the ellipsoid
+ if (i >= textureSize / 2 || j >= textureSize / 4 || k >= textureSize / 2) {
+ QVector3D distVec = QVector3D(float(k), float(j * 2), float(i)) - midPoint;
+ float adjLen = qMin(255.0f, (distVec.length() * 512.0f / float(textureSize)));
+ colorIndex = 255 - int(adjLen);
+ }
+
+ (*textureData)[index] = colorIndex;
+ index++;
+ }
+ }
+ }
+
+ volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f));
+ volumeItem->setTextureWidth(textureSize);
+ volumeItem->setTextureHeight(textureSize / 2);
+ volumeItem->setTextureDepth(textureSize);
+ volumeItem->setTextureFormat(QImage::Format_Indexed8);
+ volumeItem->setTextureData(textureData);
+
+ QVector<QRgb> colorTable(256);
+
+ for (int i = 1; i < 256; i++) {
+ if (i < 15)
+ colorTable[i] = qRgba(0, 0, 0, 0);
+ else if (i < 60)
+ colorTable[i] = qRgba((i * 2) + 120, 0, 0, 15);
+ else if (i < 120)
+ colorTable[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 50);
+ else if (i < 180)
+ colorTable[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255);
+ else
+ colorTable[i] = qRgba(i, i, i, 255);
+ }
+
+ volumeItem->setColorTable(colorTable);
+}
diff --git a/tests/qmlvolume/datasource.h b/tests/qmlvolume/datasource.h
new file mode 100644
index 00000000..fc543792
--- /dev/null
+++ b/tests/qmlvolume/datasource.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef DATASOURCE_H
+#define DATASOURCE_H
+
+#include <QtDataVisualization/QCustom3DVolume>
+#include <QtCore/QObject>
+
+using namespace QtDataVisualization;
+
+class DataSource : public QObject
+{
+ Q_OBJECT
+public:
+ explicit DataSource(QObject *parent = 0);
+ virtual ~DataSource();
+
+public:
+ Q_INVOKABLE void fillVolume(QCustom3DVolume *volumeItem);
+};
+
+#endif
diff --git a/tests/qmlvolume/main.cpp b/tests/qmlvolume/main.cpp
new file mode 100644
index 00000000..85de7eed
--- /dev/null
+++ b/tests/qmlvolume/main.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "datasource.h"
+
+#include <QtDataVisualization/qutils.h>
+
+#include <QtGui/QGuiApplication>
+#include <QtCore/QDir>
+#include <QtQml/QQmlContext>
+#include <QtQuick/QQuickView>
+#include <QtQml/QQmlEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQuickView viewer;
+
+ // The following are needed to make examples run without having to install the module
+ // in desktop environments.
+#ifdef Q_OS_WIN
+ QString extraImportPath(QStringLiteral("%1/../../../%2"));
+#else
+ QString extraImportPath(QStringLiteral("%1/../../%2"));
+#endif
+ viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(),
+ QString::fromLatin1("qml")));
+ QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close);
+
+ viewer.setTitle(QStringLiteral("QML volume example"));
+
+ DataSource dataSource;
+ viewer.rootContext()->setContextProperty("dataSource", &dataSource);
+
+ viewer.setSource(QUrl("qrc:/qml/qmlvolume/main.qml"));
+ viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+ viewer.show();
+
+ return app.exec();
+}
diff --git a/tests/qmlvolume/qml/qmlvolume/NewButton.qml b/tests/qmlvolume/qml/qmlvolume/NewButton.qml
new file mode 100644
index 00000000..e4fb99d2
--- /dev/null
+++ b/tests/qmlvolume/qml/qmlvolume/NewButton.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Controls 1.0
+import QtQuick.Controls.Styles 1.0
+
+Item {
+ id: newbutton
+
+ property alias text: buttonText.text
+
+ signal clicked
+
+ implicitWidth: buttonText.implicitWidth + 5
+ implicitHeight: buttonText.implicitHeight + 10
+
+ Button {
+ id: buttonText
+ width: parent.width
+ height: parent.height
+
+ style: ButtonStyle {
+ label: Component {
+ Text {
+ text: buttonText.text
+ clip: true
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ anchors.fill: parent
+ }
+ }
+ }
+ onClicked: newbutton.clicked()
+ }
+}
diff --git a/tests/qmlvolume/qml/qmlvolume/main.qml b/tests/qmlvolume/qml/qmlvolume/main.qml
new file mode 100644
index 00000000..aec5f075
--- /dev/null
+++ b/tests/qmlvolume/qml/qmlvolume/main.qml
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 1.0
+import QtDataVisualization 1.2
+import "."
+
+Item {
+ id: mainView
+ width: 1280
+ height: 1024
+
+ Item {
+ id: dataView
+ anchors.bottom: parent.bottom
+ width: parent.width
+ height: parent.height - buttonLayout.height
+
+ Surface3D {
+ id: surfaceGraph
+
+ width: dataView.width
+ height: dataView.height
+ orthoProjection: true
+ //measureFps: true
+
+ onCurrentFpsChanged: {
+ if (fps > 10)
+ fpsText.text = "FPS: " + Math.round(surfaceGraph.currentFps)
+ else
+ fpsText.text = "FPS: " + Math.round(surfaceGraph.currentFps * 10.0) / 10.0
+ }
+
+ Surface3DSeries {
+ id: surfaceSeries
+ drawMode: Surface3DSeries.DrawSurface;
+ flatShadingEnabled: false;
+ meshSmooth: true
+ itemLabelFormat: "@xLabel, @zLabel: @yLabel"
+ itemLabelVisible: false
+
+ onItemLabelChanged: {
+ if (surfaceSeries.selectedPoint === surfaceSeries.invalidSelectionPosition)
+ selectionText.text = "No selection"
+ else
+ selectionText.text = surfaceSeries.itemLabel
+ }
+ }
+
+ Component.onCompleted: {
+ mainView.createVolume();
+ }
+ }
+ }
+
+ Rectangle {
+ width: parent.width
+ height: 50
+ anchors.left: parent.left
+ anchors.top: parent.top
+ color: surfaceGraph.theme.backgroundColor
+
+ ColumnLayout {
+ anchors.fill: parent
+ RowLayout {
+ id: sliderLayout
+ anchors.top: parent.top
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.minimumHeight: 150
+ spacing: 0
+
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.minimumWidth: fpsText.implicitWidth + 10
+ Layout.maximumWidth: fpsText.implicitWidth + 10
+ Layout.minimumHeight: 50
+ Layout.maximumHeight: 50
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
+
+ border.color: "gray"
+ border.width: 1
+ radius: 4
+
+ Text {
+ id: fpsText
+ anchors.fill: parent
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+ }
+
+ RowLayout {
+ id: buttonLayout
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.minimumHeight: 50
+ anchors.bottom: parent.bottom
+ spacing: 0
+
+ NewButton {
+ id: sliceButton
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ text: "Slice"
+
+ onClicked: {
+ if (volumeItem.sliceIndexZ == -1) {
+ volumeItem.sliceIndexZ = 128
+ volumeItem.drawSlices = true
+ volumeItem.drawSliceFrames = true
+ } else {
+ volumeItem.sliceIndexZ = -1
+ volumeItem.drawSlices = false
+ volumeItem.drawSliceFrames = false
+ }
+ }
+ }
+ NewButton {
+ id: exitButton
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ text: "Quit"
+
+ onClicked: Qt.quit(0);
+ }
+ }
+ }
+
+ }
+
+ Custom3DVolume {
+ id: volumeItem
+ alphaMultiplier: 0.3
+ preserveOpacity: true
+ useHighDefShader: false
+ }
+
+ function createVolume() {
+ surfaceGraph.addCustomItem(volumeItem)
+ dataSource.fillVolume(volumeItem)
+ }
+}
diff --git a/tests/qmlvolume/qmlvolume.pro b/tests/qmlvolume/qmlvolume.pro
new file mode 100644
index 00000000..1d58b668
--- /dev/null
+++ b/tests/qmlvolume/qmlvolume.pro
@@ -0,0 +1,16 @@
+!include( ../tests.pri ) {
+ error( "Couldn't find the tests.pri file!" )
+}
+
+QT += datavisualization
+
+# The .cpp file which was generated for your project. Feel free to hack it.
+SOURCES += main.cpp \
+ datasource.cpp
+HEADERS += datasource.h
+
+RESOURCES += qmlvolume.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/* \
+ qml/qmlvolume/*
diff --git a/tests/qmlvolume/qmlvolume.qrc b/tests/qmlvolume/qmlvolume.qrc
new file mode 100644
index 00000000..18fe57e1
--- /dev/null
+++ b/tests/qmlvolume/qmlvolume.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/qmlvolume/main.qml</file>
+ <file>qml/qmlvolume/NewButton.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp
index 207da530..9b6f51ac 100644
--- a/tests/scattertest/main.cpp
+++ b/tests/scattertest/main.cpp
@@ -36,11 +36,13 @@
int main(int argc, char **argv)
{
QApplication app(argc, argv);
+ //QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
QWidget *widget = new QWidget;
QHBoxLayout *hLayout = new QHBoxLayout(widget);
QVBoxLayout *vLayout = new QVBoxLayout();
QVBoxLayout *vLayout2 = new QVBoxLayout();
+ QVBoxLayout *vLayout3 = new QVBoxLayout();
Q3DScatter *chart = new Q3DScatter();
QSize screenSize = chart->screen()->size();
@@ -56,6 +58,7 @@ int main(int argc, char **argv)
hLayout->addWidget(container, 1);
hLayout->addLayout(vLayout);
hLayout->addLayout(vLayout2);
+ hLayout->addLayout(vLayout3);
QPushButton *themeButton = new QPushButton(widget);
themeButton->setText(QStringLiteral("Change theme"));
@@ -235,6 +238,25 @@ int main(int argc, char **argv)
aspectRatioSlider->setValue(20);
aspectRatioSlider->setMaximum(100);
+ QSlider *horizontalAspectRatioSlider = new QSlider(Qt::Horizontal, widget);
+ horizontalAspectRatioSlider->setTickInterval(30);
+ horizontalAspectRatioSlider->setTickPosition(QSlider::TicksBelow);
+ horizontalAspectRatioSlider->setMinimum(0);
+ horizontalAspectRatioSlider->setValue(0);
+ horizontalAspectRatioSlider->setMaximum(300);
+
+ QCheckBox *optimizationStaticCB = new QCheckBox(widget);
+ optimizationStaticCB->setText(QStringLiteral("Static optimization"));
+ optimizationStaticCB->setChecked(false);
+
+ QCheckBox *orthoCB = new QCheckBox(widget);
+ orthoCB->setText(QStringLiteral("Orthographic projection"));
+ orthoCB->setChecked(false);
+
+ QCheckBox *polarCB = new QCheckBox(widget);
+ polarCB->setText(QStringLiteral("Polar graph"));
+ polarCB->setChecked(false);
+
QCheckBox *axisTitlesVisibleCB = new QCheckBox(widget);
axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible"));
axisTitlesVisibleCB->setChecked(false);
@@ -250,6 +272,34 @@ int main(int argc, char **argv)
axisLabelRotationSlider->setValue(0);
axisLabelRotationSlider->setMaximum(90);
+ QSlider *radialLabelSlider = new QSlider(Qt::Horizontal, widget);
+ radialLabelSlider->setTickInterval(10);
+ radialLabelSlider->setTickPosition(QSlider::TicksBelow);
+ radialLabelSlider->setMinimum(0);
+ radialLabelSlider->setValue(100);
+ radialLabelSlider->setMaximum(150);
+
+ QSlider *cameraTargetSliderX = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderX->setTickInterval(1);
+ cameraTargetSliderX->setMinimum(-100);
+ cameraTargetSliderX->setValue(0);
+ cameraTargetSliderX->setMaximum(100);
+ QSlider *cameraTargetSliderY = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderY->setTickInterval(1);
+ cameraTargetSliderY->setMinimum(-100);
+ cameraTargetSliderY->setValue(0);
+ cameraTargetSliderY->setMaximum(100);
+ QSlider *cameraTargetSliderZ = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderZ->setTickInterval(1);
+ cameraTargetSliderZ->setMinimum(-100);
+ cameraTargetSliderZ->setValue(0);
+ cameraTargetSliderZ->setMaximum(100);
+
+ QSlider *marginSlider = new QSlider(Qt::Horizontal, widget);
+ marginSlider->setMinimum(-1);
+ marginSlider->setValue(-1);
+ marginSlider->setMaximum(100);
+
vLayout->addWidget(themeButton, 0, Qt::AlignTop);
vLayout->addWidget(labelButton, 0, Qt::AlignTop);
vLayout->addWidget(styleButton, 0, Qt::AlignTop);
@@ -296,12 +346,26 @@ int main(int argc, char **argv)
vLayout2->addWidget(fontList);
vLayout2->addWidget(new QLabel(QStringLiteral("Adjust font size")));
vLayout2->addWidget(fontSizeSlider);
- vLayout2->addWidget(new QLabel(QStringLiteral("Adjust aspect ratio")));
- vLayout2->addWidget(aspectRatioSlider, 1, Qt::AlignTop);
- vLayout2->addWidget(axisTitlesVisibleCB);
- vLayout2->addWidget(axisTitlesFixedCB);
- vLayout2->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
- vLayout2->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust vertical aspect ratio")));
+ vLayout2->addWidget(aspectRatioSlider);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust horizontal aspect ratio")));
+ vLayout2->addWidget(horizontalAspectRatioSlider, 1, Qt::AlignTop);
+
+ vLayout3->addWidget(optimizationStaticCB);
+ vLayout3->addWidget(orthoCB);
+ vLayout3->addWidget(polarCB);
+ vLayout3->addWidget(axisTitlesVisibleCB);
+ vLayout3->addWidget(axisTitlesFixedCB);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
+ vLayout3->addWidget(axisLabelRotationSlider);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Radial label offset")));
+ vLayout3->addWidget(radialLabelSlider, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Camera target")), 0, Qt::AlignTop);
+ vLayout3->addWidget(cameraTargetSliderX, 0, Qt::AlignTop);
+ vLayout3->addWidget(cameraTargetSliderY, 0, Qt::AlignTop);
+ vLayout3->addWidget(cameraTargetSliderZ, 0, Qt::AlignTop);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Adjust margin")), 0, Qt::AlignTop);
+ vLayout3->addWidget(marginSlider, 1, Qt::AlignTop);
ScatterDataModifier *modifier = new ScatterDataModifier(chart);
@@ -389,6 +453,12 @@ int main(int argc, char **argv)
&ScatterDataModifier::setMaxY);
QObject::connect(maxSliderZ, &QSlider::valueChanged, modifier,
&ScatterDataModifier::setMaxZ);
+ QObject::connect(optimizationStaticCB, &QCheckBox::stateChanged, modifier,
+ &ScatterDataModifier::toggleStatic);
+ QObject::connect(orthoCB, &QCheckBox::stateChanged, modifier,
+ &ScatterDataModifier::toggleOrtho);
+ QObject::connect(polarCB, &QCheckBox::stateChanged, modifier,
+ &ScatterDataModifier::togglePolar);
QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier,
&ScatterDataModifier::toggleAxisTitleVisibility);
QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier,
@@ -397,13 +467,25 @@ int main(int argc, char **argv)
&ScatterDataModifier::changeLabelRotation);
QObject::connect(aspectRatioSlider, &QSlider::valueChanged, modifier,
&ScatterDataModifier::setAspectRatio);
+ QObject::connect(horizontalAspectRatioSlider, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setHorizontalAspectRatio);
+ QObject::connect(radialLabelSlider, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::changeRadialLabelOffset);
+ QObject::connect(cameraTargetSliderX, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setCameraTargetX);
+ QObject::connect(cameraTargetSliderY, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setCameraTargetY);
+ QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setCameraTargetZ);
+ QObject::connect(marginSlider, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setGraphMargin);
modifier->setFpsLabel(fpsLabel);
chart->setGeometry(QRect(0, 0, 800, 800));
modifier->start();
- modifier->renderToImage(); // Initial hidden render
+ //modifier->renderToImage(); // Initial hidden render
widget->show();
diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp
index c00c526a..7d625d3f 100644
--- a/tests/scattertest/scatterchart.cpp
+++ b/tests/scattertest/scatterchart.cpp
@@ -23,6 +23,7 @@
#include <QtDataVisualization/q3dscene.h>
#include <QtDataVisualization/q3dcamera.h>
#include <QtDataVisualization/q3dtheme.h>
+#include <QtDataVisualization/Q3DInputHandler>
#include <qmath.h>
using namespace QtDataVisualization;
@@ -46,6 +47,8 @@ ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter)
m_chart->setAxisX(new QValue3DAxis);
m_chart->setAxisY(new QValue3DAxis);
m_chart->setAxisZ(new QValue3DAxis);
+ m_chart->axisY()->setLabelFormat(QStringLiteral("%.7f"));
+ static_cast<Q3DInputHandler *>(m_chart->activeInputHandler())->setZoomAtTargetEnabled(true);
createAndAddSeries();
createAndAddSeries();
@@ -435,6 +438,10 @@ void ScatterDataModifier::testAxisReverse()
m_chart->axisX()->setRange(0.0f, 10.0f);
m_chart->axisY()->setRange(-20.0f, 50.0f);
m_chart->axisZ()->setRange(5.0f, 15.0f);
+ m_chart->axisX()->setTitle("Axis X");
+ m_chart->axisZ()->setTitle("Axis Z");
+ m_chart->axisX()->setTitleVisible(true);
+ m_chart->axisZ()->setTitleVisible(true);
m_chart->addSeries(series0);
m_chart->addSeries(series1);
}
@@ -484,9 +491,9 @@ void ScatterDataModifier::addData()
m_chart->axisX()->setRange(-50.0f, 50.0f);
m_chart->axisY()->setRange(-1.0f, 1.2f);
m_chart->axisZ()->setRange(-50.0f, 50.0f);
- m_chart->axisX()->setSegmentCount(6);
+ m_chart->axisX()->setSegmentCount(5);
m_chart->axisY()->setSegmentCount(4);
- m_chart->axisZ()->setSegmentCount(9);
+ m_chart->axisZ()->setSegmentCount(10);
m_chart->axisX()->setSubSegmentCount(2);
m_chart->axisY()->setSubSegmentCount(3);
m_chart->axisZ()->setSubSegmentCount(1);
@@ -717,8 +724,24 @@ void ScatterDataModifier::changeBunch()
if (m_targetSeries->dataProxy()->array()->size()) {
int amount = qMin(m_targetSeries->dataProxy()->array()->size(), 100);
QScatterDataArray items(amount);
- for (int i = 0; i < items.size(); i++)
+ for (int i = 0; i < items.size(); i++) {
items[i].setPosition(randVector());
+ // Change the Y-values of first few items to exact gradient boundaries
+ if (i == 0)
+ items[i].setY(0.65f);
+ else if (i == 1)
+ items[i].setY(0.1f);
+ else if (i == 2)
+ items[i].setY(-0.45f);
+ else if (i == 3)
+ items[i].setY(-1.0f);
+ else if (i == 4)
+ items[i].setY(1.2f);
+// else
+// items[i].setY(0.1001f - (0.00001f * float(i)));
+
+ }
+
m_targetSeries->dataProxy()->setItems(0, items);
qDebug() << m_loopCounter << "Changed bunch, array size:" << m_targetSeries->dataProxy()->array()->size();
}
@@ -934,6 +957,11 @@ void ScatterDataModifier::changeLabelRotation(int rotation)
m_chart->axisZ()->setLabelAutoRotation(float(rotation));
}
+void ScatterDataModifier::changeRadialLabelOffset(int offset)
+{
+ m_chart->setRadialLabelOffset(float(offset) / 100.0f);
+}
+
void ScatterDataModifier::toggleAxisTitleVisibility(bool enabled)
{
m_chart->axisX()->setTitleVisible(enabled);
@@ -970,6 +998,54 @@ void ScatterDataModifier::renderToImage()
}
}
+void ScatterDataModifier::togglePolar(bool enable)
+{
+ m_chart->setPolar(enable);
+}
+
+void ScatterDataModifier::toggleStatic(bool enable)
+{
+ if (enable)
+ m_chart->setOptimizationHints(QAbstract3DGraph::OptimizationStatic);
+ else
+ m_chart->setOptimizationHints(QAbstract3DGraph::OptimizationDefault);
+}
+
+void ScatterDataModifier::toggleOrtho(bool enable)
+{
+ m_chart->setOrthoProjection(enable);
+}
+
+void ScatterDataModifier::setCameraTargetX(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setX(float(value) / 100.0f);
+ m_chart->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void ScatterDataModifier::setCameraTargetY(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setY(float(value) / 100.0f);
+ m_chart->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void ScatterDataModifier::setCameraTargetZ(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setZ(float(value) / 100.0f);
+ m_chart->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void ScatterDataModifier::setGraphMargin(int value)
+{
+ m_chart->setMargin(qreal(value) / 100.0);
+ qDebug() << "Setting margin:" << m_chart->margin() << value;
+}
+
void ScatterDataModifier::changeShadowQuality(int quality)
{
QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality);
@@ -1019,10 +1095,16 @@ void ScatterDataModifier::setMaxZ(int max)
void ScatterDataModifier::setAspectRatio(int ratio)
{
- float aspectRatio = float(ratio) / 10.0f;
+ qreal aspectRatio = qreal(ratio) / 10.0;
m_chart->setAspectRatio(aspectRatio);
}
+void ScatterDataModifier::setHorizontalAspectRatio(int ratio)
+{
+ qreal aspectRatio = qreal(ratio) / 100.0;
+ m_chart->setHorizontalAspectRatio(aspectRatio);
+}
+
QVector3D ScatterDataModifier::randVector()
{
QVector3D retvec = QVector3D(
diff --git a/tests/scattertest/scatterchart.h b/tests/scattertest/scatterchart.h
index 97c3b1f9..a2b0f58e 100644
--- a/tests/scattertest/scatterchart.h
+++ b/tests/scattertest/scatterchart.h
@@ -53,6 +53,7 @@ public:
void setMaxY(int max);
void setMaxZ(int max);
void setAspectRatio(int ratio);
+ void setHorizontalAspectRatio(int ratio);
void start();
void massiveDataTest();
void massiveTestScroll();
@@ -90,9 +91,17 @@ public slots:
void handleAxisZChanged(QValue3DAxis *axis);
void handleFpsChange(qreal fps);
void changeLabelRotation(int rotation);
+ void changeRadialLabelOffset(int offset);
void toggleAxisTitleVisibility(bool enabled);
void toggleAxisTitleFixed(bool enabled);
void renderToImage();
+ void togglePolar(bool enable);
+ void toggleStatic(bool enable);
+ void toggleOrtho(bool enable);
+ void setCameraTargetX(int value);
+ void setCameraTargetY(int value);
+ void setCameraTargetZ(int value);
+ void setGraphMargin(int value);
signals:
void shadowQualityChanged(int quality);
@@ -113,6 +122,8 @@ private:
QScatter3DSeries *m_targetSeries;
QScatterDataArray m_massiveTestCacheArray;
QLabel *m_fpsLabel;
+ QVector3D m_cameraTarget;
+
};
#endif
diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp
index ed86f03c..7192f0dc 100644
--- a/tests/surfacetest/graphmodifier.cpp
+++ b/tests/surfacetest/graphmodifier.cpp
@@ -21,18 +21,21 @@
#include <QtDataVisualization/QSurfaceDataProxy>
#include <QtDataVisualization/QSurface3DSeries>
#include <QtDataVisualization/Q3DTheme>
+#include <QtDataVisualization/Q3DInputHandler>
#include <qmath.h>
#include <QLinearGradient>
#include <QDebug>
#include <QComboBox>
-
+#ifndef QT_NO_CURSOR
+#include <QtGui/QCursor>
+#endif
using namespace QtDataVisualization;
//#define JITTER_PLANE
//#define WONKY_PLANE
-GraphModifier::GraphModifier(Q3DSurface *graph)
+GraphModifier::GraphModifier(Q3DSurface *graph, QWidget *parentWidget)
: m_graph(graph),
m_series1(new QSurface3DSeries),
m_series2(new QSurface3DSeries),
@@ -48,12 +51,12 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
m_zCount(24),
m_activeSample(0),
m_fontSize(40),
- m_rangeX(16.0),
+ m_rangeX(34.0),
m_rangeY(16.0),
- m_rangeZ(16.0),
- m_minX(-8.0),
+ m_rangeZ(34.0),
+ m_minX(-17.0),
m_minY(-8.0),
- m_minZ(-8.0),
+ m_minZ(-17.0),
m_addRowCounter(m_zCount),
m_insertTestZPos(0),
m_insertTestIndexPos(1),
@@ -63,7 +66,10 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
m_drawMode2(QSurface3DSeries::DrawSurfaceAndWireframe),
m_drawMode3(QSurface3DSeries::DrawSurfaceAndWireframe),
m_drawMode4(QSurface3DSeries::DrawSurfaceAndWireframe),
- m_offset(4.0f)
+ m_offset(4.0f),
+ m_parentWidget(parentWidget),
+ m_ascendingX(true),
+ m_ascendingZ(true)
{
m_graph->setAxisX(new QValue3DAxis);
m_graph->axisX()->setTitle("X-Axis");
@@ -97,6 +103,8 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
m_graph->axisY()->setRange(m_minY, m_minY + m_rangeY);
m_graph->axisZ()->setRange(m_minZ, m_minZ + m_rangeZ);
+ static_cast<Q3DInputHandler *>(m_graph->activeInputHandler())->setZoomAtTargetEnabled(true);
+
for (int i = 0; i < 4; i++) {
m_multiseries[i] = new QSurface3DSeries;
m_multiseries[i]->setName(QStringLiteral("Series %1").arg(i+1));
@@ -109,6 +117,7 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
m_theSeries->setItemLabelFormat(QStringLiteral("@seriesName: (@xLabel, @zLabel): @yLabel"));
connect(&m_timer, &QTimer::timeout, this, &GraphModifier::timeout);
+ connect(&m_graphPositionQueryTimer, &QTimer::timeout, this, &GraphModifier::graphQueryTimeout);
connect(m_theSeries, &QSurface3DSeries::selectedPointChanged, this, &GraphModifier::selectedPointChanged);
QObject::connect(m_graph, &Q3DSurface::axisXChanged, this,
@@ -119,6 +128,8 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
&GraphModifier::handleAxisZChanged);
QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
&GraphModifier::handleFpsChange);
+
+ //m_graphPositionQueryTimer.start(100);
}
GraphModifier::~GraphModifier()
@@ -159,7 +170,6 @@ void GraphModifier::fillSeries()
(*newRow[s])[j].setPosition(QVector3D(x, y, z));
}
}
- qDebug() << newRow[0]->at(0).z();
*dataArray1 << newRow[0];
*dataArray2 << newRow[1];
*dataArray3 << newRow[2];
@@ -606,19 +616,30 @@ void GraphModifier::adjustZMin(int min)
void GraphModifier::gradientPressed()
{
+ static Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform;
+
+ if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
+ colorStyle = Q3DTheme::ColorStyleObjectGradient;
+ qDebug() << "Color style: ColorStyleObjectGradient";
+ } else if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
+ colorStyle = Q3DTheme::ColorStyleUniform;
+ qDebug() << "Color style: ColorStyleUniform";
+ } else {
+ colorStyle = Q3DTheme::ColorStyleRangeGradient;
+ qDebug() << "Color style: ColorStyleRangeGradient";
+ }
+
QLinearGradient gradient;
gradient.setColorAt(0.0, Qt::black);
gradient.setColorAt(0.33, Qt::blue);
gradient.setColorAt(0.67, Qt::red);
gradient.setColorAt(1.0, Qt::yellow);
-// m_graph->seriesList().at(0)->setBaseGradient(gradient);
-// m_graph->seriesList().at(0)->setSingleHighlightColor(Qt::red);
-// m_graph->seriesList().at(0)->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
QList<QLinearGradient> gradients;
gradients << gradient;
m_graph->activeTheme()->setBaseGradients(gradients);
- m_graph->activeTheme()->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
+ m_graph->activeTheme()->setColorStyle(colorStyle);
+
}
void GraphModifier::changeFont(const QFont &font)
@@ -680,6 +701,16 @@ void GraphModifier::timeout()
m_theSeries->dataProxy()->resetArray(m_planeArray);
}
+void GraphModifier::graphQueryTimeout()
+{
+#ifndef QT_NO_CURSOR
+ m_graph->scene()->setGraphPositionQuery(m_parentWidget->mapFromGlobal(QCursor::pos()));
+ qDebug() << "pos: " << (m_parentWidget->mapFromGlobal(QCursor::pos()));
+#else
+ m_graph->scene()->setGraphPositionQuery(QPoint(100, 100));
+#endif
+}
+
void GraphModifier::handleAxisXChanged(QValue3DAxis *axis)
{
qDebug() << __FUNCTION__ << axis << axis->orientation() << (axis == m_graph->axisX());
@@ -723,6 +754,8 @@ void GraphModifier::toggleAxisTitleFixed(bool enabled)
void GraphModifier::toggleXAscending(bool enabled)
{
+ m_ascendingX = enabled;
+
// Flip data array contents if necessary
foreach (QSurface3DSeries *series, m_graph->seriesList()) {
QSurfaceDataArray *array = const_cast<QSurfaceDataArray *>(series->dataProxy()->array());
@@ -751,6 +784,8 @@ void GraphModifier::toggleXAscending(bool enabled)
void GraphModifier::toggleZAscending(bool enabled)
{
+ m_ascendingZ = enabled;
+
// Flip data array contents if necessary
foreach (QSurface3DSeries *series, m_graph->seriesList()) {
QSurfaceDataArray *array = const_cast<QSurfaceDataArray *>(series->dataProxy()->array());
@@ -777,6 +812,41 @@ void GraphModifier::toggleZAscending(bool enabled)
}
}
+void GraphModifier::togglePolar(bool enabled)
+{
+ m_graph->setPolar(enabled);
+}
+
+void GraphModifier::setCameraTargetX(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setX(float(value) / 100.0f);
+ m_graph->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void GraphModifier::setCameraTargetY(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setY(float(value) / 100.0f);
+ m_graph->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void GraphModifier::setCameraTargetZ(int value)
+{
+ // Value is (-100, 100), normalize
+ m_cameraTarget.setZ(float(value) / 100.0f);
+ m_graph->scene()->activeCamera()->setTarget(m_cameraTarget);
+ qDebug() << "m_cameraTarget:" << m_cameraTarget;
+}
+
+void GraphModifier::setGraphMargin(int value)
+{
+ m_graph->setMargin(qreal(value) / 100.0);
+ qDebug() << "Setting margin:" << m_graph->margin() << value;
+}
+
void GraphModifier::resetArrayAndSliders(QSurfaceDataArray *array, float minZ, float maxZ, float minX, float maxX)
{
m_axisMinSliderX->setValue(minX);
@@ -831,7 +901,10 @@ void GraphModifier::changeRow()
int row = rand() % m_zCount;
QSurfaceDataRow *newRow = createMultiRow(row, changeRowSeries, true);
- m_multiseries[changeRowSeries]->dataProxy()->setRow(row, newRow);
+ if (m_ascendingZ)
+ m_multiseries[changeRowSeries]->dataProxy()->setRow(row, newRow);
+ else
+ m_multiseries[changeRowSeries]->dataProxy()->setRow((m_zCount - 1) - row, newRow);
changeRowSeries++;
if (changeRowSeries > 3)
@@ -848,11 +921,20 @@ QSurfaceDataRow *GraphModifier::createMultiRow(int row, int series, bool change)
float i = float(row);
QSurfaceDataRow *newRow = new QSurfaceDataRow(m_xCount);
float z = float(i) - m_limitZ + 0.5f + m_multiSampleOffsetZ[series];
- for (int j = 0; j < m_xCount; j++) {
- float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[series];
- float angle = (z * x) / float(full) * 1.57f;
- float y = qSin(angle * float(qPow(1.3f, series))) + 0.2f * float(change) + 1.1f *series;
- (*newRow)[j].setPosition(QVector3D(x, y, z));
+ if (m_ascendingX) {
+ for (int j = 0; j < m_xCount; j++) {
+ float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[series];
+ float angle = (z * x) / float(full) * 1.57f;
+ float y = qSin(angle * float(qPow(1.3f, series))) + 0.2f * float(change) + 1.1f *series;
+ (*newRow)[j].setPosition(QVector3D(x, y, z));
+ }
+ } else {
+ for (int j = m_xCount - 1; j >= 0 ; j--) {
+ float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[series];
+ float angle = (z * x) / float(full) * 1.57f;
+ float y = qSin(angle * float(qPow(1.3f, series))) + 0.2f * float(change) + 1.1f *series;
+ (*newRow)[(m_xCount - 1) - j].setPosition(QVector3D(x, y, z));
+ }
}
return newRow;
@@ -961,7 +1043,16 @@ void GraphModifier::changeItem()
float angle = (z * x) / float(full) * 1.57f;
float y = qSin(angle * float(qPow(1.3f, changeItemSeries))) + 0.2f + 1.1f *changeItemSeries;
QSurfaceDataItem newItem(QVector3D(x, y, z));
- m_multiseries[changeItemSeries]->dataProxy()->setItem(int(i), int(j), newItem);
+
+ if (m_ascendingZ && m_ascendingX)
+ m_multiseries[changeItemSeries]->dataProxy()->setItem(int(i), int(j), newItem);
+ else if (!m_ascendingZ && m_ascendingX)
+ m_multiseries[changeItemSeries]->dataProxy()->setItem(m_zCount - 1 - int(i), int(j), newItem);
+ else if (m_ascendingZ && !m_ascendingX)
+ m_multiseries[changeItemSeries]->dataProxy()->setItem(int(i), m_xCount - 1 - int(j), newItem);
+ else
+ m_multiseries[changeItemSeries]->dataProxy()->setItem(m_zCount - 1 - int(i), m_xCount - 1 - int(j), newItem);
+ //m_multiseries[changeItemSeries]->setSelectedPoint(QPoint(i, j));
changeItemSeries++;
if (changeItemSeries > 3)
changeItemSeries = 0;
@@ -1228,12 +1319,12 @@ void GraphModifier::resetArray()
void GraphModifier::resetArrayEmpty()
{
- QSurfaceDataArray *emptryArray = new QSurfaceDataArray;
+ QSurfaceDataArray *emptyArray = new QSurfaceDataArray;
#ifdef MULTI_SERIES
int series = rand() % 4;
- m_multiseries[series]->dataProxy()->resetArray(emptryArray);
+ m_multiseries[series]->dataProxy()->resetArray(emptyArray);
#else
- m_theSeries->dataProxy()->resetArray(emptryArray);
+ m_theSeries->dataProxy()->resetArray(emptyArray);
#endif
}
@@ -1569,6 +1660,20 @@ void GraphModifier::updateSamples()
void GraphModifier::setAspectRatio(int ratio)
{
- float aspectRatio = float(ratio) / 10.0f;
+ qreal aspectRatio = qreal(ratio) / 10.0;
m_graph->setAspectRatio(aspectRatio);
}
+
+void GraphModifier::setHorizontalAspectRatio(int ratio)
+{
+ qreal aspectRatio = qreal(ratio) / 100.0;
+ m_graph->setHorizontalAspectRatio(aspectRatio);
+}
+
+void GraphModifier::setSurfaceTexture(bool enabled)
+{
+ if (enabled)
+ m_multiseries[3]->setTexture(QImage(":/maps/mapimage"));
+ else
+ m_multiseries[3]->setTexture(QImage());
+}
diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h
index 5f1a252a..223a6854 100644
--- a/tests/surfacetest/graphmodifier.h
+++ b/tests/surfacetest/graphmodifier.h
@@ -41,7 +41,7 @@ public:
Map
};
- explicit GraphModifier(Q3DSurface *graph);
+ explicit GraphModifier(Q3DSurface *graph, QWidget *parentWidget);
~GraphModifier();
void toggleSeries1(bool enabled);
@@ -112,8 +112,9 @@ public:
void massiveTestAppendAndScroll();
void testAxisReverse();
void testDataOrdering();
-
void setAspectRatio(int ratio);
+ void setHorizontalAspectRatio(int ratio);
+ void setSurfaceTexture(bool enabled);
public slots:
void changeShadowQuality(int quality);
@@ -121,6 +122,7 @@ public slots:
void flipViews();
void changeSelectionMode(int mode);
void timeout();
+ void graphQueryTimeout();
void handleAxisXChanged(QValue3DAxis *axis);
void handleAxisYChanged(QValue3DAxis *axis);
@@ -131,6 +133,11 @@ public slots:
void toggleAxisTitleFixed(bool enabled);
void toggleXAscending(bool enabled);
void toggleZAscending(bool enabled);
+ void togglePolar(bool enabled);
+ void setCameraTargetX(int value);
+ void setCameraTargetY(int value);
+ void setCameraTargetZ(int value);
+ void setGraphMargin(int value);
private:
void fillSeries();
@@ -184,6 +191,11 @@ private:
float m_multiSampleOffsetX[4];
float m_multiSampleOffsetZ[4];
QSurfaceDataArray m_massiveTestCacheArray;
+ QVector3D m_cameraTarget;
+ QWidget *m_parentWidget;
+ QTimer m_graphPositionQueryTimer;
+ bool m_ascendingX;
+ bool m_ascendingZ;
};
#endif
diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp
index 5806d7b0..d1328e4e 100644
--- a/tests/surfacetest/main.cpp
+++ b/tests/surfacetest/main.cpp
@@ -46,8 +46,10 @@ int main(int argc, char *argv[])
QHBoxLayout *hLayout = new QHBoxLayout(widget);
QVBoxLayout *vLayout = new QVBoxLayout();
QVBoxLayout *vLayout2 = new QVBoxLayout();
+ QVBoxLayout *vLayout3 = new QVBoxLayout();
vLayout->setAlignment(Qt::AlignTop);
vLayout2->setAlignment(Qt::AlignTop);
+ vLayout3->setAlignment(Qt::AlignTop);
Q3DSurface *surfaceGraph = new Q3DSurface();
QSize screenSize = surfaceGraph->screen()->size();
@@ -56,7 +58,7 @@ int main(int argc, char *argv[])
surfaceGraph->activeTheme()->setType(Q3DTheme::Theme(initialTheme));
QWidget *container = QWidget::createWindowContainer(surfaceGraph);
- container->setMinimumSize(QSize(screenSize.width() / 4, screenSize.height() / 4));
+ container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 4));
container->setMaximumSize(screenSize);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
container->setFocusPolicy(Qt::StrongFocus);
@@ -66,6 +68,7 @@ int main(int argc, char *argv[])
hLayout->addWidget(container, 1);
hLayout->addLayout(vLayout);
hLayout->addLayout(vLayout2);
+ hLayout->addLayout(vLayout3);
QCheckBox *smoothCB = new QCheckBox(widget);
smoothCB->setText(QStringLiteral("Flat Surface"));
@@ -183,7 +186,7 @@ int main(int argc, char *argv[])
QSlider *axisRangeSliderX = new QSlider(Qt::Horizontal, widget);
axisRangeSliderX->setTickInterval(1);
axisRangeSliderX->setMinimum(1);
- axisRangeSliderX->setValue(16);
+ axisRangeSliderX->setValue(34);
axisRangeSliderX->setMaximum(100);
axisRangeSliderX->setEnabled(true);
QSlider *axisRangeSliderY = new QSlider(Qt::Horizontal, widget);
@@ -195,14 +198,14 @@ int main(int argc, char *argv[])
QSlider *axisRangeSliderZ = new QSlider(Qt::Horizontal, widget);
axisRangeSliderZ->setTickInterval(1);
axisRangeSliderZ->setMinimum(1);
- axisRangeSliderZ->setValue(16);
+ axisRangeSliderZ->setValue(34);
axisRangeSliderZ->setMaximum(100);
axisRangeSliderZ->setEnabled(true);
QSlider *axisMinSliderX = new QSlider(Qt::Horizontal, widget);
axisMinSliderX->setTickInterval(1);
axisMinSliderX->setMinimum(-100);
- axisMinSliderX->setValue(-8);
+ axisMinSliderX->setValue(-17);
axisMinSliderX->setMaximum(100);
axisMinSliderX->setEnabled(true);
QSlider *axisMinSliderY = new QSlider(Qt::Horizontal, widget);
@@ -214,7 +217,7 @@ int main(int argc, char *argv[])
QSlider *axisMinSliderZ = new QSlider(Qt::Horizontal, widget);
axisMinSliderZ->setTickInterval(1);
axisMinSliderZ->setMinimum(-100);
- axisMinSliderZ->setValue(-8);
+ axisMinSliderZ->setValue(-17);
axisMinSliderZ->setMaximum(100);
axisMinSliderZ->setEnabled(true);
@@ -223,6 +226,11 @@ int main(int argc, char *argv[])
aspectRatioSlider->setValue(20);
aspectRatioSlider->setMaximum(100);
+ QSlider *horizontalAspectRatioSlider = new QSlider(Qt::Horizontal, widget);
+ horizontalAspectRatioSlider->setMinimum(0);
+ horizontalAspectRatioSlider->setValue(0);
+ horizontalAspectRatioSlider->setMaximum(300);
+
QLinearGradient gr(0, 0, 100, 1);
gr.setColorAt(0.0, Qt::black);
gr.setColorAt(0.33, Qt::blue);
@@ -393,6 +401,35 @@ int main(int argc, char *argv[])
zAscendingCB->setText(QStringLiteral("Z Ascending"));
zAscendingCB->setChecked(true);
+ QCheckBox *polarCB = new QCheckBox(widget);
+ polarCB->setText(QStringLiteral("Polar"));
+ polarCB->setChecked(false);
+
+ QCheckBox *surfaceTextureCB = new QCheckBox(widget);
+ surfaceTextureCB->setText(QStringLiteral("Map texture"));
+ surfaceTextureCB->setChecked(false);
+
+ QSlider *cameraTargetSliderX = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderX->setTickInterval(1);
+ cameraTargetSliderX->setMinimum(-100);
+ cameraTargetSliderX->setValue(0);
+ cameraTargetSliderX->setMaximum(100);
+ QSlider *cameraTargetSliderY = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderY->setTickInterval(1);
+ cameraTargetSliderY->setMinimum(-100);
+ cameraTargetSliderY->setValue(0);
+ cameraTargetSliderY->setMaximum(100);
+ QSlider *cameraTargetSliderZ = new QSlider(Qt::Horizontal, widget);
+ cameraTargetSliderZ->setTickInterval(1);
+ cameraTargetSliderZ->setMinimum(-100);
+ cameraTargetSliderZ->setValue(0);
+ cameraTargetSliderZ->setMaximum(100);
+
+ QSlider *marginSlider = new QSlider(Qt::Horizontal, widget);
+ marginSlider->setMinimum(-1);
+ marginSlider->setValue(-1);
+ marginSlider->setMaximum(100);
+
// Add controls to the layout
#ifdef MULTI_SERIES
vLayout->addWidget(series1CB);
@@ -420,6 +457,7 @@ int main(int argc, char *argv[])
vLayout->addWidget(surfaceGridS4CB);
vLayout->addWidget(surfaceS4CB);
vLayout->addWidget(series4VisibleCB);
+ vLayout->addWidget(surfaceTextureCB, 1, Qt::AlignTop);
#endif
#ifndef MULTI_SERIES
vLayout->addWidget(new QLabel(QStringLiteral("Select surface sample")));
@@ -429,60 +467,71 @@ int main(int argc, char *argv[])
vLayout->addWidget(new QLabel(QStringLiteral("Adjust sample count")));
vLayout->addWidget(gridSlidersLockCB);
vLayout->addWidget(gridSliderX);
- vLayout->addWidget(gridSliderZ);
+ vLayout->addWidget(gridSliderZ, 1, Qt::AlignTop);
#endif
- vLayout->addWidget(new QLabel(QStringLiteral("Adjust aspect ratio")));
- vLayout->addWidget(aspectRatioSlider);
- vLayout->addWidget(new QLabel(QStringLiteral("Adjust axis range")));
- vLayout->addWidget(axisRangeSliderX);
- vLayout->addWidget(axisRangeSliderY);
- vLayout->addWidget(axisRangeSliderZ);
- vLayout->addWidget(new QLabel(QStringLiteral("Adjust axis minimum")));
- vLayout->addWidget(axisMinSliderX);
- vLayout->addWidget(axisMinSliderY);
- vLayout->addWidget(axisMinSliderZ);
- vLayout->addWidget(xAscendingCB);
- vLayout->addWidget(zAscendingCB);
+
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust vertical aspect ratio")));
+ vLayout2->addWidget(aspectRatioSlider);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust horizontal aspect ratio")));
+ vLayout2->addWidget(horizontalAspectRatioSlider);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust axis range")));
+ vLayout2->addWidget(axisRangeSliderX);
+ vLayout2->addWidget(axisRangeSliderY);
+ vLayout2->addWidget(axisRangeSliderZ);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust axis minimum")));
+ vLayout2->addWidget(axisMinSliderX);
+ vLayout2->addWidget(axisMinSliderY);
+ vLayout2->addWidget(axisMinSliderZ);
+ vLayout2->addWidget(xAscendingCB);
+ vLayout2->addWidget(zAscendingCB);
+ vLayout2->addWidget(polarCB);
vLayout2->addWidget(new QLabel(QStringLiteral("Change font")));
vLayout2->addWidget(fontList);
- vLayout2->addWidget(labelButton);
- vLayout2->addWidget(meshButton);
vLayout2->addWidget(new QLabel(QStringLiteral("Change theme")));
vLayout2->addWidget(themeList);
vLayout2->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")));
vLayout2->addWidget(shadowQuality);
vLayout2->addWidget(new QLabel(QStringLiteral("Selection Mode")));
vLayout2->addWidget(selectionMode);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Camera target")));
+ vLayout2->addWidget(cameraTargetSliderX);
+ vLayout2->addWidget(cameraTargetSliderY);
+ vLayout2->addWidget(cameraTargetSliderZ);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust margin")), 0, Qt::AlignTop);
+ vLayout2->addWidget(marginSlider, 1, Qt::AlignTop);
+
+ vLayout3->addWidget(labelButton);
+ vLayout3->addWidget(meshButton);
#ifndef MULTI_SERIES
- vLayout2->addWidget(selectButton);
- vLayout2->addWidget(selectionInfoLabel);
- vLayout2->addWidget(flipViewsButton);
+ vLayout3->addWidget(selectButton);
+ vLayout3->addWidget(selectionInfoLabel);
+ vLayout3->addWidget(flipViewsButton);
#endif
- vLayout2->addWidget(colorPB);
- vLayout2->addWidget(changeRowButton);
- vLayout2->addWidget(changeRowsButton);
- vLayout2->addWidget(changeMultipleRowsButton);
- vLayout2->addWidget(changeItemButton);
- vLayout2->addWidget(changeMultipleItemButton);
- vLayout2->addWidget(addRowButton);
- vLayout2->addWidget(addRowsButton);
- vLayout2->addWidget(insertRowButton);
- vLayout2->addWidget(insertRowsButton);
- vLayout2->addWidget(removeRowButton);
- vLayout2->addWidget(resetArrayButton);
- vLayout2->addWidget(resetArrayEmptyButton);
- vLayout2->addWidget(massiveDataTestButton);
- vLayout2->addWidget(testReverseButton);
- vLayout2->addWidget(testDataOrderingButton);
- vLayout2->addWidget(axisTitlesVisibleCB);
- vLayout2->addWidget(axisTitlesFixedCB);
- vLayout2->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
- vLayout2->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop);
+ vLayout3->addWidget(colorPB);
+ vLayout3->addWidget(changeRowButton);
+ vLayout3->addWidget(changeRowsButton);
+ vLayout3->addWidget(changeMultipleRowsButton);
+ vLayout3->addWidget(changeItemButton);
+ vLayout3->addWidget(changeMultipleItemButton);
+ vLayout3->addWidget(addRowButton);
+ vLayout3->addWidget(addRowsButton);
+ vLayout3->addWidget(insertRowButton);
+ vLayout3->addWidget(insertRowsButton);
+ vLayout3->addWidget(removeRowButton);
+ vLayout3->addWidget(resetArrayButton);
+ vLayout3->addWidget(resetArrayEmptyButton);
+ vLayout3->addWidget(massiveDataTestButton);
+ vLayout3->addWidget(testReverseButton);
+ vLayout3->addWidget(testDataOrderingButton);
+ vLayout3->addWidget(axisTitlesVisibleCB);
+ vLayout3->addWidget(axisTitlesFixedCB);
+ vLayout3->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
+ vLayout3->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop);
widget->show();
- GraphModifier *modifier = new GraphModifier(surfaceGraph);
+ GraphModifier *modifier = new GraphModifier(surfaceGraph, container);
// Connect controls to slots on modifier
QObject::connect(smoothCB, &QCheckBox::stateChanged,
@@ -650,9 +699,23 @@ int main(int argc, char *argv[])
modifier, &GraphModifier::toggleXAscending);
QObject::connect(zAscendingCB, &QCheckBox::stateChanged,
modifier, &GraphModifier::toggleZAscending);
+ QObject::connect(polarCB, &QCheckBox::stateChanged,
+ modifier, &GraphModifier::togglePolar);
QObject::connect(aspectRatioSlider, &QSlider::valueChanged,
modifier, &GraphModifier::setAspectRatio);
+ QObject::connect(horizontalAspectRatioSlider, &QSlider::valueChanged,
+ modifier, &GraphModifier::setHorizontalAspectRatio);
+ QObject::connect(surfaceTextureCB, &QCheckBox::stateChanged,
+ modifier, &GraphModifier::setSurfaceTexture);
+ QObject::connect(cameraTargetSliderX, &QSlider::valueChanged, modifier,
+ &GraphModifier::setCameraTargetX);
+ QObject::connect(cameraTargetSliderY, &QSlider::valueChanged, modifier,
+ &GraphModifier::setCameraTargetY);
+ QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier,
+ &GraphModifier::setCameraTargetZ);
+ QObject::connect(marginSlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::setGraphMargin);
#ifdef MULTI_SERIES
modifier->setSeries1CB(series1CB);
diff --git a/tests/surfacetest/mapimage.png b/tests/surfacetest/mapimage.png
new file mode 100644
index 00000000..079d0407
--- /dev/null
+++ b/tests/surfacetest/mapimage.png
Binary files differ
diff --git a/tests/surfacetest/surfacetest.qrc b/tests/surfacetest/surfacetest.qrc
index c18da2c4..266ed7e0 100644
--- a/tests/surfacetest/surfacetest.qrc
+++ b/tests/surfacetest/surfacetest.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/maps">
<file alias="map">Heightmap.png</file>
+ <file alias="mapimage">mapimage.png</file>
</qresource>
</RCC>
diff --git a/tests/tests.pro b/tests/tests.pro
index 8bbdc3f2..ccecd486 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -7,22 +7,33 @@ contains(QT_CONFIG, opengles1) {
TEMPLATE = subdirs
-SUBDIRS += barstest \
- scattertest \
- surfacetest \
- qmlcamera \
- qmldynamicdata \
- multigraphs \
- directional \
- qmlmultiwindow \
- itemmodeltest \
- qmlmultitest
+SUBDIRS += auto
-#SUBDIRS += kinectsurface
+qtHaveModule(quick) {
+ SUBDIRS += qmlcamera \
+ qmldynamicdata \
+ qmlmultiwindow \
+ qmlmultitest \
+ qmlvolume \
+ qmlperf
+}
-qtHaveModule(multimedia):!android:!static: SUBDIRS += spectrum
+!android:!ios {
+ SUBDIRS += barstest \
+ scattertest \
+ surfacetest \
+ multigraphs \
+ directional \
+ itemmodeltest \
+ volumetrictest
+
+ # For testing code snippets of minimal applications
+ SUBDIRS += minimalbars \
+ minimalscatter \
+ minimalsurface
-# For testing code snippets of minimal applications
-SUBDIRS += minimalbars \
- minimalscatter \
- minimalsurface
+ # Requires Kinect drivers
+ #SUBDIRS += kinectsurface
+}
+
+qtHaveModule(multimedia):!android:!static: SUBDIRS += spectrum
diff --git a/tests/volumetrictest/cubeFilledFlat.obj b/tests/volumetrictest/cubeFilledFlat.obj
new file mode 100644
index 00000000..108cf7ac
--- /dev/null
+++ b/tests/volumetrictest/cubeFilledFlat.obj
@@ -0,0 +1,54 @@
+# Blender v2.66 (sub 0) OBJ File: 'cube_filled.blend'
+# www.blender.org
+o Cube
+v -1.000000 -1.000000 1.000000
+v -1.000000 -1.000000 -1.000000
+v 1.000000 -1.000000 -1.000000
+v 1.000000 -1.000000 1.000000
+v -1.000000 1.000000 1.000000
+v -1.000000 1.000000 -1.000000
+v 1.000000 1.000000 -1.000000
+v 1.000000 1.000000 1.000000
+vt 0.666667 0.332314
+vt 0.334353 0.333333
+vt 0.665647 0.000000
+vt 0.001020 0.333333
+vt 0.000000 0.001020
+vt 0.333333 0.332314
+vt 0.333333 0.665647
+vt 0.001019 0.666667
+vt 0.000000 0.334353
+vt 0.334353 0.666667
+vt 0.333333 0.334353
+vt 0.665647 0.333333
+vt 0.333333 0.667686
+vt 0.665647 0.666667
+vt 0.666667 0.998980
+vt 0.667686 0.333333
+vt 0.666667 0.001019
+vt 0.998980 0.000000
+vt 0.333333 0.001019
+vt 0.332314 0.000000
+vt 0.332314 0.333333
+vt 0.666667 0.665647
+vt 0.334353 1.000000
+vt 1.000000 0.332314
+vn -1.000000 0.000000 0.000000
+vn 0.000000 0.000000 -1.000000
+vn 1.000000 -0.000000 0.000000
+vn 0.000000 0.000000 1.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 -1.000000 -0.000000
+s off
+f 5/1/1 6/2/1 1/3/1
+f 6/4/2 7/5/2 2/6/2
+f 7/7/3 8/8/3 4/9/3
+f 8/10/4 5/11/4 1/12/4
+f 8/13/5 7/14/5 6/15/5
+f 2/16/6 3/17/6 4/18/6
+f 6/2/1 2/19/1 1/3/1
+f 7/5/2 3/20/2 2/6/2
+f 3/21/3 7/7/3 4/9/3
+f 4/22/4 8/10/4 1/12/4
+f 5/23/5 8/13/5 6/15/5
+f 1/24/6 2/16/6 4/18/6
diff --git a/tests/volumetrictest/logo.png b/tests/volumetrictest/logo.png
new file mode 100644
index 00000000..1e7ed4cf
--- /dev/null
+++ b/tests/volumetrictest/logo.png
Binary files differ
diff --git a/tests/volumetrictest/logo_no_padding.png b/tests/volumetrictest/logo_no_padding.png
new file mode 100644
index 00000000..714234aa
--- /dev/null
+++ b/tests/volumetrictest/logo_no_padding.png
Binary files differ
diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp
new file mode 100644
index 00000000..7b18ceab
--- /dev/null
+++ b/tests/volumetrictest/main.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "volumetrictest.h"
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QRadioButton>
+#include <QtWidgets/QSlider>
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QPushButton>
+#include <QtGui/QScreen>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ //Q3DScatter *graph = new Q3DScatter();
+ //Q3DSurface *graph = new Q3DSurface();
+ Q3DBars *graph = new Q3DBars();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ QSize screenSize = graph->screen()->size();
+ container->setMinimumSize(QSize(screenSize.width() / 4, screenSize.height() / 4));
+ container->setMaximumSize(screenSize);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget *widget = new QWidget;
+ QHBoxLayout *hLayout = new QHBoxLayout(widget);
+ QVBoxLayout *vLayout = new QVBoxLayout();
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayout);
+
+ widget->setWindowTitle(QStringLiteral("Volumetric TEST"));
+ widget->resize(QSize(screenSize.width() / 1.5, screenSize.height() / 1.5));
+
+ QCheckBox *sliceXCheckBox = new QCheckBox(widget);
+ sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis"));
+ sliceXCheckBox->setChecked(false);
+ QCheckBox *sliceYCheckBox = new QCheckBox(widget);
+ sliceYCheckBox->setText(QStringLiteral("Slice volume on Y axis"));
+ sliceYCheckBox->setChecked(false);
+ QCheckBox *sliceZCheckBox = new QCheckBox(widget);
+ sliceZCheckBox->setText(QStringLiteral("Slice volume on Z axis"));
+ sliceZCheckBox->setChecked(false);
+
+ QSlider *sliceXSlider = new QSlider(Qt::Horizontal, widget);
+ sliceXSlider->setMinimum(0);
+ sliceXSlider->setMaximum(1024);
+ sliceXSlider->setValue(512);
+ sliceXSlider->setEnabled(true);
+ QSlider *sliceYSlider = new QSlider(Qt::Horizontal, widget);
+ sliceYSlider->setMinimum(0);
+ sliceYSlider->setMaximum(1024);
+ sliceYSlider->setValue(512);
+ sliceYSlider->setEnabled(true);
+ QSlider *sliceZSlider = new QSlider(Qt::Horizontal, widget);
+ sliceZSlider->setMinimum(0);
+ sliceZSlider->setMaximum(1024);
+ sliceZSlider->setValue(512);
+ sliceZSlider->setEnabled(true);
+
+ QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget);
+
+ QLabel *sliceImageXLabel = new QLabel(widget);
+ QLabel *sliceImageYLabel = new QLabel(widget);
+ QLabel *sliceImageZLabel = new QLabel(widget);
+ sliceImageXLabel->setMinimumSize(QSize(200, 100));
+ sliceImageYLabel->setMinimumSize(QSize(200, 200));
+ sliceImageZLabel->setMinimumSize(QSize(200, 100));
+ sliceImageXLabel->setMaximumSize(QSize(200, 100));
+ sliceImageYLabel->setMaximumSize(QSize(200, 200));
+ sliceImageZLabel->setMaximumSize(QSize(200, 100));
+ sliceImageXLabel->setFrameShape(QFrame::Box);
+ sliceImageYLabel->setFrameShape(QFrame::Box);
+ sliceImageZLabel->setFrameShape(QFrame::Box);
+ sliceImageXLabel->setScaledContents(true);
+ sliceImageYLabel->setScaledContents(true);
+ sliceImageZLabel->setScaledContents(true);
+
+ QPushButton *testSubTextureSetting = new QPushButton(widget);
+ testSubTextureSetting->setText(QStringLiteral("Test subtexture settings"));
+
+ QLabel *rangeSliderLabel = new QLabel(QStringLiteral("Adjust ranges:"), widget);
+
+ QSlider *rangeXSlider = new QSlider(Qt::Horizontal, widget);
+ rangeXSlider->setMinimum(0);
+ rangeXSlider->setMaximum(1024);
+ rangeXSlider->setValue(512);
+ rangeXSlider->setEnabled(true);
+ QSlider *rangeYSlider = new QSlider(Qt::Horizontal, widget);
+ rangeYSlider->setMinimum(0);
+ rangeYSlider->setMaximum(1024);
+ rangeYSlider->setValue(512);
+ rangeYSlider->setEnabled(true);
+ QSlider *rangeZSlider = new QSlider(Qt::Horizontal, widget);
+ rangeZSlider->setMinimum(0);
+ rangeZSlider->setMaximum(1024);
+ rangeZSlider->setValue(512);
+ rangeZSlider->setEnabled(true);
+
+ QPushButton *testBoundsSetting = new QPushButton(widget);
+ testBoundsSetting->setText(QStringLiteral("Test data bounds"));
+
+ vLayout->addWidget(fpsLabel);
+ vLayout->addWidget(sliceXCheckBox);
+ vLayout->addWidget(sliceXSlider);
+ vLayout->addWidget(sliceImageXLabel);
+ vLayout->addWidget(sliceYCheckBox);
+ vLayout->addWidget(sliceYSlider);
+ vLayout->addWidget(sliceImageYLabel);
+ vLayout->addWidget(sliceZCheckBox);
+ vLayout->addWidget(sliceZSlider);
+ vLayout->addWidget(sliceImageZLabel);
+ vLayout->addWidget(rangeSliderLabel);
+ vLayout->addWidget(rangeXSlider);
+ vLayout->addWidget(rangeYSlider);
+ vLayout->addWidget(rangeZSlider);
+ vLayout->addWidget(testBoundsSetting);
+ vLayout->addWidget(testSubTextureSetting, 1, Qt::AlignTop);
+
+ VolumetricModifier *modifier = new VolumetricModifier(graph);
+ modifier->setFpsLabel(fpsLabel);
+ modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel);
+
+ QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceX);
+ QObject::connect(sliceYCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceY);
+ QObject::connect(sliceZCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceZ);
+ QObject::connect(sliceXSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceX);
+ QObject::connect(sliceYSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceY);
+ QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceZ);
+ QObject::connect(testSubTextureSetting, &QPushButton::clicked, modifier,
+ &VolumetricModifier::testSubtextureSetting);
+ QObject::connect(rangeXSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustRangeX);
+ QObject::connect(rangeYSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustRangeY);
+ QObject::connect(rangeZSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustRangeZ);
+ QObject::connect(testBoundsSetting, &QPushButton::clicked, modifier,
+ &VolumetricModifier::testBoundsSetting);
+
+ widget->show();
+ return app.exec();
+}
diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp
new file mode 100644
index 00000000..24ecac3d
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.cpp
@@ -0,0 +1,716 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "volumetrictest.h"
+#include <QtDataVisualization/qbar3dseries.h>
+#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/q3dtheme.h>
+#include <QtDataVisualization/qcustom3dlabel.h>
+#include <QtCore/qmath.h>
+#include <QtGui/QRgb>
+#include <QtGui/QImage>
+#include <QtWidgets/QLabel>
+#include <QtCore/QDebug>
+
+using namespace QtDataVisualization;
+
+const int imageCount = 512;
+const float xMiddle = 100.0f;
+const float yMiddle = 2.5f;
+const float zMiddle = 100.0f;
+const float xRange = 40.0f;
+const float yRange = 7.5f;
+const float zRange = 20.0f;
+
+VolumetricModifier::VolumetricModifier(QAbstract3DGraph *scatter)
+ : m_graph(scatter),
+ m_volumeItem(0),
+ m_volumeItem2(0),
+ m_volumeItem3(0),
+ m_sliceIndexX(0),
+ m_sliceIndexY(0),
+ m_sliceIndexZ(0)
+{
+ m_graph->activeTheme()->setType(Q3DTheme::ThemeQt);
+ //m_graph->activeTheme()->setType(Q3DTheme::ThemeIsabelle);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
+ m_graph->setOrthoProjection(true);
+ //m_graph->scene()->activeCamera()->setTarget(QVector3D(-2.0f, 1.0f, 2.0f));
+ m_scatterGraph = qobject_cast<Q3DScatter *>(m_graph);
+ m_surfaceGraph = qobject_cast<Q3DSurface *>(m_graph);
+ m_barGraph = qobject_cast<Q3DBars *>(m_graph);
+ if (m_scatterGraph) {
+ m_scatterGraph->axisX()->setRange(xMiddle - xRange, xMiddle + xRange);
+ m_scatterGraph->axisX()->setSegmentCount(8);
+ m_scatterGraph->axisY()->setRange(yMiddle - yRange, yMiddle + yRange);
+ m_scatterGraph->axisY()->setSegmentCount(3);
+ m_scatterGraph->axisZ()->setRange(zMiddle - zRange, zMiddle + zRange);
+ m_scatterGraph->axisZ()->setSegmentCount(8);
+ } else if (m_surfaceGraph) {
+ m_surfaceGraph->axisX()->setRange(xMiddle - xRange, xMiddle + xRange);
+ m_surfaceGraph->axisX()->setSegmentCount(8);
+ m_surfaceGraph->axisY()->setRange(yMiddle - yRange, yMiddle + yRange);
+ m_surfaceGraph->axisY()->setSegmentCount(3);
+ m_surfaceGraph->axisZ()->setRange(zMiddle - zRange, zMiddle + zRange);
+ m_surfaceGraph->axisZ()->setSegmentCount(8);
+ } else if (m_barGraph) {
+ QStringList rowLabels;
+ QStringList columnLabels;
+ for (int i = 0; i < xMiddle + xRange; i++) {
+ if (i % 5 == 0)
+ columnLabels << QString::number(i);
+ else
+ columnLabels << QString();
+ }
+ for (int i = 0; i < zMiddle + zRange; i++) {
+ if (i % 5 == 0)
+ rowLabels << QString::number(i);
+ else
+ rowLabels << QString();
+ }
+
+ QBar3DSeries *series = new QBar3DSeries;
+ QBarDataArray *array = new QBarDataArray();
+ array->reserve(zRange * 2 + 1);
+ for (int i = 0; i < zRange * 2 + 1; i++)
+ array->append(new QBarDataRow(xRange * 2 + 1));
+
+ series->dataProxy()->resetArray(array, rowLabels, columnLabels);
+ m_barGraph->addSeries(series);
+
+ m_barGraph->columnAxis()->setRange(xMiddle - xRange, xMiddle + xRange);
+ m_barGraph->valueAxis()->setRange(yMiddle - yRange, yMiddle + yRange);
+ m_barGraph->rowAxis()->setRange(zMiddle - zRange, zMiddle + zRange);
+ //m_barGraph->setReflection(true);
+ }
+ m_graph->activeTheme()->setBackgroundEnabled(false);
+
+ createVolume();
+ createAnotherVolume();
+ createYetAnotherVolume();
+
+// m_volumeItem->setUseHighDefShader(false);
+// m_volumeItem2->setUseHighDefShader(false);
+// m_volumeItem3->setUseHighDefShader(false);
+
+ m_volumeItem->setScalingAbsolute(false);
+ m_volumeItem2->setScalingAbsolute(false);
+ m_volumeItem3->setScalingAbsolute(false);
+ m_volumeItem->setPositionAbsolute(false);
+ m_volumeItem2->setPositionAbsolute(false);
+ m_volumeItem3->setPositionAbsolute(false);
+
+
+ m_plainItem = new QCustom3DItem;
+ QImage texture(2, 2, QImage::Format_ARGB32);
+ texture.fill(QColor(200, 200, 200, 130));
+ m_plainItem->setMeshFile(QStringLiteral(":/mesh"));
+ m_plainItem->setTextureImage(texture);
+ m_plainItem->setRotation(m_volumeItem->rotation());
+ m_plainItem->setPosition(QVector3D(xMiddle + xRange / 2.0f, yMiddle + yRange / 2.0f, zMiddle));
+ m_plainItem->setScaling(QVector3D(20.0f, 5.0f, 10.0f));
+ m_plainItem->setScalingAbsolute(false);
+
+ m_graph->addCustomItem(m_volumeItem);
+ m_graph->addCustomItem(m_volumeItem2);
+ m_graph->addCustomItem(m_volumeItem3);
+ m_graph->addCustomItem(m_plainItem);
+ //m_graph->setMeasureFps(true);
+
+ // Create label to cut through the volume 3
+ QCustom3DLabel *label = new QCustom3DLabel;
+ label->setText(QStringLiteral("FOO BAR - FOO BAR - FOO BAR"));
+ QFont font;
+ font.setPixelSize(100);
+ label->setFont(font);
+ label->setScaling(QVector3D(2.0f, 2.0f, 0.0f));
+ label->setRotationAxisAndAngle(QVector3D(0.0f, 1.0f, 0.0f), 45.0f);
+ label->setPosition(m_volumeItem3->position());
+ label->setPositionAbsolute(false);
+ label->setScalingAbsolute(true);
+
+ m_graph->addCustomItem(label);
+
+ QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
+ &VolumetricModifier::handleFpsChange);
+// QObject::connect(m_graph->scene(), &Q3DScene::viewportChanged, this,
+// &VolumetricModifier::handleFpsChange);
+}
+
+VolumetricModifier::~VolumetricModifier()
+{
+ delete m_graph;
+}
+
+void VolumetricModifier::setFpsLabel(QLabel *fpsLabel)
+{
+ m_fpsLabel = fpsLabel;
+}
+
+void VolumetricModifier::sliceX(int enabled)
+{
+ m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1);
+ m_volumeItem2->setSliceIndexX(enabled ? m_sliceIndexX : -1);
+}
+
+void VolumetricModifier::sliceY(int enabled)
+{
+ m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1);
+ m_volumeItem2->setSliceIndexY(enabled ? m_sliceIndexY : -1);
+}
+
+void VolumetricModifier::sliceZ(int enabled)
+{
+ m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1);
+ m_volumeItem2->setSliceIndexZ(enabled ? m_sliceIndexZ : -1);
+}
+
+void VolumetricModifier::adjustSliceX(int value)
+{
+ m_sliceIndexX = int(float(value) / (1024.0f / m_volumeItem->textureWidth()));
+ if (m_sliceIndexX == m_volumeItem->textureWidth())
+ m_sliceIndexX--;
+ qDebug() << "m_sliceIndexX:" << m_sliceIndexX;
+ if (m_volumeItem->sliceIndexX() != -1) {
+ m_volumeItem->setSliceIndexX(m_sliceIndexX);
+ m_volumeItem2->setSliceIndexX(m_sliceIndexX);
+ }
+ m_sliceLabelX->setPixmap(QPixmap::fromImage(
+ m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX)));
+
+}
+
+void VolumetricModifier::adjustSliceY(int value)
+{
+ m_sliceIndexY = int(float(value) / (1024.0f / m_volumeItem->textureHeight()));
+ if (m_sliceIndexY == m_volumeItem->textureHeight())
+ m_sliceIndexY--;
+ qDebug() << "m_sliceIndexY:" << m_sliceIndexY;
+ if (m_volumeItem->sliceIndexY() != -1) {
+ m_volumeItem->setSliceIndexY(m_sliceIndexY);
+ m_volumeItem2->setSliceIndexY(m_sliceIndexY);
+ }
+ m_sliceLabelY->setPixmap(QPixmap::fromImage(
+ m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY)));
+}
+
+void VolumetricModifier::adjustSliceZ(int value)
+{
+ m_sliceIndexZ = int(float(value) / (1024.0f / m_volumeItem->textureDepth()));
+ if (m_sliceIndexZ == m_volumeItem->textureDepth())
+ m_sliceIndexZ--;
+ qDebug() << "m_sliceIndexZ:" << m_sliceIndexZ;
+ if (m_volumeItem->sliceIndexZ() != -1) {
+ m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
+ m_volumeItem2->setSliceIndexZ(m_sliceIndexZ);
+ }
+ m_sliceLabelZ->setPixmap(QPixmap::fromImage(
+ m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ)));
+}
+
+void VolumetricModifier::handleFpsChange()
+{
+ const QString fpsFormat = QStringLiteral("Fps: %1");
+ int fps10 = int(m_graph->currentFps() * 10.0);
+ m_fpsLabel->setText(fpsFormat.arg(QString::number(qreal(fps10) / 10.0)));
+// const QString sceneDimensionsFormat = QStringLiteral("%1 x %2");
+// m_fpsLabel->setText(sceneDimensionsFormat
+// .arg(m_graph->scene()->viewport().width())
+ // .arg(m_graph->scene()->viewport().height()));
+}
+
+void VolumetricModifier::testSubtextureSetting()
+{
+ // Setting the rendered Slice as subtexture should result in identical volume
+ QVector<uchar> dataBefore = *m_volumeItem->textureData();
+ dataBefore[0] = dataBefore.at(0); // Make sure we are detached
+
+ checkRenderCase(1, Qt::XAxis, 56, dataBefore, m_volumeItem);
+ checkRenderCase(2, Qt::YAxis, 64, dataBefore, m_volumeItem);
+ checkRenderCase(3, Qt::ZAxis, 87, dataBefore, m_volumeItem);
+
+ checkRenderCase(4, Qt::XAxis, 0, dataBefore, m_volumeItem);
+ checkRenderCase(5, Qt::YAxis, 0, dataBefore, m_volumeItem);
+ checkRenderCase(6, Qt::ZAxis, 0, dataBefore, m_volumeItem);
+
+ checkRenderCase(7, Qt::XAxis, m_volumeItem->textureWidth() - 1, dataBefore, m_volumeItem);
+ checkRenderCase(8, Qt::YAxis, m_volumeItem->textureHeight() - 1, dataBefore, m_volumeItem);
+ checkRenderCase(9, Qt::ZAxis, m_volumeItem->textureDepth() - 1, dataBefore, m_volumeItem);
+
+ dataBefore = *m_volumeItem2->textureData();
+ dataBefore[0] = dataBefore.at(0); // Make sure we are detached
+
+ checkRenderCase(11, Qt::XAxis, 56, dataBefore, m_volumeItem2);
+ checkRenderCase(12, Qt::YAxis, 64, dataBefore, m_volumeItem2);
+ checkRenderCase(13, Qt::ZAxis, 87, dataBefore, m_volumeItem2);
+
+ checkRenderCase(14, Qt::XAxis, 0, dataBefore, m_volumeItem2);
+ checkRenderCase(15, Qt::YAxis, 0, dataBefore, m_volumeItem2);
+ checkRenderCase(16, Qt::ZAxis, 0, dataBefore, m_volumeItem2);
+
+ checkRenderCase(17, Qt::XAxis, m_volumeItem2->textureWidth() - 1, dataBefore, m_volumeItem2);
+ checkRenderCase(18, Qt::YAxis, m_volumeItem2->textureHeight() - 1, dataBefore, m_volumeItem2);
+ checkRenderCase(19, Qt::ZAxis, m_volumeItem2->textureDepth() - 1, dataBefore, m_volumeItem2);
+
+ // Do some visible swaps on volume 3
+ QImage slice = m_volumeItem3->renderSlice(Qt::XAxis, 144);
+ slice = slice.mirrored();
+ m_volumeItem3->setSubTextureData(Qt::XAxis, 144, slice);
+
+ slice = m_volumeItem3->renderSlice(Qt::YAxis, 80);
+ slice = slice.mirrored();
+ m_volumeItem3->setSubTextureData(Qt::YAxis, 80, slice);
+
+ slice = m_volumeItem3->renderSlice(Qt::ZAxis, 190);
+ slice = slice.mirrored(true, false);
+ m_volumeItem3->setSubTextureData(Qt::ZAxis, 190, slice);
+}
+
+void VolumetricModifier::adjustRangeX(int value)
+{
+ float adjustment = float(value - 512) / 10.0f;
+ if (m_scatterGraph)
+ m_scatterGraph->axisX()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange);
+ if (m_surfaceGraph)
+ m_surfaceGraph->axisX()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange);
+ if (m_barGraph)
+ m_barGraph->columnAxis()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange);
+}
+
+void VolumetricModifier::adjustRangeY(int value)
+{
+ float adjustment = float(value - 512) / 10.0f;
+ if (m_scatterGraph)
+ m_scatterGraph->axisY()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange);
+ if (m_surfaceGraph)
+ m_surfaceGraph->axisY()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange);
+ if (m_barGraph)
+ m_barGraph->valueAxis()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange);
+}
+
+void VolumetricModifier::adjustRangeZ(int value)
+{
+ float adjustment = float(value - 512) / 10.0f;
+ if (m_scatterGraph)
+ m_scatterGraph->axisZ()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange);
+ if (m_surfaceGraph)
+ m_surfaceGraph->axisZ()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange);
+ if (m_barGraph)
+ m_barGraph->rowAxis()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange);
+}
+
+void VolumetricModifier::testBoundsSetting()
+{
+ static QVector3D scaling1 = m_volumeItem->scaling();
+ static QVector3D scaling2 = m_volumeItem2->scaling();
+ static QVector3D scaling3 = m_volumeItem3->scaling();
+ static QVector3D scaleVector = QVector3D(0.5f, 0.3f, 0.2f);
+
+ if (m_volumeItem->isScalingAbsolute()) {
+ m_volumeItem->setScalingAbsolute(false);
+ m_volumeItem2->setScalingAbsolute(false);
+ m_volumeItem3->setScalingAbsolute(false);
+
+ m_volumeItem->setScaling(scaling1);
+ m_volumeItem2->setScaling(scaling2);
+ m_volumeItem3->setScaling(scaling3);
+ } else {
+ m_volumeItem->setScalingAbsolute(true);
+ m_volumeItem2->setScalingAbsolute(true);
+ m_volumeItem3->setScalingAbsolute(true);
+
+ m_volumeItem->setScaling(scaleVector);
+ m_volumeItem2->setScaling(scaleVector);
+ m_volumeItem3->setScaling(scaleVector);
+ }
+}
+
+void VolumetricModifier::checkRenderCase(int id, Qt::Axis axis, int index,
+ const QVector<uchar> &dataBefore,
+ QCustom3DVolume *volumeItem)
+{
+ QImage slice = volumeItem->renderSlice(axis, index);
+ volumeItem->setSubTextureData(axis, index, slice);
+
+ if (dataBefore == *volumeItem->textureData())
+ qDebug() << __FUNCTION__ << "Case:" << id << "Vectors identical";
+ else
+ qDebug() << __FUNCTION__ << "Case:" << id << "BUG: VECTORS DIFFER!";
+}
+
+void VolumetricModifier::createVolume()
+{
+ m_volumeItem = new QCustom3DVolume;
+ m_volumeItem->setTextureFormat(QImage::Format_ARGB32);
+ m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 20.0f));
+ m_volumeItem->setPosition(QVector3D(xMiddle - (xRange / 2.0f), yMiddle + (yRange / 2.0f), zMiddle));
+ //m_volumeItem->setPosition(QVector3D(xMiddle, yMiddle, zMiddle));
+ m_volumeItem->setDrawSliceFrames(true);
+ m_volumeItem->setDrawSlices(true);
+
+ QImage logo;
+ logo.load(QStringLiteral(":/logo_no_padding.png"));
+ //logo.load(QStringLiteral(":/logo.png"));
+ qDebug() << "image dimensions:" << logo.width() << logo.height()
+ << logo.byteCount() << (logo.width() * logo.height())
+ << logo.bytesPerLine();
+
+ QVector<QImage *> imageArray(imageCount);
+ for (int i = 0; i < imageCount; i++) {
+ QImage *newImage = new QImage(logo);
+ imageArray[i] = newImage;
+ }
+
+ m_volumeItem->createTextureData(imageArray);
+
+ for (int i = 0; i < imageCount; i++)
+ delete imageArray[i];
+
+ m_sliceIndexX = m_volumeItem->textureWidth() / 2;
+ m_sliceIndexY = m_volumeItem->textureWidth() / 2;
+ m_sliceIndexZ = m_volumeItem->textureWidth() / 2;
+
+ QVector<QRgb> colorTable = m_volumeItem->colorTable();
+
+ // Hack some alpha to the picture
+ for (int i = 0; i < colorTable.size(); i++) {
+ if (qAlpha(colorTable.at(i)) > 0) {
+ colorTable[i] = qRgba(qRed(colorTable.at(i)),
+ qGreen(colorTable.at(i)),
+ qBlue(colorTable.at(i)),
+ 50);
+ }
+ }
+
+ colorTable.append(qRgba(0, 0, 0, 0));
+ colorTable.append(qRgba(255, 0, 0, 255));
+ colorTable.append(qRgba(0, 255, 0, 255));
+ colorTable.append(qRgba(0, 0, 255, 255));
+
+ m_volumeItem->setColorTable(colorTable);
+ int alphaIndex = colorTable.size() - 4;
+ int redIndex = colorTable.size() - 3;
+ int greenIndex = colorTable.size() - 2;
+ int blueIndex = colorTable.size() - 1;
+ int width = m_volumeItem->textureDataWidth();
+ int height = m_volumeItem->textureHeight();
+ int depth = m_volumeItem->textureDepth();
+ int frameSize = width * height;
+ qDebug() << width << height << depth << m_volumeItem->textureData()->size();
+ m_volumeItem->setScaling(QVector3D(xRange, yRange, zRange) / 2.0f);
+
+ uchar *data = m_volumeItem->textureData()->data();
+ uchar *p = data;
+
+ // Change one picture using subtexture replacement
+ QImage flipped = logo.mirrored();
+ m_volumeItem->setSubTextureData(Qt::ZAxis, 100, flipped);
+
+ // Clean up the two extra pixels
+ p = data + width - 1;
+ for (int k = 0; k < depth; k++) {
+ for (int j = 0; j < height; j++) {
+ *p = alphaIndex;
+ p += width;
+ }
+ }
+ p = data + width - 2;
+ for (int k = 0; k < depth; k++) {
+ for (int j = 0; j < height; j++) {
+ *p = alphaIndex;
+ p += width;
+ }
+ }
+ // Red first subtexture
+ p = data;
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+ *p = redIndex;
+ p++;
+ }
+ }
+
+ // Red last subtexture
+// p = data + frameSize * (imageCount - 1);
+// for (int j = 0; j < height; j++) {
+// for (int i = 0; i < width; i++) {
+// *p = redIndex;
+// p++;
+// }
+// }
+
+// // Blue second to last subtexture
+// p = data + frameSize * (imageCount - 2);
+// for (int j = 0; j < height; j++) {
+// for (int i = 0; i < width; i++) {
+// *p = blueIndex;
+// p++;
+// }
+// }
+
+ // Blue x = 0
+// p = data;
+// for (int k = 0; k < depth; k++) {
+// for (int j = 0; j < height; j++) {
+// *p = blueIndex;
+// p += width;
+// }
+// }
+
+ // Blue x = max
+ p = data + width - 1;
+ for (int k = 0; k < depth; k++) {
+ for (int j = 0; j < height; j++) {
+ *p = blueIndex;
+ p += width;
+ }
+ }
+ // green x = max - 1
+ p = data + width - 2;
+ for (int k = 0; k < depth; k++) {
+ for (int j = 0; j < height; j++) {
+ *p = greenIndex;
+ p += width;
+ }
+ }
+
+ // Green y = 0
+ p = data;
+ for (int k = 0; k < depth; k++) {
+ for (int i = 0; i < width; i++) {
+ *p = greenIndex;
+ p++;
+ }
+ p += (frameSize - width);
+ }
+
+ // Green y = max
+// p = data + frameSize - width;
+// for (int k = 0; k < depth; k++) {
+// for (int i = 0; i < width; i++) {
+// *p = greenIndex;
+// p++;
+// }
+// p += (frameSize - width);
+// }
+}
+
+void VolumetricModifier::createAnotherVolume()
+{
+ m_volumeItem2 = new QCustom3DVolume;
+ m_volumeItem2->setTextureFormat(QImage::Format_ARGB32);
+ m_volumeItem2->setPosition(QVector3D(xMiddle + (xRange / 2.0f), yMiddle - (yRange / 2.0f), zMiddle));
+
+ QImage logo;
+ logo.load(QStringLiteral(":/logo_no_padding.png"));
+ //logo.load(QStringLiteral(":/logo.png"));
+ logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied);
+ qDebug() << "second image dimensions:" << logo.width() << logo.height()
+ << logo.byteCount() << (logo.width() * logo.height())
+ << logo.bytesPerLine();
+
+ logo.save("d:/qt/goobar.png");
+
+ QVector<QImage *> imageArray(imageCount);
+ for (int i = 0; i < imageCount; i++) {
+ QImage *newImage = new QImage(logo);
+ imageArray[i] = newImage;
+ }
+
+ m_volumeItem2->createTextureData(imageArray);
+
+ for (int i = 0; i < imageCount; i++)
+ delete imageArray[i];
+
+ m_sliceIndexX = m_volumeItem2->textureWidth() / 2;
+ m_sliceIndexY = m_volumeItem2->textureWidth() / 2;
+ m_sliceIndexZ = m_volumeItem2->textureWidth() / 2;
+
+ int width = m_volumeItem2->textureWidth();
+ int height = m_volumeItem2->textureHeight();
+ int depth = m_volumeItem2->textureDepth();
+ qDebug() << width << height << depth << m_volumeItem2->textureData()->size();
+ m_volumeItem2->setScaling(QVector3D(float(width) / float(depth) * xRange * 2.0f,
+ float(height) / float(depth) * yRange * 2.0f,
+ zRange * 2.0f));
+
+ // Change one picture using subtexture replacement
+ QImage flipped = logo.mirrored();
+ m_volumeItem2->setSubTextureData(Qt::ZAxis, 100, flipped);
+ //m_volumeItem2->setAlphaMultiplier(0.2f);
+ m_volumeItem2->setPreserveOpacity(false);
+}
+
+void VolumetricModifier::createYetAnotherVolume()
+{
+ m_volumeItem3 = new QCustom3DVolume;
+ m_volumeItem3->setTextureFormat(QImage::Format_Indexed8);
+// m_volumeItem2->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f));
+ m_volumeItem3->setPosition(QVector3D(xMiddle - (xRange / 2.0f), yMiddle - (yRange / 2.0f), zMiddle));
+
+// m_volumeItem3->setTextureDimensions(m_volumeItem->textureDataWidth(),
+// m_volumeItem->textureHeight(),
+// m_volumeItem->textureDepth());
+ m_volumeItem3->setTextureDimensions(200, 200, 200);
+
+ QVector<uchar> *tdata = new QVector<uchar>(m_volumeItem3->textureDataWidth()
+ * m_volumeItem3->textureHeight()
+ * m_volumeItem3->textureDepth());
+ tdata->fill(0);
+ m_volumeItem3->setTextureData(tdata);
+
+ m_sliceIndexX = m_volumeItem3->textureWidth() / 2;
+ m_sliceIndexY = m_volumeItem3->textureWidth() / 2;
+ m_sliceIndexZ = m_volumeItem3->textureWidth() / 2;
+
+ QVector<QRgb> colorTable = m_volumeItem->colorTable();
+ colorTable[0] = qRgba(0, 0, 0, 0);
+ m_volumeItem3->setColorTable(colorTable);
+ int redIndex = colorTable.size() - 3;
+ int greenIndex = colorTable.size() - 2;
+ int blueIndex = colorTable.size() - 1;
+ int width = m_volumeItem3->textureDataWidth();
+ int height = m_volumeItem3->textureHeight();
+ int depth = m_volumeItem3->textureDepth();
+ int frameSize = width * height;
+ qDebug() << width << height << depth << m_volumeItem3->textureData()->size();
+ m_volumeItem3->setScaling(m_volumeItem->scaling());
+
+ uchar *data = tdata->data();
+ uchar *p = data;
+
+ // Red first subtexture
+// for (int j = 0; j < height; j++) {
+// for (int i = 0; i < width; i++) {
+// *p = redIndex;
+// p++;
+// }
+// }
+
+ // Red last subtexture
+ p = data + frameSize * (depth - 1);
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+ *p = redIndex;
+ p++;
+ }
+ }
+
+ // green edge
+ p = data + frameSize * (depth - 1);
+ for (int i = 0; i < width; i++) {
+ *p = greenIndex;
+ p++;
+ }
+ for (int j = 2; j < height; j++) {
+ *p = greenIndex;
+ p += width - 1;
+ *p = j % 7 ? greenIndex : blueIndex;
+ p++;
+ }
+ for (int i = 0; i < width; i++) {
+ *p = greenIndex;
+ p++;
+ }
+
+// // Blue second to last subtexture
+// p = data + frameSize * (depth - 2);
+// for (int j = 0; j < height; j++) {
+// for (int i = 0; i < width; i++) {
+// *p = blueIndex;
+// p++;
+// }
+// }
+
+// // green third to last subtexture
+// p = data + frameSize * (depth - 3);
+// for (int j = 0; j < height; j++) {
+// for (int i = 0; i < width; i++) {
+// *p = greenIndex;
+// p++;
+// }
+// }
+
+ // Blue x = 0
+ p = data;
+ for (int k = 0; k < depth; k++) {
+ for (int j = 0; j < height; j++) {
+ *p = blueIndex;
+ p += width;
+ }
+ }
+
+// // Blue x = max
+// p = data + width - 1;
+// for (int k = 0; k < depth; k++) {
+// for (int j = 0; j < height; j++) {
+// *p = blueIndex;
+// p += width;
+// }
+// }
+
+ // Green y = 0
+// p = data;
+// for (int k = 0; k < depth; k++) {
+// for (int i = 0; i < width; i++) {
+// *p = greenIndex;
+// p++;
+// }
+// p += (frameSize - width);
+// }
+
+// // Green y = max
+ p = data + frameSize - width;
+ for (int k = 0; k < depth; k++) {
+ for (int i = 0; i < width; i++) {
+ *p = greenIndex;
+ p++;
+ }
+ p += (frameSize - width);
+ }
+
+
+// // Fill with alternating pixels
+// p = data;
+// for (int k = 0; k < (depth * width * height / 4); k++) {
+// *p = greenIndex;
+// p++;
+// *p = greenIndex;
+// p++;
+// *p = blueIndex;
+// p++;
+// *p = redIndex;
+// p++;
+// }
+
+
+}
+
+void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel)
+{
+ m_sliceLabelX = xLabel;
+ m_sliceLabelY = yLabel;
+ m_sliceLabelZ = zLabel;
+
+ adjustSliceX(512);
+ adjustSliceY(512);
+ adjustSliceZ(512);
+}
diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h
new file mode 100644
index 00000000..9029f7b9
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef VOLUMETRICMODIFIER_H
+#define VOLUMETRICMODIFIER_H
+
+#include <QtDataVisualization/qcustom3dvolume.h>
+#include <QtDataVisualization/qcustom3ditem.h>
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtDataVisualization/q3dsurface.h>
+#include <QtDataVisualization/q3dbars.h>
+
+class QLabel;
+
+using namespace QtDataVisualization;
+
+class VolumetricModifier : public QObject
+{
+ Q_OBJECT
+public:
+ explicit VolumetricModifier(QAbstract3DGraph *scatter);
+ ~VolumetricModifier();
+
+ void setFpsLabel(QLabel *fpsLabel);
+ void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel);
+
+public slots:
+ void sliceX(int enabled);
+ void sliceY(int enabled);
+ void sliceZ(int enabled);
+ void adjustSliceX(int value);
+ void adjustSliceY(int value);
+ void adjustSliceZ(int value);
+ void handleFpsChange();
+ void testSubtextureSetting();
+ void adjustRangeX(int value);
+ void adjustRangeY(int value);
+ void adjustRangeZ(int value);
+ void testBoundsSetting();
+
+private:
+ void createVolume();
+ void createAnotherVolume();
+ void createYetAnotherVolume();
+ void checkRenderCase(int id, Qt::Axis axis, int index, const QVector<uchar> &dataBefore,
+ QCustom3DVolume *volumeItem);
+
+ QAbstract3DGraph *m_graph;
+ Q3DScatter *m_scatterGraph;
+ Q3DSurface *m_surfaceGraph;
+ Q3DBars *m_barGraph;
+ QCustom3DVolume *m_volumeItem;
+ QCustom3DVolume *m_volumeItem2;
+ QCustom3DVolume *m_volumeItem3;
+ QCustom3DItem *m_plainItem;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+ QLabel *m_fpsLabel;
+ QLabel *m_sliceLabelX;
+ QLabel *m_sliceLabelY;
+ QLabel *m_sliceLabelZ;
+};
+
+#endif
diff --git a/tests/volumetrictest/volumetrictest.pro b/tests/volumetrictest/volumetrictest.pro
new file mode 100644
index 00000000..df3ef0dd
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.pro
@@ -0,0 +1,14 @@
+!include( ../tests.pri ) {
+ error( "Couldn't find the tests.pri file!" )
+}
+
+SOURCES += main.cpp volumetrictest.cpp
+HEADERS += volumetrictest.h
+
+QT += widgets
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
+
+RESOURCES += \
+ volumetrictest.qrc
diff --git a/tests/volumetrictest/volumetrictest.qrc b/tests/volumetrictest/volumetrictest.qrc
new file mode 100644
index 00000000..7cd8533b
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>logo.png</file>
+ <file alias="mesh">cubeFilledFlat.obj</file>
+ <file>logo_no_padding.png</file>
+ </qresource>
+</RCC>
diff --git a/tools/blender/arrow.blend b/tools/blender/arrow.blend
new file mode 100644
index 00000000..9c0ef46b
--- /dev/null
+++ b/tools/blender/arrow.blend
Binary files differ
diff --git a/tools/blender/narrowArrow.blend b/tools/blender/narrowArrow.blend
new file mode 100644
index 00000000..3fd3dff9
--- /dev/null
+++ b/tools/blender/narrowArrow.blend
Binary files differ