Unverified Commit e6c49d97 authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #7202 from DonLakeFlyer/PlanLoad

Plan Load: Save camera shot count and complex distance in .plan file
parents 4636ea97 4c53c984
...@@ -124,10 +124,14 @@ bool CorridorScanComplexItem::load(const QJsonObject& complexObject, int sequenc ...@@ -124,10 +124,14 @@ bool CorridorScanComplexItem::load(const QJsonObject& complexObject, int sequenc
_entryPoint = complexObject[_jsonEntryPointKey].toInt(); _entryPoint = complexObject[_jsonEntryPointKey].toInt();
_rebuildTransects();
_ignoreRecalc = false; _ignoreRecalc = false;
_recalcComplexDistance();
if (_cameraShots == 0) {
// Shot count was possibly not available from plan file
_recalcCameraShots();
}
return true; return true;
} }
...@@ -461,14 +465,17 @@ void CorridorScanComplexItem::_rebuildTransectsPhase1(void) ...@@ -461,14 +465,17 @@ void CorridorScanComplexItem::_rebuildTransectsPhase1(void)
} }
} }
void CorridorScanComplexItem::_rebuildTransectsPhase2(void) void CorridorScanComplexItem::_recalcComplexDistance(void)
{ {
// Calculate distance flown for complex item
_complexDistance = 0; _complexDistance = 0;
for (int i=0; i<_visualTransectPoints.count() - 1; i++) { for (int i=0; i<_visualTransectPoints.count() - 1; i++) {
_complexDistance += _visualTransectPoints[i].value<QGeoCoordinate>().distanceTo(_visualTransectPoints[i+1].value<QGeoCoordinate>()); _complexDistance += _visualTransectPoints[i].value<QGeoCoordinate>().distanceTo(_visualTransectPoints[i+1].value<QGeoCoordinate>());
} }
emit complexDistanceChanged();
}
void CorridorScanComplexItem::_recalcCameraShots(void)
{
double triggerDistance = _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble(); double triggerDistance = _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble();
if (triggerDistance == 0) { if (triggerDistance == 0) {
_cameraShots = 0; _cameraShots = 0;
...@@ -480,14 +487,7 @@ void CorridorScanComplexItem::_rebuildTransectsPhase2(void) ...@@ -480,14 +487,7 @@ void CorridorScanComplexItem::_rebuildTransectsPhase2(void)
_cameraShots = singleTransectImageCount * _transectCount(); _cameraShots = singleTransectImageCount * _transectCount();
} }
} }
_coordinate = _visualTransectPoints.count() ? _visualTransectPoints.first().value<QGeoCoordinate>() : QGeoCoordinate();
_exitCoordinate = _visualTransectPoints.count() ? _visualTransectPoints.last().value<QGeoCoordinate>() : QGeoCoordinate();
emit cameraShotsChanged(); emit cameraShotsChanged();
emit complexDistanceChanged();
emit coordinateChanged(_coordinate);
emit exitCoordinateChanged(_exitCoordinate);
} }
bool CorridorScanComplexItem::readyForSave(void) const bool CorridorScanComplexItem::readyForSave(void) const
......
...@@ -65,7 +65,8 @@ private slots: ...@@ -65,7 +65,8 @@ private slots:
// Overrides from TransectStyleComplexItem // Overrides from TransectStyleComplexItem
void _rebuildTransectsPhase1 (void) final; void _rebuildTransectsPhase1 (void) final;
void _rebuildTransectsPhase2 (void) final; void _recalcComplexDistance (void) final;
void _recalcCameraShots (void) final;
private: private:
int _transectCount (void) const; int _transectCount (void) const;
......
...@@ -144,6 +144,12 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe ...@@ -144,6 +144,12 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe
if (!_loadV4V5(complexObject, sequenceNumber, errorString, version)) { if (!_loadV4V5(complexObject, sequenceNumber, errorString, version)) {
return false; return false;
} }
_recalcComplexDistance();
if (_cameraShots == 0) {
// Shot count was possibly not available from plan file
_recalcCameraShots();
}
} else { } else {
// Must be v2 or v3 // Must be v2 or v3
QJsonObject v3ComplexObject = complexObject; QJsonObject v3ComplexObject = complexObject;
...@@ -157,9 +163,10 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe ...@@ -157,9 +163,10 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe
if (!_loadV3(complexObject, sequenceNumber, errorString)) { if (!_loadV3(complexObject, sequenceNumber, errorString)) {
return false; return false;
} }
}
// V2/3 doesn't include individual items so we need to rebuild manually
_rebuildTransects(); _rebuildTransects();
}
return true; return true;
} }
...@@ -1384,42 +1391,78 @@ void SurveyComplexItem::_rebuildTransectsFromPolygon(bool refly, const QPolygonF ...@@ -1384,42 +1391,78 @@ void SurveyComplexItem::_rebuildTransectsFromPolygon(bool refly, const QPolygonF
qCDebug(SurveyComplexItemLog) << "_transects.size() " << _transects.size(); qCDebug(SurveyComplexItemLog) << "_transects.size() " << _transects.size();
} }
void SurveyComplexItem::_rebuildTransectsPhase2(void) void SurveyComplexItem::_recalcComplexDistance(void)
{ {
// Calculate distance flown for complex item
_complexDistance = 0; _complexDistance = 0;
for (int i=0; i<_visualTransectPoints.count() - 1; i++) { for (int i=0; i<_visualTransectPoints.count() - 1; i++) {
_complexDistance += _visualTransectPoints[i].value<QGeoCoordinate>().distanceTo(_visualTransectPoints[i+1].value<QGeoCoordinate>()); _complexDistance += _visualTransectPoints[i].value<QGeoCoordinate>().distanceTo(_visualTransectPoints[i+1].value<QGeoCoordinate>());
} }
emit complexDistanceChanged();
}
if (triggerDistance() == 0) { void SurveyComplexItem::_recalcCameraShots(void)
{
double triggerDistance = this->triggerDistance();
if (triggerDistance == 0) {
_cameraShots = 0; _cameraShots = 0;
} else { } else {
if (_cameraTriggerInTurnAroundFact.rawValue().toBool()) { if (_cameraTriggerInTurnAroundFact.rawValue().toBool()) {
_cameraShots = qCeil(_complexDistance / triggerDistance()); _cameraShots = qCeil(_complexDistance / triggerDistance);
} else { } else {
_cameraShots = 0; _cameraShots = 0;
if (_loadedMissionItemsParent) {
// We have to do it the hard way based on the mission items themselves
if (hoverAndCaptureEnabled()) {
// Count the number of camera triggers in the mission items
for (const MissionItem* missionItem: _loadedMissionItems) {
_cameraShots += missionItem->command() == MAV_CMD_IMAGE_START_CAPTURE ? 1 : 0;
}
} else {
bool waitingForTriggerStop = false;
QGeoCoordinate distanceStartCoord;
QGeoCoordinate distanceEndCoord;
for (const MissionItem* missionItem: _loadedMissionItems) {
if (missionItem->command() == MAV_CMD_NAV_WAYPOINT) {
if (waitingForTriggerStop) {
distanceEndCoord = QGeoCoordinate(missionItem->param5(), missionItem->param6());
} else {
distanceStartCoord = QGeoCoordinate(missionItem->param5(), missionItem->param6());
}
} else if (missionItem->command() == MAV_CMD_DO_SET_CAM_TRIGG_DIST) {
if (missionItem->param1() > 0) {
// Trigger start
waitingForTriggerStop = true;
} else {
// Trigger stop
waitingForTriggerStop = false;
_cameraShots += qCeil(distanceEndCoord.distanceTo(distanceStartCoord) / triggerDistance);
distanceStartCoord = QGeoCoordinate();
distanceEndCoord = QGeoCoordinate();
}
}
}
}
} else {
// We have transects available, calc from those
for (const QList<TransectStyleComplexItem::CoordInfo_t>& transect: _transects) { for (const QList<TransectStyleComplexItem::CoordInfo_t>& transect: _transects) {
QGeoCoordinate firstCameraCoord, lastCameraCoord; QGeoCoordinate firstCameraCoord, lastCameraCoord;
if (_hasTurnaround()) { if (_hasTurnaround() && !hoverAndCaptureEnabled()) {
firstCameraCoord = transect[1].coord; firstCameraCoord = transect[1].coord;
lastCameraCoord = transect[transect.count() - 2].coord; lastCameraCoord = transect[transect.count() - 2].coord;
} else { } else {
firstCameraCoord = transect.first().coord; firstCameraCoord = transect.first().coord;
lastCameraCoord = transect.last().coord; lastCameraCoord = transect.last().coord;
} }
_cameraShots += qCeil(firstCameraCoord.distanceTo(lastCameraCoord) / triggerDistance()); _cameraShots += qCeil(firstCameraCoord.distanceTo(lastCameraCoord) / triggerDistance);
}
} }
} }
} }
_coordinate = _visualTransectPoints.count() ? _visualTransectPoints.first().value<QGeoCoordinate>() : QGeoCoordinate();
_exitCoordinate = _visualTransectPoints.count() ? _visualTransectPoints.last().value<QGeoCoordinate>() : QGeoCoordinate();
emit cameraShotsChanged(); emit cameraShotsChanged();
emit complexDistanceChanged();
emit coordinateChanged(_coordinate);
emit exitCoordinateChanged(_exitCoordinate);
} }
// FIXME: This same exact code is in Corridor Scan. Move to TransectStyleComplex? // FIXME: This same exact code is in Corridor Scan. Move to TransectStyleComplex?
......
...@@ -78,8 +78,9 @@ signals: ...@@ -78,8 +78,9 @@ signals:
private slots: private slots:
// Overrides from TransectStyleComplexItem // Overrides from TransectStyleComplexItem
void _rebuildTransectsPhase1(void) final; void _rebuildTransectsPhase1 (void) final;
void _rebuildTransectsPhase2(void) final; void _recalcComplexDistance (void) final;
void _recalcCameraShots (void) final;
private: private:
enum CameraTriggerCode { enum CameraTriggerCode {
......
...@@ -35,6 +35,7 @@ const char* TransectStyleComplexItem::_jsonCameraCalcKey = "Cam ...@@ -35,6 +35,7 @@ const char* TransectStyleComplexItem::_jsonCameraCalcKey = "Cam
const char* TransectStyleComplexItem::_jsonVisualTransectPointsKey = "VisualTransectPoints"; const char* TransectStyleComplexItem::_jsonVisualTransectPointsKey = "VisualTransectPoints";
const char* TransectStyleComplexItem::_jsonItemsKey = "Items"; const char* TransectStyleComplexItem::_jsonItemsKey = "Items";
const char* TransectStyleComplexItem::_jsonFollowTerrainKey = "FollowTerrain"; const char* TransectStyleComplexItem::_jsonFollowTerrainKey = "FollowTerrain";
const char* TransectStyleComplexItem::_jsonCameraShotsKey = "CameraShots";
const int TransectStyleComplexItem::_terrainQueryTimeoutMsecs = 1000; const int TransectStyleComplexItem::_terrainQueryTimeoutMsecs = 1000;
...@@ -139,6 +140,7 @@ void TransectStyleComplexItem::_save(QJsonObject& complexObject) ...@@ -139,6 +140,7 @@ void TransectStyleComplexItem::_save(QJsonObject& complexObject)
innerObject[hoverAndCaptureName] = _hoverAndCaptureFact.rawValue().toBool(); innerObject[hoverAndCaptureName] = _hoverAndCaptureFact.rawValue().toBool();
innerObject[refly90DegreesName] = _refly90DegreesFact.rawValue().toBool(); innerObject[refly90DegreesName] = _refly90DegreesFact.rawValue().toBool();
innerObject[_jsonFollowTerrainKey] = _followTerrain; innerObject[_jsonFollowTerrainKey] = _followTerrain;
innerObject[_jsonCameraShotsKey] = _cameraShots;
if (_followTerrain) { if (_followTerrain) {
innerObject[terrainAdjustToleranceName] = _terrainAdjustToleranceFact.rawValue().toDouble(); innerObject[terrainAdjustToleranceName] = _terrainAdjustToleranceFact.rawValue().toDouble();
...@@ -212,6 +214,7 @@ bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString& ...@@ -212,6 +214,7 @@ bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString&
{ _jsonVisualTransectPointsKey, QJsonValue::Array, true }, { _jsonVisualTransectPointsKey, QJsonValue::Array, true },
{ _jsonItemsKey, QJsonValue::Array, true }, { _jsonItemsKey, QJsonValue::Array, true },
{ _jsonFollowTerrainKey, QJsonValue::Bool, true }, { _jsonFollowTerrainKey, QJsonValue::Bool, true },
{ _jsonCameraShotsKey, QJsonValue::Double, false }, // Not required since it was missing from initial implementation
}; };
if (!JsonHelper::validateKeys(innerObject, innerKeyInfoList, errorString)) { if (!JsonHelper::validateKeys(innerObject, innerKeyInfoList, errorString)) {
return false; return false;
...@@ -249,6 +252,12 @@ bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString& ...@@ -249,6 +252,12 @@ bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString&
_refly90DegreesFact.setRawValue (innerObject[refly90DegreesName].toBool()); _refly90DegreesFact.setRawValue (innerObject[refly90DegreesName].toBool());
_followTerrain = innerObject[_jsonFollowTerrainKey].toBool(); _followTerrain = innerObject[_jsonFollowTerrainKey].toBool();
// These two keys where not included in initial implementation so they are optional. Without them the values will be
// incorrect when loaded though.
if (innerObject.contains(_jsonCameraShotsKey)) {
_cameraShots = innerObject[_jsonCameraShotsKey].toInt();
}
if (_followTerrain) { if (_followTerrain) {
QList<JsonHelper::KeyValidateInfo> followTerrainKeyInfoList = { QList<JsonHelper::KeyValidateInfo> followTerrainKeyInfoList = {
{ terrainAdjustToleranceName, QJsonValue::Double, true }, { terrainAdjustToleranceName, QJsonValue::Double, true },
...@@ -394,7 +403,8 @@ void TransectStyleComplexItem::_rebuildTransects(void) ...@@ -394,7 +403,8 @@ void TransectStyleComplexItem::_rebuildTransects(void)
emit coordinateChanged(_coordinate); emit coordinateChanged(_coordinate);
emit exitCoordinateChanged(_exitCoordinate); emit exitCoordinateChanged(_exitCoordinate);
_rebuildTransectsPhase2(); _recalcComplexDistance();
_recalcCameraShots();
emit lastSequenceNumberChanged(lastSequenceNumber()); emit lastSequenceNumberChanged(lastSequenceNumber());
emit timeBetweenShotsChanged(); emit timeBetweenShotsChanged();
......
...@@ -129,9 +129,6 @@ signals: ...@@ -129,9 +129,6 @@ signals:
void followTerrainChanged (bool followTerrain); void followTerrainChanged (bool followTerrain);
protected slots: protected slots:
virtual void _rebuildTransectsPhase1 (void) = 0; ///< Rebuilds the _transects array
virtual void _rebuildTransectsPhase2 (void) = 0; ///< Adjust values associated with _transects array
void _setDirty (void); void _setDirty (void);
void _setIfDirty (bool dirty); void _setIfDirty (bool dirty);
void _updateCoordinateAltitudes (void); void _updateCoordinateAltitudes (void);
...@@ -139,6 +136,10 @@ protected slots: ...@@ -139,6 +136,10 @@ protected slots:
void _rebuildTransects (void); void _rebuildTransects (void);
protected: protected:
virtual void _rebuildTransectsPhase1 (void) = 0; ///< Rebuilds the _transects array
virtual void _recalcComplexDistance (void) = 0;
virtual void _recalcCameraShots (void) = 0;
void _save (QJsonObject& saveObject); void _save (QJsonObject& saveObject);
bool _load (const QJsonObject& complexObject, QString& errorString); bool _load (const QJsonObject& complexObject, QString& errorString);
void _setExitCoordinate (const QGeoCoordinate& coordinate); void _setExitCoordinate (const QGeoCoordinate& coordinate);
...@@ -197,6 +198,7 @@ protected: ...@@ -197,6 +198,7 @@ protected:
static const char* _jsonVisualTransectPointsKey; static const char* _jsonVisualTransectPointsKey;
static const char* _jsonItemsKey; static const char* _jsonItemsKey;
static const char* _jsonFollowTerrainKey; static const char* _jsonFollowTerrainKey;
static const char* _jsonCameraShotsKey;
static const int _terrainQueryTimeoutMsecs; static const int _terrainQueryTimeoutMsecs;
......
...@@ -115,10 +115,12 @@ void TransectStyleComplexItemTest::_testRebuildTransects(void) ...@@ -115,10 +115,12 @@ void TransectStyleComplexItemTest::_testRebuildTransects(void)
// lastSequenceNumberChanged signal // lastSequenceNumberChanged signal
_adjustSurveAreaPolygon(); _adjustSurveAreaPolygon();
QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called); QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called);
QVERIFY(_transectStyleItem->rebuildTransectsPhase2Called); QVERIFY(_transectStyleItem->recalcCameraShotsCalled);
QVERIFY(_transectStyleItem->recalcComplexDistanceCalled);
QVERIFY(_multiSpy->checkSignalsByMask(coveredAreaChangedMask | lastSequenceNumberChangedMask)); QVERIFY(_multiSpy->checkSignalsByMask(coveredAreaChangedMask | lastSequenceNumberChangedMask));
_transectStyleItem->rebuildTransectsPhase1Called = false; _transectStyleItem->rebuildTransectsPhase1Called = false;
_transectStyleItem->rebuildTransectsPhase2Called = false; _transectStyleItem->recalcCameraShotsCalled = false;
_transectStyleItem->recalcComplexDistanceCalled = false;
_transectStyleItem->setDirty(false); _transectStyleItem->setDirty(false);
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
...@@ -136,30 +138,36 @@ void TransectStyleComplexItemTest::_testRebuildTransects(void) ...@@ -136,30 +138,36 @@ void TransectStyleComplexItemTest::_testRebuildTransects(void)
qDebug() << fact->name(); qDebug() << fact->name();
changeFactValue(fact); changeFactValue(fact);
QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called); QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called);
QVERIFY(_transectStyleItem->rebuildTransectsPhase2Called); QVERIFY(_transectStyleItem->recalcCameraShotsCalled);
QVERIFY(_transectStyleItem->recalcComplexDistanceCalled);
QVERIFY(_multiSpy->checkSignalsByMask(lastSequenceNumberChangedMask)); QVERIFY(_multiSpy->checkSignalsByMask(lastSequenceNumberChangedMask));
_transectStyleItem->setDirty(false); _transectStyleItem->setDirty(false);
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
_transectStyleItem->rebuildTransectsPhase1Called = false; _transectStyleItem->rebuildTransectsPhase1Called = false;
_transectStyleItem->rebuildTransectsPhase2Called = false; _transectStyleItem->recalcCameraShotsCalled = false;
_transectStyleItem->recalcComplexDistanceCalled = false;
} }
rgFacts.clear(); rgFacts.clear();
_transectStyleItem->cameraCalc()->valueSetIsDistance()->setRawValue(false); _transectStyleItem->cameraCalc()->valueSetIsDistance()->setRawValue(false);
_transectStyleItem->rebuildTransectsPhase1Called = false; _transectStyleItem->rebuildTransectsPhase1Called = false;
_transectStyleItem->rebuildTransectsPhase2Called = false; _transectStyleItem->recalcCameraShotsCalled = false;
_transectStyleItem->recalcComplexDistanceCalled = false;
changeFactValue(_transectStyleItem->cameraCalc()->imageDensity()); changeFactValue(_transectStyleItem->cameraCalc()->imageDensity());
QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called); QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called);
QVERIFY(_transectStyleItem->rebuildTransectsPhase2Called); QVERIFY(_transectStyleItem->recalcCameraShotsCalled);
QVERIFY(_transectStyleItem->recalcComplexDistanceCalled);
QVERIFY(_multiSpy->checkSignalsByMask(lastSequenceNumberChangedMask)); QVERIFY(_multiSpy->checkSignalsByMask(lastSequenceNumberChangedMask));
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
_transectStyleItem->cameraCalc()->valueSetIsDistance()->setRawValue(true); _transectStyleItem->cameraCalc()->valueSetIsDistance()->setRawValue(true);
_transectStyleItem->rebuildTransectsPhase1Called = false; _transectStyleItem->rebuildTransectsPhase1Called = false;
_transectStyleItem->rebuildTransectsPhase2Called = false; _transectStyleItem->recalcCameraShotsCalled = false;
_transectStyleItem->recalcComplexDistanceCalled = false;
changeFactValue(_transectStyleItem->cameraCalc()->distanceToSurface()); changeFactValue(_transectStyleItem->cameraCalc()->distanceToSurface());
QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called); QVERIFY(_transectStyleItem->rebuildTransectsPhase1Called);
QVERIFY(_transectStyleItem->rebuildTransectsPhase2Called); QVERIFY(_transectStyleItem->recalcCameraShotsCalled);
QVERIFY(_transectStyleItem->recalcComplexDistanceCalled);
QVERIFY(_multiSpy->checkSignalsByMask(lastSequenceNumberChangedMask)); QVERIFY(_multiSpy->checkSignalsByMask(lastSequenceNumberChangedMask));
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
} }
...@@ -219,7 +227,8 @@ void TransectStyleComplexItemTest::_testAltMode(void) ...@@ -219,7 +227,8 @@ void TransectStyleComplexItemTest::_testAltMode(void)
TransectStyleItem::TransectStyleItem(Vehicle* vehicle, QObject* parent) TransectStyleItem::TransectStyleItem(Vehicle* vehicle, QObject* parent)
: TransectStyleComplexItem (vehicle, false /* flyView */, QStringLiteral("UnitTestTransect"), parent) : TransectStyleComplexItem (vehicle, false /* flyView */, QStringLiteral("UnitTestTransect"), parent)
, rebuildTransectsPhase1Called (false) , rebuildTransectsPhase1Called (false)
, rebuildTransectsPhase2Called (false) , recalcComplexDistanceCalled (false)
, recalcCameraShotsCalled (false)
{ {
} }
...@@ -229,7 +238,12 @@ void TransectStyleItem::_rebuildTransectsPhase1(void) ...@@ -229,7 +238,12 @@ void TransectStyleItem::_rebuildTransectsPhase1(void)
rebuildTransectsPhase1Called = true; rebuildTransectsPhase1Called = true;
} }
void TransectStyleItem::_rebuildTransectsPhase2(void) void TransectStyleItem::_recalcComplexDistance(void)
{ {
rebuildTransectsPhase2Called = true; recalcComplexDistanceCalled = true;
}
void TransectStyleItem::_recalcCameraShots(void)
{
recalcCameraShotsCalled = true;
} }
...@@ -97,10 +97,12 @@ public: ...@@ -97,10 +97,12 @@ public:
double additionalTimeDelay (void) const final { return 0; } double additionalTimeDelay (void) const final { return 0; }
bool rebuildTransectsPhase1Called; bool rebuildTransectsPhase1Called;
bool rebuildTransectsPhase2Called; bool recalcComplexDistanceCalled;
bool recalcCameraShotsCalled;
private slots: private slots:
// Overrides from TransectStyleComplexItem // Overrides from TransectStyleComplexItem
void _rebuildTransectsPhase1(void) final; void _rebuildTransectsPhase1 (void) final;
void _rebuildTransectsPhase2(void) final; void _recalcComplexDistance (void) final;
void _recalcCameraShots (void) final;
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment