aboutsummaryrefslogtreecommitdiff
import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
import PM 1.0

ApplicationWindow {
    id: mainWindow
    title: "Projection"
    visible: true
    onClosing: Qt.quit()
    Component.onCompleted: {
        setX(Screen.width / 2 - width / 2);
        setY(Screen.height / 2 - height / 2);
    }

    menuBar: MenuBar {
        Menu {
            title: "File"
            MenuItem { action: savePlotAction }
            MenuItem { action: screenshotAction }
            MenuItem { action: quitAction }
        }

        Menu {
            title: "Edit"
            MenuItem { action: undoManipulationAction }
            MenuItem { action: resetManipulationAction }
        }

        Menu {
            title: "Select"
            MenuItem { action: selectRPsAction }
            MenuItem { action: selectCPsAction }
        }

        Menu {
            title: "View"
            MenuItem { action: toggleOptionsAction }
        }
    }

    statusBar: StatusBar {
        RowLayout {
            anchors.fill: parent
            Label {
                id: statusLabel
                text: "Selecting control points"
            }
        }
    }

    GridLayout {
        Layout.margins: 10
        anchors.margins: 10
        anchors.fill: parent
        columnSpacing: 10
        rowSpacing: 10

        // Main panel
        ColumnLayout {
            spacing: 10

            Rectangle {
                width: 512
                height: 512
                color: "#ffffff"

                Item {
                    id: mainView
                    anchors.fill: parent
                    VoronoiSplat {
                        id: splat
                        objectName: "splat"
                        x: parent.x
                        y: parent.y
                        z: 0
                        anchors.fill: parent
                    }

                    LinePlot {
                        id: bundlePlot
                        objectName: "bundlePlot"
                        x: parent.x
                        y: parent.y
                        z: 1
                        anchors.fill: parent
                    }

                    Scatterplot {
                        id: rpPlot
                        objectName: "rpPlot"
                        x: parent.x
                        y: parent.y
                        z: 2
                        anchors.fill: parent
                        glyphSize: 3.0
                    }

                    Scatterplot {
                        id: cpPlot
                        objectName: "cpPlot"
                        x: parent.x
                        y: parent.y
                        z: 3
                        anchors.fill: parent
                    }

                    TransitionControl {
                        id: plotTC
                        objectName: "plotTC"
                        x: parent.x
                        y: parent.y
                        z: 4
                        anchors.fill: parent
                    }
                }

                Rectangle {
                    anchors.fill: parent
                    border.width: 1
                    border.color: "#cccccc"
                    color: "transparent"
                }
            }

            RowLayout {
                Layout.minimumHeight: 60
                Layout.fillHeight: true
                width: mainView.width

                Colormap {
                    Layout.fillHeight: true
                    id: cpColormap
                    objectName: "cpColormap"
                    width: 5
                    orientation: Colormap.Vertical

                    Rectangle { // Adds a border around the colormap
                        x: parent.x - 1
                        y: parent.y - 1
                        width: parent.width + 2
                        height: parent.height + 2
                        border.width: 1
                        border.color: "#000000"
                        color: "transparent"
                    }
                }

                Rectangle {
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                    color: "#ffffff"

                    Item {
                        id: bottomViewCP
                        anchors.fill: parent

                        BarChart {
                            id: cpBarChart
                            objectName: "cpBarChart"
                            anchors.fill: parent

                            Label {
                                anchors.fill: parent
                                anchors.margins: 5
                                horizontalAlignment: Text.AlignRight
                                text: "Control points"
                            }
                        }
                    }

                    Rectangle {
                        anchors.fill: parent
                        border.width: 1
                        border.color: "#cccccc"
                        color: "transparent"
                    }
                }
            }

            RowLayout {
                Layout.minimumHeight: 60
                Layout.fillHeight: true
                width: mainView.width

                Colormap {
                    Layout.fillHeight: true
                    id: rpColormap
                    objectName: "rpColormap"
                    width: 5
                    orientation: Colormap.Vertical

                    Rectangle { // Adds a border around the colormap
                        x: parent.x - 1
                        y: parent.y - 1
                        width: parent.width + 2
                        height: parent.height + 2
                        border.width: 1
                        border.color: "#000000"
                        color: "transparent"
                    }
                }

                Rectangle {
                    Layout.minimumHeight: 60
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                    color: "#ffffff"

                    Item {
                        id: bottomViewRP
                        anchors.fill: parent

                        BarChart {
                            id: rpBarChart
                            objectName: "rpBarChart"
                            anchors.fill: parent

                            Label {
                                anchors.fill: parent
                                anchors.margins: 5
                                horizontalAlignment: Text.AlignRight
                                text: "Regular points"
                            }
                        }
                    }

                    Rectangle {
                        anchors.fill: parent
                        border.width: 1
                        border.color: "#cccccc"
                        color: "transparent"
                    }
                }
            }
        }

        // Options panel
        RowLayout {
            id: optionsPanel

            ColumnLayout {
                Layout.alignment: Qt.AlignTop | Qt.AlignLeft

                GroupBox {
                    Layout.fillWidth: true
                    title: "Control points"
                    checkable: true
                    __checkbox.onClicked: {
                        cpPlot.visible = this.checked;

                        if (this.checked) {
                            cpPlot.z = 0;
                            rpPlot.z = 0;
                        } else {
                            cpPlot.z = 0;
                            rpPlot.z = 1;
                        }
                    }

                    ColumnLayout {
                        GroupBox {
                            flat: true
                            title: "Colors"

                            GridLayout {
                                columns: 2

                                Label { text: "Map to:" }
                                ComboBox {
                                    id: cpPlotMetricComboBox
                                    model: metricsModel
                                }

                                Label { text: "Color map:" }
                                ComboBox {
                                    id: cpPlotColormapCombo
                                    model: colormapModel
                                    onActivated:
                                        Main.setCPColorScale(model.get(index).value);
                                }
                            }
                        }

                        GroupBox {
                            flat: true
                            title: "Glyphs"

                            GridLayout {
                                columns: 2

                                Label { text: "Size:" }
                                SpinBox {
                                    id: cpGlyphSizeSpinBox
                                    maximumValue: 100
                                    minimumValue: 6
                                    decimals: 1
                                    stepSize: 1
                                    value: cpPlot.glyphSize
                                    onValueChanged: cpPlot.glyphSize = this.value
                                }

                                Label { text: "Opacity:" }
                                Slider {
                                    id: cpPlotOpacitySlider
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: cpPlot.opacity
                                    onValueChanged: cpPlot.opacity = this.value
                                }
                            }
                        }
                    }
                }

                GroupBox {
                    Layout.fillWidth: true
                    title: "Regular points"
                    checked: true
                    checkable: true
                    __checkbox.onClicked: {
                        rpPlot.visible = this.checked;
                        splat.visible  = this.checked;
                    }

                    ColumnLayout {
                        GroupBox {
                            flat: true
                            title: "Colors"

                            GridLayout {
                                columns: 2

                                Label { text: "Map to:" }
                                ComboBox {
                                    id: rpPlotMetricComboBox
                                    model: metricsModel
                                }

                                Label { text: "Color map:" }
                                ComboBox {
                                    id: rpPlotColormapCombo
                                    model: colormapModel
                                    onActivated:
                                        Main.setRPColorScale(model.get(index).value);
                                }
                            }
                        }

                        GroupBox {
                            flat: true
                            title: "Splat"

                            GridLayout {
                                columns: 2

                                Label { text: "Blur (α):" }
                                SpinBox {
                                    id: alphaSpinBox
                                    maximumValue: 100
                                    minimumValue: 1
                                    value: splat.alpha
                                    decimals: 2
                                    stepSize: 1
                                    onValueChanged: splat.alpha = this.value
                                }

                                Label { text: "Radius (β):" }
                                SpinBox {
                                    id: betaSpinBox
                                    maximumValue: 100
                                    minimumValue: 1
                                    value: splat.beta
                                    decimals: 2
                                    stepSize: 1
                                    onValueChanged: splat.beta = this.value
                                }

                                Label { text: "Opacity:" }
                                Slider {
                                    id: splatOpacitySlider
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: splat.opacity
                                    onValueChanged: splat.opacity = this.value
                                }
                            }
                        }

                        GroupBox {
                            flat: true
                            title: "Glyphs"

                            GridLayout {
                                columns: 2

                                Label { text: "Size:" }
                                SpinBox {
                                    id: rpGlyphSizeSpinBox
                                    maximumValue: 100
                                    minimumValue: 2
                                    decimals: 1
                                    stepSize: 1
                                    value: rpPlot.glyphSize
                                    onValueChanged: rpPlot.glyphSize = this.value
                                }

                                Label { text: "Opacity:" }
                                Slider {
                                    id: rpPlotOpacitySlider
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: rpPlot.opacity
                                    onValueChanged: rpPlot.opacity = this.value
                                }
                            }
                        }
                    }
                }

                GroupBox {
                    Layout.fillWidth: true
                    id: metricsGroupBox
                    title: "Projection metrics"
                    property RadioButton current: currentMetricRadioButton

                    Column {
                        ExclusiveGroup { id: wrtMetricsGroup }

                        RadioButton {
                            id: currentMetricRadioButton
                            text: "Current"
                            exclusiveGroup: wrtMetricsGroup
                            checked: true
                            onClicked: {
                                if (!Main.setObserverType(Main.ObserverCurrent)) {
                                    metricsGroupBox.current.checked = true;
                                } else {
                                    metricsGroupBox.current = this;
                                }
                            }
                        }
                        RadioButton {
                            id: diffPreviousMetricRadioButton
                            text: "Diff. to previous"
                            exclusiveGroup: wrtMetricsGroup
                            onClicked: {
                                if (!Main.setObserverType(Main.ObserverDiffPrevious)) {
                                    metricsGroupBox.current.checked = true;
                                } else {
                                    metricsGroupBox.current = this;
                                }
                            }
                        }
                        RadioButton {
                            id: diffFirstMetricRadioButton
                            text: "Diff. to first"
                            exclusiveGroup: wrtMetricsGroup
                            onClicked: {
                                if (!Main.setObserverType(Main.ObserverDiffFirst)) {
                                    metricsGroupBox.current.checked = true;
                                } else {
                                    metricsGroupBox.current = this;
                                }
                            }
                        }
                    }
                }
            }

            ColumnLayout {
                Layout.alignment: Qt.AlignTop | Qt.AlignLeft

                GroupBox {
                    Layout.fillWidth: true
                    id: bundlingGroupBox
                    title: "Bundling"
                    checked: true
                    checkable: true
                    __checkbox.onClicked: {
                        bundlePlot.visible = this.checked;
                    }

                    ColumnLayout {
                        GroupBox {
                            flat: true
                            title: "Main bundling"

                            GridLayout {
                                columns: 2

                                Label { text: "Iterations:" }
                                SpinBox {
                                    maximumValue: 100
                                    minimumValue: 0
                                    decimals: 0
                                    stepSize: 1
                                    value: bundlePlot.iterations
                                    onValueChanged: bundlePlot.iterations = this.value
                                }

                                Label { text: "Kernel size:" }
                                SpinBox {
                                    maximumValue: 100
                                    minimumValue: 3
                                    decimals: 1
                                    stepSize: 1
                                    value: bundlePlot.kernelSize
                                    onValueChanged: bundlePlot.kernelSize = this.value
                                }

                                Label { text: "Smoothing factor:" }
                                Slider {
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: bundlePlot.smoothingFactor
                                    onValueChanged: bundlePlot.smoothingFactor = this.value
                                }

                                Label { text: "Smoothing iterations:" }
                                SpinBox {
                                    maximumValue: 100
                                    minimumValue: 0
                                    decimals: 0
                                    stepSize: 1
                                    value: bundlePlot.smoothingIterations
                                    onValueChanged: bundlePlot.smoothingIterations = this.value
                                }
                            }
                        }

                        GroupBox {
                            flat: true
                            title: "Ends bundling"

                            GridLayout {
                                columns: 2

                                CheckBox {
                                    Layout.columnSpan: 2
                                    text: "Block endpoints"
                                    checked: bundlePlot.blockEndpoints
                                    onClicked: bundlePlot.blockEndpoints = this.checked
                                }

                                Label { text: "Iterations:" }
                                SpinBox {
                                    maximumValue: 100
                                    minimumValue: 0
                                    decimals: 0
                                    stepSize: 1
                                    value: bundlePlot.endsIterations
                                    onValueChanged: bundlePlot.endsIterations = this.value
                                }

                                Label { text: "Kernel size:" }
                                SpinBox {
                                    maximumValue: 100
                                    minimumValue: 3
                                    decimals: 1
                                    stepSize: 1
                                    value: bundlePlot.endsKernelSize
                                    onValueChanged: bundlePlot.endsKernelSize = this.value
                                }

                                Label { text: "Smoothing factor:" }
                                Slider {
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: bundlePlot.endsSmoothingFactor
                                    onValueChanged: bundlePlot.endsSmoothingFactor = this.value
                                }
                            }
                        }

                        GroupBox {
                            flat: true
                            title: "General"

                            GridLayout {
                                columns: 2

                                Label { text: "Line width:" }
                                SpinBox {
                                    maximumValue: 100
                                    minimumValue: 1
                                    decimals: 1
                                    stepSize: 1
                                    value: bundlePlot.lineWidth
                                    onValueChanged: bundlePlot.lineWidth = this.value
                                }

                                Label { text: "Opacity:" }
                                Slider {
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: bundlePlot.opacity
                                    onValueChanged: bundlePlot.opacity = this.value
                                }

                                Label { text: "Edge sampling:" }
                                SpinBox {
                                    maximumValue: 100
                                    minimumValue: 3
                                    decimals: 1
                                    stepSize: 1
                                    value: bundlePlot.edgeSampling
                                    onValueChanged: bundlePlot.edgeSampling = this.value
                                }

                                Label { text: "Advection speed:" }
                                Slider {
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: bundlePlot.advectionSpeed
                                    onValueChanged: bundlePlot.advectionSpeed = this.value
                                }

                                Label { text: "Density estimation:" }
				RowLayout {
				    ExclusiveGroup { id: densityEstimationGroup }
				    RadioButton {
					text: "Exact"
					exclusiveGroup: densityEstimationGroup
				    }
				    RadioButton {
					text: "Fast"
					exclusiveGroup: densityEstimationGroup
					checked: true
				    }
				}

                                Label { text: "Bundle shape:" }
				RowLayout {
				    ExclusiveGroup { id: bundleShapeGroup }
				    RadioButton {
					text: "FDEB"
					exclusiveGroup: bundleShapeGroup
					checked: true
				    }
				    RadioButton {
					text: "HEB"
					exclusiveGroup: bundleShapeGroup
				    }
				}

                                Label { text: "Relaxation:" }
                                Slider {
                                    tickmarksEnabled: true
                                    stepSize: 0.1
                                    maximumValue: 1
                                    minimumValue: 0
                                    value: bundlePlot.relaxation
                                    onValueChanged: bundlePlot.relaxation = this.value
                                }

                                CheckBox {
                                    Layout.columnSpan: 2
                                    text: "Use GPU"
                                    checked: bundlePlot.bundleGPU
                                    onClicked: bundlePlot.bundleGPU = this.checked
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    Dialog {
        id: prefixDialog
        title: "Save screenshot"
        standardButtons: StandardButton.Save | StandardButton.Cancel

        TextField {
            id: prefixTextField
            placeholderText: "Enter prefix"
        }

        Timer {
            id: screenshotTimer
            interval: 500
            running: false
            repeat: false
            onTriggered: {
                var prefix = prefixTextField.text;
                if (prefix.length == 0) {
                    prefix = "screenshot";
                }

                mainView.grabToImage(function(result) {
                    result.saveToFile(prefix + "-main.png");
                });

                bottomViewCP.grabToImage(function(result) {
                    result.saveToFile(prefix + "-bottom-cp.png");
                });

                bottomViewRP.grabToImage(function(result) {
                    result.saveToFile(prefix + "-bottom-rp.png");
                });

                prefixTextField.text = "";
            }
        }

        onAccepted: screenshotTimer.start()
    }

    Action {
        id: screenshotAction
        text: "Save screenshot"
        shortcut: "Ctrl+Shift+S"
        onTriggered: {
            prefixDialog.open();
            prefixTextField.forceActiveFocus();
        }
    }

    Action {
        id: savePlotAction
        text: "&Save data"
        shortcut: "Ctrl+S"
        onTriggered: Main.saveData()
    }

    Action {
        id: quitAction
        text: "&Quit"
        shortcut: "Ctrl+Q"
        onTriggered: Qt.quit()
    }

    Action {
        id: undoManipulationAction
        text: "&Undo manipulation"
        shortcut: "Ctrl+Z"
        onTriggered: Main.undoManipulation()
    }

    Action {
        id: resetManipulationAction
        text: "&Reset manipulation"
        shortcut: "Ctrl+R"
        onTriggered: Main.resetManipulation()
    }

    Action {
        id: selectRPsAction
        text: "&Regular points"
        shortcut: "R"
        onTriggered: {
            Main.setSelectRPs();
            statusLabel.text = "Selecting regular points";
        }
    }

    Action {
        id: toggleOptionsAction
        text: "&Options"
        shortcut: "Ctrl+O"
        checkable: true
        checked: true
        onToggled: {
            optionsPanel.visible = this.checked;
        }
    }

    Action {
        id: selectCPsAction
        text: "&Control points"
        shortcut: "C"
        onTriggered: {
            Main.setSelectCPs();
            statusLabel.text = "Selecting control points";
        }
    }

    ListModel {
        id: metricsModel

        ListElement { text: "Aggregate error" }
        ListElement { text: "CP influence" }
        ListElement { text: "Stress" }
    }

    ListModel {
        id: colormapModel

        Component.onCompleted: {
            // Data has to be fed this way; otherwise "value" is not correct
            this.append({
                "value": Main.ColorScaleRainbow,
                "text": "Rainbow"
            });
            this.append({
                "value": Main.ColorScaleContinuous,
                "text": "Continuous"
            });
            this.append({
                "value": Main.ColorScaleCategorical,
                "text": "Categorical"
            });
            this.append({
                "value": Main.ColorScaleDivergent,
                "text": "Divergent"
            });
        }
    }
}