KOSMIndoorMap

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

KDE's Doxygen guidelines are available online.