// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example qmlaxishandling
    \meta tags {DataVisualization, Scatter3D, Custom Input Handler, Dynamic Data, Custom Axis Formatter, Scatter Graph}
    \meta category {Graphics}
    \title Axis Handling
    \ingroup qtdatavisualization_qmlexamples
    \brief Implementing axis dragging with a custom input handler in QML, and creating a custom axis formatter.
    \since QtDataVisualization 6.5

    \e {Axis Handling} demonstrates two different custom features with axes. The features have their
    own tabs in the application.

    The following sections concentrate on those features only and skip explaining the basic
    functionality - for more detailed QML example documentation, see \l{Simple Scatter Graph}.

    \image qmlaxishandling-example.png

    \include examples-run.qdocinc

    \section1 Axis Dragging

    In the \uicontrol {Axis Dragging} tab, implement a custom input handler in QML that enables you
    to drag axis labels to change axis ranges. Further, use orthographic projection and dynamically
    update the properties of a custom item.

    \section2 Overriding Default Input Handling

    To deactivate the default input handling mechanism, set the active input handler of Scatter3D
    graph to \c{null}:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 0
    \dots

    Then, add a MouseArea and set it to fill the parent, which is the same \c Item our
    \c scatterGraph is contained in. Also, set it to accept only left mouse button presses,
    as in this example the other buttons are not needed:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 1
    \dots

    Then, listen to mouse presses, and when caught, send a selection query to the graph:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 2

    The \c{onPositionChanged} signal handler catches the current mouse position that will be
    needed for move distance calculation:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 3
    \dots

    At the end of \c{onPositionChanged}, save the previous mouse position for move distance
    calculation that will be introduced later:

    \dots 0
    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 4

    \section2 Translating Mouse Movement to Axis Range Change

    In \c {scatterGraph}, listen to \c {onSelectedElementChanged}. The signal
    is emitted after the selection query has been made in the \c{onPressed} of the \c{inputArea}.
    Set the element type into a property you defined (\c{property int selectedAxisLabel: -1}) in the
    main component, since it is of a type you are interested in:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 5

    Then, back in the \c onPositionChanged of \c{inputArea}, check if a mouse button is pressed
    and if you have a current axis label selection. If the conditions are met, call the
    function that does the conversion from mouse movement to axis range update:

    \dots 0
    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 6
    \dots 0

    The conversion is easy in this case, as the camera rotation is fixed. You can use some
    precalculated values, calculate mouse move distance, and apply the values to the
    selected axis range:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 7

    For a more sophisticated conversion from mouse movement to axis range update, see
    \l{Graph Gallery}.

    \section2 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 the \c orthoProjection
    property of \c{scatterGraph}. The example has a button for toggling it on and off:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 8

    For custom items, add one to the \c customItemList of \c{scatterGraph}:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 9

    You implement a timer to add, remove, and rotate all the items in the graph, and use the same
    timer for rotating the custom item:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 10
    \dots

    \section1 Axis Formatters

    In the \uicontrol {Axis Formatter} tab, create a custom axis formatter. It also illustrates how
    to use predefined axis formatters.

    \section2 Custom Axis Formatter

    Customizing axis formatters requires subclassing the QValue3DAxisFormatter, which cannot be
    done in QML code alone. In this example, the axis interprets the float values as
    a timestamp and shows the date in the axis labels. To achieve this, introduce a new class
    called \c CustomFormatter, which subclasses the QValue3DAxisFormatter:

    \snippet qmlaxishandling/customformatter.h 2
    \dots 0

    Since float values of a QScatter3DSeries cannot be directly cast into QDateTime values due to
    difference in data width, some sort of mapping between the two is needed. To do the mapping,
    specify an origin date for the formatter and interpret the float values from the
    QScatter3DSeries as date offsets to that origin value. The origin date is given as
    a property:

    \snippet qmlaxishandling/customformatter.h 1

    For the mapping from value to QDateTime, use the \c valueToDateTime() method:

    \snippet qmlaxishandling/customformatter.cpp 0

    To function as an axis formatter, \c CustomFormatter needs to reimplement some virtual
    methods:

    \snippet qmlaxishandling/customformatter.h 0

    The first two are simple, just create a new instance of \c CustomFormatter and copy the
    necessary data over to it. Use these two methods to create and update a cache of formatter for
    rendering purposes. Remember to call the superclass implementation of \c populateCopy():

    \snippet qmlaxishandling/customformatter.cpp 1

    \c CustomFormatter does the bulk of its work in the \c recalculate() method, where
    our formatter calculates the grid, subgrid, and label positions, as well as formats the label
    strings.
    In the custom formatter, ignore the segment count of the axis and draw a grid line always at
    midnight. Subsegment count and label positioning is handled normally:

    \snippet qmlaxishandling/customformatter.cpp 2

    The axis labels are formatted to show only the date. However, to increase the resolution of the
    timestamp of the selection label, specify another property for the custom formatter to allow
    the user to customize it:

    \snippet qmlaxishandling/customformatter.h 3

    This selection format property is used in the reimplemented \c stringToValue method, where
    the submitted format is ignored and the custom selection format substituted for it:

    \snippet qmlaxishandling/customformatter.cpp 3

    To expose our new custom formatter to the QML, declare it and make it a QML module.
    For information about how to do this, see \l{Surface Graph Gallery}.

    \section2 QML

    In the QML code, define a different axis for each dimension:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml 3

    The Z-axis is just a regular ValueAxis3D:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml 0

    For the Y-axis, define a logarithmic axis. To make ValueAxis3D show a logarithmic scale,
    specify LogValueAxis3DFormatter for \c formatter property of the axis:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml 2

    And finally, for the X-axis use the new \c CustomFormatter:

    \snippet qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml 1

    The rest of the application consists of fairly self-explanatory logic for modifying the axes and
    showing the graph.

    \section1 Example Contents
*/
