Meshroom/meshroom/ui/qml/Viewer/Viewer2D.qml

293 lines
9.1 KiB
QML

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import MaterialIcons 2.2
import Controls 1.0
FocusScope {
id: root
clip: true
property url source
property var metadata
property var viewIn3D
function clear()
{
source = ''
metadata = {}
}
// slots
Keys.onPressed: {
if(event.key == Qt.Key_F) {
root.fit();
event.accepted = true;
}
}
// functions
function fit() {
if(image.status != Image.Ready)
return;
image.scale = Math.min(root.width/image.width, root.height/image.height)
image.x = Math.max((root.width-image.width*image.scale)*0.5, 0)
image.y = Math.max((root.height-image.height*image.scale)*0.5, 0)
}
function getImageFile(type) {
if (type == "image") {
return root.source;
} else if (_reconstruction.depthMap != undefined && _reconstruction.selectedViewId >= 0) {
return Filepath.stringToUrl(_reconstruction.depthMap.internalFolder+_reconstruction.selectedViewId+"_"+type+"Map.exr");
}
return "";
}
// context menu
property Component contextMenu: Menu {
MenuItem {
text: "Fit"
onTriggered: fit()
}
MenuItem {
text: "Zoom 100%"
onTriggered: image.scale = 1
}
}
// Main Image
Image {
id: image
transformOrigin: Item.TopLeft
asynchronous: true
smooth: false
fillMode: Image.PreserveAspectFit
autoTransform: true
onWidthChanged: if(status==Image.Ready) fit()
source: getImageFile(imageType.type)
onStatusChanged: {
// update cache source when image is loaded
if(status === Image.Ready)
cache.source = source
}
// Image cache of the last loaded image
// Only visible when the main one is loading, to keep an image
// displayed at all time and smoothen transitions
Image {
id: cache
anchors.fill: parent
asynchronous: true
smooth: parent.smooth
fillMode: parent.fillMode
autoTransform: parent.autoTransform
visible: image.status === Image.Loading
}
// FeatureViewer: display view extracted feature points
// note: requires QtAliceVision plugin - use a Loader to evaluate plugin avaibility at runtime
Loader {
id: featuresViewerLoader
active: displayFeatures.checked
// handle rotation/position based on available metadata
rotation: {
var orientation = metadata ? metadata["Orientation"] : 0
switch(orientation) {
case "6": return 90;
case "8": return -90;
default: return 0;
}
}
x: rotation === 90 ? image.paintedWidth : 0
y: rotation === -90 ? image.paintedHeight : 0
Component.onCompleted: {
// instantiate and initialize a FeaturesViewer component dynamically using Loader.setSource
setSource("FeaturesViewer.qml", {
'active': Qt.binding(function() { return displayFeatures.checked; }),
'viewId': Qt.binding(function() { return _reconstruction.selectedViewId; }),
'model': Qt.binding(function() { return _reconstruction.featureExtraction.attribute("describerTypes").value; }),
'folder': Qt.binding(function() { return Filepath.stringToUrl(_reconstruction.featureExtraction.attribute("output").value); }),
})
}
}
}
// Busy indicator
BusyIndicator {
anchors.centerIn: parent
// running property binding seems broken, only dynamic binding assignment works
Component.onCompleted: running = Qt.binding(function() { return image.status === Image.Loading })
}
// mouse area
MouseArea {
anchors.fill: parent
property double factor: 1.2
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onPressed: {
image.forceActiveFocus()
if(mouse.button & Qt.MiddleButton || (mouse.button & Qt.LeftButton && mouse.modifiers & Qt.ShiftModifier))
drag.target = image // start drag
}
onReleased: {
drag.target = undefined // stop drag
if(mouse.button & Qt.RightButton) {
var menu = contextMenu.createObject(root);
menu.x = mouse.x;
menu.y = mouse.y;
menu.open()
}
}
onWheel: {
var zoomFactor = wheel.angleDelta.y > 0 ? factor : 1/factor
if(Math.min(image.width*image.scale*zoomFactor, image.height*image.scale*zoomFactor) < 10)
return
var point = mapToItem(image, wheel.x, wheel.y)
image.x += (1-zoomFactor) * point.x * image.scale
image.y += (1-zoomFactor) * point.y * image.scale
image.scale *= zoomFactor
}
}
FloatingPane {
id: topToolbar
width: parent.width
height: depthMapNodeName.height+8
radius: 0
padding: 4
// selectable filepath to source image
TextField {
width: parent.width-depthMapNodeName.width-5
padding: 0
anchors.right: depthMapNodeName.left
anchors.rightMargin: 5
background: Item {}
font.pointSize: 8
readOnly: true
selectByMouse: true
text: Filepath.urlToString(image.source)
}
// show which depthmap node is active
Label {
id: depthMapNodeName
text: _reconstruction.depthMap.label
anchors.right: parent.right
font.pointSize: 8
visible: imageType.type != "image"
}
}
// Image Metadata overlay Pane
ImageMetadataView {
width: 350
anchors {
top: topToolbar.bottom
right: parent.right
bottom: bottomToolbar.top
}
visible: metadataCB.checked
// only load metadata model if visible
metadata: visible ? root.metadata : {}
}
Loader {
id: featuresOverlay
anchors.bottom: bottomToolbar.top
anchors.left: parent.left
anchors.margins: 2
active: displayFeatures.checked
sourceComponent: FeaturesInfoOverlay {
featureExtractionNode: _reconstruction.featureExtraction
pluginStatus: featuresViewerLoader.status
featuresViewer: featuresViewerLoader.item
}
}
FloatingPane {
id: bottomToolbar
anchors.bottom: parent.bottom
anchors.margins: 0
width: parent.width
topPadding: 2
bottomPadding: topPadding
RowLayout {
anchors.fill: parent
// zoom label
Label {
text: (image.status == Image.Ready ? image.scale.toFixed(2) : "1.00") + "x"
state: "xsmall"
}
MaterialToolButton {
id: displayFeatures
font.pointSize: 11
ToolTip.text: "Display Features"
checkable: true
text: MaterialIcons.scatter_plot
}
Item {
Layout.fillWidth: true
Label {
id: resolutionLabel
text: image.sourceSize.width + "x" + image.sourceSize.height
anchors.centerIn: parent
elide: Text.ElideMiddle
}
}
ComboBox {
id: imageType
// set min size to 5 characters + one margin for the combobox
Layout.minimumWidth: 6.0 * Qt.application.font.pixelSize
Layout.preferredWidth: Layout.minimumWidth
// Layout.preferredWidth: 6.0 * Qt.application.font.pixelSize
// Layout.minimumWidth: 6.0 * Qt.application.font.pixelSize
property var types: ["image", "depth", "sim"]
property string type: types[currentIndex]
model: types
}
MaterialToolButton {
font.pointSize: 11
enabled: _reconstruction.depthMap != undefined
ToolTip.text: "View Depth Map in 3D (" + (_reconstruction.depthMap != undefined ? _reconstruction.depthMap.label : "No DepthMap Node Selected") + ")"
text: MaterialIcons.input
onClicked: {
root.viewIn3D(root.getImageFile("depth"))
}
}
ToolButton {
id: metadataCB
padding: 3
font.family: MaterialIcons.fontFamily
text: MaterialIcons.info_outline
ToolTip.text: "Image Metadata"
ToolTip.visible: hovered
font.pointSize: 12
smooth: false
flat: true
checkable: enabled
}
}
}
}