summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@digia.com>2014-06-05 12:00:24 +0300
committerMiikka Heikkinen <miikka.heikkinen@digia.com>2014-06-05 12:03:16 +0300
commitcfbfbea7d932f98c3f541bb4fc3e975ffb7a5658 (patch)
treebed376368e9611c751cf1dd75b85de2827d67325
parent8435c15d224a4e9db0920ecd33c4bea3e70d83bf (diff)
parentc51e0f83ef6dd9e85db6953995585ba0cafb35d7 (diff)
Merge branch 'develop'
-rw-r--r--.qmake.conf2
-rw-r--r--README7
-rw-r--r--dist/changes-1.1.077
-rw-r--r--examples/datavisualization/bars/doc/images/bars-example.pngbin151337 -> 188289 bytes
-rw-r--r--examples/datavisualization/bars/doc/src/bars.qdoc15
-rw-r--r--examples/datavisualization/bars/graphmodifier.cpp32
-rw-r--r--examples/datavisualization/bars/graphmodifier.h4
-rw-r--r--examples/datavisualization/bars/main.cpp34
-rw-r--r--examples/datavisualization/custominput/doc/src/custominput.qdoc40
-rw-r--r--examples/datavisualization/customitems/customitemgraph.cpp317
-rw-r--r--examples/datavisualization/customitems/customitemgraph.h58
-rw-r--r--examples/datavisualization/customitems/customitems.pro19
-rw-r--r--examples/datavisualization/customitems/customitems.qrc12
-rw-r--r--examples/datavisualization/customitems/doc/images/customitems-example.pngbin0 -> 109729 bytes
-rw-r--r--examples/datavisualization/customitems/doc/src/customitems.qdoc72
-rw-r--r--examples/datavisualization/customitems/layer_1.pngbin0 -> 34540 bytes
-rw-r--r--examples/datavisualization/customitems/layer_2.pngbin0 -> 10553 bytes
-rw-r--r--examples/datavisualization/customitems/layer_3.pngbin0 -> 7119 bytes
-rw-r--r--examples/datavisualization/customitems/main.cpp118
-rw-r--r--examples/datavisualization/customitems/oilrig.obj2322
-rw-r--r--examples/datavisualization/customitems/pipe.obj330
-rw-r--r--examples/datavisualization/customitems/refinery.obj2330
-rw-r--r--examples/datavisualization/datavisualization.pro8
-rw-r--r--examples/datavisualization/draggableaxes/axesinputhandler.cpp137
-rw-r--r--examples/datavisualization/draggableaxes/axesinputhandler.h71
-rw-r--r--examples/datavisualization/draggableaxes/data.cpp160
-rw-r--r--examples/datavisualization/draggableaxes/data.h48
-rw-r--r--examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.pngbin0 -> 90050 bytes
-rw-r--r--examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc122
-rw-r--r--examples/datavisualization/draggableaxes/draggableaxes.pro18
-rw-r--r--examples/datavisualization/draggableaxes/main.cpp58
-rw-r--r--examples/datavisualization/itemmodel/main.cpp3
-rw-r--r--examples/datavisualization/qmlaxisdrag/doc/images/qmlaxisdrag-example.pngbin0 -> 89048 bytes
-rw-r--r--examples/datavisualization/qmlaxisdrag/doc/src/qmlaxisdrag.qdoc110
-rw-r--r--examples/datavisualization/qmlaxisdrag/main.cpp48
-rw-r--r--examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/NewButton.qml44
-rw-r--r--examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cube.obj415
-rw-r--r--examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cubetexture.pngbin0 -> 10429 bytes
-rw-r--r--examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/main.qml317
-rw-r--r--examples/datavisualization/qmlaxisdrag/qmlaxisdrag.pro11
-rw-r--r--examples/datavisualization/qmlaxisdrag/qmlaxisdrag.qrc12
-rw-r--r--examples/datavisualization/qmlaxisformatter/customformatter.cpp158
-rw-r--r--examples/datavisualization/qmlaxisformatter/customformatter.h71
-rw-r--r--examples/datavisualization/qmlaxisformatter/doc/images/qmlaxisformatter-example.pngbin0 -> 132952 bytes
-rw-r--r--examples/datavisualization/qmlaxisformatter/doc/src/qmlaxisformatter.qdoc115
-rw-r--r--examples/datavisualization/qmlaxisformatter/main.cpp57
-rw-r--r--examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/Data.qml50
-rw-r--r--examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/NewButton.qml52
-rw-r--r--examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/main.qml183
-rw-r--r--examples/datavisualization/qmlaxisformatter/qmlaxisformatter.pro16
-rw-r--r--examples/datavisualization/qmlaxisformatter/qmlaxisformatter.qrc7
-rw-r--r--examples/datavisualization/qmlbars/doc/images/qmlbars-example.pngbin149167 -> 146667 bytes
-rw-r--r--examples/datavisualization/qmlbars/doc/src/qmlbars.qdoc52
-rw-r--r--examples/datavisualization/qmlbars/qml/qmlbars/Axes.qml32
-rw-r--r--examples/datavisualization/qmlbars/qml/qmlbars/Data.qml168
-rw-r--r--examples/datavisualization/qmlbars/qml/qmlbars/main.qml166
-rw-r--r--examples/datavisualization/qmllegend/qml/qmllegend/LegendItem.qml6
-rw-r--r--examples/datavisualization/qmllegend/qml/qmllegend/main.qml12
-rw-r--r--examples/datavisualization/qmloscilloscope/datasource.cpp25
-rw-r--r--examples/datavisualization/qmloscilloscope/datasource.h6
-rw-r--r--examples/datavisualization/qmloscilloscope/doc/images/qmloscilloscope-example.pngbin134021 -> 129028 bytes
-rw-r--r--examples/datavisualization/qmloscilloscope/doc/src/qmloscilloscope.qdoc19
-rw-r--r--examples/datavisualization/qmloscilloscope/qml/qmloscilloscope/main.qml57
-rw-r--r--examples/datavisualization/rotations/scatterdatamodifier.cpp21
-rw-r--r--examples/datavisualization/rotations/scatterdatamodifier.h2
-rw-r--r--examples/datavisualization/surface/doc/src/surface.qdoc4
-rw-r--r--examples/datavisualization/surface/surfacegraph.cpp3
-rw-r--r--src/datavisualization/axis/axis.pri10
-rw-r--r--src/datavisualization/axis/qabstract3daxis.cpp201
-rw-r--r--src/datavisualization/axis/qabstract3daxis.h15
-rw-r--r--src/datavisualization/axis/qabstract3daxis_p.h17
-rw-r--r--src/datavisualization/axis/qcategory3daxis.cpp21
-rw-r--r--src/datavisualization/axis/qcategory3daxis_p.h12
-rw-r--r--src/datavisualization/axis/qlogvalue3daxisformatter.cpp432
-rw-r--r--src/datavisualization/axis/qlogvalue3daxisformatter.h72
-rw-r--r--src/datavisualization/axis/qlogvalue3daxisformatter_p.h71
-rw-r--r--src/datavisualization/axis/qvalue3daxis.cpp112
-rw-r--r--src/datavisualization/axis/qvalue3daxis.h13
-rw-r--r--src/datavisualization/axis/qvalue3daxis_p.h21
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter.cpp419
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter.h86
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter_p.h91
-rw-r--r--src/datavisualization/common.pri3
-rw-r--r--src/datavisualization/data/abstractitemmodelhandler.cpp4
-rw-r--r--src/datavisualization/data/abstractitemmodelhandler_p.h6
-rw-r--r--src/datavisualization/data/abstractrenderitem_p.h2
-rw-r--r--src/datavisualization/data/baritemmodelhandler.cpp162
-rw-r--r--src/datavisualization/data/baritemmodelhandler_p.h12
-rw-r--r--src/datavisualization/data/barrenderitem.cpp7
-rw-r--r--src/datavisualization/data/barrenderitem_p.h8
-rw-r--r--src/datavisualization/data/customrenderitem.cpp42
-rw-r--r--src/datavisualization/data/customrenderitem_p.h93
-rw-r--r--src/datavisualization/data/data.pri12
-rw-r--r--src/datavisualization/data/labelitem_p.h1
-rw-r--r--src/datavisualization/data/qabstract3dseries.cpp140
-rw-r--r--src/datavisualization/data/qabstract3dseries.h8
-rw-r--r--src/datavisualization/data/qabstract3dseries_p.h26
-rw-r--r--src/datavisualization/data/qabstractdataproxy.cpp5
-rw-r--r--src/datavisualization/data/qabstractdataproxy_p.h6
-rw-r--r--src/datavisualization/data/qbar3dseries.cpp53
-rw-r--r--src/datavisualization/data/qbar3dseries_p.h1
-rw-r--r--src/datavisualization/data/qbardataitem.cpp2
-rw-r--r--src/datavisualization/data/qbardataitem_p.h3
-rw-r--r--src/datavisualization/data/qbardataproxy.cpp12
-rw-r--r--src/datavisualization/data/qbardataproxy_p.h1
-rw-r--r--src/datavisualization/data/qcustom3ditem.cpp422
-rw-r--r--src/datavisualization/data/qcustom3ditem.h102
-rw-r--r--src/datavisualization/data/qcustom3ditem_p.h99
-rw-r--r--src/datavisualization/data/qcustom3dlabel.cpp358
-rw-r--r--src/datavisualization/data/qcustom3dlabel.h93
-rw-r--r--src/datavisualization/data/qcustom3dlabel_p.h73
-rw-r--r--src/datavisualization/data/qheightmapsurfacedataproxy.cpp2
-rw-r--r--src/datavisualization/data/qitemmodelbardataproxy.cpp403
-rw-r--r--src/datavisualization/data/qitemmodelbardataproxy.h66
-rw-r--r--src/datavisualization/data/qitemmodelbardataproxy_p.h12
-rw-r--r--src/datavisualization/data/qitemmodelscatterdataproxy.cpp326
-rw-r--r--src/datavisualization/data/qitemmodelscatterdataproxy.h47
-rw-r--r--src/datavisualization/data/qitemmodelscatterdataproxy_p.h10
-rw-r--r--src/datavisualization/data/qitemmodelsurfacedataproxy.cpp503
-rw-r--r--src/datavisualization/data/qitemmodelsurfacedataproxy.h80
-rw-r--r--src/datavisualization/data/qitemmodelsurfacedataproxy_p.h14
-rw-r--r--src/datavisualization/data/qscatter3dseries.cpp47
-rw-r--r--src/datavisualization/data/qscatter3dseries_p.h1
-rw-r--r--src/datavisualization/data/qscatterdataitem.cpp2
-rw-r--r--src/datavisualization/data/qscatterdataitem_p.h3
-rw-r--r--src/datavisualization/data/qscatterdataproxy.cpp3
-rw-r--r--src/datavisualization/data/qsurface3dseries.cpp49
-rw-r--r--src/datavisualization/data/qsurface3dseries_p.h1
-rw-r--r--src/datavisualization/data/qsurfacedataitem.cpp2
-rw-r--r--src/datavisualization/data/qsurfacedataitem_p.h3
-rw-r--r--src/datavisualization/data/qsurfacedataproxy.cpp36
-rw-r--r--src/datavisualization/data/scatteritemmodelhandler.cpp76
-rw-r--r--src/datavisualization/data/scatteritemmodelhandler_p.h12
-rw-r--r--src/datavisualization/data/scatterrenderitem.cpp6
-rw-r--r--src/datavisualization/data/scatterrenderitem_p.h4
-rw-r--r--src/datavisualization/data/surfaceitemmodelhandler.cpp199
-rw-r--r--src/datavisualization/data/surfaceitemmodelhandler_p.h16
-rw-r--r--src/datavisualization/doc/qtdatavisualization.qdocconf10
-rw-r--r--src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp8
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc180
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-color.qdoc35
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization.qdoc59
-rw-r--r--src/datavisualization/engine/abstract3dcontroller.cpp563
-rw-r--r--src/datavisualization/engine/abstract3dcontroller_p.h141
-rw-r--r--src/datavisualization/engine/abstract3drenderer.cpp797
-rw-r--r--src/datavisualization/engine/abstract3drenderer_p.h123
-rw-r--r--src/datavisualization/engine/axisrendercache.cpp89
-rw-r--r--src/datavisualization/engine/axisrendercache_p.h81
-rw-r--r--src/datavisualization/engine/bars3dcontroller.cpp133
-rw-r--r--src/datavisualization/engine/bars3dcontroller_p.h23
-rw-r--r--src/datavisualization/engine/bars3drenderer.cpp2245
-rw-r--r--src/datavisualization/engine/bars3drenderer_p.h51
-rw-r--r--src/datavisualization/engine/barseriesrendercache.cpp42
-rw-r--r--src/datavisualization/engine/barseriesrendercache_p.h61
-rw-r--r--src/datavisualization/engine/drawer.cpp174
-rw-r--r--src/datavisualization/engine/drawer_p.h12
-rw-r--r--src/datavisualization/engine/engine.pri8
-rw-r--r--src/datavisualization/engine/engine.qrc5
-rw-r--r--src/datavisualization/engine/meshes/backgroundNoFloor.obj (renamed from src/datavisualization/engine/meshes/backgroundNegatives.obj)20
-rw-r--r--src/datavisualization/engine/q3dbars.cpp8
-rw-r--r--src/datavisualization/engine/q3dcamera.cpp23
-rw-r--r--src/datavisualization/engine/q3dlight.cpp4
-rw-r--r--src/datavisualization/engine/q3dobject.cpp3
-rw-r--r--src/datavisualization/engine/q3dscatter.cpp6
-rw-r--r--src/datavisualization/engine/q3dscene.cpp17
-rw-r--r--src/datavisualization/engine/q3dscene.h2
-rw-r--r--src/datavisualization/engine/q3dscene_p.h1
-rw-r--r--src/datavisualization/engine/q3dsurface.cpp6
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.cpp355
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.h65
-rw-r--r--src/datavisualization/engine/qabstract3dgraph_p.h5
-rw-r--r--src/datavisualization/engine/scatter3dcontroller.cpp85
-rw-r--r--src/datavisualization/engine/scatter3dcontroller_p.h13
-rw-r--r--src/datavisualization/engine/scatter3drenderer.cpp1924
-rw-r--r--src/datavisualization/engine/scatter3drenderer_p.h53
-rw-r--r--src/datavisualization/engine/scatterseriesrendercache.cpp50
-rw-r--r--src/datavisualization/engine/scatterseriesrendercache_p.h77
-rw-r--r--src/datavisualization/engine/selectionpointer.cpp37
-rw-r--r--src/datavisualization/engine/selectionpointer_p.h12
-rw-r--r--src/datavisualization/engine/seriesrendercache.cpp112
-rw-r--r--src/datavisualization/engine/seriesrendercache_p.h33
-rw-r--r--src/datavisualization/engine/shaders/colorOnY.frag6
-rw-r--r--src/datavisualization/engine/shaders/colorOnY_ES2.frag10
-rw-r--r--src/datavisualization/engine/shaders/default.frag8
-rw-r--r--src/datavisualization/engine/shaders/default.vert10
-rw-r--r--src/datavisualization/engine/shaders/default_ES2.frag12
-rw-r--r--src/datavisualization/engine/shaders/plainColor.frag5
-rw-r--r--src/datavisualization/engine/shaders/shadow.frag6
-rw-r--r--src/datavisualization/engine/shaders/shadow.vert8
-rw-r--r--src/datavisualization/engine/shaders/shadowNoTex.frag8
-rw-r--r--src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag6
-rw-r--r--src/datavisualization/engine/shaders/surface.frag6
-rw-r--r--src/datavisualization/engine/shaders/surfaceFlat.frag6
-rw-r--r--src/datavisualization/engine/shaders/surfaceFlat.vert8
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowFlat.frag6
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowFlat.vert8
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowNoTex.frag6
-rw-r--r--src/datavisualization/engine/shaders/surface_ES2.frag10
-rw-r--r--src/datavisualization/engine/shaders/texture.frag37
-rw-r--r--src/datavisualization/engine/shaders/texture.vert28
-rw-r--r--src/datavisualization/engine/shaders/texture_ES2.frag40
-rw-r--r--src/datavisualization/engine/surface3dcontroller.cpp100
-rw-r--r--src/datavisualization/engine/surface3dcontroller_p.h8
-rw-r--r--src/datavisualization/engine/surface3drenderer.cpp1543
-rw-r--r--src/datavisualization/engine/surface3drenderer_p.h44
-rw-r--r--src/datavisualization/engine/surfaceseriesrendercache.cpp39
-rw-r--r--src/datavisualization/engine/surfaceseriesrendercache_p.h28
-rw-r--r--src/datavisualization/global/datavisualizationglobal_p.h14
-rw-r--r--src/datavisualization/global/qdatavisualizationglobal.h4
-rw-r--r--src/datavisualization/input/q3dinputhandler.cpp16
-rw-r--r--src/datavisualization/input/qabstract3dinputhandler.cpp8
-rw-r--r--src/datavisualization/input/qabstract3dinputhandler_p.h3
-rw-r--r--src/datavisualization/input/qtouch3dinputhandler.cpp3
-rw-r--r--src/datavisualization/theme/q3dtheme.cpp4
-rw-r--r--src/datavisualization/utils/abstractobjecthelper_p.h1
-rw-r--r--src/datavisualization/utils/camerahelper_p.h2
-rw-r--r--src/datavisualization/utils/objecthelper.cpp107
-rw-r--r--src/datavisualization/utils/objecthelper_p.h24
-rw-r--r--src/datavisualization/utils/scatterobjectbufferhelper.cpp226
-rw-r--r--src/datavisualization/utils/scatterobjectbufferhelper_p.h50
-rw-r--r--src/datavisualization/utils/scatterpointbufferhelper.cpp113
-rw-r--r--src/datavisualization/utils/scatterpointbufferhelper_p.h61
-rw-r--r--src/datavisualization/utils/shaderhelper_p.h1
-rw-r--r--src/datavisualization/utils/surfaceobject.cpp211
-rw-r--r--src/datavisualization/utils/surfaceobject_p.h32
-rw-r--r--src/datavisualization/utils/texturehelper.cpp18
-rw-r--r--src/datavisualization/utils/texturehelper_p.h3
-rw-r--r--src/datavisualization/utils/utils.cpp46
-rw-r--r--src/datavisualization/utils/utils.pri8
-rw-r--r--src/datavisualization/utils/utils_p.h18
-rw-r--r--src/datavisualizationqml2/abstractdeclarative.cpp158
-rw-r--r--src/datavisualizationqml2/abstractdeclarative_p.h70
-rw-r--r--src/datavisualizationqml2/datavisualizationqml2_plugin.cpp32
-rw-r--r--src/datavisualizationqml2/datavisualizationqml2_plugin.h11
-rw-r--r--src/datavisualizationqml2/declarativebars.cpp6
-rw-r--r--src/datavisualizationqml2/declarativebars_p.h6
-rw-r--r--src/datavisualizationqml2/declarativerendernode.cpp26
-rw-r--r--src/datavisualizationqml2/declarativerendernode_p.h4
-rw-r--r--src/datavisualizationqml2/declarativescatter.cpp13
-rw-r--r--src/datavisualizationqml2/declarativescatter_p.h3
-rw-r--r--src/datavisualizationqml2/declarativeseries.cpp12
-rw-r--r--src/datavisualizationqml2/declarativesurface.cpp3
-rw-r--r--src/datavisualizationqml2/declarativesurface_p.h3
-rw-r--r--src/datavisualizationqml2/declarativetheme.cpp16
-rw-r--r--src/datavisualizationqml2/declarativetheme_p.h8
-rw-r--r--src/datavisualizationqml2/designer/Bars3DSpecifics.qml23
-rw-r--r--src/datavisualizationqml2/designer/Scatter3DSpecifics.qml37
-rw-r--r--src/datavisualizationqml2/designer/Surface3DSpecifics.qml38
-rw-r--r--src/datavisualizationqml2/enumtostringmap.cpp15
-rw-r--r--src/datavisualizationqml2/glstatestore.cpp47
-rw-r--r--src/datavisualizationqml2/glstatestore_p.h17
-rw-r--r--src/datavisualizationqml2/plugins.qmltypes450
-rw-r--r--tests/barstest/chart.cpp465
-rw-r--r--tests/barstest/chart.h19
-rw-r--r--tests/barstest/main.cpp68
-rw-r--r--tests/itemmodeltest/itemmodeltest.pro7
-rw-r--r--tests/itemmodeltest/main.cpp315
-rw-r--r--tests/multigraphs/data.cpp2
-rw-r--r--tests/qmlcamera/qml/qmlcamera/Axes.qml4
-rw-r--r--tests/qmlcamera/qml/qmlcamera/Data.qml174
-rw-r--r--tests/qmlcamera/qml/qmlcamera/main.qml45
-rw-r--r--tests/qmlcamera/qmlcamera.qrc4
-rw-r--r--tests/qmlcamera/shuttle.obj6349
-rw-r--r--tests/qmlcamera/shuttle.pngbin0 -> 1361 bytes
-rw-r--r--tests/qmlmultitest/main.cpp47
-rw-r--r--tests/qmlmultitest/qml/qmlmultitest/Data.qml69
-rw-r--r--tests/qmlmultitest/qml/qmlmultitest/NewButton.qml52
-rw-r--r--tests/qmlmultitest/qml/qmlmultitest/main.qml248
-rw-r--r--tests/qmlmultitest/qmlmultitest.pro12
-rw-r--r--tests/qmlmultitest/qmlmultitest.qrc7
-rw-r--r--tests/scattertest/main.cpp178
-rw-r--r--tests/scattertest/scatterchart.cpp561
-rw-r--r--tests/scattertest/scatterchart.h28
-rw-r--r--tests/surfacetest/graphmodifier.cpp458
-rw-r--r--tests/surfacetest/graphmodifier.h22
-rw-r--r--tests/surfacetest/main.cpp111
-rw-r--r--tests/tests.pro4
-rw-r--r--tools/blender/oilrefinery.blendbin0 -> 567896 bytes
-rw-r--r--tools/blender/oilrig.blendbin0 -> 553204 bytes
279 files changed, 30597 insertions, 4295 deletions
diff --git a/.qmake.conf b/.qmake.conf
index c44318c8..9c6617fa 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,4 @@
load(qt_build_config)
CONFIG += qt_example_installs
-MODULE_VERSION=1.0.0
+MODULE_VERSION=1.1.0
diff --git a/README b/README
index 7a4b9edb..da0715d0 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
---------------------------
-Qt Data Visualization 1.0.0
+Qt Data Visualization 1.1.0
---------------------------
Qt Data Visualization module provides multiple graph types to visualize data in 3D space
@@ -82,6 +82,9 @@ Known Issues
- 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.
-- Scatter "point" meshes do not support gradients, they always use the base color.
- 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.
diff --git a/dist/changes-1.1.0 b/dist/changes-1.1.0
new file mode 100644
index 00000000..8a470ccb
--- /dev/null
+++ b/dist/changes-1.1.0
@@ -0,0 +1,77 @@
+Qt Data Visualization 1.1
+
+New features
+------------
+
+- Support for rendering the graph into an image.
+- QValue3DAxisFormatter class for creating custom value axis formatters.
+ With a custom formatter, you can control axis grid line and label positions,
+ as well as fully customize the label strings.
+- Logarithmic value axes are now supported via a QLogValue3DAxisFormatter.
+- Support for adding custom items and labels inside the graph with
+ QCustom3DItem and QCustom3DLabel classes.
+- Q3DScene::selectionQueryPosition now can select axis labels and custom items
+ as well as data items. Selection is detected via QAbstract3DGraph::selectedElement
+ property.
+- The current frames per second (FPS) measurement can be enabled and queried via
+ QAbstract3DGraph::measureFps and QAbstract3DGraph::currentFps properties.
+- QValue3DAxis::reversed property allows drawing the axis in reverse direction.
+- A single item model role can be mapped to multiple properties of the data items
+ when using item model proxies. Regular expression search and replace can be used
+ to make the data unique for each property. Useful for parsing e.g. timestamp field
+ to get values for both rows and columns of a bar chart.
+- Support for aggregating multiple mapping matches in bar and surface item model
+ proxies into single bar or surface point.
+- Support for orthographic projection in graphs via QAbstract3DGraph::orthoProjection.
+- Axis labels can now be set to automatically orient towards the camera with
+ QAbstract3DAxis::labelAutoRotation property to increase label readability
+ at all angles.
+- Bars3D: Clicking row/column labels can now be used to highlight rows/columns if
+ the selection mode allows it.
+- Surface3D: X values in items of a row and Z values of items of a column can now be
+ either ascending or descending for the surface to be valid.
+- Aspect ratio, i.e. the ratio between horizontal and vertical axes can be changed
+ for scatter and surface graphs.
+- Axis titles can now be optionally displayed beside the axes in the primary graph view.
+- Added support for optional optimizations via QAbstract3DGraph::optimizationHints.
+ Note: This feature is currently in beta. The only optimization hint currently supported
+ is OptimizationStatic for scatter graphs, which vastly improves the render speed for
+ large static data sets, allowing millions of points to be displayed on desktop platforms.
+
+Fixed issues
+------------
+
+General:
+- Optimized series caching in renderer.
+- Optimized object mesh caching in renderer.
+- Optimized visible are calculation for surface graphs.
+- Fixed crash when setting null color/gradient to theme in QML.
+- Fixed overriding theme color with explicit series color in QML.
+- Optimized changing only single item/row in data proxies.
+- Fixed a crash when using both Qt Charts and Qt Data Visualization in the same application.
+ Note: This causes a binary break for the item model proxies.
+- Bars3D: Fixed incorrect label positioning in slice mode when grid off.
+- Bars3D: User defined meshes that have flat base no longer glimmer through graph floor
+ when viewed from below.
+- Scatter3D: Range gradient now works for MeshPoints.
+- Surface3D: Fixed a crash when shadows were supported by OpenGL but flat shading was not.
+- Surface3D: Selection texture no longer gets corrupted in case there are multiple surfaces
+ visible and the axis ranges are adjusted.
+- Surface3D: Fixed shadow culling, improving the shadow cast on the surface itself.
+
+New examples
+------------
+
+- customitems: Example about showing custom items and labels in the graph.
+- draggableaxes: Shows how to implement an input handler to enable scrolling
+ the graph via dragging the axes.
+- qmlaxisdrag: Shows how to implement an input handler to enable scrolling
+ the graph via dragging the axes in QML.
+- qmlaxisformatter: Shows how to use customize axes using axis formatters.
+
+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.
diff --git a/examples/datavisualization/bars/doc/images/bars-example.png b/examples/datavisualization/bars/doc/images/bars-example.png
index 5bd2c7d9..fb79668d 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 361078bb..1fa3f934 100644
--- a/examples/datavisualization/bars/doc/src/bars.qdoc
+++ b/examples/datavisualization/bars/doc/src/bars.qdoc
@@ -29,6 +29,7 @@
\li Create an application with Q3DBars and some widgets
\li Use QBar3DSeries and QBarDataProxy to set data to the graph
\li Adjust some graph and series properties using widget controls
+ \li Select a row or a column by clicking an axis label
\endlist
It also demonstrates how having negative bar values affects the graph.
@@ -167,6 +168,20 @@
\li Font size
\endlist
+ \section1 Selecting a row/column by clicking an axis label
+
+ Selection by axis label is default functionality for bar graphs. As an example, you can select
+ rows by clicking an axis label in the following way:
+
+ \list
+ \li Change selection mode to \c SelectionRow
+ \li Click a year label
+ \li The row with the clicked year is selected
+ \endlist
+
+ 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 Example contents
*/
diff --git a/examples/datavisualization/bars/graphmodifier.cpp b/examples/datavisualization/bars/graphmodifier.cpp
index 5df4066c..9c280bfb 100644
--- a/examples/datavisualization/bars/graphmodifier.cpp
+++ b/examples/datavisualization/bars/graphmodifier.cpp
@@ -68,9 +68,15 @@ GraphModifier::GraphModifier(Q3DBars *bargraph)
m_temperatureAxis->setSubSegmentCount(m_subSegments);
m_temperatureAxis->setRange(m_minval, m_maxval);
m_temperatureAxis->setLabelFormat(QString(QStringLiteral("%.1f ") + celsiusString));
+ m_temperatureAxis->setLabelAutoRotation(30.0f);
+ m_temperatureAxis->setTitleVisible(true);
m_yearAxis->setTitle("Year");
+ m_yearAxis->setLabelAutoRotation(30.0f);
+ m_yearAxis->setTitleVisible(true);
m_monthAxis->setTitle("Month");
+ m_monthAxis->setLabelAutoRotation(30.0f);
+ m_monthAxis->setTitleVisible(true);
m_graph->setValueAxis(m_temperatureAxis);
m_graph->setRowAxis(m_yearAxis);
@@ -236,6 +242,27 @@ void GraphModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality
emit shadowQualityChanged(quality);
}
+void GraphModifier::changeLabelRotation(int rotation)
+{
+ m_temperatureAxis->setLabelAutoRotation(float(rotation));
+ m_monthAxis->setLabelAutoRotation(float(rotation));
+ m_yearAxis->setLabelAutoRotation(float(rotation));
+}
+
+void GraphModifier::setAxisTitleVisibility(bool enabled)
+{
+ m_temperatureAxis->setTitleVisible(enabled);
+ m_monthAxis->setTitleVisible(enabled);
+ m_yearAxis->setTitleVisible(enabled);
+}
+
+void GraphModifier::setAxisTitleFixed(bool enabled)
+{
+ m_temperatureAxis->setTitleFixed(enabled);
+ m_monthAxis->setTitleFixed(enabled);
+ m_yearAxis->setTitleFixed(enabled);
+}
+
void GraphModifier::changeShadowQuality(int quality)
{
QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality);
@@ -278,3 +305,8 @@ void GraphModifier::setSeriesVisibility(int enabled)
{
m_secondarySeries->setVisible(bool(enabled));
}
+
+void GraphModifier::setReverseValueAxis(int enabled)
+{
+ m_graph->valueAxis()->setReversed(enabled);
+}
diff --git a/examples/datavisualization/bars/graphmodifier.h b/examples/datavisualization/bars/graphmodifier.h
index cac002a1..107ffbab 100644
--- a/examples/datavisualization/bars/graphmodifier.h
+++ b/examples/datavisualization/bars/graphmodifier.h
@@ -48,6 +48,7 @@ public:
void setGridEnabled(int enabled);
void setSmoothBars(int smooth);
void setSeriesVisibility(int enabled);
+ void setReverseValueAxis(int enabled);
public slots:
void changeRange(int range);
@@ -56,6 +57,9 @@ public slots:
void changeTheme(int theme);
void changeShadowQuality(int quality);
void shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality shadowQuality);
+ void changeLabelRotation(int rotation);
+ void setAxisTitleVisibility(bool enabled);
+ void setAxisTitleFixed(bool enabled);
signals:
void shadowQualityChanged(int quality);
diff --git a/examples/datavisualization/bars/main.cpp b/examples/datavisualization/bars/main.cpp
index e6461202..96abdc51 100644
--- a/examples/datavisualization/bars/main.cpp
+++ b/examples/datavisualization/bars/main.cpp
@@ -132,6 +132,10 @@ int main(int argc, char **argv)
seriesCheckBox->setText(QStringLiteral("Show second series"));
seriesCheckBox->setChecked(false);
+ QCheckBox *reverseValueAxisCheckBox = new QCheckBox(widget);
+ reverseValueAxisCheckBox->setText(QStringLiteral("Reverse value axis"));
+ reverseValueAxisCheckBox->setChecked(false);
+
//! [4]
QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget);
rotationSliderX->setTickInterval(30);
@@ -179,6 +183,21 @@ int main(int argc, char **argv)
rangeList->addItem(QStringLiteral("All"));
rangeList->setCurrentIndex(8);
+ QCheckBox *axisTitlesVisibleCB = new QCheckBox(widget);
+ axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible"));
+ axisTitlesVisibleCB->setChecked(true);
+
+ QCheckBox *axisTitlesFixedCB = new QCheckBox(widget);
+ axisTitlesFixedCB->setText(QStringLiteral("Axis titles fixed"));
+ axisTitlesFixedCB->setChecked(true);
+
+ QSlider *axisLabelRotationSlider = new QSlider(Qt::Horizontal, widget);
+ axisLabelRotationSlider->setTickInterval(10);
+ axisLabelRotationSlider->setTickPosition(QSlider::TicksBelow);
+ axisLabelRotationSlider->setMinimum(0);
+ axisLabelRotationSlider->setValue(30);
+ axisLabelRotationSlider->setMaximum(90);
+
//! [5]
vLayout->addWidget(new QLabel(QStringLiteral("Rotate horizontally")));
vLayout->addWidget(rotationSliderX, 0, Qt::AlignTop);
@@ -191,6 +210,9 @@ int main(int argc, char **argv)
vLayout->addWidget(gridCheckBox);
vLayout->addWidget(smoothCheckBox);
vLayout->addWidget(seriesCheckBox);
+ vLayout->addWidget(reverseValueAxisCheckBox);
+ vLayout->addWidget(axisTitlesVisibleCB);
+ vLayout->addWidget(axisTitlesFixedCB);
vLayout->addWidget(new QLabel(QStringLiteral("Show year")));
vLayout->addWidget(rangeList);
vLayout->addWidget(new QLabel(QStringLiteral("Change bar style")));
@@ -204,7 +226,9 @@ int main(int argc, char **argv)
vLayout->addWidget(new QLabel(QStringLiteral("Change font")));
vLayout->addWidget(fontList);
vLayout->addWidget(new QLabel(QStringLiteral("Adjust font size")));
- vLayout->addWidget(fontSizeSlider, 1, Qt::AlignTop);
+ vLayout->addWidget(fontSizeSlider);
+ vLayout->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
+ vLayout->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop);
//! [2]
GraphModifier *modifier = new GraphModifier(widgetgraph);
@@ -228,6 +252,8 @@ int main(int argc, char **argv)
&GraphModifier::setSmoothBars);
QObject::connect(seriesCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setSeriesVisibility);
+ QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setReverseValueAxis);
QObject::connect(modifier, &GraphModifier::backgroundEnabledChanged,
backgroundCheckBox, &QCheckBox::setChecked);
@@ -264,6 +290,12 @@ int main(int argc, char **argv)
QObject::connect(modifier, &GraphModifier::fontChanged, fontList,
&QFontComboBox::setCurrentFont);
+ QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setAxisTitleVisibility);
+ QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setAxisTitleFixed);
+ QObject::connect(axisLabelRotationSlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::changeLabelRotation);
//! [3]
widget->show();
return app.exec();
diff --git a/examples/datavisualization/custominput/doc/src/custominput.qdoc b/examples/datavisualization/custominput/doc/src/custominput.qdoc
index 59d3c090..46bcdb8d 100644
--- a/examples/datavisualization/custominput/doc/src/custominput.qdoc
+++ b/examples/datavisualization/custominput/doc/src/custominput.qdoc
@@ -22,49 +22,55 @@
\ingroup qtdatavisualization_examples
\brief Implementing custom input handler in a widget application.
- The Custom Input example shows how to customize the 3D graph controls in a widget application using a custom graph input handler to capture and process mouse events.
- The code in this example shows also how the camera is controlled by using QPropertyAnimation to animate the camera and item selection
- is done on mouseover rather than clicking any mouse buttons. Also the code shows how to implement similar zoom with mouse wheel functionality as the default
- input handler implements.
+ The Custom Input example shows how to customize the 3D graph controls in a widget application
+ using a custom graph input handler to capture and process mouse events.
+ The code in this example shows also how the camera is controlled by using QPropertyAnimation
+ to animate the camera and item selection is done on mouseover rather than clicking any mouse
+ buttons. Also the code shows how to implement similar zoom with mouse wheel functionality as
+ the default input handler implements.
\image custominput-example.png
\section1 Replacing default input handling
- The default input handling mechanism is replaced by setting the active input handler of \l Q3DScatter
- to \c CustomInputHandler that implements the custom behavior.
+ The default input handling mechanism is replaced by setting the active input handler of
+ Q3DScatter to \c CustomInputHandler that implements the custom behavior.
\snippet custominput/scatterdatamodifier.cpp 0
\section1 Implementing custom selection handling
- The on mouseover selection handling is implemented in the \c CustomInputHandler that captures the mouse events.
- It then stores the last known coordinates to the \l QAbstract3DInputHandler::inputPosition property.
+ The on mouseover selection handling is implemented in the \c CustomInputHandler that captures
+ the mouse events. It then stores the last known coordinates to the
+ QAbstract3DInputHandler::inputPosition property.
\snippet custominput/custominputhandler.cpp 0
- As the selection is one shot, and is cleared each time a 3D frame is rendered, a timer is setup to retrigger selection so that the selection moves to the item
- currently under the mouse cursor as the camera animates around the graph even when the mouse cursor is not moving.
+ As the selection is one shot, and is cleared each time a 3D frame is rendered, a timer is setup
+ to retrigger selection so that the selection moves to the item currently under the mouse cursor
+ as the camera animates around the graph even when the mouse cursor is not moving.
\snippet custominput/scatterdatamodifier.cpp 1
\section1 Implementing custom zoom handling
- The camera has a zoom factor that represents amount of zoom in percentages. In this example the zoom range is limited
- between 10% and 500%. This range is then divided to four subranges where \c angleDelta is scaled to different amount of zoom change
- based on the current subrange.
+ The camera has a zoom factor that represents amount of zoom in percentages. In this example the
+ zoom range is limited between 10% and 500%. This range is then divided to four subranges where
+ \c angleDelta is scaled to different amount of zoom change based on the current subrange.
\snippet custominput/custominputhandler.cpp 1
\section1 Implementing custom camera handling
- The camera is animated to constantly rotate around the graph with two animations. The rotation around the graph is done with
- a simple QPropertyAnimation that just increments during 20 seconds from 0 degrees to 360 degrees and sets the \l Q3DCamera::xRotation property.
+ The camera is animated to constantly rotate around the graph with two animations. The rotation
+ around the graph is done with a simple QPropertyAnimation that just increments during 20
+ seconds from 0 degrees to 360 degrees and sets the Q3DCamera::xRotation property.
\snippet custominput/scatterdatamodifier.cpp 2
- The camera movement up and down is implemented with a QSequentialAnimationGroup that varies the \l Q3DCamera::yRotation property of the camera
- from 5 degrees to 45 degrees and back with in and out easing.
+ The camera movement up and down is implemented with a QSequentialAnimationGroup that varies
+ the Q3DCamera::yRotation property of the camera from 5 degrees to 45 degrees and back with in
+ and out easing.
\snippet custominput/scatterdatamodifier.cpp 3
*/
diff --git a/examples/datavisualization/customitems/customitemgraph.cpp b/examples/datavisualization/customitems/customitemgraph.cpp
new file mode 100644
index 00000000..96ce5e46
--- /dev/null
+++ b/examples/datavisualization/customitems/customitemgraph.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** 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 "customitemgraph.h"
+
+#include <QtDataVisualization/Q3DTheme>
+#include <QtDataVisualization/QCustom3DItem>
+#include <QtDataVisualization/QCustom3DLabel>
+#include <QtGui/QImage>
+
+using namespace QtDataVisualization;
+
+CustomItemGraph::CustomItemGraph(Q3DSurface *surface, QLabel *label)
+ : m_graph(surface),
+ m_textField(label),
+ m_previouslyAnimatedItem(0)
+{
+ QImage layerOneHMap(":/maps/layer_1.png");
+ QHeightMapSurfaceDataProxy *layerOneProxy = new QHeightMapSurfaceDataProxy(layerOneHMap);
+ QSurface3DSeries *layerOneSeries = new QSurface3DSeries(layerOneProxy);
+ layerOneSeries->setItemLabelFormat(QStringLiteral("(@xLabel, @zLabel): @yLabel"));
+ layerOneProxy->setValueRanges(34.0f, 40.0f, 18.0f, 24.0f);
+ layerOneSeries->setDrawMode(QSurface3DSeries::DrawSurface);
+ layerOneSeries->setFlatShadingEnabled(false);
+
+ QImage layerTwoHMap(":/maps/layer_2.png");
+ QHeightMapSurfaceDataProxy *layerTwoProxy = new QHeightMapSurfaceDataProxy(layerTwoHMap);
+ QSurface3DSeries *layerTwoSeries = new QSurface3DSeries(layerTwoProxy);
+ layerTwoSeries->setItemLabelFormat(QStringLiteral("(@xLabel, @zLabel): @yLabel"));
+ layerTwoProxy->setValueRanges(34.0f, 40.0f, 18.0f, 24.0f);
+ layerTwoSeries->setDrawMode(QSurface3DSeries::DrawSurface);
+ layerTwoSeries->setFlatShadingEnabled(false);
+
+ QImage layerThreeHMap(":/maps/layer_3.png");
+ QHeightMapSurfaceDataProxy *layerThreeProxy = new QHeightMapSurfaceDataProxy(layerThreeHMap);
+ QSurface3DSeries *layerThreeSeries = new QSurface3DSeries(layerThreeProxy);
+ layerThreeSeries->setItemLabelFormat(QStringLiteral("(@xLabel, @zLabel): @yLabel"));
+ layerThreeProxy->setValueRanges(34.0f, 40.0f, 18.0f, 24.0f);
+ layerThreeSeries->setDrawMode(QSurface3DSeries::DrawSurface);
+ layerThreeSeries->setFlatShadingEnabled(false);
+
+ m_graph->axisX()->setLabelFormat("%.1f N");
+ m_graph->axisZ()->setLabelFormat("%.1f E");
+ m_graph->axisX()->setRange(34.0f, 40.0f);
+ m_graph->axisY()->setRange(0.0f, 200.0f);
+ m_graph->axisZ()->setRange(18.0f, 24.0f);
+
+ m_graph->axisX()->setTitle(QStringLiteral("Latitude"));
+ m_graph->axisY()->setTitle(QStringLiteral("Height"));
+ m_graph->axisZ()->setTitle(QStringLiteral("Longitude"));
+
+ m_graph->addSeries(layerOneSeries);
+ m_graph->addSeries(layerTwoSeries);
+ m_graph->addSeries(layerThreeSeries);
+
+ QLinearGradient grOne;
+ grOne.setColorAt(0.0, Qt::black);
+ grOne.setColorAt(0.38, Qt::darkYellow);
+ grOne.setColorAt(0.39, Qt::darkGreen);
+ grOne.setColorAt(0.5, Qt::darkGray);
+ grOne.setColorAt(1.0, Qt::gray);
+ m_graph->seriesList().at(0)->setBaseGradient(grOne);
+ m_graph->seriesList().at(0)->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
+
+ QLinearGradient grTwo;
+ grTwo.setColorAt(0.385, Qt::blue);
+ grTwo.setColorAt(0.395, Qt::white);
+ m_graph->seriesList().at(1)->setBaseGradient(grTwo);
+ m_graph->seriesList().at(1)->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
+
+ QLinearGradient grThree;
+ grThree.setColorAt(0.0, Qt::white);
+ grThree.setColorAt(0.05, Qt::black);
+ m_graph->seriesList().at(2)->setBaseGradient(grThree);
+ m_graph->seriesList().at(2)->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
+
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
+
+ connect(m_graph, &QAbstract3DGraph::selectedElementChanged,
+ this, &CustomItemGraph::handleElementSelected);
+
+ m_selectionAnimation = new QPropertyAnimation(this);
+ m_selectionAnimation->setPropertyName("scaling");
+ m_selectionAnimation->setDuration(500);
+ m_selectionAnimation->setLoopCount(-1);
+
+ QFont titleFont = QFont("Century Gothic", 30);
+ titleFont.setBold(true);
+ QCustom3DLabel *titleLabel = new QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
+ QVector3D(0.0f, 1.2f, 0.0f),
+ QVector3D(1.0f, 1.0f, 0.0f),
+ QQuaternion());
+ titleLabel->setPositionAbsolute(true);
+ titleLabel->setFacingCamera(true);
+ titleLabel->setBackgroundColor(QColor(0x66cdaa));
+ m_graph->addCustomItem(titleLabel);
+}
+
+CustomItemGraph::~CustomItemGraph()
+{
+ delete m_graph;
+}
+
+void CustomItemGraph::toggleItemOne(bool show)
+{
+ //! [1]
+ QVector3D positionOne = QVector3D(39.0f, 77.0f, 19.2f);
+ //! [1]
+ QVector3D positionOnePipe = QVector3D(39.0f, 45.0f, 19.2f);
+ QVector3D positionOneLabel = QVector3D(39.0f, 107.0f, 19.2f);
+ if (show) {
+ //! [0]
+ QImage color = QImage(2, 2, QImage::Format_RGB32);
+ color.fill(Qt::red);
+ //! [0]
+ //! [2]
+ QCustom3DItem *item = new QCustom3DItem(":/items/oilrig.obj", positionOne,
+ QVector3D(0.025f, 0.025f, 0.025f),
+ QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 45.0f),
+ color);
+ //! [2]
+ //! [3]
+ m_graph->addCustomItem(item);
+ //! [3]
+ item = new QCustom3DItem(":/items/pipe.obj", positionOnePipe,
+ QVector3D(0.005f, 0.5f, 0.005f),
+ QQuaternion(),
+ color);
+ item->setShadowCasting(false);
+ m_graph->addCustomItem(item);
+
+ QCustom3DLabel *label = new QCustom3DLabel();
+ label->setText("Oil Rig One");
+ label->setPosition(positionOneLabel);
+ label->setScaling(QVector3D(1.0f, 1.0f, 1.0f));
+ m_graph->addCustomItem(label);
+ } else {
+ resetSelection();
+ //! [4]
+ m_graph->removeCustomItemAt(positionOne);
+ //! [4]
+ m_graph->removeCustomItemAt(positionOnePipe);
+ m_graph->removeCustomItemAt(positionOneLabel);
+ }
+}
+
+void CustomItemGraph::toggleItemTwo(bool show)
+{
+ QVector3D positionTwo = QVector3D(34.5f, 77.0f, 23.4f);
+ QVector3D positionTwoPipe = QVector3D(34.5f, 45.0f, 23.4f);
+ QVector3D positionTwoLabel = QVector3D(34.5f, 107.0f, 23.4f);
+ if (show) {
+ QImage color = QImage(2, 2, QImage::Format_RGB32);
+ color.fill(Qt::red);
+ QCustom3DItem *item = new QCustom3DItem();
+ item->setMeshFile(":/items/oilrig.obj");
+ item->setPosition(positionTwo);
+ item->setScaling(QVector3D(0.025f, 0.025f, 0.025f));
+ item->setRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 25.0f));
+ item->setTextureImage(color);
+ m_graph->addCustomItem(item);
+ item = new QCustom3DItem(":/items/pipe.obj", positionTwoPipe,
+ QVector3D(0.005f, 0.5f, 0.005f),
+ QQuaternion(),
+ color);
+ item->setShadowCasting(false);
+ m_graph->addCustomItem(item);
+
+ QCustom3DLabel *label = new QCustom3DLabel();
+ label->setText("Oil Rig Two");
+ label->setPosition(positionTwoLabel);
+ label->setScaling(QVector3D(1.0f, 1.0f, 1.0f));
+ m_graph->addCustomItem(label);
+ } else {
+ resetSelection();
+ m_graph->removeCustomItemAt(positionTwo);
+ m_graph->removeCustomItemAt(positionTwoPipe);
+ m_graph->removeCustomItemAt(positionTwoLabel);
+ }
+}
+
+void CustomItemGraph::toggleItemThree(bool show)
+{
+ QVector3D positionThree = QVector3D(34.5f, 86.0f, 19.1f);
+ QVector3D positionThreeLabel = QVector3D(34.5f, 116.0f, 19.1f);
+ if (show) {
+ QImage color = QImage(2, 2, QImage::Format_RGB32);
+ color.fill(Qt::darkMagenta);
+ QCustom3DItem *item = new QCustom3DItem();
+ item->setMeshFile(":/items/refinery.obj");
+ item->setPosition(positionThree);
+ item->setScaling(QVector3D(0.04f, 0.04f, 0.04f));
+ item->setRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 75.0f));
+ item->setTextureImage(color);
+ m_graph->addCustomItem(item);
+
+ QCustom3DLabel *label = new QCustom3DLabel();
+ label->setText("Refinery");
+ label->setPosition(positionThreeLabel);
+ label->setScaling(QVector3D(1.0f, 1.0f, 1.0f));
+ m_graph->addCustomItem(label);
+ } else {
+ resetSelection();
+ m_graph->removeCustomItemAt(positionThree);
+ m_graph->removeCustomItemAt(positionThreeLabel);
+ }
+}
+
+void CustomItemGraph::toggleSeeThrough(bool seethrough)
+{
+ if (seethrough) {
+ m_graph->seriesList().at(0)->setDrawMode(QSurface3DSeries::DrawWireframe);
+ m_graph->seriesList().at(1)->setDrawMode(QSurface3DSeries::DrawWireframe);
+ } else {
+ m_graph->seriesList().at(0)->setDrawMode(QSurface3DSeries::DrawSurface);
+ m_graph->seriesList().at(1)->setDrawMode(QSurface3DSeries::DrawSurface);
+ }
+}
+
+void CustomItemGraph::toggleOilHighlight(bool highlight)
+{
+ if (highlight) {
+ QLinearGradient grThree;
+ grThree.setColorAt(0.0, Qt::black);
+ grThree.setColorAt(0.05, Qt::red);
+ m_graph->seriesList().at(2)->setBaseGradient(grThree);
+ } else {
+ QLinearGradient grThree;
+ grThree.setColorAt(0.0, Qt::white);
+ grThree.setColorAt(0.05, Qt::black);
+ m_graph->seriesList().at(2)->setBaseGradient(grThree);
+ }
+}
+
+void CustomItemGraph::toggleShadows(bool shadows)
+{
+ if (shadows)
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium);
+ else
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+}
+
+void CustomItemGraph::handleElementSelected(QAbstract3DGraph::ElementType type)
+{
+ resetSelection();
+ if (type == QAbstract3DGraph::ElementCustomItem) {
+ QCustom3DItem *item = m_graph->selectedCustomItem();
+ QString text;
+ if (qobject_cast<QCustom3DLabel *>(item) != 0) {
+ text.append("Custom label: ");
+ } else {
+ QStringList split = item->meshFile().split("/");
+ text.append(split.last());
+ text.append(": ");
+ }
+ int index = m_graph->selectedCustomItemIndex();
+ text.append(QString::number(index));
+ m_textField->setText(text);
+ m_previouslyAnimatedItem = item;
+ m_previousScaling = item->scaling();
+ m_selectionAnimation->setTargetObject(item);
+ m_selectionAnimation->setStartValue(item->scaling());
+ m_selectionAnimation->setEndValue(item->scaling() * 1.5f);
+ m_selectionAnimation->start();
+ } else if (type == QAbstract3DGraph::ElementSeries) {
+ QString text = "Surface (";
+ QSurface3DSeries *series = m_graph->selectedSeries();
+ if (series) {
+ QPoint point = series->selectedPoint();
+ QString posStr;
+ posStr.setNum(point.x());
+ text.append(posStr);
+ text.append(", ");
+ posStr.setNum(point.y());
+ text.append(posStr);
+ }
+ text.append(")");
+ m_textField->setText(text);
+ } else if (type > QAbstract3DGraph::ElementSeries
+ && type < QAbstract3DGraph::ElementCustomItem) {
+ int index = m_graph->selectedLabelIndex();
+ QString text;
+ if (type == QAbstract3DGraph::ElementAxisXLabel)
+ text.append("Axis X label: ");
+ else if (type == QAbstract3DGraph::ElementAxisYLabel)
+ text.append("Axis Y label: ");
+ else
+ text.append("Axis Z label: ");
+ text.append(QString::number(index));
+ m_textField->setText(text);
+ } else {
+ m_textField->setText("Nothing");
+ }
+}
+
+void CustomItemGraph::resetSelection()
+{
+ m_selectionAnimation->stop();
+ if (m_previouslyAnimatedItem)
+ m_previouslyAnimatedItem->setScaling(m_previousScaling);
+ m_previouslyAnimatedItem = 0;
+}
diff --git a/examples/datavisualization/customitems/customitemgraph.h b/examples/datavisualization/customitems/customitemgraph.h
new file mode 100644
index 00000000..e86d3910
--- /dev/null
+++ b/examples/datavisualization/customitems/customitemgraph.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 CUSTOMITEMGRAPH_H
+#define CUSTOMITEMGRAPH_H
+
+#include <QtDataVisualization/Q3DSurface>
+#include <QtDataVisualization/QSurfaceDataProxy>
+#include <QtDataVisualization/QHeightMapSurfaceDataProxy>
+#include <QtDataVisualization/QSurface3DSeries>
+#include <QtWidgets/QSlider>
+#include <QtWidgets/QLabel>
+#include <QtCore/QPropertyAnimation>
+
+using namespace QtDataVisualization;
+
+class CustomItemGraph : public QObject
+{
+ Q_OBJECT
+public:
+ explicit CustomItemGraph(Q3DSurface *surface, QLabel *label);
+ ~CustomItemGraph();
+
+ void toggleItemOne(bool show);
+ void toggleItemTwo(bool show);
+ void toggleItemThree(bool show);
+ void toggleSeeThrough(bool seethrough);
+ void toggleOilHighlight(bool highlight);
+ void toggleShadows(bool shadows);
+
+private:
+ void handleElementSelected(QAbstract3DGraph::ElementType type);
+ void resetSelection();
+
+private:
+ Q3DSurface *m_graph;
+ QLabel *m_textField;
+ QPropertyAnimation *m_selectionAnimation;
+ QCustom3DItem *m_previouslyAnimatedItem;
+ QVector3D m_previousScaling;
+};
+
+#endif
diff --git a/examples/datavisualization/customitems/customitems.pro b/examples/datavisualization/customitems/customitems.pro
new file mode 100644
index 00000000..99582ea6
--- /dev/null
+++ b/examples/datavisualization/customitems/customitems.pro
@@ -0,0 +1,19 @@
+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 \
+ customitemgraph.cpp
+
+HEADERS += customitemgraph.h
+
+QT += widgets
+
+RESOURCES += customitems.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
diff --git a/examples/datavisualization/customitems/customitems.qrc b/examples/datavisualization/customitems/customitems.qrc
new file mode 100644
index 00000000..1337c9fc
--- /dev/null
+++ b/examples/datavisualization/customitems/customitems.qrc
@@ -0,0 +1,12 @@
+<RCC>
+ <qresource prefix="/maps">
+ <file>layer_1.png</file>
+ <file>layer_2.png</file>
+ <file>layer_3.png</file>
+ </qresource>
+ <qresource prefix="/items">
+ <file>refinery.obj</file>
+ <file>oilrig.obj</file>
+ <file>pipe.obj</file>
+ </qresource>
+</RCC>
diff --git a/examples/datavisualization/customitems/doc/images/customitems-example.png b/examples/datavisualization/customitems/doc/images/customitems-example.png
new file mode 100644
index 00000000..2732638e
--- /dev/null
+++ b/examples/datavisualization/customitems/doc/images/customitems-example.png
Binary files differ
diff --git a/examples/datavisualization/customitems/doc/src/customitems.qdoc b/examples/datavisualization/customitems/doc/src/customitems.qdoc
new file mode 100644
index 00000000..f2699998
--- /dev/null
+++ b/examples/datavisualization/customitems/doc/src/customitems.qdoc
@@ -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
+**
+****************************************************************************/
+
+/*!
+ \example customitems
+ \title Custom Items Example
+ \ingroup qtdatavisualization_examples
+ \brief Adding custom items to a surface graph.
+ \since QtDataVisualization 1.1
+
+ The custom items example shows how to add your own custom meshes as items to a graph, and how
+ to remove them.
+
+ \image customitems-example.png
+
+ \section1 Adding custom meshes to the application
+
+ We'll add the meshes in a resource file:
+
+ \code
+ <RCC>
+ ...
+ <qresource prefix="/items">
+ <file>refinery.obj</file>
+ <file>oilrig.obj</file>
+ </qresource>
+ </RCC>
+ \endcode
+
+ \section1 Adding custom item to a graph
+
+ In this example we do not have specific textures for our meshes, so we'll just create a small
+ QImage and fill it with a single color:
+
+ \snippet customitems/customitemgraph.cpp 0
+
+ Then we'll specify the position for the item in a variable. This way we'll be able to use it
+ later for removing the correct item:
+
+ \snippet customitems/customitemgraph.cpp 1
+
+ Then we'll create a new QCustom3DItem with all the parameters:
+
+ \snippet customitems/customitemgraph.cpp 2
+
+ And finally we'll just add the item:
+
+ \snippet customitems/customitemgraph.cpp 3
+
+ \section1 Removing custom item from a graph
+
+ We'll just call \c removeCustomItemAt() with the position of the item:
+
+ \snippet customitems/customitemgraph.cpp 4
+
+ \section1 Example Contents
+*/
diff --git a/examples/datavisualization/customitems/layer_1.png b/examples/datavisualization/customitems/layer_1.png
new file mode 100644
index 00000000..9138c710
--- /dev/null
+++ b/examples/datavisualization/customitems/layer_1.png
Binary files differ
diff --git a/examples/datavisualization/customitems/layer_2.png b/examples/datavisualization/customitems/layer_2.png
new file mode 100644
index 00000000..61631ae8
--- /dev/null
+++ b/examples/datavisualization/customitems/layer_2.png
Binary files differ
diff --git a/examples/datavisualization/customitems/layer_3.png b/examples/datavisualization/customitems/layer_3.png
new file mode 100644
index 00000000..066ffbe7
--- /dev/null
+++ b/examples/datavisualization/customitems/layer_3.png
Binary files differ
diff --git a/examples/datavisualization/customitems/main.cpp b/examples/datavisualization/customitems/main.cpp
new file mode 100644
index 00000000..7f5dd01e
--- /dev/null
+++ b/examples/datavisualization/customitems/main.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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 "customitemgraph.h"
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QLabel>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DSurface *graph = new Q3DSurface();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ container->setMinimumSize(QSize(800, 600));
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget *widget = new QWidget;
+ QHBoxLayout *hLayout = new QHBoxLayout(widget);
+ QVBoxLayout *vLayoutLeft = new QVBoxLayout();
+ vLayoutLeft->setAlignment(Qt::AlignTop);
+ QVBoxLayout *vLayoutRight = new QVBoxLayout();
+ vLayoutRight->setAlignment(Qt::AlignTop);
+ hLayout->addLayout(vLayoutLeft);
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayoutRight);
+
+ QFont font = QFont("Century Gothic", 14);
+ QLabel *label = new QLabel("Show:");
+ font.setBold(true);
+ label->setFont(font);
+ vLayoutLeft->addWidget(label);
+
+ font.setBold(false);
+ QCheckBox *checkboxOne = new QCheckBox("Oil Rig 1");
+ checkboxOne->setFont(font);
+ vLayoutLeft->addWidget(checkboxOne);
+
+ QCheckBox *checkboxTwo = new QCheckBox("Oil Rig 2");
+ checkboxTwo->setFont(font);
+ vLayoutLeft->addWidget(checkboxTwo);
+
+ QCheckBox *checkboxThree = new QCheckBox("Refinery");
+ checkboxThree->setFont(font);
+ vLayoutLeft->addWidget(checkboxThree);
+
+ QLabel *label2 = new QLabel("Visuals:");
+ font.setBold(true);
+ label2->setFont(font);
+ vLayoutRight->addWidget(label2);
+
+ QCheckBox *checkboxOneRight = new QCheckBox("See-Through");
+ font.setBold(false);
+ checkboxOneRight->setFont(font);
+ vLayoutRight->addWidget(checkboxOneRight);
+
+ QCheckBox *checkboxTwoRight = new QCheckBox("Highlight Oil");
+ checkboxTwoRight->setFont(font);
+ vLayoutRight->addWidget(checkboxTwoRight);
+
+ QCheckBox *checkboxThreeRight = new QCheckBox("Shadows");
+ checkboxThreeRight->setFont(font);
+ checkboxThreeRight->setChecked(true);
+ vLayoutRight->addWidget(checkboxThreeRight);
+
+ QLabel *label3 = new QLabel("Selection:");
+ font.setBold(true);
+ label3->setFont(font);
+ vLayoutRight->addWidget(label3);
+
+ QLabel *label4 = new QLabel("Nothing");
+ font.setBold(false);
+ font.setPointSize(12);
+ label4->setFont(font);
+ vLayoutRight->addWidget(label4);
+
+ widget->setWindowTitle(QStringLiteral("Custom Items Example"));
+
+ widget->show();
+
+ CustomItemGraph *modifier = new CustomItemGraph(graph, label4);
+
+ QObject::connect(checkboxOne, &QCheckBox::stateChanged,
+ modifier, &CustomItemGraph::toggleItemOne);
+ QObject::connect(checkboxTwo, &QCheckBox::stateChanged,
+ modifier, &CustomItemGraph::toggleItemTwo);
+ QObject::connect(checkboxThree, &QCheckBox::stateChanged,
+ modifier, &CustomItemGraph::toggleItemThree);
+
+ QObject::connect(checkboxOneRight, &QCheckBox::stateChanged,
+ modifier, &CustomItemGraph::toggleSeeThrough);
+ QObject::connect(checkboxTwoRight, &QCheckBox::stateChanged,
+ modifier, &CustomItemGraph::toggleOilHighlight);
+ QObject::connect(checkboxThreeRight, &QCheckBox::stateChanged,
+ modifier, &CustomItemGraph::toggleShadows);
+
+ return app.exec();
+}
diff --git a/examples/datavisualization/customitems/oilrig.obj b/examples/datavisualization/customitems/oilrig.obj
new file mode 100644
index 00000000..c3b6ea57
--- /dev/null
+++ b/examples/datavisualization/customitems/oilrig.obj
@@ -0,0 +1,2322 @@
+# Blender v2.66 (sub 0) OBJ File: 'oilrig.blend'
+# www.blender.org
+v 0.057462 2.272318 -1.170324
+v 0.057461 8.181165 -0.128434
+v 0.055540 2.268930 -1.151111
+v 0.055539 8.177776 -0.109221
+v 0.049849 2.265673 -1.132637
+v 0.049849 8.174520 -0.090747
+v 0.040608 2.262671 -1.115611
+v 0.040608 8.171517 -0.073721
+v 0.028172 2.260039 -1.100687
+v 0.028172 8.168886 -0.058798
+v 0.013019 2.257880 -1.088440
+v 0.013018 8.166726 -0.046550
+v -0.004270 2.256275 -1.079339
+v -0.004271 8.165121 -0.037450
+v -0.023029 2.255287 -1.073735
+v -0.023030 8.164133 -0.031846
+v -0.042539 2.254953 -1.071843
+v -0.042539 8.163799 -0.029953
+v -0.062048 2.255287 -1.073735
+v -0.062048 8.164133 -0.031846
+v -0.080807 2.256275 -1.079339
+v -0.080808 8.165121 -0.037450
+v -0.098096 2.257880 -1.088440
+v -0.098096 8.166726 -0.046550
+v -0.113249 2.260039 -1.100687
+v -0.113250 8.168886 -0.058798
+v -0.125685 2.262671 -1.115611
+v -0.125686 8.171517 -0.073721
+v -0.134926 2.265673 -1.132637
+v -0.134927 8.174520 -0.090747
+v -0.140617 2.268930 -1.151111
+v -0.140618 8.177776 -0.109222
+v -0.142538 2.272318 -1.170324
+v -0.142539 8.181165 -0.128434
+v -0.140617 2.275706 -1.189536
+v -0.140618 8.184552 -0.147647
+v -0.134926 2.278963 -1.208011
+v -0.134927 8.187810 -0.166121
+v -0.125685 2.281965 -1.225037
+v -0.125686 8.190812 -0.183147
+v -0.113249 2.284597 -1.239960
+v -0.113250 8.193443 -0.198071
+v -0.098095 2.286757 -1.252208
+v -0.098096 8.195602 -0.210318
+v -0.080807 2.288361 -1.261308
+v -0.080807 8.197207 -0.219419
+v -0.062047 2.289349 -1.266912
+v -0.062048 8.198195 -0.225023
+v -0.042538 2.289683 -1.268804
+v -0.042539 8.198529 -0.226915
+v -0.023029 2.289349 -1.266912
+v -0.023030 8.198195 -0.225023
+v -0.004270 2.288361 -1.261308
+v -0.004271 8.197207 -0.219418
+v 0.013019 2.286757 -1.252207
+v 0.013018 8.195602 -0.210318
+v 0.028172 2.284597 -1.239960
+v 0.028172 8.193443 -0.198070
+v 0.040609 2.281965 -1.225036
+v 0.040608 8.190812 -0.183147
+v 0.049850 2.278963 -1.208010
+v 0.049849 8.187810 -0.166121
+v 0.055540 2.275706 -1.189536
+v 0.055539 8.184552 -0.147646
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.961940 0.308658
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.990393 0.597545
+vt 0.915735 0.222215
+vt 0.961940 0.691342
+vt 0.853553 0.146447
+vt 0.915735 0.777785
+vt 0.777785 0.084265
+vt 0.853553 0.853553
+vt 0.691342 0.038060
+vt 0.777785 0.915735
+vt 0.597545 0.009607
+vt 0.691342 0.961940
+vt 0.000000 0.500000
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.038060 0.691342
+vt 0.009607 0.597546
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.308659 0.961940
+vt 0.222215 0.915735
+vt 0.084265 0.222215
+vt 0.146447 0.853554
+vt 0.084266 0.777786
+vn 0.995185 -0.017020 0.096528
+vn 0.956940 -0.050408 0.285877
+vn 0.881921 -0.081857 0.464235
+vn 0.773009 -0.110162 0.624758
+vn 0.634397 -0.134231 0.761264
+vn 0.471397 -0.153144 0.868523
+vn 0.290285 -0.166171 0.942402
+vn 0.098018 -0.172812 0.980066
+vn -0.098022 -0.172812 0.980065
+vn -0.290285 -0.166171 0.942402
+vn -0.471392 -0.153145 0.868526
+vn -0.634399 -0.134231 0.761262
+vn -0.773009 -0.110162 0.624757
+vn -0.881923 -0.081857 0.464231
+vn -0.956941 -0.050407 0.285873
+vn -0.995185 -0.017021 0.096528
+vn -0.995185 0.017021 -0.096529
+vn -0.956940 0.050407 -0.285875
+vn -0.881920 0.081858 -0.464238
+vn -0.773015 0.110160 -0.624751
+vn -0.634391 0.134232 -0.761268
+vn -0.471394 0.153144 -0.868524
+vn -0.290288 0.166171 -0.942401
+vn -0.098006 0.172812 -0.980067
+vn 0.098019 0.172812 -0.980065
+vn 0.290285 0.166171 -0.942402
+vn 0.471401 0.153144 -0.868521
+vn 0.634393 0.134232 -0.761267
+vn 0.773011 0.110161 -0.624755
+vn 0.881922 0.081857 -0.464233
+vn -0.000000 0.984796 0.173713
+vn 0.995185 0.017020 -0.096527
+vn 0.956941 0.050407 -0.285872
+vn -0.000011 -0.984808 -0.173648
+vn 0.995185 -0.017020 0.096529
+vn 0.956940 -0.050407 0.285875
+vn 0.881920 -0.081858 0.464237
+vn 0.773011 -0.110161 0.624755
+vn 0.634396 -0.134231 0.761265
+vn 0.471389 -0.153145 0.868527
+vn 0.290287 -0.166171 0.942402
+vn 0.098019 -0.172812 0.980065
+vn -0.098021 -0.172812 0.980065
+vn -0.290282 -0.166171 0.942403
+vn -0.471402 -0.153144 0.868520
+vn -0.634395 -0.134232 0.761265
+vn -0.773010 -0.110161 0.624756
+vn -0.881922 -0.081857 0.464234
+vn -0.956940 -0.050408 0.285875
+vn -0.995185 -0.017020 0.096527
+vn -0.956940 0.050408 -0.285876
+vn -0.881921 0.081857 -0.464236
+vn -0.773009 0.110162 -0.624757
+vn -0.634391 0.134232 -0.761269
+vn -0.471398 0.153144 -0.868522
+vn -0.290282 0.166171 -0.942403
+vn -0.098016 0.172812 -0.980066
+vn 0.098018 0.172812 -0.980065
+vn 0.290287 0.166171 -0.942402
+vn 0.471399 0.153144 -0.868522
+vn 0.773014 0.110161 -0.624751
+vn 0.881921 0.081857 -0.464236
+vn -0.000000 0.984812 0.173622
+vn -0.000000 0.984812 0.173623
+vn -0.000000 0.984801 0.173685
+vn -0.000000 0.984814 0.173615
+vn -0.000000 0.984810 0.173634
+vn -0.000000 0.984808 0.173649
+vn -0.000000 0.984806 0.173660
+vn -0.000043 0.984788 0.173763
+vn -0.000000 0.984797 0.173710
+vn -0.000000 0.984805 0.173661
+vn -0.000000 0.984810 0.173635
+vn -0.000000 0.984810 0.173637
+vn -0.000000 0.984802 0.173683
+vn -0.000000 0.984814 0.173611
+vn -0.000000 0.984800 0.173689
+vn -0.000000 0.984800 0.173690
+vn -0.000000 0.984801 0.173686
+vn 0.000005 0.984810 0.173637
+vn 0.956941 0.050407 -0.285873
+vn -0.000000 -0.984818 -0.173587
+vn -0.000007 -0.984807 -0.173654
+vn -0.000000 -0.984808 -0.173648
+vn -0.000027 -0.984801 -0.173685
+vn -0.000004 -0.984807 -0.173652
+vn -0.000010 -0.984800 -0.173693
+vn -0.000020 -0.984817 -0.173596
+vn -0.000013 -0.984810 -0.173638
+vn -0.000001 -0.984807 -0.173650
+vn -0.000005 -0.984808 -0.173646
+vn -0.000002 -0.984808 -0.173648
+vn 0.000002 -0.984808 -0.173649
+vn 0.000001 -0.984808 -0.173649
+vn -0.000011 -0.984809 -0.173642
+vn -0.000004 -0.984808 -0.173646
+vn -0.000001 -0.984808 -0.173648
+vn -0.000002 -0.984808 -0.173649
+vn -0.000006 -0.984808 -0.173649
+vn 0.000004 -0.984808 -0.173648
+vn 0.000000 -0.984808 -0.173649
+vn -0.000004 -0.984807 -0.173650
+vn 0.000005 -0.984808 -0.173646
+vn 0.000003 -0.984808 -0.173647
+vn 0.000008 -0.984805 -0.173663
+s off
+f 1/1/1 2/2/1 4/3/1
+f 3/1/2 4/2/2 6/3/2
+f 5/1/3 6/2/3 8/3/3
+f 7/1/4 8/2/4 10/3/4
+f 9/1/5 10/2/5 12/3/5
+f 11/1/6 12/2/6 14/3/6
+f 13/1/7 14/2/7 16/3/7
+f 15/1/8 16/2/8 18/3/8
+f 17/1/9 18/2/9 19/4/9
+f 19/1/10 20/2/10 21/4/10
+f 21/1/11 22/2/11 23/4/11
+f 23/1/12 24/2/12 25/4/12
+f 25/1/13 26/2/13 27/4/13
+f 27/1/14 28/2/14 29/4/14
+f 29/1/15 30/2/15 31/4/15
+f 31/1/16 32/2/16 33/4/16
+f 33/1/17 34/2/17 35/4/17
+f 35/1/18 36/2/18 37/4/18
+f 37/1/19 38/2/19 39/4/19
+f 39/1/20 40/2/20 41/4/20
+f 41/1/21 42/2/21 43/4/21
+f 43/1/22 44/2/22 45/4/22
+f 45/1/23 46/2/23 47/4/23
+f 47/1/24 48/2/24 49/4/24
+f 49/1/25 50/2/25 52/3/25
+f 51/1/26 52/2/26 54/3/26
+f 53/1/27 54/2/27 56/3/27
+f 55/1/28 56/2/28 58/3/28
+f 57/1/29 58/2/29 60/3/29
+f 59/1/30 60/2/30 62/3/30
+f 48/5/31 52/6/31 50/7/31
+f 63/1/32 64/2/32 2/3/32
+f 61/1/33 62/2/33 64/3/33
+f 1/8/34 3/9/34 63/10/34
+f 3/4/35 1/1/35 4/3/35
+f 5/4/36 3/1/36 6/3/36
+f 7/4/37 5/1/37 8/3/37
+f 9/4/38 7/1/38 10/3/38
+f 11/4/39 9/1/39 12/3/39
+f 13/4/40 11/1/40 14/3/40
+f 15/4/41 13/1/41 16/3/41
+f 17/4/42 15/1/42 18/3/42
+f 18/2/43 20/3/43 19/4/43
+f 20/2/44 22/3/44 21/4/44
+f 22/2/45 24/3/45 23/4/45
+f 24/2/46 26/3/46 25/4/46
+f 26/2/47 28/3/47 27/4/47
+f 28/2/48 30/3/48 29/4/48
+f 30/2/49 32/3/49 31/4/49
+f 32/2/50 34/3/50 33/4/50
+f 34/2/17 36/3/17 35/4/17
+f 36/2/51 38/3/51 37/4/51
+f 38/2/52 40/3/52 39/4/52
+f 40/2/53 42/3/53 41/4/53
+f 42/2/54 44/3/54 43/4/54
+f 44/2/55 46/3/55 45/4/55
+f 46/2/56 48/3/56 47/4/56
+f 48/2/57 50/3/57 49/4/57
+f 51/4/58 49/1/58 52/3/58
+f 53/4/59 51/1/59 54/3/59
+f 55/4/60 53/1/60 56/3/60
+f 57/4/28 55/1/28 58/3/28
+f 59/4/61 57/1/61 60/3/61
+f 61/4/62 59/1/62 62/3/62
+f 48/5/63 54/11/63 52/6/63
+f 46/12/64 54/11/64 48/5/64
+f 46/12/65 56/13/65 54/11/65
+f 44/14/65 56/13/65 46/12/65
+f 44/14/66 58/15/66 56/13/66
+f 42/16/66 58/15/66 44/14/66
+f 42/16/67 60/17/67 58/15/67
+f 40/18/67 60/17/67 42/16/67
+f 40/18/68 62/19/68 60/17/68
+f 38/20/68 62/19/68 40/18/68
+f 38/20/69 64/21/69 62/19/69
+f 20/22/70 24/23/70 22/24/70
+f 16/25/71 20/22/71 18/26/71
+f 16/25/72 24/23/72 20/22/72
+f 36/27/69 64/21/69 38/20/69
+f 34/28/73 64/21/73 36/27/73
+f 2/9/74 64/21/74 34/28/74
+f 4/8/75 2/9/75 34/28/75
+f 4/8/75 34/28/75 32/29/75
+f 6/10/76 4/8/76 32/29/76
+f 6/10/76 32/29/76 30/30/76
+f 6/10/68 30/30/68 28/31/68
+f 8/32/68 6/10/68 28/31/68
+f 10/33/73 8/32/73 28/31/73
+f 10/33/67 28/31/67 26/34/67
+f 12/35/77 10/33/77 26/34/77
+f 12/35/78 26/34/78 24/23/78
+f 14/36/79 12/35/79 24/23/79
+f 16/25/80 14/36/80 24/23/80
+f 1/4/32 63/1/32 2/3/32
+f 63/4/81 61/1/81 64/3/81
+f 19/7/82 15/11/82 17/6/82
+f 41/31/83 37/29/83 39/30/83
+f 3/9/84 5/21/84 63/10/84
+f 45/23/85 41/31/85 43/34/85
+f 45/23/86 37/29/86 41/31/86
+f 49/22/87 45/23/87 47/24/87
+f 49/22/84 37/29/84 45/23/84
+f 49/22/84 35/28/84 37/29/84
+f 49/22/84 33/27/84 35/28/84
+f 49/22/84 31/20/84 33/27/84
+f 55/36/88 51/26/88 53/25/88
+f 59/33/89 55/36/89 57/35/89
+f 61/32/90 55/36/90 59/33/90
+f 63/10/91 55/36/91 61/32/91
+f 63/10/92 5/21/92 55/36/92
+f 5/21/93 7/19/93 55/36/93
+f 7/19/94 9/17/94 55/36/94
+f 29/18/95 25/14/95 27/16/95
+f 31/20/96 25/14/96 29/18/96
+f 49/22/97 25/14/97 31/20/97
+f 49/22/98 23/12/98 25/14/98
+f 9/17/93 11/15/93 55/36/93
+f 11/15/99 13/13/99 55/36/99
+f 13/13/100 15/11/100 55/36/100
+f 15/11/101 19/7/101 55/36/101
+f 19/7/102 21/5/102 55/36/102
+f 21/5/103 23/12/103 55/36/103
+f 23/12/104 49/22/104 55/36/104
+f 49/22/105 51/26/105 55/36/105
+v 0.053672 2.252534 1.125439
+v 0.053673 8.161380 0.083549
+v 0.051751 2.255921 1.144652
+v 0.051751 8.164768 0.102762
+v 0.046060 2.259179 1.163126
+v 0.046061 8.168025 0.121236
+v 0.036819 2.262181 1.180152
+v 0.036820 8.171027 0.138262
+v 0.024383 2.264812 1.195075
+v 0.024384 8.173658 0.153186
+v 0.009229 2.266972 1.207323
+v 0.009230 8.175818 0.165433
+v -0.008059 2.268577 1.216423
+v -0.008059 8.177423 0.174534
+v -0.026819 2.269565 1.222028
+v -0.026818 8.178411 0.180138
+v -0.046328 2.269898 1.223920
+v -0.046327 8.178745 0.182030
+v -0.065837 2.269565 1.222027
+v -0.065836 8.178411 0.180138
+v -0.084596 2.268577 1.216423
+v -0.084595 8.177423 0.174534
+v -0.101885 2.266972 1.207323
+v -0.101884 8.175818 0.165433
+v -0.117038 2.264812 1.195075
+v -0.117038 8.173658 0.153186
+v -0.129475 2.262181 1.180152
+v -0.129474 8.171027 0.138262
+v -0.138716 2.259179 1.163126
+v -0.138715 8.168025 0.121236
+v -0.144406 2.255921 1.144651
+v -0.144406 8.164768 0.102762
+v -0.146328 2.252534 1.125439
+v -0.146327 8.161380 0.083549
+v -0.144406 2.249146 1.106226
+v -0.144406 8.157992 0.064337
+v -0.138716 2.245888 1.087752
+v -0.138715 8.154735 0.045862
+v -0.129475 2.242886 1.070726
+v -0.129474 8.151732 0.028836
+v -0.117038 2.240255 1.055802
+v -0.117038 8.149101 0.013913
+v -0.101885 2.238095 1.043555
+v -0.101884 8.146942 0.001666
+v -0.084596 2.236491 1.034454
+v -0.084595 8.145337 -0.007435
+v -0.065837 2.235502 1.028850
+v -0.065836 8.144349 -0.013039
+v -0.046328 2.235169 1.026958
+v -0.046327 8.144015 -0.014931
+v -0.026819 2.235502 1.028850
+v -0.026818 8.144349 -0.013039
+v -0.008059 2.236491 1.034455
+v -0.008059 8.145337 -0.007435
+v 0.009229 2.238095 1.043555
+v 0.009230 8.146942 0.001666
+v 0.024383 2.240255 1.055803
+v 0.024384 8.149101 0.013913
+v 0.036819 2.242886 1.070726
+v 0.036820 8.151732 0.028837
+v 0.046060 2.245888 1.087752
+v 0.046061 8.154735 0.045863
+v 0.051751 2.249146 1.106226
+v 0.051752 8.157992 0.064337
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.308659 0.961940
+vt 0.222215 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 0.990393 0.402455
+vt 1.000000 0.500000
+vt 0.853553 0.146447
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.691342 0.038060
+vt 0.777785 0.084265
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.146447 0.853554
+vt 0.084266 0.777786
+vt 0.038060 0.691342
+vt 0.308658 0.038060
+vt 0.402455 0.009607
+vt 0.146446 0.146447
+vt 0.222215 0.084265
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.009607 0.597546
+vt 0.000000 0.500000
+vn 0.995185 0.017020 0.096528
+vn 0.956940 0.050408 0.285877
+vn 0.881921 0.081857 0.464236
+vn 0.773010 0.110161 0.624756
+vn 0.634396 0.134232 0.761265
+vn 0.471397 0.153144 0.868523
+vn 0.290283 0.166171 0.942403
+vn 0.098018 0.172812 0.980066
+vn -0.098018 0.172812 0.980066
+vn -0.290285 0.166171 0.942402
+vn -0.471398 0.153144 0.868522
+vn -0.634392 0.134232 0.761268
+vn -0.773007 0.110162 0.624759
+vn -0.881923 0.081857 0.464231
+vn -0.956941 0.050407 0.285874
+vn -0.995185 0.017021 0.096528
+vn -0.995185 -0.017021 -0.096529
+vn -0.956940 -0.050408 -0.285877
+vn -0.881921 -0.081857 -0.464236
+vn -0.773008 -0.110162 -0.624758
+vn -0.634391 -0.134232 -0.761269
+vn -0.471388 -0.153145 -0.868528
+vn -0.290288 -0.166171 -0.942401
+vn -0.098006 -0.172812 -0.980067
+vn 0.098021 -0.172812 -0.980065
+vn 0.290285 -0.166171 -0.942402
+vn 0.471402 -0.153144 -0.868520
+vn 0.634392 -0.134232 -0.761267
+vn 0.773010 -0.110161 -0.624755
+vn 0.881922 -0.081857 -0.464233
+vn -0.000356 0.984796 -0.173717
+vn 0.995185 -0.017020 -0.096527
+vn 0.956941 -0.050407 -0.285872
+vn -0.000005 -0.984808 0.173646
+vn 0.995185 0.017020 0.096529
+vn 0.956940 0.050407 0.285875
+vn 0.881920 0.081858 0.464237
+vn 0.773011 0.110161 0.624755
+vn 0.634396 0.134231 0.761264
+vn 0.471389 0.153145 0.868527
+vn 0.290292 0.166171 0.942400
+vn 0.098013 0.172812 0.980066
+vn -0.098019 0.172812 0.980065
+vn -0.290283 0.166171 0.942403
+vn -0.471400 0.153144 0.868521
+vn -0.634396 0.134232 0.761264
+vn -0.773010 0.110161 0.624756
+vn -0.881922 0.081857 0.464234
+vn -0.956940 0.050408 0.285876
+vn -0.995185 0.017020 0.096526
+vn -0.995185 -0.017021 -0.096530
+vn -0.956940 -0.050407 -0.285875
+vn -0.773009 -0.110161 -0.624757
+vn -0.634390 -0.134232 -0.761269
+vn -0.471399 -0.153144 -0.868522
+vn -0.290282 -0.166171 -0.942403
+vn -0.098018 -0.172812 -0.980066
+vn 0.098018 -0.172812 -0.980065
+vn 0.290287 -0.166171 -0.942402
+vn 0.471399 -0.153144 -0.868522
+vn 0.634393 -0.134232 -0.761267
+vn 0.773014 -0.110161 -0.624751
+vn 0.881921 -0.081857 -0.464236
+vn -0.000114 0.984812 -0.173626
+vn -0.000002 0.984808 -0.173648
+vn -0.000054 0.984802 -0.173678
+vn -0.000008 0.984808 -0.173648
+vn -0.000004 0.984808 -0.173647
+vn -0.000107 0.984836 -0.173487
+vn -0.000015 0.984812 -0.173622
+vn -0.000013 0.984820 -0.173580
+vn -0.000001 0.984809 -0.173639
+vn -0.000044 0.984787 -0.173766
+vn -0.000047 0.984804 -0.173672
+vn -0.000051 0.984803 -0.173676
+vn -0.000050 0.984803 -0.173675
+vn -0.000006 0.984799 -0.173697
+vn -0.000037 0.984791 -0.173743
+vn -0.000244 0.984806 -0.173659
+vn -0.000025 0.984806 -0.173659
+vn -0.000168 0.984823 -0.173560
+vn -0.000013 0.984812 -0.173624
+vn -0.000088 0.984819 -0.173584
+vn -0.000036 0.984813 -0.173618
+vn 0.000016 0.984808 -0.173645
+vn 0.000005 0.984808 -0.173650
+vn 0.000006 0.984807 -0.173650
+vn 0.000005 0.984808 -0.173649
+vn 0.000002 0.984808 -0.173648
+vn 0.000003 0.984807 -0.173650
+vn 0.000005 0.984804 -0.173667
+vn 0.956941 -0.050407 -0.285873
+vn -0.000005 -0.984808 0.173649
+vn -0.000001 -0.984818 0.173592
+vn -0.000000 -0.984808 0.173648
+vn -0.000006 -0.984808 0.173646
+vn -0.000001 -0.984808 0.173648
+vn -0.000002 -0.984808 0.173648
+vn -0.000001 -0.984806 0.173661
+vn -0.000000 -0.984805 0.173663
+vn -0.000000 -0.984810 0.173635
+vn -0.000000 -0.984810 0.173634
+vn -0.000000 -0.984807 0.173653
+vn -0.000000 -0.984807 0.173650
+vn -0.000000 -0.984808 0.173647
+vn 0.000000 -0.984807 0.173651
+vn -0.000000 -0.984807 0.173649
+vn -0.000000 -0.984810 0.173637
+vn 0.000000 -0.984806 0.173659
+vn -0.000001 -0.984805 0.173664
+s off
+f 65/37/106 66/38/106 68/39/106
+f 67/37/107 68/38/107 70/39/107
+f 69/37/108 70/38/108 72/39/108
+f 71/37/109 72/38/109 74/39/109
+f 73/37/110 74/38/110 76/39/110
+f 75/37/111 76/38/111 78/39/111
+f 77/37/112 78/38/112 80/39/112
+f 79/37/113 80/38/113 82/39/113
+f 81/37/114 82/38/114 83/40/114
+f 83/37/115 84/38/115 85/40/115
+f 85/37/116 86/38/116 87/40/116
+f 87/37/117 88/38/117 89/40/117
+f 89/37/118 90/38/118 91/40/118
+f 91/37/119 92/38/119 93/40/119
+f 93/37/120 94/38/120 95/40/120
+f 95/37/121 96/38/121 97/40/121
+f 97/37/122 98/38/122 99/40/122
+f 99/37/123 100/38/123 101/40/123
+f 101/37/124 102/38/124 103/40/124
+f 103/37/125 104/38/125 105/40/125
+f 105/37/126 106/38/126 107/40/126
+f 107/37/127 108/38/127 109/40/127
+f 109/37/128 110/38/128 111/40/128
+f 111/37/129 112/38/129 113/40/129
+f 113/37/130 114/38/130 116/39/130
+f 115/37/131 116/38/131 118/39/131
+f 117/37/132 118/38/132 120/39/132
+f 119/37/133 120/38/133 122/39/133
+f 121/37/134 122/38/134 124/39/134
+f 123/37/135 124/38/135 126/39/135
+f 68/41/136 66/42/136 70/43/136
+f 127/37/137 128/38/137 66/39/137
+f 125/37/138 126/38/138 128/39/138
+f 65/41/139 67/42/139 69/44/139
+f 67/40/140 65/37/140 68/39/140
+f 69/40/141 67/37/141 70/39/141
+f 71/40/142 69/37/142 72/39/142
+f 73/40/143 71/37/143 74/39/143
+f 75/40/144 73/37/144 76/39/144
+f 77/40/145 75/37/145 78/39/145
+f 79/40/146 77/37/146 80/39/146
+f 81/40/147 79/37/147 82/39/147
+f 82/38/148 84/39/148 83/40/148
+f 84/38/149 86/39/149 85/40/149
+f 86/38/150 88/39/150 87/40/150
+f 88/38/151 90/39/151 89/40/151
+f 90/38/152 92/39/152 91/40/152
+f 92/38/153 94/39/153 93/40/153
+f 94/38/154 96/39/154 95/40/154
+f 96/38/155 98/39/155 97/40/155
+f 98/38/156 100/39/156 99/40/156
+f 100/38/157 102/39/157 101/40/157
+f 102/38/124 104/39/124 103/40/124
+f 104/38/158 106/39/158 105/40/158
+f 106/38/159 108/39/159 107/40/159
+f 108/38/160 110/39/160 109/40/160
+f 110/38/161 112/39/161 111/40/161
+f 112/38/162 114/39/162 113/40/162
+f 115/40/163 113/37/163 116/39/163
+f 117/40/164 115/37/164 118/39/164
+f 119/40/165 117/37/165 120/39/165
+f 121/40/166 119/37/166 122/39/166
+f 123/40/167 121/37/167 124/39/167
+f 125/40/168 123/37/168 126/39/168
+f 66/42/169 128/44/169 126/45/169
+f 70/43/170 66/42/170 126/45/170
+f 72/46/171 70/43/171 74/47/171
+f 70/43/172 126/45/172 74/47/172
+f 126/45/173 124/48/173 74/47/173
+f 122/49/174 120/50/174 118/51/174
+f 124/48/175 122/49/175 118/51/175
+f 114/52/176 118/51/176 116/53/176
+f 124/48/177 118/51/177 114/52/177
+f 108/54/178 112/55/178 110/56/178
+f 104/57/179 108/54/179 106/58/179
+f 102/59/180 108/54/180 104/57/180
+f 100/60/181 108/54/181 102/59/181
+f 76/61/182 74/47/182 78/62/182
+f 78/62/183 74/47/183 80/63/183
+f 96/64/184 100/60/184 98/65/184
+f 96/64/185 108/54/185 100/60/185
+f 92/66/186 96/64/186 94/67/186
+f 90/68/187 96/64/187 92/66/187
+f 88/69/188 96/64/188 90/68/188
+f 86/70/189 96/64/189 88/69/189
+f 82/71/176 86/70/176 84/72/176
+f 74/47/190 124/48/190 80/63/190
+f 124/48/191 114/52/191 80/63/191
+f 114/52/192 112/55/192 80/63/192
+f 112/55/193 108/54/193 80/63/193
+f 108/54/194 96/64/194 80/63/194
+f 96/64/195 86/70/195 80/63/195
+f 86/70/196 82/71/196 80/63/196
+f 65/40/137 127/37/137 66/39/137
+f 127/40/197 125/37/197 128/39/197
+f 127/43/198 65/41/198 125/46/198
+f 83/52/199 79/51/199 81/53/199
+f 97/60/200 93/57/200 95/59/200
+f 99/65/200 93/57/200 97/60/200
+f 103/67/201 99/65/201 101/64/201
+f 103/67/202 93/57/202 99/65/202
+f 105/66/203 93/57/203 103/67/203
+f 115/71/199 111/70/199 113/72/199
+f 115/71/204 109/69/204 111/70/204
+f 117/63/205 109/69/205 115/71/205
+f 117/63/206 107/68/206 109/69/206
+f 119/62/207 107/68/207 117/63/207
+f 119/62/208 105/66/208 107/68/208
+f 121/61/208 105/66/208 119/62/208
+f 123/47/200 105/66/200 121/61/200
+f 125/46/209 105/66/209 123/47/209
+f 125/46/200 65/41/200 105/66/200
+f 105/66/200 65/41/200 93/57/200
+f 65/41/210 69/44/210 93/57/210
+f 69/44/209 71/45/209 93/57/209
+f 93/57/200 71/45/200 91/58/200
+f 91/58/211 71/45/211 89/54/211
+f 71/45/212 73/48/212 89/54/212
+f 73/48/211 75/49/211 89/54/211
+f 89/54/208 75/49/208 87/56/208
+f 75/49/213 77/50/213 87/56/213
+f 87/56/207 77/50/207 85/55/207
+f 77/50/214 79/51/214 85/55/214
+f 79/51/215 83/52/215 85/55/215
+v 1.116865 2.257815 -0.125221
+v 0.074976 8.166661 -0.125221
+v 1.136078 2.261203 -0.123300
+v 0.094188 8.170050 -0.123300
+v 1.154552 2.264460 -0.117609
+v 0.112663 8.173306 -0.117609
+v 1.171578 2.267462 -0.108368
+v 0.129689 8.176309 -0.108368
+v 1.186502 2.270094 -0.095932
+v 0.144612 8.178940 -0.095932
+v 1.198749 2.272254 -0.080778
+v 0.156859 8.181100 -0.080778
+v 1.207850 2.273858 -0.063490
+v 0.165960 8.182705 -0.063490
+v 1.213454 2.274846 -0.044730
+v 0.171564 8.183693 -0.044730
+v 1.215346 2.275180 -0.025221
+v 0.173456 8.184027 -0.025221
+v 1.213454 2.274846 -0.005712
+v 0.171564 8.183693 -0.005712
+v 1.207850 2.273858 0.013047
+v 0.165960 8.182705 0.013047
+v 1.198749 2.272254 0.030336
+v 0.156859 8.181100 0.030336
+v 1.186502 2.270094 0.045489
+v 0.144612 8.178940 0.045489
+v 1.171578 2.267462 0.057926
+v 0.129689 8.176309 0.057926
+v 1.154552 2.264460 0.067167
+v 0.112663 8.173306 0.067167
+v 1.136078 2.261203 0.072857
+v 0.094188 8.170050 0.072857
+v 1.116865 2.257815 0.074779
+v 0.074976 8.166661 0.074779
+v 1.097653 2.254427 0.072857
+v 0.055763 8.163274 0.072857
+v 1.079178 2.251170 0.067167
+v 0.037289 8.160016 0.067167
+v 1.062152 2.248168 0.057926
+v 0.020263 8.157014 0.057926
+v 1.047229 2.245536 0.045489
+v 0.005339 8.154383 0.045489
+v 1.034981 2.243377 0.030336
+v -0.006908 8.152224 0.030336
+v 1.025881 2.241772 0.013047
+v -0.016009 8.150619 0.013047
+v 1.020277 2.240784 -0.005712
+v -0.021613 8.149631 -0.005712
+v 1.018384 2.240450 -0.025221
+v -0.023505 8.149297 -0.025221
+v 1.020277 2.240784 -0.044730
+v -0.021613 8.149631 -0.044730
+v 1.025881 2.241772 -0.063490
+v -0.016009 8.150619 -0.063490
+v 1.034981 2.243377 -0.080778
+v -0.006908 8.152224 -0.080778
+v 1.047229 2.245536 -0.095932
+v 0.005339 8.154383 -0.095932
+v 1.062152 2.248168 -0.108368
+v 0.020263 8.157014 -0.108368
+v 1.079178 2.251170 -0.117609
+v 0.037289 8.160016 -0.117609
+v 1.097653 2.254427 -0.123300
+v 0.055763 8.163274 -0.123300
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.000000 0.500000
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.146447 0.853554
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.308659 0.961940
+vt 0.222215 0.915735
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.009607 0.597546
+vt 0.084265 0.222215
+vn 0.096528 0.017020 -0.995185
+vn 0.285876 0.050408 -0.956940
+vn 0.464235 0.081857 -0.881921
+vn 0.624757 0.110162 -0.773009
+vn 0.761264 0.134232 -0.634396
+vn 0.868521 0.153144 -0.471400
+vn 0.942403 0.166171 -0.290283
+vn 0.980066 0.172812 -0.098018
+vn 0.980066 0.172812 0.098018
+vn 0.942403 0.166171 0.290283
+vn 0.868521 0.153144 0.471399
+vn 0.761264 0.134232 0.634396
+vn 0.624757 0.110162 0.773009
+vn 0.464235 0.081857 0.881921
+vn 0.285876 0.050408 0.956940
+vn 0.096527 0.017020 0.995185
+vn -0.096528 -0.017021 0.995185
+vn -0.285875 -0.050407 0.956940
+vn -0.464235 -0.081857 0.881921
+vn -0.624754 -0.110161 0.773012
+vn -0.761268 -0.134232 0.634391
+vn -0.868524 -0.153144 0.471394
+vn -0.942401 -0.166171 0.290288
+vn -0.980066 -0.172812 0.098010
+vn -0.980065 -0.172812 -0.098018
+vn -0.942402 -0.166171 -0.290285
+vn -0.868521 -0.153144 -0.471401
+vn -0.761266 -0.134232 -0.634394
+vn -0.624751 -0.110161 -0.773014
+vn -0.464236 -0.081857 -0.881921
+vn -0.173717 0.984796 0.000356
+vn -0.096527 -0.017020 -0.995185
+vn -0.285873 -0.050407 -0.956941
+vn 0.173648 -0.984808 -0.000011
+vn 0.285874 0.050407 -0.956940
+vn 0.464237 0.081858 -0.881920
+vn 0.624752 0.110161 -0.773013
+vn 0.761267 0.134232 -0.634393
+vn 0.868524 0.153144 -0.471394
+vn 0.942402 0.166171 -0.290287
+vn 0.980065 0.172812 -0.098019
+vn 0.980065 0.172812 0.098019
+vn 0.942402 0.166171 0.290287
+vn 0.868524 0.153144 0.471394
+vn 0.761267 0.134232 0.634393
+vn 0.624752 0.110161 0.773013
+vn 0.464237 0.081858 0.881920
+vn 0.285874 0.050407 0.956940
+vn -0.096529 -0.017021 0.995185
+vn -0.285874 -0.050407 0.956940
+vn -0.624758 -0.110162 0.773008
+vn -0.761268 -0.134232 0.634392
+vn -0.868523 -0.153144 0.471397
+vn -0.942402 -0.166171 0.290284
+vn -0.980066 -0.172812 0.098018
+vn -0.980065 -0.172812 -0.098021
+vn -0.868521 -0.153144 -0.471400
+vn -0.761267 -0.134232 -0.634392
+vn -0.624756 -0.110161 -0.773010
+vn -0.464234 -0.081857 -0.881922
+vn -0.173640 0.984809 -0.000036
+vn -0.173648 0.984808 0.000040
+vn -0.173648 0.984808 0.000002
+vn -0.173650 0.984807 -0.000020
+vn -0.173652 0.984807 -0.000031
+vn -0.173635 0.984810 0.000027
+vn -0.173649 0.984808 -0.000009
+vn -0.173641 0.984809 0.000007
+vn -0.173650 0.984807 -0.000006
+vn -0.173640 0.984809 0.000006
+vn -0.173660 0.984806 -0.000014
+vn -0.173675 0.984803 0.000050
+vn -0.173675 0.984803 0.000049
+vn -0.173767 0.984787 -0.000044
+vn -0.173767 0.984787 0.000044
+vn -0.173642 0.984809 0.000000
+vn -0.173677 0.984803 0.000052
+vn -0.173717 0.984796 -0.000357
+vn -0.173646 0.984808 0.000005
+vn -0.173645 0.984808 0.000007
+vn -0.173703 0.984798 -0.000011
+vn -0.173684 0.984802 -0.000003
+vn -0.173667 0.984805 0.000002
+vn -0.173671 0.984804 -0.000015
+vn -0.173656 0.984806 -0.000026
+vn -0.173637 0.984810 0.000008
+vn -0.173643 0.984809 0.000005
+vn -0.096528 -0.017020 -0.995185
+vn -0.285872 -0.050407 -0.956941
+vn 0.173648 -0.984808 -0.000000
+vn 0.173647 -0.984808 0.000006
+vn 0.173647 -0.984808 0.000005
+vn 0.173652 -0.984807 -0.000009
+vn 0.173645 -0.984808 0.000005
+vn 0.173653 -0.984807 -0.000007
+vn 0.173643 -0.984809 0.000006
+vn 0.173652 -0.984807 -0.000003
+vn 0.173643 -0.984809 0.000004
+vn 0.173651 -0.984807 -0.000002
+vn 0.173650 -0.984807 -0.000001
+vn 0.173647 -0.984808 0.000000
+vn 0.173649 -0.984808 0.000000
+vn 0.173652 -0.984807 0.000001
+vn 0.173642 -0.984809 -0.000003
+vn 0.173653 -0.984807 0.000003
+vn 0.173642 -0.984809 -0.000004
+vn 0.173655 -0.984806 0.000007
+vn 0.173641 -0.984809 -0.000008
+vn 0.173658 -0.984806 0.000013
+vn 0.173645 -0.984808 -0.000007
+vn 0.173654 -0.984807 0.000007
+vn 0.173646 -0.984808 -0.000004
+s off
+f 129/73/216 130/74/216 132/75/216
+f 131/73/217 132/74/217 134/75/217
+f 133/73/218 134/74/218 136/75/218
+f 135/73/219 136/74/219 138/75/219
+f 137/73/220 138/74/220 140/75/220
+f 139/73/221 140/74/221 142/75/221
+f 141/73/222 142/74/222 144/75/222
+f 143/73/223 144/74/223 146/75/223
+f 145/73/224 146/74/224 148/75/224
+f 147/73/225 148/74/225 150/75/225
+f 149/73/226 150/74/226 152/75/226
+f 151/73/227 152/74/227 154/75/227
+f 153/73/228 154/74/228 156/75/228
+f 155/73/229 156/74/229 158/75/229
+f 157/73/230 158/74/230 160/75/230
+f 159/73/231 160/74/231 162/75/231
+f 161/73/232 162/74/232 163/76/232
+f 163/73/233 164/74/233 165/76/233
+f 165/73/234 166/74/234 167/76/234
+f 167/73/235 168/74/235 169/76/235
+f 169/73/236 170/74/236 171/76/236
+f 171/73/237 172/74/237 173/76/237
+f 173/73/238 174/74/238 175/76/238
+f 175/73/239 176/74/239 177/76/239
+f 177/73/240 178/74/240 179/76/240
+f 179/73/241 180/74/241 181/76/241
+f 181/73/242 182/74/242 183/76/242
+f 183/73/243 184/74/243 185/76/243
+f 185/73/244 186/74/244 187/76/244
+f 187/73/245 188/74/245 189/76/245
+f 132/77/246 130/78/246 134/79/246
+f 191/73/247 192/74/247 129/76/247
+f 189/73/248 190/74/248 191/76/248
+f 129/77/249 131/78/249 191/79/249
+f 131/76/216 129/73/216 132/75/216
+f 133/76/250 131/73/250 134/75/250
+f 135/76/251 133/73/251 136/75/251
+f 137/76/252 135/73/252 138/75/252
+f 139/76/253 137/73/253 140/75/253
+f 141/76/254 139/73/254 142/75/254
+f 143/76/255 141/73/255 144/75/255
+f 145/76/256 143/73/256 146/75/256
+f 147/76/257 145/73/257 148/75/257
+f 149/76/258 147/73/258 150/75/258
+f 151/76/259 149/73/259 152/75/259
+f 153/76/260 151/73/260 154/75/260
+f 155/76/261 153/73/261 156/75/261
+f 157/76/262 155/73/262 158/75/262
+f 159/76/263 157/73/263 160/75/263
+f 161/76/231 159/73/231 162/75/231
+f 162/74/264 164/75/264 163/76/264
+f 164/74/265 166/75/265 165/76/265
+f 166/74/234 168/75/234 167/76/234
+f 168/74/266 170/75/266 169/76/266
+f 170/74/267 172/75/267 171/76/267
+f 172/74/268 174/75/268 173/76/268
+f 174/74/269 176/75/269 175/76/269
+f 176/74/270 178/75/270 177/76/270
+f 178/74/271 180/75/271 179/76/271
+f 180/74/241 182/75/241 181/76/241
+f 182/74/272 184/75/272 183/76/272
+f 184/74/273 186/75/273 185/76/273
+f 186/74/274 188/75/274 187/76/274
+f 188/74/275 190/75/275 189/76/275
+f 130/78/276 192/80/276 134/79/276
+f 192/80/277 190/81/277 134/79/277
+f 190/81/278 188/82/278 134/79/278
+f 188/82/279 186/83/279 134/79/279
+f 186/83/280 184/84/280 134/79/280
+f 184/84/281 182/85/281 134/79/281
+f 182/85/282 180/86/282 134/79/282
+f 180/86/283 178/87/283 134/79/283
+f 178/87/284 176/88/284 134/79/284
+f 176/88/285 174/89/285 134/79/285
+f 174/89/286 172/90/286 134/79/286
+f 170/91/287 168/92/287 166/93/287
+f 170/91/288 166/93/288 164/94/288
+f 148/95/289 152/96/289 150/97/289
+f 140/98/290 144/99/290 142/100/290
+f 172/90/287 170/91/287 164/94/287
+f 134/79/291 172/90/291 164/94/291
+f 136/101/292 134/79/292 138/102/292
+f 134/79/291 164/94/291 138/102/291
+f 162/103/293 160/104/293 158/105/293
+f 162/103/294 158/105/294 156/106/294
+f 146/107/295 152/96/295 148/95/295
+f 146/107/296 154/108/296 152/96/296
+f 144/99/297 154/108/297 146/107/297
+f 140/98/298 154/108/298 144/99/298
+f 138/102/299 164/94/299 140/98/299
+f 162/103/300 156/106/300 154/108/300
+f 164/94/301 162/103/301 140/98/301
+f 162/103/302 154/108/302 140/98/302
+f 192/74/303 130/75/303 129/76/303
+f 190/74/304 192/75/304 191/76/304
+f 131/78/305 133/80/305 191/79/305
+f 133/80/306 135/81/306 191/79/306
+f 135/81/305 137/82/305 191/79/305
+f 137/82/307 139/83/307 191/79/307
+f 139/83/308 141/84/308 191/79/308
+f 141/84/309 143/85/309 191/79/309
+f 143/85/310 145/86/310 191/79/310
+f 145/86/311 147/87/311 191/79/311
+f 147/87/312 149/88/312 191/79/312
+f 149/88/313 151/89/313 191/79/313
+f 151/89/314 153/90/314 191/79/314
+f 153/90/305 155/91/305 191/79/305
+f 155/91/315 157/92/315 191/79/315
+f 157/92/305 159/93/305 191/79/305
+f 159/93/316 161/94/316 191/79/316
+f 161/94/305 163/103/305 191/79/305
+f 163/103/305 165/104/305 191/79/305
+f 165/104/317 167/105/317 191/79/317
+f 167/105/317 169/106/317 191/79/317
+f 169/106/318 171/108/318 191/79/318
+f 171/108/319 173/96/319 191/79/319
+f 173/96/320 175/97/320 191/79/320
+f 175/97/321 177/95/321 191/79/321
+f 177/95/322 179/107/322 191/79/322
+f 179/107/323 181/99/323 191/79/323
+f 181/99/324 183/100/324 191/79/324
+f 183/100/325 185/98/325 191/79/325
+f 185/98/326 187/102/326 189/101/326
+f 191/79/327 185/98/327 189/101/327
+v -1.178897 2.277600 -0.129009
+v -0.137008 8.186446 -0.129009
+v -1.159685 2.274212 -0.127088
+v -0.117795 8.183058 -0.127088
+v -1.141210 2.270954 -0.121397
+v -0.099321 8.179801 -0.121397
+v -1.124184 2.267952 -0.112156
+v -0.082295 8.176799 -0.112156
+v -1.109261 2.265321 -0.099720
+v -0.067371 8.174168 -0.099720
+v -1.097013 2.263161 -0.084566
+v -0.055124 8.172008 -0.084566
+v -1.087913 2.261557 -0.067277
+v -0.046023 8.170403 -0.067277
+v -1.082309 2.260568 -0.048518
+v -0.040419 8.169415 -0.048518
+v -1.080417 2.260235 -0.029009
+v -0.038527 8.169081 -0.029009
+v -1.082309 2.260568 -0.009500
+v -0.040419 8.169415 -0.009500
+v -1.087913 2.261557 0.009259
+v -0.046023 8.170403 0.009259
+v -1.097013 2.263161 0.026548
+v -0.055124 8.172008 0.026548
+v -1.109261 2.265321 0.041702
+v -0.067371 8.174168 0.041702
+v -1.124184 2.267952 0.054138
+v -0.082295 8.176799 0.054138
+v -1.141210 2.270954 0.063379
+v -0.099321 8.179801 0.063379
+v -1.159685 2.274212 0.069069
+v -0.117795 8.183058 0.069069
+v -1.178897 2.277600 0.070991
+v -0.137008 8.186446 0.070991
+v -1.198110 2.280987 0.069069
+v -0.156220 8.189834 0.069069
+v -1.216584 2.284245 0.063379
+v -0.174695 8.193091 0.063379
+v -1.233610 2.287247 0.054138
+v -0.191721 8.196094 0.054138
+v -1.248534 2.289878 0.041702
+v -0.206644 8.198725 0.041702
+v -1.260781 2.292038 0.026548
+v -0.218892 8.200884 0.026548
+v -1.269882 2.293643 0.009259
+v -0.227992 8.202489 0.009259
+v -1.275486 2.294631 -0.009500
+v -0.233596 8.203477 -0.009500
+v -1.277378 2.294964 -0.029009
+v -0.235489 8.203811 -0.029009
+v -1.275486 2.294631 -0.048518
+v -0.233596 8.203477 -0.048518
+v -1.269882 2.293643 -0.067278
+v -0.227992 8.202489 -0.067278
+v -1.260781 2.292038 -0.084566
+v -0.218892 8.200884 -0.084566
+v -1.248534 2.289878 -0.099720
+v -0.206644 8.198725 -0.099720
+v -1.233610 2.287247 -0.112156
+v -0.191721 8.196094 -0.112156
+v -1.216584 2.284245 -0.121397
+v -0.174695 8.193091 -0.121397
+v -1.198110 2.280987 -0.127088
+v -0.156220 8.189834 -0.127088
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.096528 -0.017020 -0.995185
+vn 0.285876 -0.050408 -0.956940
+vn 0.464236 -0.081857 -0.881921
+vn 0.624756 -0.110161 -0.773010
+vn 0.761265 -0.134232 -0.634395
+vn 0.868521 -0.153144 -0.471400
+vn 0.942403 -0.166171 -0.290282
+vn 0.980065 -0.172812 -0.098021
+vn 0.980066 -0.172812 0.098018
+vn 0.942402 -0.166171 0.290285
+vn 0.868522 -0.153144 0.471399
+vn 0.761265 -0.134232 0.634395
+vn 0.624756 -0.110161 0.773010
+vn 0.464235 -0.081857 0.881922
+vn 0.285876 -0.050408 0.956940
+vn 0.096527 -0.017020 0.995185
+vn -0.096528 0.017020 0.995185
+vn -0.285876 0.050408 0.956940
+vn -0.464234 0.081857 0.881922
+vn -0.624758 0.110162 0.773008
+vn -0.761264 0.134231 0.634396
+vn -0.868524 0.153144 0.471394
+vn -0.942401 0.166171 0.290288
+vn -0.980066 0.172812 0.098014
+vn -0.980066 0.172812 -0.098014
+vn -0.942403 0.166171 -0.290281
+vn -0.868522 0.153144 -0.471398
+vn -0.761268 0.134232 -0.634392
+vn -0.624751 0.110161 -0.773014
+vn -0.464236 0.081857 -0.881921
+vn 0.173717 0.984795 -0.000357
+vn -0.096527 0.017020 -0.995185
+vn -0.285874 0.050407 -0.956940
+vn -0.173648 -0.984808 0.000011
+vn 0.285874 -0.050407 -0.956940
+vn 0.464237 -0.081858 -0.881920
+vn 0.624755 -0.110161 -0.773011
+vn 0.761264 -0.134231 -0.634396
+vn 0.868524 -0.153144 -0.471394
+vn 0.942402 -0.166171 -0.290287
+vn 0.980066 -0.172812 -0.098013
+vn 0.980066 -0.172812 0.098013
+vn 0.942402 -0.166171 0.290287
+vn 0.868524 -0.153144 0.471394
+vn 0.761264 -0.134231 0.634396
+vn 0.624755 -0.110161 0.773011
+vn 0.464237 -0.081858 0.881920
+vn 0.285874 -0.050407 0.956940
+vn -0.096529 0.017021 0.995185
+vn -0.285874 0.050407 0.956940
+vn -0.464235 0.081857 0.881921
+vn -0.624756 0.110161 0.773010
+vn -0.761269 0.134232 0.634391
+vn -0.868522 0.153144 0.471398
+vn -0.942403 0.166171 0.290282
+vn -0.980066 0.172812 0.098018
+vn -0.980065 0.172812 -0.098019
+vn -0.942402 0.166171 -0.290285
+vn -0.868521 0.153144 -0.471400
+vn -0.761267 0.134232 -0.634392
+vn -0.624756 0.110161 -0.773010
+vn -0.464234 0.081857 -0.881922
+vn 0.173640 0.984809 0.000036
+vn 0.173648 0.984808 -0.000041
+vn 0.173648 0.984808 -0.000002
+vn 0.173650 0.984807 0.000019
+vn 0.173652 0.984807 0.000031
+vn 0.173635 0.984810 -0.000027
+vn 0.173649 0.984808 0.000009
+vn 0.173641 0.984809 -0.000007
+vn 0.173650 0.984807 0.000006
+vn 0.173641 0.984809 -0.000005
+vn 0.173660 0.984806 0.000014
+vn 0.173629 0.984811 -0.000011
+vn 0.173638 0.984810 -0.000005
+vn 0.173649 0.984808 0.000001
+vn 0.173659 0.984806 0.000005
+vn 0.173636 0.984810 -0.000002
+vn 0.173683 0.984802 0.000007
+vn 0.173611 0.984814 0.000000
+vn 0.173649 0.984808 0.000000
+vn 0.173635 0.984810 0.000001
+vn 0.173681 0.984802 -0.000008
+vn 0.173674 0.984803 -0.000006
+vn 0.173644 0.984808 0.000007
+vn 0.173666 0.984805 -0.000005
+vn 0.173677 0.984803 -0.000016
+vn 0.173633 0.984810 0.000028
+vn 0.173518 0.984831 0.000143
+vn 0.173638 0.984810 0.000022
+vn -0.096528 0.017020 -0.995185
+vn -0.285872 0.050407 -0.956941
+vn -0.173648 -0.984808 0.000000
+vn -0.173647 -0.984808 -0.000006
+vn -0.173648 -0.984808 -0.000002
+vn -0.173647 -0.984808 -0.000003
+vn -0.173652 -0.984807 0.000009
+vn -0.173645 -0.984808 -0.000005
+vn -0.173652 -0.984807 0.000005
+vn -0.173644 -0.984809 -0.000005
+vn -0.173652 -0.984807 0.000003
+vn -0.173643 -0.984809 -0.000004
+vn -0.173650 -0.984807 0.000001
+vn -0.173680 -0.984802 0.000024
+vn -0.173592 -0.984818 0.000023
+vn -0.173693 -0.984800 0.000010
+vn -0.173644 -0.984808 -0.000000
+vn -0.173638 -0.984810 0.000013
+vn -0.173653 -0.984807 -0.000003
+vn -0.173644 -0.984808 0.000011
+vn -0.173646 -0.984808 0.000007
+vn -0.173645 -0.984808 -0.000007
+vn -0.173651 -0.984807 0.000003
+vn -0.173649 -0.984808 0.000000
+vn -0.173648 -0.984808 0.000001
+vn -0.173647 -0.984808 0.000002
+vn -0.173640 -0.984809 -0.000005
+vn -0.173647 -0.984808 -0.000001
+s off
+f 193/109/328 194/110/328 196/111/328
+f 195/109/329 196/110/329 198/111/329
+f 197/109/330 198/110/330 200/111/330
+f 199/109/331 200/110/331 202/111/331
+f 201/109/332 202/110/332 204/111/332
+f 203/109/333 204/110/333 206/111/333
+f 205/109/334 206/110/334 208/111/334
+f 207/109/335 208/110/335 210/111/335
+f 209/109/336 210/110/336 212/111/336
+f 211/109/337 212/110/337 214/111/337
+f 213/109/338 214/110/338 216/111/338
+f 215/109/339 216/110/339 218/111/339
+f 217/109/340 218/110/340 220/111/340
+f 219/109/341 220/110/341 222/111/341
+f 221/109/342 222/110/342 224/111/342
+f 223/109/343 224/110/343 226/111/343
+f 225/109/344 226/110/344 227/112/344
+f 227/109/345 228/110/345 229/112/345
+f 229/109/346 230/110/346 231/112/346
+f 231/109/347 232/110/347 233/112/347
+f 233/109/348 234/110/348 235/112/348
+f 235/109/349 236/110/349 237/112/349
+f 237/109/350 238/110/350 239/112/350
+f 239/109/351 240/110/351 241/112/351
+f 241/109/352 242/110/352 243/112/352
+f 243/109/353 244/110/353 245/112/353
+f 245/109/354 246/110/354 247/112/354
+f 247/109/355 248/110/355 249/112/355
+f 249/109/356 250/110/356 251/112/356
+f 251/109/357 252/110/357 253/112/357
+f 196/113/358 194/114/358 198/115/358
+f 255/109/359 256/110/359 193/112/359
+f 253/109/360 254/110/360 255/112/360
+f 193/113/361 195/114/361 255/115/361
+f 195/112/328 193/109/328 196/111/328
+f 197/112/362 195/109/362 198/111/362
+f 199/112/363 197/109/363 200/111/363
+f 201/112/364 199/109/364 202/111/364
+f 203/112/365 201/109/365 204/111/365
+f 205/112/366 203/109/366 206/111/366
+f 207/112/367 205/109/367 208/111/367
+f 209/112/368 207/109/368 210/111/368
+f 211/112/369 209/109/369 212/111/369
+f 213/112/370 211/109/370 214/111/370
+f 215/112/371 213/109/371 216/111/371
+f 217/112/372 215/109/372 218/111/372
+f 219/112/373 217/109/373 220/111/373
+f 221/112/374 219/109/374 222/111/374
+f 223/112/375 221/109/375 224/111/375
+f 225/112/343 223/109/343 226/111/343
+f 226/110/376 228/111/376 227/112/376
+f 228/110/377 230/111/377 229/112/377
+f 230/110/378 232/111/378 231/112/378
+f 232/110/379 234/111/379 233/112/379
+f 234/110/380 236/111/380 235/112/380
+f 236/110/381 238/111/381 237/112/381
+f 238/110/382 240/111/382 239/112/382
+f 240/110/383 242/111/383 241/112/383
+f 242/110/384 244/111/384 243/112/384
+f 244/110/385 246/111/385 245/112/385
+f 246/110/386 248/111/386 247/112/386
+f 248/110/387 250/111/387 249/112/387
+f 250/110/388 252/111/388 251/112/388
+f 252/110/389 254/111/389 253/112/389
+f 194/114/390 256/116/390 198/115/390
+f 256/116/391 254/117/391 198/115/391
+f 254/117/392 252/118/392 198/115/392
+f 252/118/393 250/119/393 198/115/393
+f 250/119/394 248/120/394 198/115/394
+f 248/120/395 246/121/395 198/115/395
+f 246/121/396 244/122/396 198/115/396
+f 244/122/397 242/123/397 198/115/397
+f 242/123/398 240/124/398 198/115/398
+f 240/124/399 238/125/399 198/115/399
+f 238/125/400 236/126/400 198/115/400
+f 236/126/401 234/127/401 198/115/401
+f 234/127/402 232/128/402 198/115/402
+f 232/128/403 230/129/403 198/115/403
+f 230/129/404 228/130/404 198/115/404
+f 228/130/405 226/131/405 198/115/405
+f 226/131/406 224/132/406 198/115/406
+f 224/132/407 222/133/407 198/115/407
+f 222/133/408 220/134/408 198/115/408
+f 220/134/409 218/135/409 198/115/409
+f 218/135/410 216/136/410 198/115/410
+f 216/136/411 214/137/411 198/115/411
+f 214/137/412 212/138/412 198/115/412
+f 212/138/413 210/139/413 198/115/413
+f 210/139/398 208/140/398 198/115/398
+f 208/140/414 206/141/414 198/115/414
+f 206/141/415 204/142/415 198/115/415
+f 204/142/416 202/143/416 200/144/416
+f 198/115/417 204/142/417 200/144/417
+f 256/110/418 194/111/418 193/112/418
+f 254/110/419 256/111/419 255/112/419
+f 195/114/420 197/116/420 255/115/420
+f 197/116/421 199/117/421 255/115/421
+f 199/117/422 201/118/422 255/115/422
+f 201/118/423 203/119/423 255/115/423
+f 203/119/424 205/120/424 255/115/424
+f 205/120/425 207/121/425 255/115/425
+f 207/121/426 209/122/426 255/115/426
+f 209/122/427 211/123/427 255/115/427
+f 211/123/428 213/124/428 255/115/428
+f 213/124/429 215/125/429 255/115/429
+f 215/125/430 217/126/430 255/115/430
+f 237/136/431 233/134/431 235/135/431
+f 247/141/432 243/139/432 245/140/432
+f 241/138/433 237/136/433 239/137/433
+f 241/138/434 233/134/434 237/136/434
+f 251/143/435 247/141/435 249/142/435
+f 251/143/436 243/139/436 247/141/436
+f 219/127/437 221/128/437 223/129/437
+f 219/127/438 223/129/438 225/130/438
+f 233/134/439 229/132/439 231/133/439
+f 241/138/440 229/132/440 233/134/440
+f 255/115/437 251/143/437 253/144/437
+f 255/115/441 217/126/441 251/143/441
+f 251/143/441 217/126/441 243/139/441
+f 219/127/442 225/130/442 227/131/442
+f 217/126/443 219/127/443 227/131/443
+f 243/139/420 217/126/420 227/131/420
+f 241/138/444 243/139/444 229/132/444
+f 243/139/445 227/131/445 229/132/445
+v -3.858562 2.027707 3.871576
+v -3.858562 2.027707 -3.907549
+v 3.920563 2.027707 -3.907549
+v 3.920563 2.027707 3.871576
+v -3.858562 2.306528 3.871576
+v -3.858562 2.306528 -3.907549
+v 3.920563 2.306528 -3.907549
+v 3.920563 2.306528 3.871576
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 1.000000 1.000000
+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 261/145/446 262/146/446 257/147/446
+f 262/145/447 263/146/447 258/147/447
+f 263/145/448 264/146/448 260/148/448
+f 264/145/449 261/146/449 257/148/449
+f 257/145/450 258/146/450 259/148/450
+f 264/145/451 263/146/451 262/148/451
+f 262/146/446 258/148/446 257/147/446
+f 263/146/447 259/148/447 258/147/447
+f 259/147/448 263/145/448 260/148/448
+f 260/147/449 264/145/449 257/148/449
+f 260/147/450 257/145/450 259/148/450
+f 261/147/451 264/145/451 262/148/451
+v 2.043798 0.024218 -3.001008
+v 2.043798 2.024218 -3.001008
+v 2.238889 0.024218 -2.981793
+v 2.238889 2.024218 -2.981793
+v 2.426482 0.024218 -2.924888
+v 2.426482 2.024218 -2.924888
+v 2.599369 0.024218 -2.832478
+v 2.599369 2.024218 -2.832478
+v 2.750905 0.024218 -2.708115
+v 2.750905 2.024218 -2.708115
+v 2.875268 0.024218 -2.556578
+v 2.875268 2.024218 -2.556578
+v 2.967678 0.024218 -2.383692
+v 2.967678 2.024218 -2.383692
+v 3.024584 0.024218 -2.196099
+v 3.024584 2.024218 -2.196099
+v 3.043798 0.024218 -2.001008
+v 3.043798 2.024218 -2.001008
+v 3.024584 0.024218 -1.805918
+v 3.024584 2.024218 -1.805918
+v 2.967678 0.024218 -1.618325
+v 2.967678 2.024218 -1.618325
+v 2.875268 0.024218 -1.445438
+v 2.875268 2.024218 -1.445438
+v 2.750905 0.024218 -1.293901
+v 2.750905 2.024218 -1.293901
+v 2.599369 0.024218 -1.169539
+v 2.599369 2.024218 -1.169539
+v 2.426482 0.024218 -1.077129
+v 2.426482 2.024218 -1.077129
+v 2.238889 0.024218 -1.020223
+v 2.238889 2.024218 -1.020223
+v 2.043798 0.024218 -1.001008
+v 2.043798 2.024218 -1.001008
+v 1.848708 0.024218 -1.020223
+v 1.848708 2.024218 -1.020223
+v 1.661115 0.024218 -1.077129
+v 1.661115 2.024218 -1.077129
+v 1.488228 0.024218 -1.169539
+v 1.488228 2.024218 -1.169539
+v 1.336691 0.024218 -1.293902
+v 1.336691 2.024218 -1.293902
+v 1.212328 0.024218 -1.445439
+v 1.212328 2.024218 -1.445439
+v 1.119919 0.024218 -1.618326
+v 1.119919 2.024218 -1.618326
+v 1.063013 0.024218 -1.805919
+v 1.063013 2.024218 -1.805919
+v 1.043798 0.024218 -2.001009
+v 1.043798 2.024218 -2.001009
+v 1.063013 0.024218 -2.196100
+v 1.063013 2.024218 -2.196100
+v 1.119919 0.024218 -2.383693
+v 1.119919 2.024218 -2.383693
+v 1.212330 0.024218 -2.556580
+v 1.212330 2.024218 -2.556580
+v 1.336693 0.024218 -2.708116
+v 1.336693 2.024218 -2.708116
+v 1.488229 0.024218 -2.832479
+v 1.488229 2.024218 -2.832479
+v 1.661116 0.024218 -2.924888
+v 1.661116 2.024218 -2.924888
+v 1.848710 0.024218 -2.981794
+v 1.848710 2.024218 -2.981794
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.098018 0.000000 -0.995185
+vn 0.290285 0.000000 -0.956940
+vn 0.471397 0.000000 -0.881921
+vn 0.634393 0.000000 -0.773011
+vn 0.773011 0.000000 -0.634393
+vn 0.881921 0.000000 -0.471398
+vn 0.956940 0.000000 -0.290285
+vn 0.995185 0.000000 -0.098017
+vn 0.995185 0.000000 0.098017
+vn 0.956940 0.000000 0.290285
+vn 0.881921 0.000000 0.471397
+vn 0.773011 0.000000 0.634393
+vn 0.634393 0.000000 0.773011
+vn 0.471397 0.000000 0.881921
+vn 0.290285 0.000000 0.956940
+vn 0.098017 0.000000 0.995185
+vn -0.098018 0.000000 0.995185
+vn -0.290285 0.000000 0.956940
+vn -0.471397 0.000000 0.881921
+vn -0.634394 0.000000 0.773010
+vn -0.773011 0.000000 0.634393
+vn -0.881922 0.000000 0.471396
+vn -0.956941 0.000000 0.290283
+vn -0.995185 0.000000 0.098017
+vn -0.995185 -0.000000 -0.098018
+vn -0.956940 -0.000000 -0.290286
+vn -0.881921 -0.000000 -0.471398
+vn -0.773010 -0.000000 -0.634394
+vn -0.634392 -0.000000 -0.773012
+vn -0.471396 -0.000000 -0.881922
+vn -0.098016 -0.000000 -0.995185
+vn -0.290283 -0.000000 -0.956941
+s off
+f 265/149/452 266/150/452 268/151/452
+f 267/149/453 268/150/453 270/151/453
+f 269/149/454 270/150/454 272/151/454
+f 271/149/455 272/150/455 274/151/455
+f 273/149/456 274/150/456 276/151/456
+f 275/149/457 276/150/457 278/151/457
+f 277/149/458 278/150/458 280/151/458
+f 279/149/459 280/150/459 282/151/459
+f 281/149/460 282/150/460 284/151/460
+f 283/149/461 284/150/461 286/151/461
+f 285/149/462 286/150/462 288/151/462
+f 287/149/463 288/150/463 290/151/463
+f 289/149/464 290/150/464 292/151/464
+f 291/149/465 292/150/465 294/151/465
+f 293/149/466 294/150/466 296/151/466
+f 295/149/467 296/150/467 298/151/467
+f 297/149/468 298/150/468 299/152/468
+f 299/149/469 300/150/469 301/152/469
+f 301/149/470 302/150/470 303/152/470
+f 303/149/471 304/150/471 305/152/471
+f 305/149/472 306/150/472 307/152/472
+f 307/149/473 308/150/473 309/152/473
+f 309/149/474 310/150/474 311/152/474
+f 311/149/475 312/150/475 313/152/475
+f 313/149/476 314/150/476 315/152/476
+f 315/149/477 316/150/477 317/152/477
+f 317/149/478 318/150/478 319/152/478
+f 319/149/479 320/150/479 321/152/479
+f 321/149/480 322/150/480 323/152/480
+f 323/149/481 324/150/481 325/152/481
+f 268/153/451 266/154/451 270/155/451
+f 327/149/482 328/150/482 265/152/482
+f 325/149/483 326/150/483 327/152/483
+f 265/153/450 267/154/450 327/155/450
+f 267/152/452 265/149/452 268/151/452
+f 269/152/453 267/149/453 270/151/453
+f 271/152/454 269/149/454 272/151/454
+f 273/152/455 271/149/455 274/151/455
+f 275/152/456 273/149/456 276/151/456
+f 277/152/457 275/149/457 278/151/457
+f 279/152/458 277/149/458 280/151/458
+f 281/152/459 279/149/459 282/151/459
+f 283/152/460 281/149/460 284/151/460
+f 285/152/461 283/149/461 286/151/461
+f 287/152/462 285/149/462 288/151/462
+f 289/152/463 287/149/463 290/151/463
+f 291/152/464 289/149/464 292/151/464
+f 293/152/465 291/149/465 294/151/465
+f 295/152/466 293/149/466 296/151/466
+f 297/152/467 295/149/467 298/151/467
+f 298/150/468 300/151/468 299/152/468
+f 300/150/469 302/151/469 301/152/469
+f 302/150/470 304/151/470 303/152/470
+f 304/150/471 306/151/471 305/152/471
+f 306/150/472 308/151/472 307/152/472
+f 308/150/473 310/151/473 309/152/473
+f 310/150/474 312/151/474 311/152/474
+f 312/150/475 314/151/475 313/152/475
+f 314/150/476 316/151/476 315/152/476
+f 316/150/477 318/151/477 317/152/477
+f 318/150/478 320/151/478 319/152/478
+f 320/150/479 322/151/479 321/152/479
+f 322/150/480 324/151/480 323/152/480
+f 324/150/481 326/151/481 325/152/481
+f 266/154/451 328/156/451 270/155/451
+f 328/156/451 326/157/451 270/155/451
+f 326/157/451 324/158/451 270/155/451
+f 324/158/451 322/159/451 270/155/451
+f 322/159/451 320/160/451 270/155/451
+f 320/160/451 318/161/451 270/155/451
+f 318/161/451 316/162/451 270/155/451
+f 316/162/451 314/163/451 270/155/451
+f 314/163/451 312/164/451 270/155/451
+f 312/164/451 310/165/451 270/155/451
+f 310/165/451 308/166/451 270/155/451
+f 308/166/451 306/167/451 270/155/451
+f 306/167/451 304/168/451 270/155/451
+f 304/168/451 302/169/451 270/155/451
+f 302/169/451 300/170/451 270/155/451
+f 300/170/451 298/171/451 270/155/451
+f 298/171/451 296/172/451 270/155/451
+f 296/172/451 294/173/451 270/155/451
+f 294/173/451 292/174/451 270/155/451
+f 292/174/451 290/175/451 270/155/451
+f 290/175/451 288/176/451 270/155/451
+f 288/176/451 286/177/451 270/155/451
+f 286/177/451 284/178/451 270/155/451
+f 284/178/451 282/179/451 270/155/451
+f 282/179/451 280/180/451 270/155/451
+f 280/180/451 278/181/451 270/155/451
+f 278/181/451 276/182/451 270/155/451
+f 276/182/451 274/183/451 270/155/451
+f 274/183/451 272/184/451 270/155/451
+f 328/150/482 266/151/482 265/152/482
+f 326/150/483 328/151/483 327/152/483
+f 267/154/450 269/156/450 327/155/450
+f 269/156/450 271/157/450 327/155/450
+f 271/157/450 273/158/450 327/155/450
+f 273/158/450 275/159/450 327/155/450
+f 275/159/450 277/160/450 327/155/450
+f 277/160/450 279/161/450 327/155/450
+f 279/161/450 281/162/450 327/155/450
+f 281/162/450 283/163/450 327/155/450
+f 283/163/450 285/164/450 327/155/450
+f 285/164/450 287/165/450 327/155/450
+f 287/165/450 289/166/450 327/155/450
+f 289/166/450 291/167/450 327/155/450
+f 291/167/450 293/168/450 327/155/450
+f 293/168/450 295/169/450 327/155/450
+f 295/169/450 297/170/450 327/155/450
+f 297/170/450 299/171/450 327/155/450
+f 299/171/450 301/172/450 327/155/450
+f 301/172/450 303/173/450 327/155/450
+f 303/173/450 305/174/450 327/155/450
+f 305/174/450 307/175/450 327/155/450
+f 307/175/450 309/176/450 327/155/450
+f 309/176/450 311/177/450 327/155/450
+f 311/177/450 313/178/450 327/155/450
+f 313/178/450 315/179/450 327/155/450
+f 315/179/450 317/180/450 327/155/450
+f 317/180/450 319/181/450 327/155/450
+f 319/181/450 321/182/450 327/155/450
+f 321/182/450 323/183/450 325/184/450
+f 327/155/450 321/182/450 325/184/450
+v -2.014818 0.007922 0.998641
+v -2.014818 2.007922 0.998641
+v -1.819728 0.007922 1.017856
+v -1.819728 2.007922 1.017856
+v -1.632135 0.007922 1.074762
+v -1.632135 2.007922 1.074762
+v -1.459248 0.007922 1.167172
+v -1.459248 2.007922 1.167172
+v -1.307712 0.007922 1.291534
+v -1.307712 2.007922 1.291534
+v -1.183349 0.007922 1.443071
+v -1.183349 2.007922 1.443071
+v -1.090939 0.007922 1.615958
+v -1.090939 2.007922 1.615958
+v -1.034033 0.007922 1.803551
+v -1.034033 2.007922 1.803551
+v -1.014818 0.007922 1.998641
+v -1.014818 2.007922 1.998641
+v -1.034033 0.007922 2.193732
+v -1.034033 2.007922 2.193732
+v -1.090939 0.007922 2.381325
+v -1.090939 2.007922 2.381325
+v -1.183349 0.007922 2.554211
+v -1.183349 2.007922 2.554211
+v -1.307712 0.007922 2.705748
+v -1.307712 2.007922 2.705748
+v -1.459248 0.007922 2.830111
+v -1.459248 2.007922 2.830111
+v -1.632135 0.007922 2.922521
+v -1.632135 2.007922 2.922521
+v -1.819728 0.007922 2.979427
+v -1.819728 2.007922 2.979427
+v -2.014819 0.007922 2.998641
+v -2.014819 2.007922 2.998641
+v -2.209909 0.007922 2.979426
+v -2.209909 2.007922 2.979426
+v -2.397502 0.007922 2.922521
+v -2.397502 2.007922 2.922521
+v -2.570389 0.007922 2.830111
+v -2.570389 2.007922 2.830111
+v -2.721926 0.007922 2.705748
+v -2.721926 2.007922 2.705748
+v -2.846288 0.007922 2.554211
+v -2.846288 2.007922 2.554211
+v -2.938698 0.007922 2.381324
+v -2.938698 2.007922 2.381324
+v -2.995604 0.007922 2.193731
+v -2.995604 2.007922 2.193731
+v -3.014818 0.007922 1.998640
+v -3.014818 2.007922 1.998640
+v -2.995604 0.007922 1.803550
+v -2.995604 2.007922 1.803550
+v -2.938698 0.007922 1.615957
+v -2.938698 2.007922 1.615957
+v -2.846287 0.007922 1.443070
+v -2.846287 2.007922 1.443070
+v -2.721924 0.007922 1.291534
+v -2.721924 2.007922 1.291534
+v -2.570388 0.007922 1.167171
+v -2.570388 2.007922 1.167171
+v -2.397501 0.007922 1.074761
+v -2.397501 2.007922 1.074761
+v -2.209907 0.007922 1.017856
+v -2.209907 2.007922 1.017856
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.098017 0.000000 -0.995185
+vn 0.881921 0.000000 -0.471397
+vn 0.634393 0.000000 0.773010
+vn 0.471396 0.000000 0.881922
+vn -0.881920 -0.000000 -0.471398
+vn -0.634393 -0.000000 -0.773011
+vn -0.471395 -0.000000 -0.881922
+s off
+f 329/185/484 330/186/484 332/187/484
+f 331/185/453 332/186/453 334/187/453
+f 333/185/454 334/186/454 336/187/454
+f 335/185/455 336/186/455 338/187/455
+f 337/185/456 338/186/456 340/187/456
+f 339/185/485 340/186/485 342/187/485
+f 341/185/458 342/186/458 344/187/458
+f 343/185/459 344/186/459 346/187/459
+f 345/185/460 346/186/460 348/187/460
+f 347/185/461 348/186/461 350/187/461
+f 349/185/462 350/186/462 352/187/462
+f 351/185/463 352/186/463 354/187/463
+f 353/185/486 354/186/486 356/187/486
+f 355/185/487 356/186/487 358/187/487
+f 357/185/466 358/186/466 360/187/466
+f 359/185/467 360/186/467 362/187/467
+f 361/185/468 362/186/468 363/188/468
+f 363/185/469 364/186/469 365/188/469
+f 365/185/470 366/186/470 367/188/470
+f 367/185/471 368/186/471 369/188/471
+f 369/185/472 370/186/472 371/188/472
+f 371/185/473 372/186/473 373/188/473
+f 373/185/474 374/186/474 375/188/474
+f 375/185/475 376/186/475 377/188/475
+f 377/185/476 378/186/476 379/188/476
+f 379/185/477 380/186/477 381/188/477
+f 381/185/488 382/186/488 383/188/488
+f 383/185/479 384/186/479 385/188/479
+f 385/185/489 386/186/489 387/188/489
+f 387/185/490 388/186/490 389/188/490
+f 332/189/451 330/190/451 334/191/451
+f 391/185/482 392/186/482 329/188/482
+f 389/185/483 390/186/483 391/188/483
+f 329/189/450 331/190/450 391/191/450
+f 331/188/484 329/185/484 332/187/484
+f 333/188/453 331/185/453 334/187/453
+f 335/188/454 333/185/454 336/187/454
+f 337/188/455 335/185/455 338/187/455
+f 339/188/456 337/185/456 340/187/456
+f 341/188/485 339/185/485 342/187/485
+f 343/188/458 341/185/458 344/187/458
+f 345/188/459 343/185/459 346/187/459
+f 347/188/460 345/185/460 348/187/460
+f 349/188/461 347/185/461 350/187/461
+f 351/188/462 349/185/462 352/187/462
+f 353/188/463 351/185/463 354/187/463
+f 355/188/486 353/185/486 356/187/486
+f 357/188/487 355/185/487 358/187/487
+f 359/188/466 357/185/466 360/187/466
+f 361/188/467 359/185/467 362/187/467
+f 362/186/468 364/187/468 363/188/468
+f 364/186/469 366/187/469 365/188/469
+f 366/186/470 368/187/470 367/188/470
+f 368/186/471 370/187/471 369/188/471
+f 370/186/472 372/187/472 371/188/472
+f 372/186/473 374/187/473 373/188/473
+f 374/186/474 376/187/474 375/188/474
+f 376/186/475 378/187/475 377/188/475
+f 378/186/476 380/187/476 379/188/476
+f 380/186/477 382/187/477 381/188/477
+f 382/186/488 384/187/488 383/188/488
+f 384/186/479 386/187/479 385/188/479
+f 386/186/489 388/187/489 387/188/489
+f 388/186/490 390/187/490 389/188/490
+f 330/190/451 392/192/451 334/191/451
+f 392/192/451 390/193/451 334/191/451
+f 390/193/451 388/194/451 334/191/451
+f 388/194/451 386/195/451 334/191/451
+f 386/195/451 384/196/451 334/191/451
+f 384/196/451 382/197/451 334/191/451
+f 382/197/451 380/198/451 334/191/451
+f 380/198/451 378/199/451 334/191/451
+f 378/199/451 376/200/451 334/191/451
+f 376/200/451 374/201/451 334/191/451
+f 374/201/451 372/202/451 334/191/451
+f 372/202/451 370/203/451 334/191/451
+f 370/203/451 368/204/451 334/191/451
+f 368/204/451 366/205/451 334/191/451
+f 366/205/451 364/206/451 334/191/451
+f 364/206/451 362/207/451 334/191/451
+f 362/207/451 360/208/451 334/191/451
+f 360/208/451 358/209/451 334/191/451
+f 358/209/451 356/210/451 334/191/451
+f 356/210/451 354/211/451 334/191/451
+f 354/211/451 352/212/451 334/191/451
+f 352/212/451 350/213/451 334/191/451
+f 350/213/451 348/214/451 334/191/451
+f 348/214/451 346/215/451 334/191/451
+f 346/215/451 344/216/451 334/191/451
+f 344/216/451 342/217/451 334/191/451
+f 342/217/451 340/218/451 334/191/451
+f 340/218/451 338/219/451 334/191/451
+f 338/219/451 336/220/451 334/191/451
+f 392/186/482 330/187/482 329/188/482
+f 390/186/483 392/187/483 391/188/483
+f 331/190/450 333/192/450 391/191/450
+f 333/192/450 335/193/450 391/191/450
+f 335/193/450 337/194/450 391/191/450
+f 337/194/450 339/195/450 391/191/450
+f 339/195/450 341/196/450 391/191/450
+f 341/196/450 343/197/450 391/191/450
+f 343/197/450 345/198/450 391/191/450
+f 345/198/450 347/199/450 391/191/450
+f 347/199/450 349/200/450 391/191/450
+f 349/200/450 351/201/450 391/191/450
+f 351/201/450 353/202/450 391/191/450
+f 353/202/450 355/203/450 391/191/450
+f 355/203/450 357/204/450 391/191/450
+f 357/204/450 359/205/450 391/191/450
+f 359/205/450 361/206/450 391/191/450
+f 361/206/450 363/207/450 391/191/450
+f 363/207/450 365/208/450 391/191/450
+f 365/208/450 367/209/450 391/191/450
+f 367/209/450 369/210/450 391/191/450
+f 369/210/450 371/211/450 391/191/450
+f 371/211/450 373/212/450 391/191/450
+f 373/212/450 375/213/450 391/191/450
+f 375/213/450 377/214/450 391/191/450
+f 377/214/450 379/215/450 391/191/450
+f 379/215/450 381/216/450 391/191/450
+f 381/216/450 383/217/450 391/191/450
+f 383/217/450 385/218/450 391/191/450
+f 385/218/450 387/219/450 391/191/450
+f 387/219/450 389/220/450 391/191/450
+v -2.001621 -0.021814 -3.021079
+v -2.001621 1.978186 -3.021079
+v -1.806530 -0.021814 -3.001864
+v -1.806530 1.978186 -3.001864
+v -1.618937 -0.021814 -2.944958
+v -1.618937 1.978186 -2.944958
+v -1.446051 -0.021814 -2.852548
+v -1.446051 1.978186 -2.852548
+v -1.294514 -0.021814 -2.728185
+v -1.294514 1.978186 -2.728185
+v -1.170151 -0.021814 -2.576649
+v -1.170151 1.978186 -2.576649
+v -1.077741 -0.021814 -2.403762
+v -1.077741 1.978186 -2.403762
+v -1.020836 -0.021814 -2.216169
+v -1.020836 1.978186 -2.216169
+v -1.001621 -0.021814 -2.021079
+v -1.001621 1.978186 -2.021079
+v -1.020835 -0.021814 -1.825988
+v -1.020835 1.978186 -1.825988
+v -1.077741 -0.021814 -1.638395
+v -1.077741 1.978186 -1.638395
+v -1.170151 -0.021814 -1.465508
+v -1.170151 1.978186 -1.465508
+v -1.294514 -0.021814 -1.313972
+v -1.294514 1.978186 -1.313972
+v -1.446051 -0.021814 -1.189609
+v -1.446051 1.978186 -1.189609
+v -1.618937 -0.021814 -1.097199
+v -1.618937 1.978186 -1.097199
+v -1.806531 -0.021814 -1.040293
+v -1.806531 1.978186 -1.040293
+v -2.001621 -0.021814 -1.021079
+v -2.001621 1.978186 -1.021079
+v -2.196712 -0.021814 -1.040293
+v -2.196712 1.978186 -1.040293
+v -2.384305 -0.021814 -1.097199
+v -2.384305 1.978186 -1.097199
+v -2.557191 -0.021814 -1.189609
+v -2.557191 1.978186 -1.189609
+v -2.708728 -0.021814 -1.313972
+v -2.708728 1.978186 -1.313972
+v -2.833091 -0.021814 -1.465509
+v -2.833091 1.978186 -1.465509
+v -2.925501 -0.021814 -1.638396
+v -2.925501 1.978186 -1.638396
+v -2.982406 -0.021814 -1.825989
+v -2.982406 1.978186 -1.825989
+v -3.001621 -0.021814 -2.021080
+v -3.001621 1.978186 -2.021080
+v -2.982406 -0.021814 -2.216170
+v -2.982406 1.978186 -2.216170
+v -2.925500 -0.021814 -2.403763
+v -2.925500 1.978186 -2.403763
+v -2.833090 -0.021814 -2.576650
+v -2.833090 1.978186 -2.576650
+v -2.708727 -0.021814 -2.728186
+v -2.708727 1.978186 -2.728186
+v -2.557190 -0.021814 -2.852549
+v -2.557190 1.978186 -2.852549
+v -2.384303 -0.021814 -2.944959
+v -2.384303 1.978186 -2.944959
+v -2.196710 -0.021814 -3.001864
+v -2.196710 1.978186 -3.001864
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.471398 0.000000 -0.881921
+vn -0.634393 0.000000 0.773010
+vn -0.881921 -0.000000 -0.471397
+vn -0.773009 -0.000000 -0.634395
+vn -0.634393 -0.000000 -0.773010
+s off
+f 393/221/484 394/222/484 396/223/484
+f 395/221/453 396/222/453 398/223/453
+f 397/221/491 398/222/491 400/223/491
+f 399/221/455 400/222/455 402/223/455
+f 401/221/456 402/222/456 404/223/456
+f 403/221/485 404/222/485 406/223/485
+f 405/221/458 406/222/458 408/223/458
+f 407/221/459 408/222/459 410/223/459
+f 409/221/460 410/222/460 412/223/460
+f 411/221/461 412/222/461 414/223/461
+f 413/221/462 414/222/462 416/223/462
+f 415/221/463 416/222/463 418/223/463
+f 417/221/464 418/222/464 420/223/464
+f 419/221/465 420/222/465 422/223/465
+f 421/221/466 422/222/466 424/223/466
+f 423/221/467 424/222/467 426/223/467
+f 425/221/468 426/222/468 427/224/468
+f 427/221/469 428/222/469 429/224/469
+f 429/221/470 430/222/470 431/224/470
+f 431/221/492 432/222/492 433/224/492
+f 433/221/472 434/222/472 435/224/472
+f 435/221/473 436/222/473 437/224/473
+f 437/221/474 438/222/474 439/224/474
+f 439/221/475 440/222/475 441/224/475
+f 441/221/476 442/222/476 443/224/476
+f 443/221/477 444/222/477 445/224/477
+f 445/221/493 446/222/493 447/224/493
+f 447/221/494 448/222/494 449/224/494
+f 449/221/495 450/222/495 451/224/495
+f 451/221/490 452/222/490 453/224/490
+f 396/225/451 394/226/451 398/227/451
+f 455/221/482 456/222/482 393/224/482
+f 453/221/483 454/222/483 455/224/483
+f 393/225/450 395/226/450 455/227/450
+f 395/224/484 393/221/484 396/223/484
+f 397/224/453 395/221/453 398/223/453
+f 399/224/491 397/221/491 400/223/491
+f 401/224/455 399/221/455 402/223/455
+f 403/224/456 401/221/456 404/223/456
+f 405/224/485 403/221/485 406/223/485
+f 407/224/458 405/221/458 408/223/458
+f 409/224/459 407/221/459 410/223/459
+f 411/224/460 409/221/460 412/223/460
+f 413/224/461 411/221/461 414/223/461
+f 415/224/462 413/221/462 416/223/462
+f 417/224/463 415/221/463 418/223/463
+f 419/224/464 417/221/464 420/223/464
+f 421/224/465 419/221/465 422/223/465
+f 423/224/466 421/221/466 424/223/466
+f 425/224/467 423/221/467 426/223/467
+f 426/222/468 428/223/468 427/224/468
+f 428/222/469 430/223/469 429/224/469
+f 430/222/470 432/223/470 431/224/470
+f 432/222/492 434/223/492 433/224/492
+f 434/222/472 436/223/472 435/224/472
+f 436/222/473 438/223/473 437/224/473
+f 438/222/474 440/223/474 439/224/474
+f 440/222/475 442/223/475 441/224/475
+f 442/222/476 444/223/476 443/224/476
+f 444/222/477 446/223/477 445/224/477
+f 446/222/493 448/223/493 447/224/493
+f 448/222/494 450/223/494 449/224/494
+f 450/222/495 452/223/495 451/224/495
+f 452/222/490 454/223/490 453/224/490
+f 394/226/451 456/228/451 398/227/451
+f 456/228/451 454/229/451 398/227/451
+f 454/229/451 452/230/451 398/227/451
+f 452/230/451 450/231/451 398/227/451
+f 450/231/451 448/232/451 398/227/451
+f 448/232/451 446/233/451 398/227/451
+f 446/233/451 444/234/451 398/227/451
+f 444/234/451 442/235/451 398/227/451
+f 442/235/451 440/236/451 398/227/451
+f 440/236/451 438/237/451 398/227/451
+f 438/237/451 436/238/451 398/227/451
+f 436/238/451 434/239/451 398/227/451
+f 434/239/451 432/240/451 398/227/451
+f 432/240/451 430/241/451 398/227/451
+f 430/241/451 428/242/451 398/227/451
+f 428/242/451 426/243/451 398/227/451
+f 426/243/451 424/244/451 398/227/451
+f 424/244/451 422/245/451 398/227/451
+f 422/245/451 420/246/451 398/227/451
+f 420/246/451 418/247/451 398/227/451
+f 418/247/451 416/248/451 398/227/451
+f 416/248/451 414/249/451 398/227/451
+f 414/249/451 412/250/451 398/227/451
+f 412/250/451 410/251/451 398/227/451
+f 410/251/451 408/252/451 398/227/451
+f 408/252/451 406/253/451 398/227/451
+f 406/253/451 404/254/451 398/227/451
+f 404/254/451 402/255/451 398/227/451
+f 402/255/451 400/256/451 398/227/451
+f 456/222/482 394/223/482 393/224/482
+f 454/222/483 456/223/483 455/224/483
+f 395/226/450 397/228/450 455/227/450
+f 397/228/450 399/229/450 455/227/450
+f 399/229/450 401/230/450 455/227/450
+f 401/230/450 403/231/450 455/227/450
+f 403/231/450 405/232/450 455/227/450
+f 405/232/450 407/233/450 455/227/450
+f 407/233/450 409/234/450 455/227/450
+f 409/234/450 411/235/450 455/227/450
+f 411/235/450 413/236/450 455/227/450
+f 413/236/450 415/237/450 455/227/450
+f 415/237/450 417/238/450 455/227/450
+f 417/238/450 419/239/450 455/227/450
+f 419/239/450 421/240/450 455/227/450
+f 421/240/450 423/241/450 455/227/450
+f 423/241/450 425/242/450 455/227/450
+f 425/242/450 427/243/450 455/227/450
+f 427/243/450 429/244/450 455/227/450
+f 429/244/450 431/245/450 455/227/450
+f 431/245/450 433/246/450 455/227/450
+f 433/246/450 435/247/450 455/227/450
+f 435/247/450 437/248/450 455/227/450
+f 437/248/450 439/249/450 455/227/450
+f 439/249/450 441/250/450 455/227/450
+f 441/250/450 443/251/450 455/227/450
+f 443/251/450 445/252/450 455/227/450
+f 445/252/450 447/253/450 455/227/450
+f 447/253/450 449/254/450 455/227/450
+f 449/254/450 451/255/450 455/227/450
+f 451/255/450 453/256/450 455/227/450
+v 2.021592 0.003623 1.016610
+v 2.021592 2.003623 1.016610
+v 2.216682 0.003623 1.035825
+v 2.216682 2.003623 1.035825
+v 2.404276 0.003623 1.092731
+v 2.404276 2.003623 1.092731
+v 2.577162 0.003623 1.185141
+v 2.577162 2.003623 1.185141
+v 2.728699 0.003623 1.309503
+v 2.728699 2.003623 1.309503
+v 2.853062 0.003623 1.461040
+v 2.853062 2.003623 1.461040
+v 2.945472 0.003623 1.633927
+v 2.945472 2.003623 1.633927
+v 3.002378 0.003623 1.821520
+v 3.002378 2.003623 1.821520
+v 3.021592 0.003623 2.016610
+v 3.021592 2.003623 2.016610
+v 3.002378 0.003623 2.211700
+v 3.002378 2.003623 2.211700
+v 2.945472 0.003623 2.399293
+v 2.945472 2.003623 2.399293
+v 2.853062 0.003623 2.572180
+v 2.853062 2.003623 2.572180
+v 2.728699 0.003623 2.723717
+v 2.728699 2.003623 2.723717
+v 2.577162 0.003623 2.848080
+v 2.577162 2.003623 2.848080
+v 2.404275 0.003623 2.940490
+v 2.404275 2.003623 2.940490
+v 2.216682 0.003623 2.997396
+v 2.216682 2.003623 2.997396
+v 2.021592 0.003623 3.016610
+v 2.021592 2.003623 3.016610
+v 1.826501 0.003623 2.997395
+v 1.826501 2.003623 2.997395
+v 1.638908 0.003623 2.940490
+v 1.638908 2.003623 2.940490
+v 1.466021 0.003623 2.848079
+v 1.466021 2.003623 2.848079
+v 1.314485 0.003623 2.723716
+v 1.314485 2.003623 2.723716
+v 1.190122 0.003623 2.572180
+v 1.190122 2.003623 2.572180
+v 1.097712 0.003623 2.399293
+v 1.097712 2.003623 2.399293
+v 1.040807 0.003623 2.211699
+v 1.040807 2.003623 2.211699
+v 1.021592 0.003623 2.016609
+v 1.021592 2.003623 2.016609
+v 1.040807 0.003623 1.821519
+v 1.040807 2.003623 1.821519
+v 1.097713 0.003623 1.633926
+v 1.097713 2.003623 1.633926
+v 1.190123 0.003623 1.461039
+v 1.190123 2.003623 1.461039
+v 1.314486 0.003623 1.309502
+v 1.314486 2.003623 1.309502
+v 1.466023 0.003623 1.185140
+v 1.466023 2.003623 1.185140
+v 1.638910 0.003623 1.092730
+v 1.638910 2.003623 1.092730
+v 1.826503 0.003623 1.035825
+v 1.826503 2.003623 1.035825
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+s off
+f 457/257/484 458/258/484 460/259/484
+f 459/257/453 460/258/453 462/259/453
+f 461/257/454 462/258/454 464/259/454
+f 463/257/455 464/258/455 466/259/455
+f 465/257/456 466/258/456 468/259/456
+f 467/257/457 468/258/457 470/259/457
+f 469/257/458 470/258/458 472/259/458
+f 471/257/459 472/258/459 474/259/459
+f 473/257/460 474/258/460 476/259/460
+f 475/257/461 476/258/461 478/259/461
+f 477/257/462 478/258/462 480/259/462
+f 479/257/463 480/258/463 482/259/463
+f 481/257/464 482/258/464 484/259/464
+f 483/257/465 484/258/465 486/259/465
+f 485/257/466 486/258/466 488/259/466
+f 487/257/467 488/258/467 490/259/467
+f 489/257/468 490/258/468 491/260/468
+f 491/257/469 492/258/469 493/260/469
+f 493/257/470 494/258/470 495/260/470
+f 495/257/471 496/258/471 497/260/471
+f 497/257/472 498/258/472 499/260/472
+f 499/257/473 500/258/473 501/260/473
+f 501/257/474 502/258/474 503/260/474
+f 503/257/475 504/258/475 505/260/475
+f 505/257/476 506/258/476 507/260/476
+f 507/257/477 508/258/477 509/260/477
+f 509/257/478 510/258/478 511/260/478
+f 511/257/494 512/258/494 513/260/494
+f 513/257/489 514/258/489 515/260/489
+f 515/257/490 516/258/490 517/260/490
+f 460/261/451 458/262/451 462/263/451
+f 519/257/482 520/258/482 457/260/482
+f 517/257/483 518/258/483 519/260/483
+f 457/261/450 459/262/450 519/263/450
+f 459/260/484 457/257/484 460/259/484
+f 461/260/453 459/257/453 462/259/453
+f 463/260/454 461/257/454 464/259/454
+f 465/260/455 463/257/455 466/259/455
+f 467/260/456 465/257/456 468/259/456
+f 469/260/457 467/257/457 470/259/457
+f 471/260/458 469/257/458 472/259/458
+f 473/260/459 471/257/459 474/259/459
+f 475/260/460 473/257/460 476/259/460
+f 477/260/461 475/257/461 478/259/461
+f 479/260/462 477/257/462 480/259/462
+f 481/260/463 479/257/463 482/259/463
+f 483/260/464 481/257/464 484/259/464
+f 485/260/465 483/257/465 486/259/465
+f 487/260/466 485/257/466 488/259/466
+f 489/260/467 487/257/467 490/259/467
+f 490/258/468 492/259/468 491/260/468
+f 492/258/469 494/259/469 493/260/469
+f 494/258/470 496/259/470 495/260/470
+f 496/258/471 498/259/471 497/260/471
+f 498/258/472 500/259/472 499/260/472
+f 500/258/473 502/259/473 501/260/473
+f 502/258/474 504/259/474 503/260/474
+f 504/258/475 506/259/475 505/260/475
+f 506/258/476 508/259/476 507/260/476
+f 508/258/477 510/259/477 509/260/477
+f 510/258/478 512/259/478 511/260/478
+f 512/258/494 514/259/494 513/260/494
+f 514/258/489 516/259/489 515/260/489
+f 516/258/490 518/259/490 517/260/490
+f 458/262/451 520/264/451 462/263/451
+f 520/264/451 518/265/451 462/263/451
+f 518/265/451 516/266/451 462/263/451
+f 516/266/451 514/267/451 462/263/451
+f 514/267/451 512/268/451 462/263/451
+f 512/268/451 510/269/451 462/263/451
+f 510/269/451 508/270/451 462/263/451
+f 508/270/451 506/271/451 462/263/451
+f 506/271/451 504/272/451 462/263/451
+f 504/272/451 502/273/451 462/263/451
+f 502/273/451 500/274/451 462/263/451
+f 500/274/451 498/275/451 462/263/451
+f 498/275/451 496/276/451 462/263/451
+f 496/276/451 494/277/451 462/263/451
+f 494/277/451 492/278/451 462/263/451
+f 492/278/451 490/279/451 462/263/451
+f 490/279/451 488/280/451 462/263/451
+f 488/280/451 486/281/451 462/263/451
+f 486/281/451 484/282/451 462/263/451
+f 484/282/451 482/283/451 462/263/451
+f 482/283/451 480/284/451 462/263/451
+f 480/284/451 478/285/451 462/263/451
+f 478/285/451 476/286/451 462/263/451
+f 476/286/451 474/287/451 462/263/451
+f 474/287/451 472/288/451 462/263/451
+f 472/288/451 470/289/451 462/263/451
+f 470/289/451 468/290/451 462/263/451
+f 468/290/451 466/291/451 462/263/451
+f 466/291/451 464/292/451 462/263/451
+f 520/258/482 458/259/482 457/260/482
+f 518/258/483 520/259/483 519/260/483
+f 459/262/450 461/264/450 519/263/450
+f 461/264/450 463/265/450 519/263/450
+f 463/265/450 465/266/450 519/263/450
+f 465/266/450 467/267/450 519/263/450
+f 467/267/450 469/268/450 519/263/450
+f 469/268/450 471/269/450 519/263/450
+f 471/269/450 473/270/450 519/263/450
+f 473/270/450 475/271/450 519/263/450
+f 475/271/450 477/272/450 519/263/450
+f 477/272/450 479/273/450 519/263/450
+f 479/273/450 481/274/450 519/263/450
+f 481/274/450 483/275/450 519/263/450
+f 483/275/450 485/276/450 519/263/450
+f 485/276/450 487/277/450 519/263/450
+f 487/277/450 489/278/450 519/263/450
+f 489/278/450 491/279/450 519/263/450
+f 491/279/450 493/280/450 519/263/450
+f 493/280/450 495/281/450 519/263/450
+f 495/281/450 497/282/450 519/263/450
+f 497/282/450 499/283/450 519/263/450
+f 499/283/450 501/284/450 519/263/450
+f 501/284/450 503/285/450 519/263/450
+f 503/285/450 505/286/450 519/263/450
+f 505/286/450 507/287/450 519/263/450
+f 507/287/450 509/288/450 519/263/450
+f 509/288/450 511/289/450 519/263/450
+f 511/289/450 513/290/450 519/263/450
+f 513/290/450 515/291/450 519/263/450
+f 515/291/450 517/292/450 519/263/450
diff --git a/examples/datavisualization/customitems/pipe.obj b/examples/datavisualization/customitems/pipe.obj
new file mode 100644
index 00000000..6ccbb286
--- /dev/null
+++ b/examples/datavisualization/customitems/pipe.obj
@@ -0,0 +1,330 @@
+# Blender v2.66 (sub 0) OBJ File: 'cylinder.blend'
+# www.blender.org
+o Cylinder
+v 0.000000 -1.000000 -1.000000
+v 0.000000 1.000000 -1.000000
+v 0.195090 -1.000000 -0.980785
+v 0.195090 1.000000 -0.980785
+v 0.382683 -1.000000 -0.923880
+v 0.382683 1.000000 -0.923880
+v 0.555570 -1.000000 -0.831470
+v 0.555570 1.000000 -0.831470
+v 0.707107 -1.000000 -0.707107
+v 0.707107 1.000000 -0.707107
+v 0.831470 -1.000000 -0.555570
+v 0.831470 1.000000 -0.555570
+v 0.923880 -1.000000 -0.382683
+v 0.923880 1.000000 -0.382683
+v 0.980785 -1.000000 -0.195090
+v 0.980785 1.000000 -0.195090
+v 1.000000 -1.000000 -0.000000
+v 1.000000 1.000000 -0.000000
+v 0.980785 -1.000000 0.195090
+v 0.980785 1.000000 0.195090
+v 0.923880 -1.000000 0.382683
+v 0.923880 1.000000 0.382683
+v 0.831470 -1.000000 0.555570
+v 0.831470 1.000000 0.555570
+v 0.707107 -1.000000 0.707107
+v 0.707107 1.000000 0.707107
+v 0.555570 -1.000000 0.831470
+v 0.555570 1.000000 0.831470
+v 0.382683 -1.000000 0.923880
+v 0.382683 1.000000 0.923880
+v 0.195090 -1.000000 0.980785
+v 0.195090 1.000000 0.980785
+v -0.000000 -1.000000 1.000000
+v -0.000000 1.000000 1.000000
+v -0.195091 -1.000000 0.980785
+v -0.195091 1.000000 0.980785
+v -0.382684 -1.000000 0.923879
+v -0.382684 1.000000 0.923879
+v -0.555571 -1.000000 0.831469
+v -0.555571 1.000000 0.831469
+v -0.707107 -1.000000 0.707106
+v -0.707107 1.000000 0.707106
+v -0.831470 -1.000000 0.555570
+v -0.831470 1.000000 0.555570
+v -0.923880 -1.000000 0.382683
+v -0.923880 1.000000 0.382683
+v -0.980785 -1.000000 0.195089
+v -0.980785 1.000000 0.195089
+v -1.000000 -1.000000 -0.000001
+v -1.000000 1.000000 -0.000001
+v -0.980785 -1.000000 -0.195091
+v -0.980785 1.000000 -0.195091
+v -0.923879 -1.000000 -0.382684
+v -0.923879 1.000000 -0.382684
+v -0.831469 -1.000000 -0.555571
+v -0.831469 1.000000 -0.555571
+v -0.707106 -1.000000 -0.707108
+v -0.707106 1.000000 -0.707108
+v -0.555569 -1.000000 -0.831470
+v -0.555569 1.000000 -0.831470
+v -0.382682 -1.000000 -0.923880
+v -0.382682 1.000000 -0.923880
+v -0.195089 -1.000000 -0.980786
+v -0.195089 1.000000 -0.980786
+vt 0.289718 0.879351
+vt 0.288367 0.438844
+vt 0.330714 0.438714
+vt 0.332066 0.879221
+vt 0.370605 0.438592
+vt 0.371956 0.879099
+vt 0.406505 0.438482
+vt 0.407857 0.878988
+vt 0.437036 0.438388
+vt 0.778904 0.000000
+vt 0.780256 0.440507
+vt 0.749725 0.440601
+vt 0.748373 0.000094
+vt 0.713824 0.440711
+vt 0.712473 0.000204
+vt 0.673934 0.440833
+vt 0.672582 0.000326
+vt 0.631586 0.440963
+vt 0.630235 0.000456
+vt 0.588409 0.441095
+vt 0.587057 0.000588
+vt 0.546061 0.441225
+vt 0.544710 0.000718
+vt 0.506171 0.441348
+vt 0.504819 0.000841
+vt 0.470270 0.441458
+vt 0.468919 0.000951
+vt 0.439739 0.441552
+vt 0.720545 0.882916
+vt 0.719194 0.442409
+vt 0.755094 0.442299
+vt 0.756446 0.882806
+vt 0.794985 0.442176
+vt 0.796336 0.882683
+vt 0.837333 0.442046
+vt 0.838684 0.882553
+vt 0.881861 0.882421
+vt 0.880510 0.441914
+vt 0.924209 0.882291
+vt 0.922857 0.441784
+vt 0.964099 0.882168
+vt 0.962748 0.441662
+vt 1.000000 0.882058
+vt 0.717842 0.441552
+vt 0.719194 0.882058
+vt 0.681942 0.441662
+vt 0.683293 0.882169
+vt 0.642051 0.441784
+vt 0.643403 0.882291
+vt 0.599704 0.441914
+vt 0.601055 0.882421
+vt 0.556526 0.442046
+vt 0.557878 0.882553
+vt 0.514179 0.442176
+vt 0.515530 0.882683
+vt 0.474288 0.442299
+vt 0.475640 0.882806
+vt 0.438388 0.442409
+vt 0.097872 0.879939
+vt 0.096520 0.439433
+vt 0.128403 0.879846
+vt 0.127051 0.439339
+vt 0.164303 0.879735
+vt 0.162952 0.439229
+vt 0.204194 0.879613
+vt 0.000000 0.197605
+vt 0.008423 0.155257
+vt 0.000000 0.240783
+vt 0.246541 0.879483
+vt 0.245190 0.438976
+vt 0.202842 0.439106
+vt 0.438388 0.878895
+vt 0.438388 0.001045
+vt 0.998649 0.441552
+vt 0.439739 0.882916
+vt 0.024947 0.115367
+vt 0.048935 0.079466
+vt 0.079466 0.048935
+vt 0.115366 0.024947
+vt 0.155257 0.008424
+vt 0.197605 0.000000
+vt 0.240782 0.000000
+vt 0.283130 0.008423
+vt 0.323021 0.024947
+vt 0.358922 0.048935
+vt 0.389453 0.079466
+vt 0.413441 0.115367
+vt 0.429964 0.155257
+vt 0.438388 0.197605
+vt 0.438388 0.240783
+vt 0.429964 0.283130
+vt 0.413441 0.323021
+vt 0.389453 0.358922
+vt 0.358922 0.389453
+vt 0.323021 0.413441
+vt 0.283130 0.429964
+vt 0.240783 0.438388
+vt 0.197605 0.438388
+vt 0.155257 0.429964
+vt 0.115367 0.413441
+vt 0.079466 0.389453
+vt 0.048935 0.358922
+vt 0.024947 0.323021
+vt 0.008423 0.283130
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.685690 -0.727866
+vn 0.142003 0.685690 -0.713889
+vn 0.195074 0.000000 -0.980773
+vn 0.278542 0.685690 -0.672475
+vn 0.382672 0.000000 -0.923856
+vn 0.404370 0.685690 -0.605213
+vn 0.555559 0.000000 -0.831446
+vn 0.514664 0.685690 -0.514664
+vn 0.707083 0.000000 -0.707083
+vn 0.605213 0.685690 -0.404370
+vn 0.831446 0.000000 -0.555559
+vn 0.672475 0.685690 -0.278542
+vn 0.923856 0.000000 -0.382672
+vn 0.713889 0.685690 -0.142003
+vn 0.980773 0.000000 -0.195074
+vn 0.727866 0.685690 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.713889 0.685690 0.142003
+vn 0.980773 0.000000 0.195074
+vn 0.672475 0.685690 0.278542
+vn 0.923856 0.000000 0.382672
+vn 0.605213 0.685690 0.404370
+vn 0.831446 0.000000 0.555559
+vn 0.514664 0.685690 0.514664
+vn 0.707083 0.000000 0.707083
+vn 0.404370 0.685690 0.605213
+vn 0.555559 0.000000 0.831446
+vn 0.278542 0.685690 0.672475
+vn 0.382672 0.000000 0.923856
+vn 0.142003 0.685690 0.713889
+vn 0.195074 0.000000 0.980773
+vn 0.000000 0.685690 0.727866
+vn 0.000000 0.000000 0.999969
+vn -0.195074 0.000000 0.980773
+vn -0.142003 0.685690 0.713889
+vn -0.382672 0.000000 0.923856
+vn -0.278542 0.685690 0.672475
+vn -0.555559 0.000000 0.831446
+vn -0.404370 0.685690 0.605213
+vn -0.707083 0.000000 0.707083
+vn -0.514664 0.685690 0.514664
+vn -0.831446 0.000000 0.555559
+vn -0.605213 0.685690 0.404370
+vn -0.923856 0.000000 0.382672
+vn -0.672475 0.685690 0.278542
+vn -0.980773 0.000000 0.195074
+vn -0.713889 0.685690 0.142003
+vn -1.000000 0.000000 0.000000
+vn -0.727866 0.685690 0.000000
+vn -0.980773 0.000000 -0.195074
+vn -0.713889 0.685690 -0.142003
+vn -0.923856 0.000000 -0.382672
+vn -0.672475 0.685690 -0.278542
+vn -0.831446 0.000000 -0.555559
+vn -0.605213 0.685690 -0.404370
+vn -0.707083 0.000000 -0.707083
+vn -0.514664 0.685690 -0.514695
+vn -0.555559 0.000000 -0.831446
+vn -0.404370 0.685690 -0.605213
+vn -0.382672 0.000000 -0.923856
+vn -0.195074 0.000000 -0.980773
+vn -0.142003 0.685690 -0.713889
+vn -0.278542 0.685690 -0.672475
+s 1
+f 1/1/1 2/2/2 4/3/3
+f 3/4/4 4/3/3 6/5/5
+f 5/6/6 6/5/5 8/7/7
+f 7/8/8 8/7/7 10/9/9
+f 9/10/10 10/11/9 12/12/11
+f 11/13/12 12/12/11 14/14/13
+f 13/15/14 14/14/13 16/16/15
+f 15/17/16 16/16/15 18/18/17
+f 17/19/18 18/18/17 20/20/19
+f 19/21/20 20/20/19 22/22/21
+f 21/23/22 22/22/21 24/24/23
+f 23/25/24 24/24/23 26/26/25
+f 25/27/26 26/26/25 28/28/27
+f 27/29/28 28/30/27 30/31/29
+f 29/32/30 30/31/29 32/33/31
+f 31/34/32 32/33/31 34/35/33
+f 33/36/34 34/35/33 35/37/35
+f 35/37/35 36/38/36 37/39/37
+f 37/39/37 38/40/38 39/41/39
+f 39/41/39 40/42/40 41/43/41
+f 41/44/41 42/45/42 43/46/43
+f 43/46/43 44/47/44 45/48/45
+f 45/48/45 46/49/46 47/50/47
+f 47/50/47 48/51/48 49/52/49
+f 49/52/49 50/53/50 51/54/51
+f 51/54/51 52/55/52 53/56/53
+f 53/56/53 54/57/54 55/58/55
+f 55/59/55 56/60/56 57/61/57
+f 57/61/57 58/62/58 59/63/59
+f 59/63/59 60/64/60 61/65/61
+f 4/66/3 2/67/2 6/68/5
+f 63/69/62 64/70/63 1/1/1
+f 61/65/61 62/71/64 63/69/62
+f 3/4/4 1/1/1 4/3/3
+f 5/6/6 3/4/4 6/5/5
+f 7/8/8 5/6/6 8/7/7
+f 9/72/10 7/8/8 10/9/9
+f 11/13/12 9/10/10 12/12/11
+f 13/15/14 11/13/12 14/14/13
+f 15/17/16 13/15/14 16/16/15
+f 17/19/18 15/17/16 18/18/17
+f 19/21/20 17/19/18 20/20/19
+f 21/23/22 19/21/20 22/22/21
+f 23/25/24 21/23/22 24/24/23
+f 25/27/26 23/25/24 26/26/25
+f 27/73/28 25/27/26 28/28/27
+f 29/32/30 27/29/28 30/31/29
+f 31/34/32 29/32/30 32/33/31
+f 33/36/34 31/34/32 34/35/33
+f 34/35/33 36/38/36 35/37/35
+f 36/38/36 38/40/38 37/39/37
+f 38/40/38 40/42/40 39/41/39
+f 40/42/40 42/74/42 41/43/41
+f 42/45/42 44/47/44 43/46/43
+f 44/47/44 46/49/46 45/48/45
+f 46/49/46 48/51/48 47/50/47
+f 48/51/48 50/53/50 49/52/49
+f 50/53/50 52/55/52 51/54/51
+f 52/55/52 54/57/54 53/56/53
+f 54/57/54 56/75/56 55/58/55
+f 56/60/56 58/62/58 57/61/57
+f 58/62/58 60/64/60 59/63/59
+f 60/64/60 62/71/64 61/65/61
+f 2/67/2 64/76/63 6/68/5
+f 64/76/63 62/77/64 6/68/5
+f 62/77/64 60/78/60 6/68/5
+f 60/78/60 58/79/58 6/68/5
+f 58/79/58 56/80/56 6/68/5
+f 56/80/56 54/81/54 6/68/5
+f 54/81/54 52/82/52 6/68/5
+f 52/82/52 50/83/50 6/68/5
+f 50/83/50 48/84/48 6/68/5
+f 48/84/48 46/85/46 6/68/5
+f 46/85/46 44/86/44 6/68/5
+f 44/86/44 42/87/42 6/68/5
+f 42/87/42 40/88/40 6/68/5
+f 40/88/40 38/89/38 6/68/5
+f 38/89/38 36/90/36 6/68/5
+f 36/90/36 34/91/33 6/68/5
+f 34/91/33 32/92/31 6/68/5
+f 32/92/31 30/93/29 6/68/5
+f 30/93/29 28/94/27 6/68/5
+f 28/94/27 26/95/25 6/68/5
+f 26/95/25 24/96/23 6/68/5
+f 24/96/23 22/97/21 6/68/5
+f 22/97/21 20/98/19 6/68/5
+f 20/98/19 18/99/17 6/68/5
+f 18/99/17 16/100/15 6/68/5
+f 16/100/15 14/101/13 6/68/5
+f 14/101/13 12/102/11 6/68/5
+f 12/102/11 10/103/9 8/104/7
+f 6/68/5 12/102/11 8/104/7
+f 64/70/63 2/2/2 1/1/1
+f 62/71/64 64/70/63 63/69/62
diff --git a/examples/datavisualization/customitems/refinery.obj b/examples/datavisualization/customitems/refinery.obj
new file mode 100644
index 00000000..ed90c361
--- /dev/null
+++ b/examples/datavisualization/customitems/refinery.obj
@@ -0,0 +1,2330 @@
+# Blender v2.66 (sub 0) OBJ File: 'oilrefinery.blend'
+# www.blender.org
+v -2.719012 -0.196783 4.805554
+v -2.719012 -0.196783 -4.824533
+v 2.730989 -0.196783 -4.824533
+v 2.730989 -0.196783 4.805554
+v -2.719012 0.012961 4.805554
+v -2.719012 0.012961 -4.824533
+v 2.730989 0.012961 -4.824533
+v 2.730989 0.012961 4.805554
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 1.000000 1.000000
+vn -0.577349 0.577349 0.577349
+vn -0.577349 0.577349 -0.577349
+vn -0.577349 -0.577349 0.577349
+vn 0.577349 0.577349 -0.577349
+vn 0.577349 -0.577349 -0.577349
+vn 0.577349 0.577349 0.577349
+vn 0.577349 -0.577349 0.577349
+vn -0.577349 -0.577349 -0.577349
+s 1
+f 5/1/1 6/2/2 1/3/3
+f 6/1/2 7/2/4 3/4/5
+f 7/1/4 8/2/6 4/4/7
+f 8/1/6 5/2/1 1/4/3
+f 1/1/3 2/2/8 3/4/5
+f 8/1/6 7/2/4 6/4/2
+f 6/2/2 2/4/8 1/3/3
+f 2/3/8 6/1/2 3/4/5
+f 3/3/5 7/1/4 4/4/7
+f 4/3/7 8/1/6 1/4/3
+f 4/3/7 1/1/3 3/4/5
+f 5/3/1 8/1/6 6/4/2
+v -1.384247 1.252743 0.422195
+v -1.384247 2.069450 0.422195
+v -1.303725 1.252743 0.430041
+v -1.303725 2.069450 0.430041
+v -1.226298 1.252743 0.453279
+v -1.226298 2.069450 0.453279
+v -1.154941 1.252743 0.491015
+v -1.154941 2.069450 0.491015
+v -1.092396 1.252743 0.541799
+v -1.092396 2.069450 0.541799
+v -1.041066 1.252743 0.603679
+v -1.041066 2.069450 0.603679
+v -1.002925 1.252743 0.674278
+v -1.002925 2.069450 0.674278
+v -0.979437 1.252743 0.750883
+v -0.979437 2.069450 0.750883
+v -0.971507 1.252743 0.830548
+v -0.971507 2.069450 0.830548
+v -0.979437 1.252743 0.910214
+v -0.979437 2.069450 0.910214
+v -1.002925 1.252743 0.986818
+v -1.002925 2.069450 0.986818
+v -1.041066 1.252743 1.057417
+v -1.041066 2.069450 1.057417
+v -1.092396 1.252743 1.119298
+v -1.092396 2.069450 1.119298
+v -1.154941 1.252743 1.170082
+v -1.154941 2.069450 1.170082
+v -1.226298 1.252743 1.207818
+v -1.226298 2.069450 1.207818
+v -1.303726 1.252743 1.231055
+v -1.303726 2.069450 1.231055
+v -1.384247 1.252743 1.238902
+v -1.384247 2.069450 1.238902
+v -1.464769 1.252743 1.231055
+v -1.464769 2.069450 1.231055
+v -1.542196 1.252743 1.207818
+v -1.542196 2.069450 1.207818
+v -1.613554 1.252743 1.170082
+v -1.613554 2.069450 1.170082
+v -1.676099 1.252743 1.119298
+v -1.676099 2.069450 1.119298
+v -1.727429 1.252743 1.057417
+v -1.727429 2.069450 1.057417
+v -1.765570 1.252743 0.986818
+v -1.765570 2.069450 0.986818
+v -1.789057 1.252743 0.910214
+v -1.789057 2.069450 0.910214
+v -1.796988 1.252743 0.830548
+v -1.796988 2.069450 0.830548
+v -1.789057 1.252743 0.750882
+v -1.789057 2.069450 0.750882
+v -1.765570 1.252743 0.674278
+v -1.765570 2.069450 0.674278
+v -1.727428 1.252743 0.603679
+v -1.727428 2.069450 0.603679
+v -1.676098 1.252743 0.541798
+v -1.676098 2.069450 0.541798
+v -1.613553 1.252743 0.491015
+v -1.613553 2.069450 0.491015
+v -1.542196 1.252743 0.453279
+v -1.542196 2.069450 0.453279
+v -1.464768 1.252743 0.430041
+v -1.464768 2.069450 0.430041
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.096985 0.000000 -0.995286
+vn 0.287455 0.000000 -0.957794
+vn 0.467486 0.000000 -0.884001
+vn 0.630338 0.000000 -0.776321
+vn 0.769672 0.000000 -0.638440
+vn 0.879812 0.000000 -0.475321
+vn 0.956070 0.000000 -0.293137
+vn 0.995081 0.000000 -0.099061
+vn 0.995081 0.000000 0.099061
+vn 0.956070 0.000000 0.293137
+vn 0.879813 0.000000 0.475321
+vn 0.769672 0.000000 0.638440
+vn 0.630338 0.000000 0.776321
+vn 0.467486 0.000000 0.884001
+vn 0.287455 0.000000 0.957794
+vn 0.096986 0.000000 0.995286
+vn -0.096986 0.000000 0.995286
+vn -0.287456 0.000000 0.957794
+vn -0.467486 0.000000 0.884001
+vn -0.630338 0.000000 0.776321
+vn -0.769673 0.000000 0.638438
+vn -0.879812 0.000000 0.475322
+vn -0.956071 0.000000 0.293135
+vn -0.995081 0.000000 0.099059
+vn -0.995081 -0.000000 -0.099061
+vn -0.956070 -0.000000 -0.293138
+vn -0.879812 -0.000000 -0.475322
+vn -0.769671 -0.000000 -0.638441
+vn -0.630337 -0.000000 -0.776322
+vn -0.467484 -0.000000 -0.884001
+vn -0.000000 1.000000 0.000000
+vn -0.096985 -0.000000 -0.995286
+vn -0.287454 -0.000000 -0.957794
+vn 0.000000 -1.000000 -0.000000
+s off
+f 9/5/9 10/6/9 12/7/9
+f 11/5/10 12/6/10 14/7/10
+f 13/5/11 14/6/11 16/7/11
+f 15/5/12 16/6/12 18/7/12
+f 17/5/13 18/6/13 20/7/13
+f 19/5/14 20/6/14 22/7/14
+f 21/5/15 22/6/15 24/7/15
+f 23/5/16 24/6/16 26/7/16
+f 25/5/17 26/6/17 28/7/17
+f 27/5/18 28/6/18 30/7/18
+f 29/5/19 30/6/19 32/7/19
+f 31/5/20 32/6/20 34/7/20
+f 33/5/21 34/6/21 36/7/21
+f 35/5/22 36/6/22 38/7/22
+f 37/5/23 38/6/23 40/7/23
+f 39/5/24 40/6/24 42/7/24
+f 41/5/25 42/6/25 43/8/25
+f 43/5/26 44/6/26 45/8/26
+f 45/5/27 46/6/27 47/8/27
+f 47/5/28 48/6/28 49/8/28
+f 49/5/29 50/6/29 51/8/29
+f 51/5/30 52/6/30 53/8/30
+f 53/5/31 54/6/31 55/8/31
+f 55/5/32 56/6/32 57/8/32
+f 57/5/33 58/6/33 59/8/33
+f 59/5/34 60/6/34 61/8/34
+f 61/5/35 62/6/35 63/8/35
+f 63/5/36 64/6/36 65/8/36
+f 65/5/37 66/6/37 67/8/37
+f 67/5/38 68/6/38 69/8/38
+f 12/9/39 10/10/39 14/11/39
+f 71/5/40 72/6/40 9/8/40
+f 69/5/41 70/6/41 71/8/41
+f 9/9/42 11/10/42 71/11/42
+f 11/8/9 9/5/9 12/7/9
+f 13/8/10 11/5/10 14/7/10
+f 15/8/11 13/5/11 16/7/11
+f 17/8/12 15/5/12 18/7/12
+f 19/8/13 17/5/13 20/7/13
+f 21/8/14 19/5/14 22/7/14
+f 23/8/15 21/5/15 24/7/15
+f 25/8/16 23/5/16 26/7/16
+f 27/8/17 25/5/17 28/7/17
+f 29/8/18 27/5/18 30/7/18
+f 31/8/19 29/5/19 32/7/19
+f 33/8/20 31/5/20 34/7/20
+f 35/8/21 33/5/21 36/7/21
+f 37/8/22 35/5/22 38/7/22
+f 39/8/23 37/5/23 40/7/23
+f 41/8/24 39/5/24 42/7/24
+f 42/6/25 44/7/25 43/8/25
+f 44/6/26 46/7/26 45/8/26
+f 46/6/27 48/7/27 47/8/27
+f 48/6/28 50/7/28 49/8/28
+f 50/6/29 52/7/29 51/8/29
+f 52/6/30 54/7/30 53/8/30
+f 54/6/31 56/7/31 55/8/31
+f 56/6/32 58/7/32 57/8/32
+f 58/6/33 60/7/33 59/8/33
+f 60/6/34 62/7/34 61/8/34
+f 62/6/35 64/7/35 63/8/35
+f 64/6/36 66/7/36 65/8/36
+f 66/6/37 68/7/37 67/8/37
+f 68/6/38 70/7/38 69/8/38
+f 10/10/39 72/12/39 14/11/39
+f 72/12/39 70/13/39 14/11/39
+f 70/13/39 68/14/39 14/11/39
+f 68/14/39 66/15/39 14/11/39
+f 66/15/39 64/16/39 14/11/39
+f 64/16/39 62/17/39 14/11/39
+f 62/17/39 60/18/39 14/11/39
+f 60/18/39 58/19/39 14/11/39
+f 58/19/39 56/20/39 14/11/39
+f 56/20/39 54/21/39 14/11/39
+f 54/21/39 52/22/39 14/11/39
+f 52/22/39 50/23/39 14/11/39
+f 50/23/39 48/24/39 14/11/39
+f 48/24/39 46/25/39 14/11/39
+f 46/25/39 44/26/39 14/11/39
+f 44/26/39 42/27/39 14/11/39
+f 42/27/39 40/28/39 14/11/39
+f 40/28/39 38/29/39 14/11/39
+f 38/29/39 36/30/39 14/11/39
+f 36/30/39 34/31/39 14/11/39
+f 34/31/39 32/32/39 14/11/39
+f 32/32/39 30/33/39 14/11/39
+f 30/33/39 28/34/39 14/11/39
+f 28/34/39 26/35/39 14/11/39
+f 26/35/39 24/36/39 14/11/39
+f 24/36/39 22/37/39 14/11/39
+f 22/37/39 20/38/39 14/11/39
+f 20/38/39 18/39/39 16/40/39
+f 14/11/39 20/38/39 16/40/39
+f 72/6/40 10/7/40 9/8/40
+f 70/6/41 72/7/41 71/8/41
+f 11/10/42 13/12/42 71/11/42
+f 13/12/42 15/13/42 71/11/42
+f 15/13/42 17/14/42 71/11/42
+f 17/14/42 19/15/42 71/11/42
+f 19/15/42 21/16/42 71/11/42
+f 21/16/42 23/17/42 71/11/42
+f 23/17/42 25/18/42 71/11/42
+f 25/18/42 27/19/42 71/11/42
+f 27/19/42 29/20/42 71/11/42
+f 29/20/42 31/21/42 71/11/42
+f 31/21/42 33/22/42 71/11/42
+f 33/22/42 35/23/42 71/11/42
+f 35/23/42 37/24/42 71/11/42
+f 37/24/42 39/25/42 71/11/42
+f 39/25/42 41/26/42 71/11/42
+f 41/26/42 43/27/42 71/11/42
+f 43/27/42 45/28/42 71/11/42
+f 45/28/42 47/29/42 71/11/42
+f 47/29/42 49/30/42 71/11/42
+f 49/30/42 51/31/42 71/11/42
+f 51/31/42 53/32/42 71/11/42
+f 53/32/42 55/33/42 71/11/42
+f 55/33/42 57/34/42 71/11/42
+f 57/34/42 59/35/42 71/11/42
+f 59/35/42 61/36/42 71/11/42
+f 61/36/42 63/37/42 71/11/42
+f 63/37/42 65/38/42 71/11/42
+f 65/38/42 67/39/42 71/11/42
+f 67/39/42 69/40/42 71/11/42
+v 1.365790 1.252743 0.402799
+v 1.365790 2.069450 0.402799
+v 1.446312 1.252743 0.410646
+v 1.446312 2.069450 0.410646
+v 1.523739 1.252743 0.433883
+v 1.523739 2.069450 0.433883
+v 1.595097 1.252743 0.471619
+v 1.595097 2.069450 0.471619
+v 1.657642 1.252743 0.522403
+v 1.657642 2.069450 0.522403
+v 1.708972 1.252743 0.584284
+v 1.708972 2.069450 0.584284
+v 1.747113 1.252743 0.654883
+v 1.747113 2.069450 0.654883
+v 1.770600 1.252743 0.731487
+v 1.770600 2.069450 0.731487
+v 1.778531 1.252743 0.811153
+v 1.778531 2.069450 0.811153
+v 1.770600 1.252743 0.890818
+v 1.770600 2.069450 0.890818
+v 1.747113 1.252743 0.967423
+v 1.747113 2.069450 0.967423
+v 1.708972 1.252743 1.038022
+v 1.708972 2.069450 1.038022
+v 1.657642 1.252743 1.099902
+v 1.657642 2.069450 1.099902
+v 1.595097 1.252743 1.150686
+v 1.595097 2.069450 1.150686
+v 1.523739 1.252743 1.188422
+v 1.523739 2.069450 1.188422
+v 1.446312 1.252743 1.211660
+v 1.446312 2.069450 1.211660
+v 1.365790 1.252743 1.219506
+v 1.365790 2.069450 1.219506
+v 1.285269 1.252743 1.211660
+v 1.285269 2.069450 1.211660
+v 1.207841 1.252743 1.188422
+v 1.207841 2.069450 1.188422
+v 1.136484 1.252743 1.150686
+v 1.136484 2.069450 1.150686
+v 1.073939 1.252743 1.099902
+v 1.073939 2.069450 1.099902
+v 1.022609 1.252743 1.038021
+v 1.022609 2.069450 1.038021
+v 0.984468 1.252743 0.967422
+v 0.984468 2.069450 0.967422
+v 0.960981 1.252743 0.890818
+v 0.960981 2.069450 0.890818
+v 0.953050 1.252743 0.811152
+v 0.953050 2.069450 0.811152
+v 0.960981 1.252743 0.731486
+v 0.960981 2.069450 0.731486
+v 0.984468 1.252743 0.654882
+v 0.984468 2.069450 0.654882
+v 1.022609 1.252743 0.584283
+v 1.022609 2.069450 0.584283
+v 1.073939 1.252743 0.522403
+v 1.073939 2.069450 0.522403
+v 1.136485 1.252743 0.471619
+v 1.136485 2.069450 0.471619
+v 1.207842 1.252743 0.433883
+v 1.207842 2.069450 0.433883
+v 1.285269 1.252743 0.410646
+v 1.285269 2.069450 0.410646
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn -0.879812 0.000000 0.475321
+vn -0.956071 0.000000 0.293136
+vn -0.956070 -0.000000 -0.293137
+s off
+f 73/41/9 74/42/9 76/43/9
+f 75/41/10 76/42/10 78/43/10
+f 77/41/11 78/42/11 80/43/11
+f 79/41/12 80/42/12 82/43/12
+f 81/41/13 82/42/13 84/43/13
+f 83/41/14 84/42/14 86/43/14
+f 85/41/15 86/42/15 88/43/15
+f 87/41/16 88/42/16 90/43/16
+f 89/41/17 90/42/17 92/43/17
+f 91/41/18 92/42/18 94/43/18
+f 93/41/19 94/42/19 96/43/19
+f 95/41/20 96/42/20 98/43/20
+f 97/41/21 98/42/21 100/43/21
+f 99/41/22 100/42/22 102/43/22
+f 101/41/23 102/42/23 104/43/23
+f 103/41/24 104/42/24 106/43/24
+f 105/41/25 106/42/25 107/44/25
+f 107/41/26 108/42/26 109/44/26
+f 109/41/27 110/42/27 111/44/27
+f 111/41/28 112/42/28 113/44/28
+f 113/41/29 114/42/29 115/44/29
+f 115/41/43 116/42/43 117/44/43
+f 117/41/44 118/42/44 119/44/44
+f 119/41/32 120/42/32 121/44/32
+f 121/41/33 122/42/33 123/44/33
+f 123/41/45 124/42/45 125/44/45
+f 125/41/35 126/42/35 127/44/35
+f 127/41/36 128/42/36 129/44/36
+f 129/41/37 130/42/37 131/44/37
+f 131/41/38 132/42/38 133/44/38
+f 76/45/39 74/46/39 78/47/39
+f 135/41/40 136/42/40 73/44/40
+f 133/41/41 134/42/41 135/44/41
+f 73/45/42 75/46/42 135/47/42
+f 75/44/9 73/41/9 76/43/9
+f 77/44/10 75/41/10 78/43/10
+f 79/44/11 77/41/11 80/43/11
+f 81/44/12 79/41/12 82/43/12
+f 83/44/13 81/41/13 84/43/13
+f 85/44/14 83/41/14 86/43/14
+f 87/44/15 85/41/15 88/43/15
+f 89/44/16 87/41/16 90/43/16
+f 91/44/17 89/41/17 92/43/17
+f 93/44/18 91/41/18 94/43/18
+f 95/44/19 93/41/19 96/43/19
+f 97/44/20 95/41/20 98/43/20
+f 99/44/21 97/41/21 100/43/21
+f 101/44/22 99/41/22 102/43/22
+f 103/44/23 101/41/23 104/43/23
+f 105/44/24 103/41/24 106/43/24
+f 106/42/25 108/43/25 107/44/25
+f 108/42/26 110/43/26 109/44/26
+f 110/42/27 112/43/27 111/44/27
+f 112/42/28 114/43/28 113/44/28
+f 114/42/29 116/43/29 115/44/29
+f 116/42/43 118/43/43 117/44/43
+f 118/42/44 120/43/44 119/44/44
+f 120/42/32 122/43/32 121/44/32
+f 122/42/33 124/43/33 123/44/33
+f 124/42/45 126/43/45 125/44/45
+f 126/42/35 128/43/35 127/44/35
+f 128/42/36 130/43/36 129/44/36
+f 130/42/37 132/43/37 131/44/37
+f 132/42/38 134/43/38 133/44/38
+f 74/46/39 136/48/39 78/47/39
+f 136/48/39 134/49/39 78/47/39
+f 134/49/39 132/50/39 78/47/39
+f 132/50/39 130/51/39 78/47/39
+f 130/51/39 128/52/39 78/47/39
+f 128/52/39 126/53/39 78/47/39
+f 126/53/39 124/54/39 78/47/39
+f 124/54/39 122/55/39 78/47/39
+f 122/55/39 120/56/39 78/47/39
+f 120/56/39 118/57/39 78/47/39
+f 118/57/39 116/58/39 78/47/39
+f 116/58/39 114/59/39 78/47/39
+f 114/59/39 112/60/39 78/47/39
+f 112/60/39 110/61/39 78/47/39
+f 110/61/39 108/62/39 78/47/39
+f 108/62/39 106/63/39 78/47/39
+f 106/63/39 104/64/39 78/47/39
+f 104/64/39 102/65/39 78/47/39
+f 102/65/39 100/66/39 78/47/39
+f 100/66/39 98/67/39 78/47/39
+f 98/67/39 96/68/39 78/47/39
+f 96/68/39 94/69/39 78/47/39
+f 94/69/39 92/70/39 78/47/39
+f 92/70/39 90/71/39 78/47/39
+f 90/71/39 88/72/39 78/47/39
+f 88/72/39 86/73/39 78/47/39
+f 86/73/39 84/74/39 78/47/39
+f 84/74/39 82/75/39 80/76/39
+f 78/47/39 84/74/39 80/76/39
+f 136/42/40 74/43/40 73/44/40
+f 134/42/41 136/43/41 135/44/41
+f 75/46/42 77/48/42 135/47/42
+f 77/48/42 79/49/42 135/47/42
+f 79/49/42 81/50/42 135/47/42
+f 81/50/42 83/51/42 135/47/42
+f 83/51/42 85/52/42 135/47/42
+f 85/52/42 87/53/42 135/47/42
+f 87/53/42 89/54/42 135/47/42
+f 89/54/42 91/55/42 135/47/42
+f 91/55/42 93/56/42 135/47/42
+f 93/56/42 95/57/42 135/47/42
+f 95/57/42 97/58/42 135/47/42
+f 97/58/42 99/59/42 135/47/42
+f 99/59/42 101/60/42 135/47/42
+f 101/60/42 103/61/42 135/47/42
+f 103/61/42 105/62/42 135/47/42
+f 105/62/42 107/63/42 135/47/42
+f 107/63/42 109/64/42 135/47/42
+f 109/64/42 111/65/42 135/47/42
+f 111/65/42 113/66/42 135/47/42
+f 113/66/42 115/67/42 135/47/42
+f 115/67/42 117/68/42 135/47/42
+f 117/68/42 119/69/42 135/47/42
+f 119/69/42 121/70/42 135/47/42
+f 121/70/42 123/71/42 135/47/42
+f 123/71/42 125/72/42 135/47/42
+f 125/72/42 127/73/42 135/47/42
+f 127/73/42 129/74/42 135/47/42
+f 129/74/42 131/75/42 135/47/42
+f 131/75/42 133/76/42 135/47/42
+v -2.345663 0.025178 -0.194338
+v -2.345663 0.025178 -0.594338
+v -1.345663 0.025178 -0.594338
+v -1.345663 0.025178 -0.194338
+v -2.345663 1.525178 -0.194338
+v -2.345663 1.525178 -0.594338
+v -1.345663 1.525178 -0.594338
+v -1.345663 1.525178 -0.194338
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 1.000000 1.000000
+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
+s off
+f 141/77/46 142/78/46 137/79/46
+f 142/77/47 143/78/47 138/79/47
+f 143/77/48 144/78/48 140/80/48
+f 144/77/49 141/78/49 137/80/49
+f 137/77/42 138/78/42 139/80/42
+f 144/77/39 143/78/39 142/80/39
+f 142/78/46 138/80/46 137/79/46
+f 143/78/47 139/80/47 138/79/47
+f 139/79/48 143/77/48 140/80/48
+f 140/79/49 144/77/49 137/80/49
+f 140/79/42 137/77/42 139/80/42
+f 141/79/39 144/77/39 142/80/39
+v 1.364119 0.019809 -0.205019
+v 1.364119 0.019809 -0.605019
+v 2.364120 0.019809 -0.605019
+v 2.364120 0.019809 -0.205019
+v 1.364119 1.419809 -0.205019
+v 1.364119 1.419809 -0.605019
+v 2.364120 1.419809 -0.605019
+v 2.364120 1.419809 -0.205019
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 1.000000 1.000000
+s off
+f 149/81/46 150/82/46 145/83/46
+f 150/81/47 151/82/47 146/83/47
+f 151/81/48 152/82/48 148/84/48
+f 152/81/49 149/82/49 145/84/49
+f 145/81/42 146/82/42 147/84/42
+f 152/81/39 151/82/39 150/84/39
+f 150/82/46 146/84/46 145/83/46
+f 151/82/47 147/84/47 146/83/47
+f 147/83/48 151/81/48 148/84/48
+f 148/83/49 152/81/49 145/84/49
+f 148/83/42 145/81/42 147/84/42
+f 149/83/39 152/81/39 150/84/39
+v -1.384247 0.015116 3.993316
+v -1.384247 0.015113 -0.006684
+v -1.189157 0.034331 3.993316
+v -1.189157 0.034328 -0.006684
+v -1.001564 0.091236 3.993316
+v -1.001564 0.091234 -0.006684
+v -0.828677 0.183646 3.993316
+v -0.828677 0.183644 -0.006684
+v -0.677140 0.308009 3.993316
+v -0.677140 0.308007 -0.006684
+v -0.552777 0.459546 3.993316
+v -0.552777 0.459543 -0.006684
+v -0.460368 0.632432 3.993316
+v -0.460368 0.632430 -0.006684
+v -0.403462 0.820025 3.993316
+v -0.403462 0.820023 -0.006684
+v -0.384247 1.015116 3.993316
+v -0.384247 1.015113 -0.006684
+v -0.403462 1.210206 3.993316
+v -0.403462 1.210203 -0.006685
+v -0.460368 1.397799 3.993315
+v -0.460368 1.397797 -0.006685
+v -0.552777 1.570686 3.993315
+v -0.552777 1.570683 -0.006685
+v -0.677140 1.722223 3.993315
+v -0.677140 1.722220 -0.006685
+v -0.828677 1.846586 3.993315
+v -0.828677 1.846583 -0.006685
+v -1.001564 1.938995 3.993315
+v -1.001564 1.938993 -0.006685
+v -1.189157 1.995901 3.993315
+v -1.189157 1.995899 -0.006685
+v -1.384247 2.015116 3.993315
+v -1.384247 2.015113 -0.006685
+v -1.579338 1.995901 3.993315
+v -1.579338 1.995898 -0.006685
+v -1.766931 1.938995 3.993315
+v -1.766931 1.938993 -0.006685
+v -1.939818 1.846585 3.993315
+v -1.939818 1.846583 -0.006685
+v -2.091354 1.722222 3.993315
+v -2.091354 1.722220 -0.006685
+v -2.215717 1.570685 3.993315
+v -2.215717 1.570683 -0.006685
+v -2.308127 1.397799 3.993315
+v -2.308127 1.397796 -0.006685
+v -2.365032 1.210205 3.993316
+v -2.365032 1.210203 -0.006685
+v -2.384247 1.015115 3.993316
+v -2.384247 1.015112 -0.006684
+v -2.365032 0.820024 3.993316
+v -2.365032 0.820022 -0.006684
+v -2.308126 0.632431 3.993316
+v -2.308126 0.632429 -0.006684
+v -2.215716 0.459545 3.993316
+v -2.215716 0.459542 -0.006684
+v -2.091353 0.308008 3.993316
+v -2.091353 0.308006 -0.006684
+v -1.939816 0.183645 3.993316
+v -1.939816 0.183643 -0.006684
+v -1.766929 0.091236 3.993316
+v -1.766929 0.091233 -0.006684
+v -1.579336 0.034330 3.993316
+v -1.579336 0.034328 -0.006684
+v -1.384247 0.619839 -0.156215
+v -1.307133 0.627434 -0.156215
+v -1.232982 0.649928 -0.156215
+v -1.164645 0.686455 -0.156215
+v -1.104746 0.735612 -0.156215
+v -1.055589 0.795511 -0.156215
+v -1.019062 0.863848 -0.156216
+v -0.996568 0.937999 -0.156216
+v -0.988973 1.015113 -0.156216
+v -0.996568 1.092227 -0.156216
+v -1.019062 1.166378 -0.156216
+v -1.055589 1.234715 -0.156216
+v -1.104746 1.294614 -0.156216
+v -1.164645 1.343771 -0.156216
+v -1.232982 1.380298 -0.156216
+v -1.307133 1.402792 -0.156216
+v -1.384247 1.410387 -0.156216
+v -1.461361 1.402792 -0.156216
+v -1.535512 1.380298 -0.156216
+v -1.603849 1.343771 -0.156216
+v -1.663748 1.294613 -0.156216
+v -1.712905 1.234715 -0.156216
+v -1.749432 1.166377 -0.156216
+v -1.771926 1.092227 -0.156216
+v -1.779521 1.015113 -0.156216
+v -1.771925 0.937999 -0.156216
+v -1.749432 0.863848 -0.156216
+v -1.712905 0.795510 -0.156215
+v -1.663747 0.735612 -0.156215
+v -1.603849 0.686455 -0.156215
+v -1.535511 0.649928 -0.156215
+v -1.461360 0.627434 -0.156215
+v -1.384247 0.605419 4.159918
+v -1.304319 0.613291 4.159918
+v -1.227463 0.636605 4.159918
+v -1.156632 0.674465 4.159918
+v -1.094548 0.725416 4.159918
+v -1.043596 0.787500 4.159918
+v -1.005736 0.858332 4.159918
+v -0.982422 0.935188 4.159918
+v -0.974550 1.015116 4.159918
+v -0.982422 1.095044 4.159918
+v -1.005736 1.171900 4.159918
+v -1.043596 1.242731 4.159918
+v -1.094548 1.304815 4.159918
+v -1.156632 1.355766 4.159918
+v -1.227463 1.393626 4.159918
+v -1.304319 1.416940 4.159918
+v -1.384247 1.424813 4.159918
+v -1.464175 1.416940 4.159918
+v -1.541031 1.393626 4.159918
+v -1.611863 1.355766 4.159918
+v -1.673947 1.304815 4.159918
+v -1.724898 1.242731 4.159918
+v -1.762758 1.171900 4.159918
+v -1.786072 1.095043 4.159918
+v -1.793944 1.015115 4.159918
+v -1.786072 0.935187 4.159918
+v -1.762757 0.858331 4.159918
+v -1.724897 0.787500 4.159918
+v -1.673946 0.725416 4.159918
+v -1.611862 0.674465 4.159918
+v -1.541031 0.636605 4.159918
+v -1.464174 0.613291 4.159918
+v 0.006492 3.927105 -3.525056
+v 0.006492 4.374522 -3.525055
+v 0.050135 3.927105 -3.520757
+v 0.050135 4.374522 -3.520757
+v 0.092102 3.927104 -3.508027
+v 0.092102 4.374522 -3.508027
+v 0.130778 3.927104 -3.487354
+v 0.130778 4.374522 -3.487354
+v 0.164678 3.927104 -3.459533
+v 0.164678 4.374522 -3.459533
+v 0.192499 3.927104 -3.425632
+v 0.192499 4.374522 -3.425633
+v 0.213172 3.927105 -3.386956
+v 0.213172 4.374522 -3.386957
+v 0.225902 3.927105 -3.344990
+v 0.225902 4.374522 -3.344990
+v 0.230201 3.927105 -3.301347
+v 0.230201 4.374522 -3.301347
+v 0.225902 3.927105 -3.257704
+v 0.225902 4.374522 -3.257703
+v 0.213172 3.927105 -3.215737
+v 0.213172 4.374522 -3.215738
+v 0.192499 3.927105 -3.177061
+v 0.192499 4.374522 -3.177062
+v 0.164678 3.927105 -3.143161
+v 0.164678 4.374522 -3.143161
+v 0.130778 3.927105 -3.115340
+v 0.130778 4.374522 -3.115340
+v 0.092102 3.927105 -3.094666
+v 0.092102 4.374522 -3.094667
+v 0.050135 3.927105 -3.081937
+v 0.050135 4.374522 -3.081936
+v 0.006492 3.927105 -3.077638
+v 0.006492 4.374522 -3.077638
+v -0.037151 3.927105 -3.081937
+v -0.037151 4.374522 -3.081936
+v -0.079118 3.927105 -3.094666
+v -0.079118 4.374522 -3.094667
+v -0.117794 3.927105 -3.115340
+v -0.117794 4.374522 -3.115340
+v -0.151694 3.927105 -3.143161
+v -0.151694 4.374522 -3.143161
+v -0.179515 3.927105 -3.177061
+v -0.179515 4.374522 -3.177062
+v -0.200188 3.927105 -3.215737
+v -0.200188 4.374522 -3.215738
+v -0.212918 3.927105 -3.257704
+v -0.212918 4.374522 -3.257704
+v -0.217217 3.927105 -3.301347
+v -0.217217 4.374522 -3.301347
+v -0.212918 3.927105 -3.344991
+v -0.212918 4.374522 -3.344990
+v -0.200188 3.927105 -3.386957
+v -0.200188 4.374522 -3.386957
+v -0.179515 3.927104 -3.425633
+v -0.179515 4.374522 -3.425633
+v -0.151694 3.927104 -3.459533
+v -0.151694 4.374522 -3.459533
+v -0.117794 3.927104 -3.487354
+v -0.117794 4.374522 -3.487354
+v -0.079117 3.927104 -3.508027
+v -0.079117 4.374522 -3.508027
+v -0.037151 3.927105 -3.520757
+v -0.037151 4.374522 -3.520757
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.853553 0.853553
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.500000 0.000000
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.777785 0.084265
+vt 0.853553 0.146447
+vt 0.084265 0.222215
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.038060 0.308659
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vt 0.146447 0.853554
+vt 0.990393 0.597545
+vt 0.084266 0.777786
+vt 0.038060 0.691342
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.009607 0.597546
+vt 0.000000 0.500000
+vt 0.915735 0.222215
+vt 0.009607 0.402455
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vn 0.098018 -0.995185 0.000001
+vn 0.290285 -0.956940 0.000001
+vn 0.471397 -0.881921 0.000001
+vn 0.634393 -0.773010 0.000000
+vn 0.773010 -0.634393 0.000000
+vn 0.881921 -0.471397 0.000000
+vn 0.956940 -0.290285 0.000000
+vn 0.995185 -0.098017 0.000000
+vn 0.995185 0.098017 -0.000000
+vn 0.956940 0.290285 -0.000000
+vn 0.881922 0.471396 -0.000000
+vn 0.773010 0.634393 -0.000000
+vn 0.634393 0.773011 -0.000000
+vn 0.471397 0.881921 -0.000001
+vn 0.290284 0.956941 -0.000001
+vn 0.098017 0.995185 -0.000001
+vn -0.098017 0.995185 -0.000001
+vn -0.290285 0.956940 -0.000001
+vn -0.471397 0.881921 -0.000001
+vn -0.634394 0.773010 -0.000000
+vn -0.773011 0.634393 -0.000000
+vn -0.881922 0.471396 -0.000000
+vn -0.956941 0.290283 -0.000000
+vn -0.995185 0.098017 -0.000000
+vn -0.995185 -0.098018 0.000000
+vn -0.956940 -0.290286 0.000000
+vn -0.881920 -0.471398 0.000000
+vn -0.773010 -0.634394 0.000000
+vn -0.634393 -0.773011 0.000000
+vn -0.471395 -0.881922 0.000001
+vn -0.239975 -0.023636 -0.970491
+vn -0.098017 -0.995185 0.000001
+vn -0.290283 -0.956941 0.000001
+vn -0.210907 0.173086 0.962060
+vn 0.152975 0.186400 -0.970492
+vn -0.023635 -0.239975 -0.970491
+vn -0.152975 0.186400 -0.970492
+vn 0.239975 -0.023636 -0.970491
+vn -0.212662 -0.113672 -0.970491
+vn 0.069999 0.230752 -0.970492
+vn 0.113670 -0.212663 -0.970491
+vn -0.212662 0.113670 -0.970492
+vn 0.230752 0.069998 -0.970492
+vn -0.152975 -0.186401 -0.970491
+vn -0.023636 0.239974 -0.970492
+vn 0.186400 -0.152976 -0.970491
+vn -0.239974 0.023634 -0.970491
+vn 0.186401 0.152974 -0.970491
+vn -0.069998 -0.230753 -0.970491
+vn -0.113670 0.212662 -0.970492
+vn 0.230753 -0.069999 -0.970491
+vn -0.230752 -0.069999 -0.970491
+vn 0.113670 0.212662 -0.970491
+vn 0.069998 -0.230753 -0.970491
+vn -0.186401 0.152974 -0.970491
+vn 0.239975 0.023634 -0.970492
+vn -0.186400 -0.152976 -0.970491
+vn 0.023635 0.239974 -0.970492
+vn 0.152975 -0.186401 -0.970491
+vn -0.230752 0.069998 -0.970492
+vn 0.023636 -0.239975 -0.970491
+vn 0.212662 0.113670 -0.970492
+vn -0.113670 -0.212664 -0.970491
+vn -0.069999 0.230751 -0.970492
+vn 0.212663 -0.113671 -0.970491
+vn 0.271524 0.026743 0.962060
+vn -0.210906 -0.173085 0.962060
+vn 0.026743 0.271524 0.962060
+vn 0.173086 -0.210906 0.962060
+vn -0.261089 0.079201 0.962060
+vn 0.026743 -0.271523 0.962060
+vn 0.240621 0.128616 0.962060
+vn -0.128614 -0.240621 0.962060
+vn -0.079201 0.261090 0.962060
+vn 0.240621 -0.128614 0.962060
+vn -0.271524 -0.026742 0.962060
+vn 0.173087 0.210907 0.962060
+vn -0.026742 -0.271523 0.962060
+vn -0.173087 0.210907 0.962060
+vn 0.271524 -0.026742 0.962060
+vn -0.240621 -0.128615 0.962060
+vn 0.079201 0.261090 0.962060
+vn 0.128614 -0.240621 0.962060
+vn -0.240621 0.128616 0.962060
+vn 0.261089 0.079202 0.962060
+vn -0.173086 -0.210906 0.962060
+vn -0.026743 0.271524 0.962060
+vn 0.210907 -0.173085 0.962060
+vn -0.271524 0.026743 0.962060
+vn 0.210907 0.173087 0.962060
+vn -0.079201 -0.261088 0.962060
+vn -0.128615 0.240622 0.962060
+vn 0.261089 -0.079200 0.962060
+vn -0.261089 -0.079201 0.962060
+vn 0.128615 0.240622 0.962060
+vn 0.079201 -0.261088 0.962060
+vn 0.098012 0.000001 -0.995185
+vn 0.290289 -0.000001 -0.956939
+vn 0.471395 0.000001 -0.881922
+vn 0.634385 0.000001 -0.773018
+vn 0.773012 -0.000001 -0.634391
+vn 0.881922 -0.000001 -0.471395
+vn 0.956942 -0.000000 -0.290279
+vn 0.995184 -0.000000 -0.098022
+vn 0.995184 0.000000 0.098020
+vn 0.956940 -0.000000 0.290285
+vn 0.881922 0.000001 0.471395
+vn 0.773012 0.000001 0.634391
+vn 0.634398 0.000001 0.773007
+vn 0.471395 -0.000001 0.881922
+vn 0.290289 0.000001 0.956939
+vn 0.098010 -0.000001 0.995185
+vn -0.098033 0.000001 0.995183
+vn -0.290269 -0.000001 0.956945
+vn -0.471412 0.000001 0.881913
+vn -0.634385 -0.000001 0.773018
+vn -0.773012 0.000001 0.634391
+vn -0.881922 0.000001 0.471395
+vn -0.956942 0.000000 0.290279
+vn -0.995184 0.000000 0.098022
+vn -0.995184 -0.000000 -0.098020
+vn -0.956939 0.000000 -0.290289
+vn -0.881922 0.000001 -0.471395
+vn -0.773003 0.000001 -0.634402
+vn -0.634398 -0.000001 -0.773007
+vn -0.471395 0.000001 -0.881922
+vn -0.098032 -0.000001 -0.995183
+vn -0.290292 0.000001 -0.956938
+vn 0.098017 -0.995185 0.000001
+vn 0.881921 -0.471396 0.000000
+vn 0.881921 0.471397 -0.000000
+vn 0.634394 0.773010 -0.000001
+vn 0.471396 0.881922 -0.000001
+vn 0.290285 0.956940 -0.000001
+vn -0.098018 0.995185 -0.000001
+vn -0.881920 -0.471399 0.000000
+vn -0.239974 -0.023636 -0.970491
+vn -0.210907 0.173087 0.962060
+vn 0.000000 -0.000003 -1.000000
+vn 0.152975 0.186400 -0.970491
+vn 0.239975 -0.023635 -0.970491
+vn -0.212662 -0.113674 -0.970491
+vn 0.069998 0.230752 -0.970492
+vn 0.113671 -0.212663 -0.970491
+vn -0.212664 0.113668 -0.970491
+vn 0.230752 0.069998 -0.970491
+vn -0.023635 0.239974 -0.970492
+vn 0.186401 -0.152975 -0.970491
+vn -0.239974 0.023635 -0.970492
+vn 0.186400 0.152975 -0.970491
+vn 0.230753 -0.069998 -0.970491
+vn -0.230753 -0.069998 -0.970491
+vn -0.186400 0.152974 -0.970492
+vn 0.239974 0.023635 -0.970492
+vn -0.186400 -0.152975 -0.970491
+vn 0.023635 -0.239975 -0.970491
+vn 0.212664 0.113668 -0.970491
+vn -0.113670 -0.212663 -0.970491
+vn -0.069998 0.230752 -0.970492
+vn 0.212662 -0.113674 -0.970491
+vn -0.210905 -0.173086 0.962060
+vn -0.261089 0.079200 0.962060
+vn 0.026743 -0.271523 0.962061
+vn 0.240622 0.128615 0.962060
+vn 0.240621 -0.128615 0.962060
+vn -0.271523 -0.026743 0.962060
+vn 0.271523 -0.026743 0.962060
+vn 0.079200 0.261090 0.962060
+vn 0.128615 -0.240620 0.962060
+vn -0.240622 0.128615 0.962060
+vn 0.261089 0.079200 0.962060
+vn -0.173085 -0.210906 0.962060
+vn 0.210906 -0.173086 0.962060
+vn -0.271524 0.026742 0.962060
+vn -0.079200 -0.261088 0.962061
+vn -0.261089 -0.079200 0.962060
+vn 0.079200 -0.261088 0.962060
+vn 0.098033 -0.000001 -0.995183
+vn 0.290269 0.000001 -0.956945
+vn 0.634398 -0.000001 -0.773007
+vn 0.995184 -0.000000 0.098022
+vn 0.956942 0.000000 0.290279
+vn 0.634385 -0.000001 0.773018
+vn 0.471412 0.000001 0.881913
+vn 0.290269 -0.000001 0.956945
+vn 0.098032 0.000001 0.995183
+vn -0.098012 -0.000001 0.995185
+vn -0.290289 0.000001 0.956939
+vn -0.471395 -0.000001 0.881922
+vn -0.634398 0.000001 0.773007
+vn -0.995184 0.000000 -0.098022
+vn -0.773012 -0.000001 -0.634391
+vn -0.634385 0.000001 -0.773018
+vn -0.098010 0.000001 -0.995185
+vn -0.290272 -0.000001 -0.956944
+vn 0.000000 -1.000000 -0.000019
+vn -0.000002 -1.000000 0.000007
+vn 0.000001 -1.000000 -0.000000
+vn -0.000027 -1.000000 -0.000008
+vn -0.000009 -1.000000 -0.000001
+vn -0.000005 -1.000000 0.000000
+vn -0.000003 -1.000000 0.000001
+vn -0.000002 -1.000000 0.000001
+vn -0.000001 -1.000000 0.000001
+vn 0.000000 -1.000000 0.000001
+s off
+f 153/85/50 154/86/50 156/87/50
+f 155/85/51 156/86/51 158/87/51
+f 157/85/52 158/86/52 160/87/52
+f 159/85/53 160/86/53 162/87/53
+f 161/85/54 162/86/54 164/87/54
+f 163/85/55 164/86/55 166/87/55
+f 165/85/56 166/86/56 168/87/56
+f 167/85/57 168/86/57 170/87/57
+f 169/85/58 170/86/58 172/87/58
+f 171/85/59 172/86/59 174/87/59
+f 173/85/60 174/86/60 176/87/60
+f 175/85/61 176/86/61 178/87/61
+f 177/85/62 178/86/62 180/87/62
+f 179/85/63 180/86/63 182/87/63
+f 181/85/64 182/86/64 184/87/64
+f 183/85/65 184/86/65 186/87/65
+f 185/85/66 186/86/66 187/88/66
+f 187/85/67 188/86/67 189/88/67
+f 189/85/68 190/86/68 191/88/68
+f 191/85/69 192/86/69 193/88/69
+f 193/85/70 194/86/70 195/88/70
+f 195/85/71 196/86/71 197/88/71
+f 197/85/72 198/86/72 199/88/72
+f 199/85/73 200/86/73 201/88/73
+f 201/85/74 202/86/74 203/88/74
+f 203/85/75 204/86/75 205/88/75
+f 205/85/76 206/86/76 207/88/76
+f 207/85/77 208/86/77 209/88/77
+f 209/85/78 210/86/78 211/88/78
+f 211/85/79 212/86/79 213/88/79
+f 204/85/80 202/86/80 242/88/80
+f 215/85/81 216/86/81 153/88/81
+f 213/85/82 214/86/82 215/88/82
+f 193/85/83 195/86/83 269/88/83
+f 246/89/47 248/90/47 247/91/47
+f 180/85/84 178/86/84 229/87/84
+f 154/85/85 216/86/85 217/88/85
+f 194/85/86 192/86/86 237/88/86
+f 170/85/87 168/86/87 224/87/87
+f 208/85/88 206/86/88 244/88/88
+f 184/85/89 182/86/89 231/87/89
+f 160/85/90 158/86/90 219/87/90
+f 198/85/91 196/86/91 239/88/91
+f 174/85/92 172/86/92 226/87/92
+f 212/85/93 210/86/93 246/88/93
+f 188/85/94 186/86/94 234/88/94
+f 164/85/95 162/86/95 221/87/95
+f 202/85/96 200/86/96 241/88/96
+f 178/85/97 176/86/97 228/87/97
+f 216/85/98 214/86/98 248/88/98
+f 192/85/99 190/86/99 236/88/99
+f 168/85/100 166/86/100 223/87/100
+f 206/85/101 204/86/101 243/88/101
+f 182/85/102 180/86/102 230/87/102
+f 158/85/103 156/86/103 218/87/103
+f 196/85/104 194/86/104 238/88/104
+f 172/85/105 170/86/105 225/87/105
+f 210/85/106 208/86/106 245/88/106
+f 186/85/107 184/86/107 232/87/107
+f 162/85/108 160/86/108 220/87/108
+f 200/85/109 198/86/109 240/88/109
+f 156/85/110 154/86/110 217/87/110
+f 176/85/111 174/86/111 227/87/111
+f 214/85/112 212/86/112 247/88/112
+f 190/85/113 188/86/113 235/88/113
+f 166/85/114 164/86/114 222/87/114
+f 249/92/49 250/93/49 280/94/49
+f 169/85/115 171/86/115 258/87/115
+f 207/85/116 209/86/116 276/88/116
+f 183/85/117 185/86/117 265/87/117
+f 159/85/118 161/86/118 253/87/118
+f 197/85/119 199/86/119 271/88/119
+f 153/85/120 155/86/120 250/87/120
+f 173/85/121 175/86/121 260/87/121
+f 211/85/122 213/86/122 278/88/122
+f 187/85/123 189/86/123 266/88/123
+f 163/85/124 165/86/124 255/87/124
+f 201/85/125 203/86/125 273/88/125
+f 177/85/126 179/86/126 262/87/126
+f 215/85/127 153/86/127 280/88/127
+f 191/85/128 193/86/128 268/88/128
+f 167/85/129 169/86/129 257/87/129
+f 205/85/130 207/86/130 275/88/130
+f 181/85/131 183/86/131 264/87/131
+f 157/85/132 159/86/132 252/87/132
+f 195/85/133 197/86/133 270/88/133
+f 171/85/134 173/86/134 259/87/134
+f 209/85/135 211/86/135 277/88/135
+f 185/85/136 187/86/136 265/88/136
+f 161/85/137 163/86/137 254/87/137
+f 199/85/138 201/86/138 272/88/138
+f 175/85/139 177/86/139 261/87/139
+f 213/85/140 215/86/140 279/88/140
+f 189/85/141 191/86/141 267/88/141
+f 165/85/142 167/86/142 256/87/142
+f 203/85/143 205/86/143 274/88/143
+f 179/85/144 181/86/144 263/87/144
+f 155/85/145 157/86/145 251/87/145
+f 281/85/146 282/86/146 284/87/146
+f 283/85/147 284/86/147 286/87/147
+f 285/85/148 286/86/148 288/87/148
+f 287/85/149 288/86/149 290/87/149
+f 289/85/150 290/86/150 292/87/150
+f 291/85/151 292/86/151 294/87/151
+f 293/85/152 294/86/152 296/87/152
+f 295/85/153 296/86/153 298/87/153
+f 297/85/154 298/86/154 300/87/154
+f 299/85/155 300/86/155 302/87/155
+f 301/85/156 302/86/156 304/87/156
+f 303/85/157 304/86/157 306/87/157
+f 305/85/158 306/86/158 308/87/158
+f 307/85/159 308/86/159 310/87/159
+f 309/85/160 310/86/160 312/87/160
+f 311/85/161 312/86/161 314/87/161
+f 313/85/162 314/86/162 315/88/162
+f 315/85/163 316/86/163 317/88/163
+f 317/85/164 318/86/164 319/88/164
+f 319/85/165 320/86/165 321/88/165
+f 321/85/166 322/86/166 323/88/166
+f 323/85/167 324/86/167 325/88/167
+f 325/85/168 326/86/168 327/88/168
+f 327/85/169 328/86/169 329/88/169
+f 329/85/170 330/86/170 331/88/170
+f 331/85/171 332/86/171 333/88/171
+f 333/85/172 334/86/172 335/88/172
+f 335/85/173 336/86/173 337/88/173
+f 337/85/174 338/86/174 339/88/174
+f 339/85/175 340/86/175 341/88/175
+f 284/92/39 282/93/39 286/94/39
+f 343/85/176 344/86/176 281/88/176
+f 341/85/177 342/86/177 344/87/177
+f 281/92/42 283/93/42 343/94/42
+f 155/88/178 153/85/178 156/87/178
+f 157/88/51 155/85/51 158/87/51
+f 159/88/52 157/85/52 160/87/52
+f 161/88/53 159/85/53 162/87/53
+f 163/88/54 161/85/54 164/87/54
+f 165/88/179 163/85/179 166/87/179
+f 167/88/56 165/85/56 168/87/56
+f 169/88/57 167/85/57 170/87/57
+f 171/88/58 169/85/58 172/87/58
+f 173/88/59 171/85/59 174/87/59
+f 175/88/180 173/85/180 176/87/180
+f 177/88/61 175/85/61 178/87/61
+f 179/88/181 177/85/181 180/87/181
+f 181/88/182 179/85/182 182/87/182
+f 183/88/183 181/85/183 184/87/183
+f 185/88/65 183/85/65 186/87/65
+f 186/86/184 188/87/184 187/88/184
+f 188/86/67 190/87/67 189/88/67
+f 190/86/68 192/87/68 191/88/68
+f 192/86/69 194/87/69 193/88/69
+f 194/86/70 196/87/70 195/88/70
+f 196/86/71 198/87/71 197/88/71
+f 198/86/72 200/87/72 199/88/72
+f 200/86/73 202/87/73 201/88/73
+f 202/86/74 204/87/74 203/88/74
+f 204/86/75 206/87/75 205/88/75
+f 206/86/185 208/87/185 207/88/185
+f 208/86/77 210/87/77 209/88/77
+f 210/86/78 212/87/78 211/88/78
+f 212/86/79 214/87/79 213/88/79
+f 202/86/186 241/87/186 242/88/186
+f 216/86/81 154/87/81 153/88/81
+f 214/86/82 216/87/82 215/88/82
+f 195/86/187 270/87/187 269/88/187
+f 245/95/47 248/90/47 246/89/47
+f 244/96/47 248/90/47 245/95/47
+f 234/97/47 236/98/47 235/99/47
+f 234/97/47 237/100/47 236/98/47
+f 234/97/47 238/101/47 237/100/47
+f 229/102/47 231/103/47 230/104/47
+f 228/105/47 231/103/47 229/102/47
+f 219/94/47 221/106/47 220/107/47
+f 219/94/47 222/108/47 221/106/47
+f 218/92/47 217/93/47 219/94/47
+f 217/93/47 248/90/47 219/94/47
+f 248/90/47 244/96/47 219/94/47
+f 219/94/47 244/96/47 222/108/47
+f 244/96/188 243/109/188 222/108/188
+f 222/108/188 243/109/188 223/110/188
+f 223/110/47 243/109/47 224/111/47
+f 243/109/47 242/112/47 224/111/47
+f 242/112/47 241/113/47 224/111/47
+f 241/113/47 240/114/47 224/111/47
+f 224/111/47 240/114/47 225/115/47
+f 225/115/47 240/114/47 226/116/47
+f 240/114/47 239/117/47 226/116/47
+f 226/116/47 239/117/47 227/118/47
+f 239/117/188 238/101/188 227/118/188
+f 227/118/188 238/101/188 228/105/188
+f 228/105/47 238/101/47 231/103/47
+f 238/101/47 234/97/47 231/103/47
+f 234/97/47 233/119/47 232/120/47
+f 231/103/47 234/97/47 232/120/47
+f 230/88/189 180/85/189 229/87/189
+f 216/86/85 248/87/85 217/88/85
+f 192/86/86 236/87/86 237/88/86
+f 225/88/190 170/85/190 224/87/190
+f 206/86/191 243/87/191 244/88/191
+f 232/88/192 184/85/192 231/87/192
+f 220/88/193 160/85/193 219/87/193
+f 196/86/194 238/87/194 239/88/194
+f 227/88/195 174/85/195 226/87/195
+f 210/86/93 245/87/93 246/88/93
+f 186/86/196 233/87/196 234/88/196
+f 222/88/197 164/85/197 221/87/197
+f 200/86/198 240/87/198 241/88/198
+f 229/88/199 178/85/199 228/87/199
+f 214/86/98 247/87/98 248/88/98
+f 190/86/99 235/87/99 236/88/99
+f 224/88/200 168/85/200 223/87/200
+f 204/86/201 242/87/201 243/88/201
+f 231/88/102 182/85/102 230/87/102
+f 219/88/103 158/85/103 218/87/103
+f 194/86/202 237/87/202 238/88/202
+f 226/88/203 172/85/203 225/87/203
+f 208/86/204 244/87/204 245/88/204
+f 233/88/107 186/85/107 232/87/107
+f 221/88/108 162/85/108 220/87/108
+f 198/86/109 239/87/109 240/88/109
+f 218/88/205 156/85/205 217/87/205
+f 228/88/206 176/85/206 227/87/206
+f 212/86/207 246/87/207 247/88/207
+f 188/86/208 234/87/208 235/88/208
+f 223/88/209 166/85/209 222/87/209
+f 250/93/49 251/90/49 280/94/49
+f 251/90/49 252/91/49 280/94/49
+f 252/91/49 253/89/49 280/94/49
+f 253/89/49 254/95/49 280/94/49
+f 254/95/49 255/96/49 280/94/49
+f 255/96/49 256/109/49 280/94/49
+f 256/109/49 257/112/49 280/94/49
+f 257/112/49 258/113/49 280/94/49
+f 258/113/49 259/114/49 280/94/49
+f 259/114/49 260/117/49 280/94/49
+f 260/117/49 261/101/49 280/94/49
+f 261/101/49 262/100/49 280/94/49
+f 262/100/49 263/98/49 280/94/49
+f 263/98/49 264/99/49 280/94/49
+f 264/99/49 265/97/49 280/94/49
+f 265/97/49 266/119/49 280/94/49
+f 266/119/49 267/120/49 280/94/49
+f 267/120/49 268/103/49 280/94/49
+f 268/103/49 269/104/49 280/94/49
+f 269/104/49 270/102/49 280/94/49
+f 270/102/49 271/105/49 280/94/49
+f 271/105/49 272/118/49 280/94/49
+f 272/118/49 273/116/49 280/94/49
+f 273/116/49 274/115/49 280/94/49
+f 274/115/49 275/111/49 280/94/49
+f 275/111/49 276/110/49 280/94/49
+f 276/110/49 277/108/49 280/94/49
+f 277/108/49 278/106/49 279/107/49
+f 280/94/49 277/108/49 279/107/49
+f 257/88/115 169/85/115 258/87/115
+f 209/86/210 277/87/210 276/88/210
+f 264/88/117 183/85/117 265/87/117
+f 252/88/118 159/85/118 253/87/118
+f 199/86/211 272/87/211 271/88/211
+f 249/88/212 153/85/212 250/87/212
+f 259/88/213 173/85/213 260/87/213
+f 213/86/122 279/87/122 278/88/122
+f 189/86/123 267/87/123 266/88/123
+f 254/88/214 163/85/214 255/87/214
+f 203/86/215 274/87/215 273/88/215
+f 261/88/126 177/85/126 262/87/126
+f 153/86/127 249/87/127 280/88/127
+f 193/86/128 269/87/128 268/88/128
+f 256/88/216 167/85/216 257/87/216
+f 207/86/130 276/87/130 275/88/130
+f 263/88/217 181/85/217 264/87/217
+f 251/88/218 157/85/218 252/87/218
+f 197/86/219 271/87/219 270/88/219
+f 258/88/220 171/85/220 259/87/220
+f 211/86/221 278/87/221 277/88/221
+f 187/86/136 266/87/136 265/88/136
+f 253/88/222 161/85/222 254/87/222
+f 201/86/223 273/87/223 272/88/223
+f 260/88/139 175/85/139 261/87/139
+f 215/86/224 280/87/224 279/88/224
+f 191/86/141 268/87/141 267/88/141
+f 255/88/142 165/85/142 256/87/142
+f 205/86/225 275/87/225 274/88/225
+f 262/88/144 179/85/144 263/87/144
+f 250/88/226 155/85/226 251/87/226
+f 283/88/227 281/85/227 284/87/227
+f 285/88/228 283/85/228 286/87/228
+f 287/88/148 285/85/148 288/87/148
+f 289/88/229 287/85/229 290/87/229
+f 291/88/150 289/85/150 292/87/150
+f 293/88/151 291/85/151 294/87/151
+f 295/88/152 293/85/152 296/87/152
+f 297/88/153 295/85/153 298/87/153
+f 299/88/230 297/85/230 300/87/230
+f 301/88/231 299/85/231 302/87/231
+f 303/88/156 301/85/156 304/87/156
+f 305/88/157 303/85/157 306/87/157
+f 307/88/232 305/85/232 308/87/232
+f 309/88/233 307/85/233 310/87/233
+f 311/88/234 309/85/234 312/87/234
+f 313/88/235 311/85/235 314/87/235
+f 314/86/236 316/87/236 315/88/236
+f 316/86/237 318/87/237 317/88/237
+f 318/86/238 320/87/238 319/88/238
+f 320/86/239 322/87/239 321/88/239
+f 322/86/166 324/87/166 323/88/166
+f 324/86/167 326/87/167 325/88/167
+f 326/86/168 328/87/168 327/88/168
+f 328/86/169 330/87/169 329/88/169
+f 330/86/240 332/87/240 331/88/240
+f 332/86/171 334/87/171 333/88/171
+f 334/86/172 336/87/172 335/88/172
+f 336/86/241 338/87/241 337/88/241
+f 338/86/242 340/87/242 339/88/242
+f 340/86/175 342/87/175 341/88/175
+f 282/93/39 344/90/39 286/94/39
+f 344/90/39 342/91/39 286/94/39
+f 342/91/39 340/89/39 286/94/39
+f 340/89/39 338/95/39 286/94/39
+f 338/95/39 336/96/39 286/94/39
+f 336/96/39 334/109/39 286/94/39
+f 334/109/39 332/112/39 286/94/39
+f 332/112/39 330/113/39 286/94/39
+f 330/113/39 328/114/39 286/94/39
+f 328/114/39 326/117/39 286/94/39
+f 326/117/39 324/101/39 286/94/39
+f 324/101/39 322/100/39 286/94/39
+f 322/100/39 320/98/39 286/94/39
+f 320/98/39 318/99/39 286/94/39
+f 318/99/39 316/97/39 286/94/39
+f 316/97/39 314/119/39 286/94/39
+f 314/119/39 312/120/39 286/94/39
+f 312/120/39 310/103/39 286/94/39
+f 310/103/39 308/104/39 286/94/39
+f 308/104/39 306/102/39 286/94/39
+f 306/102/39 304/105/39 286/94/39
+f 304/105/39 302/118/39 286/94/39
+f 302/118/39 300/116/39 286/94/39
+f 300/116/39 298/115/39 286/94/39
+f 298/115/39 296/111/39 286/94/39
+f 296/111/39 294/110/39 286/94/39
+f 294/110/39 292/108/39 286/94/39
+f 292/108/39 290/106/39 286/94/39
+f 290/106/39 288/107/39 286/94/39
+f 344/86/243 282/87/243 281/88/243
+f 343/88/244 341/85/244 344/87/244
+f 283/93/245 285/90/245 343/94/245
+f 343/94/245 285/90/245 341/107/245
+f 285/90/42 287/91/42 341/107/42
+f 287/91/42 289/89/42 341/107/42
+f 289/89/42 291/95/42 341/107/42
+f 291/95/246 293/96/246 341/107/246
+f 293/96/247 295/109/247 341/107/247
+f 335/110/248 331/115/248 333/111/248
+f 295/109/247 297/112/247 341/107/247
+f 297/112/247 299/113/247 341/107/247
+f 335/110/249 329/116/249 331/115/249
+f 299/113/247 301/114/247 341/107/247
+f 335/110/250 327/118/250 329/116/250
+f 301/114/247 303/117/247 341/107/247
+f 335/110/251 325/105/251 327/118/251
+f 303/117/42 305/101/42 341/107/42
+f 335/110/252 323/102/252 325/105/252
+f 305/101/42 307/100/42 341/107/42
+f 335/110/253 321/104/253 323/102/253
+f 307/100/42 309/98/42 341/107/42
+f 335/110/253 319/103/253 321/104/253
+f 309/98/254 311/99/254 341/107/254
+f 335/110/254 317/120/254 319/103/254
+f 311/99/254 313/97/254 341/107/254
+f 315/119/254 317/120/254 335/110/254
+f 313/97/254 315/119/254 341/107/254
+f 315/119/254 335/110/254 341/107/254
+f 335/110/42 337/108/42 339/106/42
+f 341/107/42 335/110/42 339/106/42
+v 1.365790 0.015116 4.001092
+v 1.365790 0.015113 0.001092
+v 1.560881 0.034331 4.001092
+v 1.560881 0.034328 0.001092
+v 1.748474 0.091236 4.001092
+v 1.748474 0.091234 0.001092
+v 1.921361 0.183646 4.001092
+v 1.921361 0.183644 0.001092
+v 2.072897 0.308009 4.001092
+v 2.072897 0.308007 0.001092
+v 2.197260 0.459546 4.001092
+v 2.197260 0.459543 0.001092
+v 2.289670 0.632432 4.001092
+v 2.289670 0.632430 0.001092
+v 2.346576 0.820025 4.001092
+v 2.346576 0.820023 0.001092
+v 2.365790 1.015116 4.001092
+v 2.365790 1.015113 0.001092
+v 2.346576 1.210206 4.001091
+v 2.346576 1.210203 0.001091
+v 2.289670 1.397799 4.001091
+v 2.289670 1.397797 0.001091
+v 2.197260 1.570686 4.001091
+v 2.197260 1.570683 0.001091
+v 2.072897 1.722223 4.001091
+v 2.072897 1.722220 0.001091
+v 1.921361 1.846586 4.001091
+v 1.921361 1.846583 0.001091
+v 1.748474 1.938995 4.001091
+v 1.748474 1.938993 0.001091
+v 1.560881 1.995901 4.001091
+v 1.560881 1.995899 0.001091
+v 1.365790 2.015116 4.001091
+v 1.365790 2.015113 0.001091
+v 1.170700 1.995901 4.001091
+v 1.170700 1.995898 0.001091
+v 0.983107 1.938995 4.001091
+v 0.983107 1.938993 0.001091
+v 0.810220 1.846585 4.001091
+v 0.810220 1.846583 0.001091
+v 0.658683 1.722222 4.001091
+v 0.658683 1.722220 0.001091
+v 0.534320 1.570685 4.001091
+v 0.534320 1.570683 0.001091
+v 0.441911 1.397799 4.001091
+v 0.441911 1.397796 0.001091
+v 0.385005 1.210205 4.001091
+v 0.385005 1.210203 0.001091
+v 0.365790 1.015115 4.001092
+v 0.365790 1.015112 0.001092
+v 0.385005 0.820024 4.001092
+v 0.385005 0.820022 0.001092
+v 0.441911 0.632431 4.001092
+v 0.441911 0.632429 0.001092
+v 0.534322 0.459545 4.001092
+v 0.534322 0.459542 0.001092
+v 0.658685 0.308008 4.001092
+v 0.658685 0.308006 0.001092
+v 0.810221 0.183645 4.001092
+v 0.810221 0.183643 0.001092
+v 0.983108 0.091236 4.001092
+v 0.983108 0.091233 0.001092
+v 1.170702 0.034330 4.001092
+v 1.170702 0.034328 0.001092
+v 1.365791 0.649119 -0.167860
+v 1.437192 0.656151 -0.167860
+v 1.505850 0.676978 -0.167860
+v 1.569126 0.710800 -0.167860
+v 1.624587 0.756316 -0.167860
+v 1.670104 0.811778 -0.167860
+v 1.703925 0.875053 -0.167860
+v 1.724752 0.943711 -0.167860
+v 1.731785 1.015113 -0.167860
+v 1.724752 1.086515 -0.167860
+v 1.703925 1.155173 -0.167860
+v 1.670104 1.218448 -0.167861
+v 1.624587 1.273910 -0.167861
+v 1.569126 1.319426 -0.167861
+v 1.505850 1.353247 -0.167861
+v 1.437192 1.374075 -0.167861
+v 1.365790 1.381107 -0.167861
+v 1.294389 1.374075 -0.167861
+v 1.225731 1.353247 -0.167861
+v 1.162455 1.319426 -0.167861
+v 1.106993 1.273910 -0.167861
+v 1.061477 1.218448 -0.167861
+v 1.027656 1.155173 -0.167860
+v 1.006829 1.086515 -0.167860
+v 0.999797 1.015113 -0.167860
+v 1.006829 0.943711 -0.167860
+v 1.027656 0.875053 -0.167860
+v 1.061478 0.811777 -0.167860
+v 1.106994 0.756316 -0.167860
+v 1.162456 0.710800 -0.167860
+v 1.225731 0.676978 -0.167860
+v 1.294389 0.656151 -0.167860
+v 1.365791 0.605024 4.157856
+v 1.445795 0.612904 4.157856
+v 1.522726 0.636241 4.157856
+v 1.593625 0.674137 4.157856
+v 1.655769 0.725137 4.157856
+v 1.706769 0.787281 4.157856
+v 1.744666 0.858181 4.157855
+v 1.768002 0.935111 4.157855
+v 1.775882 1.015116 4.157855
+v 1.768002 1.095121 4.157855
+v 1.744666 1.172051 4.157855
+v 1.706769 1.242950 4.157855
+v 1.655769 1.305094 4.157855
+v 1.593625 1.356094 4.157855
+v 1.522726 1.393991 4.157855
+v 1.445795 1.417328 4.157855
+v 1.365790 1.425207 4.157855
+v 1.285786 1.417327 4.157855
+v 1.208855 1.393991 4.157855
+v 1.137956 1.356094 4.157855
+v 1.075812 1.305094 4.157855
+v 1.024812 1.242950 4.157855
+v 0.986915 1.172051 4.157855
+v 0.963579 1.095120 4.157855
+v 0.955699 1.015115 4.157855
+v 0.963579 0.935110 4.157855
+v 0.986916 0.858180 4.157855
+v 1.024812 0.787281 4.157856
+v 1.075812 0.725137 4.157856
+v 1.137956 0.674137 4.157856
+v 1.208856 0.636240 4.157856
+v 1.285786 0.612904 4.157856
+vt 0.000000 0.000000
+vt 0.450363 0.000088
+vt 0.519265 0.025135
+vt 0.412277 0.001462
+vt 0.592585 0.072108
+vt 0.660664 0.134431
+vt 0.714751 0.214466
+vt 0.755741 0.304410
+vt 0.778531 0.401983
+vt 0.781786 0.502742
+vt 0.147188 0.142771
+vt 0.050179 0.306373
+vt 0.093406 0.218681
+vt 0.765337 0.602148
+vt 0.019781 0.402090
+vt 0.729413 0.696327
+vt 0.000089 0.500905
+vt 0.680707 0.782826
+vt 0.000088 0.601389
+vt 0.627360 0.858268
+vt 0.022810 0.698277
+vt 0.566443 0.918999
+vt 0.063602 0.787572
+vt 0.501474 0.962949
+vt 0.119962 0.865210
+vt 0.436606 0.989270
+vt 0.188187 0.927714
+vt 0.377255 0.998855
+vt 0.263175 0.972402
+vt 0.336538 0.999912
+vt 0.208578 0.081678
+vt 0.280064 0.037078
+vt 0.344761 0.010798
+vn 0.634394 -0.773010 0.000000
+vn 0.773010 -0.634394 0.000000
+vn 0.773010 0.634394 -0.000000
+vn 0.634394 0.773010 -0.000000
+vn -0.956941 0.290284 -0.000000
+vn -0.995185 0.098016 -0.000000
+vn -0.881921 -0.471398 0.000000
+vn -0.634392 -0.773011 0.000000
+vn -0.075085 -0.247523 -0.965968
+vn -0.199427 0.163668 0.966148
+vn -0.121931 0.228117 -0.965969
+vn 0.247522 -0.075086 -0.965968
+vn -0.247522 -0.075086 -0.965968
+vn 0.121931 0.228117 -0.965969
+vn 0.075085 -0.247523 -0.965968
+vn -0.199948 0.164091 -0.965968
+vn 0.257414 0.025352 -0.965968
+vn -0.199947 -0.164093 -0.965968
+vn 0.025353 0.257414 -0.965969
+vn 0.164092 -0.199947 -0.965968
+vn -0.247522 0.075085 -0.965969
+vn 0.025353 -0.257415 -0.965968
+vn 0.228118 0.121931 -0.965969
+vn -0.121931 -0.228119 -0.965968
+vn -0.075086 0.247521 -0.965969
+vn 0.228118 -0.121932 -0.965968
+vn -0.257414 -0.025354 -0.965968
+vn 0.164092 0.199946 -0.965969
+vn -0.025353 -0.257415 -0.965968
+vn -0.164092 0.199946 -0.965969
+vn 0.257415 -0.025354 -0.965968
+vn -0.228117 -0.121932 -0.965968
+vn 0.075086 0.247521 -0.965969
+vn 0.121931 -0.228119 -0.965968
+vn -0.228117 0.121931 -0.965968
+vn 0.247522 0.075085 -0.965969
+vn -0.164092 -0.199947 -0.965968
+vn -0.025353 0.257414 -0.965969
+vn 0.199947 -0.164093 -0.965968
+vn -0.257414 0.025352 -0.965968
+vn 0.199947 0.164091 -0.965968
+vn 0.256746 0.025290 0.966148
+vn -0.199428 -0.163666 0.966148
+vn 0.025287 0.256747 0.966148
+vn 0.163666 -0.199428 0.966148
+vn -0.246880 0.074890 0.966148
+vn 0.025287 -0.256746 0.966148
+vn 0.227526 0.121615 0.966148
+vn -0.121615 -0.227526 0.966148
+vn -0.074890 0.246880 0.966148
+vn 0.227525 -0.121615 0.966148
+vn -0.256745 -0.025287 0.966148
+vn 0.163667 0.199428 0.966148
+vn -0.025287 -0.256746 0.966148
+vn -0.163667 0.199428 0.966148
+vn 0.256746 -0.025287 0.966148
+vn -0.227525 -0.121615 0.966148
+vn 0.074890 0.246880 0.966148
+vn 0.121615 -0.227526 0.966148
+vn -0.227526 0.121615 0.966148
+vn 0.246880 0.074890 0.966148
+vn -0.163666 -0.199428 0.966148
+vn -0.025287 0.256747 0.966148
+vn 0.199428 -0.163666 0.966148
+vn -0.256746 0.025289 0.966148
+vn 0.199427 0.163668 0.966148
+vn -0.074892 -0.246879 0.966148
+vn -0.121615 0.227526 0.966148
+vn 0.246879 -0.074890 0.966148
+vn -0.246879 -0.074890 0.966148
+vn 0.121615 0.227526 0.966148
+vn 0.074892 -0.246879 0.966148
+vn -0.199428 0.163666 0.966148
+vn 0.000000 -0.000004 -1.000000
+vn 0.247522 -0.075085 -0.965968
+vn -0.199947 0.164092 -0.965969
+vn 0.257414 0.025353 -0.965969
+vn -0.199947 -0.164092 -0.965968
+vn 0.228120 0.121929 -0.965968
+vn -0.121931 -0.228118 -0.965968
+vn -0.075085 0.247521 -0.965969
+vn 0.228117 -0.121935 -0.965968
+vn -0.257414 -0.025353 -0.965968
+vn 0.164092 0.199947 -0.965969
+vn 0.257415 -0.025353 -0.965968
+vn -0.228117 -0.121935 -0.965968
+vn 0.075085 0.247521 -0.965969
+vn 0.121932 -0.228118 -0.965968
+vn -0.228120 0.121928 -0.965968
+vn 0.199947 -0.164092 -0.965968
+vn -0.257414 0.025353 -0.965969
+vn 0.199946 0.164092 -0.965969
+vn -0.000004 0.000009 1.000000
+vn 0.000001 -0.000000 1.000000
+vn -0.000029 -0.000009 1.000000
+vn -0.000010 -0.000001 1.000000
+vn -0.000005 0.000001 1.000000
+vn -0.000003 0.000001 1.000000
+vn -0.000002 0.000001 1.000000
+vn -0.000001 0.000001 1.000000
+vn 0.000000 0.000001 1.000000
+vn 0.256746 0.025287 0.966148
+vn -0.074891 0.246880 0.966148
+vn 0.227529 -0.121610 0.966148
+vn -0.256746 -0.025287 0.966148
+vn -0.227529 -0.121610 0.966148
+vn -0.256746 0.025287 0.966148
+vn 0.199428 0.163666 0.966148
+vn -0.074889 -0.246879 0.966148
+vn 0.074890 -0.246879 0.966148
+s off
+f 345/121/50 346/121/50 348/121/50
+f 347/121/51 348/121/51 350/121/51
+f 349/121/52 350/121/52 352/121/52
+f 351/121/255 352/121/255 354/121/255
+f 353/121/256 354/121/256 356/121/256
+f 355/121/55 356/121/55 358/121/55
+f 357/121/56 358/121/56 360/121/56
+f 359/121/57 360/121/57 362/121/57
+f 361/121/58 362/121/58 364/121/58
+f 363/121/59 364/121/59 366/121/59
+f 365/121/60 366/121/60 368/121/60
+f 367/121/257 368/121/257 370/121/257
+f 369/121/258 370/121/258 372/121/258
+f 371/121/63 372/121/63 374/121/63
+f 373/121/64 374/121/64 376/121/64
+f 375/121/65 376/121/65 378/121/65
+f 377/121/66 378/121/66 379/121/66
+f 379/121/67 380/121/67 381/121/67
+f 381/121/68 382/121/68 383/121/68
+f 383/121/69 384/121/69 385/121/69
+f 385/121/70 386/121/70 387/121/70
+f 387/121/71 388/121/71 389/121/71
+f 389/121/259 390/121/259 391/121/259
+f 391/121/260 392/121/260 393/121/260
+f 393/121/74 394/121/74 395/121/74
+f 395/121/75 396/121/75 397/121/75
+f 397/121/261 398/121/261 399/121/261
+f 399/121/77 400/121/77 401/121/77
+f 401/121/262 402/121/262 403/121/262
+f 403/121/79 404/121/79 405/121/79
+f 408/121/263 406/121/263 440/121/263
+f 407/121/81 408/121/81 345/121/81
+f 405/121/82 406/121/82 407/121/82
+f 385/121/264 387/121/264 461/121/264
+f 437/121/47 439/121/47 438/121/47
+f 384/121/265 382/121/265 428/121/265
+f 360/121/266 358/121/266 415/121/266
+f 398/121/267 396/121/267 435/121/267
+f 374/121/268 372/121/268 422/121/268
+f 350/121/269 348/121/269 410/121/269
+f 388/121/270 386/121/270 430/121/270
+f 364/121/271 362/121/271 417/121/271
+f 402/121/272 400/121/272 437/121/272
+f 378/121/273 376/121/273 424/121/273
+f 354/121/274 352/121/274 412/121/274
+f 392/121/275 390/121/275 432/121/275
+f 348/121/276 346/121/276 409/121/276
+f 368/121/277 366/121/277 419/121/277
+f 406/121/278 404/121/278 439/121/278
+f 382/121/279 380/121/279 427/121/279
+f 358/121/280 356/121/280 414/121/280
+f 396/121/281 394/121/281 434/121/281
+f 372/121/282 370/121/282 421/121/282
+f 346/121/283 408/121/283 409/121/283
+f 386/121/284 384/121/284 429/121/284
+f 362/121/285 360/121/285 416/121/285
+f 400/121/286 398/121/286 436/121/286
+f 376/121/287 374/121/287 423/121/287
+f 352/121/288 350/121/288 411/121/288
+f 390/121/289 388/121/289 431/121/289
+f 366/121/290 364/121/290 418/121/290
+f 404/121/291 402/121/291 438/121/291
+f 380/121/292 378/121/292 426/121/292
+f 356/121/293 354/121/293 413/121/293
+f 394/121/294 392/121/294 433/121/294
+f 370/121/295 368/121/295 420/121/295
+f 441/122/49 442/123/49 472/124/49
+f 361/121/296 363/121/296 450/121/296
+f 399/121/297 401/121/297 468/121/297
+f 375/121/298 377/121/298 457/121/298
+f 351/121/299 353/121/299 445/121/299
+f 389/121/300 391/121/300 463/121/300
+f 345/121/301 347/121/301 442/121/301
+f 365/121/302 367/121/302 452/121/302
+f 403/121/303 405/121/303 470/121/303
+f 379/121/304 381/121/304 458/121/304
+f 355/121/305 357/121/305 447/121/305
+f 393/121/306 395/121/306 465/121/306
+f 369/121/307 371/121/307 454/121/307
+f 407/121/308 345/121/308 472/121/308
+f 383/121/309 385/121/309 460/121/309
+f 359/121/310 361/121/310 449/121/310
+f 397/121/311 399/121/311 467/121/311
+f 373/121/312 375/121/312 456/121/312
+f 349/121/313 351/121/313 444/121/313
+f 387/121/314 389/121/314 462/121/314
+f 363/121/315 365/121/315 451/121/315
+f 401/121/316 403/121/316 469/121/316
+f 377/121/317 379/121/317 457/121/317
+f 353/121/318 355/121/318 446/121/318
+f 391/121/319 393/121/319 464/121/319
+f 367/121/320 369/121/320 453/121/320
+f 405/121/321 407/121/321 471/121/321
+f 381/121/322 383/121/322 459/121/322
+f 357/121/323 359/121/323 448/121/323
+f 395/121/324 397/121/324 466/121/324
+f 371/121/325 373/121/325 455/121/325
+f 347/121/326 349/121/326 443/121/326
+f 347/121/178 345/121/178 348/121/178
+f 349/121/51 347/121/51 350/121/51
+f 351/121/52 349/121/52 352/121/52
+f 353/121/255 351/121/255 354/121/255
+f 355/121/256 353/121/256 356/121/256
+f 357/121/179 355/121/179 358/121/179
+f 359/121/56 357/121/56 360/121/56
+f 361/121/57 359/121/57 362/121/57
+f 363/121/58 361/121/58 364/121/58
+f 365/121/59 363/121/59 366/121/59
+f 367/121/60 365/121/60 368/121/60
+f 369/121/257 367/121/257 370/121/257
+f 371/121/181 369/121/181 372/121/181
+f 373/121/182 371/121/182 374/121/182
+f 375/121/183 373/121/183 376/121/183
+f 377/121/65 375/121/65 378/121/65
+f 378/121/184 380/121/184 379/121/184
+f 380/121/67 382/121/67 381/121/67
+f 382/121/68 384/121/68 383/121/68
+f 384/121/69 386/121/69 385/121/69
+f 386/121/70 388/121/70 387/121/70
+f 388/121/71 390/121/71 389/121/71
+f 390/121/259 392/121/259 391/121/259
+f 392/121/260 394/121/260 393/121/260
+f 394/121/74 396/121/74 395/121/74
+f 396/121/75 398/121/75 397/121/75
+f 398/121/261 400/121/261 399/121/261
+f 400/121/77 402/121/77 401/121/77
+f 402/121/262 404/121/262 403/121/262
+f 404/121/79 406/121/79 405/121/79
+f 406/121/263 439/121/263 440/121/263
+f 408/121/81 346/121/81 345/121/81
+f 406/121/82 408/121/82 407/121/82
+f 387/121/327 462/121/327 461/121/327
+f 436/121/47 439/121/47 437/121/47
+f 427/121/47 429/121/47 428/121/47
+f 427/121/47 430/121/47 429/121/47
+f 421/121/47 423/121/47 422/121/47
+f 420/121/47 423/121/47 421/121/47
+f 411/121/47 413/121/47 412/121/47
+f 411/121/47 414/121/47 413/121/47
+f 410/121/47 409/121/47 411/121/47
+f 409/121/47 440/121/47 411/121/47
+f 440/121/47 439/121/47 411/121/47
+f 439/121/47 436/121/47 411/121/47
+f 411/121/47 436/121/47 414/121/47
+f 436/121/328 435/121/328 414/121/328
+f 414/121/328 435/121/328 415/121/328
+f 435/121/47 434/121/47 415/121/47
+f 434/121/47 433/121/47 415/121/47
+f 433/121/47 432/121/47 415/121/47
+f 432/121/47 431/121/47 415/121/47
+f 415/121/47 431/121/47 416/121/47
+f 416/121/47 431/121/47 417/121/47
+f 417/121/47 431/121/47 418/121/47
+f 418/121/47 431/121/47 419/121/47
+f 431/121/328 430/121/328 419/121/328
+f 419/121/328 430/121/328 420/121/328
+f 430/121/47 427/121/47 420/121/47
+f 420/121/47 427/121/47 423/121/47
+f 427/121/47 426/121/47 423/121/47
+f 426/121/47 425/121/47 424/121/47
+f 423/121/47 426/121/47 424/121/47
+f 382/121/265 427/121/265 428/121/265
+f 416/121/329 360/121/329 415/121/329
+f 396/121/267 434/121/267 435/121/267
+f 423/121/268 374/121/268 422/121/268
+f 411/121/269 350/121/269 410/121/269
+f 386/121/330 429/121/330 430/121/330
+f 418/121/331 364/121/331 417/121/331
+f 400/121/332 436/121/332 437/121/332
+f 425/121/273 378/121/273 424/121/273
+f 413/121/274 354/121/274 412/121/274
+f 390/121/275 431/121/275 432/121/275
+f 410/121/276 348/121/276 409/121/276
+f 420/121/333 368/121/333 419/121/333
+f 404/121/334 438/121/334 439/121/334
+f 380/121/335 426/121/335 427/121/335
+f 415/121/336 358/121/336 414/121/336
+f 394/121/337 433/121/337 434/121/337
+f 422/121/338 372/121/338 421/121/338
+f 408/121/283 440/121/283 409/121/283
+f 384/121/284 428/121/284 429/121/284
+f 417/121/339 362/121/339 416/121/339
+f 398/121/340 435/121/340 436/121/340
+f 424/121/341 376/121/341 423/121/341
+f 412/121/342 352/121/342 411/121/342
+f 388/121/343 430/121/343 431/121/343
+f 419/121/290 366/121/290 418/121/290
+f 402/121/291 437/121/291 438/121/291
+f 378/121/292 425/121/292 426/121/292
+f 414/121/344 356/121/344 413/121/344
+f 392/121/345 432/121/345 433/121/345
+f 421/121/346 370/121/346 420/121/346
+f 442/123/49 443/125/49 472/124/49
+f 443/125/49 444/126/49 472/124/49
+f 444/126/49 445/127/49 472/124/49
+f 445/127/49 446/128/49 472/124/49
+f 446/128/347 447/129/347 472/124/347
+f 447/129/348 448/130/348 472/124/348
+f 468/131/349 466/132/349 467/133/349
+f 448/130/348 449/134/348 472/124/348
+f 468/131/350 465/135/350 466/132/350
+f 449/134/348 450/136/348 472/124/348
+f 468/131/351 464/137/351 465/135/351
+f 450/136/348 451/138/348 472/124/348
+f 468/131/352 463/139/352 464/137/352
+f 451/138/348 452/140/348 472/124/348
+f 468/131/353 462/141/353 463/139/353
+f 452/140/348 453/142/348 472/124/348
+f 468/131/354 461/143/354 462/141/354
+f 453/142/49 454/144/49 472/124/49
+f 468/131/354 460/145/354 461/143/354
+f 454/144/355 455/146/355 472/124/355
+f 468/131/354 459/147/354 460/145/354
+f 455/146/355 456/148/355 472/124/355
+f 468/131/355 458/149/355 459/147/355
+f 456/148/355 457/150/355 472/124/355
+f 457/150/355 458/149/355 472/124/355
+f 458/149/355 468/131/355 472/124/355
+f 468/131/49 469/151/49 472/124/49
+f 469/151/49 470/152/49 471/153/49
+f 472/124/49 469/151/49 471/153/49
+f 449/121/356 361/121/356 450/121/356
+f 401/121/297 469/121/297 468/121/297
+f 456/121/298 375/121/298 457/121/298
+f 444/121/299 351/121/299 445/121/299
+f 391/121/300 464/121/300 463/121/300
+f 441/121/301 345/121/301 442/121/301
+f 451/121/302 365/121/302 452/121/302
+f 405/121/303 471/121/303 470/121/303
+f 381/121/357 459/121/357 458/121/357
+f 446/121/358 355/121/358 447/121/358
+f 395/121/359 466/121/359 465/121/359
+f 453/121/307 369/121/307 454/121/307
+f 345/121/308 441/121/308 472/121/308
+f 385/121/309 461/121/309 460/121/309
+f 448/121/310 359/121/310 449/121/310
+f 399/121/360 468/121/360 467/121/360
+f 455/121/312 373/121/312 456/121/312
+f 443/121/313 349/121/313 444/121/313
+f 389/121/314 463/121/314 462/121/314
+f 450/121/315 363/121/315 451/121/315
+f 403/121/316 470/121/316 469/121/316
+f 379/121/317 458/121/317 457/121/317
+f 445/121/318 353/121/318 446/121/318
+f 393/121/361 465/121/361 464/121/361
+f 452/121/362 367/121/362 453/121/362
+f 407/121/363 472/121/363 471/121/363
+f 383/121/322 460/121/322 459/121/322
+f 447/121/323 357/121/323 448/121/323
+f 397/121/324 467/121/324 466/121/324
+f 454/121/325 371/121/325 455/121/325
+f 442/121/364 347/121/364 443/121/364
+v 0.000000 0.010026 -4.301087
+v 0.000000 4.010026 -4.301087
+v 0.195090 0.010026 -4.281873
+v 0.195090 4.010026 -4.281873
+v 0.382683 0.010026 -4.224967
+v 0.382683 4.010026 -4.224967
+v 0.555570 0.010026 -4.132557
+v 0.555570 4.010026 -4.132557
+v 0.707107 0.010026 -4.008194
+v 0.707107 4.010026 -4.008194
+v 0.831470 0.010026 -3.856658
+v 0.831470 4.010026 -3.856658
+v 0.923880 0.010026 -3.683771
+v 0.923880 4.010026 -3.683771
+v 0.980785 0.010026 -3.496178
+v 0.980785 4.010026 -3.496178
+v 1.000000 0.010026 -3.301088
+v 1.000000 4.010026 -3.301088
+v 0.980785 0.010026 -3.105997
+v 0.980785 4.010026 -3.105997
+v 0.923880 0.010026 -2.918404
+v 0.923880 4.010026 -2.918404
+v 0.831470 0.010026 -2.745517
+v 0.831470 4.010026 -2.745517
+v 0.707107 0.010026 -2.593981
+v 0.707107 4.010026 -2.593981
+v 0.555570 0.010026 -2.469618
+v 0.555570 4.010026 -2.469618
+v 0.382683 0.010026 -2.377208
+v 0.382683 4.010026 -2.377208
+v 0.195090 0.010026 -2.320302
+v 0.195090 4.010026 -2.320302
+v -0.000000 0.010026 -2.301088
+v -0.000000 4.010026 -2.301088
+v -0.195091 0.010026 -2.320302
+v -0.195091 4.010026 -2.320302
+v -0.382684 0.010026 -2.377208
+v -0.382684 4.010026 -2.377208
+v -0.555571 0.010026 -2.469618
+v -0.555571 4.010026 -2.469618
+v -0.707107 0.010026 -2.593981
+v -0.707107 4.010026 -2.593981
+v -0.831470 0.010026 -2.745518
+v -0.831470 4.010026 -2.745518
+v -0.923880 0.010026 -2.918405
+v -0.923880 4.010026 -2.918405
+v -0.980785 0.010026 -3.105998
+v -0.980785 4.010026 -3.105998
+v -1.000000 0.010026 -3.301089
+v -1.000000 4.010026 -3.301089
+v -0.980785 0.010026 -3.496179
+v -0.980785 4.010026 -3.496179
+v -0.923879 0.010026 -3.683772
+v -0.923879 4.010026 -3.683772
+v -0.831469 0.010026 -3.856659
+v -0.831469 4.010026 -3.856659
+v -0.707106 0.010026 -4.008195
+v -0.707106 4.010026 -4.008195
+v -0.555569 0.010026 -4.132558
+v -0.555569 4.010026 -4.132558
+v -0.382682 0.010026 -4.224968
+v -0.382682 4.010026 -4.224968
+v -0.195089 0.010026 -4.281873
+v -0.195089 4.010026 -4.281873
+v 0.000000 4.270857 -3.619825
+v 0.062183 4.270857 -3.613700
+v 0.121975 4.270857 -3.595562
+v 0.177081 4.270857 -3.566108
+v 0.225381 4.270857 -3.526469
+v 0.265020 4.270857 -3.478168
+v 0.294475 4.270857 -3.423063
+v 0.312612 4.270857 -3.363270
+v 0.318737 4.270857 -3.301088
+v 0.312613 4.270857 -3.238905
+v 0.294475 4.270857 -3.179112
+v 0.265020 4.270857 -3.124007
+v 0.225381 4.270857 -3.075707
+v 0.177081 4.270857 -3.036068
+v 0.121975 4.270857 -3.006613
+v 0.062182 4.270857 -2.988475
+v -0.000000 4.270857 -2.982351
+v -0.062183 4.270857 -2.988475
+v -0.121975 4.270857 -3.006613
+v -0.177081 4.270857 -3.036068
+v -0.225381 4.270857 -3.075707
+v -0.265020 4.270857 -3.124007
+v -0.294475 4.270857 -3.179113
+v -0.312613 4.270857 -3.238906
+v -0.318737 4.270857 -3.301088
+v -0.312612 4.270857 -3.363271
+v -0.294474 4.270857 -3.423064
+v -0.265020 4.270857 -3.478169
+v -0.225381 4.270857 -3.526469
+v -0.177080 4.270857 -3.566108
+v -0.121975 4.270857 -3.595562
+v -0.062182 4.270857 -3.613700
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.098017 0.000000 -0.995185
+vn 0.290285 0.000000 -0.956940
+vn 0.471396 0.000000 -0.881922
+vn 0.634394 0.000000 -0.773010
+vn 0.773011 0.000000 -0.634393
+vn 0.881921 0.000000 -0.471397
+vn 0.956940 0.000000 -0.290285
+vn 0.995185 0.000000 -0.098017
+vn 0.995185 0.000000 0.098017
+vn 0.881921 0.000000 0.471397
+vn 0.773011 0.000000 0.634393
+vn 0.634394 0.000000 0.773010
+vn 0.471396 0.000000 0.881921
+vn 0.290285 0.000000 0.956940
+vn 0.098017 0.000000 0.995185
+vn -0.098018 0.000000 0.995185
+vn -0.290285 0.000000 0.956940
+vn -0.471397 0.000000 0.881921
+vn -0.634394 0.000000 0.773010
+vn -0.773011 0.000000 0.634393
+vn -0.881922 0.000000 0.471396
+vn -0.956941 0.000000 0.290284
+vn -0.995185 0.000000 0.098016
+vn -0.995185 -0.000000 -0.098018
+vn -0.956940 -0.000000 -0.290286
+vn -0.881921 -0.000000 -0.471398
+vn -0.773010 -0.000000 -0.634394
+vn -0.634392 -0.000000 -0.773012
+vn -0.471397 -0.000000 -0.881921
+vn -0.357332 0.933314 -0.035195
+vn -0.098015 -0.000000 -0.995185
+vn -0.290282 -0.000000 -0.956941
+vn 0.227786 0.933314 0.277558
+vn -0.035193 0.933314 -0.357332
+vn -0.227786 0.933314 0.277558
+vn 0.357332 0.933314 -0.035194
+vn -0.316663 0.933314 -0.169261
+vn 0.104230 0.933314 0.343600
+vn 0.169260 0.933314 -0.316664
+vn -0.316664 0.933314 0.169260
+vn 0.343600 0.933314 0.104230
+vn -0.227785 0.933314 -0.277559
+vn -0.035194 0.933314 0.357332
+vn 0.277558 0.933314 -0.227786
+vn -0.357332 0.933314 0.035194
+vn 0.277558 0.933314 0.227786
+vn -0.104229 0.933314 -0.343600
+vn -0.169260 0.933314 0.316663
+vn 0.343600 0.933314 -0.104230
+vn -0.343600 0.933314 -0.104230
+vn 0.169260 0.933314 0.316664
+vn 0.104230 0.933314 -0.343600
+vn -0.277558 0.933314 0.227786
+vn 0.357332 0.933314 0.035194
+vn -0.277558 0.933314 -0.227786
+vn 0.035194 0.933314 0.357332
+vn 0.227786 0.933314 -0.277558
+vn -0.343600 0.933314 0.104230
+vn 0.035194 0.933314 -0.357332
+vn 0.316664 0.933314 0.169260
+vn -0.169260 0.933314 -0.316664
+vn -0.104230 0.933314 0.343600
+vn 0.316664 0.933314 -0.169260
+vn -0.357332 0.933314 -0.035194
+vn -0.035195 0.933314 -0.357332
+vn -0.316664 0.933314 -0.169260
+vn 0.169261 0.933314 -0.316663
+vn -0.227786 0.933314 -0.277558
+vn -0.357332 0.933314 0.035193
+vn -0.104230 0.933314 -0.343600
+vn -0.169261 0.933314 0.316663
+vn 0.035195 0.933314 -0.357332
+vn -0.169259 0.933314 -0.316664
+s off
+f 473/154/365 474/155/365 476/156/365
+f 475/154/366 476/155/366 478/156/366
+f 477/154/367 478/155/367 480/156/367
+f 479/154/368 480/155/368 482/156/368
+f 481/154/369 482/155/369 484/156/369
+f 483/154/370 484/155/370 486/156/370
+f 485/154/371 486/155/371 488/156/371
+f 487/154/372 488/155/372 490/156/372
+f 489/154/373 490/155/373 492/156/373
+f 491/154/155 492/155/155 494/156/155
+f 493/154/374 494/155/374 496/156/374
+f 495/154/375 496/155/375 498/156/375
+f 497/154/376 498/155/376 500/156/376
+f 499/154/377 500/155/377 502/156/377
+f 501/154/378 502/155/378 504/156/378
+f 503/154/379 504/155/379 506/156/379
+f 505/154/380 506/155/380 507/157/380
+f 507/154/381 508/155/381 509/157/381
+f 509/154/382 510/155/382 511/157/382
+f 511/154/383 512/155/383 513/157/383
+f 513/154/384 514/155/384 515/157/384
+f 515/154/385 516/155/385 517/157/385
+f 517/154/386 518/155/386 519/157/386
+f 519/154/387 520/155/387 521/157/387
+f 521/154/388 522/155/388 523/157/388
+f 523/154/389 524/155/389 525/157/389
+f 525/154/390 526/155/390 527/157/390
+f 527/154/391 528/155/391 529/157/391
+f 529/154/392 530/155/392 531/157/392
+f 531/154/393 532/155/393 533/157/393
+f 524/154/394 522/155/394 562/157/394
+f 535/154/395 536/155/395 473/157/395
+f 533/154/396 534/155/396 535/157/396
+f 473/158/42 475/159/42 535/160/42
+f 538/158/39 537/159/39 539/160/39
+f 500/154/397 498/155/397 549/156/397
+f 474/154/398 536/155/398 537/157/398
+f 514/154/399 512/155/399 557/157/399
+f 490/154/400 488/155/400 544/156/400
+f 528/154/401 526/155/401 564/157/401
+f 504/154/402 502/155/402 551/156/402
+f 480/154/403 478/155/403 539/156/403
+f 518/154/404 516/155/404 559/157/404
+f 494/154/405 492/155/405 546/156/405
+f 532/154/406 530/155/406 566/157/406
+f 508/154/407 506/155/407 554/157/407
+f 484/154/408 482/155/408 541/156/408
+f 522/154/409 520/155/409 561/157/409
+f 498/154/410 496/155/410 548/156/410
+f 536/154/411 534/155/411 568/157/411
+f 512/154/412 510/155/412 556/157/412
+f 488/154/413 486/155/413 543/156/413
+f 526/154/414 524/155/414 563/157/414
+f 502/154/415 500/155/415 550/156/415
+f 478/154/416 476/155/416 538/156/416
+f 516/154/417 514/155/417 558/157/417
+f 492/154/418 490/155/418 545/156/418
+f 530/154/419 528/155/419 565/157/419
+f 506/154/420 504/155/420 552/156/420
+f 482/154/421 480/155/421 540/156/421
+f 520/154/422 518/155/422 560/157/422
+f 476/154/423 474/155/423 537/156/423
+f 496/154/424 494/155/424 547/156/424
+f 534/154/425 532/155/425 567/157/425
+f 510/154/426 508/155/426 555/157/426
+f 486/154/427 484/155/427 542/156/427
+f 475/157/365 473/154/365 476/156/365
+f 477/157/366 475/154/366 478/156/366
+f 479/157/367 477/154/367 480/156/367
+f 481/157/368 479/154/368 482/156/368
+f 483/157/369 481/154/369 484/156/369
+f 485/157/370 483/154/370 486/156/370
+f 487/157/371 485/154/371 488/156/371
+f 489/157/372 487/154/372 490/156/372
+f 491/157/373 489/154/373 492/156/373
+f 493/157/155 491/154/155 494/156/155
+f 495/157/374 493/154/374 496/156/374
+f 497/157/375 495/154/375 498/156/375
+f 499/157/376 497/154/376 500/156/376
+f 501/157/377 499/154/377 502/156/377
+f 503/157/378 501/154/378 504/156/378
+f 505/157/379 503/154/379 506/156/379
+f 506/155/380 508/156/380 507/157/380
+f 508/155/381 510/156/381 509/157/381
+f 510/155/382 512/156/382 511/157/382
+f 512/155/383 514/156/383 513/157/383
+f 514/155/384 516/156/384 515/157/384
+f 516/155/385 518/156/385 517/157/385
+f 518/155/386 520/156/386 519/157/386
+f 520/155/387 522/156/387 521/157/387
+f 522/155/388 524/156/388 523/157/388
+f 524/155/389 526/156/389 525/157/389
+f 526/155/390 528/156/390 527/157/390
+f 528/155/391 530/156/391 529/157/391
+f 530/155/392 532/156/392 531/157/392
+f 532/155/393 534/156/393 533/157/393
+f 522/155/428 561/156/428 562/157/428
+f 536/155/395 474/156/395 473/157/395
+f 534/155/396 536/156/396 535/157/396
+f 475/159/42 477/161/42 535/160/42
+f 477/161/42 479/162/42 535/160/42
+f 479/162/42 481/163/42 535/160/42
+f 481/163/42 483/164/42 535/160/42
+f 483/164/42 485/165/42 535/160/42
+f 485/165/42 487/166/42 535/160/42
+f 487/166/42 489/167/42 535/160/42
+f 489/167/42 491/168/42 535/160/42
+f 491/168/42 493/169/42 535/160/42
+f 493/169/42 495/170/42 535/160/42
+f 495/170/42 497/171/42 535/160/42
+f 497/171/42 499/172/42 535/160/42
+f 499/172/42 501/173/42 535/160/42
+f 501/173/42 503/174/42 535/160/42
+f 503/174/42 505/175/42 535/160/42
+f 505/175/42 507/176/42 535/160/42
+f 507/176/42 509/177/42 535/160/42
+f 509/177/42 511/178/42 535/160/42
+f 511/178/42 513/179/42 535/160/42
+f 513/179/42 515/180/42 535/160/42
+f 515/180/42 517/181/42 535/160/42
+f 517/181/42 519/182/42 535/160/42
+f 519/182/42 521/183/42 535/160/42
+f 521/183/42 523/184/42 535/160/42
+f 523/184/42 525/185/42 535/160/42
+f 525/185/42 527/186/42 535/160/42
+f 527/186/42 529/187/42 535/160/42
+f 529/187/42 531/188/42 533/189/42
+f 535/160/42 529/187/42 533/189/42
+f 537/159/39 568/161/39 539/160/39
+f 568/161/39 567/162/39 539/160/39
+f 567/162/39 566/163/39 539/160/39
+f 566/163/39 565/164/39 539/160/39
+f 565/164/39 564/165/39 539/160/39
+f 564/165/39 563/166/39 539/160/39
+f 563/166/39 562/167/39 539/160/39
+f 562/167/39 561/168/39 539/160/39
+f 561/168/39 560/169/39 539/160/39
+f 560/169/39 559/170/39 539/160/39
+f 559/170/39 558/171/39 539/160/39
+f 558/171/39 557/172/39 539/160/39
+f 557/172/39 556/173/39 539/160/39
+f 556/173/39 555/174/39 539/160/39
+f 555/174/39 554/175/39 539/160/39
+f 554/175/39 553/176/39 539/160/39
+f 553/176/39 552/177/39 539/160/39
+f 552/177/39 551/178/39 539/160/39
+f 551/178/39 550/179/39 539/160/39
+f 550/179/39 549/180/39 539/160/39
+f 549/180/39 548/181/39 539/160/39
+f 548/181/39 547/182/39 539/160/39
+f 547/182/39 546/183/39 539/160/39
+f 546/183/39 545/184/39 539/160/39
+f 545/184/39 544/185/39 539/160/39
+f 544/185/39 543/186/39 539/160/39
+f 543/186/39 542/187/39 539/160/39
+f 542/187/39 541/188/39 539/160/39
+f 541/188/39 540/189/39 539/160/39
+f 550/157/397 500/154/397 549/156/397
+f 536/155/429 568/156/429 537/157/429
+f 512/155/399 556/156/399 557/157/399
+f 545/157/400 490/154/400 544/156/400
+f 526/155/430 563/156/430 564/157/430
+f 552/157/402 504/154/402 551/156/402
+f 540/157/431 480/154/431 539/156/431
+f 516/155/404 558/156/404 559/157/404
+f 547/157/405 494/154/405 546/156/405
+f 530/155/432 565/156/432 566/157/432
+f 506/155/407 553/156/407 554/157/407
+f 542/157/408 484/154/408 541/156/408
+f 520/155/433 560/156/433 561/157/433
+f 549/157/410 498/154/410 548/156/410
+f 534/155/434 567/156/434 568/157/434
+f 510/155/435 555/156/435 556/157/435
+f 544/157/413 488/154/413 543/156/413
+f 524/155/414 562/156/414 563/157/414
+f 551/157/415 502/154/415 550/156/415
+f 539/157/416 478/154/416 538/156/416
+f 514/155/417 557/156/417 558/157/417
+f 546/157/418 492/154/418 545/156/418
+f 528/155/419 564/156/419 565/157/419
+f 553/157/420 506/154/420 552/156/420
+f 541/157/421 482/154/421 540/156/421
+f 518/155/422 559/156/422 560/157/422
+f 538/157/436 476/154/436 537/156/436
+f 548/157/424 496/154/424 547/156/424
+f 532/155/437 566/156/437 567/157/437
+f 508/155/426 554/156/426 555/157/426
+f 543/157/427 486/154/427 542/156/427
+v 0.609274 0.018251 -1.901238
+v 0.609274 0.018251 -0.682690
+v -0.609274 0.018251 -0.682690
+v -0.609274 0.018251 -1.901238
+v 0.609274 1.236799 -1.901237
+v 0.609273 1.236799 -0.682690
+v -0.609274 1.236799 -0.682690
+v -0.609274 1.236799 -1.901238
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vn 1.000000 -0.000000 0.000001
+s off
+f 569/190/42 570/191/42 571/192/42
+f 573/190/39 576/191/39 575/192/39
+f 569/190/438 573/191/438 574/192/438
+f 570/190/49 574/191/49 571/193/49
+f 571/190/46 575/191/46 572/193/46
+f 573/190/47 569/191/47 572/192/47
+f 572/193/42 569/190/42 571/192/42
+f 574/193/39 573/190/39 575/192/39
+f 570/193/48 569/190/48 574/192/48
+f 574/191/49 575/192/49 571/193/49
+f 575/191/46 576/192/46 572/193/46
+f 576/193/47 573/190/47 572/192/47
diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro
index 4330ef4c..1f72820c 100644
--- a/examples/datavisualization/datavisualization.pro
+++ b/examples/datavisualization/datavisualization.pro
@@ -6,7 +6,9 @@ SUBDIRS += qmlbars \
qmllegend \
qmlmultigraph \
qmloscilloscope \
- qmlsurfacelayers
+ qmlsurfacelayers \
+ qmlaxisformatter \
+ qmlaxisdrag
!android:!ios {
SUBDIRS += bars \
@@ -15,7 +17,9 @@ SUBDIRS += qmlbars \
itemmodel \
scatter \
surface \
- rotations
+ rotations \
+ draggableaxes \
+ customitems
}
qtHaveModule(multimedia):!android:!ios: SUBDIRS += audiolevels
diff --git a/examples/datavisualization/draggableaxes/axesinputhandler.cpp b/examples/datavisualization/draggableaxes/axesinputhandler.cpp
new file mode 100644
index 00000000..f79f3d4e
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/axesinputhandler.cpp
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** 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 "axesinputhandler.h"
+#include <QtCore/qmath.h>
+
+AxesInputHandler::AxesInputHandler(QAbstract3DGraph *graph, QObject *parent) :
+ Q3DInputHandler(parent),
+ m_mousePressed(false),
+ m_state(StateNormal),
+ m_axisX(0),
+ m_axisZ(0),
+ m_axisY(0),
+ m_speedModifier(15.0f)
+{
+ //! [3]
+ // Connect to the item selection signal from graph
+ connect(graph, &QAbstract3DGraph::selectedElementChanged, this,
+ &AxesInputHandler::handleElementSelected);
+ //! [3]
+}
+
+//! [0]
+void AxesInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ Q3DInputHandler::mousePressEvent(event, mousePos);
+ if (Qt::LeftButton == event->button())
+ m_mousePressed = true;
+}
+//! [0]
+
+//! [2]
+void AxesInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ //! [5]
+ // Check if we're trying to drag axis label
+ if (m_mousePressed && m_state != StateNormal) {
+ //! [5]
+ setPreviousInputPos(inputPosition());
+ setInputPosition(mousePos);
+ handleAxisDragging();
+ } else {
+ Q3DInputHandler::mouseMoveEvent(event, mousePos);
+ }
+}
+//! [2]
+
+//! [1]
+void AxesInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ Q3DInputHandler::mouseReleaseEvent(event, mousePos);
+ m_mousePressed = false;
+ m_state = StateNormal;
+}
+//! [1]
+
+void AxesInputHandler::handleElementSelected(QAbstract3DGraph::ElementType type)
+{
+ //! [4]
+ switch (type) {
+ case QAbstract3DGraph::ElementAxisXLabel:
+ m_state = StateDraggingX;
+ break;
+ case QAbstract3DGraph::ElementAxisYLabel:
+ m_state = StateDraggingY;
+ break;
+ case QAbstract3DGraph::ElementAxisZLabel:
+ m_state = StateDraggingZ;
+ break;
+ default:
+ m_state = StateNormal;
+ break;
+ }
+ //! [4]
+}
+
+void AxesInputHandler::handleAxisDragging()
+{
+ float distance = 0.0f;
+
+ //! [6]
+ // Get scene orientation from active camera
+ float xRotation = scene()->activeCamera()->xRotation();
+ float yRotation = scene()->activeCamera()->yRotation();
+ //! [6]
+
+ //! [7]
+ // 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));
+ //! [7]
+
+ //! [8]
+ // Get the drag amount
+ QPoint move = inputPosition() - previousInputPos();
+
+ // Flip the effect of y movement if we're viewing from below
+ float yMove = (yRotation < 0) ? -move.y() : move.y();
+ //! [8]
+
+ //! [9]
+ // Adjust axes
+ switch (m_state) {
+ case StateDraggingX:
+ distance = (move.x() * xMulX - yMove * xMulY) / m_speedModifier;
+ m_axisX->setRange(m_axisX->min() - distance, m_axisX->max() - distance);
+ break;
+ case StateDraggingZ:
+ distance = (move.x() * zMulX + yMove * zMulY) / m_speedModifier;
+ m_axisZ->setRange(m_axisZ->min() + distance, m_axisZ->max() + distance);
+ break;
+ case StateDraggingY:
+ distance = move.y() / m_speedModifier; // No need to use adjusted y move here
+ m_axisY->setRange(m_axisY->min() + distance, m_axisY->max() + distance);
+ break;
+ default:
+ break;
+ }
+ //! [9]
+}
diff --git a/examples/datavisualization/draggableaxes/axesinputhandler.h b/examples/datavisualization/draggableaxes/axesinputhandler.h
new file mode 100644
index 00000000..e912ba74
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/axesinputhandler.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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 AXESINPUTHANDLER_H
+#define AXESINPUTHANDLER_H
+
+#include <QtDataVisualization/Q3DInputHandler>
+#include <QtDataVisualization/QAbstract3DGraph>
+#include <QtDataVisualization/QValue3DAxis>
+
+using namespace QtDataVisualization;
+
+//! [0]
+class AxesInputHandler : public Q3DInputHandler
+//! [0]
+{
+ Q_OBJECT
+
+ enum InputState {
+ StateNormal = 0,
+ StateDraggingX,
+ StateDraggingZ,
+ StateDraggingY
+ };
+
+public:
+ explicit AxesInputHandler(QAbstract3DGraph *graph, QObject *parent = 0);
+
+ inline void setAxes(QValue3DAxis *axisX, QValue3DAxis *axisZ, QValue3DAxis *axisY) {
+ m_axisX = axisX;
+ m_axisZ = axisZ;
+ m_axisY = axisY;
+ }
+
+ //! [1]
+ inline void setDragSpeedModifier(float modifier) { m_speedModifier = modifier; }
+ //! [1]
+
+ virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos);
+ virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos);
+ virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos);
+
+private:
+ void handleElementSelected(QAbstract3DGraph::ElementType type);
+ void handleAxisDragging();
+
+private:
+ bool m_mousePressed;
+ InputState m_state;
+ QValue3DAxis *m_axisX;
+ QValue3DAxis *m_axisZ;
+ QValue3DAxis *m_axisY;
+ float m_speedModifier;
+};
+
+#endif
diff --git a/examples/datavisualization/draggableaxes/data.cpp b/examples/datavisualization/draggableaxes/data.cpp
new file mode 100644
index 00000000..992c9ee8
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/data.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** 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 "data.h"
+
+#include <QtDataVisualization/QScatterDataProxy>
+#include <QtDataVisualization/Q3DScene>
+#include <QtDataVisualization/Q3DCamera>
+#include <QtDataVisualization/QScatter3DSeries>
+#include <QtDataVisualization/Q3DTheme>
+
+using namespace QtDataVisualization;
+
+const int itemCount = 500;
+
+Data::Data(Q3DScatter *scatter)
+ : m_graph(scatter),
+ //! [1]
+ m_inputHandler(new AxesInputHandler(scatter)),
+ //! [1]
+ m_autoAdjust(false)
+{
+ m_graph->activeTheme()->setType(Q3DTheme::ThemeEbony);
+ m_graph->activeTheme()->setLabelBorderEnabled(true);
+ m_graph->activeTheme()->setLabelBackgroundColor(QColor(QRgb(0x151550)));
+ m_graph->activeTheme()->setLabelTextColor(Qt::lightGray);
+ m_graph->activeTheme()->setFont(QFont("Arial Black", 30));
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight);
+
+ m_graph->axisX()->setRange(-20.0f, 20.0f);
+ m_graph->axisY()->setRange(-10.0f, 10.0f);
+ m_graph->axisZ()->setRange(-20.0f, 20.0f);
+
+ //! [0]
+ // Give ownership of the handler to the graph and make it the active handler
+ m_graph->setActiveInputHandler(m_inputHandler);
+ //! [0]
+
+ //! [2]
+ // Give our axes to the input handler
+ m_inputHandler->setAxes(m_graph->axisX(), m_graph->axisZ(), m_graph->axisY());
+ //! [2]
+
+ addData();
+}
+
+Data::~Data()
+{
+ delete m_graph;
+}
+
+void Data::toggleRanges()
+{
+ if (!m_autoAdjust) {
+ m_graph->axisX()->setAutoAdjustRange(true);
+ m_graph->axisZ()->setAutoAdjustRange(true);
+ m_graph->axisY()->setAutoAdjustRange(true);
+ m_inputHandler->setDragSpeedModifier(1.5f);
+ m_autoAdjust = true;
+ } else {
+ m_graph->axisX()->setRange(-20.0f, 20.0f);
+ m_graph->axisY()->setRange(-10.0f, 10.0f);
+ m_graph->axisZ()->setRange(-20.0f, 20.0f);
+ m_inputHandler->setDragSpeedModifier(15.0f);
+ m_autoAdjust = false;
+ }
+}
+
+void Data::addData()
+{
+ QScatter3DSeries *series = new QScatter3DSeries;
+ series->setMesh(QAbstract3DSeries::MeshCube);
+ series->setMeshSmooth(true);
+ m_graph->addSeries(series);
+
+ QScatter3DSeries *series2 = new QScatter3DSeries;
+ series2->setMesh(QAbstract3DSeries::MeshMinimal);
+ series2->setMeshSmooth(true);
+ m_graph->addSeries(series2);
+
+ QScatter3DSeries *series3 = new QScatter3DSeries;
+ series3->setMesh(QAbstract3DSeries::MeshSphere);
+ series3->setMeshSmooth(true);
+ m_graph->addSeries(series3);
+
+ QScatter3DSeries *series4 = new QScatter3DSeries;
+ series4->setMesh(QAbstract3DSeries::MeshBevelCube);
+ series4->setMeshSmooth(true);
+ m_graph->addSeries(series4);
+
+ QScatter3DSeries *series5 = new QScatter3DSeries;
+ series5->setMesh(QAbstract3DSeries::MeshSphere);
+ m_graph->addSeries(series5);
+
+ QScatterDataArray *dataArray = new QScatterDataArray;
+ dataArray->resize(itemCount);
+ QScatterDataItem *ptrToDataArray = &dataArray->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray2 = new QScatterDataArray;
+ dataArray2->resize(itemCount);
+ ptrToDataArray = &dataArray2->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray3 = new QScatterDataArray;
+ dataArray3->resize(itemCount);
+ ptrToDataArray = &dataArray3->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray4 = new QScatterDataArray;
+ dataArray4->resize(itemCount);
+ ptrToDataArray = &dataArray4->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray5 = new QScatterDataArray;
+ dataArray5->resize(itemCount);
+ ptrToDataArray = &dataArray5->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+
+ m_graph->seriesList().at(0)->dataProxy()->resetArray(dataArray);
+ m_graph->seriesList().at(1)->dataProxy()->resetArray(dataArray2);
+ m_graph->seriesList().at(2)->dataProxy()->resetArray(dataArray3);
+ m_graph->seriesList().at(3)->dataProxy()->resetArray(dataArray4);
+ m_graph->seriesList().at(4)->dataProxy()->resetArray(dataArray5);
+}
+
+QVector3D Data::randVector()
+{
+ return QVector3D(
+ (float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f,
+ (float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f,
+ (float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f);
+}
diff --git a/examples/datavisualization/draggableaxes/data.h b/examples/datavisualization/draggableaxes/data.h
new file mode 100644
index 00000000..40a69497
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/data.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** 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 DATA_H
+#define DATA_H
+
+#include "axesinputhandler.h"
+
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtGui/QVector3D>
+
+using namespace QtDataVisualization;
+
+class Data : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Data(Q3DScatter *scatter);
+ ~Data();
+
+ void toggleRanges();
+
+private:
+ void addData();
+ QVector3D randVector();
+
+private:
+ Q3DScatter *m_graph;
+ AxesInputHandler *m_inputHandler;
+ bool m_autoAdjust;
+};
+
+#endif
diff --git a/examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.png b/examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.png
new file mode 100644
index 00000000..018694b5
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.png
Binary files differ
diff --git a/examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc b/examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc
new file mode 100644
index 00000000..124bbbac
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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 draggableaxes
+ \title Axis Range Dragging With Labels Example
+ \ingroup qtdatavisualization_examples
+ \brief Implementing a custom input handler to support axis dragging.
+ \since QtDataVisualization 1.1
+
+ The Axis Range Dragging example shows how to customize the 3D graph controls in a widget
+ application to allow changing axis ranges by clicking on an axis label and dragging. This is
+ done by implementing a custom input handler to react to selection signals emitted from the
+ graph.
+
+ \image draggableaxes-example.png
+
+ \section1 Replacing default input handling
+
+ The default input handling mechanism is replaced by setting the active input handler of
+ Q3DScatter to \c AxesInputHandler that implements the custom behavior:
+
+ \snippet draggableaxes/data.cpp 0
+
+ \c m_inputHandler was initialized in the constructor:
+
+ \snippet draggableaxes/data.cpp 1
+
+ We will also need the pointers to the axes, so we will pass them to our input handler too:
+
+ \snippet draggableaxes/data.cpp 2
+
+ \section1 Extending mouse event handling
+
+ First of all, we inherited our input handler from Q3DInputHandler instead of
+ QAbstract3DInputHandler. The reason for doing this is to keep all the functionality of the
+ default input handling, and to add our own functionality on top of it:
+
+ \snippet draggableaxes/axesinputhandler.h 0
+
+ We start extending the default functionality by re-implementing some of the mouse events.
+ Let's start with \c {mousePressEvent}. We'll just add button pressed flag for left mouse button
+ into it, and keep the rest of the default functionality:
+
+ \snippet draggableaxes/axesinputhandler.cpp 0
+
+ We'll need to modify \c mouseReleaseEvent too to clear the flag and reset the internal state:
+
+ \snippet draggableaxes/axesinputhandler.cpp 1
+
+ Then we'll modify \c {mouseMoveEvent}. Here we check if the \c m_mousePressed is \c true and
+ our internal state is something other than \c StateNormal. If so, we'll set the input positions
+ for mouse move distance calculations and call the axis dragging function (see
+ \l {Implementing axis dragging} for details):
+
+ \snippet draggableaxes/axesinputhandler.cpp 2
+
+ We don't need to change the functionality of mouse wheel, so we will not re-implement that.
+
+ \section1 Implementing axis dragging
+
+ First we need to start listening to the selection signal from the graph. We do that in the
+ constructor, and connect it to \c handleElementSelected method:
+
+ \snippet draggableaxes/axesinputhandler.cpp 3
+
+ In \c handleElementSelected we check the type of the selection and set our internal state based on
+ it:
+
+ \snippet draggableaxes/axesinputhandler.cpp 4
+
+ The actual dragging logic is implemented in \c handleAxisDragging method, which we call from
+ \c mouseMoveEvent in case the required conditions are met:
+
+ \snippet draggableaxes/axesinputhandler.cpp 5
+
+ In \c handleAxisDragging we first get the scene orientation from our active camera:
+
+ \snippet draggableaxes/axesinputhandler.cpp 6
+
+ Then we calculate the modifiers to mouse move direction based on the orientation:
+
+ \snippet draggableaxes/axesinputhandler.cpp 7
+
+ After that, we calculate the mouse movement, and modify it based on the y rotation of the
+ camera:
+
+ \snippet draggableaxes/axesinputhandler.cpp 8
+
+ And finally apply the moved distance to the correct axis:
+
+ \snippet draggableaxes/axesinputhandler.cpp 9
+
+ We also have a function for setting the dragging speed:
+
+ \snippet draggableaxes/axesinputhandler.h 1
+
+ This is needed, as the mouse movement distance is absolute (in screen coordinates) and we
+ need to adjust it to the axis range. The larger the value, the slower the dragging will be.
+ Note that on this example we do not take scene zoom level into account when determining the
+ drag speed, so you'll notice changes in the range adjustment as you change the zoom level.
+
+ The modifier could be adjusted automatically based on the axis range and camera zoom level, but
+ we'll leave implementing that as an excercise for the reader.
+
+ \section1 Example contents
+*/
diff --git a/examples/datavisualization/draggableaxes/draggableaxes.pro b/examples/datavisualization/draggableaxes/draggableaxes.pro
new file mode 100644
index 00000000..34db5133
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/draggableaxes.pro
@@ -0,0 +1,18 @@
+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 \
+ data.cpp \
+ axesinputhandler.cpp
+HEADERS += data.h \
+ axesinputhandler.h
+
+QT += widgets
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
diff --git a/examples/datavisualization/draggableaxes/main.cpp b/examples/datavisualization/draggableaxes/main.cpp
new file mode 100644
index 00000000..0834313e
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/main.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 "data.h"
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QCommandLinkButton>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DScatter *graph = new Q3DScatter();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ container->setMinimumSize(800, 600);
+ 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);
+
+ QCommandLinkButton *rangeButton = new QCommandLinkButton(widget);
+ rangeButton->setText(QStringLiteral("Toggle axis ranges"));
+ rangeButton->setDescription(QStringLiteral("Switch between automatic axis ranges and preset ranges"));
+ rangeButton->setIconSize(QSize(0, 0));
+
+ vLayout->addWidget(rangeButton, 1, Qt::AlignTop);
+
+ widget->setWindowTitle(QStringLiteral("Input Handling for Axes"));
+
+ Data *graphData = new Data(graph);
+
+ QObject::connect(rangeButton, &QCommandLinkButton::clicked, graphData, &Data::toggleRanges);
+
+ widget->show();
+ return app.exec();
+}
diff --git a/examples/datavisualization/itemmodel/main.cpp b/examples/datavisualization/itemmodel/main.cpp
index ad9f0112..f00702eb 100644
--- a/examples/datavisualization/itemmodel/main.cpp
+++ b/examples/datavisualization/itemmodel/main.cpp
@@ -167,8 +167,11 @@ void GraphDataGenerator::setupModel()
// Add labels
//! [10]
m_graph->rowAxis()->setTitle("Week of year");
+ m_graph->rowAxis()->setTitleVisible(true);
m_graph->columnAxis()->setTitle("Day of week");
+ m_graph->columnAxis()->setTitleVisible(true);
m_graph->valueAxis()->setTitle("Hours spent on the Internet");
+ m_graph->valueAxis()->setTitleVisible(true);
m_graph->valueAxis()->setLabelFormat("%.1f h");
//! [10]
diff --git a/examples/datavisualization/qmlaxisdrag/doc/images/qmlaxisdrag-example.png b/examples/datavisualization/qmlaxisdrag/doc/images/qmlaxisdrag-example.png
new file mode 100644
index 00000000..de33ba66
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/doc/images/qmlaxisdrag-example.png
Binary files differ
diff --git a/examples/datavisualization/qmlaxisdrag/doc/src/qmlaxisdrag.qdoc b/examples/datavisualization/qmlaxisdrag/doc/src/qmlaxisdrag.qdoc
new file mode 100644
index 00000000..317f855f
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/doc/src/qmlaxisdrag.qdoc
@@ -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
+**
+****************************************************************************/
+
+/*!
+ \example qmlaxisdrag
+ \title Qt Quick 2 Axis Dragging Example
+ \ingroup qtdatavisualization_examples
+ \brief Implementing axis dragging in QML
+ \since QtDataVisualization 1.1
+
+ The Qt Quick 2 axis dragging example concentrates on showing how to implement axis range
+ changing by dragging axis labels in QML. It also gives a quick peek to two other new features
+ in Qt Data Visualization 1.1: orthographic projection and dynamic custom item handling.
+
+ \image qmlaxisdrag-example.png
+
+ \section1 Overriding default input handling
+
+ First we deactivate the default input handling mechanism by setting the active input handler
+ of Scatter3D graph to \c{null}:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 0
+ \dots
+
+ Then we add a MouseArea and set it to fill the parent, which is the same \c Item our
+ \c scatterGraph is contained in. We also set it to accept only left mouse button presses,
+ as in this example we are not interested in other buttons:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 1
+ \dots
+
+ Then we need to listen to mouse presses, and when caught, send a selection query to the graph:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 2
+
+ Current mouse position, that will be needed for move distance calculation, is caught in
+ \c{onPositionChanged}:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 3
+ \dots
+
+ At the end of \c{onPositionChanged}, we'll save the previous mouse position for move distance
+ calculation that will be introduced later:
+
+ \dots 0
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 4
+
+ \section1 Translating mouse movement to axis range change
+
+ in \c scatterGraph we will need to listen to \c onSelectedElementChanged signal. The signal
+ is emitted after the selection query has been made in the \c{onPressed} of \c{inputArea}. We
+ set the element type into a property we defined (\c{property int selectedAxisLabel: -1}) in our
+ main component, since it is of a type we are interested in:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 5
+
+ Then, back in the \c onPositionChanged of \c{inputArea}, we check if a mouse button is pressed
+ and if we have a current axis label selection. If the conditions are met, we'll call the
+ function that does the conversion from mouse movement to axis range update:
+
+ \dots 0
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 6
+ \dots 0
+
+ The conversion is easy in this case, as we have a fixed camera rotation. We can use some
+ precalculated values, calculate mouse move distance, and apply the values to the
+ selected axis range:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 7
+
+ For a more sophisticated conversion from mouse movement to axis range update, see
+ \l{Axis Range Dragging With Labels Example}{this example}.
+
+ \section1 Other features
+
+ The example also demonstrates how to use orthographic projection and how to update properties
+ of a custom item on the fly.
+
+ Orthographic projection is very simple. You'll just need to change \c orthoProjection property
+ of \c{scatterGraph}. In this example we have a button for toggling it on and off:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 8
+
+ For custom items, first we'll add one in the \c customItemList of \c{scatterGraph}:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 9
+
+ We have implemented a timer to add, remove, and rotate all the items in the graph,
+ and we'll use the same timer for rotating the custom item:
+
+ \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 10
+ \dots
+
+ \section1 Example contents
+*/
diff --git a/examples/datavisualization/qmlaxisdrag/main.cpp b/examples/datavisualization/qmlaxisdrag/main.cpp
new file mode 100644
index 00000000..523d8d16
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/main.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** 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 Draggable Axes"));
+
+ viewer.setSource(QUrl("qrc:/qml/qmlaxisdrag/main.qml"));
+ viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+ viewer.show();
+
+ return app.exec();
+}
diff --git a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/NewButton.qml b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/NewButton.qml
new file mode 100644
index 00000000..ce946ed8
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/NewButton.qml
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+ id: newbutton
+
+ property alias text: buttonText.text
+
+ signal clicked
+
+ height: 80
+
+ Button {
+ opacity: 0.5
+ width: parent.width
+ height: parent.height
+ Text {
+ id: buttonText
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ anchors.fill: parent
+ }
+ onClicked: newbutton.clicked()
+ }
+}
diff --git a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cube.obj b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cube.obj
new file mode 100644
index 00000000..0197618f
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cube.obj
@@ -0,0 +1,415 @@
+# Blender v2.66 (sub 0) OBJ File: 'beveled_cube.blend'
+# www.blender.org
+v -1.000000 -0.878027 0.878027
+v -0.978771 -0.929277 0.878027
+v -0.950975 -0.932562 0.932562
+v -0.978771 -0.878027 0.929277
+v -0.932562 -0.950975 0.932562
+v -0.929277 -0.978771 0.878027
+v -0.878027 -1.000000 0.878027
+v -0.878027 -0.978771 0.929277
+v -0.932562 -0.932562 0.950975
+v -0.878027 -0.929277 0.978771
+v -0.878027 -0.878027 1.000000
+v -0.929277 -0.878027 0.978771
+v -1.000000 -0.878027 -0.878027
+v -0.978771 -0.878027 -0.929277
+v -0.950975 -0.932562 -0.932562
+v -0.978771 -0.929277 -0.878027
+v -0.932562 -0.932562 -0.950975
+v -0.929277 -0.878027 -0.978771
+v -0.878027 -0.878027 -1.000000
+v -0.878027 -0.929277 -0.978771
+v -0.932562 -0.950975 -0.932562
+v -0.878027 -0.978771 -0.929277
+v -0.878027 -1.000000 -0.878027
+v -0.929277 -0.978771 -0.878027
+v 0.878027 -0.878027 -1.000000
+v 0.929277 -0.878027 -0.978771
+v 0.932562 -0.932562 -0.950975
+v 0.878027 -0.929277 -0.978771
+v 0.950975 -0.932562 -0.932562
+v 0.978771 -0.878027 -0.929277
+v 1.000000 -0.878027 -0.878027
+v 0.978771 -0.929277 -0.878027
+v 0.932562 -0.950975 -0.932562
+v 0.929277 -0.978771 -0.878027
+v 0.878027 -1.000000 -0.878027
+v 0.878027 -0.978771 -0.929277
+v 1.000000 -0.878027 0.878027
+v 0.978771 -0.878027 0.929277
+v 0.950975 -0.932562 0.932562
+v 0.978771 -0.929277 0.878027
+v 0.932562 -0.932562 0.950975
+v 0.929277 -0.878027 0.978771
+v 0.878027 -0.878027 1.000000
+v 0.878027 -0.929277 0.978771
+v 0.932562 -0.950975 0.932562
+v 0.878027 -0.978771 0.929277
+v 0.878027 -1.000000 0.878027
+v 0.929277 -0.978771 0.878027
+v -0.878027 0.878027 1.000000
+v -0.878027 0.929277 0.978771
+v -0.932562 0.932562 0.950975
+v -0.929277 0.878027 0.978771
+v -0.932562 0.950975 0.932562
+v -0.878027 0.978771 0.929277
+v -0.878027 1.000000 0.878027
+v -0.929277 0.978771 0.878027
+v -0.950975 0.932562 0.932562
+v -0.978771 0.929277 0.878027
+v -1.000000 0.878027 0.878027
+v -0.978771 0.878027 0.929277
+v -1.000000 0.878027 -0.878027
+v -0.978771 0.929277 -0.878027
+v -0.950975 0.932562 -0.932562
+v -0.978771 0.878027 -0.929277
+v -0.932562 0.950975 -0.932562
+v -0.929277 0.978771 -0.878027
+v -0.878027 1.000000 -0.878027
+v -0.878027 0.978771 -0.929277
+v -0.932562 0.932562 -0.950975
+v -0.878027 0.929277 -0.978771
+v -0.878027 0.878027 -1.000000
+v -0.929277 0.878027 -0.978771
+v 0.878027 0.878027 -1.000000
+v 0.878027 0.929277 -0.978771
+v 0.932562 0.932562 -0.950975
+v 0.929277 0.878027 -0.978771
+v 0.932562 0.950975 -0.932562
+v 0.878027 0.978771 -0.929277
+v 0.878027 1.000000 -0.878027
+v 0.929277 0.978771 -0.878027
+v 0.950975 0.932562 -0.932562
+v 0.978771 0.929277 -0.878027
+v 1.000000 0.878027 -0.878027
+v 0.978771 0.878027 -0.929277
+v 1.000000 0.878027 0.878027
+v 0.978771 0.929277 0.878027
+v 0.950975 0.932562 0.932562
+v 0.978771 0.878027 0.929277
+v 0.932562 0.950975 0.932562
+v 0.929277 0.978771 0.878027
+v 0.878027 1.000000 0.878027
+v 0.878027 0.978771 0.929277
+v 0.932562 0.932562 0.950975
+v 0.878027 0.929277 0.978771
+v 0.878027 0.878027 1.000000
+v 0.929277 0.878027 0.978771
+vt 0.024513 0.966281
+vt 0.033719 0.975487
+vt 0.033719 0.966281
+vt 0.964639 0.060986
+vt 0.964639 0.939014
+vt 0.989386 0.939014
+vt 0.939014 0.964639
+vt 0.060986 0.964639
+vt 0.939014 0.989386
+vt 0.060986 0.060986
+vt 0.060986 0.939014
+vt 0.939014 0.939014
+vt 0.035361 0.060986
+vt 0.035361 0.939014
+vt 0.010614 0.060986
+vt 0.010614 0.939014
+vt 0.989386 0.060986
+vt 0.939014 0.035361
+vt 0.060986 0.035361
+vt 0.060986 0.010614
+vt 0.060986 0.989386
+vt 0.939014 0.060986
+vt 0.966281 0.975487
+vt 0.966281 0.966281
+vt 0.975487 0.966281
+vt 0.975487 0.033719
+vt 0.966281 0.033719
+vt 0.966281 0.024513
+vt 0.033719 0.024513
+vt 0.033719 0.033719
+vt 0.024513 0.033719
+vt 0.939014 0.010614
+vn -0.713187 -0.495651 -0.495651
+vn -0.495651 -0.495651 -0.713187
+vn -0.495651 -0.713187 -0.495651
+vn 0.539384 -0.823450 0.175909
+vn 0.539384 -0.823450 -0.175909
+vn 0.823450 -0.539384 -0.175909
+vn -0.713187 0.495651 -0.495651
+vn -0.495651 0.713187 -0.495651
+vn -0.495651 0.495651 -0.713187
+vn 0.175909 -0.823450 -0.539384
+vn -0.185644 -0.825892 -0.532365
+vn 0.175909 -0.539384 -0.823450
+vn -0.193426 -0.961852 0.193426
+vn -0.193426 -0.961852 -0.193426
+vn 0.187689 -0.964110 -0.187689
+vn -0.532365 -0.185644 0.825892
+vn -0.532365 0.185644 0.825892
+vn -0.825892 -0.185644 0.532365
+vn -0.532365 0.185644 -0.825892
+vn -0.532365 -0.185644 -0.825892
+vn -0.825892 0.185644 -0.532365
+vn 0.823450 0.175909 -0.539384
+vn 0.823450 -0.175909 -0.539384
+vn 0.539384 -0.175909 -0.823450
+vn 0.539384 0.175909 0.823450
+vn 0.539384 -0.175909 0.823450
+vn 0.823450 -0.175909 0.539384
+vn 0.175909 0.823450 0.539384
+vn -0.185644 0.825892 0.532365
+vn -0.185644 0.532365 0.825892
+vn -0.185644 0.825892 -0.532365
+vn 0.175909 0.823450 -0.539384
+vn -0.185644 0.532365 -0.825892
+vn 0.539384 0.823450 -0.175909
+vn 0.539384 0.823450 0.175909
+vn 0.823450 0.539384 0.175909
+vn 0.187689 0.964110 0.187689
+vn 0.187689 0.964110 -0.187689
+vn -0.193426 0.961852 -0.193426
+vn -0.193426 0.193426 -0.961852
+vn 0.187689 0.187689 -0.964110
+vn -0.193426 -0.193426 -0.961852
+vn -0.961852 0.193426 0.193426
+vn -0.961852 0.193426 -0.193426
+vn -0.961852 -0.193426 0.193426
+vn -0.532365 -0.825892 -0.185644
+vn -0.532365 -0.825892 0.185644
+vn -0.825892 -0.532365 -0.185644
+vn 0.498856 0.498856 -0.708701
+vn 0.498856 0.708701 -0.498856
+vn 0.708701 0.498856 -0.498856
+vn 0.964110 0.187689 -0.187689
+vn 0.964110 0.187689 0.187689
+vn 0.964110 -0.187689 0.187689
+vn 0.498856 -0.498856 -0.708701
+vn 0.708701 -0.498856 -0.498856
+vn 0.498856 -0.708701 -0.498856
+vn 0.708701 0.498856 0.498856
+vn 0.498856 0.708701 0.498856
+vn 0.498856 0.498856 0.708701
+vn -0.495651 0.495651 0.713187
+vn -0.495651 0.713187 0.495651
+vn -0.713187 0.495651 0.495651
+vn 0.708701 -0.498856 0.498856
+vn 0.498856 -0.498856 0.708701
+vn 0.498856 -0.708701 0.498856
+vn -0.532365 0.825892 0.185644
+vn -0.532365 0.825892 -0.185644
+vn -0.825892 0.532365 0.185644
+vn 0.187689 0.187689 0.964110
+vn -0.193426 0.193426 0.961852
+vn -0.193426 -0.193426 0.961852
+vn -0.185644 -0.825892 0.532365
+vn 0.175909 -0.823450 0.539384
+vn 0.175909 -0.539384 0.823450
+vn -0.825892 -0.532365 0.185644
+vn -0.713187 -0.495651 0.495651
+vn -0.495651 -0.713187 0.495651
+vn -0.495651 -0.495651 0.713187
+vn -0.185644 -0.532365 0.825892
+vn -0.961852 -0.193426 -0.193426
+vn -0.825892 -0.185644 -0.532365
+vn 0.187689 -0.187689 -0.964110
+vn 0.823450 -0.539384 0.175909
+vn -0.193426 0.961852 0.193426
+vn -0.825892 0.532365 -0.185644
+vn 0.175909 0.539384 -0.823450
+vn 0.539384 0.175909 -0.823450
+vn 0.823450 0.539384 -0.175909
+vn 0.823450 0.175909 0.539384
+vn 0.175909 0.539384 0.823450
+vn -0.185644 -0.532365 -0.825892
+vn -0.825892 0.185644 0.532365
+vn 0.964110 -0.187689 -0.187689
+vn 0.187689 -0.187689 0.964110
+vn 0.187689 -0.964110 0.187689
+s 1
+f 15/1/1 17/2/2 21/3/3
+f 48/4/4 34/5/5 32/6/6
+f 63/1/7 65/3/8 69/2/9
+f 36/7/10 22/8/11 28/9/12
+f 7/10/13 23/11/14 35/12/15
+f 12/13/16 52/14/17 4/15/18
+f 72/14/19 18/13/20 64/16/21
+f 84/6/22 30/17/23 26/4/24
+f 96/5/25 42/4/26 38/17/27
+f 92/18/28 54/19/29 50/20/30
+f 68/8/31 78/7/32 70/21/33
+f 80/5/34 90/4/35 86/17/36
+f 91/22/37 79/12/38 67/11/39
+f 71/11/40 73/12/41 19/10/42
+f 59/11/43 61/12/44 1/10/45
+f 24/14/46 6/13/47 16/16/48
+f 75/23/49 77/24/50 81/25/51
+f 83/12/52 85/11/53 37/10/54
+f 27/23/55 29/25/56 33/24/57
+f 87/26/58 89/27/59 93/28/60
+f 51/29/61 53/30/62 57/31/63
+f 39/26/64 41/28/65 45/27/66
+f 56/13/67 66/14/68 58/15/69
+f 95/12/70 49/11/71 11/10/72
+f 8/19/73 46/18/74 44/32/75
+f 1/10/45 2/19/76 3/30/77
+f 5/30/78 6/13/47 7/10/13
+f 9/30/79 10/19/80 11/10/72
+f 13/22/81 14/4/82 15/27/1
+f 17/30/2 18/13/20 19/10/42
+f 21/3/3 22/8/11 23/11/14
+f 25/22/83 26/4/24 28/18/12
+f 29/27/56 30/4/23 32/18/6
+f 33/24/57 34/5/5 36/7/10
+f 37/10/54 38/13/27 40/19/84
+f 41/27/65 42/4/26 44/18/75
+f 45/27/66 46/18/74 48/4/4
+f 49/11/71 50/8/30 51/3/61
+f 53/30/62 54/19/29 55/10/85
+f 57/3/63 58/8/69 59/11/43
+f 61/12/44 62/7/86 63/24/7
+f 65/3/8 66/14/68 67/11/39
+f 69/3/9 70/8/33 71/11/40
+f 73/12/41 74/7/87 76/5/88
+f 77/24/50 78/7/32 80/5/34
+f 81/24/51 82/7/89 84/5/22
+f 85/11/53 86/8/36 88/14/90
+f 89/27/59 90/4/35 92/18/28
+f 93/24/60 94/7/91 96/5/25
+f 2/15/76 6/13/47 3/31/77
+f 8/19/73 10/20/80 5/30/78
+f 12/13/16 4/15/18 9/30/79
+f 14/15/82 18/13/20 15/31/1
+f 20/21/92 22/8/11 17/2/2
+f 24/14/46 16/16/48 21/3/3
+f 26/4/24 30/17/23 29/26/56
+f 32/6/6 34/5/5 33/24/57
+f 36/7/10 28/9/12 27/23/55
+f 38/17/27 42/4/26 41/27/65
+f 44/32/75 46/18/74 45/27/66
+f 48/4/4 40/17/84 39/26/64
+f 50/20/30 54/19/29 51/29/61
+f 56/13/67 58/15/69 53/30/62
+f 60/16/93 52/14/17 57/1/63
+f 62/16/86 66/14/68 63/1/7
+f 68/8/31 70/21/33 65/3/8
+f 72/14/19 64/16/21 69/3/9
+f 74/9/87 78/7/32 77/24/50
+f 80/5/34 82/6/89 81/25/51
+f 84/6/22 76/5/88 75/24/49
+f 86/17/36 90/4/35 89/27/59
+f 92/18/28 94/32/91 93/28/60
+f 96/5/25 88/6/90 87/25/58
+f 55/10/85 67/11/39 56/13/67
+f 61/12/44 59/11/43 62/7/86
+f 71/11/40 19/10/42 72/14/19
+f 13/22/81 61/12/44 14/4/82
+f 23/11/14 7/10/13 24/14/46
+f 1/10/45 13/22/81 2/19/76
+f 11/10/72 49/11/71 12/13/16
+f 59/11/43 1/10/45 60/14/93
+f 67/11/39 79/12/38 68/8/31
+f 73/12/41 71/11/40 74/7/87
+f 83/12/52 31/22/94 30/4/23
+f 25/22/83 73/12/41 76/5/88
+f 35/12/15 23/11/14 36/7/10
+f 19/10/42 25/22/83 20/19/92
+f 79/12/38 91/22/37 90/4/35
+f 85/11/53 83/12/52 82/7/89
+f 95/12/70 43/22/95 42/4/26
+f 37/10/54 85/11/53 88/14/90
+f 47/22/96 35/12/15 34/5/5
+f 31/22/94 37/10/54 40/19/84
+f 91/22/37 55/10/85 54/19/29
+f 49/11/71 95/12/70 94/7/91
+f 7/10/13 47/22/96 46/18/74
+f 43/22/95 11/10/72 10/19/80
+f 3/31/77 5/30/78 9/29/79
+f 40/17/84 48/4/4 32/6/6
+f 22/8/11 20/21/92 28/9/12
+f 47/22/96 7/10/13 35/12/15
+f 52/14/17 60/16/93 4/15/18
+f 18/13/20 14/15/82 64/16/21
+f 76/5/88 84/6/22 26/4/24
+f 88/6/90 96/5/25 38/17/27
+f 94/32/91 92/18/28 50/20/30
+f 78/7/32 74/9/87 70/21/33
+f 82/6/89 80/5/34 86/17/36
+f 55/10/85 91/22/37 67/11/39
+f 73/12/41 25/22/83 19/10/42
+f 61/12/44 13/22/81 1/10/45
+f 6/13/47 2/15/76 16/16/48
+f 31/22/94 83/12/52 37/10/54
+f 66/14/68 62/16/86 58/15/69
+f 43/22/95 95/12/70 11/10/72
+f 10/20/80 8/19/73 44/32/75
+f 4/13/18 1/10/45 3/30/77
+f 8/19/73 5/30/78 7/10/13
+f 12/13/16 9/30/79 11/10/72
+f 16/18/48 13/22/81 15/27/1
+f 20/19/92 17/30/2 19/10/42
+f 24/14/46 21/3/3 23/11/14
+f 26/4/24 27/27/55 28/18/12
+f 30/4/23 31/22/94 32/18/6
+f 34/5/5 35/12/15 36/7/10
+f 38/13/27 39/30/64 40/19/84
+f 42/4/26 43/22/95 44/18/75
+f 46/18/74 47/22/96 48/4/4
+f 52/14/17 49/11/71 51/3/61
+f 56/13/67 53/30/62 55/10/85
+f 60/14/93 57/3/63 59/11/43
+f 64/5/21 61/12/44 63/24/7
+f 68/8/31 65/3/8 67/11/39
+f 72/14/19 69/3/9 71/11/40
+f 74/7/87 75/24/49 76/5/88
+f 78/7/32 79/12/38 80/5/34
+f 82/7/89 83/12/52 84/5/22
+f 86/8/36 87/3/58 88/14/90
+f 90/4/35 91/22/37 92/18/28
+f 94/7/91 95/12/70 96/5/25
+f 6/13/47 5/30/78 3/31/77
+f 10/20/80 9/29/79 5/30/78
+f 4/15/18 3/31/77 9/30/79
+f 18/13/20 17/30/2 15/31/1
+f 22/8/11 21/3/3 17/2/2
+f 16/16/48 15/1/1 21/3/3
+f 27/27/55 26/4/24 29/26/56
+f 29/25/56 32/6/6 33/24/57
+f 33/24/57 36/7/10 27/23/55
+f 39/26/64 38/17/27 41/27/65
+f 41/28/65 44/32/75 45/27/66
+f 45/27/66 48/4/4 39/26/64
+f 54/19/29 53/30/62 51/29/61
+f 58/15/69 57/31/63 53/30/62
+f 52/14/17 51/3/61 57/1/63
+f 66/14/68 65/3/8 63/1/7
+f 70/21/33 69/2/9 65/3/8
+f 64/16/21 63/1/7 69/3/9
+f 75/23/49 74/9/87 77/24/50
+f 77/24/50 80/5/34 81/25/51
+f 81/25/51 84/6/22 75/24/49
+f 87/26/58 86/17/36 89/27/59
+f 89/27/59 92/18/28 93/28/60
+f 93/24/60 96/5/25 87/25/58
+f 67/11/39 66/14/68 56/13/67
+f 59/11/43 58/8/69 62/7/86
+f 19/10/42 18/13/20 72/14/19
+f 61/12/44 64/5/21 14/4/82
+f 7/10/13 6/13/47 24/14/46
+f 13/22/81 16/18/48 2/19/76
+f 49/11/71 52/14/17 12/13/16
+f 1/10/45 4/13/18 60/14/93
+f 79/12/38 78/7/32 68/8/31
+f 71/11/40 70/8/33 74/7/87
+f 84/5/22 83/12/52 30/4/23
+f 26/4/24 25/22/83 76/5/88
+f 23/11/14 22/8/11 36/7/10
+f 25/22/83 28/18/12 20/19/92
+f 80/5/34 79/12/38 90/4/35
+f 86/8/36 85/11/53 82/7/89
+f 96/5/25 95/12/70 42/4/26
+f 38/13/27 37/10/54 88/14/90
+f 48/4/4 47/22/96 34/5/5
+f 32/18/6 31/22/94 40/19/84
+f 92/18/28 91/22/37 54/19/29
+f 50/8/30 49/11/71 94/7/91
+f 8/19/73 7/10/13 46/18/74
+f 44/18/75 43/22/95 10/19/80
diff --git a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cubetexture.png b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cubetexture.png
new file mode 100644
index 00000000..3cea6863
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cubetexture.png
Binary files differ
diff --git a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/main.qml b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/main.qml
new file mode 100644
index 00000000..91685297
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/main.qml
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** 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 QtDataVisualization 1.1
+import "."
+
+Item {
+ id: mainView
+ width: 800
+ height: 600
+ visible: true
+
+ property int selectedAxisLabel: -1
+ property real dragSpeedModifier: 100.0
+ property int currentMouseX: -1
+ property int currentMouseY: -1
+ property int previousMouseX: -1
+ property int previousMouseY: -1
+
+ ListModel {
+ id: graphModel
+ ListElement{ xPos: 0.0; yPos: 0.0; zPos: 0.0; rotation: "@0,0,0,0" }
+ ListElement{ xPos: 1.0; yPos: 1.0; zPos: 1.0; rotation: "@45,1,1,1" }
+ }
+
+ Timer {
+ id: dataTimer
+ interval: 1
+ running: true
+ repeat: true
+ property bool isIncreasing: true
+ property real rotationAngle: 0
+
+ function generateQuaternion() {
+ return "@" + Math.random() * 360 + "," + Math.random() + ","
+ + Math.random() + "," + Math.random()
+ }
+
+ function appendRow() {
+ graphModel.append({"xPos": Math.random(),
+ "yPos": Math.random(),
+ "zPos": Math.random(),
+ "rotation": generateQuaternion()
+ });
+ }
+
+ //! [10]
+ onTriggered: {
+ rotationAngle = rotationAngle + 1
+ qtCube.setRotationAxisAndAngle(Qt.vector3d(1,0,1), rotationAngle)
+ //! [10]
+ scatterSeries.setMeshAxisAndAngle(Qt.vector3d(1,1,1), rotationAngle)
+ if (isIncreasing) {
+ for (var i = 0; i < 10; i++)
+ appendRow()
+ if (graphModel.count > 2002) {
+ scatterGraph.theme = isabelleTheme
+ isIncreasing = false
+ }
+ } else {
+ graphModel.remove(2, 10);
+ if (graphModel.count == 2) {
+ scatterGraph.theme = dynamicColorTheme
+ isIncreasing = true
+ }
+ }
+ }
+ }
+
+ ThemeColor {
+ id: dynamicColor
+ ColorAnimation on color {
+ from: "red"
+ to: "yellow"
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ }
+
+ Theme3D {
+ id: dynamicColorTheme
+ type: Theme3D.ThemeEbony
+ baseColors: [dynamicColor]
+ font.pointSize: 50
+ labelBorderEnabled: true
+ labelBackgroundColor: "gold"
+ labelTextColor: "black"
+ }
+
+ Theme3D {
+ id: isabelleTheme
+ type: Theme3D.ThemeIsabelle
+ font.pointSize: 50
+ labelBorderEnabled: true
+ labelBackgroundColor: "gold"
+ labelTextColor: "black"
+ }
+
+ Item {
+ id: dataView
+ anchors.bottom: parent.bottom
+ width: parent.width
+ height: parent.height
+
+ //! [0]
+ Scatter3D {
+ id: scatterGraph
+ inputHandler: null
+ //! [0]
+ width: dataView.width
+ height: dataView.height
+ theme: dynamicColorTheme
+ shadowQuality: AbstractGraph3D.ShadowQualityLow
+ scene.activeCamera.yRotation: 45.0
+ scene.activeCamera.xRotation: 45.0
+ scene.activeCamera.zoomLevel: 75.0
+
+ Scatter3DSeries {
+ id: scatterSeries
+ itemLabelFormat: "X:@xLabel Y:@yLabel Z:@zLabel"
+ mesh: Abstract3DSeries.MeshCube
+
+ ItemModelScatterDataProxy {
+ itemModel: graphModel
+ xPosRole: "xPos"
+ yPosRole: "yPos"
+ zPosRole: "zPos"
+ rotationRole: "rotation"
+ }
+ }
+ //! [9]
+ customItemList: [
+ Custom3DItem {
+ id: qtCube
+ meshFile: ":/mesh/cube"
+ textureFile: ":/texture/texture"
+ position: Qt.vector3d(0.65,0.35,0.65)
+ scaling: Qt.vector3d(0.3,0.3,0.3)
+ }
+ ]
+ //! [9]
+ //! [5]
+ onSelectedElementChanged: {
+ if (selectedElement >= AbstractGraph3D.ElementAxisXLabel
+ && selectedElement <= AbstractGraph3D.ElementAxisZLabel)
+ selectedAxisLabel = selectedElement
+ else
+ selectedAxisLabel = -1
+ }
+ //! [5]
+ }
+
+ //! [1]
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ acceptedButtons: Qt.LeftButton
+ //! [1]
+
+ //! [3]
+ onPositionChanged: {
+ currentMouseX = mouse.x;
+ currentMouseY = mouse.y;
+ //! [3]
+ //! [6]
+ if (pressed && selectedAxisLabel != -1)
+ dragAxis();
+ //! [6]
+ //! [4]
+ previousMouseX = currentMouseX;
+ previousMouseY = currentMouseY;
+ }
+ //! [4]
+
+ //! [2]
+ onPressed: {
+ scatterGraph.scene.selectionQueryPosition = Qt.point(mouse.x, mouse.y);
+ }
+ //! [2]
+
+ onReleased: {
+ // We need to clear mouse positions and selected axis, because touch devices cannot
+ // track position all the time
+ selectedAxisLabel = -1
+ currentMouseX = -1
+ currentMouseY = -1
+ previousMouseX = -1
+ previousMouseY = -1
+ }
+ }
+ }
+
+ //! [7]
+ function dragAxis() {
+ // Do nothing if previous mouse position is uninitialized
+ if (previousMouseX === -1)
+ return
+
+ // Directional drag multipliers based on rotation. Camera is locked to 45 degrees, so we
+ // can use one precalculated value instead of calculating xx, xy, zx and zy individually
+ var cameraMultiplier = 0.70710678
+
+ // Calculate the mouse move amount
+ var moveX = currentMouseX - previousMouseX
+ var moveY = currentMouseY - previousMouseY
+
+ // Adjust axes
+ switch (selectedAxisLabel) {
+ case AbstractGraph3D.ElementAxisXLabel:
+ var distance = ((moveX - moveY) * cameraMultiplier) / dragSpeedModifier
+ // Check if we need to change min or max first to avoid invalid ranges
+ if (distance > 0) {
+ scatterGraph.axisX.min -= distance
+ scatterGraph.axisX.max -= distance
+ } else {
+ scatterGraph.axisX.max -= distance
+ scatterGraph.axisX.min -= distance
+ }
+ break
+ case AbstractGraph3D.ElementAxisYLabel:
+ distance = moveY / dragSpeedModifier
+ // Check if we need to change min or max first to avoid invalid ranges
+ if (distance > 0) {
+ scatterGraph.axisY.max += distance
+ scatterGraph.axisY.min += distance
+ } else {
+ scatterGraph.axisY.min += distance
+ scatterGraph.axisY.max += distance
+ }
+ break
+ case AbstractGraph3D.ElementAxisZLabel:
+ distance = ((moveX + moveY) * cameraMultiplier) / dragSpeedModifier
+ // Check if we need to change min or max first to avoid invalid ranges
+ if (distance > 0) {
+ scatterGraph.axisZ.max += distance
+ scatterGraph.axisZ.min += distance
+ } else {
+ scatterGraph.axisZ.min += distance
+ scatterGraph.axisZ.max += distance
+ }
+ break
+ }
+ }
+ //! [7]
+
+ NewButton {
+ id: rangeToggle
+ width: parent.width / 3 // We're adding 3 buttons and want to divide them equally
+ text: "Use Preset Range"
+ anchors.left: parent.left
+ property bool autoRange: true
+ onClicked: {
+ if (autoRange) {
+ text = "Use Automatic Range"
+ scatterGraph.axisX.min = 0.3
+ scatterGraph.axisX.max = 0.7
+ scatterGraph.axisY.min = 0.3
+ scatterGraph.axisY.max = 0.7
+ scatterGraph.axisZ.min = 0.3
+ scatterGraph.axisZ.max = 0.7
+ autoRange = false
+ dragSpeedModifier = 200.0
+ } else {
+ text = "Use Preset Range"
+ autoRange = true
+ dragSpeedModifier = 100.0
+ }
+ scatterGraph.axisX.autoAdjustRange = autoRange
+ scatterGraph.axisY.autoAdjustRange = autoRange
+ scatterGraph.axisZ.autoAdjustRange = autoRange
+ }
+ }
+
+ //! [8]
+ NewButton {
+ id: orthoToggle
+ width: parent.width / 3
+ text: "Display Orthographic"
+ anchors.left: rangeToggle.right
+ onClicked: {
+ if (scatterGraph.orthoProjection) {
+ text = "Display Orthographic";
+ scatterGraph.orthoProjection = false
+ // Orthographic projection disables shadows, so we need to switch them back on
+ scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualityLow
+ } else {
+ text = "Display Perspective";
+ scatterGraph.orthoProjection = true
+ }
+ }
+ }
+ //! [8]
+
+ NewButton {
+ id: exitButton
+ width: parent.width / 3
+ text: "Quit"
+ anchors.left: orthoToggle.right
+ onClicked: Qt.quit(0);
+ }
+}
diff --git a/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.pro b/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.pro
new file mode 100644
index 00000000..d45525ad
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.pro
@@ -0,0 +1,11 @@
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+SOURCES += main.cpp
+
+RESOURCES += qmlaxisdrag.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/* \
+ qml/qmlaxisdrag/*
diff --git a/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.qrc b/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.qrc
new file mode 100644
index 00000000..c6c45e26
--- /dev/null
+++ b/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.qrc
@@ -0,0 +1,12 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/qmlaxisdrag/main.qml</file>
+ <file>qml/qmlaxisdrag/NewButton.qml</file>
+ </qresource>
+ <qresource prefix="/mesh">
+ <file alias="cube">qml/qmlaxisdrag/cube.obj</file>
+ </qresource>
+ <qresource prefix="/texture">
+ <file alias="texture">qml/qmlaxisdrag/cubetexture.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/datavisualization/qmlaxisformatter/customformatter.cpp b/examples/datavisualization/qmlaxisformatter/customformatter.cpp
new file mode 100644
index 00000000..486287ef
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/customformatter.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 "customformatter.h"
+#include <QtDataVisualization/QValue3DAxis>
+#include <QtQml/QQmlExtensionPlugin>
+#include <QtCore/QDebug>
+
+using namespace QtDataVisualization;
+
+Q_DECLARE_METATYPE(QValue3DAxisFormatter *)
+
+static const qreal oneDayMs = 60.0 * 60.0 * 24.0 * 1000.0;
+
+CustomFormatter::CustomFormatter(QObject *parent) :
+ QValue3DAxisFormatter(parent)
+{
+ qRegisterMetaType<QValue3DAxisFormatter *>();
+}
+
+CustomFormatter::~CustomFormatter()
+{
+}
+
+//! [1]
+QValue3DAxisFormatter *CustomFormatter::createNewInstance() const
+{
+ return new CustomFormatter();
+}
+
+void CustomFormatter::populateCopy(QValue3DAxisFormatter &copy) const
+{
+ QValue3DAxisFormatter::populateCopy(copy);
+
+ CustomFormatter *customFormatter = static_cast<CustomFormatter *>(&copy);
+ customFormatter->m_originDate = m_originDate;
+ customFormatter->m_selectionFormat = m_selectionFormat;
+}
+//! [1]
+
+//! [2]
+void CustomFormatter::recalculate()
+{
+ // We want our axis to always have gridlines at date breaks
+
+ // Convert range into QDateTimes
+ QDateTime minTime = valueToDateTime(qreal(axis()->min()));
+ QDateTime maxTime = valueToDateTime(qreal(axis()->max()));
+
+ // Find out the grid counts
+ QTime midnight(0, 0);
+ QDateTime minFullDate(minTime.date(), midnight);
+ int gridCount = 0;
+ if (minFullDate != minTime)
+ minFullDate = minFullDate.addDays(1);
+ QDateTime maxFullDate(maxTime.date(), midnight);
+
+ gridCount += minFullDate.daysTo(maxFullDate) + 1;
+ int subGridCount = axis()->subSegmentCount() - 1;
+
+ // Reserve space for position arrays and label strings
+ gridPositions().resize(gridCount);
+ subGridPositions().resize((gridCount + 1) * subGridCount);
+ labelPositions().resize(gridCount);
+ labelStrings().reserve(gridCount);
+
+ // Calculate positions and format labels
+ qint64 startMs = minTime.toMSecsSinceEpoch();
+ qint64 endMs = maxTime.toMSecsSinceEpoch();
+ qreal dateNormalizer = endMs - startMs;
+ qreal firstLineOffset = (minFullDate.toMSecsSinceEpoch() - startMs) / dateNormalizer;
+ qreal segmentStep = oneDayMs / dateNormalizer;
+ qreal subSegmentStep = 0;
+ if (subGridCount > 0)
+ subSegmentStep = segmentStep / qreal(subGridCount + 1);
+
+ for (int i = 0; i < gridCount; i++) {
+ qreal gridValue = firstLineOffset + (segmentStep * qreal(i));
+ gridPositions()[i] = float(gridValue);
+ labelPositions()[i] = float(gridValue);
+ labelStrings() << minFullDate.addDays(i).toString(axis()->labelFormat());
+ }
+
+ for (int i = 0; i <= gridCount; i++) {
+ if (subGridPositions().size()) {
+ for (int j = 0; j < subGridCount; j++) {
+ float position;
+ if (i)
+ position = gridPositions().at(i - 1) + subSegmentStep * (j + 1);
+ else
+ position = gridPositions().at(0) - segmentStep + subSegmentStep * (j + 1);
+ if (position > 1.0f || position < 0.0f)
+ position = gridPositions().at(0);
+ subGridPositions()[i * subGridCount + j] = position;
+ }
+ }
+ }
+}
+//! [2]
+
+//! [3]
+QString CustomFormatter::stringForValue(qreal value, const QString &format) const
+{
+ Q_UNUSED(format)
+
+ return valueToDateTime(value).toString(m_selectionFormat);
+}
+//! [3]
+
+QDate CustomFormatter::originDate() const
+{
+ return m_originDate;
+}
+
+QString CustomFormatter::selectionFormat() const
+{
+ return m_selectionFormat;
+}
+
+void CustomFormatter::setOriginDate(const QDate &date)
+{
+ if (m_originDate != date) {
+ m_originDate = date;
+ markDirty(true);
+ emit originDateChanged(date);
+ }
+}
+
+void CustomFormatter::setSelectionFormat(const QString &format)
+{
+ if (m_selectionFormat != format) {
+ m_selectionFormat = format;
+ markDirty(true); // Necessary to regenerate already visible selection label
+ emit selectionFormatChanged(format);
+ }
+}
+
+//! [0]
+QDateTime CustomFormatter::valueToDateTime(qreal value) const
+{
+ return QDateTime(m_originDate).addMSecs(qint64(oneDayMs * value));
+}
+//! [0]
diff --git a/examples/datavisualization/qmlaxisformatter/customformatter.h b/examples/datavisualization/qmlaxisformatter/customformatter.h
new file mode 100644
index 00000000..d439e56a
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/customformatter.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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 CUSTOMFORMATTER_H
+#define CUSTOMFORMATTER_H
+
+#include <QtDataVisualization/QValue3DAxisFormatter>
+#include <QtCore/QDateTime>
+#include <QtCore/QVector>
+
+using namespace QtDataVisualization;
+
+//! [2]
+class CustomFormatter : public QValue3DAxisFormatter
+{
+ //! [2]
+ Q_OBJECT
+
+ //! [1]
+ Q_PROPERTY(QDate originDate READ originDate WRITE setOriginDate NOTIFY originDateChanged)
+ //! [1]
+ //! [3]
+ Q_PROPERTY(QString selectionFormat READ selectionFormat WRITE setSelectionFormat NOTIFY selectionFormatChanged)
+ //! [3]
+public:
+ explicit CustomFormatter(QObject *parent = 0);
+ virtual ~CustomFormatter();
+
+ //! [0]
+ virtual QValue3DAxisFormatter *createNewInstance() const;
+ virtual void populateCopy(QValue3DAxisFormatter &copy) const;
+ virtual void recalculate();
+ virtual QString stringForValue(qreal value, const QString &format) const;
+ //! [0]
+
+ QDate originDate() const;
+ QString selectionFormat() const;
+
+public slots:
+ void setOriginDate(const QDate &date);
+ void setSelectionFormat(const QString &format);
+
+signals:
+ void originDateChanged(const QDate &date);
+ void selectionFormatChanged(const QString &format);
+
+private:
+ Q_DISABLE_COPY(CustomFormatter)
+
+ QDateTime valueToDateTime(qreal value) const;
+
+ QDate m_originDate;
+ QString m_selectionFormat;
+};
+
+#endif
diff --git a/examples/datavisualization/qmlaxisformatter/doc/images/qmlaxisformatter-example.png b/examples/datavisualization/qmlaxisformatter/doc/images/qmlaxisformatter-example.png
new file mode 100644
index 00000000..fbfbd833
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/doc/images/qmlaxisformatter-example.png
Binary files differ
diff --git a/examples/datavisualization/qmlaxisformatter/doc/src/qmlaxisformatter.qdoc b/examples/datavisualization/qmlaxisformatter/doc/src/qmlaxisformatter.qdoc
new file mode 100644
index 00000000..3568e507
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/doc/src/qmlaxisformatter.qdoc
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 qmlaxisformatter
+ \title Qt Quick 2 Axis Formatter Example
+ \ingroup qtdatavisualization_examples
+ \brief Example of a hybrid C++ and QML application demonstrating different axis formatters.
+ \since QtDataVisualization 1.1
+
+ The Qt Quick axis formatter example shows how to use predefined axis formatters and how to
+ create a custom one.
+
+ \image qmlaxisformatter-example.png
+
+ The interesting thing about this example is axis formatters, so we'll concentrate on
+ that and skip explaining the basic functionality - for
+ more detailed QML example documentation, see \l{Qt Quick 2 Scatter Example}.
+
+ \section1 Custom axis formatter
+
+ Customizing axis formatters requires subclassing the QValue3DAxisFormatter, which cannot be
+ done in QML code alone. In this example we want an axis that interprets the float values as
+ a timestamp and shows the date in the axis labels. To achieve this, we introduce a new class
+ called \c CustomFormatter, which subclasses the QValue3DAxisFormatter:
+
+ \snippet qmlaxisformatter/customformatter.h 2
+ \dots 0
+
+ Since float values of a QScatter3DSeries cannot be directly cast into QDateTime values due to
+ difference in data width, we need some sort of mapping between the two. We chose to do the
+ mapping by specifying an origin date for the formatter and interpreting the float values
+ from the QScatter3DSeries as date offsets to that origin value. The origin date is given as
+ a property:
+
+ \snippet qmlaxisformatter/customformatter.h 1
+
+ The mapping from value to QDateTime is done using \c valueToDateTime() method:
+
+ \snippet qmlaxisformatter/customformatter.cpp 0
+
+ To function as an axis formatter, our \c CustomFormatter needs to reimplement some virtual
+ methods:
+
+ \snippet qmlaxisformatter/customformatter.h 0
+
+ The first two are simple, we just create a new instance of \c CustomFormatter and copy the
+ necessary data over to it. These two methods are used to create and update a cache of formatter for
+ rendering purposes. It is important to remember to call the superclass implementation
+ of \c populateCopy():
+
+ \snippet qmlaxisformatter/customformatter.cpp 1
+
+ Bulk of the work done by \c CustomFormatter is done in the \c recalculate() method, where
+ our formatter calculates the grid, subgrid, and label positions, as well as formats the label
+ strings.
+ In our custom formatter we ignore the segment count of the axis and draw a grid line always at
+ midnight. Subsegment count and label positioning is handled normally:
+
+ \snippet qmlaxisformatter/customformatter.cpp 2
+
+ The axis labels are formatted to show only the date, but for selection label we want little more
+ resolution for the timestamp, so we specify another property for our custom formatter to allow
+ user to customize it:
+
+ \snippet qmlaxisformatter/customformatter.h 3
+
+ This selection format property is used in the reimplemented \c stringToValue method, where we
+ ignore the submitted format and substitute the custom selection format for it:
+
+ \snippet qmlaxisformatter/customformatter.cpp 3
+
+ To expose our new custom formatter to the QML, we must declare and register it:
+
+ \snippet qmlaxisformatter/main.cpp 0
+ \dots 0
+ \snippet qmlaxisformatter/main.cpp 1
+
+ \section1 QML
+
+ In the QML codes, we define a different axis for each dimension:
+
+ \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 3
+
+ Z-axis is just a regular ValueAxis3D:
+
+ \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 0
+
+ For the Y-axis we define a logarithmic axis. ValueAxis3D can be made to show logarithmic scale
+ by specifying LogValueAxis3DFormatter for \c formatter property of the axis:
+
+ \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 2
+
+ And finally, for the X-axis we use our new \c CustomFormatter:
+
+ \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 1
+
+ Rest of the application consists of fairly self-explanatory logic for modifying the axes and
+ showing the graph.
+*/
diff --git a/examples/datavisualization/qmlaxisformatter/main.cpp b/examples/datavisualization/qmlaxisformatter/main.cpp
new file mode 100644
index 00000000..05ed7b37
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/main.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** 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 "customformatter.h"
+
+#include <QtGui/QGuiApplication>
+#include <QtQuick/QQuickView>
+#include <QtQml>
+
+//! [0]
+Q_DECLARE_METATYPE(CustomFormatter *)
+//! [0]
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ //! [1]
+ qmlRegisterType<CustomFormatter>("CustomFormatter", 1, 0, "CustomFormatter");
+ //! [1]
+
+ 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("Axis formatter example"));
+
+ viewer.setSource(QUrl("qrc:/qml/qmlaxisformatter/main.qml"));
+ viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+ viewer.show();
+
+ return app.exec();
+}
diff --git a/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/Data.qml b/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/Data.qml
new file mode 100644
index 00000000..e692c090
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/Data.qml
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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{ xPos: 2.456103; yPos: 1.0; zPos: 5.0 }
+ ListElement{ xPos: 5.687549; yPos: 3.0; zPos: 2.5 }
+ ListElement{ xPos: 2.357458; yPos: 4.1; zPos: 1.0 }
+ ListElement{ xPos: 4.567458; yPos: 4.75; zPos: 3.9 }
+ ListElement{ xPos: 6.885439; yPos: 4.9; zPos: 7.2 }
+ ListElement{ xPos: 2.366769; yPos: 13.42; zPos: 3.5 }
+ ListElement{ xPos: 7.546457; yPos: 233.1; zPos: 6.9 }
+ ListElement{ xPos: 2.475867; yPos: 32.91; zPos: 4.1 }
+ ListElement{ xPos: 8.456546; yPos: 153.68; zPos: 9.52 }
+ ListElement{ xPos: 3.456348; yPos: 52.96; zPos: 1.6 }
+ ListElement{ xPos: 1.536446; yPos: 32.4; zPos: 2.92 }
+ ListElement{ xPos: 8.456666; yPos: 114.74; zPos: 8.18 }
+ ListElement{ xPos: 5.468486; yPos: 83.1; zPos: 3.8 }
+ ListElement{ xPos: 6.546586 ; yPos: 63.66; zPos: 3.58 }
+ ListElement{ xPos: 8.567516 ; yPos: 1.82; zPos: 4.64 }
+ ListElement{ xPos: 7.678984 ; yPos: 213.18; zPos: 7.22 }
+ ListElement{ xPos: 7.457569 ; yPos: 63.06; zPos: 4.3 }
+ ListElement{ xPos: 8.456755 ; yPos: 122.64; zPos: 6.44 }
+ ListElement{ xPos: 6.234536 ; yPos: 63.96; zPos: 4.38 }
+ ListElement{ xPos: 9.456718 ; yPos: 243.32; zPos: 4.04 }
+ ListElement{ xPos: 10.789889 ; yPos: 43.4; zPos: 2.78 }
+ ListElement{ xPos: 11.346554 ; yPos: 345.12; zPos: 3.1 }
+ ListElement{ xPos: 12.023454 ; yPos: 500.0; zPos: 3.68 }
+ }
+}
diff --git a/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/NewButton.qml b/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/NewButton.qml
new file mode 100644
index 00000000..e4fb99d2
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/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/qmlaxisformatter/qml/qmlaxisformatter/main.qml b/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/main.qml
new file mode 100644
index 00000000..7aba08c6
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/main.qml
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** 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 QtDataVisualization 1.1
+import CustomFormatter 1.0
+import "."
+
+Rectangle {
+ id: mainView
+ width: 1280
+ height: 1024
+
+ Data {
+ id: seriesData
+ }
+
+ Theme3D {
+ id: themeIsabelle
+ type: Theme3D.ThemePrimaryColors
+ font.family: "Lucida Handwriting"
+ font.pointSize: 40
+ }
+
+ //! [1]
+ ValueAxis3D {
+ id: dateAxis
+ formatter: CustomFormatter {
+ originDate: "2014-01-01"
+ selectionFormat: "yyyy-MM-dd HH:mm:ss"
+ }
+ subSegmentCount: 2
+ labelFormat: "yyyy-MM-dd"
+ min: 0
+ max: 14
+ }
+ //! [1]
+
+ //! [2]
+ ValueAxis3D {
+ id: logAxis
+ formatter: LogValueAxis3DFormatter {
+ id: logAxisFormatter
+ base: 10
+ autoSubGrid: true
+ showEdgeLabels: true
+ }
+ labelFormat: "%.2f"
+ }
+ //! [2]
+
+ ValueAxis3D {
+ id: linearAxis
+ labelFormat: "%.2f"
+ min: 0
+ max: 500
+ }
+
+ //! [0]
+ ValueAxis3D {
+ id: valueAxis
+ segmentCount: 5
+ subSegmentCount: 2
+ labelFormat: "%.2f"
+ min: 0
+ max: 10
+ }
+ //! [0]
+
+ Item {
+ id: dataView
+ anchors.bottom: parent.bottom
+ width: parent.width
+ height: parent.height - buttonLayout.height
+
+ Scatter3D {
+ id: scatterGraph
+ width: dataView.width
+ height: dataView.height
+ theme: themeIsabelle
+ shadowQuality: AbstractGraph3D.ShadowQualitySoftLow
+ scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricRight
+ //! [3]
+ axisZ: valueAxis
+ axisY: logAxis
+ axisX: dateAxis
+ //! [3]
+
+ Scatter3DSeries {
+ id: scatterSeries
+ itemLabelFormat: "@xLabel - (@yLabel, @zLabel)"
+ meshSmooth: true
+ ItemModelScatterDataProxy {
+ itemModel: seriesData.model
+ xPosRole: "xPos"
+ yPosRole: "yPos"
+ zPosRole: "zPos"
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ id: buttonLayout
+ Layout.minimumHeight: exitButton.height
+ width: parent.width
+ anchors.left: parent.left
+ spacing: 0
+
+ NewButton {
+ id: yAxisBaseChange
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ state: "enabled"
+ onClicked: {
+ if (logAxisFormatter.base === 10)
+ logAxisFormatter.base = 0
+ else if (logAxisFormatter.base === 2)
+ logAxisFormatter.base = 10
+ else
+ logAxisFormatter.base = 2
+ }
+ states: [
+ State {
+ name: "enabled"
+ PropertyChanges {
+ target: yAxisBaseChange
+ text: "Y-axis log base: " + logAxisFormatter.base
+ enabled: true
+ }
+ },
+ State {
+ name: "disabled"
+ PropertyChanges {
+ target: yAxisBaseChange
+ text: "Y-axis linear"
+ enabled: false
+ }
+ }
+ ]
+ }
+
+ NewButton {
+ id: yAxisToggle
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ text: "Toggle Y-axis"
+ onClicked: {
+ if (scatterGraph.axisY === linearAxis) {
+ scatterGraph.axisY = logAxis
+ yAxisBaseChange.state = "enabled"
+ } else {
+ scatterGraph.axisY = linearAxis
+ yAxisBaseChange.state = "disabled"
+ }
+ }
+ }
+
+ NewButton {
+ id: exitButton
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ text: "Quit"
+ onClicked: Qt.quit(0);
+ }
+ }
+}
diff --git a/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.pro b/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.pro
new file mode 100644
index 00000000..0f3b2f80
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.pro
@@ -0,0 +1,16 @@
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+QT += datavisualization
+
+# The .cpp file which was generated for your project. Feel free to hack it.
+SOURCES += main.cpp \
+ customformatter.cpp
+HEADERS += customformatter.h
+
+RESOURCES += qmlaxisformatter.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/* \
+ qml/qmlaxisformatter/*
diff --git a/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.qrc b/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.qrc
new file mode 100644
index 00000000..0cd9e927
--- /dev/null
+++ b/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/qmlaxisformatter/main.qml</file>
+ <file>qml/qmlaxisformatter/NewButton.qml</file>
+ <file>qml/qmlaxisformatter/Data.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/datavisualization/qmlbars/doc/images/qmlbars-example.png b/examples/datavisualization/qmlbars/doc/images/qmlbars-example.png
index 64ad6b0e..c2ab2459 100644
--- a/examples/datavisualization/qmlbars/doc/images/qmlbars-example.png
+++ b/examples/datavisualization/qmlbars/doc/images/qmlbars-example.png
Binary files differ
diff --git a/examples/datavisualization/qmlbars/doc/src/qmlbars.qdoc b/examples/datavisualization/qmlbars/doc/src/qmlbars.qdoc
index d3da5e6a..01131388 100644
--- a/examples/datavisualization/qmlbars/doc/src/qmlbars.qdoc
+++ b/examples/datavisualization/qmlbars/doc/src/qmlbars.qdoc
@@ -39,9 +39,9 @@
\snippet qmlbars/qml/qmlbars/Data.qml 0
\dots
- Each data item has four roles: year, month, income, and expenses. Years and months are natural to
- map to rows and columns of a bar chart, but we can only show either income or expenses as the
- value.
+ Each data item has three roles: timestamp, income, and expenses. The timestamp value is in
+ format: \c{<four digit year>-<two digit month>}. Years and months are natural to map to rows and
+ columns of a bar chart, but we can only show either income or expenses as the value.
Now we need to add the data to the Bars3D graph. We will create two Bar3DSeries inside it,
starting with a series for the income:
@@ -50,23 +50,47 @@
\dots
The data is attached to the \c itemModel property of the ItemModelBarDataProxy inside the
- series.
+ series. For \c valueRole we simply specify the \c income field, as it contains the value we
+ want, but getting the years and months is a bit more complicated, since they are both found
+ in the same field. To extract those values, we specify the \c timestamp field for both
+ \c rowRole and \c columnRole, and additionally specify a search pattern and a replace rule
+ for those roles to extract the correct portion of the field contents for each role.
+ The search pattern is a normal JavaScript regular expression and the replace rule specifies
+ what the field content that matches the regular expression is replaced with.
+ In this case we want to replace the entire field content with just the year or the month,
+ which is the first captured substring for both rows and columns.
+ For more information how the replace using regular expressions works, see
+ QString::replace(const QRegExp &rx, const QString &after) function documentation.
+
+ The \c multiMatchBehavior property specifies what to do in case multiple item model items match
+ the same row/column combination. In this case we want to add their values together.
+ This property has no effect when we are showing values for each month, as there are no
+ duplicate months in our item model, but it becomes relevant later when we want to show
+ the yearly totals.
Then we add another series for the expenses:
\snippet qmlbars/qml/qmlbars/main.qml 4
\dots
+ The model contains expenses as negative values, but we want to show them as positive bars, so
+ that we can easily compare them to income bars. We use \c valueRolePattern to remove the minus
+ sign to achieve this. No replacement string needs to be specified as the default replacement
+ is an empty string.
+
We use the \c visible property of the series to hide the second series for now.
\section1 Custom axis labels
One interesting tidbit about axes is that we redefine the category labels for column axis in
- \c Axes.qml. This is done because the data contains abbreviated month names, which we don't want
+ \c Axes.qml. This is done because the data contains numbers for months, which we don't want
to use for our column labels:
\snippet qmlbars/qml/qmlbars/Axes.qml 0
+ We also set automatic axis label rotation to make axis labels more readable at low camera
+ angles.
+
\section1 Switching series
In the \c main.qml, we set up the graph and various UI elements. There are three interesting
@@ -76,15 +100,23 @@
\snippet qmlbars/qml/qmlbars/main.qml 0
- The axis change is done because income and expenses have a different label format. The same could have
- been achieved using a single axis and just changing the label format.
+ The axis label format and item selection label formats are tweaked to get the negative sign
+ showing properly for expenses, which were actually resolved as positive values.
- The second interesting block is where we filter some of the rows away from the visualized data:
+ The second interesting block is where we change the visualized data by adjusting the proxy
+ propertes:
\snippet qmlbars/qml/qmlbars/main.qml 1
- The filtering is done by setting \c autoRowCategories to false on the ItemModelBarDataProxy item and defining
- the row categories explicitly. This way, only the items in specified rows are visualized.
+ To show yearly totals, we need to combine the twelve months of each year into a single bar.
+ We achieve this by specifying a \c columnRolePattern that matches all model items. That way
+ the data proxy will only have a single column. The cumulative \c multiMatchBehavior we
+ specified earlier for the proxy becomes relevant now, causing the values of all twelve months
+ of each year to be added up into a single bar.
+
+ To show just a subset of years, we set \c autoRowCategories to false on the
+ ItemModelBarDataProxy item and define the row categories explicitly. This way, only the items
+ in specified row categories are visualized.
The third interesting block shows how to get the row and column index of an item if you know the
row and column values by using ItemModelBarDataProxy methods \c rowCategoryIndex() and \c columnCategoryIndex():
diff --git a/examples/datavisualization/qmlbars/qml/qmlbars/Axes.qml b/examples/datavisualization/qmlbars/qml/qmlbars/Axes.qml
index 29979e1b..f316eef5 100644
--- a/examples/datavisualization/qmlbars/qml/qmlbars/Axes.qml
+++ b/examples/datavisualization/qmlbars/qml/qmlbars/Axes.qml
@@ -17,15 +17,13 @@
****************************************************************************/
import QtQuick 2.1
-import QtDataVisualization 1.0
+import QtDataVisualization 1.1
Item {
property alias column: columnAxis
- property alias expenses: expensesAxis
- property alias income: incomeAxis
-
- // For row labels we can use row labels from data proxy, so default axis
- // suffices for rows.
+ property alias row: rowAxis
+ property alias value: valueAxis
+ property alias total: totalAxis
// Custom labels for columns, since the data contains abbreviated month names.
//! [0]
@@ -33,20 +31,26 @@ Item {
id: columnAxis
labels: ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]
+ labelAutoRotation: 30
}
//! [0]
+ CategoryAxis3D {
+ id: totalAxis
+ labels: ["Yearly total"]
+ labelAutoRotation: 30
+ }
+ CategoryAxis3D {
+ // For row labels we can use row labels from data proxy, no labels defined for rows.
+ id: rowAxis
+ labelAutoRotation: 30
+ }
+
ValueAxis3D {
- id: incomeAxis
+ id: valueAxis
min: 0
max: 35
labelFormat: "%.2f M\u20AC"
title: "Monthly income"
- }
- ValueAxis3D {
- id: expensesAxis
- min: 0
- max: 35
- labelFormat: "-%.2f M\u20AC"
- title: "Monthly expenses"
+ labelAutoRotation: 90
}
}
diff --git a/examples/datavisualization/qmlbars/qml/qmlbars/Data.qml b/examples/datavisualization/qmlbars/qml/qmlbars/Data.qml
index 7e0978c6..dcb89dbf 100644
--- a/examples/datavisualization/qmlbars/qml/qmlbars/Data.qml
+++ b/examples/datavisualization/qmlbars/qml/qmlbars/Data.qml
@@ -24,96 +24,96 @@ Item {
//! [0]
ListModel {
id: dataModel
- ListElement{ year: "2006"; month: "Jan"; expenses: "4"; income: "5" }
- ListElement{ year: "2006"; month: "Feb"; expenses: "5"; income: "6" }
- ListElement{ year: "2006"; month: "Mar"; expenses: "7"; income: "4" }
+ ListElement{ timestamp: "2006-01"; expenses: "-4"; income: "5" }
+ ListElement{ timestamp: "2006-02"; expenses: "-5"; income: "6" }
+ ListElement{ timestamp: "2006-03"; expenses: "-7"; income: "4" }
//! [0]
- ListElement{ year: "2006"; month: "Apr"; expenses: "3"; income: "2" }
- ListElement{ year: "2006"; month: "May"; expenses: "4"; income: "1" }
- ListElement{ year: "2006"; month: "Jun"; expenses: "2"; income: "2" }
- ListElement{ year: "2006"; month: "Jul"; expenses: "1"; income: "3" }
- ListElement{ year: "2006"; month: "Aug"; expenses: "5"; income: "1" }
- ListElement{ year: "2006"; month: "Sep"; expenses: "2"; income: "3" }
- ListElement{ year: "2006"; month: "Oct"; expenses: "5"; income: "2" }
- ListElement{ year: "2006"; month: "Nov"; expenses: "8"; income: "5" }
- ListElement{ year: "2006"; month: "Dec"; expenses: "3"; income: "3" }
+ ListElement{ timestamp: "2006-04"; expenses: "-3"; income: "2" }
+ ListElement{ timestamp: "2006-05"; expenses: "-4"; income: "1" }
+ ListElement{ timestamp: "2006-06"; expenses: "-2"; income: "2" }
+ ListElement{ timestamp: "2006-07"; expenses: "-1"; income: "3" }
+ ListElement{ timestamp: "2006-08"; expenses: "-5"; income: "1" }
+ ListElement{ timestamp: "2006-09"; expenses: "-2"; income: "3" }
+ ListElement{ timestamp: "2006-10"; expenses: "-5"; income: "2" }
+ ListElement{ timestamp: "2006-11"; expenses: "-8"; income: "5" }
+ ListElement{ timestamp: "2006-12"; expenses: "-3"; income: "3" }
- ListElement{ year: "2007"; month: "Jan"; expenses: "3"; income: "1" }
- ListElement{ year: "2007"; month: "Feb"; expenses: "4"; income: "2" }
- ListElement{ year: "2007"; month: "Mar"; expenses: "12"; income: "4" }
- ListElement{ year: "2007"; month: "Apr"; expenses: "13"; income: "6" }
- ListElement{ year: "2007"; month: "May"; expenses: "14"; income: "11" }
- ListElement{ year: "2007"; month: "Jun"; expenses: "7"; income: "7" }
- ListElement{ year: "2007"; month: "Jul"; expenses: "6"; income: "4" }
- ListElement{ year: "2007"; month: "Aug"; expenses: "4"; income: "15" }
- ListElement{ year: "2007"; month: "Sep"; expenses: "2"; income: "18" }
- ListElement{ year: "2007"; month: "Oct"; expenses: "29"; income: "25" }
- ListElement{ year: "2007"; month: "Nov"; expenses: "23"; income: "29" }
- ListElement{ year: "2007"; month: "Dec"; expenses: "5"; income: "9" }
+ ListElement{ timestamp: "2007-01"; expenses: "-3"; income: "1" }
+ ListElement{ timestamp: "2007-02"; expenses: "-4"; income: "2" }
+ ListElement{ timestamp: "2007-03"; expenses: "-12"; income: "4" }
+ ListElement{ timestamp: "2007-04"; expenses: "-13"; income: "6" }
+ ListElement{ timestamp: "2007-05"; expenses: "-14"; income: "11" }
+ ListElement{ timestamp: "2007-06"; expenses: "-7"; income: "7" }
+ ListElement{ timestamp: "2007-07"; expenses: "-6"; income: "4" }
+ ListElement{ timestamp: "2007-08"; expenses: "-4"; income: "15" }
+ ListElement{ timestamp: "2007-09"; expenses: "-2"; income: "18" }
+ ListElement{ timestamp: "2007-10"; expenses: "-29"; income: "25" }
+ ListElement{ timestamp: "2007-11"; expenses: "-23"; income: "29" }
+ ListElement{ timestamp: "2007-12"; expenses: "-5"; income: "9" }
- ListElement{ year: "2008"; month: "Jan"; expenses: "3"; income: "8" }
- ListElement{ year: "2008"; month: "Feb"; expenses: "8"; income: "14" }
- ListElement{ year: "2008"; month: "Mar"; expenses: "10"; income: "20" }
- ListElement{ year: "2008"; month: "Apr"; expenses: "12"; income: "24" }
- ListElement{ year: "2008"; month: "May"; expenses: "10"; income: "19" }
- ListElement{ year: "2008"; month: "Jun"; expenses: "5"; income: "8" }
- ListElement{ year: "2008"; month: "Jul"; expenses: "1"; income: "4" }
- ListElement{ year: "2008"; month: "Aug"; expenses: "7"; income: "12" }
- ListElement{ year: "2008"; month: "Sep"; expenses: "4"; income: "16" }
- ListElement{ year: "2008"; month: "Oct"; expenses: "22"; income: "33" }
- ListElement{ year: "2008"; month: "Nov"; expenses: "16"; income: "25" }
- ListElement{ year: "2008"; month: "Dec"; expenses: "2"; income: "7" }
+ ListElement{ timestamp: "2008-01"; expenses: "-3"; income: "8" }
+ ListElement{ timestamp: "2008-02"; expenses: "-8"; income: "14" }
+ ListElement{ timestamp: "2008-03"; expenses: "-10"; income: "20" }
+ ListElement{ timestamp: "2008-04"; expenses: "-12"; income: "24" }
+ ListElement{ timestamp: "2008-05"; expenses: "-10"; income: "19" }
+ ListElement{ timestamp: "2008-06"; expenses: "-5"; income: "8" }
+ ListElement{ timestamp: "2008-07"; expenses: "-1"; income: "4" }
+ ListElement{ timestamp: "2008-08"; expenses: "-7"; income: "12" }
+ ListElement{ timestamp: "2008-09"; expenses: "-4"; income: "16" }
+ ListElement{ timestamp: "2008-10"; expenses: "-22"; income: "33" }
+ ListElement{ timestamp: "2008-11"; expenses: "-16"; income: "25" }
+ ListElement{ timestamp: "2008-12"; expenses: "-2"; income: "7" }
- ListElement{ year: "2009"; month: "Jan"; expenses: "4"; income: "5" }
- ListElement{ year: "2009"; month: "Feb"; expenses: "4"; income: "7" }
- ListElement{ year: "2009"; month: "Mar"; expenses: "11"; income: "14" }
- ListElement{ year: "2009"; month: "Apr"; expenses: "16"; income: "22" }
- ListElement{ year: "2009"; month: "May"; expenses: "3"; income: "5" }
- ListElement{ year: "2009"; month: "Jun"; expenses: "4"; income: "8" }
- ListElement{ year: "2009"; month: "Jul"; expenses: "7"; income: "9" }
- ListElement{ year: "2009"; month: "Aug"; expenses: "9"; income: "13" }
- ListElement{ year: "2009"; month: "Sep"; expenses: "1"; income: "6" }
- ListElement{ year: "2009"; month: "Oct"; expenses: "14"; income: "25" }
- ListElement{ year: "2009"; month: "Nov"; expenses: "19"; income: "29" }
- ListElement{ year: "2009"; month: "Dec"; expenses: "5"; income: "7" }
+ ListElement{ timestamp: "2009-01"; expenses: "-4"; income: "5" }
+ ListElement{ timestamp: "2009-02"; expenses: "-4"; income: "7" }
+ ListElement{ timestamp: "2009-03"; expenses: "-11"; income: "14" }
+ ListElement{ timestamp: "2009-04"; expenses: "-16"; income: "22" }
+ ListElement{ timestamp: "2009-05"; expenses: "-3"; income: "5" }
+ ListElement{ timestamp: "2009-06"; expenses: "-4"; income: "8" }
+ ListElement{ timestamp: "2009-07"; expenses: "-7"; income: "9" }
+ ListElement{ timestamp: "2009-08"; expenses: "-9"; income: "13" }
+ ListElement{ timestamp: "2009-09"; expenses: "-1"; income: "6" }
+ ListElement{ timestamp: "2009-10"; expenses: "-14"; income: "25" }
+ ListElement{ timestamp: "2009-11"; expenses: "-19"; income: "29" }
+ ListElement{ timestamp: "2009-12"; expenses: "-5"; income: "7" }
- ListElement{ year: "2010"; month: "Jan"; expenses: "14"; income: "22" }
- ListElement{ year: "2010"; month: "Feb"; expenses: "5"; income: "7" }
- ListElement{ year: "2010"; month: "Mar"; expenses: "1"; income: "9" }
- ListElement{ year: "2010"; month: "Apr"; expenses: "1"; income: "12" }
- ListElement{ year: "2010"; month: "May"; expenses: "5"; income: "9" }
- ListElement{ year: "2010"; month: "Jun"; expenses: "5"; income: "8" }
- ListElement{ year: "2010"; month: "Jul"; expenses: "3"; income: "7" }
- ListElement{ year: "2010"; month: "Aug"; expenses: "1"; income: "5" }
- ListElement{ year: "2010"; month: "Sep"; expenses: "2"; income: "4" }
- ListElement{ year: "2010"; month: "Oct"; expenses: "10"; income: "13" }
- ListElement{ year: "2010"; month: "Nov"; expenses: "12"; income: "17" }
- ListElement{ year: "2010"; month: "Dec"; expenses: "6"; income: "9" }
+ ListElement{ timestamp: "2010-01"; expenses: "-14"; income: "22" }
+ ListElement{ timestamp: "2010-02"; expenses: "-5"; income: "7" }
+ ListElement{ timestamp: "2010-03"; expenses: "-1"; income: "9" }
+ ListElement{ timestamp: "2010-04"; expenses: "-1"; income: "12" }
+ ListElement{ timestamp: "2010-05"; expenses: "-5"; income: "9" }
+ ListElement{ timestamp: "2010-06"; expenses: "-5"; income: "8" }
+ ListElement{ timestamp: "2010-07"; expenses: "-3"; income: "7" }
+ ListElement{ timestamp: "2010-08"; expenses: "-1"; income: "5" }
+ ListElement{ timestamp: "2010-09"; expenses: "-2"; income: "4" }
+ ListElement{ timestamp: "2010-10"; expenses: "-10"; income: "13" }
+ ListElement{ timestamp: "2010-11"; expenses: "-12"; income: "17" }
+ ListElement{ timestamp: "2010-12"; expenses: "-6"; income: "9" }
- ListElement{ year: "2011"; month: "Jan"; expenses: "2"; income: "6" }
- ListElement{ year: "2011"; month: "Feb"; expenses: "4"; income: "8" }
- ListElement{ year: "2011"; month: "Mar"; expenses: "7"; income: "12" }
- ListElement{ year: "2011"; month: "Apr"; expenses: "9"; income: "15" }
- ListElement{ year: "2011"; month: "May"; expenses: "7"; income: "19" }
- ListElement{ year: "2011"; month: "Jun"; expenses: "9"; income: "18" }
- ListElement{ year: "2011"; month: "Jul"; expenses: "13"; income: "17" }
- ListElement{ year: "2011"; month: "Aug"; expenses: "5"; income: "9" }
- ListElement{ year: "2011"; month: "Sep"; expenses: "3"; income: "8" }
- ListElement{ year: "2011"; month: "Oct"; expenses: "13"; income: "15" }
- ListElement{ year: "2011"; month: "Nov"; expenses: "8"; income: "17" }
- ListElement{ year: "2011"; month: "Dec"; expenses: "7"; income: "10" }
+ ListElement{ timestamp: "2011-01"; expenses: "-2"; income: "6" }
+ ListElement{ timestamp: "2011-02"; expenses: "-4"; income: "8" }
+ ListElement{ timestamp: "2011-03"; expenses: "-7"; income: "12" }
+ ListElement{ timestamp: "2011-04"; expenses: "-9"; income: "15" }
+ ListElement{ timestamp: "2011-05"; expenses: "-7"; income: "19" }
+ ListElement{ timestamp: "2011-06"; expenses: "-9"; income: "18" }
+ ListElement{ timestamp: "2011-07"; expenses: "-13"; income: "17" }
+ ListElement{ timestamp: "2011-08"; expenses: "-5"; income: "9" }
+ ListElement{ timestamp: "2011-09"; expenses: "-3"; income: "8" }
+ ListElement{ timestamp: "2011-10"; expenses: "-13"; income: "15" }
+ ListElement{ timestamp: "2011-11"; expenses: "-8"; income: "17" }
+ ListElement{ timestamp: "2011-12"; expenses: "-7"; income: "10" }
- ListElement{ year: "2012"; month: "Jan"; expenses: "12"; income: "16" }
- ListElement{ year: "2012"; month: "Feb"; expenses: "24"; income: "28" }
- ListElement{ year: "2012"; month: "Mar"; expenses: "27"; income: "22" }
- ListElement{ year: "2012"; month: "Apr"; expenses: "29"; income: "25" }
- ListElement{ year: "2012"; month: "May"; expenses: "27"; income: "29" }
- ListElement{ year: "2012"; month: "Jun"; expenses: "19"; income: "18" }
- ListElement{ year: "2012"; month: "Jul"; expenses: "13"; income: "17" }
- ListElement{ year: "2012"; month: "Aug"; expenses: "15"; income: "19" }
- ListElement{ year: "2012"; month: "Sep"; expenses: "3"; income: "8" }
- ListElement{ year: "2012"; month: "Oct"; expenses: "3"; income: "6" }
- ListElement{ year: "2012"; month: "Nov"; expenses: "4"; income: "8" }
- ListElement{ year: "2012"; month: "Dec"; expenses: "5"; income: "9" }
+ ListElement{ timestamp: "2012-01"; expenses: "-12"; income: "16" }
+ ListElement{ timestamp: "2012-02"; expenses: "-24"; income: "28" }
+ ListElement{ timestamp: "2012-03"; expenses: "-27"; income: "22" }
+ ListElement{ timestamp: "2012-04"; expenses: "-29"; income: "25" }
+ ListElement{ timestamp: "2012-05"; expenses: "-27"; income: "29" }
+ ListElement{ timestamp: "2012-06"; expenses: "-19"; income: "18" }
+ ListElement{ timestamp: "2012-07"; expenses: "-13"; income: "17" }
+ ListElement{ timestamp: "2012-08"; expenses: "-15"; income: "19" }
+ ListElement{ timestamp: "2012-09"; expenses: "-3"; income: "8" }
+ ListElement{ timestamp: "2012-10"; expenses: "-3"; income: "6" }
+ ListElement{ timestamp: "2012-11"; expenses: "-4"; income: "8" }
+ ListElement{ timestamp: "2012-12"; expenses: "-5"; income: "9" }
}
}
diff --git a/examples/datavisualization/qmlbars/qml/qmlbars/main.qml b/examples/datavisualization/qmlbars/qml/qmlbars/main.qml
index 0df8d8ae..5172e27d 100644
--- a/examples/datavisualization/qmlbars/qml/qmlbars/main.qml
+++ b/examples/datavisualization/qmlbars/qml/qmlbars/main.qml
@@ -19,7 +19,7 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
-import QtDataVisualization 1.0
+import QtDataVisualization 1.1
import QtQuick.Window 2.0
import "."
@@ -49,15 +49,18 @@ Rectangle {
// Set tableView current row to selected bar
var rowRole = series.dataProxy.rowLabels[position.x];
- var colRole = series.dataProxy.columnLabels[position.y];
+ var colRole
+ if (barGraph.columnAxis === graphAxes.total)
+ colRole = "01";
+ else
+ colRole = series.dataProxy.columnLabels[position.y];
+ var checkTimestamp = rowRole + "-" + colRole
var currentRow = tableView.currentRow
- if (currentRow === -1 || rowRole !== graphData.model.get(currentRow).year
- || colRole !== graphData.model.get(currentRow).month) {
+ if (currentRow === -1 || checkTimestamp !== graphData.model.get(currentRow).timestamp) {
var totalRows = tableView.rowCount;
for (var i = 0; i < totalRows; i++) {
- var currentRowRole = graphData.model.get(i).year
- var currentColRole = graphData.model.get(i).month
- if (currentRowRole === rowRole && currentColRole === colRole) {
+ var modelTimestamp = graphData.model.get(i).timestamp
+ if (modelTimestamp === checkTimestamp) {
tableView.currentRow = i
// Workaround to 5.2 row selection issue
if (typeof tableView.selection != "undefined") {
@@ -100,45 +103,28 @@ Rectangle {
barSpacingRelative: false
scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh
columnAxis: graphAxes.column
- valueAxis: graphAxes.income
-
- //! [3]
- Bar3DSeries {
- id: barSeries
- itemLabelFormat: "Income for @colLabel, @rowLabel: @valueLabel"
- baseGradient: barGradient
-
- ItemModelBarDataProxy {
- id: modelProxy
- itemModel: graphData.model
- rowRole: "year"
- columnRole: "month"
- valueRole: "income"
- }
- //! [3]
-
- ColorGradient {
- id: barGradient
- ColorGradientStop { position: 1.0; color: "#00FF00" }
- ColorGradientStop { position: 0.0; color: "#006000" }
- }
-
- onSelectedBarChanged: handleSelectionChange(barSeries, position)
- }
+ rowAxis: graphAxes.row
+ valueAxis: graphAxes.value
//! [4]
Bar3DSeries {
id: secondarySeries
visible: false
- itemLabelFormat: "Expenses for @colLabel, @rowLabel: @valueLabel"
+ itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel"
baseGradient: secondaryGradient
ItemModelBarDataProxy {
id: secondaryProxy
itemModel: graphData.model
- rowRole: "year"
- columnRole: "month"
+ rowRole: "timestamp"
+ columnRole: "timestamp"
valueRole: "expenses"
+ rowRolePattern: /^(\d\d\d\d).*$/
+ columnRolePattern: /^.*-(\d\d)$/
+ valueRolePattern: /-/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\1"
+ multiMatchBehavior: ItemModelBarDataProxy.MMBCumulative
}
//! [4]
@@ -150,6 +136,35 @@ Rectangle {
onSelectedBarChanged: handleSelectionChange(secondarySeries, position)
}
+
+ //! [3]
+ Bar3DSeries {
+ id: barSeries
+ itemLabelFormat: "Income, @colLabel, @rowLabel: @valueLabel"
+ baseGradient: barGradient
+
+ ItemModelBarDataProxy {
+ id: modelProxy
+ itemModel: graphData.model
+ rowRole: "timestamp"
+ columnRole: "timestamp"
+ valueRole: "income"
+ rowRolePattern: /^(\d\d\d\d).*$/
+ columnRolePattern: /^.*-(\d\d)$/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\1"
+ multiMatchBehavior: ItemModelBarDataProxy.MMBCumulative
+ }
+ //! [3]
+
+ ColorGradient {
+ id: barGradient
+ ColorGradientStop { position: 1.0; color: "#00FF00" }
+ ColorGradientStop { position: 0.0; color: "#006000" }
+ }
+
+ onSelectedBarChanged: handleSelectionChange(barSeries, position)
+ }
}
}
@@ -157,16 +172,53 @@ Rectangle {
id: tableView
anchors.top: parent.top
anchors.left: parent.left
- TableViewColumn{ role: "year" ; title: "Year" ; width: tableView.width / 4 }
- TableViewColumn{ role: "month" ; title: "Month" ; width: tableView.width / 4 }
+ TableViewColumn{ role: "timestamp" ; title: "Month" ; width: tableView.width / 2 }
TableViewColumn{ role: "expenses" ; title: "Expenses" ; width: tableView.width / 4 }
TableViewColumn{ role: "income" ; title: "Income" ; width: tableView.width / 4 }
+ itemDelegate: Item {
+ Text {
+ id: delegateText
+ anchors.verticalCenter: parent.verticalCenter
+ width: parent.width
+ anchors.leftMargin: 4
+ anchors.left: parent.left
+ anchors.right: parent.right
+ color: styleData.textColor
+ elide: styleData.elideMode
+ text: customText
+ horizontalAlignment: styleData.textAlignment
+
+ property string originalText: styleData.value
+ property string customText
+
+ onOriginalTextChanged: {
+ if (styleData.column === 0) {
+ if (delegateText.originalText !== "") {
+ var pattern = /(\d\d\d\d)-(\d\d)/
+ var matches = pattern.exec(delegateText.originalText)
+ var colIndex = parseInt(matches[2], 10) - 1
+ delegateText.customText = matches[1] + " - " + graphAxes.column.labels[colIndex]
+ }
+ } else {
+ delegateText.customText = originalText
+ }
+ }
+ }
+ }
+
model: graphData.model
//! [2]
onCurrentRowChanged: {
- var rowIndex = modelProxy.rowCategoryIndex(graphData.model.get(currentRow).year)
- var colIndex = modelProxy.columnCategoryIndex(graphData.model.get(currentRow).month)
+ var timestamp = graphData.model.get(currentRow).timestamp
+ var pattern = /(\d\d\d\d)-(\d\d)/
+ var matches = pattern.exec(timestamp)
+ var rowIndex = modelProxy.rowCategoryIndex(matches[1])
+ var colIndex
+ if (barGraph.columnAxis === graphAxes.total)
+ colIndex = 0 // Just one column when showing yearly totals
+ else
+ colIndex = modelProxy.columnCategoryIndex(matches[2])
if (selectedSeries.visible)
mainview.selectedSeries.selectedBar = Qt.point(rowIndex, colIndex)
else if (barSeries.visible)
@@ -182,25 +234,38 @@ Rectangle {
spacing: 0
Button {
- id: dataToggle
+ id: changeDataButton
Layout.fillWidth: true
Layout.fillHeight: true
text: "Show 2010 - 2012"
clip: true
//! [1]
onClicked: {
- if (barGraph.rowAxis.max !== 6) {
- text = "Show 2010 - 2012"
+ if (text === "Show yearly totals") {
modelProxy.autoRowCategories = true
secondaryProxy.autoRowCategories = true
- } else {
+ modelProxy.columnRolePattern = /^.*$/
+ secondaryProxy.columnRolePattern = /^.*$/
+ graphAxes.value.autoAdjustRange = true
+ barGraph.columnAxis = graphAxes.total
text = "Show all years"
+ } else if (text === "Show all years") {
+ modelProxy.autoRowCategories = true
+ secondaryProxy.autoRowCategories = true
+ modelProxy.columnRolePattern = /^.*-(\d\d)$/
+ secondaryProxy.columnRolePattern = /^.*-(\d\d)$/
+ graphAxes.value.min = 0
+ graphAxes.value.max = 35
+ barGraph.columnAxis = graphAxes.column
+ text = "Show 2010 - 2012"
+ } else { // text === "Show 2010 - 2012"
// Explicitly defining row categories, since we do not want to show data for
// all years in the model, just for the selected ones.
modelProxy.autoRowCategories = false
secondaryProxy.autoRowCategories = false
modelProxy.rowCategories = ["2010", "2011", "2012"]
secondaryProxy.rowCategories = ["2010", "2011", "2012"]
+ text = "Show yearly totals"
}
}
//! [1]
@@ -232,19 +297,20 @@ Rectangle {
clip: true
//! [0]
onClicked: {
- if (!secondarySeries.visible) {
- text = "Show Both"
- barGraph.valueAxis = graphAxes.expenses
+ if (text === "Show Expenses") {
barSeries.visible = false
secondarySeries.visible = true
- } else if (!barSeries.visible){
+ barGraph.valueAxis.labelFormat = "-%.2f M\u20AC"
+ secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: @valueLabel"
+ text = "Show Both"
+ } else if (text === "Show Both") {
barSeries.visible = true
+ barGraph.valueAxis.labelFormat = "%.2f M\u20AC"
+ secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: -@valueLabel"
text = "Show Income"
- barGraph.valueAxis = graphAxes.income
- } else {
+ } else { // text === "Show Income"
secondarySeries.visible = false
text = "Show Expenses"
- barGraph.valueAxis = graphAxes.income
}
}
//! [0]
diff --git a/examples/datavisualization/qmllegend/qml/qmllegend/LegendItem.qml b/examples/datavisualization/qmllegend/qml/qmllegend/LegendItem.qml
index 50be7a8d..f8c71650 100644
--- a/examples/datavisualization/qmllegend/qml/qmllegend/LegendItem.qml
+++ b/examples/datavisualization/qmllegend/qml/qmllegend/LegendItem.qml
@@ -31,6 +31,12 @@ Rectangle {
id: legendItem
state: "unselected"
+ // Workaround for a layout bug that in some situations causes changing from fully opaque color
+ // to a transparent one to use black background instead of what is actually under the items.
+ // Having the control always slighthly transparent forces the background to be refreshed
+ // properly.
+ opacity: 0.999
+
//! [1]
RowLayout {
anchors.fill: parent
diff --git a/examples/datavisualization/qmllegend/qml/qmllegend/main.qml b/examples/datavisualization/qmllegend/qml/qmllegend/main.qml
index f7e2d803..0fe107cb 100644
--- a/examples/datavisualization/qmllegend/qml/qmllegend/main.qml
+++ b/examples/datavisualization/qmllegend/qml/qmllegend/main.qml
@@ -121,34 +121,22 @@ Rectangle {
Layout.fillHeight: true
series: station1
theme: barGraph.theme
- onColorChanged: legendPanel.relayout()
}
LegendItem {
Layout.fillWidth: true
Layout.fillHeight: true
series: station2
theme: barGraph.theme
- onColorChanged: legendPanel.relayout()
}
LegendItem {
Layout.fillWidth: true
Layout.fillHeight: true
series: station3
theme: barGraph.theme
- onColorChanged: legendPanel.relayout()
}
}
//! [0]
- function relayout() {
- // Workaround for a layout bug that causes transparent colors to use black background
- // instead of what is actually under the items if just the color changes.
- // Forcing a relayout by adjusting layout's available area fixes the background.
- var originalWidth = border.width
- border.width = originalWidth + 1
- border.width = originalWidth
- }
-
states: [
State {
name: "topleft"
diff --git a/examples/datavisualization/qmloscilloscope/datasource.cpp b/examples/datavisualization/qmloscilloscope/datasource.cpp
index 01d7e73d..f466b2b0 100644
--- a/examples/datavisualization/qmloscilloscope/datasource.cpp
+++ b/examples/datavisualization/qmloscilloscope/datasource.cpp
@@ -23,7 +23,6 @@ using namespace QtDataVisualization;
//! [3]
Q_DECLARE_METATYPE(QSurface3DSeries *)
-Q_DECLARE_METATYPE(QValue3DAxis *)
//! [3]
DataSource::DataSource(QObject *parent) :
@@ -33,7 +32,6 @@ DataSource::DataSource(QObject *parent) :
{
//! [4]
qRegisterMetaType<QSurface3DSeries *>();
- qRegisterMetaType<QValue3DAxis *>();
//! [4]
}
@@ -138,29 +136,6 @@ void DataSource::update(QSurface3DSeries *series)
}
//! [1]
-//! [2]
-QString DataSource::selectionLabel(QSurface3DSeries *series, QValue3DAxis *axisX,
- QValue3DAxis *axisY, QValue3DAxis *axisZ)
-{
- QString label;
-
- if (series && series->selectedPoint() != QSurface3DSeries::invalidSelectionPosition()) {
- const QSurfaceDataItem *item = series->dataProxy()->itemAt(series->selectedPoint());
- QString x;
- QString y;
- QString z;
- x.sprintf(axisX->labelFormat().toUtf8().constData(), int(item->x()));
- y.sprintf(axisY->labelFormat().toUtf8().constData(), int(item->y()));
- z.sprintf(axisZ->labelFormat().toUtf8().constData(), int(item->z()));
- label = QStringLiteral("%1, %3: %2").arg(x).arg(y).arg(z);
- } else {
- label = QStringLiteral("No selection");
- }
-
- return label;
-}
-//! [2]
-
void DataSource::clearData()
{
for (int i(0); i < m_data.size(); i++) {
diff --git a/examples/datavisualization/qmloscilloscope/datasource.h b/examples/datavisualization/qmloscilloscope/datasource.h
index ef2f7acb..483523e0 100644
--- a/examples/datavisualization/qmloscilloscope/datasource.h
+++ b/examples/datavisualization/qmloscilloscope/datasource.h
@@ -20,9 +20,6 @@
#define DATASOURCE_H
#include <QtDataVisualization/QSurface3DSeries>
-#include <QtDataVisualization/QValue3DAxis>
-
-class QQuickView;
using namespace QtDataVisualization;
@@ -39,9 +36,6 @@ public slots:
float xMin, float xMax, float yMin, float yMax, float zMin, float zMax);
void update(QSurface3DSeries *series);
-
- QString selectionLabel(QSurface3DSeries *series, QValue3DAxis *axisX,
- QValue3DAxis *axisY, QValue3DAxis *axisZ);
//! [0]
private:
void clearData();
diff --git a/examples/datavisualization/qmloscilloscope/doc/images/qmloscilloscope-example.png b/examples/datavisualization/qmloscilloscope/doc/images/qmloscilloscope-example.png
index a3e1baab..d8a79a36 100644
--- a/examples/datavisualization/qmloscilloscope/doc/images/qmloscilloscope-example.png
+++ b/examples/datavisualization/qmloscilloscope/doc/images/qmloscilloscope-example.png
Binary files differ
diff --git a/examples/datavisualization/qmloscilloscope/doc/src/qmloscilloscope.qdoc b/examples/datavisualization/qmloscilloscope/doc/src/qmloscilloscope.qdoc
index c574950b..e13320c3 100644
--- a/examples/datavisualization/qmloscilloscope/doc/src/qmloscilloscope.qdoc
+++ b/examples/datavisualization/qmloscilloscope/doc/src/qmloscilloscope.qdoc
@@ -59,19 +59,14 @@
we still need to call QSurfaceDataProxy::resetArray() after changing the data in it to prompt
the graph to render the data.
- The final method, \c selectionLabel(), is used to generate a label string we can show on the
- QML ui. This method utilizes the axis formats to format the label:
-
- \snippet qmloscilloscope/datasource.cpp 2
-
To be able to access the \c DataSource methods from QML, we need to expose it. We do this by
defining a context property in application main:
\snippet qmloscilloscope/main.cpp 0
- To make it possible to use Qt Data Visualization class pointers as parameters on the
+ To make it possible to use QSurface3DSeries pointers as parameters on the
\c DataSource class methods on all environments and builds, we need to make sure the meta
- types are registered:
+ type is registered:
\snippet qmloscilloscope/datasource.cpp 3
\dots 0
@@ -86,15 +81,11 @@
One interesting detail is that we don't specify a proxy for the Surface3DSeries we attach
to the graph. This makes the series to utilize the default QSurfaceDataProxy.
- We also specify an empty string for \l{Abstract3DSeries::itemLabelFormat}{itemLabelFormat}, since we want to display
- the selected item information in a \c Text element instead of a label above the selection pointer.
+ We also hide the item label with \l{Abstract3DSeries::itemLabelVisible}{itemLabelFormat}, since
+ we want to display the selected item information in a \c Text element instead of a floating
+ label above the selection pointer.
This is done because the selection pointer moves around a lot as the data changes, which makes
the regular selection label difficult to read.
- When selection point changes, we update the label text using a helper function
- \c updateSelectionLabel(), which calls one of the methods we defined for our \c DataSource class
- to obtain the label:
-
- \snippet qmloscilloscope/qml/qmloscilloscope/main.qml 1
We initialize the \c DataSource cache when the graph is complete by calling a helper function
\c generateData(), which calls the method with the same name on the \c DataSource:
diff --git a/examples/datavisualization/qmloscilloscope/qml/qmloscilloscope/main.qml b/examples/datavisualization/qmloscilloscope/qml/qmloscilloscope/main.qml
index 81884154..c2e6b70b 100644
--- a/examples/datavisualization/qmloscilloscope/qml/qmloscilloscope/main.qml
+++ b/examples/datavisualization/qmloscilloscope/qml/qmloscilloscope/main.qml
@@ -19,7 +19,7 @@
import QtQuick 2.1
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
-import QtDataVisualization 1.0
+import QtDataVisualization 1.1
import "."
Item {
@@ -65,6 +65,14 @@ Item {
axisX.segmentCount: 4
axisY.segmentCount: 4
axisZ.segmentCount: 4
+ 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
+ }
//! [0]
Surface3DSeries {
@@ -72,9 +80,15 @@ Item {
drawMode: Surface3DSeries.DrawSurface;
flatShadingEnabled: false;
meshSmooth: true
- itemLabelFormat: ""
-
- onSelectedPointChanged: mainView.updateSelectionLabel()
+ itemLabelFormat: "@xLabel, @zLabel: @yLabel"
+ itemLabelVisible: false
+
+ onItemLabelChanged: {
+ if (surfaceSeries.selectedPoint === surfaceSeries.invalidSelectionPosition)
+ selectionText.text = "No selection"
+ else
+ selectionText.text = surfaceSeries.itemLabel
+ }
}
//! [0]
@@ -90,10 +104,7 @@ Item {
interval: 1000 / frequencySlider.value
running: true
repeat: true
- onTriggered: {
- dataSource.update(surfaceSeries)
- mainView.updateSelectionLabel()
- }
+ onTriggered: dataSource.update(surfaceSeries)
}
//! [3]
@@ -209,6 +220,25 @@ Item {
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
+ Layout.minimumWidth: fpsText.implicitWidth + 10
+ Layout.maximumWidth: fpsText.implicitWidth + 10
+ 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
+ }
+ }
+
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
Layout.minimumWidth: selectionText.implicitWidth + 10
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
@@ -218,10 +248,10 @@ Item {
Text {
id: selectionText
- text: "No selection"
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
+ text: "No selection"
}
}
}
@@ -285,15 +315,6 @@ Item {
}
- //! [1]
- function updateSelectionLabel() {
- selectionText.text = dataSource.selectionLabel(surfaceSeries,
- surfaceGraph.axisX,
- surfaceGraph.axisY,
- surfaceGraph.axisZ)
- }
- //! [1]
-
//! [4]
function generateData() {
dataSource.generateData(mainView.sampleCache, mainView.sampleRows,
diff --git a/examples/datavisualization/rotations/scatterdatamodifier.cpp b/examples/datavisualization/rotations/scatterdatamodifier.cpp
index c4b439b1..d2c2c52d 100644
--- a/examples/datavisualization/rotations/scatterdatamodifier.cpp
+++ b/examples/datavisualization/rotations/scatterdatamodifier.cpp
@@ -23,6 +23,7 @@
#include <QtDataVisualization/q3dcamera.h>
#include <QtDataVisualization/qscatter3dseries.h>
#include <QtDataVisualization/q3dtheme.h>
+#include <QtDataVisualization/QCustom3DItem>
#include <QtCore/qmath.h>
using namespace QtDataVisualization;
@@ -40,7 +41,7 @@ ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter)
m_fieldLines(12),
m_arrowsPerLine(16),
m_magneticField(new QScatter3DSeries),
- m_sun(new QScatter3DSeries),
+ m_sun(new QCustom3DItem),
m_magneticFieldArray(0),
m_angleOffset(0.0f),
m_angleStep(doublePi / m_arrowsPerLine / animationFrames)
@@ -62,17 +63,15 @@ ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter)
m_magneticField->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
//! [4]
- // For 'sun' we use a custom large sphere.
- m_sun->setItemSize(0.2f);
- m_sun->setName(QStringLiteral("Sun"));
- m_sun->setItemLabelFormat(QStringLiteral("@seriesName"));
- m_sun->setMesh(QAbstract3DSeries::MeshUserDefined);
- m_sun->setUserDefinedMesh(QStringLiteral(":/mesh/largesphere.obj"));
- m_sun->setBaseColor(QColor(0xff, 0xBB, 0x00));
- m_sun->dataProxy()->addItem(QScatterDataItem(QVector3D()));
+ // For 'sun' we use a custom large sphere
+ m_sun->setScaling(QVector3D(0.07f, 0.07f, 0.07f));
+ m_sun->setMeshFile(QStringLiteral(":/mesh/largesphere.obj"));
+ QImage sunColor = QImage(2, 2, QImage::Format_RGB32);
+ sunColor.fill(QColor(0xff, 0xbb, 0x00));
+ m_sun->setTextureImage(sunColor);
m_graph->addSeries(m_magneticField);
- m_graph->addSeries(m_sun);
+ m_graph->addCustomItem(m_sun);
// Configure the axes according to the data
m_graph->axisX()->setRange(-horizontalRange, horizontalRange);
@@ -171,7 +170,7 @@ void ScatterDataModifier::triggerRotation()
void ScatterDataModifier::toggleSun()
{
- m_sun->setVisible(!m_graph->seriesList().at(1)->isVisible());
+ m_sun->setVisible(!m_sun->isVisible());
}
void ScatterDataModifier::toggleRotation()
diff --git a/examples/datavisualization/rotations/scatterdatamodifier.h b/examples/datavisualization/rotations/scatterdatamodifier.h
index 9df1f26a..c4f7e7a4 100644
--- a/examples/datavisualization/rotations/scatterdatamodifier.h
+++ b/examples/datavisualization/rotations/scatterdatamodifier.h
@@ -47,7 +47,7 @@ private:
int m_fieldLines;
int m_arrowsPerLine;
QScatter3DSeries *m_magneticField;
- QScatter3DSeries *m_sun;
+ QCustom3DItem *m_sun;
QScatterDataArray *m_magneticFieldArray;
float m_angleOffset;
float m_angleStep;
diff --git a/examples/datavisualization/surface/doc/src/surface.qdoc b/examples/datavisualization/surface/doc/src/surface.qdoc
index 31b41c59..af74bd58 100644
--- a/examples/datavisualization/surface/doc/src/surface.qdoc
+++ b/examples/datavisualization/surface/doc/src/surface.qdoc
@@ -85,8 +85,8 @@
\c {Sqrt & Sin} radio button, the selected series is activated with the following
code. First we set the decorative issues like enable the grid for the surface and
select the flat shading mode. Next lines define the axis label format and value
- ranges. Finally we make sure the correct series is added to the
- graph:
+ ranges. Automatic label rotation is set to improve label readability at low camera angles.
+ Finally we make sure the correct series is added to the graph:
\snippet surface/surfacegraph.cpp 3
diff --git a/examples/datavisualization/surface/surfacegraph.cpp b/examples/datavisualization/surface/surfacegraph.cpp
index d7524fbc..172b4daf 100644
--- a/examples/datavisualization/surface/surfacegraph.cpp
+++ b/examples/datavisualization/surface/surfacegraph.cpp
@@ -100,6 +100,9 @@ void SurfaceGraph::enableSqrtSinModel(bool enable)
m_graph->axisX()->setRange(sampleMin, sampleMax);
m_graph->axisY()->setRange(0.0f, 2.0f);
m_graph->axisZ()->setRange(sampleMin, sampleMax);
+ m_graph->axisX()->setLabelAutoRotation(30);
+ m_graph->axisY()->setLabelAutoRotation(90);
+ m_graph->axisZ()->setLabelAutoRotation(30);
m_graph->removeSeries(m_heightMapSeries);
m_graph->addSeries(m_sqrtSinSeries);
diff --git a/src/datavisualization/axis/axis.pri b/src/datavisualization/axis/axis.pri
index 2c8bf70e..0173b597 100644
--- a/src/datavisualization/axis/axis.pri
+++ b/src/datavisualization/axis/axis.pri
@@ -4,9 +4,15 @@ HEADERS += \
$$PWD/qvalue3daxis.h \
$$PWD/qvalue3daxis_p.h \
$$PWD/qcategory3daxis.h \
- $$PWD/qcategory3daxis_p.h
+ $$PWD/qcategory3daxis_p.h \
+ $$PWD/qvalue3daxisformatter.h \
+ $$PWD/qvalue3daxisformatter_p.h \
+ $$PWD/qlogvalue3daxisformatter.h \
+ $$PWD/qlogvalue3daxisformatter_p.h
SOURCES += \
$$PWD/qabstract3daxis.cpp \
$$PWD/qvalue3daxis.cpp \
- $$PWD/qcategory3daxis.cpp
+ $$PWD/qcategory3daxis.cpp \
+ $$PWD/qvalue3daxisformatter.cpp \
+ $$PWD/qlogvalue3daxisformatter.cpp
diff --git a/src/datavisualization/axis/qabstract3daxis.cpp b/src/datavisualization/axis/qabstract3daxis.cpp
index 3a327caa..9fd5a832 100644
--- a/src/datavisualization/axis/qabstract3daxis.cpp
+++ b/src/datavisualization/axis/qabstract3daxis.cpp
@@ -16,7 +16,6 @@
**
****************************************************************************/
-#include "qabstract3daxis.h"
#include "qabstract3daxis_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -25,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QAbstract3DAxis
* \inmodule QtDataVisualization
* \brief QAbstract3DAxis is base class for axes of a graph.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* You should not need to use this class directly, but one of its subclasses instead.
*
@@ -42,12 +41,15 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* This type is uncreatable, but contains properties that are exposed via subtypes.
*
- * For AbstractAxis3D enums, see \l QAbstract3DAxis::AxisOrientation and \l QAbstract3DAxis::AxisType
+ * For AbstractAxis3D enums, see \l QAbstract3DAxis::AxisOrientation and
+ * \l{QAbstract3DAxis::AxisType}.
*/
/*!
* \qmlproperty string AbstractAxis3D::title
* Defines the title for the axis.
+ *
+ * \sa titleVisible, titleFixed
*/
/*!
@@ -88,6 +90,36 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* If set, the axis will automatically adjust the range so that all data fits in it.
*/
+/*!
+ * \qmlproperty real AbstractAxis3D::labelAutoRotation
+ *
+ * Defines the maximum \a angle the labels can autorotate when the camera angle changes.
+ * The \a angle can be between 0 and 90, inclusive. The default value is 0.
+ * If the value is 0, axis labels do not automatically rotate.
+ * If the value is greater than zero, labels attempt to orient themselves toward the camera, up to
+ * the specified angle.
+ */
+
+/*!
+ * \qmlproperty bool AbstractAxis3D::titleVisible
+ *
+ * Defines if the axis title is visible in the primary graph view. The default value is \c{false}.
+ *
+ * \sa title, titleFixed
+ */
+
+/*!
+ * \qmlproperty bool AbstractAxis3D::titleFixed
+ *
+ * If \c{true}, axis titles in the primary graph view will be rotated towards the camera similarly
+ * to the axis labels.
+ * If \c{false}, axis titles are only rotated around their axis but are not otherwise oriented
+ * towards the camera.
+ * This property doesn't have any effect if labelAutoRotation property value is zero.
+ * Default value is \c{true}.
+ *
+ * \sa labelAutoRotation, title, titleVisible
+ */
/*!
* \enum QAbstract3DAxis::AxisOrientation
@@ -150,6 +182,8 @@ QAbstract3DAxis::AxisType QAbstract3DAxis::type() const
* \property QAbstract3DAxis::title
*
* Defines the title for the axis.
+ *
+ * \sa titleVisible, titleFixed
*/
void QAbstract3DAxis::setTitle(const QString &title)
{
@@ -193,6 +227,77 @@ void QAbstract3DAxis::setRange(float min, float max)
}
/*!
+ * \property QAbstract3DAxis::labelAutoRotation
+ *
+ * Defines the maximum \a angle the labels can autorotate when the camera angle changes.
+ * The \a angle can be between 0 and 90, inclusive. The default value is 0.
+ * If the value is 0, axis labels do not automatically rotate.
+ * If the value is greater than zero, labels attempt to orient themselves toward the camera, up to
+ * the specified angle.
+ */
+void QAbstract3DAxis::setLabelAutoRotation(float angle)
+{
+ if (angle < 0.0f)
+ angle = 0.0f;
+ if (angle > 90.0f)
+ angle = 90.0f;
+ if (d_ptr->m_labelAutoRotation != angle) {
+ d_ptr->m_labelAutoRotation = angle;
+ emit labelAutoRotationChanged(angle);
+ }
+}
+
+float QAbstract3DAxis::labelAutoRotation() const
+{
+ return d_ptr->m_labelAutoRotation;
+}
+
+/*!
+ * \property QAbstract3DAxis::titleVisible
+ *
+ * Defines if the axis title is visible in the primary graph view. The default value is \c{false}.
+ *
+ * \sa title, titleFixed
+ */
+void QAbstract3DAxis::setTitleVisible(bool visible)
+{
+ if (d_ptr->m_titleVisible != visible) {
+ d_ptr->m_titleVisible = visible;
+ emit titleVisibilityChanged(visible);
+ }
+}
+
+bool QAbstract3DAxis::isTitleVisible() const
+{
+ return d_ptr->m_titleVisible;
+}
+
+/*!
+ * \property QAbstract3DAxis::titleFixed
+ *
+ * If \c{true}, axis titles in the primary graph view will be rotated towards the camera similarly
+ * to the axis labels.
+ * If \c{false}, axis titles are only rotated around their axis but are not otherwise oriented
+ * towards the camera.
+ * This property doesn't have any effect if labelAutoRotation property value is zero.
+ * Default value is \c{true}.
+ *
+ * \sa labelAutoRotation, title, titleVisible
+ */
+void QAbstract3DAxis::setTitleFixed(bool fixed)
+{
+ if (d_ptr->m_titleFixed != fixed) {
+ d_ptr->m_titleFixed = fixed;
+ emit titleFixedChanged(fixed);
+ }
+}
+
+bool QAbstract3DAxis::isTitleFixed() const
+{
+ return d_ptr->m_titleFixed;
+}
+
+/*!
* \property QAbstract3DAxis::min
*
* Defines the minimum value on the axis.
@@ -266,8 +371,9 @@ QAbstract3DAxisPrivate::QAbstract3DAxisPrivate(QAbstract3DAxis *q, QAbstract3DAx
m_min(0.0f),
m_max(10.0f),
m_autoAdjust(true),
- m_onlyPositiveValues(false),
- m_allowMinMaxSame(false)
+ m_labelAutoRotation(0.0f),
+ m_titleVisible(false),
+ m_titleFixed(true)
{
}
@@ -290,17 +396,28 @@ void QAbstract3DAxisPrivate::updateLabels()
// Default implementation does nothing
}
-void QAbstract3DAxisPrivate::setRange(float min, float max)
+void QAbstract3DAxisPrivate::setRange(float min, float max, bool suppressWarnings)
{
bool adjusted = false;
- if (m_onlyPositiveValues) {
- if (min < 0.0f) {
- min = 0.0f;
- adjusted = true;
- }
- if (max < 0.0f) {
- max = 0.0f;
- adjusted = true;
+ if (!allowNegatives()) {
+ if (allowZero()) {
+ if (min < 0.0f) {
+ min = 0.0f;
+ adjusted = true;
+ }
+ if (max < 0.0f) {
+ max = 0.0f;
+ adjusted = true;
+ }
+ } else {
+ if (min <= 0.0f) {
+ min = 1.0f;
+ adjusted = true;
+ }
+ if (max <= 0.0f) {
+ max = 1.0f;
+ adjusted = true;
+ }
}
}
// If min >= max, we adjust ranges so that
@@ -312,8 +429,8 @@ void QAbstract3DAxisPrivate::setRange(float min, float max)
m_min = min;
minDirty = true;
}
- if (m_max != max || min > max || (!m_allowMinMaxSame && min == max)) {
- if (min > max || (!m_allowMinMaxSame && min == max)) {
+ if (m_max != max || min > max || (!allowMinMaxSame() && min == max)) {
+ if (min > max || (!allowMinMaxSame() && min == max)) {
m_max = min + 1.0f;
adjusted = true;
} else {
@@ -323,7 +440,7 @@ void QAbstract3DAxisPrivate::setRange(float min, float max)
}
if (minDirty || maxDirty) {
- if (adjusted) {
+ if (adjusted && !suppressWarnings) {
qWarning() << "Warning: Tried to set invalid range for axis."
" Range automatically adjusted to a valid one:"
<< min << "-" << max << "-->" << m_min << "-" << m_max;
@@ -339,17 +456,25 @@ void QAbstract3DAxisPrivate::setRange(float min, float max)
void QAbstract3DAxisPrivate::setMin(float min)
{
- if (m_onlyPositiveValues) {
- if (min < 0.0f) {
- min = 0.0f;
- qWarning() << "Warning: Tried to set negative minimum for an axis that only supports"
- " positive values:" << min;
+ if (!allowNegatives()) {
+ if (allowZero()) {
+ if (min < 0.0f) {
+ min = 0.0f;
+ qWarning() << "Warning: Tried to set negative minimum for an axis that only"
+ "supports positive values and zero:" << min;
+ }
+ } else {
+ if (min <= 0.0f) {
+ min = 1.0f;
+ qWarning() << "Warning: Tried to set negative or zero minimum for an axis that only"
+ "supports positive values:" << min;
+ }
}
}
if (m_min != min) {
bool maxChanged = false;
- if (min > m_max || (!m_allowMinMaxSame && min == m_max)) {
+ if (min > m_max || (!allowMinMaxSame() && min == m_max)) {
float oldMax = m_max;
m_max = min + 1.0f;
qWarning() << "Warning: Tried to set minimum to equal or larger than maximum for"
@@ -368,22 +493,34 @@ void QAbstract3DAxisPrivate::setMin(float min)
void QAbstract3DAxisPrivate::setMax(float max)
{
- if (m_onlyPositiveValues) {
- if (max < 0.0f) {
- max = 0.0f;
- qWarning() << "Warning: Tried to set negative maximum for an axis that only supports"
- " positive values:" << max;
+ if (!allowNegatives()) {
+ if (allowZero()) {
+ if (max < 0.0f) {
+ max = 0.0f;
+ qWarning() << "Warning: Tried to set negative maximum for an axis that only"
+ "supports positive values and zero:" << max;
+ }
+ } else {
+ if (max <= 0.0f) {
+ max = 1.0f;
+ qWarning() << "Warning: Tried to set negative or zero maximum for an axis that only"
+ "supports positive values:" << max;
+ }
}
}
if (m_max != max) {
bool minChanged = false;
- if (m_min > max || (!m_allowMinMaxSame && m_min == max)) {
+ if (m_min > max || (!allowMinMaxSame() && m_min == max)) {
float oldMin = m_min;
m_min = max - 1.0f;
- if (m_onlyPositiveValues && m_min < 0.0f) {
- m_min = 0.0f;
- if (!m_allowMinMaxSame && max == 0.0f) {
+ if (!allowNegatives() && m_min < 0.0f) {
+ if (allowZero())
+ m_min = 0.0f;
+ else
+ m_min = max / 2.0f; // Need some positive value smaller than max
+
+ if (!allowMinMaxSame() && max == 0.0f) {
m_min = oldMin;
qWarning() << "Unable to set maximum value to zero.";
return;
diff --git a/src/datavisualization/axis/qabstract3daxis.h b/src/datavisualization/axis/qabstract3daxis.h
index 2b2be229..a9f75550 100644
--- a/src/datavisualization/axis/qabstract3daxis.h
+++ b/src/datavisualization/axis/qabstract3daxis.h
@@ -40,6 +40,9 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DAxis : public QObject
Q_PROPERTY(float min READ min WRITE setMin NOTIFY minChanged)
Q_PROPERTY(float max READ max WRITE setMax NOTIFY maxChanged)
Q_PROPERTY(bool autoAdjustRange READ isAutoAdjustRange WRITE setAutoAdjustRange NOTIFY autoAdjustRangeChanged)
+ Q_PROPERTY(float labelAutoRotation READ labelAutoRotation WRITE setLabelAutoRotation NOTIFY labelAutoRotationChanged REVISION 1)
+ Q_PROPERTY(bool titleVisible READ isTitleVisible WRITE setTitleVisible NOTIFY titleVisibilityChanged REVISION 1)
+ Q_PROPERTY(bool titleFixed READ isTitleFixed WRITE setTitleFixed NOTIFY titleFixedChanged REVISION 1)
public:
enum AxisOrientation {
@@ -81,6 +84,15 @@ public:
void setRange(float min, float max);
+ void setLabelAutoRotation(float angle);
+ float labelAutoRotation() const;
+
+ void setTitleVisible(bool visible);
+ bool isTitleVisible() const;
+
+ void setTitleFixed(bool fixed);
+ bool isTitleFixed() const;
+
signals:
void titleChanged(const QString &newTitle);
void labelsChanged();
@@ -89,6 +101,9 @@ signals:
void maxChanged(float value);
void rangeChanged(float min, float max);
void autoAdjustRangeChanged(bool autoAdjust);
+ Q_REVISION(1) void labelAutoRotationChanged(float angle);
+ Q_REVISION(1) void titleVisibilityChanged(bool visible);
+ Q_REVISION(1) void titleFixedChanged(bool fixed);
protected:
QScopedPointer<QAbstract3DAxisPrivate> d_ptr;
diff --git a/src/datavisualization/axis/qabstract3daxis_p.h b/src/datavisualization/axis/qabstract3daxis_p.h
index 4eb8de68..7181362f 100644
--- a/src/datavisualization/axis/qabstract3daxis_p.h
+++ b/src/datavisualization/axis/qabstract3daxis_p.h
@@ -26,13 +26,12 @@
//
// We mean it.
-#include "datavisualizationglobal_p.h"
-#include "qabstract3daxis.h"
-#include "abstract3dcontroller_p.h"
-
#ifndef QABSTRACT3DAXIS_P_H
#define QABSTRACT3DAXIS_P_H
+#include "datavisualizationglobal_p.h"
+#include "qabstract3daxis.h"
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QAbstract3DAxisPrivate : public QObject
@@ -47,12 +46,15 @@ public:
inline bool isDefaultAxis() { return m_isDefaultAxis; }
inline void setDefaultAxis(bool isDefault) { m_isDefaultAxis = isDefault; }
- virtual void setRange(float min, float max);
+ virtual void setRange(float min, float max, bool suppressWarnings = false);
virtual void setMin(float min);
virtual void setMax (float max);
protected:
virtual void updateLabels();
+ virtual bool allowZero() = 0;
+ virtual bool allowNegatives() = 0;
+ virtual bool allowMinMaxSame() = 0;
QAbstract3DAxis *q_ptr;
@@ -64,8 +66,9 @@ protected:
float m_min;
float m_max;
bool m_autoAdjust;
- bool m_onlyPositiveValues;
- bool m_allowMinMaxSame;
+ float m_labelAutoRotation;
+ bool m_titleVisible;
+ bool m_titleFixed;
friend class QAbstract3DAxis;
friend class QValue3DAxis;
diff --git a/src/datavisualization/axis/qcategory3daxis.cpp b/src/datavisualization/axis/qcategory3daxis.cpp
index c11a65eb..f2cd0a84 100644
--- a/src/datavisualization/axis/qcategory3daxis.cpp
+++ b/src/datavisualization/axis/qcategory3daxis.cpp
@@ -16,10 +16,8 @@
**
****************************************************************************/
-#include "qcategory3daxis.h"
#include "qcategory3daxis_p.h"
#include "bars3dcontroller_p.h"
-#include "qbardataproxy.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -27,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QCategory3DAxis
* \inmodule QtDataVisualization
* \brief The QCategory3DAxis class is used for manipulating an axis of a graph.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QCategory3DAxis provides an axis that can be given labels. The axis is divided into equal-sized
* categories based on the data window size defined by setting the axis range.
@@ -121,8 +119,6 @@ QCategory3DAxisPrivate::QCategory3DAxisPrivate(QCategory3DAxis *q)
: QAbstract3DAxisPrivate(q, QAbstract3DAxis::AxisTypeCategory),
m_labelsExplicitlySet(false)
{
- m_onlyPositiveValues = true;
- m_allowMinMaxSame = true;
}
QCategory3DAxisPrivate::~QCategory3DAxisPrivate()
@@ -142,6 +138,21 @@ void QCategory3DAxisPrivate::setDataLabels(const QStringList &labels)
}
}
+bool QCategory3DAxisPrivate::allowZero()
+{
+ return true;
+}
+
+bool QCategory3DAxisPrivate::allowNegatives()
+{
+ return false;
+}
+
+bool QCategory3DAxisPrivate::allowMinMaxSame()
+{
+ return true;
+}
+
QCategory3DAxis *QCategory3DAxisPrivate::qptr()
{
return static_cast<QCategory3DAxis *>(q_ptr);
diff --git a/src/datavisualization/axis/qcategory3daxis_p.h b/src/datavisualization/axis/qcategory3daxis_p.h
index 1ba5ccb0..27126620 100644
--- a/src/datavisualization/axis/qcategory3daxis_p.h
+++ b/src/datavisualization/axis/qcategory3daxis_p.h
@@ -26,13 +26,12 @@
//
// We mean it.
-#include "qcategory3daxis.h"
-#include "qabstract3daxis_p.h"
-#include "qbardataitem.h"
-
#ifndef QCATEGORY3DAXIS_P_H
#define QCATEGORY3DAXIS_P_H
+#include "qcategory3daxis.h"
+#include "qabstract3daxis_p.h"
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QCategory3DAxisPrivate : public QAbstract3DAxisPrivate
@@ -45,6 +44,11 @@ public:
void setDataLabels(const QStringList &labels);
+protected:
+ virtual bool allowZero();
+ virtual bool allowNegatives();
+ virtual bool allowMinMaxSame();
+
private:
QCategory3DAxis *qptr();
diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
new file mode 100644
index 00000000..7367e7c5
--- /dev/null
+++ b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** 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 "qlogvalue3daxisformatter_p.h"
+#include "qvalue3daxis_p.h"
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+/*!
+ * \class QLogValue3DAxisFormatter
+ * \inmodule QtDataVisualization
+ * \brief QLogValue3DAxisFormatter implements logarithmic value axis formatter.
+ * \since QtDataVisualization 1.1
+ *
+ * This class provides formatting rules for a logarithmic QValue3DAxis.
+ *
+ * When a QLogValue3DAxisFormatter is attached to a QValue3DAxis, the axis range
+ * cannot include negative values or the zero.
+ *
+ * \sa QValue3DAxisFormatter
+ */
+
+/*!
+ * \qmltype LogValueAxis3DFormatter
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.1
+ * \ingroup datavisualization_qml
+ * \instantiates QLogValue3DAxisFormatter
+ * \brief LogValueAxis3DFormatter implements logarithmic value axis formatter.
+ *
+ * This type provides formatting rules for a logarithmic ValueAxis3D.
+ * When a LogValueAxis3DFormatter is attached to a ValueAxis3D, the axis range
+ * cannot include negative values or the zero.
+ */
+
+/*!
+ * \qmlproperty real LogValueAxis3DFormatter::base
+ *
+ * The \a base of the logarithm used to map axis values. If the base is non-zero, the parent axis
+ * segment count will be ignored when the grid line and label positions are calculated.
+ * If you want the range to be divided into equal segments like normal value axis, set this
+ * property value to zero.
+ *
+ * The \a base has to be zero or positive value and not equal to one.
+ * Defaults to ten.
+ *
+ * \sa ValueAxis3D::segmentCount
+ */
+
+/*!
+ * \qmlproperty bool LogValueAxis3DFormatter::autoSubGrid
+ *
+ * If this property value is set to \c true, the parent axis sub-segment count is ignored
+ * when calculating sub-grid line positions. The sub-grid positions are generated automatically
+ * according to the base property value.
+ * The number of sub-grid lines is set to base value minus one, rounded down.
+ * This property is ignored when base property value is zero.
+ * Defaults to \c true.
+ *
+ * \sa base, ValueAxis3D::subSegmentCount
+ */
+
+/*!
+ * \qmlproperty bool LogValueAxis3DFormatter::showEdgeLabels
+ *
+ * When the base property value is non-zero, the whole axis range is often not equally divided into
+ * segments. The first and last segments are often smaller than the other segments.
+ * In extreme cases this can lead to overlapping labels on the first and last two grid lines.
+ * By setting this property to \c false, you can suppress showing the minimum and maximum labels
+ * for the axis in cases where the segments do not exactly fit the axis.
+ * Defaults to \c true.
+ *
+ * \sa base, AbstractAxis3D::labels
+ */
+
+/*!
+ * \internal
+ */
+QLogValue3DAxisFormatter::QLogValue3DAxisFormatter(QLogValue3DAxisFormatterPrivate *d,
+ QObject *parent) :
+ QValue3DAxisFormatter(d, parent)
+{
+ setAllowNegatives(false);
+ setAllowZero(false);
+}
+
+/*!
+ * Constructs a new QLogValue3DAxisFormatter instance with optional \a parent.
+ */
+QLogValue3DAxisFormatter::QLogValue3DAxisFormatter(QObject *parent) :
+ QValue3DAxisFormatter(new QLogValue3DAxisFormatterPrivate(this), parent)
+{
+ setAllowNegatives(false);
+ setAllowZero(false);
+}
+
+/*!
+ * Destroys QLogValue3DAxisFormatter.
+ */
+QLogValue3DAxisFormatter::~QLogValue3DAxisFormatter()
+{
+}
+
+/*!
+ * \property QLogValue3DAxisFormatter::base
+ *
+ * The \a base of the logarithm used to map axis values. If the base is non-zero, the parent axis
+ * segment count will be ignored when the grid line and label positions are calculated.
+ * If you want the range to be divided into equal segments like normal value axis, set this
+ * property value to zero.
+ *
+ * The \a base has to be zero or positive value and not equal to one.
+ * Defaults to ten.
+ *
+ * \sa QValue3DAxis::segmentCount
+ */
+void QLogValue3DAxisFormatter::setBase(qreal base)
+{
+ if (base < 0.0f || base == 1.0f) {
+ qWarning() << "Warning: The logarithm base must be greater than 0 and not equal to 1,"
+ << "attempted:" << base;
+ return;
+ }
+ if (dptr()->m_base != base) {
+ dptr()->m_base = base;
+ markDirty(true);
+ emit baseChanged(base);
+ }
+}
+
+qreal QLogValue3DAxisFormatter::base() const
+{
+ return dptrc()->m_base;
+}
+
+/*!
+ * \property QLogValue3DAxisFormatter::autoSubGrid
+ *
+ * If this property value is set to \c true, the parent axis sub-segment count is ignored
+ * when calculating sub-grid line positions. The sub-grid positions are generated automatically
+ * according to the base property value.
+ * The number of sub-grid lines is set to base value minus one, rounded down.
+ * This property is ignored when base property value is zero.
+ * Defaults to \c true.
+ *
+ * \sa base, QValue3DAxis::subSegmentCount
+ */
+void QLogValue3DAxisFormatter::setAutoSubGrid(bool enabled)
+{
+ if (dptr()->m_autoSubGrid != enabled) {
+ dptr()->m_autoSubGrid = enabled;
+ markDirty(false);
+ emit autoSubGridChanged(enabled);
+ }
+}
+
+bool QLogValue3DAxisFormatter::autoSubGrid() const
+{
+ return dptrc()->m_autoSubGrid;
+}
+
+/*!
+ * \property QLogValue3DAxisFormatter::showEdgeLabels
+ *
+ * When the base property value is non-zero, the whole axis range is often not equally divided into
+ * segments. The first and last segments are often smaller than the other segments.
+ * In extreme cases this can lead to overlapping labels on the first and last two grid lines.
+ * By setting this property to \c false, you can suppress showing the minimum and maximum labels
+ * for the axis in cases where the segments do not exactly fit the axis.
+ * Defaults to \c true.
+ *
+ * \sa base, QAbstract3DAxis::labels
+ */
+void QLogValue3DAxisFormatter::setShowEdgeLabels(bool enabled)
+{
+ if (dptr()->m_showEdgeLabels != enabled) {
+ dptr()->m_showEdgeLabels = enabled;
+ markDirty(true);
+ emit showEdgeLabelsChanged(enabled);
+ }
+}
+
+bool QLogValue3DAxisFormatter::showEdgeLabels() const
+{
+ return dptrc()->m_showEdgeLabels;
+}
+
+/*!
+ * \internal
+ */
+QValue3DAxisFormatter *QLogValue3DAxisFormatter::createNewInstance() const
+{
+ return new QLogValue3DAxisFormatter();
+}
+
+/*!
+ * \internal
+ */
+void QLogValue3DAxisFormatter::recalculate()
+{
+ dptr()->recalculate();
+}
+
+/*!
+ * \internal
+ */
+float QLogValue3DAxisFormatter::positionAt(float value) const
+{
+ return dptrc()->positionAt(value);
+}
+
+/*!
+ * \internal
+ */
+float QLogValue3DAxisFormatter::valueAt(float position) const
+{
+ return dptrc()->valueAt(position);
+}
+
+/*!
+ * \internal
+ */
+void QLogValue3DAxisFormatter::populateCopy(QValue3DAxisFormatter &copy) const
+{
+ QValue3DAxisFormatter::populateCopy(copy);
+ dptrc()->populateCopy(copy);
+}
+
+/*!
+ * \internal
+ */
+QLogValue3DAxisFormatterPrivate *QLogValue3DAxisFormatter::dptr()
+{
+ return static_cast<QLogValue3DAxisFormatterPrivate *>(d_ptr.data());
+}
+
+/*!
+ * \internal
+ */
+const QLogValue3DAxisFormatterPrivate *QLogValue3DAxisFormatter::dptrc() const
+{
+ return static_cast<const QLogValue3DAxisFormatterPrivate *>(d_ptr.data());
+}
+
+// QLogValue3DAxisFormatterPrivate
+QLogValue3DAxisFormatterPrivate::QLogValue3DAxisFormatterPrivate(QLogValue3DAxisFormatter *q)
+ : QValue3DAxisFormatterPrivate(q),
+ m_base(10.0),
+ m_logMin(0.0),
+ m_logMax(0.0),
+ m_logRangeNormalizer(0.0),
+ m_autoSubGrid(true),
+ m_showEdgeLabels(true),
+ m_evenMinSegment(true),
+ m_evenMaxSegment(true)
+{
+}
+
+QLogValue3DAxisFormatterPrivate::~QLogValue3DAxisFormatterPrivate()
+{
+}
+
+void QLogValue3DAxisFormatterPrivate::recalculate()
+{
+ // When doing position/value mappings, base doesn't matter, so just use natural logarithm
+ m_logMin = qLn(qreal(m_min));
+ m_logMax = qLn(qreal(m_max));
+ m_logRangeNormalizer = m_logMax - m_logMin;
+
+ int subGridCount = m_axis->subSegmentCount() - 1;
+ int segmentCount = m_axis->segmentCount();
+ QString labelFormat = m_axis->labelFormat();
+ qreal segmentStep;
+ if (m_base > 0.0) {
+ // Update parent axis segment counts
+ qreal logMin = qLn(qreal(m_min)) / qLn(m_base);
+ qreal logMax = qLn(qreal(m_max)) / qLn(m_base);
+ qreal logRangeNormalizer = logMax - logMin;
+
+ qreal minDiff = qCeil(logMin) - logMin;
+ qreal maxDiff = logMax - qFloor(logMax);
+
+ m_evenMinSegment = qFuzzyCompare(0.0, minDiff);
+ m_evenMaxSegment = qFuzzyCompare(0.0, maxDiff);
+
+ segmentCount = qRound(logRangeNormalizer - minDiff - maxDiff);
+
+ if (!m_evenMinSegment)
+ segmentCount++;
+ if (!m_evenMaxSegment)
+ segmentCount++;
+
+ segmentStep = 1.0 / logRangeNormalizer;
+
+ if (m_autoSubGrid) {
+ subGridCount = qCeil(m_base) - 2; // -2 for subgrid because subsegment count is base - 1
+ if (subGridCount < 0)
+ subGridCount = 0;
+ }
+
+ m_gridPositions.resize(segmentCount + 1);
+ m_subGridPositions.resize(segmentCount * subGridCount);
+ m_labelPositions.resize(segmentCount + 1);
+ m_labelStrings.clear();
+ m_labelStrings.reserve(segmentCount + 1);
+
+ // Calculate segment positions
+ int index = 0;
+ if (!m_evenMinSegment) {
+ m_gridPositions[0] = 0.0f;
+ m_labelPositions[0] = 0.0f;
+ if (m_showEdgeLabels)
+ m_labelStrings << qptr()->stringForValue(qreal(m_min), labelFormat);
+ else
+ m_labelStrings << QString();
+ index++;
+ }
+ for (int i = 0; i < segmentCount; i++) {
+ float gridValue = float((minDiff + qreal(i)) / qreal(logRangeNormalizer));
+ m_gridPositions[index] = gridValue;
+ m_labelPositions[index] = gridValue;
+ m_labelStrings << qptr()->stringForValue(qPow(m_base, minDiff + qreal(i) + logMin),
+ labelFormat);
+ index++;
+ }
+ // Ensure max value doesn't suffer from any rounding errors
+ m_gridPositions[segmentCount] = 1.0f;
+ m_labelPositions[segmentCount] = 1.0f;
+ QString finalLabel;
+ if (m_showEdgeLabels || m_evenMaxSegment)
+ finalLabel = qptr()->stringForValue(qreal(m_max), labelFormat);
+
+ if (m_labelStrings.size() > segmentCount)
+ m_labelStrings.replace(segmentCount, finalLabel);
+ else
+ m_labelStrings << finalLabel;
+ } else {
+ // Grid lines and label positions are the same as the parent class, so call parent impl
+ // first to populate those
+ QValue3DAxisFormatterPrivate::doRecalculate();
+
+ // Label string list needs to be repopulated
+ segmentStep = 1.0 / qreal(segmentCount);
+
+ m_labelStrings << qptr()->stringForValue(qreal(m_min), labelFormat);
+ for (int i = 1; i < m_labelPositions.size() - 1; i++)
+ m_labelStrings[i] = qptr()->stringForValue(qExp(segmentStep * qreal(i)
+ * m_logRangeNormalizer + m_logMin),
+ labelFormat);
+ m_labelStrings << qptr()->stringForValue(qreal(m_max), labelFormat);
+
+ m_evenMaxSegment = true;
+ m_evenMinSegment = true;
+ }
+
+ // Subgrid line positions are logarithmically spaced
+ if (subGridCount > 0) {
+ float oneSegmentRange = valueAt(float(segmentStep)) - m_min;
+ float subSegmentStep = oneSegmentRange / float(subGridCount + 1);
+
+ // Since the logarithm has the same curvature across whole axis range, we can just calculate
+ // subgrid positions for the first segment and replicate them to other segments.
+ QVector<float> actualSubSegmentSteps(subGridCount);
+
+ for (int i = 0; i < subGridCount; i++) {
+ float currentSubPosition = positionAt(m_min + ((i + 1) * subSegmentStep));
+ actualSubSegmentSteps[i] = currentSubPosition;
+ }
+
+ float firstPartialSegmentAdjustment = float(segmentStep) - m_gridPositions.at(1);
+ for (int i = 0; i < segmentCount; i++) {
+ for (int j = 0; j < subGridCount; j++) {
+ float position = m_gridPositions.at(i) + actualSubSegmentSteps.at(j);
+ if (!m_evenMinSegment && i == 0)
+ position -= firstPartialSegmentAdjustment;
+ if (position > 1.0f)
+ position = 1.0f;
+ if (position < 0.0f)
+ position = 0.0f;
+ m_subGridPositions[i * subGridCount + j] = position;
+ }
+ }
+ }
+}
+
+void QLogValue3DAxisFormatterPrivate::populateCopy(QValue3DAxisFormatter &copy) const
+{
+ QLogValue3DAxisFormatter *logFormatter = static_cast<QLogValue3DAxisFormatter *>(&copy);
+ QLogValue3DAxisFormatterPrivate *priv = logFormatter->dptr();
+
+ priv->m_base = m_base;
+ priv->m_logMin = m_logMin;
+ priv->m_logMax = m_logMax;
+ priv->m_logRangeNormalizer = m_logRangeNormalizer;
+}
+
+float QLogValue3DAxisFormatterPrivate::positionAt(float value) const
+{
+ qreal logValue = qLn(qreal(value));
+ float retval = float((logValue - m_logMin) / m_logRangeNormalizer);
+
+ return retval;
+}
+
+float QLogValue3DAxisFormatterPrivate::valueAt(float position) const
+{
+ qreal logValue = (qreal(position) * m_logRangeNormalizer) + m_logMin;
+ return float(qExp(logValue));
+}
+
+QLogValue3DAxisFormatter *QLogValue3DAxisFormatterPrivate::qptr()
+{
+ return static_cast<QLogValue3DAxisFormatter *>(q_ptr);
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter.h b/src/datavisualization/axis/qlogvalue3daxisformatter.h
new file mode 100644
index 00000000..62714a7d
--- /dev/null
+++ b/src/datavisualization/axis/qlogvalue3daxisformatter.h
@@ -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
+**
+****************************************************************************/
+
+#ifndef QLOGVALUE3DAXISFORMATTER_H
+#define QLOGVALUE3DAXISFORMATTER_H
+
+#include <QtDataVisualization/qvalue3daxisformatter.h>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QLogValue3DAxisFormatterPrivate;
+
+class QT_DATAVISUALIZATION_EXPORT QLogValue3DAxisFormatter : public QValue3DAxisFormatter
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal base READ base WRITE setBase NOTIFY baseChanged)
+ Q_PROPERTY(bool autoSubGrid READ autoSubGrid WRITE setAutoSubGrid NOTIFY autoSubGridChanged)
+ Q_PROPERTY(bool showEdgeLabels READ showEdgeLabels WRITE setShowEdgeLabels NOTIFY showEdgeLabelsChanged)
+
+protected:
+ explicit QLogValue3DAxisFormatter(QLogValue3DAxisFormatterPrivate *d, QObject *parent = 0);
+public:
+ explicit QLogValue3DAxisFormatter(QObject *parent = 0);
+ virtual ~QLogValue3DAxisFormatter();
+
+ void setBase(qreal base);
+ qreal base() const;
+ void setAutoSubGrid(bool enabled);
+ bool autoSubGrid() const;
+ void setShowEdgeLabels(bool enabled);
+ bool showEdgeLabels() const;
+
+signals:
+ void baseChanged(qreal base);
+ void autoSubGridChanged(bool enabled);
+ void showEdgeLabelsChanged(bool enabled);
+
+protected:
+ virtual QValue3DAxisFormatter *createNewInstance() const;
+ virtual void recalculate();
+ virtual float positionAt(float value) const;
+ virtual float valueAt(float position) const;
+ virtual void populateCopy(QValue3DAxisFormatter &copy) const;
+
+ QLogValue3DAxisFormatterPrivate *dptr();
+ const QLogValue3DAxisFormatterPrivate *dptrc() const;
+
+private:
+ Q_DISABLE_COPY(QLogValue3DAxisFormatter)
+
+ friend class QLogValue3DAxisFormatterPrivate;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter_p.h b/src/datavisualization/axis/qlogvalue3daxisformatter_p.h
new file mode 100644
index 00000000..df2d41d1
--- /dev/null
+++ b/src/datavisualization/axis/qlogvalue3daxisformatter_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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 QLOGVALUE3DAXISFORMATTER_P_H
+#define QLOGVALUE3DAXISFORMATTER_P_H
+
+#include "datavisualizationglobal_p.h"
+#include "qlogvalue3daxisformatter.h"
+#include "qvalue3daxisformatter_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QLogValue3DAxisFormatterPrivate : public QValue3DAxisFormatterPrivate
+{
+ Q_OBJECT
+
+public:
+ QLogValue3DAxisFormatterPrivate(QLogValue3DAxisFormatter *q);
+ virtual ~QLogValue3DAxisFormatterPrivate();
+
+ void recalculate();
+ void populateCopy(QValue3DAxisFormatter &copy) const;
+
+ float positionAt(float value) const;
+ float valueAt(float position) const;
+
+protected:
+ QLogValue3DAxisFormatter *qptr();
+
+ qreal m_base;
+ qreal m_logMin;
+ qreal m_logMax;
+ qreal m_logRangeNormalizer;
+ bool m_autoSubGrid;
+ bool m_showEdgeLabels;
+
+private:
+ bool m_evenMinSegment;
+ bool m_evenMaxSegment;
+
+ friend class QLogValue3DAxisFormatter;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/axis/qvalue3daxis.cpp b/src/datavisualization/axis/qvalue3daxis.cpp
index 0d53291d..8207174f 100644
--- a/src/datavisualization/axis/qvalue3daxis.cpp
+++ b/src/datavisualization/axis/qvalue3daxis.cpp
@@ -16,9 +16,8 @@
**
****************************************************************************/
-#include "qvalue3daxis.h"
#include "qvalue3daxis_p.h"
-#include "utils_p.h"
+#include "qvalue3daxisformatter_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -26,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QValue3DAxis
* \inmodule QtDataVisualization
* \brief The QValue3DAxis class is used for manipulating an axis of a graph.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QValue3DAxis provides an axis that can be given a range of values and segment and subsegment
* counts to divide the range into.
@@ -74,11 +73,30 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty ValueAxis3DFormatter ValueAxis3D::formatter
+ * \since QtDataVisualization 1.1
+ *
+ * Defines the axis \a formatter to be used. Any existing formatter is deleted when a new formatter
+ * is set.
+ *
+ */
+
+/*!
+ * \qmlproperty bool ValueAxis3D::reversed
+ * \since QtDataVisualization 1.1
+ *
+ * If \c{true}, the axis will be rendered in reverse, i.e. the positions of minimum and maximum
+ * values are swapped when the graph is rendered. This property doesn't affect the actual
+ * minimum and maximum values of the axis.
+ */
+
+/*!
* Constructs QValue3DAxis with the given \a parent.
*/
QValue3DAxis::QValue3DAxis(QObject *parent) :
QAbstract3DAxis(new QValue3DAxisPrivate(this), parent)
{
+ setFormatter(new QValue3DAxisFormatter);
}
/*!
@@ -88,7 +106,6 @@ QValue3DAxis::~QValue3DAxis()
{
}
-
/*!
* \property QValue3DAxis::segmentCount
*
@@ -105,7 +122,7 @@ void QValue3DAxis::setSegmentCount(int count)
<< count << "-> 1";
count = 1;
}
- if (dptr()->m_segmentCount != count){
+ if (dptr()->m_segmentCount != count) {
dptr()->m_segmentCount = count;
dptr()->emitLabelsChanged();
emit segmentCountChanged(count);
@@ -169,6 +186,53 @@ QString QValue3DAxis::labelFormat() const
}
/*!
+ * \property QValue3DAxis::formatter
+ * \since QtDataVisualization 1.1
+ *
+ * Defines the axis \a formatter to be used. Any existing formatter is deleted when a new formatter
+ * is set.
+ */
+void QValue3DAxis::setFormatter(QValue3DAxisFormatter *formatter)
+{
+ Q_ASSERT(formatter);
+
+ if (formatter != dptr()->m_formatter) {
+ delete dptr()->m_formatter;
+ dptr()->m_formatter = formatter;
+ formatter->setParent(this);
+ formatter->d_ptr->setAxis(this);
+ emit formatterChanged(formatter);
+ emit dptr()->formatterDirty();
+ }
+}
+
+QValue3DAxisFormatter *QValue3DAxis::formatter() const
+{
+ return dptrc()->m_formatter;
+}
+
+/*!
+ * \property QValue3DAxis::reversed
+ * \since QtDataVisualization 1.1
+ *
+ * If \c{true}, the axis will be rendered in reverse, i.e. the positions of minimum and maximum
+ * values are swapped when the graph is rendered. This property doesn't affect the actual
+ * minimum and maximum values of the axis.
+ */
+void QValue3DAxis::setReversed(bool enable)
+{
+ if (dptr()->m_reversed != enable) {
+ dptr()->m_reversed = enable;
+ emit reversedChanged(enable);
+ }
+}
+
+bool QValue3DAxis::reversed() const
+{
+ return dptrc()->m_reversed;
+}
+
+/*!
* \internal
*/
QValue3DAxisPrivate *QValue3DAxis::dptr()
@@ -189,7 +253,9 @@ QValue3DAxisPrivate::QValue3DAxisPrivate(QValue3DAxis *q)
m_segmentCount(5),
m_subSegmentCount(1),
m_labelFormat(Utils::defaultLabelFormat()),
- m_labelsDirty(true)
+ m_labelsDirty(true),
+ m_formatter(0),
+ m_reversed(false)
{
}
@@ -197,11 +263,11 @@ QValue3DAxisPrivate::~QValue3DAxisPrivate()
{
}
-void QValue3DAxisPrivate::setRange(float min, float max)
+void QValue3DAxisPrivate::setRange(float min, float max, bool suppressWarnings)
{
bool dirty = (min != m_min || max != m_max);
- QAbstract3DAxisPrivate::setRange(min, max);
+ QAbstract3DAxisPrivate::setRange(min, max, suppressWarnings);
if (dirty)
emitLabelsChanged();
@@ -240,26 +306,24 @@ void QValue3DAxisPrivate::updateLabels()
m_labelsDirty = false;
- QStringList newLabels;
- newLabels.reserve(m_segmentCount + 1);
-
- // First label is at axis min, which is an extra segment
- float segmentStep = (m_max - m_min) / m_segmentCount;
+ m_formatter->d_ptr->recalculate();
- QString formatString(m_labelFormat);
- Utils::ParamType paramType = Utils::findFormatParamType(formatString);
- QByteArray formatArray = formatString.toUtf8();
+ m_labels = m_formatter->labelStrings();
+}
- for (int i = 0; i < m_segmentCount; i++) {
- float value = m_min + (segmentStep * i);
- newLabels.append(Utils::formatLabel(formatArray, paramType, value));
- }
+bool QValue3DAxisPrivate::allowZero()
+{
+ return m_formatter->allowZero();
+}
- // Ensure max label doesn't suffer from any rounding errors
- newLabels.append(Utils::formatLabel(formatArray, paramType, m_max));
+bool QValue3DAxisPrivate::allowNegatives()
+{
+ return m_formatter->allowNegatives();
+}
- if (m_labels != newLabels)
- m_labels = newLabels;
+bool QValue3DAxisPrivate::allowMinMaxSame()
+{
+ return false;
}
QValue3DAxis *QValue3DAxisPrivate::qptr()
diff --git a/src/datavisualization/axis/qvalue3daxis.h b/src/datavisualization/axis/qvalue3daxis.h
index f0af759b..f552269c 100644
--- a/src/datavisualization/axis/qvalue3daxis.h
+++ b/src/datavisualization/axis/qvalue3daxis.h
@@ -20,6 +20,7 @@
#define QVALUE3DAXIS_H
#include <QtDataVisualization/qabstract3daxis.h>
+#include <QtDataVisualization/qvalue3daxisformatter.h>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -31,6 +32,8 @@ class QT_DATAVISUALIZATION_EXPORT QValue3DAxis : public QAbstract3DAxis
Q_PROPERTY(int segmentCount READ segmentCount WRITE setSegmentCount NOTIFY segmentCountChanged)
Q_PROPERTY(int subSegmentCount READ subSegmentCount WRITE setSubSegmentCount NOTIFY subSegmentCountChanged)
Q_PROPERTY(QString labelFormat READ labelFormat WRITE setLabelFormat NOTIFY labelFormatChanged)
+ Q_PROPERTY(QValue3DAxisFormatter* formatter READ formatter WRITE setFormatter NOTIFY formatterChanged REVISION 1)
+ Q_PROPERTY(bool reversed READ reversed WRITE setReversed NOTIFY reversedChanged REVISION 1)
public:
explicit QValue3DAxis(QObject *parent = 0);
@@ -45,10 +48,18 @@ public:
void setLabelFormat(const QString &format);
QString labelFormat() const;
+ void setFormatter(QValue3DAxisFormatter *formatter);
+ QValue3DAxisFormatter *formatter() const;
+
+ void setReversed(bool enable);
+ bool reversed() const;
+
signals:
void segmentCountChanged(int count);
void subSegmentCountChanged(int count);
void labelFormatChanged(const QString &format);
+ Q_REVISION(1) void formatterChanged(QValue3DAxisFormatter *formatter);
+ Q_REVISION(1) void reversedChanged(bool enable);
protected:
QValue3DAxisPrivate *dptr();
@@ -56,9 +67,11 @@ protected:
private:
Q_DISABLE_COPY(QValue3DAxis)
+ friend class Abstract3DController;
friend class Bars3DController;
friend class Scatter3DController;
friend class Surface3DController;
+ friend class QValue3DAxisFormatterPrivate;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/axis/qvalue3daxis_p.h b/src/datavisualization/axis/qvalue3daxis_p.h
index 21fd78ab..b7394c0a 100644
--- a/src/datavisualization/axis/qvalue3daxis_p.h
+++ b/src/datavisualization/axis/qvalue3daxis_p.h
@@ -26,12 +26,12 @@
//
// We mean it.
-#include "qvalue3daxis.h"
-#include "qabstract3daxis_p.h"
-
#ifndef QVALUE3DAXIS_P_H
#define QVALUE3DAXIS_P_H
+#include "qvalue3daxis.h"
+#include "qabstract3daxis_p.h"
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QValue3DAxisPrivate : public QAbstract3DAxisPrivate
@@ -42,23 +42,34 @@ public:
QValue3DAxisPrivate(QValue3DAxis *q);
virtual ~QValue3DAxisPrivate();
- virtual void setRange(float min, float max);
+ virtual void setRange(float min, float max, bool suppressWarnings = false);
virtual void setMin(float min);
virtual void setMax (float max);
-protected:
void emitLabelsChanged();
+
+signals:
+ void formatterDirty();
+
+protected:
virtual void updateLabels();
+ virtual bool allowZero();
+ virtual bool allowNegatives();
+ virtual bool allowMinMaxSame();
+
int m_segmentCount;
int m_subSegmentCount;
QString m_labelFormat;
bool m_labelsDirty;
+ QValue3DAxisFormatter *m_formatter;
+ bool m_reversed;
private:
QValue3DAxis *qptr();
friend class QValue3DAxis;
+ friend class Abstract3DController;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/axis/qvalue3daxisformatter.cpp b/src/datavisualization/axis/qvalue3daxisformatter.cpp
new file mode 100644
index 00000000..56ca3b0f
--- /dev/null
+++ b/src/datavisualization/axis/qvalue3daxisformatter.cpp
@@ -0,0 +1,419 @@
+/****************************************************************************
+**
+** 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 "qvalue3daxisformatter_p.h"
+#include "qvalue3daxis_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+/*!
+ * \class QValue3DAxisFormatter
+ * \inmodule QtDataVisualization
+ * \brief QValue3DAxisFormatter is base class for value axis formatters.
+ * \since QtDataVisualization 1.1
+ *
+ * This class provides formatting rules for a linear QValue3DAxis. Subclass it if you
+ * want to implement custom value axes.
+ *
+ * The base class has no public API beyond constructors and destructors. It is meant to be only
+ * used internally. However, subclasses may implement public properties as needed.
+ *
+ * \sa QLogValue3DAxisFormatter
+ */
+
+/*!
+ * \qmltype ValueAxis3DFormatter
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.1
+ * \ingroup datavisualization_qml
+ * \instantiates QValue3DAxisFormatter
+ * \brief ValueAxis3DFormatter is base type for value axis formatters.
+ *
+ * This type provides formatting rules for a linear ValueAxis3D.
+ * This type is the default type for ValueAxis3D and thus never needs to be explicitly created.
+ * This type has not public functionality.
+ */
+
+/*!
+ * \internal
+ */
+QValue3DAxisFormatter::QValue3DAxisFormatter(QValue3DAxisFormatterPrivate *d, QObject *parent) :
+ QObject(parent),
+ d_ptr(d)
+{
+}
+
+/*!
+ * Constructs a new QValue3DAxisFormatter instance with an optional \a parent.
+ */
+QValue3DAxisFormatter::QValue3DAxisFormatter(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QValue3DAxisFormatterPrivate(this))
+{
+}
+
+/*!
+ * Destroys QValue3DAxisFormatter.
+ */
+QValue3DAxisFormatter::~QValue3DAxisFormatter()
+{
+}
+
+/*!
+ * Allow the parent axis to have negative values if \a allow is true.
+ */
+void QValue3DAxisFormatter::setAllowNegatives(bool allow)
+{
+ d_ptr->m_allowNegatives = allow;
+}
+
+/*!
+ * \return \c true if negative values are valid values for parent axis.
+ * The default implementation always returns true.
+ */
+bool QValue3DAxisFormatter::allowNegatives() const
+{
+ return d_ptr->m_allowNegatives;
+}
+
+/*!
+ * Allow the parent axis to have zero value if \a allow is true.
+ */
+void QValue3DAxisFormatter::setAllowZero(bool allow)
+{
+ d_ptr->m_allowZero = allow;
+}
+
+/*!
+ * \return \c true if zero is a valid value for parent axis.
+ * The default implementation always returns true.
+ */
+bool QValue3DAxisFormatter::allowZero() const
+{
+ return d_ptr->m_allowZero;
+}
+
+/*!
+ * Creates a new empty instance of this formatter. Must be reimplemented in a subclass.
+ *
+ * \return the new instance. The renderer uses this method to cache a copy of the
+ * the formatter. The ownership of the new copy transfers to the caller.
+ */
+QValue3DAxisFormatter *QValue3DAxisFormatter::createNewInstance() const
+{
+ return new QValue3DAxisFormatter();
+}
+
+/*!
+ * This method resizes and populates the label and grid line position arrays and the label strings
+ * array, as well as calculates any values needed for mapping between value and position.
+ * It is allowed to access the parent axis from inside this function.
+ *
+ * This method must be reimplemented in a subclass if the default array contents are not suitable.
+ *
+ * See gridPositions(), subGridPositions(), labelPositions(), and labelStrings() methods for
+ * documentation about the arrays that need to be resized and populated.
+ *
+ * \sa gridPositions(), subGridPositions(), labelPositions(), labelStrings(), axis()
+ */
+void QValue3DAxisFormatter::recalculate()
+{
+ d_ptr->doRecalculate();
+}
+
+/*!
+ * This method is used to format a string using the specified value and the specified format.
+ * Reimplement this method in a subclass to resolve the formatted string for a given \a value
+ * if the default formatting rules specified for QValue3DAxis::labelFormat property are not
+ * sufficient.
+ *
+ * \return the formatted label string using a \a value and a \a format.
+ *
+ * \sa recalculate(), labelStrings(), QValue3DAxis::labelFormat
+ */
+QString QValue3DAxisFormatter::stringForValue(qreal value, const QString &format) const
+{
+ return d_ptr->stringForValue(value, format);
+}
+
+/*!
+ * Reimplement this method if the position cannot be resolved by linear interpolation
+ * between the parent axis minimum and maximum values.
+ *
+ * \return the normalized position along the axis for the given \a value.
+ * The returned value should be between 0.0 (for minimum value) and 1.0 (for maximum value),
+ * inclusive, if the value is within the parent axis range.
+ *
+ * \sa recalculate(), valueAt()
+ */
+float QValue3DAxisFormatter::positionAt(float value) const
+{
+ return d_ptr->positionAt(value);
+}
+
+/*!
+ * Reimplement this method if the value cannot be resolved by linear interpolation
+ * between the parent axis minimum and maximum values.
+ *
+ * \return the value at the normalized \a position along the axis.
+ * The \a position value should be between 0.0 (for minimum value) and 1.0 (for maximum value),
+ * inclusive to obtain values within the parent axis range.
+ *
+ * \sa recalculate(), positionAt()
+ */
+float QValue3DAxisFormatter::valueAt(float position) const
+{
+ return d_ptr->valueAt(position);
+}
+
+/*!
+ * Copies all necessary values for resolving positions, values, and strings with this formatter
+ * from this formatter to the \a copy.
+ * When reimplementing this method in a subclass, call the the superclass version at some point.
+ * The renderer uses this method to cache a copy of the the formatter.
+ *
+ * \return the new copy. The ownership of the new copy transfers to the caller.
+ */
+void QValue3DAxisFormatter::populateCopy(QValue3DAxisFormatter &copy) const
+{
+ d_ptr->doPopulateCopy(*(copy.d_ptr.data()));
+}
+
+/*!
+ * Marks this formatter dirty, prompting the renderer to make a new copy of its cache on the next
+ * renderer synchronization. This method should be called by a subclass whenever the formatter
+ * is changed in a way that affects the resolved values. Specify \c true for \a labelsChange
+ * parameter if the change was such that it requires regenerating the parent axis label strings.
+ */
+void QValue3DAxisFormatter::markDirty(bool labelsChange)
+{
+ d_ptr->markDirty(labelsChange);
+}
+
+/*!
+ * \return the parent axis. The parent axis must only be accessed in recalculate()
+ * method to maintain thread safety in environments using a threaded renderer.
+ *
+ * \sa recalculate()
+ */
+QValue3DAxis *QValue3DAxisFormatter::axis() const
+{
+ return d_ptr->m_axis;
+}
+
+/*!
+ * \return a reference to the array of normalized grid line positions.
+ * The default array size is equal to the segment count of the parent axis plus one, but
+ * a subclassed implementation of recalculate method may resize the array differently.
+ * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive.
+ *
+ * \sa QValue3DAxis::segmentCount, recalculate()
+ */
+QVector<float> &QValue3DAxisFormatter::gridPositions() const
+{
+ return d_ptr->m_gridPositions;
+}
+
+/*!
+ * \return a reference to the array of normalized subgrid line positions.
+ * The default array size is equal to segment count of the parent axis times sub-segment count
+ * of the parent axis minus one, but a subclassed implementation of recalculate method may resize
+ * the array differently.
+ * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive.
+ *
+ * \sa QValue3DAxis::segmentCount, QValue3DAxis::subSegmentCount, recalculate()
+ */
+QVector<float> &QValue3DAxisFormatter::subGridPositions() const
+{
+ return d_ptr->m_subGridPositions;
+}
+
+/*!
+ * \return a reference to the array of normalized label positions.
+ * The default array size is equal to the segment count of the parent axis plus one, but
+ * a subclassed implementation of recalculate method may resize the array differently.
+ * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive.
+ * The default behavior is that the label at the index zero corresponds to the minimum value
+ * of the axis.
+ *
+ * \sa QValue3DAxis::segmentCount, QAbstract3DAxis::labels, recalculate()
+ */
+QVector<float> &QValue3DAxisFormatter::labelPositions() const
+{
+ return d_ptr->m_labelPositions;
+}
+
+/*!
+ * \return a reference to the string list containing formatter label strings.
+ * The array size must be equal to the size of the label positions array and
+ * the indexes correspond to that array as well.
+ *
+ * \sa labelPositions()
+ */
+QStringList &QValue3DAxisFormatter::labelStrings() const
+{
+ return d_ptr->m_labelStrings;
+}
+
+// QValue3DAxisFormatterPrivate
+QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q)
+ : QObject(0),
+ q_ptr(q),
+ m_needsRecalculate(true),
+ m_min(0.0f),
+ m_max(0.0f),
+ m_rangeNormalizer(0.0f),
+ m_axis(0),
+ m_preparsedParamType(Utils::ParamTypeUnknown),
+ m_allowNegatives(true),
+ m_allowZero(true)
+{
+}
+
+QValue3DAxisFormatterPrivate::~QValue3DAxisFormatterPrivate()
+{
+}
+
+void QValue3DAxisFormatterPrivate::recalculate()
+{
+ // Only recalculate if we need to and have m_axis pointer. If we do not have
+ // m_axis, either we are not attached to an axis or this is a renderer cache.
+ if (m_axis && m_needsRecalculate) {
+ m_min = m_axis->min();
+ m_max = m_axis->max();
+ m_rangeNormalizer = (m_max - m_min);
+
+ q_ptr->recalculate();
+ m_needsRecalculate = false;
+ }
+}
+
+void QValue3DAxisFormatterPrivate::doRecalculate()
+{
+ int segmentCount = m_axis->segmentCount();
+ int subGridCount = m_axis->subSegmentCount() - 1;
+ QString labelFormat = m_axis->labelFormat();
+
+ m_gridPositions.resize(segmentCount + 1);
+ m_subGridPositions.resize(segmentCount * subGridCount);
+
+ m_labelPositions.resize(segmentCount + 1);
+ m_labelStrings.clear();
+ m_labelStrings.reserve(segmentCount + 1);
+
+ // Use qreals for intermediate calculations for better accuracy on label values
+ qreal segmentStep = 1.0 / qreal(segmentCount);
+ qreal subSegmentStep = 0;
+ if (subGridCount > 0)
+ subSegmentStep = segmentStep / qreal(subGridCount + 1);
+
+ // Calculate positions
+ qreal rangeNormalizer = qreal(m_max - m_min);
+ for (int i = 0; i < segmentCount; i++) {
+ qreal gridValue = segmentStep * qreal(i);
+ m_gridPositions[i] = float(gridValue);
+ m_labelPositions[i] = float(gridValue);
+ m_labelStrings << q_ptr->stringForValue(gridValue * rangeNormalizer + qreal(m_min),
+ labelFormat);
+ if (m_subGridPositions.size()) {
+ for (int j = 0; j < subGridCount; j++)
+ m_subGridPositions[i * subGridCount + j] = gridValue + subSegmentStep * (j + 1);
+ }
+ }
+
+ // Ensure max value doesn't suffer from any rounding errors
+ m_gridPositions[segmentCount] = 1.0f;
+ m_labelPositions[segmentCount] = 1.0f;
+ m_labelStrings << q_ptr->stringForValue(qreal(m_max), labelFormat);
+}
+
+void QValue3DAxisFormatterPrivate::populateCopy(QValue3DAxisFormatter &copy)
+{
+ recalculate();
+ q_ptr->populateCopy(copy);
+}
+
+void QValue3DAxisFormatterPrivate::doPopulateCopy(QValue3DAxisFormatterPrivate &copy)
+{
+ copy.m_min = m_min;
+ copy.m_max = m_max;
+ copy.m_rangeNormalizer = m_rangeNormalizer;
+
+ copy.m_gridPositions = m_gridPositions;
+ copy.m_labelPositions = m_labelPositions;
+ copy.m_subGridPositions = m_subGridPositions;
+}
+
+QString QValue3DAxisFormatterPrivate::stringForValue(qreal value, const QString &format)
+{
+ 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();
+ }
+
+ return Utils::formatLabel(m_labelFormatArray, m_preparsedParamType, value);
+}
+
+float QValue3DAxisFormatterPrivate::positionAt(float value) const
+{
+ return ((value - m_min) / m_rangeNormalizer);
+}
+
+float QValue3DAxisFormatterPrivate::valueAt(float position) const
+{
+ return ((position * m_rangeNormalizer) + m_min);
+}
+
+void QValue3DAxisFormatterPrivate::setAxis(QValue3DAxis *axis)
+{
+ Q_ASSERT(axis);
+
+ // These signals are all connected to markDirtyNoLabelChange slot, even though most of them
+ // do require labels to be regenerated. This is because the label regeneration is triggered
+ // elsewhere in these cases.
+ connect(axis, &QValue3DAxis::segmentCountChanged,
+ this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange);
+ connect(axis, &QValue3DAxis::subSegmentCountChanged,
+ this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange);
+ connect(axis, &QValue3DAxis::labelFormatChanged,
+ this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange);
+ connect(axis, &QAbstract3DAxis::rangeChanged,
+ this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange);
+
+ m_axis = axis;
+}
+
+void QValue3DAxisFormatterPrivate::markDirty(bool labelsChange)
+{
+ m_needsRecalculate = true;
+ if (m_axis) {
+ if (labelsChange)
+ m_axis->dptr()->emitLabelsChanged();
+ if (m_axis && m_axis->orientation() != QAbstract3DAxis::AxisOrientationNone)
+ emit m_axis->dptr()->formatterDirty();
+ }
+}
+
+void QValue3DAxisFormatterPrivate::markDirtyNoLabelChange()
+{
+ markDirty(false);
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/axis/qvalue3daxisformatter.h b/src/datavisualization/axis/qvalue3daxisformatter.h
new file mode 100644
index 00000000..c7b0bac5
--- /dev/null
+++ b/src/datavisualization/axis/qvalue3daxisformatter.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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 QVALUE3DAXISFORMATTER_H
+#define QVALUE3DAXISFORMATTER_H
+
+#include <QtDataVisualization/qdatavisualizationglobal.h>
+#include <QtCore/QObject>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QVector>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QValue3DAxisFormatterPrivate;
+class QValue3DAxis;
+
+class QT_DATAVISUALIZATION_EXPORT QValue3DAxisFormatter : public QObject
+{
+ Q_OBJECT
+protected:
+ explicit QValue3DAxisFormatter(QValue3DAxisFormatterPrivate *d, QObject *parent = 0);
+public:
+ explicit QValue3DAxisFormatter(QObject *parent = 0);
+ virtual ~QValue3DAxisFormatter();
+
+protected:
+ void setAllowNegatives(bool allow);
+ bool allowNegatives() const;
+ void setAllowZero(bool allow);
+ bool allowZero() const;
+
+ virtual QValue3DAxisFormatter *createNewInstance() const;
+ virtual void recalculate();
+ virtual QString stringForValue(qreal value, const QString &format) const;
+ virtual float positionAt(float value) const;
+ virtual float valueAt(float position) const;
+ virtual void populateCopy(QValue3DAxisFormatter &copy) const;
+
+ void markDirty(bool labelsChange = false);
+ QValue3DAxis *axis() const;
+
+ QVector<float> &gridPositions() const;
+ QVector<float> &subGridPositions() const;
+ QVector<float> &labelPositions() const;
+ QStringList &labelStrings() const;
+
+ QScopedPointer<QValue3DAxisFormatterPrivate> d_ptr;
+
+private:
+ Q_DISABLE_COPY(QValue3DAxisFormatter)
+
+ friend class Abstract3DController;
+ friend class Abstract3DRenderer;
+ friend class Bars3DRenderer;
+ friend class Scatter3DRenderer;
+ friend class Surface3DRenderer;
+ friend class SurfaceObject;
+ friend class QValue3DAxisFormatterPrivate;
+ friend class QLogValue3DAxisFormatter;
+ friend class QValue3DAxis;
+ friend class QValue3DAxisPrivate;
+ friend class AxisRenderCache;
+ friend class QBar3DSeriesPrivate;
+ friend class QScatter3DSeriesPrivate;
+ friend class QSurface3DSeriesPrivate;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/axis/qvalue3daxisformatter_p.h b/src/datavisualization/axis/qvalue3daxisformatter_p.h
new file mode 100644
index 00000000..2d1dc920
--- /dev/null
+++ b/src/datavisualization/axis/qvalue3daxisformatter_p.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
+**
+****************************************************************************/
+
+//
+// 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 QVALUE3DAXISFORMATTER_P_H
+#define QVALUE3DAXISFORMATTER_P_H
+
+#include "datavisualizationglobal_p.h"
+#include "qvalue3daxisformatter.h"
+#include "utils_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QValue3DAxis;
+
+class QValue3DAxisFormatterPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q);
+ virtual ~QValue3DAxisFormatterPrivate();
+
+ void recalculate();
+ void doRecalculate();
+ void populateCopy(QValue3DAxisFormatter &copy);
+ void doPopulateCopy(QValue3DAxisFormatterPrivate &copy);
+
+ QString stringForValue(qreal value, const QString &format);
+ float positionAt(float value) const;
+ float valueAt(float position) const;
+
+ void setAxis(QValue3DAxis *axis);
+ void markDirty(bool labelsChange);
+
+public slots:
+ void markDirtyNoLabelChange();
+
+protected:
+ QValue3DAxisFormatter *q_ptr;
+
+ bool m_needsRecalculate;
+
+ float m_min;
+ float m_max;
+ float m_rangeNormalizer;
+
+ QVector<float> m_gridPositions;
+ QVector<float> m_subGridPositions;
+ QVector<float> m_labelPositions;
+ QStringList m_labelStrings;
+
+ QValue3DAxis *m_axis;
+
+ QString m_previousLabelFormat;
+ QByteArray m_labelFormatArray;
+ Utils::ParamType m_preparsedParamType;
+
+ bool m_allowNegatives;
+ bool m_allowZero;
+
+ friend class QValue3DAxisFormatter;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/common.pri b/src/datavisualization/common.pri
index 4ffa5646..9a497c2c 100644
--- a/src/datavisualization/common.pri
+++ b/src/datavisualization/common.pri
@@ -1,6 +1,3 @@
-# This qmake file is included by all Patternist projects and contains common Qt defines,
-# compiler warnings, and include paths.
-
INCLUDEPATH += $$PWD/engine \
$$PWD/global \
$$PWD/utils \
diff --git a/src/datavisualization/data/abstractitemmodelhandler.cpp b/src/datavisualization/data/abstractitemmodelhandler.cpp
index 9f2ccd86..f8ddf7b7 100644
--- a/src/datavisualization/data/abstractitemmodelhandler.cpp
+++ b/src/datavisualization/data/abstractitemmodelhandler.cpp
@@ -34,7 +34,7 @@ AbstractItemModelHandler::~AbstractItemModelHandler()
{
}
-void AbstractItemModelHandler::setItemModel(const QAbstractItemModel *itemModel)
+void AbstractItemModelHandler::setItemModel(QAbstractItemModel *itemModel)
{
if (itemModel != m_itemModel.data()) {
if (!m_itemModel.isNull())
@@ -69,7 +69,7 @@ void AbstractItemModelHandler::setItemModel(const QAbstractItemModel *itemModel)
}
}
-const QAbstractItemModel *AbstractItemModelHandler::itemModel() const
+QAbstractItemModel *AbstractItemModelHandler::itemModel() const
{
return m_itemModel.data();
}
diff --git a/src/datavisualization/data/abstractitemmodelhandler_p.h b/src/datavisualization/data/abstractitemmodelhandler_p.h
index ecbfe61c..e11d229a 100644
--- a/src/datavisualization/data/abstractitemmodelhandler_p.h
+++ b/src/datavisualization/data/abstractitemmodelhandler_p.h
@@ -43,8 +43,8 @@ public:
AbstractItemModelHandler(QObject *parent = 0);
virtual ~AbstractItemModelHandler();
- virtual void setItemModel(const QAbstractItemModel *itemModel);
- virtual const QAbstractItemModel *itemModel() const;
+ virtual void setItemModel(QAbstractItemModel *itemModel);
+ virtual QAbstractItemModel *itemModel() const;
public slots:
virtual void handleColumnsInserted(const QModelIndex &parent, int start, int end);
@@ -71,7 +71,7 @@ signals:
protected:
virtual void resolveModel() = 0;
- QPointer<const QAbstractItemModel> m_itemModel; // Not owned
+ QPointer<QAbstractItemModel> m_itemModel; // Not owned
bool resolvePending;
QTimer m_resolveTimer;
bool m_fullReset;
diff --git a/src/datavisualization/data/abstractrenderitem_p.h b/src/datavisualization/data/abstractrenderitem_p.h
index 912a09f3..57977a3c 100644
--- a/src/datavisualization/data/abstractrenderitem_p.h
+++ b/src/datavisualization/data/abstractrenderitem_p.h
@@ -32,8 +32,6 @@
#include "datavisualizationglobal_p.h"
#include "labelitem_p.h"
-#include <QtCore/QString>
-#include <QtGui/QOpenGLFunctions>
#include <QtGui/QVector3D>
#include <QtGui/QQuaternion>
diff --git a/src/datavisualization/data/baritemmodelhandler.cpp b/src/datavisualization/data/baritemmodelhandler.cpp
index 4f44fe1d..62e6390d 100644
--- a/src/datavisualization/data/baritemmodelhandler.cpp
+++ b/src/datavisualization/data/baritemmodelhandler.cpp
@@ -26,7 +26,11 @@ BarItemModelHandler::BarItemModelHandler(QItemModelBarDataProxy *proxy, QObject
: AbstractItemModelHandler(parent),
m_proxy(proxy),
m_proxyArray(0),
- m_columnCount(0)
+ m_columnCount(0),
+ m_valueRole(noRoleIndex),
+ m_rotationRole(noRoleIndex),
+ m_haveValuePattern(false),
+ m_haveRotationPattern(false)
{
}
@@ -34,6 +38,50 @@ BarItemModelHandler::~BarItemModelHandler()
{
}
+void BarItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight,
+ const QVector<int> &roles)
+{
+ // Do nothing if full reset already pending
+ if (!m_fullReset) {
+ if (!m_proxy->useModelCategories()) {
+ // If the data model doesn't directly map rows and columns, we cannot optimize
+ AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
+ } else {
+ int startRow = qMin(topLeft.row(), bottomRight.row());
+ int endRow = qMax(topLeft.row(), bottomRight.row());
+ int startCol = qMin(topLeft.column(), bottomRight.column());
+ int endCol = qMax(topLeft.column(), bottomRight.column());
+
+ for (int i = startRow; i <= endRow; i++) {
+ for (int j = startCol; j <= endCol; j++) {
+ QModelIndex index = m_itemModel->index(i, j);
+ QBarDataItem item;
+ QVariant valueVar = index.data(m_valueRole);
+ float value;
+ if (m_haveValuePattern)
+ value = valueVar.toString().replace(m_valuePattern, m_valueReplace).toFloat();
+ else
+ value = valueVar.toFloat();
+ item.setValue(value);
+ if (m_rotationRole != noRoleIndex) {
+ QVariant rotationVar = index.data(m_rotationRole);
+ float rotation;
+ if (m_haveRotationPattern) {
+ rotation = rotationVar.toString().replace(m_rotationPattern,
+ m_rotationReplace).toFloat();
+ } else {
+ rotation = rotationVar.toFloat();
+ }
+ item.setRotation(rotation);
+ }
+ m_proxy->setItem(i, j, item);
+ }
+ }
+ }
+ }
+}
+
// Resolve entire item model into QBarDataArray.
void BarItemModelHandler::resolveModel()
{
@@ -48,14 +96,29 @@ void BarItemModelHandler::resolveModel()
return;
}
+ // Value and rotation patterns can be reused on single item changes,
+ // so store them to member variables.
+ QRegExp rowPattern(m_proxy->rowRolePattern());
+ QRegExp colPattern(m_proxy->columnRolePattern());
+ m_valuePattern = m_proxy->valueRolePattern();
+ m_rotationPattern = m_proxy->rotationRolePattern();
+ QString rowReplace = m_proxy->rowRoleReplace();
+ QString colReplace = m_proxy->columnRoleReplace();
+ m_valueReplace = m_proxy->valueRoleReplace();
+ m_rotationReplace = m_proxy->rotationRoleReplace();
+ bool haveRowPattern = !rowPattern.isEmpty() && rowPattern.isValid();
+ bool haveColPattern = !colPattern.isEmpty() && colPattern.isValid();
+ m_haveValuePattern = !m_valuePattern.isEmpty() && m_valuePattern.isValid();
+ m_haveRotationPattern = !m_rotationPattern.isEmpty() && m_rotationPattern.isValid();
+
QStringList rowLabels;
QStringList columnLabels;
QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
// Default value role to display role if no mapping
- int valueRole = roleHash.key(m_proxy->valueRole().toLatin1(), Qt::DisplayRole);
- int rotationRole = roleHash.key(m_proxy->rotationRole().toLatin1(), noRoleIndex);
+ m_valueRole = roleHash.key(m_proxy->valueRole().toLatin1(), Qt::DisplayRole);
+ m_rotationRole = roleHash.key(m_proxy->rotationRole().toLatin1(), noRoleIndex);
int rowCount = m_itemModel->rowCount();
int columnCount = m_itemModel->columnCount();
@@ -71,9 +134,25 @@ void BarItemModelHandler::resolveModel()
for (int i = 0; i < rowCount; i++) {
QBarDataRow &newProxyRow = *m_proxyArray->at(i);
for (int j = 0; j < columnCount; j++) {
- newProxyRow[j].setValue(m_itemModel->index(i, j).data(valueRole).toReal());
- if (rotationRole != noRoleIndex)
- newProxyRow[j].setRotation(m_itemModel->index(i, j).data(rotationRole).toReal());
+ QModelIndex index = m_itemModel->index(i, j);
+ QVariant valueVar = index.data(m_valueRole);
+ float value;
+ if (m_haveValuePattern)
+ value = valueVar.toString().replace(m_valuePattern, m_valueReplace).toFloat();
+ else
+ value = valueVar.toFloat();
+ newProxyRow[j].setValue(value);
+ if (m_rotationRole != noRoleIndex) {
+ QVariant rotationVar = index.data(m_rotationRole);
+ float rotation;
+ if (m_haveRotationPattern) {
+ rotation = rotationVar.toString().replace(m_rotationPattern,
+ m_rotationReplace).toFloat();
+ } else {
+ rotation = rotationVar.toFloat();
+ }
+ newProxyRow[j].setRotation(rotation);
+ }
}
}
// Generate labels from headers if using model rows/columns
@@ -97,16 +176,62 @@ void BarItemModelHandler::resolveModel()
// Sort values into rows and columns
typedef QHash<QString, float> ColumnValueMap;
- QHash <QString, ColumnValueMap> itemValueMap;
- QHash <QString, ColumnValueMap> itemRotationMap;
+ QHash<QString, ColumnValueMap> itemValueMap;
+ QHash<QString, ColumnValueMap> itemRotationMap;
+
+ bool cumulative = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBAverage
+ || m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBCumulative;
+ bool countMatches = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBAverage;
+ bool takeFirst = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBFirst;
+ QHash<QString, QHash<QString, int> > *matchCountMap = 0;
+ if (countMatches)
+ matchCountMap = new QHash<QString, QHash<QString, int> >;
+
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columnCount; j++) {
QModelIndex index = m_itemModel->index(i, j);
QString rowRoleStr = index.data(rowRole).toString();
+ if (haveRowPattern)
+ rowRoleStr.replace(rowPattern, rowReplace);
QString columnRoleStr = index.data(columnRole).toString();
- itemValueMap[rowRoleStr][columnRoleStr] = index.data(valueRole).toReal();
- if (rotationRole != noRoleIndex)
- itemRotationMap[rowRoleStr][columnRoleStr] = index.data(rotationRole).toReal();
+ if (haveColPattern)
+ columnRoleStr.replace(colPattern, colReplace);
+ QVariant valueVar = index.data(m_valueRole);
+ float value;
+ if (m_haveValuePattern)
+ value = valueVar.toString().replace(m_valuePattern, m_valueReplace).toFloat();
+ else
+ value = valueVar.toFloat();
+ if (countMatches)
+ (*matchCountMap)[rowRoleStr][columnRoleStr]++;
+
+ if (cumulative) {
+ itemValueMap[rowRoleStr][columnRoleStr] += value;
+ } else {
+ if (takeFirst && itemValueMap.contains(rowRoleStr)) {
+ if (itemValueMap.value(rowRoleStr).contains(columnRoleStr))
+ continue; // We already have a value for this row/column combo
+ }
+ itemValueMap[rowRoleStr][columnRoleStr] = value;
+ }
+
+ if (m_rotationRole != noRoleIndex) {
+ QVariant rotationVar = index.data(m_rotationRole);
+ float rotation;
+ if (m_haveRotationPattern) {
+ rotation = rotationVar.toString().replace(m_rotationPattern,
+ m_rotationReplace).toFloat();
+ } else {
+ rotation = rotationVar.toFloat();
+ }
+ if (cumulative) {
+ itemRotationMap[rowRoleStr][columnRoleStr] += rotation;
+ } else {
+ // We know we are in take last mode if we get here,
+ // as take first mode skips to next loop already earlier
+ itemRotationMap[rowRoleStr][columnRoleStr] = rotation;
+ }
+ }
if (generateRows && !rowListHash.value(rowRoleStr, false)) {
rowListHash.insert(rowRoleStr, true);
rowList << rowRoleStr;
@@ -141,15 +266,24 @@ void BarItemModelHandler::resolveModel()
QString rowKey = rowList.at(i);
QBarDataRow &newProxyRow = *m_proxyArray->at(i);
for (int j = 0; j < columnList.size(); j++) {
- newProxyRow[j].setValue(itemValueMap[rowKey][columnList.at(j)]);
- if (rotationRole != noRoleIndex)
- newProxyRow[j].setRotation(itemRotationMap[rowKey][columnList.at(j)]);
+ float value = itemValueMap[rowKey][columnList.at(j)];
+ if (countMatches)
+ value /= float((*matchCountMap)[rowKey][columnList.at(j)]);
+ newProxyRow[j].setValue(value);
+ if (m_rotationRole != noRoleIndex) {
+ float angle = itemRotationMap[rowKey][columnList.at(j)];
+ if (countMatches)
+ angle /= float((*matchCountMap)[rowKey][columnList.at(j)]);
+ newProxyRow[j].setRotation(angle);
+ }
}
}
rowLabels = rowList;
columnLabels = columnList;
m_columnCount = columnList.size();
+
+ delete matchCountMap;
}
m_proxy->resetArray(m_proxyArray, rowLabels, columnLabels);
diff --git a/src/datavisualization/data/baritemmodelhandler_p.h b/src/datavisualization/data/baritemmodelhandler_p.h
index 7bf7b0a1..6dea906e 100644
--- a/src/datavisualization/data/baritemmodelhandler_p.h
+++ b/src/datavisualization/data/baritemmodelhandler_p.h
@@ -41,12 +41,24 @@ public:
BarItemModelHandler(QItemModelBarDataProxy *proxy, QObject *parent = 0);
virtual ~BarItemModelHandler();
+public slots:
+ virtual void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QVector<int> &roles = QVector<int> ());
+
protected:
void virtual resolveModel();
QItemModelBarDataProxy *m_proxy; // Not owned
QBarDataArray *m_proxyArray; // Not owned
int m_columnCount;
+ int m_valueRole;
+ int m_rotationRole;
+ QRegExp m_valuePattern;
+ QRegExp m_rotationPattern;
+ QString m_valueReplace;
+ QString m_rotationReplace;
+ bool m_haveValuePattern;
+ bool m_haveRotationPattern;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/barrenderitem.cpp b/src/datavisualization/data/barrenderitem.cpp
index 50d2a4b4..eab5178b 100644
--- a/src/datavisualization/data/barrenderitem.cpp
+++ b/src/datavisualization/data/barrenderitem.cpp
@@ -17,15 +17,13 @@
****************************************************************************/
#include "barrenderitem_p.h"
-#include "bars3drenderer_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
BarRenderItem::BarRenderItem()
: AbstractRenderItem(),
m_value(0),
- m_height(0.0f),
- m_seriesIndex(0)
+ m_height(0.0f)
{
}
@@ -35,7 +33,6 @@ BarRenderItem::BarRenderItem(const BarRenderItem &other)
m_value = other.m_value;
m_position = other.m_position;
m_height = other.m_height;
- m_seriesIndex = other.m_seriesIndex;
}
BarRenderItem::~BarRenderItem()
@@ -67,8 +64,8 @@ void BarRenderSliceItem::setItem(const BarRenderItem &renderItem)
m_value = renderItem.value();
m_position = renderItem.position();
m_height = renderItem.height();
- m_seriesIndex = renderItem.seriesIndex();
m_sliceLabel = QString();
+ delete m_sliceLabelItem;
m_sliceLabelItem = 0;
}
diff --git a/src/datavisualization/data/barrenderitem_p.h b/src/datavisualization/data/barrenderitem_p.h
index 1122053d..72c5abc1 100644
--- a/src/datavisualization/data/barrenderitem_p.h
+++ b/src/datavisualization/data/barrenderitem_p.h
@@ -33,8 +33,6 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-class Bars3DRenderer;
-
class BarRenderItem : public AbstractRenderItem
{
public:
@@ -54,16 +52,10 @@ public:
inline void setHeight(GLfloat height) { m_height = height; }
inline GLfloat height() const { return m_height; }
- // Series index in visual series that this item belongs to.
- // This is only utilized by slicing, so it may not be up to date on all items.
- inline void setSeriesIndex(int seriesIndex) { m_seriesIndex = seriesIndex; }
- inline int seriesIndex() const { return m_seriesIndex; }
-
protected:
float m_value;
QPoint m_position; // x = row, y = column
GLfloat m_height;
- int m_seriesIndex;
friend class QBarDataItem;
};
diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp
new file mode 100644
index 00000000..a1c70057
--- /dev/null
+++ b/src/datavisualization/data/customrenderitem.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 "customrenderitem_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+CustomRenderItem::CustomRenderItem()
+ : AbstractRenderItem(),
+ m_texture(0),
+ m_object(0),
+ m_visible(true),
+ m_renderer(0)
+{
+}
+
+CustomRenderItem::~CustomRenderItem()
+{
+ ObjectHelper::releaseObjectHelper(m_renderer, m_object);
+}
+
+void CustomRenderItem::setMesh(const QString &meshFile)
+{
+ ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFile);
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h
new file mode 100644
index 00000000..4fb94276
--- /dev/null
+++ b/src/datavisualization/data/customrenderitem_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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 CUSTOMRENDERITEM_P_H
+#define CUSTOMRENDERITEM_P_H
+
+#include "abstractrenderitem_p.h"
+#include "objecthelper_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QCustom3DItem;
+class Abstract3DRenderer;
+
+class CustomRenderItem : public AbstractRenderItem
+{
+public:
+ CustomRenderItem();
+ virtual ~CustomRenderItem();
+
+ inline void setTexture(GLuint texture) { m_texture = texture; }
+ inline GLuint texture() const { return m_texture; }
+ 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 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 void setBlendNeeded(bool blend) { m_needBlend = blend; }
+ inline bool isBlendNeeded() const { return m_needBlend; }
+ inline void setVisible(bool visible) { m_visible = visible; }
+ inline bool isVisible() const { return m_visible; }
+ inline void setItemPointer(QCustom3DItem *item) { m_item = item; }
+ inline QCustom3DItem *itemPointer() const { return m_item; }
+ inline void setValid(bool valid) { m_valid = valid; }
+ inline bool isValid() const { return m_valid; }
+ inline void setIndex(int index) { m_index = index; }
+ inline int index() const { return m_index; }
+ inline void setShadowCasting(bool shadowCasting) { m_shadowCasting = shadowCasting; }
+ inline bool isShadowCasting() const { return m_shadowCasting; }
+ inline void setFacingCamera(bool facing) { m_isFacingCamera = facing; }
+ inline bool isFacingCamera() const { return m_isFacingCamera; }
+ inline void setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; }
+
+private:
+ Q_DISABLE_COPY(CustomRenderItem)
+
+ GLuint m_texture;
+ QVector3D m_scaling;
+ QVector3D m_position;
+ bool m_absolute;
+ ObjectHelper *m_object; // shared reference
+ bool m_needBlend;
+ bool m_visible;
+ bool m_valid;
+ int m_index;
+ bool m_shadowCasting;
+ bool m_isFacingCamera;
+ QCustom3DItem *m_item;
+ Abstract3DRenderer *m_renderer;
+};
+typedef QHash<QCustom3DItem *, CustomRenderItem *> CustomRenderItemArray;
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/data.pri b/src/datavisualization/data/data.pri
index 6ebfed6b..73f398bf 100644
--- a/src/datavisualization/data/data.pri
+++ b/src/datavisualization/data/data.pri
@@ -36,7 +36,12 @@ HEADERS += \
$$PWD/qscatter3dseries.h \
$$PWD/qscatter3dseries_p.h \
$$PWD/qsurface3dseries.h \
- $$PWD/qsurface3dseries_p.h
+ $$PWD/qsurface3dseries_p.h \
+ $$PWD/customrenderitem_p.h \
+ $$PWD/qcustom3ditem.h \
+ $$PWD/qcustom3ditem_p.h \
+ $$PWD/qcustom3dlabel.h \
+ $$PWD/qcustom3dlabel_p.h
SOURCES += \
$$PWD/labelitem.cpp \
@@ -61,4 +66,7 @@ SOURCES += \
$$PWD/qabstract3dseries.cpp \
$$PWD/qbar3dseries.cpp \
$$PWD/qscatter3dseries.cpp \
- $$PWD/qsurface3dseries.cpp
+ $$PWD/qsurface3dseries.cpp \
+ $$PWD/customrenderitem.cpp \
+ $$PWD/qcustom3ditem.cpp \
+ $$PWD/qcustom3dlabel.cpp
diff --git a/src/datavisualization/data/labelitem_p.h b/src/datavisualization/data/labelitem_p.h
index 3a2c1eb1..89b6cc56 100644
--- a/src/datavisualization/data/labelitem_p.h
+++ b/src/datavisualization/data/labelitem_p.h
@@ -30,7 +30,6 @@
#define LABELITEM_P_H
#include "datavisualizationglobal_p.h"
-#include <QtGui/QOpenGLFunctions>
#include <QtCore/QSize>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qabstract3dseries.cpp b/src/datavisualization/data/qabstract3dseries.cpp
index e593a1d9..f8fe6b4f 100644
--- a/src/datavisualization/data/qabstract3dseries.cpp
+++ b/src/datavisualization/data/qabstract3dseries.cpp
@@ -16,7 +16,6 @@
**
****************************************************************************/
-#include "qabstract3dseries.h"
#include "qabstract3dseries_p.h"
#include "qabstractdataproxy_p.h"
#include "abstract3dcontroller_p.h"
@@ -27,7 +26,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QAbstract3DSeries
* \inmodule QtDataVisualization
* \brief Base class for all QtDataVisualization series.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* You use the visualization type specific inherited classes instead of the base class.
* \sa QBar3DSeries, QScatter3DSeries, QSurface3DSeries, {Qt Data Visualization Data Handling}
@@ -53,7 +52,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* This type is uncreatable, but contains properties that are exposed via subtypes.
*
- * For Abstract3DSeries enums, see \l QAbstract3DSeries::SeriesType and \l QAbstract3DSeries::Mesh
+ * For Abstract3DSeries enums, see \l QAbstract3DSeries::SeriesType and \l{QAbstract3DSeries::Mesh}.
*
* \sa Bar3DSeries, Scatter3DSeries, Surface3DSeries, {Qt Data Visualization Data Handling}
*/
@@ -102,7 +101,8 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* Arrow pointing upwards.
* \value MeshPoint
* 2D point. Usable only with Q3DScatter.
- * \b Note: Shadows and color gradients do not affect this style.
+ * \b Note: Shadows do not affect this style. Color style Q3DTheme::ColorStyleObjectGradient
+ * is not supported by this style.
*/
/*!
@@ -240,6 +240,27 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty string Abstract3DSeries::itemLabel
+ * \since QtDataVisualization 1.1
+ *
+ * Contains the formatted item label. If there is no selected item or the selected item is not
+ * visible, returns an empty string.
+ *
+ * \sa itemLabelFormat
+ */
+
+/*!
+ * \qmlproperty bool Abstract3DSeries::itemLabelVisible
+ * \since QtDataVisualization 1.1
+ *
+ * If \c true, item labels are drawn as floating labels in the graph. Otherwise item labels are not
+ * drawn. If you prefer to show the item label in an external control, set this property to
+ * \c false. Defaults to \c true.
+ *
+ * \sa itemLabelFormat, itemLabel
+ */
+
+/*!
* \qmlmethod void Abstract3DSeries::setMeshAxisAndAngle(vector3d axis, real angle)
*
* A convenience function to construct mesh rotation quaternion from axis and angle.
@@ -586,9 +607,47 @@ QString QAbstract3DSeries::name() const
return d_ptr->m_name;
}
+/*!
+ * \property QAbstract3DSeries::itemLabel
+ * \since QtDataVisualization 1.1
+ *
+ * Contains the formatted item label. If there is no selected item or the selected item is not
+ * visible, returns an empty string.
+ *
+ * \sa itemLabelFormat
+ */
+QString QAbstract3DSeries::itemLabel() const
+{
+ return d_ptr->itemLabel();
+}
+
+/*!
+ * \property QAbstract3DSeries::itemLabelVisible
+ * \since QtDataVisualization 1.1
+ *
+ * If \c true, item labels are drawn as floating labels in the graph. Otherwise item labels are not
+ * drawn. If you prefer to show the item label in an external control, set this property to
+ * \c false. Defaults to \c true.
+ *
+ * \sa itemLabelFormat, itemLabel
+ */
+void QAbstract3DSeries::setItemLabelVisible(bool visible)
+{
+ if (d_ptr->m_itemLabelVisible != visible) {
+ d_ptr->setItemLabelVisible(visible);
+ emit itemLabelVisibilityChanged(visible);
+ }
+}
+
+bool QAbstract3DSeries::isItemLabelVisible() const
+{
+ return d_ptr->m_itemLabelVisible;
+}
+
// QAbstract3DSeriesPrivate
-QAbstract3DSeriesPrivate::QAbstract3DSeriesPrivate(QAbstract3DSeries *q, QAbstract3DSeries::SeriesType type)
+QAbstract3DSeriesPrivate::QAbstract3DSeriesPrivate(QAbstract3DSeries *q,
+ QAbstract3DSeries::SeriesType type)
: QObject(0),
q_ptr(q),
m_type(type),
@@ -597,7 +656,9 @@ QAbstract3DSeriesPrivate::QAbstract3DSeriesPrivate(QAbstract3DSeries *q, QAbstra
m_controller(0),
m_mesh(QAbstract3DSeries::MeshCube),
m_meshSmooth(false),
- m_colorStyle(Q3DTheme::ColorStyleUniform)
+ m_colorStyle(Q3DTheme::ColorStyleUniform),
+ m_itemLabelDirty(true),
+ m_itemLabelVisible(true)
{
}
@@ -630,53 +691,67 @@ void QAbstract3DSeriesPrivate::setController(Abstract3DController *controller)
connectControllerAndProxy(controller);
m_controller = controller;
q_ptr->setParent(controller);
+ markItemLabelDirty();
}
void QAbstract3DSeriesPrivate::setItemLabelFormat(const QString &format)
{
m_itemLabelFormat = format;
- m_changeTracker.itemLabelFormatChanged = true;
- if (m_controller)
- m_controller->markSeriesVisualsDirty();
+ markItemLabelDirty();
}
void QAbstract3DSeriesPrivate::setVisible(bool visible)
{
m_visible = visible;
- if (m_controller)
- m_controller->markSeriesVisualsDirty();
+ markItemLabelDirty();
}
void QAbstract3DSeriesPrivate::setMesh(QAbstract3DSeries::Mesh mesh)
{
m_mesh = mesh;
m_changeTracker.meshChanged = true;
- if (m_controller)
+ if (m_controller) {
m_controller->markSeriesVisualsDirty();
+
+ if (m_controller->optimizationHints().testFlag(QAbstract3DGraph::OptimizationStatic))
+ m_controller->markDataDirty();
+ }
}
void QAbstract3DSeriesPrivate::setMeshSmooth(bool enable)
{
m_meshSmooth = enable;
m_changeTracker.meshSmoothChanged = true;
- if (m_controller)
+ if (m_controller) {
m_controller->markSeriesVisualsDirty();
+
+ if (m_controller->optimizationHints().testFlag(QAbstract3DGraph::OptimizationStatic))
+ m_controller->markDataDirty();
+ }
}
void QAbstract3DSeriesPrivate::setMeshRotation(const QQuaternion &rotation)
{
m_meshRotation = rotation;
m_changeTracker.meshRotationChanged = true;
- if (m_controller)
+ if (m_controller) {
m_controller->markSeriesVisualsDirty();
+
+ if (m_controller->optimizationHints().testFlag(QAbstract3DGraph::OptimizationStatic))
+ m_controller->markDataDirty();
+ }
}
void QAbstract3DSeriesPrivate::setUserDefinedMesh(const QString &meshFile)
{
m_userDefinedMesh = meshFile;
m_changeTracker.userDefinedMeshChanged = true;
- if (m_controller)
+ if (m_controller) {
m_controller->markSeriesVisualsDirty();
+
+ if (m_controller->optimizationHints().testFlag(QAbstract3DGraph::OptimizationStatic))
+ m_controller->markDataDirty();
+ }
}
void QAbstract3DSeriesPrivate::setColorStyle(Q3DTheme::ColorStyle style)
@@ -738,9 +813,8 @@ void QAbstract3DSeriesPrivate::setMultiHighlightGradient(const QLinearGradient &
void QAbstract3DSeriesPrivate::setName(const QString &name)
{
m_name = name;
+ markItemLabelDirty();
m_changeTracker.nameChanged = true;
- if (m_controller)
- m_controller->markSeriesVisualsDirty();
}
void QAbstract3DSeriesPrivate::resetToTheme(const Q3DTheme &theme, int seriesIndex, bool force)
@@ -780,4 +854,36 @@ void QAbstract3DSeriesPrivate::resetToTheme(const Q3DTheme &theme, int seriesInd
}
}
+QString QAbstract3DSeriesPrivate::itemLabel()
+{
+ if (m_itemLabelDirty) {
+ QString oldLabel = m_itemLabel;
+ if (m_controller && m_visible)
+ createItemLabel();
+ else
+ m_itemLabel = QString();
+ m_itemLabelDirty = false;
+
+ if (oldLabel != m_itemLabel)
+ emit q_ptr->itemLabelChanged(m_itemLabel);
+ }
+
+ return m_itemLabel;
+}
+
+void QAbstract3DSeriesPrivate::markItemLabelDirty()
+{
+ m_itemLabelDirty = true;
+ m_changeTracker.itemLabelChanged = true;
+ if (m_controller)
+ m_controller->markSeriesVisualsDirty();
+}
+
+void QAbstract3DSeriesPrivate::setItemLabelVisible(bool visible)
+{
+ m_itemLabelVisible = visible;
+ markItemLabelDirty();
+ m_changeTracker.itemLabelVisibilityChanged = true;
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qabstract3dseries.h b/src/datavisualization/data/qabstract3dseries.h
index bf56422f..87c4f3c1 100644
--- a/src/datavisualization/data/qabstract3dseries.h
+++ b/src/datavisualization/data/qabstract3dseries.h
@@ -50,6 +50,8 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DSeries : public QObject
Q_PROPERTY(QColor multiHighlightColor READ multiHighlightColor WRITE setMultiHighlightColor NOTIFY multiHighlightColorChanged)
Q_PROPERTY(QLinearGradient multiHighlightGradient READ multiHighlightGradient WRITE setMultiHighlightGradient NOTIFY multiHighlightGradientChanged)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+ Q_PROPERTY(QString itemLabel READ itemLabel NOTIFY itemLabelChanged REVISION 1)
+ Q_PROPERTY(bool itemLabelVisible READ isItemLabelVisible WRITE setItemLabelVisible NOTIFY itemLabelVisibilityChanged REVISION 1)
public:
enum SeriesType {
@@ -119,6 +121,10 @@ public:
void setName(const QString &name);
QString name() const;
+ QString itemLabel() const;
+ void setItemLabelVisible(bool visible);
+ bool isItemLabelVisible() const;
+
signals:
void itemLabelFormatChanged(const QString &format);
void visibilityChanged(bool visible);
@@ -134,6 +140,8 @@ signals:
void multiHighlightColorChanged(const QColor &color);
void multiHighlightGradientChanged(const QLinearGradient &gradient);
void nameChanged(const QString &name);
+ Q_REVISION(1) void itemLabelChanged(const QString &label);
+ Q_REVISION(1) void itemLabelVisibilityChanged(bool visible);
protected:
QScopedPointer<QAbstract3DSeriesPrivate> d_ptr;
diff --git a/src/datavisualization/data/qabstract3dseries_p.h b/src/datavisualization/data/qabstract3dseries_p.h
index a803e99b..dd574e03 100644
--- a/src/datavisualization/data/qabstract3dseries_p.h
+++ b/src/datavisualization/data/qabstract3dseries_p.h
@@ -26,19 +26,18 @@
//
// We mean it.
-#include "datavisualizationglobal_p.h"
-#include "qabstract3dseries.h"
-
#ifndef QABSTRACT3DSERIES_P_H
#define QABSTRACT3DSERIES_P_H
+#include "datavisualizationglobal_p.h"
+#include "qabstract3dseries.h"
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QAbstractDataProxy;
class Abstract3DController;
struct QAbstract3DSeriesChangeBitField {
- bool itemLabelFormatChanged : 1;
bool meshChanged : 1;
bool meshSmoothChanged : 1;
bool meshRotationChanged : 1;
@@ -51,10 +50,12 @@ struct QAbstract3DSeriesChangeBitField {
bool multiHighlightColorChanged : 1;
bool multiHighlightGradientChanged : 1;
bool nameChanged : 1;
+ bool itemLabelChanged : 1;
+ bool itemLabelVisibilityChanged : 1;
+ bool visibilityChanged : 1;
QAbstract3DSeriesChangeBitField()
- : itemLabelFormatChanged(true),
- meshChanged(true),
+ : meshChanged(true),
meshSmoothChanged(true),
meshRotationChanged(true),
userDefinedMeshChanged(true),
@@ -65,7 +66,10 @@ struct QAbstract3DSeriesChangeBitField {
singleHighlightGradientChanged(true),
multiHighlightColorChanged(true),
multiHighlightGradientChanged(true),
- nameChanged(true)
+ nameChanged(true),
+ itemLabelChanged(true),
+ itemLabelVisibilityChanged(true),
+ visibilityChanged(true)
{
}
};
@@ -102,6 +106,7 @@ public:
virtual void setDataProxy(QAbstractDataProxy *proxy);
virtual void setController(Abstract3DController *controller);
virtual void connectControllerAndProxy(Abstract3DController *newController) = 0;
+ virtual void createItemLabel() = 0;
void setItemLabelFormat(const QString &format);
void setVisible(bool visible);
@@ -120,6 +125,10 @@ public:
void setName(const QString &name);
void resetToTheme(const Q3DTheme &theme, int seriesIndex, bool force);
+ QString itemLabel();
+ void markItemLabelDirty();
+ inline bool itemLabelDirty() const { return m_itemLabelDirty; }
+ void setItemLabelVisible(bool visible);
QAbstract3DSeriesChangeBitField m_changeTracker;
QAbstract3DSeriesThemeOverrideBitField m_themeTracker;
@@ -143,6 +152,9 @@ public:
QLinearGradient m_multiHighlightGradient;
QString m_name;
+ QString m_itemLabel;
+ bool m_itemLabelDirty;
+ bool m_itemLabelVisible;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qabstractdataproxy.cpp b/src/datavisualization/data/qabstractdataproxy.cpp
index 18d88971..eb15cec8 100644
--- a/src/datavisualization/data/qabstractdataproxy.cpp
+++ b/src/datavisualization/data/qabstractdataproxy.cpp
@@ -16,7 +16,6 @@
**
****************************************************************************/
-#include "qabstractdataproxy.h"
#include "qabstractdataproxy_p.h"
#include "qabstract3dseries_p.h"
@@ -26,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QAbstractDataProxy
* \inmodule QtDataVisualization
* \brief Base class for all QtDataVisualization data proxies.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* You use the visualization type specific inherited classes instead of the base class.
* \sa QBarDataProxy, QScatterDataProxy, QSurfaceDataProxy, {Qt Data Visualization Data Handling}
@@ -42,7 +41,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* This type is uncreatable, but contains properties that are exposed via subtypes.
*
- * For AbstractDataProxy enums, see \l QAbstractDataProxy::DataType
+ * For AbstractDataProxy enums, see \l{QAbstractDataProxy::DataType}.
*
* \sa BarDataProxy, ScatterDataProxy, SurfaceDataProxy, {Qt Data Visualization Data Handling}
*/
diff --git a/src/datavisualization/data/qabstractdataproxy_p.h b/src/datavisualization/data/qabstractdataproxy_p.h
index eb901f4c..c2f53369 100644
--- a/src/datavisualization/data/qabstractdataproxy_p.h
+++ b/src/datavisualization/data/qabstractdataproxy_p.h
@@ -26,12 +26,12 @@
//
// We mean it.
-#include "datavisualizationglobal_p.h"
-#include "qabstractdataproxy.h"
-
#ifndef QABSTRACTDATAPROXY_P_H
#define QABSTRACTDATAPROXY_P_H
+#include "datavisualizationglobal_p.h"
+#include "qabstractdataproxy.h"
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QAbstract3DSeries;
diff --git a/src/datavisualization/data/qbar3dseries.cpp b/src/datavisualization/data/qbar3dseries.cpp
index ed4ffaba..3440a3db 100644
--- a/src/datavisualization/data/qbar3dseries.cpp
+++ b/src/datavisualization/data/qbar3dseries.cpp
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QBar3DSeries
* \inmodule QtDataVisualization
* \brief Base series class for Q3DBars.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QBar3DSeries manages the series specific visual elements, as well as series data
* (via data proxy).
@@ -317,6 +317,56 @@ void QBar3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *newCon
}
}
+void QBar3DSeriesPrivate::createItemLabel()
+{
+ static const QString rowIndexTag(QStringLiteral("@rowIdx"));
+ static const QString rowLabelTag(QStringLiteral("@rowLabel"));
+ static const QString rowTitleTag(QStringLiteral("@rowTitle"));
+ static const QString colIndexTag(QStringLiteral("@colIdx"));
+ static const QString colLabelTag(QStringLiteral("@colLabel"));
+ static const QString colTitleTag(QStringLiteral("@colTitle"));
+ static const QString valueTitleTag(QStringLiteral("@valueTitle"));
+ static const QString valueLabelTag(QStringLiteral("@valueLabel"));
+ static const QString seriesNameTag(QStringLiteral("@seriesName"));
+
+ if (m_selectedBar == QBar3DSeries::invalidSelectionPosition()) {
+ m_itemLabel = QString();
+ return;
+ }
+
+ QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_controller->axisZ());
+ QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_controller->axisX());
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_controller->axisY());
+ qreal selectedBarValue = qreal(qptr()->dataProxy()->itemAt(m_selectedBar)->value());
+
+ // Custom format expects printf format specifier. There is no tag for it.
+ m_itemLabel = valueAxis->formatter()->stringForValue(selectedBarValue, m_itemLabelFormat);
+
+ int selBarPosRow = m_selectedBar.x();
+ int selBarPosCol = m_selectedBar.y();
+ m_itemLabel.replace(rowIndexTag, QString::number(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));
+ if (categoryAxisX->labels().size() > selBarPosCol)
+ m_itemLabel.replace(colLabelTag, categoryAxisX->labels().at(selBarPosCol));
+ else
+ m_itemLabel.replace(colLabelTag, QString());
+ m_itemLabel.replace(colTitleTag, categoryAxisX->title());
+ m_itemLabel.replace(valueTitleTag, valueAxis->title());
+
+ if (m_itemLabel.contains(valueLabelTag)) {
+ QString valueLabelText = valueAxis->formatter()->stringForValue(selectedBarValue,
+ valueAxis->labelFormat());
+ m_itemLabel.replace(valueLabelTag, valueLabelText);
+ }
+
+ m_itemLabel.replace(seriesNameTag, m_name);
+}
+
void QBar3DSeriesPrivate::handleMeshRotationChanged(const QQuaternion &rotation)
{
emit qptr()->meshAngleChanged(quaternionAngle(rotation));
@@ -325,6 +375,7 @@ void QBar3DSeriesPrivate::handleMeshRotationChanged(const QQuaternion &rotation)
void QBar3DSeriesPrivate::setSelectedBar(const QPoint &position)
{
if (position != m_selectedBar) {
+ markItemLabelDirty();
m_selectedBar = position;
emit qptr()->selectedBarChanged(m_selectedBar);
}
diff --git a/src/datavisualization/data/qbar3dseries_p.h b/src/datavisualization/data/qbar3dseries_p.h
index 718f1237..c5b51108 100644
--- a/src/datavisualization/data/qbar3dseries_p.h
+++ b/src/datavisualization/data/qbar3dseries_p.h
@@ -43,6 +43,7 @@ public:
virtual void setDataProxy(QAbstractDataProxy *proxy);
virtual void connectControllerAndProxy(Abstract3DController *newController);
+ virtual void createItemLabel();
void handleMeshRotationChanged(const QQuaternion &rotation);
diff --git a/src/datavisualization/data/qbardataitem.cpp b/src/datavisualization/data/qbardataitem.cpp
index 37c2033f..8f370e24 100644
--- a/src/datavisualization/data/qbardataitem.cpp
+++ b/src/datavisualization/data/qbardataitem.cpp
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QBarDataItem
* \inmodule QtDataVisualization
* \brief The QBarDataItem class provides a container for resolved data to be added to bar graphs.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* A QBarDataItem holds data for a single rendered bar in a graph.
* Bar data proxies parse data into QBarDataItem instances for visualizing.
diff --git a/src/datavisualization/data/qbardataitem_p.h b/src/datavisualization/data/qbardataitem_p.h
index c06ddea9..623c4012 100644
--- a/src/datavisualization/data/qbardataitem_p.h
+++ b/src/datavisualization/data/qbardataitem_p.h
@@ -39,9 +39,6 @@ class QBarDataItemPrivate
public:
QBarDataItemPrivate();
virtual ~QBarDataItemPrivate();
-
-protected:
- friend class QBarDataItem;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qbardataproxy.cpp b/src/datavisualization/data/qbardataproxy.cpp
index 1c1170ff..8e4f25de 100644
--- a/src/datavisualization/data/qbardataproxy.cpp
+++ b/src/datavisualization/data/qbardataproxy.cpp
@@ -16,7 +16,6 @@
**
****************************************************************************/
-#include "qbardataproxy.h"
#include "qbardataproxy_p.h"
#include "qbar3dseries_p.h"
@@ -26,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QBarDataProxy
* \inmodule QtDataVisualization
* \brief Base proxy class for Q3DBars.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QBarDataProxy handles adding, inserting, changing and removing rows of data.
*
@@ -554,7 +553,8 @@ void QBarDataProxyPrivate::setRow(int rowIndex, QBarDataRow *row, const QString
}
}
-void QBarDataProxyPrivate::setRows(int rowIndex, const QBarDataArray &rows, const QStringList *labels)
+void QBarDataProxyPrivate::setRows(int rowIndex, const QBarDataArray &rows,
+ const QStringList *labels)
{
QBarDataArray &dataArray = *m_dataArray;
Q_ASSERT(rowIndex >= 0 && (rowIndex + rows.size()) <= dataArray.size());
@@ -604,7 +604,8 @@ void QBarDataProxyPrivate::insertRow(int rowIndex, QBarDataRow *row, const QStri
m_dataArray->insert(rowIndex, row);
}
-void QBarDataProxyPrivate::insertRows(int rowIndex, const QBarDataArray &rows, const QStringList *labels)
+void QBarDataProxyPrivate::insertRows(int rowIndex, const QBarDataArray &rows,
+ const QStringList *labels)
{
Q_ASSERT(rowIndex >= 0 && rowIndex <= m_dataArray->size());
if (labels)
@@ -656,7 +657,8 @@ void QBarDataProxyPrivate::clearArray()
* \internal
* Fixes the row label array to include specified labels.
*/
-void QBarDataProxyPrivate::fixRowLabels(int startIndex, int count, const QStringList &newLabels, bool isInsert)
+void QBarDataProxyPrivate::fixRowLabels(int startIndex, int count, const QStringList &newLabels,
+ bool isInsert)
{
bool changed = false;
int currentSize = m_rowLabels.size();
diff --git a/src/datavisualization/data/qbardataproxy_p.h b/src/datavisualization/data/qbardataproxy_p.h
index eb0a0873..4d1489d9 100644
--- a/src/datavisualization/data/qbardataproxy_p.h
+++ b/src/datavisualization/data/qbardataproxy_p.h
@@ -31,7 +31,6 @@
#include "qbardataproxy.h"
#include "qabstractdataproxy_p.h"
-#include "qbardataitem.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp
new file mode 100644
index 00000000..f5d8470f
--- /dev/null
+++ b/src/datavisualization/data/qcustom3ditem.cpp
@@ -0,0 +1,422 @@
+/****************************************************************************
+**
+** 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 "qcustom3ditem_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+/*!
+ * \class QCustom3DItem
+ * \inmodule QtDataVisualization
+ * \brief The QCustom3DItem class is for creating custom items to be added to a graph.
+ * \since QtDataVisualization 1.1
+ *
+ * This class is for creating custom items to be added to a graph. The item has a custom mesh,
+ * position, scaling, rotation, and an optional texture.
+ *
+ * \sa QAbstract3DGraph::addCustomItem()
+ */
+
+/*!
+ * \qmltype Custom3DItem
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.1
+ * \ingroup datavisualization_qml
+ * \instantiates QCustom3DItem
+ * \brief The Custom3DItem type is for creating custom items to be added to a graph.
+ *
+ * This type is for creating custom items to be added to a graph. The item has a custom mesh,
+ * position, scaling, rotation, and an optional texture.
+ */
+
+/*! \qmlproperty string Custom3DItem::meshFile
+ *
+ * Holds item mesh file name. Item in the file must be in Wavefront obj format and include
+ * vertices, normals, and UVs. It also needs to be in triangles.
+ */
+
+/*! \qmlproperty string Custom3DItem::textureFile
+ *
+ * Holds the texture file name for the item. If left unset, a solid gray texture will be
+ * used.
+ *
+ * \note To conserve memory the Image loaded from the file is cleared after a texture is created.
+ */
+
+/*! \qmlproperty vector3d Custom3DItem::position
+ *
+ * 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
+ * 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}.
+ *
+ * \sa positionAbsolute
+ */
+
+/*! \qmlproperty bool Custom3DItem::positionAbsolute
+ *
+ * This property dictates if item position is to be handled in data coordinates or in absolute
+ * coordinates. Defaults to \c{false}. Items with absolute coordinates will always be rendered,
+ * whereas items with data coordinates are only rendered if they are within axis ranges.
+ *
+ * \sa position
+ */
+
+/*! \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.
+ */
+
+/*! \qmlproperty quaternion Custom3DItem::rotation
+ *
+ * Holds the item \a rotation as a quaternion. Defaults to \c {quaternion(0.0, 0.0, 0.0, 0.0)}.
+ */
+
+/*! \qmlproperty bool Custom3DItem::visible
+ *
+ * Sets the item \a visible. Defaults to \c{true}.
+ */
+
+/*! \qmlproperty bool Custom3DItem::shadowCasting
+ *
+ * Sets shadow casting for the item to \a enabled. Defaults to \c{true}.
+ * If set \c{false}, the item does not cast shadows regardless of
+ * \l{QAbstract3DGraph::ShadowQuality}{ShadowQuality}.
+ */
+
+/*!
+ * \qmlmethod void Custom3DItem::setRotationAxisAndAngle(vector3d axis, real angle)
+ *
+ * A convenience function to construct rotation quaternion from \a axis and \a angle.
+ *
+ * \sa rotation
+ */
+
+/*!
+ * Constructs QCustom3DItem with given \a parent.
+ */
+QCustom3DItem::QCustom3DItem(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QCustom3DItemPrivate(this))
+{
+ setTextureImage(QImage());
+}
+
+/*!
+ * \internal
+ */
+QCustom3DItem::QCustom3DItem(QCustom3DItemPrivate *d, QObject *parent) :
+ QObject(parent),
+ d_ptr(d)
+{
+ setTextureImage(QImage());
+}
+
+/*!
+ * Constructs QCustom3DItem with given \a meshFile, \a position, \a scaling,
+ * \a rotation, \a texture image, and optional \a parent.
+ */
+QCustom3DItem::QCustom3DItem(const QString &meshFile, const QVector3D &position,
+ const QVector3D &scaling, const QQuaternion &rotation,
+ const QImage &texture, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QCustom3DItemPrivate(this, meshFile, position, scaling, rotation))
+{
+ setTextureImage(texture);
+}
+
+/*!
+ * Destroys QCustom3DItem.
+ */
+QCustom3DItem::~QCustom3DItem()
+{
+}
+
+/*! \property QCustom3DItem::meshFile
+ *
+ * Holds item mesh file name. Item in the file must be in Wavefront obj format and include
+ * vertices, normals, and UVs. It also needs to be in triangles.
+ */
+void QCustom3DItem::setMeshFile(const QString &meshFile)
+{
+ if (d_ptr->m_meshFile != meshFile) {
+ d_ptr->m_meshFile = meshFile;
+ d_ptr->m_dirtyBits.meshDirty = true;
+ emit meshFileChanged(meshFile);
+ emit d_ptr->needUpdate();
+ }
+}
+
+QString QCustom3DItem::meshFile() const
+{
+ return d_ptr->m_meshFile;
+}
+
+/*! \property QCustom3DItem::position
+ *
+ * Holds the item \a position as a QVector3D. Defaults to \c {QVector3D(0.0, 0.0, 0.0)}.
+ *
+ * Item position is either in data coordinates or in absolute coordinates, depending on
+ * 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}.
+ *
+ * \sa positionAbsolute
+ */
+void QCustom3DItem::setPosition(const QVector3D &position)
+{
+ if (d_ptr->m_position != position) {
+ d_ptr->m_position = position;
+ d_ptr->m_dirtyBits.positionDirty = true;
+ emit positionChanged(position);
+ emit d_ptr->needUpdate();
+ }
+}
+
+QVector3D QCustom3DItem::position() const
+{
+ return d_ptr->m_position;
+}
+
+/*! \property QCustom3DItem::positionAbsolute
+ *
+ * This property dictates if item position is to be handled in data coordinates or in absolute
+ * coordinates. Defaults to \c{false}. Items with absolute coordinates will always be rendered,
+ * whereas items with data coordinates are only rendered if they are within axis ranges.
+ *
+ * \sa position
+ */
+void QCustom3DItem::setPositionAbsolute(bool positionAbsolute)
+{
+ if (d_ptr->m_positionAbsolute != positionAbsolute) {
+ d_ptr->m_positionAbsolute = positionAbsolute;
+ d_ptr->m_dirtyBits.positionAbsoluteDirty = true;
+ emit positionAbsoluteChanged(positionAbsolute);
+ emit d_ptr->needUpdate();
+ }
+}
+
+bool QCustom3DItem::isPositionAbsolute() const
+{
+ return d_ptr->m_positionAbsolute;
+}
+
+/*! \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.
+ */
+void QCustom3DItem::setScaling(const QVector3D &scaling)
+{
+ if (d_ptr->m_scaling != scaling) {
+ d_ptr->m_scaling = scaling;
+ d_ptr->m_dirtyBits.scalingDirty = true;
+ emit scalingChanged(scaling);
+ emit d_ptr->needUpdate();
+ }
+}
+
+QVector3D QCustom3DItem::scaling() const
+{
+ return d_ptr->m_scaling;
+}
+
+/*! \property QCustom3DItem::rotation
+ *
+ * Holds the item \a rotation as a QQuaternion. Defaults to \c {QQuaternion(0.0, 0.0, 0.0, 0.0)}.
+ */
+void QCustom3DItem::setRotation(const QQuaternion &rotation)
+{
+ if (d_ptr->m_rotation != rotation) {
+ d_ptr->m_rotation = rotation;
+ d_ptr->m_dirtyBits.rotationDirty = true;
+ emit rotationChanged(rotation);
+ emit d_ptr->needUpdate();
+ }
+}
+
+QQuaternion QCustom3DItem::rotation()
+{
+ return d_ptr->m_rotation;
+}
+
+/*! \property QCustom3DItem::visible
+ *
+ * Sets the item \a visible. Defaults to \c{true}.
+ */
+void QCustom3DItem::setVisible(bool visible)
+{
+ if (d_ptr->m_visible != visible) {
+ d_ptr->m_visible = visible;
+ d_ptr->m_dirtyBits.visibleDirty = true;
+ emit visibleChanged(visible);
+ emit d_ptr->needUpdate();
+ }
+}
+
+bool QCustom3DItem::isVisible() const
+{
+ return d_ptr->m_visible;
+}
+
+
+/*! \property QCustom3DItem::shadowCasting
+ *
+ * Sets shadow casting for the item to \a enabled. Defaults to \c{true}.
+ * If set \c{false}, the item does not cast shadows regardless of QAbstract3DGraph::ShadowQuality.
+ */
+void QCustom3DItem::setShadowCasting(bool enabled)
+{
+ if (d_ptr->m_shadowCasting != enabled) {
+ d_ptr->m_shadowCasting = enabled;
+ d_ptr->m_dirtyBits.shadowCastingDirty = true;
+ emit shadowCastingChanged(enabled);
+ emit d_ptr->needUpdate();
+ }
+}
+
+bool QCustom3DItem::isShadowCasting() const
+{
+ return d_ptr->m_shadowCasting;
+}
+
+/*!
+ * A convenience function to construct rotation quaternion from \a axis and \a angle.
+ *
+ * \sa rotation
+ */
+void QCustom3DItem::setRotationAxisAndAngle(const QVector3D &axis, float angle)
+{
+ setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
+}
+
+/*!
+ * Set the \a textureImage as a QImage for the item. Texture defaults to solid gray.
+ *
+ * \note To conserve memory the given QImage is cleared after a texture is created.
+ */
+void QCustom3DItem::setTextureImage(const QImage &textureImage)
+{
+ if (textureImage != d_ptr->m_textureImage) {
+ if (textureImage.isNull()) {
+ // Make a solid gray texture
+ d_ptr->m_textureImage = QImage(2, 2, QImage::Format_RGB32);
+ d_ptr->m_textureImage.fill(Qt::gray);
+ } else {
+ d_ptr->m_textureImage = textureImage;
+ }
+
+ if (!d_ptr->m_textureFile.isEmpty()) {
+ d_ptr->m_textureFile.clear();
+ emit textureFileChanged(d_ptr->m_textureFile);
+ }
+ d_ptr->m_dirtyBits.textureDirty = true;
+ emit d_ptr->needUpdate();
+ }
+}
+
+/*! \property QCustom3DItem::textureFile
+ *
+ * Holds the texture file name for the item. If both this and texture image are unset, a solid
+ * gray texture will be used.
+ *
+ * \note To conserve memory the QImage loaded from the file is cleared after a texture is created.
+ */
+void QCustom3DItem::setTextureFile(const QString &textureFile)
+{
+ if (d_ptr->m_textureFile != textureFile) {
+ d_ptr->m_textureFile = textureFile;
+ if (!textureFile.isEmpty()) {
+ d_ptr->m_textureImage = QImage(textureFile);
+ } else {
+ d_ptr->m_textureImage = QImage(2, 2, QImage::Format_RGB32);
+ d_ptr->m_textureImage.fill(Qt::gray);
+ }
+ emit textureFileChanged(textureFile);
+ d_ptr->m_dirtyBits.textureDirty = true;
+ emit d_ptr->needUpdate();
+ }
+}
+
+QString QCustom3DItem::textureFile() const
+{
+ return d_ptr->m_textureFile;
+}
+
+QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) :
+ q_ptr(q),
+ 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_visible(true),
+ m_shadowCasting(true),
+ m_isLabelItem(false)
+{
+}
+
+QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &meshFile,
+ const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation) :
+ q_ptr(q),
+ m_meshFile(meshFile),
+ m_position(position),
+ m_positionAbsolute(false),
+ m_scaling(scaling),
+ m_rotation(rotation),
+ m_visible(true),
+ m_shadowCasting(true),
+ m_isLabelItem(false)
+{
+}
+
+QCustom3DItemPrivate::~QCustom3DItemPrivate()
+{
+}
+
+QImage QCustom3DItemPrivate::textureImage()
+{
+ return m_textureImage;
+}
+
+void QCustom3DItemPrivate::clearTextureImage()
+{
+ m_textureImage = QImage();
+ m_textureFile.clear();
+}
+
+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;
+ m_dirtyBits.shadowCastingDirty = false;
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qcustom3ditem.h b/src/datavisualization/data/qcustom3ditem.h
new file mode 100644
index 00000000..2f7f37cf
--- /dev/null
+++ b/src/datavisualization/data/qcustom3ditem.h
@@ -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
+**
+****************************************************************************/
+
+#ifndef QCUSTOM3DITEM_H
+#define QCUSTOM3DITEM_H
+
+#include <QtDataVisualization/qdatavisualizationglobal.h>
+#include <QtGui/QImage>
+#include <QtGui/QVector3D>
+#include <QtGui/QQuaternion>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QCustom3DItemPrivate;
+
+class QT_DATAVISUALIZATION_EXPORT QCustom3DItem : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString meshFile READ meshFile WRITE setMeshFile NOTIFY meshFileChanged)
+ Q_PROPERTY(QString textureFile READ textureFile WRITE setTextureFile NOTIFY textureFileChanged)
+ Q_PROPERTY(QVector3D position READ position WRITE setPosition NOTIFY positionChanged)
+ Q_PROPERTY(bool positionAbsolute READ isPositionAbsolute WRITE setPositionAbsolute NOTIFY positionAbsoluteChanged)
+ Q_PROPERTY(QVector3D scaling READ scaling WRITE setScaling NOTIFY scalingChanged)
+ 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)
+
+public:
+ explicit QCustom3DItem(QObject *parent = 0);
+ explicit QCustom3DItem(const QString &meshFile, const QVector3D &position,
+ const QVector3D &scaling, const QQuaternion &rotation,
+ const QImage &texture, QObject *parent = 0);
+ virtual ~QCustom3DItem();
+
+ void setMeshFile(const QString &meshFile);
+ QString meshFile() const;
+
+ void setTextureFile(const QString &textureFile);
+ QString textureFile() const;
+
+ void setPosition(const QVector3D &position);
+ QVector3D position() const;
+
+ void setPositionAbsolute(bool positionAbsolute);
+ bool isPositionAbsolute() const;
+
+ void setScaling(const QVector3D &scaling);
+ QVector3D scaling() const;
+
+ void setRotation(const QQuaternion &rotation);
+ QQuaternion rotation();
+
+ void setVisible(bool visible);
+ bool isVisible() const;
+
+ void setShadowCasting(bool enabled);
+ bool isShadowCasting() const;
+
+ Q_INVOKABLE void setRotationAxisAndAngle(const QVector3D &axis, float angle);
+
+ void setTextureImage(const QImage &textureImage);
+
+signals:
+ void meshFileChanged(const QString &meshFile);
+ void textureFileChanged(const QString &textureFile);
+ void positionChanged(const QVector3D &position);
+ void positionAbsoluteChanged(bool positionAbsolute);
+ void scalingChanged(const QVector3D &scaling);
+ void rotationChanged(const QQuaternion &rotation);
+ void visibleChanged(bool visible);
+ void shadowCastingChanged(bool shadowCasting);
+
+protected:
+ QCustom3DItem(QCustom3DItemPrivate *d, QObject *parent = 0);
+
+ QScopedPointer<QCustom3DItemPrivate> d_ptr;
+
+private:
+ Q_DISABLE_COPY(QCustom3DItem)
+
+ friend class Abstract3DRenderer;
+ friend class Abstract3DController;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qcustom3ditem_p.h b/src/datavisualization/data/qcustom3ditem_p.h
new file mode 100644
index 00000000..c1ce5996
--- /dev/null
+++ b/src/datavisualization/data/qcustom3ditem_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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 QCUSTOM3DITEM_P_H
+#define QCUSTOM3DITEM_P_H
+
+#include "qcustom3ditem.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+struct QCustomItemDirtyBitField {
+ bool textureDirty : 1;
+ bool meshDirty : 1;
+ bool positionDirty : 1;
+ bool positionAbsoluteDirty : 1;
+ bool scalingDirty : 1;
+ bool rotationDirty : 1;
+ bool visibleDirty : 1;
+ bool shadowCastingDirty : 1;
+
+ QCustomItemDirtyBitField()
+ : textureDirty(false),
+ meshDirty(false),
+ positionDirty(false),
+ positionAbsoluteDirty(false),
+ scalingDirty(false),
+ rotationDirty(false),
+ visibleDirty(false),
+ shadowCastingDirty(false)
+ {
+ }
+};
+
+class QCustom3DItemPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QCustom3DItemPrivate(QCustom3DItem *q);
+ QCustom3DItemPrivate(QCustom3DItem *q, const QString &meshFile, const QVector3D &position,
+ const QVector3D &scaling, const QQuaternion &rotation);
+ virtual ~QCustom3DItemPrivate();
+
+ QImage textureImage();
+ void clearTextureImage();
+ void resetDirtyBits();
+
+public:
+ QCustom3DItem *q_ptr;
+ QImage m_textureImage;
+ QString m_textureFile;
+ QString m_meshFile;
+ QVector3D m_position;
+ bool m_positionAbsolute;
+ QVector3D m_scaling;
+ QQuaternion m_rotation;
+ bool m_visible;
+ bool m_shadowCasting;
+
+ bool m_isLabelItem;
+
+ QCustomItemDirtyBitField m_dirtyBits;
+
+signals:
+ void needUpdate();
+
+private:
+ QCustom3DItemPrivate(QCustom3DItemPrivate *d);
+
+ friend class QCustom3DItem;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qcustom3dlabel.cpp b/src/datavisualization/data/qcustom3dlabel.cpp
new file mode 100644
index 00000000..85a37e2d
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dlabel.cpp
@@ -0,0 +1,358 @@
+/****************************************************************************
+**
+** 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 "qcustom3dlabel_p.h"
+#include "utils_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+/*!
+ * \class QCustom3DLabel
+ * \inmodule QtDataVisualization
+ * \brief The QCustom3DLabel class is for creating custom labels to be added to a graph.
+ * \since QtDataVisualization 1.1
+ *
+ * This class is for creating custom labels to be added to a graph. You can set text, font,
+ * position, scaling, rotation, and colors. You can also toggle borders and background for the
+ * label. Colors, borders and background are used from active theme unless any of them is set
+ * explicitly.
+ *
+ * \note In scaling, z has no effect. Setting the same x and y retains the original
+ * font dimensions.
+ *
+ * \sa QAbstract3DGraph::addCustomItem()
+ */
+
+/*!
+ * \qmltype Custom3DLabel
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.1
+ * \ingroup datavisualization_qml
+ * \instantiates QCustom3DLabel
+ * \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,
+ * position, scaling, rotation, and colors. You can also toggle borders and background for the
+ * label. Colors, borders and background are used from active theme unless any of them is set
+ * explicitly.
+ *
+ * \note In scaling, z has no effect. Setting the same x and y retains the original
+ * font dimensions.
+ */
+
+/*! \qmlproperty string Custom3DLabel::text
+ *
+ * The text for the label. Rich text is not supported.
+ */
+
+/*! \qmlproperty font Custom3DLabel::font
+ *
+ * The font to be used for the label. Defaults to \c{Font {family: "Arial"; pointSize: 20}}.
+ * Special formatting (for example outlined) is not supported.
+ */
+
+/*! \qmlproperty color Custom3DLabel::textColor
+ *
+ * Color for the label text. Also affects label border, if enabled. Defaults to \c{"white"}.
+ *
+ * \sa borderEnabled
+ */
+
+/*! \qmlproperty color Custom3DLabel::backgroundColor
+ *
+ * Color for the label background, if enabled. Defaults to \c{"gray"}.
+ *
+ * \sa backgroundEnabled
+ */
+
+/*! \qmlproperty bool Custom3DLabel::backgroundEnabled
+ *
+ * Enable label background. If set to \c{false}, backgroundColor has no effect. Defaults
+ * to \c{true}.
+ */
+
+/*! \qmlproperty bool Custom3DLabel::borderEnabled
+ *
+ * Enable label borders. Defaults to \c{true}.
+ */
+
+/*! \qmlproperty bool Custom3DLabel::facingCamera
+ *
+ * Forces the label to face camera always. Defaults to \c{false}. If set to \c{true}, rotation()
+ * has no effect.
+ */
+
+/*!
+ * Constructs QCustom3DLabel with given \a parent.
+ */
+QCustom3DLabel::QCustom3DLabel(QObject *parent) :
+ QCustom3DItem(new QCustom3DLabelPrivate(this), parent)
+{
+}
+
+/*!
+ * Constructs QCustom3DLabel with given \a text, \a font, \a position, \a scaling,
+ * \a rotation, and optional \a parent.
+ *
+ * \note Setting the same x and y for \a scaling retains the original font dimensions.
+ */
+QCustom3DLabel::QCustom3DLabel(const QString &text, const QFont &font,
+ const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation, QObject *parent) :
+ QCustom3DItem(new QCustom3DLabelPrivate(this, text, font, position, scaling, rotation),
+ parent)
+{
+}
+
+/*!
+ * Destroys QCustom3DLabel.
+ */
+QCustom3DLabel::~QCustom3DLabel()
+{
+}
+
+/*! \property QCustom3DLabel::text
+ *
+ * The text for the label. Rich text is not supported.
+ */
+void QCustom3DLabel::setText(const QString &text)
+{
+ if (dptr()->m_text != text) {
+ dptr()->m_text = text;
+ dptr()->handleTextureChange();
+ emit textChanged(text);
+ emit dptr()->needUpdate();
+ }
+}
+
+QString QCustom3DLabel::text() const
+{
+ return dptrc()->m_text;
+}
+
+/*! \property QCustom3DLabel::font
+ *
+ * The font to be used for the label. Defaults to \c{QFont("Arial", 20)}. Special formatting
+ * (for example outlined) is not supported.
+ */
+void QCustom3DLabel::setFont(const QFont &font)
+{
+ if (dptr()->m_font != font) {
+ dptr()->m_font = font;
+ dptr()->handleTextureChange();
+ emit fontChanged(font);
+ emit dptr()->needUpdate();
+ }
+}
+
+QFont QCustom3DLabel::font() const
+{
+ return dptrc()->m_font;
+}
+
+/*! \property QCustom3DLabel::textColor
+ *
+ * Color for the label text. Also affects label border, if enabled. Defaults to \c{Qt::white}.
+ *
+ * \sa borderEnabled
+ */
+void QCustom3DLabel::setTextColor(const QColor &color)
+{
+ if (dptr()->m_txtColor != color) {
+ dptr()->m_txtColor = color;
+ dptr()->m_customVisuals = true;
+ dptr()->handleTextureChange();
+ emit textColorChanged(color);
+ emit dptr()->needUpdate();
+ }
+}
+
+QColor QCustom3DLabel::textColor() const
+{
+ return dptrc()->m_txtColor;
+}
+
+/*! \property QCustom3DLabel::backgroundColor
+ *
+ * Color for the label background, if enabled. Defaults to \c{Qt::gray}.
+ *
+ * \sa backgroundEnabled
+ */
+void QCustom3DLabel::setBackgroundColor(const QColor &color)
+{
+ if (dptr()->m_bgrColor != color) {
+ dptr()->m_bgrColor = color;
+ dptr()->m_customVisuals = true;
+ dptr()->handleTextureChange();
+ emit backgroundColorChanged(color);
+ emit dptr()->needUpdate();
+ }
+}
+
+QColor QCustom3DLabel::backgroundColor() const
+{
+ return dptrc()->m_bgrColor;
+}
+
+/*! \property QCustom3DLabel::borderEnabled
+ *
+ * Enable label borders. Defaults to \c{true}.
+ */
+void QCustom3DLabel::setBorderEnabled(bool enabled)
+{
+ if (dptr()->m_borders != enabled) {
+ dptr()->m_borders = enabled;
+ dptr()->m_customVisuals = true;
+ dptr()->handleTextureChange();
+ emit borderEnabledChanged(enabled);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DLabel::isBorderEnabled() const
+{
+ return dptrc()->m_borders;
+}
+
+/*! \property QCustom3DLabel::backgroundEnabled
+ *
+ * Enable label background. If set to \c{false}, backgroundColor() has no effect. Defaults
+ * to \c{true}.
+ */
+void QCustom3DLabel::setBackgroundEnabled(bool enabled)
+{
+ if (dptr()->m_background != enabled) {
+ dptr()->m_background = enabled;
+ dptr()->m_customVisuals = true;
+ dptr()->handleTextureChange();
+ emit backgroundEnabledChanged(enabled);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DLabel::isBackgroundEnabled() const
+{
+ return dptrc()->m_background;
+}
+
+/*! \property QCustom3DLabel::facingCamera
+ *
+ * Forces the label to face camera always. Defaults to \c{false}. If set to \c{true}, rotation()
+ * has no effect.
+ */
+void QCustom3DLabel::setFacingCamera(bool enabled)
+{
+ if (dptr()->m_facingCamera != enabled) {
+ dptr()->m_facingCamera = enabled;
+ dptr()->m_facingCameraDirty = true;
+ emit facingCameraChanged(enabled);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DLabel::isFacingCamera() const
+{
+ return dptrc()->m_facingCamera;
+}
+
+/*!
+ * \internal
+ */
+QCustom3DLabelPrivate *QCustom3DLabel::dptr()
+{
+ return static_cast<QCustom3DLabelPrivate *>(d_ptr.data());
+}
+
+/*!
+ * \internal
+ */
+const QCustom3DLabelPrivate *QCustom3DLabel::dptrc() const
+{
+ return static_cast<const QCustom3DLabelPrivate *>(d_ptr.data());
+}
+
+QCustom3DLabelPrivate::QCustom3DLabelPrivate(QCustom3DLabel *q) :
+ QCustom3DItemPrivate(q),
+ m_font(QFont(QStringLiteral("Arial"), 20)),
+ m_bgrColor(Qt::gray),
+ m_txtColor(Qt::white),
+ m_background(true),
+ m_borders(true),
+ m_facingCamera(false),
+ m_customVisuals(false),
+ m_facingCameraDirty(false)
+{
+ m_isLabelItem = true;
+ m_shadowCasting = false;
+ m_meshFile = QStringLiteral(":/defaultMeshes/plane");
+ createTextureImage();
+}
+
+QCustom3DLabelPrivate::QCustom3DLabelPrivate(QCustom3DLabel *q, const QString &text,
+ const QFont &font, const QVector3D &position,
+ const QVector3D &scaling,
+ const QQuaternion &rotation) :
+ QCustom3DItemPrivate(q, QStringLiteral(":/defaultMeshes/plane"), position, scaling, rotation),
+ m_text(text),
+ m_font(font),
+ m_bgrColor(Qt::gray),
+ m_txtColor(Qt::white),
+ m_background(true),
+ m_borders(true),
+ m_facingCamera(false),
+ m_customVisuals(false),
+ m_facingCameraDirty(false)
+{
+ m_isLabelItem = true;
+ m_shadowCasting = false;
+ createTextureImage();
+}
+
+QCustom3DLabelPrivate::~QCustom3DLabelPrivate()
+{
+}
+
+void QCustom3DLabelPrivate::resetDirtyBits()
+{
+ QCustom3DItemPrivate::resetDirtyBits();
+ m_facingCameraDirty = false;
+}
+
+void QCustom3DLabelPrivate::createTextureImage()
+{
+ createTextureImage(m_bgrColor, m_txtColor, m_background, m_borders);
+}
+
+void QCustom3DLabelPrivate::createTextureImage(const QColor &bgrColor, const QColor &txtColor,
+ bool background, bool borders)
+{
+ m_textureImage = Utils::printTextToImage(m_font, m_text, bgrColor, txtColor, background,
+ borders, 0);
+}
+
+void QCustom3DLabelPrivate::handleTextureChange()
+{
+ createTextureImage();
+ m_dirtyBits.textureDirty = true;
+ if (!m_textureFile.isEmpty()) {
+ m_textureFile.clear();
+ emit q_ptr->textureFileChanged(m_textureFile);
+ }
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qcustom3dlabel.h b/src/datavisualization/data/qcustom3dlabel.h
new file mode 100644
index 00000000..f42ee378
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dlabel.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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 QCUSTOMLABELITEM_H
+#define QCUSTOMLABELITEM_H
+
+#include <QtDataVisualization/qdatavisualizationglobal.h>
+#include <QtDataVisualization/QCustom3DItem>
+#include <QtGui/QVector3D>
+#include <QtGui/QQuaternion>
+#include <QtGui/QFont>
+#include <QtGui/QColor>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QCustom3DLabelPrivate;
+
+class QT_DATAVISUALIZATION_EXPORT QCustom3DLabel : public QCustom3DItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
+ Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
+ Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged)
+ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged)
+ Q_PROPERTY(bool borderEnabled READ isBorderEnabled WRITE setBorderEnabled NOTIFY borderEnabledChanged)
+ Q_PROPERTY(bool backgroundEnabled READ isBackgroundEnabled WRITE setBackgroundEnabled NOTIFY backgroundEnabledChanged)
+ Q_PROPERTY(bool facingCamera READ isFacingCamera WRITE setFacingCamera NOTIFY facingCameraChanged)
+
+public:
+ explicit QCustom3DLabel(QObject *parent = 0);
+ explicit QCustom3DLabel(const QString &text, const QFont &font, const QVector3D &position,
+ const QVector3D &scaling, const QQuaternion &rotation,
+ QObject *parent = 0);
+ virtual ~QCustom3DLabel();
+
+ void setText(const QString &text);
+ QString text() const;
+
+ void setFont(const QFont &font);
+ QFont font() const;
+
+ void setTextColor(const QColor &color);
+ QColor textColor() const;
+
+ void setBackgroundColor(const QColor &color);
+ QColor backgroundColor() const;
+
+ void setBorderEnabled(bool enabled);
+ bool isBorderEnabled() const;
+
+ void setBackgroundEnabled(bool enabled);
+ bool isBackgroundEnabled() const;
+
+ void setFacingCamera(bool enabled);
+ bool isFacingCamera() const;
+
+signals:
+ void textChanged(const QString &text);
+ void fontChanged(const QFont &font);
+ void textColorChanged(const QColor &color);
+ void backgroundColorChanged(const QColor &color);
+ void borderEnabledChanged(bool enabled);
+ void backgroundEnabledChanged(bool enabled);
+ void facingCameraChanged(bool enabled);
+
+protected:
+ QCustom3DLabelPrivate *dptr();
+ const QCustom3DLabelPrivate *dptrc() const;
+
+private:
+ Q_DISABLE_COPY(QCustom3DLabel)
+
+ friend class Abstract3DRenderer;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qcustom3dlabel_p.h b/src/datavisualization/data/qcustom3dlabel_p.h
new file mode 100644
index 00000000..0bb0e017
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dlabel_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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 QCUSTOMLABELITEM_P_H
+#define QCUSTOMLABELITEM_P_H
+
+#include "qcustom3dlabel.h"
+#include "qcustom3ditem_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QCustom3DLabelPrivate : public QCustom3DItemPrivate
+{
+ Q_OBJECT
+
+public:
+ QCustom3DLabelPrivate(QCustom3DLabel *q);
+ QCustom3DLabelPrivate(QCustom3DLabel *q, const QString &text, const QFont &font,
+ const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation);
+ virtual ~QCustom3DLabelPrivate();
+
+ void resetDirtyBits();
+ void createTextureImage();
+ void createTextureImage(const QColor &bgrColor, const QColor &txtColor, bool background,
+ bool borders);
+ void handleTextureChange();
+
+public:
+ QString m_text;
+ QFont m_font;
+ QColor m_bgrColor;
+ QColor m_txtColor;
+ bool m_background;
+ bool m_borders;
+ bool m_facingCamera;
+
+ bool m_customVisuals;
+
+ bool m_facingCameraDirty;
+
+private:
+ friend class QCustom3DLabel;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
index 56fcf5d1..d64046be 100644
--- a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
+++ b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
@@ -28,7 +28,7 @@ const float defaultMaxValue = 10.0f;
* \class QHeightMapSurfaceDataProxy
* \inmodule QtDataVisualization
* \brief Base proxy class for Q3DSurface.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QHeightMapSurfaceDataProxy takes care of surface related height map data handling. It provides a
* way to give a height map to be visualized as a surface plot.
diff --git a/src/datavisualization/data/qitemmodelbardataproxy.cpp b/src/datavisualization/data/qitemmodelbardataproxy.cpp
index 2281e33f..3d4a980a 100644
--- a/src/datavisualization/data/qitemmodelbardataproxy.cpp
+++ b/src/datavisualization/data/qitemmodelbardataproxy.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QItemModelBarDataProxy
* \inmodule QtDataVisualization
* \brief Proxy class for presenting data in item models with Q3DBars.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QItemModelBarDataProxy allows you to use QAbstractItemModel derived models as a data source
* for Q3DBars. It uses the defined mappings to map data from the model to rows, columns, and
@@ -33,6 +33,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* The data is resolved asynchronously whenever mappings or the model changes.
* QBarDataProxy::arrayReset() is emitted when the data has been resolved.
+ * However, when useModelCategories property is set to true, single item changes are resolved
+ * synchronously, unless the same frame also contains a change that causes the whole model to be
+ * resolved.
*
* There are three ways to use mappings:
*
@@ -50,12 +53,22 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* and in which order by defining an explicit list of categories for either or both of rows and
* columns.
*
- * For example, assume that you have a custom QAbstractItemModel for storing various monthly values
- * related to a business.
- * Each item in the model has the roles "year", "month", "income", and "expenses".
- * You could do the following to display the data in a bar graph:
+ * For example, assume that you have a custom QAbstractItemModel for storing various monthly values
+ * related to a business.
+ * Each item in the model has the roles "year", "month", "income", and "expenses".
+ * You could do the following to display the data in a bar graph:
+ *
+ * \snippet doc_src_qtdatavisualization.cpp 3
+ *
+ * If the fields of the model do not contain the data in the exact format you need, you can specify
+ * a search pattern regular expression and a replace rule for each role to get the value in a
+ * format you need. For more information how the replace using regular expressions works, see
+ * QString::replace(const QRegExp &rx, const QString &after) function documentation. Note that
+ * using regular expressions has an impact on the performance, so it's more efficient to utilize
+ * item models where doing search and replace is not necessary to get the desired values.
*
- * \snippet doc_src_qtdatavisualization.cpp 3
+ * For example about using the search patterns in conjunction with the roles, see
+ * \l{Qt Quick 2 Bars Example}.
*
* \sa {Qt Data Visualization Data Handling}
*/
@@ -74,6 +87,8 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* Data is resolved asynchronously whenever the mapping or the model changes.
* QBarDataProxy::arrayReset() is emitted when the data has been resolved.
*
+ * For ItemModelBarDataProxy enums, see \l{QItemModelBarDataProxy::MultiMatchBehavior}.
+ *
* For more details, see QItemModelBarDataProxy documentation.
*
* Usage example:
@@ -90,35 +105,36 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty string ItemModelBarDataProxy::rowRole
- * The row role of the mapping.
+ * Defines the item model role to map into row category.
*/
/*!
* \qmlproperty string ItemModelBarDataProxy::columnRole
- * The column role of the mapping.
+ * Defines the item model role to map into column category.
*/
/*!
* \qmlproperty string ItemModelBarDataProxy::valueRole
- * The value role of the mapping.
+ * Defines the item model role to map into bar value.
*/
/*!
* \qmlproperty string ItemModelBarDataProxy::rotationRole
- *
- * Defines the rotation role for the mapping.
+ * Defines the item model role to map into bar rotation angle.
*/
/*!
* \qmlproperty list<String> ItemModelBarDataProxy::rowCategories
- * The row categories of the mapping. Only items with row roles that are found in this list are
- * included when the data is resolved. The rows are ordered in the same order as they are in this list.
+ * The row categories of the mapping. Only items with row role values that are found in this list
+ * are included when the data is resolved. The rows are ordered in the same order as they are in
+ * this list.
*/
/*!
* \qmlproperty list<String> ItemModelBarDataProxy::columnCategories
- * The column categories of the mapping. Only items with column roles that are found in this list are
- * included when the data is resolved. The columns are ordered in the same order as they are in this list.
+ * The column categories of the mapping. Only items with column role values that are found in this
+ * list are included when the data is resolved. The columns are ordered in the same order as they
+ * are in this list.
*/
/*!
@@ -143,6 +159,117 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty regExp ItemModelBarDataProxy::rowRolePattern
+ * When set, a search and replace is done on the value mapped by row role before it is used as
+ * a row category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and rowRoleReplace property contains the replacement string.
+ * This is useful for example in parsing row and column categories from a single
+ * timestamp field in the item model.
+ *
+ * \sa rowRole, rowRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelBarDataProxy::columnRolePattern
+ * When set, a search and replace is done on the value mapped by column role before it is used
+ * as a column category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and columnRoleReplace property contains the replacement string.
+ * This is useful for example in parsing row and column categories from
+ * a single timestamp field in the item model.
+ *
+ * \sa columnRole, columnRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelBarDataProxy::valueRolePattern
+ * When set, a search and replace is done on the value mapped by value role before it is used as
+ * a bar value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and valueRoleReplace property contains the replacement string.
+ *
+ * \sa valueRole, valueRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelBarDataProxy::rotationRolePattern
+ * When set, a search and replace is done on the value mapped by rotation role before it is used
+ * as a bar rotation angle. This property specifies the regular expression to find the portion
+ * of the mapped value to replace and rotationRoleReplace property contains the replacement string.
+ *
+ * \sa rotationRole, rotationRoleReplace
+ */
+
+/*!
+ * \qmlproperty string ItemModelBarDataProxy::rowRoleReplace
+ * This property defines the replace content to be used in conjunction with rowRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rowRole, rowRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelBarDataProxy::columnRoleReplace
+ * This property defines the replace content to be used in conjunction with columnRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa columnRole, columnRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelBarDataProxy::valueRoleReplace
+ * This property defines the replace content to be used in conjunction with valueRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa valueRole, valueRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelBarDataProxy::rotationRoleReplace
+ * This property defines the replace content to be used in conjunction with rotationRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rotationRole, rotationRolePattern
+ */
+
+/*!
+ * \qmlproperty ItemModelBarDataProxy.MultiMatchBehavior ItemModelBarDataProxy::multiMatchBehavior
+ * This property defines how multiple matches for each row/column combination are handled.
+ * Defaults to ItemModelBarDataProxy.MMBLast. The chosen behavior affects both bar value
+ * and rotation.
+ *
+ * For example, you might have an item model with timestamped data taken at irregular intervals
+ * and you want to visualize total value of data items on each day with a bar graph.
+ * This can be done by specifying row and column categories so that each bar represents a day,
+ * and setting multiMatchBehavior to ItemModelBarDataProxy.MMBCumulative.
+ */
+
+/*!
+ * \enum QItemModelBarDataProxy::MultiMatchBehavior
+ *
+ * Behavior types for QItemModelBarDataProxy::multiMatchBehavior property.
+ *
+ * \value MMBFirst
+ * The value is taken from the first item in the item model that matches
+ * each row/column combination.
+ * \value MMBLast
+ * The value is taken from the last item in the item model that matches
+ * each row/column combination.
+ * \value MMBAverage
+ * The values from all items matching each row/column combination are
+ * averaged together and the average is used as the bar value.
+ * \value MMBCumulative
+ * The values from all items matching each row/column combination are
+ * added together and the total is used as the bar value.
+ */
+
+/*!
* Constructs QItemModelBarDataProxy with optional \a parent.
*/
QItemModelBarDataProxy::QItemModelBarDataProxy(QObject *parent)
@@ -155,7 +282,7 @@ QItemModelBarDataProxy::QItemModelBarDataProxy(QObject *parent)
* Constructs QItemModelBarDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
* ownership of the \a itemModel, as typically item models are owned by other controls.
*/
-QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemModel, QObject *parent)
+QItemModelBarDataProxy::QItemModelBarDataProxy(QAbstractItemModel *itemModel, QObject *parent)
: QBarDataProxy(new QItemModelBarDataProxyPrivate(this), parent)
{
setItemModel(itemModel);
@@ -169,7 +296,7 @@ QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemMod
* This constructor is meant to be used with models that have data properly sorted
* in rows and columns already, so it also sets useModelCategories property to true.
*/
-QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemModel,
+QItemModelBarDataProxy::QItemModelBarDataProxy(QAbstractItemModel *itemModel,
const QString &valueRole, QObject *parent)
: QBarDataProxy(new QItemModelBarDataProxyPrivate(this), parent)
{
@@ -184,7 +311,7 @@ QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemMod
* ownership of the \a itemModel, as typically item models are owned by other controls.
* The role mappings are set with \a rowRole, \a columnRole, and \a valueRole.
*/
-QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemModel,
+QItemModelBarDataProxy::QItemModelBarDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &valueRole, QObject *parent)
@@ -202,7 +329,7 @@ QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemMod
* ownership of the \a itemModel, as typically item models are owned by other controls.
* The role mappings are set with \a rowRole, \a columnRole, \a valueRole, and \a rotationRole.
*/
-QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemModel,
+QItemModelBarDataProxy::QItemModelBarDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &valueRole,
@@ -225,7 +352,7 @@ QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemMod
* Row and column categories are set with \a rowCategories and \a columnCategories.
* This constructor also sets autoRowCategories and autoColumnCategories to false.
*/
-QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemModel,
+QItemModelBarDataProxy::QItemModelBarDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &valueRole,
@@ -252,7 +379,7 @@ QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemMod
* Row and column categories are set with \a rowCategories and \a columnCategories.
* This constructor also sets autoRowCategories and autoColumnCategories to false.
*/
-QItemModelBarDataProxy::QItemModelBarDataProxy(const QAbstractItemModel *itemModel,
+QItemModelBarDataProxy::QItemModelBarDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &valueRole,
@@ -287,12 +414,12 @@ QItemModelBarDataProxy::~QItemModelBarDataProxy()
* Defines item model. Does not take ownership of the model, but does connect to it to listen for
* changes.
*/
-void QItemModelBarDataProxy::setItemModel(const QAbstractItemModel *itemModel)
+void QItemModelBarDataProxy::setItemModel(QAbstractItemModel *itemModel)
{
dptr()->m_itemModelHandler->setItemModel(itemModel);
}
-const QAbstractItemModel *QItemModelBarDataProxy::itemModel() const
+QAbstractItemModel *QItemModelBarDataProxy::itemModel() const
{
return dptrc()->m_itemModelHandler->itemModel();
}
@@ -506,6 +633,215 @@ int QItemModelBarDataProxy::columnCategoryIndex(const QString &category)
}
/*!
+ * \property QItemModelBarDataProxy::rowRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by row role before it is used as
+ * a row category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and rowRoleReplace property contains the replacement string.
+ * This is useful for example in parsing row and column categories from a single
+ * timestamp field in the item model.
+ *
+ * \sa rowRole, rowRoleReplace
+ */
+void QItemModelBarDataProxy::setRowRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_rowRolePattern != pattern) {
+ dptr()->m_rowRolePattern = pattern;
+ emit rowRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelBarDataProxy::rowRolePattern() const
+{
+ return dptrc()->m_rowRolePattern;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::columnRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by column role before it is used
+ * as a column category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and columnRoleReplace property contains the replacement string.
+ * This is useful for example in parsing row and column categories from
+ * a single timestamp field in the item model.
+ *
+ * \sa columnRole, columnRoleReplace
+ */
+void QItemModelBarDataProxy::setColumnRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_columnRolePattern != pattern) {
+ dptr()->m_columnRolePattern = pattern;
+ emit columnRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelBarDataProxy::columnRolePattern() const
+{
+ return dptrc()->m_columnRolePattern;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::valueRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by value role before it is used as
+ * a bar value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and valueRoleReplace property contains the replacement string.
+ *
+ * \sa valueRole, valueRoleReplace
+ */
+void QItemModelBarDataProxy::setValueRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_valueRolePattern != pattern) {
+ dptr()->m_valueRolePattern = pattern;
+ emit valueRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelBarDataProxy::valueRolePattern() const
+{
+ return dptrc()->m_valueRolePattern;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::rotationRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by rotation role before it is used
+ * as a bar rotation angle. This property specifies the regular expression to find the portion
+ * of the mapped value to replace and rotationRoleReplace property contains the replacement string.
+ *
+ * \sa rotationRole, rotationRoleReplace
+ */
+void QItemModelBarDataProxy::setRotationRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_rotationRolePattern != pattern) {
+ dptr()->m_rotationRolePattern = pattern;
+ emit rotationRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelBarDataProxy::rotationRolePattern() const
+{
+ return dptrc()->m_rotationRolePattern;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::rowRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with rowRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rowRole, rowRolePattern
+ */
+void QItemModelBarDataProxy::setRowRoleReplace(const QString &replace)
+{
+ if (dptr()->m_rowRoleReplace != replace) {
+ dptr()->m_rowRoleReplace = replace;
+ emit rowRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelBarDataProxy::rowRoleReplace() const
+{
+ return dptrc()->m_rowRoleReplace;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::columnRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with columnRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa columnRole, columnRolePattern
+ */
+void QItemModelBarDataProxy::setColumnRoleReplace(const QString &replace)
+{
+ if (dptr()->m_columnRoleReplace != replace) {
+ dptr()->m_columnRoleReplace = replace;
+ emit columnRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelBarDataProxy::columnRoleReplace() const
+{
+ return dptrc()->m_columnRoleReplace;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::valueRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with valueRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa valueRole, valueRolePattern
+ */
+void QItemModelBarDataProxy::setValueRoleReplace(const QString &replace)
+{
+ if (dptr()->m_valueRoleReplace != replace) {
+ dptr()->m_valueRoleReplace = replace;
+ emit valueRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelBarDataProxy::valueRoleReplace() const
+{
+ return dptrc()->m_valueRoleReplace;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::rotationRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with rotationRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rotationRole, rotationRolePattern
+ */
+void QItemModelBarDataProxy::setRotationRoleReplace(const QString &replace)
+{
+ if (dptr()->m_rotationRoleReplace != replace) {
+ dptr()->m_rotationRoleReplace = replace;
+ emit rotationRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelBarDataProxy::rotationRoleReplace() const
+{
+ return dptrc()->m_rotationRoleReplace;
+}
+
+/*!
+ * \property QItemModelBarDataProxy::multiMatchBehavior
+ *
+ * This property defines how multiple matches for each row/column combination are handled.
+ * Defaults to QItemModelBarDataProxy::MMBLast. The chosen behavior affects both bar value
+ * and rotation.
+ *
+ * For example, you might have an item model with timestamped data taken at irregular intervals
+ * and you want to visualize total value of data items on each day with a bar graph.
+ * This can be done by specifying row and column categories so that each bar represents a day,
+ * and setting multiMatchBehavior to QItemModelBarDataProxy::MMBCumulative.
+ */
+void QItemModelBarDataProxy::setMultiMatchBehavior(QItemModelBarDataProxy::MultiMatchBehavior behavior)
+{
+ if (dptr()->m_multiMatchBehavior != behavior) {
+ dptr()->m_multiMatchBehavior = behavior;
+ emit multiMatchBehaviorChanged(behavior);
+ }
+}
+
+QItemModelBarDataProxy::MultiMatchBehavior QItemModelBarDataProxy::multiMatchBehavior() const
+{
+ return dptrc()->m_multiMatchBehavior;
+}
+
+/*!
* \internal
*/
QItemModelBarDataProxyPrivate *QItemModelBarDataProxy::dptr()
@@ -528,7 +864,8 @@ QItemModelBarDataProxyPrivate::QItemModelBarDataProxyPrivate(QItemModelBarDataPr
m_itemModelHandler(new BarItemModelHandler(q)),
m_useModelCategories(false),
m_autoRowCategories(true),
- m_autoColumnCategories(true)
+ m_autoColumnCategories(true),
+ m_multiMatchBehavior(QItemModelBarDataProxy::MMBLast)
{
}
@@ -564,6 +901,24 @@ void QItemModelBarDataProxyPrivate::connectItemModelHandler()
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
QObject::connect(qptr(), &QItemModelBarDataProxy::autoColumnCategoriesChanged,
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::rowRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::columnRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::valueRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::rotationRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::rowRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::columnRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::valueRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::rotationRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::multiMatchBehaviorChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qitemmodelbardataproxy.h b/src/datavisualization/data/qitemmodelbardataproxy.h
index f19b4445..d7394dcf 100644
--- a/src/datavisualization/data/qitemmodelbardataproxy.h
+++ b/src/datavisualization/data/qitemmodelbardataproxy.h
@@ -21,6 +21,7 @@
#include <QtDataVisualization/qbardataproxy.h>
#include <QtCore/QAbstractItemModel>
+#include <QtCore/QRegExp>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -29,7 +30,8 @@ class QItemModelBarDataProxyPrivate;
class QT_DATAVISUALIZATION_EXPORT QItemModelBarDataProxy : public QBarDataProxy
{
Q_OBJECT
- Q_PROPERTY(const QAbstractItemModel* itemModel READ itemModel WRITE setItemModel NOTIFY itemModelChanged)
+ Q_ENUMS(MultiMatchBehavior)
+ Q_PROPERTY(QAbstractItemModel* itemModel READ itemModel WRITE setItemModel NOTIFY itemModelChanged)
Q_PROPERTY(QString rowRole READ rowRole WRITE setRowRole NOTIFY rowRoleChanged)
Q_PROPERTY(QString columnRole READ columnRole WRITE setColumnRole NOTIFY columnRoleChanged)
Q_PROPERTY(QString valueRole READ valueRole WRITE setValueRole NOTIFY valueRoleChanged)
@@ -39,30 +41,46 @@ class QT_DATAVISUALIZATION_EXPORT QItemModelBarDataProxy : public QBarDataProxy
Q_PROPERTY(bool useModelCategories READ useModelCategories WRITE setUseModelCategories NOTIFY useModelCategoriesChanged)
Q_PROPERTY(bool autoRowCategories READ autoRowCategories WRITE setAutoRowCategories NOTIFY autoRowCategoriesChanged)
Q_PROPERTY(bool autoColumnCategories READ autoColumnCategories WRITE setAutoColumnCategories NOTIFY autoColumnCategoriesChanged)
+ Q_PROPERTY(QRegExp rowRolePattern READ rowRolePattern WRITE setRowRolePattern NOTIFY rowRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp columnRolePattern READ columnRolePattern WRITE setColumnRolePattern NOTIFY columnRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp valueRolePattern READ valueRolePattern WRITE setValueRolePattern NOTIFY valueRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp rotationRolePattern READ rotationRolePattern WRITE setRotationRolePattern NOTIFY rotationRolePatternChanged REVISION 1)
+ Q_PROPERTY(QString rowRoleReplace READ rowRoleReplace WRITE setRowRoleReplace NOTIFY rowRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString columnRoleReplace READ columnRoleReplace WRITE setColumnRoleReplace NOTIFY columnRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString valueRoleReplace READ valueRoleReplace WRITE setValueRoleReplace NOTIFY valueRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString rotationRoleReplace READ rotationRoleReplace WRITE setRotationRoleReplace NOTIFY rotationRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(MultiMatchBehavior multiMatchBehavior READ multiMatchBehavior WRITE setMultiMatchBehavior NOTIFY multiMatchBehaviorChanged REVISION 1)
public:
+ enum MultiMatchBehavior {
+ MMBFirst = 0,
+ MMBLast = 1,
+ MMBAverage = 2,
+ MMBCumulative = 3
+ };
+
explicit QItemModelBarDataProxy(QObject *parent = 0);
- QItemModelBarDataProxy(const QAbstractItemModel *itemModel, QObject *parent = 0);
- QItemModelBarDataProxy(const QAbstractItemModel *itemModel, const QString &valueRole,
+ QItemModelBarDataProxy(QAbstractItemModel *itemModel, QObject *parent = 0);
+ QItemModelBarDataProxy(QAbstractItemModel *itemModel, const QString &valueRole,
QObject *parent = 0);
- QItemModelBarDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelBarDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &valueRole,
QObject *parent = 0);
- QItemModelBarDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelBarDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &valueRole,
const QString &rotationRole, QObject *parent = 0);
- QItemModelBarDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelBarDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &valueRole,
const QStringList &rowCategories, const QStringList &columnCategories,
QObject *parent = 0);
- QItemModelBarDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelBarDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &valueRole,
const QString &rotationRole, const QStringList &rowCategories,
const QStringList &columnCategories, QObject *parent = 0);
virtual ~QItemModelBarDataProxy();
- void setItemModel(const QAbstractItemModel *itemModel);
- const QAbstractItemModel *itemModel() const;
+ void setItemModel(QAbstractItemModel *itemModel);
+ QAbstractItemModel *itemModel() const;
void setRowRole(const QString &role);
QString rowRole() const;
@@ -93,6 +111,27 @@ public:
Q_INVOKABLE int rowCategoryIndex(const QString& category);
Q_INVOKABLE int columnCategoryIndex(const QString& category);
+ void setRowRolePattern(const QRegExp &pattern);
+ QRegExp rowRolePattern() const;
+ void setColumnRolePattern(const QRegExp &pattern);
+ QRegExp columnRolePattern() const;
+ void setValueRolePattern(const QRegExp &pattern);
+ QRegExp valueRolePattern() const;
+ void setRotationRolePattern(const QRegExp &pattern);
+ QRegExp rotationRolePattern() const;
+
+ void setRowRoleReplace(const QString &replace);
+ QString rowRoleReplace() const;
+ void setColumnRoleReplace(const QString &replace);
+ QString columnRoleReplace() const;
+ void setValueRoleReplace(const QString &replace);
+ QString valueRoleReplace() const;
+ void setRotationRoleReplace(const QString &replace);
+ QString rotationRoleReplace() const;
+
+ void setMultiMatchBehavior(MultiMatchBehavior behavior);
+ MultiMatchBehavior multiMatchBehavior() const;
+
signals:
void itemModelChanged(const QAbstractItemModel* itemModel);
void rowRoleChanged(const QString &role);
@@ -104,6 +143,15 @@ signals:
void useModelCategoriesChanged(bool enable);
void autoRowCategoriesChanged(bool enable);
void autoColumnCategoriesChanged(bool enable);
+ Q_REVISION(1) void rowRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void columnRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void valueRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void rotationRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void rowRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void columnRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void valueRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void rotationRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void multiMatchBehaviorChanged(MultiMatchBehavior behavior);
protected:
QItemModelBarDataProxyPrivate *dptr();
diff --git a/src/datavisualization/data/qitemmodelbardataproxy_p.h b/src/datavisualization/data/qitemmodelbardataproxy_p.h
index 2fd74bb2..84564d02 100644
--- a/src/datavisualization/data/qitemmodelbardataproxy_p.h
+++ b/src/datavisualization/data/qitemmodelbardataproxy_p.h
@@ -63,6 +63,18 @@ private:
bool m_autoRowCategories;
bool m_autoColumnCategories;
+ QRegExp m_rowRolePattern;
+ QRegExp m_columnRolePattern;
+ QRegExp m_valueRolePattern;
+ QRegExp m_rotationRolePattern;
+
+ QString m_rowRoleReplace;
+ QString m_columnRoleReplace;
+ QString m_valueRoleReplace;
+ QString m_rotationRoleReplace;
+
+ QItemModelBarDataProxy::MultiMatchBehavior m_multiMatchBehavior;
+
friend class BarItemModelHandler;
friend class QItemModelBarDataProxy;
};
diff --git a/src/datavisualization/data/qitemmodelscatterdataproxy.cpp b/src/datavisualization/data/qitemmodelscatterdataproxy.cpp
index 16a7b8b5..4e6464ea 100644
--- a/src/datavisualization/data/qitemmodelscatterdataproxy.cpp
+++ b/src/datavisualization/data/qitemmodelscatterdataproxy.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QItemModelScatterDataProxy
* \inmodule QtDataVisualization
* \brief Proxy class for presenting data in item models with Q3DScatter.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QItemModelScatterDataProxy allows you to use QAbstractItemModel derived models as a data source
* for Q3DScatter. It maps roles of QAbstractItemModel to the XYZ-values of Q3DScatter points.
@@ -36,7 +36,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* unless the same frame also contains a change that causes the whole model to be resolved.
*
* Mapping ignores rows and columns of the QAbstractItemModel and treats
- * all items equally. It requires the model to provide at least three roles for the data items
+ * all items equally. It requires the model to provide roles for the data items
* that can be mapped to X, Y, and Z-values for the scatter points.
*
* For example, assume that you have a custom QAbstractItemModel for storing various measurements
@@ -45,6 +45,16 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* \snippet doc_src_qtdatavisualization.cpp 4
*
+ * If the fields of the model do not contain the data in the exact format you need, you can specify
+ * a search pattern regular expression and a replace rule for each role to get the value in a
+ * format you need. For more information how the replace using regular expressions works, see
+ * QString::replace(const QRegExp &rx, const QString &after) function documentation. Note that
+ * using regular expressions has an impact on the performance, so it's more efficient to utilize
+ * item models where doing search and replace is not necessary to get the desired values.
+ *
+ * For example about using the search patterns in conjunction with the roles, see
+ * ItemModelBarDataProxy usage in \l{Qt Quick 2 Bars Example}.
+ *
* \sa {Qt Data Visualization Data Handling}
*/
@@ -78,27 +88,109 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty string ItemModelScatterDataProxy::xPosRole
- * The X position role of the mapping.
+ * Defines the item model role to map into X position.
*/
/*!
* \qmlproperty string ItemModelScatterDataProxy::yPosRole
- * The Y position role of the mapping.
+ * Defines the item model role to map into Y position.
*/
/*!
* \qmlproperty string ItemModelScatterDataProxy::zPosRole
- * The Z position role of the mapping.
+ * Defines the item model role to map into Z position.
*/
/*!
* \qmlproperty string ItemModelScatterDataProxy::rotationRole
*
- * Defines the rotation role for the mapping.
+ * Defines the item model role to map into item rotation.
* The model may supply the value for rotation as either variant that is directly convertible
- * to QQuaternion, or as one of the string representations: \c{"scalar,x,y,z"} or \c{"@angle,x,y,z"}. The first
- * will construct the quaternion directly with given values, and the second one will construct
- * the quaternion using QQuaternion::fromAxisAndAngle() method.
+ * to QQuaternion, or as one of the string representations: \c{"scalar,x,y,z"} or
+ * \c{"@angle,x,y,z"}. The first format will construct the quaternion directly with given values,
+ * and the second one will construct the quaternion using QQuaternion::fromAxisAndAngle() method.
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelScatterDataProxy::xPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by xPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and xPosRoleReplace property contains the replacement string.
+ *
+ * \sa xPosRole, xPosRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelScatterDataProxy::yPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by yPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and yPosRoleReplace property contains the replacement string.
+ *
+ * \sa yPosRole, yPosRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelScatterDataProxy::zPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by zPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and zPosRoleReplace property contains the replacement string.
+ *
+ * \sa zPosRole, zPosRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelScatterDataProxy::rotationRolePattern
+ * When set, a search and replace is done on the value mapped by rotation role before it is used
+ * as a item rotation. This property specifies the regular expression to find the portion
+ * of the mapped value to replace and rotationRoleReplace property contains the replacement string.
+ *
+ * \sa rotationRole, rotationRoleReplace
+ */
+
+/*!
+ * \qmlproperty string ItemModelScatterDataProxy::xPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with xPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa xPosRole, xPosRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelScatterDataProxy::yPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with yPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa yPosRole, yPosRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelScatterDataProxy::zPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with zPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa zPosRole, zPosRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelScatterDataProxy::rotationRoleReplace
+ * This property defines the replace content to be used in conjunction with rotationRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rotationRole, rotationRolePattern
*/
/*!
@@ -114,7 +206,7 @@ QItemModelScatterDataProxy::QItemModelScatterDataProxy(QObject *parent)
* Constructs QItemModelScatterDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
* ownership of the \a itemModel, as typically item models are owned by other controls.
*/
-QItemModelScatterDataProxy::QItemModelScatterDataProxy(const QAbstractItemModel *itemModel,
+QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
QObject *parent)
: QScatterDataProxy(new QItemModelScatterDataProxyPrivate(this), parent)
{
@@ -128,7 +220,7 @@ QItemModelScatterDataProxy::QItemModelScatterDataProxy(const QAbstractItemModel
* The xPosRole property is set to \a xPosRole, yPosRole property to \a yPosRole, and zPosRole property
* to \a zPosRole.
*/
-QItemModelScatterDataProxy::QItemModelScatterDataProxy(const QAbstractItemModel *itemModel,
+QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
const QString &xPosRole,
const QString &yPosRole,
const QString &zPosRole,
@@ -148,7 +240,7 @@ QItemModelScatterDataProxy::QItemModelScatterDataProxy(const QAbstractItemModel
* The xPosRole property is set to \a xPosRole, yPosRole property to \a yPosRole, zPosRole property
* to \a zPosRole, and rotationRole property to \a rotationRole.
*/
-QItemModelScatterDataProxy::QItemModelScatterDataProxy(const QAbstractItemModel *itemModel,
+QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
const QString &xPosRole,
const QString &yPosRole,
const QString &zPosRole,
@@ -177,12 +269,12 @@ QItemModelScatterDataProxy::~QItemModelScatterDataProxy()
* Defines the item model. Does not take ownership of the model, but does connect to it to listen for
* changes.
*/
-void QItemModelScatterDataProxy::setItemModel(const QAbstractItemModel *itemModel)
+void QItemModelScatterDataProxy::setItemModel(QAbstractItemModel *itemModel)
{
dptr()->m_itemModelHandler->setItemModel(itemModel);
}
-const QAbstractItemModel *QItemModelScatterDataProxy::itemModel() const
+QAbstractItemModel *QItemModelScatterDataProxy::itemModel() const
{
return dptrc()->m_itemModelHandler->itemModel();
}
@@ -190,7 +282,7 @@ const QAbstractItemModel *QItemModelScatterDataProxy::itemModel() const
/*!
* \property QItemModelScatterDataProxy::xPosRole
*
- * Defines the X position \a role for the mapping.
+ * Defines the item model role to map into X position.
*/
void QItemModelScatterDataProxy::setXPosRole(const QString &role)
{
@@ -208,7 +300,7 @@ QString QItemModelScatterDataProxy::xPosRole() const
/*!
* \property QItemModelScatterDataProxy::yPosRole
*
- * Defines the Y position \a role for the mapping.
+ * Defines the item model role to map into Y position.
*/
void QItemModelScatterDataProxy::setYPosRole(const QString &role)
{
@@ -226,7 +318,7 @@ QString QItemModelScatterDataProxy::yPosRole() const
/*!
* \property QItemModelScatterDataProxy::zPosRole
*
- * Defines the Z position \a role for the mapping.
+ * Defines the item model role to map into Z position.
*/
void QItemModelScatterDataProxy::setZPosRole(const QString &role)
{
@@ -244,7 +336,7 @@ QString QItemModelScatterDataProxy::zPosRole() const
/*!
* \property QItemModelScatterDataProxy::rotationRole
*
- * Defines the rotation \a role for the mapping.
+ * Defines the item model role to map into item rotation.
*
* The model may supply the value for rotation as either variant that is directly convertible
* to QQuaternion, or as one of the string representations: \c{"scalar,x,y,z"} or \c{"@angle,x,y,z"}.
@@ -265,6 +357,186 @@ QString QItemModelScatterDataProxy::rotationRole() const
}
/*!
+ * \property QItemModelScatterDataProxy::xPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by xPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and xPosRoleReplace property contains the replacement string.
+ *
+ * \sa xPosRole, xPosRoleReplace
+ */
+void QItemModelScatterDataProxy::setXPosRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_xPosRolePattern != pattern) {
+ dptr()->m_xPosRolePattern = pattern;
+ emit xPosRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelScatterDataProxy::xPosRolePattern() const
+{
+ return dptrc()->m_xPosRolePattern;
+}
+
+/*!
+ * \property QItemModelScatterDataProxy::yPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by yPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and yPosRoleReplace property contains the replacement string.
+ *
+ * \sa yPosRole, yPosRoleReplace
+ */
+void QItemModelScatterDataProxy::setYPosRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_yPosRolePattern != pattern) {
+ dptr()->m_yPosRolePattern = pattern;
+ emit yPosRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelScatterDataProxy::yPosRolePattern() const
+{
+ return dptrc()->m_yPosRolePattern;
+}
+
+/*!
+ * \property QItemModelScatterDataProxy::zPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by zPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and zPosRoleReplace property contains the replacement string.
+ *
+ * \sa zPosRole, zPosRoleReplace
+ */
+void QItemModelScatterDataProxy::setZPosRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_zPosRolePattern != pattern) {
+ dptr()->m_zPosRolePattern = pattern;
+ emit zPosRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelScatterDataProxy::zPosRolePattern() const
+{
+ return dptrc()->m_zPosRolePattern;
+}
+
+/*!
+ * \property QItemModelScatterDataProxy::rotationRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by rotation role before it is used
+ * as a item rotation. This property specifies the regular expression to find the portion
+ * of the mapped value to replace and rotationRoleReplace property contains the replacement string.
+ *
+ * \sa rotationRole, rotationRoleReplace
+ */
+void QItemModelScatterDataProxy::setRotationRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_rotationRolePattern != pattern) {
+ dptr()->m_rotationRolePattern = pattern;
+ emit rotationRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelScatterDataProxy::rotationRolePattern() const
+{
+ return dptrc()->m_rotationRolePattern;
+}
+
+/*!
+ * \property QItemModelScatterDataProxy::xPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with xPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa xPosRole, xPosRolePattern
+ */
+void QItemModelScatterDataProxy::setXPosRoleReplace(const QString &replace)
+{
+ if (dptr()->m_xPosRoleReplace != replace) {
+ dptr()->m_xPosRoleReplace = replace;
+ emit xPosRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelScatterDataProxy::xPosRoleReplace() const
+{
+ return dptrc()->m_xPosRoleReplace;
+}
+
+/*!
+ * \property QItemModelScatterDataProxy::yPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with yPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa yPosRole, yPosRolePattern
+ */
+void QItemModelScatterDataProxy::setYPosRoleReplace(const QString &replace)
+{
+ if (dptr()->m_yPosRoleReplace != replace) {
+ dptr()->m_yPosRoleReplace = replace;
+ emit yPosRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelScatterDataProxy::yPosRoleReplace() const
+{
+ return dptrc()->m_yPosRoleReplace;
+}
+
+/*!
+ * \property QItemModelScatterDataProxy::zPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with zPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa zPosRole, zPosRolePattern
+ */
+void QItemModelScatterDataProxy::setZPosRoleReplace(const QString &replace)
+{
+ if (dptr()->m_zPosRoleReplace != replace) {
+ dptr()->m_zPosRoleReplace = replace;
+ emit zPosRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelScatterDataProxy::zPosRoleReplace() const
+{
+ return dptrc()->m_zPosRoleReplace;
+}
+
+/*!
+ * \property QItemModelScatterDataProxy::rotationRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with rotationRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rotationRole, rotationRolePattern
+ */
+void QItemModelScatterDataProxy::setRotationRoleReplace(const QString &replace)
+{
+ if (dptr()->m_rotationRoleReplace != replace) {
+ dptr()->m_rotationRoleReplace = replace;
+ emit rotationRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelScatterDataProxy::rotationRoleReplace() const
+{
+ return dptrc()->m_rotationRoleReplace;
+}
+
+/*!
* Changes \a xPosRole, \a yPosRole, \a zPosRole, and \a rotationRole mapping.
*/
void QItemModelScatterDataProxy::remap(const QString &xPosRole, const QString &yPosRole,
@@ -320,6 +592,24 @@ void QItemModelScatterDataProxyPrivate::connectItemModelHandler()
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
QObject::connect(qptr(), &QItemModelScatterDataProxy::zPosRoleChanged,
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::rotationRoleChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::xPosRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::yPosRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::zPosRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::rotationRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::xPosRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::yPosRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::zPosRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelScatterDataProxy::rotationRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qitemmodelscatterdataproxy.h b/src/datavisualization/data/qitemmodelscatterdataproxy.h
index c6d2245d..3215c688 100644
--- a/src/datavisualization/data/qitemmodelscatterdataproxy.h
+++ b/src/datavisualization/data/qitemmodelscatterdataproxy.h
@@ -22,6 +22,7 @@
#include <QtDataVisualization/qscatterdataproxy.h>
#include <QtCore/QAbstractItemModel>
#include <QtCore/QString>
+#include <QtCore/QRegExp>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -30,26 +31,34 @@ class QItemModelScatterDataProxyPrivate;
class QT_DATAVISUALIZATION_EXPORT QItemModelScatterDataProxy : public QScatterDataProxy
{
Q_OBJECT
- Q_PROPERTY(const QAbstractItemModel* itemModel READ itemModel WRITE setItemModel NOTIFY itemModelChanged)
+ Q_PROPERTY(QAbstractItemModel* itemModel READ itemModel WRITE setItemModel NOTIFY itemModelChanged)
Q_PROPERTY(QString xPosRole READ xPosRole WRITE setXPosRole NOTIFY xPosRoleChanged)
Q_PROPERTY(QString yPosRole READ yPosRole WRITE setYPosRole NOTIFY yPosRoleChanged)
Q_PROPERTY(QString zPosRole READ zPosRole WRITE setZPosRole NOTIFY zPosRoleChanged)
Q_PROPERTY(QString rotationRole READ rotationRole WRITE setRotationRole NOTIFY rotationRoleChanged)
+ Q_PROPERTY(QRegExp xPosRolePattern READ xPosRolePattern WRITE setXPosRolePattern NOTIFY xPosRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp yPosRolePattern READ yPosRolePattern WRITE setYPosRolePattern NOTIFY yPosRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp zPosRolePattern READ zPosRolePattern WRITE setZPosRolePattern NOTIFY zPosRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp rotationRolePattern READ rotationRolePattern WRITE setRotationRolePattern NOTIFY rotationRolePatternChanged REVISION 1)
+ Q_PROPERTY(QString xPosRoleReplace READ xPosRoleReplace WRITE setXPosRoleReplace NOTIFY xPosRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString yPosRoleReplace READ yPosRoleReplace WRITE setYPosRoleReplace NOTIFY yPosRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString zPosRoleReplace READ zPosRoleReplace WRITE setZPosRoleReplace NOTIFY zPosRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString rotationRoleReplace READ rotationRoleReplace WRITE setRotationRoleReplace NOTIFY rotationRoleReplaceChanged REVISION 1)
public:
explicit QItemModelScatterDataProxy(QObject *parent = 0);
- QItemModelScatterDataProxy(const QAbstractItemModel *itemModel, QObject *parent = 0);
- QItemModelScatterDataProxy(const QAbstractItemModel *itemModel,
+ QItemModelScatterDataProxy(QAbstractItemModel *itemModel, QObject *parent = 0);
+ QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
const QString &xPosRole, const QString &yPosRole,
const QString &zPosRole, QObject *parent = 0);
- QItemModelScatterDataProxy(const QAbstractItemModel *itemModel,
+ QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
const QString &xPosRole, const QString &yPosRole,
const QString &zPosRole, const QString &rotationRole,
QObject *parent = 0);
virtual ~QItemModelScatterDataProxy();
- void setItemModel(const QAbstractItemModel *itemModel);
- const QAbstractItemModel *itemModel() const;
+ void setItemModel(QAbstractItemModel *itemModel);
+ QAbstractItemModel *itemModel() const;
void setXPosRole(const QString &role);
QString xPosRole() const;
@@ -63,12 +72,38 @@ public:
void remap(const QString &xPosRole, const QString &yPosRole, const QString &zPosRole,
const QString &rotationRole);
+ void setXPosRolePattern(const QRegExp &pattern);
+ QRegExp xPosRolePattern() const;
+ void setYPosRolePattern(const QRegExp &pattern);
+ QRegExp yPosRolePattern() const;
+ void setZPosRolePattern(const QRegExp &pattern);
+ QRegExp zPosRolePattern() const;
+ void setRotationRolePattern(const QRegExp &pattern);
+ QRegExp rotationRolePattern() const;
+
+ void setXPosRoleReplace(const QString &replace);
+ QString xPosRoleReplace() const;
+ void setYPosRoleReplace(const QString &replace);
+ QString yPosRoleReplace() const;
+ void setZPosRoleReplace(const QString &replace);
+ QString zPosRoleReplace() const;
+ void setRotationRoleReplace(const QString &replace);
+ QString rotationRoleReplace() const;
+
signals:
void itemModelChanged(const QAbstractItemModel* itemModel);
void xPosRoleChanged(const QString &role);
void yPosRoleChanged(const QString &role);
void zPosRoleChanged(const QString &role);
void rotationRoleChanged(const QString &role);
+ Q_REVISION(1) void xPosRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void yPosRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void zPosRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void rotationRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void rotationRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void xPosRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void yPosRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void zPosRoleReplaceChanged(const QString &replace);
protected:
QItemModelScatterDataProxyPrivate *dptr();
diff --git a/src/datavisualization/data/qitemmodelscatterdataproxy_p.h b/src/datavisualization/data/qitemmodelscatterdataproxy_p.h
index 4e1f321f..733cbb1e 100644
--- a/src/datavisualization/data/qitemmodelscatterdataproxy_p.h
+++ b/src/datavisualization/data/qitemmodelscatterdataproxy_p.h
@@ -54,6 +54,16 @@ private:
QString m_zPosRole;
QString m_rotationRole;
+ QRegExp m_xPosRolePattern;
+ QRegExp m_yPosRolePattern;
+ QRegExp m_zPosRolePattern;
+ QRegExp m_rotationRolePattern;
+
+ QString m_xPosRoleReplace;
+ QString m_yPosRoleReplace;
+ QString m_zPosRoleReplace;
+ QString m_rotationRoleReplace;
+
friend class ScatterItemModelHandler;
friend class QItemModelScatterDataProxy;
};
diff --git a/src/datavisualization/data/qitemmodelsurfacedataproxy.cpp b/src/datavisualization/data/qitemmodelsurfacedataproxy.cpp
index 440ce2d6..5117d386 100644
--- a/src/datavisualization/data/qitemmodelsurfacedataproxy.cpp
+++ b/src/datavisualization/data/qitemmodelsurfacedataproxy.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QItemModelSurfaceDataProxy
* \inmodule QtDataVisualization
* \brief Proxy class for presenting data in item models with Q3DSurface.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QItemModelSurfaceDataProxy allows you to use QAbstractItemModel derived models as a data source
* for Q3DSurface. It uses the defined mappings to map data from the model to rows, columns, and
@@ -33,6 +33,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* Data is resolved asynchronously whenever the mapping or the model changes.
* QSurfaceDataProxy::arrayReset() is emitted when the data has been resolved.
+ * However, when useModelCategories property is set to true, single item changes are resolved
+ * synchronously, unless the same frame also contains a change that causes the whole model to be
+ * resolved.
*
* There are three ways to use mappings:
*
@@ -54,13 +57,24 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* and in which order by defining an explicit list of categories for either or both of rows and
* columns.
*
- * For example, assume that you have a custom QAbstractItemModel storing surface topography data.
- * Each item in the model has the roles "longitude", "latitude", and "height". The item model already
- * contains the data properly sorted so that longitudes and latitudes are first encountered in
- * correct order, which enables us to utilize the row and column category autogeneration.
- * You could do the following to display the data in a surface graph:
+ * For example, assume that you have a custom QAbstractItemModel storing surface topography data.
+ * Each item in the model has the roles "longitude", "latitude", and "height".
+ * The item model already contains the data properly sorted so that longitudes and latitudes are
+ * first encountered in correct order, which enables us to utilize the row and column category
+ * autogeneration.
+ * You could do the following to display the data in a surface graph:
+ *
+ * \snippet doc_src_qtdatavisualization.cpp 5
*
- * \snippet doc_src_qtdatavisualization.cpp 5
+ * If the fields of the model do not contain the data in the exact format you need, you can specify
+ * a search pattern regular expression and a replace rule for each role to get the value in a
+ * format you need. For more information how the replace using regular expressions works, see
+ * QString::replace(const QRegExp &rx, const QString &after) function documentation. Note that
+ * using regular expressions has an impact on the performance, so it's more efficient to utilize
+ * item models where doing search and replace is not necessary to get the desired values.
+ *
+ * For example about using the search patterns in conjunction with the roles, see
+ * ItemModelBarDataProxy usage in \l{Qt Quick 2 Bars Example}.
*
* \sa {Qt Data Visualization Data Handling}
*/
@@ -79,6 +93,8 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* Data is resolved asynchronously whenever the mapping or the model changes.
* QSurfaceDataProxy::arrayReset() is emitted when the data has been resolved.
*
+ * For ItemModelSurfaceDataProxy enums, see \l{QItemModelSurfaceDataProxy::MultiMatchBehavior}.
+ *
* For more details, see QItemModelSurfaceDataProxy documentation.
*
* Usage example:
@@ -95,33 +111,35 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty string ItemModelSurfaceDataProxy::rowRole
- * The row role of the mapping.
+ * Defines the item model role to map into row category.
* In addition to defining which row the data belongs to, the value indicated by row role
- * is also set as the Z-coordinate value of the QSurfaceDataItem when model data is resolved.
+ * is also set as the Z-coordinate value of the QSurfaceDataItem when model data is resolved,
+ * unless a separate zPos role is also defined.
*/
/*!
* \qmlproperty string ItemModelSurfaceDataProxy::columnRole
- * The column role of the mapping.
+ * Defines the item model role to map into column category.
* In addition to defining which column the data belongs to, the value indicated by column role
- * is also set as the X-coordinate value of the QSurfaceDataItem when model data is resolved.
+ * is also set as the X-coordinate value of the QSurfaceDataItem when model data is resolved,
+ * unless a separate xPos role is also defined.
*/
/*!
* \qmlproperty string ItemModelSurfaceDataProxy::xPosRole
- * The X position role of the mapping. If this role is not defined, columnRole is used to
- * determine the X-coordinate value of resolved QSurfaceDataItems.
+ * Defines the item model role to map into X position. If this role is not defined, columnRole is
+ * used to determine the X-coordinate value of resolved QSurfaceDataItems.
*/
/*!
* \qmlproperty string ItemModelSurfaceDataProxy::yPosRole
- * The Y position role of the mapping.
+ * Defines the item model role to map into Y position.
*/
/*!
* \qmlproperty string ItemModelSurfaceDataProxy::zPosRole
- * The Z position role of the mapping. If this role is not defined, rowRole is used to
- * determine the Z-coordinate value of resolved QSurfaceDataItems.
+ * Defines the item model role to map into Z position. If this role is not defined, rowRole is
+ * used to determine the Z-coordinate value of resolved QSurfaceDataItems.
*/
/*!
@@ -132,8 +150,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty list<String> ItemModelSurfaceDataProxy::columnCategories
- * The column categories of the mapping. Only items with column roles that are found in this list are
- * included when data is resolved. The columns are ordered in the same order as they are in this list.
+ * The column categories of the mapping. Only items with column roles that are found in this
+ * list are included when data is resolved. The columns are ordered in the same order as they are
+ * in this list.
*/
/*!
@@ -159,6 +178,141 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty regExp ItemModelSurfaceDataProxy::rowRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by row role before it is used as
+ * a row category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and rowRoleReplace property contains the replacement string.
+ *
+ * \sa rowRole, rowRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelSurfaceDataProxy::columnRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by column role before it is used
+ * as a column category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and columnRoleReplace property contains the replacement string.
+ *
+ * \sa columnRole, columnRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelSurfaceDataProxy::xPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by xPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and xPosRoleReplace property contains the replacement string.
+ *
+ * \sa xPosRole, xPosRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelSurfaceDataProxy::yPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by yPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and yPosRoleReplace property contains the replacement string.
+ *
+ * \sa yPosRole, yPosRoleReplace
+ */
+
+/*!
+ * \qmlproperty regExp ItemModelSurfaceDataProxy::zPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by zPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and zPosRoleReplace property contains the replacement string.
+ *
+ * \sa zPosRole, zPosRoleReplace
+ */
+
+/*!
+ * \qmlproperty string ItemModelSurfaceDataProxy::rowRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with rowRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rowRole, rowRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelSurfaceDataProxy::columnRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with columnRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa columnRole, columnRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelSurfaceDataProxy::xPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with xPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa xPosRole, xPosRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelSurfaceDataProxy::yPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with yPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa yPosRole, yPosRolePattern
+ */
+
+/*!
+ * \qmlproperty string ItemModelSurfaceDataProxy::zPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with zPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa zPosRole, zPosRolePattern
+ */
+
+/*!
+ * \qmlproperty ItemModelSurfaceDataProxy.MultiMatchBehavior ItemModelSurfaceDataProxy::multiMatchBehavior
+ * This property defines how multiple matches for each row/column combination are handled.
+ * Defaults to ItemModelSurfaceDataProxy.MMBLast.
+ *
+ * For example, you might have an item model with timestamped data taken at irregular intervals
+ * and you want to visualize an average position of data items on each hour with a surface graph.
+ * This can be done by specifying row and column categories so that each surface point represents
+ * an hour, and setting multiMatchBehavior to ItemModelSurfaceDataProxy.MMBAverage.
+ */
+
+/*!
+ * \enum QItemModelSurfaceDataProxy::MultiMatchBehavior
+ *
+ * Behavior types for QItemModelSurfaceDataProxy::multiMatchBehavior property.
+ *
+ * \value MMBFirst
+ * The position values are taken from the first item in the item model that matches
+ * each row/column combination.
+ * \value MMBLast
+ * The position values are taken from the last item in the item model that matches
+ * each row/column combination.
+ * \value MMBAverage
+ * The position values from all items matching each row/column combination are
+ * averaged together and the averages are used as the surface point position.
+ * \value MMBCumulativeY
+ * For X and Z values this acts just like \c{MMBAverage}, but Y values are added together
+ * instead of averaged and the total is used as the surface point Y position.
+ */
+
+/*!
* Constructs QItemModelSurfaceDataProxy with optional \a parent.
*/
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QObject *parent)
@@ -171,7 +325,7 @@ QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QObject *parent)
* Constructs QItemModelSurfaceDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
* ownership of the \a itemModel, as typically item models are owned by other controls.
*/
-QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel,
+QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
QObject *parent)
: QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
@@ -186,7 +340,7 @@ QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel
* This constructor is meant to be used with models that have data properly sorted
* in rows and columns already, so it also sets useModelCategories property to true.
*/
-QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel,
+QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
const QString &yPosRole,
QObject *parent)
: QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
@@ -203,7 +357,7 @@ QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel
* The role mappings are set with \a rowRole, \a columnRole, and \a yPosRole.
* The zPosRole and the xPosRole are set to \a rowRole and \a columnRole, respectively.
*/
-QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel,
+QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &yPosRole,
@@ -225,7 +379,7 @@ QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel
* The role mappings are set with \a rowRole, \a columnRole, \a xPosRole, \a yPosRole, and
* \a zPosRole.
*/
-QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel,
+QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &xPosRole,
@@ -251,7 +405,7 @@ QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel
* Row and column categories are set with \a rowCategories and \a columnCategories.
* This constructor also sets autoRowCategories and autoColumnCategories to false.
*/
-QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel,
+QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &yPosRole,
@@ -281,7 +435,7 @@ QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel
* Row and column categories are set with \a rowCategories and \a columnCategories.
* This constructor also sets autoRowCategories and autoColumnCategories to false.
*/
-QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel,
+QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
const QString &rowRole,
const QString &columnRole,
const QString &xPosRole,
@@ -318,12 +472,12 @@ QItemModelSurfaceDataProxy::~QItemModelSurfaceDataProxy()
* Defines item model. Does not take ownership of the model, but does connect to it to listen for
* changes.
*/
-void QItemModelSurfaceDataProxy::setItemModel(const QAbstractItemModel *itemModel)
+void QItemModelSurfaceDataProxy::setItemModel(QAbstractItemModel *itemModel)
{
dptr()->m_itemModelHandler->setItemModel(itemModel);
}
-const QAbstractItemModel *QItemModelSurfaceDataProxy::itemModel() const
+QAbstractItemModel *QItemModelSurfaceDataProxy::itemModel() const
{
return dptrc()->m_itemModelHandler->itemModel();
}
@@ -331,7 +485,10 @@ const QAbstractItemModel *QItemModelSurfaceDataProxy::itemModel() const
/*!
* \property QItemModelSurfaceDataProxy::rowRole
*
- * Defines the row \a role for the mapping.
+ * Defines the item model role to map into row category.
+ * In addition to defining which row the data belongs to, the value indicated by row role
+ * is also set as the Z-coordinate value of the QSurfaceDataItem when model data is resolved,
+ * unless a separate zPos role is also defined.
*/
void QItemModelSurfaceDataProxy::setRowRole(const QString &role)
{
@@ -349,7 +506,10 @@ QString QItemModelSurfaceDataProxy::rowRole() const
/*!
* \property QItemModelSurfaceDataProxy::columnRole
*
- * Defines the column \a role for the mapping.
+ * Defines the item model role to map into column category.
+ * In addition to defining which column the data belongs to, the value indicated by column role
+ * is also set as the X-coordinate value of the QSurfaceDataItem when model data is resolved,
+ * unless a separate xPos role is also defined.
*/
void QItemModelSurfaceDataProxy::setColumnRole(const QString &role)
{
@@ -367,9 +527,8 @@ QString QItemModelSurfaceDataProxy::columnRole() const
/*!
* \property QItemModelSurfaceDataProxy::xPosRole
*
- * Defines the X position \a role for the mapping.
- * If this role is not defined, columnRole is used to determine the X-coordinate
- * value of resolved QSurfaceDataItems.
+ * Defines the item model role to map into X position. If this role is not defined, columnRole is
+ * used to determine the X-coordinate value of resolved QSurfaceDataItems.
*/
void QItemModelSurfaceDataProxy::setXPosRole(const QString &role)
{
@@ -387,7 +546,7 @@ QString QItemModelSurfaceDataProxy::xPosRole() const
/*!
* \property QItemModelSurfaceDataProxy::yPosRole
*
- * Defines the Y position \a role for the mapping.
+ * Defines the item model role to map into Y position.
*/
void QItemModelSurfaceDataProxy::setYPosRole(const QString &role)
{
@@ -405,9 +564,8 @@ QString QItemModelSurfaceDataProxy::yPosRole() const
/*!
* \property QItemModelSurfaceDataProxy::zPosRole
*
- * Defines the Z position \a role for the mapping.
- * If this role is not defined, rowRole is used to determine the Z-coordinate
- * value of resolved QSurfaceDataItems.
+ * Defines the item model role to map into Z position. If this role is not defined, rowRole is
+ * used to determine the Z-coordinate value of resolved QSurfaceDataItems.
*/
void QItemModelSurfaceDataProxy::setZPosRole(const QString &role)
{
@@ -561,6 +719,256 @@ int QItemModelSurfaceDataProxy::columnCategoryIndex(const QString &category)
}
/*!
+ * \property QItemModelSurfaceDataProxy::rowRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by row role before it is used as
+ * a row category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and rowRoleReplace property contains the replacement string.
+ *
+ * \sa rowRole, rowRoleReplace
+ */
+void QItemModelSurfaceDataProxy::setRowRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_rowRolePattern != pattern) {
+ dptr()->m_rowRolePattern = pattern;
+ emit rowRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelSurfaceDataProxy::rowRolePattern() const
+{
+ return dptrc()->m_rowRolePattern;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::columnRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by column role before it is used
+ * as a column category. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and columnRoleReplace property contains the replacement string.
+ *
+ * \sa columnRole, columnRoleReplace
+ */
+void QItemModelSurfaceDataProxy::setColumnRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_columnRolePattern != pattern) {
+ dptr()->m_columnRolePattern = pattern;
+ emit columnRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelSurfaceDataProxy::columnRolePattern() const
+{
+ return dptrc()->m_columnRolePattern;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::xPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by xPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and xPosRoleReplace property contains the replacement string.
+ *
+ * \sa xPosRole, xPosRoleReplace
+ */
+void QItemModelSurfaceDataProxy::setXPosRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_xPosRolePattern != pattern) {
+ dptr()->m_xPosRolePattern = pattern;
+ emit xPosRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelSurfaceDataProxy::xPosRolePattern() const
+{
+ return dptrc()->m_xPosRolePattern;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::yPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by yPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and yPosRoleReplace property contains the replacement string.
+ *
+ * \sa yPosRole, yPosRoleReplace
+ */
+void QItemModelSurfaceDataProxy::setYPosRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_yPosRolePattern != pattern) {
+ dptr()->m_yPosRolePattern = pattern;
+ emit yPosRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelSurfaceDataProxy::yPosRolePattern() const
+{
+ return dptrc()->m_yPosRolePattern;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::zPosRolePattern
+ *
+ * When set, a search and replace is done on the value mapped by zPos role before it is used as
+ * a item position value. This property specifies the regular expression to find the portion of the
+ * mapped value to replace and zPosRoleReplace property contains the replacement string.
+ *
+ * \sa zPosRole, zPosRoleReplace
+ */
+void QItemModelSurfaceDataProxy::setZPosRolePattern(const QRegExp &pattern)
+{
+ if (dptr()->m_zPosRolePattern != pattern) {
+ dptr()->m_zPosRolePattern = pattern;
+ emit zPosRolePatternChanged(pattern);
+ }
+}
+
+QRegExp QItemModelSurfaceDataProxy::zPosRolePattern() const
+{
+ return dptrc()->m_zPosRolePattern;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::rowRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with rowRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa rowRole, rowRolePattern
+ */
+void QItemModelSurfaceDataProxy::setRowRoleReplace(const QString &replace)
+{
+ if (dptr()->m_rowRoleReplace != replace) {
+ dptr()->m_rowRoleReplace = replace;
+ emit rowRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelSurfaceDataProxy::rowRoleReplace() const
+{
+ return dptrc()->m_rowRoleReplace;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::columnRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with columnRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa columnRole, columnRolePattern
+ */
+void QItemModelSurfaceDataProxy::setColumnRoleReplace(const QString &replace)
+{
+ if (dptr()->m_columnRoleReplace != replace) {
+ dptr()->m_columnRoleReplace = replace;
+ emit columnRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelSurfaceDataProxy::columnRoleReplace() const
+{
+ return dptrc()->m_columnRoleReplace;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::xPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with xPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa xPosRole, xPosRolePattern
+ */
+void QItemModelSurfaceDataProxy::setXPosRoleReplace(const QString &replace)
+{
+ if (dptr()->m_xPosRoleReplace != replace) {
+ dptr()->m_xPosRoleReplace = replace;
+ emit xPosRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelSurfaceDataProxy::xPosRoleReplace() const
+{
+ return dptrc()->m_xPosRoleReplace;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::yPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with yPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa yPosRole, yPosRolePattern
+ */
+void QItemModelSurfaceDataProxy::setYPosRoleReplace(const QString &replace)
+{
+ if (dptr()->m_yPosRoleReplace != replace) {
+ dptr()->m_yPosRoleReplace = replace;
+ emit yPosRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelSurfaceDataProxy::yPosRoleReplace() const
+{
+ return dptrc()->m_yPosRoleReplace;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::zPosRoleReplace
+ *
+ * This property defines the replace content to be used in conjunction with zPosRolePattern.
+ * Defaults to empty string. For more information on how the search and replace using regular
+ * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
+ * function documentation.
+ *
+ * \sa zPosRole, zPosRolePattern
+ */
+void QItemModelSurfaceDataProxy::setZPosRoleReplace(const QString &replace)
+{
+ if (dptr()->m_zPosRoleReplace != replace) {
+ dptr()->m_zPosRoleReplace = replace;
+ emit zPosRoleReplaceChanged(replace);
+ }
+}
+
+QString QItemModelSurfaceDataProxy::zPosRoleReplace() const
+{
+ return dptrc()->m_zPosRoleReplace;
+}
+
+/*!
+ * \property QItemModelSurfaceDataProxy::multiMatchBehavior
+ *
+ * This property defines how multiple matches for each row/column combination are handled.
+ * Defaults to QItemModelSurfaceDataProxy::MMBLast.
+ *
+ * For example, you might have an item model with timestamped data taken at irregular intervals
+ * and you want to visualize an average position of data items on each hour with a surface graph.
+ * This can be done by specifying row and column categories so that each surface point represents
+ * an hour, and setting multiMatchBehavior to QItemModelSurfaceDataProxy::MMBAverage.
+ */
+
+void QItemModelSurfaceDataProxy::setMultiMatchBehavior(QItemModelSurfaceDataProxy::MultiMatchBehavior behavior)
+{
+ if (dptr()->m_multiMatchBehavior != behavior) {
+ dptr()->m_multiMatchBehavior = behavior;
+ emit multiMatchBehaviorChanged(behavior);
+ }
+}
+
+QItemModelSurfaceDataProxy::MultiMatchBehavior QItemModelSurfaceDataProxy::multiMatchBehavior() const
+{
+ return dptrc()->m_multiMatchBehavior;
+}
+
+/*!
* \internal
*/
QItemModelSurfaceDataProxyPrivate *QItemModelSurfaceDataProxy::dptr()
@@ -583,7 +991,8 @@ QItemModelSurfaceDataProxyPrivate::QItemModelSurfaceDataProxyPrivate(QItemModelS
m_itemModelHandler(new SurfaceItemModelHandler(q)),
m_useModelCategories(false),
m_autoRowCategories(true),
- m_autoColumnCategories(true)
+ m_autoColumnCategories(true),
+ m_multiMatchBehavior(QItemModelSurfaceDataProxy::MMBLast)
{
}
@@ -621,6 +1030,28 @@ void QItemModelSurfaceDataProxyPrivate::connectItemModelHandler()
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
QObject::connect(qptr(), &QItemModelSurfaceDataProxy::autoColumnCategoriesChanged,
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::rowRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::columnRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::xPosRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::yPosRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::zPosRolePatternChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::rowRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::columnRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::xPosRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::yPosRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::zPosRoleReplaceChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelSurfaceDataProxy::multiMatchBehaviorChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qitemmodelsurfacedataproxy.h b/src/datavisualization/data/qitemmodelsurfacedataproxy.h
index b1ebbeed..1b95ed90 100644
--- a/src/datavisualization/data/qitemmodelsurfacedataproxy.h
+++ b/src/datavisualization/data/qitemmodelsurfacedataproxy.h
@@ -22,6 +22,7 @@
#include <QtDataVisualization/qsurfacedataproxy.h>
#include <QtCore/QAbstractItemModel>
#include <QtCore/QStringList>
+#include <QtCore/QRegExp>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -30,7 +31,8 @@ class QItemModelSurfaceDataProxyPrivate;
class QT_DATAVISUALIZATION_EXPORT QItemModelSurfaceDataProxy : public QSurfaceDataProxy
{
Q_OBJECT
- Q_PROPERTY(const QAbstractItemModel* itemModel READ itemModel WRITE setItemModel NOTIFY itemModelChanged)
+ Q_ENUMS(MultiMatchBehavior)
+ Q_PROPERTY(QAbstractItemModel* itemModel READ itemModel WRITE setItemModel NOTIFY itemModelChanged)
Q_PROPERTY(QString rowRole READ rowRole WRITE setRowRole NOTIFY rowRoleChanged)
Q_PROPERTY(QString columnRole READ columnRole WRITE setColumnRole NOTIFY columnRoleChanged)
Q_PROPERTY(QString xPosRole READ xPosRole WRITE setXPosRole NOTIFY xPosRoleChanged)
@@ -41,32 +43,52 @@ class QT_DATAVISUALIZATION_EXPORT QItemModelSurfaceDataProxy : public QSurfaceDa
Q_PROPERTY(bool useModelCategories READ useModelCategories WRITE setUseModelCategories NOTIFY useModelCategoriesChanged)
Q_PROPERTY(bool autoRowCategories READ autoRowCategories WRITE setAutoRowCategories NOTIFY autoRowCategoriesChanged)
Q_PROPERTY(bool autoColumnCategories READ autoColumnCategories WRITE setAutoColumnCategories NOTIFY autoColumnCategoriesChanged)
+ Q_PROPERTY(QRegExp rowRolePattern READ rowRolePattern WRITE setRowRolePattern NOTIFY rowRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp columnRolePattern READ columnRolePattern WRITE setColumnRolePattern NOTIFY columnRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp xPosRolePattern READ xPosRolePattern WRITE setXPosRolePattern NOTIFY xPosRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp yPosRolePattern READ yPosRolePattern WRITE setYPosRolePattern NOTIFY yPosRolePatternChanged REVISION 1)
+ Q_PROPERTY(QRegExp zPosRolePattern READ zPosRolePattern WRITE setZPosRolePattern NOTIFY zPosRolePatternChanged REVISION 1)
+ Q_PROPERTY(QString rowRoleReplace READ rowRoleReplace WRITE setRowRoleReplace NOTIFY rowRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString columnRoleReplace READ columnRoleReplace WRITE setColumnRoleReplace NOTIFY columnRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString xPosRoleReplace READ xPosRoleReplace WRITE setXPosRoleReplace NOTIFY xPosRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString yPosRoleReplace READ yPosRoleReplace WRITE setYPosRoleReplace NOTIFY yPosRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(QString zPosRoleReplace READ zPosRoleReplace WRITE setZPosRoleReplace NOTIFY zPosRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(MultiMatchBehavior multiMatchBehavior READ multiMatchBehavior WRITE setMultiMatchBehavior NOTIFY multiMatchBehaviorChanged REVISION 1)
public:
+ enum MultiMatchBehavior {
+ MMBFirst = 0,
+ MMBLast = 1,
+ MMBAverage = 2,
+ MMBCumulativeY = 3
+ };
+
explicit QItemModelSurfaceDataProxy(QObject *parent = 0);
- QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel, QObject *parent = 0);
- QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel, const QString &yPosRole,
+ QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel, QObject *parent = 0);
+ QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel, const QString &yPosRole,
QObject *parent = 0);
- QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &yPosRole,
QObject *parent = 0);
- QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &xPosRole,
const QString &yPosRole, const QString &zPosRole,
QObject *parent = 0);
- QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &yPosRole,
- const QStringList &rowCategories, const QStringList &columnCategories,
+ const QStringList &rowCategories,
+ const QStringList &columnCategories,
QObject *parent = 0);
- QItemModelSurfaceDataProxy(const QAbstractItemModel *itemModel, const QString &rowRole,
+ QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel, const QString &rowRole,
const QString &columnRole, const QString &xPosRole,
const QString &yPosRole, const QString &zPosRole,
- const QStringList &rowCategories, const QStringList &columnCategories,
+ const QStringList &rowCategories,
+ const QStringList &columnCategories,
QObject *parent = 0);
virtual ~QItemModelSurfaceDataProxy();
- void setItemModel(const QAbstractItemModel *itemModel);
- const QAbstractItemModel *itemModel() const;
+ void setItemModel(QAbstractItemModel *itemModel);
+ QAbstractItemModel *itemModel() const;
void setRowRole(const QString &role);
QString rowRole() const;
@@ -99,6 +121,31 @@ public:
Q_INVOKABLE int rowCategoryIndex(const QString& category);
Q_INVOKABLE int columnCategoryIndex(const QString& category);
+ void setRowRolePattern(const QRegExp &pattern);
+ QRegExp rowRolePattern() const;
+ void setColumnRolePattern(const QRegExp &pattern);
+ QRegExp columnRolePattern() const;
+ void setXPosRolePattern(const QRegExp &pattern);
+ QRegExp xPosRolePattern() const;
+ void setYPosRolePattern(const QRegExp &pattern);
+ QRegExp yPosRolePattern() const;
+ void setZPosRolePattern(const QRegExp &pattern);
+ QRegExp zPosRolePattern() const;
+
+ void setRowRoleReplace(const QString &replace);
+ QString rowRoleReplace() const;
+ void setColumnRoleReplace(const QString &replace);
+ QString columnRoleReplace() const;
+ void setXPosRoleReplace(const QString &replace);
+ QString xPosRoleReplace() const;
+ void setYPosRoleReplace(const QString &replace);
+ QString yPosRoleReplace() const;
+ void setZPosRoleReplace(const QString &replace);
+ QString zPosRoleReplace() const;
+
+ void setMultiMatchBehavior(MultiMatchBehavior behavior);
+ MultiMatchBehavior multiMatchBehavior() const;
+
signals:
void itemModelChanged(const QAbstractItemModel* itemModel);
void rowRoleChanged(const QString &role);
@@ -111,6 +158,17 @@ signals:
void useModelCategoriesChanged(bool enable);
void autoRowCategoriesChanged(bool enable);
void autoColumnCategoriesChanged(bool enable);
+ Q_REVISION(1) void rowRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void columnRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void xPosRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void yPosRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void zPosRolePatternChanged(const QRegExp &pattern);
+ Q_REVISION(1) void rowRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void columnRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void xPosRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void yPosRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void zPosRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void multiMatchBehaviorChanged(MultiMatchBehavior behavior);
protected:
QItemModelSurfaceDataProxyPrivate *dptr();
diff --git a/src/datavisualization/data/qitemmodelsurfacedataproxy_p.h b/src/datavisualization/data/qitemmodelsurfacedataproxy_p.h
index 0aaea8fd..9a79dca2 100644
--- a/src/datavisualization/data/qitemmodelsurfacedataproxy_p.h
+++ b/src/datavisualization/data/qitemmodelsurfacedataproxy_p.h
@@ -64,6 +64,20 @@ private:
bool m_autoRowCategories;
bool m_autoColumnCategories;
+ QRegExp m_rowRolePattern;
+ QRegExp m_columnRolePattern;
+ QRegExp m_xPosRolePattern;
+ QRegExp m_yPosRolePattern;
+ QRegExp m_zPosRolePattern;
+
+ QString m_rowRoleReplace;
+ QString m_columnRoleReplace;
+ QString m_xPosRoleReplace;
+ QString m_yPosRoleReplace;
+ QString m_zPosRoleReplace;
+
+ QItemModelSurfaceDataProxy::MultiMatchBehavior m_multiMatchBehavior;
+
friend class SurfaceItemModelHandler;
friend class QItemModelSurfaceDataProxy;
};
diff --git a/src/datavisualization/data/qscatter3dseries.cpp b/src/datavisualization/data/qscatter3dseries.cpp
index 43bde4dd..e81933ed 100644
--- a/src/datavisualization/data/qscatter3dseries.cpp
+++ b/src/datavisualization/data/qscatter3dseries.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QScatter3DSeries
* \inmodule QtDataVisualization
* \brief Base series class for Q3DScatter.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QScatter3DSeries manages the series specific visual elements, as well as series data
* (via data proxy).
@@ -298,9 +298,54 @@ void QScatter3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *ne
}
}
+void QScatter3DSeriesPrivate::createItemLabel()
+{
+ static const QString xTitleTag(QStringLiteral("@xTitle"));
+ static const QString yTitleTag(QStringLiteral("@yTitle"));
+ static const QString zTitleTag(QStringLiteral("@zTitle"));
+ static const QString xLabelTag(QStringLiteral("@xLabel"));
+ static const QString yLabelTag(QStringLiteral("@yLabel"));
+ static const QString zLabelTag(QStringLiteral("@zLabel"));
+ static const QString seriesNameTag(QStringLiteral("@seriesName"));
+
+ if (m_selectedItem == QScatter3DSeries::invalidSelectionIndex()) {
+ m_itemLabel = QString();
+ return;
+ }
+
+ QValue3DAxis *axisX = static_cast<QValue3DAxis *>(m_controller->axisX());
+ QValue3DAxis *axisY = static_cast<QValue3DAxis *>(m_controller->axisY());
+ QValue3DAxis *axisZ = static_cast<QValue3DAxis *>(m_controller->axisZ());
+ QVector3D selectedPosition = qptr()->dataProxy()->itemAt(m_selectedItem)->position();
+
+ m_itemLabel = m_itemLabelFormat;
+
+ m_itemLabel.replace(xTitleTag, axisX->title());
+ m_itemLabel.replace(yTitleTag, axisY->title());
+ m_itemLabel.replace(zTitleTag, axisZ->title());
+
+ if (m_itemLabel.contains(xLabelTag)) {
+ QString valueLabelText = axisX->formatter()->stringForValue(
+ qreal(selectedPosition.x()), axisX->labelFormat());
+ m_itemLabel.replace(xLabelTag, valueLabelText);
+ }
+ if (m_itemLabel.contains(yLabelTag)) {
+ QString valueLabelText = axisY->formatter()->stringForValue(
+ qreal(selectedPosition.y()), axisY->labelFormat());
+ m_itemLabel.replace(yLabelTag, valueLabelText);
+ }
+ if (m_itemLabel.contains(zLabelTag)) {
+ QString valueLabelText = axisZ->formatter()->stringForValue(
+ qreal(selectedPosition.z()), axisZ->labelFormat());
+ m_itemLabel.replace(zLabelTag, valueLabelText);
+ }
+ m_itemLabel.replace(seriesNameTag, m_name);
+}
+
void QScatter3DSeriesPrivate::setSelectedItem(int index)
{
if (index != m_selectedItem) {
+ markItemLabelDirty();
m_selectedItem = index;
emit qptr()->selectedItemChanged(m_selectedItem);
}
diff --git a/src/datavisualization/data/qscatter3dseries_p.h b/src/datavisualization/data/qscatter3dseries_p.h
index 1abbd406..8717a616 100644
--- a/src/datavisualization/data/qscatter3dseries_p.h
+++ b/src/datavisualization/data/qscatter3dseries_p.h
@@ -43,6 +43,7 @@ public:
virtual void setDataProxy(QAbstractDataProxy *proxy);
virtual void connectControllerAndProxy(Abstract3DController *newController);
+ virtual void createItemLabel();
void setSelectedItem(int index);
void setItemSize(float size);
diff --git a/src/datavisualization/data/qscatterdataitem.cpp b/src/datavisualization/data/qscatterdataitem.cpp
index 9751dfeb..e45b22f3 100644
--- a/src/datavisualization/data/qscatterdataitem.cpp
+++ b/src/datavisualization/data/qscatterdataitem.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \inmodule QtDataVisualization
* \brief The QScatterDataItem class provides a container for resolved data to be added to scatter
* graphs.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* A QScatterDataItem holds data for a single rendered item in a scatter graph.
* Scatter data proxies parse data into QScatterDataItem instances for visualizing.
diff --git a/src/datavisualization/data/qscatterdataitem_p.h b/src/datavisualization/data/qscatterdataitem_p.h
index efb1cc5c..2884c2e3 100644
--- a/src/datavisualization/data/qscatterdataitem_p.h
+++ b/src/datavisualization/data/qscatterdataitem_p.h
@@ -39,9 +39,6 @@ class QScatterDataItemPrivate
public:
QScatterDataItemPrivate();
virtual ~QScatterDataItemPrivate();
-
-protected:
- friend class QScatterDataItem;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qscatterdataproxy.cpp b/src/datavisualization/data/qscatterdataproxy.cpp
index 84bb57e7..cc2e3bd8 100644
--- a/src/datavisualization/data/qscatterdataproxy.cpp
+++ b/src/datavisualization/data/qscatterdataproxy.cpp
@@ -16,7 +16,6 @@
**
****************************************************************************/
-#include "qscatterdataproxy.h"
#include "qscatterdataproxy_p.h"
#include "qscatter3dseries_p.h"
@@ -26,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QScatterDataProxy
* \inmodule QtDataVisualization
* \brief Base proxy class for Q3DScatter.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QScatterDataProxy handles adding, inserting, changing, and removing data items.
*
diff --git a/src/datavisualization/data/qsurface3dseries.cpp b/src/datavisualization/data/qsurface3dseries.cpp
index 72967bae..1107d721 100644
--- a/src/datavisualization/data/qsurface3dseries.cpp
+++ b/src/datavisualization/data/qsurface3dseries.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QSurface3DSeries
* \inmodule QtDataVisualization
* \brief Base series class for Q3DSurface.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QSurface3DSeries manages the series specific visual elements, as well as series data
* (via data proxy).
@@ -75,7 +75,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* This type manages the series specific visual elements, as well as series data
* (via data proxy).
*
- * For Surface3DSeries enums, see \l QSurface3DSeries::DrawFlag
+ * For Surface3DSeries enums, see \l{QSurface3DSeries::DrawFlag}.
*
* For more complete description, see QSurface3DSeries.
*
@@ -373,9 +373,54 @@ void QSurface3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *ne
}
}
+void QSurface3DSeriesPrivate::createItemLabel()
+{
+ static const QString xTitleTag(QStringLiteral("@xTitle"));
+ static const QString yTitleTag(QStringLiteral("@yTitle"));
+ static const QString zTitleTag(QStringLiteral("@zTitle"));
+ static const QString xLabelTag(QStringLiteral("@xLabel"));
+ static const QString yLabelTag(QStringLiteral("@yLabel"));
+ static const QString zLabelTag(QStringLiteral("@zLabel"));
+ static const QString seriesNameTag(QStringLiteral("@seriesName"));
+
+ if (m_selectedPoint == QSurface3DSeries::invalidSelectionPosition()) {
+ m_itemLabel = QString();
+ return;
+ }
+
+ QValue3DAxis *axisX = static_cast<QValue3DAxis *>(m_controller->axisX());
+ QValue3DAxis *axisY = static_cast<QValue3DAxis *>(m_controller->axisY());
+ QValue3DAxis *axisZ = static_cast<QValue3DAxis *>(m_controller->axisZ());
+ QVector3D selectedPosition = qptr()->dataProxy()->itemAt(m_selectedPoint)->position();
+
+ m_itemLabel = m_itemLabelFormat;
+
+ m_itemLabel.replace(xTitleTag, axisX->title());
+ m_itemLabel.replace(yTitleTag, axisY->title());
+ m_itemLabel.replace(zTitleTag, axisZ->title());
+
+ if (m_itemLabel.contains(xLabelTag)) {
+ QString valueLabelText = axisX->formatter()->stringForValue(
+ qreal(selectedPosition.x()), axisX->labelFormat());
+ m_itemLabel.replace(xLabelTag, valueLabelText);
+ }
+ if (m_itemLabel.contains(yLabelTag)) {
+ QString valueLabelText = axisY->formatter()->stringForValue(
+ qreal(selectedPosition.y()), axisY->labelFormat());
+ m_itemLabel.replace(yLabelTag, valueLabelText);
+ }
+ if (m_itemLabel.contains(zLabelTag)) {
+ QString valueLabelText = axisZ->formatter()->stringForValue(
+ qreal(selectedPosition.z()), axisZ->labelFormat());
+ m_itemLabel.replace(zLabelTag, valueLabelText);
+ }
+ m_itemLabel.replace(seriesNameTag, m_name);
+}
+
void QSurface3DSeriesPrivate::setSelectedPoint(const QPoint &position)
{
if (position != m_selectedPoint) {
+ markItemLabelDirty();
m_selectedPoint = position;
emit qptr()->selectedPointChanged(m_selectedPoint);
}
diff --git a/src/datavisualization/data/qsurface3dseries_p.h b/src/datavisualization/data/qsurface3dseries_p.h
index bc8157bd..d4cc2820 100644
--- a/src/datavisualization/data/qsurface3dseries_p.h
+++ b/src/datavisualization/data/qsurface3dseries_p.h
@@ -43,6 +43,7 @@ public:
virtual void setDataProxy(QAbstractDataProxy *proxy);
virtual void connectControllerAndProxy(Abstract3DController *newController);
+ virtual void createItemLabel();
void setSelectedPoint(const QPoint &position);
void setFlatShadingEnabled(bool enabled);
diff --git a/src/datavisualization/data/qsurfacedataitem.cpp b/src/datavisualization/data/qsurfacedataitem.cpp
index c8a76a67..cd3819c7 100644
--- a/src/datavisualization/data/qsurfacedataitem.cpp
+++ b/src/datavisualization/data/qsurfacedataitem.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \inmodule QtDataVisualization
* \brief The QSurfaceDataItem class provides a container for resolved data to be added to surface
* graphs.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* A QSurfaceDataItem holds data for a single vertex in surface graph.
* Surface data proxies parse data into QSurfaceDataItem instances for visualizing.
diff --git a/src/datavisualization/data/qsurfacedataitem_p.h b/src/datavisualization/data/qsurfacedataitem_p.h
index 0cec7eab..e00134e3 100644
--- a/src/datavisualization/data/qsurfacedataitem_p.h
+++ b/src/datavisualization/data/qsurfacedataitem_p.h
@@ -39,9 +39,6 @@ class QSurfaceDataItemPrivate
public:
QSurfaceDataItemPrivate();
virtual ~QSurfaceDataItemPrivate();
-
-protected:
- friend class QSurfaceDataItem;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qsurfacedataproxy.cpp b/src/datavisualization/data/qsurfacedataproxy.cpp
index 2e66bb5b..dbe7cc49 100644
--- a/src/datavisualization/data/qsurfacedataproxy.cpp
+++ b/src/datavisualization/data/qsurfacedataproxy.cpp
@@ -16,7 +16,6 @@
**
****************************************************************************/
-#include "qsurfacedataproxy.h"
#include "qsurfacedataproxy_p.h"
#include "qsurface3dseries_p.h"
@@ -26,10 +25,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QSurfaceDataProxy
* \inmodule QtDataVisualization
* \brief Base proxy class for Q3DSurface.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
- * QSurfaceDataProxy takes care of surface related data handling. The QSurfaceDataProxy handles the data
- * in rows and for this it provides two auxiliary typedefs. QSurfaceDataArray is a QList for
+ * QSurfaceDataProxy takes care of surface related data handling. The QSurfaceDataProxy handles the
+ * data in rows and for this it provides two auxiliary typedefs. QSurfaceDataArray is a QList for
* controlling the rows. For rows there is a QVector QSurfaceDataRow which contains QSurfaceDataItem
* objects. See Q3DSurface documentation and basic sample code there how to feed the data for the
* QSurfaceDataProxy.
@@ -41,17 +40,18 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* If you use QSurfaceDataRow pointers to directly modify data after adding the array to the proxy,
* you must also emit proper signal to make the graph update.
*
- * To make a sensible surface, the X-value of each successive item in the same row must be greater than the
- * previous item in that row, and the the Z-value of each successive item in a column must be greater than
- * the previous item in that column.
+ * To make a sensible surface, the X-value of each successive item in all rows must be
+ * either ascending or descending throughout the row.
+ * Similarly, the Z-value of each successive item in all columns must be either ascending or
+ * descending throughout the column.
*
- * \note In the initial release, only surfaces with straight rows and columns are fully supported. Any row
- * with items that do not have the exact same Z-value or any columns with items that do not have the exact
- * same X-value may get clipped incorrectly if the whole surface doesn't fit to the visible X or Z axis
- * ranges.
+ * \note Currently only surfaces with straight rows and columns are fully supported. Any row
+ * with items that do not have the exact same Z-value or any column with items that do not have
+ * the exact same X-value may get clipped incorrectly if the whole surface doesn't completely fit
+ * in the visible X or Z axis ranges.
*
* \note Surfaces with less than two rows or columns are not considered valid surfaces and will
- * not get rendered.
+ * not be rendered.
*
* \sa {Qt Data Visualization Data Handling}
*/
@@ -520,10 +520,14 @@ void QSurfaceDataProxyPrivate::limitValues(QVector3D &minValues, QVector3D &maxV
minValues.setY(min);
maxValues.setY(max);
if (columns) {
- minValues.setX(m_dataArray->at(0)->at(0).x());
- minValues.setZ(m_dataArray->at(0)->at(0).z());
- maxValues.setX(m_dataArray->at(0)->last().x());
- maxValues.setZ(m_dataArray->last()->at(0).z());
+ float xLow = m_dataArray->at(0)->at(0).x();
+ float xHigh = m_dataArray->at(0)->last().x();
+ float zLow = m_dataArray->at(0)->at(0).z();
+ float zHigh = m_dataArray->last()->at(0).z();
+ minValues.setX(qMin(xLow, xHigh));
+ minValues.setZ(qMin(zLow, zHigh));
+ maxValues.setX(qMax(xLow, xHigh));
+ maxValues.setZ(qMax(zLow, zHigh));
} else {
minValues.setX(0.0f);
minValues.setZ(0.0f);
diff --git a/src/datavisualization/data/scatteritemmodelhandler.cpp b/src/datavisualization/data/scatteritemmodelhandler.cpp
index 08ed12f3..8b4a6f89 100644
--- a/src/datavisualization/data/scatteritemmodelhandler.cpp
+++ b/src/datavisualization/data/scatteritemmodelhandler.cpp
@@ -25,7 +25,15 @@ static const int noRoleIndex = -1;
ScatterItemModelHandler::ScatterItemModelHandler(QItemModelScatterDataProxy *proxy, QObject *parent)
: AbstractItemModelHandler(parent),
m_proxy(proxy),
- m_proxyArray(0)
+ m_proxyArray(0),
+ m_xPosRole(noRoleIndex),
+ m_yPosRole(noRoleIndex),
+ m_zPosRole(noRoleIndex),
+ m_rotationRole(noRoleIndex),
+ m_haveXPosPattern(false),
+ m_haveYPosPattern(false),
+ m_haveZPosPattern(false),
+ m_haveRotationPattern(false)
{
}
@@ -130,17 +138,48 @@ void ScatterItemModelHandler::modelPosToScatterItem(int modelRow, int modelColum
QScatterDataItem &item)
{
QModelIndex index = m_itemModel->index(modelRow, modelColumn);
- float xPos(0.0f);
- float yPos(0.0f);
- float zPos(0.0f);
- if (m_xPosRole != noRoleIndex)
- xPos = index.data(m_xPosRole).toFloat();
- if (m_yPosRole != noRoleIndex)
- yPos = index.data(m_yPosRole).toFloat();
- if (m_zPosRole != noRoleIndex)
- zPos = index.data(m_zPosRole).toFloat();
- if (m_rotationRole != noRoleIndex)
- item.setRotation(toQuaternion(index.data(m_rotationRole)));
+ float xPos;
+ float yPos;
+ float zPos;
+ if (m_xPosRole != noRoleIndex) {
+ QVariant xValueVar = index.data(m_xPosRole);
+ if (m_haveXPosPattern)
+ xPos = xValueVar.toString().replace(m_xPosPattern, m_xPosReplace).toFloat();
+ else
+ xPos = xValueVar.toFloat();
+ } else {
+ xPos = 0.0f;
+ }
+ if (m_yPosRole != noRoleIndex) {
+ QVariant yValueVar = index.data(m_yPosRole);
+ if (m_haveYPosPattern)
+ yPos = yValueVar.toString().replace(m_yPosPattern, m_yPosReplace).toFloat();
+ else
+ yPos = yValueVar.toFloat();
+ } else {
+ yPos = 0.0f;
+ }
+ if (m_zPosRole != noRoleIndex) {
+ QVariant zValueVar = index.data(m_zPosRole);
+ if (m_haveZPosPattern)
+ zPos = zValueVar.toString().replace(m_zPosPattern, m_zPosReplace).toFloat();
+ else
+ zPos = zValueVar.toFloat();
+ } else {
+ zPos = 0.0f;
+ }
+ if (m_rotationRole != noRoleIndex) {
+ QVariant rotationVar = index.data(m_rotationRole);
+ if (m_haveRotationPattern) {
+ item.setRotation(
+ toQuaternion(
+ QVariant(rotationVar.toString().replace(m_rotationPattern,
+ m_rotationReplace))));
+ } else {
+ item.setRotation(toQuaternion(rotationVar));
+ }
+ }
+
item.setPosition(QVector3D(xPos, yPos, zPos));
}
@@ -153,6 +192,19 @@ void ScatterItemModelHandler::resolveModel()
return;
}
+ m_xPosPattern = m_proxy->xPosRolePattern();
+ m_yPosPattern = m_proxy->yPosRolePattern();
+ m_zPosPattern = m_proxy->zPosRolePattern();
+ m_rotationPattern = m_proxy->rotationRolePattern();
+ m_xPosReplace = m_proxy->xPosRoleReplace();
+ m_yPosReplace = m_proxy->yPosRoleReplace();
+ m_zPosReplace = m_proxy->zPosRoleReplace();
+ m_rotationReplace = m_proxy->rotationRoleReplace();
+ m_haveXPosPattern = !m_xPosPattern.isEmpty() && m_xPosPattern.isValid();
+ m_haveYPosPattern = !m_yPosPattern.isEmpty() && m_yPosPattern.isValid();
+ m_haveZPosPattern = !m_zPosPattern.isEmpty() && m_zPosPattern.isValid();
+ m_haveRotationPattern = !m_rotationPattern.isEmpty() && m_rotationPattern.isValid();
+
QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
m_xPosRole = roleHash.key(m_proxy->xPosRole().toLatin1(), noRoleIndex);
m_yPosRole = roleHash.key(m_proxy->yPosRole().toLatin1(), noRoleIndex);
diff --git a/src/datavisualization/data/scatteritemmodelhandler_p.h b/src/datavisualization/data/scatteritemmodelhandler_p.h
index 0661d734..817b245d 100644
--- a/src/datavisualization/data/scatteritemmodelhandler_p.h
+++ b/src/datavisualization/data/scatteritemmodelhandler_p.h
@@ -59,6 +59,18 @@ private:
int m_yPosRole;
int m_zPosRole;
int m_rotationRole;
+ QRegExp m_xPosPattern;
+ QRegExp m_yPosPattern;
+ QRegExp m_zPosPattern;
+ QRegExp m_rotationPattern;
+ QString m_xPosReplace;
+ QString m_yPosReplace;
+ QString m_zPosReplace;
+ QString m_rotationReplace;
+ bool m_haveXPosPattern;
+ bool m_haveYPosPattern;
+ bool m_haveZPosPattern;
+ bool m_haveRotationPattern;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/scatterrenderitem.cpp b/src/datavisualization/data/scatterrenderitem.cpp
index 3b2e64c5..0b5398f1 100644
--- a/src/datavisualization/data/scatterrenderitem.cpp
+++ b/src/datavisualization/data/scatterrenderitem.cpp
@@ -17,8 +17,6 @@
****************************************************************************/
#include "scatterrenderitem_p.h"
-#include "scatter3drenderer_p.h"
-#include "qscatterdataproxy.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -29,10 +27,10 @@ ScatterRenderItem::ScatterRenderItem()
}
ScatterRenderItem::ScatterRenderItem(const ScatterRenderItem &other)
- : AbstractRenderItem(other),
- m_visible(false)
+ : AbstractRenderItem(other)
{
m_position = other.m_position;
+ m_visible = other.m_visible;
}
ScatterRenderItem::~ScatterRenderItem()
diff --git a/src/datavisualization/data/scatterrenderitem_p.h b/src/datavisualization/data/scatterrenderitem_p.h
index eb070682..d754a8ca 100644
--- a/src/datavisualization/data/scatterrenderitem_p.h
+++ b/src/datavisualization/data/scatterrenderitem_p.h
@@ -33,8 +33,6 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-class Scatter3DRenderer;
-
class ScatterRenderItem : public AbstractRenderItem
{
public:
@@ -55,8 +53,6 @@ public:
protected:
QVector3D m_position;
bool m_visible;
-
- friend class QScatterDataItem;
};
typedef QVector<ScatterRenderItem> ScatterRenderItemArray;
diff --git a/src/datavisualization/data/surfaceitemmodelhandler.cpp b/src/datavisualization/data/surfaceitemmodelhandler.cpp
index f4383dbf..e6d1e70d 100644
--- a/src/datavisualization/data/surfaceitemmodelhandler.cpp
+++ b/src/datavisualization/data/surfaceitemmodelhandler.cpp
@@ -25,7 +25,13 @@ static const int noRoleIndex = -1;
SurfaceItemModelHandler::SurfaceItemModelHandler(QItemModelSurfaceDataProxy *proxy, QObject *parent)
: AbstractItemModelHandler(parent),
m_proxy(proxy),
- m_proxyArray(0)
+ m_proxyArray(0),
+ m_xPosRole(noRoleIndex),
+ m_yPosRole(noRoleIndex),
+ m_zPosRole(noRoleIndex),
+ m_haveXPosPattern(false),
+ m_haveYPosPattern(false),
+ m_haveZPosPattern(false)
{
}
@@ -33,6 +39,62 @@ SurfaceItemModelHandler::~SurfaceItemModelHandler()
{
}
+void SurfaceItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight,
+ const QVector<int> &roles)
+{
+ // Do nothing if full reset already pending
+ if (!m_fullReset) {
+ if (!m_proxy->useModelCategories()) {
+ // If the data model doesn't directly map rows and columns, we cannot optimize
+ AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
+ } else {
+ int startRow = qMin(topLeft.row(), bottomRight.row());
+ int endRow = qMax(topLeft.row(), bottomRight.row());
+ int startCol = qMin(topLeft.column(), bottomRight.column());
+ int endCol = qMax(topLeft.column(), bottomRight.column());
+
+ for (int i = startRow; i <= endRow; i++) {
+ for (int j = startCol; j <= endCol; j++) {
+ QModelIndex index = m_itemModel->index(i, j);
+ QSurfaceDataItem item;
+ QVariant xValueVar = index.data(m_xPosRole);
+ QVariant yValueVar = index.data(m_yPosRole);
+ QVariant zValueVar = index.data(m_zPosRole);
+ const QSurfaceDataItem *oldItem = m_proxy->itemAt(i, j);
+ float xPos;
+ float yPos;
+ float zPos;
+ if (m_xPosRole != noRoleIndex) {
+ if (m_haveXPosPattern)
+ xPos = xValueVar.toString().replace(m_xPosPattern, m_xPosReplace).toFloat();
+ else
+ xPos = xValueVar.toFloat();
+ } else {
+ xPos = oldItem->x();
+ }
+
+ if (m_haveYPosPattern)
+ yPos = yValueVar.toString().replace(m_yPosPattern, m_yPosReplace).toFloat();
+ else
+ yPos = yValueVar.toFloat();
+
+ if (m_zPosRole != noRoleIndex) {
+ if (m_haveZPosPattern)
+ zPos = zValueVar.toString().replace(m_zPosPattern, m_zPosReplace).toFloat();
+ else
+ zPos = zValueVar.toFloat();
+ } else {
+ zPos = oldItem->z();
+ }
+ item.setPosition(QVector3D(xPos, yPos, zPos));
+ m_proxy->setItem(i, j, item);
+ }
+ }
+ }
+ }
+}
+
// Resolve entire item model into QSurfaceDataArray.
void SurfaceItemModelHandler::resolveModel()
{
@@ -49,12 +111,29 @@ void SurfaceItemModelHandler::resolveModel()
return;
}
+ // Position patterns can be reused on single item changes, so store them to member variables.
+ QRegExp rowPattern(m_proxy->rowRolePattern());
+ QRegExp colPattern(m_proxy->columnRolePattern());
+ m_xPosPattern = m_proxy->xPosRolePattern();
+ m_yPosPattern = m_proxy->yPosRolePattern();
+ m_zPosPattern = m_proxy->zPosRolePattern();
+ QString rowReplace = m_proxy->rowRoleReplace();
+ QString colReplace = m_proxy->columnRoleReplace();
+ m_xPosReplace = m_proxy->xPosRoleReplace();
+ m_yPosReplace = m_proxy->yPosRoleReplace();
+ m_zPosReplace = m_proxy->zPosRoleReplace();
+ bool haveRowPattern = !rowPattern.isEmpty() && rowPattern.isValid();
+ bool haveColPattern = !colPattern.isEmpty() && colPattern.isValid();
+ m_haveXPosPattern = !m_xPosPattern.isEmpty() && m_xPosPattern.isValid();
+ m_haveYPosPattern = !m_yPosPattern.isEmpty() && m_yPosPattern.isValid();
+ m_haveZPosPattern = !m_zPosPattern.isEmpty() && m_zPosPattern.isValid();
+
QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
// Default to display role if no mapping
- int xPosRole = roleHash.key(m_proxy->xPosRole().toLatin1(), noRoleIndex);
- int yPosRole = roleHash.key(m_proxy->yPosRole().toLatin1(), Qt::DisplayRole);
- int zPosRole = roleHash.key(m_proxy->zPosRole().toLatin1(), noRoleIndex);
+ m_xPosRole = roleHash.key(m_proxy->xPosRole().toLatin1(), noRoleIndex);
+ m_yPosRole = roleHash.key(m_proxy->yPosRole().toLatin1(), Qt::DisplayRole);
+ m_zPosRole = roleHash.key(m_proxy->zPosRole().toLatin1(), noRoleIndex);
int rowCount = m_itemModel->rowCount();
int columnCount = m_itemModel->columnCount();
@@ -70,41 +149,58 @@ void SurfaceItemModelHandler::resolveModel()
for (int i = 0; i < rowCount; i++) {
QSurfaceDataRow &newProxyRow = *m_proxyArray->at(i);
for (int j = 0; j < columnCount; j++) {
- float xPos = j;
- float zPos = i;
- if (xPosRole != noRoleIndex) {
- xPos = m_itemModel->index(i, j).data(xPosRole).toFloat();
+ QModelIndex index = m_itemModel->index(i, j);
+ QVariant xValueVar = index.data(m_xPosRole);
+ QVariant yValueVar = index.data(m_yPosRole);
+ QVariant zValueVar = index.data(m_zPosRole);
+ float xPos;
+ float yPos;
+ float zPos;
+ if (m_xPosRole != noRoleIndex) {
+ if (m_haveXPosPattern)
+ xPos = xValueVar.toString().replace(m_xPosPattern, m_xPosReplace).toFloat();
+ else
+ xPos = xValueVar.toFloat();
} else {
QString header = m_itemModel->headerData(j, Qt::Horizontal).toString();
bool ok = false;
float headerValue = header.toFloat(&ok);
if (ok)
xPos = headerValue;
+ else
+ xPos = float(j);
}
- if (zPosRole != noRoleIndex) {
- zPos = m_itemModel->index(i, j).data(zPosRole).toFloat();
+ if (m_haveYPosPattern)
+ yPos = yValueVar.toString().replace(m_yPosPattern, m_yPosReplace).toFloat();
+ else
+ yPos = yValueVar.toFloat();
+
+ if (m_zPosRole != noRoleIndex) {
+ if (m_haveZPosPattern)
+ zPos = zValueVar.toString().replace(m_zPosPattern, m_zPosReplace).toFloat();
+ else
+ zPos = zValueVar.toFloat();
} else {
QString header = m_itemModel->headerData(i, Qt::Vertical).toString();
bool ok = false;
float headerValue = header.toFloat(&ok);
if (ok)
zPos = headerValue;
+ else
+ zPos = float(i);
}
- newProxyRow[j].setPosition(
- QVector3D(xPos,
- m_itemModel->index(i, j).data(yPosRole).toFloat(),
- zPos));
+ newProxyRow[j].setPosition(QVector3D(xPos, yPos, zPos));
}
}
} else {
int rowRole = roleHash.key(m_proxy->rowRole().toLatin1());
int columnRole = roleHash.key(m_proxy->columnRole().toLatin1());
- if (xPosRole == noRoleIndex)
- xPosRole = columnRole;
- if (zPosRole == noRoleIndex)
- zPosRole = rowRole;
+ if (m_xPosRole == noRoleIndex)
+ m_xPosRole = columnRole;
+ if (m_zPosRole == noRoleIndex)
+ m_zPosRole = rowRole;
bool generateRows = m_proxy->autoRowCategories();
bool generateColumns = m_proxy->autoColumnCategories();
@@ -116,6 +212,14 @@ void SurfaceItemModelHandler::resolveModel()
QHash<QString, bool> rowListHash;
QHash<QString, bool> columnListHash;
+ bool cumulative = m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBAverage
+ || m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBCumulativeY;
+ bool average = m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBAverage;
+ bool takeFirst = m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBFirst;
+ QHash<QString, QHash<QString, int> > *matchCountMap = 0;
+ if (cumulative)
+ matchCountMap = new QHash<QString, QHash<QString, int> >;
+
// Sort values into rows and columns
typedef QHash<QString, QVector3D> ColumnValueMap;
QHash <QString, ColumnValueMap> itemValueMap;
@@ -123,11 +227,45 @@ void SurfaceItemModelHandler::resolveModel()
for (int j = 0; j < columnCount; j++) {
QModelIndex index = m_itemModel->index(i, j);
QString rowRoleStr = index.data(rowRole).toString();
+ if (haveRowPattern)
+ rowRoleStr.replace(rowPattern, rowReplace);
QString columnRoleStr = index.data(columnRole).toString();
- QVector3D itemPos(index.data(xPosRole).toReal(),
- index.data(yPosRole).toReal(),
- index.data(zPosRole).toReal());
- itemValueMap[rowRoleStr][columnRoleStr] = itemPos;
+ if (haveColPattern)
+ columnRoleStr.replace(colPattern, colReplace);
+ QVariant xValueVar = index.data(m_xPosRole);
+ QVariant yValueVar = index.data(m_yPosRole);
+ QVariant zValueVar = index.data(m_zPosRole);
+ float xPos;
+ float yPos;
+ float zPos;
+ if (m_haveXPosPattern)
+ xPos = xValueVar.toString().replace(m_xPosPattern, m_xPosReplace).toFloat();
+ else
+ xPos = xValueVar.toFloat();
+ if (m_haveYPosPattern)
+ yPos = yValueVar.toString().replace(m_yPosPattern, m_yPosReplace).toFloat();
+ else
+ yPos = yValueVar.toFloat();
+ if (m_haveZPosPattern)
+ zPos = zValueVar.toString().replace(m_zPosPattern, m_zPosReplace).toFloat();
+ else
+ zPos = zValueVar.toFloat();
+
+ QVector3D itemPos(xPos, yPos, zPos);
+
+ if (cumulative)
+ (*matchCountMap)[rowRoleStr][columnRoleStr]++;
+
+ if (cumulative) {
+ itemValueMap[rowRoleStr][columnRoleStr] += itemPos;
+ } else {
+ if (takeFirst && itemValueMap.contains(rowRoleStr)) {
+ if (itemValueMap.value(rowRoleStr).contains(columnRoleStr))
+ continue; // We already have a value for this row/column combo
+ }
+ itemValueMap[rowRoleStr][columnRoleStr] = itemPos;
+ }
+
if (generateRows && !rowListHash.value(rowRoleStr, false)) {
rowListHash.insert(rowRoleStr, true);
rowList << rowRoleStr;
@@ -161,9 +299,22 @@ void SurfaceItemModelHandler::resolveModel()
for (int i = 0; i < rowList.size(); i++) {
QString rowKey = rowList.at(i);
QSurfaceDataRow &newProxyRow = *m_proxyArray->at(i);
- for (int j = 0; j < columnList.size(); j++)
- newProxyRow[j].setPosition(itemValueMap[rowKey][columnList.at(j)]);
+ for (int j = 0; j < columnList.size(); j++) {
+ QVector3D &itemPos = itemValueMap[rowKey][columnList.at(j)];
+ if (cumulative) {
+ if (average) {
+ itemPos /= float((*matchCountMap)[rowKey][columnList.at(j)]);
+ } else { // cumulativeY
+ float divisor = float((*matchCountMap)[rowKey][columnList.at(j)]);
+ itemPos.setX(itemPos.x() / divisor);
+ itemPos.setZ(itemPos.z() / divisor);
+ }
+ }
+ newProxyRow[j].setPosition(itemPos);
+ }
}
+
+ delete matchCountMap;
}
m_proxy->resetArray(m_proxyArray);
diff --git a/src/datavisualization/data/surfaceitemmodelhandler_p.h b/src/datavisualization/data/surfaceitemmodelhandler_p.h
index ae426433..572788d6 100644
--- a/src/datavisualization/data/surfaceitemmodelhandler_p.h
+++ b/src/datavisualization/data/surfaceitemmodelhandler_p.h
@@ -41,11 +41,27 @@ public:
SurfaceItemModelHandler(QItemModelSurfaceDataProxy *proxy, QObject *parent = 0);
virtual ~SurfaceItemModelHandler();
+public slots:
+ virtual void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QVector<int> &roles = QVector<int> ());
+
protected:
void virtual resolveModel();
QItemModelSurfaceDataProxy *m_proxy; // Not owned
QSurfaceDataArray *m_proxyArray; // Not owned
+ int m_xPosRole;
+ int m_yPosRole;
+ int m_zPosRole;
+ QRegExp m_xPosPattern;
+ QRegExp m_yPosPattern;
+ QRegExp m_zPosPattern;
+ QString m_xPosReplace;
+ QString m_yPosReplace;
+ QString m_zPosReplace;
+ bool m_haveXPosPattern;
+ bool m_haveYPosPattern;
+ bool m_haveZPosPattern;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/doc/qtdatavisualization.qdocconf b/src/datavisualization/doc/qtdatavisualization.qdocconf
index 5c871b5a..6e9f09b4 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.0.0
+version = 1.1.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.100
+qhp.QtDataVisualization.namespace = com.digia.qtdatavisualization.110
qhp.QtDataVisualization.virtualFolder = qtdatavisualization
qhp.QtDataVisualization.indexTitle = Qt Data Visualization
qhp.QtDataVisualization.indexRoot =
-qhp.QtDataVisualization.filterAttributes = qtdatavisualization 1.0.0 qtrefdoc
-qhp.QtDataVisualization.customFilters.Qt.name = QtDataVisualization 1.0.0
-qhp.QtDataVisualization.customFilters.Qt.filterAttributes = qtdatavisualization 1.0.0
+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.subprojects = gettingstarted examples classes types
qhp.QtDataVisualization.subprojects.gettingstarted.title = Getting Started
qhp.QtDataVisualization.subprojects.gettingstarted.indexTitle = Qt Data Visualization Getting Started
diff --git a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp
index 0c54c2fe..56bfc5ee 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.0
+import QtDataVisualization 1.1
//! [0]
//! [1]
import QtQuick 2.0
-import QtDataVisualization 1.0
+import QtDataVisualization 1.1
Item {
width: 640
@@ -61,7 +61,7 @@ Item {
//! [2]
import QtQuick 2.0
-import QtDataVisualization 1.0
+import QtDataVisualization 1.1
Item {
width: 640
@@ -94,7 +94,7 @@ Item {
//! [3]
import QtQuick 2.0
-import QtDataVisualization 1.0
+import QtDataVisualization 1.1
Item {
width: 640
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
index 891cc5c1..ba3173b0 100644
--- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
@@ -28,8 +28,9 @@
Note that this type is uncreatable, but contains properties that are shared between
the 3D visualizations.
- For AbstractGraph3D enums, see \l QAbstract3DGraph::SelectionFlag and
- \l QAbstract3DGraph::ShadowQuality
+ For AbstractGraph3D enums, see \l{QAbstract3DGraph::SelectionFlag},
+ \l{QAbstract3DGraph::ShadowQuality}, \l{QAbstract3DGraph::ElementType}, and
+ \l{QAbstract3DGraph::OptimizationHint}.
\sa Bars3D, Scatter3D, Surface3D, {Qt Data Visualization C++ Classes}
*/
@@ -57,7 +58,7 @@
/*!
\qmlproperty AbstractInputHandler3D AbstractGraph3D::inputHandler
- Input handler. You can disable default input handlers by setting this property to \c null.
+ Input handler. You can disable default input handlers by setting this property to \c {null}.
*/
/*!
@@ -119,13 +120,184 @@
When renderingMode is \c RenderDirectToBackground or \c RenderDirectToBackground_NoClear, this
property value is read-only and returns the number of samples specified by the window surface
format.
- Defaults to 4.
+ Defaults to \c{4}.
\sa renderingMode
*/
/*!
+ * \qmlproperty bool AbstractGraph3D::measureFps
+ * \since QtDataVisualization 1.1
+ *
+ * If \c {true}, the rendering is done continuously instead of on demand, and currentFps property
+ * is updated. Defaults to \c{false}.
+ *
+ * \sa currentFps
+ */
+
+/*!
+ * \qmlproperty int AbstractGraph3D::currentFps
+ * \since QtDataVisualization 1.1
+ *
+ * When fps measuring is enabled, the results for the last second are stored in this read-only
+ * property. It takes at least a second before this value updates after measurement is activated.
+ *
+ * \sa measureFps
+ */
+
+/*!
* \qmlmethod void AbstractGraph3D::clearSelection()
* Clears selection from all attached series.
*/
+/*!
+ * \qmlmethod int AbstractGraph3D::addCustomItem(Custom3DItem item)
+ *
+ * Adds a Custom3DItem \a item to the graph. Graph takes ownership of the added item.
+ *
+ * \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()
+ *
+ * \since QtDataVisualization 1.1
+ */
+
+/*!
+ * \qmlmethod void AbstractGraph3D::removeCustomItems()
+ *
+ * Removes all custom items. Deletes the resources allocated to them.
+ *
+ * \since QtDataVisualization 1.1
+ */
+
+/*!
+ * \qmlmethod void AbstractGraph3D::removeCustomItem(Custom3DItem item)
+ *
+ * Removes the custom \a {item}. Deletes the resources allocated to it.
+ *
+ * \since QtDataVisualization 1.1
+ */
+
+/*!
+ * \qmlmethod void AbstractGraph3D::removeCustomItemAt(vector3d position)
+ *
+ * Removes all custom items at \a {position}. Deletes the resources allocated to them.
+ *
+ * \since QtDataVisualization 1.1
+ */
+
+/*!
+ * \qmlmethod void AbstractGraph3D::releaseCustomItem(Custom3DItem item)
+ *
+ * Gets ownership of \a item back and removes the \a item from the graph.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \note If the same item is added back to the graph, the texture file needs to be re-set.
+ *
+ * \sa Custom3DItem::textureFile
+ */
+
+/*!
+ * \qmlmethod int AbstractGraph3D::selectedLabelIndex()
+ *
+ * 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.
+ *
+ * \return index of the selected label, or -1.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+
+/*!
+ * \qmlmethod Abstract3DAxis AbstractGraph3D::selectedAxis()
+ *
+ * Can be used to get the selected axis after receiving \c selectedElementChanged signal with any label
+ * type. Selection is valid until the next \c selectedElementChanged signal.
+ *
+ * \return the selected axis, or null.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+
+/*!
+ * \qmlmethod int AbstractGraph3D::selectedCustomItemIndex()
+ *
+ * Can be used to query the index of the selected custom item after receiving \c selectedElementChanged
+ * signal with \l{QAbstract3DGraph::ElementCustomItem}{ElementCustomItem} type. Selection is valid
+ * until the next \c selectedElementChanged signal.
+ *
+ * \return index of the selected custom item, or -1.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+
+/*!
+ * \qmlmethod Custom3DItem AbstractGraph3D::selectedCustomItem()
+ *
+ * Can be used to get the selected custom item after receiving \c selectedElementChanged signal with
+ * \l{QAbstract3DGraph::ElementCustomItem}{ElementCustomItem} type. Ownership of the item remains
+ * with the graph. Selection is valid until the next \c selectedElementChanged signal.
+ *
+ * \return the selected custom item, or null.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+
+/*!
+ * \qmlproperty AbstractGraph3D.ElementType AbstractGraph3D::selectedElement
+ *
+ * Can be used to query the selected element type.
+ * Type is valid until the next \c selectedElementChanged signal.
+ *
+ * \c selectedElementChanged signal is emitted when a selection is made in the graph.
+ *
+ * Signal can be used for example for implementing customized input handling, as demonstrated in
+ * this \l {Qt Quick 2 Axis Dragging Example}{example}.
+ *
+ * \sa selectedLabelIndex(), selectedAxis(), selectedCustomItemIndex(), selectedCustomItem(),
+ * Bars3D::selectedSeries, Scatter3D::selectedSeries, Scene3D::selectionQueryPosition
+ *
+ * \since QtDataVisualization 1.1
+ */
+
+/*!
+ * \qmlproperty bool AbstractGraph3D::orthoProjection
+ * \since QtDataVisualization 1.1
+ *
+ * If \c {true}, orthographic projection will be used for displaying the graph. Defaults to \c{false}.
+ * \note Shadows will be disabled when set to \c{true}.
+ */
+
+/*!
+ * \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}.
+ *
+ * \note Has no effect on Bars3D.
+ */
+
+/*!
+ * \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}
+ */
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-color.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-color.qdoc
new file mode 100644
index 00000000..e845adea
--- /dev/null
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-color.qdoc
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** 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
+**
+****************************************************************************/
+
+/*!
+ \qmltype ThemeColor
+ \inqmlmodule QtDataVisualization
+ \since QtDataVisualization 1.0
+ \ingroup datavisualization_qml
+ \brief Defines the color in a Theme3D.
+
+ Defines a color in Theme3D::baseColors property of Theme3D.
+*/
+
+/*!
+ \qmlproperty color ThemeColor::color
+
+ The color property describes the color of this ThemeColor.
+
+ The default color is black.
+*/
diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc
index 836823e6..af419814 100644
--- a/src/datavisualization/doc/src/qtdatavisualization.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc
@@ -37,7 +37,7 @@
*/
/*!
- \qmlmodule QtDataVisualization 1.0
+ \qmlmodule QtDataVisualization 1.1
\title Qt Data Visualization QML Types
\ingroup qmlmodules
@@ -73,8 +73,8 @@
in the package manager.
After installation Qt Data Visualization documentation and examples are available in Qt Creator.
- You can find all Qt Data Visualization examples by typing \c visualization in the
- \c {Search in Examples...} field.
+ Examples can be found on the examples page of Qt Creator by selecting the Qt Data Visualization
+ component from the drop-down menu.
The source code is installed into the QtDataVisualization folder under EnterpriseAddOns.
@@ -238,9 +238,18 @@
and which role specifies the value of the item. When the proxy resolves the data from the model,
it uses these mappings to generate the rows and columns of the bar graph.
+ Often the item models will have a single role that contains information you want to map to
+ multiple values. A typical example of this is a timestamp field when generating a bar graph
+ with two time related axes, for example years and months. To enable mapping a single item
+ model role to more than one data field, pattern matching and replacing mechanism is provided
+ by item model proxies. You can also use this mechanism to reformat data even in one-to-one
+ mapping cases.
+
Depending on the visualization type, proxies may support other functionalities as well,
such as QItemModelBarDataProxy optionally mapping QAbstractItemModel rows and columns directly
- into bar graph rows and columns. See individual proxy classes for more information and examples
+ into bar graph rows and columns.
+
+ See individual proxy classes for more information and examples
about how to use them: QItemModelBarDataProxy, QItemModelScatterDataProxy, and
QItemModelSurfaceDataProxy.
@@ -257,28 +266,27 @@
When you have a data set that updates rapidly, it is important to handle data properly to
ensure good performance. Since memory allocation is a costly operation, always use
- QList::reserve() and QVector::resize() where possible to avoid reallocations when constructing
- the array to give to the proxy. If you need to change the entire data set for each frame,
- it is in most cases best to reuse the existing array - especially if the array dimensions do not
- change. If you need to add, insert, remove, or change several rows or items for each frame, it
- is always more efficient to do it with one method call instead of multiple calls affecting
- a single row or item each. For example, adding ten rows with a single QBarDataProxy::addRows() call
- is much more efficient than ten separate QBarDataProxy::addRow() calls.
-
- Bars renderer is optimized to access only data that is within the data window and thus should not
- suffer noticeable slowdown even if more data is continually added to the proxy.
+ QList::reserve() and QVector::resize() where possible to avoid unnecessary reallocations when
+ constructing the array to give to the proxy. If you need to change the entire data set
+ for each frame, it is in most cases best to reuse the existing array - especially if the
+ array dimensions do not change. If you need to add, insert, remove, or change several
+ rows or items for each frame, it is always more efficient to do it with one method call
+ instead of multiple calls affecting a single row or item each. For example, adding ten
+ rows with a single QBarDataProxy::addRows() call is much more efficient than ten
+ separate QBarDataProxy::addRow() calls.
+
+ Bars renderer is optimized to access only data that is within the data window and thus
+ should not suffer noticeable slowdown even if more data is continually added to the proxy.
Due to the unsorted nature of the scatter data, any change in the data window ranges requires
all data points to be checked for visibility, which can cause increasing slowdown if data is
- continually added to the proxy.
+ continually added to the proxy. For the best performance with the scatter graphs, only keep
+ the data you need in the proxy.
Surface data, while on item level similar to scatter data, is already assigned into rows and
- columns, so the surface renderer can do some optimization by making the assumption that the data in
- rows and columns is sorted along their respective axes, but it is nowhere near as efficient
- as in the bars case. Surface rendering can suffer significant slowdown if the data size grows unchecked.
-
- For the best performance with the scatter and surface graphs, only keep the data you need in the
- proxy.
+ columns, so the surface renderer can optimize drawing by making the assumption that
+ the data in the rows and columns is sorted along their respective axes. It is not quite as
+ efficient as in the bars case, but nearly so.
*/
/*!
@@ -303,7 +311,8 @@
a single row or column.
Bar graph additionally supports simply highlighting the whole row and/or column of the selected bar
- without opening the slice view.
+ without opening the slice view. Bar graph also supports selecting/slicing a whole row and/or
+ column by clicking the axis label, based on selection mode.
When multiple series are added to a graph, selecting an item in one of them will clear the selection
on other series.
@@ -322,10 +331,12 @@
\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 Scatter "point" meshes (QAbstract3DSeries::MeshPoint) do not support gradients, they
- always use the base color.
\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.
\endlist
*/
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp
index ec18a850..8658d463 100644
--- a/src/datavisualization/engine/abstract3dcontroller.cpp
+++ b/src/datavisualization/engine/abstract3dcontroller.cpp
@@ -17,22 +17,16 @@
****************************************************************************/
#include "abstract3dcontroller_p.h"
-#include "camerahelper_p.h"
#include "qabstract3daxis_p.h"
-#include "qvalue3daxis.h"
-#include "qcategory3daxis.h"
+#include "qvalue3daxis_p.h"
#include "abstract3drenderer_p.h"
-#include "q3dcamera.h"
-#include "q3dlight.h"
-#include "qabstractdataproxy_p.h"
#include "qabstract3dinputhandler_p.h"
#include "qtouch3dinputhandler.h"
-#include "qabstract3dseries_p.h"
#include "thememanager_p.h"
#include "q3dtheme_p.h"
-#include "q3dscene_p.h"
-#include "q3dscene.h"
+#include "qcustom3ditem_p.h"
#include <QtCore/QThread>
+#include <QtGui/QOpenGLFramebufferObject>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -42,6 +36,9 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
m_themeManager(new ThemeManager(this)),
m_selectionMode(QAbstract3DGraph::SelectionItem),
m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium),
+ m_useOrthoProjection(false),
+ m_aspectRatio(2.0f),
+ m_optimizationHints(QAbstract3DGraph::OptimizationDefault),
m_scene(scene),
m_activeInputHandler(0),
m_axisX(0),
@@ -49,9 +46,13 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
m_axisZ(0),
m_renderer(0),
m_isDataDirty(true),
- m_isSeriesVisibilityDirty(true),
+ m_isCustomDataDirty(true),
+ m_isCustomItemDirty(true),
m_isSeriesVisualsDirty(true),
- m_renderPending(false)
+ m_renderPending(false),
+ m_measureFps(false),
+ m_numFrames(0),
+ m_currentFps(0.0)
{
if (!m_scene)
m_scene = new Q3DScene;
@@ -84,6 +85,9 @@ Abstract3DController::~Abstract3DController()
destroyRenderer();
delete m_scene;
delete m_themeManager;
+ foreach (QCustom3DItem *item, m_customItems)
+ delete item;
+ m_customItems.clear();
}
void Abstract3DController::destroyRenderer()
@@ -142,7 +146,7 @@ void Abstract3DController::removeSeries(QAbstract3DSeries *series)
this, &Abstract3DController::handleSeriesVisibilityChanged);
series->d_ptr->setController(0);
m_isDataDirty = true;
- m_isSeriesVisibilityDirty = true;
+ m_isSeriesVisualsDirty = true;
emitNeedRender();
}
}
@@ -182,6 +186,46 @@ void Abstract3DController::synchDataToRenderer()
m_changeTracker.selectionModeChanged = false;
}
+ if (m_changeTracker.projectionChanged) {
+ m_renderer->m_useOrthoProjection = m_useOrthoProjection;
+ m_changeTracker.projectionChanged = false;
+ }
+
+ if (m_changeTracker.aspectRatioChanged) {
+ m_renderer->updateAspectRatio(m_aspectRatio);
+ m_changeTracker.aspectRatioChanged = false;
+ }
+
+ if (m_changeTracker.optimizationHintChanged) {
+ m_renderer->updateOptimizationHint(m_optimizationHints);
+ m_changeTracker.optimizationHintChanged = false;
+ }
+
+ if (m_changeTracker.axisXFormatterChanged) {
+ m_changeTracker.axisXFormatterChanged = false;
+ if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
+ m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationX,
+ valueAxisX->formatter());
+ }
+ }
+ if (m_changeTracker.axisYFormatterChanged) {
+ m_changeTracker.axisYFormatterChanged = false;
+ if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
+ m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationY,
+ valueAxisY->formatter());
+ }
+ }
+ if (m_changeTracker.axisZFormatterChanged) {
+ m_changeTracker.axisZFormatterChanged = false;
+ if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
+ m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationZ,
+ valueAxisZ->formatter());
+ }
+ }
+
if (m_changeTracker.axisXTypeChanged) {
m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationX, m_axisX->type());
m_changeTracker.axisXTypeChanged = false;
@@ -325,9 +369,89 @@ void Abstract3DController::synchDataToRenderer()
}
}
- if (m_isSeriesVisibilityDirty || m_isSeriesVisualsDirty) {
- m_renderer->updateSeries(m_seriesList, m_isSeriesVisibilityDirty);
- m_isSeriesVisibilityDirty = false;
+ if (m_changeTracker.axisXReversedChanged) {
+ m_changeTracker.axisXReversedChanged = false;
+ if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
+ m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationX,
+ valueAxisX->reversed());
+ }
+ }
+
+ if (m_changeTracker.axisYReversedChanged) {
+ m_changeTracker.axisYReversedChanged = false;
+ if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
+ m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationY,
+ valueAxisY->reversed());
+ }
+ }
+
+ if (m_changeTracker.axisZReversedChanged) {
+ m_changeTracker.axisZReversedChanged = false;
+ if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
+ m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationZ,
+ valueAxisZ->reversed());
+ }
+ }
+
+ if (m_changeTracker.axisXLabelAutoRotationChanged) {
+ m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationX,
+ m_axisX->labelAutoRotation());
+ m_changeTracker.axisXLabelAutoRotationChanged = false;
+ }
+
+ if (m_changeTracker.axisYLabelAutoRotationChanged) {
+ m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationY,
+ m_axisY->labelAutoRotation());
+ m_changeTracker.axisYLabelAutoRotationChanged = false;
+ }
+
+ if (m_changeTracker.axisZLabelAutoRotationChanged) {
+ m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationZ,
+ m_axisZ->labelAutoRotation());
+ m_changeTracker.axisZLabelAutoRotationChanged = false;
+ }
+
+ if (m_changeTracker.axisXTitleVisibilityChanged) {
+ m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationX,
+ m_axisX->isTitleVisible());
+ m_changeTracker.axisXTitleVisibilityChanged = false;
+ }
+ if (m_changeTracker.axisYTitleVisibilityChanged) {
+ m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationY,
+ m_axisY->isTitleVisible());
+ m_changeTracker.axisYTitleVisibilityChanged = false;
+ }
+ if (m_changeTracker.axisZTitleVisibilityChanged) {
+ m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationZ,
+ m_axisZ->isTitleVisible());
+ m_changeTracker.axisZTitleVisibilityChanged = false;
+ }
+ if (m_changeTracker.axisXTitleFixedChanged) {
+ m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationX,
+ m_axisX->isTitleFixed());
+ m_changeTracker.axisXTitleFixedChanged = false;
+ }
+ if (m_changeTracker.axisYTitleFixedChanged) {
+ m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationY,
+ m_axisY->isTitleFixed());
+ m_changeTracker.axisYTitleFixedChanged = false;
+ }
+ if (m_changeTracker.axisZTitleFixedChanged) {
+ m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationZ,
+ m_axisZ->isTitleFixed());
+ m_changeTracker.axisZTitleFixedChanged = false;
+ }
+
+ if (m_changedSeriesList.size()) {
+ m_renderer->modifiedSeriesList(m_changedSeriesList);
+ m_changedSeriesList.clear();
+ }
+
+ if (m_isSeriesVisualsDirty) {
+ m_renderer->updateSeries(m_seriesList);
m_isSeriesVisualsDirty = false;
}
@@ -337,6 +461,16 @@ void Abstract3DController::synchDataToRenderer()
m_renderer->updateData();
m_isDataDirty = false;
}
+
+ if (m_isCustomDataDirty) {
+ m_renderer->updateCustomData(m_customItems);
+ m_isCustomDataDirty = false;
+ }
+
+ if (m_isCustomItemDirty) {
+ m_renderer->updateCustomItems();
+ m_isCustomItemDirty = false;
+ }
}
void Abstract3DController::render(const GLuint defaultFboHandle)
@@ -347,12 +481,21 @@ void Abstract3DController::render(const GLuint defaultFboHandle)
if (!m_renderer)
return;
- m_renderer->render(defaultFboHandle);
+ if (m_measureFps) {
+ // Measure speed (as milliseconds per frame)
+ m_numFrames++;
+ int elapsed = m_frameTimer.elapsed();
+ if (elapsed >= 1000) {
+ m_currentFps = qreal(m_numFrames) * 1000.0 / qreal(elapsed);
+ emit currentFpsChanged(m_currentFps);
+ m_numFrames = 0;
+ m_frameTimer.restart();
+ }
+ // To get meaningful framerate, don't just do render on demand.
+ emitNeedRender();
+ }
-#ifdef DISPLAY_RENDER_SPEED
- // To get meaningful framerate, don't just do render on demand.
- emitNeedRender();
-#endif
+ m_renderer->render(defaultFboHandle);
}
void Abstract3DController::mouseDoubleClickEvent(QMouseEvent *event)
@@ -445,7 +588,8 @@ void Abstract3DController::handleThemeSingleHighlightColorChanged(const QColor &
markSeriesVisualsDirty();
}
-void Abstract3DController::handleThemeSingleHighlightGradientChanged(const QLinearGradient &gradient)
+void Abstract3DController::handleThemeSingleHighlightGradientChanged(
+ const QLinearGradient &gradient)
{
// Set value for series that have not explicitly set this value
foreach (QAbstract3DSeries *series, m_seriesList) {
@@ -503,7 +647,7 @@ void Abstract3DController::setAxisX(QAbstract3DAxis *axis)
}
}
-QAbstract3DAxis *Abstract3DController::axisX()
+QAbstract3DAxis *Abstract3DController::axisX() const
{
return m_axisX;
}
@@ -517,7 +661,7 @@ void Abstract3DController::setAxisY(QAbstract3DAxis *axis)
}
}
-QAbstract3DAxis *Abstract3DController::axisY()
+QAbstract3DAxis *Abstract3DController::axisY() const
{
return m_axisY;
}
@@ -531,7 +675,7 @@ void Abstract3DController::setAxisZ(QAbstract3DAxis *axis)
}
}
-QAbstract3DAxis *Abstract3DController::axisZ()
+QAbstract3DAxis *Abstract3DController::axisZ() const
{
return m_axisZ;
}
@@ -648,19 +792,6 @@ QList<QAbstract3DInputHandler *> Abstract3DController::inputHandlers() const
return m_inputHandlers;
}
-int Abstract3DController::zoomLevel()
-{
- return m_scene->activeCamera()->zoomLevel();
-}
-
-void Abstract3DController::setZoomLevel(int zoomLevel)
-{
- m_scene->activeCamera()->setZoomLevel(zoomLevel);
-
- m_changeTracker.zoomLevelChanged = true;
- emitNeedRender();
-}
-
void Abstract3DController::addTheme(Q3DTheme *theme)
{
m_themeManager->addTheme(theme);
@@ -675,12 +806,13 @@ void Abstract3DController::releaseTheme(Q3DTheme *theme)
if (oldTheme != m_themeManager->activeTheme())
emit activeThemeChanged(m_themeManager->activeTheme());
}
+
QList<Q3DTheme *> Abstract3DController::themes() const
{
return m_themeManager->themes();
}
-void Abstract3DController::setActiveTheme(Q3DTheme *theme)
+void Abstract3DController::setActiveTheme(Q3DTheme *theme, bool force)
{
if (theme != m_themeManager->activeTheme()) {
m_themeManager->setActiveTheme(theme);
@@ -689,7 +821,7 @@ void Abstract3DController::setActiveTheme(Q3DTheme *theme)
Q3DTheme *newActiveTheme = m_themeManager->activeTheme();
// Reset all attached series to the new theme
for (int i = 0; i < m_seriesList.size(); i++)
- m_seriesList.at(i)->d_ptr->resetToTheme(*newActiveTheme, i, true);
+ m_seriesList.at(i)->d_ptr->resetToTheme(*newActiveTheme, i, force);
markSeriesVisualsDirty();
emit activeThemeChanged(newActiveTheme);
}
@@ -717,6 +849,12 @@ QAbstract3DGraph::SelectionFlags Abstract3DController::selectionMode() const
void Abstract3DController::setShadowQuality(QAbstract3DGraph::ShadowQuality quality)
{
+ if (!m_useOrthoProjection)
+ doSetShadowQuality(quality);
+}
+
+void Abstract3DController::doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality)
+{
if (quality != m_shadowQuality) {
m_shadowQuality = quality;
m_changeTracker.shadowQualityChanged = true;
@@ -730,6 +868,22 @@ QAbstract3DGraph::ShadowQuality Abstract3DController::shadowQuality() const
return m_shadowQuality;
}
+void Abstract3DController::setOptimizationHints(QAbstract3DGraph::OptimizationHints hints)
+{
+ if (hints != m_optimizationHints) {
+ m_optimizationHints = hints;
+ m_changeTracker.optimizationHintChanged = true;
+ m_isDataDirty = true;
+ emit optimizationHintsChanged(hints);
+ emitNeedRender();
+ }
+}
+
+QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() const
+{
+ return m_optimizationHints;
+}
+
bool Abstract3DController::shadowsSupported() const
{
#if defined(QT_OPENGL_ES_2)
@@ -747,7 +901,6 @@ bool Abstract3DController::isSlicingActive() const
void Abstract3DController::setSlicingActive(bool isSlicing)
{
m_scene->setSlicingActive(isSlicing);
- emitNeedRender();
}
Q3DScene *Abstract3DController::scene()
@@ -758,6 +911,8 @@ Q3DScene *Abstract3DController::scene()
void Abstract3DController::markDataDirty()
{
m_isDataDirty = true;
+
+ markSeriesItemLabelsDirty();
emitNeedRender();
}
@@ -767,6 +922,79 @@ void Abstract3DController::markSeriesVisualsDirty()
emitNeedRender();
}
+void Abstract3DController::requestRender(QOpenGLFramebufferObject *fbo)
+{
+ m_renderer->render(fbo->handle());
+}
+
+int Abstract3DController::addCustomItem(QCustom3DItem *item)
+{
+ if (!item)
+ return -1;
+
+ int index = m_customItems.indexOf(item);
+
+ if (index != -1)
+ return index;
+
+ item->setParent(this);
+ connect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate,
+ this, &Abstract3DController::updateCustomItem);
+ m_customItems.append(item);
+ item->d_ptr->resetDirtyBits();
+ m_isCustomDataDirty = true;
+ emitNeedRender();
+ return m_customItems.count() - 1;
+}
+
+void Abstract3DController::deleteCustomItems()
+{
+ foreach (QCustom3DItem *item, m_customItems)
+ delete item;
+ m_customItems.clear();
+ m_isCustomDataDirty = true;
+ emitNeedRender();
+}
+
+void Abstract3DController::deleteCustomItem(QCustom3DItem *item)
+{
+ if (!item)
+ return;
+
+ m_customItems.removeOne(item);
+ delete item;
+ item = 0;
+ m_isCustomDataDirty = true;
+ emitNeedRender();
+}
+
+void Abstract3DController::deleteCustomItem(const QVector3D &position)
+{
+ // Get the item for the position
+ foreach (QCustom3DItem *item, m_customItems) {
+ if (item->position() == position)
+ deleteCustomItem(item);
+ }
+}
+
+void Abstract3DController::releaseCustomItem(QCustom3DItem *item)
+{
+ if (item && m_customItems.contains(item)) {
+ disconnect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate,
+ this, &Abstract3DController::updateCustomItem);
+ m_customItems.removeOne(item);
+ item->setParent(0);
+ m_isCustomDataDirty = true;
+ emitNeedRender();
+ }
+}
+
+void Abstract3DController::updateCustomItem()
+{
+ m_isCustomItemDirty = true;
+ emitNeedRender();
+}
+
void Abstract3DController::handleAxisTitleChanged(const QString &title)
{
Q_UNUSED(title)
@@ -783,6 +1011,8 @@ void Abstract3DController::handleAxisTitleChangedBySender(QObject *sender)
m_changeTracker.axisZTitleChanged = true;
else
qWarning() << __FUNCTION__ << "invoked for invalid axis";
+
+ markSeriesItemLabelsDirty();
emitNeedRender();
}
@@ -801,6 +1031,8 @@ void Abstract3DController::handleAxisLabelsChangedBySender(QObject *sender)
m_changeTracker.axisZLabelsChanged = true;
else
qWarning() << __FUNCTION__ << "invoked for invalid axis";
+
+ markSeriesItemLabelsDirty();
emitNeedRender();
}
@@ -882,6 +1114,35 @@ void Abstract3DController::handleAxisLabelFormatChanged(const QString &format)
handleAxisLabelFormatChangedBySender(sender());
}
+void Abstract3DController::handleAxisReversedChanged(bool enable)
+{
+ Q_UNUSED(enable)
+ handleAxisReversedChangedBySender(sender());
+}
+
+void Abstract3DController::handleAxisFormatterDirty()
+{
+ handleAxisFormatterDirtyBySender(sender());
+}
+
+void Abstract3DController::handleAxisLabelAutoRotationChanged(float angle)
+{
+ Q_UNUSED(angle)
+ handleAxisLabelAutoRotationChangedBySender(sender());
+}
+
+void Abstract3DController::handleAxisTitleVisibilityChanged(bool visible)
+{
+ Q_UNUSED(visible)
+ handleAxisTitleVisibilityChangedBySender(sender());
+}
+
+void Abstract3DController::handleAxisTitleFixedChanged(bool fixed)
+{
+ Q_UNUSED(fixed)
+ handleAxisTitleFixedChangedBySender(sender());
+}
+
void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::InputView view)
{
// When in automatic slicing mode, input view change to primary disables slice mode
@@ -890,15 +1151,12 @@ void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::Input
setSlicingActive(false);
}
- m_changeTracker.inputViewChanged = true;
emitNeedRender();
}
void Abstract3DController::handleInputPositionChanged(const QPoint &position)
{
Q_UNUSED(position)
-
- m_changeTracker.inputPositionChanged = true;
emitNeedRender();
}
@@ -914,6 +1172,21 @@ void Abstract3DController::handleRequestShadowQuality(QAbstract3DGraph::ShadowQu
setShadowQuality(quality);
}
+void Abstract3DController::setMeasureFps(bool enable)
+{
+ if (m_measureFps != enable) {
+ m_measureFps = enable;
+ m_currentFps = 0.0;
+
+ if (enable) {
+ m_frameTimer.start();
+ m_numFrames = -1;
+ emitNeedRender();
+ }
+ emit measureFpsChanged(enable);
+ }
+}
+
void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender)
{
// Label format changing needs to dirty the data so that labels are reset.
@@ -932,15 +1205,104 @@ void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender)
emitNeedRender();
}
+void Abstract3DController::handleAxisReversedChangedBySender(QObject *sender)
+{
+ // Reversing change needs to dirty the data so item positions are recalculated
+ if (sender == m_axisX) {
+ m_isDataDirty = true;
+ m_changeTracker.axisXReversedChanged = true;
+ } else if (sender == m_axisY) {
+ m_isDataDirty = true;
+ m_changeTracker.axisYReversedChanged = true;
+ } else if (sender == m_axisZ) {
+ m_isDataDirty = true;
+ m_changeTracker.axisZReversedChanged = true;
+ } else {
+ qWarning() << __FUNCTION__ << "invoked for invalid axis";
+ }
+ emitNeedRender();
+}
+
+void Abstract3DController::handleAxisFormatterDirtyBySender(QObject *sender)
+{
+ // Sender is QValue3DAxisPrivate
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxisPrivate *>(sender)->qptr();
+ if (valueAxis == m_axisX) {
+ m_isDataDirty = true;
+ m_changeTracker.axisXFormatterChanged = true;
+ } else if (valueAxis == m_axisY) {
+ m_isDataDirty = true;
+ m_changeTracker.axisYFormatterChanged = true;
+ } else if (valueAxis == m_axisZ) {
+ m_isDataDirty = true;
+ m_changeTracker.axisZFormatterChanged = true;
+ } else {
+ qWarning() << __FUNCTION__ << "invoked for invalid axis";
+ }
+ emitNeedRender();
+}
+
+void Abstract3DController::handleAxisLabelAutoRotationChangedBySender(QObject *sender)
+{
+ if (sender == m_axisX)
+ m_changeTracker.axisXLabelAutoRotationChanged = true;
+ else if (sender == m_axisY)
+ m_changeTracker.axisYLabelAutoRotationChanged = true;
+ else if (sender == m_axisZ)
+ m_changeTracker.axisZLabelAutoRotationChanged = true;
+ else
+ qWarning() << __FUNCTION__ << "invoked for invalid axis";
+
+ emitNeedRender();
+}
+
+void Abstract3DController::handleAxisTitleVisibilityChangedBySender(QObject *sender)
+{
+ if (sender == m_axisX)
+ m_changeTracker.axisXTitleVisibilityChanged = true;
+ else if (sender == m_axisY)
+ m_changeTracker.axisYTitleVisibilityChanged = true;
+ else if (sender == m_axisZ)
+ m_changeTracker.axisZTitleVisibilityChanged = true;
+ else
+ qWarning() << __FUNCTION__ << "invoked for invalid axis";
+
+ emitNeedRender();
+}
+
+void Abstract3DController::handleAxisTitleFixedChangedBySender(QObject *sender)
+{
+ if (sender == m_axisX)
+ m_changeTracker.axisXTitleFixedChanged = true;
+ else if (sender == m_axisY)
+ m_changeTracker.axisYTitleFixedChanged = true;
+ else if (sender == m_axisZ)
+ m_changeTracker.axisZTitleFixedChanged = true;
+ else
+ qWarning() << __FUNCTION__ << "invoked for invalid axis";
+
+ emitNeedRender();
+}
+
void Abstract3DController::handleSeriesVisibilityChangedBySender(QObject *sender)
{
- Q_UNUSED(sender)
+ QAbstract3DSeries *series = static_cast<QAbstract3DSeries *>(sender);
+ series->d_ptr->m_changeTracker.visibilityChanged = true;
m_isDataDirty = true;
- m_isSeriesVisibilityDirty = true;
+ m_isSeriesVisualsDirty = true;
+
+ adjustAxisRanges();
+
emitNeedRender();
}
+void Abstract3DController::markSeriesItemLabelsDirty()
+{
+ for (int i = 0; i < m_seriesList.size(); i++)
+ m_seriesList.at(i)->d_ptr->markItemLabelDirty();
+}
+
void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation,
QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr)
{
@@ -978,6 +1340,12 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient
this, &Abstract3DController::handleAxisRangeChanged);
QObject::connect(axis, &QAbstract3DAxis::autoAdjustRangeChanged,
this, &Abstract3DController::handleAxisAutoAdjustRangeChanged);
+ QObject::connect(axis, &QAbstract3DAxis::labelAutoRotationChanged,
+ this, &Abstract3DController::handleAxisLabelAutoRotationChanged);
+ QObject::connect(axis, &QAbstract3DAxis::titleVisibilityChanged,
+ this, &Abstract3DController::handleAxisTitleVisibilityChanged);
+ QObject::connect(axis, &QAbstract3DAxis::titleFixedChanged,
+ this, &Abstract3DController::handleAxisTitleFixedChanged);
if (orientation == QAbstract3DAxis::AxisOrientationX)
m_changeTracker.axisXTypeChanged = true;
@@ -991,6 +1359,9 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient
handleAxisRangeChangedBySender(axis);
handleAxisAutoAdjustRangeChangedInOrientation(axis->orientation(),
axis->isAutoAdjustRange());
+ handleAxisLabelAutoRotationChangedBySender(axis);
+ handleAxisTitleVisibilityChangedBySender(axis);
+ handleAxisTitleFixedChangedBySender(axis);
if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
@@ -1000,14 +1371,21 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient
this, &Abstract3DController::handleAxisSubSegmentCountChanged);
QObject::connect(valueAxis, &QValue3DAxis::labelFormatChanged,
this, &Abstract3DController::handleAxisLabelFormatChanged);
+ QObject::connect(valueAxis, &QValue3DAxis::reversedChanged,
+ this, &Abstract3DController::handleAxisReversedChanged);
+ QObject::connect(valueAxis->dptr(), &QValue3DAxisPrivate::formatterDirty,
+ this, &Abstract3DController::handleAxisFormatterDirty);
handleAxisSegmentCountChangedBySender(valueAxis);
handleAxisSubSegmentCountChangedBySender(valueAxis);
handleAxisLabelFormatChangedBySender(valueAxis);
+ handleAxisReversedChangedBySender(valueAxis);
+ handleAxisFormatterDirtyBySender(valueAxis->dptr());
}
}
-QAbstract3DAxis *Abstract3DController::createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation)
+QAbstract3DAxis *Abstract3DController::createDefaultAxis(
+ QAbstract3DAxis::AxisOrientation orientation)
{
Q_UNUSED(orientation)
@@ -1047,4 +1425,97 @@ void Abstract3DController::emitNeedRender()
}
}
+void Abstract3DController::handlePendingClick()
+{
+ QAbstract3DGraph::ElementType type = m_renderer->clickedType();
+ emit elementSelected(type);
+}
+
+int Abstract3DController::selectedLabelIndex() const
+{
+ int index = m_renderer->m_selectedLabelIndex;
+ QAbstract3DAxis *axis = selectedAxis();
+ if (axis && axis->labels().count() <= index)
+ index = -1;
+ return index;
+}
+
+QAbstract3DAxis *Abstract3DController::selectedAxis() const
+{
+ QAbstract3DAxis *axis = 0;
+ QAbstract3DGraph::ElementType type = m_renderer->clickedType();
+ switch (type) {
+ case QAbstract3DGraph::ElementAxisXLabel:
+ axis = axisX();
+ break;
+ case QAbstract3DGraph::ElementAxisYLabel:
+ axis = axisY();
+ break;
+ case QAbstract3DGraph::ElementAxisZLabel:
+ axis = axisZ();
+ break;
+ default:
+ axis = 0;
+ break;
+ }
+
+ return axis;
+}
+
+int Abstract3DController::selectedCustomItemIndex() const
+{
+ int index = m_renderer->m_selectedCustomItemIndex;
+ if (m_customItems.count() <= index)
+ index = -1;
+ return index;
+}
+
+QCustom3DItem *Abstract3DController::selectedCustomItem() const
+{
+ QCustom3DItem *item = 0;
+ int index = selectedCustomItemIndex();
+ if (index >= 0)
+ item = m_customItems[index];
+ return item;
+}
+
+QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const
+{
+ return m_renderer->clickedType();
+}
+
+void Abstract3DController::setOrthoProjection(bool enable)
+{
+ if (enable != m_useOrthoProjection) {
+ m_useOrthoProjection = enable;
+ m_changeTracker.projectionChanged = true;
+ emit orthoProjectionChanged(m_useOrthoProjection);
+ // If changed to ortho, disable shadows
+ if (m_useOrthoProjection)
+ doSetShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ emitNeedRender();
+ }
+}
+
+bool Abstract3DController::isOrthoProjection() const
+{
+ return m_useOrthoProjection;
+}
+
+void Abstract3DController::setAspectRatio(float ratio)
+{
+ if (m_aspectRatio != ratio) {
+ m_aspectRatio = ratio;
+ m_changeTracker.aspectRatioChanged = true;
+ emit aspectRatioChanged(m_aspectRatio);
+ m_isDataDirty = true;
+ emitNeedRender();
+ }
+}
+
+float Abstract3DController::aspectRatio()
+{
+ return m_aspectRatio;
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h
index 06be450e..0e4d1add 100644
--- a/src/datavisualization/engine/abstract3dcontroller_p.h
+++ b/src/datavisualization/engine/abstract3dcontroller_p.h
@@ -33,25 +33,25 @@
#include "qabstract3daxis.h"
#include "drawer_p.h"
#include "qabstract3dinputhandler.h"
-#include "qabstractdataproxy.h"
+#include "qabstract3dgraph.h"
#include "q3dscene_p.h"
+#include "qcustom3ditem.h"
#include <QtGui/QLinearGradient>
+#include <QtCore/QTime>
-class QFont;
+class QOpenGLFramebufferObject;
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-class CameraHelper;
class Abstract3DRenderer;
class QAbstract3DSeries;
class ThemeManager;
struct Abstract3DChangeBitField {
- bool zoomLevelChanged : 1;
bool themeChanged : 1;
bool shadowQualityChanged : 1;
bool selectionModeChanged : 1;
- bool objFileChanged : 1;
+ bool optimizationHintChanged : 1;
bool axisXTypeChanged : 1;
bool axisYTypeChanged : 1;
bool axisZTypeChanged : 1;
@@ -73,15 +73,29 @@ struct Abstract3DChangeBitField {
bool axisXLabelFormatChanged : 1;
bool axisYLabelFormatChanged : 1;
bool axisZLabelFormatChanged : 1;
- bool inputViewChanged : 1;
- bool inputPositionChanged : 1;
+ bool axisXReversedChanged : 1;
+ bool axisYReversedChanged : 1;
+ bool axisZReversedChanged : 1;
+ bool axisXFormatterChanged : 1;
+ bool axisYFormatterChanged : 1;
+ bool axisZFormatterChanged : 1;
+ bool projectionChanged : 1;
+ bool axisXLabelAutoRotationChanged : 1;
+ bool axisYLabelAutoRotationChanged : 1;
+ bool axisZLabelAutoRotationChanged : 1;
+ bool aspectRatioChanged : 1;
+ bool axisXTitleVisibilityChanged : 1;
+ bool axisYTitleVisibilityChanged : 1;
+ bool axisZTitleVisibilityChanged : 1;
+ bool axisXTitleFixedChanged : 1;
+ bool axisYTitleFixedChanged : 1;
+ bool axisZTitleFixedChanged : 1;
Abstract3DChangeBitField() :
- zoomLevelChanged(true),
themeChanged(true),
shadowQualityChanged(true),
selectionModeChanged(true),
- objFileChanged(true),
+ optimizationHintChanged(true),
axisXTypeChanged(true),
axisYTypeChanged(true),
axisZTypeChanged(true),
@@ -102,7 +116,24 @@ struct Abstract3DChangeBitField {
axisZSubSegmentCountChanged(true),
axisXLabelFormatChanged(true),
axisYLabelFormatChanged(true),
- axisZLabelFormatChanged(true)
+ axisZLabelFormatChanged(true),
+ axisXReversedChanged(true),
+ axisYReversedChanged(true),
+ axisZReversedChanged(true),
+ axisXFormatterChanged(true),
+ axisYFormatterChanged(true),
+ axisZFormatterChanged(true),
+ projectionChanged(true),
+ axisXLabelAutoRotationChanged(true),
+ axisYLabelAutoRotationChanged(true),
+ axisZLabelAutoRotationChanged(true),
+ aspectRatioChanged(true),
+ axisXTitleVisibilityChanged(true),
+ axisYTitleVisibilityChanged(true),
+ axisZTitleVisibilityChanged(true),
+ axisXTitleFixedChanged(true),
+ axisYTitleFixedChanged(true),
+ axisZTitleFixedChanged(true)
{
}
};
@@ -119,28 +150,19 @@ public:
SelectionColumn
};
- enum MouseState {
- MouseNone = 0,
- MouseOnScene,
- MouseOnOverview,
- MouseOnZoom,
- MouseRotating,
- MouseOnPinch
- };
-
private:
Abstract3DChangeBitField m_changeTracker;
- GLfloat m_horizontalRotation;
- GLfloat m_verticalRotation;
ThemeManager *m_themeManager;
QAbstract3DGraph::SelectionFlags m_selectionMode;
QAbstract3DGraph::ShadowQuality m_shadowQuality;
+ bool m_useOrthoProjection;
+ float m_aspectRatio;
+ QAbstract3DGraph::OptimizationHints m_optimizationHints;
protected:
Q3DScene *m_scene;
QList<QAbstract3DInputHandler *> m_inputHandlers; // List of all added input handlers
QAbstract3DInputHandler *m_activeInputHandler;
- CameraHelper *m_cameraHelper;
// Active axes
QAbstract3DAxis *m_axisX;
QAbstract3DAxis *m_axisY;
@@ -149,12 +171,22 @@ protected:
QList<QAbstract3DAxis *> m_axes; // List of all added axes
Abstract3DRenderer *m_renderer;
bool m_isDataDirty;
- bool m_isSeriesVisibilityDirty;
+ bool m_isCustomDataDirty;
+ bool m_isCustomItemDirty;
bool m_isSeriesVisualsDirty;
bool m_renderPending;
QList<QAbstract3DSeries *> m_seriesList;
+ bool m_measureFps;
+ QTime m_frameTimer;
+ int m_numFrames;
+ qreal m_currentFps;
+
+ QVector<QAbstract3DSeries *> m_changedSeriesList;
+
+ QList<QCustom3DItem *> m_customItems;
+
explicit Abstract3DController(QRect initialViewport, Q3DScene *scene, QObject *parent = 0);
public:
@@ -173,11 +205,11 @@ public:
QList<QAbstract3DSeries *> seriesList();
virtual void setAxisX(QAbstract3DAxis *axis);
- virtual QAbstract3DAxis *axisX();
+ virtual QAbstract3DAxis *axisX() const;
virtual void setAxisY(QAbstract3DAxis *axis);
- virtual QAbstract3DAxis *axisY();
+ virtual QAbstract3DAxis *axisY() const;
virtual void setAxisZ(QAbstract3DAxis *axis);
- virtual QAbstract3DAxis *axisZ();
+ virtual QAbstract3DAxis *axisZ() const;
virtual void addAxis(QAbstract3DAxis *axis);
virtual void releaseAxis(QAbstract3DAxis *axis);
virtual QList<QAbstract3DAxis *> axes() const; // Omits default axes
@@ -188,12 +220,9 @@ public:
virtual QAbstract3DInputHandler *activeInputHandler();
virtual QList<QAbstract3DInputHandler *> inputHandlers() const;
- virtual int zoomLevel();
- virtual void setZoomLevel(int zoomLevel);
-
virtual void addTheme(Q3DTheme *theme);
virtual void releaseTheme(Q3DTheme *theme);
- virtual void setActiveTheme(Q3DTheme *theme);
+ virtual void setActiveTheme(Q3DTheme *theme, bool force = true);
virtual Q3DTheme *activeTheme() const;
virtual QList<Q3DTheme *> themes() const;
@@ -201,9 +230,13 @@ public:
virtual QAbstract3DGraph::SelectionFlags selectionMode() const;
virtual void setShadowQuality(QAbstract3DGraph::ShadowQuality quality);
+ virtual void doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality);
virtual QAbstract3DGraph::ShadowQuality shadowQuality() const;
virtual bool shadowsSupported() const;
+ void setOptimizationHints(QAbstract3DGraph::OptimizationHints hints);
+ QAbstract3DGraph::OptimizationHints optimizationHints() const;
+
bool isSlicingActive() const;
void setSlicingActive(bool isSlicing);
@@ -212,6 +245,22 @@ public:
void markDataDirty();
void markSeriesVisualsDirty();
+ void requestRender(QOpenGLFramebufferObject *fbo);
+
+ int addCustomItem(QCustom3DItem *item);
+ void deleteCustomItems();
+ void deleteCustomItem(QCustom3DItem *item);
+ void deleteCustomItem(const QVector3D &position);
+ void releaseCustomItem(QCustom3DItem *item);
+
+ int selectedLabelIndex() const;
+ QAbstract3DAxis *selectedAxis() const;
+ int selectedCustomItemIndex() const;
+ QCustom3DItem *selectedCustomItem() const;
+
+ void setOrthoProjection(bool enable);
+ bool isOrthoProjection() const;
+
void emitNeedRender();
virtual void clearSelection() = 0;
@@ -231,8 +280,16 @@ public:
virtual void handleAxisAutoAdjustRangeChangedInOrientation(
QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust) = 0;
virtual void handleAxisLabelFormatChangedBySender(QObject *sender);
+ virtual void handleAxisReversedChangedBySender(QObject *sender);
+ virtual void handleAxisFormatterDirtyBySender(QObject *sender);
+ virtual void handleAxisLabelAutoRotationChangedBySender(QObject *sender);
+ virtual void handleAxisTitleVisibilityChangedBySender(QObject *sender);
+ virtual void handleAxisTitleFixedChangedBySender(QObject *sender);
virtual void handleSeriesVisibilityChangedBySender(QObject *sender);
virtual void handlePendingClick() = 0;
+ virtual void adjustAxisRanges() = 0;
+
+ void markSeriesItemLabelsDirty();
public slots:
void handleAxisTitleChanged(const QString &title);
@@ -242,6 +299,11 @@ public slots:
void handleAxisSubSegmentCountChanged(int count);
void handleAxisAutoAdjustRangeChanged(bool autoAdjust);
void handleAxisLabelFormatChanged(const QString &format);
+ void handleAxisReversedChanged(bool enable);
+ void handleAxisFormatterDirty();
+ void handleAxisLabelAutoRotationChanged(float angle);
+ void handleAxisTitleVisibilityChanged(bool visible);
+ void handleAxisTitleFixedChanged(bool fixed);
void handleInputViewChanged(QAbstract3DInputHandler::InputView view);
void handleInputPositionChanged(const QPoint &position);
void handleSeriesVisibilityChanged(bool visible);
@@ -258,6 +320,17 @@ 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);
@@ -267,6 +340,12 @@ signals:
void axisXChanged(QAbstract3DAxis *axis);
void axisYChanged(QAbstract3DAxis *axis);
void axisZChanged(QAbstract3DAxis *axis);
+ void elementSelected(QAbstract3DGraph::ElementType type);
+ void measureFpsChanged(bool enabled);
+ void currentFpsChanged(qreal fps);
+ void orthoProjectionChanged(bool enabled);
+ void aspectRatioChanged(float ratio);
+ void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints);
protected:
virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation);
@@ -278,7 +357,9 @@ private:
void setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis *axis,
QAbstract3DAxis **axisPtr);
+ friend class AbstractDeclarative;
friend class Bars3DController;
+ friend class QAbstract3DGraphPrivate;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp
index 3122cf76..5d97a6ca 100644
--- a/src/datavisualization/engine/abstract3drenderer.cpp
+++ b/src/datavisualization/engine/abstract3drenderer.cpp
@@ -17,15 +17,13 @@
****************************************************************************/
#include "abstract3drenderer_p.h"
-#include "qvalue3daxis.h"
#include "texturehelper_p.h"
-#include "utils_p.h"
-#include "q3dscene_p.h"
#include "q3dcamera_p.h"
-#include "q3dlight_p.h"
-#include "qabstract3dseries_p.h"
#include "q3dtheme_p.h"
-#include "objecthelper_p.h"
+#include "qvalue3daxisformatter_p.h"
+#include "shaderhelper_p.h"
+#include "qcustom3ditem_p.h"
+#include "qcustom3dlabel_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -37,6 +35,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
m_cachedShadowQuality(QAbstract3DGraph::ShadowQualityMedium),
m_autoScaleAdjustment(1.0f),
m_cachedSelectionMode(QAbstract3DGraph::SelectionNone),
+ m_cachedOptimizationHint(QAbstract3DGraph::OptimizationDefault),
m_textureHelper(0),
m_cachedScene(new Q3DScene()),
m_selectionDirty(true),
@@ -45,12 +44,18 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
m_selectionLabelDirty(true),
m_clickPending(false),
m_clickedSeries(0),
- m_selectionLabelItem(0)
-#ifdef DISPLAY_RENDER_SPEED
- , m_isFirstFrame(true),
- m_numFrames(0)
-#endif
-
+ m_clickedType(QAbstract3DGraph::ElementNone),
+ m_selectionLabelItem(0),
+ m_visibleSeriesCount(0),
+ m_customItemShader(0),
+ m_useOrthoProjection(false),
+ m_xFlipped(false),
+ m_yFlipped(false),
+ m_zFlipped(false),
+ m_backgroundObj(0),
+ m_gridLineObj(0),
+ m_labelObj(0),
+ m_graphAspectRatio(2.0f)
{
QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures);
QObject::connect(this, &Abstract3DRenderer::needRender, controller,
@@ -61,14 +66,30 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
Abstract3DRenderer::~Abstract3DRenderer()
{
- for (int i = 0; i < m_visibleSeriesList.size(); i++)
- m_visibleSeriesList[i].cleanup(m_textureHelper);
-
delete m_drawer;
- delete m_textureHelper;
delete m_cachedScene;
delete m_cachedTheme;
delete m_selectionLabelItem;
+ delete m_customItemShader;
+
+ foreach (SeriesRenderCache *cache, m_renderCacheList) {
+ cache->cleanup(m_textureHelper);
+ delete cache;
+ }
+ m_renderCacheList.clear();
+
+ foreach (CustomRenderItem *item, m_customRenderCache) {
+ GLuint texture = item->texture();
+ m_textureHelper->deleteTexture(&texture);
+ delete item;
+ }
+ m_customRenderCache.clear();
+
+ ObjectHelper::releaseObjectHelper(this, m_backgroundObj);
+ ObjectHelper::releaseObjectHelper(this, m_gridLineObj);
+ ObjectHelper::releaseObjectHelper(this, m_labelObj);
+
+ delete m_textureHelper;
}
void Abstract3DRenderer::initializeOpenGL()
@@ -95,23 +116,6 @@ void Abstract3DRenderer::initializeOpenGL()
void Abstract3DRenderer::render(const GLuint defaultFboHandle)
{
-#ifdef DISPLAY_RENDER_SPEED
- // For speed computation
- if (m_isFirstFrame) {
- m_lastFrameTime.start();
- m_isFirstFrame = false;
- }
-
- // Measure speed (as milliseconds per frame)
- m_numFrames++;
- if (m_lastFrameTime.elapsed() >= 1000) { // print only if last measurement was more than 1s ago
- qDebug() << float(m_lastFrameTime.elapsed()) / float(m_numFrames) << "ms/frame (="
- << float(m_numFrames) << "fps)";
- m_numFrames = 0;
- m_lastFrameTime.restart();
- }
-#endif
-
if (defaultFboHandle) {
glDepthMask(true);
glEnable(GL_DEPTH_TEST);
@@ -131,20 +135,12 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle)
m_viewport.width(),
m_viewport.height());
glEnable(GL_SCISSOR_TEST);
- QVector3D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor());
+ 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);
glDisable(GL_SCISSOR_TEST);
}
-QString Abstract3DRenderer::generateValueLabel(const QString &format, float value)
-{
- QString valueLabelFormat = format;
- Utils::ParamType valueParamType = Utils::findFormatParamType(valueLabelFormat);
- QByteArray valueFormatArray = valueLabelFormat.toUtf8();
- return Utils::formatLabel(valueFormatArray, valueParamType, value);
-}
-
void Abstract3DRenderer::updateSelectionState(SelectionState state)
{
m_selectionState = state;
@@ -163,6 +159,15 @@ void Abstract3DRenderer::initGradientShaders(const QString &vertexShader,
Q_UNUSED(fragmentShader)
}
+void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader,
+ const QString &fragmentShader)
+{
+ if (m_customItemShader)
+ delete m_customItemShader;
+ m_customItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_customItemShader->initialize();
+}
+
void Abstract3DRenderer::updateTheme(Q3DTheme *theme)
{
// Synchronize the controller theme with renderer
@@ -229,6 +234,8 @@ void Abstract3DRenderer::reInitShaders()
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"));
@@ -236,6 +243,8 @@ void Abstract3DRenderer::reInitShaders()
QStringLiteral(":/shaders/fragment"));
initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
QStringLiteral(":/shaders/fragment"));
+ initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
}
#else
initGradientShaders(QStringLiteral(":/shaders/vertex"),
@@ -244,6 +253,8 @@ void Abstract3DRenderer::reInitShaders()
QStringLiteral(":/shaders/fragmentES2"));
initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
QStringLiteral(":/shaders/fragmentES2"));
+ initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTextureES2"));
#endif
}
@@ -266,18 +277,28 @@ void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mo
m_selectionDirty = true;
}
+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::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
+{
+ m_cachedOptimizationHint = hint;
+}
+
void Abstract3DRenderer::handleResize()
{
if (m_primarySubViewport.width() == 0 || m_primarySubViewport.height() == 0)
return;
- // Calculate zoom level based on aspect ratio
- GLfloat div;
- GLfloat zoomAdjustment;
- div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height());
- zoomAdjustment = defaultRatio * ((m_primarySubViewport.width() / div)
- / (m_primarySubViewport.height() / div));
- m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f
+ // Recalculate zoom
+ calculateZoomLevel();
// Re-init selection buffer
initSelectionBuffer();
@@ -288,6 +309,18 @@ void Abstract3DRenderer::handleResize()
#endif
}
+void Abstract3DRenderer::calculateZoomLevel()
+{
+ // Calculate zoom level based on aspect ratio
+ GLfloat div;
+ GLfloat zoomAdjustment;
+ div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height());
+ zoomAdjustment = 2.0f * defaultRatio
+ * ((m_primarySubViewport.width() / div)
+ / (m_primarySubViewport.height() / div)) / m_graphAspectRatio;
+ m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f
+}
+
void Abstract3DRenderer::updateAxisType(QAbstract3DAxis::AxisOrientation orientation,
QAbstract3DAxis::AxisType type)
{
@@ -312,18 +345,25 @@ void Abstract3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orient
AxisRenderCache &cache = axisCacheForOrientation(orientation);
cache.setMin(min);
cache.setMax(max);
+
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+
+ updateCustomItemPositions();
}
void Abstract3DRenderer::updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation,
int count)
{
- axisCacheForOrientation(orientation).setSegmentCount(count);
+ AxisRenderCache &cache = axisCacheForOrientation(orientation);
+ cache.setSegmentCount(count);
}
void Abstract3DRenderer::updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation,
int count)
{
- axisCacheForOrientation(orientation).setSubSegmentCount(count);
+ AxisRenderCache &cache = axisCacheForOrientation(orientation);
+ cache.setSubSegmentCount(count);
}
void Abstract3DRenderer::updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation,
@@ -332,6 +372,67 @@ void Abstract3DRenderer::updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation
axisCacheForOrientation(orientation).setLabelFormat(format);
}
+void Abstract3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation,
+ bool enable)
+{
+ axisCacheForOrientation(orientation).setReversed(enable);
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+
+ updateCustomItemPositions();
+}
+
+void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation,
+ QValue3DAxisFormatter *formatter)
+{
+ AxisRenderCache &cache = axisCacheForOrientation(orientation);
+ if (cache.ctrlFormatter() != formatter) {
+ delete cache.formatter();
+ cache.setFormatter(formatter->createNewInstance());
+ cache.setCtrlFormatter(formatter);
+ }
+ formatter->d_ptr->populateCopy(*(cache.formatter()));
+ cache.markPositionsDirty();
+
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+
+ updateCustomItemPositions();
+}
+
+void Abstract3DRenderer::updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,
+ float angle)
+{
+ AxisRenderCache &cache = axisCacheForOrientation(orientation);
+ if (cache.labelAutoRotation() != angle)
+ cache.setLabelAutoRotation(angle);
+}
+
+void Abstract3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible)
+{
+ AxisRenderCache &cache = axisCacheForOrientation(orientation);
+ if (cache.isTitleVisible() != visible)
+ cache.setTitleVisible(visible);
+}
+
+void Abstract3DRenderer::updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation,
+ bool fixed)
+{
+ AxisRenderCache &cache = axisCacheForOrientation(orientation);
+ if (cache.isTitleFixed() != fixed)
+ cache.setTitleFixed(fixed);
+}
+
+void Abstract3DRenderer::modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList)
+{
+ foreach (QAbstract3DSeries *series, seriesList) {
+ SeriesRenderCache *cache = m_renderCacheList.value(series, 0);
+ if (cache)
+ cache->setDataDirty(true);
+ }
+}
+
void Abstract3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
{
// Default implementation does nothing.
@@ -339,41 +440,84 @@ void Abstract3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::M
Q_UNUSED(mesh)
}
-void Abstract3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList,
- bool updateVisibility)
+void Abstract3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
{
- int visibleCount = 0;
- if (updateVisibility) {
- int oldSize = m_visibleSeriesList.size();
- foreach (QAbstract3DSeries *current, seriesList) {
- if (current->isVisible())
- visibleCount++;
- }
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setValid(false);
- // Clean up series caches that are about to be permanently deleted.
- // Can't just use cache destructor, as resize will call that to all items.
- if (visibleCount < oldSize) {
- for (int i = visibleCount; i < oldSize; i++)
- m_visibleSeriesList[i].cleanup(m_textureHelper);
+ m_visibleSeriesCount = 0;
+ int seriesCount = seriesList.size();
+ for (int i = 0; i < seriesCount; i++) {
+ QAbstract3DSeries *series = seriesList.at(i);
+ SeriesRenderCache *cache = m_renderCacheList.value(series);
+ bool newSeries = false;
+ if (!cache) {
+ cache = createNewCache(series);
+ m_renderCacheList[series] = cache;
+ newSeries = true;
}
+ cache->setValid(true);
+ cache->populate(newSeries);
+ if (cache->isVisible())
+ m_visibleSeriesCount++;
+ }
+
+ // Remove non-valid objects from the cache list
+ foreach (SeriesRenderCache *cache, m_renderCacheList) {
+ if (!cache->isValid())
+ cleanCache(cache);
+ }
+}
+
+void Abstract3DRenderer::updateCustomData(const QList<QCustom3DItem *> &customItems)
+{
+ if (customItems.isEmpty() && m_customRenderCache.isEmpty())
+ return;
- if (visibleCount != oldSize)
- m_visibleSeriesList.resize(visibleCount);
+ foreach (CustomRenderItem *item, m_customRenderCache)
+ item->setValid(false);
- visibleCount = 0;
+ int itemCount = customItems.size();
+ // Check custom item list for items that are not yet in render item cache
+ for (int i = 0; i < itemCount; i++) {
+ QCustom3DItem *item = customItems.at(i);
+ CustomRenderItem *renderItem = m_customRenderCache.value(item);
+ if (!renderItem)
+ renderItem = addCustomItem(item);
+ renderItem->setValid(true);
+ renderItem->setIndex(i); // always update index, as it must match the custom item index
}
- foreach (QAbstract3DSeries *current, seriesList) {
- if (current->isVisible()) {
- // Item selection label may need update
- if (current->d_ptr->m_changeTracker.nameChanged
- || current->d_ptr->m_changeTracker.itemLabelFormatChanged) {
- m_selectionLabelDirty = true;
- }
- m_visibleSeriesList[visibleCount++].populate(current, this);
+
+ // Check render item cache and remove items that are not in customItems list anymore
+ foreach (CustomRenderItem *renderItem, m_customRenderCache) {
+ if (!renderItem->isValid()) {
+ m_customRenderCache.remove(renderItem->itemPointer());
+ GLuint texture = renderItem->texture();
+ m_textureHelper->deleteTexture(&texture);
+ delete renderItem;
}
}
}
+void Abstract3DRenderer::updateCustomItems()
+{
+ // Check all items
+ foreach (CustomRenderItem *item, m_customRenderCache)
+ updateCustomItem(item);
+}
+
+SeriesRenderCache *Abstract3DRenderer::createNewCache(QAbstract3DSeries *series)
+{
+ return new SeriesRenderCache(series, this);
+}
+
+void Abstract3DRenderer::cleanCache(SeriesRenderCache *cache)
+{
+ m_renderCacheList.remove(cache->series());
+ cache->cleanup(m_textureHelper);
+ delete cache;
+}
+
AxisRenderCache &Abstract3DRenderer::axisCacheForOrientation(
QAbstract3DAxis::AxisOrientation orientation)
{
@@ -428,26 +572,243 @@ void Abstract3DRenderer::lowerShadowQuality()
updateShadowQuality(newQuality);
}
-void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture)
+void Abstract3DRenderer::drawAxisTitleY(const QVector3D &sideLabelRotation,
+ const QVector3D &backLabelRotation,
+ const QVector3D &sideLabelTrans,
+ const QVector3D &backLabelTrans,
+ const QQuaternion &totalSideRotation,
+ const QQuaternion &totalBackRotation,
+ AbstractRenderItem &dummyItem,
+ const Q3DCamera *activeCamera,
+ float labelsMaxWidth,
+ const QMatrix4x4 &viewMatrix,
+ const QMatrix4x4 &projectionMatrix,
+ ShaderHelper *shader)
{
- if (*texture) {
- m_textureHelper->deleteTexture(texture);
- *texture = 0;
+ float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheY.titleItem().size().height();
+ float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
+ float yRotation;
+ QVector3D titleTrans;
+ QQuaternion totalRotation;
+ if (m_xFlipped == m_zFlipped) {
+ yRotation = backLabelRotation.y();
+ titleTrans = backLabelTrans;
+ totalRotation = totalBackRotation;
+ } else {
+ yRotation = sideLabelRotation.y();
+ titleTrans = sideLabelTrans;
+ totalRotation = totalSideRotation;
+ }
+
+ QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation);
+ QVector3D titleOffsetVector =
+ offsetRotator.rotatedVector(QVector3D(-titleOffset, 0.0f, 0.0f));
+
+ 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);
+ } else {
+ titleRotation = totalRotation
+ * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f);
}
+ dummyItem.setTranslation(titleTrans + titleOffsetVector);
+
+ m_drawer->drawLabel(dummyItem, m_axisCacheY.titleItem(), viewMatrix,
+ projectionMatrix, zeroVector, titleRotation, 0,
+ m_cachedSelectionMode, shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, Qt::AlignBottom);
+}
+void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation,
+ const QVector3D &labelTrans,
+ const QQuaternion &totalRotation,
+ AbstractRenderItem &dummyItem,
+ const Q3DCamera *activeCamera,
+ float labelsMaxWidth,
+ const QMatrix4x4 &viewMatrix,
+ const QMatrix4x4 &projectionMatrix,
+ ShaderHelper *shader)
+{
+ float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheX.titleItem().size().height();
+ float 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) {
+ alignment = Qt::AlignBottom;
+ if (m_zFlipped) {
+ zRotation = 180.0f;
+ titleOffset = -titleOffset;
+ if (m_xFlipped) {
+ offsetRotation = -offsetRotation;
+ extraRotation = -extraRotation;
+ } else {
+ xRotation = -90.0f - labelRotation.z();
+ }
+ } else {
+ zRotation = 180.0f;
+ yRotation = 180.0f;
+ if (m_xFlipped) {
+ offsetRotation = -offsetRotation;
+ xRotation = -90.0f - labelRotation.z();
+ } else {
+ extraRotation = -extraRotation;
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ titleOffset = -titleOffset;
+ if (m_xFlipped) {
+ yRotation = 180.0f;
+ offsetRotation = -offsetRotation;
+ } else {
+ yRotation = 180.0f;
+ xRotation = -90.0f - labelRotation.z();
+ extraRotation = -extraRotation;
+ }
+ } else {
+ if (m_xFlipped) {
+ offsetRotation = -offsetRotation;
+ xRotation = -90.0f - labelRotation.z();
+ extraRotation = -extraRotation;
+ }
+ }
+ }
+
+ if (offsetRotation == 180.0f || offsetRotation == -180.0f)
+ offsetRotation = 0.0f;
+ QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation);
+ QVector3D titleOffsetVector =
+ offsetRotator.rotatedVector(QVector3D(0.0f, 0.0f, titleOffset));
+
+ QQuaternion titleRotation;
+ if (m_axisCacheX.isTitleFixed()) {
+ titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation)
+ * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
+ * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation);
+ } else {
+ titleRotation = totalRotation
+ * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation);
+ }
+ dummyItem.setTranslation(labelTrans + titleOffsetVector);
+
+ m_drawer->drawLabel(dummyItem, m_axisCacheX.titleItem(), viewMatrix,
+ projectionMatrix, zeroVector, titleRotation, 0,
+ m_cachedSelectionMode, shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, alignment);
+}
+
+void Abstract3DRenderer::drawAxisTitleZ(const QVector3D &labelRotation,
+ const QVector3D &labelTrans,
+ const QQuaternion &totalRotation,
+ AbstractRenderItem &dummyItem,
+ const Q3DCamera *activeCamera,
+ float labelsMaxWidth,
+ const QMatrix4x4 &viewMatrix,
+ const QMatrix4x4 &projectionMatrix,
+ ShaderHelper *shader)
+{
+ float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheZ.titleItem().size().height();
+ float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
+ float zRotation = labelRotation.z();
+ float yRotation = -90.0f;
+ float xRotation = -90.0f;
+ float extraRotation = 90.0f;
+ Qt::AlignmentFlag alignment = Qt::AlignTop;
+ if (m_yFlipped) {
+ alignment = Qt::AlignBottom;
+ xRotation = -xRotation;
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ zRotation = -zRotation;
+ extraRotation = -extraRotation;
+ } else {
+ zRotation = -zRotation;
+ yRotation = -yRotation;
+ }
+ } else {
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ } else {
+ extraRotation = -extraRotation;
+ yRotation = -yRotation;
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ zRotation = -zRotation;
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ } else {
+ extraRotation = -extraRotation;
+ yRotation = -yRotation;
+ }
+ } else {
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ extraRotation = -extraRotation;
+ } else {
+ yRotation = -yRotation;
+ }
+ }
+ }
+
+ float offsetRotation = zRotation;
+ if (offsetRotation == 180.0f || offsetRotation == -180.0f)
+ offsetRotation = 0.0f;
+ QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, offsetRotation);
+ QVector3D titleOffsetVector =
+ offsetRotator.rotatedVector(QVector3D(titleOffset, 0.0f, 0.0f));
+
+ QQuaternion titleRotation;
+ if (m_axisCacheZ.isTitleFixed()) {
+ titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation)
+ * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
+ * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation);
+ } else {
+ titleRotation = totalRotation
+ * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation);
+ }
+ dummyItem.setTranslation(labelTrans + titleOffsetVector);
+
+ m_drawer->drawLabel(dummyItem, m_axisCacheZ.titleItem(), viewMatrix,
+ projectionMatrix, zeroVector, titleRotation, 0,
+ m_cachedSelectionMode, shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, alignment);
+}
+
+void Abstract3DRenderer::loadGridLineMesh()
+{
+ ObjectHelper::resetObjectHelper(this, m_gridLineObj,
+ QStringLiteral(":/defaultMeshes/plane"));
+}
+
+void Abstract3DRenderer::loadLabelMesh()
+{
+ ObjectHelper::resetObjectHelper(this, m_labelObj,
+ QStringLiteral(":/defaultMeshes/plane"));
+}
+
+
+void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture)
+{
+ m_textureHelper->deleteTexture(texture);
*texture = m_textureHelper->createUniformTexture(color);
}
-void Abstract3DRenderer::fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture)
+void Abstract3DRenderer::fixGradientAndGenerateTexture(QLinearGradient *gradient,
+ GLuint *gradientTexture)
{
// Readjust start/stop to match gradient texture size
gradient->setStart(qreal(gradientTextureWidth), qreal(gradientTextureHeight));
gradient->setFinalStop(0.0, 0.0);
- if (*gradientTexture) {
- m_textureHelper->deleteTexture(gradientTexture);
- *gradientTexture = 0;
- }
+ m_textureHelper->deleteTexture(gradientTexture);
*gradientTexture = m_textureHelper->createGradientTexture(*gradient);
}
@@ -471,4 +832,270 @@ QString &Abstract3DRenderer::selectionLabel()
return m_selectionLabel;
}
+QVector4D Abstract3DRenderer::indexToSelectionColor(GLint index)
+{
+ GLubyte idxRed = index & 0xff;
+ GLubyte idxGreen = (index & 0xff00) >> 8;
+ GLubyte idxBlue = (index & 0xff0000) >> 16;
+
+ return QVector4D(idxRed, idxGreen, idxBlue, 0);
+}
+
+CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
+{
+ CustomRenderItem *newItem = new CustomRenderItem();
+ newItem->setRenderer(this);
+ newItem->setItemPointer(item); // Store pointer for render item updates
+ newItem->setMesh(item->meshFile());
+ QVector3D scaling = item->scaling();
+ QImage textureImage = item->d_ptr->textureImage();
+ bool facingCamera = false;
+ if (item->d_ptr->m_isLabelItem) {
+ QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
+ float pointSize = labelItem->font().pointSizeF();
+ // Check do we have custom visuals or need to use theme
+ if (!labelItem->dptr()->m_customVisuals) {
+ // Recreate texture using theme
+ labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(),
+ m_cachedTheme->labelTextColor(),
+ m_cachedTheme->isLabelBackgroundEnabled(),
+ m_cachedTheme->isLabelBorderEnabled());
+ pointSize = m_cachedTheme->font().pointSizeF();
+ textureImage = item->d_ptr->textureImage();
+ }
+ // Calculate scaling based on text (texture size), font size and asked scaling
+ float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height());
+ scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
+ scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
+ // Check if facing camera
+ facingCamera = labelItem->isFacingCamera();
+ }
+ newItem->setScaling(scaling);
+ 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);
+ 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);
+ m_customRenderCache.insert(item, newItem);
+ return newItem;
+}
+
+void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
+{
+ QCustom3DItem *item = renderItem->itemPointer();
+ if (item->d_ptr->m_dirtyBits.meshDirty) {
+ renderItem->setMesh(item->meshFile());
+ item->d_ptr->m_dirtyBits.meshDirty = false;
+ }
+ if (item->d_ptr->m_dirtyBits.scalingDirty) {
+ QVector3D scaling = item->scaling();
+ // 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);
+ float pointSize = labelItem->font().pointSizeF();
+ // Check do we have custom visuals or need to use theme
+ if (labelItem->dptr()->m_customVisuals) {
+ // Recreate texture
+ labelItem->dptr()->createTextureImage();
+ } else {
+ // Recreate texture using theme
+ labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(),
+ m_cachedTheme->labelTextColor(),
+ m_cachedTheme->isLabelBackgroundEnabled(),
+ m_cachedTheme->isLabelBorderEnabled());
+ pointSize = m_cachedTheme->font().pointSizeF();
+ }
+ QImage textureImage = item->d_ptr->textureImage();
+ // Calculate scaling based on text (texture size), font size and asked scaling
+ float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height());
+ scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
+ scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
+ item->d_ptr->clearTextureImage();
+ }
+ renderItem->setScaling(scaling);
+ item->d_ptr->m_dirtyBits.scalingDirty = false;
+ }
+ if (item->d_ptr->m_dirtyBits.rotationDirty) {
+ renderItem->setRotation(item->rotation());
+ item->d_ptr->m_dirtyBits.rotationDirty = false;
+ }
+ if (item->d_ptr->m_dirtyBits.textureDirty) {
+ QImage textureImage = item->d_ptr->textureImage();
+ if (item->d_ptr->m_isLabelItem) {
+ QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
+ // Check do we have custom visuals or need to use theme
+ if (!labelItem->dptr()->m_customVisuals) {
+ // Recreate texture using theme
+ labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(),
+ m_cachedTheme->labelTextColor(),
+ m_cachedTheme->isLabelBackgroundEnabled(),
+ m_cachedTheme->isLabelBorderEnabled());
+ textureImage = item->d_ptr->textureImage();
+ }
+ }
+ 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;
+ }
+ if (item->d_ptr->m_dirtyBits.shadowCastingDirty) {
+ renderItem->setShadowCasting(item->isShadowCasting());
+ item->d_ptr->m_dirtyBits.shadowCastingDirty = false;
+ }
+ if (item->d_ptr->m_isLabelItem) {
+ QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
+ if (labelItem->dptr()->m_facingCameraDirty) {
+ renderItem->setFacingCamera(labelItem->isFacingCamera());
+ labelItem->dptr()->m_facingCameraDirty = false;
+ }
+ }
+}
+
+void Abstract3DRenderer::updateCustomItemPositions()
+{
+ foreach (CustomRenderItem *renderItem, m_customRenderCache) {
+ QVector3D translation = convertPositionToTranslation(renderItem->position(),
+ renderItem->isPositionAbsolute());
+ renderItem->setTranslation(translation);
+ }
+}
+
+void Abstract3DRenderer::drawCustomItems(RenderingState state,
+ ShaderHelper *shader,
+ const QMatrix4x4 &viewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &depthProjectionViewMatrix,
+ GLuint depthTexture,
+ GLfloat shadowQuality)
+{
+ if (m_customRenderCache.isEmpty())
+ return;
+
+ 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;
+ }
+
+ 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);
+ }
+
+ 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
+ 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);
+ } else {
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ }
+
+#if !defined(QT_OPENGL_ES_2)
+ 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());
+ }
+ } 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);
+ }
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h
index 4eb8426f..0dfc7367 100644
--- a/src/datavisualization/engine/abstract3drenderer_p.h
+++ b/src/datavisualization/engine/abstract3drenderer_p.h
@@ -29,18 +29,13 @@
#ifndef ABSTRACT3DRENDERER_P_H
#define ABSTRACT3DRENDERER_P_H
-//#define DISPLAY_RENDER_SPEED
-
#include <QtGui/QOpenGLFunctions>
-#ifdef DISPLAY_RENDER_SPEED
-#include <QtCore/QTime>
-#endif
#include "datavisualizationglobal_p.h"
#include "abstract3dcontroller_p.h"
#include "axisrendercache_p.h"
-#include "qabstractdataproxy.h"
#include "seriesrendercache_p.h"
+#include "customrenderitem_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -60,18 +55,27 @@ protected:
SelectOnSlice
};
- QString generateValueLabel(const QString &format, float value);
+ enum RenderingState {
+ RenderingNormal = 0,
+ RenderingSelection,
+ RenderingDepth
+ };
public:
virtual ~Abstract3DRenderer();
virtual void updateData() = 0;
- virtual void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility);
-
+ virtual void updateSeries(const QList<QAbstract3DSeries *> &seriesList);
+ virtual void updateCustomData(const QList<QCustom3DItem *> &customItems);
+ virtual void updateCustomItems();
+ virtual void updateCustomItemPositions();
+ virtual SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
+ virtual void cleanCache(SeriesRenderCache *cache);
virtual void render(GLuint defaultFboHandle);
virtual void updateTheme(Q3DTheme *theme);
virtual void updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode);
+ virtual void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint);
virtual void updateScene(Q3DScene *scene);
virtual void updateTextures() = 0;
virtual void initSelectionBuffer() = 0;
@@ -84,27 +88,64 @@ public:
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 initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) = 0;
- virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type);
- virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, const QString &title);
- virtual void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, const QStringList &labels);
- virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, float max);
+ virtual void initBackgroundShaders(const QString &vertexShader,
+ const QString &fragmentShader) = 0;
+ virtual void initCustomItemShaders(const QString &vertexShader,
+ const QString &fragmentShader);
+ virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation,
+ QAbstract3DAxis::AxisType type);
+ virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation,
+ const QString &title);
+ virtual void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels);
+ virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min,
+ float max);
virtual void updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count);
- virtual void updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count);
- virtual void updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation, const QString &format);
+ virtual void updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation,
+ int count);
+ virtual void updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation,
+ const QString &format);
+ virtual void updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation,
+ bool enable);
+ virtual void updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation,
+ QValue3DAxisFormatter *formatter);
+ virtual void updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,
+ float angle);
+ virtual void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible);
+ virtual void updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation,
+ bool fixed);
+ virtual void modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList);
virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh);
+
+ virtual CustomRenderItem *addCustomItem(QCustom3DItem *item);
+ virtual void updateCustomItem(CustomRenderItem *renderItem);
+
+ virtual void updateAspectRatio(float ratio);
+
+ virtual QVector3D convertPositionToTranslation(const QVector3D &position,
+ bool isAbsolute) = 0;
+
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 QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; }
+ inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; }
LabelItem &selectionLabelItem();
void setSelectionLabel(const QString &label);
QString &selectionLabel();
+ void drawCustomItems(RenderingState state, ShaderHelper *shader,
+ const QMatrix4x4 &viewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &depthProjectionViewMatrix,
+ GLuint depthTexture, GLfloat shadowQuality);
+ QVector4D indexToSelectionColor(GLint index);
+
signals:
void needRender(); // Emit this if something in renderer causes need for another render pass.
void requestShadowQuality(QAbstract3DGraph::ShadowQuality quality); // For automatic quality adjustments
@@ -124,6 +165,28 @@ protected:
void fixGradient(QLinearGradient *gradient, GLuint *gradientTexture);
+ void calculateZoomLevel();
+ void drawAxisTitleY(const QVector3D &sideLabelRotation, const QVector3D &backLabelRotation,
+ const QVector3D &sideLabelTrans, const QVector3D &backLabelTrans,
+ const QQuaternion &totalSideRotation, const QQuaternion &totalBackRotation,
+ AbstractRenderItem &dummyItem, const Q3DCamera *activeCamera,
+ float labelsMaxWidth,
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
+ ShaderHelper *shader);
+ void drawAxisTitleX(const QVector3D &labelRotation, const QVector3D &labelTrans,
+ const QQuaternion &totalRotation, AbstractRenderItem &dummyItem,
+ const Q3DCamera *activeCamera, float labelsMaxWidth,
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
+ ShaderHelper *shader);
+ void drawAxisTitleZ(const QVector3D &labelRotation, const QVector3D &labelTrans,
+ const QQuaternion &totalRotation, AbstractRenderItem &dummyItem,
+ const Q3DCamera *activeCamera, float labelsMaxWidth,
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
+ ShaderHelper *shader);
+
+ void loadGridLineMesh();
+ void loadLabelMesh();
+
bool m_hasNegativeValues;
Q3DTheme *m_cachedTheme;
Drawer *m_drawer;
@@ -132,6 +195,7 @@ protected:
GLfloat m_autoScaleAdjustment;
QAbstract3DGraph::SelectionFlags m_cachedSelectionMode;
+ QAbstract3DGraph::OptimizationHints m_cachedOptimizationHint;
AxisRenderCache m_axisCacheX;
AxisRenderCache m_axisCacheY;
@@ -142,22 +206,37 @@ protected:
bool m_selectionDirty;
SelectionState m_selectionState;
QPoint m_inputPosition;
- QVector<SeriesRenderCache> m_visibleSeriesList;
+ QHash<QAbstract3DSeries *, SeriesRenderCache *> m_renderCacheList;
+ CustomRenderItemArray m_customRenderCache;
QRect m_primarySubViewport;
QRect m_secondarySubViewport;
float m_devicePixelRatio;
bool m_selectionLabelDirty;
bool m_clickPending;
QAbstract3DSeries *m_clickedSeries;
+ QAbstract3DGraph::ElementType m_clickedType;
+ int m_selectedLabelIndex;
+ int m_selectedCustomItemIndex;
QString m_selectionLabel;
LabelItem *m_selectionLabelItem;
+ int m_visibleSeriesCount;
-#ifdef DISPLAY_RENDER_SPEED
- bool m_isFirstFrame;
- QTime m_lastFrameTime;
- GLint m_numFrames;
-#endif
+ ShaderHelper *m_customItemShader;
+
+ bool m_useOrthoProjection;
+ bool m_xFlipped;
+ bool m_yFlipped;
+ bool m_zFlipped;
+
+ ObjectHelper *m_backgroundObj; // Shared reference
+ ObjectHelper *m_gridLineObj; // Shared reference
+ ObjectHelper *m_labelObj; // Shared reference
+
+ float m_graphAspectRatio;
+
+private:
+ friend class Abstract3DController;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp
index a107dd23..0b7a5c7a 100644
--- a/src/datavisualization/engine/axisrendercache.cpp
+++ b/src/datavisualization/engine/axisrendercache.cpp
@@ -18,7 +18,6 @@
#include "axisrendercache_p.h"
-#include <QtCore/qmath.h>
#include <QtGui/QFontMetrics>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -29,10 +28,17 @@ AxisRenderCache::AxisRenderCache()
m_max(10.0f),
m_segmentCount(5),
m_subSegmentCount(1),
+ m_reversed(false),
m_font(QFont(QStringLiteral("Arial"))),
+ m_formatter(0),
+ m_ctrlFormatter(0),
m_drawer(0),
- m_segmentStep(10.0f),
- m_subSegmentStep(10.0f)
+ m_positionsDirty(true),
+ m_translate(0.0f),
+ m_scale(1.0f),
+ m_labelAutoRotation(0.0f),
+ m_titleVisible(false),
+ m_titleFixed(false)
{
}
@@ -40,6 +46,8 @@ AxisRenderCache::~AxisRenderCache()
{
foreach (LabelItem *label, m_labelItems)
delete label;
+
+ delete m_formatter;
}
void AxisRenderCache::setDrawer(Drawer *drawer)
@@ -69,8 +77,6 @@ void AxisRenderCache::setType(QAbstract3DAxis::AxisType type)
foreach (LabelItem *label, m_labelItems)
delete label;
m_labelItems.clear();
- m_segmentStep = 10.0f;
- m_subSegmentStep = 10.0f;
}
void AxisRenderCache::setTitle(const QString &title)
@@ -110,28 +116,44 @@ void AxisRenderCache::setLabels(const QStringList &labels)
}
}
-void AxisRenderCache::setMin(float min)
-{
- m_min = min;
- updateSegmentStep();
-}
-
-void AxisRenderCache::setMax(float max)
+void AxisRenderCache::updateAllPositions()
{
- m_max = max;
- updateSegmentStep();
-}
-
-void AxisRenderCache::setSegmentCount(int count)
-{
- m_segmentCount = count;
- updateSegmentStep();
-}
+ // As long as grid and subgrid lines are drawn identically, we can further optimize
+ // by caching all grid and subgrid positions into a single vector.
+ // If subgrid lines are ever themed separately, this array will probably become obsolete.
+ if (m_formatter) {
+ int gridCount = m_formatter->gridPositions().size();
+ int subGridCount = m_formatter->subGridPositions().size();
+ int labelCount = m_formatter->labelPositions().size();
+ int fullSize = gridCount + subGridCount;
+
+ m_adjustedGridLinePositions.resize(fullSize);
+ m_adjustedLabelPositions.resize(labelCount);
+ int index = 0;
+ int grid = 0;
+ int label = 0;
+ float position = 0.0f;
+ for (; label < labelCount; label++) {
+ position = m_formatter->labelPositions().at(label);
+ if (m_reversed)
+ position = 1.0f - position;
+ m_adjustedLabelPositions[label] = position * m_scale + m_translate;
+ }
+ for (; grid < gridCount; grid++) {
+ position = m_formatter->gridPositions().at(grid);
+ if (m_reversed)
+ position = 1.0f - position;
+ m_adjustedGridLinePositions[index++] = position * m_scale + m_translate;
+ }
+ for (int subGrid = 0; subGrid < subGridCount; subGrid++) {
+ position = m_formatter->subGridPositions().at(subGrid);
+ if (m_reversed)
+ position = 1.0f - position;
+ m_adjustedGridLinePositions[index++] = position * m_scale + m_translate;
+ }
-void AxisRenderCache::setSubSegmentCount(int count)
-{
- m_subSegmentCount = count;
- updateSubSegmentStep();
+ m_positionsDirty = false;
+ }
}
void AxisRenderCache::updateTextures()
@@ -153,23 +175,6 @@ void AxisRenderCache::updateTextures()
}
}
-void AxisRenderCache::updateSegmentStep()
-{
- if (m_segmentCount > 0)
- m_segmentStep = qFabs((m_max - m_min) / m_segmentCount);
- else
- m_segmentStep = 0.0f; // Irrelevant
- updateSubSegmentStep();
-}
-
-void AxisRenderCache::updateSubSegmentStep()
-{
- if (m_subSegmentCount > 1)
- m_subSegmentStep = m_segmentStep / m_subSegmentCount;
- else
- m_subSegmentStep = m_segmentStep;
-}
-
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 e1c51e7c..90321740 100644
--- a/src/datavisualization/engine/axisrendercache_p.h
+++ b/src/datavisualization/engine/axisrendercache_p.h
@@ -30,9 +30,8 @@
#define AXISRENDERCACHE_P_H
#include "datavisualizationglobal_p.h"
-#include "labelitem_p.h"
-#include "qabstract3daxis_p.h"
#include "drawer_p.h"
+#include <QtCore/QPointer>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -48,31 +47,70 @@ public:
void setType(QAbstract3DAxis::AxisType type);
inline QAbstract3DAxis::AxisType type() const { return m_type; }
void setTitle(const QString &title);
- inline const QString &title() { return m_title; }
+ inline const QString &title() const { return m_title; }
void setLabels(const QStringList &labels);
- inline const QStringList &labels() { return m_labels; }
- void setMin(float min);
- inline float min() { return m_min; }
- void setMax(float max);
- inline float max() { return m_max; }
- void setSegmentCount(int count);
+ inline const QStringList &labels() const { return m_labels; }
+ inline void setMin(float min) { m_min = min; }
+ inline float min() const { return m_min; }
+ inline void setMax(float max) { m_max = max; }
+ inline float max() const { return m_max; }
+ inline void setSegmentCount(int count) { m_segmentCount = count; m_positionsDirty = true; }
inline int segmentCount() const { return m_segmentCount; }
- void setSubSegmentCount(int count);
+ inline void setSubSegmentCount(int count) { m_subSegmentCount = count; m_positionsDirty = true; }
inline int subSegmentCount() const { return m_subSegmentCount; }
inline void setLabelFormat(const QString &format) { m_labelFormat = format; }
- inline const QString &labelFormat() { return m_labelFormat; }
+ inline const QString &labelFormat() const { return m_labelFormat; }
+ inline void setReversed(bool enable) { m_reversed = enable; m_positionsDirty = true; }
+ inline bool reversed() const { return m_reversed; }
+ inline void setFormatter(QValue3DAxisFormatter *formatter)
+ {
+ m_formatter = formatter; m_positionsDirty = true;
+ }
+ inline QValue3DAxisFormatter *formatter() const { return m_formatter; }
+ inline void setCtrlFormatter(QValue3DAxisFormatter *formatter)
+ {
+ m_ctrlFormatter = formatter;
+ }
+ inline QValue3DAxisFormatter *ctrlFormatter() const { return m_ctrlFormatter; }
inline LabelItem &titleItem() { return m_titleItem; }
inline QList<LabelItem *> &labelItems() { return m_labelItems; }
- inline GLfloat segmentStep() const { return m_segmentStep; }
- inline GLfloat subSegmentStep() const { return m_subSegmentStep; }
+ inline float gridLinePosition(int index) { return m_adjustedGridLinePositions.at(index); }
+ inline int gridLineCount() { return m_adjustedGridLinePositions.size(); }
+ inline float labelPosition(int index) { return m_adjustedLabelPositions.at(index); }
+ inline int labelCount() {
+ // Some value axis formatters may opt to not show all labels,
+ // so use positions array for determining count in that case.
+ if (m_type == QAbstract3DAxis::AxisTypeValue)
+ return m_adjustedLabelPositions.size();
+ else
+ return m_labels.size();
+ }
+ void updateAllPositions();
+ inline bool positionsDirty() const { return m_positionsDirty; }
+ inline void markPositionsDirty() { m_positionsDirty = true; }
+ inline void setTranslate(float translate) { m_translate = translate; m_positionsDirty = true; }
+ inline float translate() { return m_translate; }
+ inline void setScale(float scale) { m_scale = scale; m_positionsDirty = true; }
+ inline float scale() { return m_scale; }
+ inline float positionAt(float value)
+ {
+ if (m_reversed)
+ return (1.0f - m_formatter->positionAt(value)) * m_scale + m_translate;
+ else
+ return m_formatter->positionAt(value) * m_scale + m_translate;
+ }
+ inline float labelAutoRotation() const { return m_labelAutoRotation; }
+ inline void setLabelAutoRotation(float angle) { m_labelAutoRotation = angle; }
+ inline bool isTitleVisible() const { return m_titleVisible; }
+ inline void setTitleVisible(bool visible) { m_titleVisible = visible; }
+ inline bool isTitleFixed() const { return m_titleFixed; }
+ inline void setTitleFixed(bool fixed) { m_titleFixed = fixed; }
public slots:
void updateTextures();
private:
- void updateSegmentStep();
- void updateSubSegmentStep();
int maxLabelWidth(const QStringList &labels) const;
// Cached axis values
@@ -84,14 +122,23 @@ private:
int m_segmentCount;
int m_subSegmentCount;
QString m_labelFormat;
+ bool m_reversed;
QFont m_font;
+ QValue3DAxisFormatter *m_formatter;
+ QPointer<QValue3DAxisFormatter> m_ctrlFormatter;
// Renderer items
Drawer *m_drawer; // Not owned
LabelItem m_titleItem;
QList<LabelItem *> m_labelItems;
- GLfloat m_segmentStep;
- GLfloat m_subSegmentStep;
+ QVector<float> m_adjustedGridLinePositions;
+ QVector<float> m_adjustedLabelPositions;
+ bool m_positionsDirty;
+ float m_translate;
+ float m_scale;
+ float m_labelAutoRotation;
+ bool m_titleVisible;
+ bool m_titleFixed;
Q_DISABLE_COPY(AxisRenderCache)
};
diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp
index 91240259..3a240c24 100644
--- a/src/datavisualization/engine/bars3dcontroller.cpp
+++ b/src/datavisualization/engine/bars3dcontroller.cpp
@@ -18,8 +18,6 @@
#include "bars3dcontroller_p.h"
#include "bars3drenderer_p.h"
-#include "camerahelper_p.h"
-#include "qabstract3daxis_p.h"
#include "qvalue3daxis_p.h"
#include "qcategory3daxis_p.h"
#include "qbardataproxy_p.h"
@@ -27,9 +25,6 @@
#include "thememanager_p.h"
#include "q3dtheme_p.h"
-#include <QtGui/QMatrix4x4>
-#include <QtCore/qmath.h>
-
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
Bars3DController::Bars3DController(QRect boundRect, Q3DScene *scene)
@@ -81,9 +76,28 @@ void Bars3DController::synchDataToRenderer()
series->d_ptr->m_changeTracker.meshChanged = true;
}
+ // If y range or reverse changed, scene needs to be updated to update camera limits
+ bool needSceneUpdate = false;
+ if (Abstract3DController::m_changeTracker.axisYRangeChanged
+ || Abstract3DController::m_changeTracker.axisYReversedChanged) {
+ needSceneUpdate = true;
+ }
+
Abstract3DController::synchDataToRenderer();
// Notify changes to renderer
+ if (m_changeTracker.rowsChanged) {
+ m_renderer->updateRows(m_changedRows);
+ m_changeTracker.rowsChanged = false;
+ m_changedRows.clear();
+ }
+
+ if (m_changeTracker.itemChanged) {
+ m_renderer->updateItems(m_changedItems);
+ m_changeTracker.itemChanged = false;
+ m_changedItems.clear();
+ }
+
if (m_changeTracker.multiSeriesScalingChanged) {
m_renderer->updateMultiSeriesScaling(m_isMultiSeriesUniform);
m_changeTracker.multiSeriesScalingChanged = false;
@@ -99,6 +113,13 @@ void Bars3DController::synchDataToRenderer()
m_renderer->updateSelectedBar(m_selectedBar, m_selectedBarSeries);
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();
+ }
}
void Bars3DController::handleArrayReset()
@@ -107,7 +128,10 @@ void Bars3DController::handleArrayReset()
if (series->isVisible()) {
adjustAxisRanges();
m_isDataDirty = true;
+ series->d_ptr->markItemLabelDirty();
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
// Clear selection unless still valid
setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
emitNeedRender();
@@ -122,19 +146,45 @@ void Bars3DController::handleRowsAdded(int startIndex, int count)
adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
emitNeedRender();
}
void Bars3DController::handleRowsChanged(int startIndex, int count)
{
- Q_UNUSED(startIndex)
- Q_UNUSED(count)
QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustAxisRanges();
- m_isDataDirty = true;
+ int oldChangeCount = m_changedRows.size();
+ if (!oldChangeCount)
+ m_changedRows.reserve(count);
+
+ for (int i = 0; i < count; i++) {
+ bool newItem = true;
+ int candidate = startIndex + i;
+ for (int j = 0; j < oldChangeCount; j++) {
+ const ChangeRow &oldChangeItem = m_changedRows.at(j);
+ if (oldChangeItem.row == candidate && series == oldChangeItem.series) {
+ newItem = false;
+ break;
+ }
+ }
+ if (newItem) {
+ ChangeRow newChangeItem = {series, candidate};
+ m_changedRows.append(newChangeItem);
+ if (series == m_selectedBarSeries && m_selectedBar.x() == candidate)
+ series->d_ptr->markItemLabelDirty();
+ }
+ }
+ if (count) {
+ m_changeTracker.rowsChanged = true;
+
+ if (series->isVisible())
+ adjustAxisRanges();
+
+ // Clear selection unless still valid (row length might have changed)
+ setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
+ emitNeedRender();
}
- emitNeedRender();
}
void Bars3DController::handleRowsRemoved(int startIndex, int count)
@@ -160,6 +210,8 @@ void Bars3DController::handleRowsRemoved(int startIndex, int count)
adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
emitNeedRender();
}
@@ -182,20 +234,36 @@ void Bars3DController::handleRowsInserted(int startIndex, int count)
adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
emitNeedRender();
}
void Bars3DController::handleItemChanged(int rowIndex, int columnIndex)
{
- Q_UNUSED(rowIndex)
- Q_UNUSED(columnIndex)
QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustAxisRanges();
- m_isDataDirty = true;
+
+ bool newItem = true;
+ QPoint candidate(rowIndex, columnIndex);
+ foreach (ChangeItem item, m_changedItems) {
+ if (item.point == candidate && item.series == series) {
+ newItem = false;
+ break;
+ }
+ }
+
+ if (newItem) {
+ ChangeItem newItem = {series, candidate};
+ m_changedItems.append(newItem);
+ m_changeTracker.itemChanged = true;
+
+ if (series == m_selectedBarSeries && m_selectedBar == candidate)
+ series->d_ptr->markItemLabelDirty();
+ if (series->isVisible())
+ adjustAxisRanges();
+ emitNeedRender();
}
- emitNeedRender();
}
void Bars3DController::handleDataRowLabelsChanged()
@@ -238,8 +306,6 @@ void Bars3DController::handleSeriesVisibilityChangedBySender(QObject *sender)
{
Abstract3DController::handleSeriesVisibilityChangedBySender(sender);
- adjustAxisRanges();
-
// Visibility changes may require disabling slicing,
// so just reset selection to ensure everything is still valid.
setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
@@ -253,6 +319,8 @@ void Bars3DController::handlePendingClick()
setSelectedBar(position, series, true);
+ Abstract3DController::handlePendingClick();
+
m_renderer->resetClickedStatus();
}
@@ -337,9 +405,6 @@ void Bars3DController::insertSeries(int index, QAbstract3DSeries *series)
Abstract3DController::insertSeries(index, series);
if (oldSize != m_seriesList.size()) {
- if (series->isVisible())
- adjustAxisRanges();
-
QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(series);
if (!oldSize) {
m_primarySeries = barSeries;
@@ -460,7 +525,8 @@ void Bars3DController::setSelectedBar(const QPoint &position, QBar3DSeries *seri
adjustSelectionPosition(pos, series);
if (selectionMode().testFlag(QAbstract3DGraph::SelectionSlice)) {
- // If the selected bar is outside data window, or there is no visible selected bar, disable slicing
+ // If the selected bar is outside data window, or there is no visible selected bar,
+ // disable slicing.
if (pos.x() < m_axisZ->min() || pos.x() > m_axisZ->max()
|| pos.y() < m_axisX->min() || pos.y() > m_axisX->max()
|| !series->isVisible()) {
@@ -518,7 +584,8 @@ void Bars3DController::adjustAxisRanges()
int seriesCount = m_seriesList.size();
if (adjustZ || adjustX) {
for (int series = 0; series < seriesCount; series++) {
- const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series));
+ const QBar3DSeries *barSeries =
+ static_cast<QBar3DSeries *>(m_seriesList.at(series));
if (barSeries->isVisible()) {
const QBarDataProxy *proxy = barSeries->dataProxy();
@@ -546,22 +613,24 @@ void Bars3DController::adjustAxisRanges()
}
// Call private implementations of setRange to avoid unsetting auto adjust flag
if (adjustZ)
- categoryAxisZ->dptr()->setRange(0.0f, float(maxRowCount));
+ categoryAxisZ->dptr()->setRange(0.0f, float(maxRowCount), true);
if (adjustX)
- categoryAxisX->dptr()->setRange(0.0f, float(maxColumnCount));
+ categoryAxisX->dptr()->setRange(0.0f, float(maxColumnCount), true);
}
// Now that we know the row and column ranges, figure out the value axis range
if (adjustY) {
for (int series = 0; series < seriesCount; series++) {
- const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series));
+ const QBar3DSeries *barSeries =
+ static_cast<QBar3DSeries *>(m_seriesList.at(series));
if (barSeries->isVisible()) {
const QBarDataProxy *proxy = barSeries->dataProxy();
if (adjustY && proxy) {
- QPair<GLfloat, GLfloat> limits = proxy->dptrc()->limitValues(categoryAxisZ->min(),
- categoryAxisZ->max(),
- categoryAxisX->min(),
- categoryAxisX->max());
+ QPair<GLfloat, GLfloat> limits =
+ proxy->dptrc()->limitValues(categoryAxisZ->min(),
+ categoryAxisZ->max(),
+ categoryAxisX->min(),
+ categoryAxisX->max());
if (!series) {
// First series initializes the values
minValue = limits.first;
@@ -583,7 +652,7 @@ void Bars3DController::adjustAxisRanges()
minValue = 0.0f;
maxValue = 1.0f;
}
- valueAxis->dptr()->setRange(minValue, maxValue);
+ valueAxis->dptr()->setRange(minValue, maxValue, true);
}
}
}
diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h
index 9ea59c89..4f0e1f20 100644
--- a/src/datavisualization/engine/bars3dcontroller_p.h
+++ b/src/datavisualization/engine/bars3dcontroller_p.h
@@ -38,16 +38,18 @@ class Bars3DRenderer;
class QBar3DSeries;
struct Bars3DChangeBitField {
- bool slicingActiveChanged : 1;
bool multiSeriesScalingChanged : 1;
bool barSpecsChanged : 1;
bool selectedBarChanged : 1;
+ bool rowsChanged : 1;
+ bool itemChanged : 1;
Bars3DChangeBitField() :
- slicingActiveChanged(true),
multiSeriesScalingChanged(true),
barSpecsChanged(true),
- selectedBarChanged(true)
+ selectedBarChanged(true),
+ rowsChanged(false),
+ itemChanged(false)
{
}
};
@@ -56,8 +58,20 @@ class QT_DATAVISUALIZATION_EXPORT Bars3DController : public Abstract3DController
{
Q_OBJECT
+public:
+ struct ChangeItem {
+ QBar3DSeries *series;
+ QPoint point;
+ };
+ struct ChangeRow {
+ QBar3DSeries *series;
+ int row;
+ };
+
private:
Bars3DChangeBitField m_changeTracker;
+ QVector<ChangeItem> m_changedItems;
+ QVector<ChangeRow> m_changedRows;
// Interaction
QPoint m_selectedBar; // Points to row & column in data window.
@@ -118,6 +132,7 @@ public:
virtual QList<QBar3DSeries *> barSeriesList();
virtual void handleAxisRangeChangedBySender(QObject *sender);
+ virtual void adjustAxisRanges();
public slots:
void handleArrayReset();
@@ -137,11 +152,9 @@ protected:
virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation);
private:
- void adjustAxisRanges();
void adjustSelectionPosition(QPoint &pos, const QBar3DSeries *series);
Q_DISABLE_COPY(Bars3DController)
-
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp
index a7f9e2b3..3805e760 100644
--- a/src/datavisualization/engine/bars3drenderer.cpp
+++ b/src/datavisualization/engine/bars3drenderer.cpp
@@ -17,20 +17,12 @@
****************************************************************************/
#include "bars3drenderer_p.h"
-#include "bars3dcontroller_p.h"
#include "q3dcamera_p.h"
#include "shaderhelper_p.h"
-#include "objecthelper_p.h"
#include "texturehelper_p.h"
#include "utils_p.h"
-#include "drawer_p.h"
-#include "qbardataitem.h"
-#include "q3dlight.h"
-#include "qbar3dseries_p.h"
-
-#include <QtGui/QMatrix4x4>
-#include <QtGui/QMouseEvent>
-#include <QtCore/QThread>
+#include "barseriesrendercache_p.h"
+
#include <QtCore/qmath.h>
// You can verify that depth buffer drawing works correctly by uncommenting this.
@@ -39,9 +31,7 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-const GLfloat labelMargin = 0.05f;
const GLfloat gridLineWidth = 0.005f;
-
const bool sliceGridLabels = true;
Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
@@ -52,9 +42,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_selectedBar(0),
m_sliceCache(0),
m_sliceTitleItem(0),
- m_xFlipped(false),
- m_zFlipped(false),
- m_yFlipped(false),
m_updateLabels(false),
m_barShader(0),
m_barGradientShader(0),
@@ -62,9 +49,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_selectionShader(0),
m_backgroundShader(0),
m_labelShader(0),
- m_backgroundObj(0),
- m_gridLineObj(0),
- m_labelObj(0),
m_bgrTexture(0),
m_depthTexture(0),
m_selectionTexture(0),
@@ -74,7 +58,7 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_shadowQualityToShader(100.0f),
m_shadowQualityMultiplier(3),
m_heightNormalizer(1.0f),
- m_negativeBackgroundAdjustment(0.0f),
+ m_backgroundAdjustment(0.0f),
m_rowWidth(0),
m_columnDepth(0),
m_maxDimension(0),
@@ -83,18 +67,23 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_scaleFactor(0),
m_maxSceneSize(40.0f),
m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()),
- m_visualSelectedBarSeriesIndex(-1),
- m_hasHeightAdjustmentChanged(true),
+ m_resetCameraBaseOrientation(true),
m_selectedBarPos(Bars3DController::invalidSelectionPosition()),
- m_selectedBarSeries(0),
+ m_selectedSeriesCache(0),
m_noZeroInRange(false),
m_seriesScaleX(0.0f),
m_seriesScaleZ(0.0f),
m_seriesStep(0.0f),
m_seriesStart(0.0f),
m_clickedPosition(Bars3DController::invalidSelectionPosition()),
- m_keepSeriesUniform(false)
+ m_keepSeriesUniform(false),
+ m_haveUniformColorSeries(false),
+ m_haveGradientSeries(false),
+ m_zeroPosition(0.0f)
{
+ m_axisCacheY.setScale(2.0f);
+ m_axisCacheY.setTranslate(-1.0f);
+
initializeOpenGLFunctions();
initializeOpenGL();
}
@@ -114,9 +103,6 @@ Bars3DRenderer::~Bars3DRenderer()
delete m_depthShader;
delete m_selectionShader;
delete m_backgroundShader;
- delete m_backgroundObj;
- delete m_gridLineObj;
- delete m_labelObj;
delete m_labelShader;
}
@@ -148,23 +134,18 @@ void Bars3DRenderer::initializeOpenGL()
void Bars3DRenderer::updateData()
{
- int seriesCount = m_visibleSeriesList.size();
int minRow = m_axisCacheZ.min();
int maxRow = m_axisCacheZ.max();
int minCol = m_axisCacheX.min();
int maxCol = m_axisCacheX.max();
int newRows = maxRow - minRow + 1;
int newColumns = maxCol - minCol + 1;
- int updateSize = 0;
int dataRowCount = 0;
int maxDataRowCount = 0;
- if (m_renderingArrays.size() != seriesCount) {
- m_renderingArrays.resize(seriesCount);
- m_seriesScaleX = 1.0f / float(seriesCount);
- m_seriesStep = 1.0f / float(seriesCount);
- m_seriesStart = -((float(seriesCount) - 1.0f) / 2.0f) * m_seriesStep;
- }
+ m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
+ m_seriesStep = 1.0f / float(m_visibleSeriesCount);
+ m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) / 2.0f) * m_seriesStep;
if (m_keepSeriesUniform)
m_seriesScaleZ = m_seriesScaleX;
@@ -175,7 +156,6 @@ void Bars3DRenderer::updateData()
// Force update for selection related items
m_sliceCache = 0;
m_sliceTitleItem = 0;
- m_sliceSelection.clear();
m_cachedColumnCount = newColumns;
m_cachedRowCount = newRows;
@@ -187,88 +167,235 @@ void Bars3DRenderer::updateData()
calculateSceneScalingFactors();
}
- for (int series = 0; series < seriesCount; series++) {
- BarRenderItemArray &renderArray = m_renderingArrays[series];
- if (newRows != renderArray.size()
- || newColumns != renderArray.at(0).size()) {
- // Destroy old render items and reallocate new array
- renderArray.resize(newRows);
- for (int i = 0; i < newRows; i++)
- renderArray[i].resize(newColumns);
- }
+ m_zeroPosition = m_axisCacheY.formatter()->positionAt(0.0f);
+
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
+ if (cache->isVisible()) {
+ const QBar3DSeries *currentSeries = cache->series();
+ BarRenderItemArray &renderArray = cache->renderArray();
+ bool dimensionsChanged = false;
+ if (newRows != renderArray.size()
+ || newColumns != renderArray.at(0).size()) {
+ // Destroy old render items and reallocate new array
+ dimensionsChanged = true;
+ renderArray.resize(newRows);
+ for (int i = 0; i < newRows; i++)
+ renderArray[i].resize(newColumns);
+ cache->sliceArray().clear();
+ }
- // Update cached data window
- QBarDataProxy *dataProxy =
- static_cast<QBar3DSeries *>(m_visibleSeriesList.at(series).series())->dataProxy();
- dataRowCount = dataProxy->rowCount();
- if (maxDataRowCount < dataRowCount)
- maxDataRowCount = qMin(dataRowCount, newRows);
- int dataRowIndex = minRow;
- GLfloat heightValue = 0.0f;
- for (int i = 0; i < newRows; i++) {
- int j = 0;
- BarRenderItemRow &renderRow = renderArray[i];
- if (dataRowIndex < dataRowCount) {
- const QBarDataRow *dataRow = dataProxy->rowAt(dataRowIndex);
- updateSize = qMin((dataRow->size() - minCol), renderRow.size());
- if (dataRow) {
- int dataColIndex = minCol;
- for (; j < updateSize ; j++) {
- float value = dataRow->at(dataColIndex).value();
- if (!m_noZeroInRange) {
- heightValue = GLfloat(value);
- } else {
- // Adjust height to range
- if (!m_hasNegativeValues) {
- heightValue = value - m_axisCacheY.min();
- if (heightValue < 0.0f)
- heightValue = 0.0f;
- } else if (m_axisCacheY.max() < 0.0f) {
- heightValue = value - m_axisCacheY.max();
- if (heightValue > 0.0f)
- heightValue = 0.0f;
- }
- }
- renderRow[j].setValue(value);
- renderRow[j].setHeight(heightValue / m_heightNormalizer);
- float angle = dataRow->at(dataColIndex).rotation();
- if (angle) {
- renderRow[j].setRotation(
- QQuaternion::fromAxisAndAngle(
- upVector, angle));
- } else {
- renderRow[j].setRotation(identityQuaternion);
- }
- dataColIndex++;
- }
+ if (cache->dataDirty() || dimensionsChanged) {
+ QBarDataProxy *dataProxy = currentSeries->dataProxy();
+ dataRowCount = dataProxy->rowCount();
+ if (maxDataRowCount < dataRowCount)
+ maxDataRowCount = qMin(dataRowCount, newRows);
+ int dataRowIndex = minRow;
+ for (int i = 0; i < newRows; i++) {
+ BarRenderItemRow &renderRow = renderArray[i];
+ const QBarDataRow *dataRow = 0;
+ if (dataRowIndex < dataRowCount)
+ dataRow = dataProxy->rowAt(dataRowIndex);
+ updateRenderRow(dataRow, renderRow);
+ dataRowIndex++;
}
+ cache->setDataDirty(false);
}
- for (; j < m_renderingArrays.at(series).at(i).size(); j++) {
- renderRow[j].setValue(0.0f);
- renderRow[j].setHeight(0.0f);
- renderRow[j].setRotation(identityQuaternion);
- }
- dataRowIndex++;
}
}
// Reset selected bar to update selection
- updateSelectedBar(m_selectedBarPos, m_selectedBarSeries);
+ updateSelectedBar(m_selectedBarPos,
+ m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0);
+}
+
+void Bars3DRenderer::updateRenderRow(const QBarDataRow *dataRow, BarRenderItemRow &renderRow)
+{
+ int j = 0;
+ int renderRowSize = renderRow.size();
+ int startIndex = m_axisCacheX.min();
+
+ if (dataRow) {
+ int updateSize = qMin((dataRow->size() - startIndex), renderRowSize);
+ int dataColIndex = startIndex;
+ for (; j < updateSize ; j++) {
+ updateRenderItem(dataRow->at(dataColIndex), renderRow[j]);
+ dataColIndex++;
+ }
+ }
+ for (; j < renderRowSize; j++) {
+ renderRow[j].setValue(0.0f);
+ renderRow[j].setHeight(0.0f);
+ renderRow[j].setRotation(identityQuaternion);
+ }
+}
+
+void Bars3DRenderer::updateRenderItem(const QBarDataItem &dataItem, BarRenderItem &renderItem)
+{
+ float value = dataItem.value();
+ float heightValue = m_axisCacheY.formatter()->positionAt(value);
+ if (m_noZeroInRange) {
+ if (m_hasNegativeValues) {
+ heightValue = -1.0f + heightValue;
+ if (heightValue > 0.0f)
+ heightValue = 0.0f;
+ } else {
+ if (heightValue < 0.0f)
+ heightValue = 0.0f;
+ }
+ } else {
+ heightValue -= m_zeroPosition;
+ }
+ if (m_axisCacheY.reversed())
+ heightValue = -heightValue;
+
+ renderItem.setValue(value);
+ renderItem.setHeight(heightValue);
+
+ float angle = dataItem.rotation();
+ if (angle) {
+ renderItem.setRotation(
+ QQuaternion::fromAxisAndAngle(
+ upVector, angle));
+ } else {
+ renderItem.setRotation(identityQuaternion);
+ }
+}
+
+void Bars3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
+{
+ Abstract3DRenderer::updateSeries(seriesList);
+
+ bool noSelection = true;
+ int seriesCount = seriesList.size();
+ int visualIndex = 0;
+ m_haveUniformColorSeries = false;
+ m_haveGradientSeries = false;
+ for (int i = 0; i < seriesCount; i++) {
+ QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(seriesList[i]);
+ BarSeriesRenderCache *cache =
+ static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(barSeries));
+ if (barSeries->isVisible()) {
+ if (noSelection
+ && barSeries->selectedBar() != QBar3DSeries::invalidSelectionPosition()) {
+ if (selectionLabel() != cache->itemLabel())
+ m_selectionLabelDirty = true;
+ noSelection = false;
+ }
+ cache->setVisualIndex(visualIndex++);
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
+ m_haveUniformColorSeries = true;
+ else
+ m_haveGradientSeries = true;
+ } else {
+ cache->setVisualIndex(-1);
+ }
+
+ }
+ if (noSelection) {
+ if (!selectionLabel().isEmpty())
+ m_selectionLabelDirty = true;
+ m_selectedSeriesCache = 0;
+ }
+}
+
+SeriesRenderCache *Bars3DRenderer::createNewCache(QAbstract3DSeries *series)
+{
+ return new BarSeriesRenderCache(series, this);
+}
+
+void Bars3DRenderer::updateRows(const QVector<Bars3DController::ChangeRow> &rows)
+{
+ int minRow = m_axisCacheZ.min();
+ int maxRow = m_axisCacheZ.max();
+ BarSeriesRenderCache *cache = 0;
+ const QBar3DSeries *prevSeries = 0;
+ const QBarDataArray *dataArray = 0;
+
+ foreach (Bars3DController::ChangeRow item, rows) {
+ const int row = item.row;
+ if (row < minRow || row > maxRow)
+ continue;
+ QBar3DSeries *currentSeries = item.series;
+ if (currentSeries != prevSeries) {
+ cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries));
+ prevSeries = currentSeries;
+ dataArray = item.series->dataProxy()->array();
+ // Invisible series render caches are not updated, but instead just marked dirty, so that
+ // they can be completely recalculated when they are turned visible.
+ if (!cache->isVisible() && !cache->dataDirty())
+ cache->setDataDirty(true);
+ }
+ if (cache->isVisible()) {
+ updateRenderRow(dataArray->at(row), cache->renderArray()[row - minRow]);
+ if (m_cachedIsSlicingActivated
+ && cache == m_selectedSeriesCache
+ && m_selectedBarPos.x() == row) {
+ m_selectionDirty = true; // Need to update slice view
+ }
+ }
+ }
+}
+
+void Bars3DRenderer::updateItems(const QVector<Bars3DController::ChangeItem> &items)
+{
+ int minRow = m_axisCacheZ.min();
+ int maxRow = m_axisCacheZ.max();
+ int minCol = m_axisCacheX.min();
+ int maxCol = m_axisCacheX.max();
+ BarSeriesRenderCache *cache = 0;
+ const QBar3DSeries *prevSeries = 0;
+ const QBarDataArray *dataArray = 0;
+
+ foreach (Bars3DController::ChangeItem item, items) {
+ const int row = item.point.x();
+ const int col = item.point.y();
+ if (row < minRow || row > maxRow || col < minCol || col > maxCol)
+ continue;
+ QBar3DSeries *currentSeries = item.series;
+ if (currentSeries != prevSeries) {
+ cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries));
+ prevSeries = currentSeries;
+ dataArray = item.series->dataProxy()->array();
+ // Invisible series render caches are not updated, but instead just marked dirty, so that
+ // they can be completely recalculated when they are turned visible.
+ if (!cache->isVisible() && !cache->dataDirty())
+ cache->setDataDirty(true);
+ }
+ if (cache->isVisible()) {
+ updateRenderItem(dataArray->at(row)->at(col),
+ cache->renderArray()[row - minRow][col - minCol]);
+ if (m_cachedIsSlicingActivated
+ && cache == m_selectedSeriesCache
+ && m_selectedBarPos == QPoint(row, col)) {
+ m_selectionDirty = true; // Need to update slice view
+ }
+ }
+ }
}
void Bars3DRenderer::updateScene(Q3DScene *scene)
{
- if (m_hasNegativeValues)
+ if (!m_noZeroInRange) {
scene->activeCamera()->d_ptr->setMinYRotation(-90.0);
- else
- scene->activeCamera()->d_ptr->setMinYRotation(0.0f);
+ scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
+ } else {
+ if ((m_hasNegativeValues && !m_axisCacheY.reversed())
+ || (!m_hasNegativeValues && m_axisCacheY.reversed())) {
+ scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
+ scene->activeCamera()->d_ptr->setMaxYRotation(0.0);
+ } else {
+ scene->activeCamera()->d_ptr->setMinYRotation(0.0f);
+ scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
+ }
+ }
- if (m_hasHeightAdjustmentChanged) {
+ if (m_resetCameraBaseOrientation) {
// Set initial camera position. Also update if height adjustment has changed.
scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector,
zeroVector,
upVector);
- m_hasHeightAdjustmentChanged = false;
+ m_resetCameraBaseOrientation = false;
}
Abstract3DRenderer::updateScene(scene);
@@ -281,6 +408,9 @@ void Bars3DRenderer::render(GLuint defaultFboHandle)
// Handle GL state setup for FBO buffers and clearing of the render surface
Abstract3DRenderer::render(defaultFboHandle);
+ if (m_axisCacheY.positionsDirty())
+ m_axisCacheY.updateAllPositions();
+
drawScene(defaultFboHandle);
if (m_cachedIsSlicingActivated)
drawSlicedScene();
@@ -290,7 +420,7 @@ void Bars3DRenderer::drawSlicedScene()
{
GLfloat barPosX = 0;
QVector3D lightPos;
- QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
static QQuaternion ninetyDegreeRotation = QQuaternion::fromAxisAndAngle(upVector, 90.0f);
// Specify viewport
@@ -301,8 +431,16 @@ void Bars3DRenderer::drawSlicedScene()
// Set up projection matrix
QMatrix4x4 projectionMatrix;
- projectionMatrix.perspective(35.0f, (GLfloat)m_secondarySubViewport.width()
- / (GLfloat)m_secondarySubViewport.height(), 0.1f, 100.0f);
+ GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
+ / (GLfloat)m_primarySubViewport.height();
+ if (m_useOrthoProjection) {
+ GLfloat orthoRatio = 2.0f / m_autoScaleAdjustment;
+ projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
+ -orthoRatio, orthoRatio,
+ 0.0f, 100.0f);
+ } else {
+ projectionMatrix.perspective(35.0f, viewPortRatio, 0.1f, 100.0f);
+ }
// Set view matrix
QMatrix4x4 viewMatrix;
@@ -323,27 +461,37 @@ void Bars3DRenderer::drawSlicedScene()
bool itemMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem);
GLfloat barPosYAdjustment = -0.8f; // Translate to -1.0 + 0.2 for row/column labels
+ GLfloat gridAdjustment = 1.0f + barPosYAdjustment - m_backgroundAdjustment;
GLfloat scaleFactor = 0.0f;
if (rowMode)
scaleFactor = (1.1f * m_rowWidth) / m_scaleFactor;
else
scaleFactor = (1.1f * m_columnDepth) / m_scaleFactor;
- GLfloat barLabelYPos = barPosYAdjustment - 0.4f - labelMargin; // 0.4 for labels
+ GLfloat barLabelYPos = barPosYAdjustment - labelMargin;
GLfloat zeroPosAdjustment = 0.0f;
- if (!m_noZeroInRange)
- zeroPosAdjustment = 2.0f * m_axisCacheY.min() / m_heightNormalizer;
- else if (m_hasNegativeValues)
- zeroPosAdjustment = -2.0f;
+ GLfloat directionMultiplier = 2.0f;
+ GLfloat directionBase = 0.0f;
+ if (m_axisCacheY.reversed()) {
+ directionMultiplier = -2.0f;
+ directionBase = -2.0f;
+ }
+ zeroPosAdjustment = directionBase +
+ directionMultiplier * m_axisCacheY.min() / m_heightNormalizer;
+ zeroPosAdjustment = qBound(-2.0f, zeroPosAdjustment, 0.0f);
// 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
// Bind line shader
lineShader->bind();
// Set unchanging shader bindings
- QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
+ QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
lineShader->setUniformValue(lineShader->lightP(), lightPos);
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
@@ -352,20 +500,18 @@ void Bars3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->lightS(), 0.0f);
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
- GLfloat gridStep = (2.0f * m_axisCacheY.subSegmentStep()) / m_heightNormalizer;
- GLfloat gridPos = barPosYAdjustment;
- int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
- // Use the position of the bottom grid line as the base y position for bar labels
-
// Horizontal lines
if (m_axisCacheY.segmentCount() > 0) {
+ int gridLineCount = m_axisCacheY.gridLineCount();
+
QVector3D gridLineScale(scaleFactor, gridLineWidth, gridLineWidth);
bool noZero = true;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
+ GLfloat gridPos = m_axisCacheY.gridLinePosition(line) + gridAdjustment;
modelMatrix.translate(0.0f, gridPos, 0.0f);
modelMatrix.scale(gridLineScale);
itModelMatrix = modelMatrix;
@@ -378,14 +524,17 @@ 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
// Check if we have a line at zero position already
if (gridPos == (barPosYAdjustment + zeroPosAdjustment))
noZero = false;
-
- gridPos += gridStep;
}
+
// Draw a line at zero, if none exists
if (!m_noZeroInRange && noZero) {
QMatrix4x4 modelMatrix;
@@ -400,10 +549,15 @@ void Bars3DRenderer::drawSlicedScene()
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
lineShader->setUniformValue(lineShader->color(),
- Utils::vectorFromColor(m_cachedTheme->backgroundColor()));
+ Utils::vectorFromColor(
+ m_cachedTheme->labelTextColor()));
// Draw the object
+#if !(defined QT_OPENGL_ES_2)
m_drawer->drawObject(lineShader, m_gridLineObj);
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
}
@@ -417,24 +571,21 @@ void Bars3DRenderer::drawSlicedScene()
// Draw grid labels
int labelNbr = 0;
- int labelCount = m_axisCacheY.labels().size();
- gridStep = (2.0f * m_axisCacheY.segmentStep()) / m_heightNormalizer;
- gridPos = barPosYAdjustment;
- QVector3D backLabelRotation(0.0f, 0.0f, 0.0f);
+ int labelCount = m_axisCacheY.labelCount();
QVector3D labelTrans = QVector3D(scaleFactor + labelMargin, 0.0f, 0.0f);
for (int i = 0; i < labelCount; i++) {
if (m_axisCacheY.labelItems().size() > labelNbr) {
const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
+ GLfloat gridPos = m_axisCacheY.labelPosition(i) + gridAdjustment;
labelTrans.setY(gridPos);
m_dummyBarRenderItem.setTranslation(labelTrans);
m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix,
- projectionMatrix, zeroVector, backLabelRotation, 0,
+ projectionMatrix, zeroVector, identityQuaternion, 0,
m_cachedSelectionMode, m_labelShader, m_labelObj,
- activeCamera, true, true, Drawer::LabelMid, Qt::AlignRight);
+ activeCamera, true, true, Drawer::LabelMid, Qt::AlignLeft);
}
labelNbr++;
- gridPos += gridStep;
}
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
@@ -470,37 +621,40 @@ void Bars3DRenderer::drawSlicedScene()
ShaderHelper *barShader = m_barShader;
barShader->bind();
- int currentSeriesIndex = -1;
Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform;
ObjectHelper *barObj = 0;
- QVector3D highlightColor;
- QVector3D baseColor;
+ QVector4D highlightColor;
+ QVector4D baseColor;
GLuint highlightGradientTexture = 0;
GLuint baseGradientTexture = 0;
- const SeriesRenderCache *currentSeries = 0;
bool colorStyleIsUniform = true;
+ int firstVisualIndex = m_renderCacheList.size();
+ QVector<BarRenderSliceItem> *firstVisualSliceArray = 0;
+ BarRenderSliceItem *selectedItem = 0;
+
+ QQuaternion seriesRotation;
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()
+ && (baseCache == m_selectedSeriesCache
+ || m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries))) {
+ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
+ QVector<BarRenderSliceItem> &sliceArray = cache->sliceArray();
+ int sliceCount = sliceArray.size();
+ if (firstVisualIndex > cache->visualIndex()) {
+ firstVisualIndex = cache->visualIndex();
+ firstVisualSliceArray = &sliceArray;
+ }
- int sliceItemCount = m_sliceSelection.size();
- for (int bar = 0; bar < sliceItemCount; bar++) {
- const BarRenderSliceItem &item = m_sliceSelection.at(bar);
- if (!item.value())
- continue;
-
- QQuaternion seriesRotation;
-
- if (item.seriesIndex() != currentSeriesIndex) {
- currentSeriesIndex = item.seriesIndex();
- currentSeries = &(m_visibleSeriesList.at(currentSeriesIndex));
- barObj = currentSeries->object();
- colorStyle = currentSeries->colorStyle();
+ barObj = cache->object();
+ colorStyle = cache->colorStyle();
colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
if (colorStyleIsUniform) {
- highlightColor = currentSeries->singleHighlightColor();
- baseColor = currentSeries->baseColor();
+ highlightColor = cache->singleHighlightColor();
+ baseColor = cache->baseColor();
} else {
- highlightGradientTexture = currentSeries->singleHighlightGradientTexture();
- baseGradientTexture = currentSeries->baseGradientTexture();
+ highlightGradientTexture = cache->singleHighlightGradientTexture();
+ baseGradientTexture = cache->baseGradientTexture();
}
// Rebind shader if it has changed
@@ -510,7 +664,6 @@ void Bars3DRenderer::drawSlicedScene()
else
barShader = m_barGradientShader;
barShader->bind();
-
}
if (!colorStyleIsUniform && (previousColorStyle != colorStyle)
@@ -519,76 +672,88 @@ void Bars3DRenderer::drawSlicedScene()
}
previousColorStyle = colorStyle;
- seriesRotation = currentSeries->meshRotation();
- }
+ seriesRotation = cache->meshRotation();
+ bool selectedSeries = (cache == m_selectedSeriesCache);
+
+ for (int bar = 0; bar < sliceCount; bar++) {
+ BarRenderSliceItem &item = cache->sliceArray()[bar];
+ if (selectedSeries && itemMode && sliceGridLabels
+ && m_visualSelectedBarPos.x() == item.position().x()
+ && m_visualSelectedBarPos.y() == item.position().y()) {
+ selectedItem = &item;
+ }
+ if (!item.value())
+ continue;
- if (item.height() < 0)
- glCullFace(GL_FRONT);
- else
- glCullFace(GL_BACK);
+ if (item.height() < 0)
+ glCullFace(GL_FRONT);
+ else
+ glCullFace(GL_BACK);
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 modelMatrix;
- QMatrix4x4 itModelMatrix;
- QQuaternion barRotation = item.rotation();
- GLfloat barPosY = item.translation().y() + barPosYAdjustment - zeroPosAdjustment;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ QQuaternion barRotation = item.rotation();
+ GLfloat barPosY = item.translation().y() + barPosYAdjustment - zeroPosAdjustment;
+
+ if (rowMode) {
+ barPosX = item.translation().x();
+ } else {
+ barPosX = -(item.translation().z()); // flip z; frontmost bar to the left
+ barRotation *= ninetyDegreeRotation;
+ }
- if (rowMode) {
- barPosX = item.translation().x();
- } else {
- barPosX = -(item.translation().z()); // flip z; frontmost bar to the left
- barRotation *= ninetyDegreeRotation;
- }
+ modelMatrix.translate(barPosX, barPosY, 0.0f);
+ modelMatrixScaler.setY(item.height());
- modelMatrix.translate(barPosX, barPosY, 0.0f);
- modelMatrixScaler.setY(item.height());
+ if (!seriesRotation.isIdentity())
+ barRotation *= seriesRotation;
- if (!seriesRotation.isIdentity())
- barRotation *= seriesRotation;
+ if (!barRotation.isIdentity()) {
+ modelMatrix.rotate(barRotation);
+ itModelMatrix.rotate(barRotation);
+ }
- if (!barRotation.isIdentity()) {
- modelMatrix.rotate(barRotation);
- itModelMatrix.rotate(barRotation);
- }
+ modelMatrix.scale(modelMatrixScaler);
+ itModelMatrix.scale(modelMatrixScaler);
- modelMatrix.scale(modelMatrixScaler);
- itModelMatrix.scale(modelMatrixScaler);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ QVector4D barColor;
+ GLuint gradientTexture = 0;
- QVector3D barColor;
- GLuint gradientTexture = 0;
+ if (itemMode && m_visualSelectedBarPos.x() == item.position().x()
+ && m_visualSelectedBarPos.y() == item.position().y()) {
+ if (colorStyleIsUniform)
+ barColor = highlightColor;
+ else
+ gradientTexture = highlightGradientTexture;
+ } else {
+ if (colorStyleIsUniform)
+ barColor = baseColor;
+ else
+ gradientTexture = baseGradientTexture;
+ }
- if (itemMode && m_visualSelectedBarPos.x() == item.position().x()
- && m_visualSelectedBarPos.y() == item.position().y()) {
- if (colorStyleIsUniform)
- barColor = highlightColor;
- else
- gradientTexture = highlightGradientTexture;
- } else {
- if (colorStyleIsUniform)
- barColor = baseColor;
- else
- gradientTexture = baseGradientTexture;
- }
+ if (item.height() != 0) {
+ // Set shader bindings
+ barShader->setUniformValue(barShader->model(), modelMatrix);
+ barShader->setUniformValue(barShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ barShader->setUniformValue(barShader->MVP(), MVPMatrix);
+ if (colorStyleIsUniform) {
+ barShader->setUniformValue(barShader->color(), barColor);
+ } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
+ barShader->setUniformValue(barShader->gradientHeight(),
+ (qAbs(item.height()) / m_gradientFraction));
+ }
- if (item.height() != 0) {
- // Set shader bindings
- barShader->setUniformValue(barShader->model(), modelMatrix);
- barShader->setUniformValue(barShader->nModel(),
- itModelMatrix.inverted().transposed());
- barShader->setUniformValue(barShader->MVP(), MVPMatrix);
- if (colorStyleIsUniform) {
- barShader->setUniformValue(barShader->color(), barColor);
- } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
- barShader->setUniformValue(barShader->gradientHeight(),
- (qAbs(item.height()) / m_gradientFraction));
+ // Draw the object
+ m_drawer->drawObject(barShader,
+ barObj,
+ gradientTexture);
+ }
}
-
- // Draw the object
- m_drawer->drawObject(barShader,
- barObj,
- gradientTexture);
}
}
@@ -607,97 +772,111 @@ void Bars3DRenderer::drawSlicedScene()
// Draw labels for bars
QVector3D sliceValueRotation(0.0f, 0.0f, 90.0f);
QVector3D sliceLabelRotation(0.0f, 0.0f, -45.0f);
+ QQuaternion totalSliceValueRotation = Utils::calculateRotation(sliceValueRotation);
+ QQuaternion totalSliceLabelRotation = Utils::calculateRotation(sliceLabelRotation);
- int lastLabel = m_sliceCache->labelItems().size() - 1;
+ int labelCount = m_sliceCache->labelItems().size();
- for (int labelNo = 0; labelNo <= lastLabel; labelNo++) {
+ for (int labelNo = 0; labelNo < labelCount; labelNo++) {
// Get labels from first series only
- const BarRenderSliceItem &item = m_sliceSelection.at(labelNo);
+ const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo);
m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
barLabelYPos,
item.translation().z()));
+
// Draw labels
m_drawer->drawLabel(m_dummyBarRenderItem, *m_sliceCache->labelItems().at(labelNo),
- viewMatrix, projectionMatrix, positionComp, sliceLabelRotation,
+ viewMatrix, projectionMatrix, positionComp, totalSliceLabelRotation,
0, m_cachedSelectionMode, m_labelShader,
m_labelObj, activeCamera, false, false, Drawer::LabelMid,
- Qt::AlignRight, true);
+ Qt::AlignmentFlag(Qt::AlignLeft | Qt::AlignTop), true);
}
- for (int col = 0; col < sliceItemCount; col++) {
- BarRenderSliceItem &item = m_sliceSelection[col];
-
- if (!sliceGridLabels) {
- // Draw values
- if (item.height() != 0.0f || (!m_noZeroInRange && item.value() == 0.0f)) {
- // Create label texture if we need it
- if (item.sliceLabel().isNull() || m_updateLabels) {
- item.setSliceLabel(generateValueLabel(m_axisCacheY.labelFormat(),
- item.value()));
- m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel());
- m_updateLabels = false;
- }
- Qt::AlignmentFlag alignment = (item.height() < 0) ? Qt::AlignBottom : Qt::AlignTop;
- Drawer::LabelPosition labelPos = (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
- m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
- barPosYAdjustment - zeroPosAdjustment
- + item.height(),
- item.translation().z()));
-
- m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix,
- projectionMatrix, zeroVector, sliceValueRotation,
- item.height(), m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, false, false, labelPos,
- alignment, true);
- }
- } else {
- // Only draw value for selected item when grid labels are on
- if (itemMode && m_visualSelectedBarPos.x() == item.position().x()
- && m_visualSelectedBarPos.y() == item.position().y()
- && item.seriesIndex() == m_visualSelectedBarSeriesIndex) {
- // Create label texture if we need it
- if (item.sliceLabel().isNull() || m_updateLabels) {
- item.setSliceLabel(generateValueLabel(m_axisCacheY.labelFormat(),
- item.value()));
- m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel());
- m_updateLabels = false;
+ if (!sliceGridLabels) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
+ QVector<BarRenderSliceItem> &sliceArray = cache->sliceArray();
+ int sliceCount = sliceArray.size();
+ for (int col = 0; col < sliceCount; col++) {
+ BarRenderSliceItem &item = sliceArray[col];
+
+ // Draw values
+ if (item.height() != 0.0f || (!m_noZeroInRange && item.value() == 0.0f)) {
+ // Create label texture if we need it
+ if (item.sliceLabel().isNull() || m_updateLabels) {
+ QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
+ qreal(item.value()), m_axisCacheY.labelFormat());
+ item.setSliceLabel(valueLabelText);
+ m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel());
+ m_updateLabels = false;
+ }
+ Qt::AlignmentFlag alignment =
+ (item.height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
+ Drawer::LabelPosition labelPos =
+ (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
+ m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
+ barPosYAdjustment
+ - zeroPosAdjustment
+ + item.height(),
+ item.translation().z()));
+
+ m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix,
+ projectionMatrix, zeroVector, totalSliceValueRotation,
+ item.height(), m_cachedSelectionMode, m_labelShader,
+ m_labelObj, activeCamera, false, false, labelPos,
+ alignment, true);
+ }
}
- Qt::AlignmentFlag alignment = (item.height() < 0) ? Qt::AlignBottom : Qt::AlignTop;
- Drawer::LabelPosition labelPos = (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
- m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
- barPosYAdjustment - zeroPosAdjustment
- + item.height(),
- item.translation().z()));
-
- m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix,
- projectionMatrix, zeroVector, sliceValueRotation,
- item.height(), m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, false, false, labelPos,
- alignment, true);
}
}
+ } else if (selectedItem) {
+ // Only draw value for selected item when grid labels are on
+ // Create label texture if we need it
+ if (selectedItem->sliceLabel().isNull() || m_updateLabels) {
+ QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
+ qreal(selectedItem->value()), m_axisCacheY.labelFormat());
+ selectedItem->setSliceLabel(valueLabelText);
+ m_drawer->generateLabelItem(selectedItem->sliceLabelItem(), selectedItem->sliceLabel());
+ m_updateLabels = false;
+ }
+ Qt::AlignmentFlag alignment = (selectedItem->height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
+ Drawer::LabelPosition labelPos =
+ (selectedItem->height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
+ m_dummyBarRenderItem.setTranslation(QVector3D(selectedItem->translation().x(),
+ barPosYAdjustment - zeroPosAdjustment
+ + selectedItem->height(),
+ selectedItem->translation().z()));
+
+ m_drawer->drawLabel(m_dummyBarRenderItem, selectedItem->sliceLabelItem(), viewMatrix,
+ projectionMatrix, zeroVector, totalSliceValueRotation,
+ selectedItem->height(), m_cachedSelectionMode, m_labelShader,
+ m_labelObj, activeCamera, false, false, labelPos,
+ alignment, true);
}
// Draw labels for axes
if (rowMode) {
if (m_sliceTitleItem) {
m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix,
- positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, false, false, Drawer::LabelTop,
- Qt::AlignCenter, true);
+ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, activeCamera, false, false,
+ Drawer::LabelTop, Qt::AlignCenter, true);
}
m_drawer->drawLabel(*dummyItem, m_axisCacheX.titleItem(), viewMatrix, projectionMatrix,
- positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
- Qt::AlignCenter, true);
+ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, activeCamera, false, false,
+ Drawer::LabelBottom, Qt::AlignCenter, true);
} else {
m_drawer->drawLabel(*dummyItem, m_axisCacheZ.titleItem(), viewMatrix, projectionMatrix,
- positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader,
+ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
+ m_labelShader,
m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
Qt::AlignCenter, true);
if (m_sliceTitleItem) {
m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix,
- positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader,
+ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
+ m_labelShader,
m_labelObj, activeCamera, false, false, Drawer::LabelTop,
Qt::AlignCenter, true);
}
@@ -706,9 +885,9 @@ void Bars3DRenderer::drawSlicedScene()
QVector3D labelTrans = QVector3D(-scaleFactor - labelMargin, 0.2f, 0.0f); // y = 0.2 for row/column labels (see barPosYAdjustment)
m_dummyBarRenderItem.setTranslation(labelTrans);
m_drawer->drawLabel(m_dummyBarRenderItem, m_axisCacheY.titleItem(), viewMatrix,
- projectionMatrix, zeroVector, QVector3D(0.0f, 0.0f, 90.0f), 0,
+ projectionMatrix, zeroVector, totalSliceValueRotation, 0,
m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
- false, false, Drawer::LabelMid, Qt::AlignHCenter);
+ false, false, Drawer::LabelMid, Qt::AlignBottom);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
@@ -733,9 +912,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
GLfloat colPos = 0;
GLfloat rowPos = 0;
- QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
-
- int seriesCount = m_visibleSeriesList.size();
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
@@ -748,7 +925,14 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 projectionMatrix;
GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
/ (GLfloat)m_primarySubViewport.height();
- projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
+ if (m_useOrthoProjection) {
+ GLfloat orthoRatio = 2.0f;
+ projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
+ -orthoRatio, orthoRatio,
+ 0.0f, 100.0f);
+ } else {
+ projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
+ }
// Get the view matrix
QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
@@ -808,6 +992,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
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) {
// Render scene into a depth texture for using with shadow mapping
@@ -837,72 +1026,80 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw bars to depth buffer
QVector3D shadowScaler(m_scaleX * m_seriesScaleX * 0.9f, 0.0f,
m_scaleZ * m_seriesScaleZ * 0.9f);
- float seriesPos = m_seriesStart;
- for (int series = 0; series < seriesCount; series++) {
- ObjectHelper *barObj = m_visibleSeriesList.at(series).object();
- QQuaternion seriesRotation(m_visibleSeriesList.at(series).meshRotation());
- for (int row = startRow; row != stopRow; row += stepRow) {
- for (int bar = startBar; bar != stopBar; bar += stepBar) {
- GLfloat shadowOffset = 0.0f;
- const BarRenderItem &item = m_renderingArrays.at(series).at(row).at(bar);
- if (!item.value())
- continue;
- // Set front face culling for negative valued bars and back face culling for
- // positive valued bars to remove peter-panning issues
- if (item.height() > 0) {
- glCullFace(GL_BACK);
- if (m_yFlipped)
- shadowOffset = 0.015f;
- } else {
- glCullFace(GL_FRONT);
- if (!m_yFlipped)
- shadowOffset = -0.015f;
- }
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
+ float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f;
+ ObjectHelper *barObj = cache->object();
+ QQuaternion seriesRotation(cache->meshRotation());
+ const BarRenderItemArray &renderArray = cache->renderArray();
+ for (int row = startRow; row != stopRow; row += stepRow) {
+ const BarRenderItemRow &renderRow = renderArray.at(row);
+ for (int bar = startBar; bar != stopBar; bar += stepBar) {
+ const BarRenderItem &item = renderRow.at(bar);
+ if (!item.value())
+ continue;
+ GLfloat shadowOffset = 0.0f;
+ // Set front face culling for negative valued bars and back face culling
+ // for positive valued bars to remove peter-panning issues
+ if (item.height() > 0) {
+ glCullFace(GL_BACK);
+ if (m_yFlipped)
+ shadowOffset = 0.015f;
+ } else {
+ glCullFace(GL_FRONT);
+ if (!m_yFlipped)
+ shadowOffset = -0.015f;
+ }
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
- colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width());
- rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
+ colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
+ rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
- // Draw shadows for bars "on the other side" a bit off ground to avoid seeing
- // shadows through the ground
- modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
- item.height() + shadowOffset,
- (m_columnDepth - rowPos) / m_scaleFactor);
- // Scale the bars down in X and Z to reduce self-shadowing issues
- shadowScaler.setY(item.height());
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
- modelMatrix.rotate(seriesRotation * item.rotation());
- modelMatrix.scale(shadowScaler);
+ // Draw shadows for bars "on the other side" a bit off ground to avoid
+ // seeing shadows through the ground
+ modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
+ item.height() + shadowOffset,
+ (m_columnDepth - rowPos) / m_scaleFactor);
+ // Scale the bars down in X and Z to reduce self-shadowing issues
+ shadowScaler.setY(item.height());
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
+ modelMatrix.rotate(seriesRotation * item.rotation());
+ modelMatrix.scale(shadowScaler);
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
- m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
+ m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_depthShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf());
- glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_depthShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
+ (void *)0);
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf());
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf());
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT,
- (void *)0);
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT,
+ (void *)0);
- // Free buffers
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
- glDisableVertexAttribArray(m_depthShader->posAtt());
+ glDisableVertexAttribArray(m_depthShader->posAtt());
+ }
}
}
- seriesPos += m_seriesStep;
}
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -919,7 +1116,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
// Skip selection mode drawing if we're slicing or have no selection mode
if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
- && m_selectionState == SelectOnScene && seriesCount > 0) {
+ && m_selectionState == SelectOnScene
+ && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) {
// Bind selection shader
m_selectionShader->bind();
@@ -933,71 +1131,65 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Set clear color to white (= selectionSkipColor)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer
glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled
- float seriesPos = m_seriesStart;
- for (int series = 0; series < seriesCount; series++) {
- ObjectHelper *barObj = m_visibleSeriesList.at(series).object();
- QQuaternion seriesRotation(m_visibleSeriesList.at(series).meshRotation());
- for (int row = startRow; row != stopRow; row += stepRow) {
- for (int bar = startBar; bar != stopBar; bar += stepBar) {
- const BarRenderItem &item = m_renderingArrays.at(series).at(row).at(bar);
- if (!item.value())
- continue;
-
- if (item.height() < 0)
- glCullFace(GL_FRONT);
- else
- glCullFace(GL_BACK);
-
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
-
- colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width());
- rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
-
- modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
- item.height(),
- (m_columnDepth - rowPos) / m_scaleFactor);
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
- modelMatrix.rotate(seriesRotation * item.rotation());
- modelMatrix.scale(QVector3D(m_scaleX * m_seriesScaleX,
- item.height(),
- m_scaleZ * m_seriesScaleZ));
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
+ float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f;
+ ObjectHelper *barObj = cache->object();
+ QQuaternion seriesRotation(cache->meshRotation());
+ const BarRenderItemArray &renderArray = cache->renderArray();
+ for (int row = startRow; row != stopRow; row += stepRow) {
+ const BarRenderItemRow &renderRow = renderArray.at(row);
+ for (int bar = startBar; bar != stopBar; bar += stepBar) {
+ const BarRenderItem &item = renderRow.at(bar);
+ if (!item.value())
+ continue;
+
+ if (item.height() < 0)
+ glCullFace(GL_FRONT);
+ else
+ glCullFace(GL_BACK);
- QVector3D barColor = QVector3D(GLfloat(row) / 255.0f,
- GLfloat(bar) / 255.0f,
- GLfloat(series) / 255.0f);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
- m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
- m_selectionShader->setUniformValue(m_selectionShader->color(), barColor);
+ colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
+ rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_selectionShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf());
- glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
+ modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
+ item.height(),
+ (m_columnDepth - rowPos) / m_scaleFactor);
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
+ modelMatrix.rotate(seriesRotation * item.rotation());
+ modelMatrix.scale(QVector3D(m_scaleX * m_seriesScaleX,
+ item.height(),
+ m_scaleZ * m_seriesScaleZ));
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf());
+ MVPMatrix = projectionViewMatrix * modelMatrix;
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT,
- (void *)0);
+ QVector4D barColor = QVector4D(GLfloat(row) / 255.0f,
+ GLfloat(bar) / 255.0f,
+ GLfloat(cache->visualIndex()) / 255.0f,
+ itemAlpha);
- // Free buffers
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+ m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
+ m_selectionShader->setUniformValue(m_selectionShader->color(), barColor);
- glDisableVertexAttribArray(m_selectionShader->posAtt());
+ m_drawer->drawSelectionObject(m_selectionShader, barObj);
+ }
}
}
- seriesPos += m_seriesStep;
}
+ glCullFace(GL_BACK);
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+ drawLabels(true, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor,
+ columnScaleFactor);
glEnable(GL_DITHER);
// Read color under cursor
- QVector3D clickedColor = Utils::getSelection(m_inputPosition,
+ QVector4D clickedColor = Utils::getSelection(m_inputPosition,
m_viewport.height());
m_clickedPosition = selectionColorToArrayPosition(clickedColor);
m_clickedSeries = selectionColorToSeries(clickedColor);
@@ -1018,18 +1210,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
ShaderHelper *barShader = 0;
GLuint gradientTexture = 0;
Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
- bool haveUniformColorSeries = false;
- bool haveGradientSeries = false;
-
- for (int i = 0; i < seriesCount; i++) {
- if (m_visibleSeriesList.at(i).colorStyle() == Q3DTheme::ColorStyleUniform)
- haveUniformColorSeries = true;
- else
- haveGradientSeries = true;
- }
// Set unchanging shader bindings
- if (haveGradientSeries) {
+ if (m_haveGradientSeries) {
m_barGradientShader->bind();
m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos);
m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix);
@@ -1039,7 +1222,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor);
}
- if (haveUniformColorSeries) {
+ if (m_haveUniformColorSeries) {
m_barShader->bind();
m_barShader->setUniformValue(m_barShader->lightP(), lightPos);
m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
@@ -1052,17 +1235,13 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
previousColorStyle = Q3DTheme::ColorStyleRangeGradient;
}
+ int sliceReserveAmount = 0;
if (m_selectionDirty && m_cachedIsSlicingActivated) {
// Slice doesn't own its items, no need to delete them - just clear
- m_sliceSelection.clear();
- int reserveAmount;
if (rowMode)
- reserveAmount = m_cachedColumnCount;
+ sliceReserveAmount = m_cachedColumnCount;
else
- reserveAmount = m_cachedRowCount;
- if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries))
- reserveAmount *= m_visibleSeriesList.size();
- m_sliceSelection.resize(reserveAmount);
+ sliceReserveAmount = m_cachedRowCount;
// Set slice cache, i.e. axis cache from where slice labels are taken
if (rowMode)
@@ -1073,259 +1252,253 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
// Draw bars
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(0.5f, 1.0f);
GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
GLfloat adjustedHighlightStrength = m_cachedTheme->highlightLightStrength() / 10.0f;
bool barSelectionFound = false;
- BarRenderItem *selectedBar(0);
- QVector3D baseColor;
- QVector3D barColor;
+ QVector4D baseColor;
+ QVector4D barColor;
QVector3D modelScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ);
- bool somethingSelected = (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition());
- float seriesPos = m_seriesStart;
- for (int series = 0; series < seriesCount; series++) {
- const SeriesRenderCache &currentSeries = m_visibleSeriesList.at(series);
- QQuaternion seriesRotation(currentSeries.meshRotation());
- ObjectHelper *barObj = currentSeries.object();
- Q3DTheme::ColorStyle colorStyle = currentSeries.colorStyle();
- bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
-
- // Rebind shader if it has changed
- if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
- if (colorStyleIsUniform)
- barShader = m_barShader;
- else
- barShader = m_barGradientShader;
- barShader->bind();
- }
+ bool somethingSelected =
+ (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition());
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
+ float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f;
+ ObjectHelper *barObj = cache->object();
+ QQuaternion seriesRotation(cache->meshRotation());
+ Q3DTheme::ColorStyle colorStyle = cache->colorStyle();
+ BarRenderItemArray &renderArray = cache->renderArray();
+ bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
+ if (sliceReserveAmount)
+ cache->sliceArray().resize(sliceReserveAmount);
- if (colorStyleIsUniform) {
- baseColor = currentSeries.baseColor();
- } else if ((previousColorStyle != colorStyle)
- && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
- m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f);
- }
+ // Rebind shader if it has changed
+ if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
+ if (colorStyleIsUniform)
+ barShader = m_barShader;
+ else
+ barShader = m_barGradientShader;
+ barShader->bind();
+ }
- // Always use base color when no selection mode
- if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone) {
- if (colorStyleIsUniform)
- barColor = baseColor;
- else
- gradientTexture = currentSeries.baseGradientTexture();
- }
+ if (colorStyleIsUniform) {
+ baseColor = cache->baseColor();
+ } else if ((previousColorStyle != colorStyle)
+ && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
+ m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f);
+ }
- previousColorStyle = colorStyle;
- int sliceSeriesAdjust = 0;
- if (m_selectionDirty && m_cachedIsSlicingActivated) {
- int seriesMultiplier = 0;
- if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries))
- seriesMultiplier = series;
- if (rowMode)
- sliceSeriesAdjust = seriesMultiplier * m_cachedColumnCount;
- else
- sliceSeriesAdjust = seriesMultiplier * m_cachedRowCount;
- }
+ // Always use base color when no selection mode
+ if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone) {
+ if (colorStyleIsUniform)
+ barColor = baseColor;
+ else
+ gradientTexture = cache->baseGradientTexture();
+ }
- for (int row = startRow; row != stopRow; row += stepRow) {
- for (int bar = startBar; bar != stopBar; bar += stepBar) {
- BarRenderItem &item = m_renderingArrays[series][row][bar];
+ previousColorStyle = colorStyle;
- if (item.height() < 0)
- glCullFace(GL_FRONT);
- else
- glCullFace(GL_BACK);
+ 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)
+ glCullFace(GL_FRONT);
+ else
+ glCullFace(GL_BACK);
- QMatrix4x4 modelMatrix;
- QMatrix4x4 itModelMatrix;
- QMatrix4x4 MVPMatrix;
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ QMatrix4x4 MVPMatrix;
- colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width());
- rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
-
- modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
- item.height(),
- (m_columnDepth - rowPos) / m_scaleFactor);
- modelScaler.setY(item.height());
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
- QQuaternion totalRotation = seriesRotation * item.rotation();
- modelMatrix.rotate(totalRotation);
- itModelMatrix.rotate(totalRotation);
- }
- modelMatrix.scale(modelScaler);
- itModelMatrix.scale(modelScaler);
+ colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
+ rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
+
+ modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
+ item.height(),
+ (m_columnDepth - rowPos) / m_scaleFactor);
+ modelScaler.setY(item.height());
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
+ QQuaternion totalRotation = seriesRotation * item.rotation();
+ modelMatrix.rotate(totalRotation);
+ itModelMatrix.rotate(totalRotation);
+ }
+ modelMatrix.scale(modelScaler);
+ itModelMatrix.scale(modelScaler);
#ifdef SHOW_DEPTH_TEXTURE_SCENE
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- GLfloat lightStrength = m_cachedTheme->lightStrength();
- GLfloat shadowLightStrength = adjustedLightStrength;
-
- if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
- Bars3DController::SelectionType selectionType = Bars3DController::SelectionNone;
- if (somethingSelected)
- selectionType = isSelected(row, bar, series);
-
- switch (selectionType) {
- case Bars3DController::SelectionItem: {
- if (colorStyleIsUniform)
- barColor = currentSeries.singleHighlightColor();
- else
- gradientTexture = currentSeries.singleHighlightGradientTexture();
-
- lightStrength = m_cachedTheme->highlightLightStrength();
- shadowLightStrength = adjustedHighlightStrength;
- // Insert position data into render item. We have no ownership, don't delete the previous one
- if (!m_cachedIsSlicingActivated
- && m_visualSelectedBarSeriesIndex == series) {
- selectedBar = &item;
- selectedBar->setPosition(QPoint(row, bar));
- item.setTranslation(modelMatrix.column(3).toVector3D());
- barSelectionFound = true;
- }
- if (m_selectionDirty && m_cachedIsSlicingActivated) {
- QVector3D translation = modelMatrix.column(3).toVector3D();
- if (m_cachedSelectionMode & QAbstract3DGraph::SelectionColumn
- && seriesCount > 1) {
- translation.setZ((m_columnDepth - ((row + 0.5f + seriesPos)
- * (m_cachedBarSpacing.height())))
- / m_scaleFactor);
- }
- item.setTranslation(translation);
- item.setPosition(QPoint(row, bar));
- item.setSeriesIndex(series);
- if (rowMode)
- m_sliceSelection[sliceSeriesAdjust + bar].setItem(item);
+ GLfloat lightStrength = m_cachedTheme->lightStrength();
+ GLfloat shadowLightStrength = adjustedLightStrength;
+
+ if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
+ Bars3DController::SelectionType selectionType =
+ Bars3DController::SelectionNone;
+ if (somethingSelected)
+ selectionType = isSelected(row, bar, cache);
+
+ switch (selectionType) {
+ case Bars3DController::SelectionItem: {
+ if (colorStyleIsUniform)
+ barColor = cache->singleHighlightColor();
else
- m_sliceSelection[sliceSeriesAdjust + row].setItem(item);
- }
- break;
- }
- case Bars3DController::SelectionRow: {
- // Current bar is on the same row as the selected bar
- if (colorStyleIsUniform)
- barColor = currentSeries.multiHighlightColor();
- else
- gradientTexture = currentSeries.multiHighlightGradientTexture();
-
- lightStrength = m_cachedTheme->highlightLightStrength();
- shadowLightStrength = adjustedHighlightStrength;
- if (m_cachedIsSlicingActivated) {
- item.setTranslation(modelMatrix.column(3).toVector3D());
- item.setPosition(QPoint(row, bar));
- if (m_selectionDirty) {
- item.setSeriesIndex(series);
- if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row)
- m_sliceTitleItem = m_axisCacheZ.labelItems().at(row);
- m_sliceSelection[sliceSeriesAdjust + bar].setItem(item);
+ gradientTexture = cache->singleHighlightGradientTexture();
+
+ lightStrength = m_cachedTheme->highlightLightStrength();
+ shadowLightStrength = adjustedHighlightStrength;
+ // Insert position data into render item
+ // We have no ownership, don't delete the previous one
+ if (!m_cachedIsSlicingActivated
+ && m_selectedSeriesCache == cache) {
+ selectedBar = &item;
+ selectedBar->setPosition(QPoint(row, bar));
+ item.setTranslation(modelMatrix.column(3).toVector3D());
+ barSelectionFound = true;
+ }
+ if (m_selectionDirty && m_cachedIsSlicingActivated) {
+ QVector3D translation = modelMatrix.column(3).toVector3D();
+ if (m_cachedSelectionMode & QAbstract3DGraph::SelectionColumn
+ && m_visibleSeriesCount > 1) {
+ translation.setZ((m_columnDepth
+ - ((row + seriesPos)
+ * (m_cachedBarSpacing.height())))
+ / m_scaleFactor);
+ }
+ item.setTranslation(translation);
+ item.setPosition(QPoint(row, bar));
+ if (rowMode)
+ cache->sliceArray()[bar].setItem(item);
+ else
+ cache->sliceArray()[row].setItem(item);
}
+ break;
}
- break;
- }
- case Bars3DController::SelectionColumn: {
- // Current bar is on the same column as the selected bar
- if (colorStyleIsUniform)
- barColor = currentSeries.multiHighlightColor();
- else
- gradientTexture = currentSeries.multiHighlightGradientTexture();
-
- lightStrength = m_cachedTheme->highlightLightStrength();
- shadowLightStrength = adjustedHighlightStrength;
- if (m_cachedIsSlicingActivated) {
- QVector3D translation = modelMatrix.column(3).toVector3D();
- if (seriesCount > 1) {
- translation.setZ((m_columnDepth - ((row + 0.5f + seriesPos)
- * (m_cachedBarSpacing.height())))
- / m_scaleFactor);
+ case Bars3DController::SelectionRow: {
+ // Current bar is on the same row as the selected bar
+ if (colorStyleIsUniform)
+ barColor = cache->multiHighlightColor();
+ else
+ gradientTexture = cache->multiHighlightGradientTexture();
+
+ lightStrength = m_cachedTheme->highlightLightStrength();
+ shadowLightStrength = adjustedHighlightStrength;
+ if (m_cachedIsSlicingActivated) {
+ item.setTranslation(modelMatrix.column(3).toVector3D());
+ item.setPosition(QPoint(row, bar));
+ if (m_selectionDirty) {
+ if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row)
+ m_sliceTitleItem = m_axisCacheZ.labelItems().at(row);
+ cache->sliceArray()[bar].setItem(item);
+ }
}
- item.setTranslation(translation);
- item.setPosition(QPoint(row, bar));
- if (m_selectionDirty) {
- item.setSeriesIndex(series);
- if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar)
- m_sliceTitleItem = m_axisCacheX.labelItems().at(bar);
- m_sliceSelection[sliceSeriesAdjust + row].setItem(item);
+ break;
+ }
+ case Bars3DController::SelectionColumn: {
+ // Current bar is on the same column as the selected bar
+ if (colorStyleIsUniform)
+ barColor = cache->multiHighlightColor();
+ else
+ gradientTexture = cache->multiHighlightGradientTexture();
+
+ lightStrength = m_cachedTheme->highlightLightStrength();
+ shadowLightStrength = adjustedHighlightStrength;
+ if (m_cachedIsSlicingActivated) {
+ QVector3D translation = modelMatrix.column(3).toVector3D();
+ if (m_visibleSeriesCount > 1) {
+ translation.setZ((m_columnDepth
+ - ((row + seriesPos)
+ * (m_cachedBarSpacing.height())))
+ / m_scaleFactor);
+ }
+ item.setTranslation(translation);
+ item.setPosition(QPoint(row, bar));
+ if (m_selectionDirty) {
+ if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar)
+ m_sliceTitleItem = m_axisCacheX.labelItems().at(bar);
+ cache->sliceArray()[row].setItem(item);
+ }
}
+ break;
+ }
+ case Bars3DController::SelectionNone: {
+ // Current bar is not selected, nor on a row or column
+ if (colorStyleIsUniform)
+ barColor = baseColor;
+ else
+ gradientTexture = cache->baseGradientTexture();
+ break;
+ }
}
- break;
- }
- case Bars3DController::SelectionNone: {
- // Current bar is not selected, nor on a row or column
- if (colorStyleIsUniform)
- barColor = baseColor;
- else
- gradientTexture = currentSeries.baseGradientTexture();
- break;
- }
}
- }
- // Skip drawing of 0-height bars
- if (item.height() != 0) {
- // Set shader bindings
- barShader->setUniformValue(barShader->model(), modelMatrix);
- barShader->setUniformValue(barShader->nModel(),
- itModelMatrix.transposed().inverted());
- barShader->setUniformValue(barShader->MVP(), MVPMatrix);
- if (colorStyleIsUniform) {
- barShader->setUniformValue(barShader->color(), barColor);
- } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
- barShader->setUniformValue(barShader->gradientHeight(),
- qAbs(item.height()) / m_gradientFraction);
- }
+ // Skip drawing of 0-height bars
+ if (item.height() != 0) {
+ // Set shader bindings
+ barShader->setUniformValue(barShader->model(), modelMatrix);
+ barShader->setUniformValue(barShader->nModel(),
+ itModelMatrix.transposed().inverted());
+ barShader->setUniformValue(barShader->MVP(), MVPMatrix);
+ if (colorStyleIsUniform) {
+ barShader->setUniformValue(barShader->color(), barColor);
+ } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
+ barShader->setUniformValue(barShader->gradientHeight(),
+ qAbs(item.height()) / m_gradientFraction);
+ }
#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- barShader->setUniformValue(barShader->shadowQ(), m_shadowQualityToShader);
- barShader->setUniformValue(barShader->depth(), depthMVPMatrix);
- barShader->setUniformValue(barShader->lightS(), shadowLightStrength);
- barShader->setUniformValue(barShader->lightColor(), lightColor);
-
- // Draw the object
- m_drawer->drawObject(barShader, barObj, gradientTexture, m_depthTexture);
- } else
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ barShader->setUniformValue(barShader->shadowQ(),
+ m_shadowQualityToShader);
+ barShader->setUniformValue(barShader->depth(), depthMVPMatrix);
+ barShader->setUniformValue(barShader->lightS(), shadowLightStrength);
+ barShader->setUniformValue(barShader->lightColor(), lightColor);
+
+ // Draw the object
+ m_drawer->drawObject(barShader, barObj, gradientTexture,
+ m_depthTexture);
+ } else
#else
- Q_UNUSED(shadowLightStrength);
+ Q_UNUSED(shadowLightStrength);
#endif
- {
- // Set shadowless shader bindings
- barShader->setUniformValue(barShader->lightS(), lightStrength);
+ {
+ // Set shadowless shader bindings
+ barShader->setUniformValue(barShader->lightS(), lightStrength);
- // Draw the object
- m_drawer->drawObject(barShader, barObj, gradientTexture);
+ // Draw the object
+ m_drawer->drawObject(barShader, barObj, gradientTexture);
+ }
}
}
}
}
- seriesPos += m_seriesStep;
}
- // Bind background shader
- m_backgroundShader->bind();
+ glDisable(GL_POLYGON_OFFSET_FILL);
// Reset culling
glCullFace(GL_BACK);
- // Draw background
- GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor;
- GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor;
+ // Bind background shader
+ m_backgroundShader->bind();
+ // Draw background
if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor);
- if (m_hasNegativeValues) {
- backgroundScaler.setY(0.5f);
- modelMatrix.translate(0.0f, m_negativeBackgroundAdjustment, 0.0f);
- } else {
- modelMatrix.translate(0.0f, 1.0f, 0.0f);
- }
+ modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f);
+
modelMatrix.scale(backgroundScaler);
itModelMatrix.scale(backgroundScaler);
modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
@@ -1336,7 +1509,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
+ QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
// Set shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos);
@@ -1373,44 +1546,42 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_drawer->drawObject(m_backgroundShader, m_backgroundObj);
}
- // Draw floor for graph with negatives
- if (m_hasNegativeValues) {
- modelMatrix = QMatrix4x4();
- itModelMatrix = QMatrix4x4();
+ // Draw floor
+ modelMatrix = QMatrix4x4();
+ itModelMatrix = QMatrix4x4();
- modelMatrix.scale(backgroundScaler);
+ 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);
+ 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 = modelMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ 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);
+ // 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);
#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
+ 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(m_backgroundShader, m_gridLineObj);
}
}
@@ -1418,15 +1589,19 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
glDisable(GL_TEXTURE_2D);
// Draw grid lines
- if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) {
+ 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
QQuaternion lineRotation;
// Bind bar shader
lineShader->bind();
// Set unchanging shader bindings
- QVector3D barColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
+ QVector4D barColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
lineShader->setUniformValue(lineShader->lightP(), lightPos);
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), barColor);
@@ -1446,11 +1621,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_cachedTheme->lightStrength() / 2.5f);
}
- GLfloat yFloorLinePosition = 0.0f;
+ GLfloat yFloorLinePosition = gridLineOffset;
if (m_yFlipped)
- yFloorLinePosition -= gridLineOffset;
- else
- yFloorLinePosition += gridLineOffset;
+ yFloorLinePosition = -yFloorLinePosition;
QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth);
@@ -1488,15 +1661,19 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } 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);
for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) {
QMatrix4x4 modelMatrix;
@@ -1526,40 +1703,31 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } 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
- GLfloat heightStep = m_axisCacheY.subSegmentStep();
- GLfloat startLine = 0.0f;
- int segmentCount = m_axisCacheY.segmentCount() * m_axisCacheY.subSegmentCount();
+ int gridLineCount = m_axisCacheY.gridLineCount();
GLfloat zWallLinePosition = -columnScaleFactor + gridLineOffset;
if (m_zFlipped)
zWallLinePosition = -zWallLinePosition;
- if (m_hasNegativeValues) {
- if (m_noZeroInRange)
- startLine = m_axisCacheY.min() - m_axisCacheY.max();
- else
- startLine = m_axisCacheY.min();
- }
-
- GLfloat lineHeight = startLine;
gridLineScaler = QVector3D(rowScaleFactor, gridLineWidth, gridLineWidth);
- for (int segment = 0; segment <= segmentCount; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(0.0f,
- 2.0f * lineHeight / m_heightNormalizer,
+ m_axisCacheY.gridLinePosition(line),
zWallLinePosition);
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
@@ -1583,13 +1751,13 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- lineHeight += heightStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
// Wall lines: side wall
@@ -1602,15 +1770,14 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
else
lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
- lineHeight = startLine;
gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor);
- for (int segment = 0; segment <= segmentCount; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(xWallLinePosition,
- 2.0f * lineHeight / m_heightNormalizer,
+ m_axisCacheY.gridLinePosition(line),
0.0f);
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
@@ -1632,251 +1799,457 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- lineHeight += heightStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
}
}
- // Bind label shader
- m_labelShader->bind();
+ 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) {
+ ShaderHelper *shader = 0;
+ GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
+ GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
+ GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
+ if (drawSelection) {
+ shader = m_selectionShader;
+ // m_selectionShader is already bound
+ } else {
+ shader = m_labelShader;
+ shader->bind();
+
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POLYGON_OFFSET_FILL);
+ float labelAutoAngle = m_axisCacheY.labelAutoRotation();
+ float labelAngleFraction = labelAutoAngle / 90.0f;
+ float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
+ float fractionCamX = activeCamera->xRotation() * labelAngleFraction;
+ float labelsMaxWidth = 0.0f;
+
+ int startIndex;
+ int endIndex;
+ int indexStep;
+
// Y Labels
- int labelNbr = 0;
- GLfloat heightStep = m_axisCacheY.segmentStep();
- GLfloat startLine = 0.0f;
- int labelCount = m_axisCacheY.labels().size();
- if (m_hasNegativeValues) {
- if (m_noZeroInRange)
- startLine = m_axisCacheY.min() - m_axisCacheY.max();
- else
- startLine = m_axisCacheY.min();
- }
- GLfloat labelPos = startLine;
+ int labelCount = m_axisCacheY.labelCount();
GLfloat labelMarginXTrans = labelMargin;
GLfloat labelMarginZTrans = labelMargin;
GLfloat labelXTrans = rowScaleFactor;
GLfloat labelZTrans = columnScaleFactor;
QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
- Qt::AlignmentFlag backAlignment = Qt::AlignLeft;
- Qt::AlignmentFlag sideAlignment = Qt::AlignLeft;
+ Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+ Qt::AlignmentFlag sideAlignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+
if (!m_xFlipped) {
labelXTrans = -labelXTrans;
labelMarginXTrans = -labelMargin;
- backLabelRotation.setY(90.0f);
- sideAlignment = Qt::AlignRight;
}
if (m_zFlipped) {
labelZTrans = -labelZTrans;
labelMarginZTrans = -labelMargin;
- backAlignment = Qt::AlignRight;
- sideLabelRotation.setY(180.f);
}
+
+ if (labelAutoAngle == 0.0f) {
+ if (!m_xFlipped)
+ backLabelRotation.setY(90.0f);
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.f);
+ } else {
+ // Orient side labels somewhat towards the camera
+ if (m_xFlipped) {
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ sideLabelRotation.setY(-fractionCamX);
+ backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
+ } else {
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ sideLabelRotation.setY(-fractionCamX);
+ backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
+ }
+ }
+ sideLabelRotation.setX(-fractionCamY);
+ backLabelRotation.setX(-fractionCamY);
+
+ QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation);
+ QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation);
+
QVector3D backLabelTrans = QVector3D(labelXTrans, 0.0f,
labelZTrans + labelMarginZTrans);
QVector3D sideLabelTrans = QVector3D(-labelXTrans - labelMarginXTrans,
0.0f, -labelZTrans);
- for (int i = 0; i < labelCount; i++) {
- if (m_axisCacheY.labelItems().size() > labelNbr) {
- backLabelTrans.setY(2.0f * labelPos / m_heightNormalizer);
- sideLabelTrans.setY(backLabelTrans.y());
-
- glPolygonOffset(GLfloat(i) / -10.0f, 1.0f);
+ if (m_yFlipped) {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ } else {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
+ }
+ for (int i = startIndex; i != endIndex; i = i + indexStep) {
+ backLabelTrans.setY(m_axisCacheY.labelPosition(i));
+ sideLabelTrans.setY(backLabelTrans.y());
- const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
+ glPolygonOffset(GLfloat(i) / -10.0f, 1.0f);
- // Back wall
- m_dummyBarRenderItem.setTranslation(backLabelTrans);
- m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, backLabelRotation, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, backAlignment);
+ const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i);
- // Side wall
- m_dummyBarRenderItem.setTranslation(sideLabelTrans);
- m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, sideLabelRotation, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, sideAlignment);
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(0.0f, 0.0f, i / 255.0f,
+ alphaForValueSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
- labelNbr++;
- labelPos += heightStep;
+
+ // Back wall
+ m_dummyBarRenderItem.setTranslation(backLabelTrans);
+ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalBackRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, backAlignment, false, drawSelection);
+
+ // Side wall
+ m_dummyBarRenderItem.setTranslation(sideLabelTrans);
+ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalSideRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, sideAlignment, false, drawSelection);
+
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+
+ if (!drawSelection && m_axisCacheY.isTitleVisible()) {
+ sideLabelTrans.setY(m_backgroundAdjustment);
+ backLabelTrans.setY(m_backgroundAdjustment);
+ drawAxisTitleY(sideLabelRotation, backLabelRotation, sideLabelTrans, backLabelTrans,
+ totalSideRotation, totalBackRotation, m_dummyBarRenderItem, activeCamera,
+ labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
+ // Z labels
// Calculate the positions for row and column labels and store them
+ labelsMaxWidth = 0.0f;
+ labelAutoAngle = m_axisCacheZ.labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ 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;
- QVector3D labelRotation(-90.0f, 0.0f, 0.0f);
- if (m_zFlipped)
- labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ GLfloat rowPos = 0.0f;
+ GLfloat colPos = 0.0f;
+ Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+ QVector3D labelRotation;
+
+ if (labelAutoAngle == 0.0f) {
+ labelRotation.setX(-90.0f);
if (m_zFlipped)
- labelRotation.setY(0.0f);
- else
labelRotation.setY(180.0f);
- labelRotation.setZ(180.0f);
- }
-
- Qt::AlignmentFlag alignment = m_xFlipped ? Qt::AlignLeft : Qt::AlignRight;
- for (int row = 0; row != m_cachedRowCount; row++) {
- if (m_axisCacheZ.labelItems().size() > row) {
- // Go through all rows and get position of max+1 or min-1 column, depending on x flip
- // We need only positions for them, labels have already been generated
- rowPos = (row + 0.5f) * m_cachedBarSpacing.height();
- if (m_xFlipped)
- colPos = -colPosValue;
+ if (m_yFlipped) {
+ if (m_zFlipped)
+ labelRotation.setY(0.0f);
else
- colPos = colPosValue;
-
- glPolygonOffset(GLfloat(row) / -10.0f, 1.0f);
-
- QVector3D labelPos = QVector3D(colPos,
- labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
- (m_columnDepth - rowPos) / m_scaleFactor);
-
- m_dummyBarRenderItem.setTranslation(labelPos);
- const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(row);
-
- m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, labelRotation, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, alignment, m_cachedIsSlicingActivated);
+ labelRotation.setY(180.0f);
+ labelRotation.setZ(180.0f);
+ }
+ } else {
+ if (m_zFlipped)
+ labelRotation.setY(180.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ }
}
}
- labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
- if (m_xFlipped)
- labelRotation.setY(-90.0f);
- if (m_yFlipped) {
+
+ QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
+ labelCount = qMin(m_axisCacheZ.labelCount(), m_cachedRowCount);
+ if (m_zFlipped) {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
+ } else {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ }
+ for (int row = startIndex; row != endIndex; row = row + indexStep) {
+ // Go through all rows and get position of max+1 or min-1 column, depending on x flip
+ // We need only positions for them, labels have already been generated
+ rowPos = (row + 0.5f) * m_cachedBarSpacing.height();
if (m_xFlipped)
- labelRotation.setY(90.0f);
+ colPos = -colPosValue;
else
- labelRotation.setY(-90.0f);
- labelRotation.setZ(180.0f);
- }
-
- alignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
- for (int column = 0; column != m_cachedColumnCount; column++) {
- if (m_axisCacheX.labelItems().size() > column) {
- // Go through all columns and get position of max+1 or min-1 row, depending on z flip
- // We need only positions for them, labels have already been generated
- colPos = (column + 0.5f) * m_cachedBarSpacing.width();
- if (m_zFlipped)
- rowPos = -rowPosValue;
- else
- rowPos = rowPosValue;
+ colPos = colPosValue;
- glPolygonOffset(GLfloat(column) / -10.0f, 1.0f);
+ glPolygonOffset(GLfloat(row) / -10.0f, 1.0f);
- QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor,
- labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
- rowPos);
+ QVector3D labelPos = QVector3D(colPos,
+ labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
+ (m_columnDepth - rowPos) / m_scaleFactor);
- m_dummyBarRenderItem.setTranslation(labelPos);
- const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(column);
+ m_dummyBarRenderItem.setTranslation(labelPos);
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(row);
- m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, labelRotation, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, alignment);
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(row / 255.0f, 0.0f, 0.0f, alphaForRowSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
+
+ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, alignment,
+ false, drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
}
- glDisable(GL_POLYGON_OFFSET_FILL);
+ if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
+ QVector3D titleTrans(colPos, 0.0f, 0.0f);
+ drawAxisTitleZ(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem,
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
+ }
- // 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) {
- static const QString rowIndexTag(QStringLiteral("@rowIdx"));
- static const QString rowLabelTag(QStringLiteral("@rowLabel"));
- static const QString rowTitleTag(QStringLiteral("@rowTitle"));
- static const QString colIndexTag(QStringLiteral("@colIdx"));
- static const QString colLabelTag(QStringLiteral("@colLabel"));
- static const QString colTitleTag(QStringLiteral("@colTitle"));
- static const QString valueTitleTag(QStringLiteral("@valueTitle"));
- static const QString valueLabelTag(QStringLiteral("@valueLabel"));
- static const QString seriesNameTag(QStringLiteral("@seriesName"));
-
- // Custom format expects printf format specifier. There is no tag for it.
- labelText = generateValueLabel(
- m_visibleSeriesList[m_visualSelectedBarSeriesIndex].itemLabelFormat(),
- selectedBar->value());
-
- int selBarPosRow = selectedBar->position().x();
- int selBarPosCol = selectedBar->position().y();
- labelText.replace(rowIndexTag, QString::number(selBarPosRow));
- if (m_axisCacheZ.labels().size() > selBarPosRow)
- labelText.replace(rowLabelTag, m_axisCacheZ.labels().at(selBarPosRow));
- else
- labelText.replace(rowLabelTag, QString());
- labelText.replace(rowTitleTag, m_axisCacheZ.title());
- labelText.replace(colIndexTag, QString::number(selBarPosCol));
- if (m_axisCacheX.labels().size() > selBarPosCol)
- labelText.replace(colLabelTag, m_axisCacheX.labels().at(selBarPosCol));
- else
- labelText.replace(colLabelTag, QString());
- labelText.replace(colTitleTag, m_axisCacheX.title());
- labelText.replace(valueTitleTag, m_axisCacheY.title());
-
- if (labelText.contains(valueLabelTag)) {
- QString labelFormat = m_axisCacheY.labelFormat();
- if (labelFormat.isEmpty())
- labelFormat = Utils::defaultLabelFormat();
- QString valueLabelText = generateValueLabel(labelFormat, selectedBar->value());
- labelText.replace(valueLabelTag, valueLabelText);
+ // X labels
+ labelsMaxWidth = 0.0f;
+ labelAutoAngle = m_axisCacheX.labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ fractionCamY = activeCamera->yRotation() * labelAngleFraction;
+ fractionCamX = activeCamera->xRotation() * labelAngleFraction;
+ alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+ if (labelAutoAngle == 0.0f) {
+ labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
+ if (m_xFlipped)
+ labelRotation.setY(-90.0f);
+ if (m_yFlipped) {
+ if (m_xFlipped)
+ labelRotation.setY(90.0f);
+ else
+ labelRotation.setY(-90.0f);
+ labelRotation.setZ(180.0f);
+ }
+ } else {
+ if (m_xFlipped)
+ labelRotation.setY(-90.0f);
+ else
+ labelRotation.setY(90.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f + fractionCamX
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - fractionCamX
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f - fractionCamX
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + fractionCamX
+ * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
}
-
- labelText.replace(seriesNameTag,
- m_visibleSeriesList[m_visualSelectedBarSeriesIndex].name());
-
- setSelectionLabel(labelText);
- m_selectionLabelDirty = false;
}
- m_drawer->generateLabelItem(labelItem, labelText);
- m_selectedBar = selectedBar;
}
+ }
- m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix,
- zeroVector, zeroVector, selectedBar->height(),
- m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, true, false);
-
- // Reset label update flag; they should have been updated when we get here
- m_updateLabels = false;
-
- glEnable(GL_DEPTH_TEST);
+ totalRotation = Utils::calculateRotation(labelRotation);
+ labelCount = qMin(m_axisCacheX.labelCount(), m_cachedColumnCount);
+ if (m_xFlipped) {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
} else {
- m_selectedBar = 0;
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
}
+ for (int column = startIndex; column != endIndex; column = column + indexStep) {
+ // Go through all columns and get position of max+1 or min-1 row, depending on z flip
+ // We need only positions for them, labels have already been generated
+ colPos = (column + 0.5f) * m_cachedBarSpacing.width();
+ if (m_zFlipped)
+ rowPos = -rowPosValue;
+ else
+ rowPos = rowPosValue;
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
+ glPolygonOffset(GLfloat(column) / -10.0f, 1.0f);
- // Release shader
- glUseProgram(0);
+ QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor,
+ labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
+ rowPos);
- m_selectionDirty = false;
+ m_dummyBarRenderItem.setTranslation(labelPos);
+ const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(column);
+
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(0.0f, column / 255.0f, 0.0f,
+ alphaForColumnSelection);
+ shader->setUniformValue(shader->color(), labelColor);
+ }
+
+ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, alignment, false, drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+
+ if (!drawSelection && m_axisCacheX.isTitleVisible()) {
+ QVector3D titleTrans(0.0f, 0.0f, rowPos);
+ drawAxisTitleX(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem,
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
+ }
+
+#if 0 // Debug label
+ static LabelItem debugLabelItem;
+ QString debugLabelString(QStringLiteral("Flips: x:%1 y:%2 z:%3 xr:%4 yr:%5"));
+ QString finalDebugString = debugLabelString.arg(m_xFlipped).arg(m_yFlipped).arg(m_zFlipped)
+ .arg(activeCamera->xRotation()).arg(activeCamera->yRotation());
+ m_dummyBarRenderItem.setTranslation(QVector3D(m_xFlipped ? -1.5f : 1.5f,
+ m_yFlipped ? 1.5f : -1.5f,
+ m_zFlipped ? -1.5f : 1.5f));
+
+ m_drawer->generateLabelItem(debugLabelItem, finalDebugString);
+ m_drawer->drawLabel(m_dummyBarRenderItem, debugLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, identityQuaternion, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, false, Drawer::LabelMid, Qt::AlignHCenter, false, drawSelection);
+#endif
+ glDisable(GL_POLYGON_OFFSET_FILL);
}
void Bars3DRenderer::updateMultiSeriesScaling(bool uniform)
@@ -1884,7 +2257,7 @@ void Bars3DRenderer::updateMultiSeriesScaling(bool uniform)
m_keepSeriesUniform = uniform;
// Recalculate scale factors
- m_seriesScaleX = 1.0f / float(m_visibleSeriesList.size());
+ m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
if (m_keepSeriesUniform)
m_seriesScaleZ = m_seriesScaleX;
else
@@ -1920,46 +2293,41 @@ void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientatio
Abstract3DRenderer::updateAxisRange(orientation, min, max);
if (orientation == QAbstract3DAxis::AxisOrientationY) {
- calculateHeightAdjustment();
// Check if we have negative values
- if (min < 0 && !m_hasNegativeValues) {
+ if (min < 0)
m_hasNegativeValues = true;
- // Reload background
- loadBackgroundMesh();
- emit needRender();
- } else if (min >= 0 && m_hasNegativeValues) {
+ else if (min >= 0)
m_hasNegativeValues = false;
- // Reload background
- loadBackgroundMesh();
- emit needRender();
- }
+ calculateHeightAdjustment();
}
}
-void Bars3DRenderer::updateSelectedBar(const QPoint &position, const QBar3DSeries *series)
+void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable)
+{
+ Abstract3DRenderer::updateAxisReversed(orientation, enable);
+ if (orientation == QAbstract3DAxis::AxisOrientationY)
+ calculateHeightAdjustment();
+}
+
+
+void Bars3DRenderer::updateSelectedBar(const QPoint &position, QBar3DSeries *series)
{
m_selectedBarPos = position;
- m_selectedBarSeries = series;
+ m_selectedSeriesCache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(series, 0));
m_selectionDirty = true;
m_selectionLabelDirty = true;
- m_visualSelectedBarSeriesIndex = -1;
- if (m_renderingArrays.isEmpty()) {
+ if (!m_selectedSeriesCache
+ || !m_selectedSeriesCache->isVisible()
+ || m_selectedSeriesCache->renderArray().isEmpty()) {
m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition();
return;
}
int adjustedZ = m_selectedBarPos.x() - int(m_axisCacheZ.min());
int adjustedX = m_selectedBarPos.y() - int(m_axisCacheX.min());
- int maxZ = m_renderingArrays.at(0).size() - 1;
- int maxX = maxZ >= 0 ? m_renderingArrays.at(0).at(0).size() - 1 : -1;
-
- for (int i = 0; i < m_visibleSeriesList.size(); i++) {
- if (m_visibleSeriesList.at(i).series() == series) {
- m_visualSelectedBarSeriesIndex = i;
- break;
- }
- }
+ int maxZ = m_selectedSeriesCache->renderArray().size() - 1;
+ int maxX = maxZ >= 0 ? m_selectedSeriesCache->renderArray().at(0).size() - 1 : -1;
if (m_selectedBarPos == Bars3DController::invalidSelectionPosition()
|| adjustedZ < 0 || adjustedZ > maxZ
@@ -2020,29 +2388,8 @@ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality
void Bars3DRenderer::loadBackgroundMesh()
{
- if (m_backgroundObj)
- delete m_backgroundObj;
- if (m_hasNegativeValues)
- m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/negativeBackground"));
- else
- m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background"));
- m_backgroundObj->load();
-}
-
-void Bars3DRenderer::loadGridLineMesh()
-{
- if (m_gridLineObj)
- delete m_gridLineObj;
- m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane"));
- m_gridLineObj->load();
-}
-
-void Bars3DRenderer::loadLabelMesh()
-{
- if (m_labelObj)
- delete m_labelObj;
- m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane"));
- m_labelObj->load();
+ ObjectHelper::resetObjectHelper(this, m_backgroundObj,
+ QStringLiteral(":/defaultMeshes/backgroundNoFloor"));
}
void Bars3DRenderer::updateTextures()
@@ -2075,7 +2422,7 @@ void Bars3DRenderer::calculateSceneScalingFactors()
void Bars3DRenderer::calculateHeightAdjustment()
{
- GLfloat newAdjustment = 0.0f;
+ GLfloat newAdjustment = 1.0f;
GLfloat maxAbs = qFabs(m_axisCacheY.max());
if (m_axisCacheY.max() < 0.0f) {
@@ -2086,7 +2433,8 @@ void Bars3DRenderer::calculateHeightAdjustment()
}
// Height fractions are used in gradient calculations and are therefore doubled
- if (m_axisCacheY.max() < 0.0f || m_axisCacheY.min() > 0.0f) {
+ // 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) {
m_noZeroInRange = true;
m_gradientFraction = 2.0f;
} else {
@@ -2095,22 +2443,24 @@ void Bars3DRenderer::calculateHeightAdjustment()
m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f;
}
- // Calculate translation adjustment for negative background
- newAdjustment = qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) * 2.0f - 0.5f;
+ // Calculate translation adjustment for background floor
+ newAdjustment = (qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) - 0.5f) * 2.0f;
+ if (m_axisCacheY.reversed())
+ newAdjustment = -newAdjustment;
- if (newAdjustment != m_negativeBackgroundAdjustment) {
- m_hasHeightAdjustmentChanged = true;
- m_negativeBackgroundAdjustment = newAdjustment;
+ if (newAdjustment != m_backgroundAdjustment) {
+ m_backgroundAdjustment = newAdjustment;
+ m_axisCacheY.setTranslate(m_backgroundAdjustment - 1.0f);
}
}
-Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar, int seriesIndex)
+Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar,
+ const BarSeriesRenderCache *cache)
{
Bars3DController::SelectionType isSelectedType = Bars3DController::SelectionNone;
if ((m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)
- && m_visualSelectedBarSeriesIndex >= 0)
- || seriesIndex == m_visualSelectedBarSeriesIndex) {
+ && m_selectedSeriesCache) || cache == m_selectedSeriesCache) {
if (row == m_visualSelectedBarPos.x() && bar == m_visualSelectedBarPos.y()
&& (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem))) {
isSelectedType = Bars3DController::SelectionItem;
@@ -2126,24 +2476,68 @@ Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar, int
return isSelectedType;
}
-QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector3D &selectionColor)
+QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector4D &selectionColor)
{
- QPoint position;
- if (selectionColor == selectionSkipColor) {
- position = Bars3DController::invalidSelectionPosition();
- } else {
+ QPoint position = Bars3DController::invalidSelectionPosition();
+ m_clickedType = QAbstract3DGraph::ElementNone;
+ m_selectedLabelIndex = -1;
+ m_selectedCustomItemIndex = -1;
+ if (selectionColor.w() == itemAlpha) {
+ // Normal selection item
position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())),
int(selectionColor.y()) + int(m_axisCacheX.min()));
+ // Pass item clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementSeries;
+ } else if (selectionColor.w() == labelRowAlpha) {
+ // Row selection
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) {
+ // Use column from previous selection in case we have row + column mode
+ GLint previousCol = qMax(0, m_selectedBarPos.y()); // Use 0 if previous is invalid
+ position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), previousCol);
+ }
+ m_selectedLabelIndex = selectionColor.x();
+ // Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
+ } else if (selectionColor.w() == labelColumnAlpha) {
+ // Column selection
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
+ // Use row from previous selection in case we have row + column mode
+ GLint previousRow = qMax(0, m_selectedBarPos.x()); // Use 0 if previous is invalid
+ position = QPoint(previousRow, int(selectionColor.y()) + int(m_axisCacheX.min()));
+ }
+ m_selectedLabelIndex = selectionColor.y();
+ // Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
+ } else if (selectionColor.w() == labelValueAlpha) {
+ // Value selection
+ position = Bars3DController::invalidSelectionPosition();
+ m_selectedLabelIndex = selectionColor.z();
+ // Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
+ } else if (selectionColor.w() == customItemAlpha) {
+ // Custom item selection
+ position = Bars3DController::invalidSelectionPosition();
+ m_selectedCustomItemIndex = int(selectionColor.x())
+ + (int(selectionColor.y()) << 8)
+ + (int(selectionColor.z()) << 16);
+ m_clickedType = QAbstract3DGraph::ElementCustomItem;
}
return position;
}
-QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector3D &selectionColor)
+QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector4D &selectionColor)
{
- if (selectionColor == selectionSkipColor)
+ if (selectionColor == selectionSkipColor) {
return 0;
- else
- return static_cast<QBar3DSeries *>(m_visibleSeriesList.at(int(selectionColor.z())).series());
+ } else {
+ int seriesIndexFromColor(selectionColor.z());
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
+ if (cache->visualIndex() == seriesIndexFromColor)
+ return cache->series();
+ }
+ }
+ return 0;
}
void Bars3DRenderer::updateSlicingActive(bool isSlicing)
@@ -2190,10 +2584,7 @@ void Bars3DRenderer::initSelectionShader()
void Bars3DRenderer::initSelectionBuffer()
{
- if (m_selectionTexture) {
- m_textureHelper->deleteTexture(&m_selectionTexture);
- m_selectionTexture = 0;
- }
+ m_textureHelper->deleteTexture(&m_selectionTexture);
if (m_cachedIsSlicingActivated || m_primarySubViewport.size().isEmpty())
return;
@@ -2215,10 +2606,7 @@ void Bars3DRenderer::initDepthShader()
void Bars3DRenderer::updateDepthBuffer()
{
- if (m_depthTexture) {
- m_textureHelper->deleteTexture(&m_depthTexture);
- m_depthTexture = 0;
- }
+ m_textureHelper->deleteTexture(&m_depthTexture);
if (m_primarySubViewport.size().isEmpty())
return;
@@ -2250,4 +2638,29 @@ void Bars3DRenderer::initLabelShaders(const QString &vertexShader, const QString
m_labelShader->initialize();
}
+QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute)
+{
+ float xTrans = 0.0f;
+ float yTrans = 0.0f;
+ 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;
+ yTrans = m_axisCacheY.positionAt(position.y());
+ } else {
+ xTrans = position.x() * m_scaleX;
+ yTrans = position.y() + m_backgroundAdjustment;
+ zTrans = position.z() * m_scaleZ;
+ }
+ return QVector3D(xTrans, yTrans, zTrans);
+}
+
+void Bars3DRenderer::updateAspectRatio(float ratio)
+{
+ Q_UNUSED(ratio)
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h
index 37ac2b76..3a0ab3b8 100644
--- a/src/datavisualization/engine/bars3drenderer_p.h
+++ b/src/datavisualization/engine/bars3drenderer_p.h
@@ -32,19 +32,17 @@
#include "datavisualizationglobal_p.h"
#include "bars3dcontroller_p.h"
#include "abstract3drenderer_p.h"
-#include "qbardataproxy.h"
#include "barrenderitem_p.h"
class QPoint;
class QSizeF;
-class QOpenGLShaderProgram;
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class ShaderHelper;
-class ObjectHelper;
class LabelItem;
class Q3DScene;
+class BarSeriesRenderCache;
class QT_DATAVISUALIZATION_EXPORT Bars3DRenderer : public Abstract3DRenderer
{
@@ -60,12 +58,8 @@ private:
// Internal state
BarRenderItem *m_selectedBar; // points to renderitem array
- QVector<BarRenderSliceItem> m_sliceSelection;
AxisRenderCache *m_sliceCache; // not owned
const LabelItem *m_sliceTitleItem; // not owned
- bool m_xFlipped;
- bool m_zFlipped;
- bool m_yFlipped;
bool m_updateLabels;
ShaderHelper *m_barShader;
ShaderHelper *m_barGradientShader;
@@ -73,9 +67,6 @@ private:
ShaderHelper *m_selectionShader;
ShaderHelper *m_backgroundShader;
ShaderHelper *m_labelShader;
- ObjectHelper *m_backgroundObj;
- ObjectHelper *m_gridLineObj;
- ObjectHelper *m_labelObj;
GLuint m_bgrTexture;
GLuint m_depthTexture;
GLuint m_selectionTexture;
@@ -86,7 +77,7 @@ private:
GLint m_shadowQualityMultiplier;
GLfloat m_heightNormalizer;
GLfloat m_gradientFraction;
- GLfloat m_negativeBackgroundAdjustment;
+ GLfloat m_backgroundAdjustment;
GLfloat m_rowWidth;
GLfloat m_columnDepth;
GLfloat m_maxDimension;
@@ -95,12 +86,10 @@ private:
GLfloat m_scaleFactor;
GLfloat m_maxSceneSize;
QPoint m_visualSelectedBarPos;
- int m_visualSelectedBarSeriesIndex;
- bool m_hasHeightAdjustmentChanged;
+ bool m_resetCameraBaseOrientation;
QPoint m_selectedBarPos;
- const QBar3DSeries *m_selectedBarSeries;
+ BarSeriesRenderCache *m_selectedSeriesCache;
BarRenderItem m_dummyBarRenderItem;
- QVector<BarRenderItemArray> m_renderingArrays;
bool m_noZeroInRange;
float m_seriesScaleX;
float m_seriesScaleZ;
@@ -108,15 +97,26 @@ private:
float m_seriesStart;
QPoint m_clickedPosition;
bool m_keepSeriesUniform;
+ bool m_haveUniformColorSeries;
+ bool m_haveGradientSeries;
+ float m_zeroPosition;
public:
explicit Bars3DRenderer(Bars3DController *controller);
~Bars3DRenderer();
void updateData();
+ void updateSeries(const QList<QAbstract3DSeries *> &seriesList);
+ SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
+ void updateRows(const QVector<Bars3DController::ChangeRow> &rows);
+ void updateItems(const QVector<Bars3DController::ChangeItem> &items);
void updateScene(Q3DScene *scene);
void render(GLuint defaultFboHandle = 0);
+ QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
+
+ void updateAspectRatio(float ratio);
+
protected:
virtual void initializeOpenGL();
@@ -126,13 +126,15 @@ public slots:
const QSizeF &spacing = QSizeF(1.0, 1.0),
bool relative = true);
void updateSlicingActive(bool isSlicing);
- void updateSelectedBar(const QPoint &position, const QBar3DSeries *series);
+ void updateSelectedBar(const QPoint &position, QBar3DSeries *series);
inline QPoint clickedPosition() const { return m_clickedPosition; }
void resetClickedStatus();
// Overloaded from abstract renderer
virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min,
float max);
+ virtual void updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation,
+ bool enable);
private:
virtual void initShaders(const QString &vertexShader, const QString &fragmentShader);
@@ -143,10 +145,11 @@ private:
void drawSlicedScene();
void drawScene(GLuint defaultFboHandle);
+ void drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
+ GLfloat rowScaleFactor, GLfloat columnScaleFactor);
void loadBackgroundMesh();
- void loadGridLineMesh();
- void loadLabelMesh();
void initSelectionShader();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
@@ -157,13 +160,15 @@ private:
#endif
void calculateSceneScalingFactors();
void calculateHeightAdjustment();
- Abstract3DController::SelectionType isSelected(int row, int bar, int seriesIndex);
- QPoint selectionColorToArrayPosition(const QVector3D &selectionColor);
- QBar3DSeries *selectionColorToSeries(const QVector3D &selectionColor);
+ Abstract3DController::SelectionType isSelected(int row, int bar,
+ const BarSeriesRenderCache *cache);
+ QPoint selectionColorToArrayPosition(const QVector4D &selectionColor);
+ QBar3DSeries *selectionColorToSeries(const QVector4D &selectionColor);
- Q_DISABLE_COPY(Bars3DRenderer)
+ inline void updateRenderRow(const QBarDataRow *dataRow, BarRenderItemRow &renderRow);
+ inline void updateRenderItem(const QBarDataItem &dataItem, BarRenderItem &renderItem);
- friend class BarRenderItem;
+ Q_DISABLE_COPY(Bars3DRenderer)
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/barseriesrendercache.cpp b/src/datavisualization/engine/barseriesrendercache.cpp
new file mode 100644
index 00000000..95da3680
--- /dev/null
+++ b/src/datavisualization/engine/barseriesrendercache.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 "barseriesrendercache_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+BarSeriesRenderCache::BarSeriesRenderCache(QAbstract3DSeries *series,
+ Abstract3DRenderer *renderer)
+ : SeriesRenderCache(series, renderer),
+ m_visualIndex(-1)
+{
+}
+
+BarSeriesRenderCache::~BarSeriesRenderCache()
+{
+}
+
+void BarSeriesRenderCache::cleanup(TextureHelper *texHelper)
+{
+ m_renderArray.clear();
+ m_sliceArray.clear();
+
+ SeriesRenderCache::cleanup(texHelper);
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/barseriesrendercache_p.h b/src/datavisualization/engine/barseriesrendercache_p.h
new file mode 100644
index 00000000..4e169300
--- /dev/null
+++ b/src/datavisualization/engine/barseriesrendercache_p.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 BARSERIESRENDERCACHE_P_H
+#define BARSERIESRENDERCACHE_P_H
+
+#include "datavisualizationglobal_p.h"
+#include "seriesrendercache_p.h"
+#include "qbar3dseries_p.h"
+#include "barrenderitem_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class BarSeriesRenderCache : public SeriesRenderCache
+{
+public:
+ BarSeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer);
+ virtual ~BarSeriesRenderCache();
+
+ void cleanup(TextureHelper *texHelper);
+
+ inline BarRenderItemArray &renderArray() { return m_renderArray; }
+ inline QBar3DSeries *series() const { return static_cast<QBar3DSeries *>(m_series); }
+ inline QVector<BarRenderSliceItem> &sliceArray() { return m_sliceArray; }
+ inline void setVisualIndex(int index) { m_visualIndex = index; }
+ inline int visualIndex() {return m_visualIndex; }
+
+protected:
+ BarRenderItemArray m_renderArray;
+ QVector<BarRenderSliceItem> m_sliceArray;
+ int m_visualIndex; // order of the series is relevant
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp
index 55a2c2a5..b8726840 100644
--- a/src/datavisualization/engine/drawer.cpp
+++ b/src/datavisualization/engine/drawer.cpp
@@ -18,13 +18,11 @@
#include "drawer_p.h"
#include "shaderhelper_p.h"
-#include "objecthelper_p.h"
-#include "abstractobjecthelper_p.h"
#include "surfaceobject_p.h"
-#include "q3dcamera.h"
#include "utils_p.h"
#include "texturehelper_p.h"
#include "abstract3drenderer_p.h"
+#include "scatterpointbufferhelper_p.h"
#include <QtGui/QMatrix4x4>
#include <QtCore/qmath.h>
@@ -45,18 +43,28 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
// Vertex array buffer for point
const GLfloat point_data[] = {0.0f, 0.0f, 0.0f};
+// Vertex array buffer for line
+const GLfloat line_data[] = {
+ -1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+};
+
Drawer::Drawer(Q3DTheme *theme)
: m_theme(theme),
m_textureHelper(0),
- m_pointbuffer(0)
+ m_pointbuffer(0),
+ m_linebuffer(0),
+ m_scaledFontSize(0.0f)
{
}
Drawer::~Drawer()
{
delete m_textureHelper;
- if (QOpenGLContext::currentContext())
+ if (QOpenGLContext::currentContext()) {
glDeleteBuffers(1, &m_pointbuffer);
+ glDeleteBuffers(1, &m_linebuffer);
+ }
}
void Drawer::initializeOpenGL()
@@ -70,6 +78,7 @@ void Drawer::initializeOpenGL()
void Drawer::setTheme(Q3DTheme *theme)
{
m_theme = theme;
+ m_scaledFontSize = 0.05f + m_theme->font().pointSizeF() / 500.0f;
emit drawerChanged();
}
@@ -140,6 +149,18 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
}
}
+void Drawer::drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object)
+{
+ glEnableVertexAttribArray(shader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
+ glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
+ glDrawElements(GL_TRIANGLES, object->indexCount(), GL_UNSIGNED_SHORT, (void *)0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glDisableVertexAttribArray(shader->posAtt());
+}
+
void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object)
{
// 1st attribute buffer : vertices
@@ -162,6 +183,8 @@ void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object)
void Drawer::drawPoint(ShaderHelper *shader)
{
+ // Draw a single point
+
// Generate vertex buffer for point if it does not exist
if (!m_pointbuffer) {
glGenBuffers(1, &m_pointbuffer);
@@ -183,13 +206,55 @@ void Drawer::drawPoint(ShaderHelper *shader)
glDisableVertexAttribArray(shader->posAtt());
}
+void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object)
+{
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(shader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->pointBuf());
+ glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+
+ // Draw the points
+ glDrawArrays(GL_POINTS, 0, object->indexCount());
+
+ // Free buffers
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(shader->posAtt());
+}
+
+void Drawer::drawLine(ShaderHelper *shader)
+{
+ // Draw a single line
+
+ // Generate vertex buffer for line if it does not exist
+ if (!m_linebuffer) {
+ glGenBuffers(1, &m_linebuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_linebuffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(line_data), line_data, GL_STATIC_DRAW);
+ }
+
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(shader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, m_linebuffer);
+ glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+
+ // Draw the line
+ glDrawArrays(GL_LINES, 0, 2);
+
+ // Free buffers
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(shader->posAtt());
+}
+
void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem,
const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix,
- const QVector3D &positionComp, const QVector3D &rotation,
+ const QVector3D &positionComp, const QQuaternion &rotation,
GLfloat itemHeight, QAbstract3DGraph::SelectionFlags mode,
ShaderHelper *shader, ObjectHelper *object,
const Q3DCamera *camera, bool useDepth, bool rotateAlong,
- LabelPosition position, Qt::AlignmentFlag alignment, bool isSlicing)
+ LabelPosition position, Qt::AlignmentFlag alignment, bool isSlicing,
+ bool isSelecting)
{
// Draw label
if (!labelItem.textureId())
@@ -246,68 +311,20 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte
}
// Calculate scale factor to get uniform font size
- GLfloat scaledFontSize = 0.05f + m_theme->font().pointSizeF() / 500.0f;
- GLfloat scaleFactor = scaledFontSize / (GLfloat)textureSize.height();
+ GLfloat scaleFactor = m_scaledFontSize / (GLfloat)textureSize.height();
// Apply alignment
- GLfloat xAlignment = 0.0f;
- GLfloat yAlignment = 0.0f;
- GLfloat zAlignment = 0.0f;
- GLfloat sinRotY = qFabs(qSin(qDegreesToRadians(rotation.y())));
- GLfloat cosRotY = qFabs(qCos(qDegreesToRadians(rotation.y())));
- GLfloat sinRotZ = 0.0f;
- GLfloat cosRotZ = 0.0f;
- if (rotation.z()) {
- sinRotZ = qFabs(qSin(qDegreesToRadians(rotation.z())));
- cosRotZ = qFabs(qCos(qDegreesToRadians(rotation.z())));
- }
- switch (alignment) {
- case Qt::AlignLeft: {
- if (rotation.z() && rotation.z() != 180.0f && !rotation.y()) {
- xAlignment = ((-(GLfloat)textureSize.width() * scaleFactor) * cosRotZ
- - ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ) / 2.0f;
- yAlignment = (((GLfloat)textureSize.width() * scaleFactor) * sinRotZ
- + ((GLfloat)textureSize.height() * scaleFactor) * cosRotZ) / 2.0f;
- } else {
- xAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * cosRotY;
- zAlignment = ((GLfloat)textureSize.width() * scaleFactor) * sinRotY;
- }
- break;
- }
- case Qt::AlignRight: {
- if (rotation.z() && rotation.z() != 180.0f && !rotation.y()) {
- xAlignment = (((GLfloat)textureSize.width() * scaleFactor) * cosRotZ
- + ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ) / 2.0f;
- yAlignment = (((GLfloat)textureSize.width() * scaleFactor) * sinRotZ
- + ((GLfloat)textureSize.height() * scaleFactor) * cosRotZ) / 2.0f;
- } else {
- xAlignment = ((GLfloat)textureSize.width() * scaleFactor) * cosRotY;
- zAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * sinRotY;
- }
- break;
- }
- case Qt::AlignTop: {
- yAlignment = ((GLfloat)textureSize.width() * scaleFactor) * cosRotY;
- break;
- }
- case Qt::AlignBottom: {
- yAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * cosRotY;
- break;
- }
- case Qt::AlignHCenter: {
- xAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * cosRotZ
- - ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ;
- break;
- }
- case Qt::AlignVCenter: {
- yAlignment = ((GLfloat)textureSize.width() * scaleFactor) * cosRotZ
- + ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ;
- break;
- }
- default: {
- break;
- }
- }
+ QVector3D anchorPoint;
+
+ if (alignment & Qt::AlignLeft)
+ anchorPoint.setX(float(textureSize.width()) * scaleFactor);
+ else if (alignment & Qt::AlignRight)
+ anchorPoint.setX(float(-textureSize.width()) * scaleFactor);
+
+ if (alignment & Qt::AlignTop)
+ anchorPoint.setY(float(-textureSize.height()) * scaleFactor);
+ else if (alignment & Qt::AlignBottom)
+ anchorPoint.setY(float(textureSize.height()) * scaleFactor);
if (position < LabelBottom) {
xPosition = item.translation().x();
@@ -318,15 +335,9 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte
}
// Position label
- modelMatrix.translate(xPosition + xAlignment, yPosition + yAlignment, zPosition + zAlignment);
+ modelMatrix.translate(xPosition, yPosition, zPosition);
// Rotate
- QQuaternion rotQuatX = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, rotation.x());
- QQuaternion rotQuatY = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rotation.y());
- QQuaternion rotQuatZ = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, rotation.z());
- QQuaternion rotQuaternion = rotQuatY * rotQuatZ * rotQuatX;
- modelMatrix.rotate(rotQuaternion);
-
if (useDepth && !rotateAlong) {
float yComp = float(qRadiansToDegrees(qTan(positionComp.y() / cameraDistance)));
// Apply negative camera rotations to keep labels facing camera
@@ -334,20 +345,27 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte
float camRotationY = camera->yRotation();
modelMatrix.rotate(-camRotationX, 0.0f, 1.0f, 0.0f);
modelMatrix.rotate(-camRotationY - yComp, 1.0f, 0.0f, 0.0f);
+ } else {
+ modelMatrix.rotate(rotation);
}
+ modelMatrix.translate(anchorPoint);
// Scale label based on text size
modelMatrix.scale(QVector3D((GLfloat)textureSize.width() * scaleFactor,
- scaledFontSize,
+ m_scaledFontSize,
0.0f));
MVPMatrix = projectionmatrix * viewmatrix * modelMatrix;
- // Set shader bindings
shader->setUniformValue(shader->MVP(), MVPMatrix);
- // Draw the object
- drawObject(shader, object, labelItem.textureId());
+ if (isSelecting) {
+ // Draw the selection object
+ drawSelectionObject(shader, object);
+ } else {
+ // Draw the object
+ drawObject(shader, object, labelItem.textureId());
+ }
}
void Drawer::generateSelectionLabelTexture(Abstract3DRenderer *renderer)
diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h
index 8e98aa3a..ffcea315 100644
--- a/src/datavisualization/engine/drawer_p.h
+++ b/src/datavisualization/engine/drawer_p.h
@@ -44,6 +44,7 @@ class SurfaceObject;
class TextureHelper;
class Q3DCamera;
class Abstract3DRenderer;
+class ScatterPointBufferHelper;
class Drawer : public QObject, public QOpenGLFunctions
{
@@ -71,18 +72,23 @@ public:
void setTheme(Q3DTheme *theme);
Q3DTheme *theme() const;
QFont font() const;
+ inline GLfloat scaledFontSize() const { return m_scaledFontSize; }
void drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId = 0,
GLuint depthTextureId = 0);
+ void drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object);
void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object);
void drawPoint(ShaderHelper *shader);
+ void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object);
+ void drawLine(ShaderHelper *shader);
void drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem,
const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix,
- const QVector3D &positionComp, const QVector3D &rotation, GLfloat itemHeight,
+ const QVector3D &positionComp, const QQuaternion &rotation, GLfloat itemHeight,
QAbstract3DGraph::SelectionFlags mode, ShaderHelper *shader, ObjectHelper *object,
const Q3DCamera *camera, bool useDepth = false, bool rotateAlong = false,
LabelPosition position = LabelOver,
- Qt::AlignmentFlag alignment = Qt::AlignCenter, bool isSlicing = false);
+ Qt::AlignmentFlag alignment = Qt::AlignCenter, bool isSlicing = false,
+ bool isSelecting = false);
void generateSelectionLabelTexture(Abstract3DRenderer *item);
void generateLabelItem(LabelItem &item, const QString &text, int widestLabel = 0);
@@ -94,6 +100,8 @@ private:
Q3DTheme *m_theme;
TextureHelper *m_textureHelper;
GLuint m_pointbuffer;
+ GLuint m_linebuffer;
+ GLfloat m_scaledFontSize;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/engine.pri b/src/datavisualization/engine/engine.pri
index 9c2e71e4..96fa7fa9 100644
--- a/src/datavisualization/engine/engine.pri
+++ b/src/datavisualization/engine/engine.pri
@@ -26,7 +26,9 @@ HEADERS += $$PWD/qabstract3dgraph_p.h \
$$PWD/q3dobject.h \
$$PWD/q3dobject_p.h \
$$PWD/q3dscene_p.h \
- $$PWD/surfaceseriesrendercache_p.h
+ $$PWD/surfaceseriesrendercache_p.h \
+ $$PWD/barseriesrendercache_p.h \
+ $$PWD/scatterseriesrendercache_p.h
SOURCES += $$PWD/qabstract3dgraph.cpp \
$$PWD/q3dbars.cpp \
@@ -48,7 +50,9 @@ SOURCES += $$PWD/qabstract3dgraph.cpp \
$$PWD/q3dlight.cpp \
$$PWD/q3dobject.cpp \
$$PWD/q3dscene.cpp \
- $$PWD/surfaceseriesrendercache.cpp
+ $$PWD/surfaceseriesrendercache.cpp \
+ $$PWD/barseriesrendercache.cpp \
+ $$PWD/scatterseriesrendercache.cpp
RESOURCES += engine/engine.qrc
diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc
index 18cba7fe..673b6ee0 100644
--- a/src/datavisualization/engine/engine.qrc
+++ b/src/datavisualization/engine/engine.qrc
@@ -24,7 +24,7 @@
<file alias="bevelbarSmoothFull">meshes/barFilledSmooth.obj</file>
<file alias="barFull">meshes/cubeFilledFlat.obj</file>
<file alias="barSmoothFull">meshes/cubeFilledSmooth.obj</file>
- <file alias="negativeBackground">meshes/backgroundNegatives.obj</file>
+ <file alias="backgroundNoFloor">meshes/backgroundNoFloor.obj</file>
<file alias="minimal">meshes/minimalFlat.obj</file>
<file alias="minimalSmooth">meshes/minimalSmooth.obj</file>
<file alias="arrow">meshes/arrowFlat.obj</file>
@@ -54,5 +54,8 @@
<file alias="fragmentSurfaceShadowNoTex">shaders/surfaceShadowNoTex.frag</file>
<file alias="fragmentSurfaceShadowFlat">shaders/surfaceShadowFlat.frag</file>
<file alias="vertexSurfaceShadowFlat">shaders/surfaceShadowFlat.vert</file>
+ <file alias="fragmentTexture">shaders/texture.frag</file>
+ <file alias="fragmentTextureES2">shaders/texture_ES2.frag</file>
+ <file alias="vertexTexture">shaders/texture.vert</file>
</qresource>
</RCC>
diff --git a/src/datavisualization/engine/meshes/backgroundNegatives.obj b/src/datavisualization/engine/meshes/backgroundNoFloor.obj
index 76c7c1d8..0b94617f 100644
--- a/src/datavisualization/engine/meshes/backgroundNegatives.obj
+++ b/src/datavisualization/engine/meshes/backgroundNoFloor.obj
@@ -1,18 +1,18 @@
# Blender v2.66 (sub 0) OBJ File: 'backgroudNegativesWall.blend'
# www.blender.org
o Cube
-v 0.999999 1.000000 1.000001
+v 1.000000 1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
-v 1.000000 -3.000000 1.000000
-v -1.000000 -3.000000 1.000000
-v -1.000000 -3.000000 -1.000000
-vt 0.000100 0.000100
-vt 0.500000 0.000100
-vt 0.500000 0.999900
-vt 0.999900 0.000100
-vt 0.999900 0.999900
-vt 0.000100 0.999900
+v 1.000000 -1.000000 1.000000
+v -1.000000 -1.000000 1.000000
+v -1.000000 -1.000000 -1.000000
+vt 0.000000 0.000000
+vt 0.500000 0.000000
+vt 0.500000 1.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
vn 0.000000 -0.000000 -1.000000
vn 1.000000 0.000000 0.000000
s off
diff --git a/src/datavisualization/engine/q3dbars.cpp b/src/datavisualization/engine/q3dbars.cpp
index 7b37715f..a903d831 100644
--- a/src/datavisualization/engine/q3dbars.cpp
+++ b/src/datavisualization/engine/q3dbars.cpp
@@ -16,13 +16,7 @@
**
****************************************************************************/
-#include "q3dbars.h"
#include "q3dbars_p.h"
-#include "bars3dcontroller_p.h"
-#include "qvalue3daxis.h"
-#include "qcategory3daxis.h"
-#include "q3dcamera.h"
-#include "qbar3dseries_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -30,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class Q3DBars
* \inmodule QtDataVisualization
* \brief The Q3DBars class provides methods for rendering 3D bar graphs.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* This class enables developers to render bar graphs in 3D and to view them by rotating the scene
* freely. Rotation is done by holding down the right mouse button and moving the mouse. Zooming
diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp
index 50f2e319..9866a34e 100644
--- a/src/datavisualization/engine/q3dcamera.cpp
+++ b/src/datavisualization/engine/q3dcamera.cpp
@@ -16,10 +16,7 @@
**
****************************************************************************/
-#include "q3dcamera.h"
#include "q3dcamera_p.h"
-#include "q3dscene.h"
-#include "q3dobject.h"
#include "utils_p.h"
#include <QtCore/qmath.h>
@@ -30,7 +27,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class Q3DCamera
* \inmodule QtDataVisualization
* \brief Representation of a camera in 3D space.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* Q3DCamera represents a basic orbit around centerpoint 3D camera that is used when rendering the
* data visualization. The class offers simple methods for rotating the camera around the origin
@@ -85,7 +82,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* data visualization. The type offers simple methods for rotating the camera around the origin
* and setting zoom level.
*
- * For Camera3D enums, see \l Q3DCamera::CameraPreset
+ * For Camera3D enums, see \l{Q3DCamera::CameraPreset}.
*/
/*!
@@ -208,10 +205,12 @@ float Q3DCamera::xRotation() const {
void Q3DCamera::setXRotation(float rotation)
{
- if (d_ptr->m_wrapXRotation)
+ if (d_ptr->m_wrapXRotation) {
rotation = Utils::wrapValue(rotation, d_ptr->m_minXRotation, d_ptr->m_maxXRotation);
- else
- rotation = qBound(float(d_ptr->m_minXRotation), float(rotation), float(d_ptr->m_maxXRotation));
+ } else {
+ rotation = qBound(float(d_ptr->m_minXRotation), float(rotation),
+ float(d_ptr->m_maxXRotation));
+ }
if (d_ptr->m_xRotation != rotation) {
d_ptr->setXRotation(rotation);
@@ -235,10 +234,12 @@ float Q3DCamera::yRotation() const {
void Q3DCamera::setYRotation(float rotation)
{
- if (d_ptr->m_wrapYRotation)
+ if (d_ptr->m_wrapYRotation) {
rotation = Utils::wrapValue(rotation, d_ptr->m_minYRotation, d_ptr->m_maxYRotation);
- else
- rotation = qBound(float(d_ptr->m_minYRotation), float(rotation), float(d_ptr->m_maxYRotation));
+ } else {
+ rotation = qBound(float(d_ptr->m_minYRotation), float(rotation),
+ float(d_ptr->m_maxYRotation));
+ }
if (d_ptr->m_yRotation != rotation) {
d_ptr->setYRotation(rotation);
diff --git a/src/datavisualization/engine/q3dlight.cpp b/src/datavisualization/engine/q3dlight.cpp
index 8ac9e3a0..03f094cb 100644
--- a/src/datavisualization/engine/q3dlight.cpp
+++ b/src/datavisualization/engine/q3dlight.cpp
@@ -16,8 +16,6 @@
**
****************************************************************************/
-#include "q3dlight.h"
-#include "q3dscene.h"
#include "q3dlight_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -26,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class Q3DLight
* \inmodule QtDataVisualization
* \brief Representation of a light source in 3D space.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* Q3DLight represents a monochrome non variable light source in 3D space.
*/
diff --git a/src/datavisualization/engine/q3dobject.cpp b/src/datavisualization/engine/q3dobject.cpp
index 05edf287..56edb098 100644
--- a/src/datavisualization/engine/q3dobject.cpp
+++ b/src/datavisualization/engine/q3dobject.cpp
@@ -16,7 +16,6 @@
**
****************************************************************************/
-#include "q3dobject.h"
#include "q3dobject_p.h"
#include "q3dscene_p.h"
@@ -26,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
\class Q3DObject
\inmodule QtDataVisualization
\brief Simple baseclass for all the objects in the 3D scene.
- \since Qt Data Visualization 1.0
+ \since QtDataVisualization 1.0
Q3DObject is a baseclass that contains only position information for an object in 3D scene.
The object is considered to be a single point in the coordinate space without dimensions.
diff --git a/src/datavisualization/engine/q3dscatter.cpp b/src/datavisualization/engine/q3dscatter.cpp
index 7c7809f3..40bd5021 100644
--- a/src/datavisualization/engine/q3dscatter.cpp
+++ b/src/datavisualization/engine/q3dscatter.cpp
@@ -18,10 +18,6 @@
#include "q3dscatter.h"
#include "q3dscatter_p.h"
-#include "scatter3dcontroller_p.h"
-#include "qvalue3daxis.h"
-#include "q3dcamera.h"
-#include "qscatter3dseries_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -29,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class Q3DScatter
* \inmodule QtDataVisualization
* \brief The Q3DScatter class provides methods for rendering 3D scatter graphs.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* This class enables developers to render scatter graphs in 3D and to view them by rotating the scene
* freely. Rotation is done by holding down the right mouse button and moving the mouse. Zooming
diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp
index be64b928..9464bc8d 100644
--- a/src/datavisualization/engine/q3dscene.cpp
+++ b/src/datavisualization/engine/q3dscene.cpp
@@ -16,22 +16,17 @@
**
****************************************************************************/
-#include "datavisualizationglobal_p.h"
-
-#include "q3dscene.h"
#include "q3dscene_p.h"
#include "q3dcamera_p.h"
#include "q3dlight_p.h"
-#include <QtCore/qmath.h>
-
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \class Q3DScene
* \inmodule QtDataVisualization
* \brief Q3DScene class provides description of the 3D scene being visualized.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* The 3D scene contains a single active camera and a single active light source.
* Visualized data is assumed to be at a fixed location.
@@ -260,8 +255,12 @@ void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport)
*
* 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
- * graph tries to select a data item at the given \a point within the primary viewport.
- * After the rendering pass the property is returned to its default state of invalidSelectionPoint().
+ * 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
+ * invalidSelectionPoint().
+ *
+ * \sa QAbstract3DGraph::selectedElement
*/
void Q3DScene::setSelectionQueryPosition(const QPoint &point)
{
@@ -411,6 +410,7 @@ void Q3DScene::setActiveLight(Q3DLight *light)
d_ptr->m_sceneDirty = true;
emit activeLightChanged(light);
+ emit d_ptr->needRender();
}
}
@@ -554,7 +554,6 @@ void Q3DScenePrivate::setWindowSize(const QSize &size)
m_windowSize = size;
updateGLViewport();
m_changeTracker.windowSizeChanged = true;
- m_sceneDirty = true;
emit needRender();
}
}
diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h
index d663744e..1699b125 100644
--- a/src/datavisualization/engine/q3dscene.h
+++ b/src/datavisualization/engine/q3dscene.h
@@ -93,7 +93,9 @@ private:
friend class AbstractDeclarative;
friend class QAbstract3DGraph;
+ friend class QAbstract3DGraphPrivate;
friend class Abstract3DController;
+ friend class Bars3DController;
friend class Q3DScenePrivate;
friend class Abstract3DRenderer;
friend class Bars3DRenderer;
diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h
index bc6a7223..2c69e5e0 100644
--- a/src/datavisualization/engine/q3dscene_p.h
+++ b/src/datavisualization/engine/q3dscene_p.h
@@ -36,7 +36,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class Q3DCamera;
class Q3DLight;
-class Q3DScene;
struct Q3DSceneChangeBitField {
bool viewportChanged : 1;
diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp
index 7724cb24..162b7a67 100644
--- a/src/datavisualization/engine/q3dsurface.cpp
+++ b/src/datavisualization/engine/q3dsurface.cpp
@@ -18,10 +18,6 @@
#include "q3dsurface.h"
#include "q3dsurface_p.h"
-#include "qvalue3daxis.h"
-#include "qsurfacedataproxy.h"
-#include "q3dcamera.h"
-#include "qsurface3dseries_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -29,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class Q3DSurface
* \inmodule QtDataVisualization
* \brief The Q3DSurface class provides methods for rendering 3D surface plots.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* This class enables developers to render 3D surface plots and to view them by rotating the scene
* freely. The visual properties of the surface such as draw mode and shading can be controlled
diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp
index bde5b585..dabec744 100644
--- a/src/datavisualization/engine/qabstract3dgraph.cpp
+++ b/src/datavisualization/engine/qabstract3dgraph.cpp
@@ -27,6 +27,8 @@
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLPaintDevice>
#include <QtGui/QPainter>
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QOffscreenSurface>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -34,7 +36,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QAbstract3DGraph
* \inmodule QtDataVisualization
* \brief The QAbstract3DGraph class provides a window and render loop for graphs.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* This class subclasses a QWindow and provides render loop for graphs inheriting it.
*
@@ -118,6 +120,38 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ \enum QAbstract3DGraph::ElementType
+ \since QtDataVisualization 1.1
+
+ Type of an element in the graph.
+
+ \value ElementNone
+ No defined element.
+ \value ElementSeries
+ A series (i.e. an item in a series).
+ \value ElementAxisXLabel
+ X axis label.
+ \value ElementAxisYLabel
+ Y axis label.
+ \value ElementAxisZLabel
+ Z axis label.
+ \value ElementCustomItem
+ Custom item.
+*/
+
+/*!
+ \enum QAbstract3DGraph::OptimizationHint
+ \since Qt Data Visualization 1.1
+
+ The optimization hint for rendering.
+
+ \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.
+*/
+
+/*!
* \internal
*/
QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format,
@@ -126,6 +160,7 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor
d_ptr(d)
{
qRegisterMetaType<QAbstract3DGraph::ShadowQuality>("QAbstract3DGraph::ShadowQuality");
+ qRegisterMetaType<QAbstract3DGraph::ElementType>("QAbstract3DGraph::ElementType");
// Default to frameless window, as typically graphs are not toplevel
setFlags(flags() | Qt::FramelessWindowHint);
@@ -164,7 +199,8 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor
#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(' '));
+ 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
@@ -342,7 +378,8 @@ bool QAbstract3DGraph::shadowsSupported() const
/*!
* \property QAbstract3DGraph::scene
*
- * This property contains the read only Q3DScene that can be used to access, for example, a camera object.
+ * This property contains the read only Q3DScene that can be used to access, for example, a camera
+ * object.
*/
Q3DScene *QAbstract3DGraph::scene() const
{
@@ -358,6 +395,257 @@ void QAbstract3DGraph::clearSelection()
}
/*!
+ * Adds a QCustom3DItem \a item to the graph. Graph takes ownership of the added item.
+ *
+ * \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()
+ *
+ * \since QtDataVisualization 1.1
+ */
+int QAbstract3DGraph::addCustomItem(QCustom3DItem *item)
+{
+ return d_ptr->m_visualController->addCustomItem(item);
+}
+
+/*!
+ * Removes all custom items. Deletes the resources allocated to them.
+ *
+ * \since QtDataVisualization 1.1
+ */
+void QAbstract3DGraph::removeCustomItems()
+{
+ d_ptr->m_visualController->deleteCustomItems();
+}
+
+/*!
+ * Removes the custom \a {item}. Deletes the resources allocated to it.
+ *
+ * \since QtDataVisualization 1.1
+ */
+void QAbstract3DGraph::removeCustomItem(QCustom3DItem *item)
+{
+ d_ptr->m_visualController->deleteCustomItem(item);
+}
+
+/*!
+ * Removes all custom items at \a {position}. Deletes the resources allocated to them.
+ *
+ * \since QtDataVisualization 1.1
+ */
+void QAbstract3DGraph::removeCustomItemAt(const QVector3D &position)
+{
+ d_ptr->m_visualController->deleteCustomItem(position);
+}
+
+/*!
+ * Gets ownership of given \a item back and removes the \a item from the graph.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \note If the same item is added back to the graph, the texture or the texture file needs to be
+ * re-set.
+ *
+ * \sa QCustom3DItem::setTextureImage(), QCustom3DItem::setTextureFile()
+ */
+void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item)
+{
+ return d_ptr->m_visualController->releaseCustomItem(item);
+}
+
+/*!
+ * 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.
+ *
+ * \return index of the selected label, or -1.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+int QAbstract3DGraph::selectedLabelIndex() const
+{
+ return d_ptr->m_visualController->selectedLabelIndex();
+}
+
+/*!
+ * Can be used to get the selected axis after receiving \c selectedElementChanged signal with any label
+ * type. Selection is valid until the next \c selectedElementChanged signal.
+ *
+ * \return pointer to the selected axis, or null.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+QAbstract3DAxis *QAbstract3DGraph::selectedAxis() const
+{
+ return d_ptr->m_visualController->selectedAxis();
+}
+
+/*!
+ * Can be used to query the index of the selected custom item after receiving \c selectedElementChanged
+ * signal with QAbstract3DGraph::ElementCustomItem type. Selection is valid until the next
+ * \c selectedElementChanged signal.
+ *
+ * \return index of the selected custom item, or -1.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+int QAbstract3DGraph::selectedCustomItemIndex() const
+{
+ return d_ptr->m_visualController->selectedCustomItemIndex();
+}
+
+/*!
+ * Can be used to get the selected custom item after receiving \c selectedElementChanged signal with
+ * QAbstract3DGraph::ElementCustomItem type. Ownership of the item remains with the graph.
+ * Selection is valid until the next \c selectedElementChanged signal.
+ *
+ * \return pointer to the selected custom item, or null.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \sa selectedElement
+ */
+QCustom3DItem *QAbstract3DGraph::selectedCustomItem() const
+{
+ return d_ptr->m_visualController->selectedCustomItem();
+}
+
+/*!
+ * \property QAbstract3DGraph::selectedElement
+ *
+ * Can be used to query the selected element type.
+ * Type is valid until the next \c selectedElementChanged signal.
+ *
+ * \c selectedElementChanged signal is emitted when a selection is made in the graph.
+ *
+ * Signal can be used for example for implementing custom input handlers, as demonstrated in this
+ * \l {Axis Range Dragging With Labels Example}{example}.
+ *
+ * \sa selectedLabelIndex(), selectedAxis(), selectedCustomItemIndex(), selectedCustomItem(),
+ * Q3DBars::selectedSeries(), Q3DScatter::selectedSeries(), Q3DSurface::selectedSeries(),
+ * Q3DScene::setSelectionQueryPosition()
+ *
+ * \since QtDataVisualization 1.1
+ */
+QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const
+{
+ return d_ptr->m_visualController->selectedElement();
+}
+
+/*!
+ * Renders current frame to an image of \a imageSize. Default size is the window size. Image is
+ * rendered with antialiasing level given in \a msaaSamples. Default level is \c{0}.
+ *
+ * \since QtDataVisualization 1.1
+ *
+ * \return rendered image.
+ */
+QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize)
+{
+ QSize renderSize = imageSize;
+ if (renderSize.isEmpty())
+ renderSize = size();
+ return d_ptr->renderToImage(msaaSamples, renderSize);
+}
+
+/*!
+ * \property QAbstract3DGraph::measureFps
+ * \since QtDataVisualization 1.1
+ *
+ * If \c {true}, the rendering is done continuously instead of on demand, and currentFps property
+ * is updated. Defaults to \c{false}.
+ *
+ * \sa currentFps
+ */
+void QAbstract3DGraph::setMeasureFps(bool enable)
+{
+ d_ptr->m_visualController->setMeasureFps(enable);
+}
+
+bool QAbstract3DGraph::measureFps() const
+{
+ return d_ptr->m_visualController->measureFps();
+}
+
+/*!
+ * \property QAbstract3DGraph::currentFps
+ * \since QtDataVisualization 1.1
+ *
+ * When fps measuring is enabled, the results for the last second are stored in this read-only
+ * property. It takes at least a second before this value updates after measurement is activated.
+ *
+ * \sa measureFps
+ */
+qreal QAbstract3DGraph::currentFps() const
+{
+ return d_ptr->m_visualController->currentFps();
+}
+
+/*!
+ * \property QAbstract3DGraph::orthoProjection
+ * \since QtDataVisualization 1.1
+ *
+ * If \c {true}, orthographic projection will be used for displaying the graph. Defaults to \c{false}.
+ * \note Shadows will be disabled when set to \c{true}.
+ */
+void QAbstract3DGraph::setOrthoProjection(bool enable)
+{
+ d_ptr->m_visualController->setOrthoProjection(enable);
+}
+
+bool QAbstract3DGraph::isOrthoProjection() const
+{
+ return d_ptr->m_visualController->isOrthoProjection();
+}
+
+/*!
+ * \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}.
+ *
+ * \note Has no effect on Q3DBars.
+ */
+void QAbstract3DGraph::setAspectRatio(qreal ratio)
+{
+ d_ptr->m_visualController->setAspectRatio(float(ratio));
+}
+
+qreal QAbstract3DGraph::aspectRatio() const
+{
+ return d_ptr->m_visualController->aspectRatio();
+}
+
+/*!
+ * \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.
+ * Defaults to \c{OptimizationDefault}.
+ */
+void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints)
+{
+ d_ptr->m_visualController->setOptimizationHints(hints);
+}
+
+QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const
+{
+ return d_ptr->m_visualController->optimizationHints();
+}
+
+/*!
* \internal
*/
bool QAbstract3DGraph::event(QEvent *event)
@@ -453,12 +741,17 @@ QAbstract3DGraphPrivate::QAbstract3DGraphPrivate(QAbstract3DGraph *q)
q_ptr(q),
m_updatePending(false),
m_visualController(0),
- m_devicePixelRatio(1.f)
+ m_devicePixelRatio(1.f),
+ m_offscreenSurface(0)
{
}
QAbstract3DGraphPrivate::~QAbstract3DGraphPrivate()
{
+ if (m_offscreenSurface) {
+ m_offscreenSurface->destroy();
+ delete m_offscreenSurface;
+ }
if (m_context)
m_context->makeCurrent(q_ptr);
@@ -477,6 +770,11 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll
&QAbstract3DGraph::selectionModeChanged);
QObject::connect(m_visualController, &Abstract3DController::shadowQualityChanged, q_ptr,
&QAbstract3DGraph::shadowQualityChanged);
+ QObject::connect(m_visualController, &Abstract3DController::optimizationHintsChanged, q_ptr,
+ &QAbstract3DGraph::optimizationHintsChanged);
+ QObject::connect(m_visualController, &Abstract3DController::elementSelected, q_ptr,
+ &QAbstract3DGraph::selectedElementChanged);
+
QObject::connect(m_visualController, &Abstract3DController::needRender, this,
&QAbstract3DGraphPrivate::renderLater);
@@ -486,6 +784,17 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll
&QAbstract3DGraphPrivate::handleAxisYChanged);
QObject::connect(m_visualController, &Abstract3DController::axisZChanged, this,
&QAbstract3DGraphPrivate::handleAxisZChanged);
+
+ QObject::connect(m_visualController, &Abstract3DController::measureFpsChanged, q_ptr,
+ &QAbstract3DGraph::measureFpsChanged);
+ QObject::connect(m_visualController, &Abstract3DController::currentFpsChanged, q_ptr,
+ &QAbstract3DGraph::currentFpsChanged);
+
+ QObject::connect(m_visualController, &Abstract3DController::orthoProjectionChanged, q_ptr,
+ &QAbstract3DGraph::orthoProjectionChanged);
+
+ QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr,
+ &QAbstract3DGraph::aspectRatioChanged);
}
void QAbstract3DGraphPrivate::handleDevicePixelRatioChange()
@@ -526,4 +835,42 @@ void QAbstract3DGraphPrivate::renderNow()
m_context->swapBuffers(q_ptr);
}
+QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imageSize)
+{
+ QImage image;
+ QOpenGLFramebufferObject *fbo;
+ QOpenGLFramebufferObjectFormat fboFormat;
+ if (!m_offscreenSurface) {
+ // Create an offscreen surface for rendering to images without rendering on screen
+ m_offscreenSurface = new QOffscreenSurface(q_ptr->screen());
+ m_offscreenSurface->setFormat(q_ptr->requestedFormat());
+ m_offscreenSurface->create();
+ }
+ // Render the wanted frame offscreen
+ m_context->makeCurrent(m_offscreenSurface);
+ fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ fboFormat.setInternalTextureFormat(GL_RGB);
+ fboFormat.setSamples(msaaSamples);
+ fbo = new QOpenGLFramebufferObject(imageSize, fboFormat);
+ if (fbo->isValid()) {
+ QRect originalViewport = m_visualController->m_scene->viewport();
+ m_visualController->m_scene->d_ptr->setWindowSize(imageSize);
+ m_visualController->m_scene->d_ptr->setViewport(QRect(0, 0,
+ imageSize.width(),
+ imageSize.height()));
+ m_visualController->synchDataToRenderer();
+ fbo->bind();
+ m_context->swapBuffers(m_offscreenSurface);
+ m_visualController->requestRender(fbo);
+ image = fbo->toImage();
+ fbo->release();
+ m_visualController->m_scene->d_ptr->setWindowSize(originalViewport.size());
+ m_visualController->m_scene->d_ptr->setViewport(originalViewport);
+ }
+ delete fbo;
+ m_context->makeCurrent(q_ptr);
+
+ return image;
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h
index 18eda7df..59f61aae 100644
--- a/src/datavisualization/engine/qabstract3dgraph.h
+++ b/src/datavisualization/engine/qabstract3dgraph.h
@@ -29,20 +29,31 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QAbstract3DGraphPrivate;
+class QCustom3DItem;
+class QAbstract3DAxis;
class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected QOpenGLFunctions
{
Q_OBJECT
Q_ENUMS(ShadowQuality)
+ Q_ENUMS(ElementType)
Q_FLAGS(SelectionFlag SelectionFlags)
+ Q_FLAGS(OptimizationHint OptimizationHints)
Q_PROPERTY(QAbstract3DInputHandler* activeInputHandler READ activeInputHandler WRITE setActiveInputHandler NOTIFY activeInputHandlerChanged)
Q_PROPERTY(Q3DTheme* activeTheme READ activeTheme WRITE setActiveTheme NOTIFY activeThemeChanged)
Q_PROPERTY(SelectionFlags selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged)
Q_PROPERTY(ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality NOTIFY shadowQualityChanged)
Q_PROPERTY(Q3DScene* scene READ scene)
+ Q_PROPERTY(bool measureFps READ measureFps WRITE setMeasureFps NOTIFY measureFpsChanged)
+ Q_PROPERTY(qreal currentFps READ currentFps NOTIFY currentFpsChanged)
+ Q_PROPERTY(bool orthoProjection READ isOrthoProjection WRITE setOrthoProjection NOTIFY orthoProjectionChanged)
+ 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)
protected:
- explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, QWindow *parent = 0);
+ explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format,
+ QWindow *parent = 0);
public:
enum SelectionFlag {
@@ -69,6 +80,21 @@ public:
ShadowQualitySoftHigh
};
+ enum ElementType {
+ ElementNone = 0,
+ ElementSeries,
+ ElementAxisXLabel,
+ ElementAxisYLabel,
+ ElementAxisZLabel,
+ ElementCustomItem
+ };
+
+ enum OptimizationHint {
+ OptimizationDefault = 0,
+ OptimizationStatic = 1
+ };
+ Q_DECLARE_FLAGS(OptimizationHints, OptimizationHint)
+
public:
virtual ~QAbstract3DGraph();
@@ -95,6 +121,35 @@ public:
void clearSelection();
+ int addCustomItem(QCustom3DItem *item);
+ void removeCustomItems();
+ void removeCustomItem(QCustom3DItem *item);
+ void removeCustomItemAt(const QVector3D &position);
+ void releaseCustomItem(QCustom3DItem *item);
+
+ int selectedLabelIndex() const;
+ QAbstract3DAxis *selectedAxis() const;
+
+ int selectedCustomItemIndex() const;
+ QCustom3DItem *selectedCustomItem() const;
+
+ QImage renderToImage(int msaaSamples = 0, const QSize &imageSize = QSize());
+
+ void setMeasureFps(bool enable);
+ bool measureFps() const;
+ qreal currentFps() const;
+
+ void setOrthoProjection(bool enable);
+ bool isOrthoProjection() const;
+
+ ElementType selectedElement() const;
+
+ void setAspectRatio(qreal ratio);
+ qreal aspectRatio() const;
+
+ void setOptimizationHints(OptimizationHints hints);
+ OptimizationHints optimizationHints() const;
+
protected:
bool event(QEvent *event);
void resizeEvent(QResizeEvent *event);
@@ -107,12 +162,17 @@ protected:
void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
-
signals:
void activeInputHandlerChanged(QAbstract3DInputHandler *inputHandler);
void activeThemeChanged(Q3DTheme *theme);
void selectionModeChanged(QAbstract3DGraph::SelectionFlags mode);
void shadowQualityChanged(QAbstract3DGraph::ShadowQuality quality);
+ void selectedElementChanged(QAbstract3DGraph::ElementType type);
+ void measureFpsChanged(bool enabled);
+ void currentFpsChanged(qreal fps);
+ void orthoProjectionChanged(bool enabled);
+ void aspectRatioChanged(qreal ratio);
+ void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints);
private:
Q_DISABLE_COPY(QAbstract3DGraph)
@@ -123,6 +183,7 @@ private:
friend class Q3DSurface;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstract3DGraph::SelectionFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstract3DGraph::OptimizationHints)
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/qabstract3dgraph_p.h b/src/datavisualization/engine/qabstract3dgraph_p.h
index d28495ab..a8b0965a 100644
--- a/src/datavisualization/engine/qabstract3dgraph_p.h
+++ b/src/datavisualization/engine/qabstract3dgraph_p.h
@@ -32,7 +32,7 @@
#include "datavisualizationglobal_p.h"
class QOpenGLContext;
-class QOpenGLPaintDevice;
+class QOffscreenSurface;
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -52,6 +52,8 @@ public:
void render();
+ QImage renderToImage(int msaaSamples, const QSize &imageSize);
+
public slots:
void renderLater();
void renderNow();
@@ -67,6 +69,7 @@ public:
QOpenGLContext *m_context;
Abstract3DController *m_visualController;
float m_devicePixelRatio;
+ QOffscreenSurface *m_offscreenSurface;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/scatter3dcontroller.cpp b/src/datavisualization/engine/scatter3dcontroller.cpp
index 54292ac0..da40fc85 100644
--- a/src/datavisualization/engine/scatter3dcontroller.cpp
+++ b/src/datavisualization/engine/scatter3dcontroller.cpp
@@ -18,15 +18,10 @@
#include "scatter3dcontroller_p.h"
#include "scatter3drenderer_p.h"
-#include "camerahelper_p.h"
-#include "qabstract3daxis_p.h"
#include "qvalue3daxis_p.h"
#include "qscatterdataproxy_p.h"
#include "qscatter3dseries_p.h"
-#include <QtGui/QMatrix4x4>
-#include <QtCore/qmath.h>
-
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
static const int insertRemoveRecordReserveSize = 31;
@@ -71,6 +66,12 @@ void Scatter3DController::synchDataToRenderer()
Abstract3DController::synchDataToRenderer();
// Notify changes to renderer
+ if (m_changeTracker.itemChanged) {
+ m_renderer->updateItems(m_changedItems);
+ m_changeTracker.itemChanged = false;
+ m_changedItems.clear();
+ }
+
if (m_changeTracker.selectedItemChanged) {
m_renderer->updateSelectedItem(m_selectedItem, m_selectedItemSeries);
m_changeTracker.selectedItemChanged = false;
@@ -83,9 +84,6 @@ void Scatter3DController::addSeries(QAbstract3DSeries *series)
Abstract3DController::addSeries(series);
- if (series->isVisible())
- adjustValueAxisRange();
-
QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(series);
if (scatterSeries->selectedItem() != invalidSelectionIndex())
setSelectedItem(scatterSeries->selectedItem(), scatterSeries);
@@ -101,7 +99,7 @@ void Scatter3DController::removeSeries(QAbstract3DSeries *series)
setSelectedItem(invalidSelectionIndex(), 0);
if (wasVisible)
- adjustValueAxisRange();
+ adjustAxisRanges();
}
QList<QScatter3DSeries *> Scatter3DController::scatterSeriesList()
@@ -121,10 +119,13 @@ void Scatter3DController::handleArrayReset()
{
QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series();
if (series->isVisible()) {
- adjustValueAxisRange();
+ adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
setSelectedItem(m_selectedItem, m_selectedItemSeries);
+ series->d_ptr->markItemLabelDirty();
emitNeedRender();
}
@@ -134,22 +135,45 @@ void Scatter3DController::handleItemsAdded(int startIndex, int count)
Q_UNUSED(count)
QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series();
if (series->isVisible()) {
- adjustValueAxisRange();
+ adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
emitNeedRender();
}
void Scatter3DController::handleItemsChanged(int startIndex, int count)
{
- Q_UNUSED(startIndex)
- Q_UNUSED(count)
QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustValueAxisRange();
- m_isDataDirty = true;
+ int oldChangeCount = m_changedItems.size();
+ if (!oldChangeCount)
+ m_changedItems.reserve(count);
+
+ for (int i = 0; i < count; i++) {
+ bool newItem = true;
+ int candidate = startIndex + i;
+ for (int j = 0; j < oldChangeCount; j++) {
+ const ChangeItem &oldChangeItem = m_changedItems.at(j);
+ if (oldChangeItem.index == candidate && series == oldChangeItem.series) {
+ newItem = false;
+ break;
+ }
+ }
+ if (newItem) {
+ ChangeItem newChangeItem = {series, candidate};
+ m_changedItems.append(newChangeItem);
+ if (series == m_selectedItemSeries && m_selectedItem == candidate)
+ series->d_ptr->markItemLabelDirty();
+ }
+ }
+
+ if (count) {
+ m_changeTracker.itemChanged = true;
+ if (series->isVisible())
+ adjustAxisRanges();
+ emitNeedRender();
}
- emitNeedRender();
}
void Scatter3DController::handleItemsRemoved(int startIndex, int count)
@@ -171,9 +195,11 @@ void Scatter3DController::handleItemsRemoved(int startIndex, int count)
}
if (series->isVisible()) {
- adjustValueAxisRange();
+ adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
if (m_recordInsertsAndRemoves) {
InsertRemoveRecord record(false, startIndex, count, series);
@@ -198,9 +224,11 @@ void Scatter3DController::handleItemsInserted(int startIndex, int count)
}
if (series->isVisible()) {
- adjustValueAxisRange();
+ adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
if (m_recordInsertsAndRemoves) {
InsertRemoveRecord record(true, startIndex, count, series);
@@ -229,7 +257,7 @@ void Scatter3DController::handleAxisAutoAdjustRangeChangedInOrientation(
{
Q_UNUSED(orientation)
Q_UNUSED(autoAdjust)
- adjustValueAxisRange();
+ adjustAxisRanges();
}
void Scatter3DController::handleAxisRangeChangedBySender(QObject *sender)
@@ -240,13 +268,6 @@ void Scatter3DController::handleAxisRangeChangedBySender(QObject *sender)
setSelectedItem(m_selectedItem, m_selectedItemSeries);
}
-void Scatter3DController::handleSeriesVisibilityChangedBySender(QObject *sender)
-{
- Abstract3DController::handleSeriesVisibilityChangedBySender(sender);
-
- adjustValueAxisRange();
-}
-
void Scatter3DController::handlePendingClick()
{
int index = m_renderer->clickedIndex();
@@ -274,6 +295,8 @@ void Scatter3DController::handlePendingClick()
setSelectedItem(index, series);
+ Abstract3DController::handlePendingClick();
+
m_renderer->resetClickedStatus();
}
@@ -329,7 +352,7 @@ void Scatter3DController::clearSelection()
setSelectedItem(invalidSelectionIndex(), 0);
}
-void Scatter3DController::adjustValueAxisRange()
+void Scatter3DController::adjustAxisRanges()
{
QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
@@ -407,7 +430,7 @@ void Scatter3DController::adjustValueAxisRange()
adjustment = defaultAdjustment;
}
}
- valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment);
+ valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment, true);
}
if (adjustY) {
// If all points at same coordinate, need to default to some valid range
@@ -415,7 +438,7 @@ void Scatter3DController::adjustValueAxisRange()
float adjustment = 0.0f;
if (minValueY == maxValueY)
adjustment = defaultAdjustment;
- valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment);
+ valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment, true);
}
if (adjustZ) {
// If all points at same coordinate, need to default to some valid range
@@ -434,7 +457,7 @@ void Scatter3DController::adjustValueAxisRange()
adjustment = defaultAdjustment;
}
}
- valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment);
+ valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment, true);
}
}
}
diff --git a/src/datavisualization/engine/scatter3dcontroller_p.h b/src/datavisualization/engine/scatter3dcontroller_p.h
index 53d24549..db5a95f5 100644
--- a/src/datavisualization/engine/scatter3dcontroller_p.h
+++ b/src/datavisualization/engine/scatter3dcontroller_p.h
@@ -40,9 +40,11 @@ class QScatter3DSeries;
struct Scatter3DChangeBitField {
bool selectedItemChanged : 1;
+ bool itemChanged : 1;
Scatter3DChangeBitField() :
- selectedItemChanged(true)
+ selectedItemChanged(true),
+ itemChanged(false)
{
}
};
@@ -51,8 +53,14 @@ class QT_DATAVISUALIZATION_EXPORT Scatter3DController : public Abstract3DControl
{
Q_OBJECT
+public:
+ struct ChangeItem {
+ QScatter3DSeries *series;
+ int index;
+ };
private:
Scatter3DChangeBitField m_changeTracker;
+ QVector<ChangeItem> m_changedItems;
// Rendering
Scatter3DRenderer *m_renderer;
@@ -108,8 +116,8 @@ public:
virtual void handleAxisAutoAdjustRangeChangedInOrientation(
QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust);
virtual void handleAxisRangeChangedBySender(QObject *sender);
- virtual void handleSeriesVisibilityChangedBySender(QObject *sender);
virtual void handlePendingClick();
+ virtual void adjustAxisRanges();
public slots:
void handleArrayReset();
@@ -125,7 +133,6 @@ protected:
virtual void startRecordingRemovesAndInserts();
private:
- void adjustValueAxisRange();
Q_DISABLE_COPY(Scatter3DController)
};
diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp
index de1a769a..21d86d03 100644
--- a/src/datavisualization/engine/scatter3drenderer.cpp
+++ b/src/datavisualization/engine/scatter3drenderer.cpp
@@ -17,25 +17,16 @@
****************************************************************************/
#include "scatter3drenderer_p.h"
-#include "scatter3dcontroller_p.h"
-#include "q3dcamera.h"
#include "q3dcamera_p.h"
#include "shaderhelper_p.h"
-#include "objecthelper_p.h"
#include "texturehelper_p.h"
#include "utils_p.h"
-#include "q3dlight.h"
-#include "qscatter3dseries_p.h"
+#include "scatterseriesrendercache_p.h"
+#include "scatterobjectbufferhelper_p.h"
+#include "scatterpointbufferhelper_p.h"
-#include <QtGui/QMatrix4x4>
-#include <QtGui/QMouseEvent>
-#include <QtCore/QThread>
#include <QtCore/qmath.h>
-// Commenting this draws the shadow map with perspective projection. Otherwise it's drawn in
-// orthographic projection.
-//#define USE_WIDER_SHADOWS
-
// You can verify that depth buffer drawing works correctly by uncommenting this.
// You should see the scene from where the light is
//#define SHOW_DEPTH_TEXTURE_SCENE
@@ -44,20 +35,14 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
//#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values
-const GLfloat aspectRatio = 2.0f; // Forced ratio of x and z to y. Dynamic will make it look odd.
-const GLfloat labelMargin = 0.05f;
const GLfloat defaultMinSize = 0.01f;
const GLfloat defaultMaxSize = 0.1f;
-const GLfloat defaultMargin = 1.0f + defaultMaxSize; // Default margin for background
const GLfloat itemScaler = 3.0f;
const GLfloat gridLineWidth = 0.005f;
Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
: Abstract3DRenderer(controller),
m_selectedItem(0),
- m_xFlipped(false),
- m_zFlipped(false),
- m_yFlipped(false),
m_updateLabels(false),
m_dotShader(0),
m_dotGradientShader(0),
@@ -68,9 +53,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
m_selectionShader(0),
m_backgroundShader(0),
m_labelShader(0),
- m_backgroundObj(0),
- m_gridLineObj(0),
- m_labelObj(0),
m_bgrTexture(0),
m_depthTexture(0),
m_selectionTexture(0),
@@ -82,16 +64,22 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
m_heightNormalizer(1.0f),
m_scaleFactor(0),
m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()),
- m_selectedItemTotalIndex(Scatter3DController::invalidSelectionIndex()),
- m_selectedItemSeriesIndex(Scatter3DController::invalidSelectionIndex()),
- m_selectedSeries(0),
+ m_selectedSeriesCache(0),
+ m_oldSelectedSeriesCache(0),
m_areaSize(QSizeF(0.0, 0.0)),
m_dotSizeScale(1.0f),
m_hasHeightAdjustmentChanged(true),
- m_backgroundMargin(defaultMargin),
+ m_backgroundMargin(defaultMaxSize),
m_maxItemSize(0.0f),
- m_clickedIndex(Scatter3DController::invalidSelectionIndex())
+ m_clickedIndex(Scatter3DController::invalidSelectionIndex()),
+ m_havePointSeries(false),
+ m_haveMeshSeries(false),
+ m_haveUniformColorMeshSeries(false),
+ m_haveGradientMeshSeries(false)
{
+ m_axisCacheY.setScale(2.0f);
+ m_axisCacheY.setTranslate(-1.0f);
+
initializeOpenGLFunctions();
initializeOpenGL();
}
@@ -111,9 +99,6 @@ Scatter3DRenderer::~Scatter3DRenderer()
delete m_selectionShader;
delete m_backgroundShader;
delete m_labelShader;
- delete m_backgroundObj;
- delete m_gridLineObj;
- delete m_labelObj;
}
void Scatter3DRenderer::initializeOpenGL()
@@ -135,8 +120,10 @@ void Scatter3DRenderer::initializeOpenGL()
// Init selection shader
initSelectionShader();
+#if !defined(QT_OPENGL_ES_2)
// Load grid line mesh
loadGridLineMesh();
+#endif
// Load label mesh
loadLabelMesh();
@@ -151,78 +138,153 @@ void Scatter3DRenderer::initializeOpenGL()
loadBackgroundMesh();
}
-void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList,
- bool updateVisibility)
+void Scatter3DRenderer::updateData()
{
- Abstract3DRenderer::updateSeries(seriesList, updateVisibility);
+ calculateSceneScalingFactors();
+ int totalDataSize = 0;
- int seriesCount = m_visibleSeriesList.size();
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
+ if (cache->isVisible() && cache->dataDirty()) {
+ 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);
+
+ for (int i = 0; i < dataSize; i++)
+ updateRenderItem(dataArray.at(i), renderArray[i]);
+ cache->setDataDirty(false);
+ }
+ }
+
+ if (totalDataSize) {
+ m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))),
+ defaultMaxSize));
+ }
+
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
+ if (cache->isVisible()) {
+ ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int renderArraySize = renderArray.size();
+
+ if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
+ ScatterPointBufferHelper *points = cache->bufferPoints();
+ if (!points) {
+ points = new ScatterPointBufferHelper();
+ cache->setBufferPoints(points);
+ }
+ points->load(cache);
+ } else {
+ ScatterObjectBufferHelper *object = cache->bufferObject();
+ if (!object) {
+ object = new ScatterObjectBufferHelper();
+ cache->setBufferObject(object);
+ }
+ if (renderArraySize != cache->oldArraySize()
+ || cache->object()->objectFile() != cache->oldMeshFileName()) {
+ object->fullLoad(cache, m_dotSizeScale);
+ cache->setOldArraySize(renderArraySize);
+ cache->setOldMeshFileName(cache->object()->objectFile());
+ } else {
+ object->update(cache, m_dotSizeScale);
+ }
+ }
+ }
+ }
+ }
+
+ updateSelectedItem(m_selectedItemIndex,
+ m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0);
+}
+
+void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
+{
+ Abstract3DRenderer::updateSeries(seriesList);
+
+ int seriesCount = seriesList.size();
float maxItemSize = 0.0f;
float itemSize = 0.0f;
+ bool noSelection = true;
- if (m_cachedItemSize.size() != seriesCount)
- m_cachedItemSize.resize(seriesCount);
+ m_havePointSeries = false;
+ m_haveMeshSeries = false;
+ m_haveUniformColorMeshSeries = false;
+ m_haveGradientMeshSeries = false;
- for (int series = 0; series < seriesCount; series++) {
- itemSize = static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->itemSize();
- if (maxItemSize < itemSize)
- maxItemSize = itemSize;
- if (m_cachedItemSize.at(series) != itemSize)
- m_cachedItemSize[series] = itemSize;
+ for (int i = 0; i < seriesCount; i++) {
+ QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]);
+ if (scatterSeries->isVisible()) {
+ ScatterSeriesRenderCache *cache =
+ static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(scatterSeries));
+ itemSize = scatterSeries->itemSize();
+ if (maxItemSize < itemSize)
+ maxItemSize = itemSize;
+ if (cache->itemSize() != itemSize)
+ cache->setItemSize(itemSize);
+ if (noSelection
+ && scatterSeries->selectedItem() != QScatter3DSeries::invalidSelectionIndex()) {
+ if (m_selectionLabel != cache->itemLabel())
+ m_selectionLabelDirty = true;
+ noSelection = false;
+ }
+
+ if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
+ m_havePointSeries = true;
+ } else {
+ m_haveMeshSeries = true;
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
+ m_haveUniformColorMeshSeries = true;
+ else
+ m_haveGradientMeshSeries = true;
+ }
+ }
}
- m_backgroundMargin = defaultMargin;
m_maxItemSize = maxItemSize;
if (maxItemSize > defaultMaxSize)
- m_backgroundMargin += maxItemSize / itemScaler;
+ m_backgroundMargin = maxItemSize / itemScaler;
+ else
+ m_backgroundMargin = defaultMaxSize;
+
+ if (noSelection) {
+ if (!selectionLabel().isEmpty())
+ m_selectionLabelDirty = true;
+ m_selectedSeriesCache = 0;
+ }
}
-void Scatter3DRenderer::updateData()
+SeriesRenderCache *Scatter3DRenderer::createNewCache(QAbstract3DSeries *series)
{
- int seriesCount = m_visibleSeriesList.size();
- calculateSceneScalingFactors();
- float minX = float(m_axisCacheX.min());
- float maxX = float(m_axisCacheX.max());
- float minY = float(m_axisCacheY.min());
- float maxY = float(m_axisCacheY.max());
- float minZ = float(m_axisCacheZ.min());
- float maxZ = float(m_axisCacheZ.max());
- int totalDataSize = 0;
+ return new ScatterSeriesRenderCache(series, this);
+}
- if (m_renderingArrays.size() != seriesCount)
- m_renderingArrays.resize(seriesCount);
-
- for (int series = 0; series < seriesCount; series++) {
- QScatterDataProxy *dataProxy =
- static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->dataProxy();
- const QScatterDataArray &dataArray = *dataProxy->array();
- int dataSize = dataArray.size();
- totalDataSize += dataSize;
-
- if (dataSize != m_renderingArrays.at(series).size())
- m_renderingArrays[series].resize(dataSize);
-
- for (int i = 0; i < dataSize; i++) {
- QVector3D dotPos = dataArray.at(i).position();
- ScatterRenderItem &renderItem = m_renderingArrays[series][i];
- if ((dotPos.x() >= minX && dotPos.x() <= maxX )
- && (dotPos.y() >= minY && dotPos.y() <= maxY)
- && (dotPos.z() >= minZ && dotPos.z() <= maxZ)) {
- renderItem.setPosition(dotPos);
- renderItem.setVisible(true);
- if (!dataArray.at(i).rotation().isIdentity())
- renderItem.setRotation(dataArray.at(i).rotation().normalized());
- else
- renderItem.setRotation(identityQuaternion);
- calculateTranslation(renderItem);
- } else {
- renderItem.setVisible(false);
- }
+void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeItem> &items)
+{
+ ScatterSeriesRenderCache *cache = 0;
+ const QScatter3DSeries *prevSeries = 0;
+ const QScatterDataArray *dataArray = 0;
+
+ foreach (Scatter3DController::ChangeItem item, items) {
+ QScatter3DSeries *currentSeries = item.series;
+ if (currentSeries != prevSeries) {
+ cache = static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(currentSeries));
+ prevSeries = currentSeries;
+ dataArray = item.series->dataProxy()->array();
+ // Invisible series render caches are not updated, but instead just marked dirty, so that
+ // they can be completely recalculated when they are turned visible.
+ if (!cache->isVisible() && !cache->dataDirty())
+ cache->setDataDirty(true);
+ }
+ if (cache->isVisible()) {
+ const int index = item.index;
+ updateRenderItem(dataArray->at(index), cache->renderArray()[index]);
}
}
- m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))),
- defaultMaxSize));
-
- updateSelectedItem(m_selectedItemIndex, m_selectedSeries);
}
void Scatter3DRenderer::updateScene(Q3DScene *scene)
@@ -231,7 +293,8 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene)
if (m_hasHeightAdjustmentChanged) {
// Set initial camera position. Also update if height adjustment has changed.
- scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, upVector);
+ scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector,
+ upVector);
m_hasHeightAdjustmentChanged = false;
}
@@ -249,6 +312,13 @@ void Scatter3DRenderer::render(GLuint defaultFboHandle)
// Handle GL state setup for FBO buffers and clearing of the render surface
Abstract3DRenderer::render(defaultFboHandle);
+ if (m_axisCacheX.positionsDirty())
+ m_axisCacheX.updateAllPositions();
+ if (m_axisCacheY.positionsDirty())
+ m_axisCacheY.updateAllPositions();
+ if (m_axisCacheZ.positionsDirty())
+ m_axisCacheZ.updateAllPositions();
+
// Draw dots scene
drawScene(defaultFboHandle);
}
@@ -258,9 +328,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
GLfloat backgroundRotation = 0;
GLfloat selectedItemSize = 0.0f;
+ // Get the optimization flag
+ const bool optimizationDefault =
+ !m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic);
+
const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
- QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
// Specify viewport
glViewport(m_primarySubViewport.x(),
@@ -272,14 +346,19 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QMatrix4x4 projectionMatrix;
GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
/ (GLfloat)m_primarySubViewport.height();
- projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
+ if (m_useOrthoProjection) {
+ GLfloat orthoRatio = 2.0f;
+ projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
+ -orthoRatio, orthoRatio,
+ 0.0f, 100.0f);
+ } else {
+ projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
+ }
// Calculate view matrix
QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
- int seriesCount = m_visibleSeriesList.size();
-
// Calculate label flipping
if (viewMatrix.row(0).x() > 0)
m_zFlipped = false;
@@ -314,25 +393,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QMatrix4x4 depthProjectionMatrix;
QMatrix4x4 depthProjectionViewMatrix;
- // Check if we have any series with points
- bool havePointSeries = false;
- bool haveMeshSeries = false;
- bool haveUniformColorMeshSeries = false;
- bool haveGradientMeshSeries = false;
- for (int i = 0; i < seriesCount; i++) {
- if (m_visibleSeriesList.at(i).mesh() == QAbstract3DSeries::MeshPoint) {
- havePointSeries = true;
- } else {
- haveMeshSeries = true;
- if (m_visibleSeriesList.at(i).colorStyle() == Q3DTheme::ColorStyleUniform)
- haveUniformColorMeshSeries = true;
- else
- haveGradientMeshSeries = true;
- }
- }
-
#if !defined(QT_OPENGL_ES_2)
- if (havePointSeries) {
+ if (m_havePointSeries) {
glEnable(GL_POINT_SMOOTH);
glEnable(GL_PROGRAM_POINT_SIZE);
}
@@ -360,77 +422,107 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment);
depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
// Set the depth projection matrix
-#ifndef USE_WIDER_SHADOWS
- // Use this for perspective shadows
depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f);
-#else
- // Use these for orthographic shadows
- GLfloat testAspectRatio = viewPortRatio;
- depthProjectionMatrix.ortho(-testAspectRatio * 2.0f, testAspectRatio * 2.0f,
- -m_heightNormalizer * 2.0f, m_heightNormalizer * 2.0f,
- 0.0f, 100.0f);
-#endif
depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
// Draw dots to depth buffer
- for (int series = 0; series < seriesCount; series++) {
- ObjectHelper *dotObj = m_visibleSeriesList.at(series).object();
- QQuaternion seriesRotation = m_visibleSeriesList.at(series).meshRotation();
- bool drawingPoints = (m_visibleSeriesList.at(series).mesh() == QAbstract3DSeries::MeshPoint);
-
- float itemSize = m_cachedItemSize.at(series) / 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);
-
- for (int dot = 0; dot < m_renderingArrays.at(series).size(); dot++) {
- const ScatterRenderItem &item = m_renderingArrays.at(series).at(dot);
- if (!item.isVisible())
- continue;
-
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
-
- modelMatrix.translate(item.translation());
- if (!drawingPoints) {
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
- modelMatrix.rotate(seriesRotation * item.rotation());
- modelMatrix.scale(modelScaler);
- }
-
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
-
- m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
-
+ 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) {
- m_drawer->drawPoint(m_depthShader);
- } else {
- // 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());
+ // Scale points based on shadow quality for shadows, not by zoom level
+ 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)
+ 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;
+
+ m_depthShader->setUniformValue(m_depthShader->MVP(), 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());
+
+ // 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);
+
// Disable drawing to framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -446,14 +538,14 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
ShaderHelper *pointSelectionShader = m_selectionShader;
#else
- Q_UNUSED(havePointSeries);
ShaderHelper *pointSelectionShader = m_pointShader;
#endif
ShaderHelper *selectionShader = m_selectionShader;
// Skip selection mode drawing if we have no selection mode
if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
- && SelectOnScene == m_selectionState && seriesCount > 0) {
+ && SelectOnScene == m_selectionState
+ && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) {
// Draw dots to selection buffer
glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
glViewport(0, 0,
@@ -465,94 +557,80 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer
glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled
- int arraySize = 0;
- int totalArraySize = 0;
- int dotNo = 0;
-
- // Init previous to opposite of first to ensure we change binding for first series
- bool previousDrawingPoints = (m_visibleSeriesList.at(0).mesh() != QAbstract3DSeries::MeshPoint);
- for (int series = 0; series < seriesCount; series++) {
- ObjectHelper *dotObj = m_visibleSeriesList.at(series).object();
- QQuaternion seriesRotation = m_visibleSeriesList.at(series).meshRotation();
- bool drawingPoints = (m_visibleSeriesList.at(series).mesh() == QAbstract3DSeries::MeshPoint);
-
- float itemSize = m_cachedItemSize.at(series) / itemScaler;
- if (itemSize == 0.0f)
- itemSize = m_dotSizeScale;
+ bool previousDrawingPoints = false;
+ int totalIndex = 0;
+ 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 !defined(QT_OPENGL_ES_2)
- if (drawingPoints)
- glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom
-#endif
- QVector3D modelScaler(itemSize, itemSize, itemSize);
-
- // Rebind selection shader if it has changed
- if (drawingPoints != previousDrawingPoints) {
- previousDrawingPoints = drawingPoints;
if (drawingPoints)
- selectionShader = pointSelectionShader;
- else
- selectionShader = m_selectionShader;
-
- selectionShader->bind();
- }
- arraySize = m_renderingArrays.at(series).size();
- totalArraySize += arraySize;
-
- if (totalArraySize > 0xfffffe) // Max possible different selection colors, 0xffffff being skipColor
- qFatal("Too many objects");
-
- for (int dot = 0; dot < arraySize; dot++) {
- const ScatterRenderItem &item = m_renderingArrays.at(series).at(dot);
- if (!item.isVisible())
- continue;
+ glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom
+#endif
+ QVector3D modelScaler(itemSize, itemSize, itemSize);
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
+ // Rebind selection shader if it has changed
+ if (!totalIndex || drawingPoints != previousDrawingPoints) {
+ previousDrawingPoints = drawingPoints;
+ if (drawingPoints)
+ selectionShader = pointSelectionShader;
+ else
+ selectionShader = m_selectionShader;
- modelMatrix.translate(item.translation());
- if (!drawingPoints) {
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
- modelMatrix.rotate(seriesRotation * item.rotation());
- modelMatrix.scale(modelScaler);
+ selectionShader->bind();
}
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
-
- QVector3D dotColor = indexToSelectionColor(dotNo);
- dotColor /= 255.0f;
-
- selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix);
- selectionShader->setUniformValue(selectionShader->color(), dotColor);
-
- if (drawingPoints) {
- m_drawer->drawPoint(selectionShader);
- } else {
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(selectionShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf());
- glVertexAttribPointer(selectionShader->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(selectionShader->posAtt());
+ cache->setSelectionIndexOffset(totalIndex);
+ for (int dot = 0; dot < renderArraySize; dot++) {
+ const ScatterRenderItem &item = renderArray.at(dot);
+ if (!item.isVisible()) {
+ totalIndex++;
+ continue;
+ }
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.translate(item.translation());
+ if (!drawingPoints) {
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
+ modelMatrix.rotate(seriesRotation * item.rotation());
+ modelMatrix.scale(modelScaler);
+ }
+
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ QVector4D dotColor = indexToSelectionColor(totalIndex++);
+ dotColor /= 255.0f;
+
+ selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix);
+ selectionShader->setUniformValue(selectionShader->color(), dotColor);
+
+ if (drawingPoints)
+ m_drawer->drawPoint(selectionShader);
+ else
+ m_drawer->drawSelectionObject(selectionShader, dotObj);
}
- dotNo++;
}
}
+
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+
+ drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
+
glEnable(GL_DITHER);
// Read color under cursor
- QVector3D clickedColor = Utils::getSelection(m_inputPosition,
+ QVector4D clickedColor = Utils::getSelection(m_inputPosition,
m_viewport.height());
selectionColorToSeriesAndIndex(clickedColor, m_clickedIndex, m_clickedSeries);
@@ -571,15 +649,14 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
GLuint gradientTexture = 0;
bool dotSelectionFound = false;
ScatterRenderItem *selectedItem(0);
- int dotNo = 0;
- QVector3D baseColor;
- QVector3D dotColor;
+ QVector4D baseColor;
+ QVector4D dotColor;
bool previousDrawingPoints = false;
Q3DTheme::ColorStyle previousMeshColorStyle = Q3DTheme::ColorStyleUniform;
- if (haveMeshSeries) {
+ if (m_haveMeshSeries) {
// Set unchanging shader bindings
- if (haveGradientMeshSeries) {
+ if (m_haveGradientMeshSeries) {
m_dotGradientShader->bind();
m_dotGradientShader->setUniformValue(m_dotGradientShader->lightP(), lightPos);
m_dotGradientShader->setUniformValue(m_dotGradientShader->view(), viewMatrix);
@@ -587,7 +664,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
m_cachedTheme->ambientLightStrength());
m_dotGradientShader->setUniformValue(m_dotGradientShader->lightColor(), lightColor);
}
- if (haveUniformColorMeshSeries) {
+ if (m_haveUniformColorMeshSeries) {
m_dotShader->bind();
m_dotShader->setUniformValue(m_dotShader->lightP(), lightPos);
m_dotShader->setUniformValue(m_dotShader->view(), viewMatrix);
@@ -607,152 +684,281 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
dotShader->bind();
}
- for (int series = 0; series < seriesCount; series++) {
- const SeriesRenderCache &currentSeries = m_visibleSeriesList.at(series);
- QQuaternion seriesRotation = currentSeries.meshRotation();
- ObjectHelper *dotObj = currentSeries.object();
- bool drawingPoints = (currentSeries.mesh() == QAbstract3DSeries::MeshPoint);
- Q3DTheme::ColorStyle colorStyle = currentSeries.colorStyle();
- bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
- bool useColor = colorStyleIsUniform || drawingPoints;
-
- float itemSize = m_cachedItemSize.at(series) / itemScaler;
- if (itemSize == 0.0f)
- itemSize = m_dotSizeScale;
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ ScatterSeriesRenderCache *cache =
+ static_cast<ScatterSeriesRenderCache *>(baseCache);
+ ObjectHelper *dotObj = cache->object();
+ QQuaternion seriesRotation(cache->meshRotation());
+ ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int renderArraySize = renderArray.size();
+ bool selectedSeries = m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
+ && (m_selectedSeriesCache == cache);
+ bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
+ Q3DTheme::ColorStyle colorStyle = cache->colorStyle();
+ bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
+ bool useColor = colorStyleIsUniform || drawingPoints;
+ bool rangeGradientPoints = drawingPoints
+ && (colorStyle == Q3DTheme::ColorStyleRangeGradient);
+ float itemSize = cache->itemSize() / itemScaler;
+ 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)
+ glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom
#endif
- QVector3D modelScaler(itemSize, itemSize, itemSize);
-
- // Rebind shader if it has changed
- if (drawingPoints != previousDrawingPoints
- || (!drawingPoints &&
- (colorStyleIsUniform != (previousMeshColorStyle == Q3DTheme::ColorStyleUniform)))) {
- previousDrawingPoints = drawingPoints;
- if (drawingPoints) {
- dotShader = pointSelectionShader;
- } else {
- if (colorStyleIsUniform)
- dotShader = m_dotShader;
- else
- dotShader = m_dotGradientShader;
+ QVector3D modelScaler(itemSize, itemSize, itemSize);
+
+ // Rebind shader if it has changed
+ if (drawingPoints != previousDrawingPoints
+ || (!drawingPoints &&
+ (colorStyleIsUniform != (previousMeshColorStyle
+ == Q3DTheme::ColorStyleUniform)))) {
+ previousDrawingPoints = drawingPoints;
+ if (drawingPoints) {
+ dotShader = pointSelectionShader;
+ } else {
+ if (colorStyleIsUniform)
+ dotShader = m_dotShader;
+ else
+ dotShader = m_dotGradientShader;
+ }
+ dotShader->bind();
}
- dotShader->bind();
- }
- 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);
- } else {
- // Each ball is of uniform color according to its Y-coordinate
- m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f);
+ 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);
+ } else {
+ // Each dot is of uniform color according to its Y-coordinate
+ m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(),
+ 0.0f);
+ }
}
- }
- if (!drawingPoints)
- previousMeshColorStyle = colorStyle;
+ if (!drawingPoints)
+ previousMeshColorStyle = colorStyle;
- if (useColor) {
- baseColor = currentSeries.baseColor();
- dotColor = baseColor;
- }
+ if (useColor) {
+ baseColor = cache->baseColor();
+ dotColor = baseColor;
+ }
+ int loopCount = 1;
+ if (optimizationDefault)
+ loopCount = renderArraySize;
+ for (int i = 0; i < loopCount; i++) {
+ ScatterRenderItem &item = renderArray[i];
+ if (!item.isVisible() && optimizationDefault)
+ continue;
- int seriesSize = m_renderingArrays.at(series).size();
- for (int dot = 0; dot < seriesSize; dot++) {
- ScatterRenderItem &item = m_renderingArrays[series][dot];
- if (!item.isVisible())
- continue;
-
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- 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);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ if (optimizationDefault) {
+ 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);
+ }
}
- modelMatrix.scale(modelScaler);
- itModelMatrix.scale(modelScaler);
- }
#ifdef SHOW_DEPTH_TEXTURE_SCENE
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- if (useColor)
- dotColor = baseColor;
- else
- gradientTexture = currentSeries.baseGradientTexture();
+ if (useColor) {
+ 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;
+ dotColor = Utils::vectorFromColor(
+ cache->gradientImage().pixel(0, position));
+ } else {
+ dotColor = baseColor;
+ }
+ } else {
+ gradientTexture = cache->baseGradientTexture();
+ }
+
+ GLfloat lightStrength = m_cachedTheme->lightStrength();
+ if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) {
+ if (useColor)
+ dotColor = cache->singleHighlightColor();
+ else
+ gradientTexture = cache->singleHighlightGradientTexture();
+ lightStrength = m_cachedTheme->highlightLightStrength();
+ // Insert data to ScatterRenderItem
+ // We don't have ownership, so don't delete the previous one
+ 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());
+ }
+
+ 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 !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (!drawingPoints) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ dotShader->setUniformValue(dotShader->shadowQ(), m_shadowQualityToShader);
+ dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix);
+ dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f);
+
+ // Draw the object
+ if (optimizationDefault) {
+ m_drawer->drawObject(dotShader, dotObj, gradientTexture,
+ m_depthTexture);
+ } else {
+ m_drawer->drawObject(dotShader, cache->bufferObject(), gradientTexture,
+ m_depthTexture);
+ }
+ } else {
+ // Draw the object
+ if (optimizationDefault)
+ m_drawer->drawPoint(dotShader);
+ else
+ m_drawer->drawPoints(dotShader, cache->bufferPoints());
+ }
+ } else
+#endif
+ {
+ if (!drawingPoints) {
+ // Set shadowless shader bindings
+ dotShader->setUniformValue(dotShader->lightS(), lightStrength);
+ // Draw the object
+ if (optimizationDefault)
+ m_drawer->drawObject(dotShader, dotObj, gradientTexture);
+ else
+ m_drawer->drawObject(dotShader, cache->bufferObject(), gradientTexture);
+ } else {
+ // Draw the object
+ if (optimizationDefault)
+ m_drawer->drawPoint(dotShader);
+ else
+ m_drawer->drawPoints(dotShader, cache->bufferPoints());
+ }
+ }
+ }
+
+ // Draw the selected item on static optimization
+ if (!optimizationDefault && selectedSeries
+ && m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) {
+ ScatterRenderItem &item = renderArray[m_selectedItemIndex];
+ ObjectHelper *dotObj = cache->object();
+
+ 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);
+ }
+
+ QMatrix4x4 MVPMatrix;
+#ifdef SHOW_DEPTH_TEXTURE_SCENE
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+#else
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+#endif
- GLfloat lightStrength = m_cachedTheme->lightStrength();
- if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
- && (m_selectedItemTotalIndex == dotNo)) {
if (useColor)
- dotColor = currentSeries.singleHighlightColor();
+ dotColor = cache->singleHighlightColor();
else
- gradientTexture = currentSeries.singleHighlightGradientTexture();
- lightStrength = m_cachedTheme->highlightLightStrength();
- // Insert data to ScatterRenderItem. We have no ownership, don't delete the previous one
+ 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
+ // 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());
- }
-
- 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 !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
if (!drawingPoints) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- dotShader->setUniformValue(dotShader->shadowQ(), m_shadowQualityToShader);
- dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix);
- dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f);
+ // Set shader bindings
+ dotShader->setUniformValue(dotShader->model(), modelMatrix);
+ dotShader->setUniformValue(dotShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ }
- // Draw the object
- m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawPoint(dotShader);
+ 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);
}
- } else
-#endif
- {
+
if (!drawingPoints) {
- // Set shadowless shader bindings
- dotShader->setUniformValue(dotShader->lightS(), lightStrength);
- // Draw the object
- m_drawer->drawObject(dotShader, dotObj, gradientTexture);
- } else {
- // Draw the object
- m_drawer->drawPoint(dotShader);
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(-0.5f, 1.0f);
+ }
+
+#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);
+ }
+ } else
+#endif
+ {
+ if (!drawingPoints) {
+ // Set shadowless shader bindings
+ dotShader->setUniformValue(dotShader->lightS(), lightStrength);
+ // Draw the object
+ m_drawer->drawObject(dotShader, dotObj, gradientTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawPoint(dotShader);
+ }
}
+
+ if (!drawingPoints)
+ glDisable(GL_POLYGON_OFFSET_FILL);
}
- dotNo++;
}
}
#if !defined(QT_OPENGL_ES_2)
- if (havePointSeries) {
+ if (m_havePointSeries) {
glDisable(GL_POINT_SMOOTH);
glDisable(GL_PROGRAM_POINT_SIZE);
}
@@ -770,17 +976,19 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QMatrix4x4 itModelMatrix;
#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor;
- GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor;
+ 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, m_backgroundMargin, zScale);
+ QVector3D bgScale(xScale, 1.0f + m_backgroundMargin, zScale);
#else // ..and this if we want uniform scaling based on largest dimension
- QVector3D bgScale((aspectRatio * m_backgroundMargin),
- m_backgroundMargin,
- (aspectRatio * m_backgroundMargin));
+ QVector3D bgScale((m_graphAspectRatio + m_backgroundMargin),
+ 1.0f + m_backgroundMargin,
+ (m_graphAspectRatio + m_backgroundMargin));
#endif
modelMatrix.scale(bgScale);
// If we're viewing from below, background object must be flipped
@@ -797,7 +1005,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
+ QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
// Set shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos);
@@ -839,22 +1047,18 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
glDisable(GL_TEXTURE_2D);
// Draw grid lines
-#ifdef USE_UNIFORM_SCALING
- AxisRenderCache *axisCacheMax;
- if (m_axisCacheZ.max() > m_axisCacheX.max())
- axisCacheMax = &m_axisCacheZ;
- else
- axisCacheMax = &m_axisCacheX;
-#endif
-
- if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) {
+ 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
// Bind line shader
lineShader->bind();
// Set unchanging shader bindings
- QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
+ QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
lineShader->setUniformValue(lineShader->lightP(), lightPos);
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
@@ -887,39 +1091,33 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
else
lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f);
- GLfloat yFloorLinePosition = -m_backgroundMargin + gridLineOffset;
+ GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset;
if (m_yFlipped)
yFloorLinePosition = -yFloorLinePosition;
// Rows (= Z)
if (m_axisCacheZ.segmentCount() > 0) {
// Floor lines
-#ifndef USE_UNIFORM_SCALING
- GLfloat lineStep = aspectRatio * m_axisCacheZ.subSegmentStep();
- GLfloat linePos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); // Start line
- int lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount();
-#else
- GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep();
- GLfloat linePos = -aspectRatio * m_scaleFactor; // Start line
- int lastSegment = axisCacheMax->subSegmentCount() * axisCacheMax->segmentCount();
-#endif
+ int gridLineCount = m_axisCacheZ.gridLineCount();
#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor;
+ 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((aspectRatio * m_backgroundMargin),
+ QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin),
gridLineWidth, gridLineWidth);
#endif
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(0.0f, yFloorLinePosition, linePos / m_scaleFactor);
+ modelMatrix.translate(0.0f, yFloorLinePosition,
+ m_axisCacheZ.gridLinePosition(line));
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
@@ -942,42 +1140,45 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos -= lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
// Side wall lines
- gridLineScaler = QVector3D(gridLineWidth, m_backgroundMargin, gridLineWidth);
+ gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth);
#ifndef USE_UNIFORM_SCALING
- GLfloat lineXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width())
- / m_scaleFactor - gridLineOffset;
+ GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width())
+ / m_scaleFactor - gridLineOffset + m_backgroundMargin;
if (m_maxItemSize > lineXTrans)
lineXTrans = m_maxItemSize - gridLineOffset;
- linePos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); // Start line
#else
- GLfloat lineXTrans = aspectRatio * m_backgroundMargin - gridLineOffset;
- linePos = -aspectRatio * m_scaleFactor; // Start line
+ GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
#endif
if (!m_xFlipped)
lineXTrans = -lineXTrans;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(lineXTrans, 0.0f, linePos / m_scaleFactor);
+ modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
+#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;
@@ -994,41 +1195,42 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos -= lineStep;
+#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
// Floor lines
+ int gridLineCount = m_axisCacheX.gridLineCount();
+
#ifndef USE_UNIFORM_SCALING
- GLfloat lineStep = aspectRatio * m_axisCacheX.subSegmentStep();
- GLfloat linePos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x());
- int lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount();
- GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor;
+ GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor
+ + m_backgroundMargin;
if (m_maxItemSize > zScale)
zScale = m_maxItemSize;
QVector3D gridLineScaler(gridLineWidth, gridLineWidth, zScale);
#else
- GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep();
- GLfloat linePos = -aspectRatio * m_scaleFactor;
- int lastSegment = axisCacheMax->subSegmentCount() * axisCacheMax->segmentCount();
QVector3D gridLineScaler(gridLineWidth, gridLineWidth,
- aspectRatio * m_backgroundMargin);
+ m_graphAspectRatio + m_backgroundMargin);
#endif
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(linePos / m_scaleFactor, yFloorLinePosition, 0.0f);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
+ 0.0f);
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
@@ -1051,45 +1253,48 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
// Back wall lines
#ifndef USE_UNIFORM_SCALING
- GLfloat lineZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height())
- / m_scaleFactor - gridLineOffset;
+ GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height())
+ / m_scaleFactor - gridLineOffset + m_backgroundMargin;
if (m_maxItemSize > lineZTrans)
lineZTrans = m_maxItemSize - gridLineOffset;
- linePos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x());
#else
- GLfloat lineZTrans = aspectRatio * m_backgroundMargin - gridLineOffset;
- linePos = -aspectRatio * m_scaleFactor;
+ GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
#endif
if (!m_zFlipped)
lineZTrans = -lineZTrans;
- gridLineScaler = QVector3D(gridLineWidth, m_backgroundMargin, gridLineWidth);
+ gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth);
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(linePos / m_scaleFactor, 0.0f, lineZTrans);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
+#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
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1106,46 +1311,45 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
}
// Horizontal wall lines
if (m_axisCacheY.segmentCount() > 0) {
// Back wall
- GLfloat lineStep = m_axisCacheY.subSegmentStep();
- GLfloat linePos = m_axisCacheY.min() - m_translationOffset.y();
- int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
+ int gridLineCount = m_axisCacheY.gridLineCount();
#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat lineZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height())
- / m_scaleFactor - gridLineOffset;
+ GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height())
+ / m_scaleFactor - gridLineOffset + m_backgroundMargin;
if (m_maxItemSize > lineZTrans)
lineZTrans = m_maxItemSize - gridLineOffset;
- GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor;
+ 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 = aspectRatio * m_backgroundMargin - gridLineOffset;
- QVector3D gridLineScaler((aspectRatio * m_backgroundMargin),
+ GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
+ QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin),
gridLineWidth, gridLineWidth);
#endif
if (!m_zFlipped)
lineZTrans = -lineZTrans;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(0.0f, linePos / m_heightNormalizer, lineZTrans);
+ modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans);
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
@@ -1170,42 +1374,40 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
// Side wall
- linePos = m_axisCacheY.min() - m_translationOffset.y();
- lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat lineXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width())
- / m_scaleFactor - gridLineOffset;
+ GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width())
+ / m_scaleFactor - gridLineOffset + m_backgroundMargin;
if (m_maxItemSize > lineXTrans)
lineXTrans = m_maxItemSize - gridLineOffset;
- GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height())
- / m_scaleFactor;
+ 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 = aspectRatio * m_backgroundMargin - gridLineOffset;
+ GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
gridLineScaler = QVector3D(gridLineWidth, gridLineWidth,
- aspectRatio * m_backgroundMargin);
+ m_graphAspectRatio + m_backgroundMargin);
#endif
if (!m_xFlipped)
lineXTrans = -lineXTrans;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(lineXTrans, linePos / m_heightNormalizer, 0.0f);
+ modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f);
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
@@ -1228,328 +1430,478 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
+ }
+ }
+ }
+
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+
+ drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
+
+ // Handle selection clearing and selection label drawing
+ if (!dotSelectionFound) {
+ // We have no ownership, don't delete. Just NULL the pointer.
+ m_selectedItem = NULL;
+ } else {
+ glDisable(GL_DEPTH_TEST);
+ // Draw the selection label
+ LabelItem &labelItem = selectionLabelItem();
+ if (m_selectedItem != selectedItem || 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_selectedItem = selectedItem;
}
+
+ m_drawer->drawLabel(*selectedItem, labelItem, viewMatrix, projectionMatrix,
+ zeroVector, identityQuaternion, selectedItemSize, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, activeCamera, true, false,
+ Drawer::LabelOver);
+
+ // Reset label update flag; they should have been updated when we get here
+ m_updateLabels = false;
+ glEnable(GL_DEPTH_TEST);
}
- // Draw axis labels
- // Bind label shader
- m_labelShader->bind();
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+
+ // Release shader
+ glUseProgram(0);
+
+ m_selectionDirty = false;
+}
+
+void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
+ const QMatrix4x4 &viewMatrix,
+ const QMatrix4x4 &projectionMatrix) {
+ ShaderHelper *shader = 0;
+ GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
+ GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
+ GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
+ if (drawSelection) {
+ shader = m_selectionShader;
+ // m_selectionShader is already bound
+ } else {
+ shader = m_labelShader;
+ shader->bind();
+
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POLYGON_OFFSET_FILL);
+ float labelAutoAngle = m_axisCacheZ.labelAutoRotation();
+ float labelAngleFraction = labelAutoAngle / 90.0f;
+ float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
+ float fractionCamX = activeCamera->xRotation() * labelAngleFraction;
+ float labelsMaxWidth = 0.0f;
+
+ int startIndex;
+ int endIndex;
+ int indexStep;
+
// Z Labels
if (m_axisCacheZ.segmentCount() > 0) {
+ int labelCount = m_axisCacheZ.labelCount();
#ifndef USE_UNIFORM_SCALING
- GLfloat posStep = aspectRatio * m_axisCacheZ.segmentStep();
- GLfloat labelPos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z());
- int lastSegment = m_axisCacheZ.segmentCount();
- GLfloat labelXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width())
- / m_scaleFactor + labelMargin;
+ GLfloat labelXTrans = (m_graphAspectRatio * m_areaSize.width())
+ / m_scaleFactor + labelMargin + m_backgroundMargin;
if (m_maxItemSize > labelXTrans)
labelXTrans = m_maxItemSize + labelMargin;
#else
- GLfloat posStep = aspectRatio * axisCacheMax->segmentStep();
- GLfloat labelPos = aspectRatio * m_scaleFactor;
- int lastSegment = axisCacheMax->segmentCount();
- GLfloat labelXTrans = aspectRatio * m_backgroundMargin + labelMargin;
+ GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin;
#endif
- int labelNbr = 0;
- GLfloat labelYTrans = -m_backgroundMargin;
- GLfloat rotLabelX = -90.0f;
- GLfloat rotLabelY = 0.0f;
- GLfloat rotLabelZ = 0.0f;
- Qt::AlignmentFlag alignment = Qt::AlignRight;
- if (m_zFlipped)
- rotLabelY = 180.0f;
- if (m_xFlipped) {
+ GLfloat labelYTrans = -1.0f - m_backgroundMargin;
+ Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+ QVector3D labelRotation;
+ if (m_xFlipped)
labelXTrans = -labelXTrans;
- alignment = Qt::AlignLeft;
- }
- if (m_yFlipped) {
- rotLabelZ += 180.0f;
- rotLabelY += 180.0f;
+ if (m_yFlipped)
labelYTrans = -labelYTrans;
+ if (labelAutoAngle == 0.0f) {
+ labelRotation.setX(-90.0f);
+ if (m_zFlipped)
+ labelRotation.setY(180.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped)
+ labelRotation.setY(0.0f);
+ else
+ labelRotation.setY(180.0f);
+ labelRotation.setZ(180.0f);
+ }
+ } else {
+ if (m_zFlipped)
+ labelRotation.setY(180.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ }
+ }
}
- QVector3D labelRotateVector(rotLabelX, rotLabelY, rotLabelZ);
+ QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
QVector3D labelTrans = QVector3D(labelXTrans, labelYTrans, 0.0f);
- for (int segment = 0; segment <= lastSegment; segment++) {
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- if (m_axisCacheZ.labelItems().size() > labelNbr) {
-#else // ..and this if we want uniform scaling based on largest dimension
- if (axisCacheMax->labelItems().size() > labelNbr) {
-#endif
- labelTrans.setZ(labelPos / m_scaleFactor);
+ if (m_zFlipped) {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
+ } else {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ }
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
+ labelTrans.setZ(m_axisCacheZ.labelPosition(label));
- glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f);
+ glPolygonOffset(GLfloat(label) / -10.0f, 1.0f);
- // Draw the label here
- m_dummyRenderItem.setTranslation(labelTrans);
-#ifndef USE_UNIFORM_SCALING
- const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(labelNbr);
-#else
- const LabelItem &axisLabelItem = *axisCacheMax->labelItems().at(labelNbr);
-#endif
+ // Draw the label here
+ m_dummyRenderItem.setTranslation(labelTrans);
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, labelRotateVector, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera, true, true,
- Drawer::LabelMid, alignment);
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
+ alphaForRowSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
- labelNbr++;
- labelPos -= posStep;
+
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera, true, true,
+ Drawer::LabelMid, alignment, false, drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+ if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
+ labelTrans.setZ(0.0f);
+ drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
}
+
// X Labels
if (m_axisCacheX.segmentCount() > 0) {
+ labelsMaxWidth = 0.0f;
+ labelAutoAngle = m_axisCacheX.labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ fractionCamY = activeCamera->yRotation() * labelAngleFraction;
+ fractionCamX = activeCamera->xRotation() * labelAngleFraction;
+ int labelCount = m_axisCacheX.labelCount();
#ifndef USE_UNIFORM_SCALING
- GLfloat posStep = aspectRatio * m_axisCacheX.segmentStep();
- GLfloat labelPos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x());
- int lastSegment = m_axisCacheX.segmentCount();
- GLfloat labelZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height())
- / m_scaleFactor + labelMargin;
+ GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height())
+ / m_scaleFactor + labelMargin + m_backgroundMargin;
if (m_maxItemSize > labelZTrans)
labelZTrans = m_maxItemSize + labelMargin;
#else
- GLfloat posStep = aspectRatio * axisCacheMax->segmentStep();
- GLfloat labelPos = -aspectRatio * m_scaleFactor;
- int lastSegment = axisCacheMax->segmentCount();
- GLfloat labelZTrans = aspectRatio * m_backgroundMargin + labelMargin;
+ GLfloat labelZTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin;
#endif
- int labelNbr = 0;
- GLfloat labelYTrans = -m_backgroundMargin;
- GLfloat rotLabelX = -90.0f;
- GLfloat rotLabelY = 90.0f;
- GLfloat rotLabelZ = 0.0f;
- Qt::AlignmentFlag alignment = Qt::AlignLeft;
- if (m_xFlipped)
- rotLabelY = -90.0f;
- if (m_zFlipped) {
+ GLfloat labelYTrans = -1.0f - m_backgroundMargin;
+ Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+ QVector3D labelRotation;
+ if (m_zFlipped)
labelZTrans = -labelZTrans;
- alignment = Qt::AlignRight;
- }
- if (m_yFlipped) {
- rotLabelZ += 180.0f;
- rotLabelY += 180.0f;
+ if (m_yFlipped)
labelYTrans = -labelYTrans;
+ if (labelAutoAngle == 0.0f) {
+ labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
+ if (m_xFlipped)
+ labelRotation.setY(-90.0f);
+ if (m_yFlipped) {
+ if (m_xFlipped)
+ labelRotation.setY(90.0f);
+ else
+ labelRotation.setY(-90.0f);
+ labelRotation.setZ(180.0f);
+ }
+ } else {
+ if (m_xFlipped)
+ labelRotation.setY(-90.0f);
+ else
+ labelRotation.setY(90.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f + fractionCamX
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - fractionCamX
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f - fractionCamX
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + fractionCamX
+ * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ }
+ }
+ }
}
- QVector3D labelRotateVector(rotLabelX, rotLabelY, rotLabelZ);
+
+ QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans);
- for (int segment = 0; segment <= lastSegment; segment++) {
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- if (m_axisCacheX.labelItems().size() > labelNbr) {
-#else // ..and this if we want uniform scaling based on largest dimension
- if (axisCacheMax->labelItems().size() > labelNbr) {
-#endif
- labelTrans.setX(labelPos / m_scaleFactor);
+ if (m_xFlipped) {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ } else {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
+ }
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
+ labelTrans.setX(m_axisCacheX.labelPosition(label));
- glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f);
+ glPolygonOffset(GLfloat(label) / -10.0f, 1.0f);
- // Draw the label here
- m_dummyRenderItem.setTranslation(labelTrans);
-#ifndef USE_UNIFORM_SCALING
- const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(labelNbr);
-#else
- const LabelItem &axisLabelItem = *axisCacheMax->labelItems().at(labelNbr);
-#endif
+ // Draw the label here
+ m_dummyRenderItem.setTranslation(labelTrans);
+ const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label);
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, labelRotateVector, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera, true, true,
- Drawer::LabelMid, alignment);
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
+ alphaForColumnSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
- labelNbr++;
- labelPos += posStep;
+
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera, true, true,
+ Drawer::LabelMid, alignment, false, drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+ if (!drawSelection && m_axisCacheX.isTitleVisible()) {
+ labelTrans.setX(0.0f);
+ drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
}
+
// Y Labels
if (m_axisCacheY.segmentCount() > 0) {
- GLfloat posStep = m_axisCacheY.segmentStep();
- GLfloat labelPos = m_axisCacheY.min() - m_translationOffset.y();
- int labelNbr = 0;
+ labelsMaxWidth = 0.0f;
+ labelAutoAngle = m_axisCacheY.labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ 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 = (aspectRatio * m_backgroundMargin * m_areaSize.width())
- / m_scaleFactor;
- GLfloat labelZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height())
- / m_scaleFactor;
+ 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 = aspectRatio * m_backgroundMargin;
+ GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin;
GLfloat labelZTrans = labelXTrans;
#endif
- // Back wall init
+ // Back & side wall
GLfloat labelMarginXTrans = labelMargin;
GLfloat labelMarginZTrans = labelMargin;
- GLfloat rotLabelX = 0.0f;
- GLfloat rotLabelY = -90.0f;
- GLfloat rotLabelZ = 0.0f;
- Qt::AlignmentFlag alignmentBack = Qt::AlignLeft;
+ 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;
+ Qt::AlignmentFlag sideAlignment =
+ (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
if (!m_xFlipped) {
labelXTrans = -labelXTrans;
labelMarginXTrans = -labelMargin;
- rotLabelY = 90.0f;
}
if (m_zFlipped) {
labelZTrans = -labelZTrans;
labelMarginZTrans = -labelMargin;
- alignmentBack = Qt::AlignRight;
}
- QVector3D labelRotateVectorBack(rotLabelX, rotLabelY, rotLabelZ);
- QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans);
+ if (labelAutoAngle == 0.0f) {
+ if (!m_xFlipped)
+ backLabelRotation.setY(90.0f);
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.f);
+ } else {
+ // Orient side labels somewhat towards the camera
+ if (m_xFlipped) {
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ sideLabelRotation.setY(-fractionCamX);
+ backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
+ } else {
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ sideLabelRotation.setY(-fractionCamX);
+ backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
+ }
+ }
+ sideLabelRotation.setX(-fractionCamY);
+ backLabelRotation.setX(-fractionCamY);
- // Side wall init
- Qt::AlignmentFlag alignmentSide = Qt::AlignLeft;
- if (m_xFlipped)
- alignmentSide = Qt::AlignLeft;
- else
- alignmentSide = Qt::AlignRight;
- if (m_zFlipped)
- rotLabelY = 180.0f;
- else
- rotLabelY = 0.0f;
+ QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation);
+ QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation);
- QVector3D labelRotateVectorSide(rotLabelX, rotLabelY, rotLabelZ);
+ QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans);
QVector3D labelTransSide(-labelXTrans - labelMarginXTrans, 0.0f, -labelZTrans);
- for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) {
- if (m_axisCacheY.labelItems().size() > labelNbr) {
- const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
- const GLfloat labelYTrans = labelPos / m_heightNormalizer;
-
- glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f);
-
- // Back wall
- labelTransBack.setY(labelYTrans);
- m_dummyRenderItem.setTranslation(labelTransBack);
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, labelRotateVectorBack, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera, true, true,
- Drawer::LabelMid, alignmentBack);
-
- // Side wall
- labelTransSide.setY(labelYTrans);
- m_dummyRenderItem.setTranslation(labelTransSide);
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- zeroVector, labelRotateVectorSide, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera, true, true,
- Drawer::LabelMid, alignmentSide);
- }
- labelNbr++;
- labelPos += posStep;
+ if (m_yFlipped) {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ } else {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
}
- }
- glDisable(GL_POLYGON_OFFSET_FILL);
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
+ const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label);
+ const GLfloat labelYTrans = m_axisCacheY.labelPosition(label);
- // Handle selection clearing and selection label drawing
- if (!dotSelectionFound) {
- // We have no ownership, don't delete. Just NULL the pointer.
- m_selectedItem = NULL;
- } else {
- glDisable(GL_DEPTH_TEST);
- // Draw the selection label
- LabelItem &labelItem = selectionLabelItem();
- if (m_selectedItem != selectedItem || m_updateLabels
- || !labelItem.textureId() || m_selectionLabelDirty) {
- QString labelText = selectionLabel();
- if (labelText.isNull() || m_selectionLabelDirty) {
- static const QString xTitleTag(QStringLiteral("@xTitle"));
- static const QString yTitleTag(QStringLiteral("@yTitle"));
- static const QString zTitleTag(QStringLiteral("@zTitle"));
- static const QString xLabelTag(QStringLiteral("@xLabel"));
- static const QString yLabelTag(QStringLiteral("@yLabel"));
- static const QString zLabelTag(QStringLiteral("@zLabel"));
- static const QString seriesNameTag(QStringLiteral("@seriesName"));
-
- labelText = m_visibleSeriesList[m_selectedItemSeriesIndex].itemLabelFormat();
-
- labelText.replace(xTitleTag, m_axisCacheX.title());
- labelText.replace(yTitleTag, m_axisCacheY.title());
- labelText.replace(zTitleTag, m_axisCacheZ.title());
-
- if (labelText.contains(xLabelTag)) {
- QString labelFormat = m_axisCacheX.labelFormat();
- if (labelFormat.isEmpty())
- labelFormat = Utils::defaultLabelFormat();
- QString valueLabelText = generateValueLabel(labelFormat,
- selectedItem->position().x());
- labelText.replace(xLabelTag, valueLabelText);
- }
- if (labelText.contains(yLabelTag)) {
- QString labelFormat = m_axisCacheY.labelFormat();
- if (labelFormat.isEmpty())
- labelFormat = Utils::defaultLabelFormat();
- QString valueLabelText = generateValueLabel(labelFormat,
- selectedItem->position().y());
- labelText.replace(yLabelTag, valueLabelText);
- }
- if (labelText.contains(zLabelTag)) {
- QString labelFormat = m_axisCacheZ.labelFormat();
- if (labelFormat.isEmpty())
- labelFormat = Utils::defaultLabelFormat();
- QString valueLabelText = generateValueLabel(labelFormat,
- selectedItem->position().z());
- labelText.replace(zLabelTag, valueLabelText);
- }
- labelText.replace(seriesNameTag, m_visibleSeriesList[m_selectedItemSeriesIndex].name());
+ glPolygonOffset(GLfloat(label) / -10.0f, 1.0f);
- setSelectionLabel(labelText);
- m_selectionLabelDirty = false;
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f,
+ alphaForValueSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
- m_drawer->generateLabelItem(labelItem, labelText);
- m_selectedItem = selectedItem;
- }
- m_drawer->drawLabel(*selectedItem, labelItem, viewMatrix, projectionMatrix,
- zeroVector, zeroVector, selectedItemSize, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera, true, false,
- Drawer::LabelOver);
+ // Back wall
+ labelTransBack.setY(labelYTrans);
+ m_dummyRenderItem.setTranslation(labelTransBack);
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalBackRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera, true, true,
+ Drawer::LabelMid, backAlignment, false, drawSelection);
- // Reset label update flag; they should have been updated when we get here
- m_updateLabels = false;
- glEnable(GL_DEPTH_TEST);
+ // Side wall
+ labelTransSide.setY(labelYTrans);
+ m_dummyRenderItem.setTranslation(labelTransSide);
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ zeroVector, totalSideRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera, true, true,
+ Drawer::LabelMid, sideAlignment, false, drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+ if (!drawSelection && m_axisCacheY.isTitleVisible()) {
+ labelTransSide.setY(0.0f);
+ labelTransBack.setY(0.0f);
+ drawAxisTitleY(sideLabelRotation, backLabelRotation, labelTransSide, labelTransBack,
+ totalSideRotation, totalBackRotation, m_dummyRenderItem, activeCamera,
+ labelsMaxWidth, viewMatrix, projectionMatrix,
+ shader);
+ }
}
-
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
-
- // Release shader
- glUseProgram(0);
-
- m_selectionDirty = false;
+ glDisable(GL_POLYGON_OFFSET_FILL);
}
-void Scatter3DRenderer::updateSelectedItem(int index, const QScatter3DSeries *series)
+void Scatter3DRenderer::updateSelectedItem(int index, QScatter3DSeries *series)
{
m_selectionDirty = true;
m_selectionLabelDirty = true;
- m_selectedSeries = series;
+ m_selectedSeriesCache =
+ static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(series, 0));
m_selectedItemIndex = Scatter3DController::invalidSelectionIndex();
- m_selectedItemTotalIndex = Scatter3DController::invalidSelectionIndex();
- m_selectedItemSeriesIndex = Scatter3DController::invalidSelectionIndex();
- if (!m_renderingArrays.isEmpty() && index != Scatter3DController::invalidSelectionIndex()) {
- int totalIndex = 0;
- for (int i = 0; i < m_visibleSeriesList.size(); i++) {
- if (m_visibleSeriesList.at(i).series() == series) {
- m_selectedItemSeriesIndex = i;
- m_selectedItemIndex = index;
- m_selectedItemTotalIndex = index + totalIndex;
- break;
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && m_oldSelectedSeriesCache
+ && m_oldSelectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) {
+ m_oldSelectedSeriesCache->bufferPoints()->popPoint();
+ m_oldSelectedSeriesCache = 0;
+ }
+
+ if (m_selectedSeriesCache) {
+ const ScatterRenderItemArray &renderArray = m_selectedSeriesCache->renderArray();
+ if (index < renderArray.size() && index >= 0) {
+ m_selectedItemIndex = index;
+
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && m_selectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) {
+ m_selectedSeriesCache->bufferPoints()->pushPoint(m_selectedItemIndex);
+ m_oldSelectedSeriesCache = m_selectedSeriesCache;
}
- totalIndex += m_renderingArrays.at(i).size();
}
}
}
@@ -1598,26 +1950,8 @@ void Scatter3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual
void Scatter3DRenderer::loadBackgroundMesh()
{
- if (m_backgroundObj)
- delete m_backgroundObj;
- m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background"));
- m_backgroundObj->load();
-}
-
-void Scatter3DRenderer::loadGridLineMesh()
-{
- if (m_gridLineObj)
- delete m_gridLineObj;
- m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane"));
- m_gridLineObj->load();
-}
-
-void Scatter3DRenderer::loadLabelMesh()
-{
- if (m_labelObj)
- delete m_labelObj;
- m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane"));
- m_labelObj->load();
+ ObjectHelper::resetObjectHelper(this, m_backgroundObj,
+ QStringLiteral(":/defaultMeshes/background"));
}
void Scatter3DRenderer::updateTextures()
@@ -1640,11 +1974,10 @@ void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Me
void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item)
{
// We need to normalize translations
- GLfloat xTrans = (aspectRatio * (item.position().x() - m_translationOffset.x()))
- / m_scaleFactor;
- GLfloat zTrans = -(aspectRatio * (item.position().z() - m_translationOffset.z()))
- / m_scaleFactor;
- GLfloat yTrans = (item.position().y() - m_translationOffset.y()) / m_heightNormalizer;
+ const QVector3D &pos = item.position();
+ float xTrans = m_axisCacheX.positionAt(pos.x());
+ float yTrans = m_axisCacheY.positionAt(pos.y());
+ float zTrans = m_axisCacheZ.positionAt(pos.z());
item.setTranslation(QVector3D(xTrans, yTrans, zTrans));
}
@@ -1655,10 +1988,16 @@ void Scatter3DRenderer::calculateSceneScalingFactors()
m_areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min()) / 2.0f);
m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height());
- // Calculate translation offsets
- m_translationOffset = QVector3D((m_axisCacheX.max() + m_axisCacheX.min()) / 2.0f,
- (m_axisCacheY.max() + m_axisCacheY.min()) / 2.0f,
- (m_axisCacheZ.max() + m_axisCacheZ.min()) / 2.0f);
+#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);
}
void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
@@ -1689,10 +2028,7 @@ void Scatter3DRenderer::initSelectionShader()
void Scatter3DRenderer::initSelectionBuffer()
{
- if (m_selectionTexture) {
- m_textureHelper->deleteTexture(&m_selectionTexture);
- m_selectionTexture = 0;
- }
+ m_textureHelper->deleteTexture(&m_selectionTexture);
if (m_primarySubViewport.size().isEmpty())
return;
@@ -1714,10 +2050,7 @@ void Scatter3DRenderer::initDepthShader()
void Scatter3DRenderer::updateDepthBuffer()
{
- if (m_depthTexture) {
- m_textureHelper->deleteTexture(&m_depthTexture);
- m_depthTexture = 0;
- }
+ m_textureHelper->deleteTexture(&m_depthTexture);
if (m_primarySubViewport.size().isEmpty())
return;
@@ -1758,30 +2091,54 @@ void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QStr
m_labelShader->initialize();
}
-QVector3D Scatter3DRenderer::indexToSelectionColor(GLint index)
-{
- GLubyte dotIdxRed = index & 0xff;
- GLubyte dotIdxGreen = (index & 0xff00) >> 8;
- GLubyte dotIdxBlue = (index & 0xff0000) >> 16;
-
- return QVector3D(dotIdxRed, dotIdxGreen, dotIdxBlue);
-}
-
-void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector3D &color,
+void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color,
int &index,
QAbstract3DSeries *&series)
{
+ m_clickedType = QAbstract3DGraph::ElementNone;
+ m_selectedLabelIndex = -1;
+ m_selectedCustomItemIndex = -1;
if (color != selectionSkipColor) {
- index = int(color.x())
- + (int(color.y()) << 8)
- + (int(color.z()) << 16);
- // Find the series and adjust the index accordingly
- for (int i = 0; i < m_renderingArrays.size(); i++) {
- if (index < m_renderingArrays.at(i).size()) {
- series = m_visibleSeriesList.at(i).series();
- return; // Valid found and already set to return parameters, so we can return
- } else {
- index -= m_renderingArrays.at(i).size();
+ if (color.w() == labelRowAlpha) {
+ // Row selection
+ index = Scatter3DController::invalidSelectionIndex();
+ m_selectedLabelIndex = color.x();
+ m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
+ } else if (color.w() == labelColumnAlpha) {
+ // Column selection
+ index = Scatter3DController::invalidSelectionIndex();
+ m_selectedLabelIndex = color.y();
+ m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
+ } else if (color.w() == labelValueAlpha) {
+ // Value selection
+ index = Scatter3DController::invalidSelectionIndex();
+ m_selectedLabelIndex = color.z();
+ m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
+ } else if (color.w() == customItemAlpha) {
+ // Custom item selection
+ index = Scatter3DController::invalidSelectionIndex();
+ m_selectedCustomItemIndex = int(color.x())
+ + (int(color.y()) << 8)
+ + (int(color.z()) << 16);
+ m_clickedType = QAbstract3DGraph::ElementCustomItem;
+ } else {
+ int totalIndex = int(color.x())
+ + (int(color.y()) << 8)
+ + (int(color.z()) << 16);
+ // Find the series and adjust the index accordingly
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ ScatterSeriesRenderCache *cache =
+ static_cast<ScatterSeriesRenderCache *>(baseCache);
+ int offset = cache->selectionIndexOffset();
+ if (totalIndex >= offset
+ && totalIndex < (offset + cache->renderArray().size())) {
+ index = totalIndex - offset;
+ series = cache->series();
+ m_clickedType = QAbstract3DGraph::ElementSeries;
+ return;
+ }
+ }
}
}
}
@@ -1791,4 +2148,41 @@ void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector3D &color,
series = 0;
}
+void Scatter3DRenderer::updateRenderItem(const QScatterDataItem &dataItem,
+ ScatterRenderItem &renderItem)
+{
+ QVector3D dotPos = dataItem.position();
+ if ((dotPos.x() >= m_axisCacheX.min() && dotPos.x() <= m_axisCacheX.max() )
+ && (dotPos.y() >= m_axisCacheY.min() && dotPos.y() <= m_axisCacheY.max())
+ && (dotPos.z() >= m_axisCacheZ.min() && dotPos.z() <= m_axisCacheZ.max())) {
+ renderItem.setPosition(dotPos);
+ renderItem.setVisible(true);
+ if (!dataItem.rotation().isIdentity())
+ renderItem.setRotation(dataItem.rotation().normalized());
+ else
+ renderItem.setRotation(identityQuaternion);
+ calculateTranslation(renderItem);
+ } else {
+ renderItem.setVisible(false);
+ }
+}
+
+QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &position,
+ bool isAbsolute)
+{
+ float xTrans = 0.0f;
+ float yTrans = 0.0f;
+ float zTrans = 0.0f;
+ if (!isAbsolute) {
+ xTrans = m_axisCacheX.positionAt(position.x());
+ 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;
+ }
+ return QVector3D(xTrans, yTrans, zTrans);
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h
index 5591a362..7f213179 100644
--- a/src/datavisualization/engine/scatter3drenderer_p.h
+++ b/src/datavisualization/engine/scatter3drenderer_p.h
@@ -32,20 +32,16 @@
#include "datavisualizationglobal_p.h"
#include "scatter3dcontroller_p.h"
#include "abstract3drenderer_p.h"
-#include "qscatterdataproxy.h"
#include "scatterrenderitem_p.h"
-class QPoint;
class QSizeF;
-class QOpenGLShaderProgram;
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class ShaderHelper;
-class ObjectHelper;
-class LabelItem;
class Q3DScene;
-class QAbstractAxisPrivate;
+class ScatterSeriesRenderCache;
+class QScatterDataItem;
class QT_DATAVISUALIZATION_EXPORT Scatter3DRenderer : public Abstract3DRenderer
{
@@ -54,9 +50,6 @@ class QT_DATAVISUALIZATION_EXPORT Scatter3DRenderer : public Abstract3DRenderer
private:
// Internal state
ScatterRenderItem *m_selectedItem; // points to renderitem array
- bool m_xFlipped;
- bool m_zFlipped;
- bool m_yFlipped;
bool m_updateLabels;
ShaderHelper *m_dotShader;
ShaderHelper *m_dotGradientShader;
@@ -67,9 +60,6 @@ private:
ShaderHelper *m_selectionShader;
ShaderHelper *m_backgroundShader;
ShaderHelper *m_labelShader;
- ObjectHelper *m_backgroundObj;
- ObjectHelper *m_gridLineObj;
- ObjectHelper *m_labelObj;
GLuint m_bgrTexture;
GLuint m_depthTexture;
GLuint m_selectionTexture;
@@ -81,33 +71,40 @@ private:
GLfloat m_heightNormalizer;
GLfloat m_scaleFactor;
int m_selectedItemIndex;
- int m_selectedItemTotalIndex;
- int m_selectedItemSeriesIndex;
- const QScatter3DSeries *m_selectedSeries;
+ ScatterSeriesRenderCache *m_selectedSeriesCache;
+ ScatterSeriesRenderCache *m_oldSelectedSeriesCache;
QSizeF m_areaSize;
GLfloat m_dotSizeScale;
- QVector3D m_translationOffset;
bool m_hasHeightAdjustmentChanged;
ScatterRenderItem m_dummyRenderItem;
- QVector<ScatterRenderItemArray> m_renderingArrays;
GLfloat m_backgroundMargin;
GLfloat m_maxItemSize;
- QVector<float> m_cachedItemSize;
int m_clickedIndex;
+ bool m_havePointSeries;
+ bool m_haveMeshSeries;
+ bool m_haveUniformColorMeshSeries;
+ bool m_haveGradientMeshSeries;
public:
explicit Scatter3DRenderer(Scatter3DController *controller);
~Scatter3DRenderer();
- void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility);
void updateData();
+ void updateSeries(const QList<QAbstract3DSeries *> &seriesList);
+ SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
+ void updateItems(const QVector<Scatter3DController::ChangeItem> &items);
void updateScene(Q3DScene *scene);
+ QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
+
inline int clickedIndex() const { return m_clickedIndex; }
void resetClickedStatus();
void render(GLuint defaultFboHandle);
+public slots:
+ void updateSelectedItem(int index, QScatter3DSeries *series);
+
protected:
virtual void initializeOpenGL();
@@ -119,10 +116,10 @@ private:
virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh);
void drawScene(GLuint defaultFboHandle);
+ void drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix);
void loadBackgroundMesh();
- void loadGridLineMesh();
- void loadLabelMesh();
void initSelectionShader();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
@@ -136,17 +133,11 @@ private:
void calculateTranslation(ScatterRenderItem &item);
void calculateSceneScalingFactors();
- Q_DISABLE_COPY(Scatter3DRenderer)
-
- friend class ScatterRenderItem;
-
-public slots:
- void updateSelectedItem(int index, const QScatter3DSeries *series);
-
-private:
- QVector3D indexToSelectionColor(GLint index);
- void selectionColorToSeriesAndIndex(const QVector3D &color, int &index,
+ void selectionColorToSeriesAndIndex(const QVector4D &color, int &index,
QAbstract3DSeries *&series);
+ inline void updateRenderItem(const QScatterDataItem &dataItem, ScatterRenderItem &renderItem);
+
+ Q_DISABLE_COPY(Scatter3DRenderer)
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/scatterseriesrendercache.cpp b/src/datavisualization/engine/scatterseriesrendercache.cpp
new file mode 100644
index 00000000..e8888d19
--- /dev/null
+++ b/src/datavisualization/engine/scatterseriesrendercache.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 "scatterseriesrendercache_p.h"
+#include "scatterobjectbufferhelper_p.h"
+#include "scatterpointbufferhelper_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+ScatterSeriesRenderCache::ScatterSeriesRenderCache(QAbstract3DSeries *series,
+ Abstract3DRenderer *renderer)
+ : SeriesRenderCache(series, renderer),
+ m_itemSize(0.0f),
+ m_selectionIndexOffset(0),
+ m_oldRenderArraySize(0),
+ m_oldMeshFileName(QString()),
+ m_scatterBufferObj(0),
+ m_scatterBufferPoints(0)
+{
+}
+
+ScatterSeriesRenderCache::~ScatterSeriesRenderCache()
+{
+ delete m_scatterBufferObj;
+ delete m_scatterBufferPoints;
+}
+
+void ScatterSeriesRenderCache::cleanup(TextureHelper *texHelper)
+{
+ m_renderArray.clear();
+
+ SeriesRenderCache::cleanup(texHelper);
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/scatterseriesrendercache_p.h b/src/datavisualization/engine/scatterseriesrendercache_p.h
new file mode 100644
index 00000000..490e21fb
--- /dev/null
+++ b/src/datavisualization/engine/scatterseriesrendercache_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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 SCATTERSERIESRENDERCACHE_P_H
+#define SCATTERSERIESRENDERCACHE_P_H
+
+#include "datavisualizationglobal_p.h"
+#include "seriesrendercache_p.h"
+#include "qscatter3dseries_p.h"
+#include "scatterrenderitem_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class ScatterObjectBufferHelper;
+class ScatterPointBufferHelper;
+
+class ScatterSeriesRenderCache : public SeriesRenderCache
+{
+public:
+ ScatterSeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer);
+ virtual ~ScatterSeriesRenderCache();
+
+ void cleanup(TextureHelper *texHelper);
+
+ inline ScatterRenderItemArray &renderArray() { return m_renderArray; }
+ inline QScatter3DSeries *series() const { return static_cast<QScatter3DSeries *>(m_series); }
+ inline void setItemSize(float size) { m_itemSize = size; }
+ inline float itemSize() const { return m_itemSize; }
+ inline void setSelectionIndexOffset(int offset) { m_selectionIndexOffset = offset; }
+ inline int selectionIndexOffset() const { return m_selectionIndexOffset; }
+ inline int oldArraySize() const { return m_oldRenderArraySize; }
+ inline void setOldArraySize(int size) { m_oldRenderArraySize = size; }
+ inline const QString &oldMeshFileName() const { return m_oldMeshFileName; }
+ inline void setOldMeshFileName(const QString &meshFileName) { m_oldMeshFileName = meshFileName; }
+ inline void setBufferObject(ScatterObjectBufferHelper *object) { m_scatterBufferObj = object; }
+ inline ScatterObjectBufferHelper *bufferObject() const { return m_scatterBufferObj; }
+ inline void setBufferPoints(ScatterPointBufferHelper *object) { m_scatterBufferPoints = object; }
+ inline ScatterPointBufferHelper *bufferPoints() const { return m_scatterBufferPoints; }
+
+protected:
+ ScatterRenderItemArray m_renderArray;
+ float m_itemSize;
+ int m_selectionIndexOffset; // Temporarily cached value for selection color calculations
+ 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;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp
index d4e635bc..183d3f8e 100644
--- a/src/datavisualization/engine/selectionpointer.cpp
+++ b/src/datavisualization/engine/selectionpointer.cpp
@@ -17,16 +17,11 @@
****************************************************************************/
#include "selectionpointer_p.h"
-#include "surface3dcontroller_p.h"
#include "shaderhelper_p.h"
#include "objecthelper_p.h"
#include "texturehelper_p.h"
#include "q3dcamera_p.h"
-#include "drawer_p.h"
#include "utils_p.h"
-#include "q3dlight.h"
-
-#include <QtGui/QMatrix4x4>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -54,7 +49,6 @@ SelectionPointer::~SelectionPointer()
{
delete m_labelShader;
delete m_pointShader;
- delete m_labelObj;
delete m_textureHelper;
}
@@ -66,7 +60,6 @@ void SelectionPointer::initializeOpenGL()
m_drawer->initializeOpenGL();
initShaders();
- loadLabelMesh();
}
void SelectionPointer::updateScene(Q3DScene *scene)
@@ -74,7 +67,7 @@ void SelectionPointer::updateScene(Q3DScene *scene)
m_cachedScene = scene;
}
-void SelectionPointer::render(GLuint defaultFboHandle)
+void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho)
{
Q_UNUSED(defaultFboHandle)
@@ -89,17 +82,22 @@ void SelectionPointer::render(GLuint defaultFboHandle)
// Get view matrix
QMatrix4x4 viewMatrix;
QMatrix4x4 projectionMatrix;
+ GLfloat viewPortRatio = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height();
if (m_cachedIsSlicingActivated) {
- GLfloat aspect = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height();
GLfloat sliceUnitsScaled = sliceUnits / m_autoScaleAdjustment;
viewMatrix.lookAt(QVector3D(0.0f, 0.0f, 1.0f), zeroVector, upVector);
- projectionMatrix.ortho(-sliceUnitsScaled * aspect, sliceUnitsScaled * aspect,
+ projectionMatrix.ortho(-sliceUnitsScaled * viewPortRatio, sliceUnitsScaled * viewPortRatio,
-sliceUnitsScaled, sliceUnitsScaled,
-1.0f, 4.0f);
+ } else if (useOrtho) {
+ viewMatrix = camera->d_ptr->viewMatrix();
+ GLfloat orthoRatio = 2.0f;
+ projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
+ -orthoRatio, orthoRatio,
+ 0.0f, 100.0f);
} else {
viewMatrix = camera->d_ptr->viewMatrix();
- projectionMatrix.perspective(45.0f, (GLfloat)m_mainViewPort.width()
- / (GLfloat)m_mainViewPort.height(), 0.1f, 100.0f);
+ projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
}
// Calculate scale factor to get uniform font size
@@ -209,7 +207,7 @@ void SelectionPointer::updateSliceData(bool sliceActivated, GLfloat autoScaleAdj
m_autoScaleAdjustment = autoScaleAdjustment;
}
-void SelectionPointer::setHighlightColor(const QVector3D &colorVector)
+void SelectionPointer::setHighlightColor(const QVector4D &colorVector)
{
m_highlightColor = colorVector;
}
@@ -230,6 +228,11 @@ void SelectionPointer::setPointerObject(ObjectHelper *object)
m_pointObj = object;
}
+void SelectionPointer::setLabelObject(ObjectHelper *object)
+{
+ m_labelObj = object;
+}
+
void SelectionPointer::handleDrawerChange()
{
m_cachedTheme = m_drawer->theme();
@@ -264,12 +267,4 @@ void SelectionPointer::initShaders()
}
-void SelectionPointer::loadLabelMesh()
-{
- if (m_labelObj)
- delete m_labelObj;
- m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane"));
- m_labelObj->load();
-}
-
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/selectionpointer_p.h b/src/datavisualization/engine/selectionpointer_p.h
index 1eac22be..7dc28024 100644
--- a/src/datavisualization/engine/selectionpointer_p.h
+++ b/src/datavisualization/engine/selectionpointer_p.h
@@ -29,7 +29,6 @@
#ifndef SELECTIONPOINTER_P_H
#define SELECTIONPOINTER_P_H
-#include "q3dscene.h"
#include "datavisualizationglobal_p.h"
#include "surface3dcontroller_p.h"
@@ -37,7 +36,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class ShaderHelper;
class ObjectHelper;
-class SurfaceObject;
class TextureHelper;
class Drawer;
@@ -49,26 +47,26 @@ public:
explicit SelectionPointer(Drawer *drawer);
~SelectionPointer();
- void render(GLuint defaultFboHandle = 0);
+ void render(GLuint defaultFboHandle = 0, bool useOrtho = false);
void setPosition(const QVector3D &position);
void setLabel(const QString &label);
void setPointerObject(ObjectHelper *object);
+ void setLabelObject(ObjectHelper *object);
void handleDrawerChange();
void updateBoundingRect(const QRect &rect);
void updateScene(Q3DScene *scene);
void updateSliceData(bool sliceActivated, GLfloat autoScaleAdjustment);
- void setHighlightColor(const QVector3D &colorVector);
+ void setHighlightColor(const QVector4D &colorVector);
void setRotation(const QQuaternion &rotation);
private:
void initializeOpenGL();
void initShaders();
- void loadLabelMesh();
private:
ShaderHelper *m_labelShader;
ShaderHelper *m_pointShader;
- ObjectHelper *m_labelObj;
+ ObjectHelper *m_labelObj; // Not owned
ObjectHelper *m_pointObj; // Not owned
TextureHelper *m_textureHelper;
Q3DTheme *m_cachedTheme;
@@ -81,7 +79,7 @@ private:
QString m_label;
bool m_cachedIsSlicingActivated;
GLfloat m_autoScaleAdjustment;
- QVector3D m_highlightColor;
+ QVector4D m_highlightColor;
QQuaternion m_rotation;
};
diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp
index 896b3b28..dc4b9db3 100644
--- a/src/datavisualization/engine/seriesrendercache.cpp
+++ b/src/datavisualization/engine/seriesrendercache.cpp
@@ -17,7 +17,6 @@
****************************************************************************/
#include "seriesrendercache_p.h"
-#include "objecthelper_p.h"
#include "abstract3drenderer_p.h"
#include "texturehelper_p.h"
#include "utils_p.h"
@@ -26,14 +25,19 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
const QString smoothString(QStringLiteral("Smooth"));
-SeriesRenderCache::SeriesRenderCache()
- : m_series(0),
+SeriesRenderCache::SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer)
+ : m_series(series),
m_object(0),
m_mesh(QAbstract3DSeries::MeshCube),
m_baseUniformTexture(0),
m_baseGradientTexture(0),
+ m_gradientImage(0),
m_singleHighlightGradientTexture(0),
- m_multiHighlightGradientTexture(0)
+ m_multiHighlightGradientTexture(0),
+ m_valid(false),
+ m_visible(false),
+ m_renderer(renderer),
+ m_objectDirty(true)
{
}
@@ -41,27 +45,13 @@ SeriesRenderCache::~SeriesRenderCache()
{
}
-void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer *renderer)
+void SeriesRenderCache::populate(bool newSeries)
{
- Q_ASSERT(series);
+ QAbstract3DSeriesChangeBitField &changeTracker = m_series->d_ptr->m_changeTracker;
- bool seriesChanged = false;
-
- if (m_series != series) {
- m_series = series;
- seriesChanged = true;
- }
-
- QAbstract3DSeriesChangeBitField &changeTracker = series->d_ptr->m_changeTracker;
-
- if (seriesChanged || changeTracker.itemLabelFormatChanged) {
- m_itemLabelFormat = series->itemLabelFormat();
- changeTracker.itemLabelFormatChanged = false;
- }
-
- if (seriesChanged || changeTracker.meshChanged || changeTracker.meshSmoothChanged
+ if (newSeries || changeTracker.meshChanged || changeTracker.meshSmoothChanged
|| changeTracker.userDefinedMeshChanged) {
- m_mesh = series->mesh();
+ m_mesh = m_series->mesh();
changeTracker.meshChanged = false;
changeTracker.meshSmoothChanged = false;
changeTracker.userDefinedMeshChanged = false;
@@ -71,7 +61,7 @@ void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer *
// Compose mesh filename
if (m_mesh == QAbstract3DSeries::MeshUserDefined) {
// Always use the supplied mesh directly
- meshFileName = series->userDefinedMesh();
+ meshFileName = m_series->userDefinedMesh();
} else {
switch (m_mesh) {
case QAbstract3DSeries::MeshBar:
@@ -111,24 +101,18 @@ void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer *
break;
}
- if (series->isMeshSmooth() && m_mesh != QAbstract3DSeries::MeshPoint)
+ if (m_series->isMeshSmooth() && m_mesh != QAbstract3DSeries::MeshPoint)
meshFileName += smoothString;
// Give renderer an opportunity to customize the mesh
- renderer->fixMeshFileName(meshFileName, m_mesh);
+ m_renderer->fixMeshFileName(meshFileName, m_mesh);
}
- delete m_object;
- if (meshFileName.isEmpty()) {
- m_object = 0;
- } else {
- m_object = new ObjectHelper(meshFileName);
- m_object->load();
- }
+ ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFileName);
}
- if (seriesChanged || changeTracker.meshRotationChanged) {
- m_meshRotation = series->meshRotation().normalized();
+ if (newSeries || changeTracker.meshRotationChanged) {
+ m_meshRotation = m_series->meshRotation().normalized();
if (m_series->type() == QAbstract3DSeries::SeriesTypeBar
&& (m_meshRotation.x() || m_meshRotation.z())) {
m_meshRotation = identityQuaternion;
@@ -136,55 +120,73 @@ void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer *
changeTracker.meshRotationChanged = false;
}
- if (seriesChanged || changeTracker.colorStyleChanged) {
- m_colorStyle = series->colorStyle();
+ if (newSeries || changeTracker.colorStyleChanged) {
+ m_colorStyle = m_series->colorStyle();
changeTracker.colorStyleChanged = false;
}
- if (seriesChanged || changeTracker.baseColorChanged) {
- m_baseColor = Utils::vectorFromColor(series->baseColor());
+ if (newSeries || changeTracker.baseColorChanged) {
+ m_baseColor = Utils::vectorFromColor(m_series->baseColor());
if (m_series->type() == QAbstract3DSeries::SeriesTypeSurface)
- renderer->generateBaseColorTexture(series->baseColor(), &m_baseUniformTexture);
+ m_renderer->generateBaseColorTexture(m_series->baseColor(), &m_baseUniformTexture);
changeTracker.baseColorChanged = false;
}
- if (seriesChanged || changeTracker.baseGradientChanged) {
- QLinearGradient gradient = series->baseGradient();
- renderer->fixGradientAndGenerateTexture(&gradient, &m_baseGradientTexture);
+ if (newSeries || changeTracker.baseGradientChanged) {
+ QLinearGradient gradient = m_series->baseGradient();
+ m_gradientImage = Utils::getGradientImage(gradient);
+ m_renderer->fixGradientAndGenerateTexture(&gradient, &m_baseGradientTexture);
changeTracker.baseGradientChanged = false;
}
- if (seriesChanged || changeTracker.singleHighlightColorChanged) {
- m_singleHighlightColor = Utils::vectorFromColor(series->singleHighlightColor());
+ if (newSeries || changeTracker.singleHighlightColorChanged) {
+ m_singleHighlightColor = Utils::vectorFromColor(m_series->singleHighlightColor());
changeTracker.singleHighlightColorChanged = false;
}
- if (seriesChanged || changeTracker.singleHighlightGradientChanged) {
- QLinearGradient gradient = series->singleHighlightGradient();
- renderer->fixGradientAndGenerateTexture(&gradient, &m_singleHighlightGradientTexture);
+ if (newSeries || changeTracker.singleHighlightGradientChanged) {
+ QLinearGradient gradient = m_series->singleHighlightGradient();
+ m_renderer->fixGradientAndGenerateTexture(&gradient, &m_singleHighlightGradientTexture);
changeTracker.singleHighlightGradientChanged = false;
}
- if (seriesChanged || changeTracker.multiHighlightColorChanged) {
- m_multiHighlightColor = Utils::vectorFromColor(series->multiHighlightColor());
+ if (newSeries || changeTracker.multiHighlightColorChanged) {
+ m_multiHighlightColor = Utils::vectorFromColor(m_series->multiHighlightColor());
changeTracker.multiHighlightColorChanged = false;
}
- if (seriesChanged || changeTracker.multiHighlightGradientChanged) {
- QLinearGradient gradient = series->multiHighlightGradient();
- renderer->fixGradientAndGenerateTexture(&gradient, &m_multiHighlightGradientTexture);
+ if (newSeries || changeTracker.multiHighlightGradientChanged) {
+ QLinearGradient gradient = m_series->multiHighlightGradient();
+ m_renderer->fixGradientAndGenerateTexture(&gradient, &m_multiHighlightGradientTexture);
changeTracker.multiHighlightGradientChanged = false;
}
- if (seriesChanged || changeTracker.nameChanged) {
- m_name = series->name();
+ if (newSeries || changeTracker.nameChanged) {
+ m_name = m_series->name();
changeTracker.nameChanged = false;
}
+
+ if (newSeries || changeTracker.itemLabelChanged
+ || changeTracker.itemLabelVisibilityChanged) {
+ changeTracker.itemLabelChanged = false;
+ changeTracker.itemLabelVisibilityChanged = false;
+ // series->itemLabel() call resolves the item label and emits the changed signal
+ // if it is dirty, so we need to call it even if m_itemLabel is eventually set
+ // to an empty string.
+ m_itemLabel = m_series->itemLabel();
+ if (!m_series->isItemLabelVisible())
+ m_itemLabel = QString();
+ }
+
+ if (newSeries || changeTracker.visibilityChanged) {
+ m_visible = m_series->isVisible();
+ changeTracker.visibilityChanged = false;
+ }
}
void SeriesRenderCache::cleanup(TextureHelper *texHelper)
{
- delete m_object;
+ ObjectHelper::releaseObjectHelper(m_renderer, m_object);
if (QOpenGLContext::currentContext()) {
texHelper->deleteTexture(&m_baseUniformTexture);
texHelper->deleteTexture(&m_baseGradientTexture);
diff --git a/src/datavisualization/engine/seriesrendercache_p.h b/src/datavisualization/engine/seriesrendercache_p.h
index 77e050b0..96b61b87 100644
--- a/src/datavisualization/engine/seriesrendercache_p.h
+++ b/src/datavisualization/engine/seriesrendercache_p.h
@@ -41,48 +41,59 @@ class TextureHelper;
class SeriesRenderCache
{
public:
- SeriesRenderCache();
+ SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer);
virtual ~SeriesRenderCache();
- void populate(QAbstract3DSeries *series, Abstract3DRenderer *renderer);
+ virtual void populate(bool newSeries);
virtual void cleanup(TextureHelper *texHelper);
// NOTE: Series pointer can only be used to access the series when syncing with controller.
// It is not guaranteed to be valid while rendering and should only be used as an identifier.
inline QAbstract3DSeries *series() const { return m_series; }
- inline const QString &itemLabelFormat() const { return m_itemLabelFormat; }
inline const QAbstract3DSeries::Mesh &mesh() const { return m_mesh; }
inline const QQuaternion &meshRotation() const { return m_meshRotation; }
inline void setMeshRotation(const QQuaternion &rotation) { m_meshRotation = rotation; }
inline ObjectHelper *object() const { return m_object; }
inline const Q3DTheme::ColorStyle &colorStyle() const { return m_colorStyle; }
- inline const QVector3D &baseColor() const { return m_baseColor; }
+ inline const QVector4D &baseColor() const { return m_baseColor; }
inline const GLuint &baseUniformTexture() const { return m_baseUniformTexture; }
inline const GLuint &baseGradientTexture() const { return m_baseGradientTexture; }
- inline const QVector3D &singleHighlightColor() const { return m_singleHighlightColor; }
+ inline const QImage &gradientImage() const { return m_gradientImage; }
+ inline const QVector4D &singleHighlightColor() const { return m_singleHighlightColor; }
inline const GLuint &singleHighlightGradientTexture() const { return m_singleHighlightGradientTexture; }
- inline const QVector3D &multiHighlightColor() const { return m_multiHighlightColor; }
+ inline const QVector4D &multiHighlightColor() const { return m_multiHighlightColor; }
inline const GLuint &multiHighlightGradientTexture() const { return m_multiHighlightGradientTexture; }
inline const QString &name() const { return m_name; }
+ inline const QString &itemLabel() const { return m_itemLabel; }
+ inline void setValid(bool valid) { m_valid = valid; }
+ inline bool isValid() const { return m_valid; }
+ inline bool isVisible() const { return m_visible; }
+ inline void setDataDirty(bool state) { m_objectDirty = state; }
+ inline bool dataDirty() const { return m_objectDirty; }
protected:
QAbstract3DSeries *m_series;
- QString m_itemLabelFormat;
- ObjectHelper *m_object;
+ ObjectHelper *m_object; // Shared reference
QAbstract3DSeries::Mesh m_mesh;
QQuaternion m_meshRotation;
Q3DTheme::ColorStyle m_colorStyle;
- QVector3D m_baseColor;
+ QVector4D m_baseColor;
GLuint m_baseUniformTexture;
GLuint m_baseGradientTexture;
- QVector3D m_singleHighlightColor;
+ QImage m_gradientImage;
+ QVector4D m_singleHighlightColor;
GLuint m_singleHighlightGradientTexture;
- QVector3D m_multiHighlightColor;
+ QVector4D m_multiHighlightColor;
GLuint m_multiHighlightGradientTexture;
QString m_name;
+ QString m_itemLabel;
+ bool m_valid;
+ bool m_visible;
+ Abstract3DRenderer *m_renderer;
+ bool m_objectDirty;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/shaders/colorOnY.frag b/src/datavisualization/engine/shaders/colorOnY.frag
index 8c610cd7..7a5eb46b 100644
--- a/src/datavisualization/engine/shaders/colorOnY.frag
+++ b/src/datavisualization/engine/shaders/colorOnY.frag
@@ -6,7 +6,7 @@ uniform highp float ambientStrength;
uniform sampler2D textureSampler;
uniform highp float gradMin;
uniform highp float gradHeight;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
varying highp vec3 position_wrld;
varying highp vec3 normal_cmr;
@@ -17,8 +17,8 @@ varying highp vec2 coords_mdl;
void main() {
highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight));
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ 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);
diff --git a/src/datavisualization/engine/shaders/colorOnY_ES2.frag b/src/datavisualization/engine/shaders/colorOnY_ES2.frag
index 5b553562..4352de05 100644
--- a/src/datavisualization/engine/shaders/colorOnY_ES2.frag
+++ b/src/datavisualization/engine/shaders/colorOnY_ES2.frag
@@ -1,11 +1,11 @@
-uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform sampler2D textureSampler;
uniform highp float gradMin;
uniform highp float gradHeight;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
+varying highp vec3 lightPosition_wrld_frag;
varying highp vec3 position_wrld;
varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
@@ -15,10 +15,10 @@ varying highp vec2 coords_mdl;
void main() {
highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight));
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
- highp float distance = length(lightPosition_wrld - position_wrld);
+ highp float distance = length(lightPosition_wrld_frag - position_wrld);
highp vec3 n = normalize(normal_cmr);
highp vec3 l = normalize(lightDirection_cmr);
highp float cosTheta = dot(n, l);
diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag
index ca6fefb9..d16055a3 100644
--- a/src/datavisualization/engine/shaders/default.frag
+++ b/src/datavisualization/engine/shaders/default.frag
@@ -8,15 +8,15 @@ varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
uniform highp vec3 lightPosition_wrld;
-uniform highp vec3 color_mdl;
+uniform highp vec4 color_mdl;
uniform highp float lightStrength;
uniform highp float ambientStrength;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
void main() {
highp vec3 materialDiffuseColor = color_mdl.rgb;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
highp float distance = length(lightPosition_wrld - position_wrld);
diff --git a/src/datavisualization/engine/shaders/default.vert b/src/datavisualization/engine/shaders/default.vert
index efb40862..b454913b 100644
--- a/src/datavisualization/engine/shaders/default.vert
+++ b/src/datavisualization/engine/shaders/default.vert
@@ -8,6 +8,7 @@ uniform highp mat4 M;
uniform highp mat4 itM;
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;
@@ -17,10 +18,11 @@ varying highp vec2 coords_mdl;
void main() {
gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
coords_mdl = vertexPosition_mdl.xy;
- position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz;
- vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz;
+ 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 = (V * vec4(lightPosition_wrld, 1.0)).xyz;
+ vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
- normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ normal_cmr = vec4(V * itM * 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 bc5c18b6..73d66d5b 100644
--- a/src/datavisualization/engine/shaders/default_ES2.frag
+++ b/src/datavisualization/engine/shaders/default_ES2.frag
@@ -4,19 +4,19 @@ varying highp vec3 position_wrld;
varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
+varying highp vec3 lightPosition_wrld_frag;
-uniform highp vec3 lightPosition_wrld;
-uniform highp vec3 color_mdl;
+uniform highp vec4 color_mdl;
uniform highp float lightStrength;
uniform highp float ambientStrength;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
void main() {
highp vec3 materialDiffuseColor = color_mdl.rgb;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
- highp float distance = length(lightPosition_wrld - position_wrld);
+ highp float distance = length(lightPosition_wrld_frag - position_wrld);
highp vec3 n = normalize(normal_cmr);
highp vec3 l = normalize(lightDirection_cmr);
diff --git a/src/datavisualization/engine/shaders/plainColor.frag b/src/datavisualization/engine/shaders/plainColor.frag
index 099c87a1..da9ee060 100644
--- a/src/datavisualization/engine/shaders/plainColor.frag
+++ b/src/datavisualization/engine/shaders/plainColor.frag
@@ -1,7 +1,6 @@
-uniform highp vec3 color_mdl;
+uniform highp vec4 color_mdl;
void main() {
- gl_FragColor.rgb = color_mdl;
- gl_FragColor.a = 1.0;
+ gl_FragColor = color_mdl;
}
diff --git a/src/datavisualization/engine/shaders/shadow.frag b/src/datavisualization/engine/shaders/shadow.frag
index e2286dc5..237e9780 100644
--- a/src/datavisualization/engine/shaders/shadow.frag
+++ b/src/datavisualization/engine/shaders/shadow.frag
@@ -5,7 +5,7 @@ uniform highp float ambientStrength;
uniform highp float shadowQuality;
uniform highp sampler2D textureSampler;
uniform highp sampler2DShadow shadowMap;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
varying highp vec4 shadowCoord;
varying highp vec2 UV;
@@ -33,8 +33,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
void main() {
highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor * 0.2;
+ 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);
diff --git a/src/datavisualization/engine/shaders/shadow.vert b/src/datavisualization/engine/shaders/shadow.vert
index e29a8a30..0adcd43c 100644
--- a/src/datavisualization/engine/shaders/shadow.vert
+++ b/src/datavisualization/engine/shaders/shadow.vert
@@ -28,10 +28,10 @@ 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 = (M * vec4(vertexPosition_mdl, 1.0)).xyz;
- vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz;
+ 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;
- lightDirection_cmr = (V * vec4(lightPosition_wrld, 0.0)).xyz;
- normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ 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/shadowNoTex.frag b/src/datavisualization/engine/shaders/shadowNoTex.frag
index d54efea9..b2e7adfc 100644
--- a/src/datavisualization/engine/shaders/shadowNoTex.frag
+++ b/src/datavisualization/engine/shaders/shadowNoTex.frag
@@ -3,9 +3,9 @@
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp float shadowQuality;
-uniform highp vec3 color_mdl;
+uniform highp vec4 color_mdl;
uniform highp sampler2DShadow shadowMap;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
varying highp vec4 shadowCoord;
varying highp vec3 position_wrld;
@@ -32,8 +32,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
void main() {
highp vec3 materialDiffuseColor = color_mdl.rgb;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
highp vec3 n = normalize(normal_cmr);
highp vec3 l = normalize(lightDirection_cmr);
diff --git a/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag b/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag
index e986a52a..73b84138 100644
--- a/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag
+++ b/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag
@@ -7,7 +7,7 @@ uniform highp sampler2DShadow shadowMap;
uniform sampler2D textureSampler;
uniform highp float gradMin;
uniform highp float gradHeight;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
varying highp vec4 shadowCoord;
varying highp vec3 position_wrld;
@@ -36,8 +36,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
void main() {
highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight));
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
highp vec3 n = normalize(normal_cmr);
highp vec3 l = normalize(lightDirection_cmr);
diff --git a/src/datavisualization/engine/shaders/surface.frag b/src/datavisualization/engine/shaders/surface.frag
index b5205d2d..f17dd73e 100644
--- a/src/datavisualization/engine/shaders/surface.frag
+++ b/src/datavisualization/engine/shaders/surface.frag
@@ -10,13 +10,13 @@ uniform sampler2D textureSampler;
uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
void main() {
highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
highp float distance = length(lightPosition_wrld - position_wrld);
diff --git a/src/datavisualization/engine/shaders/surfaceFlat.frag b/src/datavisualization/engine/shaders/surfaceFlat.frag
index 7eaa917f..748fb3dd 100644
--- a/src/datavisualization/engine/shaders/surfaceFlat.frag
+++ b/src/datavisualization/engine/shaders/surfaceFlat.frag
@@ -12,13 +12,13 @@ uniform sampler2D textureSampler;
uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
void main() {
highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
highp float distance = length(lightPosition_wrld - position_wrld);
diff --git a/src/datavisualization/engine/shaders/surfaceFlat.vert b/src/datavisualization/engine/shaders/surfaceFlat.vert
index 0d39f6bc..102bea78 100644
--- a/src/datavisualization/engine/shaders/surfaceFlat.vert
+++ b/src/datavisualization/engine/shaders/surfaceFlat.vert
@@ -20,10 +20,10 @@ varying highp vec3 coords_mdl;
void main() {
gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
coords_mdl = vertexPosition_mdl;
- position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz;
- vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz;
+ 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 = (V * vec4(lightPosition_wrld, 1.0)).xyz;
+ vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
- normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
}
diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
index 9b9305ab..0613a40c 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
+++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
@@ -15,7 +15,7 @@ uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp float shadowQuality;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.94558609, -0.76890725),
@@ -37,8 +37,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
void main() {
highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
highp vec3 n = normalize(normal_cmr);
highp vec3 l = normalize(lightDirection_cmr);
diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
index 0a6e967f..8da7b196 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
+++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
@@ -28,10 +28,10 @@ void main() {
gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
coords_mdl = vertexPosition_mdl;
shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0);
- position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz;
- vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz;
+ 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 = (V * vec4(lightPosition_wrld, 1.0)).xyz;
+ vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
- normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
}
diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
index 3427fbae..1acf8f69 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
+++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
@@ -13,7 +13,7 @@ uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp float shadowQuality;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.94558609, -0.76890725),
@@ -35,8 +35,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
void main() {
highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
highp vec3 n = normalize(normal_cmr);
highp vec3 l = normalize(lightDirection_cmr);
diff --git a/src/datavisualization/engine/shaders/surface_ES2.frag b/src/datavisualization/engine/shaders/surface_ES2.frag
index 0e17cacd..58d13834 100644
--- a/src/datavisualization/engine/shaders/surface_ES2.frag
+++ b/src/datavisualization/engine/shaders/surface_ES2.frag
@@ -4,20 +4,20 @@ varying highp vec3 position_wrld;
varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
+varying highp vec3 lightPosition_wrld_frag;
uniform sampler2D textureSampler;
-uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
-uniform highp vec3 lightColor;
+uniform highp vec4 lightColor;
void main() {
highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
- highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor;
- highp vec3 materialSpecularColor = lightColor;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
- highp float distance = length(lightPosition_wrld - position_wrld);
+ highp float distance = length(lightPosition_wrld_frag - position_wrld);
highp vec3 n = normalize(normal_cmr);
highp vec3 l = normalize(lightDirection_cmr);
diff --git a/src/datavisualization/engine/shaders/texture.frag b/src/datavisualization/engine/shaders/texture.frag
new file mode 100644
index 00000000..41c4259b
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture.frag
@@ -0,0 +1,37 @@
+#version 120
+
+varying highp vec2 UV;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+
+uniform highp vec3 lightPosition_wrld;
+uniform highp sampler2D textureSampler;
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp vec4 lightColor;
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb;
+ 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 = texture2D(textureSampler, UV).a;
+ gl_FragColor.rgb = clamp(gl_FragColor.rgb, 0.0, 1.0);
+}
+
diff --git a/src/datavisualization/engine/shaders/texture.vert b/src/datavisualization/engine/shaders/texture.vert
new file mode 100644
index 00000000..90c0ac23
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture.vert
@@ -0,0 +1,28 @@
+uniform highp mat4 MVP;
+uniform highp mat4 V;
+uniform highp mat4 M;
+uniform highp mat4 itM;
+uniform highp vec3 lightPosition_wrld;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+attribute highp vec3 vertexNormal_mdl;
+
+varying highp vec3 lightPosition_wrld_frag;
+varying highp vec2 UV;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+
+void main() {
+ gl_Position = MVP * 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;
+ normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ UV = vertexUV;
+ lightPosition_wrld_frag = lightPosition_wrld;
+}
diff --git a/src/datavisualization/engine/shaders/texture_ES2.frag b/src/datavisualization/engine/shaders/texture_ES2.frag
new file mode 100644
index 00000000..82ad6614
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture_ES2.frag
@@ -0,0 +1,40 @@
+varying highp vec2 UV;
+varying highp vec2 coords_mdl;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec3 lightPosition_wrld_frag;
+
+uniform highp sampler2D textureSampler;
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp vec4 lightColor;
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
+
+ highp float distance = length(lightPosition_wrld_frag - position_wrld);
+
+ highp vec3 n = normalize(normal_cmr);
+ highp vec3 l = normalize(lightDirection_cmr);
+ highp float cosTheta = dot(n, l);
+ if (cosTheta < 0.0) cosTheta = 0.0;
+ else if (cosTheta > 1.0) cosTheta = 1.0;
+
+ highp vec3 E = normalize(eyeDirection_cmr);
+ highp vec3 R = reflect(-l, n);
+ highp float cosAlpha = dot(E, R);
+ if (cosAlpha < 0.0) cosAlpha = 0.0;
+ else if (cosAlpha > 1.0) cosAlpha = 1.0;
+
+ gl_FragColor.rgb =
+ materialAmbientColor +
+ materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance +
+ materialSpecularColor * lightStrength * (cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha) / distance;
+ gl_FragColor.a = texture2D(textureSampler, UV).a;
+ gl_FragColor.rgb = clamp(gl_FragColor.rgb, 0.0, 1.0);
+}
+
diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp
index 991a1ce8..c03bafd8 100644
--- a/src/datavisualization/engine/surface3dcontroller.cpp
+++ b/src/datavisualization/engine/surface3dcontroller.cpp
@@ -18,15 +18,9 @@
#include "surface3dcontroller_p.h"
#include "surface3drenderer_p.h"
-#include "camerahelper_p.h"
-#include "qabstract3daxis_p.h"
#include "qvalue3daxis_p.h"
-#include "qcategory3daxis.h"
#include "qsurfacedataproxy_p.h"
#include "qsurface3dseries_p.h"
-#include "shaderhelper_p.h"
-
-#include <QtGui/QMatrix4x4>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -66,11 +60,6 @@ void Surface3DController::synchDataToRenderer()
if (!isInitialized())
return;
- if (m_changedSeriesList.size()) {
- m_renderer->modifiedSeriesList(m_changedSeriesList);
- m_changedSeriesList.clear();
- }
-
Abstract3DController::synchDataToRenderer();
// Notify changes to renderer
@@ -81,7 +70,7 @@ void Surface3DController::synchDataToRenderer()
}
if (m_changeTracker.itemChanged) {
- m_renderer->updateItem(m_changedItems);
+ m_renderer->updateItems(m_changedItems);
m_changeTracker.itemChanged = false;
m_changedItems.clear();
}
@@ -98,7 +87,7 @@ void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation(
Q_UNUSED(orientation)
Q_UNUSED(autoAdjust)
- adjustValueAxisRange();
+ adjustAxisRanges();
}
void Surface3DController::handleAxisRangeChangedBySender(QObject *sender)
@@ -113,8 +102,6 @@ void Surface3DController::handleSeriesVisibilityChangedBySender(QObject *sender)
{
Abstract3DController::handleSeriesVisibilityChangedBySender(sender);
- adjustValueAxisRange();
-
// Visibility changes may require disabling slicing,
// so just reset selection to ensure everything is still valid.
setSelectedPoint(m_selectedPoint, m_selectedSeries, false);
@@ -128,6 +115,8 @@ void Surface3DController::handlePendingClick()
setSelectedPoint(position, series, true);
+ Abstract3DController::handlePendingClick();
+
m_renderer->resetClickedStatus();
}
@@ -148,9 +137,6 @@ void Surface3DController::addSeries(QAbstract3DSeries *series)
Abstract3DController::addSeries(series);
- if (series->isVisible())
- adjustValueAxisRange();
-
QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
if (surfaceSeries->selectedPoint() != invalidSelectionPosition())
setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries, false);
@@ -166,7 +152,7 @@ void Surface3DController::removeSeries(QAbstract3DSeries *series)
setSelectedPoint(invalidSelectionPosition(), 0, false);
if (wasVisible)
- adjustValueAxisRange();
+ adjustAxisRanges();
}
QList<QSurface3DSeries *> Surface3DController::surfaceSeriesList()
@@ -214,7 +200,8 @@ void Surface3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode
}
}
-void Surface3DController::setSelectedPoint(const QPoint &position, QSurface3DSeries *series, bool enterSlice)
+void Surface3DController::setSelectedPoint(const QPoint &position, QSurface3DSeries *series,
+ bool enterSlice)
{
// If the selection targets non-existent point, clear selection instead.
QPoint pos = position;
@@ -290,13 +277,15 @@ void Surface3DController::handleArrayReset()
{
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series();
if (series->isVisible()) {
- adjustValueAxisRange();
- if (!m_changedSeriesList.contains(series))
- m_changedSeriesList.append(series);
+ adjustAxisRanges();
m_isDataDirty = true;
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
+
// Clear selection unless still valid
setSelectedPoint(m_selectedPoint, m_selectedSeries, false);
+ series->d_ptr->markItemLabelDirty();
emitNeedRender();
}
@@ -315,33 +304,34 @@ void Surface3DController::handleFlatShadingSupportedChange(bool supported)
void Surface3DController::handleRowsChanged(int startIndex, int count)
{
- QSurfaceDataProxy *sender = static_cast<QSurfaceDataProxy *>(QObject::sender());
- if (m_changedRows.size() == 0)
- m_changedRows.reserve(sender->rowCount());
-
- QSurface3DSeries *series = sender->series();
+ QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(QObject::sender())->series();
int oldChangeCount = m_changedRows.size();
+ if (!oldChangeCount)
+ m_changedRows.reserve(count);
+
+ int selectedRow = m_selectedPoint.x();
for (int i = 0; i < count; i++) {
bool newItem = true;
int candidate = startIndex + i;
- for (int i = 0; i < oldChangeCount; i++) {
- if (m_changedRows.at(i).row == candidate &&
- series == m_changedRows.at(i).series) {
+ for (int j = 0; j < oldChangeCount; j++) {
+ const ChangeRow &oldChangeItem = m_changedRows.at(j);
+ if (oldChangeItem.row == candidate && series == oldChangeItem.series) {
newItem = false;
break;
}
}
if (newItem) {
- ChangeRow newItem = {series, candidate};
- m_changedRows.append(newItem);
+ ChangeRow newChangeItem = {series, candidate};
+ m_changedRows.append(newChangeItem);
+ if (series == m_selectedSeries && selectedRow == candidate)
+ series->d_ptr->markItemLabelDirty();
}
}
- if (m_changedRows.size()) {
+ if (count) {
m_changeTracker.rowsChanged = true;
- adjustValueAxisRange();
- // Clear selection unless still valid
- setSelectedPoint(m_selectedPoint, m_selectedSeries, false);
+ if (series->isVisible())
+ adjustAxisRanges();
emitNeedRender();
}
}
@@ -352,7 +342,7 @@ void Surface3DController::handleItemChanged(int rowIndex, int columnIndex)
QSurface3DSeries *series = sender->series();
bool newItem = true;
- QPoint candidate(columnIndex, rowIndex);
+ QPoint candidate(rowIndex, columnIndex);
foreach (ChangeItem item, m_changedItems) {
if (item.point == candidate && item.series == series) {
newItem = false;
@@ -364,9 +354,11 @@ void Surface3DController::handleItemChanged(int rowIndex, int columnIndex)
m_changedItems.append(newItem);
m_changeTracker.itemChanged = true;
- adjustValueAxisRange();
- // Clear selection unless still valid
- setSelectedPoint(m_selectedPoint, m_selectedSeries, false);
+ if (series == m_selectedSeries && m_selectedPoint == candidate)
+ series->d_ptr->markItemLabelDirty();
+
+ if (series->isVisible())
+ adjustAxisRanges();
emitNeedRender();
}
}
@@ -377,11 +369,11 @@ void Surface3DController::handleRowsAdded(int startIndex, int count)
Q_UNUSED(count)
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series();
if (series->isVisible()) {
- adjustValueAxisRange();
+ adjustAxisRanges();
m_isDataDirty = true;
- if (!m_changedSeriesList.contains(series))
- m_changedSeriesList.append(series);
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
emitNeedRender();
}
@@ -400,11 +392,11 @@ void Surface3DController::handleRowsInserted(int startIndex, int count)
}
if (series->isVisible()) {
- adjustValueAxisRange();
+ adjustAxisRanges();
m_isDataDirty = true;
- if (!m_changedSeriesList.contains(series))
- m_changedSeriesList.append(series);
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
emitNeedRender();
}
@@ -428,16 +420,16 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count)
}
if (series->isVisible()) {
- adjustValueAxisRange();
+ adjustAxisRanges();
m_isDataDirty = true;
- if (!m_changedSeriesList.contains(series))
- m_changedSeriesList.append(series);
}
+ if (!m_changedSeriesList.contains(series))
+ m_changedSeriesList.append(series);
emitNeedRender();
}
-void Surface3DController::adjustValueAxisRange()
+void Surface3DController::adjustAxisRanges()
{
QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
@@ -517,7 +509,7 @@ void Surface3DController::adjustValueAxisRange()
adjustment = defaultAdjustment;
}
}
- valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment);
+ valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment, true);
}
if (adjustY) {
// If all points at same coordinate, need to default to some valid range
@@ -525,7 +517,7 @@ void Surface3DController::adjustValueAxisRange()
float adjustment = 0.0f;
if (minValueY == maxValueY)
adjustment = defaultAdjustment;
- valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment);
+ valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment, true);
}
if (adjustZ) {
// If all points at same coordinate, need to default to some valid range
@@ -544,7 +536,7 @@ void Surface3DController::adjustValueAxisRange()
adjustment = defaultAdjustment;
}
}
- valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment);
+ valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment, true);
}
}
}
diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h
index 14c0dd40..2be74f35 100644
--- a/src/datavisualization/engine/surface3dcontroller_p.h
+++ b/src/datavisualization/engine/surface3dcontroller_p.h
@@ -38,15 +38,11 @@ class Surface3DRenderer;
class QSurface3DSeries;
struct Surface3DChangeBitField {
- bool smoothStatusChanged : 1;
- bool surfaceGridChanged : 1;
bool selectedPointChanged : 1;
bool rowsChanged : 1;
bool itemChanged : 1;
Surface3DChangeBitField() :
- smoothStatusChanged(true),
- surfaceGridChanged(true),
selectedPointChanged(true),
rowsChanged(false),
itemChanged(false)
@@ -77,7 +73,6 @@ private:
bool m_flatShadingSupported;
QVector<ChangeItem> m_changedItems;
QVector<ChangeRow> m_changedRows;
- QVector<QSurface3DSeries *> m_changedSeriesList;
public:
explicit Surface3DController(QRect rect, Q3DScene *scene = 0);
@@ -97,6 +92,7 @@ public:
virtual void handleAxisRangeChangedBySender(QObject *sender);
virtual void handleSeriesVisibilityChangedBySender(QObject *sender);
virtual void handlePendingClick();
+ virtual void adjustAxisRanges();
static QPoint invalidSelectionPosition();
bool isFlatShadingSupported();
@@ -119,8 +115,6 @@ signals:
void selectedSeriesChanged(QSurface3DSeries *series);
private:
- void adjustValueAxisRange();
-
Q_DISABLE_COPY(Surface3DController)
};
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp
index 41415393..12627966 100644
--- a/src/datavisualization/engine/surface3drenderer.cpp
+++ b/src/datavisualization/engine/surface3drenderer.cpp
@@ -16,22 +16,12 @@
**
****************************************************************************/
-#include "surface3dcontroller_p.h"
#include "surface3drenderer_p.h"
-#include "q3dcamera.h"
#include "q3dcamera_p.h"
#include "shaderhelper_p.h"
-#include "objecthelper_p.h"
-#include "surfaceobject_p.h"
#include "texturehelper_p.h"
-#include "selectionpointer_p.h"
#include "utils_p.h"
-#include "drawer_p.h"
-#include "q3dlight.h"
-#include "qsurface3dseries_p.h"
-#include <QtGui/QMatrix4x4>
-#include <QtGui/QMouseEvent>
#include <QtCore/qmath.h>
static const int ID_TO_RGBA_MASK = 0xff;
@@ -40,12 +30,15 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
//#define SHOW_DEPTH_TEXTURE_SCENE
-const GLfloat aspectRatio = 2.0f; // Forced ratio of x and z to y. Dynamic will make it look odd.
-const GLfloat backgroundMargin = 1.1f; // Margin for background (1.1f = make it 10% larger to avoid items being drawn inside background)
-const GLfloat labelMargin = 0.05f;
+// 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;
+const uint blueMultiplier = 65536;
+const uint alphaMultiplier = 16777216;
Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
: Abstract3DRenderer(controller),
@@ -65,15 +58,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
m_scaleZ(0.0f),
m_scaleXWithBackground(0.0f),
m_scaleZWithBackground(0.0f),
- m_minVisibleColumnValue(0.0f),
- m_maxVisibleColumnValue(0.0f),
- m_minVisibleRowValue(0.0f),
- m_maxVisibleRowValue(0.0f),
- m_visibleColumnRange(0.0f),
- m_visibleRowRange(0.0f),
- m_backgroundObj(0),
- m_gridLineObj(0),
- m_labelObj(0),
m_depthTexture(0),
m_depthModelTexture(0),
m_depthFrameBuffer(0),
@@ -83,9 +67,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
m_shadowQualityToShader(33.3f),
m_flatSupported(true),
m_selectionActive(false),
- m_xFlipped(false),
- m_zFlipped(false),
- m_yFlipped(false),
m_shadowQualityMultiplier(3),
m_hasHeightAdjustmentChanged(true),
m_selectedPoint(Surface3DController::invalidSelectionPosition()),
@@ -94,6 +75,9 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
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"));
@@ -131,16 +115,6 @@ Surface3DRenderer::~Surface3DRenderer()
delete m_surfaceSliceFlatShader;
delete m_surfaceSliceSmoothShader;
delete m_labelShader;
-
- delete m_backgroundObj;
- delete m_gridLineObj;
- delete m_labelObj;
-
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
- cache->cleanup(m_textureHelper);
- delete cache;
- }
- m_renderCacheList.clear();
}
void Surface3DRenderer::initializeOpenGL()
@@ -160,8 +134,10 @@ void Surface3DRenderer::initializeOpenGL()
// Init selection shader
initSelectionShaders();
+#if !(defined QT_OPENGL_ES_2)
// Load grid line mesh
loadGridLineMesh();
+#endif
// Load label mesh
loadLabelMesh();
@@ -183,22 +159,25 @@ void Surface3DRenderer::updateData()
{
calculateSceneScalingFactors();
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
- const QSurface3DSeries *currentSeries = cache->series();
- QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
- const QSurfaceDataArray &array = *dataProxy->array();
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
+ if (cache->isVisible() && cache->dataDirty()) {
+ const QSurface3DSeries *currentSeries = cache->series();
+ QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
+ const QSurfaceDataArray &array = *dataProxy->array();
+ QSurfaceDataArray &dataArray = cache->dataArray();
+ QRect sampleSpace;
- // Need minimum of 2x2 array to draw a surface
- if (array.size() >= 2 && array.at(0)->size() >= 2) {
- QRect sampleSpace = calculateSampleRect(cache, array);
+ // Need minimum of 2x2 array to draw a surface
+ if (array.size() >= 2 && array.at(0)->size() >= 2)
+ sampleSpace = calculateSampleRect(array);
- QSurfaceDataArray &dataArray = cache->dataArray();
- bool dimensionChanged = false;
+ bool dimensionsChanged = false;
if (cache->sampleSpace() != sampleSpace) {
if (sampleSpace.width() >= 2)
m_selectionTexturesDirty = true;
- dimensionChanged = true;
+ dimensionsChanged = true;
cache->setSampleSpace(sampleSpace);
for (int i = 0; i < dataArray.size(); i++)
@@ -207,7 +186,7 @@ void Surface3DRenderer::updateData()
}
if (sampleSpace.width() >= 2 && sampleSpace.height() >= 2) {
- if (dimensionChanged) {
+ if (dimensionsChanged) {
dataArray.reserve(sampleSpace.height());
for (int i = 0; i < sampleSpace.height(); i++)
dataArray << new QSurfaceDataRow(sampleSpace.width());
@@ -219,13 +198,13 @@ void Surface3DRenderer::updateData()
}
}
- if (dataArray.size() > 0 && (cache->objectDirty() || dimensionChanged)) {
- checkFlatSupport(cache);
- updateObjects(cache, dimensionChanged);
- cache->setObjectDirty(false);
- cache->setFlatStatusDirty(false);
- }
+ checkFlatSupport(cache);
+ updateObjects(cache, dimensionsChanged);
+ cache->setFlatStatusDirty(false);
+ } else {
+ cache->surfaceObject()->clear();
}
+ cache->setDataDirty(false);
}
}
@@ -235,31 +214,22 @@ void Surface3DRenderer::updateData()
updateSelectedPoint(m_selectedPoint, m_selectedSeries);
}
-void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList,
- bool updateVisibility)
+void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
{
- Q_UNUSED(updateVisibility);
-
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList)
- cache->setValid(false);
+ Abstract3DRenderer::updateSeries(seriesList);
+ bool noSelection = true;
foreach (QAbstract3DSeries *series, seriesList) {
- // Item selection label may need update
- if (series->d_ptr->m_changeTracker.nameChanged
- || series->d_ptr->m_changeTracker.itemLabelFormatChanged) {
- m_selectionLabelDirty = true;
- }
-
QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
- SurfaceSeriesRenderCache *cache = m_renderCacheList.value(surfaceSeries);
- if (!cache) {
- cache = new SurfaceSeriesRenderCache;
- m_renderCacheList[surfaceSeries] = cache;
-
- m_selectionTexturesDirty = true;
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>( m_renderCacheList.value(series));
+ if (noSelection
+ && surfaceSeries->selectedPoint() != QSurface3DSeries::invalidSelectionPosition()) {
+ if (selectionLabel() != cache->itemLabel())
+ m_selectionLabelDirty = true;
+ noSelection = false;
}
- cache->setValid(true);
- cache->populate(surfaceSeries, this);
+
if (cache->isFlatStatusDirty() && cache->sampleSpace().width()) {
checkFlatSupport(cache);
updateObjects(cache, true);
@@ -267,24 +237,16 @@ void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis
}
}
- // Remove non-valid objects from the cache list
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
- if (!cache->isValid()) {
- if (cache->series() == m_selectedSeries)
- updateSelectedPoint(Surface3DController::invalidSelectionPosition(), 0);
-
- m_renderCacheList.remove(cache->series());
- cache->cleanup(m_textureHelper);
- delete cache;
-
- m_selectionTexturesDirty = true;
- }
+ if (noSelection && !selectionLabel().isEmpty()) {
+ m_selectionLabelDirty = true;
+ updateSelectedPoint(Surface3DController::invalidSelectionPosition(), 0);
}
// Selection pointer issues
if (m_selectedSeries) {
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
- QVector3D highlightColor =
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
+ QVector4D highlightColor =
Utils::vectorFromColor(cache->series()->singleHighlightColor());
SelectionPointer *slicePointer = cache->sliceSelectionPointer();
if (slicePointer) {
@@ -302,19 +264,23 @@ void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis
}
}
-void Surface3DRenderer::modifiedSeriesList(const QVector<QSurface3DSeries *> &seriesList)
+SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series)
{
- foreach (QSurface3DSeries *series, seriesList) {
- SurfaceSeriesRenderCache *cache = m_renderCacheList.value(series);
- if (cache)
- cache->setObjectDirty(true);
- }
+ m_selectionTexturesDirty = true;
+ return new SurfaceSeriesRenderCache(series, this);
+}
+
+void Surface3DRenderer::cleanCache(SeriesRenderCache *cache)
+{
+ Abstract3DRenderer::cleanCache(cache);
+ m_selectionTexturesDirty = true;
}
void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow> &rows)
{
foreach (Surface3DController::ChangeRow item, rows) {
- SurfaceSeriesRenderCache *cache = m_renderCacheList.value(item.series);
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(item.series));
QSurfaceDataArray &dstArray = cache->dataArray();
const QRect &sampleSpace = cache->sampleSpace();
@@ -335,15 +301,10 @@ void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow>
srcArray->at(row)->at(j + sampleSpace.x());
}
- if (cache->isFlatShadingEnabled()) {
- cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y(),
- m_heightNormalizer,
- m_axisCacheY.min());
- } else {
- cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y(),
- m_heightNormalizer,
- m_axisCacheY.min());
- }
+ if (cache->isFlatShadingEnabled())
+ cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y());
+ else
+ cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y());
}
if (updateBuffers)
cache->surfaceObject()->uploadBuffers();
@@ -353,10 +314,11 @@ void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow>
updateSelectedPoint(m_selectedPoint, m_selectedSeries);
}
-void Surface3DRenderer::updateItem(const QVector<Surface3DController::ChangeItem> &points)
+void Surface3DRenderer::updateItems(const QVector<Surface3DController::ChangeItem> &points)
{
foreach (Surface3DController::ChangeItem item, points) {
- SurfaceSeriesRenderCache *cache = m_renderCacheList.value(item.series);
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(item.series));
QSurfaceDataArray &dstArray = cache->dataArray();
const QRect &sampleSpace = cache->sampleSpace();
@@ -370,22 +332,20 @@ void Surface3DRenderer::updateItem(const QVector<Surface3DController::ChangeItem
int sampleSpaceTop = sampleSpace.y() + sampleSpace.height();
int sampleSpaceRight = sampleSpace.x() + sampleSpace.width();
bool updateBuffers = false;
+ // Note: Point is (row, column), samplespace is (columns x rows)
QPoint point = item.point;
- if (point.y() <= sampleSpaceTop && point.y() >= sampleSpace.y() &&
- point.x() <= sampleSpaceRight && point.x() >= sampleSpace.x()) {
+ if (point.x() <= sampleSpaceTop && point.x() >= sampleSpace.y() &&
+ point.y() <= sampleSpaceRight && point.y() >= sampleSpace.x()) {
updateBuffers = true;
- int x = point.x() - sampleSpace.x();
- int y = point.y() - sampleSpace.y();
- (*(dstArray.at(y)))[x] = srcArray->at(point.y())->at(point.x());
-
- if (cache->isFlatShadingEnabled()) {
- cache->surfaceObject()->updateCoarseItem(dstArray, y, x, m_heightNormalizer,
- m_axisCacheY.min());
- } else {
- cache->surfaceObject()->updateSmoothItem(dstArray, y, x, m_heightNormalizer,
- m_axisCacheY.min());
- }
+ int x = point.y() - sampleSpace.x();
+ int y = point.x() - sampleSpace.y();
+ (*(dstArray.at(y)))[x] = srcArray->at(point.x())->at(point.y());
+
+ if (cache->isFlatShadingEnabled())
+ cache->surfaceObject()->updateCoarseItem(dstArray, y, x);
+ else
+ cache->surfaceObject()->updateSmoothItem(dstArray, y, x);
}
if (updateBuffers)
cache->surfaceObject()->uploadBuffers();
@@ -396,31 +356,22 @@ void Surface3DRenderer::updateItem(const QVector<Surface3DController::ChangeItem
updateSelectedPoint(m_selectedPoint, m_selectedSeries);
}
-void Surface3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min,
- float max)
-{
- Abstract3DRenderer::updateAxisRange(orientation, min, max);
-
- if (orientation == QAbstract3DAxis::AxisOrientationY) {
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList)
- cache->setObjectDirty(true);
- }
-}
-
void Surface3DRenderer::updateSliceDataModel(const QPoint &point)
{
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList)
- cache->sliceSurfaceObject()->clear();
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList)
+ static_cast<SurfaceSeriesRenderCache *>(baseCache)->sliceSurfaceObject()->clear();
if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)) {
// Find axis coordinates for the selected point
- SurfaceSeriesRenderCache *selectedCache =
- m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries));
- QSurfaceDataArray &dataArray = selectedCache->dataArray();
+ SeriesRenderCache *selectedCache =
+ m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries));
+ QSurfaceDataArray &dataArray =
+ static_cast<SurfaceSeriesRenderCache *>(selectedCache)->dataArray();
QSurfaceDataItem item = dataArray.at(point.x())->at(point.y());
QPointF coords(item.x(), item.z());
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->series() != m_selectedSeries) {
QPoint mappedPoint = mapCoordsToSampleSpace(cache, coords);
updateSliceObject(cache, mappedPoint);
@@ -431,9 +382,10 @@ void Surface3DRenderer::updateSliceDataModel(const QPoint &point)
} else {
if (m_selectedSeries) {
SurfaceSeriesRenderCache *cache =
- m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries));
+ static_cast<SurfaceSeriesRenderCache *>(
+ m_renderCacheList.value(m_selectedSeries));
if (cache)
- updateSliceObject(cache, point);
+ updateSliceObject(static_cast<SurfaceSeriesRenderCache *>(cache), point);
}
}
}
@@ -536,7 +488,7 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const
int row = point.x();
if ((m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow) && row == -1) ||
- (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn) && column == -1)) {
+ (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn) && column == -1)) {
cache->sliceSurfaceObject()->clear();
return;
}
@@ -550,19 +502,27 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const
QSurfaceDataRow *sliceRow;
QSurfaceDataArray &dataArray = cache->dataArray();
float adjust = (0.025f * m_heightNormalizer) / 2.0f;
- float stepDown = 2.0f * adjust;
+ float doubleAdjust = 2.0f * adjust;
+ bool flipZX = false;
+ float zBack;
+ float zFront;
if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) {
QSurfaceDataRow *src = dataArray.at(row);
sliceRow = new QSurfaceDataRow(src->size());
+ zBack = m_axisCacheZ.min();
+ zFront = m_axisCacheZ.max();
for (int i = 0; i < sliceRow->size(); i++)
- (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, -1.0f));
+ (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, zFront));
} else {
+ flipZX = true;
const QRect &sampleSpace = cache->sampleSpace();
sliceRow = new QSurfaceDataRow(sampleSpace.height());
+ zBack = m_axisCacheX.min();
+ zFront = m_axisCacheX.max();
for (int i = 0; i < sampleSpace.height(); i++) {
(*sliceRow)[i].setPosition(QVector3D(dataArray.at(i)->at(column).z(),
dataArray.at(i)->at(column).y() + adjust,
- -1.0f));
+ zFront));
}
}
sliceDataArray << sliceRow;
@@ -571,114 +531,134 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const
QSurfaceDataRow *duplicateRow = new QSurfaceDataRow(*sliceRow);
for (int i = 0; i < sliceRow->size(); i++) {
(*sliceRow)[i].setPosition(QVector3D(sliceRow->at(i).x(),
- sliceRow->at(i).y() - stepDown,
- 1.0f));
+ sliceRow->at(i).y() - doubleAdjust,
+ zBack));
}
sliceDataArray << duplicateRow;
QRect sliceRect(0, 0, sliceRow->size(), 2);
if (sliceRow->size() > 0) {
- if (cache->isFlatShadingEnabled()) {
- cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect,
- m_heightNormalizer,
- m_axisCacheY.min(), true);
+ if (cache->isFlatShadingEnabled())
+ cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, flipZX);
+ else
+ cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, flipZX);
+ }
+}
+
+inline static float getDataValue(const QSurfaceDataArray &array, bool searchRow, int index)
+{
+ if (searchRow)
+ return array.at(0)->at(index).x();
+ else
+ return array.at(index)->at(0).z();
+}
+
+inline static int binarySearchArray(const QSurfaceDataArray &array, int maxIdx, float limitValue,
+ bool searchRow, bool lowBound, bool ascending)
+{
+ int min = 0;
+ int max = maxIdx;
+ int mid = 0;
+ int retVal;
+ while (max >= min) {
+ mid = (min + max) / 2;
+ float arrayValue = getDataValue(array, searchRow, mid);
+ if (arrayValue == limitValue)
+ return mid;
+ if (ascending) {
+ if (arrayValue < limitValue)
+ min = mid + 1;
+ else
+ max = mid - 1;
} else {
- cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect,
- m_heightNormalizer,
- m_axisCacheY.min(), true);
+ if (arrayValue > limitValue)
+ min = mid + 1;
+ else
+ max = mid - 1;
}
}
+
+ // Exact match not found, return closest depending on bound.
+ // The boundary is between last mid and min/max.
+ if (lowBound == ascending) {
+ if (mid > max)
+ retVal = mid;
+ else
+ retVal = min;
+ } else {
+ if (mid > max)
+ retVal = max;
+ else
+ retVal = mid;
+ }
+ if (retVal < 0 || retVal > maxIdx) {
+ retVal = -1;
+ } else if (lowBound) {
+ if (getDataValue(array, searchRow, retVal) < limitValue)
+ retVal = -1;
+ } else {
+ if (getDataValue(array, searchRow, retVal) > limitValue)
+ retVal = -1;
+ }
+ return retVal;
}
-QRect Surface3DRenderer::calculateSampleRect(SurfaceSeriesRenderCache *cache,
- const QSurfaceDataArray &array)
+QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array)
{
QRect sampleSpace;
- int rowCount = array.size();
- int columnCount = array.at(0)->size();
+ const int maxRow = array.size() - 1;
+ const int maxColumn = array.at(0)->size() - 1;
- int i;
- bool found;
- float axisMinX = m_axisCacheX.min();
- float axisMaxX = m_axisCacheX.max();
- float axisMinZ = m_axisCacheZ.min();
- float axisMaxZ = m_axisCacheZ.max();
+ // We assume data is ordered sequentially in rows for X-value and in columns for Z-value.
+ // Determine if data is ascending or descending in each case.
+ const bool ascendingX = array.at(0)->at(0).x() < array.at(0)->at(maxColumn).x();
+ const bool ascendingZ = array.at(0)->at(0).z() < array.at(maxRow)->at(0).z();
- // m_minVisibleColumnValue
- for (i = 0, found = false; i < columnCount; i++) {
- if (array.at(0)->at(i).x() >= axisMinX) {
- found = true;
- break;
- }
- }
- if (found) {
- m_minVisibleColumnValue = array.at(0)->at(i).x();
- sampleSpace.setLeft(i);
+ int idx = binarySearchArray(array, maxColumn, m_axisCacheX.min(), true, true, ascendingX);
+ if (idx != -1) {
+ if (ascendingX)
+ sampleSpace.setLeft(idx);
+ else
+ sampleSpace.setRight(idx);
} else {
sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
return sampleSpace;
}
- // m_maxVisibleColumnValue
- for (i = columnCount - 1, found = false; i >= 0; i--) {
- if (array.at(0)->at(i).x() <= axisMaxX) {
- found = true;
- break;
- }
- }
- if (found) {
- m_maxVisibleColumnValue = array.at(0)->at(i).x();
- sampleSpace.setRight(i);
+ idx = binarySearchArray(array, maxColumn, m_axisCacheX.max(), true, false, ascendingX);
+ if (idx != -1) {
+ if (ascendingX)
+ sampleSpace.setRight(idx);
+ else
+ sampleSpace.setLeft(idx);
} else {
sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
return sampleSpace;
}
- // m_minVisibleRowValue
- for (i = 0, found = false; i < rowCount; i++) {
- if (array.at(i)->at(0).z() >= axisMinZ) {
- found = true;
- break;
- }
- }
- if (found) {
- m_minVisibleRowValue = array.at(i)->at(0).z();
- sampleSpace.setTop(i);
+ idx = binarySearchArray(array, maxRow, m_axisCacheZ.min(), false, true, ascendingZ);
+ if (idx != -1) {
+ if (ascendingZ)
+ sampleSpace.setTop(idx);
+ else
+ sampleSpace.setBottom(idx);
} else {
sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
return sampleSpace;
}
- // m_maxVisibleRowValue
- for (i = rowCount - 1, found = false; i >= 0; i--) {
- if (array.at(i)->at(0).z() <= axisMaxZ) {
- found = true;
- break;
- }
- }
- if (found) {
- m_maxVisibleRowValue = array.at(i)->at(0).z();
- sampleSpace.setBottom(i);
+ idx = binarySearchArray(array, maxRow, m_axisCacheZ.max(), false, false, ascendingZ);
+ if (idx != -1) {
+ if (ascendingZ)
+ sampleSpace.setBottom(idx);
+ else
+ sampleSpace.setTop(idx);
} else {
sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
return sampleSpace;
}
- m_visibleColumnRange = m_maxVisibleColumnValue - m_minVisibleColumnValue;
- m_visibleRowRange = m_maxVisibleRowValue - m_minVisibleRowValue;
- GLfloat surfaceScaleX = m_scaleX * m_visibleColumnRange / m_areaSize.width();
- GLfloat surfaceScaleZ = m_scaleZ * m_visibleRowRange / m_areaSize.height();
- GLfloat axis2XCenterX = axisMinX + axisMaxX;
- GLfloat axis2XCenterZ = axisMinZ + axisMaxZ;
- GLfloat data2XCenterX = GLfloat(m_minVisibleColumnValue + m_maxVisibleColumnValue);
- GLfloat data2XCenterZ = GLfloat(m_minVisibleRowValue + m_maxVisibleRowValue);
- GLfloat surfaceOffsetX = m_scaleX * (data2XCenterX - axis2XCenterX) / m_areaSize.width();
- GLfloat surfaceOffsetZ = -m_scaleZ * (data2XCenterZ - axis2XCenterZ) / m_areaSize.height();
-
- cache->setScale(QVector3D(surfaceScaleX, 1.0f, surfaceScaleZ));
- cache->setOffset(QVector3D(surfaceOffsetX, 0.0f, surfaceOffsetZ));
-
return sampleSpace;
}
@@ -708,6 +688,13 @@ void Surface3DRenderer::render(GLuint defaultFboHandle)
// Handle GL state setup for FBO buffers and clearing of the render surface
Abstract3DRenderer::render(defaultFboHandle);
+ if (m_axisCacheX.positionsDirty())
+ m_axisCacheX.updateAllPositions();
+ if (m_axisCacheY.positionsDirty())
+ m_axisCacheY.updateAllPositions();
+ if (m_axisCacheZ.positionsDirty())
+ m_axisCacheZ.updateAllPositions();
+
drawScene(defaultFboHandle);
if (m_cachedIsSlicingActivated)
drawSlicedScene();
@@ -715,13 +702,14 @@ void Surface3DRenderer::render(GLuint defaultFboHandle)
// Render selection ball
if (m_selectionActive
&& m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem)) {
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->slicePointerActive() && cache->renderable() &&
m_cachedIsSlicingActivated ) {
cache->sliceSelectionPointer()->render(defaultFboHandle);
}
if (cache->mainPointerActive() && cache->renderable())
- cache->mainSelectionPointer()->render(defaultFboHandle);
+ cache->mainSelectionPointer()->render(defaultFboHandle, m_useOrthoProjection);
}
}
}
@@ -730,7 +718,7 @@ void Surface3DRenderer::drawSlicedScene()
{
QVector3D lightPos;
- QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
// Specify viewport
glViewport(m_secondarySubViewport.x(),
@@ -760,13 +748,18 @@ void Surface3DRenderer::drawSlicedScene()
const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
+ AxisRenderCache &sliceCache = rowMode ? m_axisCacheX : m_axisCacheZ;
GLfloat scaleXBackground = 0.0f;
- if (m_renderCacheList.size()) {
+ // Disable culling to avoid ugly conditionals with reversed axes and data
+ glDisable(GL_CULL_FACE);
+
+ if (!m_renderCacheList.isEmpty()) {
bool drawGrid = false;
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->sliceSurfaceObject()->indexCount() && cache->renderable()) {
if (!drawGrid && cache->surfaceGridVisible()) {
glEnable(GL_POLYGON_OFFSET_FILL);
@@ -774,24 +767,16 @@ void Surface3DRenderer::drawSlicedScene()
drawGrid = true;
}
- GLfloat scaleX = 0.0f;
- GLfloat offset = 0.0f;
- if (rowMode) {
- scaleX = cache->scale().x();
+ if (rowMode)
scaleXBackground = m_scaleXWithBackground;
- offset = cache->offset().x();
- } else {
- scaleX = cache->scale().z();
+ else
scaleXBackground = m_scaleZWithBackground;
- offset = -cache->offset().z();
- }
QMatrix4x4 MVPMatrix;
QMatrix4x4 modelMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(offset, 0.0f, 0.0f);
- QVector3D scaling(scaleX, 1.0f, sliceZScale);
+ QVector3D scaling(1.0f, 1.0f, sliceZScale);
modelMatrix.scale(scaling);
itModelMatrix.scale(scaling);
@@ -832,10 +817,13 @@ void Surface3DRenderer::drawSlicedScene()
m_surfaceGridShader->bind();
m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(),
Utils::vectorFromColor(m_cachedTheme->gridLineColor()));
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
- if (cache->sliceSurfaceObject()->indexCount() && cache->isSeriesVisible() &&
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(baseCache);
+ if (cache->sliceSurfaceObject()->indexCount() && cache->isVisible() &&
cache->surfaceGridVisible()) {
- m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), cache->MVPMatrix());
+ m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(),
+ cache->MVPMatrix());
m_drawer->drawSurfaceGrid(m_surfaceGridShader, cache->sliceSurfaceObject());
}
}
@@ -845,14 +833,22 @@ 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
+
// Bind line shader
lineShader->bind();
// Set unchanging shader bindings
- QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
+ QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
lineShader->setUniformValue(lineShader->lightP(), lightPos);
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
@@ -862,19 +858,16 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
// Horizontal lines
+ int gridLineCount = m_axisCacheY.gridLineCount();
if (m_axisCacheY.segmentCount() > 0) {
QVector3D gridLineScaleX(scaleXBackground, gridLineWidth, gridLineWidth);
- GLfloat lineStep = 2.0f * m_axisCacheY.subSegmentStep() / m_heightNormalizer;
- GLfloat linePos = -1.0f;
- int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
-
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(0.0f, linePos, -sliceZScale);
+ modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), -1.0f);
modelMatrix.scale(gridLineScaleX);
itModelMatrix.scale(gridLineScaleX);
@@ -888,36 +881,30 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
+#if !(defined QT_OPENGL_ES_2)
m_drawer->drawObject(lineShader, m_gridLineObj);
-
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
}
// Vertical lines
QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth);
- int lastSegment;
- GLfloat lineStep;
- GLfloat linePos;
- if (rowMode) {
- lineStep = -2.0f * aspectRatio * m_axisCacheX.subSegmentStep() / m_scaleFactor;
- lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount();
- linePos = m_scaleX;
- } else {
- lineStep = -2.0f * aspectRatio * m_axisCacheZ.subSegmentStep() / m_scaleFactor;
- lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount();
- linePos = m_scaleZ;
- }
-
- for (int segment = 0; segment <= lastSegment; segment++) {
+ gridLineCount = sliceCache.gridLineCount();
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(linePos, 0.0f, -sliceZScale);
+ 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
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -928,9 +915,11 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
+#if !(defined QT_OPENGL_ES_2)
m_drawer->drawObject(lineShader, m_gridLineObj);
-
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
}
@@ -938,96 +927,76 @@ void Surface3DRenderer::drawSlicedScene()
m_labelShader->bind();
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
- glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Y Labels to back wall
- GLfloat posStep = 2.0f * m_axisCacheY.segmentStep() / m_heightNormalizer;
- GLfloat labelPos = -1.0f;
int labelNbr = 0;
QVector3D positionComp(0.0f, 0.0f, 0.0f);
- QVector3D rotation(0.0f, 0.0f, 0.0f);
- QVector3D labelTrans = QVector3D(scaleXBackground + labelMargin, labelPos, 0.0f);
- for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) {
+ QVector3D labelTrans = QVector3D(scaleXBackground + labelMargin, 0.0f, 0.0f);
+ int labelCount = m_axisCacheY.labelCount();
+ for (int label = 0; label < labelCount; label++) {
if (m_axisCacheY.labelItems().size() > labelNbr) {
- labelTrans.setY(labelPos);
+ labelTrans.setY(m_axisCacheY.labelPosition(label));
const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
// Draw the label here
m_dummyRenderItem.setTranslation(labelTrans);
m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- positionComp, rotation, 0, m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, Qt::AlignRight, true);
+ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, activeCamera, true, true,
+ Drawer::LabelMid, Qt::AlignLeft, true);
}
labelNbr++;
- labelPos += posStep;
}
// X Labels to ground
- int countLabelItems;
- int lastSegment;
- if (rowMode) {
- posStep = 2.0f * aspectRatio * m_axisCacheX.segmentStep() / m_scaleFactor;
- labelPos = -m_scaleX;
- lastSegment = m_axisCacheX.segmentCount();
- countLabelItems = m_axisCacheX.labelItems().size();
- } else {
- posStep = 2.0f * aspectRatio * m_axisCacheZ.segmentStep() / m_scaleFactor;
- labelPos = -m_scaleZ;
- lastSegment = m_axisCacheZ.segmentCount();
- countLabelItems = m_axisCacheZ.labelItems().size();
- }
+ int countLabelItems = sliceCache.labelItems().size();
+
+ QVector3D rotation(0.0f, 0.0f, -45.0f);
+ QQuaternion totalRotation = Utils::calculateRotation(rotation);
labelNbr = 0;
positionComp.setY(-0.1f);
- rotation.setZ(-45.0f);
labelTrans.setY(-backgroundMargin);
- for (int segment = 0; segment <= lastSegment; segment++) {
+ labelCount = sliceCache.labelCount();
+ for (int label = 0; label < labelCount; label++) {
if (countLabelItems > labelNbr) {
// Draw the label here
- labelTrans.setX(labelPos);
+ labelTrans.setX(sliceCache.labelPosition(label));
m_dummyRenderItem.setTranslation(labelTrans);
LabelItem *axisLabelItem;
- if (rowMode)
- axisLabelItem = m_axisCacheX.labelItems().at(labelNbr);
- else
- axisLabelItem = m_axisCacheZ.labelItems().at(labelNbr);
+ axisLabelItem = sliceCache.labelItems().at(labelNbr);
m_drawer->drawLabel(m_dummyRenderItem, *axisLabelItem, viewMatrix, projectionMatrix,
- positionComp, rotation, 0, QAbstract3DGraph::SelectionRow,
+ positionComp, totalRotation, 0, QAbstract3DGraph::SelectionRow,
m_labelShader, m_labelObj, activeCamera,
- false, false, Drawer::LabelBelow, Qt::AlignBottom, true);
+ false, false, Drawer::LabelBelow,
+ Qt::AlignmentFlag(Qt::AlignLeft | Qt::AlignTop), true);
}
labelNbr++;
- labelPos += posStep;
}
// Draw labels for axes
AbstractRenderItem *dummyItem(0);
positionComp.setY(m_autoScaleAdjustment);
- if (rowMode) {
- m_drawer->drawLabel(*dummyItem, m_axisCacheX.titleItem(), viewMatrix, projectionMatrix,
- positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
- Qt::AlignCenter, true);
- } else {
- m_drawer->drawLabel(*dummyItem, m_axisCacheZ.titleItem(), viewMatrix, projectionMatrix,
- positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
- Qt::AlignCenter, true);
- }
+ m_drawer->drawLabel(*dummyItem, sliceCache.titleItem(), viewMatrix, projectionMatrix,
+ positionComp, identityQuaternion, 0, m_cachedSelectionMode, m_labelShader,
+ m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
+ Qt::AlignCenter, true);
+
// Y-axis label
+ rotation = QVector3D(0.0f, 0.0f, 90.0f);
+ totalRotation = Utils::calculateRotation(rotation);
labelTrans = QVector3D(-scaleXBackground - labelMargin, 0.0f, 0.0f);
m_dummyRenderItem.setTranslation(labelTrans);
m_drawer->drawLabel(m_dummyRenderItem, m_axisCacheY.titleItem(), viewMatrix,
- projectionMatrix, zeroVector, QVector3D(0.0f, 0.0f, 90.0f), 0,
+ projectionMatrix, zeroVector, totalRotation, 0,
m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
- false, false, Drawer::LabelMid, Qt::AlignHCenter);
+ false, false, Drawer::LabelMid, Qt::AlignBottom);
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
@@ -1042,7 +1011,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
bool noShadows = true;
GLfloat backgroundRotation = 0;
- QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
glViewport(m_primarySubViewport.x(),
m_primarySubViewport.y(),
@@ -1051,8 +1020,16 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
// Set up projection matrix
QMatrix4x4 projectionMatrix;
- projectionMatrix.perspective(45.0f, (GLfloat)m_primarySubViewport.width()
- / (GLfloat)m_primarySubViewport.height(), 0.1f, 100.0f);
+ GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
+ / (GLfloat)m_primarySubViewport.height();
+ if (m_useOrthoProjection) {
+ GLfloat orthoRatio = 2.0f;
+ projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
+ -orthoRatio, orthoRatio,
+ 0.0f, 100.0f);
+ } else {
+ projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
+ }
const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
@@ -1089,8 +1066,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw depth buffer
#if !defined(QT_OPENGL_ES_2)
- GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && m_renderCacheList.size()) {
+ GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
+ if (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
glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
@@ -1121,15 +1099,14 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glDisable(GL_CULL_FACE);
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
SurfaceObject *object = cache->surfaceObject();
- if (object->indexCount() && cache->surfaceVisible() && cache->isSeriesVisible()
+ if (object->indexCount() && cache->surfaceVisible() && cache->isVisible()
&& cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
- modelMatrix.translate(cache->offset());
- modelMatrix.scale(cache->scale());
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
cache->setMVPMatrix(MVPMatrix);
m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
@@ -1149,16 +1126,18 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
- 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 (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
SurfaceObject *object = cache->surfaceObject();
- if (object->indexCount() && cache->surfaceVisible() && cache->isSeriesVisible()
+ if (object->indexCount() && cache->surfaceVisible() && cache->isVisible()
&& cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) {
m_depthShader->setUniformValue(m_depthShader->MVP(), cache->MVPMatrix());
@@ -1183,6 +1162,10 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glDisableVertexAttribArray(m_depthShader->posAtt());
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -1201,7 +1184,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glEnable(GL_TEXTURE_2D);
// Draw selection buffer
- if (!m_cachedIsSlicingActivated && m_renderCacheList.size() && m_selectionState == SelectOnScene
+ if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty()
+ || !m_customRenderCache.isEmpty())
+ && m_selectionState == SelectOnScene
&& m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
m_selectionShader->bind();
glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
@@ -1217,14 +1202,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glDisable(GL_CULL_FACE);
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->surfaceObject()->indexCount() && cache->renderable()) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
- modelMatrix.translate(cache->offset());
- modelMatrix.scale(cache->scale());
-
MVPMatrix = projectionViewMatrix * modelMatrix;
m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
@@ -1232,21 +1215,23 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
cache->selectionTexture());
}
}
+ m_surfaceGridShader->bind();
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+ drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
glEnable(GL_DITHER);
- GLubyte pixel[4] = {0, 0, 0, 0};
- glReadPixels(m_inputPosition.x(), m_viewport.height() - m_inputPosition.y(),
- 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixel);
+ QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height());
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
// Put the RGBA value back to uint
-#if !defined(QT_OPENGL_ES_2)
- uint selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536 + pixel[3] * 16777216;
-#else
- uint selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536;
-#endif
+ uint selectionId = uint(clickedColor.x())
+ + uint(clickedColor.y()) * greenMultiplier
+ + uint(clickedColor.z()) * blueMultiplier
+ + uint(clickedColor.w()) * alphaMultiplier;
m_clickedPosition = selectionIdToSurfacePoint(selectionId);
@@ -1260,21 +1245,18 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
}
// Draw the surface
- if (m_renderCacheList.size()) {
+ if (!m_renderCacheList.isEmpty()) {
// For surface we can see climpses from underneath
glDisable(GL_CULL_FACE);
bool drawGrid = false;
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(cache->offset());
- modelMatrix.scale(cache->scale());
- itModelMatrix.scale(cache->scale());
-
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
@@ -1283,7 +1265,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
cache->setMVPMatrix(MVPMatrix);
const QRect &sampleSpace = cache->sampleSpace();
- if (cache->surfaceObject()->indexCount() && cache->isSeriesVisible() &&
+ if (cache->surfaceObject()->indexCount() && cache->isVisible() &&
sampleSpace.width() >= 2 && sampleSpace.height() >= 2) {
noShadows = false;
if (!drawGrid && cache->surfaceGridVisible()) {
@@ -1346,13 +1328,18 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glDisable(GL_POLYGON_OFFSET_FILL);
m_surfaceGridShader->bind();
m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(),
- Utils::vectorFromColor(m_cachedTheme->gridLineColor()));
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
- m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), cache->MVPMatrix());
+ Utils::vectorFromColor(
+ m_cachedTheme->gridLineColor()));
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(baseCache);
+ m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(),
+ cache->MVPMatrix());
const QRect &sampleSpace = cache->sampleSpace();
- if (cache->surfaceObject()->indexCount() && cache->surfaceGridVisible() &&
- cache->isSeriesVisible() && sampleSpace.width() >= 2 && sampleSpace.height() >= 2) {
+ if (cache->surfaceObject()->indexCount() && cache->surfaceGridVisible()
+ && cache->isVisible() && sampleSpace.width() >= 2
+ && sampleSpace.height() >= 2) {
m_drawer->drawSurfaceGrid(m_surfaceGridShader, cache->surfaceObject());
}
}
@@ -1388,7 +1375,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
+ QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
// Set shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos);
@@ -1412,7 +1399,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
adjustedLightStrength);
// Draw the object
- if (noShadows)
+ if (noShadows && m_customRenderCache.isEmpty())
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_noShadowTexture);
else
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
@@ -1436,13 +1423,17 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, 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
// Bind line shader
lineShader->bind();
// Set unchanging shader bindings
- QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
+ QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
lineShader->setUniformValue(lineShader->lightP(), lightPos);
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
@@ -1482,16 +1473,15 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
// Rows (= Z)
if (m_axisCacheZ.segmentCount() > 0) {
// Floor lines
- GLfloat lineStep = 2.0f * aspectRatio * m_axisCacheZ.subSegmentStep() / m_scaleFactor;
- GLfloat linePos = m_scaleZ; // Start line
- int lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount();
+ int gridLineCount = m_axisCacheZ.gridLineCount();
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(0.0f, yFloorLinePosition, linePos);
+ modelMatrix.translate(0.0f, yFloorLinePosition,
+ m_axisCacheZ.gridLinePosition(line));
modelMatrix.scale(gridLineScaleX);
itModelMatrix.scale(gridLineScaleX);
@@ -1514,34 +1504,38 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos -= lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
// Side wall lines
GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
- linePos = m_scaleZ; // Start line
if (!m_xFlipped)
lineXTrans = -lineXTrans;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(lineXTrans, 0.0f, linePos);
+ modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
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;
@@ -1558,29 +1552,31 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos -= lineStep;
+#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
// Floor lines
- GLfloat lineStep = -2.0f * aspectRatio * m_axisCacheX.subSegmentStep() / m_scaleFactor;
- GLfloat linePos = m_scaleX;
- int lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount();
+ int gridLineCount = m_axisCacheX.gridLineCount();
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(linePos, yFloorLinePosition, 0.0f);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
+ 0.0f);
modelMatrix.scale(gridLineScaleZ);
itModelMatrix.scale(gridLineScaleZ);
@@ -1603,36 +1599,40 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
// Back wall lines
GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
- linePos = m_scaleX;
if (!m_zFlipped)
lineZTrans = -lineZTrans;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(linePos, 0.0f, lineZTrans);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
modelMatrix.scale(gridLineScaleY);
itModelMatrix.scale(gridLineScaleY);
+#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
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1649,34 +1649,32 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
}
// Horizontal wall lines
if (m_axisCacheY.segmentCount() > 0) {
// Back wall
- GLfloat lineStep = 2.0f * m_axisCacheY.subSegmentStep() / m_heightNormalizer;
- GLfloat linePos = -1.0f;
- int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
+ int gridLineCount = m_axisCacheY.gridLineCount();
GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
if (!m_zFlipped)
lineZTrans = -lineZTrans;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(0.0f, linePos, lineZTrans);
+ modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans);
modelMatrix.scale(gridLineScaleX);
itModelMatrix.scale(gridLineScaleX);
@@ -1701,29 +1699,27 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
// Side wall
- linePos = -1.0f;
- lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
if (!m_xFlipped)
lineXTrans = -lineXTrans;
- for (int segment = 0; segment <= lastSegment; segment++) {
+ for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- modelMatrix.translate(lineXTrans, linePos, 0.0f);
+ modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f);
modelMatrix.scale(gridLineScaleZ);
itModelMatrix.scale(gridLineScaleZ);
@@ -1746,221 +1742,434 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Draw the object
m_drawer->drawObject(lineShader, m_gridLineObj);
}
- linePos += lineStep;
+#else
+ m_drawer->drawLine(lineShader);
+#endif
}
}
}
- // Draw axis labels
- m_labelShader->bind();
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+
+ drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
+
+ // Release shader
+ glUseProgram(0);
+
+ // Selection handling
+ if (m_selectionDirty || m_selectionLabelDirty) {
+ QPoint visiblePoint = Surface3DController::invalidSelectionPosition();
+ if (m_selectedSeries) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(
+ m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)));
+ if (cache && m_selectedPoint != Surface3DController::invalidSelectionPosition()) {
+ const QRect &sampleSpace = cache->sampleSpace();
+ int x = m_selectedPoint.x() - sampleSpace.y();
+ int y = m_selectedPoint.y() - sampleSpace.x();
+ if (x >= 0 && y >= 0 && x < sampleSpace.height() && y < sampleSpace.width()
+ && cache->dataArray().size()) {
+ visiblePoint = QPoint(x, y);
+ }
+ }
+ }
+
+ if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone
+ || visiblePoint == Surface3DController::invalidSelectionPosition()) {
+ m_selectionActive = false;
+ } else {
+ if (m_cachedIsSlicingActivated)
+ updateSliceDataModel(visiblePoint);
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem))
+ surfacePointSelected(visiblePoint);
+ m_selectionActive = true;
+ }
+
+ m_selectionDirty = false;
+ }
+}
+
+void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
+ const QMatrix4x4 &viewMatrix,
+ const QMatrix4x4 &projectionMatrix)
+{
+ ShaderHelper *shader = 0;
+ GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
+ GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
+ GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
+ if (drawSelection) {
+ shader = m_surfaceGridShader;
+ } else {
+ shader = m_labelShader;
+ shader->bind();
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POLYGON_OFFSET_FILL);
+ float labelAutoAngle = m_axisCacheZ.labelAutoRotation();
+ float labelAngleFraction = labelAutoAngle / 90.0f;
+ float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
+ float fractionCamX = activeCamera->xRotation() * labelAngleFraction;
+ float labelsMaxWidth = 0.0f;
+
+ int startIndex;
+ int endIndex;
+ int indexStep;
+
// Z Labels
QVector3D positionZComp(0.0f, 0.0f, 0.0f);
if (m_axisCacheZ.segmentCount() > 0) {
- GLfloat posStep = 2.0f * aspectRatio * m_axisCacheZ.segmentStep() / m_scaleFactor;
- GLfloat labelPos = m_scaleZ;
- int lastSegment = m_axisCacheZ.segmentCount();
- int labelNbr = 0;
+ int labelCount = m_axisCacheZ.labelCount();
GLfloat labelXTrans = m_scaleXWithBackground + labelMargin;
GLfloat labelYTrans = -backgroundMargin;
- GLfloat rotLabelX = -90.0f;
- GLfloat rotLabelY = 0.0f;
- GLfloat rotLabelZ = 0.0f;
- Qt::AlignmentFlag alignment = Qt::AlignRight;
- if (m_zFlipped)
- rotLabelY = 180.0f;
- if (m_xFlipped) {
+ Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+ QVector3D labelRotation;
+ if (m_xFlipped)
labelXTrans = -labelXTrans;
- alignment = Qt::AlignLeft;
- }
- if (m_yFlipped) {
- rotLabelZ += 180.0f;
- rotLabelY += 180.0f;
+ if (m_yFlipped)
labelYTrans = -labelYTrans;
+ if (labelAutoAngle == 0.0f) {
+ labelRotation.setX(-90.0f);
+ if (m_zFlipped)
+ labelRotation.setY(180.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped)
+ labelRotation.setY(0.0f);
+ else
+ labelRotation.setY(180.0f);
+ labelRotation.setZ(180.0f);
+ }
+ } else {
+ if (m_zFlipped)
+ labelRotation.setY(180.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ }
+ }
}
+
+ QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
+
QVector3D labelTrans = QVector3D(labelXTrans,
labelYTrans,
- labelPos);
- QVector3D rotation(rotLabelX, rotLabelY, rotLabelZ);
-
- for (int segment = 0; segment <= lastSegment; segment++) {
- if (m_axisCacheZ.labelItems().size() > labelNbr) {
- glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f);
- // Draw the label here
- labelTrans.setZ(labelPos);
- m_dummyRenderItem.setTranslation(labelTrans);
- const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(labelNbr);
-
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- positionZComp, rotation, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, alignment);
+ 0.0f);
+
+ if (m_zFlipped) {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
+ } else {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ }
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
+ glPolygonOffset(GLfloat(label) / -10.0f, 1.0f);
+ // Draw the label here
+ labelTrans.setZ(m_axisCacheZ.labelPosition(label));
+ m_dummyRenderItem.setTranslation(labelTrans);
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
+
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
+ alphaForRowSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
- labelNbr++;
- labelPos -= posStep;
+
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ positionZComp, totalRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, alignment, false, drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+ if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
+ labelTrans.setZ(0.0f);
+ drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
}
// X Labels
if (m_axisCacheX.segmentCount() > 0) {
- GLfloat posStep = 2.0f * aspectRatio * m_axisCacheX.segmentStep() / m_scaleFactor;
- GLfloat labelPos = -m_scaleX;
- int lastSegment = m_axisCacheX.segmentCount();
+ labelsMaxWidth = 0.0f;
+ labelAutoAngle = m_axisCacheX.labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ fractionCamY = activeCamera->yRotation() * labelAngleFraction;
+ fractionCamX = activeCamera->xRotation() * labelAngleFraction;
+ int labelCount = m_axisCacheX.labelCount();
- int labelNbr = 0;
GLfloat labelZTrans = m_scaleZWithBackground + labelMargin;
GLfloat labelYTrans = -backgroundMargin;
- GLfloat rotLabelX = -90.0f;
- GLfloat rotLabelY = 90.0f;
- GLfloat rotLabelZ = 0.0f;
- Qt::AlignmentFlag alignment = Qt::AlignLeft;
- if (m_xFlipped)
- rotLabelY = -90.0f;
- if (m_zFlipped) {
+ Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
+ QVector3D labelRotation;
+ if (m_zFlipped)
labelZTrans = -labelZTrans;
- alignment = Qt::AlignRight;
- }
- if (m_yFlipped) {
- rotLabelZ += 180.0f;
- rotLabelY += 180.0f;
+ if (m_yFlipped)
labelYTrans = -labelYTrans;
+ if (labelAutoAngle == 0.0f) {
+ labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
+ if (m_xFlipped)
+ labelRotation.setY(-90.0f);
+ if (m_yFlipped) {
+ if (m_xFlipped)
+ labelRotation.setY(90.0f);
+ else
+ labelRotation.setY(-90.0f);
+ labelRotation.setZ(180.0f);
+ }
+ } else {
+ if (m_xFlipped)
+ labelRotation.setY(-90.0f);
+ else
+ labelRotation.setY(90.0f);
+ if (m_yFlipped) {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(90.0f + fractionCamX
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - fractionCamX
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (m_xFlipped) {
+ labelRotation.setX(-90.0f - fractionCamX
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + fractionCamX
+ * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ }
+ }
+ }
}
- QVector3D labelTrans = QVector3D(labelPos,
+ QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
+
+ QVector3D labelTrans = QVector3D(0.0f,
labelYTrans,
labelZTrans);
- QVector3D rotation(rotLabelX, rotLabelY, rotLabelZ);
-
- for (int segment = 0; segment <= lastSegment; segment++) {
- if (m_axisCacheX.labelItems().size() > labelNbr) {
- glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f);
- // Draw the label here
- labelTrans.setX(labelPos);
- m_dummyRenderItem.setTranslation(labelTrans);
- const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(labelNbr);
-
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- positionZComp, rotation, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, alignment);
+
+ if (m_xFlipped) {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ } else {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
+ }
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
+ glPolygonOffset(GLfloat(label) / -10.0f, 1.0f);
+ // Draw the label here
+ labelTrans.setX(m_axisCacheX.labelPosition(label));
+ m_dummyRenderItem.setTranslation(labelTrans);
+ const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label);
+
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
+ alphaForColumnSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
- labelNbr++;
- labelPos += posStep;
+
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ positionZComp, totalRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, alignment, false, drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+ if (!drawSelection && m_axisCacheX.isTitleVisible()) {
+ labelTrans.setX(0.0f);
+ drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
}
// Y Labels
if (m_axisCacheY.segmentCount() > 0) {
- GLfloat posStep = 2.0f * m_axisCacheY.segmentStep() / m_heightNormalizer;
- GLfloat labelPos = -1.0f;
- int labelNbr = 0;
+ labelsMaxWidth = 0.0f;
+ labelAutoAngle = m_axisCacheY.labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ fractionCamY = activeCamera->yRotation() * labelAngleFraction;
+ fractionCamX = activeCamera->xRotation() * labelAngleFraction;
+ int labelCount = m_axisCacheY.labelCount();
+
GLfloat labelXTrans = m_scaleXWithBackground;
GLfloat labelZTrans = m_scaleZWithBackground;
- // Back wall init
+ // Back & side wall
GLfloat labelMarginXTrans = labelMargin;
GLfloat labelMarginZTrans = labelMargin;
- GLfloat rotLabelX = 0.0f;
- GLfloat rotLabelY = -90.0f;
- GLfloat rotLabelZ = 0.0f;
- Qt::AlignmentFlag alignmentBack = Qt::AlignLeft;
+ 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;
+ Qt::AlignmentFlag sideAlignment =
+ (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
if (!m_xFlipped) {
labelXTrans = -labelXTrans;
labelMarginXTrans = -labelMargin;
- rotLabelY = 90.0f;
}
if (m_zFlipped) {
labelZTrans = -labelZTrans;
labelMarginZTrans = -labelMargin;
- alignmentBack = Qt::AlignRight;
}
- QVector3D labelRotateVectorBack(rotLabelX, rotLabelY, rotLabelZ);
- QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans);
+ if (labelAutoAngle == 0.0f) {
+ if (!m_xFlipped)
+ backLabelRotation.setY(90.0f);
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.f);
+ } else {
+ // Orient side labels somewhat towards the camera
+ if (m_xFlipped) {
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ sideLabelRotation.setY(-fractionCamX);
+ backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
+ } else {
+ if (m_zFlipped)
+ sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ sideLabelRotation.setY(-fractionCamX);
+ backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
+ }
+ }
+ sideLabelRotation.setX(-fractionCamY);
+ backLabelRotation.setX(-fractionCamY);
- // Side wall init
- Qt::AlignmentFlag alignmentSide = Qt::AlignLeft;
- if (m_xFlipped)
- alignmentSide = Qt::AlignLeft;
- else
- alignmentSide = Qt::AlignRight;
- if (m_zFlipped)
- rotLabelY = 180.0f;
- else
- rotLabelY = 0.0f;
+ QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation);
+ QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation);
- QVector3D labelRotateVectorSide(rotLabelX, rotLabelY, rotLabelZ);
+ QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans);
QVector3D labelTransSide(-labelXTrans - labelMarginXTrans, 0.0f, -labelZTrans);
- for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) {
- if (m_axisCacheY.labelItems().size() > labelNbr) {
- const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
-
- glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f);
-
- // Back wall
- labelTransBack.setY(labelPos);
- m_dummyRenderItem.setTranslation(labelTransBack);
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- positionZComp, labelRotateVectorBack, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, alignmentBack);
-
- // Side wall
- labelTransSide.setY(labelPos);
- m_dummyRenderItem.setTranslation(labelTransSide);
- m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
- positionZComp, labelRotateVectorSide, 0, m_cachedSelectionMode,
- m_labelShader, m_labelObj, activeCamera,
- true, true, Drawer::LabelMid, alignmentSide);
- }
- labelNbr++;
- labelPos += posStep;
+ if (m_yFlipped) {
+ startIndex = labelCount - 1;
+ endIndex = -1;
+ indexStep = -1;
+ } else {
+ startIndex = 0;
+ endIndex = labelCount;
+ indexStep = 1;
}
- }
- glDisable(GL_POLYGON_OFFSET_FILL);
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
+ const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label);
+ const GLfloat labelYTrans = m_axisCacheY.labelPosition(label);
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
+ glPolygonOffset(GLfloat(label) / -10.0f, 1.0f);
- // Release shader
- glUseProgram(0);
-
- // Selection handling
- if (m_selectionDirty || m_selectionLabelDirty) {
- QPoint visiblePoint = Surface3DController::invalidSelectionPosition();
- if (m_selectedSeries) {
- SurfaceSeriesRenderCache *cache =
- m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries));
- if (cache && m_selectedPoint != Surface3DController::invalidSelectionPosition()) {
- const QRect &sampleSpace = cache->sampleSpace();
- int x = m_selectedPoint.x() - sampleSpace.y();
- int y = m_selectedPoint.y() - sampleSpace.x();
- if (x >= 0 && y >= 0 && x < sampleSpace.height() && y < sampleSpace.width()
- && cache->dataArray().size()) {
- visiblePoint = QPoint(x, y);
- }
+ if (drawSelection) {
+ QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f,
+ alphaForValueSelection);
+ shader->setUniformValue(shader->color(), labelColor);
}
- }
- if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone
- || visiblePoint == Surface3DController::invalidSelectionPosition()) {
- m_selectionActive = false;
- } else {
- if (m_cachedIsSlicingActivated)
- updateSliceDataModel(visiblePoint);
- if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem))
- surfacePointSelected(visiblePoint);
- m_selectionActive = true;
+ // Back wall
+ labelTransBack.setY(labelYTrans);
+ m_dummyRenderItem.setTranslation(labelTransBack);
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ positionZComp, totalBackRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, backAlignment, false,
+ drawSelection);
+
+ // Side wall
+ labelTransSide.setY(labelYTrans);
+ m_dummyRenderItem.setTranslation(labelTransSide);
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ positionZComp, totalSideRotation, 0, m_cachedSelectionMode,
+ shader, m_labelObj, activeCamera,
+ true, true, Drawer::LabelMid, sideAlignment, false,
+ drawSelection);
+ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
+ }
+ if (!drawSelection && m_axisCacheY.isTitleVisible()) {
+ labelTransSide.setY(0.0f);
+ labelTransBack.setY(0.0f);
+ drawAxisTitleY(sideLabelRotation, backLabelRotation, labelTransSide, labelTransBack,
+ totalSideRotation, totalBackRotation, m_dummyRenderItem, activeCamera,
+ labelsMaxWidth, viewMatrix, projectionMatrix,
+ shader);
}
+ }
+ glDisable(GL_POLYGON_OFFSET_FILL);
- m_selectionDirty = false;
+ if (!drawSelection) {
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
}
}
@@ -1976,7 +2185,9 @@ void Surface3DRenderer::updateSelectionTextures()
{
uint lastSelectionId = 1;
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(baseCache);
GLuint texture = cache->selectionTexture();
m_textureHelper->deleteTexture(&texture);
createSelectionTexture(cache, lastSelectionId);
@@ -1992,6 +2203,13 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache,
const QRect &sampleSpace = cache->sampleSpace();
int idImageWidth = (sampleSpace.width() - 1) * 4;
int idImageHeight = (sampleSpace.height() - 1) * 4;
+
+ if (idImageHeight <= 0 || idImageWidth <= 0) {
+ cache->setSelectionIdRange(-1, -1);
+ cache->setSelectionTexture(0);
+ return;
+ }
+
int stride = idImageWidth * 4 * sizeof(uchar); // 4 = number of color components (rgba)
uint idStart = lastSelectionId;
@@ -2031,10 +2249,7 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache,
void Surface3DRenderer::initSelectionBuffer()
{
// Create the result selection texture and buffers
- if (m_selectionResultTexture) {
- m_textureHelper->deleteTexture(&m_selectionResultTexture);
- m_selectionResultTexture = 0;
- }
+ m_textureHelper->deleteTexture(&m_selectionResultTexture);
m_selectionResultTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(),
m_selectionFrameBuffer,
@@ -2076,10 +2291,16 @@ void Surface3DRenderer::calculateSceneScalingFactors()
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 = aspectRatio * m_areaSize.width() / m_scaleFactor;
- m_scaleZ = aspectRatio * m_areaSize.height() / m_scaleFactor;
- m_scaleXWithBackground = m_scaleX * backgroundMargin;
- m_scaleZWithBackground = m_scaleZ * backgroundMargin;
+ 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);
}
void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache)
@@ -2098,17 +2319,13 @@ void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dime
QSurfaceDataArray &dataArray = cache->dataArray();
const QRect &sampleSpace = cache->sampleSpace();
-
- if (cache->isFlatShadingEnabled()) {
- cache->surfaceObject()->setUpData(dataArray, sampleSpace, m_heightNormalizer,
- m_axisCacheY.min(), dimensionChanged);
- } else {
- cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, m_heightNormalizer,
- m_axisCacheY.min(), dimensionChanged);
- }
+ if (cache->isFlatShadingEnabled())
+ cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged);
+ else
+ cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged);
}
-void Surface3DRenderer::updateSelectedPoint(const QPoint &position, const QSurface3DSeries *series)
+void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series)
{
m_selectedPoint = position;
m_selectedSeries = series;
@@ -2123,23 +2340,15 @@ void Surface3DRenderer::resetClickedStatus()
void Surface3DRenderer::loadBackgroundMesh()
{
- if (m_backgroundObj)
- delete m_backgroundObj;
- m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background"));
- m_backgroundObj->load();
-}
-
-void Surface3DRenderer::loadGridLineMesh()
-{
- if (m_gridLineObj)
- delete m_gridLineObj;
- m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane"));
- m_gridLineObj->load();
+ ObjectHelper::resetObjectHelper(this, m_backgroundObj,
+ QStringLiteral(":/defaultMeshes/background"));
}
void Surface3DRenderer::surfacePointSelected(const QPoint &point)
{
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(baseCache);
cache->setSlicePointerActivity(false);
cache->setMainPointerActivity(false);
}
@@ -2147,12 +2356,15 @@ void Surface3DRenderer::surfacePointSelected(const QPoint &point)
if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)) {
// Find axis coordinates for the selected point
SurfaceSeriesRenderCache *selectedCache =
- m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries));
+ static_cast<SurfaceSeriesRenderCache *>(
+ m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)));
QSurfaceDataArray &dataArray = selectedCache->dataArray();
QSurfaceDataItem item = dataArray.at(point.x())->at(point.y());
QPointF coords(item.x(), item.z());
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->series() != m_selectedSeries) {
QPoint mappedPoint = mapCoordsToSampleSpace(cache, coords);
updateSelectionPoint(cache, mappedPoint, false);
@@ -2163,7 +2375,8 @@ void Surface3DRenderer::surfacePointSelected(const QPoint &point)
} else {
if (m_selectedSeries) {
SurfaceSeriesRenderCache *cache =
- m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries));
+ static_cast<SurfaceSeriesRenderCache *>(
+ m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)));
if (cache)
updateSelectionPoint(cache, point, true);
}
@@ -2179,9 +2392,6 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co
if (column < 0 || row < 0)
return;
- QSurfaceDataArray &dataArray = cache->dataArray();
- float value = dataArray.at(row)->at(column).y();
-
SelectionPointer *slicePointer = cache->sliceSelectionPointer();
if (!slicePointer && m_cachedIsSlicingActivated) {
slicePointer = new SelectionPointer(m_drawer);
@@ -2193,28 +2403,28 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co
cache->setMainSelectionPointer(mainPointer);
}
- const QVector3D &scale = cache->scale();
- const QVector3D &offset = cache->offset();
QString selectionLabel;
- if (label)
- selectionLabel = createSelectionLabel(cache, value, column, row);
+ if (label) {
+ m_selectionLabelDirty = false;
+ selectionLabel = cache->itemLabel();
+ }
if (m_cachedIsSlicingActivated) {
- QVector3D subPos;
+ QVector3D subPosFront;
+ QVector3D subPosBack;
if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) {
- subPos = cache->sliceSurfaceObject()->vertexAt(column, 0);
- subPos *= QVector3D(scale.x(), 1.0f, 0.0f);
- subPos += QVector3D(offset.x(), 0.0f, 0.0f);
+ subPosFront = cache->sliceSurfaceObject()->vertexAt(column, 0);
+ subPosBack = cache->sliceSurfaceObject()->vertexAt(column, 1);
} else if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
- subPos = cache->sliceSurfaceObject()->vertexAt(row, 0);
- subPos *= QVector3D(scale.z(), 1.0f, 0.0f);
- subPos += QVector3D(-offset.z(), 0.0f, 0.0f);
+ subPosFront = cache->sliceSurfaceObject()->vertexAt(row, 0);
+ subPosBack = cache->sliceSurfaceObject()->vertexAt(row, 1);
}
slicePointer->updateBoundingRect(m_secondarySubViewport);
slicePointer->updateSliceData(true, m_autoScaleAdjustment);
- slicePointer->setPosition(subPos);
+ slicePointer->setPosition((subPosFront + subPosBack) / 2.0f);
slicePointer->setLabel(selectionLabel);
slicePointer->setPointerObject(cache->object());
+ slicePointer->setLabelObject(m_labelObj);
slicePointer->setHighlightColor(cache->singleHighlightColor());
slicePointer->updateScene(m_cachedScene);
slicePointer->setRotation(cache->meshRotation());
@@ -2223,13 +2433,12 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co
QVector3D mainPos;
mainPos = cache->surfaceObject()->vertexAt(column, row);
- mainPos *= scale;
- mainPos += offset;
mainPointer->updateBoundingRect(m_primarySubViewport);
mainPointer->updateSliceData(false, m_autoScaleAdjustment);
mainPointer->setPosition(mainPos);
mainPointer->setLabel(selectionLabel);
mainPointer->setPointerObject(cache->object());
+ mainPointer->setLabelObject(m_labelObj);
mainPointer->setHighlightColor(cache->singleHighlightColor());
mainPointer->updateScene(m_cachedScene);
mainPointer->setRotation(cache->meshRotation());
@@ -2239,8 +2448,33 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co
// Maps selection Id to surface point in data array
QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id)
{
+ m_clickedType = QAbstract3DGraph::ElementNone;
+ m_selectedLabelIndex = -1;
+ m_selectedCustomItemIndex = -1;
+ // Check for label and custom item selection
+ if (id / alphaMultiplier == labelRowAlpha) {
+ m_selectedLabelIndex = id - (alphaMultiplier * uint(labelRowAlpha));
+ m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
+ return Surface3DController::invalidSelectionPosition();
+ } else if (id / alphaMultiplier == labelColumnAlpha) {
+ m_selectedLabelIndex = (id - (alphaMultiplier * uint(labelColumnAlpha))) / greenMultiplier;
+ m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
+ return Surface3DController::invalidSelectionPosition();
+ } else if (id / alphaMultiplier == labelValueAlpha) {
+ m_selectedLabelIndex = (id - (alphaMultiplier * uint(labelValueAlpha))) / blueMultiplier;
+ m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
+ return Surface3DController::invalidSelectionPosition();
+ } else if (id / alphaMultiplier == customItemAlpha) {
+ // Custom item selection
+ m_clickedType = QAbstract3DGraph::ElementCustomItem;
+ m_selectedCustomItemIndex = id - (alphaMultiplier * uint(customItemAlpha));
+ return Surface3DController::invalidSelectionPosition();
+ }
+
+ // Not a label selection
SurfaceSeriesRenderCache *selectedCache = 0;
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->isWithinIdRange(id)) {
selectedCache = cache;
break;
@@ -2257,57 +2491,10 @@ QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id)
int row = ((idInSeries - 1) / sampleSpace.width()) + sampleSpace.y();
m_clickedSeries = selectedCache->series();
+ m_clickedType = QAbstract3DGraph::ElementSeries;
return QPoint(row, column);
}
-QString Surface3DRenderer::createSelectionLabel(SurfaceSeriesRenderCache *cache, float value,
- int column, int row)
-{
- QSurfaceDataArray &dataArray = cache->dataArray();
- QString labelText = cache->itemLabelFormat();
- static const QString xTitleTag(QStringLiteral("@xTitle"));
- static const QString yTitleTag(QStringLiteral("@yTitle"));
- static const QString zTitleTag(QStringLiteral("@zTitle"));
- static const QString xLabelTag(QStringLiteral("@xLabel"));
- static const QString yLabelTag(QStringLiteral("@yLabel"));
- static const QString zLabelTag(QStringLiteral("@zLabel"));
- static const QString seriesNameTag(QStringLiteral("@seriesName"));
-
- labelText.replace(xTitleTag, m_axisCacheX.title());
- labelText.replace(yTitleTag, m_axisCacheY.title());
- labelText.replace(zTitleTag, m_axisCacheZ.title());
-
- if (labelText.contains(xLabelTag)) {
- QString labelFormat = m_axisCacheX.labelFormat();
- if (labelFormat.isEmpty())
- labelFormat = Utils::defaultLabelFormat();
- QString valueLabelText = generateValueLabel(labelFormat,
- dataArray.at(row)->at(column).x());
- labelText.replace(xLabelTag, valueLabelText);
- }
- if (labelText.contains(yLabelTag)) {
- QString labelFormat = m_axisCacheY.labelFormat();
- if (labelFormat.isEmpty())
- labelFormat = Utils::defaultLabelFormat();
- QString valueLabelText = generateValueLabel(labelFormat, value);
- labelText.replace(yLabelTag, valueLabelText);
- }
- if (labelText.contains(zLabelTag)) {
- QString labelFormat = m_axisCacheZ.labelFormat();
- if (labelFormat.isEmpty())
- labelFormat = Utils::defaultLabelFormat();
- QString valueLabelText = generateValueLabel(labelFormat,
- dataArray.at(row)->at(column).z());
- labelText.replace(zLabelTag, valueLabelText);
- }
-
- labelText.replace(seriesNameTag, cache->name());
-
- m_selectionLabelDirty = false;
-
- return labelText;
-}
-
void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)
{
m_cachedShadowQuality = quality;
@@ -2371,20 +2558,13 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing)
m_selectionDirty = true;
- foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->mainSelectionPointer())
cache->mainSelectionPointer()->updateBoundingRect(m_primarySubViewport);
}
}
-void Surface3DRenderer::loadLabelMesh()
-{
- if (m_labelObj)
- delete m_labelObj;
- m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane"));
- m_labelObj->load();
-}
-
void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
{
Q_UNUSED(vertexShader);
@@ -2408,17 +2588,22 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &
m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
QStringLiteral(":/shaders/fragmentSurface"));
}
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceShadowFlat"));
- } else {
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceFlat"));
- }
m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
QStringLiteral(":/shaders/fragmentSurface"));
- m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceFlat"));
+ if (m_flatSupported) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceShadowFlat"));
+ } else {
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceFlat"));
+ }
+ m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceFlat"));
+ } else {
+ m_surfaceFlatShader = 0;
+ m_surfaceSliceFlatShader = 0;
+ }
#else
m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
QStringLiteral(":/shaders/fragmentSurfaceES2"));
@@ -2430,9 +2615,11 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &
QStringLiteral(":/shaders/fragmentSurfaceES2"));
#endif
m_surfaceSmoothShader->initialize();
- m_surfaceFlatShader->initialize();
m_surfaceSliceSmoothShader->initialize();
- m_surfaceSliceFlatShader->initialize();
+ if (m_flatSupported) {
+ m_surfaceFlatShader->initialize();
+ m_surfaceSliceFlatShader->initialize();
+ }
}
void Surface3DRenderer::initBackgroundShaders(const QString &vertexShader,
@@ -2486,14 +2673,8 @@ void Surface3DRenderer::initDepthShader()
void Surface3DRenderer::updateDepthBuffer()
{
- if (m_depthTexture) {
- m_textureHelper->deleteTexture(&m_depthTexture);
- m_depthTexture = 0;
- }
- if (m_depthModelTexture) {
- m_textureHelper->deleteTexture(&m_depthModelTexture);
- m_depthModelTexture = 0;
- }
+ m_textureHelper->deleteTexture(&m_depthTexture);
+ m_textureHelper->deleteTexture(&m_depthModelTexture);
if (m_primarySubViewport.size().isEmpty())
return;
@@ -2512,4 +2693,22 @@ void Surface3DRenderer::updateDepthBuffer()
}
#endif
+QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &position,
+ bool isAbsolute)
+{
+ float xTrans = 0.0f;
+ float yTrans = 0.0f;
+ float zTrans = 0.0f;
+ if (!isAbsolute) {
+ xTrans = m_axisCacheX.positionAt(position.x());
+ 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;
+ }
+ return QVector3D(xTrans, yTrans, zTrans);
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h
index 2c55d902..efa8ff7e 100644
--- a/src/datavisualization/engine/surface3drenderer_p.h
+++ b/src/datavisualization/engine/surface3drenderer_p.h
@@ -32,17 +32,12 @@
#include "datavisualizationglobal_p.h"
#include "surface3dcontroller_p.h"
#include "abstract3drenderer_p.h"
-#include "scatterrenderitem_p.h"
-#include "qsurfacedataproxy.h"
#include "surfaceseriesrendercache_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class ShaderHelper;
-class ObjectHelper;
-class SurfaceObject;
class Q3DScene;
-class SelectionPointer;
class QT_DATAVISUALIZATION_EXPORT Surface3DRenderer : public Abstract3DRenderer
{
@@ -67,15 +62,6 @@ private:
GLfloat m_scaleZ;
GLfloat m_scaleXWithBackground;
GLfloat m_scaleZWithBackground;
- GLfloat m_minVisibleColumnValue;
- GLfloat m_maxVisibleColumnValue;
- GLfloat m_minVisibleRowValue;
- GLfloat m_maxVisibleRowValue;
- GLfloat m_visibleColumnRange;
- GLfloat m_visibleRowRange;
- ObjectHelper *m_backgroundObj;
- ObjectHelper *m_gridLineObj;
- ObjectHelper *m_labelObj;
GLuint m_depthTexture;
GLuint m_depthModelTexture;
GLuint m_depthFrameBuffer;
@@ -85,17 +71,13 @@ private:
GLfloat m_shadowQualityToShader;
bool m_flatSupported;
bool m_selectionActive;
- bool m_xFlipped;
- bool m_zFlipped;
- bool m_yFlipped;
AbstractRenderItem m_dummyRenderItem;
GLint m_shadowQualityMultiplier;
QSizeF m_areaSize;
bool m_hasHeightAdjustmentChanged;
QPoint m_selectedPoint;
- const QSurface3DSeries *m_selectedSeries;
+ QSurface3DSeries *m_selectedSeries;
QPoint m_clickedPosition;
- QHash<QSurface3DSeries *, SurfaceSeriesRenderCache *> m_renderCacheList;
bool m_selectionTexturesDirty;
GLuint m_noShadowTexture;
@@ -104,19 +86,19 @@ public:
~Surface3DRenderer();
void updateData();
- void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility);
+ void updateSeries(const QList<QAbstract3DSeries *> &seriesList);
+ SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
+ void cleanCache(SeriesRenderCache *cache);
void updateSelectionMode(QAbstract3DGraph::SelectionFlags mode);
- void modifiedSeriesList(const QVector<QSurface3DSeries *> &seriesList);
void updateRows(const QVector<Surface3DController::ChangeRow> &rows);
- void updateItem(const QVector<Surface3DController::ChangeItem> &points);
- void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, float max);
+ void updateItems(const QVector<Surface3DController::ChangeItem> &points);
void updateScene(Q3DScene *scene);
void updateSlicingActive(bool isSlicing);
- void updateSelectedPoint(const QPoint &position, const QSurface3DSeries *series);
+ void updateSelectedPoint(const QPoint &position, QSurface3DSeries *series);
inline QPoint clickedPosition() const { return m_clickedPosition; }
void resetClickedStatus();
+ QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
- void drawSlicedScene();
void render(GLuint defaultFboHandle = 0);
protected:
@@ -136,11 +118,14 @@ private:
void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality);
void updateTextures();
void initShaders(const QString &vertexShader, const QString &fragmentShader);
- QRect calculateSampleRect(SurfaceSeriesRenderCache *cache, const QSurfaceDataArray &array);
+ QRect calculateSampleRect(const QSurfaceDataArray &array);
void loadBackgroundMesh();
- void loadGridLineMesh();
- void loadLabelMesh();
+
+ void drawSlicedScene();
void drawScene(GLuint defaultFboHandle);
+ void drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix);
+
void calculateSceneScalingFactors();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
@@ -155,13 +140,14 @@ private:
void surfacePointSelected(const QPoint &point);
void updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point, bool label);
QPoint selectionIdToSurfacePoint(uint id);
- QString createSelectionLabel(SurfaceSeriesRenderCache *cache, float value, int column, int row);
#if !defined(QT_OPENGL_ES_2)
void updateDepthBuffer();
#endif
void emitSelectedPointChanged(QPoint position);
Q_DISABLE_COPY(Surface3DRenderer)
+
+ friend class SurfaceObject;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/surfaceseriesrendercache.cpp b/src/datavisualization/engine/surfaceseriesrendercache.cpp
index ba25d71d..1cce5288 100644
--- a/src/datavisualization/engine/surfaceseriesrendercache.cpp
+++ b/src/datavisualization/engine/surfaceseriesrendercache.cpp
@@ -16,35 +16,30 @@
**
****************************************************************************/
-#include "seriesrendercache_p.h"
#include "surfaceseriesrendercache_p.h"
-#include "objecthelper_p.h"
-#include "abstract3drenderer_p.h"
+#include "surface3drenderer_p.h"
#include "texturehelper_p.h"
-#include "utils_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-SurfaceSeriesRenderCache::SurfaceSeriesRenderCache()
- : m_surfaceVisible(false),
+SurfaceSeriesRenderCache::SurfaceSeriesRenderCache(QAbstract3DSeries *series,
+ Surface3DRenderer *renderer)
+ : SeriesRenderCache(series, renderer),
+ m_surfaceVisible(false),
m_surfaceGridVisible(false),
- m_surfaceFlatShading(true),
- m_surfaceObj(new SurfaceObject),
- m_sliceSurfaceObj(new SurfaceObject),
- m_sampleSpace(QRect(0, 0, 0 , 0)),
+ m_surfaceFlatShading(false),
+ m_surfaceObj(new SurfaceObject(renderer)),
+ m_sliceSurfaceObj(new SurfaceObject(renderer)),
+ m_sampleSpace(QRect(0, 0, 0, 0)),
m_selectionTexture(0),
m_selectionIdStart(0),
m_selectionIdEnd(0),
m_flatChangeAllowed(true),
- m_flatStatusDirty(false),
- m_scale(QVector3D(1.0f, 1.0f, 1.0f)),
- m_offset(QVector3D(0.0f, 0.0f, 0.0f)),
+ m_flatStatusDirty(true),
m_sliceSelectionPointer(0),
m_mainSelectionPointer(0),
m_slicePointerActive(false),
- m_mainPointerActive(false),
- m_valid(false),
- m_objectDirty(true)
+ m_mainPointerActive(false)
{
}
@@ -52,17 +47,15 @@ SurfaceSeriesRenderCache::~SurfaceSeriesRenderCache()
{
}
-void SurfaceSeriesRenderCache::populate(QSurface3DSeries *series, Abstract3DRenderer *renderer)
+void SurfaceSeriesRenderCache::populate(bool newSeries)
{
- Q_ASSERT(series);
+ SeriesRenderCache::populate(newSeries);
- SeriesRenderCache::populate(series, renderer);
-
- QSurface3DSeries::DrawFlags drawMode = series->drawMode();
+ QSurface3DSeries::DrawFlags drawMode = series()->drawMode();
m_surfaceVisible = drawMode.testFlag(QSurface3DSeries::DrawSurface);
m_surfaceGridVisible = drawMode.testFlag(QSurface3DSeries::DrawWireframe);
- if (m_flatChangeAllowed && m_surfaceFlatShading != series->isFlatShadingEnabled()) {
- m_surfaceFlatShading = series->isFlatShadingEnabled();
+ if (m_flatChangeAllowed && m_surfaceFlatShading != series()->isFlatShadingEnabled()) {
+ m_surfaceFlatShading = series()->isFlatShadingEnabled();
m_flatStatusDirty = true;
}
}
diff --git a/src/datavisualization/engine/surfaceseriesrendercache_p.h b/src/datavisualization/engine/surfaceseriesrendercache_p.h
index 2dda0670..b6254a75 100644
--- a/src/datavisualization/engine/surfaceseriesrendercache_p.h
+++ b/src/datavisualization/engine/surfaceseriesrendercache_p.h
@@ -31,7 +31,6 @@
#include "datavisualizationglobal_p.h"
#include "seriesrendercache_p.h"
-#include "qabstract3dseries_p.h"
#include "qsurface3dseries_p.h"
#include "surfaceobject_p.h"
#include "selectionpointer_p.h"
@@ -40,17 +39,15 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-class Abstract3DRenderer;
-class ObjectHelper;
-class TextureHelper;
+class Surface3DRenderer;
class SurfaceSeriesRenderCache : public SeriesRenderCache
{
public:
- SurfaceSeriesRenderCache();
+ SurfaceSeriesRenderCache(QAbstract3DSeries *series, Surface3DRenderer *renderer);
virtual ~SurfaceSeriesRenderCache();
- void populate(QSurface3DSeries *series, Abstract3DRenderer *renderer);
+ virtual void populate(bool newSeries);
virtual void cleanup(TextureHelper *texHelper);
inline bool surfaceVisible() const { return m_surfaceVisible; }
@@ -58,8 +55,6 @@ public:
inline bool isFlatShadingEnabled() const { return m_surfaceFlatShading; }
inline void setFlatShadingEnabled(bool enabled) { m_surfaceFlatShading = enabled; }
inline void setFlatChangeAllowed(bool allowed) { m_flatChangeAllowed = allowed; }
- inline void setValid(bool valid) { m_valid = valid; }
- inline bool isValid() const { return m_valid; }
inline SurfaceObject *surfaceObject() { return m_surfaceObj; }
inline SurfaceObject *sliceSurfaceObject() { return m_sliceSurfaceObj; }
inline const QRect &sampleSpace() const { return m_sampleSpace; }
@@ -67,11 +62,8 @@ public:
inline QSurface3DSeries *series() const { return static_cast<QSurface3DSeries *>(m_series); }
inline QSurfaceDataArray &dataArray() { return m_dataArray; }
inline QSurfaceDataArray &sliceDataArray() { return m_sliceDataArray; }
- inline bool isSeriesVisible() const { return m_series->isVisible(); }
- inline bool renderable() const { return m_series->isVisible() && (m_surfaceVisible ||
- m_surfaceGridVisible); }
- inline void setObjectDirty(bool state) { m_objectDirty = state; }
- inline bool objectDirty() const { return m_objectDirty; }
+ inline bool renderable() const { return m_visible && (m_surfaceVisible ||
+ m_surfaceGridVisible); }
inline void setSelectionTexture(GLuint texture) { m_selectionTexture = texture; }
inline GLuint selectionTexture() const { return m_selectionTexture; }
inline void setSelectionIdRange(uint start, uint end) { m_selectionIdStart = start;
@@ -81,11 +73,6 @@ public:
selection <= m_selectionIdEnd; }
inline bool isFlatStatusDirty() const { return m_flatStatusDirty; }
inline void setFlatStatusDirty(bool status) { m_flatStatusDirty = status; }
- inline void setScale(const QVector3D &scale) { m_scale = scale; }
- inline const QVector3D &scale() const { return m_scale; }
- inline void setOffset(const QVector3D &offset) { m_offset = offset; }
- inline const QVector3D &offset() const { return m_offset; }
- // m_MVPMatrix is volatile, used only for optimizing rendering a bit
inline void setMVPMatrix(const QMatrix4x4 &matrix) { m_MVPMatrix = matrix; }
inline const QMatrix4x4 &MVPMatrix() { return m_MVPMatrix; }
@@ -113,16 +100,11 @@ protected:
uint m_selectionIdEnd;
bool m_flatChangeAllowed;
bool m_flatStatusDirty;
- QVector3D m_scale;
- QVector3D m_offset;
QMatrix4x4 m_MVPMatrix;
SelectionPointer *m_sliceSelectionPointer;
SelectionPointer *m_mainSelectionPointer;
bool m_slicePointerActive;
bool m_mainPointerActive;
-
- bool m_valid;
- bool m_objectDirty;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/global/datavisualizationglobal_p.h b/src/datavisualization/global/datavisualizationglobal_p.h
index e448c1cb..abdac998 100644
--- a/src/datavisualization/global/datavisualizationglobal_p.h
+++ b/src/datavisualization/global/datavisualizationglobal_p.h
@@ -43,7 +43,11 @@ static const GLfloat cameraDistance = 6.0f;
// Size of font to be used in label texture rendering. Doesn't affect the actual font size.
static const int textureFontSize = 50;
static const GLfloat defaultRatio = 1.0f / 1.6f; // default aspect ratio 16:10
+#if !(defined QT_OPENGL_ES)
static const float gridLineOffset = 0.0001f; // Offset for lifting grid lines off background
+#else
+static const float gridLineOffset = 0.0035f; // Offset for lifting grid lines off background
+#endif
// Default light position. To have shadows working correctly, light should be as far as camera, or a bit further
// 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);
@@ -53,12 +57,18 @@ static const QVector3D cameraDistanceVector = QVector3D(0.0f, 0.0f, cameraDistan
static const QQuaternion identityQuaternion;
// Skip color == selection texture's background color
-static const QVector3D selectionSkipColor = QVector3D(255.0f, 255.0f, 255.0f);
-static const QVector3D invalidColorVector = QVector3D(-1.0f, -1.0f, -1.0f);
+static const QVector4D selectionSkipColor = QVector4D(255.0f, 255.0f, 255.0f, 255.0f);
+static const QVector4D invalidColorVector = QVector4D(-1.0f, -1.0f, -1.0f, -1.0f);
+static const GLfloat itemAlpha = 0.0f;
+static const GLfloat customItemAlpha = 252.0f;
+static const GLfloat labelValueAlpha = 253.0f;
+static const GLfloat labelRowAlpha = 254.0f;
+static const GLfloat labelColumnAlpha = 255.0f;
static const GLfloat gradientTextureHeight = 1024.0f;
static const GLfloat gradientTextureWidth = 2.0f;
static const GLfloat uniformTextureHeight = 64.0f;
static const GLfloat uniformTextureWidth = 2.0f;
+static const GLfloat labelMargin = 0.05f;
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/global/qdatavisualizationglobal.h b/src/datavisualization/global/qdatavisualizationglobal.h
index 186db94c..e84d03c0 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.0.0"
+#define QT_DATAVISUALIZATION_VERSION_STR "1.1.0"
/*
QT_DATAVISUALIZATION_VERSION is (major << 16) + (minor << 8) + patch.
*/
-#define QT_DATAVISUALIZATION_VERSION 0x010000
+#define QT_DATAVISUALIZATION_VERSION 0x010100
/*
can be used like #if (QT_DATAVISUALIZATION_VERSION >= QT_DATAVISUALIZATION_VERSION_CHECK(1, 0, 0))
*/
diff --git a/src/datavisualization/input/q3dinputhandler.cpp b/src/datavisualization/input/q3dinputhandler.cpp
index 1a197418..f0044096 100644
--- a/src/datavisualization/input/q3dinputhandler.cpp
+++ b/src/datavisualization/input/q3dinputhandler.cpp
@@ -18,7 +18,6 @@
#include "datavisualizationglobal_p.h"
#include "q3dinputhandler_p.h"
-#include "q3dcamera_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -37,7 +36,7 @@ const float rotationSpeed = 100.0f;
* \class Q3DInputHandler
* \inmodule QtDataVisualization
* \brief Basic wheel mouse based input handler.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* Q3DInputHandler is the basic input handler for wheel mouse type of input devices.
*
@@ -88,19 +87,18 @@ Q3DInputHandler::~Q3DInputHandler()
*/
void Q3DInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos)
{
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
+#if defined(Q_OS_IOS)
Q_UNUSED(event);
Q_UNUSED(mousePos);
#else
if (Qt::LeftButton == event->button()) {
if (scene()->isSlicingActive()) {
- if (scene()->isPointInPrimarySubView(mousePos)) {
+ if (scene()->isPointInPrimarySubView(mousePos))
setInputView(InputViewOnPrimary);
- } else if (scene()->isPointInSecondarySubView(mousePos)) {
+ else if (scene()->isPointInSecondarySubView(mousePos))
setInputView(InputViewOnSecondary);
- } else {
+ else
setInputView(InputViewNone);
- }
} else {
// update mouse positions to prevent jumping when releasing or repressing a button
setInputPosition(mousePos);
@@ -128,7 +126,7 @@ void Q3DInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos
void Q3DInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos)
{
Q_UNUSED(event);
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
+#if defined(Q_OS_IOS)
Q_UNUSED(mousePos);
#else
if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState) {
@@ -147,7 +145,7 @@ void Q3DInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mouseP
void Q3DInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
{
Q_UNUSED(event);
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
+#if defined(Q_OS_IOS)
Q_UNUSED(mousePos);
#else
if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState) {
diff --git a/src/datavisualization/input/qabstract3dinputhandler.cpp b/src/datavisualization/input/qabstract3dinputhandler.cpp
index 9e1e314c..5109631f 100644
--- a/src/datavisualization/input/qabstract3dinputhandler.cpp
+++ b/src/datavisualization/input/qabstract3dinputhandler.cpp
@@ -17,7 +17,6 @@
****************************************************************************/
#include "qabstract3dinputhandler_p.h"
-#include "q3dscene.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -25,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class QAbstract3DInputHandler
* \inmodule QtDataVisualization
* \brief The base class for implementations of input handlers.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QAbstract3DInputHandler is the base class that is subclassed by different input handling implementations
* that take input events and translate those to camera and light movements. Input handlers also translate
@@ -56,7 +55,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* This type is uncreatable.
*
- * For AbstractInputHandler3D enums, see \l QAbstract3DInputHandler::InputView
+ * For AbstractInputHandler3D enums, see \l{QAbstract3DInputHandler::InputView}.
*/
/*!
@@ -231,7 +230,8 @@ QAbstract3DInputHandlerPrivate::QAbstract3DInputHandlerPrivate(QAbstract3DInputH
m_previousInputPos(QPoint(0,0)),
m_inputView(QAbstract3DInputHandler::InputViewNone),
m_inputPosition(QPoint(0,0)),
- m_scene(0)
+ m_scene(0),
+ m_isDefaultHandler(false)
{
}
diff --git a/src/datavisualization/input/qabstract3dinputhandler_p.h b/src/datavisualization/input/qabstract3dinputhandler_p.h
index fa5a2315..b4b2eda2 100644
--- a/src/datavisualization/input/qabstract3dinputhandler_p.h
+++ b/src/datavisualization/input/qabstract3dinputhandler_p.h
@@ -55,9 +55,6 @@ public:
int m_prevDistance;
QPoint m_previousInputPos;
- GLfloat m_defaultXRotation;
- GLfloat m_defaultYRotation;
-
private:
QAbstract3DInputHandler::InputView m_inputView;
QPoint m_inputPosition;
diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp
index d40fbf5a..30f31d4a 100644
--- a/src/datavisualization/input/qtouch3dinputhandler.cpp
+++ b/src/datavisualization/input/qtouch3dinputhandler.cpp
@@ -17,7 +17,6 @@
****************************************************************************/
#include "qtouch3dinputhandler_p.h"
-#include "q3dcamera_p.h"
#include <QtCore/QTimer>
#include <QtCore/qmath.h>
@@ -39,7 +38,7 @@ const int maxZoomLevel = 500;
* \class QTouch3DInputHandler
* \inmodule QtDataVisualization
* \brief Basic touch display based input handler.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* QTouch3DInputHandler is the basic input handler for touch screen devices.
*
diff --git a/src/datavisualization/theme/q3dtheme.cpp b/src/datavisualization/theme/q3dtheme.cpp
index 83da96f8..eaed3c41 100644
--- a/src/datavisualization/theme/q3dtheme.cpp
+++ b/src/datavisualization/theme/q3dtheme.cpp
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \class Q3DTheme
* \inmodule QtDataVisualization
* \brief Q3DTheme class provides a visual style for graphs.
- * \since Qt Data Visualization 1.0
+ * \since QtDataVisualization 1.0
*
* Q3DTheme is used to specify visual properties that affect the whole graph. There are several
* built-in themes that can be used directly, or be modified freely. User can also create a theme
@@ -236,7 +236,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* \snippet doc_src_q3dtheme.cpp 6
*
- * For Theme3D enums, see \l Q3DTheme::ColorStyle and \l Q3DTheme::Theme
+ * For Theme3D enums, see \l Q3DTheme::ColorStyle and \l{Q3DTheme::Theme}.
*/
/*!
diff --git a/src/datavisualization/utils/abstractobjecthelper_p.h b/src/datavisualization/utils/abstractobjecthelper_p.h
index 3220b37d..c1618909 100644
--- a/src/datavisualization/utils/abstractobjecthelper_p.h
+++ b/src/datavisualization/utils/abstractobjecthelper_p.h
@@ -30,7 +30,6 @@
#define ABSTRACTOBJECTHELPER_H
#include "datavisualizationglobal_p.h"
-#include <QtGui/QOpenGLFunctions>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/camerahelper_p.h b/src/datavisualization/utils/camerahelper_p.h
index f410ceb5..89dd48f4 100644
--- a/src/datavisualization/utils/camerahelper_p.h
+++ b/src/datavisualization/utils/camerahelper_p.h
@@ -33,9 +33,7 @@
#include "q3dcamera.h"
class QMatrix4x4;
-class QVector3D;
class QPoint;
-class QPointF;
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/objecthelper.cpp b/src/datavisualization/utils/objecthelper.cpp
index 97695193..a66e0f7e 100644
--- a/src/datavisualization/utils/objecthelper.cpp
+++ b/src/datavisualization/utils/objecthelper.cpp
@@ -19,7 +19,6 @@
#include "meshloader_p.h"
#include "vertexindexer_p.h"
#include "objecthelper_p.h"
-#include "abstractobjecthelper_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -27,15 +26,87 @@ ObjectHelper::ObjectHelper(const QString &objectFile)
: m_objectFile(objectFile)
{
m_indicesType = GL_UNSIGNED_SHORT;
+ load();
}
+struct ObjectHelperRef {
+ int refCount;
+ ObjectHelper *obj;
+};
+
+// The "Abstract3DRenderer *" key identifies the renderer
+static QHash<const Abstract3DRenderer *, QHash<QString, ObjectHelperRef *> *> cacheTable;
+
ObjectHelper::~ObjectHelper()
{
}
-void ObjectHelper::setObjectFile(const QString &objectFile)
+void ObjectHelper::resetObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj,
+ const QString &meshFile)
+{
+ Q_ASSERT(cacheId);
+
+ if (obj) {
+ const QString &oldFile = obj->objectFile();
+ if (meshFile == oldFile)
+ return; // same file, do nothing
+ releaseObjectHelper(cacheId, obj);
+ }
+ obj = getObjectHelper(cacheId, meshFile);
+}
+
+void ObjectHelper::releaseObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj)
+{
+ Q_ASSERT(cacheId);
+
+ if (obj) {
+ QHash<QString, ObjectHelperRef *> *objectTable = cacheTable.value(cacheId, 0);
+ if (objectTable) {
+ // Delete object if last reference is released
+ ObjectHelperRef *objRef = objectTable->value(obj->m_objectFile, 0);
+ if (objRef) {
+ objRef->refCount--;
+ if (objRef->refCount <= 0) {
+ objectTable->remove(obj->m_objectFile);
+ delete objRef->obj;
+ delete objRef;
+ }
+ }
+ if (objectTable->isEmpty()) {
+ // Remove the entire cache if last object was removed
+ cacheTable.remove(cacheId);
+ delete objectTable;
+ }
+ } else {
+ // Just delete the object if unknown cache
+ delete obj;
+ }
+ obj = 0;
+ }
+}
+
+ObjectHelper *ObjectHelper::getObjectHelper(const Abstract3DRenderer *cacheId,
+ const QString &objectFile)
{
- m_objectFile = objectFile;
+ if (objectFile.isEmpty())
+ return 0;
+
+ QHash<QString, ObjectHelperRef *> *objectTable = cacheTable.value(cacheId, 0);
+ if (!objectTable) {
+ objectTable = new QHash<QString, ObjectHelperRef *>;
+ cacheTable.insert(cacheId, objectTable);
+ }
+
+ // Check if object helper for this mesh already exists
+ ObjectHelperRef *objRef = objectTable->value(objectFile, 0);
+ if (!objRef) {
+ objRef = new ObjectHelperRef;
+ objRef->refCount = 0;
+ objRef->obj = new ObjectHelper(objectFile);
+ objectTable->insert(objectFile, objRef);
+ }
+ objRef->refCount++;
+ return objRef->obj;
}
void ObjectHelper::load()
@@ -47,6 +118,10 @@ void ObjectHelper::load()
glDeleteBuffers(1, &m_uvbuffer);
glDeleteBuffers(1, &m_normalbuffer);
glDeleteBuffers(1, &m_elementbuffer);
+ m_indices.clear();
+ m_indexedVertices.clear();
+ m_indexedUVs.clear();
+ m_indexedNormals.clear();
}
QVector<QVector3D> vertices;
QVector<QVector2D> uvs;
@@ -56,36 +131,32 @@ void ObjectHelper::load()
qFatal("loading failed");
// Index vertices
- QVector<unsigned short> indices;
- QVector<QVector3D> indexed_vertices;
- QVector<QVector2D> indexed_uvs;
- QVector<QVector3D> indexed_normals;
- VertexIndexer::indexVBO(vertices, uvs, normals, indices, indexed_vertices, indexed_uvs,
- indexed_normals);
+ VertexIndexer::indexVBO(vertices, uvs, normals, m_indices, m_indexedVertices, m_indexedUVs,
+ m_indexedNormals);
- m_indexCount = indices.size();
+ m_indexCount = m_indices.size();
glGenBuffers(1, &m_vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
- glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(QVector3D),
- &indexed_vertices.at(0),
+ glBufferData(GL_ARRAY_BUFFER, m_indexedVertices.size() * sizeof(QVector3D),
+ &m_indexedVertices.at(0),
GL_STATIC_DRAW);
glGenBuffers(1, &m_normalbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_normalbuffer);
- glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(QVector3D),
- &indexed_normals.at(0),
+ glBufferData(GL_ARRAY_BUFFER, m_indexedNormals.size() * sizeof(QVector3D),
+ &m_indexedNormals.at(0),
GL_STATIC_DRAW);
glGenBuffers(1, &m_uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
- glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(QVector2D),
- &indexed_uvs.at(0), GL_STATIC_DRAW);
+ glBufferData(GL_ARRAY_BUFFER, m_indexedUVs.size() * sizeof(QVector2D),
+ &m_indexedUVs.at(0), GL_STATIC_DRAW);
glGenBuffers(1, &m_elementbuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short),
- &indices.at(0), GL_STATIC_DRAW);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(unsigned short),
+ &m_indices.at(0), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
diff --git a/src/datavisualization/utils/objecthelper_p.h b/src/datavisualization/utils/objecthelper_p.h
index 0260dd05..c84f53bd 100644
--- a/src/datavisualization/utils/objecthelper_p.h
+++ b/src/datavisualization/utils/objecthelper_p.h
@@ -31,22 +31,38 @@
#include "datavisualizationglobal_p.h"
#include "abstractobjecthelper_p.h"
-#include <QtGui/QOpenGLFunctions>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+class Abstract3DRenderer;
+
class ObjectHelper : public AbstractObjectHelper
{
+private:
+ ObjectHelper(const QString &objectFile);
public:
- ObjectHelper(const QString &objectFile = QString());
~ObjectHelper();
- void setObjectFile(const QString &objectFile);
+ static void resetObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj,
+ const QString &meshFile);
+ static void releaseObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj);
+ inline const QString &objectFile() { return m_objectFile; }
- void load();
+ inline const QVector<unsigned short> &indices() const { return m_indices; }
+ inline const QVector<QVector3D> &indexedvertices() const { return m_indexedVertices; }
+ inline const QVector<QVector2D> &indexedUVs() const { return m_indexedUVs; }
+ inline const QVector<QVector3D> &indexedNormals() const { return m_indexedNormals; }
private:
+ static ObjectHelper *getObjectHelper(const Abstract3DRenderer *cacheId,
+ const QString &objectFile);
+ void load();
+
QString m_objectFile;
+ QVector<unsigned short> m_indices;
+ QVector<QVector3D> m_indexedVertices;
+ QVector<QVector2D> m_indexedUVs;
+ QVector<QVector3D> m_indexedNormals;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp
new file mode 100644
index 00000000..123588f1
--- /dev/null
+++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** 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 "scatterobjectbufferhelper_p.h"
+#include "objecthelper_p.h"
+#include <QtGui/QVector2D>
+#include <QtGui/QMatrix4x4>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+const GLfloat itemScaler = 3.0f;
+
+ScatterObjectBufferHelper::ScatterObjectBufferHelper()
+{
+ m_indicesType = GL_UNSIGNED_INT;
+}
+
+ScatterObjectBufferHelper::~ScatterObjectBufferHelper()
+{
+}
+
+void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale)
+{
+ initializeOpenGLFunctions();
+
+ ObjectHelper *dotObj = cache->object();
+ ScatterRenderItemArray &renderArray = cache->renderArray();
+ const uint renderArraySize = renderArray.size();
+ uint itemCount = renderArraySize;
+ 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);
+ }
+
+ // 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();
+
+ float itemSize = cache->itemSize() / itemScaler;
+ if (itemSize == 0.0f)
+ itemSize = dotScale;
+ QVector3D modelScaler(itemSize, itemSize, itemSize);
+ QMatrix4x4 modelMatrix;
+ if (!seriesRotation.isIdentity()) {
+ QMatrix4x4 matrix;
+ matrix.rotate(seriesRotation);
+ modelMatrix = matrix.transposed();
+ }
+ modelMatrix.scale(modelScaler);
+
+ QVector<QVector3D> scaled_vertices;
+ scaled_vertices.resize(verticeCount);
+ for (int i = 0; i < verticeCount; i++)
+ scaled_vertices[i] = indexed_vertices[i] * modelMatrix;
+
+ QVector<GLuint> buffered_indices;
+ QVector<QVector3D> buffered_vertices;
+ QVector<QVector2D> buffered_uvs;
+ QVector<QVector3D> buffered_normals;
+
+ buffered_indices.resize(indicesCount * renderArraySize);
+ buffered_vertices.resize(verticeCount * renderArraySize);
+ buffered_uvs.resize(uvsCount * renderArraySize);
+ buffered_normals.resize(normalsCount * renderArraySize);
+ uint pos = 0;
+ for (uint i = 0; i < renderArraySize; i++) {
+ ScatterRenderItem &item = renderArray[i];
+ if (!item.isVisible()) {
+ itemCount--;
+ continue;
+ }
+
+ int offset = pos * verticeCount;
+ if (item.rotation().isIdentity()) {
+ for (int j = 0; j < verticeCount; j++)
+ buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
+ } else {
+ QMatrix4x4 matrix;
+ matrix.rotate(seriesRotation * item.rotation());
+ modelMatrix = matrix.transposed();
+ modelMatrix.scale(modelScaler);
+
+ for (int j = 0; j < verticeCount; j++)
+ buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
+ + item.translation();
+ }
+
+ 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];
+
+ int offsetVertice = i * verticeCount;
+ offset = pos * indicesCount;
+ for (int j = 0; j < indicesCount; j++) {
+ buffered_indices[j + offset] = GLuint(indices[j] + offsetVertice);
+ }
+
+ pos++;
+ }
+
+ m_indexCount = indicesCount * itemCount;
+
+ glGenBuffers(1, &m_vertexbuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
+ glBufferData(GL_ARRAY_BUFFER, verticeCount * itemCount * sizeof(QVector3D),
+ &buffered_vertices.at(0),
+ GL_STATIC_DRAW);
+
+ glGenBuffers(1, &m_normalbuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_normalbuffer);
+ glBufferData(GL_ARRAY_BUFFER, normalsCount * itemCount * sizeof(QVector3D),
+ &buffered_normals.at(0),
+ GL_STATIC_DRAW);
+
+ glGenBuffers(1, &m_uvbuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
+ glBufferData(GL_ARRAY_BUFFER, uvsCount * itemCount * sizeof(QVector2D),
+ &buffered_uvs.at(0), GL_STATIC_DRAW);
+
+ glGenBuffers(1, &m_elementbuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount * itemCount * sizeof(GLint),
+ &buffered_indices.at(0), GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ m_meshDataLoaded = true;
+}
+
+void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale)
+{
+ initializeOpenGLFunctions();
+
+ ObjectHelper *dotObj = cache->object();
+ ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int renderArraySize = renderArray.size();
+ QQuaternion seriesRotation(cache->meshRotation());
+
+ // Index vertices
+ const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
+ int verticeCount = indexed_vertices.count();
+
+ float itemSize = cache->itemSize() / itemScaler;
+ if (itemSize == 0.0f)
+ itemSize = dotScale;
+ QVector3D modelScaler(itemSize, itemSize, itemSize);
+ QMatrix4x4 modelMatrix;
+ if (!seriesRotation.isIdentity()) {
+ QMatrix4x4 matrix;
+ matrix.rotate(seriesRotation);
+ modelMatrix = matrix.transposed();
+ }
+ modelMatrix.scale(modelScaler);
+
+ QVector<QVector3D> scaled_vertices;
+ scaled_vertices.resize(verticeCount);
+ for (int i = 0; i < verticeCount; i++)
+ scaled_vertices[i] = indexed_vertices[i] * modelMatrix;
+
+ QVector<QVector3D> buffered_vertices;
+
+ buffered_vertices.resize(verticeCount * renderArraySize);
+ for (int i = 0; i < renderArraySize; i++) {
+ ScatterRenderItem &item = renderArray[i];
+ if (!item.isVisible())
+ continue;
+
+ const int offset = i * verticeCount;
+ if (item.rotation().isIdentity()) {
+ for (int j = 0; j < verticeCount; j++)
+ buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
+ } else {
+ QMatrix4x4 matrix;
+ matrix.rotate(seriesRotation * item.rotation());
+ modelMatrix = matrix.transposed();
+ modelMatrix.scale(modelScaler);
+
+ for (int j = 0; j < verticeCount; j++)
+ buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
+ + item.translation();
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
+ glBufferData(GL_ARRAY_BUFFER, buffered_vertices.size() * sizeof(QVector3D),
+ &buffered_vertices.at(0),
+ GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ m_meshDataLoaded = true;
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/scatterobjectbufferhelper_p.h b/src/datavisualization/utils/scatterobjectbufferhelper_p.h
new file mode 100644
index 00000000..952c3d7d
--- /dev/null
+++ b/src/datavisualization/utils/scatterobjectbufferhelper_p.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 SCATTEROBJECTBUFFERHELPER_P_H
+#define SCATTEROBJECTBUFFERHELPER_P_H
+
+#include "datavisualizationglobal_p.h"
+#include "abstractobjecthelper_p.h"
+#include "scatterseriesrendercache_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class ScatterObjectBufferHelper : public AbstractObjectHelper
+{
+public:
+ ScatterObjectBufferHelper();
+ ~ScatterObjectBufferHelper();
+
+ void fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale);
+ void update(ScatterSeriesRenderCache *cache, qreal dotScale);
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp
new file mode 100644
index 00000000..0f290aeb
--- /dev/null
+++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** 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 "scatterpointbufferhelper_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+const GLfloat itemScaler = 3.0f;
+const QVector3D hiddenPos(-1000.0f, -1000.0f, -1000.0f);
+
+ScatterPointBufferHelper::ScatterPointBufferHelper()
+ : m_pointbuffer(0),
+ m_oldRemoveIndex(0),
+ m_oldRemove(false)
+{
+ m_indicesType = GL_UNSIGNED_INT;
+}
+
+ScatterPointBufferHelper::~ScatterPointBufferHelper()
+{
+ if (QOpenGLContext::currentContext())
+ glDeleteBuffers(1, &m_pointbuffer);
+}
+
+GLuint ScatterPointBufferHelper::pointBuf()
+{
+ if (!m_meshDataLoaded)
+ qFatal("No loaded object");
+ return m_pointbuffer;
+}
+
+void ScatterPointBufferHelper::pushPoint(uint pointIndex)
+{
+ glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
+
+ if (m_oldRemove && m_oldRemoveIndex < pointIndex) {
+ glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D),
+ sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex));
+ }
+
+ glBufferSubData(GL_ARRAY_BUFFER, pointIndex * sizeof(QVector3D),
+ sizeof(QVector3D),
+ &hiddenPos);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ m_oldRemoveIndex = pointIndex;
+ m_oldRemove = true;
+}
+
+void ScatterPointBufferHelper::popPoint()
+{
+ if (m_oldRemove) {
+ 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;
+}
+
+void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache)
+{
+ initializeOpenGLFunctions();
+
+ ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int renderArraySize = renderArray.size();
+
+ if (m_meshDataLoaded) {
+ // Delete old data
+ glDeleteBuffers(1, &m_pointbuffer);
+ m_bufferedPoints.clear();
+ }
+
+ m_bufferedPoints.resize(renderArraySize);
+ for (int i = 0; i < renderArraySize; i++) {
+ ScatterRenderItem &item = renderArray[i];
+ if (!item.isVisible())
+ m_bufferedPoints[i] = hiddenPos;
+ else
+ m_bufferedPoints[i] = item.translation();
+ }
+
+ m_indexCount = renderArraySize;
+
+ 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);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ m_meshDataLoaded = true;
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/scatterpointbufferhelper_p.h b/src/datavisualization/utils/scatterpointbufferhelper_p.h
new file mode 100644
index 00000000..b3adcfa8
--- /dev/null
+++ b/src/datavisualization/utils/scatterpointbufferhelper_p.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 SCATTERPOINTBUFFERHELPER_P_H
+#define SCATTERPOINTBUFFERHELPER_P_H
+
+#include "datavisualizationglobal_p.h"
+#include "abstractobjecthelper_p.h"
+#include "scatterseriesrendercache_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class ScatterPointBufferHelper : public AbstractObjectHelper
+{
+public:
+ ScatterPointBufferHelper();
+ ~ScatterPointBufferHelper();
+
+ GLuint pointBuf();
+
+ void pushPoint(uint pointIndex);
+ void popPoint();
+ void load(ScatterSeriesRenderCache *cache);
+
+public:
+ GLuint m_pointbuffer;
+
+private:
+ QVector<QVector3D> m_bufferedPoints;
+ uint m_oldRemoveIndex;
+ bool m_oldRemove;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h
index ced27572..fdef0dff 100644
--- a/src/datavisualization/utils/shaderhelper_p.h
+++ b/src/datavisualization/utils/shaderhelper_p.h
@@ -30,7 +30,6 @@
#define SHADERHELPER_P_H
#include "datavisualizationglobal_p.h"
-#include <QtGui/QOpenGLFunctions>
class QOpenGLShaderProgram;
diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp
index 9bcdfee2..d999ba90 100644
--- a/src/datavisualization/utils/surfaceobject.cpp
+++ b/src/datavisualization/utils/surfaceobject.cpp
@@ -17,17 +17,21 @@
****************************************************************************/
#include "surfaceobject_p.h"
-#include "abstractobjecthelper_p.h"
+#include "surface3drenderer_p.h"
#include <QtGui/QVector2D>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-SurfaceObject::SurfaceObject()
+SurfaceObject::SurfaceObject(Surface3DRenderer *renderer)
: m_surfaceType(Undefined),
m_columns(0),
m_rows(0),
- m_gridIndexCount(0)
+ m_gridIndexCount(0),
+ m_axisCacheX(renderer->m_axisCacheX),
+ m_axisCacheY(renderer->m_axisCacheY),
+ m_axisCacheZ(renderer->m_axisCacheZ)
+
{
m_indicesType = GL_UNSIGNED_INT;
initializeOpenGLFunctions();
@@ -45,16 +49,11 @@ SurfaceObject::~SurfaceObject()
}
void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space,
- GLfloat yRange, GLfloat yMin, bool changeGeometry)
+ bool changeGeometry, bool flipXZ)
{
m_columns = space.width();
m_rows = space.height();
int totalSize = m_rows * m_columns;
- GLfloat xMin = dataArray.at(0)->at(0).x();
- GLfloat zMin = dataArray.at(0)->at(0).z();
- GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f;
- GLfloat yNormalizer = yRange / 2.0f;
- GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f;
GLfloat uvX = 1.0f / GLfloat(m_columns - 1);
GLfloat uvY = 1.0f / GLfloat(m_rows - 1);
@@ -68,21 +67,31 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
if (changeGeometry)
uvs.resize(totalSize);
int totalIndex = 0;
+
+ AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX;
+ AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ;
+
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 = ((data.x() - xMin) / xNormalizer);
- float normalizedY = ((data.y() - yMin) / yNormalizer);
- float normalizedZ = ((data.z() - zMin) / zNormalizer);
- m_vertices[totalIndex] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f,
- normalizedZ + 1.0f);
+ 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);
if (changeGeometry)
uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY);
totalIndex++;
}
}
+ if (flipXZ) {
+ for (int i = 0; i < m_vertices.size(); i++) {
+ m_vertices[i].setX(-m_vertices.at(i).x());
+ m_vertices[i].setZ(-m_vertices.at(i).z());
+ }
+ }
+
// Create normals
int rowLimit = m_rows - 1;
int colLimit = m_columns - 1;
@@ -92,25 +101,30 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
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));
+ 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));
+ 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));
+ m_vertices.at(j + 1),
+ flipNormal);
}
m_normals[totalIndex++] = normal(m_vertices.at(totalLimit),
m_vertices.at(totalLimit - 1),
- m_vertices.at(totalLimit - m_columns));
+ m_vertices.at(totalLimit - m_columns),
+ flipNormal);
// Create indices table
if (changeGeometry)
@@ -123,24 +137,18 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
createBuffers(m_vertices, uvs, m_normals, 0, changeGeometry);
}
-void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex,
- GLfloat yRange, GLfloat yMin)
+void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex)
{
- GLfloat xMin = dataArray.at(0)->at(0).x();
- GLfloat zMin = dataArray.at(0)->at(0).z();
- GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f;
- GLfloat yNormalizer = yRange / 2.0f;
- GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f;
-
// 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 = ((data.x() - xMin) / xNormalizer);
- float normalizedY = ((data.y() - yMin) / yNormalizer);
- float normalizedZ = ((data.z() - zMin) / zNormalizer);
- m_vertices[p++] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f, normalizedZ + 1.0f);
+ 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);
}
// Create normals
@@ -153,18 +161,21 @@ void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowI
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));
+ 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));
+ m_vertices.at(p - 1),
+ flipNormal);
}
if (rowIndex == m_rows - 1) {
// Top most line, nothing above, must have different handling.
@@ -173,32 +184,26 @@ void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowI
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));
+ 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));
+ m_vertices.at(rowLimit - m_columns),
+ flipNormal);
}
}
-void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row,
- int column, GLfloat yRange, GLfloat yMin)
+void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column)
{
- GLfloat xMin = dataArray.at(0)->at(0).x();
- GLfloat zMin = dataArray.at(0)->at(0).z();
- GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f;
- GLfloat yNormalizer = yRange / 2.0f;
- GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f;
-
// Update a vertice
const QSurfaceDataItem &data = dataArray.at(row)->at(column);
- float normalizedX = ((data.x() - xMin) / xNormalizer);
- float normalizedY = ((data.y() - yMin) / yNormalizer);
- float normalizedZ = ((data.z() - zMin) / zNormalizer);
- m_vertices[row * m_columns + column] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f,
- normalizedZ + 1.0f);
+ 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);
// Create normals
int startRow = row;
@@ -210,6 +215,7 @@ void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row
int rightCol = m_columns - 1;
int topRow = m_rows - 1;
+ const bool flipNormal = checkFlipNormal(dataArray);
for (int i = startRow; i <= row; i++) {
for (int j = startCol; j <= column; j++) {
int p = i * m_columns + j;
@@ -218,12 +224,14 @@ void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row
// One right and one up
m_normals[p] = normal(m_vertices.at(p),
m_vertices.at(p + 1),
- m_vertices.at(p + m_columns));
+ 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));
+ m_vertices.at(p - 1),
+ flipNormal);
}
} else {
// Top most line, nothing above, must have different handling.
@@ -231,12 +239,14 @@ void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row
// 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));
+ 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));
+ m_vertices.at(p - m_columns),
+ flipNormal);
}
}
}
@@ -321,16 +331,11 @@ void SurfaceObject::createSmoothGridlineIndices(int x, int y, int endX, int endY
}
void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &space,
- GLfloat yRange, GLfloat yMin, bool changeGeometry)
+ bool changeGeometry, bool flipXZ)
{
m_columns = space.width();
m_rows = space.height();
int totalSize = m_rows * m_columns * 2;
- GLfloat xMin = dataArray.at(0)->at(0).x();
- GLfloat zMin = dataArray.at(0)->at(0).z();
- GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f;
- GLfloat yNormalizer = yRange / 2.0f;
- GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f;
GLfloat uvX = 1.0f / GLfloat(m_columns - 1);
GLfloat uvY = 1.0f / GLfloat(m_rows - 1);
@@ -350,15 +355,17 @@ 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;
+
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 = ((data.x() - xMin) / xNormalizer);
- float normalizedY = ((data.y() - yMin) / yNormalizer);
- float normalizedZ = ((data.z() - zMin) / zNormalizer);
- m_vertices[totalIndex] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f,
- normalizedZ + 1.0f);
+ 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);
if (changeGeometry)
uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY);
@@ -373,6 +380,13 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
}
}
+ if (flipXZ) {
+ for (int i = 0; i < m_vertices.size(); i++) {
+ m_vertices[i].setX(-m_vertices.at(i).x());
+ m_vertices[i].setZ(-m_vertices.at(i).z());
+ }
+ }
+
// Create normals & indices table
GLint *indices = 0;
int p = 0;
@@ -384,6 +398,7 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
}
totalIndex = 0;
+ const bool flipNormal = checkFlipNormal(dataArray);
for (int row = 0, upperRow = doubleColumns;
row < rowColLimit;
row += doubleColumns, upperRow += doubleColumns) {
@@ -391,12 +406,14 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
// 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));
+ 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));
+ m_vertices.at(upperRow + j),
+ flipNormal);
if (changeGeometry) {
// Left triangle
@@ -421,26 +438,20 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
delete[] indices;
}
-void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex,
- GLfloat yRange, GLfloat yMin)
+void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex)
{
- GLfloat xMin = dataArray.at(0)->at(0).x();
- GLfloat zMin = dataArray.at(0)->at(0).z();
- GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f;
- GLfloat yNormalizer = yRange / 2.0f;
- GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f;
-
int colLimit = m_columns - 1;
int doubleColumns = m_columns * 2 - 2;
int p = rowIndex * doubleColumns;
const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex);
+
for (int j = 0; j < m_columns; j++) {
const QSurfaceDataItem &data = dataRow.at(j);
- float normalizedX = ((data.x() - xMin) / xNormalizer);
- float normalizedY = ((data.y() - yMin) / yNormalizer);
- float normalizedZ = ((data.z() - zMin) / zNormalizer);
- m_vertices[p++] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f, normalizedZ + 1.0f);
+ 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);
if (j > 0 && j < colLimit) {
m_vertices[p] = m_vertices[p - 1];
@@ -455,6 +466,7 @@ 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) {
@@ -462,35 +474,30 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI
// 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));
+ 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));
+ m_vertices.at(upperRow + j),
+ flipNormal);
}
}
}
-void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row,
- int column, GLfloat yRange, GLfloat yMin)
+void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column)
{
- GLfloat xMin = dataArray.at(0)->at(0).x();
- GLfloat zMin = dataArray.at(0)->at(0).z();
- GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f;
- GLfloat yNormalizer = yRange / 2.0f;
- GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f;
-
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 = ((data.x() - xMin) / xNormalizer);
- float normalizedY = ((data.y() - yMin) / yNormalizer);
- float normalizedZ = ((data.z() - zMin) / zNormalizer);
- m_vertices[p] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f, normalizedZ + 1.0f);
+ 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++;
if (column > 0 && column < colLimit)
@@ -508,19 +515,22 @@ 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));
+ 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));
+ m_vertices.at(p + doubleColumns - 1),
+ flipNormal);
}
}
}
@@ -656,6 +666,13 @@ void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVec
m_meshDataLoaded = true;
}
+bool SurfaceObject::checkFlipNormal(const QSurfaceDataArray &array)
+{
+ 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;
+}
+
GLuint SurfaceObject::gridElementBuf()
{
if (!m_meshDataLoaded)
@@ -686,13 +703,19 @@ void SurfaceObject::clear()
m_gridIndexCount = 0;
m_indexCount = 0;
m_surfaceType = Undefined;
+ m_vertices.clear();
+ m_normals.clear();
}
-QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c)
+QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c,
+ bool flipNormal)
{
QVector3D v1 = b - a;
QVector3D v2 = c - a;
- return QVector3D::crossProduct(v1, v2);
+ QVector3D normal = QVector3D::crossProduct(v1, v2);
+ if (flipNormal)
+ normal *= -1;
+ return normal;
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h
index 69cb7e5d..9c18dcb2 100644
--- a/src/datavisualization/utils/surfaceobject_p.h
+++ b/src/datavisualization/utils/surfaceobject_p.h
@@ -37,6 +37,9 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+class Surface3DRenderer;
+class AxisRenderCache;
+
class SurfaceObject : public AbstractObjectHelper
{
public:
@@ -47,21 +50,17 @@ public:
};
public:
- SurfaceObject();
+ SurfaceObject(Surface3DRenderer *renderer);
~SurfaceObject();
- void setUpData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange,
- GLfloat yMin, bool changeGeometry);
- void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange,
- GLfloat yMin, bool changeGeometry);
- void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex,
- GLfloat yRange, GLfloat yMin);
- void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow,
- GLfloat yRange, GLfloat yMin);
- void updateSmoothItem(const QSurfaceDataArray &dataArray, int row,
- int column, GLfloat yRange, GLfloat yMin);
- void updateCoarseItem(const QSurfaceDataArray &dataArray, int row,
- int column, GLfloat yRange, GLfloat yMin);
+ void setUpData(const QSurfaceDataArray &dataArray, const QRect &space,
+ bool changeGeometry, 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);
void createSmoothIndices(int x, int y, int endX, int endY);
void createCoarseIndices(int x, int y, int columns, int rows);
void createSmoothGridlineIndices(int x, int y, int endX, int endY);
@@ -73,10 +72,11 @@ public:
void clear();
private:
- QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c);
+ QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, bool flipNormal);
void createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs,
const QVector<QVector3D> &normals, const GLint *indices,
bool changeGeometry);
+ bool checkFlipNormal(const QSurfaceDataArray &array);
private:
SurfaceType m_surfaceType;
@@ -86,6 +86,10 @@ private:
GLuint m_gridIndexCount;
QVector<QVector3D> m_vertices;
QVector<QVector3D> m_normals;
+ // Caches are not owned
+ AxisRenderCache &m_axisCacheX;
+ AxisRenderCache &m_axisCacheY;
+ AxisRenderCache &m_axisCacheZ;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp
index 52c673dc..185d99e4 100644
--- a/src/datavisualization/utils/texturehelper.cpp
+++ b/src/datavisualization/utils/texturehelper.cpp
@@ -108,13 +108,8 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf
glBindTexture(GL_TEXTURE_2D, textureid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-#if !defined(QT_OPENGL_ES_2)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, NULL);
-#else
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB,
- GL_UNSIGNED_BYTE, NULL);
-#endif
glBindTexture(GL_TEXTURE_2D, 0);
// Create render buffer
@@ -198,7 +193,8 @@ GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint textureSize)
#endif
#if !defined(QT_OPENGL_ES_2)
-GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize)
+GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer,
+ GLuint textureSize)
{
GLuint depthtextureid = createDepthTexture(size, textureSize);
@@ -228,7 +224,8 @@ GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &f
#endif
#if !defined(QT_OPENGL_ES_2)
-void TextureHelper::fillDepthTexture(GLuint texture,const QSize &size, GLuint textureSize, GLfloat value)
+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];
@@ -244,9 +241,12 @@ void TextureHelper::fillDepthTexture(GLuint texture,const QSize &size, GLuint te
}
#endif
-void TextureHelper::deleteTexture(const GLuint *texture)
+void TextureHelper::deleteTexture(GLuint *texture)
{
- glDeleteTextures(1, texture);
+ if (texture && *texture) {
+ glDeleteTextures(1, texture);
+ *texture = 0;
+ }
}
QImage TextureHelper::convertToGLFormat(const QImage &srcImage)
diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h
index ebfaa042..aec137a4 100644
--- a/src/datavisualization/utils/texturehelper_p.h
+++ b/src/datavisualization/utils/texturehelper_p.h
@@ -30,7 +30,6 @@
#define TEXTUREHELPER_P_H
#include "datavisualizationglobal_p.h"
-#include <QtGui/QOpenGLFunctions>
#include <QtGui/QRgb>
#include <QtGui/QLinearGradient>
@@ -56,7 +55,7 @@ class TextureHelper : protected QOpenGLFunctions
GLuint createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize);
void fillDepthTexture(GLuint texture, const QSize &size, GLuint textureSize, GLfloat value);
#endif
- void deleteTexture(const GLuint *texture);
+ void deleteTexture(GLuint *texture);
private:
QImage convertToGLFormat(const QImage &srcImage);
diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp
index e0b1370e..5852bf11 100644
--- a/src/datavisualization/utils/utils.cpp
+++ b/src/datavisualization/utils/utils.cpp
@@ -18,12 +18,7 @@
#include "utils_p.h"
-#include <QtGui/QColor>
#include <QtGui/QPainter>
-#include <QtCore/QPoint>
-#include <QtGui/QImage>
-#include <QtCore/QRegExp>
-#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -38,14 +33,21 @@ GLuint Utils::getNearestPowerOfTwo(GLuint value, GLuint &padding)
return powOfTwoValue;
}
-QVector3D Utils::vectorFromColor(const QColor &color)
+QVector4D Utils::vectorFromColor(const QColor &color)
{
- return QVector3D(color.redF(), color.greenF(), color.blueF());
+ return QVector4D(color.redF(), color.greenF(), color.blueF(), color.alphaF());
}
QColor Utils::colorFromVector(const QVector3D &colorVector)
{
- return QColor(colorVector.x() * 255.0f, colorVector.y() * 255.0f, colorVector.z() * 255.0f);
+ return QColor(colorVector.x() * 255.0f, colorVector.y() * 255.0f,
+ colorVector.z() * 255.0f, 255.0f);
+}
+
+QColor Utils::colorFromVector(const QVector4D &colorVector)
+{
+ return QColor(colorVector.x() * 255.0f, colorVector.y() * 255.0f,
+ colorVector.z() * 255.0f, colorVector.w() * 255.0f);
}
QImage Utils::printTextToImage(const QFont &font, const QString &text, const QColor &bgrColor,
@@ -126,18 +128,27 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo
return image;
}
-QVector3D Utils::getSelection(QPoint mousepos, int height)
+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, 0};
+ GLubyte pixel[4] = {255, 255, 255, 255};
glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
(void *)pixel);
- QVector3D selectedColor(pixel[0], pixel[1], pixel[2]);
-
+ QVector4D selectedColor(pixel[0], pixel[1], pixel[2], pixel[3]);
return selectedColor;
}
+QImage Utils::getGradientImage(const QLinearGradient &gradient)
+{
+ QImage image(QSize(1, 101), QImage::Format_RGB32);
+ QPainter pmp(&image);
+ pmp.setBrush(QBrush(gradient));
+ pmp.setPen(Qt::NoPen);
+ pmp.drawRect(0, 0, 1, 101);
+ return image;
+}
+
Utils::ParamType Utils::mapFormatCharToParamType(const QChar &formatChar)
{
ParamType retVal = ParamTypeUnknown;
@@ -177,7 +188,7 @@ Utils::ParamType Utils::findFormatParamType(const QString &format)
return ParamTypeUnknown;
}
-QString Utils::formatLabel(const QByteArray &format, ParamType paramType, float value)
+QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal value)
{
switch (paramType) {
case ParamTypeInt:
@@ -218,4 +229,13 @@ float Utils::wrapValue(float value, float min, float max)
return value;
}
+QQuaternion Utils::calculateRotation(const QVector3D &xyzRotations)
+{
+ QQuaternion rotQuatX = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xyzRotations.x());
+ QQuaternion rotQuatY = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, xyzRotations.y());
+ QQuaternion rotQuatZ = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, xyzRotations.z());
+ QQuaternion totalRotation = rotQuatY * rotQuatZ * rotQuatX;
+ return totalRotation;
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/utils.pri b/src/datavisualization/utils/utils.pri
index 8ce8794e..30bfb156 100644
--- a/src/datavisualization/utils/utils.pri
+++ b/src/datavisualization/utils/utils.pri
@@ -7,7 +7,9 @@ HEADERS += $$PWD/meshloader_p.h \
$$PWD/utils_p.h \
$$PWD/abstractobjecthelper_p.h \
$$PWD/surfaceobject_p.h \
- $$PWD/qutils.h
+ $$PWD/qutils.h \
+ $$PWD/scatterobjectbufferhelper_p.h \
+ $$PWD/scatterpointbufferhelper_p.h
SOURCES += $$PWD/meshloader.cpp \
$$PWD/vertexindexer.cpp \
@@ -17,4 +19,6 @@ SOURCES += $$PWD/meshloader.cpp \
$$PWD/texturehelper.cpp \
$$PWD/utils.cpp \
$$PWD/abstractobjecthelper.cpp \
- $$PWD/surfaceobject.cpp
+ $$PWD/surfaceobject.cpp \
+ $$PWD/scatterobjectbufferhelper.cpp \
+ $$PWD/scatterpointbufferhelper.cpp
diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h
index 7288419b..d7187c16 100644
--- a/src/datavisualization/utils/utils_p.h
+++ b/src/datavisualization/utils/utils_p.h
@@ -31,12 +31,7 @@
#include "datavisualizationglobal_p.h"
-class QVector3D;
-class QColor;
-class QPainter;
-class QString;
-class QPoint;
-class QImage;
+class QLinearGradient;
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -51,10 +46,9 @@ public:
};
static GLuint getNearestPowerOfTwo(GLuint value, GLuint &padding);
- static QVector3D vectorFromColor(const QColor &color);
+ static QVector4D vectorFromColor(const QColor &color);
static QColor colorFromVector(const QVector3D &colorVector);
- static void printText(QPainter *painter, const QString &text, const QSize &position,
- bool absoluteCoords = true, float rotation = 0.0f, float scale = 1.0f);
+ static QColor colorFromVector(const QVector4D &colorVector);
static QImage printTextToImage(const QFont &font,
const QString &text,
const QColor &bgrColor,
@@ -62,13 +56,15 @@ public:
bool labelBackground,
bool borders = false,
int maxLabelWidth = 0);
- static QVector3D getSelection(QPoint mousepos, int height);
+ static QVector4D getSelection(QPoint mousepos, int height);
+ static QImage getGradientImage(const QLinearGradient &gradient);
static ParamType findFormatParamType(const QString &format);
- static QString formatLabel(const QByteArray &format, ParamType paramType, float value);
+ static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value);
static QString defaultLabelFormat();
static float wrapValue(float value, float min, float max);
+ static QQuaternion calculateRotation(const QVector3D &xyzRotations);
private:
static ParamType mapFormatCharToParamType(const QChar &formatChar);
diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp
index 65e7c6c3..a6dee6eb 100644
--- a/src/datavisualizationqml2/abstractdeclarative.cpp
+++ b/src/datavisualizationqml2/abstractdeclarative.cpp
@@ -17,7 +17,6 @@
****************************************************************************/
#include "abstractdeclarative_p.h"
-#include "qvalue3daxis.h"
#include "declarativetheme_p.h"
#include "declarativerendernode_p.h"
@@ -37,18 +36,18 @@ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) :
m_controller(0),
m_contextWindow(0),
m_renderMode(RenderIndirect),
-#if defined(QT_OPENGL_ES_2)
+ #if defined(QT_OPENGL_ES_2)
m_samples(0),
-#else
+ #else
m_samples(4),
-#endif
+ #endif
m_windowSamples(0),
m_initialisedSize(0, 0),
-#ifdef USE_SHARED_CONTEXT
+ #ifdef USE_SHARED_CONTEXT
m_context(0),
-#else
+ #else
m_stateStore(0),
-#endif
+ #endif
m_qtContext(0),
m_mainThread(QThread::currentThread()),
m_contextThread(0)
@@ -171,7 +170,7 @@ Declarative3DScene* AbstractDeclarative::scene() const
void AbstractDeclarative::setTheme(Q3DTheme *theme)
{
- m_controller->setActiveTheme(theme);
+ m_controller->setActiveTheme(theme, isComponentComplete());
}
Q3DTheme *AbstractDeclarative::theme() const
@@ -211,6 +210,84 @@ bool AbstractDeclarative::shadowsSupported() const
return m_controller->shadowsSupported();
}
+int AbstractDeclarative::addCustomItem(QCustom3DItem *item)
+{
+ return m_controller->addCustomItem(item);
+}
+
+void AbstractDeclarative::removeCustomItems()
+{
+ m_controller->deleteCustomItems();
+}
+
+void AbstractDeclarative::removeCustomItem(QCustom3DItem *item)
+{
+ m_controller->deleteCustomItem(item);
+}
+
+void AbstractDeclarative::removeCustomItemAt(const QVector3D &position)
+{
+ m_controller->deleteCustomItem(position);
+}
+
+void AbstractDeclarative::releaseCustomItem(QCustom3DItem *item)
+{
+ return m_controller->releaseCustomItem(item);
+}
+
+int AbstractDeclarative::selectedLabelIndex() const
+{
+ return m_controller->selectedLabelIndex();
+}
+
+QAbstract3DAxis *AbstractDeclarative::selectedAxis() const
+{
+ return m_controller->selectedAxis();
+}
+
+int AbstractDeclarative::selectedCustomItemIndex() const
+{
+ return m_controller->selectedCustomItemIndex();
+}
+
+QCustom3DItem *AbstractDeclarative::selectedCustomItem() const
+{
+ return m_controller->selectedCustomItem();
+}
+
+QQmlListProperty<QCustom3DItem> AbstractDeclarative::customItemList()
+{
+ return QQmlListProperty<QCustom3DItem>(this, this,
+ &AbstractDeclarative::appendCustomItemFunc,
+ &AbstractDeclarative::countCustomItemFunc,
+ &AbstractDeclarative::atCustomItemFunc,
+ &AbstractDeclarative::clearCustomItemFunc);
+}
+
+void AbstractDeclarative::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
+ QCustom3DItem *item)
+{
+ AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data);
+ decl->addCustomItem(item);
+}
+
+int AbstractDeclarative::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
+{
+ return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.size();
+}
+
+QCustom3DItem *AbstractDeclarative::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
+ int index)
+{
+ return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.at(index);
+}
+
+void AbstractDeclarative::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
+{
+ AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data);
+ decl->removeCustomItems();
+}
+
void AbstractDeclarative::setSharedController(Abstract3DController *controller)
{
Q_ASSERT(controller);
@@ -230,6 +307,8 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller)
&AbstractDeclarative::themeChanged);
QObject::connect(m_controller.data(), &Abstract3DController::selectionModeChanged, this,
&AbstractDeclarative::handleSelectionModeChange);
+ QObject::connect(m_controller.data(), &Abstract3DController::elementSelected, this,
+ &AbstractDeclarative::selectedElementChanged);
QObject::connect(m_controller.data(), &Abstract3DController::axisXChanged, this,
&AbstractDeclarative::handleAxisXChanged);
@@ -237,6 +316,17 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller)
&AbstractDeclarative::handleAxisYChanged);
QObject::connect(m_controller.data(), &Abstract3DController::axisZChanged, this,
&AbstractDeclarative::handleAxisZChanged);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::measureFpsChanged, this,
+ &AbstractDeclarative::measureFpsChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::currentFpsChanged, this,
+ &AbstractDeclarative::currentFpsChanged);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::orthoProjectionChanged, this,
+ &AbstractDeclarative::orthoProjectionChanged);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::aspectRatioChanged, this,
+ &AbstractDeclarative::aspectRatioChanged);
}
void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window)
@@ -569,6 +659,58 @@ void AbstractDeclarative::checkWindowList(QQuickWindow *window)
}
}
+void AbstractDeclarative::setMeasureFps(bool enable)
+{
+ m_controller->setMeasureFps(enable);
+}
+
+bool AbstractDeclarative::measureFps() const
+{
+ return m_controller->measureFps();
+}
+
+qreal AbstractDeclarative::currentFps() const
+{
+ return m_controller->currentFps();
+}
+
+void AbstractDeclarative::setOrthoProjection(bool enable)
+{
+ m_controller->setOrthoProjection(enable);
+}
+
+bool AbstractDeclarative::isOrthoProjection() const
+{
+ return m_controller->isOrthoProjection();
+}
+
+AbstractDeclarative::ElementType AbstractDeclarative::selectedElement() const
+{
+ return ElementType(m_controller->selectedElement());
+}
+
+void AbstractDeclarative::setAspectRatio(qreal ratio)
+{
+ m_controller->setAspectRatio(float(ratio));
+}
+
+qreal AbstractDeclarative::aspectRatio() const
+{
+ return m_controller->aspectRatio();
+}
+
+void AbstractDeclarative::setOptimizationHints(OptimizationHints hints)
+{
+ int intmode = int(hints);
+ m_controller->setOptimizationHints(QAbstract3DGraph::OptimizationHints(intmode));
+}
+
+AbstractDeclarative::OptimizationHints AbstractDeclarative::optimizationHints() const
+{
+ int intmode = int(m_controller->optimizationHints());
+ return OptimizationHints(intmode);
+}
+
void AbstractDeclarative::windowDestroyed(QObject *obj)
{
// Remove destroyed window from window lists
diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h
index 87b108db..ebe8b49c 100644
--- a/src/datavisualizationqml2/abstractdeclarative_p.h
+++ b/src/datavisualizationqml2/abstractdeclarative_p.h
@@ -31,12 +31,9 @@
#include "datavisualizationglobal_p.h"
#include "abstract3dcontroller_p.h"
-#include "qabstract3dinputhandler.h"
#include "declarativescene_p.h"
-#include <QtCore/QAbstractItemModel>
#include <QtQuick/QQuickItem>
-#include <QtQuick/QQuickWindow>
#include <QtCore/QPointer>
#include <QtCore/QThread>
@@ -57,7 +54,9 @@ class AbstractDeclarative : public QQuickItem
Q_OBJECT
Q_ENUMS(ShadowQuality)
Q_ENUMS(RenderingMode)
+ Q_ENUMS(ElementType)
Q_FLAGS(SelectionFlag SelectionFlags)
+ Q_FLAGS(OptimizationHint OptimizationHints)
Q_PROPERTY(SelectionFlags selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged)
Q_PROPERTY(ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality NOTIFY shadowQualityChanged)
Q_PROPERTY(bool shadowsSupported READ shadowsSupported NOTIFY shadowsSupportedChanged)
@@ -66,6 +65,13 @@ class AbstractDeclarative : public QQuickItem
Q_PROPERTY(QAbstract3DInputHandler* inputHandler READ inputHandler WRITE setInputHandler NOTIFY inputHandlerChanged)
Q_PROPERTY(Q3DTheme* theme READ theme WRITE setTheme NOTIFY themeChanged)
Q_PROPERTY(RenderingMode renderingMode READ renderingMode WRITE setRenderingMode NOTIFY renderingModeChanged)
+ Q_PROPERTY(bool measureFps READ measureFps WRITE setMeasureFps NOTIFY measureFpsChanged REVISION 1)
+ Q_PROPERTY(qreal currentFps READ currentFps NOTIFY currentFpsChanged REVISION 1)
+ Q_PROPERTY(QQmlListProperty<QCustom3DItem> customItemList READ customItemList REVISION 1)
+ Q_PROPERTY(bool orthoProjection READ isOrthoProjection WRITE setOrthoProjection NOTIFY orthoProjectionChanged REVISION 1)
+ 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)
public:
enum SelectionFlag {
@@ -92,12 +98,27 @@ public:
ShadowQualitySoftHigh
};
+ enum ElementType {
+ ElementNone = 0,
+ ElementSeries,
+ ElementAxisXLabel,
+ ElementAxisYLabel,
+ ElementAxisZLabel,
+ ElementCustomItem
+ };
+
enum RenderingMode {
RenderDirectToBackground = 0,
RenderDirectToBackground_NoClear,
RenderIndirect
};
+ enum OptimizationHint {
+ OptimizationDefault = 0,
+ OptimizationStatic = 1
+ };
+ Q_DECLARE_FLAGS(OptimizationHints, OptimizationHint)
+
public:
explicit AbstractDeclarative(QQuickItem *parent = 0);
virtual ~AbstractDeclarative();
@@ -126,7 +147,26 @@ public:
Q_INVOKABLE virtual void clearSelection();
- virtual void geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry);
+ Q_REVISION(1) Q_INVOKABLE virtual int addCustomItem(QCustom3DItem *item);
+ Q_REVISION(1) Q_INVOKABLE virtual void removeCustomItems();
+ Q_REVISION(1) Q_INVOKABLE virtual void removeCustomItem(QCustom3DItem *item);
+ Q_REVISION(1) Q_INVOKABLE virtual void removeCustomItemAt(const QVector3D &position);
+ Q_REVISION(1) Q_INVOKABLE virtual void releaseCustomItem(QCustom3DItem *item);
+
+ Q_REVISION(1) Q_INVOKABLE virtual int selectedLabelIndex() const;
+ Q_REVISION(1) Q_INVOKABLE virtual QAbstract3DAxis *selectedAxis() const;
+
+ Q_REVISION(1) Q_INVOKABLE virtual int selectedCustomItemIndex() const;
+ Q_REVISION(1) Q_INVOKABLE virtual QCustom3DItem *selectedCustomItem() const;
+
+ QQmlListProperty<QCustom3DItem> customItemList();
+ static void appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
+ QCustom3DItem *item);
+ static int countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list);
+ static QCustom3DItem *atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, int index);
+ static void clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list);
+
+ virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
void setSharedController(Abstract3DController *controller);
// Used to synch up data model from controller to renderer while main thread is locked
@@ -138,6 +178,21 @@ public:
void checkWindowList(QQuickWindow *window);
+ void setMeasureFps(bool enable);
+ bool measureFps() const;
+ qreal currentFps() const;
+
+ void setOrthoProjection(bool enable);
+ bool isOrthoProjection() const;
+
+ AbstractDeclarative::ElementType selectedElement() const;
+
+ void setAspectRatio(qreal ratio);
+ qreal aspectRatio() const;
+
+ void setOptimizationHints(OptimizationHints hints);
+ OptimizationHints optimizationHints() const;
+
public slots:
virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0;
virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0;
@@ -167,6 +222,12 @@ signals:
void inputHandlerChanged(QAbstract3DInputHandler *inputHandler);
void themeChanged(Q3DTheme *theme);
void renderingModeChanged(AbstractDeclarative::RenderingMode mode);
+ Q_REVISION(1) void measureFpsChanged(bool enabled);
+ Q_REVISION(1) void currentFpsChanged(qreal fps);
+ Q_REVISION(1) void selectedElementChanged(QAbstract3DGraph::ElementType type);
+ Q_REVISION(1) void orthoProjectionChanged(bool enabled);
+ Q_REVISION(1) void aspectRatioChanged(qreal ratio);
+ Q_REVISION(1) void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints);
private:
QPointer<Abstract3DController> m_controller;
@@ -187,6 +248,7 @@ private:
bool m_runningInDesigner;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractDeclarative::SelectionFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractDeclarative::OptimizationHints)
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp
index 04e70ecb..09780dc5 100644
--- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp
+++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp
@@ -25,8 +25,11 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
void QtDataVisualizationQml2Plugin::registerTypes(const char *uri)
{
// @uri QtDataVisualization
- qmlRegisterUncreatableType<const QAbstractItemModel>(uri, 1, 0, "AbstractItemModel",
- QLatin1String("Trying to create uncreatable: AbstractItemModel."));
+
+ // QtDataVisualization 1.0
+
+ qmlRegisterUncreatableType<QAbstractItemModel>(uri, 1, 0, "AbstractItemModel",
+ QLatin1String("Trying to create uncreatable: AbstractItemModel."));
qmlRegisterUncreatableType<QAbstract3DAxis>(uri, 1, 0, "AbstractAxis3D",
QLatin1String("Trying to create uncreatable: AbstractAxis."));
qmlRegisterUncreatableType<QAbstractDataProxy>(uri, 1, 0, "AbstractDataProxy",
@@ -40,7 +43,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri)
qmlRegisterUncreatableType<AbstractDeclarative>(uri, 1, 0, "AbstractGraph3D",
QLatin1String("Trying to create uncreatable: AbstractGraph3D."));
qmlRegisterUncreatableType<Declarative3DScene>(uri, 1, 0, "Scene3D",
- QLatin1String("Trying to create uncreatable: Scene3D."));
+ QLatin1String("Trying to create uncreatable: Scene3D."));
qmlRegisterUncreatableType<QAbstract3DSeries>(uri, 1, 0, "Abstract3DSeries",
QLatin1String("Trying to create uncreatable: Abstract3DSeries."));
qmlRegisterUncreatableType<QBar3DSeries>(uri, 1, 0, "QBar3DSeries",
@@ -80,6 +83,29 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri)
qmlRegisterType<DeclarativeSurface3DSeries>(uri, 1, 0, "Surface3DSeries");
qRegisterMetaType<QAbstract3DGraph::ShadowQuality>("QAbstract3DGraph::ShadowQuality");
+
+ // QtDataVisualization 1.1
+
+ // New revisions
+ qmlRegisterUncreatableType<QAbstract3DAxis, 1>(uri, 1, 1, "AbstractAxis3D",
+ QLatin1String("Trying to create uncreatable: AbstractAxis."));
+ qmlRegisterUncreatableType<QAbstract3DSeries, 1>(uri, 1, 1, "Abstract3DSeries",
+ 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");
+ qmlRegisterType<QItemModelScatterDataProxy, 1>(uri, 1, 1, "ItemModelScatterDataProxy");
+
+ // New types
+ qmlRegisterType<QValue3DAxisFormatter>(uri, 1, 1, "ValueAxis3DFormatter");
+ qmlRegisterType<QLogValue3DAxisFormatter>(uri, 1, 1, "LogValueAxis3DFormatter");
+ qmlRegisterType<QCustom3DItem>(uri, 1, 1, "Custom3DItem");
+ qmlRegisterType<QCustom3DLabel>(uri, 1, 1, "Custom3DLabel");
+
+ // New metatypes
+ qRegisterMetaType<QAbstract3DGraph::ElementType>("QAbstract3DGraph::ElementType");
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.h b/src/datavisualizationqml2/datavisualizationqml2_plugin.h
index e39d6b35..21ef85b8 100644
--- a/src/datavisualizationqml2/datavisualizationqml2_plugin.h
+++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.h
@@ -28,6 +28,8 @@
#include "qitemmodelsurfacedataproxy.h"
#include "qheightmapsurfacedataproxy.h"
#include "qvalue3daxis.h"
+#include "qvalue3daxisformatter.h"
+#include "qlogvalue3daxisformatter.h"
#include "qcategory3daxis.h"
#include "q3dobject.h"
#include "q3dcamera.h"
@@ -43,6 +45,8 @@
#include "qabstract3dinputhandler.h"
#include "declarativecolor_p.h"
#include "declarativescene_p.h"
+#include "qcustom3ditem.h"
+#include "qcustom3dlabel.h"
#include <QtQml/QQmlExtensionPlugin>
@@ -53,11 +57,13 @@ QML_DECLARE_TYPE(DeclarativeBars)
QML_DECLARE_TYPE(DeclarativeScatter)
QML_DECLARE_TYPE(DeclarativeSurface)
-QML_DECLARE_TYPE(const QAbstractItemModel)
+QML_DECLARE_TYPE(QAbstractItemModel)
QML_DECLARE_TYPE(QAbstract3DAxis)
QML_DECLARE_TYPE(QCategory3DAxis)
QML_DECLARE_TYPE(QValue3DAxis)
+QML_DECLARE_TYPE(QValue3DAxisFormatter)
+QML_DECLARE_TYPE(QLogValue3DAxisFormatter)
QML_DECLARE_TYPE(Q3DScene)
QML_DECLARE_TYPE(Declarative3DScene)
@@ -92,6 +98,9 @@ QML_DECLARE_TYPE(DeclarativeTheme3D)
QML_DECLARE_TYPE(QAbstract3DInputHandler)
+QML_DECLARE_TYPE(QCustom3DItem)
+QML_DECLARE_TYPE(QCustom3DLabel)
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QtDataVisualizationQml2Plugin : public QQmlExtensionPlugin
diff --git a/src/datavisualizationqml2/declarativebars.cpp b/src/datavisualizationqml2/declarativebars.cpp
index 4f984c32..9670a7db 100644
--- a/src/datavisualizationqml2/declarativebars.cpp
+++ b/src/datavisualizationqml2/declarativebars.cpp
@@ -17,9 +17,6 @@
****************************************************************************/
#include "declarativebars_p.h"
-#include "qvalue3daxis.h"
-#include "qitemmodelbardataproxy.h"
-#include "declarativescene_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -90,7 +87,8 @@ bool DeclarativeBars::isMultiSeriesUniform() const
void DeclarativeBars::setBarThickness(float thicknessRatio)
{
if (thicknessRatio != barThickness()) {
- m_barsController->setBarSpecs(GLfloat(thicknessRatio), barSpacing(), isBarSpacingRelative());
+ m_barsController->setBarSpecs(GLfloat(thicknessRatio), barSpacing(),
+ isBarSpacingRelative());
emit barThicknessChanged(thicknessRatio);
}
}
diff --git a/src/datavisualizationqml2/declarativebars_p.h b/src/datavisualizationqml2/declarativebars_p.h
index 97f5882a..52690813 100644
--- a/src/datavisualizationqml2/declarativebars_p.h
+++ b/src/datavisualizationqml2/declarativebars_p.h
@@ -32,11 +32,6 @@
#include "datavisualizationglobal_p.h"
#include "abstractdeclarative_p.h"
#include "bars3dcontroller_p.h"
-#include "declarativebars_p.h"
-#include "qvalue3daxis.h"
-#include "qcategory3daxis.h"
-#include "qbardataproxy.h"
-#include "qbar3dseries.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -88,7 +83,6 @@ public:
Q_INVOKABLE void insertSeries(int index, QBar3DSeries *series);
void setPrimarySeries(QBar3DSeries *series);
QBar3DSeries *primarySeries() const;
-
QBar3DSeries *selectedSeries() const;
public slots:
diff --git a/src/datavisualizationqml2/declarativerendernode.cpp b/src/datavisualizationqml2/declarativerendernode.cpp
index 1f6d4b56..e9bb65fd 100644
--- a/src/datavisualizationqml2/declarativerendernode.cpp
+++ b/src/datavisualizationqml2/declarativerendernode.cpp
@@ -17,24 +17,22 @@
****************************************************************************/
#include "declarativerendernode_p.h"
-#include "abstract3dcontroller_p.h"
#include "abstractdeclarative_p.h"
-#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFramebufferObject>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
DeclarativeRenderNode::DeclarativeRenderNode(AbstractDeclarative *declarative)
: QSGGeometryNode(),
- m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4),
- m_texture(0),
- m_declarative(declarative),
- m_controller(0),
- m_fbo(0),
- m_multisampledFBO(0),
- m_window(0),
- m_samples(0),
- m_dirtyFBO(false)
+ m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4),
+ m_texture(0),
+ m_declarative(declarative),
+ m_controller(0),
+ m_fbo(0),
+ m_multisampledFBO(0),
+ m_window(0),
+ m_samples(0),
+ m_dirtyFBO(false)
{
setMaterial(&m_material);
setOpaqueMaterial(&m_materialO);
@@ -92,8 +90,10 @@ void DeclarativeRenderNode::updateFBO()
QSGGeometry::updateTexturedRectGeometry(&m_geometry,
QRectF(0, 0,
- m_size.width() / m_controller->scene()->devicePixelRatio(),
- m_size.height() / m_controller->scene()->devicePixelRatio()),
+ m_size.width()
+ / m_controller->scene()->devicePixelRatio(),
+ m_size.height()
+ / m_controller->scene()->devicePixelRatio()),
QRectF(0, 1, 1, -1));
delete m_texture;
diff --git a/src/datavisualizationqml2/declarativerendernode_p.h b/src/datavisualizationqml2/declarativerendernode_p.h
index e35791d4..a7ede03a 100644
--- a/src/datavisualizationqml2/declarativerendernode_p.h
+++ b/src/datavisualizationqml2/declarativerendernode_p.h
@@ -33,12 +33,8 @@
#include <QtQuick/QSGGeometryNode>
#include <QtQuick/QSGTextureMaterial>
-#include <QtQuick/QSGOpaqueTextureMaterial>
#include <QtQuick/QQuickWindow>
-class QOpenGLContext;
-class QOpenGLFramebufferObject;
-
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class Abstract3DController;
diff --git a/src/datavisualizationqml2/declarativescatter.cpp b/src/datavisualizationqml2/declarativescatter.cpp
index dcc52a3d..96af6df6 100644
--- a/src/datavisualizationqml2/declarativescatter.cpp
+++ b/src/datavisualizationqml2/declarativescatter.cpp
@@ -17,8 +17,6 @@
****************************************************************************/
#include "declarativescatter_p.h"
-#include "qitemmodelscatterdataproxy.h"
-#include "declarativescene_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -79,13 +77,14 @@ QScatter3DSeries *DeclarativeScatter::selectedSeries() const
QQmlListProperty<QScatter3DSeries> DeclarativeScatter::seriesList()
{
return QQmlListProperty<QScatter3DSeries>(this, this,
- &DeclarativeScatter::appendSeriesFunc,
- &DeclarativeScatter::countSeriesFunc,
- &DeclarativeScatter::atSeriesFunc,
- &DeclarativeScatter::clearSeriesFunc);
+ &DeclarativeScatter::appendSeriesFunc,
+ &DeclarativeScatter::countSeriesFunc,
+ &DeclarativeScatter::atSeriesFunc,
+ &DeclarativeScatter::clearSeriesFunc);
}
-void DeclarativeScatter::appendSeriesFunc(QQmlListProperty<QScatter3DSeries> *list, QScatter3DSeries *series)
+void DeclarativeScatter::appendSeriesFunc(QQmlListProperty<QScatter3DSeries> *list,
+ QScatter3DSeries *series)
{
reinterpret_cast<DeclarativeScatter *>(list->data)->addSeries(series);
}
diff --git a/src/datavisualizationqml2/declarativescatter_p.h b/src/datavisualizationqml2/declarativescatter_p.h
index 79b56e02..370750ea 100644
--- a/src/datavisualizationqml2/declarativescatter_p.h
+++ b/src/datavisualizationqml2/declarativescatter_p.h
@@ -32,9 +32,6 @@
#include "datavisualizationglobal_p.h"
#include "abstractdeclarative_p.h"
#include "scatter3dcontroller_p.h"
-#include "declarativescatter_p.h"
-#include "qvalue3daxis.h"
-#include "qscatterdataproxy.h"
#include "qscatter3dseries.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/declarativeseries.cpp b/src/datavisualizationqml2/declarativeseries.cpp
index 21555f45..661f87ba 100644
--- a/src/datavisualizationqml2/declarativeseries.cpp
+++ b/src/datavisualizationqml2/declarativeseries.cpp
@@ -17,14 +17,12 @@
****************************************************************************/
#include "declarativeseries_p.h"
-#include "qbardataproxy.h"
-#include "qscatterdataproxy.h"
-#include "qsurfacedataproxy.h"
#include <QtCore/QMetaMethod>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-static void setSeriesGradient(QAbstract3DSeries *series, const ColorGradient &gradient, GradientType type)
+static void setSeriesGradient(QAbstract3DSeries *series, const ColorGradient &gradient,
+ GradientType type)
{
QLinearGradient newGradient;
QGradientStops stops;
@@ -198,7 +196,8 @@ QQmlListProperty<QObject> DeclarativeScatter3DSeries::seriesChildren()
, 0, 0, 0);
}
-void DeclarativeScatter3DSeries::appendSeriesChildren(QQmlListProperty<QObject> *list, QObject *element)
+void DeclarativeScatter3DSeries::appendSeriesChildren(QQmlListProperty<QObject> *list,
+ QObject *element)
{
QScatterDataProxy *proxy = qobject_cast<QScatterDataProxy *>(element);
if (proxy)
@@ -293,7 +292,8 @@ QQmlListProperty<QObject> DeclarativeSurface3DSeries::seriesChildren()
, 0, 0, 0);
}
-void DeclarativeSurface3DSeries::appendSeriesChildren(QQmlListProperty<QObject> *list, QObject *element)
+void DeclarativeSurface3DSeries::appendSeriesChildren(QQmlListProperty<QObject> *list,
+ QObject *element)
{
QSurfaceDataProxy *proxy = qobject_cast<QSurfaceDataProxy *>(element);
if (proxy)
diff --git a/src/datavisualizationqml2/declarativesurface.cpp b/src/datavisualizationqml2/declarativesurface.cpp
index 78519586..3075d207 100644
--- a/src/datavisualizationqml2/declarativesurface.cpp
+++ b/src/datavisualizationqml2/declarativesurface.cpp
@@ -17,9 +17,6 @@
****************************************************************************/
#include "declarativesurface_p.h"
-#include "qvalue3daxis.h"
-#include "qitemmodelsurfacedataproxy.h"
-#include "declarativescene_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/declarativesurface_p.h b/src/datavisualizationqml2/declarativesurface_p.h
index a4747167..6fe800ba 100644
--- a/src/datavisualizationqml2/declarativesurface_p.h
+++ b/src/datavisualizationqml2/declarativesurface_p.h
@@ -32,9 +32,6 @@
#include "datavisualizationglobal_p.h"
#include "abstractdeclarative_p.h"
#include "surface3dcontroller_p.h"
-#include "declarativesurface_p.h"
-#include "qvalue3daxis.h"
-#include "qsurfacedataproxy.h"
#include "qsurface3dseries.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualizationqml2/declarativetheme.cpp b/src/datavisualizationqml2/declarativetheme.cpp
index 5aec2408..ab10155e 100644
--- a/src/datavisualizationqml2/declarativetheme.cpp
+++ b/src/datavisualizationqml2/declarativetheme.cpp
@@ -36,17 +36,17 @@ DeclarativeTheme3D::~DeclarativeTheme3D()
{
}
-QQmlListProperty<QObject> DeclarativeTheme3D::seriesChildren()
+QQmlListProperty<QObject> DeclarativeTheme3D::themeChildren()
{
- return QQmlListProperty<QObject>(this, this, &DeclarativeTheme3D::appendSeriesChildren,
+ return QQmlListProperty<QObject>(this, this, &DeclarativeTheme3D::appendThemeChildren,
0, 0, 0);
}
-void DeclarativeTheme3D::appendSeriesChildren(QQmlListProperty<QObject> *list, QObject *element)
+void DeclarativeTheme3D::appendThemeChildren(QQmlListProperty<QObject> *list, QObject *element)
{
Q_UNUSED(list)
Q_UNUSED(element)
- // Nothing to do, seriesChildren is there only to enable scoping gradient items in Theme3D item.
+ // Nothing to do, themeChildren is there only to enable scoping gradient items in Theme3D item.
}
void DeclarativeTheme3D::handleTypeChange(Theme themeType)
@@ -236,6 +236,10 @@ ColorGradient *DeclarativeTheme3D::convertGradient(const QLinearGradient &gradie
void DeclarativeTheme3D::addColor(DeclarativeColor *color)
{
+ if (!color) {
+ qWarning("Color is invalid, use ThemeColor");
+ return;
+ }
clearDummyColors();
m_colors.append(color);
connect(color, &DeclarativeColor::colorChanged,
@@ -283,6 +287,10 @@ void DeclarativeTheme3D::clearDummyColors()
void DeclarativeTheme3D::addGradient(ColorGradient *gradient)
{
+ if (!gradient) {
+ qWarning("Gradient is invalid, use ColorGradient");
+ return;
+ }
clearDummyGradients();
m_gradients.append(gradient);
connect(gradient, &ColorGradient::updated,
diff --git a/src/datavisualizationqml2/declarativetheme_p.h b/src/datavisualizationqml2/declarativetheme_p.h
index a7f40b1e..89b66f8c 100644
--- a/src/datavisualizationqml2/declarativetheme_p.h
+++ b/src/datavisualizationqml2/declarativetheme_p.h
@@ -42,19 +42,19 @@ class DeclarativeTheme3D : public Q3DTheme, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
- Q_PROPERTY(QQmlListProperty<QObject> seriesChildren READ seriesChildren)
+ Q_PROPERTY(QQmlListProperty<QObject> themeChildren READ themeChildren)
Q_PROPERTY(QQmlListProperty<DeclarativeColor> baseColors READ baseColors)
Q_PROPERTY(QQmlListProperty<ColorGradient> baseGradients READ baseGradients)
Q_PROPERTY(ColorGradient *singleHighlightGradient READ singleHighlightGradient WRITE setSingleHighlightGradient NOTIFY singleHighlightGradientChanged)
Q_PROPERTY(ColorGradient *multiHighlightGradient READ multiHighlightGradient WRITE setMultiHighlightGradient NOTIFY multiHighlightGradientChanged)
- Q_CLASSINFO("DefaultProperty", "seriesChildren")
+ Q_CLASSINFO("DefaultProperty", "themeChildren")
public:
DeclarativeTheme3D(QObject *parent = 0);
virtual ~DeclarativeTheme3D();
- QQmlListProperty<QObject> seriesChildren();
- static void appendSeriesChildren(QQmlListProperty<QObject> *list, QObject *element);
+ QQmlListProperty<QObject> themeChildren();
+ static void appendThemeChildren(QQmlListProperty<QObject> *list, QObject *element);
QQmlListProperty<DeclarativeColor> baseColors();
static void appendBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list,
diff --git a/src/datavisualizationqml2/designer/Bars3DSpecifics.qml b/src/datavisualizationqml2/designer/Bars3DSpecifics.qml
index e52320ac..cb5fb4a0 100644
--- a/src/datavisualizationqml2/designer/Bars3DSpecifics.qml
+++ b/src/datavisualizationqml2/designer/Bars3DSpecifics.qml
@@ -266,6 +266,29 @@ Column {
}
}
}
+ Label {
+ text: qsTr("measureFps")
+ toolTip: qsTr("Measure Frames Per Second")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ backendValue: backendValues.measureFps
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("orthoProjection")
+ toolTip: qsTr("Use Orthographic Projection")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ backendValue: backendValues.orthoProjection
+ Layout.fillWidth: true
+ }
+ }
+
// Kept for debugging
Label { }
SecondColumnLayout {
diff --git a/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml b/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml
index b9a9ef97..1e2556ec 100644
--- a/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml
+++ b/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml
@@ -84,6 +84,43 @@ Column {
scope: "AbstractGraph3D"
}
}
+ Label {
+ text: qsTr("measureFps")
+ toolTip: qsTr("Measure Frames Per Second")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ backendValue: backendValues.measureFps
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("orthoProjection")
+ toolTip: qsTr("Use Orthographic Projection")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ backendValue: backendValues.orthoProjection
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("aspectRatio")
+ toolTip: qsTr("Horizontal to Vertical Aspect Ratio")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.aspectRatio
+ minimumValue: 0.1
+ maximumValue: 10.0
+ stepSize: 0.1
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
}
}
}
diff --git a/src/datavisualizationqml2/designer/Surface3DSpecifics.qml b/src/datavisualizationqml2/designer/Surface3DSpecifics.qml
index 74470e4b..65a65d37 100644
--- a/src/datavisualizationqml2/designer/Surface3DSpecifics.qml
+++ b/src/datavisualizationqml2/designer/Surface3DSpecifics.qml
@@ -204,6 +204,44 @@ Column {
}
}
}
+ Label {
+ text: qsTr("measureFps")
+ toolTip: qsTr("Measure Frames Per Second")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ backendValue: backendValues.measureFps
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("orthoProjection")
+ toolTip: qsTr("Use Orthographic Projection")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ CheckBox {
+ backendValue: backendValues.orthoProjection
+ Layout.fillWidth: true
+ }
+ }
+ Label {
+ text: qsTr("aspectRatio")
+ toolTip: qsTr("Horizontal to Vertical Aspect Ratio")
+ Layout.fillWidth: true
+ }
+ SecondColumnLayout {
+ SpinBox {
+ backendValue: backendValues.aspectRatio
+ minimumValue: 0.1
+ maximumValue: 10.0
+ stepSize: 0.1
+ decimals: 1
+ Layout.fillWidth: true
+ }
+ }
+
// Kept for debugging
Label { }
SecondColumnLayout {
diff --git a/src/datavisualizationqml2/enumtostringmap.cpp b/src/datavisualizationqml2/enumtostringmap.cpp
index 249fbae3..b4b04fc2 100644
--- a/src/datavisualizationqml2/enumtostringmap.cpp
+++ b/src/datavisualizationqml2/enumtostringmap.cpp
@@ -15,12 +15,13 @@
** contact form at http://qt.digia.com
**
****************************************************************************/
+
#include "enumtostringmap_p.h"
-#include <QString>
-#include <QDebug>
#ifdef VERBOSE_STATE_STORE
+#include <QDebug>
+
static EnumToStringMap *theInstance = 0;
static unsigned int theInstanceCount = 0;
@@ -367,7 +368,8 @@ EnumToStringMap::EnumToStringMap() :
m_map[GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE] = "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE";
m_map[GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME] = "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME";
m_map[GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL] = "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL";
- m_map[GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE] = "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE";
+ m_map[GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE] =
+ "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE";
m_map[GL_COLOR_ATTACHMENT0] = "COLOR_ATTACHMENT0";
m_map[GL_DEPTH_ATTACHMENT] = "DEPTH_ATTACHMENT";
@@ -375,11 +377,16 @@ EnumToStringMap::EnumToStringMap() :
m_map[GL_FRAMEBUFFER_COMPLETE] = "FRAMEBUFFER_COMPLETE";
m_map[GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT] = "FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
- m_map[GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT] = "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ m_map[GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT] =
+ "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
m_map[GL_FRAMEBUFFER_UNSUPPORTED] = "FRAMEBUFFER_UNSUPPORTED";
m_map[GL_FRAMEBUFFER_BINDING] = "FRAMEBUFFER_BINDING";
+#if !defined(QT_OPENGL_ES_2)
m_map[GL_RENDERBUFFER_BINDING] = "RENDERBUFFER_BINDING";
+#else
+ m_map[GL_RENDERBUFFER] = "RENDERBUFFER_BINDING";
+#endif
m_map[GL_MAX_RENDERBUFFER_SIZE] = "MAX_RENDERBUFFER_SIZE";
m_map[GL_INVALID_FRAMEBUFFER_OPERATION] = "INVALID_FRAMEBUFFER_OPERATION";
diff --git a/src/datavisualizationqml2/glstatestore.cpp b/src/datavisualizationqml2/glstatestore.cpp
index f053078b..973d5054 100644
--- a/src/datavisualizationqml2/glstatestore.cpp
+++ b/src/datavisualizationqml2/glstatestore.cpp
@@ -15,6 +15,7 @@
** contact form at http://qt.digia.com
**
****************************************************************************/
+
#include "glstatestore_p.h"
#include <QDebug>
#include <QColor>
@@ -50,13 +51,13 @@ GLStateStore::GLStateStore(QOpenGLContext *context, QObject *parent) :
#endif
m_maxVertexAttribs = qMin(maxVertexAttribs, 2); // Datavis only uses 2 attribs max
- m_vertexAttribArrayEnabledStates = new GLint[maxVertexAttribs];
- m_vertexAttribArrayBoundBuffers = new GLint[maxVertexAttribs];
- m_vertexAttribArraySizes = new GLint[maxVertexAttribs];
- m_vertexAttribArrayTypes = new GLint[maxVertexAttribs];
- m_vertexAttribArrayNormalized = new GLint[maxVertexAttribs];
- m_vertexAttribArrayStrides = new GLint[maxVertexAttribs];
- m_vertexAttribArrayOffsets = new GLint[maxVertexAttribs];
+ m_vertexAttribArrayEnabledStates.reset(new GLint[maxVertexAttribs]);
+ m_vertexAttribArrayBoundBuffers.reset(new GLint[maxVertexAttribs]);
+ m_vertexAttribArraySizes.reset(new GLint[maxVertexAttribs]);
+ m_vertexAttribArrayTypes.reset(new GLint[maxVertexAttribs]);
+ m_vertexAttribArrayNormalized.reset(new GLint[maxVertexAttribs]);
+ m_vertexAttribArrayStrides.reset(new GLint[maxVertexAttribs]);
+ m_vertexAttribArrayOffsets.reset(new GLint[maxVertexAttribs]);
initGLDefaultState();
}
@@ -67,8 +68,6 @@ GLStateStore::~GLStateStore()
EnumToStringMap::deleteInstance();
m_map = 0;
#endif
- delete m_vertexAttribArrayEnabledStates;
- delete m_vertexAttribArrayBoundBuffers;
}
void GLStateStore::storeGLState()
@@ -80,8 +79,10 @@ void GLStateStore::storeGLState()
#if !defined(QT_OPENGL_ES_2)
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &m_drawFramebuffer);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &m_readFramebuffer);
-#endif
glGetIntegerv(GL_RENDERBUFFER_BINDING, &m_renderbuffer);
+#else
+ glGetIntegerv(GL_RENDERBUFFER, &m_renderbuffer);
+#endif
glGetFloatv(GL_COLOR_CLEAR_VALUE, m_clearColor);
m_isBlendingEnabled = glIsEnabled(GL_BLEND);
m_isDepthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
@@ -111,11 +112,14 @@ void GLStateStore::storeGLState()
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &m_boundElementArrayBuffer);
for (int i = 0; i < m_maxVertexAttribs;i++) {
- glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &m_vertexAttribArrayEnabledStates[i]);
- glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &m_vertexAttribArrayBoundBuffers[i]);
+ glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED,
+ &m_vertexAttribArrayEnabledStates[i]);
+ glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
+ &m_vertexAttribArrayBoundBuffers[i]);
glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &m_vertexAttribArraySizes[i]);
glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &m_vertexAttribArrayTypes[i]);
- glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &m_vertexAttribArrayNormalized[i]);
+ glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED,
+ &m_vertexAttribArrayNormalized[i]);
glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &m_vertexAttribArrayStrides[i]);
}
}
@@ -173,8 +177,10 @@ void GLStateStore::printCurrentState(bool in)
#if !defined(QT_OPENGL_ES_2)
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFramebuffer);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readFramebuffer);
-#endif
glGetIntegerv(GL_RENDERBUFFER_BINDING, &renderbuffer);
+#else
+ glGetIntegerv(GL_RENDERBUFFER, &renderbuffer);
+#endif
glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth);
glGetIntegerv(GL_DEPTH_FUNC, &depthFunc);
@@ -198,11 +204,14 @@ void GLStateStore::printCurrentState(bool in)
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &arrayBufferBinding);
for (int i = 0; i < m_maxVertexAttribs;i++) {
- glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vertexAttribArrayEnabledStates[i]);
- glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vertexAttribArrayBoundBuffers[i]);
+ glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED,
+ &vertexAttribArrayEnabledStates[i]);
+ glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
+ &vertexAttribArrayBoundBuffers[i]);
glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &vertexAttribArraySizes[i]);
glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &vertexAttribArrayTypes[i]);
- glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &vertexAttribArrayNormalized[i]);
+ glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED,
+ &vertexAttribArrayNormalized[i]);
glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &vertexAttribArrayStrides[i]);
}
@@ -265,8 +274,10 @@ void GLStateStore::restoreGLState()
#if !defined(QT_OPENGL_ES_2)
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_readFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_drawFramebuffer);
-#endif
glBindRenderbuffer(GL_RENDERBUFFER_BINDING, m_renderbuffer);
+#else
+ glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
+#endif
if (m_isScissorTestEnabled)
glEnable(GL_SCISSOR_TEST);
diff --git a/src/datavisualizationqml2/glstatestore_p.h b/src/datavisualizationqml2/glstatestore_p.h
index 14c46c43..4add606b 100644
--- a/src/datavisualizationqml2/glstatestore_p.h
+++ b/src/datavisualizationqml2/glstatestore_p.h
@@ -29,9 +29,8 @@
#ifndef GLSTATESTORE_P_H
#define GLSTATESTORE_P_H
-#include <QObject>
#include <QtGui/QOpenGLFunctions>
-#include <QtGui/QOpenGLContext>
+#include <QtCore/QScopedArrayPointer>
#include "enumtostringmap_p.h"
class GLStateStore : public QObject, protected QOpenGLFunctions
@@ -66,13 +65,13 @@ public:
GLboolean m_isDepthWriteEnabled;
GLint m_currentProgram;
GLint m_maxVertexAttribs;
- GLint *m_vertexAttribArrayEnabledStates;
- GLint *m_vertexAttribArrayBoundBuffers;
- GLint *m_vertexAttribArraySizes;
- GLint *m_vertexAttribArrayTypes;
- GLint *m_vertexAttribArrayNormalized;
- GLint *m_vertexAttribArrayStrides;
- GLint *m_vertexAttribArrayOffsets;
+ QScopedArrayPointer<GLint> m_vertexAttribArrayEnabledStates;
+ QScopedArrayPointer<GLint> m_vertexAttribArrayBoundBuffers;
+ QScopedArrayPointer<GLint> m_vertexAttribArraySizes;
+ QScopedArrayPointer<GLint> m_vertexAttribArrayTypes;
+ QScopedArrayPointer<GLint> m_vertexAttribArrayNormalized;
+ QScopedArrayPointer<GLint> m_vertexAttribArrayStrides;
+ QScopedArrayPointer<GLint> m_vertexAttribArrayOffsets;
GLint m_activeTexture;
GLint m_texBinding2D;
diff --git a/src/datavisualizationqml2/plugins.qmltypes b/src/datavisualizationqml2/plugins.qmltypes
index 48a30665..99fa53de 100644
--- a/src/datavisualizationqml2/plugins.qmltypes
+++ b/src/datavisualizationqml2/plugins.qmltypes
@@ -4,16 +4,19 @@ import QtQuick.tooling 1.1
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtDataVisualization 1.0'
+// 'qmlplugindump -nonrelocatable QtDataVisualization 1.1'
Module {
Component {
name: "QtDataVisualization::AbstractDeclarative"
defaultProperty: "data"
prototype: "QQuickItem"
- exports: ["QtDataVisualization/AbstractGraph3D 1.0"]
+ exports: [
+ "QtDataVisualization/AbstractGraph3D 1.0",
+ "QtDataVisualization/AbstractGraph3D 1.1"
+ ]
isCreatable: false
- exportMetaObjectRevisions: [0]
+ exportMetaObjectRevisions: [0, 1]
Enum {
name: "SelectionFlag"
values: {
@@ -57,6 +60,17 @@ Module {
}
}
Enum {
+ name: "ElementType"
+ values: {
+ "ElementNone": 0,
+ "ElementSeries": 1,
+ "ElementAxisXLabel": 2,
+ "ElementAxisYLabel": 3,
+ "ElementAxisZLabel": 4,
+ "ElementCustomItem": 5
+ }
+ }
+ Enum {
name: "RenderingMode"
values: {
"RenderDirectToBackground": 0,
@@ -66,18 +80,35 @@ Module {
}
Property { name: "selectionMode"; type: "SelectionFlags" }
Property { name: "shadowQuality"; type: "ShadowQuality" }
+ Property { name: "shadowsSupported"; type: "bool"; isReadonly: true }
Property { name: "msaaSamples"; type: "int" }
Property { name: "scene"; type: "Declarative3DScene"; isReadonly: true; isPointer: true }
Property { name: "inputHandler"; type: "QAbstract3DInputHandler"; isPointer: true }
Property { name: "theme"; type: "Q3DTheme"; isPointer: true }
Property { name: "renderingMode"; type: "RenderingMode" }
+ Property { name: "measureFps"; revision: 1; type: "bool" }
+ Property { name: "currentFps"; revision: 1; type: "double"; isReadonly: true }
+ Property {
+ name: "customItemList"
+ revision: 1
+ type: "QCustom3DItem"
+ isList: true
+ isReadonly: true
+ }
+ Property { name: "orthoProjection"; revision: 1; type: "bool" }
+ Property { name: "selectedElement"; revision: 1; type: "ElementType"; isReadonly: true }
+ Property { name: "aspectRatio"; revision: 1; type: "double" }
Signal {
name: "selectionModeChanged"
- Parameter { name: "mode"; type: "SelectionFlags" }
+ Parameter { name: "mode"; type: "AbstractDeclarative::SelectionFlags" }
}
Signal {
name: "shadowQualityChanged"
- Parameter { name: "quality"; type: "ShadowQuality" }
+ Parameter { name: "quality"; type: "AbstractDeclarative::ShadowQuality" }
+ }
+ Signal {
+ name: "shadowsSupportedChanged"
+ Parameter { name: "supported"; type: "bool" }
}
Signal {
name: "msaaSamplesChanged"
@@ -97,7 +128,32 @@ Module {
}
Signal {
name: "renderingModeChanged"
- Parameter { name: "mode"; type: "RenderingMode" }
+ Parameter { name: "mode"; type: "AbstractDeclarative::RenderingMode" }
+ }
+ Signal {
+ name: "measureFpsChanged"
+ revision: 1
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "currentFpsChanged"
+ revision: 1
+ Parameter { name: "fps"; type: "double" }
+ }
+ Signal {
+ name: "selectedElementChanged"
+ revision: 1
+ Parameter { name: "type"; type: "QAbstract3DGraph::ElementType" }
+ }
+ Signal {
+ name: "orthoProjectionChanged"
+ revision: 1
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "aspectRatioChanged"
+ revision: 1
+ Parameter { name: "ratio"; type: "double" }
}
Method {
name: "handleAxisXChanged"
@@ -116,6 +172,32 @@ Module {
Parameter { name: "obj"; type: "QObject"; isPointer: true }
}
Method { name: "clearSelection" }
+ Method {
+ name: "addCustomItem"
+ revision: 1
+ type: "int"
+ Parameter { name: "item"; type: "QCustom3DItem"; isPointer: true }
+ }
+ Method { name: "removeCustomItems"; revision: 1 }
+ Method {
+ name: "removeCustomItem"
+ revision: 1
+ Parameter { name: "item"; type: "QCustom3DItem"; isPointer: true }
+ }
+ Method {
+ name: "removeCustomItemAt"
+ revision: 1
+ Parameter { name: "position"; type: "QVector3D" }
+ }
+ Method {
+ name: "releaseCustomItem"
+ revision: 1
+ Parameter { name: "item"; type: "QCustom3DItem"; isPointer: true }
+ }
+ Method { name: "selectedLabelIndex"; revision: 1; type: "int" }
+ Method { name: "selectedAxis"; revision: 1; type: "QAbstract3DAxis*" }
+ Method { name: "selectedCustomItemIndex"; revision: 1; type: "int" }
+ Method { name: "selectedCustomItem"; revision: 1; type: "QCustom3DItem*" }
}
Component {
name: "QtDataVisualization::ColorGradient"
@@ -437,11 +519,11 @@ Module {
}
Component {
name: "QtDataVisualization::DeclarativeTheme3D"
- defaultProperty: "seriesChildren"
+ defaultProperty: "themeChildren"
prototype: "QtDataVisualization::Q3DTheme"
exports: ["QtDataVisualization/Theme3D 1.0"]
exportMetaObjectRevisions: [0]
- Property { name: "seriesChildren"; type: "QObject"; isList: true; isReadonly: true }
+ Property { name: "themeChildren"; type: "QObject"; isList: true; isReadonly: true }
Property { name: "baseColors"; type: "DeclarativeColor"; isList: true; isReadonly: true }
Property { name: "baseGradients"; type: "ColorGradient"; isList: true; isReadonly: true }
Property { name: "singleHighlightGradient"; type: "ColorGradient"; isPointer: true }
@@ -492,7 +574,7 @@ Module {
}
Property { name: "xRotation"; type: "double" }
Property { name: "yRotation"; type: "double" }
- Property { name: "zoomLevel"; type: "int" }
+ Property { name: "zoomLevel"; type: "double" }
Property { name: "cameraPreset"; type: "CameraPreset" }
Property { name: "wrapXRotation"; type: "bool" }
Property { name: "wrapYRotation"; type: "bool" }
@@ -506,11 +588,11 @@ Module {
}
Signal {
name: "zoomLevelChanged"
- Parameter { name: "zoomLevel"; type: "int" }
+ Parameter { name: "zoomLevel"; type: "double" }
}
Signal {
name: "cameraPresetChanged"
- Parameter { name: "preset"; type: "CameraPreset" }
+ Parameter { name: "preset"; type: "Q3DCamera::CameraPreset" }
}
Signal {
name: "wrapXRotationChanged"
@@ -642,7 +724,7 @@ Module {
Property { name: "colorStyle"; type: "ColorStyle" }
Signal {
name: "typeChanged"
- Parameter { name: "themeType"; type: "Theme" }
+ Parameter { name: "themeType"; type: "Q3DTheme::Theme" }
}
Signal {
name: "baseColorsChanged"
@@ -726,15 +808,18 @@ Module {
}
Signal {
name: "colorStyleChanged"
- Parameter { name: "style"; type: "ColorStyle" }
+ Parameter { name: "style"; type: "Q3DTheme::ColorStyle" }
}
}
Component {
name: "QtDataVisualization::QAbstract3DAxis"
prototype: "QObject"
- exports: ["QtDataVisualization/AbstractAxis3D 1.0"]
+ exports: [
+ "QtDataVisualization/AbstractAxis3D 1.0",
+ "QtDataVisualization/AbstractAxis3D 1.1"
+ ]
isCreatable: false
- exportMetaObjectRevisions: [0]
+ exportMetaObjectRevisions: [0, 1]
Enum {
name: "AxisOrientation"
values: {
@@ -759,13 +844,14 @@ Module {
Property { name: "min"; type: "double" }
Property { name: "max"; type: "double" }
Property { name: "autoAdjustRange"; type: "bool" }
+ Property { name: "labelAutoRotation"; revision: 1; type: "double" }
Signal {
name: "titleChanged"
Parameter { name: "newTitle"; type: "string" }
}
Signal {
name: "orientationChanged"
- Parameter { name: "orientation"; type: "AxisOrientation" }
+ Parameter { name: "orientation"; type: "QAbstract3DAxis::AxisOrientation" }
}
Signal {
name: "minChanged"
@@ -784,6 +870,11 @@ Module {
name: "autoAdjustRangeChanged"
Parameter { name: "autoAdjust"; type: "bool" }
}
+ Signal {
+ name: "labelAutoRotationChanged"
+ revision: 1
+ Parameter { name: "angle"; type: "double" }
+ }
}
Component {
name: "QtDataVisualization::QAbstract3DInputHandler"
@@ -808,7 +899,7 @@ Module {
}
Signal {
name: "inputViewChanged"
- Parameter { name: "view"; type: "InputView" }
+ Parameter { name: "view"; type: "QAbstract3DInputHandler::InputView" }
}
Signal {
name: "sceneChanged"
@@ -818,9 +909,12 @@ Module {
Component {
name: "QtDataVisualization::QAbstract3DSeries"
prototype: "QObject"
- exports: ["QtDataVisualization/Abstract3DSeries 1.0"]
+ exports: [
+ "QtDataVisualization/Abstract3DSeries 1.0",
+ "QtDataVisualization/Abstract3DSeries 1.1"
+ ]
isCreatable: false
- exportMetaObjectRevisions: [0]
+ exportMetaObjectRevisions: [0, 1]
Enum {
name: "SeriesType"
values: {
@@ -862,6 +956,8 @@ Module {
Property { name: "multiHighlightColor"; type: "QColor" }
Property { name: "multiHighlightGradient"; type: "QLinearGradient" }
Property { name: "name"; type: "string" }
+ Property { name: "itemLabel"; revision: 1; type: "string"; isReadonly: true }
+ Property { name: "itemLabelVisible"; revision: 1; type: "bool" }
Signal {
name: "itemLabelFormatChanged"
Parameter { name: "format"; type: "string" }
@@ -872,7 +968,7 @@ Module {
}
Signal {
name: "meshChanged"
- Parameter { name: "mesh"; type: "Mesh" }
+ Parameter { name: "mesh"; type: "QAbstract3DSeries::Mesh" }
}
Signal {
name: "meshSmoothChanged"
@@ -918,6 +1014,16 @@ Module {
name: "nameChanged"
Parameter { name: "name"; type: "string" }
}
+ Signal {
+ name: "itemLabelChanged"
+ revision: 1
+ Parameter { name: "label"; type: "string" }
+ }
+ Signal {
+ name: "itemLabelVisibilityChanged"
+ revision: 1
+ Parameter { name: "visible"; type: "bool" }
+ }
Method {
name: "setMeshAxisAndAngle"
Parameter { name: "axis"; type: "QVector3D" }
@@ -1016,6 +1122,57 @@ Module {
Property { name: "labels"; type: "QStringList" }
}
Component {
+ name: "QtDataVisualization::QCustom3DItem"
+ prototype: "QObject"
+ exports: ["QtDataVisualization/Custom3DItem 1.1"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "meshFile"; type: "string" }
+ Property { name: "textureFile"; type: "string" }
+ Property { name: "position"; type: "QVector3D" }
+ Property { name: "positionAbsolute"; type: "bool" }
+ Property { name: "scaling"; type: "QVector3D" }
+ Property { name: "rotation"; type: "QQuaternion" }
+ Property { name: "visible"; type: "bool" }
+ Property { name: "shadowCasting"; type: "bool" }
+ Signal {
+ name: "meshFileChanged"
+ Parameter { name: "meshFile"; type: "string" }
+ }
+ Signal {
+ name: "textureFileChanged"
+ Parameter { name: "textureFile"; type: "string" }
+ }
+ Signal {
+ name: "positionChanged"
+ Parameter { name: "position"; type: "QVector3D" }
+ }
+ Signal {
+ name: "positionAbsoluteChanged"
+ Parameter { name: "positionAbsolute"; type: "bool" }
+ }
+ Signal {
+ name: "scalingChanged"
+ Parameter { name: "scaling"; type: "QVector3D" }
+ }
+ Signal {
+ name: "rotationChanged"
+ Parameter { name: "rotation"; type: "QQuaternion" }
+ }
+ Signal {
+ name: "visibleChanged"
+ Parameter { name: "visible"; type: "bool" }
+ }
+ Signal {
+ name: "shadowCastingChanged"
+ Parameter { name: "shadowCasting"; type: "bool" }
+ }
+ Method {
+ name: "setRotationAxisAndAngle"
+ Parameter { name: "axis"; type: "QVector3D" }
+ Parameter { name: "angle"; type: "double" }
+ }
+ }
+ Component {
name: "QtDataVisualization::QHeightMapSurfaceDataProxy"
prototype: "QtDataVisualization::QSurfaceDataProxy"
exports: ["QtDataVisualization/HeightMapSurfaceDataProxy 1.0"]
@@ -1054,8 +1211,20 @@ Module {
Component {
name: "QtDataVisualization::QItemModelBarDataProxy"
prototype: "QtDataVisualization::QBarDataProxy"
- exports: ["QtDataVisualization/ItemModelBarDataProxy 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/ItemModelBarDataProxy 1.0",
+ "QtDataVisualization/ItemModelBarDataProxy 1.1"
+ ]
+ exportMetaObjectRevisions: [0, 1]
+ Enum {
+ name: "MultiMatchBehavior"
+ values: {
+ "MMBFirst": 0,
+ "MMBLast": 1,
+ "MMBAverage": 2,
+ "MMBCumulative": 3
+ }
+ }
Property { name: "itemModel"; type: "const QAbstractItemModel"; isPointer: true }
Property { name: "rowRole"; type: "string" }
Property { name: "columnRole"; type: "string" }
@@ -1066,6 +1235,15 @@ Module {
Property { name: "useModelCategories"; type: "bool" }
Property { name: "autoRowCategories"; type: "bool" }
Property { name: "autoColumnCategories"; type: "bool" }
+ Property { name: "rowRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "columnRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "valueRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "rotationRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "rowRoleReplace"; revision: 1; type: "string" }
+ Property { name: "columnRoleReplace"; revision: 1; type: "string" }
+ Property { name: "valueRoleReplace"; revision: 1; type: "string" }
+ Property { name: "rotationRoleReplace"; revision: 1; type: "string" }
+ Property { name: "multiMatchBehavior"; revision: 1; type: "MultiMatchBehavior" }
Signal {
name: "itemModelChanged"
Parameter { name: "itemModel"; type: "const QAbstractItemModel"; isPointer: true }
@@ -1098,6 +1276,51 @@ Module {
name: "autoColumnCategoriesChanged"
Parameter { name: "enable"; type: "bool" }
}
+ Signal {
+ name: "rowRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "columnRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "valueRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "rotationRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "rowRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "columnRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "valueRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "rotationRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "multiMatchBehaviorChanged"
+ revision: 1
+ Parameter { name: "behavior"; type: "MultiMatchBehavior" }
+ }
Method {
name: "rowCategoryIndex"
type: "int"
@@ -1112,13 +1335,24 @@ Module {
Component {
name: "QtDataVisualization::QItemModelScatterDataProxy"
prototype: "QtDataVisualization::QScatterDataProxy"
- exports: ["QtDataVisualization/ItemModelScatterDataProxy 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/ItemModelScatterDataProxy 1.0",
+ "QtDataVisualization/ItemModelScatterDataProxy 1.1"
+ ]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "itemModel"; type: "const QAbstractItemModel"; isPointer: true }
Property { name: "xPosRole"; type: "string" }
Property { name: "yPosRole"; type: "string" }
Property { name: "zPosRole"; type: "string" }
Property { name: "rotationRole"; type: "string" }
+ Property { name: "xPosRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "yPosRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "zPosRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "rotationRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "xPosRoleReplace"; revision: 1; type: "string" }
+ Property { name: "yPosRoleReplace"; revision: 1; type: "string" }
+ Property { name: "zPosRoleReplace"; revision: 1; type: "string" }
+ Property { name: "rotationRoleReplace"; revision: 1; type: "string" }
Signal {
name: "itemModelChanged"
Parameter { name: "itemModel"; type: "const QAbstractItemModel"; isPointer: true }
@@ -1139,12 +1373,64 @@ Module {
name: "rotationRoleChanged"
Parameter { name: "role"; type: "string" }
}
+ Signal {
+ name: "xPosRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "yPosRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "zPosRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "rotationRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "rotationRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "xPosRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "yPosRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "zPosRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
}
Component {
name: "QtDataVisualization::QItemModelSurfaceDataProxy"
prototype: "QtDataVisualization::QSurfaceDataProxy"
- exports: ["QtDataVisualization/ItemModelSurfaceDataProxy 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/ItemModelSurfaceDataProxy 1.0",
+ "QtDataVisualization/ItemModelSurfaceDataProxy 1.1"
+ ]
+ exportMetaObjectRevisions: [0, 1]
+ Enum {
+ name: "MultiMatchBehavior"
+ values: {
+ "MMBFirst": 0,
+ "MMBLast": 1,
+ "MMBAverage": 2,
+ "MMBCumulativeY": 3
+ }
+ }
Property { name: "itemModel"; type: "const QAbstractItemModel"; isPointer: true }
Property { name: "rowRole"; type: "string" }
Property { name: "columnRole"; type: "string" }
@@ -1156,6 +1442,17 @@ Module {
Property { name: "useModelCategories"; type: "bool" }
Property { name: "autoRowCategories"; type: "bool" }
Property { name: "autoColumnCategories"; type: "bool" }
+ Property { name: "rowRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "columnRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "xPosRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "yPosRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "zPosRolePattern"; revision: 1; type: "QRegExp" }
+ Property { name: "rowRoleReplace"; revision: 1; type: "string" }
+ Property { name: "columnRoleReplace"; revision: 1; type: "string" }
+ Property { name: "xPosRoleReplace"; revision: 1; type: "string" }
+ Property { name: "yPosRoleReplace"; revision: 1; type: "string" }
+ Property { name: "zPosRoleReplace"; revision: 1; type: "string" }
+ Property { name: "multiMatchBehavior"; revision: 1; type: "MultiMatchBehavior" }
Signal {
name: "itemModelChanged"
Parameter { name: "itemModel"; type: "const QAbstractItemModel"; isPointer: true }
@@ -1192,6 +1489,61 @@ Module {
name: "autoColumnCategoriesChanged"
Parameter { name: "enable"; type: "bool" }
}
+ Signal {
+ name: "rowRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "columnRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "xPosRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "yPosRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "zPosRolePatternChanged"
+ revision: 1
+ Parameter { name: "pattern"; type: "QRegExp" }
+ }
+ Signal {
+ name: "rowRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "columnRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "xPosRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "yPosRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "zPosRoleReplaceChanged"
+ revision: 1
+ Parameter { name: "replace"; type: "string" }
+ }
+ Signal {
+ name: "multiMatchBehaviorChanged"
+ revision: 1
+ Parameter { name: "behavior"; type: "MultiMatchBehavior" }
+ }
Method {
name: "rowCategoryIndex"
type: "int"
@@ -1204,6 +1556,27 @@ Module {
}
}
Component {
+ name: "QtDataVisualization::QLogValue3DAxisFormatter"
+ prototype: "QtDataVisualization::QValue3DAxisFormatter"
+ exports: ["QtDataVisualization/LogValueAxis3DFormatter 1.1"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "base"; type: "double" }
+ Property { name: "autoSubGrid"; type: "bool" }
+ Property { name: "showEdgeLabels"; type: "bool" }
+ Signal {
+ name: "baseChanged"
+ Parameter { name: "base"; type: "double" }
+ }
+ Signal {
+ name: "autoSubGridChanged"
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "showEdgeLabelsChanged"
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ }
+ Component {
name: "QtDataVisualization::QScatter3DSeries"
prototype: "QtDataVisualization::QAbstract3DSeries"
exports: ["QtDataVisualization/QScatter3DSeries 1.0"]
@@ -1366,11 +1739,16 @@ Module {
Component {
name: "QtDataVisualization::QValue3DAxis"
prototype: "QtDataVisualization::QAbstract3DAxis"
- exports: ["QtDataVisualization/ValueAxis3D 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtDataVisualization/ValueAxis3D 1.0",
+ "QtDataVisualization/ValueAxis3D 1.1"
+ ]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "segmentCount"; type: "int" }
Property { name: "subSegmentCount"; type: "int" }
Property { name: "labelFormat"; type: "string" }
+ Property { name: "formatter"; revision: 1; type: "QValue3DAxisFormatter"; isPointer: true }
+ Property { name: "reversed"; revision: 1; type: "bool" }
Signal {
name: "segmentCountChanged"
Parameter { name: "count"; type: "int" }
@@ -1383,5 +1761,21 @@ Module {
name: "labelFormatChanged"
Parameter { name: "format"; type: "string" }
}
+ Signal {
+ name: "formatterChanged"
+ revision: 1
+ Parameter { name: "formatter"; type: "QValue3DAxisFormatter"; isPointer: true }
+ }
+ Signal {
+ name: "reversedChanged"
+ revision: 1
+ Parameter { name: "enable"; type: "bool" }
+ }
+ }
+ Component {
+ name: "QtDataVisualization::QValue3DAxisFormatter"
+ prototype: "QObject"
+ exports: ["QtDataVisualization/ValueAxis3DFormatter 1.1"]
+ exportMetaObjectRevisions: [0]
}
}
diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp
index e7a0e2ca..d3c38d85 100644
--- a/tests/barstest/chart.cpp
+++ b/tests/barstest/chart.cpp
@@ -20,12 +20,14 @@
#include "custominputhandler.h"
#include <QtDataVisualization/qcategory3daxis.h>
#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/qlogvalue3daxisformatter.h>
#include <QtDataVisualization/qbardataproxy.h>
#include <QtDataVisualization/q3dscene.h>
#include <QtDataVisualization/q3dcamera.h>
#include <QtDataVisualization/q3dtheme.h>
#include <QtDataVisualization/q3dinputhandler.h>
-#include <QTime>
+#include <QtCore/QTime>
+#include <QtCore/qmath.h>
using namespace QtDataVisualization;
@@ -42,7 +44,7 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog)
m_barSpacingX(0.1f),
m_barSpacingZ(0.1f),
m_fontSize(20),
- m_segments(4),
+ m_segments(10),
m_subSegments(3),
m_minval(-16.0f),
m_maxval(20.0f),
@@ -69,7 +71,8 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog)
m_defaultInputHandler(0),
m_ownTheme(0),
m_builtinTheme(new Q3DTheme(Q3DTheme::ThemeStoneMoss)),
- m_customInputHandler(new CustomInputHandler)
+ m_customInputHandler(new CustomInputHandler),
+ m_extraSeries(0)
{
m_temperatureData->setObjectName("m_temperatureData");
m_temperatureData2->setObjectName("m_temperatureData2");
@@ -143,7 +146,7 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog)
m_dummyData4->setName("Dummy 4");
m_dummyData5->setName("Dummy 5");
- m_temperatureData->setItemLabelFormat(QStringLiteral("@seriesName: @valueTitle for @colLabel @rowLabel: @valueLabel"));
+ m_temperatureData->setItemLabelFormat(QStringLiteral("@seriesName: @valueTitle for @colLabel @rowLabel: @valueLabel ~ %.4f"));
m_temperatureData2->setItemLabelFormat(QStringLiteral("@seriesName: @valueTitle for @colLabel @rowLabel: @valueLabel"));
m_genericData->setItemLabelFormat(QStringLiteral("@seriesName: @valueTitle for (@rowIdx, @colIdx): @valueLabel"));
@@ -221,6 +224,8 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog)
&GraphModifier::handleValueAxisChanged);
QObject::connect(m_graph, &Q3DBars::primarySeriesChanged, this,
&GraphModifier::handlePrimarySeriesChanged);
+ QObject::connect(m_temperatureAxis, &QAbstract3DAxis::labelsChanged, this,
+ &GraphModifier::handleValueAxisLabelsChanged);
QObject::connect(&m_insertRemoveTimer, &QTimer::timeout, this,
&GraphModifier::insertRemoveTimerTimeout);
@@ -233,6 +238,10 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog)
QObject::connect(&m_rotationTimer, &QTimer::timeout, this,
&GraphModifier::triggerRotation);
+ QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
+ &GraphModifier::handleFpsChange);
+
+
resetTemperatureData();
}
@@ -304,7 +313,7 @@ void GraphModifier::swapAxis()
void GraphModifier::releaseAxes()
{
- // Releases all axes - results in default axes for all dimensions.
+ // Releases all axes we have created - results in default axes for all dimensions.
// Axes reset when the graph is switched as set*Axis calls are made, which
// implicitly add axes.
m_graph->releaseAxis(m_autoAdjustingAxis);
@@ -316,23 +325,10 @@ void GraphModifier::releaseAxes()
m_graph->releaseAxis(m_genericColumnAxis);
}
-void GraphModifier::releaseProxies()
+void GraphModifier::releaseSeries()
{
- // Releases all series/add all series toggle
- if (m_graph->seriesList().size() > 0) {
- m_graph->removeSeries(m_temperatureData);
- m_graph->removeSeries(m_temperatureData2);
- m_graph->removeSeries(m_genericData);
- m_graph->removeSeries(m_dummyData);
- m_graph->removeSeries(m_dummyData2);
- m_graph->removeSeries(m_dummyData3);
- m_graph->removeSeries(m_dummyData4);
- m_graph->removeSeries(m_dummyData5);
- } else {
- m_graph->addSeries(m_temperatureData);
- m_graph->addSeries(m_temperatureData2);
- m_graph->addSeries(m_genericData);
- }
+ foreach (QBar3DSeries *series, m_graph->seriesList())
+ m_graph->removeSeries(series);
}
void GraphModifier::flipViews()
@@ -762,7 +758,7 @@ void GraphModifier::changeShadowQuality(int quality)
void GraphModifier::showFiveSeries()
{
- releaseProxies();
+ releaseSeries();
releaseAxes();
m_graph->setSelectionMode(QAbstract3DGraph::SelectionItemRowAndColumn | QAbstract3DGraph::SelectionMultiSeries);
@@ -829,7 +825,7 @@ void GraphModifier::primarySeriesTest()
case 0: {
qDebug() << "Step 0 - Init:";
m_graph->addSeries(m_dummyData); // Add one series to enforce release in releaseProxies()
- releaseProxies();
+ releaseSeries();
releaseAxes();
m_dummyData->dataProxy()->resetArray(makeDummyData(),
testLabels,
@@ -1008,8 +1004,6 @@ void GraphModifier::primarySeriesTest()
nextStep = 0;
break;
}
-
-
}
void GraphModifier::insertRemoveTestToggle()
@@ -1019,11 +1013,11 @@ void GraphModifier::insertRemoveTestToggle()
m_selectionTimer.stop();
m_graph->removeSeries(m_dummyData);
m_graph->removeSeries(m_dummyData2);
- releaseProxies();
+ releaseSeries();
releaseAxes();
m_graph->setActiveInputHandler(m_defaultInputHandler);
} else {
- releaseProxies();
+ releaseSeries();
releaseAxes();
m_graph->rowAxis()->setRange(0, 32);
m_graph->columnAxis()->setRange(0, 10);
@@ -1044,6 +1038,381 @@ void GraphModifier::toggleRotation()
m_rotationTimer.start(20);
}
+void GraphModifier::useLogAxis()
+{
+ static int counter = -1;
+ static QLogValue3DAxisFormatter *logFormatter = 0;
+ static float minRange = 1.0f;
+ counter++;
+
+ switch (counter) {
+ case 0: {
+ qDebug() << "Case" << counter << ": Default log axis";
+ logFormatter = new QLogValue3DAxisFormatter;
+ m_graph->valueAxis()->setFormatter(logFormatter);
+ m_graph->valueAxis()->setRange(minRange, 1200.0f);
+ m_graph->valueAxis()->setLabelFormat(QStringLiteral("%.3f"));
+ break;
+ }
+ case 1: {
+ qDebug() << "Case" << counter << ": Hide max label";
+ logFormatter->setShowEdgeLabels(false);
+ break;
+ }
+ case 2: {
+ qDebug() << "Case" << counter << ": Try to hide subgrid unsuccessfully";
+ m_graph->valueAxis()->setSubSegmentCount(1);
+ break;
+ }
+ case 3: {
+ qDebug() << "Case" << counter << ": Hide subgrid property";
+ logFormatter->setAutoSubGrid(false);
+ m_graph->valueAxis()->setSubSegmentCount(1);
+ break;
+ }
+ case 4: {
+ qDebug() << "Case" << counter << ": Different base: 2";
+ logFormatter->setBase(2.0f);
+ logFormatter->setAutoSubGrid(true);
+ break;
+ }
+ case 5: {
+ qDebug() << "Case" << counter << ": Different base: 16";
+ logFormatter->setBase(16.0f);
+ break;
+ }
+ case 6: {
+ qDebug() << "Case" << counter << ": Invalid bases";
+ logFormatter->setBase(-1.0f);
+ logFormatter->setBase(1.0f);
+ break;
+ }
+ case 7: {
+ qDebug() << "Case" << counter << ": Zero base";
+ logFormatter->setBase(0.0f);
+ break;
+ }
+ case 8: {
+ qDebug() << "Case" << counter << ": Explicit ranges and segments";
+ int segmentCount = 6;
+ int base = 4;
+ m_graph->valueAxis()->setSegmentCount(segmentCount);
+ m_graph->valueAxis()->setSubSegmentCount(base - 1);
+ m_graph->valueAxis()->setRange(1.0f / float(base * base), qPow(base, segmentCount - 2));
+ break;
+ }
+ case 9: {
+ qDebug() << "Case" << counter << ": Negative range";
+ m_graph->valueAxis()->setRange(-20.0f, 50.0f);
+ break;
+ }
+ case 10: {
+ qDebug() << "Case" << counter << ": Non-integer base";
+ logFormatter->setBase(3.3f);
+ logFormatter->setAutoSubGrid(true);
+ break;
+ }
+ default:
+ qDebug() << "Resetting logaxis test";
+ minRange++;
+ counter = -1;
+ break;
+ }
+}
+
+void GraphModifier::changeValueAxisFormat(const QString & text)
+{
+ m_graph->valueAxis()->setLabelFormat(text);
+}
+
+void GraphModifier::changeLogBase(const QString &text)
+{
+ QLogValue3DAxisFormatter *formatter =
+ qobject_cast<QLogValue3DAxisFormatter *>(m_graph->valueAxis()->formatter());
+ if (formatter)
+ formatter->setBase(qreal(text.toDouble()));
+}
+
+void GraphModifier::addRemoveSeries()
+{
+ static int counter = 0;
+
+ switch (counter) {
+ case 0: {
+ qDebug() << __FUNCTION__ << counter << "New series";
+ m_extraSeries = new QBar3DSeries;
+ m_graph->addSeries(m_extraSeries);
+ QObject::connect(m_extraSeries, &QBar3DSeries::selectedBarChanged, this,
+ &GraphModifier::handleSelectionChange);
+ }
+ break;
+ case 1: {
+ qDebug() << __FUNCTION__ << counter << "Add data to series";
+ QBarDataArray *array = new QBarDataArray;
+ array->reserve(5);
+ for (int i = 0; i < 5; i++) {
+ array->append(new QBarDataRow(10));
+ for (int j = 0; j < 10; j++)
+ (*array->at(i))[j].setValue(i * j);
+ }
+ m_extraSeries->dataProxy()->resetArray(array);
+ }
+ break;
+ case 2: {
+ qDebug() << __FUNCTION__ << counter << "Hide series";
+ m_extraSeries->setVisible(false);
+ }
+ break;
+ case 3: {
+ qDebug() << __FUNCTION__ << counter << "Modify data when hidden";
+ QBarDataArray array;
+ array.reserve(5);
+ for (int i = 0; i < 5; i++) {
+ array.append(new QBarDataRow(10));
+ for (int j = 0; j < 10; j++)
+ (*array.at(i))[j].setValue(2 * i * j);
+ }
+ m_extraSeries->dataProxy()->addRows(array);
+ }
+ break;
+ case 4: {
+ qDebug() << __FUNCTION__ << counter << "Show series again";
+ m_extraSeries->setVisible(true);
+ }
+ break;
+ case 5: {
+ qDebug() << __FUNCTION__ << counter << "Remove series";
+ m_graph->removeSeries(m_extraSeries);
+ delete m_extraSeries;
+ m_extraSeries = 0;
+ }
+ break;
+ default:
+ qDebug() << __FUNCTION__ << "Resetting test";
+ counter = -1;
+ }
+ counter++;
+}
+
+void GraphModifier::testItemAndRowChanges()
+{
+ static int counter = 0;
+ const int rowCount = 12;
+ const int colCount = 10;
+ const float flatValue = 10.0f;
+ static QBar3DSeries *series0 = 0;
+ static QBar3DSeries *series1 = 0;
+ static QBar3DSeries *series2 = 0;
+ QBarDataItem item25;
+ QBarDataItem item50;
+ QBarDataItem item75;
+ item25.setValue(25);
+ item50.setValue(50);
+ item75.setValue(75);
+
+ switch (counter) {
+ case 0: {
+ qDebug() << __FUNCTION__ << counter << "Setup test";
+ releaseSeries();
+ releaseAxes();
+ delete series0;
+ delete series1;
+ delete series2;
+ series0 = new QBar3DSeries;
+ series1 = new QBar3DSeries;
+ series2 = new QBar3DSeries;
+ populateFlatSeries(series0, rowCount, colCount, flatValue);
+ populateFlatSeries(series1, rowCount, colCount, flatValue);
+ populateFlatSeries(series2, rowCount, colCount, flatValue);
+ m_graph->rowAxis()->setRange(4.0f, 8.0f);
+ m_graph->columnAxis()->setRange(3.0f, 6.0f);
+ m_graph->valueAxis()->setRange(0.0f, 100.0f);
+ m_graph->addSeries(series0);
+ m_graph->addSeries(series1);
+ m_graph->addSeries(series2);
+ //counter = 11; // skip single item tests
+ }
+ break;
+ case 1: {
+ qDebug() << __FUNCTION__ << counter << "Change single item, unselected";
+ series0->dataProxy()->setItem(4, 3, item50);
+ }
+ break;
+ case 2: {
+ qDebug() << __FUNCTION__ << counter << "Change single item, selected";
+ series1->setSelectedBar(QPoint(4, 5));
+ series1->dataProxy()->setItem(4, 5, item25);
+ }
+ break;
+ case 3: {
+ qDebug() << __FUNCTION__ << counter << "Change item outside visible area";
+ series1->dataProxy()->setItem(0, 3, item25);
+ }
+ break;
+ case 4: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, unselected";
+ series0->dataProxy()->setItem(5, 3, item25);
+ series1->dataProxy()->setItem(5, 3, item75);
+ }
+ break;
+ case 5: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, one selected";
+ series0->dataProxy()->setItem(5, 4, item25);
+ series1->dataProxy()->setItem(4, 5, item75);
+ }
+ break;
+ case 6: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, one outside range";
+ series0->dataProxy()->setItem(1, 2, item25);
+ series1->dataProxy()->setItem(6, 6, item75);
+ }
+ break;
+ case 7: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, both outside range";
+ series0->dataProxy()->setItem(1, 2, item25);
+ series1->dataProxy()->setItem(8, 8, item75);
+ }
+ break;
+ case 8: {
+ qDebug() << __FUNCTION__ << counter << "Change item to same value";
+ series1->dataProxy()->setItem(6, 6, item75);
+ }
+ break;
+ case 9: {
+ qDebug() << __FUNCTION__ << counter << "Change 3 items on each series";
+ series0->dataProxy()->setItem(7, 3, item25);
+ series0->dataProxy()->setItem(7, 4, item50);
+ series0->dataProxy()->setItem(7, 5, item75);
+ series1->dataProxy()->setItem(6, 3, item25);
+ series1->dataProxy()->setItem(6, 4, item50);
+ series1->dataProxy()->setItem(6, 5, item75);
+ }
+ break;
+ case 10: {
+ qDebug() << __FUNCTION__ << counter << "Level the field single item at a time";
+ QBarDataItem item;
+ item.setValue(15.0f);
+ for (int i = 0; i < rowCount; i++) {
+ for (int j = 0; j < colCount; j++) {
+ series0->dataProxy()->setItem(i, j, item);
+ series1->dataProxy()->setItem(i, j, item);
+ series2->dataProxy()->setItem(i, j, item);
+ }
+ }
+ }
+ break;
+ case 11: {
+ qDebug() << __FUNCTION__ << counter << "Change same items multiple times";
+ series0->dataProxy()->setItem(7, 3, item25);
+ series1->dataProxy()->setItem(7, 3, item25);
+ series0->dataProxy()->setItem(7, 3, item50);
+ series1->dataProxy()->setItem(7, 3, item50);
+ series0->dataProxy()->setItem(7, 3, item75);
+ series1->dataProxy()->setItem(7, 3, item75);
+ }
+ break;
+ case 12: {
+ qDebug() << __FUNCTION__ << counter << "Change row";
+ series0->dataProxy()->setRow(5, createFlatRow(colCount, 50.0f));
+ }
+ break;
+ case 13: {
+ qDebug() << __FUNCTION__ << counter << "Change row with selected item";
+ series1->setSelectedBar(QPoint(6, 6));
+ series1->dataProxy()->setRow(6, createFlatRow(colCount, 40.0f));
+ }
+ break;
+ case 14: {
+ qDebug() << __FUNCTION__ << counter << "Change hidden row";
+ series1->dataProxy()->setRow(9, createFlatRow(colCount, 50.0f));
+ }
+ break;
+ case 15: {
+ qDebug() << __FUNCTION__ << counter << "Change multiple rows singly";
+ series0->dataProxy()->setRow(6, createFlatRow(colCount, 70.0f));
+ series1->dataProxy()->setRow(6, createFlatRow(colCount, 80.0f));
+ series2->dataProxy()->setRow(6, createFlatRow(colCount, 90.0f));
+ }
+ break;
+ case 16: {
+ qDebug() << __FUNCTION__ << counter << "Change multiple rows many at a time";
+ QBarDataArray newRows;
+ newRows.reserve(4);
+ newRows.append(createFlatRow(colCount, 26.0f));
+ newRows.append(createFlatRow(colCount, 30.0f));
+ newRows.append(createFlatRow(colCount, 34.0f));
+ newRows.append(createFlatRow(colCount, 38.0f));
+ series0->dataProxy()->setRows(2, newRows);
+ newRows[0] = createFlatRow(colCount, 26.0f);
+ newRows[1] = createFlatRow(colCount, 30.0f);
+ newRows[2] = createFlatRow(colCount, 34.0f);
+ newRows[3] = createFlatRow(colCount, 38.0f);
+ series1->dataProxy()->setRows(3, newRows);
+ newRows[0] = createFlatRow(colCount, 26.0f);
+ newRows[1] = createFlatRow(colCount, 30.0f);
+ newRows[2] = createFlatRow(colCount, 34.0f);
+ newRows[3] = createFlatRow(colCount, 38.0f);
+ series2->dataProxy()->setRows(4, newRows);
+ }
+ break;
+ case 17: {
+ qDebug() << __FUNCTION__ << counter << "Change same rows multiple times";
+ QBarDataArray newRows;
+ newRows.reserve(4);
+ newRows.append(createFlatRow(colCount, 65.0f));
+ newRows.append(createFlatRow(colCount, 65.0f));
+ newRows.append(createFlatRow(colCount, 65.0f));
+ newRows.append(createFlatRow(colCount, 65.0f));
+ series0->dataProxy()->setRows(4, newRows);
+ newRows[0] = createFlatRow(colCount, 65.0f);
+ newRows[1] = createFlatRow(colCount, 65.0f);
+ newRows[2] = createFlatRow(colCount, 65.0f);
+ newRows[3] = createFlatRow(colCount, 65.0f);
+ series1->dataProxy()->setRows(4, newRows);
+ newRows[0] = createFlatRow(colCount, 65.0f);
+ newRows[1] = createFlatRow(colCount, 65.0f);
+ newRows[2] = createFlatRow(colCount, 65.0f);
+ newRows[3] = createFlatRow(colCount, 65.0f);
+ series2->dataProxy()->setRows(4, newRows);
+ series0->dataProxy()->setRow(6, createFlatRow(colCount, 20.0f));
+ series1->dataProxy()->setRow(6, createFlatRow(colCount, 20.0f));
+ series2->dataProxy()->setRow(6, createFlatRow(colCount, 20.0f));
+ series0->dataProxy()->setRow(6, createFlatRow(colCount, 90.0f));
+ series1->dataProxy()->setRow(6, createFlatRow(colCount, 90.0f));
+ series2->dataProxy()->setRow(6, createFlatRow(colCount, 90.0f));
+ }
+ break;
+ case 18: {
+ qDebug() << __FUNCTION__ << counter << "Change row to different length";
+ series0->dataProxy()->setRow(4, createFlatRow(5, 20.0f));
+ series1->dataProxy()->setRow(4, createFlatRow(0, 20.0f));
+ series2->dataProxy()->setRow(4, 0);
+ }
+ break;
+ case 19: {
+ qDebug() << __FUNCTION__ << counter << "Change selected row shorter so that selected item is no longer valid";
+ series1->dataProxy()->setRow(6, createFlatRow(6, 20.0f));
+ }
+ break;
+ default:
+ qDebug() << __FUNCTION__ << "Resetting test";
+ counter = -1;
+ }
+ counter++;
+}
+
+void GraphModifier::reverseValueAxis(int enabled)
+{
+ m_graph->valueAxis()->setReversed(enabled);
+}
+
+void GraphModifier::changeValueAxisSegments(int value)
+{
+ qDebug() << __FUNCTION__ << value;
+ m_segments = value;
+ m_graph->valueAxis()->setSegmentCount(m_segments);
+}
+
void GraphModifier::insertRemoveTimerTimeout()
{
if (m_insertRemoveStep < 32) {
@@ -1100,6 +1469,43 @@ void GraphModifier::triggerRotation()
}
}
+void GraphModifier::handleValueAxisLabelsChanged()
+{
+ qDebug() << __FUNCTION__;
+}
+
+void GraphModifier::handleFpsChange(qreal fps)
+{
+ static const QString fpsPrefix(QStringLiteral("FPS: "));
+ m_fpsLabel->setText(fpsPrefix + QString::number(qRound(fps)));
+}
+
+void GraphModifier::populateFlatSeries(QBar3DSeries *series, int rows, int columns, float value)
+{
+ QBarDataArray *dataArray = new QBarDataArray;
+ dataArray->reserve(rows);
+ for (int i = 0; i < rows; i++) {
+ QBarDataRow *dataRow = new QBarDataRow(columns);
+ for (int j = 0; j < columns; j++)
+ (*dataRow)[j].setValue(value);
+ dataArray->append(dataRow);
+ }
+ QStringList axisLabels;
+ int count = qMax(rows, columns);
+ for (int i = 0; i < count; i++)
+ axisLabels << QString::number(i);
+
+ series->dataProxy()->resetArray(dataArray, axisLabels, axisLabels);
+}
+
+QBarDataRow *GraphModifier::createFlatRow(int columns, float value)
+{
+ QBarDataRow *dataRow = new QBarDataRow(columns);
+ for (int j = 0; j < columns; j++)
+ (*dataRow)[j].setValue(value);
+ return dataRow;
+}
+
void GraphModifier::setBackgroundEnabled(int enabled)
{
m_graph->activeTheme()->setBackgroundEnabled(bool(enabled));
@@ -1122,6 +1528,11 @@ void GraphModifier::rotateY(int rotation)
m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation);
}
+void GraphModifier::setFpsMeasurement(bool enable)
+{
+ m_graph->setMeasureFps(enable);
+}
+
void GraphModifier::setSpecsRatio(int barwidth)
{
m_graph->setBarThickness((float)barwidth / 30.0f);
diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h
index 5758262c..47d29c25 100644
--- a/tests/barstest/chart.h
+++ b/tests/barstest/chart.h
@@ -29,6 +29,7 @@
#include <QPointer>
#include <QColorDialog>
#include <QTimer>
+#include <QLabel>
using namespace QtDataVisualization;
@@ -58,6 +59,7 @@ public:
void changeFontSize(int fontsize);
void rotateX(int rotation);
void rotateY(int rotation);
+ void setFpsMeasurement(bool enable);
void setBackgroundEnabled(int enabled);
void setGridEnabled(int enabled);
void setSpecsRatio(int barwidth);
@@ -75,7 +77,7 @@ public:
void selectBar();
void swapAxis();
void releaseAxes();
- void releaseProxies();
+ void releaseSeries();
void createMassiveArray();
void useOwnTheme();
void changeBaseColor(const QColor &color);
@@ -85,6 +87,13 @@ public:
void primarySeriesTest();
void insertRemoveTestToggle();
void toggleRotation();
+ void useLogAxis();
+ void changeValueAxisFormat(const QString & text);
+ void changeLogBase(const QString & text);
+ void setFpsLabel(QLabel *fpsLabel) { m_fpsLabel = fpsLabel; }
+ void addRemoveSeries();
+ void testItemAndRowChanges();
+ void reverseValueAxis(int enabled);
public slots:
void flipViews();
@@ -94,6 +103,7 @@ public slots:
void shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality shadowQuality);
void handleSelectionChange(const QPoint &position);
void setUseNullInputHandler(bool useNull);
+ void changeValueAxisSegments(int value);
void handleRowAxisChanged(QCategory3DAxis *axis);
void handleColumnAxisChanged(QCategory3DAxis *axis);
@@ -103,11 +113,16 @@ public slots:
void insertRemoveTimerTimeout();
void triggerSelection();
void triggerRotation();
+ void handleValueAxisLabelsChanged();
+ void handleFpsChange(qreal fps);
signals:
void shadowQualityChanged(int quality);
private:
+ void populateFlatSeries(QBar3DSeries *series, int rows, int columns, float value);
+ QBarDataRow *createFlatRow(int columns, float value);
+
Q3DBars *m_graph;
QColorDialog *m_colorDialog;
int m_columnCount;
@@ -152,6 +167,8 @@ private:
QAbstract3DInputHandler *m_customInputHandler;
QTimer m_selectionTimer;
QTimer m_rotationTimer;
+ QLabel *m_fpsLabel;
+ QBar3DSeries *m_extraSeries;
};
#endif
diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp
index 8189f917..5ecf63a4 100644
--- a/tests/barstest/main.cpp
+++ b/tests/barstest/main.cpp
@@ -32,6 +32,8 @@
#include <QLinearGradient>
#include <QPainter>
#include <QColorDialog>
+#include <QLineEdit>
+#include <QSpinBox>
int main(int argc, char **argv)
{
@@ -53,7 +55,7 @@ int main(int argc, char **argv)
QSize screenSize = widgetchart->screen()->size();
QWidget *container = QWidget::createWindowContainer(widgetchart);
- container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 2));
+ container->setMinimumSize(QSize(screenSize.width() / 3, screenSize.height() / 3));
container->setMaximumSize(screenSize);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
container->setFocusPolicy(Qt::StrongFocus);
@@ -64,6 +66,10 @@ int main(int argc, char **argv)
hLayout->addLayout(vLayout);
hLayout->addLayout(vLayout2);
+ QPushButton *addSeriesButton = new QPushButton(widget);
+ addSeriesButton->setText(QStringLiteral("Add / Remove a series"));
+ addSeriesButton->setEnabled(true);
+
QPushButton *addDataButton = new QPushButton(widget);
addDataButton->setText(QStringLiteral("Add a row of data"));
addDataButton->setEnabled(false);
@@ -165,6 +171,14 @@ int main(int argc, char **argv)
toggleRotationButton->setText(QStringLiteral("Toggle rotation"));
toggleRotationButton->setEnabled(true);
+ QPushButton *logAxisButton = new QPushButton(widget);
+ logAxisButton->setText(QStringLiteral("Use Log Axis"));
+ logAxisButton->setEnabled(true);
+
+ QPushButton *testItemAndRowChangesButton = new QPushButton(widget);
+ testItemAndRowChangesButton->setText(QStringLiteral("Test Item/Row changing"));
+ testItemAndRowChangesButton->setEnabled(true);
+
QColorDialog *colorDialog = new QColorDialog(widget);
QLinearGradient grBtoY(0, 0, 100, 0);
@@ -181,6 +195,16 @@ int main(int argc, char **argv)
gradientBtoYPB->setIcon(QIcon(pm));
gradientBtoYPB->setIconSize(QSize(100, 24));
+ QLabel *fpsLabel = new QLabel(QStringLiteral(""));
+
+ QCheckBox *fpsCheckBox = new QCheckBox(widget);
+ fpsCheckBox->setText(QStringLiteral("Measure Fps"));
+ fpsCheckBox->setChecked(false);
+
+ QCheckBox *reverseValueAxisCheckBox = new QCheckBox(widget);
+ reverseValueAxisCheckBox->setText(QStringLiteral("Reverse value axis"));
+ reverseValueAxisCheckBox->setChecked(false);
+
QCheckBox *backgroundCheckBox = new QCheckBox(widget);
backgroundCheckBox->setText(QStringLiteral("Show background"));
backgroundCheckBox->setChecked(true);
@@ -286,6 +310,14 @@ int main(int argc, char **argv)
shadowQuality->addItem(QStringLiteral("High Soft"));
shadowQuality->setCurrentIndex(5);
+ QLineEdit *valueAxisFormatEdit = new QLineEdit(widget);
+ QLineEdit *logBaseEdit = new QLineEdit(widget);
+ QSpinBox *valueAxisSegmentsSpin = new QSpinBox(widget);
+ valueAxisSegmentsSpin->setMinimum(1);
+ valueAxisSegmentsSpin->setMaximum(100);
+ valueAxisSegmentsSpin->setValue(10);
+
+ vLayout->addWidget(addSeriesButton, 0, Qt::AlignTop);
vLayout->addWidget(addDataButton, 0, Qt::AlignTop);
vLayout->addWidget(addMultiDataButton, 0, Qt::AlignTop);
vLayout->addWidget(insertDataButton, 0, Qt::AlignTop);
@@ -313,7 +345,9 @@ int main(int argc, char **argv)
vLayout->addWidget(ownThemeButton, 0, Qt::AlignTop);
vLayout->addWidget(primarySeriesTestsButton, 0, Qt::AlignTop);
vLayout->addWidget(toggleRotationButton, 0, Qt::AlignTop);
- vLayout->addWidget(gradientBtoYPB, 1, Qt::AlignTop);
+ vLayout->addWidget(gradientBtoYPB, 0, Qt::AlignTop);
+ vLayout->addWidget(logAxisButton, 0, Qt::AlignTop);
+ vLayout->addWidget(testItemAndRowChangesButton, 1, Qt::AlignTop);
vLayout2->addWidget(staticCheckBox, 0, Qt::AlignTop);
vLayout2->addWidget(rotationCheckBox, 0, Qt::AlignTop);
@@ -332,6 +366,9 @@ int main(int argc, char **argv)
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);
@@ -339,7 +376,13 @@ int main(int argc, char **argv)
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, 1, 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
widget->show();
@@ -375,6 +418,12 @@ int main(int argc, char **argv)
&QComboBox::setCurrentIndex);
QObject::connect(fontSizeSlider, &QSlider::valueChanged, modifier,
&GraphModifier::changeFontSize);
+ QObject::connect(valueAxisFormatEdit, &QLineEdit::textEdited, modifier,
+ &GraphModifier::changeValueAxisFormat);
+ QObject::connect(logBaseEdit, &QLineEdit::textEdited, modifier,
+ &GraphModifier::changeLogBase);
+ QObject::connect(valueAxisSegmentsSpin, SIGNAL(valueChanged(int)), modifier,
+ SLOT(changeValueAxisSegments(int)));
QObject::connect(multiScaleButton, &QPushButton::clicked, modifier,
&GraphModifier::toggleMultiseriesScaling);
@@ -385,6 +434,7 @@ int main(int argc, char **argv)
QObject::connect(labelButton, &QPushButton::clicked, modifier,
&GraphModifier::changeLabelStyle);
QObject::connect(addDataButton, &QPushButton::clicked, modifier, &GraphModifier::addRow);
+ QObject::connect(addSeriesButton, &QPushButton::clicked, modifier, &GraphModifier::addRemoveSeries);
QObject::connect(addMultiDataButton, &QPushButton::clicked, modifier, &GraphModifier::addRows);
QObject::connect(insertDataButton, &QPushButton::clicked, modifier, &GraphModifier::insertRow);
QObject::connect(insertMultiDataButton, &QPushButton::clicked, modifier, &GraphModifier::insertRows);
@@ -406,7 +456,7 @@ int main(int argc, char **argv)
QObject::connect(releaseAxesButton, &QPushButton::clicked, modifier,
&GraphModifier::releaseAxes);
QObject::connect(releaseProxiesButton, &QPushButton::clicked, modifier,
- &GraphModifier::releaseProxies);
+ &GraphModifier::releaseSeries);
QObject::connect(flipViewsButton, &QPushButton::clicked, modifier,
&GraphModifier::flipViews);
@@ -418,6 +468,10 @@ int main(int argc, char **argv)
&GraphModifier::primarySeriesTest);
QObject::connect(toggleRotationButton, &QPushButton::clicked, modifier,
&GraphModifier::toggleRotation);
+ QObject::connect(logAxisButton, &QPushButton::clicked, modifier,
+ &GraphModifier::useLogAxis);
+ QObject::connect(testItemAndRowChangesButton, &QPushButton::clicked, modifier,
+ &GraphModifier::testItemAndRowChanges);
QObject::connect(colorDialog, &QColorDialog::currentColorChanged, modifier,
&GraphModifier::changeBaseColor);
QObject::connect(gradientBtoYPB, &QPushButton::clicked, modifier,
@@ -426,6 +480,10 @@ int main(int argc, char **argv)
QObject::connect(fontList, &QFontComboBox::currentFontChanged, modifier,
&GraphModifier::changeFont);
+ QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setFpsMeasurement);
+ QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::reverseValueAxis);
QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setBackgroundEnabled);
QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier,
@@ -479,6 +537,8 @@ int main(int argc, char **argv)
&QSlider::setEnabled);
QObject::connect(staticCheckBox, &QCheckBox::stateChanged, modifier, &GraphModifier::restart);
+ modifier->setFpsLabel(fpsLabel);
+
modifier->start();
return app.exec();
diff --git a/tests/itemmodeltest/itemmodeltest.pro b/tests/itemmodeltest/itemmodeltest.pro
new file mode 100644
index 00000000..47f12915
--- /dev/null
+++ b/tests/itemmodeltest/itemmodeltest.pro
@@ -0,0 +1,7 @@
+!include( ../tests.pri ) {
+ error( "Couldn't find the tests.pri file!" )
+}
+
+SOURCES += main.cpp
+
+QT += widgets
diff --git a/tests/itemmodeltest/main.cpp b/tests/itemmodeltest/main.cpp
new file mode 100644
index 00000000..b2231594
--- /dev/null
+++ b/tests/itemmodeltest/main.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** 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 <QtDataVisualization/q3dbars.h>
+#include <QtDataVisualization/q3dsurface.h>
+#include <QtDataVisualization/qcategory3daxis.h>
+#include <QtDataVisualization/qitemmodelbardataproxy.h>
+#include <QtDataVisualization/qitemmodelsurfacedataproxy.h>
+#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/qbar3dseries.h>
+#include <QtDataVisualization/q3dtheme.h>
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QTableWidget>
+#include <QtGui/QScreen>
+#include <QtCore/QTimer>
+#include <QtCore/QDebug>
+#include <QtWidgets/QHeaderView>
+#include <QtWidgets/QPushButton>
+
+#define USE_STATIC_DATA
+
+using namespace QtDataVisualization;
+
+class GraphDataGenerator : public QObject
+{
+public:
+ explicit GraphDataGenerator(Q3DBars *bargraph, Q3DSurface * surfaceGraph,
+ QTableWidget *tableWidget);
+ ~GraphDataGenerator();
+
+ void setupModel();
+ void addRow();
+ void start();
+ void selectFromTable(const QPoint &selection);
+ void selectedFromTable(int currentRow, int currentColumn, int previousRow, int previousColumn);
+ void fixTableSize();
+ void changeSelectedButtonClicked();
+
+private:
+ Q3DBars *m_barGraph;
+ Q3DSurface *m_surfaceGraph;
+ QTimer *m_dataTimer;
+ QTimer *m_styleTimer;
+ QTimer *m_presetTimer;
+ QTimer *m_themeTimer;
+ int m_columnCount;
+ int m_rowCount;
+ QTableWidget *m_tableWidget; // not owned
+};
+
+GraphDataGenerator::GraphDataGenerator(Q3DBars *bargraph, Q3DSurface * surfaceGraph,
+ QTableWidget *tableWidget)
+ : m_barGraph(bargraph),
+ m_surfaceGraph(surfaceGraph),
+ m_dataTimer(0),
+ m_styleTimer(0),
+ m_presetTimer(0),
+ m_themeTimer(0),
+ m_columnCount(100),
+ m_rowCount(50),
+ m_tableWidget(tableWidget)
+{
+ // Set up bar specifications; make the bars as wide as they are deep,
+ // and add a small space between them
+ m_barGraph->setBarThickness(1.0f);
+ m_barGraph->setBarSpacing(QSizeF(0.2, 0.2));
+
+#ifndef USE_STATIC_DATA
+ // Set up sample space; make it as deep as it's wide
+ m_barGraph->rowAxis()->setRange(0, m_rowCount);
+ m_barGraph->columnAxis()->setRange(0, m_columnCount);
+ m_tableWidget->setColumnCount(m_columnCount);
+
+ // Set selection mode to full
+ m_barGraph->setSelectionMode(QAbstract3DGraph::SelectionItemRowAndColumn);
+
+ // Hide axis labels by explicitly setting one empty string as label list
+ m_barGraph->rowAxis()->setLabels(QStringList(QString()));
+ m_barGraph->columnAxis()->setLabels(QStringList(QString()));
+
+ m_barGraph->seriesList().at(0)->setItemLabelFormat(QStringLiteral("@valueLabel"));
+#else
+ // Set selection mode to slice row
+ m_barGraph->setSelectionMode(
+ QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice);
+ m_surfaceGraph->setSelectionMode(
+ QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice);
+#endif
+}
+
+GraphDataGenerator::~GraphDataGenerator()
+{
+ if (m_dataTimer) {
+ m_dataTimer->stop();
+ delete m_dataTimer;
+ }
+ delete m_barGraph;
+ delete m_surfaceGraph;
+}
+
+void GraphDataGenerator::start()
+{
+#ifndef USE_STATIC_DATA
+ m_dataTimer = new QTimer();
+ m_dataTimer->setTimerType(Qt::CoarseTimer);
+ QObject::connect(m_dataTimer, &QTimer::timeout, this, &GraphDataGenerator::addRow);
+ m_dataTimer->start(0);
+ m_tableWidget->setFixedWidth(m_graph->width());
+#else
+ setupModel();
+
+ // Table needs to be shown before the size of its headers can be accurately obtained,
+ // so we postpone it a bit
+ m_dataTimer = new QTimer();
+ m_dataTimer->setSingleShot(true);
+ QObject::connect(m_dataTimer, &QTimer::timeout, this, &GraphDataGenerator::fixTableSize);
+ m_dataTimer->start(0);
+#endif
+}
+
+void GraphDataGenerator::setupModel()
+{
+ // Set up row and column names
+ QStringList days;
+ days << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday" << "Saturday" << "Sunday";
+ QStringList weeks;
+ weeks << "week 1" << "week 2" << "week 3" << "week 4" << "week 5";
+
+ // Set up data
+ const char *hours[5][7] =
+ // Mon Tue Wed Thu Fri Sat Sun
+ {{"9/10/2.0/30", "9/11/1.0/30", "9/12/3.0/30", "9/13/0.2/30", "9/14/1.0/30", "9/15/5.0/30", "9/16/10.0/30"}, // week 1
+ {"8/10/0.5/45", "8/11/1.0/45", "8/12/3.0/45", "8/13/1.0/45", "8/14/2.0/45", "8/15/2.0/45", "8/16/3.0/45"}, // week 2
+ {"7/10/1.0/60", "7/11/1.0/60", "7/12/2.0/60", "7/13/1.0/60", "7/14/4.0/60", "7/15/4.0/60", "7/16/4.0/60"}, // week 3
+ {"6/10/0.0/75", "6/11/1.0/75", "6/12/0.0/75", "6/13/0.0/75", "6/14/2.0/75", "6/15/2.0/75", "6/16/0.3/75"}, // week 4
+ {"5/10/3.0/90", "5/11/3.0/90", "5/12/6.0/90", "5/13/2.0/90", "5/14/2.0/90", "5/15/1.0/90", "5/16/1.0/90"}}; // week 5
+
+ // Add labels
+ m_barGraph->rowAxis()->setTitle("Week of year");
+ m_barGraph->columnAxis()->setTitle("Day of week");
+ m_barGraph->valueAxis()->setTitle("Hours spent on the Internet");
+ m_barGraph->valueAxis()->setLabelFormat("%.1f h");
+
+ m_surfaceGraph->axisZ()->setTitle("Week of year");
+ m_surfaceGraph->axisX()->setTitle("Day of week");
+ m_surfaceGraph->axisY()->setTitle("Hours spent on the Internet");
+ m_surfaceGraph->axisY()->setLabelFormat("%.1f h");
+
+ m_tableWidget->setRowCount(5);
+ m_tableWidget->setColumnCount(7);
+ m_tableWidget->setHorizontalHeaderLabels(days);
+ m_tableWidget->setVerticalHeaderLabels(weeks);
+ m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_tableWidget->setCurrentCell(-1, -1);
+
+ for (int week = 0; week < weeks.size(); week++) {
+ for (int day = 0; day < days.size(); day++) {
+ QModelIndex index = m_tableWidget->model()->index(week, day);
+ m_tableWidget->model()->setData(index, hours[week][day]);
+ }
+ }
+}
+
+void GraphDataGenerator::addRow()
+{
+ m_tableWidget->model()->insertRow(0);
+ if (m_tableWidget->model()->rowCount() > m_rowCount)
+ m_tableWidget->model()->removeRow(m_rowCount);
+ for (int i = 0; i < m_columnCount; i++) {
+ QModelIndex index = m_tableWidget->model()->index(0, i);
+ m_tableWidget->model()->setData(index,
+ ((float)i / (float)m_columnCount) / 2.0f
+ + (float)(rand() % 30) / 100.0f);
+ }
+ m_tableWidget->resizeColumnsToContents();
+}
+
+void GraphDataGenerator::selectFromTable(const QPoint &selection)
+{
+ m_tableWidget->setFocus();
+ m_tableWidget->setCurrentCell(selection.x(), selection.y());
+}
+
+void GraphDataGenerator::selectedFromTable(int currentRow, int currentColumn,
+ int previousRow, int previousColumn)
+{
+ Q_UNUSED(previousRow)
+ Q_UNUSED(previousColumn)
+ m_barGraph->seriesList().at(0)->setSelectedBar(QPoint(currentRow, currentColumn));
+ m_surfaceGraph->seriesList().at(0)->setSelectedPoint(QPoint(currentRow, currentColumn));
+}
+
+void GraphDataGenerator::fixTableSize()
+{
+ int width = m_tableWidget->horizontalHeader()->length();
+ width += m_tableWidget->verticalHeader()->width();
+ m_tableWidget->setFixedWidth(width + 2);
+ int height = m_tableWidget->verticalHeader()->length();
+ height += m_tableWidget->horizontalHeader()->height();
+ m_tableWidget->setFixedHeight(height + 2);
+}
+
+void GraphDataGenerator::changeSelectedButtonClicked()
+{
+ // Change all selected cells to a random value 1-10
+ QVariant value = QVariant::fromValue(float((rand() % 10) + 1));
+ QList<QTableWidgetItem *> selectedItems = m_tableWidget->selectedItems();
+ foreach (QTableWidgetItem *item, selectedItems) {
+ QString oldData = item->data(Qt::DisplayRole).toString();
+ item->setData(Qt::DisplayRole,
+ oldData.left(5)
+ .append(QString::number(value.toReal()))
+ .append("/")
+ .append(QString::number(value.toReal() * 10)));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DBars *barGraph = new Q3DBars();
+ Q3DSurface *surfaceGraph = new Q3DSurface();
+ QWidget *barContainer = QWidget::createWindowContainer(barGraph);
+ QWidget *surfaceContainer = QWidget::createWindowContainer(surfaceGraph);
+
+ barContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ barContainer->setFocusPolicy(Qt::StrongFocus);
+ surfaceContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ surfaceContainer->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget widget;
+ QVBoxLayout *mainLayout = new QVBoxLayout(&widget);
+ QHBoxLayout *graphLayout = new QHBoxLayout();
+ QVBoxLayout *buttonLayout = new QVBoxLayout();
+ QHBoxLayout *bottomLayout = new QHBoxLayout();
+ QTableWidget *tableWidget = new QTableWidget(&widget);
+ QPushButton *changeSelectedButton = new QPushButton(&widget);
+ changeSelectedButton->setText(QStringLiteral("Change Selected"));
+
+ buttonLayout->addWidget(changeSelectedButton);
+ graphLayout->addWidget(barContainer);
+ graphLayout->addWidget(surfaceContainer);
+ bottomLayout->addLayout(buttonLayout);
+ bottomLayout->addWidget(tableWidget);
+ mainLayout->addLayout(graphLayout);
+ mainLayout->addLayout(bottomLayout);
+
+ tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ tableWidget->setAlternatingRowColors(true);
+ widget.setWindowTitle(QStringLiteral("Hours spent on the Internet"));
+
+ // Since we are dealing with QTableWidget, the model will already have data sorted properly
+ // into rows and columns, so we simply set useModelCategories property to true to utilize this.
+ QItemModelBarDataProxy *barProxy = new QItemModelBarDataProxy(tableWidget->model());
+ QItemModelSurfaceDataProxy *surfaceProxy = new QItemModelSurfaceDataProxy(tableWidget->model());
+ barProxy->setUseModelCategories(true);
+ surfaceProxy->setUseModelCategories(true);
+ barProxy->setRotationRole(tableWidget->model()->roleNames().value(Qt::DisplayRole));
+ barProxy->setValueRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$")));
+ barProxy->setRotationRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)\\d*\\/\\d*([\\.\\,]?)\\d*(\\/)(\\d*[\\.\\,]?\\d*)$")));
+ barProxy->setValueRoleReplace(QStringLiteral("\\4"));
+ barProxy->setRotationRoleReplace(QStringLiteral("\\5"));
+ surfaceProxy->setXPosRole(tableWidget->model()->roleNames().value(Qt::DisplayRole));
+ surfaceProxy->setZPosRole(tableWidget->model()->roleNames().value(Qt::DisplayRole));
+ surfaceProxy->setXPosRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ surfaceProxy->setXPosRoleReplace(QStringLiteral("\\2"));
+ surfaceProxy->setYPosRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$")));
+ surfaceProxy->setYPosRoleReplace(QStringLiteral("\\3"));
+ surfaceProxy->setZPosRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$")));
+ surfaceProxy->setZPosRoleReplace(QStringLiteral("\\1"));
+ QBar3DSeries *barSeries = new QBar3DSeries(barProxy);
+ QSurface3DSeries *surfaceSeries = new QSurface3DSeries(surfaceProxy);
+ barSeries->setMesh(QAbstract3DSeries::MeshPyramid);
+ barGraph->addSeries(barSeries);
+ surfaceGraph->addSeries(surfaceSeries);
+
+ barGraph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetBehind);
+ surfaceGraph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
+
+ GraphDataGenerator generator(barGraph, surfaceGraph, tableWidget);
+ QObject::connect(barSeries, &QBar3DSeries::selectedBarChanged, &generator,
+ &GraphDataGenerator::selectFromTable);
+ QObject::connect(surfaceSeries, &QSurface3DSeries::selectedPointChanged, &generator,
+ &GraphDataGenerator::selectFromTable);
+ QObject::connect(tableWidget, &QTableWidget::currentCellChanged, &generator,
+ &GraphDataGenerator::selectedFromTable);
+ QObject::connect(changeSelectedButton, &QPushButton::clicked, &generator,
+ &GraphDataGenerator::changeSelectedButtonClicked);
+
+ QSize screenSize = barGraph->screen()->size();
+ widget.resize(QSize(screenSize.width() / 2, screenSize.height() / 2));
+ widget.show();
+ generator.start();
+ return app.exec();
+}
diff --git a/tests/multigraphs/data.cpp b/tests/multigraphs/data.cpp
index dc736d46..c1e7b409 100644
--- a/tests/multigraphs/data.cpp
+++ b/tests/multigraphs/data.cpp
@@ -268,6 +268,8 @@ void Data::changeMode(int mode)
void Data::start()
{
m_started = true;
+ // Reset resolution before starting (otherwise restart will crash due to empty data)
+ setResolution(m_resolutionLevel);
updateData();
m_statusArea->append(QStringLiteral("<b>Started<\b>"));
}
diff --git a/tests/qmlcamera/qml/qmlcamera/Axes.qml b/tests/qmlcamera/qml/qmlcamera/Axes.qml
index a6b8d4de..6494adbc 100644
--- a/tests/qmlcamera/qml/qmlcamera/Axes.qml
+++ b/tests/qmlcamera/qml/qmlcamera/Axes.qml
@@ -35,15 +35,11 @@ Item {
}
ValueAxis3D {
id: incomeAxis
- min: 0
- max: 35
labelFormat: "%.2f M\u20AC"
title: "Monthly income"
}
ValueAxis3D {
id: expensesAxis
- min: 0
- max: 35
labelFormat: "-%.2f M\u20AC"
title: "Monthly expenses"
}
diff --git a/tests/qmlcamera/qml/qmlcamera/Data.qml b/tests/qmlcamera/qml/qmlcamera/Data.qml
index bab6bf78..8a301925 100644
--- a/tests/qmlcamera/qml/qmlcamera/Data.qml
+++ b/tests/qmlcamera/qml/qmlcamera/Data.qml
@@ -17,7 +17,7 @@
****************************************************************************/
import QtQuick 2.1
-import QtDataVisualization 1.0
+import QtDataVisualization 1.1
Item {
property alias model: dataModel
@@ -31,6 +31,10 @@ Item {
columnRole: "month"
valueRole: "expenses"
rotationRole: "angle"
+ valueRolePattern: /@*(\d*)t*/
+ rotationRolePattern: /jjj/
+ valueRoleReplace: "\\1"
+ rotationRoleReplace: "1"
}
Bar3DSeries {
@@ -43,95 +47,95 @@ Item {
ListModel {
id: dataModel
- ListElement{ year: "2006"; month: "Jan"; expenses: "4"; angle: "1"; income: "5" }
- ListElement{ year: "2006"; month: "Feb"; expenses: "5"; angle: "2"; income: "6" }
- ListElement{ year: "2006"; month: "Mar"; expenses: "7"; angle: "3"; income: "4" }
- ListElement{ year: "2006"; month: "Apr"; expenses: "3"; angle: "4"; income: "2" }
- ListElement{ year: "2006"; month: "May"; expenses: "4"; angle: "5"; income: "1" }
- ListElement{ year: "2006"; month: "Jun"; expenses: "2"; angle: "6"; income: "2" }
- ListElement{ year: "2006"; month: "Jul"; expenses: "1"; angle: "7"; income: "3" }
- ListElement{ year: "2006"; month: "Aug"; expenses: "5"; angle: "8"; income: "1" }
- ListElement{ year: "2006"; month: "Sep"; expenses: "2"; angle: "9"; income: "3" }
- ListElement{ year: "2006"; month: "Oct"; expenses: "5"; angle: "10"; income: "2" }
- ListElement{ year: "2006"; month: "Nov"; expenses: "8"; angle: "11"; income: "5" }
- ListElement{ year: "2006"; month: "Dec"; expenses: "3"; angle: "12"; income: "3" }
+ ListElement{ year: "2006"; month: "Jan"; expenses: "@@4ttt"; angle: "jjj1"; income: "5" }
+ ListElement{ year: "2006"; month: "Feb"; expenses: "@@5ttt"; angle: "jjj2"; income: "6" }
+ ListElement{ year: "2006"; month: "Mar"; expenses: "@@7ttt"; angle: "jjj3"; income: "4" }
+ ListElement{ year: "2006"; month: "Apr"; expenses: "@@3ttt"; angle: "jjj4"; income: "2" }
+ ListElement{ year: "2006"; month: "May"; expenses: "@@4ttt"; angle: "jjj5"; income: "1" }
+ ListElement{ year: "2006"; month: "Jun"; expenses: "@@2ttt"; angle: "jjj6"; income: "2" }
+ ListElement{ year: "2006"; month: "Jul"; expenses: "@@1ttt"; angle: "jjj7"; income: "3" }
+ ListElement{ year: "2006"; month: "Aug"; expenses: "@@5ttt"; angle: "jjj8"; income: "1" }
+ ListElement{ year: "2006"; month: "Sep"; expenses: "@@2ttt"; angle: "jjj9"; income: "3" }
+ ListElement{ year: "2006"; month: "Oct"; expenses: "@@5ttt"; angle: "jjj10"; income: "2" }
+ ListElement{ year: "2006"; month: "Nov"; expenses: "@@8ttt"; angle: "jjj11"; income: "5" }
+ ListElement{ year: "2006"; month: "Dec"; expenses: "@@3ttt"; angle: "jjj12"; income: "3" }
- ListElement{ year: "2007"; month: "Jan"; expenses: "3"; angle: "13"; income: "1" }
- ListElement{ year: "2007"; month: "Feb"; expenses: "4"; angle: "14"; income: "2" }
- ListElement{ year: "2007"; month: "Mar"; expenses: "12";angle: "15"; income: "4" }
- ListElement{ year: "2007"; month: "Apr"; expenses: "13";angle: "16"; income: "6" }
- ListElement{ year: "2007"; month: "May"; expenses: "14";angle: "17"; income: "11" }
- ListElement{ year: "2007"; month: "Jun"; expenses: "7"; angle: "18"; income: "7" }
- ListElement{ year: "2007"; month: "Jul"; expenses: "6"; angle: "19"; income: "4" }
- ListElement{ year: "2007"; month: "Aug"; expenses: "4"; angle: "20"; income: "15" }
- ListElement{ year: "2007"; month: "Sep"; expenses: "2"; angle: "21"; income: "18" }
- ListElement{ year: "2007"; month: "Oct"; expenses: "29";angle: "22"; income: "25" }
- ListElement{ year: "2007"; month: "Nov"; expenses: "23";angle: "23"; income: "29" }
- ListElement{ year: "2007"; month: "Dec"; expenses: "5"; angle: "24"; income: "9" }
+ ListElement{ year: "2007"; month: "Jan"; expenses: "@@3ttt"; angle: "jjj13"; income: "1" }
+ ListElement{ year: "2007"; month: "Feb"; expenses: "@@4ttt"; angle: "jjj14"; income: "2" }
+ ListElement{ year: "2007"; month: "Mar"; expenses: "@@12ttt"; angle: "jjj15"; income: "4" }
+ ListElement{ year: "2007"; month: "Apr"; expenses: "@@13ttt"; angle: "jjj16"; income: "6" }
+ ListElement{ year: "2007"; month: "May"; expenses: "@@14ttt"; angle: "jjj17"; income: "11" }
+ ListElement{ year: "2007"; month: "Jun"; expenses: "@@7ttt"; angle: "jjj18"; income: "7" }
+ ListElement{ year: "2007"; month: "Jul"; expenses: "@@6ttt"; angle: "jjj19"; income: "4" }
+ ListElement{ year: "2007"; month: "Aug"; expenses: "@@4ttt"; angle: "jjj20"; income: "15" }
+ ListElement{ year: "2007"; month: "Sep"; expenses: "@@2ttt"; angle: "jjj21"; income: "18" }
+ ListElement{ year: "2007"; month: "Oct"; expenses: "@@29ttt"; angle: "jjj22"; income: "25" }
+ ListElement{ year: "2007"; month: "Nov"; expenses: "@@23ttt"; angle: "jjj23"; income: "29" }
+ ListElement{ year: "2007"; month: "Dec"; expenses: "@@5ttt"; angle: "jjj24"; income: "9" }
- ListElement{ year: "2008"; month: "Jan"; expenses: "3"; income: "8" }
- ListElement{ year: "2008"; month: "Feb"; expenses: "8"; income: "14" }
- ListElement{ year: "2008"; month: "Mar"; expenses: "10"; income: "20" }
- ListElement{ year: "2008"; month: "Apr"; expenses: "12"; income: "24" }
- ListElement{ year: "2008"; month: "May"; expenses: "10"; income: "19" }
- ListElement{ year: "2008"; month: "Jun"; expenses: "5"; income: "8" }
- ListElement{ year: "2008"; month: "Jul"; expenses: "1"; income: "4" }
- ListElement{ year: "2008"; month: "Aug"; expenses: "7"; income: "12" }
- ListElement{ year: "2008"; month: "Sep"; expenses: "4"; income: "16" }
- ListElement{ year: "2008"; month: "Oct"; expenses: "22"; income: "33" }
- ListElement{ year: "2008"; month: "Nov"; expenses: "16"; income: "25" }
- ListElement{ year: "2008"; month: "Dec"; expenses: "2"; income: "7" }
+ ListElement{ year: "2008"; month: "Jan"; expenses: "@@3"; income: "8" }
+ ListElement{ year: "2008"; month: "Feb"; expenses: "@@8"; income: "14" }
+ ListElement{ year: "2008"; month: "Mar"; expenses: "@@10"; income: "20" }
+ ListElement{ year: "2008"; month: "Apr"; expenses: "@@12"; income: "24" }
+ ListElement{ year: "2008"; month: "May"; expenses: "@@10"; income: "19" }
+ ListElement{ year: "2008"; month: "Jun"; expenses: "@@5"; income: "8" }
+ ListElement{ year: "2008"; month: "Jul"; expenses: "@@1"; income: "4" }
+ ListElement{ year: "2008"; month: "Aug"; expenses: "@@7"; income: "12" }
+ ListElement{ year: "2008"; month: "Sep"; expenses: "@@4"; income: "16" }
+ ListElement{ year: "2008"; month: "Oct"; expenses: "@@22"; income: "33" }
+ ListElement{ year: "2008"; month: "Nov"; expenses: "@@16"; income: "25" }
+ ListElement{ year: "2008"; month: "Dec"; expenses: "@@2"; income: "7" }
- ListElement{ year: "2009"; month: "Jan"; expenses: "4"; angle: "37"; income: "5" }
- ListElement{ year: "2009"; month: "Feb"; expenses: "4"; angle: "38"; income: "7" }
- ListElement{ year: "2009"; month: "Mar"; expenses: "11";angle: "39"; income: "14" }
- ListElement{ year: "2009"; month: "Apr"; expenses: "16";angle: "40"; income: "22" }
- ListElement{ year: "2009"; month: "May"; expenses: "3"; angle: "41"; income: "5" }
- ListElement{ year: "2009"; month: "Jun"; expenses: "4"; angle: "42"; income: "8" }
- ListElement{ year: "2009"; month: "Jul"; expenses: "7"; angle: "43"; income: "9" }
- ListElement{ year: "2009"; month: "Aug"; expenses: "9"; angle: "44"; income: "13" }
- ListElement{ year: "2009"; month: "Sep"; expenses: "1"; angle: "45"; income: "6" }
- ListElement{ year: "2009"; month: "Oct"; expenses: "14";angle: "46"; income: "25" }
- ListElement{ year: "2009"; month: "Nov"; expenses: "19";angle: "47"; income: "29" }
- ListElement{ year: "2009"; month: "Dec"; expenses: "5"; angle: "48"; income: "7" }
+ ListElement{ year: "2009"; month: "Jan"; expenses: "@@4ttt"; angle: "jjj37"; income: "5" }
+ ListElement{ year: "2009"; month: "Feb"; expenses: "@@4ttt"; angle: "jjj38"; income: "7" }
+ ListElement{ year: "2009"; month: "Mar"; expenses: "@@11ttt"; angle: "jjj39"; income: "14" }
+ ListElement{ year: "2009"; month: "Apr"; expenses: "@@16ttt"; angle: "jjj40"; income: "22" }
+ ListElement{ year: "2009"; month: "May"; expenses: "@@3ttt"; angle: "jjj41"; income: "5" }
+ ListElement{ year: "2009"; month: "Jun"; expenses: "@@4ttt"; angle: "jjj42"; income: "8" }
+ ListElement{ year: "2009"; month: "Jul"; expenses: "@@7ttt"; angle: "jjj43"; income: "9" }
+ ListElement{ year: "2009"; month: "Aug"; expenses: "@@9ttt"; angle: "jjj44"; income: "13" }
+ ListElement{ year: "2009"; month: "Sep"; expenses: "@@1ttt"; angle: "jjj45"; income: "6" }
+ ListElement{ year: "2009"; month: "Oct"; expenses: "@@14ttt"; angle: "jjj46"; income: "25" }
+ ListElement{ year: "2009"; month: "Nov"; expenses: "@@19ttt"; angle: "jjj47"; income: "29" }
+ ListElement{ year: "2009"; month: "Dec"; expenses: "@@5ttt"; angle: "jjj48"; income: "7" }
- ListElement{ year: "2010"; month: "Jan"; expenses: "14";angle: "49"; income: "22" }
- ListElement{ year: "2010"; month: "Feb"; expenses: "5"; angle: "50"; income: "7" }
- ListElement{ year: "2010"; month: "Mar"; expenses: "1"; angle: "51"; income: "9" }
- ListElement{ year: "2010"; month: "Apr"; expenses: "1"; angle: "52"; income: "12" }
- ListElement{ year: "2010"; month: "May"; expenses: "5"; angle: "53"; income: "9" }
- ListElement{ year: "2010"; month: "Jun"; expenses: "5"; angle: "54"; income: "8" }
- ListElement{ year: "2010"; month: "Jul"; expenses: "3"; angle: "55"; income: "7" }
- ListElement{ year: "2010"; month: "Aug"; expenses: "1"; angle: "56"; income: "5" }
- ListElement{ year: "2010"; month: "Sep"; expenses: "2"; angle: "57"; income: "4" }
- ListElement{ year: "2010"; month: "Oct"; expenses: "10";angle: "58"; income: "13" }
- ListElement{ year: "2010"; month: "Nov"; expenses: "12";angle: "59"; income: "17" }
- ListElement{ year: "2010"; month: "Dec"; expenses: "6"; angle: "60"; income: "9" }
+ ListElement{ year: "2010"; month: "Jan"; expenses: "@@14ttt"; angle: "jjj49"; income: "22" }
+ ListElement{ year: "2010"; month: "Feb"; expenses: "@@5ttt"; angle: "jjj50"; income: "7" }
+ ListElement{ year: "2010"; month: "Mar"; expenses: "@@1ttt"; angle: "jjj51"; income: "9" }
+ ListElement{ year: "2010"; month: "Apr"; expenses: "@@1ttt"; angle: "jjj52"; income: "12" }
+ ListElement{ year: "2010"; month: "May"; expenses: "@@5ttt"; angle: "jjj53"; income: "9" }
+ ListElement{ year: "2010"; month: "Jun"; expenses: "@@5ttt"; angle: "jjj54"; income: "8" }
+ ListElement{ year: "2010"; month: "Jul"; expenses: "@@3ttt"; angle: "jjj55"; income: "7" }
+ ListElement{ year: "2010"; month: "Aug"; expenses: "@@1ttt"; angle: "jjj56"; income: "5" }
+ ListElement{ year: "2010"; month: "Sep"; expenses: "@@2ttt"; angle: "jjj57"; income: "4" }
+ ListElement{ year: "2010"; month: "Oct"; expenses: "@@10ttt"; angle: "jjj58"; income: "13" }
+ ListElement{ year: "2010"; month: "Nov"; expenses: "@@12ttt"; angle: "jjj59"; income: "17" }
+ ListElement{ year: "2010"; month: "Dec"; expenses: "@@6ttt"; angle: "jjj60"; income: "9" }
- ListElement{ year: "2011"; month: "Jan"; expenses: "2"; angle: "61"; income: "6" }
- ListElement{ year: "2011"; month: "Feb"; expenses: "4"; angle: "62"; income: "8" }
- ListElement{ year: "2011"; month: "Mar"; expenses: "7"; angle: "63"; income: "12" }
- ListElement{ year: "2011"; month: "Apr"; expenses: "9"; angle: "64"; income: "15" }
- ListElement{ year: "2011"; month: "May"; expenses: "7"; angle: "65"; income: "19" }
- ListElement{ year: "2011"; month: "Jun"; expenses: "9"; angle: "66"; income: "18" }
- ListElement{ year: "2011"; month: "Jul"; expenses: "13";angle: "67"; income: "17" }
- ListElement{ year: "2011"; month: "Aug"; expenses: "5"; angle: "68"; income: "9" }
- ListElement{ year: "2011"; month: "Sep"; expenses: "3"; angle: "69"; income: "8" }
- ListElement{ year: "2011"; month: "Oct"; expenses: "13";angle: "70"; income: "15" }
- ListElement{ year: "2011"; month: "Nov"; expenses: "8"; angle: "71"; income: "17" }
- ListElement{ year: "2011"; month: "Dec"; expenses: "7"; angle: "72"; income: "10" }
+ ListElement{ year: "2011"; month: "Jan"; expenses: "@@2ttt"; angle: "jjj61"; income: "6" }
+ ListElement{ year: "2011"; month: "Feb"; expenses: "@@4ttt"; angle: "jjj62"; income: "8" }
+ ListElement{ year: "2011"; month: "Mar"; expenses: "@@7ttt"; angle: "jjj63"; income: "12" }
+ ListElement{ year: "2011"; month: "Apr"; expenses: "@@9ttt"; angle: "jjj64"; income: "15" }
+ ListElement{ year: "2011"; month: "May"; expenses: "@@7ttt"; angle: "jjj65"; income: "19" }
+ ListElement{ year: "2011"; month: "Jun"; expenses: "@@9ttt"; angle: "jjj66"; income: "18" }
+ ListElement{ year: "2011"; month: "Jul"; expenses: "@@13ttt"; angle: "jjj67"; income: "17" }
+ ListElement{ year: "2011"; month: "Aug"; expenses: "@@5ttt"; angle: "jjj68"; income: "9" }
+ ListElement{ year: "2011"; month: "Sep"; expenses: "@@3ttt"; angle: "jjj69"; income: "8" }
+ ListElement{ year: "2011"; month: "Oct"; expenses: "@@13ttt"; angle: "jjj70"; income: "15" }
+ ListElement{ year: "2011"; month: "Nov"; expenses: "@@8ttt"; angle: "jjj71"; income: "17" }
+ ListElement{ year: "2011"; month: "Dec"; expenses: "@@7ttt"; angle: "jjj72"; income: "10" }
- ListElement{ year: "2012"; month: "Jan"; expenses: "12";angle: "73"; income: "16" }
- ListElement{ year: "2012"; month: "Feb"; expenses: "24";angle: "74"; income: "28" }
- ListElement{ year: "2012"; month: "Mar"; expenses: "27";angle: "75"; income: "22" }
- ListElement{ year: "2012"; month: "Apr"; expenses: "29";angle: "76"; income: "25" }
- ListElement{ year: "2012"; month: "May"; expenses: "27";angle: "77"; income: "29" }
- ListElement{ year: "2012"; month: "Jun"; expenses: "19";angle: "78"; income: "18" }
- ListElement{ year: "2012"; month: "Jul"; expenses: "13";angle: "79"; income: "17" }
- ListElement{ year: "2012"; month: "Aug"; expenses: "15";angle: "80"; income: "19" }
- ListElement{ year: "2012"; month: "Sep"; expenses: "3"; angle: "81"; income: "8" }
- ListElement{ year: "2012"; month: "Oct"; expenses: "3"; angle: "82"; income: "6" }
- ListElement{ year: "2012"; month: "Nov"; expenses: "4"; angle: "83"; income: "8" }
- ListElement{ year: "2012"; month: "Dec"; expenses: "5"; angle: "84"; income: "9" }
+ ListElement{ year: "2012"; month: "Jan"; expenses: "@@12ttt"; angle: "jjj73"; income: "16" }
+ ListElement{ year: "2012"; month: "Feb"; expenses: "@@24ttt"; angle: "jjj74"; income: "28" }
+ ListElement{ year: "2012"; month: "Mar"; expenses: "@@27ttt"; angle: "jjj75"; income: "22" }
+ ListElement{ year: "2012"; month: "Apr"; expenses: "@@29ttt"; angle: "jjj76"; income: "25" }
+ ListElement{ year: "2012"; month: "May"; expenses: "@@27ttt"; angle: "jjj77"; income: "29" }
+ ListElement{ year: "2012"; month: "Jun"; expenses: "@@19ttt"; angle: "jjj78"; income: "18" }
+ ListElement{ year: "2012"; month: "Jul"; expenses: "@@13ttt"; angle: "jjj79"; income: "17" }
+ ListElement{ year: "2012"; month: "Aug"; expenses: "@@15ttt"; angle: "jjj80"; income: "19" }
+ ListElement{ year: "2012"; month: "Sep"; expenses: "@@3ttt"; angle: "jjj81"; income: "8" }
+ ListElement{ year: "2012"; month: "Oct"; expenses: "@@3ttt"; angle: "jjj82"; income: "6" }
+ ListElement{ year: "2012"; month: "Nov"; expenses: "@@4ttt"; angle: "jjj83"; income: "8" }
+ ListElement{ year: "2012"; month: "Dec"; expenses: "@@5ttt"; angle: "jjj84"; income: "9" }
}
}
diff --git a/tests/qmlcamera/qml/qmlcamera/main.qml b/tests/qmlcamera/qml/qmlcamera/main.qml
index 264d613e..444e175c 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.0
+import QtDataVisualization 1.1
import "."
Rectangle {
@@ -66,6 +66,25 @@ Rectangle {
scene.activeCamera.yRotation: camControlArea.yValue
scene.activeCamera.zoomLevel: zoomSlider.value
inputHandler: null
+
+ customItemList: [shuttleItem, labelItem]
+ }
+
+ Custom3DItem {
+ id: shuttleItem
+ meshFile: ":/items/shuttle.obj"
+ textureFile: ":/items/shuttle.png"
+ position: Qt.vector3d(5.0,29.0,3.0)
+ scaling: Qt.vector3d(0.2,0.2,0.2)
+ }
+
+ Custom3DLabel {
+ id: labelItem
+ facingCamera: true
+ positionAbsolute: true
+ position: Qt.vector3d(0.0,1.5,0.0)
+ scaling: Qt.vector3d(1.0,1.0,1.0)
+ text: "Qt Shuttle"
}
MouseArea {
@@ -75,9 +94,10 @@ Rectangle {
property bool selectionOn: false
onPressed: {
- if (mouse.button == Qt.LeftButton)
+ if (mouse.button == Qt.LeftButton) {
selectionOn = true;
testChart.scene.selectionQueryPosition = Qt.point(mouse.x, mouse.y);
+ }
}
onReleased: {
@@ -141,6 +161,7 @@ Rectangle {
onClicked: {
currentAngle += 5
chartData.series.meshAngle = currentAngle
+ shuttleItem.setRotationAxisAndAngle(Qt.vector3d(0.0, 1.0, 1.0), currentAngle)
}
}
@@ -162,4 +183,24 @@ Rectangle {
}
}
}
+
+ Button {
+ id: shuttleAdd
+ anchors.bottom: dataToggle.top
+ width: camControlArea.width
+ text: "Remove Shuttle"
+ property bool addObject: false
+ onClicked: {
+ if (addObject === true) {
+ shuttleItem.textureFile = ":/items/shuttle.png"
+ testChart.addCustomItem(shuttleItem)
+ text = "Remove Shuttle"
+ addObject = false
+ } else {
+ testChart.releaseCustomItem(shuttleItem)
+ text = "Add Shuttle"
+ addObject = true
+ }
+ }
+ }
}
diff --git a/tests/qmlcamera/qmlcamera.qrc b/tests/qmlcamera/qmlcamera.qrc
index 90cb0867..3612fa9f 100644
--- a/tests/qmlcamera/qmlcamera.qrc
+++ b/tests/qmlcamera/qmlcamera.qrc
@@ -5,4 +5,8 @@
<file>qml/qmlcamera/Data.qml</file>
<file>qml/qmlcamera/main.qml</file>
</qresource>
+ <qresource prefix="/items">
+ <file>shuttle.obj</file>
+ <file>shuttle.png</file>
+ </qresource>
</RCC>
diff --git a/tests/qmlcamera/shuttle.obj b/tests/qmlcamera/shuttle.obj
new file mode 100644
index 00000000..e872228d
--- /dev/null
+++ b/tests/qmlcamera/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/qmlcamera/shuttle.png b/tests/qmlcamera/shuttle.png
new file mode 100644
index 00000000..52d6244c
--- /dev/null
+++ b/tests/qmlcamera/shuttle.png
Binary files differ
diff --git a/tests/qmlmultitest/main.cpp b/tests/qmlmultitest/main.cpp
new file mode 100644
index 00000000..a4754f56
--- /dev/null
+++ b/tests/qmlmultitest/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 multitest"));
+ viewer.setSource(QUrl("qrc:/qml/qmlmultitest/main.qml"));
+ viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+ viewer.show();
+
+ return app.exec();
+}
diff --git a/tests/qmlmultitest/qml/qmlmultitest/Data.qml b/tests/qmlmultitest/qml/qmlmultitest/Data.qml
new file mode 100644
index 00000000..2ef168da
--- /dev/null
+++ b/tests/qmlmultitest/qml/qmlmultitest/Data.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 sharedData: dataModel
+
+ ListModel {
+ id: dataModel
+ ListElement{ coords: "0,0"; data: "20.0/10.0/4.75"; }
+ ListElement{ coords: "1,0"; data: "21.1/10.3/3.00"; }
+ ListElement{ coords: "2,0"; data: "22.5/10.7/1.24"; }
+ ListElement{ coords: "3,0"; data: "24.0/10.5/2.53"; }
+ ListElement{ coords: "0,1"; data: "20.2/11.2/3.55"; }
+ ListElement{ coords: "1,1"; data: "21.3/11.5/3.03"; }
+ ListElement{ coords: "2,1"; data: "22.6/11.7/3.46"; }
+ ListElement{ coords: "3,1"; data: "23.4/11.5/4.12"; }
+ ListElement{ coords: "0,2"; data: "20.2/12.3/3.37"; }
+ ListElement{ coords: "1,2"; data: "21.1/12.4/2.98"; }
+ ListElement{ coords: "2,2"; data: "22.5/12.1/3.33"; }
+ ListElement{ coords: "3,2"; data: "23.3/12.7/3.23"; }
+ ListElement{ coords: "0,3"; data: "20.7/13.3/5.34"; }
+ ListElement{ coords: "1,3"; data: "21.5/13.2/4.54"; }
+ ListElement{ coords: "2,3"; data: "22.4/13.6/4.65"; }
+ ListElement{ coords: "3,3"; data: "23.2/13.4/6.67"; }
+ ListElement{ coords: "0,4"; data: "20.6/15.0/6.01"; }
+ ListElement{ coords: "1,4"; data: "21.3/14.6/5.83"; }
+ ListElement{ coords: "2,4"; data: "22.5/14.8/7.32"; }
+ ListElement{ coords: "3,4"; data: "23.7/14.3/6.90"; }
+
+ ListElement{ coords: "0,0"; data: "40.0/30.0/14.75"; }
+ ListElement{ coords: "1,0"; data: "41.1/30.3/13.00"; }
+ ListElement{ coords: "2,0"; data: "42.5/30.7/11.24"; }
+ ListElement{ coords: "3,0"; data: "44.0/30.5/12.53"; }
+ ListElement{ coords: "0,1"; data: "40.2/31.2/13.55"; }
+ ListElement{ coords: "1,1"; data: "41.3/31.5/13.03"; }
+ ListElement{ coords: "2,1"; data: "42.6/31.7/13.46"; }
+ ListElement{ coords: "3,1"; data: "43.4/31.5/14.12"; }
+ ListElement{ coords: "0,2"; data: "40.2/32.3/13.37"; }
+ ListElement{ coords: "1,2"; data: "41.1/32.4/12.98"; }
+ ListElement{ coords: "2,2"; data: "42.5/32.1/13.33"; }
+ ListElement{ coords: "3,2"; data: "43.3/32.7/13.23"; }
+ ListElement{ coords: "0,3"; data: "40.7/33.3/15.34"; }
+ ListElement{ coords: "1,3"; data: "41.5/33.2/14.54"; }
+ ListElement{ coords: "2,3"; data: "42.4/33.6/14.65"; }
+ ListElement{ coords: "3,3"; data: "43.2/33.4/16.67"; }
+ ListElement{ coords: "0,4"; data: "40.6/35.0/16.01"; }
+ ListElement{ coords: "1,4"; data: "41.3/34.6/15.83"; }
+ ListElement{ coords: "2,4"; data: "42.5/34.8/17.32"; }
+ ListElement{ coords: "3,4"; data: "43.7/34.3/16.90"; }
+ }
+}
+
diff --git a/tests/qmlmultitest/qml/qmlmultitest/NewButton.qml b/tests/qmlmultitest/qml/qmlmultitest/NewButton.qml
new file mode 100644
index 00000000..e4fb99d2
--- /dev/null
+++ b/tests/qmlmultitest/qml/qmlmultitest/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/qmlmultitest/qml/qmlmultitest/main.qml b/tests/qmlmultitest/qml/qmlmultitest/main.qml
new file mode 100644
index 00000000..e71b30de
--- /dev/null
+++ b/tests/qmlmultitest/qml/qmlmultitest/main.qml
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** 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 QtDataVisualization 1.1
+import "."
+
+Rectangle {
+ id: mainView
+ width: 800
+ height: 600
+
+ Data {
+ id: data
+ }
+
+ GridLayout {
+ id: gridLayout
+ columns: 2
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ anchors.top: mainView.top
+ anchors.bottom: mainView.bottom
+ anchors.left: mainView.left
+ anchors.right: mainView.right
+
+ 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 {
+ id: surfaceProxy
+ itemModel: data.sharedData
+ // The surface data points are not neatly lined up in rows and columns,
+ // so we define explicit row and column roles.
+ rowRole: "coords"
+ columnRole: "coords"
+ xPosRole: "data"
+ zPosRole: "data"
+ yPosRole: "data"
+ rowRolePattern: /(\d),\d/
+ columnRolePattern: /(\d),(\d)/
+ xPosRolePattern: /^([asd]*)([fgh]*)([jkl]*)[^\/]*\/([^\/]*)\/.*$/
+ yPosRolePattern: /^([^\/]*)\/([^\/]*)\/(.*)$/
+ zPosRolePattern: /^([asd]*)([qwe]*)([tyu]*)([fgj]*)([^\/]*)\/[^\/]*\/.*$/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\2"
+ xPosRoleReplace: "\\4"
+ yPosRoleReplace: "\\3"
+ zPosRoleReplace: "\\5"
+ }
+ }
+ }
+ }
+
+ // We'll use one grid cell for buttons
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ GridLayout {
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ columns: 2
+
+ NewButton {
+ Layout.minimumWidth: parent.width / 2
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ text: "Clear Selections"
+ onClicked: clearSelections() // call a helper function to keep button itself simpler
+ }
+
+ NewButton {
+ Layout.minimumWidth: parent.width / 2
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ text: "Quit"
+ onClicked: Qt.quit(0);
+ }
+
+ NewButton {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ text: "Reset Cameras"
+ onClicked: resetCameras() // call a helper function to keep button itself simpler
+ }
+
+ NewButton {
+ id: mmbButton
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ text: "MMB: Last"
+ onClicked: changeMMB() // call a helper function to keep button itself simpler
+ }
+ }
+ }
+
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ border.color: scatterGraph.theme.gridLineColor
+ border.width: 2
+
+ Scatter3D {
+ id: scatterGraph
+ anchors.fill: parent
+ anchors.margins: parent.border.width
+ theme: Theme3D {
+ type: Theme3D.ThemeDigia
+ font.pointSize: 60
+ }
+ scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh
+
+ Scatter3DSeries {
+ itemLabelFormat: "Pop density at (@xLabel N, @zLabel E): @yLabel"
+ mesh: Abstract3DSeries.MeshCube
+ ItemModelScatterDataProxy {
+ id: scatterProxy
+ itemModel: data.sharedData
+ // Mapping model roles to scatter series item coordinates.
+ xPosRole: "data"
+ zPosRole: "data"
+ yPosRole: "data"
+ rotationRole: "coords"
+ xPosRolePattern: /^([asd]*)([fgh]*)([jkl]*)[^\/]*\/([^\/]*)\/.*$/
+ yPosRolePattern: /^([^\/]*)\/([^\/]*)\/(.*)$/
+ zPosRolePattern: /^([asd]*)([qwe]*)([tyu]*)([fgj]*)([^\/]*)\/[^\/]*\/.*$/
+ rotationRolePattern: /(\d)\,(\d)/
+ xPosRoleReplace: "\\4"
+ yPosRoleReplace: "\\3"
+ zPosRoleReplace: "\\5"
+ rotationRoleReplace: "@\\2\\1,0,1,0"
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ border.color: barGraph.theme.gridLineColor
+ border.width: 2
+
+ Bars3D {
+ id: barGraph
+ anchors.fill: parent
+ anchors.margins: parent.border.width
+ theme: Theme3D {
+ type: Theme3D.ThemeQt
+ font.pointSize: 60
+ }
+ selectionMode: AbstractGraph3D.SelectionItemAndRow | AbstractGraph3D.SelectionSlice
+ scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh
+
+ Bar3DSeries {
+ itemLabelFormat: "@seriesName: @valueLabel"
+ name: "Population density"
+
+ ItemModelBarDataProxy {
+ id: barProxy
+ itemModel: data.sharedData
+ // Mapping model roles to bar series rows, columns, and values.
+ rowRole: "coords"
+ columnRole: "coords"
+ valueRole: "data"
+ rotationRole: "coords"
+ rowRolePattern: /(\d),\d/
+ columnRolePattern: /(\d),(\d)/
+ valueRolePattern: /^([^\/]*)\/([^\/]*)\/(.*)$/
+ rotationRolePattern: /(\d)\,(\d)/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\2"
+ valueRoleReplace: "\\3"
+ rotationRoleReplace: "\\2\\1"
+ }
+ }
+ }
+ }
+ }
+
+ 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
+ }
+
+ function changeMMB() {
+ if (barProxy.multiMatchBehavior === ItemModelBarDataProxy.MMBLast) {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBAverage
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBAverage
+ mmbButton.text = "MMB: Average"
+ } else if (barProxy.multiMatchBehavior === ItemModelBarDataProxy.MMBAverage) {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBCumulative
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBCumulativeY
+ mmbButton.text = "MMB: Cumulative"
+ } else if (barProxy.multiMatchBehavior === ItemModelBarDataProxy.MMBCumulative) {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBFirst
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBFirst
+ mmbButton.text = "MMB: First"
+ } else {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBLast
+ surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBLast
+ mmbButton.text = "MMB: Last"
+ }
+ }
+}
diff --git a/tests/qmlmultitest/qmlmultitest.pro b/tests/qmlmultitest/qmlmultitest.pro
new file mode 100644
index 00000000..6e5e1b98
--- /dev/null
+++ b/tests/qmlmultitest/qmlmultitest.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 += qmlmultitest.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/* \
+ qml/qmlmultitest/*
diff --git a/tests/qmlmultitest/qmlmultitest.qrc b/tests/qmlmultitest/qmlmultitest.qrc
new file mode 100644
index 00000000..7fc9ade2
--- /dev/null
+++ b/tests/qmlmultitest/qmlmultitest.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/qmlmultitest/Data.qml</file>
+ <file>qml/qmlmultitest/main.qml</file>
+ <file>qml/qmlmultitest/NewButton.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp
index 30382ca5..207da530 100644
--- a/tests/scattertest/main.cpp
+++ b/tests/scattertest/main.cpp
@@ -40,6 +40,7 @@ int main(int argc, char **argv)
QWidget *widget = new QWidget;
QHBoxLayout *hLayout = new QHBoxLayout(widget);
QVBoxLayout *vLayout = new QVBoxLayout();
+ QVBoxLayout *vLayout2 = new QVBoxLayout();
Q3DScatter *chart = new Q3DScatter();
QSize screenSize = chart->screen()->size();
@@ -54,6 +55,7 @@ int main(int argc, char **argv)
hLayout->addWidget(container, 1);
hLayout->addLayout(vLayout);
+ hLayout->addLayout(vLayout2);
QPushButton *themeButton = new QPushButton(widget);
themeButton->setText(QStringLiteral("Change theme"));
@@ -100,6 +102,9 @@ int main(int argc, char **argv)
QPushButton *setSelectedItemButton = new QPushButton(widget);
setSelectedItemButton->setText(QStringLiteral("Select/deselect item 3"));
+ QPushButton *clearSeriesDataButton = new QPushButton(widget);
+ clearSeriesDataButton->setText(QStringLiteral("Clear series data"));
+
QPushButton *addSeriesButton = new QPushButton(widget);
addSeriesButton->setText(QStringLiteral("Add Series"));
@@ -115,6 +120,18 @@ int main(int argc, char **argv)
QPushButton *startTimerButton = new QPushButton(widget);
startTimerButton->setText(QStringLiteral("Start/stop timer"));
+ QPushButton *massiveDataTestButton = new QPushButton(widget);
+ massiveDataTestButton->setText(QStringLiteral("Massive data test"));
+
+ QPushButton *testItemChangesButton = new QPushButton(widget);
+ testItemChangesButton->setText(QStringLiteral("Test Item changing"));
+
+ QPushButton *testReverseButton = new QPushButton(widget);
+ testReverseButton->setText(QStringLiteral("Test Axis Reversing"));
+
+ QPushButton *renderToImageButton = new QPushButton(widget);
+ renderToImageButton->setText(QStringLiteral("Render the graph to an image"));
+
QLinearGradient grBtoY(0, 0, 100, 0);
grBtoY.setColorAt(1.0, Qt::black);
grBtoY.setColorAt(0.67, Qt::blue);
@@ -129,6 +146,12 @@ int main(int argc, char **argv)
gradientBtoYPB->setIcon(QIcon(pm));
gradientBtoYPB->setIconSize(QSize(100, 24));
+ QLabel *fpsLabel = new QLabel(QStringLiteral(""));
+
+ QCheckBox *fpsCheckBox = new QCheckBox(widget);
+ fpsCheckBox->setText(QStringLiteral("Measure Fps"));
+ fpsCheckBox->setChecked(false);
+
QCheckBox *backgroundCheckBox = new QCheckBox(widget);
backgroundCheckBox->setText(QStringLiteral("Show background"));
backgroundCheckBox->setChecked(true);
@@ -150,17 +173,83 @@ int main(int argc, char **argv)
QFontComboBox *fontList = new QFontComboBox(widget);
QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, widget);
- fontSizeSlider->setTickInterval(1);
+ fontSizeSlider->setTickInterval(15);
+ fontSizeSlider->setTickPosition(QSlider::TicksBelow);
fontSizeSlider->setMinimum(1);
fontSizeSlider->setValue(30);
fontSizeSlider->setMaximum(200);
QSlider *pointSizeSlider = new QSlider(Qt::Horizontal, widget);
- pointSizeSlider->setTickInterval(1);
+ pointSizeSlider->setTickInterval(15);
+ pointSizeSlider->setTickPosition(QSlider::TicksBelow);
pointSizeSlider->setMinimum(1);
pointSizeSlider->setValue(30);
pointSizeSlider->setMaximum(100);
+ QSlider *minSliderX = new QSlider(Qt::Horizontal, widget);
+ minSliderX->setTickInterval(50);
+ minSliderX->setTickPosition(QSlider::TicksBelow);
+ minSliderX->setMinimum(-100);
+ minSliderX->setValue(-50);
+ minSliderX->setMaximum(100);
+
+ QSlider *minSliderY = new QSlider(Qt::Horizontal, widget);
+ minSliderY->setTickInterval(100);
+ minSliderY->setTickPosition(QSlider::TicksBelow);
+ minSliderY->setMinimum(-200);
+ minSliderY->setValue(-100);
+ minSliderY->setMaximum(200);
+
+ QSlider *minSliderZ = new QSlider(Qt::Horizontal, widget);
+ minSliderZ->setTickInterval(50);
+ minSliderZ->setTickPosition(QSlider::TicksBelow);
+ minSliderZ->setMinimum(-100);
+ minSliderZ->setValue(-50);
+ minSliderZ->setMaximum(100);
+
+ QSlider *maxSliderX = new QSlider(Qt::Horizontal, widget);
+ maxSliderX->setTickInterval(50);
+ maxSliderX->setTickPosition(QSlider::TicksAbove);
+ maxSliderX->setMinimum(-100);
+ maxSliderX->setValue(50);
+ maxSliderX->setMaximum(100);
+
+ QSlider *maxSliderY = new QSlider(Qt::Horizontal, widget);
+ maxSliderY->setTickInterval(100);
+ maxSliderY->setTickPosition(QSlider::TicksAbove);
+ maxSliderY->setMinimum(-200);
+ maxSliderY->setValue(120);
+ maxSliderY->setMaximum(200);
+
+ QSlider *maxSliderZ = new QSlider(Qt::Horizontal, widget);
+ maxSliderZ->setTickInterval(50);
+ maxSliderZ->setTickPosition(QSlider::TicksAbove);
+ maxSliderZ->setMinimum(-100);
+ maxSliderZ->setValue(50);
+ maxSliderZ->setMaximum(100);
+
+ QSlider *aspectRatioSlider = new QSlider(Qt::Horizontal, widget);
+ aspectRatioSlider->setTickInterval(10);
+ aspectRatioSlider->setTickPosition(QSlider::TicksBelow);
+ aspectRatioSlider->setMinimum(1);
+ aspectRatioSlider->setValue(20);
+ aspectRatioSlider->setMaximum(100);
+
+ QCheckBox *axisTitlesVisibleCB = new QCheckBox(widget);
+ axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible"));
+ axisTitlesVisibleCB->setChecked(false);
+
+ QCheckBox *axisTitlesFixedCB = new QCheckBox(widget);
+ axisTitlesFixedCB->setText(QStringLiteral("Axis titles fixed"));
+ axisTitlesFixedCB->setChecked(true);
+
+ QSlider *axisLabelRotationSlider = new QSlider(Qt::Horizontal, widget);
+ axisLabelRotationSlider->setTickInterval(10);
+ axisLabelRotationSlider->setTickPosition(QSlider::TicksBelow);
+ axisLabelRotationSlider->setMinimum(0);
+ axisLabelRotationSlider->setValue(0);
+ axisLabelRotationSlider->setMaximum(90);
+
vLayout->addWidget(themeButton, 0, Qt::AlignTop);
vLayout->addWidget(labelButton, 0, Qt::AlignTop);
vLayout->addWidget(styleButton, 0, Qt::AlignTop);
@@ -176,24 +265,43 @@ int main(int argc, char **argv)
vLayout->addWidget(removeOneButton, 0, Qt::AlignTop);
vLayout->addWidget(removeBunchButton, 0, Qt::AlignTop);
vLayout->addWidget(setSelectedItemButton, 0, Qt::AlignTop);
+ vLayout->addWidget(clearSeriesDataButton, 0, Qt::AlignTop);
vLayout->addWidget(addSeriesButton, 0, Qt::AlignTop);
vLayout->addWidget(removeSeriesButton, 0, Qt::AlignTop);
vLayout->addWidget(toggleSeriesVisibilityButton, 0, Qt::AlignTop);
vLayout->addWidget(changeSeriesNameButton, 0, Qt::AlignTop);
vLayout->addWidget(startTimerButton, 0, Qt::AlignTop);
- vLayout->addWidget(gradientBtoYPB, 0, Qt::AlignTop);
- vLayout->addWidget(backgroundCheckBox);
- vLayout->addWidget(gridCheckBox);
- vLayout->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")));
- vLayout->addWidget(shadowQuality);
- vLayout->addWidget(new QLabel(QStringLiteral("Change font")));
- vLayout->addWidget(fontList);
- vLayout->addWidget(new QLabel(QStringLiteral("Adjust font size")));
- vLayout->addWidget(fontSizeSlider, 1, Qt::AlignTop);
- vLayout->addWidget(new QLabel(QStringLiteral("Adjust point size")));
- vLayout->addWidget(pointSizeSlider, 1, Qt::AlignTop);
-
- widget->show();
+ vLayout->addWidget(massiveDataTestButton, 0, Qt::AlignTop);
+ vLayout->addWidget(testItemChangesButton, 0, Qt::AlignTop);
+ vLayout->addWidget(testReverseButton, 0, Qt::AlignTop);
+ vLayout->addWidget(renderToImageButton, 1, Qt::AlignTop);
+
+ vLayout2->addWidget(gradientBtoYPB, 0, Qt::AlignTop);
+ vLayout2->addWidget(fpsLabel, 0, Qt::AlignTop);
+ vLayout2->addWidget(fpsCheckBox, 0, Qt::AlignTop);
+ vLayout2->addWidget(backgroundCheckBox);
+ vLayout2->addWidget(gridCheckBox);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")));
+ vLayout2->addWidget(shadowQuality, 0, Qt::AlignTop);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust point size")));
+ vLayout2->addWidget(pointSizeSlider, 0, Qt::AlignTop);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Adjust data window")));
+ vLayout2->addWidget(minSliderX, 0, Qt::AlignTop);
+ vLayout2->addWidget(maxSliderX, 0, Qt::AlignTop);
+ vLayout2->addWidget(minSliderY, 0, Qt::AlignTop);
+ vLayout2->addWidget(maxSliderY, 0, Qt::AlignTop);
+ vLayout2->addWidget(minSliderZ, 0, Qt::AlignTop);
+ vLayout2->addWidget(maxSliderZ, 0, Qt::AlignTop);
+ vLayout2->addWidget(new QLabel(QStringLiteral("Change font")));
+ 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);
ScatterDataModifier *modifier = new ScatterDataModifier(chart);
@@ -228,6 +336,8 @@ int main(int argc, char **argv)
&ScatterDataModifier::removeBunch);
QObject::connect(setSelectedItemButton, &QPushButton::clicked, modifier,
&ScatterDataModifier::selectItem);
+ QObject::connect(clearSeriesDataButton, &QPushButton::clicked, modifier,
+ &ScatterDataModifier::clearSeriesData);
QObject::connect(addSeriesButton, &QPushButton::clicked, modifier,
&ScatterDataModifier::addSeries);
QObject::connect(removeSeriesButton, &QPushButton::clicked, modifier,
@@ -238,6 +348,14 @@ int main(int argc, char **argv)
&ScatterDataModifier::changeSeriesName);
QObject::connect(startTimerButton, &QPushButton::clicked, modifier,
&ScatterDataModifier::startStopTimer);
+ QObject::connect(massiveDataTestButton, &QPushButton::clicked, modifier,
+ &ScatterDataModifier::massiveDataTest);
+ QObject::connect(testItemChangesButton, &QPushButton::clicked, modifier,
+ &ScatterDataModifier::testItemChanges);
+ QObject::connect(testReverseButton, &QPushButton::clicked, modifier,
+ &ScatterDataModifier::testAxisReverse);
+ QObject::connect(renderToImageButton, &QPushButton::clicked, modifier,
+ &ScatterDataModifier::renderToImage);
QObject::connect(gradientBtoYPB, &QPushButton::clicked, modifier,
&ScatterDataModifier::setGradient);
QObject::connect(themeButton, &QPushButton::clicked, modifier,
@@ -252,12 +370,42 @@ int main(int argc, char **argv)
QObject::connect(fontList, &QFontComboBox::currentFontChanged, modifier,
&ScatterDataModifier::changeFont);
+ QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier,
+ &ScatterDataModifier::setFpsMeasurement);
QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier,
&ScatterDataModifier::setBackgroundEnabled);
QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier,
&ScatterDataModifier::setGridEnabled);
+ QObject::connect(minSliderX, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setMinX);
+ QObject::connect(minSliderY, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setMinY);
+ QObject::connect(minSliderZ, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setMinZ);
+ QObject::connect(maxSliderX, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setMaxX);
+ QObject::connect(maxSliderY, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setMaxY);
+ QObject::connect(maxSliderZ, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setMaxZ);
+ QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier,
+ &ScatterDataModifier::toggleAxisTitleVisibility);
+ QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier,
+ &ScatterDataModifier::toggleAxisTitleFixed);
+ QObject::connect(axisLabelRotationSlider, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::changeLabelRotation);
+ QObject::connect(aspectRatioSlider, &QSlider::valueChanged, modifier,
+ &ScatterDataModifier::setAspectRatio);
+
+ modifier->setFpsLabel(fpsLabel);
+
+ chart->setGeometry(QRect(0, 0, 800, 800));
+
modifier->start();
+ modifier->renderToImage(); // Initial hidden render
+
+ widget->show();
return app.exec();
}
diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp
index aa0c5454..c00c526a 100644
--- a/tests/scattertest/scatterchart.cpp
+++ b/tests/scattertest/scatterchart.cpp
@@ -62,6 +62,8 @@ ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter)
&ScatterDataModifier::handleAxisYChanged);
QObject::connect(m_chart, &Q3DScatter::axisZChanged, this,
&ScatterDataModifier::handleAxisZChanged);
+ QObject::connect(m_chart, &QAbstract3DGraph::currentFpsChanged, this,
+ &ScatterDataModifier::handleFpsChange);
}
ScatterDataModifier::~ScatterDataModifier()
@@ -74,15 +76,420 @@ void ScatterDataModifier::start()
addData();
}
+static const int itemsPerUnit = 100; // "unit" is one unit range along Z-axis
+
+void ScatterDataModifier::massiveDataTest()
+{
+ static int testPhase = 0;
+ static QTimer *massiveTestTimer = 0;
+
+ if (!massiveTestTimer)
+ massiveTestTimer = new QTimer;
+
+ int items = 1000000;
+ int visibleRange = 200;
+ int unitCount = items / itemsPerUnit;
+ int cacheSize = visibleRange * itemsPerUnit * 5;
+
+ switch (testPhase) {
+ case 0: {
+ float yRangeMin = 0.0f;
+ float yRangeMax = 1.0f;
+ float yRangeMargin = 0.05f;
+ float minY = yRangeMin + yRangeMargin;
+ float maxY = yRangeMax - yRangeMargin;
+ float unitBase = minY;
+ float direction = 1.0f;
+
+ if (!m_massiveTestCacheArray.size()) {
+ m_massiveTestCacheArray.resize(cacheSize);
+ int totalIndex = 0;
+ for (int i = 0; i < unitCount && totalIndex < cacheSize; i++) {
+ unitBase += direction * (float(rand() % 3) / 100.0f);
+ if (unitBase > maxY) {
+ unitBase = maxY;
+ direction = -1.0f;
+ } else if (unitBase < minY) {
+ unitBase = minY;
+ direction = 1.0f;
+ }
+ for (int j = 0; j < itemsPerUnit && totalIndex < cacheSize; j++) {
+ float randFactor = float(rand() % 100) / (100 / yRangeMargin);
+ m_massiveTestCacheArray[totalIndex].setPosition(
+ QVector3D(float(qrand() % itemsPerUnit),
+ unitBase + randFactor, 0.0f));
+ // Z value is irrelevant, we replace it anyway when we take item to use
+ totalIndex++;
+ }
+ }
+ }
+
+ qDebug() << __FUNCTION__ << testPhase << ": Setting the graph up...";
+ QValue3DAxis *xAxis = new QValue3DAxis();
+ QValue3DAxis *yAxis = new QValue3DAxis();
+ QValue3DAxis *zAxis = new QValue3DAxis();
+ xAxis->setRange(0.0f, float(itemsPerUnit - 1));
+ yAxis->setRange(yRangeMin, yRangeMax);
+ zAxis->setRange(0.0f, float(visibleRange - 1));
+ xAxis->setSegmentCount(1);
+ yAxis->setSegmentCount(1);
+ zAxis->setSegmentCount(1);
+ m_chart->setAxisX(xAxis);
+ m_chart->setAxisY(yAxis);
+ m_chart->setAxisZ(zAxis);
+ m_chart->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetRight);
+ m_chart->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ foreach (QAbstract3DSeries *series, m_chart->seriesList())
+ m_chart->removeSeries(static_cast<QScatter3DSeries *>(series));
+
+ qDebug() << __FUNCTION__ << testPhase << ": Creating massive array..." << items;
+ QScatterDataArray *massiveArray = new QScatterDataArray;
+ massiveArray->resize(items);
+
+ int cacheIndex = 0;
+ for (int i = 0; i < items; i++) {
+ // Use qreals for precicion as the numbers can overflow int
+ float currentZ = float(qreal(i) * qreal(unitCount) / qreal(items));
+ (*massiveArray)[i] = m_massiveTestCacheArray.at(cacheIndex++);
+ (*massiveArray)[i].setZ(currentZ);
+ if (cacheIndex >= cacheSize)
+ cacheIndex = 0;
+ }
+ qDebug() << __FUNCTION__ << testPhase << ": Massive array creation finished!";
+
+ QScatter3DSeries *series = new QScatter3DSeries;
+ series->dataProxy()->resetArray(massiveArray);
+ series->setMesh(QAbstract3DSeries::MeshPoint);
+ m_chart->addSeries(series);
+ break;
+ }
+ case 1: {
+ qDebug() << __FUNCTION__ << testPhase << ": Scroll";
+ QObject::disconnect(massiveTestTimer, 0, this, 0);
+ QObject::connect(massiveTestTimer, &QTimer::timeout, this,
+ &ScatterDataModifier::massiveTestScroll);
+ massiveTestTimer->start(16);
+ break;
+ }
+ case 2: {
+ qDebug() << __FUNCTION__ << testPhase << ": Append and scroll";
+ massiveTestTimer->stop();
+ QObject::disconnect(massiveTestTimer, 0, this, 0);
+ QObject::connect(massiveTestTimer, &QTimer::timeout, this,
+ &ScatterDataModifier::massiveTestAppendAndScroll);
+ m_chart->axisZ()->setRange(unitCount - visibleRange, unitCount);
+ massiveTestTimer->start(16);
+ break;
+ }
+ default:
+ QObject::disconnect(massiveTestTimer, 0, this, 0);
+ massiveTestTimer->stop();
+ qDebug() << __FUNCTION__ << testPhase << ": Resetting the test";
+ testPhase = -1;
+ }
+ testPhase++;
+}
+
+void ScatterDataModifier::massiveTestScroll()
+{
+ const int scrollAmount = 20;
+ int itemCount = m_chart->seriesList().at(0)->dataProxy()->itemCount();
+ int min = m_chart->axisZ()->min() + scrollAmount;
+ int max = m_chart->axisZ()->max() + scrollAmount;
+ if (max >= itemCount / itemsPerUnit) {
+ max = max - min - 1;
+ min = 0;
+ }
+ m_chart->axisZ()->setRange(min, max);
+}
+
+void ScatterDataModifier::massiveTestAppendAndScroll()
+{
+ const int addedUnits = 50;
+ const int addedItems = itemsPerUnit * addedUnits;
+ int cacheSize = m_massiveTestCacheArray.size();
+ int itemCount = m_chart->seriesList().at(0)->dataProxy()->itemCount();
+ static int cacheIndex = 0;
+
+ // Copy items from cache
+ QScatterDataArray appendArray;
+ appendArray.resize(addedItems);
+
+ float zOffset = m_chart->seriesList().at(0)->dataProxy()->itemAt(itemCount - 1)->z();
+ for (int i = 0; i < addedItems; i++) {
+ float currentZ = zOffset + float(qreal(i) * qreal(addedUnits) / qreal(addedItems));
+ appendArray[i] = m_massiveTestCacheArray.at(cacheIndex++);
+ appendArray[i].setZ(currentZ);
+ if (cacheIndex >= cacheSize)
+ cacheIndex = 0;
+ }
+
+ m_chart->seriesList().at(0)->dataProxy()->addItems(appendArray);
+ int min = m_chart->axisZ()->min() + addedUnits;
+ int max = m_chart->axisZ()->max() + addedUnits;
+ m_chart->axisZ()->setRange(min, max);
+}
+
+void ScatterDataModifier::setFpsMeasurement(bool enable)
+{
+ m_chart->setMeasureFps(enable);
+}
+
+void ScatterDataModifier::testItemChanges()
+{
+ static int counter = 0;
+ const int rowCount = 12;
+ const int colCount = 10;
+ static QScatter3DSeries *series0 = 0;
+ static QScatter3DSeries *series1 = 0;
+ static QScatter3DSeries *series2 = 0;
+
+ switch (counter) {
+ case 0: {
+ qDebug() << __FUNCTION__ << counter << "Setup test";
+ foreach (QScatter3DSeries *series, m_chart->seriesList())
+ m_chart->removeSeries(series);
+ foreach (QValue3DAxis *axis, m_chart->axes())
+ deleteAxis(axis);
+ delete series0;
+ delete series1;
+ delete series2;
+ series0 = new QScatter3DSeries;
+ series1 = new QScatter3DSeries;
+ series2 = new QScatter3DSeries;
+ populateFlatSeries(series0, rowCount, colCount, 10.0f);
+ populateFlatSeries(series1, rowCount, colCount, 30.0f);
+ populateFlatSeries(series2, rowCount, colCount, 50.0f);
+ m_chart->axisX()->setRange(3.0f, 6.0f);
+ m_chart->axisY()->setRange(0.0f, 100.0f);
+ m_chart->axisZ()->setRange(4.0f, 8.0f);
+ m_chart->addSeries(series0);
+ m_chart->addSeries(series1);
+ m_chart->addSeries(series2);
+ }
+ break;
+ case 1: {
+ qDebug() << __FUNCTION__ << counter << "Change single item, unselected";
+ int itemIndex = 3 * colCount + 5;
+ QScatterDataItem item = *series0->dataProxy()->itemAt(itemIndex);
+ item.setY(75.0f);
+ series0->dataProxy()->setItem(itemIndex, item);
+ }
+ break;
+ case 2: {
+ qDebug() << __FUNCTION__ << counter << "Change single item, selected";
+ int itemIndex = 4 * colCount + 4;
+ series1->setSelectedItem(itemIndex);
+ QScatterDataItem item = *series1->dataProxy()->itemAt(itemIndex);
+ item.setY(75.0f);
+ series1->dataProxy()->setItem(itemIndex, item);
+ }
+ break;
+ case 3: {
+ qDebug() << __FUNCTION__ << counter << "Change item outside visible area";
+ int itemIndex = 2;
+ QScatterDataItem item = *series1->dataProxy()->itemAt(itemIndex);
+ item.setY(75.0f);
+ series1->dataProxy()->setItem(itemIndex, item);
+ }
+ break;
+ case 4: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, unselected";
+ int itemIndex = 4 * colCount + 6;
+ QScatterDataItem item0 = *series0->dataProxy()->itemAt(itemIndex);
+ QScatterDataItem item1 = *series1->dataProxy()->itemAt(itemIndex);
+ item0.setY(65.0f);
+ item1.setY(85.0f);
+ series0->dataProxy()->setItem(itemIndex, item0);
+ series1->dataProxy()->setItem(itemIndex, item1);
+ }
+ break;
+ case 5: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, one selected";
+ int itemIndex0 = 5 * colCount + 5;
+ int itemIndex1 = 4 * colCount + 4;
+ QScatterDataItem item0 = *series0->dataProxy()->itemAt(itemIndex0);
+ QScatterDataItem item1 = *series1->dataProxy()->itemAt(itemIndex1);
+ item0.setY(65.0f);
+ item1.setY(85.0f);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ series1->dataProxy()->setItem(itemIndex1, item1);
+ }
+ break;
+ case 6: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, one outside range";
+ int itemIndex0 = 6 * colCount + 6;
+ int itemIndex1 = 9 * colCount + 2;
+ QScatterDataItem item0 = *series0->dataProxy()->itemAt(itemIndex0);
+ QScatterDataItem item1 = *series1->dataProxy()->itemAt(itemIndex1);
+ item0.setY(65.0f);
+ item1.setY(85.0f);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ series1->dataProxy()->setItem(itemIndex1, item1);
+ }
+ break;
+ case 7: {
+ qDebug() << __FUNCTION__ << counter << "Change single item from two series, both outside range";
+ int itemIndex0 = 1 * colCount + 3;
+ int itemIndex1 = 9 * colCount + 2;
+ QScatterDataItem item0 = *series0->dataProxy()->itemAt(itemIndex0);
+ QScatterDataItem item1 = *series1->dataProxy()->itemAt(itemIndex1);
+ item0.setY(65.0f);
+ item1.setY(85.0f);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ series1->dataProxy()->setItem(itemIndex1, item1);
+ }
+ break;
+ case 8: {
+ qDebug() << __FUNCTION__ << counter << "Change item to same value as previously";
+ int itemIndex0 = 5 * colCount + 7;
+ int itemIndex1 = 4 * colCount + 7;
+ QScatterDataItem item0 = *series0->dataProxy()->itemAt(itemIndex0);
+ QScatterDataItem item1 = *series1->dataProxy()->itemAt(itemIndex1);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ series1->dataProxy()->setItem(itemIndex1, item1);
+ }
+ break;
+ case 9: {
+ qDebug() << __FUNCTION__ << counter << "Change 3 items on each series";
+ int itemIndex0 = 5 * colCount + 6;
+ int itemIndex1 = 4 * colCount + 6;
+ QScatterDataItem item00 = *series0->dataProxy()->itemAt(itemIndex0);
+ QScatterDataItem item01 = *series0->dataProxy()->itemAt(itemIndex0 + 1);
+ QScatterDataItem item02 = *series0->dataProxy()->itemAt(itemIndex0 + 2);
+ QScatterDataItem item10 = *series1->dataProxy()->itemAt(itemIndex1);
+ QScatterDataItem item11 = *series1->dataProxy()->itemAt(itemIndex1 + 1);
+ QScatterDataItem item12 = *series1->dataProxy()->itemAt(itemIndex1 + 2);
+ item00.setY(65.0f);
+ item01.setY(70.0f);
+ item02.setY(75.0f);
+ item10.setY(80.0f);
+ item11.setY(85.0f);
+ item12.setY(90.0f);
+ series0->dataProxy()->setItem(itemIndex0, item00);
+ series0->dataProxy()->setItem(itemIndex0 + 1, item01);
+ series0->dataProxy()->setItem(itemIndex0 + 2, item02);
+ series1->dataProxy()->setItem(itemIndex1, item10);
+ series1->dataProxy()->setItem(itemIndex1 + 1, item11);
+ series1->dataProxy()->setItem(itemIndex1 + 2, item12);
+ }
+ break;
+ case 10: {
+ qDebug() << __FUNCTION__ << counter << "Level the field single item at a time";
+ QScatterDataItem item;
+ for (int i = 0; i < rowCount; i++) {
+ for (int j = 0; j < colCount; j++) {
+ int itemIndex = i * colCount + j;
+ QScatterDataItem item0 = *series0->dataProxy()->itemAt(itemIndex);
+ QScatterDataItem item1 = *series1->dataProxy()->itemAt(itemIndex);
+ QScatterDataItem item2 = *series2->dataProxy()->itemAt(itemIndex);
+ item0.setY(10.0f);
+ item1.setY(15.0f);
+ item2.setY(20.0f);
+ series0->dataProxy()->setItem(itemIndex, item0);
+ series1->dataProxy()->setItem(itemIndex, item1);
+ series2->dataProxy()->setItem(itemIndex, item2);
+ }
+ }
+ }
+ break;
+ case 11: {
+ qDebug() << __FUNCTION__ << counter << "Change same items multiple times";
+ int itemIndex0 = 6 * colCount + 6;
+ QScatterDataItem item0 = *series0->dataProxy()->itemAt(itemIndex0);
+ item0.setY(90.0f);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ series0->dataProxy()->setItem(itemIndex0, item0);
+ }
+ break;
+ default:
+ qDebug() << __FUNCTION__ << "Resetting test";
+ counter = -1;
+ }
+ counter++;
+}
+
+void ScatterDataModifier::testAxisReverse()
+{
+ static int counter = 0;
+ const int rowCount = 16;
+ const int colCount = 16;
+ static QScatter3DSeries *series0 = 0;
+ static QScatter3DSeries *series1 = 0;
+
+ switch (counter) {
+ case 0: {
+ qDebug() << __FUNCTION__ << counter << "Setup test";
+ foreach (QScatter3DSeries *series, m_chart->seriesList())
+ m_chart->removeSeries(series);
+ foreach (QValue3DAxis *axis, m_chart->axes())
+ deleteAxis(axis);
+ delete series0;
+ delete series1;
+ series0 = new QScatter3DSeries;
+ series1 = new QScatter3DSeries;
+ populateRisingSeries(series0, rowCount, colCount, 0.0f, 50.0f);
+ populateRisingSeries(series1, rowCount, colCount, -20.0f, 30.0f);
+ 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->addSeries(series0);
+ m_chart->addSeries(series1);
+ }
+ break;
+ case 1: {
+ qDebug() << __FUNCTION__ << counter << "Reverse X axis";
+ m_chart->axisX()->setReversed(true);
+ }
+ break;
+ case 2: {
+ qDebug() << __FUNCTION__ << counter << "Reverse Y axis";
+ m_chart->axisY()->setReversed(true);
+ }
+ break;
+ case 3: {
+ qDebug() << __FUNCTION__ << counter << "Reverse Z axis";
+ m_chart->axisZ()->setReversed(true);
+ }
+ break;
+ case 4: {
+ qDebug() << __FUNCTION__ << counter << "Return all axes to normal";
+ m_chart->axisX()->setReversed(false);
+ m_chart->axisY()->setReversed(false);
+ m_chart->axisZ()->setReversed(false);
+ }
+ break;
+ case 5: {
+ qDebug() << __FUNCTION__ << counter << "Reverse all axes";
+ m_chart->axisX()->setReversed(true);
+ m_chart->axisY()->setReversed(true);
+ m_chart->axisZ()->setReversed(true);
+ }
+ break;
+ default:
+ qDebug() << __FUNCTION__ << "Resetting test";
+ counter = -1;
+ }
+ counter++;
+}
+
void ScatterDataModifier::addData()
{
// Add labels
- m_chart->axisX()->setTitle("X");
- m_chart->axisY()->setTitle("Y");
- m_chart->axisZ()->setTitle("Z");
+ m_chart->axisX()->setTitle("X - Axis");
+ m_chart->axisY()->setTitle("Y - Axis");
+ m_chart->axisZ()->setTitle("Z - Axis");
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->axisY()->setSegmentCount(4);
+ m_chart->axisZ()->setSegmentCount(9);
+ m_chart->axisX()->setSubSegmentCount(2);
+ m_chart->axisY()->setSubSegmentCount(3);
+ m_chart->axisZ()->setSubSegmentCount(1);
QScatterDataArray *dataArray = new QScatterDataArray;
dataArray->resize(numberOfItems);
@@ -220,18 +627,27 @@ void ScatterDataModifier::clear()
qDebug() << m_loopCounter << "Cleared array";
}
+void ScatterDataModifier::deleteAxis(QValue3DAxis *axis)
+{
+ m_chart->releaseAxis(axis);
+ delete axis;
+}
+
void ScatterDataModifier::resetAxes()
{
- m_chart->releaseAxis(m_chart->axisX());
- m_chart->releaseAxis(m_chart->axisY());
- m_chart->releaseAxis(m_chart->axisZ());
+ deleteAxis(m_chart->axisX());
+ deleteAxis(m_chart->axisY());
+ deleteAxis(m_chart->axisZ());
m_chart->setAxisX(new QValue3DAxis);
m_chart->setAxisY(new QValue3DAxis);
m_chart->setAxisZ(new QValue3DAxis);
- m_chart->axisX()->setSegmentCount(5);
- m_chart->axisY()->setSegmentCount(5);
- m_chart->axisZ()->setSegmentCount(5);
+ m_chart->axisX()->setSegmentCount(6);
+ m_chart->axisY()->setSegmentCount(4);
+ m_chart->axisZ()->setSegmentCount(9);
+ m_chart->axisX()->setSubSegmentCount(2);
+ m_chart->axisY()->setSubSegmentCount(3);
+ m_chart->axisZ()->setSubSegmentCount(1);
m_chart->axisX()->setTitle("X");
m_chart->axisY()->setTitle("Y");
m_chart->axisZ()->setTitle("Z");
@@ -450,6 +866,12 @@ void ScatterDataModifier::setGradient()
}
}
+void ScatterDataModifier::clearSeriesData()
+{
+ if (m_targetSeries)
+ m_targetSeries->dataProxy()->resetArray(0);
+}
+
void ScatterDataModifier::addSeries()
{
QScatter3DSeries *series = createAndAddSeries();
@@ -499,6 +921,55 @@ void ScatterDataModifier::handleAxisZChanged(QValue3DAxis *axis)
qDebug() << __FUNCTION__ << axis << axis->orientation() << (axis == m_chart->axisZ());
}
+void ScatterDataModifier::handleFpsChange(qreal fps)
+{
+ static const QString fpsPrefix(QStringLiteral("FPS: "));
+ m_fpsLabel->setText(fpsPrefix + QString::number(qRound(fps)));
+}
+
+void ScatterDataModifier::changeLabelRotation(int rotation)
+{
+ m_chart->axisX()->setLabelAutoRotation(float(rotation));
+ m_chart->axisY()->setLabelAutoRotation(float(rotation));
+ m_chart->axisZ()->setLabelAutoRotation(float(rotation));
+}
+
+void ScatterDataModifier::toggleAxisTitleVisibility(bool enabled)
+{
+ m_chart->axisX()->setTitleVisible(enabled);
+ m_chart->axisY()->setTitleVisible(enabled);
+ m_chart->axisZ()->setTitleVisible(enabled);
+}
+
+void ScatterDataModifier::toggleAxisTitleFixed(bool enabled)
+{
+ m_chart->axisX()->setTitleFixed(enabled);
+ m_chart->axisY()->setTitleFixed(enabled);
+ m_chart->axisZ()->setTitleFixed(enabled);
+}
+
+void ScatterDataModifier::renderToImage()
+{
+ QImage renderedImage8AA = m_chart->renderToImage(8);
+ QImage renderedImageNoAA = m_chart->renderToImage(0);
+ QImage renderedImage8AASmall = m_chart->renderToImage(8, QSize(100, 100));
+ QImage renderedImageNoAASmall = m_chart->renderToImage(0, QSize(100, 100));
+
+ if (m_chart->isVisible()) {
+ renderedImage8AA.save(QStringLiteral("./renderedImage8AA_visible.png"));
+ renderedImageNoAA.save(QStringLiteral("./renderedImageNoAA_visible.png"));
+ renderedImage8AASmall.save(QStringLiteral("./renderedImage8AASmall_visible.png"));
+ renderedImageNoAASmall.save(QStringLiteral("./renderedImageNoAASmall_visible.png"));
+ qDebug() << "Visible images rendered!";
+ } else {
+ renderedImage8AA.save(QStringLiteral("./renderedImage8AA_hidden.png"));
+ renderedImageNoAA.save(QStringLiteral("./renderedImageNoAA_hidden.png"));
+ renderedImage8AASmall.save(QStringLiteral("./renderedImage8AASmall_hidden.png"));
+ renderedImageNoAASmall.save(QStringLiteral("./renderedImageNoAASmall_hidden.png"));
+ qDebug() << "Hidden images rendered!";
+ }
+}
+
void ScatterDataModifier::changeShadowQuality(int quality)
{
QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality);
@@ -516,12 +987,52 @@ void ScatterDataModifier::setGridEnabled(int enabled)
m_chart->activeTheme()->setGridEnabled((bool)enabled);
}
+void ScatterDataModifier::setMinX(int min)
+{
+ m_chart->axisX()->setMin(min);
+}
+
+void ScatterDataModifier::setMinY(int min)
+{
+ m_chart->axisY()->setMin(float(min) / 100.0f);
+}
+
+void ScatterDataModifier::setMinZ(int min)
+{
+ m_chart->axisZ()->setMin(min);
+}
+
+void ScatterDataModifier::setMaxX(int max)
+{
+ m_chart->axisX()->setMax(max);
+}
+
+void ScatterDataModifier::setMaxY(int max)
+{
+ m_chart->axisY()->setMax(float(max) / 100.0f);
+}
+
+void ScatterDataModifier::setMaxZ(int max)
+{
+ m_chart->axisZ()->setMax(max);
+}
+
+void ScatterDataModifier::setAspectRatio(int ratio)
+{
+ float aspectRatio = float(ratio) / 10.0f;
+ m_chart->setAspectRatio(aspectRatio);
+}
+
QVector3D ScatterDataModifier::randVector()
{
- return QVector3D(
+ QVector3D retvec = QVector3D(
(float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f,
(float)(rand() % 100) / 100.0f - (float)(rand() % 100) / 100.0f,
(float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f);
+
+ qDebug() << __FUNCTION__ << retvec;
+
+ return retvec;
}
QScatter3DSeries *ScatterDataModifier::createAndAddSeries()
@@ -535,7 +1046,7 @@ QScatter3DSeries *ScatterDataModifier::createAndAddSeries()
m_chart->addSeries(series);
series->setName(QString("Series %1").arg(counter++));
- series->setItemLabelFormat(QStringLiteral("@seriesName: @xLabel - @yLabel - @zLabel"));
+ series->setItemLabelFormat(QStringLiteral("@seriesName: (X:@xLabel / Z:@zLabel) Y:@yLabel"));
series->setMesh(QAbstract3DSeries::MeshSphere);
series->setMeshSmooth(true);
series->setBaseColor(QColor(rand() % 256, rand() % 256, rand() % 256));
@@ -546,3 +1057,31 @@ QScatter3DSeries *ScatterDataModifier::createAndAddSeries()
return series;
}
+
+void ScatterDataModifier::populateFlatSeries(QScatter3DSeries *series, int rows, int columns,
+ float value)
+{
+ QScatterDataArray *dataArray = new QScatterDataArray;
+ dataArray->resize(rows * columns);
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++)
+ (*dataArray)[i * columns + j].setPosition(QVector3D(float(i), value, float(j)));
+ }
+ series->dataProxy()->resetArray(dataArray);
+}
+
+void ScatterDataModifier::populateRisingSeries(QScatter3DSeries *series, int rows, int columns,
+ float minValue, float maxValue)
+{
+ QScatterDataArray *dataArray = new QScatterDataArray;
+ int arraySize = rows * columns;
+ dataArray->resize(arraySize);
+ float range = maxValue - minValue;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++) {
+ float yValue = minValue + (range * i * j / arraySize);
+ (*dataArray)[i * columns + j].setPosition(QVector3D(float(i), yValue, float(j)));
+ }
+ }
+ series->dataProxy()->resetArray(dataArray);
+}
diff --git a/tests/scattertest/scatterchart.h b/tests/scattertest/scatterchart.h
index 21357d62..97c3b1f9 100644
--- a/tests/scattertest/scatterchart.h
+++ b/tests/scattertest/scatterchart.h
@@ -25,6 +25,7 @@
#include <QFont>
#include <QDebug>
#include <QTimer>
+#include <QLabel>
using namespace QtDataVisualization;
@@ -45,7 +46,21 @@ public:
void changePointSize(int pointSize);
void setBackgroundEnabled(int enabled);
void setGridEnabled(int enabled);
+ void setMinX(int min);
+ void setMinY(int min);
+ void setMinZ(int min);
+ void setMaxX(int max);
+ void setMaxY(int max);
+ void setMaxZ(int max);
+ void setAspectRatio(int ratio);
void start();
+ void massiveDataTest();
+ void massiveTestScroll();
+ void massiveTestAppendAndScroll();
+ void setFpsMeasurement(bool enable);
+ void setFpsLabel(QLabel *fpsLabel) { m_fpsLabel = fpsLabel; }
+ void testItemChanges();
+ void testAxisReverse();
public slots:
void changeShadowQuality(int quality);
@@ -65,14 +80,19 @@ public slots:
void selectItem();
void handleSelectionChange(int index);
void setGradient();
+ void clearSeriesData();
void addSeries();
void removeSeries();
void toggleSeriesVisibility();
void changeSeriesName();
-
void handleAxisXChanged(QValue3DAxis *axis);
void handleAxisYChanged(QValue3DAxis *axis);
void handleAxisZChanged(QValue3DAxis *axis);
+ void handleFpsChange(qreal fps);
+ void changeLabelRotation(int rotation);
+ void toggleAxisTitleVisibility(bool enabled);
+ void toggleAxisTitleFixed(bool enabled);
+ void renderToImage();
signals:
void shadowQualityChanged(int quality);
@@ -80,6 +100,10 @@ signals:
private:
QVector3D randVector();
QScatter3DSeries *createAndAddSeries();
+ void populateFlatSeries(QScatter3DSeries *series, int rows, int columns, float value);
+ void populateRisingSeries(QScatter3DSeries *series, int rows, int columns, float minValue,
+ float maxValue);
+ void deleteAxis(QValue3DAxis *axis);
Q3DScatter *m_chart;
int m_fontSize;
@@ -87,6 +111,8 @@ private:
int m_loopCounter;
int m_selectedItem;
QScatter3DSeries *m_targetSeries;
+ QScatterDataArray m_massiveTestCacheArray;
+ QLabel *m_fpsLabel;
};
#endif
diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp
index 7f2a3ef2..ed86f03c 100644
--- a/tests/surfacetest/graphmodifier.cpp
+++ b/tests/surfacetest/graphmodifier.cpp
@@ -49,8 +49,10 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
m_activeSample(0),
m_fontSize(40),
m_rangeX(16.0),
+ m_rangeY(16.0),
m_rangeZ(16.0),
m_minX(-8.0),
+ m_minY(-8.0),
m_minZ(-8.0),
m_addRowCounter(m_zCount),
m_insertTestZPos(0),
@@ -85,14 +87,15 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
m_multiSampleOffsetX[3] = m_offset;
m_multiSampleOffsetZ[3] = m_offset;
- m_graph->axisX()->setRange(-m_limitX - m_offset, m_limitX + m_offset);
- m_graph->axisY()->setRange(-1.0f, 4.5f);
- m_graph->axisZ()->setRange(-m_limitZ - m_offset, m_limitZ + m_offset);
+// m_graph->axisX()->setRange(-m_limitX - m_offset, m_limitX + m_offset);
+// m_graph->axisY()->setRange(-1.0f, 4.5f);
+// m_graph->axisZ()->setRange(-m_limitZ - m_offset, m_limitZ + m_offset);
#else
- m_graph->axisX()->setRange(m_minX, m_minX + m_rangeX);
- m_graph->axisZ()->setRange(m_minZ, m_minZ + m_rangeZ);
m_graph->addSeries(m_theSeries);
#endif
+ m_graph->axisX()->setRange(m_minX, m_minX + m_rangeX);
+ m_graph->axisY()->setRange(m_minY, m_minY + m_rangeY);
+ m_graph->axisZ()->setRange(m_minZ, m_minZ + m_rangeZ);
for (int i = 0; i < 4; i++) {
m_multiseries[i] = new QSurface3DSeries;
@@ -114,6 +117,8 @@ GraphModifier::GraphModifier(Q3DSurface *graph)
&GraphModifier::handleAxisYChanged);
QObject::connect(m_graph, &Q3DSurface::axisZChanged, this,
&GraphModifier::handleAxisZChanged);
+ QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
+ &GraphModifier::handleFpsChange);
}
GraphModifier::~GraphModifier()
@@ -134,18 +139,27 @@ void GraphModifier::fillSeries()
QSurfaceDataArray *dataArray4 = new QSurfaceDataArray;
dataArray4->reserve(m_zCount);
+
for (int i = 0; i < m_zCount; i++) {
QSurfaceDataRow *newRow[4];
+ float zAdjust = 0.0f;
+ if (i == 2)
+ zAdjust = 0.7f;
+
for (int s = 0; s < 4; s++) {
newRow[s] = new QSurfaceDataRow(m_xCount);
- float z = float(i) - m_limitZ + 0.5f + m_multiSampleOffsetZ[s];
+ float z = float(i) - m_limitZ + 0.5f + m_multiSampleOffsetZ[s] + zAdjust;
for (int j = 0; j < m_xCount; j++) {
- float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[s];
+ float xAdjust = 0.0f;
+ if (j == 4)
+ xAdjust = 0.7f;
+ float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[s] + xAdjust;
float angle = (z * x) / full * 1.57f;
- float y = qSin(angle * float(qPow(1.3f, s))) + 1.1f * s;
+ float y = (qSin(angle * float(qPow(1.3f, s))) + 1.1f * s) * 3.0f - 5.0f + xAdjust + zAdjust;
(*newRow[s])[j].setPosition(QVector3D(x, y, z));
}
}
+ qDebug() << newRow[0]->at(0).z();
*dataArray1 << newRow[0];
*dataArray2 << newRow[1];
*dataArray3 << newRow[2];
@@ -550,6 +564,14 @@ void GraphModifier::adjustXRange(int range)
qDebug() << "X Range =" << range;
}
+void GraphModifier::adjustYRange(int range)
+{
+ m_rangeY = range;
+ m_graph->axisY()->setRange(m_minY, m_minY + m_rangeY);
+
+ qDebug() << "Y Range =" << range;
+}
+
void GraphModifier::adjustZRange(int range)
{
m_rangeZ = range;
@@ -566,6 +588,14 @@ void GraphModifier::adjustXMin(int min)
qDebug() << "X Minimum =" << min;
}
+void GraphModifier::adjustYMin(int min)
+{
+ m_minY = min;
+ m_graph->axisY()->setRange(m_minY, m_minY + m_rangeY);
+
+ qDebug() << "Y Minimum =" << min;
+}
+
void GraphModifier::adjustZMin(int min)
{
m_minZ = min;
@@ -665,6 +695,88 @@ void GraphModifier::handleAxisZChanged(QValue3DAxis *axis)
qDebug() << __FUNCTION__ << axis << axis->orientation() << (axis == m_graph->axisZ());
}
+void GraphModifier::handleFpsChange(qreal fps)
+{
+ qDebug() << "FPS:" << fps;
+}
+
+void GraphModifier::changeLabelRotation(int rotation)
+{
+ m_graph->axisX()->setLabelAutoRotation(float(rotation));
+ m_graph->axisY()->setLabelAutoRotation(float(rotation));
+ m_graph->axisZ()->setLabelAutoRotation(float(rotation));
+}
+
+void GraphModifier::toggleAxisTitleVisibility(bool enabled)
+{
+ m_graph->axisX()->setTitleVisible(enabled);
+ m_graph->axisY()->setTitleVisible(enabled);
+ m_graph->axisZ()->setTitleVisible(enabled);
+}
+
+void GraphModifier::toggleAxisTitleFixed(bool enabled)
+{
+ m_graph->axisX()->setTitleFixed(enabled);
+ m_graph->axisY()->setTitleFixed(enabled);
+ m_graph->axisZ()->setTitleFixed(enabled);
+}
+
+void GraphModifier::toggleXAscending(bool enabled)
+{
+ // Flip data array contents if necessary
+ foreach (QSurface3DSeries *series, m_graph->seriesList()) {
+ QSurfaceDataArray *array = const_cast<QSurfaceDataArray *>(series->dataProxy()->array());
+ const int rowCount = array->size();
+ const int columnCount = array->at(0)->size();
+ const bool dataAscending = array->at(0)->at(0).x() < array->at(0)->at(columnCount - 1).x();
+ if (dataAscending != enabled) {
+ // Create new array of equal size
+ QSurfaceDataArray *newArray = new QSurfaceDataArray;
+ newArray->reserve(rowCount);
+ for (int i = 0; i < rowCount; i++)
+ newArray->append(new QSurfaceDataRow(columnCount));
+
+ // Flip each row
+ for (int i = 0; i < rowCount; i++) {
+ QSurfaceDataRow *oldRow = array->at(i);
+ QSurfaceDataRow *newRow = newArray->at(i);
+ for (int j = 0; j < columnCount; j++)
+ (*newRow)[j] = oldRow->at(columnCount - 1 - j);
+ }
+
+ series->dataProxy()->resetArray(newArray);
+ }
+ }
+}
+
+void GraphModifier::toggleZAscending(bool enabled)
+{
+ // Flip data array contents if necessary
+ foreach (QSurface3DSeries *series, m_graph->seriesList()) {
+ QSurfaceDataArray *array = const_cast<QSurfaceDataArray *>(series->dataProxy()->array());
+ const int rowCount = array->size();
+ const int columnCount = array->at(0)->size();
+ const bool dataAscending = array->at(0)->at(0).z() < array->at(rowCount - 1)->at(0).z();
+ if (dataAscending != enabled) {
+ // Create new array of equal size
+ QSurfaceDataArray *newArray = new QSurfaceDataArray;
+ newArray->reserve(rowCount);
+ for (int i = 0; i < rowCount; i++)
+ newArray->append(new QSurfaceDataRow(columnCount));
+
+ // Flip each column
+ for (int i = 0; i < rowCount; i++) {
+ QSurfaceDataRow *oldRow = array->at(rowCount - 1 - i);
+ QSurfaceDataRow *newRow = newArray->at(i);
+ for (int j = 0; j < columnCount; j++)
+ (*newRow)[j] = oldRow->at(j);
+ }
+
+ series->dataProxy()->resetArray(newArray);
+ }
+ }
+}
+
void GraphModifier::resetArrayAndSliders(QSurfaceDataArray *array, float minZ, float maxZ, float minX, float maxX)
{
m_axisMinSliderX->setValue(minX);
@@ -746,6 +858,28 @@ QSurfaceDataRow *GraphModifier::createMultiRow(int row, int series, bool change)
return newRow;
}
+void GraphModifier::populateRisingSeries(QSurface3DSeries *series, int rows, int columns,
+ float minValue, float maxValue, bool ascendingX,
+ bool ascendingZ)
+{
+ QSurfaceDataArray *dataArray = new QSurfaceDataArray;
+ dataArray->reserve(rows);
+ float range = maxValue - minValue;
+ int arraySize = rows * columns;
+ for (int i = 0; i < rows; i++) {
+ QSurfaceDataRow *dataRow = new QSurfaceDataRow(columns);
+ for (int j = 0; j < columns; j++) {
+ float xValue = ascendingX ? float(j) : float(columns - j - 1);
+ float yValue = minValue + (range * i * j / arraySize);
+ float zValue = ascendingZ ? float(i) : float(rows - i - 1);
+ (*dataRow)[j].setPosition(QVector3D(xValue, yValue, zValue));
+ }
+ dataArray->append(dataRow);
+ }
+ series->dataProxy()->resetArray(dataArray);
+
+}
+
void GraphModifier::changeRows()
{
if (m_activeSample == GraphModifier::SqrtSin) {
@@ -1065,6 +1199,308 @@ void GraphModifier::removeRow()
m_zCount--;
}
+void GraphModifier::resetArray()
+{
+ qDebug() << "Reset series data array";
+ int rows = 10;
+ int columns = 10;
+ float randFactor = float(rand() % 100) / 100.0f;
+ QSurfaceDataArray *planeArray = new QSurfaceDataArray;
+ planeArray->reserve(rows);
+
+ for (int i = 0; i < rows; i++) {
+ planeArray->append(new QSurfaceDataRow);
+ (*planeArray)[i]->resize(columns);
+ for (int j = 0; j < columns; j++) {
+ (*planeArray->at(i))[j].setX(float(j) * randFactor);
+ (*planeArray->at(i))[j].setY(float(i - j) * randFactor);
+ (*planeArray->at(i))[j].setZ(float(i));
+ }
+ }
+
+#ifdef MULTI_SERIES
+ int series = rand() % 4;
+ m_multiseries[series]->dataProxy()->resetArray(planeArray);
+#else
+ m_theSeries->dataProxy()->resetArray(planeArray);
+#endif
+}
+
+void GraphModifier::resetArrayEmpty()
+{
+ QSurfaceDataArray *emptryArray = new QSurfaceDataArray;
+#ifdef MULTI_SERIES
+ int series = rand() % 4;
+ m_multiseries[series]->dataProxy()->resetArray(emptryArray);
+#else
+ m_theSeries->dataProxy()->resetArray(emptryArray);
+#endif
+}
+
+void GraphModifier::massiveDataTest()
+{
+ static int testPhase = 0;
+ static const int cacheSize = 1000;
+ const int columns = 200;
+ const int rows = 200000;
+ const int visibleRows = 200;
+ const float yRangeMin = 0.0f;
+ const float yRangeMax = 1.0f;
+ const float yRangeMargin = 0.05f;
+ static QTimer *massiveTestTimer = 0;
+ static QSurface3DSeries *series = new QSurface3DSeries;
+
+ // To speed up massive array creation, we generate a smaller cache array
+ // and copy rows from that to our main array
+ if (!m_massiveTestCacheArray.size()) {
+ m_massiveTestCacheArray.reserve(cacheSize);
+ float minY = yRangeMin + yRangeMargin;
+ float maxY = yRangeMax - yRangeMargin;
+ float rowBase = minY;
+ float direction = 1.0f;
+ for (int i = 0; i < cacheSize; i++) {
+ m_massiveTestCacheArray.append(new QSurfaceDataRow);
+ m_massiveTestCacheArray[i]->resize(columns);
+ rowBase += direction * (float(rand() % 3) / 100.0f);
+ if (rowBase > maxY) {
+ rowBase = maxY;
+ direction = -1.0f;
+ } else if (rowBase < minY) {
+ rowBase = minY;
+ direction = 1.0f;
+ }
+ for (int j = 0; j < columns; j++) {
+ float randFactor = float(rand() % 100) / (100 / yRangeMargin);
+ (*m_massiveTestCacheArray.at(i))[j].setX(float(j));
+ (*m_massiveTestCacheArray.at(i))[j].setY(rowBase + randFactor);
+ // Z value is irrelevant, we replace it anyway when we take row to use
+ }
+ }
+ massiveTestTimer = new QTimer;
+ }
+
+ switch (testPhase) {
+ case 0: {
+ qDebug() << __FUNCTION__ << testPhase << ": Setting the graph up...";
+ QValue3DAxis *xAxis = new QValue3DAxis();
+ QValue3DAxis *yAxis = new QValue3DAxis();
+ QValue3DAxis *zAxis = new QValue3DAxis();
+ xAxis->setRange(0.0f, float(columns));
+ yAxis->setRange(yRangeMin, yRangeMax);
+ zAxis->setRange(0.0f, float(visibleRows));
+ xAxis->setSegmentCount(1);
+ yAxis->setSegmentCount(1);
+ zAxis->setSegmentCount(1);
+ m_graph->setMeasureFps(true);
+ m_graph->setAxisX(xAxis);
+ m_graph->setAxisY(yAxis);
+ m_graph->setAxisZ(zAxis);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetRight);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ foreach (QAbstract3DSeries *series, m_graph->seriesList())
+ m_graph->removeSeries(static_cast<QSurface3DSeries *>(series));
+
+ qDebug() << __FUNCTION__ << testPhase << ": Creating massive array..."
+ << rows << "x" << columns;
+ // Reset to zero first to avoid having memory allocated for two massive arrays at the same
+ // time on the second and subsequent runs.
+ series->dataProxy()->resetArray(0);
+ QSurfaceDataArray *massiveArray = new QSurfaceDataArray;
+ massiveArray->reserve(rows);
+
+ for (int i = 0; i < rows; i++) {
+ QSurfaceDataRow *newRow = new QSurfaceDataRow(*m_massiveTestCacheArray.at(i % cacheSize));
+ for (int j = 0; j < columns; j++)
+ (*newRow)[j].setZ(float(i));
+ massiveArray->append(newRow);
+ }
+ qDebug() << __FUNCTION__ << testPhase << ": Massive array creation finished!";
+
+ series->dataProxy()->resetArray(massiveArray);
+ m_graph->addSeries(series);
+ break;
+ }
+ case 1: {
+ qDebug() << __FUNCTION__ << testPhase << ": Scroll";
+ QObject::disconnect(massiveTestTimer, 0, this, 0);
+ QObject::connect(massiveTestTimer, &QTimer::timeout, this,
+ &GraphModifier::massiveTestScroll);
+ massiveTestTimer->start(16);
+ break;
+ }
+ case 2: {
+ qDebug() << __FUNCTION__ << testPhase << ": Append and scroll";
+ massiveTestTimer->stop();
+ QObject::disconnect(massiveTestTimer, 0, this, 0);
+ QObject::connect(massiveTestTimer, &QTimer::timeout, this,
+ &GraphModifier::massiveTestAppendAndScroll);
+ m_graph->axisZ()->setRange(rows - visibleRows, rows);
+ massiveTestTimer->start(16);
+ break;
+ }
+ default:
+ QObject::disconnect(massiveTestTimer, 0, this, 0);
+ massiveTestTimer->stop();
+ qDebug() << __FUNCTION__ << testPhase << ": Resetting the test";
+ testPhase = -1;
+ }
+ testPhase++;
+}
+
+void GraphModifier::massiveTestScroll()
+{
+ const int scrollAmount = 20;
+ int maxRows = m_graph->seriesList().at(0)->dataProxy()->rowCount();
+ int min = m_graph->axisZ()->min() + scrollAmount;
+ int max = m_graph->axisZ()->max() + scrollAmount;
+ if (max >= maxRows) {
+ max = max - min;
+ min = 0;
+ }
+ m_graph->axisZ()->setRange(min, max);
+}
+
+void GraphModifier::massiveTestAppendAndScroll()
+{
+ const int addedRows = 50;
+ int maxRows = m_graph->seriesList().at(0)->dataProxy()->rowCount();
+ int columns = m_graph->seriesList().at(0)->dataProxy()->columnCount();
+
+ QSurfaceDataArray appendArray;
+ appendArray.reserve(addedRows);
+ for (int i = 0; i < addedRows; i++) {
+ QSurfaceDataRow *newRow = new QSurfaceDataRow(*m_massiveTestCacheArray.at((i + maxRows) % 1000));
+ for (int j = 0; j < columns; j++)
+ (*newRow)[j].setZ(float(maxRows + i));
+ appendArray.append(newRow);
+ }
+ m_graph->seriesList().at(0)->dataProxy()->addRows(appendArray);
+ int min = m_graph->axisZ()->min() + addedRows;
+ int max = m_graph->axisZ()->max() + addedRows;
+ m_graph->axisZ()->setRange(min, max);
+}
+
+void GraphModifier::testAxisReverse()
+{
+ static int counter = 0;
+ const int rowCount = 16;
+ const int colCount = 16;
+ static QSurface3DSeries *series0 = 0;
+ static QSurface3DSeries *series1 = 0;
+
+ switch (counter) {
+ case 0: {
+ qDebug() << __FUNCTION__ << counter << "Setup test";
+ foreach (QSurface3DSeries *series, m_graph->seriesList())
+ m_graph->removeSeries(series);
+ foreach (QValue3DAxis *axis, m_graph->axes())
+ m_graph->releaseAxis(axis);
+ delete series0;
+ delete series1;
+ series0 = new QSurface3DSeries;
+ series1 = new QSurface3DSeries;
+ populateRisingSeries(series0, rowCount, colCount, 0.0f, 50.0f, true, true);
+ populateRisingSeries(series1, rowCount, colCount, -20.0f, 30.0f, true, true);
+ m_graph->axisX()->setRange(0.0f, 10.0f);
+ m_graph->axisY()->setRange(-20.0f, 50.0f);
+ m_graph->axisZ()->setRange(5.0f, 15.0f);
+ m_graph->addSeries(series0);
+ m_graph->addSeries(series1);
+ }
+ break;
+ case 1: {
+ qDebug() << __FUNCTION__ << counter << "Reverse X axis";
+ m_graph->axisX()->setReversed(true);
+ }
+ break;
+ case 2: {
+ qDebug() << __FUNCTION__ << counter << "Reverse Y axis";
+ m_graph->axisY()->setReversed(true);
+ }
+ break;
+ case 3: {
+ qDebug() << __FUNCTION__ << counter << "Reverse Z axis";
+ m_graph->axisZ()->setReversed(true);
+ }
+ break;
+ case 4: {
+ qDebug() << __FUNCTION__ << counter << "Return all axes to normal";
+ m_graph->axisX()->setReversed(false);
+ m_graph->axisY()->setReversed(false);
+ m_graph->axisZ()->setReversed(false);
+ }
+ break;
+ case 5: {
+ qDebug() << __FUNCTION__ << counter << "Reverse all axes";
+ m_graph->axisX()->setReversed(true);
+ m_graph->axisY()->setReversed(true);
+ m_graph->axisZ()->setReversed(true);
+ }
+ break;
+ default:
+ qDebug() << __FUNCTION__ << "Resetting test";
+ counter = -1;
+ }
+ counter++;
+}
+
+void GraphModifier::testDataOrdering()
+{
+ static int counter = 0;
+ const int rowCount = 20;
+ const int colCount = 20;
+ static QSurface3DSeries *series0 = 0;
+ static QSurface3DSeries *series1 = 0;
+ const float series0min = 0.0f;
+ const float series0max = 50.0f;
+ const float series1min = -20.0f;
+ const float series1max = 30.0f;
+
+ switch (counter) {
+ case 0: {
+ qDebug() << __FUNCTION__ << counter << "Setup test - both ascending";
+ foreach (QSurface3DSeries *series, m_graph->seriesList())
+ m_graph->removeSeries(series);
+ foreach (QValue3DAxis *axis, m_graph->axes())
+ m_graph->releaseAxis(axis);
+ delete series0;
+ delete series1;
+ series0 = new QSurface3DSeries;
+ series1 = new QSurface3DSeries;
+ populateRisingSeries(series0, rowCount, colCount, series0min, series0max, true, true);
+ populateRisingSeries(series1, rowCount, colCount, series1min, series1max, true, true);
+ m_graph->axisX()->setRange(5.0f, 15.0f);
+ m_graph->axisY()->setRange(-20.0f, 50.0f);
+ m_graph->axisZ()->setRange(5.0f, 15.0f);
+ m_graph->addSeries(series0);
+ m_graph->addSeries(series1);
+ }
+ break;
+ case 1: {
+ qDebug() << __FUNCTION__ << counter << "Ascending X, descending Z";
+ populateRisingSeries(series0, rowCount, colCount, series0min, series0max, true, false);
+ populateRisingSeries(series1, rowCount, colCount, series1min, series1max, true, false);
+ }
+ break;
+ case 2: {
+ qDebug() << __FUNCTION__ << counter << "Descending X, ascending Z";
+ populateRisingSeries(series0, rowCount, colCount, series0min, series0max, false, true);
+ populateRisingSeries(series1, rowCount, colCount, series1min, series1max, false, true);
+ }
+ break;
+ case 3: {
+ qDebug() << __FUNCTION__ << counter << "Both descending";
+ populateRisingSeries(series0, rowCount, colCount, series0min, series0max, false, false);
+ populateRisingSeries(series1, rowCount, colCount, series1min, series1max, false, false);
+ }
+ break;
+ default:
+ qDebug() << __FUNCTION__ << "Resetting test";
+ counter = -1;
+ }
+ counter++;
+}
+
void GraphModifier::changeMesh()
{
static int model = 0;
@@ -1130,3 +1566,9 @@ void GraphModifier::updateSamples()
break;
}
}
+
+void GraphModifier::setAspectRatio(int ratio)
+{
+ float aspectRatio = float(ratio) / 10.0f;
+ m_graph->setAspectRatio(aspectRatio);
+}
diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h
index 7d7d425e..5f1a252a 100644
--- a/tests/surfacetest/graphmodifier.h
+++ b/tests/surfacetest/graphmodifier.h
@@ -82,8 +82,10 @@ public:
void adjustXCount(int count);
void adjustZCount(int count);
void adjustXRange(int range);
+ void adjustYRange(int range);
void adjustZRange(int range);
void adjustXMin(int min);
+ void adjustYMin(int min);
void adjustZMin(int min);
void updateSamples();
void gradientPressed();
@@ -103,6 +105,15 @@ public:
void insertRow();
void insertRows();
void removeRow();
+ void resetArray();
+ void resetArrayEmpty();
+ void massiveDataTest();
+ void massiveTestScroll();
+ void massiveTestAppendAndScroll();
+ void testAxisReverse();
+ void testDataOrdering();
+
+ void setAspectRatio(int ratio);
public slots:
void changeShadowQuality(int quality);
@@ -114,12 +125,20 @@ public slots:
void handleAxisXChanged(QValue3DAxis *axis);
void handleAxisYChanged(QValue3DAxis *axis);
void handleAxisZChanged(QValue3DAxis *axis);
+ void handleFpsChange(qreal fps);
+ void changeLabelRotation(int rotation);
+ void toggleAxisTitleVisibility(bool enabled);
+ void toggleAxisTitleFixed(bool enabled);
+ void toggleXAscending(bool enabled);
+ void toggleZAscending(bool enabled);
private:
void fillSeries();
void resetArrayAndSliders(QSurfaceDataArray *array, float minZ, float maxZ, float minX,
float maxX);
QSurfaceDataRow *createMultiRow(int row, int series, bool change);
+ void populateRisingSeries(QSurface3DSeries *series, int rows, int columns, float minValue,
+ float maxValue, bool ascendingX, bool ascendingZ);
Q3DSurface *m_graph;
QSurface3DSeries *m_multiseries[4];
@@ -143,8 +162,10 @@ private:
int m_activeSample;
int m_fontSize;
float m_rangeX;
+ float m_rangeY;
float m_rangeZ;
float m_minX;
+ float m_minY;
float m_minZ;
int m_addRowCounter;
int m_insertTestZPos;
@@ -162,6 +183,7 @@ private:
float m_offset;
float m_multiSampleOffsetX[4];
float m_multiSampleOffsetZ[4];
+ QSurfaceDataArray m_massiveTestCacheArray;
};
#endif
diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp
index 99c60893..5806d7b0 100644
--- a/tests/surfacetest/main.cpp
+++ b/tests/surfacetest/main.cpp
@@ -56,7 +56,7 @@ int main(int argc, char *argv[])
surfaceGraph->activeTheme()->setType(Q3DTheme::Theme(initialTheme));
QWidget *container = QWidget::createWindowContainer(surfaceGraph);
- container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 2));
+ container->setMinimumSize(QSize(screenSize.width() / 4, screenSize.height() / 4));
container->setMaximumSize(screenSize);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
container->setFocusPolicy(Qt::StrongFocus);
@@ -182,30 +182,47 @@ int main(int argc, char *argv[])
QSlider *axisRangeSliderX = new QSlider(Qt::Horizontal, widget);
axisRangeSliderX->setTickInterval(1);
- axisRangeSliderX->setMinimum(2);
+ axisRangeSliderX->setMinimum(1);
axisRangeSliderX->setValue(16);
axisRangeSliderX->setMaximum(100);
axisRangeSliderX->setEnabled(true);
+ QSlider *axisRangeSliderY = new QSlider(Qt::Horizontal, widget);
+ axisRangeSliderY->setTickInterval(1);
+ axisRangeSliderY->setMinimum(1);
+ axisRangeSliderY->setValue(16);
+ axisRangeSliderY->setMaximum(100);
+ axisRangeSliderY->setEnabled(true);
QSlider *axisRangeSliderZ = new QSlider(Qt::Horizontal, widget);
axisRangeSliderZ->setTickInterval(1);
- axisRangeSliderZ->setMinimum(2);
+ axisRangeSliderZ->setMinimum(1);
axisRangeSliderZ->setValue(16);
axisRangeSliderZ->setMaximum(100);
axisRangeSliderZ->setEnabled(true);
QSlider *axisMinSliderX = new QSlider(Qt::Horizontal, widget);
axisMinSliderX->setTickInterval(1);
- axisMinSliderX->setMinimum(-50);
+ axisMinSliderX->setMinimum(-100);
axisMinSliderX->setValue(-8);
- axisMinSliderX->setMaximum(50);
+ axisMinSliderX->setMaximum(100);
axisMinSliderX->setEnabled(true);
+ QSlider *axisMinSliderY = new QSlider(Qt::Horizontal, widget);
+ axisMinSliderY->setTickInterval(1);
+ axisMinSliderY->setMinimum(-100);
+ axisMinSliderY->setValue(-8);
+ axisMinSliderY->setMaximum(100);
+ axisMinSliderY->setEnabled(true);
QSlider *axisMinSliderZ = new QSlider(Qt::Horizontal, widget);
axisMinSliderZ->setTickInterval(1);
- axisMinSliderZ->setMinimum(-50);
+ axisMinSliderZ->setMinimum(-100);
axisMinSliderZ->setValue(-8);
- axisMinSliderZ->setMaximum(50);
+ axisMinSliderZ->setMaximum(100);
axisMinSliderZ->setEnabled(true);
+ QSlider *aspectRatioSlider = new QSlider(Qt::Horizontal, widget);
+ aspectRatioSlider->setMinimum(1);
+ aspectRatioSlider->setValue(20);
+ aspectRatioSlider->setMaximum(100);
+
QLinearGradient gr(0, 0, 100, 1);
gr.setColorAt(0.0, Qt::black);
gr.setColorAt(0.33, Qt::blue);
@@ -326,6 +343,21 @@ int main(int argc, char *argv[])
QPushButton *removeRowButton = new QPushButton(widget);
removeRowButton->setText(QStringLiteral("Remove a row"));
+ QPushButton *resetArrayButton = new QPushButton(widget);
+ resetArrayButton->setText(QStringLiteral("Reset Series Array to plane"));
+
+ QPushButton *resetArrayEmptyButton = new QPushButton(widget);
+ resetArrayEmptyButton->setText(QStringLiteral("Reset Series Array to empty"));
+
+ QPushButton *massiveDataTestButton = new QPushButton(widget);
+ massiveDataTestButton->setText(QStringLiteral("Massive data test"));
+
+ QPushButton *testReverseButton = new QPushButton(widget);
+ testReverseButton->setText(QStringLiteral("Test Axis Reversing"));
+
+ QPushButton *testDataOrderingButton = new QPushButton(widget);
+ testDataOrderingButton->setText(QStringLiteral("Test data ordering"));
+
QFrame* line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
@@ -338,6 +370,29 @@ int main(int argc, char *argv[])
line3->setFrameShape(QFrame::HLine);
line3->setFrameShadow(QFrame::Sunken);
+ QCheckBox *axisTitlesVisibleCB = new QCheckBox(widget);
+ axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible"));
+ axisTitlesVisibleCB->setChecked(false);
+
+ QCheckBox *axisTitlesFixedCB = new QCheckBox(widget);
+ axisTitlesFixedCB->setText(QStringLiteral("Axis titles fixed"));
+ axisTitlesFixedCB->setChecked(true);
+
+ QSlider *axisLabelRotationSlider = new QSlider(Qt::Horizontal, widget);
+ axisLabelRotationSlider->setTickInterval(10);
+ axisLabelRotationSlider->setTickPosition(QSlider::TicksBelow);
+ axisLabelRotationSlider->setMinimum(0);
+ axisLabelRotationSlider->setValue(0);
+ axisLabelRotationSlider->setMaximum(90);
+
+ QCheckBox *xAscendingCB = new QCheckBox(widget);
+ xAscendingCB->setText(QStringLiteral("X Ascending"));
+ xAscendingCB->setChecked(true);
+
+ QCheckBox *zAscendingCB = new QCheckBox(widget);
+ zAscendingCB->setText(QStringLiteral("Z Ascending"));
+ zAscendingCB->setChecked(true);
+
// Add controls to the layout
#ifdef MULTI_SERIES
vLayout->addWidget(series1CB);
@@ -376,12 +431,18 @@ int main(int argc, char *argv[])
vLayout->addWidget(gridSliderX);
vLayout->addWidget(gridSliderZ);
#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("Change font")));
vLayout2->addWidget(fontList);
vLayout2->addWidget(labelButton);
@@ -409,6 +470,15 @@ int main(int argc, char *argv[])
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);
widget->show();
@@ -510,10 +580,14 @@ int main(int argc, char *argv[])
#endif
QObject::connect(axisRangeSliderX, &QSlider::valueChanged,
modifier, &GraphModifier::adjustXRange);
+ QObject::connect(axisRangeSliderY, &QSlider::valueChanged,
+ modifier, &GraphModifier::adjustYRange);
QObject::connect(axisRangeSliderZ, &QSlider::valueChanged,
modifier, &GraphModifier::adjustZRange);
QObject::connect(axisMinSliderX, &QSlider::valueChanged,
modifier, &GraphModifier::adjustXMin);
+ QObject::connect(axisMinSliderY, &QSlider::valueChanged,
+ modifier, &GraphModifier::adjustYMin);
QObject::connect(axisMinSliderZ, &QSlider::valueChanged,
modifier, &GraphModifier::adjustZMin);
QObject::connect(colorPB, &QPushButton::pressed,
@@ -556,6 +630,29 @@ int main(int argc, char *argv[])
modifier, &GraphModifier::insertRows);
QObject::connect(removeRowButton,&QPushButton::clicked,
modifier, &GraphModifier::removeRow);
+ QObject::connect(resetArrayButton,&QPushButton::clicked,
+ modifier, &GraphModifier::resetArray);
+ QObject::connect(resetArrayEmptyButton,&QPushButton::clicked,
+ modifier, &GraphModifier::resetArrayEmpty);
+ QObject::connect(massiveDataTestButton,&QPushButton::clicked,
+ modifier, &GraphModifier::massiveDataTest);
+ QObject::connect(testReverseButton, &QPushButton::clicked,
+ modifier, &GraphModifier::testAxisReverse);
+ QObject::connect(testDataOrderingButton, &QPushButton::clicked,
+ modifier, &GraphModifier::testDataOrdering);
+ QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged,
+ modifier, &GraphModifier::toggleAxisTitleVisibility);
+ QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged,
+ modifier, &GraphModifier::toggleAxisTitleFixed);
+ QObject::connect(axisLabelRotationSlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::changeLabelRotation);
+ QObject::connect(xAscendingCB, &QCheckBox::stateChanged,
+ modifier, &GraphModifier::toggleXAscending);
+ QObject::connect(zAscendingCB, &QCheckBox::stateChanged,
+ modifier, &GraphModifier::toggleZAscending);
+
+ QObject::connect(aspectRatioSlider, &QSlider::valueChanged,
+ modifier, &GraphModifier::setAspectRatio);
#ifdef MULTI_SERIES
modifier->setSeries1CB(series1CB);
diff --git a/tests/tests.pro b/tests/tests.pro
index 613534e9..8bbdc3f2 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -14,7 +14,9 @@ SUBDIRS += barstest \
qmldynamicdata \
multigraphs \
directional \
- qmlmultiwindow
+ qmlmultiwindow \
+ itemmodeltest \
+ qmlmultitest
#SUBDIRS += kinectsurface
diff --git a/tools/blender/oilrefinery.blend b/tools/blender/oilrefinery.blend
new file mode 100644
index 00000000..ab50b8d7
--- /dev/null
+++ b/tools/blender/oilrefinery.blend
Binary files differ
diff --git a/tools/blender/oilrig.blend b/tools/blender/oilrig.blend
new file mode 100644
index 00000000..d7a3b5fa
--- /dev/null
+++ b/tools/blender/oilrig.blend
Binary files differ