12#include <QtCore/QPointF>
13#include <QtCore/qminmax.h>
14#include <QtCore/qnumeric.h>
23constexpr double kMaxSegmentMeters = 50.0;
24constexpr int kMaxSubdivisionsPerEdge = 64;
25constexpr int kFloatsPerVertex = 3;
35 if (
scene == _scene) {
39 disconnect(_scene,
nullptr,
this,
nullptr);
45 connect(_scene, &QObject::destroyed,
this, [
this] {
61 disconnect(_surfaceModel,
nullptr,
this,
nullptr);
66 connect(_surfaceModel, &QObject::destroyed,
this, [
this] {
67 _surfaceModel =
nullptr;
85 const QGeoCoordinate coordinate = variant.value<QGeoCoordinate>();
86 if (coordinate.isValid()) {
87 _ring.append(coordinate);
91 if ((_ring.size() > 1) && (_ring.first().distanceTo(_ring.last()) < 0.01)) {
108void FenceWallGeometry::_scheduleRebuild()
110 if (_rebuildPending) {
113 _rebuildPending =
true;
114 QMetaObject::invokeMethod(
this, &FenceWallGeometry::_rebuild, Qt::QueuedConnection);
117void FenceWallGeometry::_rebuild()
119 _rebuildPending =
false;
120 if (!_scene || (_ring.size() < 3) || (_heightAglMeters <= 0.0)) {
121 _setAnchorCoordinate(QGeoCoordinate());
123 setStride(kFloatsPerVertex *
sizeof(
float));
124 setVertexData(QByteArray());
125 setPrimitiveType(QQuick3DGeometry::PrimitiveType::TriangleStrip);
126 setBounds(QVector3D(), QVector3D());
132 QList<QGeoCoordinate> points;
133 for (qsizetype i = 0; i < _ring.size(); i++) {
134 const QGeoCoordinate& from = _ring[i];
135 const QGeoCoordinate& to = _ring[(i + 1) % _ring.size()];
136 const double distance = from.distanceTo(to);
137 const int steps = qBound(1,
static_cast<int>(std::ceil(distance / kMaxSegmentMeters)), kMaxSubdivisionsPerEdge);
138 const double azimuth = from.azimuthTo(to);
140 for (
int step = 1; step < steps; step++) {
141 points.append(from.atDistanceAndAzimuth(distance * step / steps, azimuth));
144 points.append(points.first());
146 const QGeoCoordinate& anchor = points.first();
147 const double anchorTerrain = _terrainHeight(anchor);
148 _setAnchorCoordinate(QGeoCoordinate(anchor.latitude(), anchor.longitude(), anchorTerrain));
152 const float wallHeight =
static_cast<float>(_heightAglMeters * verticalScale);
154 QByteArray vertexData;
155 vertexData.resize(points.size() * 2 * kFloatsPerVertex *
static_cast<qsizetype
>(
sizeof(float)));
156 float* vertex =
reinterpret_cast<float*
>(vertexData.data());
159 for (qsizetype i = 0; i < points.size(); i++) {
161 const float x =
static_cast<float>(world.x() - anchorWorld.x());
162 const float y =
static_cast<float>(world.y() - anchorWorld.y());
163 const float zBottom =
static_cast<float>((_terrainHeight(points[i]) - anchorTerrain) * verticalScale);
169 *vertex++ = zBottom + wallHeight;
170 const QVector3D bottom(x, y, zBottom);
171 const QVector3D top(x, y, zBottom + wallHeight);
176 minBounds = QVector3D(qMin(minBounds.x(), x), qMin(minBounds.y(), y), qMin(minBounds.z(), zBottom));
177 maxBounds = QVector3D(qMax(maxBounds.x(), x), qMax(maxBounds.y(), y), qMax(maxBounds.z(), top.z()));
182 setStride(kFloatsPerVertex *
sizeof(
float));
183 setVertexData(vertexData);
184 setPrimitiveType(QQuick3DGeometry::PrimitiveType::TriangleStrip);
185 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
186 setBounds(minBounds, maxBounds);
190double FenceWallGeometry::_terrainHeight(
const QGeoCoordinate& coordinate)
const
192 if (!_surfaceModel) {
199void FenceWallGeometry::_setAnchorCoordinate(
const QGeoCoordinate& anchorCoordinate)
void anchorCoordinateChanged()
void setScene(GeoScene *scene)
QVariantList coordinates() const
void surfaceModelChanged()
SurfacePatchModel * surfaceModel() const
void coordinatesChanged()
FenceWallGeometry(QQuick3DObject *parent=nullptr)
void setSurfaceModel(SurfacePatchModel *surfaceModel)
void setCoordinates(const QVariantList &coordinates)
void heightAglMetersChanged()
void setHeightAglMeters(qreal heightAglMeters)
QGeoCoordinate anchorCoordinate() const
qreal heightAglMeters() const
qreal verticalScale() const
void sceneOriginChanged()
void terrainHeightsChanged()
terrainHeightAt answers changed somewhere: consumers re-query
Q_INVOKABLE double terrainHeightAt(const QGeoCoordinate &coordinate) const
double distance(const Ecef &left, const Ecef &right)
Straight-line distance between left and right, in metres.
QPointF geoToWorld(const QGeoCoordinate &coord)
Geo -> world meters. Latitude is clamped to +/-kMaxLatitude.