Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ui] 2D viewer: image sequence player #1989

Merged
merged 24 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6c135e1
[ui] FloatImage: keep image visible when loading next one
mugulmd Mar 29, 2023
d926447
[ui] basic sequence player: control selected viewId with a slider
mugulmd Feb 9, 2023
45a95b5
[ui] sequence player: adjust size to fill 2D viewer
mugulmd Mar 24, 2023
47d3089
[ui] sequence player: play-pause sequence using a timer
mugulmd Mar 24, 2023
6efaa8e
[ui] sequence player: previous-next frame buttons
mugulmd Mar 24, 2023
b209799
[ui] sequence player: FPS spinbox
mugulmd Mar 27, 2023
3da3c89
[ui] sequence player: frame label
mugulmd Apr 12, 2023
9295d22
[ui] sequence player: repeat button
mugulmd Apr 12, 2023
f84f114
[ui] sequence player: improve separation of concerns
mugulmd Apr 12, 2023
c22c5e9
[ui] sequence player: documentation
mugulmd Apr 12, 2023
8a3daed
[ui] sequence player: draw cached frames indicator
mugulmd Apr 17, 2023
f1de961
[ui] SequencePlayer: draw cached frame regions instead of single frames
mugulmd Apr 19, 2023
4c58d4b
[ui] new concept of pickedViewId to scroll in 2D viewer without const…
mugulmd Apr 20, 2023
76f4fc8
[ui] float image viewer: specify when to use sequence or single image
mugulmd Apr 24, 2023
03dc812
[ui] sequence player: minor bug fixes
mugulmd Apr 25, 2023
0e8c8ca
[ui] sequence player: checkbox to show or hide sequence player widget
mugulmd Jun 19, 2023
c0526d8
[ui] Viewer2D: factorize helper functions
mugulmd Jun 29, 2023
2641195
[ui] sequence player: support output attribute sequences
mugulmd Jul 3, 2023
7af051d
[ui] sequence player: feed sorted viewIds from Viewer2D to SequencePl…
mugulmd Jul 3, 2023
525e0aa
[Viewer] SequencePlayer: Use correct syntax for QML connections
cbentejac Jul 10, 2023
73533cf
[Viewer] SequencePlayer: Update QtQuick imports for Qt 5.15
cbentejac Jul 10, 2023
4f61fd5
[ui] panorama viewer: disable sequence caching
mugulmd Jul 12, 2023
86fe360
[ui] SequencePlayer: type coercion
mugulmd Jul 12, 2023
7e85915
[ui] Viewer2D: fix JSON parse error
mugulmd Jul 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions meshroom/ui/qml/Viewer/FloatImage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ AliceVision.FloatImageViewer {

width: textureSize.width
height: textureSize.height
visible: (status === Image.Ready)
visible: true

// paintedWidth / paintedHeight / status for compatibility with standard Image
property int paintedWidth: textureSize.width
Expand Down Expand Up @@ -62,10 +62,9 @@ AliceVision.FloatImageViewer {

property int pointsNumber: (surface.subdivisions + 1) * (surface.subdivisions + 1);

property int index: 0;
property int idView: 0;

clearBeforeLoad: true
clearBeforeLoad: false

property alias containsMouse: mouseArea.containsMouse
property alias mouseX: mouseArea.mouseX
Expand Down
4 changes: 2 additions & 2 deletions meshroom/ui/qml/Viewer/PanoramaViewer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -293,15 +293,15 @@ AliceVision.PanoramaViewer {
'surface.pitch': Qt.binding(function() { return root.pitch; }),
'surface.yaw': Qt.binding(function() { return root.yaw; }),
'surface.roll': Qt.binding(function() { return root.roll; }),
'index' : index,
'idView': Qt.binding(function() { return idViewItem; }),
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue; }),
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue; }),
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue; }),
'downscaleLevel' : Qt.binding(function() { return downscale; }),
'source': Qt.binding(function() { return sourceItem; }),
'surface.msfmData': Qt.binding(function() { return root.msfmData }),
'canBeHovered': true
'canBeHovered': true,
'useSequence': false
})
imageLoaded = Qt.binding(function() { return repeater.itemAt(index).item.status === Image.Ready ? true : false; })
}
Expand Down
262 changes: 262 additions & 0 deletions meshroom/ui/qml/Viewer/SequencePlayer.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11

import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0

/**
* The Sequence Player is a UI for manipulating
* the currently selected (and displayed) viewpoint
* in an ordered set of viewpoints (i.e. a sequence).
*
* The viewpoint manipulation process can be manual
* (for example by dragging a slider to change the current frame)
* or automatic
* (by playing the sequence, i.e. incrementing the current frame at a given time rate).
*/
FloatingPane {
id: root

// Exposed properties
property var sortedViewIds: []
property var viewer: null

function updateReconstructionView() {
if (_reconstruction && m.frame >= 0 && m.frame < sortedViewIds.length) {
if (m.syncSelected) {
_reconstruction.selectedViewId = sortedViewIds[m.frame];
} else {
_reconstruction.pickedViewId = sortedViewIds[m.frame];
}
}
}

// Sequence player model:
// - current frame
// - data related to automatic sequence playing
QtObject {
id: m

property int frame: 0
property bool syncSelected: true
property bool playing: false
property bool repeat: false
property real fps: 1

onFrameChanged: {
updateReconstructionView();
}

onSyncSelectedChanged: {
updateReconstructionView();
}

onPlayingChanged: {
syncSelected = !playing;
}
}

// Update the frame property
// when the selected view ID is changed externally
Connections {
target: _reconstruction
function onSelectedViewIdChanged() {
for (let idx = 0; idx < sortedViewIds.length; idx++) {
if (_reconstruction.selectedViewId === sortedViewIds[idx] && (m.frame != idx)) {
m.frame = idx;
}
}
}
}

// In play mode
// we use a timer to increment the frame property
// at a given time rate (defined by the fps property)
Timer {
id: timer

repeat: true
running: m.playing
interval: 1000 / m.fps

onTriggered: {
let nextIndex = m.frame + 1;
if (nextIndex == sortedViewIds.length) {
if (m.repeat) {
m.frame = 0;
return;
}
else {
m.playing = false;
return;
}
}
m.frame = nextIndex;
}
}

// Widgets:
// - "Previous Frame" button
// - "Play - Pause" button
// - "Next Frame" button
// - frame label
// - frame slider
// - FPS spin box
// - "Repeat" button
RowLayout {

anchors.fill: parent

MaterialToolButton {
id: prevButton

text: MaterialIcons.skip_previous
ToolTip.text: "Previous Frame"

onClicked: {
m.frame -= 1;
}
}

MaterialToolButton {
id: playButton

checkable: true
checked: false
text: checked ? MaterialIcons.pause : MaterialIcons.play_arrow
ToolTip.text: checked ? "Pause Player" : "Play Sequence"

onCheckedChanged: {
m.playing = checked;
}

Connections {
target: m
function onPlayingChanged() {
playButton.checked = m.playing;
}
}
}

MaterialToolButton {
id: nextButton

text: MaterialIcons.skip_next
ToolTip.text: "Next Frame"

onClicked: {
m.frame += 1;
}
}

Label {
id: frameLabel

text: m.frame
Layout.preferredWidth: frameMetrics.width
}

Slider {
id: frameSlider

Layout.fillWidth: true

stepSize: 1
snapMode: Slider.SnapAlways
live: true

from: 0
to: sortedViewIds.length - 1

onValueChanged: {
m.frame = value;
}

onPressedChanged: {
m.syncSelected = !pressed;
}

Connections {
target: m
function onFrameChanged() {
frameSlider.value = m.frame;
}
}

background: Rectangle {
x: frameSlider.leftPadding
y: frameSlider.topPadding + frameSlider.height / 2 - height / 2
width: frameSlider.availableWidth
height: 4
radius: 2
color: Colors.grey

Repeater {
id: cacheView

model: viewer ? viewer.cachedFrames : []
property real frameLength: sortedViewIds.length > 0 ? frameSlider.width / sortedViewIds.length : 0

Rectangle {
x: modelData.x * cacheView.frameLength
y: 0
width: cacheView.frameLength * (modelData.y - modelData.x + 1)
height: 4
radius: 2
color: Colors.blue
}
}
}
}

RowLayout {
Label {
text: "FPS:"
ToolTip.text: "Frame Per Second"
}

SpinBox {
id: fpsSpinBox

Layout.preferredWidth: fpsMetrics.width + up.implicitIndicatorWidth

from: 1
to: 60
stepSize: 1

onValueChanged: {
m.fps = value;
}
}
}

MaterialToolButton {
id: repeatButton

checkable: true
checked: false
text: MaterialIcons.replay
ToolTip.text: "Repeat"

onCheckedChanged: {
m.repeat = checked;
}
}
}

TextMetrics {
id: frameMetrics

font: frameLabel.font
text: "10000"
}

TextMetrics {
id: fpsMetrics

font: fpsSpinBox.font
text: "100"
}
}
Loading