1/****************************************************************************
3 * (c) 2009-2024 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
5 * QGroundControl is licensed according to the terms in the file
6 * COPYING.md in the root of the source code directory.
8 ****************************************************************************/
15import QGroundControl.Controls
16import QGroundControl.GeoMap
18/// Vehicle flight-path trail for the GeoMap: a red 3D ribbon in the scene at
19/// true flight altitude, depth-tested so terrain occludes it. Width stays
20/// constant in screen pixels via vertex-shader expansion. Stays visible in 2D
21/// mode (crossfade3D off): the altitude offsets flatten with terrainScale so
22/// it renders as the classic flat trail line. Altitudes get the same
23/// DEM-vs-home-AMSL bias correction as GeoMapVehicleItem, applied at the
24/// anchor so the whole trail shifts with it.
29 property real lineWidth: ScreenTools.defaultFontPixelHeight / 5
30 property color lineColor: "red"
32 altitudeMode: GeoMapItem.Absolute
35 if (!_geometry || !_geometry.anchorCoordinate.isValid) {
36 return QtPositioning.coordinate()
38 const anchor = _geometry.anchorCoordinate
39 return QtPositioning.coordinate(anchor.latitude, anchor.longitude, anchor.altitude + _homeTerrainBias)
43 // The geometry lives inside the 3D delegate (created/destroyed with the
44 // scene); this bridges its anchor out for the coordinate binding above
45 property var _geometry: null
47 readonly property var _camera: scene ? scene.camera : null
49 // Pixel-to-scene-unit factor at unit distance; the shader scales it by
50 // each vertex's own camera distance
51 readonly property real _screenFactor: _camera && _camera.viewportSize.height > 0
52 ? 2 * Math.tan(_camera.fieldOfView * Math.PI / 360) / _camera.viewportSize.height
55 // DEM height minus vehicle-reported AMSL, sampled at home (same
56 // correction as GeoMapVehicleItem so trail and marker stay aligned)
57 property real _homeTerrainBias: 0
59 function _updateHomeTerrainBias() {
60 if (surfaceModel && vehicle && vehicle.homePosition.isValid && !isNaN(vehicle.homePosition.altitude)) {
61 _homeTerrainBias = surfaceModel.terrainHeightAt(vehicle.homePosition) - vehicle.homePosition.altitude
67 function _reloadPath() {
69 _geometry.setPath(vehicle ? vehicle.trajectoryPoints.list() : [])
74 _updateHomeTerrainBias()
77 onSurfaceModelChanged: _updateHomeTerrainBias()
79 delegate3D: Component {
80 // z offsets are relative altitudes; flatten them with the terrain
81 // during the 2D<->3D transition (anchor z already tracks terrainScale
82 // through GeoMapItem)
84 scale: Qt.vector3d(1, 1, root.scene ? root.scene.terrainScale : 1)
87 geometry: FlightPathGeometry {
93 materials: CustomMaterial {
94 shadingMode: CustomMaterial.Unshaded
95 cullMode: Material.NoCulling
96 vertexShader: "shaders/flightpath.vert"
97 fragmentShader: "shaders/flightpath.frag"
99 property real lineWidth: root.lineWidth
100 property real screenFactor: root._screenFactor
101 property color pathColor: root.lineColor
102 // Ramps in as the terrain flattens, effectively disabling
103 // depth testing in 2D mode (see flightpath.vert)
104 property real depthPull: root.scene ? 0.5 * (1.0 - root.scene.terrainScale) : 0.0
107 Component.onCompleted: {
108 root._geometry = pathGeometry
111 Component.onDestruction: root._geometry = null
115 target: root.vehicle ? root.vehicle.trajectoryPoints : null
117 function onPointAdded(coordinate) { pathGeometry.appendPoint(coordinate) }
118 function onUpdateLastPoint(coordinate) { pathGeometry.updateLastPoint(coordinate) }
119 function onPointsCleared() { pathGeometry.clearPath() }
125 target: root.surfaceModel
127 function onTerrainHeightsChanged() {
128 root._updateHomeTerrainBias()
135 function onHomePositionChanged() {
136 root._updateHomeTerrainBias()