2021-08-14 20:17:17 +00:00
|
|
|
/**
|
2022-03-05 16:49:35 +00:00
|
|
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
2024-01-10 23:11:09 +00:00
|
|
|
* Copyright (C) 2021-2024 Ad5001
|
2021-08-14 20:17:17 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2023-05-21 22:15:09 +00:00
|
|
|
import QtQuick
|
|
|
|
import QtQuick.Controls
|
2023-05-22 01:41:20 +00:00
|
|
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
2024-03-28 02:09:37 +00:00
|
|
|
import "js/mathlib.mjs" as MathLib
|
|
|
|
import "js/historylib.mjs" as HistoryLib
|
2021-08-14 20:17:17 +00:00
|
|
|
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmltype PickLocationOverlay
|
|
|
|
\inqmlmodule eu.ad5001.LogarithmPlotter
|
|
|
|
\brief Overlay used to pick a new location for an object.
|
|
|
|
|
|
|
|
Provides an overlay over the canvas that can be shown when the user clicks the "Set position" button
|
|
|
|
on a specific object. It allows the user to pick a new location on the canvas to place the object at.
|
|
|
|
This overlay allows to set the precision of the pick as well as whether the pick should be on the plot grid.
|
|
|
|
|
|
|
|
\sa LogarithmPlotter, LogGraphCanvas
|
|
|
|
*/
|
2021-08-14 20:17:17 +00:00
|
|
|
Item {
|
|
|
|
id: pickerRoot
|
|
|
|
visible: false
|
2023-05-22 01:41:20 +00:00
|
|
|
clip: true
|
2021-08-14 20:17:17 +00:00
|
|
|
|
2023-10-08 20:36:23 +00:00
|
|
|
/*!
|
|
|
|
\qmlsignal PickLocationOverlay::picked(var obj)
|
|
|
|
|
|
|
|
Emitted when a location has been picked
|
|
|
|
The corresponding handler is \c onPicked.
|
|
|
|
*/
|
|
|
|
signal picked(var obj)
|
|
|
|
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty var PickLocationOverlay::canvas
|
|
|
|
logGraphCanvas instance.
|
|
|
|
*/
|
2021-08-14 20:17:17 +00:00
|
|
|
property var canvas
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty string PickLocationOverlay::objType
|
|
|
|
Type of object whose position the user is picking.
|
|
|
|
*/
|
2021-08-14 20:17:17 +00:00
|
|
|
property string objType: 'Point'
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty string PickLocationOverlay::objType
|
|
|
|
Name of the object whose position the user is picking.
|
|
|
|
*/
|
2021-08-14 20:17:17 +00:00
|
|
|
property string objName: 'A'
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty bool PickLocationOverlay::pickX
|
2023-05-24 06:00:58 +00:00
|
|
|
true if the property in propertyX is pickable.
|
2022-01-29 17:17:19 +00:00
|
|
|
*/
|
2021-08-14 20:17:17 +00:00
|
|
|
property bool pickX: true
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty bool PickLocationOverlay::pickY
|
2023-05-24 06:00:58 +00:00
|
|
|
true if the property in propertyY is pickable.
|
2022-01-29 17:17:19 +00:00
|
|
|
*/
|
|
|
|
property bool pickY: true
|
|
|
|
/*!
|
|
|
|
\qmlproperty string PickLocationOverlay::propertyX
|
|
|
|
Name of the object's property whose x value is being changed.
|
|
|
|
*/
|
2021-08-14 20:17:17 +00:00
|
|
|
property string propertyX: 'x'
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty string PickLocationOverlay::propertyY
|
|
|
|
Name of the object's property whose y value is being changed.
|
|
|
|
*/
|
2021-08-14 20:17:17 +00:00
|
|
|
property string propertyY: 'y'
|
2022-01-29 17:17:19 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty int PickLocationOverlay::precision
|
|
|
|
Precision of the picked value (post-dot precision).
|
|
|
|
*/
|
2021-08-14 23:18:13 +00:00
|
|
|
property alias precision: precisionSlider.value
|
2023-05-24 06:00:58 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty bool PickLocationOverlay::userPickX
|
|
|
|
true if the user can and wants to be picking a position on the x axis.
|
|
|
|
*/
|
|
|
|
readonly property bool userPickX: pickX && pickXCheckbox.checked
|
|
|
|
/*!
|
|
|
|
\qmlproperty bool PickLocationOverlay::userPickY
|
|
|
|
true if the user can and wants to be picking a position on the y axis.
|
|
|
|
*/
|
|
|
|
readonly property bool userPickY: pickY && pickYCheckbox.checked
|
2021-08-14 20:17:17 +00:00
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
color: sysPalette.window
|
|
|
|
opacity: 0.35
|
|
|
|
anchors.fill: parent
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: picker
|
|
|
|
anchors.fill: parent
|
|
|
|
hoverEnabled: parent.visible
|
|
|
|
cursorShape: Qt.CrossCursor
|
2021-08-15 15:18:50 +00:00
|
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
2023-05-21 22:15:09 +00:00
|
|
|
onClicked: function(mouse) {
|
2021-08-15 15:18:50 +00:00
|
|
|
if(mouse.button == Qt.LeftButton) { // Validate
|
2023-05-24 06:00:58 +00:00
|
|
|
let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX)
|
|
|
|
let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY)
|
2024-03-29 00:55:13 +00:00
|
|
|
let obj = Modules.Objects.currentObjectsByName[objName]
|
2022-10-19 15:11:54 +00:00
|
|
|
// Set values
|
2023-05-24 06:00:58 +00:00
|
|
|
if(parent.userPickX && parent.userPickY) {
|
2022-10-19 15:11:54 +00:00
|
|
|
history.addToHistory(new HistoryLib.EditedPosition(
|
|
|
|
objName, objType, obj[propertyX], newValueX, obj[propertyY], newValueY
|
|
|
|
))
|
|
|
|
obj[propertyX] = newValueX
|
|
|
|
obj[propertyY] = newValueY
|
2023-05-24 06:00:58 +00:00
|
|
|
obj.update()
|
|
|
|
objectLists.update()
|
2023-10-08 20:36:23 +00:00
|
|
|
pickerRoot.picked(obj)
|
2023-05-24 06:00:58 +00:00
|
|
|
} else if(parent.userPickX) {
|
2021-08-15 15:18:50 +00:00
|
|
|
history.addToHistory(new HistoryLib.EditedProperty(
|
2022-10-19 15:11:54 +00:00
|
|
|
objName, objType, propertyX, obj[propertyX], newValueX
|
2021-08-15 15:18:50 +00:00
|
|
|
))
|
2022-10-19 15:11:54 +00:00
|
|
|
obj[propertyX] = newValueX
|
2023-05-24 06:00:58 +00:00
|
|
|
obj.update()
|
|
|
|
objectLists.update()
|
2023-10-08 20:36:23 +00:00
|
|
|
pickerRoot.picked(obj)
|
2023-05-24 06:00:58 +00:00
|
|
|
} else if(parent.userPickY) {
|
2021-08-15 15:18:50 +00:00
|
|
|
history.addToHistory(new HistoryLib.EditedProperty(
|
2022-10-19 15:11:54 +00:00
|
|
|
objName, objType, propertyY, obj[propertyY], newValueY
|
2021-08-15 15:18:50 +00:00
|
|
|
))
|
2022-10-19 15:11:54 +00:00
|
|
|
obj[propertyY] = newValueY
|
2023-05-24 06:00:58 +00:00
|
|
|
obj.update()
|
|
|
|
objectLists.update()
|
2023-10-08 20:36:23 +00:00
|
|
|
pickerRoot.picked(obj)
|
2021-08-15 15:18:50 +00:00
|
|
|
}
|
2021-08-14 20:17:17 +00:00
|
|
|
}
|
|
|
|
pickerRoot.visible = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-22 01:41:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: pickerSettings
|
|
|
|
radius: 15
|
|
|
|
color: sysPalette.window
|
|
|
|
width: pickerSettingsColumn.width + 30;
|
|
|
|
height: pickerSettingsColumn.height + 20
|
|
|
|
property bool folded: false;
|
|
|
|
x: -15 - ((width-55) * folded);
|
|
|
|
y: 10
|
|
|
|
z: 2
|
2021-08-14 23:18:13 +00:00
|
|
|
|
2023-05-22 01:41:20 +00:00
|
|
|
Row {
|
|
|
|
id: pickerSettingsColumn
|
|
|
|
anchors {
|
|
|
|
left: parent.left
|
|
|
|
top: parent.top
|
|
|
|
leftMargin: 20
|
|
|
|
topMargin: 10
|
|
|
|
}
|
|
|
|
spacing: 15
|
|
|
|
property int cellHeight: 15
|
|
|
|
|
|
|
|
Column {
|
|
|
|
spacing: 5
|
2023-05-25 13:08:12 +00:00
|
|
|
// width: 100
|
2023-05-22 01:41:20 +00:00
|
|
|
|
|
|
|
Text {
|
|
|
|
text: qsTr("Pointer precision:")
|
|
|
|
color: sysPalette.windowText
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
height: pickerSettingsColumn.cellHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
Text {
|
2023-05-24 06:00:58 +00:00
|
|
|
text: qsTr("Snap to grid:")
|
2023-05-22 01:41:20 +00:00
|
|
|
color: sysPalette.windowText
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
height: pickerSettingsColumn.cellHeight
|
|
|
|
}
|
2023-05-24 06:00:58 +00:00
|
|
|
|
|
|
|
CheckBox {
|
|
|
|
id: pickXCheckbox
|
|
|
|
height: pickerSettingsColumn.cellHeight
|
|
|
|
text: qsTr("Pick X")
|
|
|
|
checked: pickX
|
|
|
|
visible: pickX
|
|
|
|
}
|
2023-05-22 01:41:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Column {
|
|
|
|
spacing: 5
|
|
|
|
|
|
|
|
Slider {
|
|
|
|
id: precisionSlider
|
|
|
|
from: 0
|
|
|
|
value: 2
|
|
|
|
to: 10
|
|
|
|
stepSize: 1
|
|
|
|
height: pickerSettingsColumn.cellHeight
|
|
|
|
|
|
|
|
ToolTip {
|
|
|
|
parent: precisionSlider.handle
|
|
|
|
visible: precisionSlider.pressed
|
|
|
|
text: precisionSlider.value.toFixed(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CheckBox {
|
|
|
|
id: snapToGridCheckbox
|
|
|
|
height: pickerSettingsColumn.cellHeight
|
|
|
|
// text: qsTr("Snap to grid")
|
|
|
|
checked: false
|
|
|
|
}
|
2023-05-24 06:00:58 +00:00
|
|
|
|
|
|
|
CheckBox {
|
|
|
|
id: pickYCheckbox
|
|
|
|
height: pickerSettingsColumn.cellHeight
|
|
|
|
text: qsTr("Pick Y")
|
|
|
|
checked: pickY
|
|
|
|
visible: pickY
|
|
|
|
}
|
2023-05-22 01:41:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
width: 24
|
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
flat: true
|
|
|
|
|
|
|
|
onClicked: pickerSettings.folded = !pickerSettings.folded
|
|
|
|
|
|
|
|
ToolTip.visible: hovered
|
|
|
|
ToolTip.delay: 200
|
|
|
|
ToolTip.text: pickerSettings.folded ? qsTr("Open picker settings") : qsTr("Hide picker settings")
|
|
|
|
|
|
|
|
Setting.Icon {
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
width: 18
|
|
|
|
height: 18
|
|
|
|
color: sysPalette.windowText
|
|
|
|
source: `../icons/common/settings.svg`
|
|
|
|
}
|
2021-08-14 23:18:13 +00:00
|
|
|
}
|
2021-08-21 20:30:50 +00:00
|
|
|
}
|
2021-08-14 23:18:13 +00:00
|
|
|
}
|
|
|
|
|
2021-08-14 20:17:17 +00:00
|
|
|
Rectangle {
|
|
|
|
id: xCursor
|
|
|
|
width: 1
|
|
|
|
height: parent.height
|
2021-08-14 23:18:13 +00:00
|
|
|
color: 'black'
|
2021-08-14 20:17:17 +00:00
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.left: parent.left
|
2024-03-29 00:55:13 +00:00
|
|
|
anchors.leftMargin: Modules.Canvas.x2px(picked.mouseX)
|
2023-05-24 06:00:58 +00:00
|
|
|
visible: parent.userPickX
|
2021-08-14 20:17:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: yCursor
|
|
|
|
width: parent.width
|
|
|
|
height: 1
|
2021-08-14 23:18:13 +00:00
|
|
|
color: 'black'
|
2021-08-14 20:17:17 +00:00
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.left: parent.left
|
2024-03-29 00:55:13 +00:00
|
|
|
anchors.topMargin: Modules.Canvas.y2px(picked.mouseY)
|
2023-05-24 06:00:58 +00:00
|
|
|
visible: parent.userPickY
|
2021-08-14 20:17:17 +00:00
|
|
|
}
|
|
|
|
|
2021-08-14 23:18:13 +00:00
|
|
|
Text {
|
2021-08-21 20:30:50 +00:00
|
|
|
id: picked
|
2021-08-14 20:17:17 +00:00
|
|
|
x: picker.mouseX - width - 5
|
|
|
|
y: picker.mouseY - height - 5
|
2023-05-24 06:00:58 +00:00
|
|
|
color: 'black'
|
2024-03-29 00:55:13 +00:00
|
|
|
property double axisX: Modules.Canvas.axesStep.x.value
|
|
|
|
property double axisY: Modules.Canvas.axesStep.y.value
|
2021-08-21 20:30:50 +00:00
|
|
|
property double mouseX: {
|
2024-03-29 00:55:13 +00:00
|
|
|
let xpos = Modules.Canvas.px2x(picker.mouseX)
|
2021-08-21 20:30:50 +00:00
|
|
|
if(snapToGridCheckbox.checked) {
|
|
|
|
if(canvas.logscalex) {
|
|
|
|
// Calculate the logged power
|
|
|
|
let pow = Math.pow(10, Math.floor(Math.log10(xpos)))
|
|
|
|
return pow*Math.round(xpos/pow)
|
|
|
|
} else {
|
2024-03-29 00:55:13 +00:00
|
|
|
return axisX*Math.round(xpos/axisX)
|
2021-08-21 20:30:50 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return xpos.toFixed(parent.precision)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
property double mouseY: {
|
2024-03-29 00:55:13 +00:00
|
|
|
let ypos = Modules.Canvas.px2y(picker.mouseY)
|
2021-08-21 20:30:50 +00:00
|
|
|
if(snapToGridCheckbox.checked) {
|
2024-03-29 00:55:13 +00:00
|
|
|
return axisY*Math.round(ypos/axisY)
|
2021-08-21 20:30:50 +00:00
|
|
|
} else {
|
|
|
|
return ypos.toFixed(parent.precision)
|
|
|
|
}
|
|
|
|
}
|
2021-08-14 20:17:17 +00:00
|
|
|
text: {
|
2023-05-24 06:00:58 +00:00
|
|
|
if(parent.userPickX && parent.userPickY)
|
2021-08-14 20:17:17 +00:00
|
|
|
return `(${mouseX}, ${mouseY})`
|
2023-05-24 06:00:58 +00:00
|
|
|
else if(parent.userPickX)
|
2021-08-14 23:18:13 +00:00
|
|
|
return `X = ${mouseX}`
|
2023-05-24 06:00:58 +00:00
|
|
|
else if(parent.userPickY)
|
2021-08-14 23:18:13 +00:00
|
|
|
return `Y = ${mouseY}`
|
2023-05-24 06:00:58 +00:00
|
|
|
else
|
|
|
|
return qsTr('(no pick selected)')
|
2021-08-14 20:17:17 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-21 20:30:50 +00:00
|
|
|
|
|
|
|
|
2022-10-19 15:11:54 +00:00
|
|
|
/*!
|
|
|
|
\qmlmethod void History::parseValue(string value, string objType, string propertyName)
|
|
|
|
Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType.
|
|
|
|
*/
|
|
|
|
function parseValue(value, objType, propertyName) {
|
2024-03-29 00:55:13 +00:00
|
|
|
if(Modules.Objects.types[objType].properties()[propertyName] == 'number')
|
2022-10-20 14:23:12 +00:00
|
|
|
return parseFloat(value)
|
|
|
|
else
|
|
|
|
return new MathLib.Expression(value)
|
2022-10-19 15:11:54 +00:00
|
|
|
}
|
2021-08-14 20:17:17 +00:00
|
|
|
}
|