12#include <QtCore/QByteArray>
13#include <QtCore/QPointF>
14#include <QtCore/qminmax.h>
15#include <QtCore/qnumeric.h>
16#include <QtGui/QVector3D>
30 if (
scene == _scene) {
34 disconnect(_scene,
nullptr,
this,
nullptr);
40 connect(_scene, &QObject::destroyed,
this, [
this] {
53 _points.reserve(coordinates.size());
54 for (
const QVariant& variant : coordinates) {
55 const QGeoCoordinate coordinate = variant.value<QGeoCoordinate>();
56 if (coordinate.isValid()) {
57 _points.append(coordinate);
67 if (!coordinate.isValid()) {
70 _points.append(coordinate);
71 if (_points.size() == 1) {
75 if (!_scene || (_points.size() < 3)) {
81 const qsizetype last = _points.size() - 1;
82 _positions.append(_positionFor(coordinate));
83 _segmentDirections.append(_segmentDirection(last - 1));
84 _vertexData.resize(_vertexData.size() + (2 *
kFloatsPerVertex *
static_cast<qsizetype
>(
sizeof(
float))));
85 _writeVertexPair(last - 1, _tangentAt(last - 1));
86 _writeVertexPair(last, _tangentAt(last));
87 _growBounds(_positions.last());
88 setVertexData(_vertexData);
89 setBounds(_minBounds, _maxBounds);
95 if (!coordinate.isValid() || _points.isEmpty()) {
98 _points.last() = coordinate;
99 if (_points.size() == 1) {
102 if (!_scene || (_points.size() < 2)) {
108 const qsizetype last = _points.size() - 1;
109 _positions.last() = _positionFor(coordinate);
110 _segmentDirections.last() = _segmentDirection(last - 1);
111 _writeVertexPair(last - 1, _tangentAt(last - 1));
112 _writeVertexPair(last, _tangentAt(last));
113 _growBounds(_positions.last());
114 const qsizetype offset = (last - 1) * 2 *
kFloatsPerVertex *
static_cast<qsizetype
>(
sizeof(
float));
115 const qsizetype length = 4 *
kFloatsPerVertex *
static_cast<qsizetype
>(
sizeof(float));
116 setVertexData(
static_cast<int>(offset), QByteArray(_vertexData.constData() + offset, length));
117 setBounds(_minBounds, _maxBounds);
123 if (_points.isEmpty()) {
132void FlightPathGeometry::_rebuild()
135 _segmentDirections.clear();
138 if (!_scene || (_points.size() < 2)) {
141 setVertexData(QByteArray());
142 setPrimitiveType(QQuick3DGeometry::PrimitiveType::TriangleStrip);
143 setBounds(QVector3D(), QVector3D());
148 _positions.reserve(_points.size());
149 for (
const QGeoCoordinate& point : _points) {
150 _positions.append(_positionFor(point));
153 _segmentDirections.reserve(_positions.size() - 1);
154 for (qsizetype i = 0; i < _positions.size() - 1; i++) {
155 _segmentDirections.append(_segmentDirection(i));
158 _vertexData.resize(_positions.size() * 2 *
kFloatsPerVertex *
static_cast<qsizetype
>(
sizeof(
float)));
159 _minBounds = _positions.first();
160 _maxBounds = _positions.first();
161 for (qsizetype i = 0; i < _positions.size(); i++) {
162 _writeVertexPair(i, _tangentAt(i));
163 _growBounds(_positions[i]);
168 setVertexData(_vertexData);
169 setPrimitiveType(QQuick3DGeometry::PrimitiveType::TriangleStrip);
170 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
171 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 3 *
sizeof(
float), QQuick3DGeometry::Attribute::F32Type);
172 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic, 6 *
sizeof(
float),
173 QQuick3DGeometry::Attribute::F32Type);
174 setBounds(_minBounds, _maxBounds);
179QVector3D FlightPathGeometry::_positionFor(
const QGeoCoordinate& coordinate)
const
181 const QGeoCoordinate& anchor = _points.first();
183 const double anchorAltitude = std::isfinite(anchor.altitude()) ? anchor.altitude() : 0.0;
185 const double altitude = std::isfinite(coordinate.altitude()) ? coordinate.altitude() : 0.0;
186 return QVector3D(
static_cast<float>(world.x() - anchorWorld.x()),
static_cast<float>(world.y() - anchorWorld.y()),
187 static_cast<float>((altitude - anchorAltitude) * _scene->
verticalScale()));
192QVector3D FlightPathGeometry::_segmentDirection(qsizetype index)
const
194 const QVector3D direction = _positions[index + 1] - _positions[index];
195 if (!qFuzzyIsNull(direction.lengthSquared())) {
196 return direction.normalized();
198 return (index > 0) ? _segmentDirections[index - 1] : QVector3D(0.0f, 1.0f, 0.0f);
202QVector3D FlightPathGeometry::_tangentAt(qsizetype index)
const
206 direction = _segmentDirections.first();
207 }
else if (index == _positions.size() - 1) {
208 direction = _segmentDirections.last();
210 direction = _segmentDirections[index - 1] + _segmentDirections[index];
212 if (qFuzzyIsNull(direction.lengthSquared())) {
213 direction = _segmentDirections[index - 1];
215 direction.normalize();
219void FlightPathGeometry::_writeVertexPair(qsizetype index,
const QVector3D& tangent)
221 const QVector3D& position = _positions[index];
222 float* vertex =
reinterpret_cast<float*
>(_vertexData.data()) + (index * 2 *
kFloatsPerVertex);
223 for (
const float side : {-1.0f, 1.0f}) {
224 *vertex++ = position.x();
225 *vertex++ = position.y();
226 *vertex++ = position.z();
227 *vertex++ = tangent.x();
228 *vertex++ = tangent.y();
229 *vertex++ = tangent.z();
235void FlightPathGeometry::_growBounds(
const QVector3D& position)
237 _minBounds = QVector3D(qMin(_minBounds.x(), position.x()), qMin(_minBounds.y(), position.y()),
238 qMin(_minBounds.z(), position.z()));
239 _maxBounds = QVector3D(qMax(_maxBounds.x(), position.x()), qMax(_maxBounds.y(), position.y()),
240 qMax(_maxBounds.z(), position.z()));
static constexpr int kFloatsPerVertex
position 3, tangent 3, side flag 2
FlightPathGeometry(QQuick3DObject *parent=nullptr)
void setScene(GeoScene *scene)
Q_INVOKABLE void clearPath()
Q_INVOKABLE void setPath(const QVariantList &coordinates)
Replace the whole path (QGeoCoordinate variants, TrajectoryPoints::list() format)
void anchorCoordinateChanged()
Q_INVOKABLE void appendPoint(const QGeoCoordinate &coordinate)
Q_INVOKABLE void updateLastPoint(const QGeoCoordinate &coordinate)
Replace the last point (TrajectoryPoints emits this for colinear motion)
qreal verticalScale() const
void sceneOriginChanged()
QPointF geoToWorld(const QGeoCoordinate &coord)
Geo -> world meters. Latitude is clamped to +/-kMaxLatitude.