KOSMIndoorMap

hitdetector.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "hitdetector.h"
8
9#include "../scene/scenegraph.h"
10#include "../scene/scenegraphitem.h"
11#include "../scene/scenegeometry_p.h"
12#include "../scene/view.h"
13
14#include <QBrush>
15#include <QFontMetrics>
16
17using namespace KOSMIndoorMap;
18
19const SceneGraphItem* HitDetector::itemAt(QPointF pos, const SceneGraph& sg, const View* view) const
20{
21 auto items = itemsAt(pos, sg, view);
22 if (items.empty()) {
23 return nullptr;
24 }
25 if (items.size() == 1) {
26 return items[0];
27 }
28
29 // multiple candidates
30 // (1) top element is non-transparent, use that:
31 const auto top = items.back();
32 qDebug() << top->element.url() << itemFillAlpha(top);
33 if (itemFillAlpha(top) >= 0.5f) {
34 return top;
35 }
36
37 // (2) in presence of transparency, use the smallest item at this position
38 std::sort(items.begin(), items.end(), [view](auto lhs, auto rhs) {
39 const auto lhsBbox = lhs->payload->boundingRect(view);
40 const auto rhsBbox = rhs->payload->boundingRect(view);
41 return (lhsBbox.width() * lhsBbox.height()) < (rhsBbox.width() * rhsBbox.height());
42 });
43 return items.front();
44}
45
46std::vector<const SceneGraphItem*> HitDetector::itemsAt(QPointF pos, const SceneGraph &sg, const View *view) const
47{
48 std::vector<const SceneGraphItem*> result;
49 for (const auto &item : sg.items()) {
50 if (item.payload->renderPhases() == SceneGraphItemPayload::NoPhase || !item.payload->boundingRect(view).contains(view->mapScreenToScene(pos))) {
51 continue;
52 }
53 if (!itemContainsPoint(item, pos, view)) {
54 continue;
55 }
56 result.push_back(&item);
57 }
58
59 return result;
60}
61
62bool HitDetector::itemContainsPoint(const SceneGraphItem &item, QPointF screenPos, const View *view) const
63{
64 if (const auto i = dynamic_cast<PolygonItem*>(item.payload.get())) {
65 return itemContainsPoint(i, view->mapScreenToScene(screenPos));
66 }
67 if (const auto i = dynamic_cast<MultiPolygonItem*>(item.payload.get())) {
68 return itemContainsPoint(i, view->mapScreenToScene(screenPos));
69 }
70 if (const auto i = dynamic_cast<PolylineItem*>(item.payload.get())) {
71 return itemContainsPoint(i, view->mapScreenToScene(screenPos), view);
72 }
73 if (const auto i = dynamic_cast<LabelItem*>(item.payload.get())) {
74 return itemContainsPoint(i, screenPos, view);
75 }
76
77 return true;
78}
79
80bool HitDetector::itemContainsPoint(const MultiPolygonItem *item, QPointF scenePos) const
81{
82 return item->path.contains(scenePos);
83}
84
85bool HitDetector::itemContainsPoint(const PolygonItem *item, QPointF scenePos) const
86{
87 return item->polygon.containsPoint(scenePos, Qt::OddEvenFill);
88}
89
90bool HitDetector::itemContainsPoint(const PolylineItem *item, QPointF scenePos, const View *view) const
91{
92 if (item->path.size() < 2) {
93 return false;
94 }
95
96 const auto lineWidth = view->mapMetersToScene(item->pen.widthF())
97 + view->mapScreenDistanceToSceneDistance(item->casingPen.widthF());
98
99 double dist = std::numeric_limits<double>::max();
100 // TODO do we need to wrap around here for closed lines?
101 for (auto it = std::next(item->path.begin()); it != item->path.end(); ++it) {
102 QLineF line(*std::prev(it), *it);
103 dist = std::min(dist, SceneGeometry::distanceToLine(line, scenePos));
104 }
105
106 return dist <= lineWidth;
107}
108
109bool HitDetector::itemContainsPoint(const LabelItem *item, QPointF screenPos, const View *view) const
110{
111 // TODO item->angle != 0
112 if (item->iconHidden) {
113 return false;
114 }
115
116 if (item->textHidden) {
117 const auto hitBox = item->iconHitBox(view);
118 return hitBox.contains(screenPos);
119 }
120
121 const auto hitBox = item->shieldHitBox(view);
122 return hitBox.contains(screenPos);
123}
124
125float HitDetector::itemFillAlpha(const SceneGraphItem *item) const
126{
127 if (const auto i = dynamic_cast<PolygonItem*>(item->payload.get())) {
128 return std::max(i->fillBrush.color().alphaF(), i->textureBrush.color().alphaF());
129 }
130 if (const auto i = dynamic_cast<MultiPolygonItem*>(item->payload.get())) {
131 return std::max(i->fillBrush.color().alphaF(), i->textureBrush.color().alphaF());
132 }
133 return 1.0f;
134}
std::vector< const SceneGraphItem * > itemsAt(QPointF pos, const SceneGraph &sg, const View *view) const
All items (in z-order) at the given screen position.
const SceneGraphItem * itemAt(QPointF pos, const SceneGraph &sg, const View *view) const
Highest (in z-order) item at the given screen position.
A text or icon label.
Multi-polygon item, used for polygons with "holes" in them.
A single filled polygon.
A path/way/line item in the scenegraph.
Scene graph item description and handle for its content.
Scene graph of the currently displayed level.
Definition scenegraph.h:29
View transformations and transformation manipulation.
Definition view.h:40
Q_INVOKABLE QPointF mapScreenToScene(QPointF screenPos) const
Converts a point in screen coordinates to scene coordinates.
Definition view.cpp:185
OSM-based multi-floor indoor maps for buildings.
iterator begin()
iterator end()
qsizetype size() const const
bool contains(const QPainterPath &p) const const
qreal widthF() const const
bool containsPoint(const QPointF &point, Qt::FillRule fillRule) const const
bool contains(const QPointF &point) const const
OddEvenFill
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:06:15 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.