mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-02 19:56:46 +02:00
[ui] Controls: Add DirectionalLightPane
component
This commit is contained in:
parent
69ea5b88d2
commit
9b48531cb7
2 changed files with 263 additions and 1 deletions
261
meshroom/ui/qml/Controls/DirectionalLightPane.qml
Normal file
261
meshroom/ui/qml/Controls/DirectionalLightPane.qml
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects // for OpacityMask & Glow
|
||||||
|
import MaterialIcons 2.2
|
||||||
|
import Utils 1.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directional Light Pane
|
||||||
|
*
|
||||||
|
* @biref A small pane to control a directional light with a 2d ball controller.
|
||||||
|
*
|
||||||
|
* @param lightYawValue - directional light yaw (degrees)
|
||||||
|
* @param lightPitchValue - directional light pitch (degrees)
|
||||||
|
*/
|
||||||
|
|
||||||
|
FloatingPane {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// yaw and pitch properties
|
||||||
|
property double lightYawValue: 0
|
||||||
|
property double lightPitchValue: 0
|
||||||
|
|
||||||
|
// 2d controller display size properties
|
||||||
|
readonly property real controllerSize: 80
|
||||||
|
readonly property real controllerRadius: controllerSize * 0.5
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
lightYawValue = 0;
|
||||||
|
lightPitchValue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update 2d controller if yaw value changed
|
||||||
|
onLightYawValueChanged: { lightBallController.update() }
|
||||||
|
|
||||||
|
// update 2d controller if pitch value changed
|
||||||
|
onLightPitchValueChanged: { lightBallController.update() }
|
||||||
|
|
||||||
|
anchors.margins: 0
|
||||||
|
padding: 5
|
||||||
|
radius: 0
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
// header
|
||||||
|
RowLayout {
|
||||||
|
// pane title
|
||||||
|
Label {
|
||||||
|
text: "Directional Light"
|
||||||
|
font.bold: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// minimize or maximize button
|
||||||
|
MaterialToolButton {
|
||||||
|
id: bodyButton
|
||||||
|
text: lightPaneBody.visible ? MaterialIcons.arrow_drop_down : MaterialIcons.arrow_drop_up
|
||||||
|
font.pointSize: 10
|
||||||
|
ToolTip.text: lightPaneBody.visible ? "Minimize" : "Maximize"
|
||||||
|
onClicked: { lightPaneBody.visible = !lightPaneBody.visible }
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset button
|
||||||
|
MaterialToolButton {
|
||||||
|
id: resetButton
|
||||||
|
text: MaterialIcons.refresh
|
||||||
|
font.pointSize: 10
|
||||||
|
ToolTip.text: "Reset"
|
||||||
|
onClicked: reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// body
|
||||||
|
RowLayout {
|
||||||
|
id: lightPaneBody
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
// light parameters
|
||||||
|
GridLayout {
|
||||||
|
columns: 3
|
||||||
|
rowSpacing: 2
|
||||||
|
columnSpacing: 8
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
|
// light yaw
|
||||||
|
Label {
|
||||||
|
text: "Yaw"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: lightYawTF
|
||||||
|
text: lightYawValue.toFixed(2)
|
||||||
|
selectByMouse: true
|
||||||
|
horizontalAlignment: TextInput.AlignRight
|
||||||
|
validator: doubleDegreeValidator
|
||||||
|
onEditingFinished: { lightYawValue = lightYawTF.text }
|
||||||
|
ToolTip.text: "Light yaw (degrees)."
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
Layout.preferredWidth: textMetricsDegreeValue.width
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "°"
|
||||||
|
}
|
||||||
|
|
||||||
|
// light pitch
|
||||||
|
Label {
|
||||||
|
text: "Pitch"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: lightPitchTF
|
||||||
|
text: lightPitchValue.toFixed(2)
|
||||||
|
selectByMouse: true
|
||||||
|
horizontalAlignment: TextInput.AlignRight
|
||||||
|
validator: doubleDegreeValidator
|
||||||
|
onEditingFinished: { lightPitchValue = lightPitchTF.text }
|
||||||
|
ToolTip.text: "Light pitch (degrees)."
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
Layout.preferredWidth: textMetricsDegreeValue.width
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// directional light ball controller
|
||||||
|
Rectangle {
|
||||||
|
id: lightBallController
|
||||||
|
anchors.margins: 0
|
||||||
|
width: controllerSize
|
||||||
|
height: controllerSize
|
||||||
|
radius: 180 // circle
|
||||||
|
color: "#FF000000"
|
||||||
|
Layout.rightMargin: 5
|
||||||
|
Layout.leftMargin: 5
|
||||||
|
Layout.bottomMargin: 5
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
// get point from light yaw and pitch
|
||||||
|
var y = (lightPitchValue / 90 * controllerRadius)
|
||||||
|
var xMax = Math.sqrt(controllerRadius * controllerRadius - y * y) // get sphere maximum x coordinate
|
||||||
|
var x = (lightYawValue / 90 * xMax)
|
||||||
|
|
||||||
|
// get angle and distance
|
||||||
|
var angleRad = Math.atan2(y, x)
|
||||||
|
var distance = Math.sqrt(x * x + y * y)
|
||||||
|
|
||||||
|
// avoid controller overflow
|
||||||
|
if(distance > controllerRadius)
|
||||||
|
{
|
||||||
|
x = controllerRadius * Math.cos(angleRad)
|
||||||
|
y = controllerRadius * Math.sin(angleRad)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute distance function for light gradient emulation
|
||||||
|
var distanceRatio = Math.min(distance, controllerRadius) / controllerRadius
|
||||||
|
var distanceFunct = distanceRatio * distanceRatio * 0.3
|
||||||
|
|
||||||
|
// update light point
|
||||||
|
lightPoint.x = lightPoint.startOffset + x
|
||||||
|
lightPoint.y = lightPoint.startOffset + y
|
||||||
|
|
||||||
|
// update light gradient
|
||||||
|
lightGradient.angle = angleRad * (180 / Math.PI) // radians to degrees
|
||||||
|
lightGradient.horizontalRadius = controllerSize * (1 - distanceFunct)
|
||||||
|
lightGradient.verticalRadius = controllerSize * (1 + distanceFunct)
|
||||||
|
lightGradient.horizontalOffset = x * (1 - distanceFunct)
|
||||||
|
lightGradient.verticalOffset = y * (1 - distanceFunct)
|
||||||
|
}
|
||||||
|
|
||||||
|
RadialGradient {
|
||||||
|
id: lightGradient
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: controllerSize
|
||||||
|
height: width
|
||||||
|
horizontalRadius: controllerSize
|
||||||
|
verticalRadius: controllerSize
|
||||||
|
angle: 0
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0.00; color: "#FFFFFFFF" }
|
||||||
|
GradientStop { position: 0.10; color: "#FFAAAAAA" }
|
||||||
|
GradientStop { position: 0.50; color: "#FF0C0C0C" }
|
||||||
|
}
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
id: mask
|
||||||
|
maskSource: Rectangle {
|
||||||
|
height: lightGradient.height
|
||||||
|
width: lightGradient.width
|
||||||
|
radius: 180 // circle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: lightPoint
|
||||||
|
|
||||||
|
property double startOffset : (parent.width - width) * 0.5
|
||||||
|
|
||||||
|
x: startOffset
|
||||||
|
y: startOffset
|
||||||
|
width: controllerRadius / 6
|
||||||
|
height: width
|
||||||
|
radius: 180 // circle
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
Glow {
|
||||||
|
anchors.fill: lightPoint
|
||||||
|
radius: controllerRadius / 5
|
||||||
|
samples: 17
|
||||||
|
color: "white"
|
||||||
|
source: lightPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: lightMouseArea
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onPositionChanged: {
|
||||||
|
// get coordinates from center
|
||||||
|
var x = mouseX - controllerRadius
|
||||||
|
var y = mouseY - controllerRadius
|
||||||
|
|
||||||
|
// get distance to center
|
||||||
|
var distance = Math.sqrt(x * x + y * y)
|
||||||
|
|
||||||
|
// avoid controller overflow
|
||||||
|
if(distance > controllerRadius)
|
||||||
|
{
|
||||||
|
var angleRad = Math.atan2(y, x)
|
||||||
|
x = controllerRadius * Math.cos(angleRad)
|
||||||
|
y = controllerRadius * Math.sin(angleRad)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get sphere maximum x coordinate
|
||||||
|
var xMax = Math.sqrt(controllerRadius * controllerRadius - y * y)
|
||||||
|
|
||||||
|
// update light yaw and pitch
|
||||||
|
lightYawValue = (xMax > 0) ? ((x / xMax) * 90) : 0 // between -90 and 90 degrees
|
||||||
|
lightPitchValue = (y / controllerRadius) * 90 // between -90 and 90 degrees
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleValidator {
|
||||||
|
id: doubleDegreeValidator
|
||||||
|
locale: 'C' // use '.' decimal separator disregarding of the system locale
|
||||||
|
bottom: -90.0
|
||||||
|
top: 90.0
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: textMetricsDegreeValue
|
||||||
|
font: lightYawTF.font
|
||||||
|
text: "12.3456"
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,4 +14,5 @@ ExifOrientedViewer 1.0 ExifOrientedViewer.qml
|
||||||
FilterComboBox 1.0 FilterComboBox.qml
|
FilterComboBox 1.0 FilterComboBox.qml
|
||||||
IntSelector 1.0 IntSelector.qml
|
IntSelector 1.0 IntSelector.qml
|
||||||
MScrollBar 1.0 MScrollBar.qml
|
MScrollBar 1.0 MScrollBar.qml
|
||||||
MSplitView 1.0 MSplitView.qml
|
MSplitView 1.0 MSplitView.qml
|
||||||
|
DirectionalLightPane 1.0 DirectionalLightPane.qml
|
||||||
|
|
Loading…
Add table
Reference in a new issue