mirror of https://github.com/qt/qtgraphs.git
Add ray picking to all graph types
Adding missing docs for picking Task-number: QTBUG-129385 Change-Id: Ief9d3a0d50382ae937660f9566b1552c2444c9b6 Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
This commit is contained in:
parent
c89d24cdbf
commit
1530321fda
|
|
@ -1988,6 +1988,90 @@ bool QQuickGraphsBars::doPicking(QPointF position)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool QQuickGraphsBars::doRayPicking(const QVector3D &origin, const QVector3D &direction)
|
||||
{
|
||||
if (!QQuickGraphsItem::doRayPicking(origin, direction))
|
||||
return false;
|
||||
|
||||
m_selectionDirty = true;
|
||||
QList<QQuick3DPickResult> pickResults = rayPickAll(origin, direction);
|
||||
QQuick3DModel *selectedModel = nullptr;
|
||||
QVector3D instancePos = {.0f, .0f, .0f};
|
||||
if (!selectionMode().testFlag(QtGraphs3D::SelectionFlag::None)) {
|
||||
if (!pickResults.isEmpty()) {
|
||||
for (const auto &picked : std::as_const(pickResults)) {
|
||||
if (const auto &hit = picked.objectHit()) {
|
||||
if (hit == backgroundBB() || hit == background()) {
|
||||
resetClickedStatus();
|
||||
continue;
|
||||
} else if (hit->objectName().contains(QStringLiteral("BarModel"))) {
|
||||
if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
|
||||
selectedModel = hit;
|
||||
for (const auto barlist : std::as_const(m_barModelsMap)) {
|
||||
for (const auto barModel : *barlist) {
|
||||
if (barModel->model == selectedModel) {
|
||||
setSelectedBar(barModel->coord,
|
||||
m_barModelsMap.key(barlist),
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
|
||||
BarInstancing *barIns = static_cast<BarInstancing *>(hit->instancing());
|
||||
// Prevents to select bars with a height of 0 which affect picking.
|
||||
if (!barIns->dataArray().isEmpty()
|
||||
&& barIns->dataArray().at(picked.instanceIndex())->heightValue
|
||||
!= 0) {
|
||||
selectedModel = hit;
|
||||
instancePos = selectedModel->instancing()->instancePosition(
|
||||
picked.instanceIndex());
|
||||
for (const auto barlist : std::as_const(m_barModelsMap)) {
|
||||
for (const auto barModel : *barlist) {
|
||||
QList<BarItemHolder *> barItemList = barModel->instancing
|
||||
->dataArray();
|
||||
for (const auto bih : barItemList) {
|
||||
if (bih->position == instancePos) {
|
||||
setSelectedBar(bih->coord,
|
||||
m_barModelsMap.key(barlist),
|
||||
false);
|
||||
if (isSliceEnabled())
|
||||
setSliceActivatedChanged(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (hit->objectName().contains(QStringLiteral("ElementAxis"))) {
|
||||
QPoint coord = invalidSelectionPosition();
|
||||
if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)
|
||||
&& selectedAxis() == axisX()) {
|
||||
// Use row from previous selection in case of row + column mode
|
||||
int previousRow = qMax(0, m_selectedBar.x());
|
||||
coord = QPoint(previousRow, selectedLabelIndex());
|
||||
} else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)
|
||||
&& selectedAxis() == axisZ()) {
|
||||
// Use column from previous selection in case of row + column mode
|
||||
int previousCol = qMax(0, m_selectedBar.y());
|
||||
coord = QPoint(selectedLabelIndex(), previousCol);
|
||||
}
|
||||
for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
|
||||
if (it.key()->isVisible())
|
||||
setSelectedBar(coord, it.key(), false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resetClickedStatus();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QAbstract3DAxis *QQuickGraphsBars::createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation)
|
||||
{
|
||||
QAbstract3DAxis *defaultAxis = 0;
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ protected:
|
|||
void handleLabelCountChanged(QQuick3DRepeater *repeater, QColor axisLabelColor) override;
|
||||
void updateSelectionMode(QtGraphs3D::SelectionFlags mode) override;
|
||||
bool doPicking(QPointF position) override;
|
||||
Q_REVISION(6, 9) bool doRayPicking(const QVector3D &origin, const QVector3D &direction) override;
|
||||
QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation) override;
|
||||
void updateSliceItemLabel(const QString &label, QVector3D position) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -397,6 +397,36 @@ constexpr float polarRoundness = 64.0f;
|
|||
* \sa Custom3DItem::textureFile
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlmethod void GraphsItem3D::releaseCustomItem(Custom3DItem item)
|
||||
*
|
||||
* Gets ownership of \a item back and removes the \a item from the graph.
|
||||
*
|
||||
* \note If the same item is added back to the graph, the texture file needs to
|
||||
* be re-set.
|
||||
*
|
||||
* \sa Custom3DItem::textureFile
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlmethod void GraphsItem3D::doPicking(QPoint point)
|
||||
*
|
||||
* Performs picking using view coordinates from \a point
|
||||
* on the elements of the graph, selecting the first item hit.
|
||||
* Default input handling performs this upon receiving the onTapped event.
|
||||
*
|
||||
* \sa selectedElement
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlmethod void GraphsItem3D::doPicking(QVector3D origin, QVector3D direction)
|
||||
*
|
||||
* Performs picking starting from \a origin and in \a direction
|
||||
* on the elements of the graph, selecting the first item hit.
|
||||
*
|
||||
* \sa selectedElement
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlmethod int GraphsItem3D::selectedLabelIndex()
|
||||
*
|
||||
|
|
@ -5693,6 +5723,56 @@ bool QQuickGraphsItem::doPicking(QPointF point)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool QQuickGraphsItem::doRayPicking(const QVector3D &origin, const QVector3D &direction)
|
||||
{
|
||||
checkSliceEnabled();
|
||||
|
||||
QList<QQuick3DPickResult> results = rayPickAll(origin, direction);
|
||||
if (!m_customItemList.isEmpty()) {
|
||||
// Try to pick custom item only
|
||||
for (const auto &result : results) {
|
||||
QCustom3DItem *customItem = m_customItemList.key(result.objectHit(), nullptr);
|
||||
|
||||
if (customItem) {
|
||||
qsizetype selectedIndex = m_customItems.indexOf(customItem);
|
||||
m_selectedCustomItemIndex = selectedIndex;
|
||||
handleSelectedElementChange(QtGraphs3D::ElementType::CustomItem);
|
||||
// Don't allow picking in subclasses if custom item is picked
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &result : results) {
|
||||
if (!result.objectHit())
|
||||
continue;
|
||||
QString objName = result.objectHit()->objectName();
|
||||
if (objName.contains(QStringLiteral("ElementAxisXLabel"))) {
|
||||
for (int i = 0; i < repeaterX()->count(); i++) {
|
||||
auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(i));
|
||||
if (result.objectHit() == obj)
|
||||
m_selectedLabelIndex = i;
|
||||
}
|
||||
handleSelectedElementChange(QtGraphs3D::ElementType::AxisXLabel);
|
||||
break;
|
||||
} else if (objName.contains(QStringLiteral("ElementAxisYLabel"))) {
|
||||
handleSelectedElementChange(QtGraphs3D::ElementType::AxisYLabel);
|
||||
break;
|
||||
} else if (objName.contains(QStringLiteral("ElementAxisZLabel"))) {
|
||||
for (int i = 0; i < repeaterX()->count(); i++) {
|
||||
auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(i));
|
||||
if (result.objectHit() == obj)
|
||||
m_selectedLabelIndex = i;
|
||||
}
|
||||
handleSelectedElementChange(QtGraphs3D::ElementType::AxisZLabel);
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::minimizeMainGraph()
|
||||
{
|
||||
QQuickItem *anchor = QQuickItemPrivate::get(this)->anchors()->fill();
|
||||
|
|
|
|||
|
|
@ -519,6 +519,8 @@ public:
|
|||
|
||||
Q_INVOKABLE virtual bool doPicking(QPointF point);
|
||||
|
||||
Q_REVISION(6, 9) Q_INVOKABLE virtual bool doRayPicking(const QVector3D &origin, const QVector3D &direction);
|
||||
|
||||
void minimizeMainGraph();
|
||||
|
||||
int horizontalFlipFactor() const;
|
||||
|
|
|
|||
|
|
@ -1405,6 +1405,36 @@ bool QQuickGraphsScatter::doPicking(QPointF position)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool QQuickGraphsScatter::doRayPicking(const QVector3D &origin, const QVector3D &direction)
|
||||
{
|
||||
if (!QQuickGraphsItem::doRayPicking(origin, direction))
|
||||
return false;
|
||||
|
||||
if (selectionMode() == QtGraphs3D::SelectionFlag::Item) {
|
||||
QList<QQuick3DPickResult> results = rayPickAll(origin, direction);
|
||||
if (!results.empty()) {
|
||||
for (const auto &result : std::as_const(results)) {
|
||||
if (const auto &hit = result.objectHit()) {
|
||||
if (hit == backgroundBB() || hit == background()) {
|
||||
clearSelectionModel();
|
||||
continue;
|
||||
}
|
||||
if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
|
||||
setSelected(hit);
|
||||
break;
|
||||
} else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
|
||||
setSelected(hit, result.instanceIndex());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearSelectionModel();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QQuickGraphsScatter::updateShadowQuality(QtGraphs3D::ShadowQuality quality)
|
||||
{
|
||||
// Were shadows visible before?
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ protected:
|
|||
void calculateSceneScalingFactors() override;
|
||||
void componentComplete() override;
|
||||
bool doPicking(QPointF position) override;
|
||||
Q_REVISION(6, 9) bool doRayPicking(const QVector3D &origin, const QVector3D &direction) override;
|
||||
void updateShadowQuality(QtGraphs3D::ShadowQuality quality) override;
|
||||
void updateLightStrength() override;
|
||||
void startRecordingRemovesAndInserts() override;
|
||||
|
|
|
|||
|
|
@ -2126,6 +2126,90 @@ bool QQuickGraphsSurface::doPicking(QPointF position)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool QQuickGraphsSurface::doRayPicking(const QVector3D &origin, const QVector3D &direction)
|
||||
{
|
||||
if (!m_pickThisFrame && m_proxyDirty) {
|
||||
m_pickThisFrame = true;
|
||||
QVector3D toScene = mapFrom3DScene(origin);
|
||||
m_lastPick = QPointF(toScene.x(), toScene.y());
|
||||
for (auto model : m_model)
|
||||
updateProxyModel(model);
|
||||
return false;
|
||||
}
|
||||
if (!QQuickGraphsItem::doRayPicking(origin, direction))
|
||||
return false;
|
||||
|
||||
m_selectionDirty = true;
|
||||
QList<QQuick3DPickResult> pickResult = rayPickAll(origin, direction);
|
||||
QVector3D pickedPos(0.0f, 0.0f, 0.0f);
|
||||
QQuick3DModel *pickedModel = nullptr;
|
||||
|
||||
if (!selectionMode().testFlag(QtGraphs3D::SelectionFlag::None)) {
|
||||
if (!sliceView() && selectionMode().testFlag(QtGraphs3D::SelectionFlag::Slice))
|
||||
createSliceView();
|
||||
|
||||
if (!pickResult.isEmpty()) {
|
||||
for (auto picked : pickResult) {
|
||||
bool inBounds = qAbs(picked.position().y()) < scaleWithBackground().y();
|
||||
if (inBounds && picked.objectHit()
|
||||
&& picked.objectHit()->objectName().contains(QStringLiteral("ProxyModel"))) {
|
||||
pickedPos = picked.position();
|
||||
pickedModel = qobject_cast<QQuick3DModel *>(picked.objectHit()->parentItem());
|
||||
bool visible = false;
|
||||
for (auto model : m_model) {
|
||||
if (model->model == pickedModel)
|
||||
visible = model->series->isVisible();
|
||||
}
|
||||
if (!pickedPos.isNull() && visible)
|
||||
break;
|
||||
} else {
|
||||
clearSelection();
|
||||
for (auto model : m_model)
|
||||
model->picked = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool inRange = qAbs(pickedPos.x()) < scaleWithBackground().x()
|
||||
&& qAbs(pickedPos.z()) < scaleWithBackground().z();
|
||||
|
||||
if (!pickedPos.isNull() && inRange) {
|
||||
float min = -1.0f;
|
||||
|
||||
for (auto model : m_model) {
|
||||
if (!model->series->isVisible()) {
|
||||
model->picked = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
model->picked = (model->model == pickedModel);
|
||||
|
||||
SurfaceVertex selectedVertex;
|
||||
for (auto vertex : model->vertices) {
|
||||
QVector3D pos = vertex.position;
|
||||
float dist = pickedPos.distanceToPoint(pos);
|
||||
if (selectedVertex.position.isNull() || dist < min) {
|
||||
min = dist;
|
||||
selectedVertex = vertex;
|
||||
}
|
||||
}
|
||||
model->selectedVertex = selectedVertex;
|
||||
if (!selectedVertex.position.isNull() && model->picked) {
|
||||
model->series->setSelectedPoint(selectedVertex.coord);
|
||||
setSlicingActive(false);
|
||||
if (isSliceEnabled())
|
||||
setSliceActivatedChanged(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearSelection();
|
||||
for (auto model : m_model)
|
||||
model->picked = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QQuickGraphsSurface::updateSelectedPoint()
|
||||
{
|
||||
bool labelVisible = false;
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ protected:
|
|||
void updateLightStrength() override;
|
||||
void handleThemeTypeChange() override;
|
||||
bool doPicking(QPointF position) override;
|
||||
Q_REVISION(6, 9) bool doRayPicking(const QVector3D &origin, const QVector3D &direction) override;
|
||||
|
||||
void createSliceView() override;
|
||||
void updateSliceItemLabel(const QString &label, QVector3D position) override;
|
||||
|
|
|
|||
|
|
@ -936,12 +936,31 @@ void Q3DGraphsWidgetItem::setMsaaSamples(int samples)
|
|||
d->m_graphsItem->setMsaaSamples(samples);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Performs picking using view coordinates from \a point
|
||||
* on the elements of the graph, selecting the first item hit.
|
||||
* Default input handling performs this upon receiving the onTapped event.
|
||||
*
|
||||
* \sa selectedElement
|
||||
*/
|
||||
void Q3DGraphsWidgetItem::doPicking(QPoint point)
|
||||
{
|
||||
Q_D(Q3DGraphsWidgetItem);
|
||||
d->m_graphsItem->doPicking(point);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Performs picking starting from \a origin and in \a direction
|
||||
* on the elements of the graph, selecting the first item hit.
|
||||
*
|
||||
* \sa selectedElement
|
||||
*/
|
||||
void Q3DGraphsWidgetItem::doRayPicking(const QVector3D &origin, const QVector3D &direction)
|
||||
{
|
||||
Q_D(Q3DGraphsWidgetItem);
|
||||
d->m_graphsItem->doRayPicking(origin, direction);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \property Q3DGraphsWidgetItem::measureFps
|
||||
*
|
||||
|
|
|
|||
|
|
@ -225,6 +225,8 @@ public:
|
|||
void setMsaaSamples(int samples);
|
||||
|
||||
void doPicking(QPoint point);
|
||||
Q_REVISION(6, 9)
|
||||
void doRayPicking(const QVector3D &origin, const QVector3D &direction);
|
||||
|
||||
float ambientLightStrength() const;
|
||||
void setAmbientLightStrength(float newAmbientLightStrength);
|
||||
|
|
|
|||
|
|
@ -106,6 +106,18 @@ Item {
|
|||
to: Qt.vector3d(360, 0, 360)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var origin = view3d.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 0))
|
||||
var far = view3d.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 1))
|
||||
var direction = Qt.vector3d(far.x - origin.x,
|
||||
far.y - origin.y,
|
||||
far.z - origin.z );
|
||||
bars.doRayPicking(origin, direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Slider {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,18 @@ Rectangle {
|
|||
clearColor: scatter.theme.backgroundColor
|
||||
backgroundMode: SceneEnvironment.Color
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var origin = topLeftView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 0))
|
||||
var far = topLeftView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 1))
|
||||
var direction = Qt.vector3d(far.x - origin.x,
|
||||
far.y - origin.y,
|
||||
far.z - origin.z );
|
||||
scatter.doRayPicking(origin, direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +126,18 @@ Rectangle {
|
|||
clearColor: scatter.theme.backgroundColor
|
||||
backgroundMode: SceneEnvironment.Color
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var origin = topRightView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 0))
|
||||
var far = topRightView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 1))
|
||||
var direction = Qt.vector3d(far.x - origin.x,
|
||||
far.y - origin.y,
|
||||
far.z - origin.z );
|
||||
scatter.doRayPicking(origin, direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +161,17 @@ Rectangle {
|
|||
clearColor: scatter.theme.backgroundColor
|
||||
backgroundMode: SceneEnvironment.Color
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var origin = bottomLeftView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 0))
|
||||
var far = bottomLeftView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 1))
|
||||
var direction = Qt.vector3d(far.x - origin.x,
|
||||
far.y - origin.y,
|
||||
far.z - origin.z );
|
||||
scatter.doRayPicking(origin, direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +195,18 @@ Rectangle {
|
|||
clearColor: scatter.theme.backgroundColor
|
||||
backgroundMode: SceneEnvironment.Color
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var origin = bottomRightView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 0))
|
||||
var far = bottomRightView.mapTo3DScene(Qt.vector3d(mouseX, mouseY, 1))
|
||||
var direction = Qt.vector3d(far.x - origin.x,
|
||||
far.y - origin.y,
|
||||
far.z - origin.z );
|
||||
scatter.doRayPicking(origin, direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue