From 1fde998db35d761d21d165f39dfe747430ddcffd Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Sun, 19 Aug 2018 10:44:16 +0200 Subject: [PATCH 01/15] [Survey transects generation] introduce function to handle one polygon at a time --- src/MissionManager/SurveyComplexItem.cc | 5 +++++ src/MissionManager/SurveyComplexItem.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 4758baf..769d946 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1068,6 +1068,11 @@ void SurveyComplexItem::_rebuildTransectsPhase1(void) void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) { + _rebuildTranscetsFromPolygon(refly); +} + +void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly) +{ if (_ignoreRecalc) { return; } diff --git a/src/MissionManager/SurveyComplexItem.h b/src/MissionManager/SurveyComplexItem.h index 7935b48..2d63a8b 100644 --- a/src/MissionManager/SurveyComplexItem.h +++ b/src/MissionManager/SurveyComplexItem.h @@ -111,6 +111,7 @@ private: bool _loadV3(const QJsonObject& complexObject, int sequenceNumber, QString& errorString); bool _loadV4(const QJsonObject& complexObject, int sequenceNumber, QString& errorString); void _rebuildTransectsPhase1Worker(bool refly); + void _rebuildTranscetsFromPolygon(bool refly); ///< Adds to the _transects array from one polygon QMap _metaDataMap; From 85ad87bbed48950dd1df4a63fe9c3496b17f47db Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Sun, 19 Aug 2018 11:51:43 +0200 Subject: [PATCH 02/15] [Survey transects generation] move some of the logic handling out of function that handles only one polygon --- src/MissionManager/SurveyComplexItem.cc | 24 +++++++++++++----------- src/MissionManager/SurveyComplexItem.h | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 769d946..f083533 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1068,11 +1068,6 @@ void SurveyComplexItem::_rebuildTransectsPhase1(void) void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) { - _rebuildTranscetsFromPolygon(refly); -} - -void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly) -{ if (_ignoreRecalc) { return; } @@ -1112,6 +1107,19 @@ void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly) qCDebug(SurveyComplexItemLog) << "_rebuildTransectsPhase1 vertex:x:y" << vertex << polygonPoints.last().x() << polygonPoints.last().y(); } + // convert into QPolygonF + QPolygonF polygon; + for (int i=0; i _metaDataMap; From 78155168c8ec2edacd35f89a374e19c7ab5ec945 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Sun, 19 Aug 2018 12:05:27 +0200 Subject: [PATCH 03/15] [Survey transects generation] introduce polygon list --- src/MissionManager/SurveyComplexItem.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index f083533..de282c5 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1115,7 +1115,14 @@ void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) } polygon << polygonPoints[0]; - _rebuildTranscetsFromPolygon(refly, polygon, tangentOrigin); + // Create list of separate polygons + QList polygons; + polygons << polygon; + + // iterate over polygons + for (const auto& p : polygons) { + _rebuildTranscetsFromPolygon(refly, p, tangentOrigin); + } } void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin) From fb10d04def0d9fb3a7bd8735ae985930c46b2a54 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Sun, 19 Aug 2018 12:09:06 +0200 Subject: [PATCH 04/15] [Survey transects generation] introduce decompose function (dummy for now) --- src/MissionManager/SurveyComplexItem.cc | 7 ++++++- src/MissionManager/SurveyComplexItem.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index de282c5..8ae260b 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1117,7 +1117,7 @@ void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) // Create list of separate polygons QList polygons; - polygons << polygon; + _PolygonDecomposeConvex(polygon, polygons); // iterate over polygons for (const auto& p : polygons) { @@ -1125,6 +1125,11 @@ void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) } } +void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList& polygons) +{ + polygons << polygon; +} + void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin) { // Generate transects diff --git a/src/MissionManager/SurveyComplexItem.h b/src/MissionManager/SurveyComplexItem.h index 85cf34d..1086a2e 100644 --- a/src/MissionManager/SurveyComplexItem.h +++ b/src/MissionManager/SurveyComplexItem.h @@ -113,6 +113,7 @@ private: void _rebuildTransectsPhase1Worker(bool refly); /// Adds to the _transects array from one polygon void _rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin); + void _PolygonDecomposeConvex(const QPolygonF& polygon, QList& polygons); QMap _metaDataMap; From d03dbb93c6374a4223125d8cafb322c915b0d328 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Sun, 19 Aug 2018 13:54:46 +0200 Subject: [PATCH 05/15] [Survey transects generation] close polygon after decomposition --- src/MissionManager/SurveyComplexItem.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 8ae260b..3ab0367 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1113,14 +1113,17 @@ void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) qCDebug(SurveyComplexItemLog) << "Vertex" << polygonPoints[i]; polygon << polygonPoints[i]; } - polygon << polygonPoints[0]; // Create list of separate polygons QList polygons; _PolygonDecomposeConvex(polygon, polygons); // iterate over polygons - for (const auto& p : polygons) { + for (auto& p : polygons) { + // close polygon + p << p.front(); + // build transects for this polygon + // TODO figure out tangent origin _rebuildTranscetsFromPolygon(refly, p, tangentOrigin); } } From 059578e628fa76d81738efc62c36d414e901c2a6 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Sun, 19 Aug 2018 13:55:30 +0200 Subject: [PATCH 06/15] [Survey transects generation] add basic implementation for handling multiple polygons need to fully implement vertexvisibility --- src/MissionManager/SurveyComplexItem.cc | 85 +++++++++++++++++++++++++++++++-- src/MissionManager/SurveyComplexItem.h | 5 +- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 3ab0367..36e886a 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1115,7 +1115,7 @@ void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) } // Create list of separate polygons - QList polygons; + QList polygons{}; _PolygonDecomposeConvex(polygon, polygons); // iterate over polygons @@ -1124,13 +1124,91 @@ void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) p << p.front(); // build transects for this polygon // TODO figure out tangent origin + qCDebug(SurveyComplexItemLog) << "Transects from polynom p " << p; _rebuildTranscetsFromPolygon(refly, p, tangentOrigin); } } -void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList& polygons) +void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList& decomposedPolygons) { - polygons << polygon; + qCDebug(SurveyComplexItemLog) << "_PolygonDecomposeConvex polygon.size() " << polygon.size(); + if (polygon.size() < 3) return; + + int decompSize = std::numeric_limits::max(); + QList decomposedPolygonsMin{}; + + for (auto vertex = polygon.begin(); vertex != polygon.end(); ++vertex) + { + // is vertex reflex? + auto vertexBefore = vertex == polygon.begin() ? polygon.end() - 1 : vertex - 1; + auto vertexAfter = vertex == polygon.end() -1 ? polygon.begin() : vertex + 1; + auto area = (((vertex->x() - vertexBefore->x())*(vertexAfter->y() - vertexBefore->y()))-((vertexAfter->x() - vertexBefore->x())*(vertex->y() - vertexBefore->y()))); + bool vertexIsReflex = area > 0; + qCDebug(SurveyComplexItemLog) << "area " << area << " vertexIsReflex " << vertexIsReflex; + + if (!vertexIsReflex) continue; + + for (auto vertexOther = polygon.begin(); vertexOther != polygon.end(); ++vertexOther) + { + if (vertexOther == vertex) continue; + bool canSee = _VertexCanSeeOther(polygon, vertex, vertexOther); + if (!canSee) continue; + + QPolygonF polyLeft; + auto v = vertex; + while ( v != vertexOther) { + polyLeft << *v; + ++v; + if (v == polygon.end()) v = polygon.begin(); + } + polyLeft << *vertexOther; + qCDebug(SurveyComplexItemLog) << "polyLeft.size() " << polyLeft.size(); + + QPolygonF polyRight; + v = vertexOther; + while ( v != vertex) { + polyRight << *v; + ++v; + if (v == polygon.end()) v = polygon.begin(); + } + polyRight << *vertex; + qCDebug(SurveyComplexItemLog) << "polyRight.size() " << polyRight.size(); + + // recursion + QList polyLeftDecomposed{}; + _PolygonDecomposeConvex(polyLeft, polyLeftDecomposed); + QList polyRightDecomposed{}; + _PolygonDecomposeConvex(polyRight, polyRightDecomposed); + + // compositon + if (polyLeftDecomposed.size() + polyRightDecomposed.size() < decompSize) { + decompSize = polyLeftDecomposed.size() + polyRightDecomposed.size(); + decomposedPolygonsMin = polyLeftDecomposed + polyRightDecomposed; + qCDebug(SurveyComplexItemLog) << "changing decomposedPolygonsMin"; + } + else { + qCDebug(SurveyComplexItemLog) << "NOT changing decomposedPolygonsMin"; + } + } + + } + + // assemble output + if (decomposedPolygonsMin.size() > 0) { + qCDebug(SurveyComplexItemLog) << "use decomposed polygon, decomposedPolygonsMin.size() " << decomposedPolygonsMin.size(); + decomposedPolygons << decomposedPolygonsMin; + } else { + qCDebug(SurveyComplexItemLog) << "use default polygon"; + decomposedPolygons << polygon; + } +} + +bool SurveyComplexItem::_VertexCanSeeOther(const QPolygonF& polygon, const QPointF* VertexA, const QPointF* VertexB) { + if (VertexA == VertexB) return false; + if (VertexA + 1 == VertexB) return false; + if (VertexA - 1 == VertexB) return false; + + return true; } void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin) @@ -1301,6 +1379,7 @@ void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF _transects.append(coordInfoTransect); } + qCDebug(SurveyComplexItemLog) << "_transects.size() " << _transects.size(); } void SurveyComplexItem::_rebuildTransectsPhase2(void) diff --git a/src/MissionManager/SurveyComplexItem.h b/src/MissionManager/SurveyComplexItem.h index 1086a2e..f1541fc 100644 --- a/src/MissionManager/SurveyComplexItem.h +++ b/src/MissionManager/SurveyComplexItem.h @@ -113,7 +113,10 @@ private: void _rebuildTransectsPhase1Worker(bool refly); /// Adds to the _transects array from one polygon void _rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin); - void _PolygonDecomposeConvex(const QPolygonF& polygon, QList& polygons); + // Decompose polygon into list of convex sub polygons + void _PolygonDecomposeConvex(const QPolygonF& polygon, QList& decomposedPolygons); + // return true if vertex a can see vertex b + bool _VertexCanSeeOther(const QPolygonF& polygon, const QPointF* VertexA, const QPointF* VertexB); QMap _metaDataMap; From 9ae5cc7e15b4c474939878cdc709f791ad5c6792 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Mon, 20 Aug 2018 07:45:59 +0200 Subject: [PATCH 07/15] [Survey transects generation] towards concave polygons --- src/MissionManager/SurveyComplexItem.cc | 165 +++++++++++++++++++++++++++----- src/MissionManager/SurveyComplexItem.h | 5 +- 2 files changed, 142 insertions(+), 28 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 36e886a..f65ef90 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -20,6 +20,7 @@ #include QGC_LOGGING_CATEGORY(SurveyComplexItemLog, "SurveyComplexItemLog") +QGC_LOGGING_CATEGORY(PolygonDecomposeLog, "PolygonDecomposeLog") const char* SurveyComplexItem::jsonComplexItemTypeValue = "survey"; const char* SurveyComplexItem::jsonV3ComplexItemTypeValue = "survey"; @@ -1116,78 +1117,132 @@ void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) // Create list of separate polygons QList polygons{}; + qCDebug(PolygonDecomposeLog) << "*********_PolygonDecomposeConvex begin of recursion**************"; _PolygonDecomposeConvex(polygon, polygons); + qCDebug(PolygonDecomposeLog) << "polygons.size() " << polygons.size() ; // iterate over polygons - for (auto& p : polygons) { + for (auto p = polygons.begin(); p != polygons.end(); ++p) { + QPointF* vMatch = nullptr; + // find matching vertex in previous polygon + if (p != polygons.begin()) { + auto pLast = p - 1; + for (auto& i : *p) { + for (auto& j : *pLast) { + if (i == j) { + vMatch = &i; + break; + } + if (vMatch) break; + } + } + if (nullptr == vMatch) qCDebug(PolygonDecomposeLog) << "no match found"; + + } + + // close polygon - p << p.front(); + *p << p->front(); // build transects for this polygon // TODO figure out tangent origin - qCDebug(SurveyComplexItemLog) << "Transects from polynom p " << p; - _rebuildTranscetsFromPolygon(refly, p, tangentOrigin); + // TODO improve selection of entry points +// qCDebug(SurveyComplexItemLog) << "Transects from polynom p " << p; + _rebuildTranscetsFromPolygon(refly, *p, tangentOrigin, vMatch); } } void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList& decomposedPolygons) { - qCDebug(SurveyComplexItemLog) << "_PolygonDecomposeConvex polygon.size() " << polygon.size(); +// qCDebug(SurveyComplexItemLog) << "_PolygonDecomposeConvex polygon.size() " << polygon.size(); + int decompSize = std::numeric_limits::max(); if (polygon.size() < 3) return; + if (polygon.size() == 3) { + decomposedPolygons << polygon; +// qCDebug(PolygonDecomposeLog) << polygon << " polygon of 3"; + return; + } - int decompSize = std::numeric_limits::max(); QList decomposedPolygonsMin{}; for (auto vertex = polygon.begin(); vertex != polygon.end(); ++vertex) { // is vertex reflex? - auto vertexBefore = vertex == polygon.begin() ? polygon.end() - 1 : vertex - 1; - auto vertexAfter = vertex == polygon.end() -1 ? polygon.begin() : vertex + 1; - auto area = (((vertex->x() - vertexBefore->x())*(vertexAfter->y() - vertexBefore->y()))-((vertexAfter->x() - vertexBefore->x())*(vertex->y() - vertexBefore->y()))); - bool vertexIsReflex = area > 0; - qCDebug(SurveyComplexItemLog) << "area " << area << " vertexIsReflex " << vertexIsReflex; + bool vertexIsReflex = _VertexIsReflex(polygon, vertex); +// qCDebug(SurveyComplexItemLog) << "area " << area << " vertexIsReflex " << vertexIsReflex; if (!vertexIsReflex) continue; for (auto vertexOther = polygon.begin(); vertexOther != polygon.end(); ++vertexOther) { + auto vertexBefore = vertex == polygon.begin() ? polygon.end() - 1 : vertex - 1; + auto vertexAfter = vertex == polygon.end() - 1 ? polygon.begin() : vertex + 1; if (vertexOther == vertex) continue; + if (vertexAfter == vertexOther) continue; + if (vertexBefore == vertexOther) continue; bool canSee = _VertexCanSeeOther(polygon, vertex, vertexOther); +// qCDebug(SurveyComplexItemLog) << "canSee " << canSee; if (!canSee) continue; QPolygonF polyLeft; auto v = vertex; + auto polyLeftContainsReflex = false; while ( v != vertexOther) { + if (v != vertex && _VertexIsReflex(polygon, v)) { + polyLeftContainsReflex = true; + } polyLeft << *v; ++v; if (v == polygon.end()) v = polygon.begin(); } polyLeft << *vertexOther; - qCDebug(SurveyComplexItemLog) << "polyLeft.size() " << polyLeft.size(); + auto polyLeftValid = !(polyLeftContainsReflex && polyLeft.size() == 3); QPolygonF polyRight; v = vertexOther; + auto polyRightContainsReflex = false; while ( v != vertex) { + if (v != vertex && _VertexIsReflex(polygon, v)) { + polyRightContainsReflex = true; + } polyRight << *v; ++v; if (v == polygon.end()) v = polygon.begin(); } polyRight << *vertex; - qCDebug(SurveyComplexItemLog) << "polyRight.size() " << polyRight.size(); + auto polyRightValid = !(polyRightContainsReflex && polyRight.size() == 3); + + if (!polyLeftValid || ! polyRightValid) { +// decompSize = std::numeric_limits::max(); + continue; + } // recursion QList polyLeftDecomposed{}; +// qCDebug(PolygonDecomposeLog) << " polyLeft "<< polyLeft; _PolygonDecomposeConvex(polyLeft, polyLeftDecomposed); + QList polyRightDecomposed{}; +// qCDebug(PolygonDecomposeLog) << " polyRight "<< polyRight; _PolygonDecomposeConvex(polyRight, polyRightDecomposed); // compositon - if (polyLeftDecomposed.size() + polyRightDecomposed.size() < decompSize) { - decompSize = polyLeftDecomposed.size() + polyRightDecomposed.size(); + auto subSize = polyLeftDecomposed.size() + polyRightDecomposed.size(); + if ((polyLeftContainsReflex && polyLeftDecomposed.size() == 1) + || (polyRightContainsReflex && polyRightDecomposed.size() == 1)) + { + // don't accept polygons that contian reflex vertices and were not split + subSize = std::numeric_limits::max(); + } + if (subSize < decompSize) { + decompSize = subSize; decomposedPolygonsMin = polyLeftDecomposed + polyRightDecomposed; - qCDebug(SurveyComplexItemLog) << "changing decomposedPolygonsMin"; +// qCDebug(PolygonDecomposeLog) << "_PolygonDecomposeConvex polygon " << polygon; +// qCDebug(PolygonDecomposeLog) << "polyLeft.size() " << polyLeft.size() << " polyRight.size() " << polyRight.size() << " out of " << polygon.size(); +// qCDebug(PolygonDecomposeLog) << "vertex " << *vertex << " vertexOther " << *vertexOther << " vertexAfter " << *vertexAfter << " vertexBefore " << *vertexBefore; +// qCDebug(SurveyComplexItemLog) << "changing decomposedPolygonsMin"; } else { - qCDebug(SurveyComplexItemLog) << "NOT changing decomposedPolygonsMin"; +// qCDebug(SurveyComplexItemLog) << "NOT changing decomposedPolygonsMin"; } } @@ -1195,23 +1250,71 @@ void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList< // assemble output if (decomposedPolygonsMin.size() > 0) { - qCDebug(SurveyComplexItemLog) << "use decomposed polygon, decomposedPolygonsMin.size() " << decomposedPolygonsMin.size(); +// qCDebug(SurveyComplexItemLog) << "use decomposed polygon, decomposedPolygonsMin.size() " << decomposedPolygonsMin.size(); decomposedPolygons << decomposedPolygonsMin; +// qCDebug(PolygonDecomposeLog) << decomposedPolygonsMin; } else { - qCDebug(SurveyComplexItemLog) << "use default polygon"; +// qCDebug(SurveyComplexItemLog) << "use default polygon"; decomposedPolygons << polygon; +// qCDebug(PolygonDecomposeLog) << polygon << " empty polygon"; } + + return; } -bool SurveyComplexItem::_VertexCanSeeOther(const QPolygonF& polygon, const QPointF* VertexA, const QPointF* VertexB) { - if (VertexA == VertexB) return false; - if (VertexA + 1 == VertexB) return false; - if (VertexA - 1 == VertexB) return false; +bool SurveyComplexItem::_VertexCanSeeOther(const QPolygonF& polygon, const QPointF* vertexA, const QPointF* vertexB) { + if (vertexA == vertexB) return false; + auto vertexAAfter = vertexA + 1 == polygon.end() ? polygon.begin() : vertexA + 1; + auto vertexABefore = vertexA == polygon.begin() ? polygon.end() - 1 : vertexA - 1; + if (vertexAAfter == vertexB) return false; + if (vertexABefore == vertexB) return false; +// qCDebug(SurveyComplexItemLog) << "_VertexCanSeeOther false after first checks "; + + bool visible = true; +// auto diff = *vertexA - *vertexB; + QLineF lineAB{*vertexA, *vertexB}; + auto distanceAB = lineAB.length();//sqrtf(diff.x() * diff.x() + diff.y()*diff.y()); + +// qCDebug(SurveyComplexItemLog) << "_VertexCanSeeOther distanceAB " << distanceAB; + for (auto vertexC = polygon.begin(); vertexC != polygon.end(); ++vertexC) + { + if (vertexC == vertexA) continue; + if (vertexC == vertexB) continue; + auto vertexD = vertexC + 1 == polygon.end() ? polygon.begin() : vertexC + 1; + if (vertexD == vertexA) continue; + if (vertexD == vertexB) continue; + QLineF lineCD(*vertexC, *vertexD); + QPointF intersection{}; + auto intersects = lineAB.intersect(lineCD, &intersection); + if (intersects == QLineF::IntersectType::BoundedIntersection) { +// auto diffIntersection = *vertexA - intersection; +// auto distanceIntersection = sqrtf(diffIntersection.x() * diffIntersection.x() + diffIntersection.y()*diffIntersection.y()); +// qCDebug(SurveyComplexItemLog) << "*vertexA " << *vertexA << "*vertexB " << *vertexB << " intersection " << intersection; + + QLineF lineIntersection{*vertexA, intersection}; + auto distanceIntersection = lineIntersection.length();//sqrtf(diff.x() * diff.x() + diff.y()*diff.y()); + qCDebug(SurveyComplexItemLog) << "_VertexCanSeeOther distanceIntersection " << distanceIntersection; + if (distanceIntersection < distanceAB) { + visible = false; + break; + } + } - return true; + } + + return visible; } -void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin) +bool SurveyComplexItem::_VertexIsReflex(const QPolygonF& polygon, const QPointF* vertex) { + auto vertexBefore = vertex == polygon.begin() ? polygon.end() - 1 : vertex - 1; + auto vertexAfter = vertex == polygon.end() - 1 ? polygon.begin() : vertex + 1; + auto area = (((vertex->x() - vertexBefore->x())*(vertexAfter->y() - vertexBefore->y()))-((vertexAfter->x() - vertexBefore->x())*(vertex->y() - vertexBefore->y()))); + return area > 0; + +} + + +void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin, const QPointF* const transitionPoint) { // Generate transects @@ -1282,9 +1385,19 @@ void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF // Convert from NED to Geo QList> transects; - for (const QLineF& line: resultLines) { + + if (transitionPoint != nullptr) { + QList transect; QGeoCoordinate coord; + convertNedToGeo(transitionPoint->y(), transitionPoint->x(), 0, tangentOrigin, &coord); + transect.append(coord); + transect.append(coord); //TODO + transects.append(transect); + } + + for (const QLineF& line: resultLines) { QList transect; + QGeoCoordinate coord; convertNedToGeo(line.p1().y(), line.p1().x(), 0, tangentOrigin, &coord); transect.append(coord); diff --git a/src/MissionManager/SurveyComplexItem.h b/src/MissionManager/SurveyComplexItem.h index f1541fc..0173226 100644 --- a/src/MissionManager/SurveyComplexItem.h +++ b/src/MissionManager/SurveyComplexItem.h @@ -112,11 +112,12 @@ private: bool _loadV4(const QJsonObject& complexObject, int sequenceNumber, QString& errorString); void _rebuildTransectsPhase1Worker(bool refly); /// Adds to the _transects array from one polygon - void _rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin); + void _rebuildTranscetsFromPolygon(bool refly, const QPolygonF& polygon, const QGeoCoordinate& tangentOrigin, const QPointF* const transitionPoint); // Decompose polygon into list of convex sub polygons void _PolygonDecomposeConvex(const QPolygonF& polygon, QList& decomposedPolygons); // return true if vertex a can see vertex b - bool _VertexCanSeeOther(const QPolygonF& polygon, const QPointF* VertexA, const QPointF* VertexB); + bool _VertexCanSeeOther(const QPolygonF& polygon, const QPointF* vertexA, const QPointF* vertexB); + bool _VertexIsReflex(const QPolygonF& polygon, const QPointF* vertex); QMap _metaDataMap; From 6f2a99531a0d41c40a6cb7fed8b1ae1761bd6447 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 1 Oct 2018 09:18:34 -0400 Subject: [PATCH 08/15] CP - Add setting to toggle splitting concave polygon on/off --- src/PlanView/SurveyItemEditor.qml | 8 ++++++++ src/Settings/App.SettingsGroup.json | 7 +++++++ src/Settings/AppSettings.cc | 11 +++++++++++ src/Settings/AppSettings.h | 8 ++++++-- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/PlanView/SurveyItemEditor.qml b/src/PlanView/SurveyItemEditor.qml index 0b22b12..d0072b0 100644 --- a/src/PlanView/SurveyItemEditor.qml +++ b/src/PlanView/SurveyItemEditor.qml @@ -9,6 +9,7 @@ import QGroundControl 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Vehicle 1.0 import QGroundControl.Controls 1.0 +import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 import QGroundControl.FlightMap 1.0 @@ -120,6 +121,13 @@ Rectangle { } FactCheckBox { + text: qsTr("Split concave polygons") + fact: _splitConcave + visible: _splitConcave.visible + property Fact _splitConcave: QGroundControl.settingsManager.appSettings.splitConcavePolygons + } + + FactCheckBox { text: qsTr("Hover and capture image") fact: missionItem.hoverAndCapture visible: missionItem.hoverAndCaptureAllowed diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index d8fc697..f083d93 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -199,5 +199,12 @@ "enumStrings": "Never,Always,When in Follow Me Flight Mode", "enumValues": "0,1,2", "defaultValue": 0 +}, +{ + "name": "SplitConcavePolygons", + "shortDescription": "Split mission concave polygons", + "longDescription": "Split mission concave polygons into separate regular, convex polygons", + "type": "bool", + "defaultValue": false } ] diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index b9f341c..af485f5 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -41,6 +41,7 @@ const char* AppSettings::esriTokenName = "EsriTok const char* AppSettings::defaultFirmwareTypeName = "DefaultFirmwareType"; const char* AppSettings::gstDebugName = "GstreamerDebugLevel"; const char* AppSettings::followTargetName = "FollowTarget"; +const char* AppSettings::splitConcavePolygonsName = "SplitConcavePolygons"; const char* AppSettings::parameterFileExtension = "params"; const char* AppSettings::planFileExtension = "plan"; @@ -84,6 +85,7 @@ AppSettings::AppSettings(QObject* parent) , _defaultFirmwareTypeFact (NULL) , _gstDebugFact (NULL) , _followTargetFact (NULL) + , _splitConcavePolygonsFact (NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); @@ -428,3 +430,12 @@ Fact* AppSettings::followTarget(void) return _followTargetFact; } +Fact* AppSettings::splitConcavePolygons(void) +{ + if (!_splitConcavePolygonsFact) { + _splitConcavePolygonsFact = _createSettingsFact(splitConcavePolygonsName); + } + + return _splitConcavePolygonsFact; +} + diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index 327ce30..b12bdf5 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -43,6 +43,7 @@ public: Q_PROPERTY(Fact* defaultFirmwareType READ defaultFirmwareType CONSTANT) Q_PROPERTY(Fact* gstDebug READ gstDebug CONSTANT) Q_PROPERTY(Fact* followTarget READ followTarget CONSTANT) + Q_PROPERTY(Fact* splitConcavePolygons READ splitConcavePolygons CONSTANT) Q_PROPERTY(QString missionSavePath READ missionSavePath NOTIFY savePathsChanged) Q_PROPERTY(QString parameterSavePath READ parameterSavePath NOTIFY savePathsChanged) @@ -82,13 +83,14 @@ public: Fact* defaultFirmwareType (void); Fact* gstDebug (void); Fact* followTarget (void); + Fact* splitConcavePolygons (void); QString missionSavePath (void); QString parameterSavePath (void); QString telemetrySavePath (void); QString logSavePath (void); - QString videoSavePath (void); - QString crashSavePath (void); + QString videoSavePath (void); + QString crashSavePath (void); static MAV_AUTOPILOT offlineEditingFirmwareTypeFromFirmwareType(MAV_AUTOPILOT firmwareType); static MAV_TYPE offlineEditingVehicleTypeFromVehicleType(MAV_TYPE vehicleType); @@ -119,6 +121,7 @@ public: static const char* defaultFirmwareTypeName; static const char* gstDebugName; static const char* followTargetName; + static const char* splitConcavePolygonsName; // Application wide file extensions static const char* parameterFileExtension; @@ -170,6 +173,7 @@ private: SettingsFact* _defaultFirmwareTypeFact; SettingsFact* _gstDebugFact; SettingsFact* _followTargetFact; + SettingsFact* _splitConcavePolygonsFact; }; #endif From 130e12cf3030e012b97a8ea7b0b14baabe17f8f0 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Mon, 1 Oct 2018 19:14:22 +0200 Subject: [PATCH 09/15] [SurveyComplexItem] wire up to splitConcavePolygons setting (lots of code duplication) --- src/MissionManager/SurveyComplexItem.cc | 231 +++++++++++++++++++++++++++++++- src/MissionManager/SurveyComplexItem.h | 2 + 2 files changed, 230 insertions(+), 3 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index f65ef90..5b0f859 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1061,13 +1061,238 @@ bool SurveyComplexItem::_hoverAndCaptureEnabled(void) const void SurveyComplexItem::_rebuildTransectsPhase1(void) { - _rebuildTransectsPhase1Worker(false /* refly */); + bool split = qgcApp()->toolbox()->settingsManager()->appSettings()->splitConcavePolygons()->rawValue().toBool(); + if (split) { + _rebuildTransectsPhase1WorkerSplitPolygons(false /* refly */); + } else { + _rebuildTransectsPhase1WorkerSinglePolygon(false /* refly */); + } if (_refly90DegreesFact.rawValue().toBool()) { - _rebuildTransectsPhase1Worker(true /* refly */); + if (split) { + _rebuildTransectsPhase1WorkerSplitPolygons(true /* refly */); + } else { + _rebuildTransectsPhase1WorkerSinglePolygon(true /* refly */); + } } } -void SurveyComplexItem::_rebuildTransectsPhase1Worker(bool refly) +void SurveyComplexItem::_rebuildTransectsPhase1WorkerSinglePolygon(bool refly) +{ + if (_ignoreRecalc) { + return; + } + + // If the transects are getting rebuilt then any previously loaded mission items are now invalid + if (_loadedMissionItemsParent) { + _loadedMissionItems.clear(); + _loadedMissionItemsParent->deleteLater(); + _loadedMissionItemsParent = NULL; + } + + // First pass will clear old transect data, refly will append to existing data + if (!refly) { + _transects.clear(); + _transectsPathHeightInfo.clear(); + } + + if (_surveyAreaPolygon.count() < 3) { + return; + } + + // Convert polygon to NED + + QList polygonPoints; + QGeoCoordinate tangentOrigin = _surveyAreaPolygon.pathModel().value(0)->coordinate(); + qCDebug(SurveyComplexItemLog) << "_rebuildTransectsPhase1 Convert polygon to NED - _surveyAreaPolygon.count():tangentOrigin" << _surveyAreaPolygon.count() << tangentOrigin; + for (int i=0; i<_surveyAreaPolygon.count(); i++) { + double y, x, down; + QGeoCoordinate vertex = _surveyAreaPolygon.pathModel().value(i)->coordinate(); + if (i == 0) { + // This avoids a nan calculation that comes out of convertGeoToNed + x = y = 0; + } else { + convertGeoToNed(vertex, tangentOrigin, &y, &x, &down); + } + polygonPoints += QPointF(x, y); + qCDebug(SurveyComplexItemLog) << "_rebuildTransectsPhase1 vertex:x:y" << vertex << polygonPoints.last().x() << polygonPoints.last().y(); + } + + // Generate transects + + double gridAngle = _gridAngleFact.rawValue().toDouble(); + double gridSpacing = _cameraCalc.adjustedFootprintSide()->rawValue().toDouble(); + + gridAngle = _clampGridAngle90(gridAngle); + gridAngle += refly ? 90 : 0; + qCDebug(SurveyComplexItemLog) << "_rebuildTransectsPhase1 Clamped grid angle" << gridAngle; + + qCDebug(SurveyComplexItemLog) << "_rebuildTransectsPhase1 gridSpacing:gridAngle:refly" << gridSpacing << gridAngle << refly; + + // Convert polygon to bounding rect + + qCDebug(SurveyComplexItemLog) << "_rebuildTransectsPhase1 Polygon"; + QPolygonF polygon; + for (int i=0; i lineList; + + // Transects are generated to be as long as the largest width/height of the bounding rect plus some fudge factor. + // This way they will always be guaranteed to intersect with a polygon edge no matter what angle they are rotated to. + // They are initially generated with the transects flowing from west to east and then points within the transect north to south. + double maxWidth = qMax(boundingRect.width(), boundingRect.height()) + 2000.0; + double halfWidth = maxWidth / 2.0; + double transectX = boundingCenter.x() - halfWidth; + double transectXMax = transectX + maxWidth; + while (transectX < transectXMax) { + double transectYTop = boundingCenter.y() - halfWidth; + double transectYBottom = boundingCenter.y() + halfWidth; + + lineList += QLineF(_rotatePoint(QPointF(transectX, transectYTop), boundingCenter, gridAngle), _rotatePoint(QPointF(transectX, transectYBottom), boundingCenter, gridAngle)); + transectX += gridSpacing; + } + + // Now intersect the lines with the polygon + QList intersectLines; +#if 1 + _intersectLinesWithPolygon(lineList, polygon, intersectLines); +#else + // This is handy for debugging grid problems, not for release + intersectLines = lineList; +#endif + + // Less than two transects intersected with the polygon: + // Create a single transect which goes through the center of the polygon + // Intersect it with the polygon + if (intersectLines.count() < 2) { + _surveyAreaPolygon.center(); + QLineF firstLine = lineList.first(); + QPointF lineCenter = firstLine.pointAt(0.5); + QPointF centerOffset = boundingCenter - lineCenter; + firstLine.translate(centerOffset); + lineList.clear(); + lineList.append(firstLine); + intersectLines = lineList; + _intersectLinesWithPolygon(lineList, polygon, intersectLines); + } + + // Make sure all lines are going the same direction. Polygon intersection leads to lines which + // can be in varied directions depending on the order of the intesecting sides. + QList resultLines; + _adjustLineDirection(intersectLines, resultLines); + + // Convert from NED to Geo + QList> transects; + foreach (const QLineF& line, resultLines) { + QGeoCoordinate coord; + QList transect; + + convertNedToGeo(line.p1().y(), line.p1().x(), 0, tangentOrigin, &coord); + transect.append(coord); + convertNedToGeo(line.p2().y(), line.p2().x(), 0, tangentOrigin, &coord); + transect.append(coord); + + transects.append(transect); + } + + _adjustTransectsToEntryPointLocation(transects); + + if (refly) { + _optimizeTransectsForShortestDistance(_transects.last().last().coord, transects); + } + + if (_flyAlternateTransectsFact.rawValue().toBool()) { + QList> alternatingTransects; + for (int i=0; i0; i--) { + if (i & 1) { + alternatingTransects.append(transects[i]); + } + } + transects = alternatingTransects; + } + + // Adjust to lawnmower pattern + bool reverseVertices = false; + for (int i=0; i transectVertices = transects[i]; + if (reverseVertices) { + reverseVertices = false; + QList reversedVertices; + for (int j=transectVertices.count()-1; j>=0; j--) { + reversedVertices.append(transectVertices[j]); + } + transectVertices = reversedVertices; + } else { + reverseVertices = true; + } + transects[i] = transectVertices; + } + + // Convert to CoordInfo transects and append to _transects + foreach (const QList& transect, transects) { + QGeoCoordinate coord; + QList coordInfoTransect; + TransectStyleComplexItem::CoordInfo_t coordInfo; + + coordInfo = { transect[0], CoordTypeSurveyEdge }; + coordInfoTransect.append(coordInfo); + coordInfo = { transect[1], CoordTypeSurveyEdge }; + coordInfoTransect.append(coordInfo); + + // For hover and capture we need points for each camera location within the transect + if (triggerCamera() && hoverAndCaptureEnabled()) { + double transectLength = transect[0].distanceTo(transect[1]); + double transectAzimuth = transect[0].azimuthTo(transect[1]); + if (triggerDistance() < transectLength) { + int cInnerHoverPoints = floor(transectLength / triggerDistance()); + qCDebug(SurveyComplexItemLog) << "cInnerHoverPoints" << cInnerHoverPoints; + for (int i=0; i Date: Thu, 4 Oct 2018 12:33:03 -0400 Subject: [PATCH 10/15] Update survey when split polygon option is toggled. --- src/MissionManager/SurveyComplexItem.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 5b0f859..91d1c42 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -89,6 +89,8 @@ SurveyComplexItem::SurveyComplexItem(Vehicle* vehicle, bool flyView, const QStri connect(&_flyAlternateTransectsFact,&Fact::valueChanged, this, &SurveyComplexItem::_rebuildTransects); connect(this, &SurveyComplexItem::refly90DegreesChanged, this, &SurveyComplexItem::_rebuildTransects); + connect(qgcApp()->toolbox()->settingsManager()->appSettings()->splitConcavePolygons(), &Fact::valueChanged, this, &SurveyComplexItem::_rebuildTransects); + // FIXME: Shouldn't these be in TransectStyleComplexItem? They are also in CorridorScanComplexItem constructur connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::coordinateHasRelativeAltitudeChanged); connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::exitCoordinateHasRelativeAltitudeChanged); From 1650af8585f8a2e577321e78372847239efde998 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Mon, 29 Oct 2018 08:22:55 +0100 Subject: [PATCH 11/15] [SurveyComplexItem] Get rid of foreach --- src/MissionManager/SurveyComplexItem.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 91d1c42..7d111ac 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1194,7 +1194,7 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSinglePolygon(bool refly) // Convert from NED to Geo QList> transects; - foreach (const QLineF& line, resultLines) { + for (const QLineF& line : resultLines) { QGeoCoordinate coord; QList transect; @@ -1246,7 +1246,7 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSinglePolygon(bool refly) } // Convert to CoordInfo transects and append to _transects - foreach (const QList& transect, transects) { + for (const QList& transect : transects) { QGeoCoordinate coord; QList coordInfoTransect; TransectStyleComplexItem::CoordInfo_t coordInfo; From 336a387c37ac6cc40985d0df7eeb9788b62878ba Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Tue, 30 Oct 2018 13:16:59 +0100 Subject: [PATCH 12/15] update changelog --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index d387577..5b93756 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -13,6 +13,7 @@ Note: This file only contains high level features or important fixes. * Make Heading to Home available for display from instrument panel. * Edit Position dialog available on polygon vertices. * Fixed Wing Landing Pattern: Add stop photo/video support. Defaults to on such that doing an RTL will stop camera. +* Survey Planning: add mode that supports concave polygons ## 3.4 From bec854d02820f549fb91fa907deb9079eaacec33 Mon Sep 17 00:00:00 2001 From: Thomas Gubler Date: Wed, 7 Nov 2018 08:28:29 +0100 Subject: [PATCH 13/15] [SurveyComplexItem] add link to algorithm description, remove debug code --- src/MissionManager/SurveyComplexItem.cc | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 7d111ac..8f4cc91 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -20,7 +20,6 @@ #include QGC_LOGGING_CATEGORY(SurveyComplexItemLog, "SurveyComplexItemLog") -QGC_LOGGING_CATEGORY(PolygonDecomposeLog, "PolygonDecomposeLog") const char* SurveyComplexItem::jsonComplexItemTypeValue = "survey"; const char* SurveyComplexItem::jsonV3ComplexItemTypeValue = "survey"; @@ -1344,9 +1343,7 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSplitPolygons(bool refly) // Create list of separate polygons QList polygons{}; - qCDebug(PolygonDecomposeLog) << "*********_PolygonDecomposeConvex begin of recursion**************"; _PolygonDecomposeConvex(polygon, polygons); - qCDebug(PolygonDecomposeLog) << "polygons.size() " << polygons.size() ; // iterate over polygons for (auto p = polygons.begin(); p != polygons.end(); ++p) { @@ -1363,7 +1360,6 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSplitPolygons(bool refly) if (vMatch) break; } } - if (nullptr == vMatch) qCDebug(PolygonDecomposeLog) << "no match found"; } @@ -1380,12 +1376,11 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSplitPolygons(bool refly) void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList& decomposedPolygons) { -// qCDebug(SurveyComplexItemLog) << "_PolygonDecomposeConvex polygon.size() " << polygon.size(); + // this follows "Mark Keil's Algorithm" https://mpen.ca/406/keil int decompSize = std::numeric_limits::max(); if (polygon.size() < 3) return; if (polygon.size() == 3) { decomposedPolygons << polygon; -// qCDebug(PolygonDecomposeLog) << polygon << " polygon of 3"; return; } @@ -1395,7 +1390,6 @@ void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList< { // is vertex reflex? bool vertexIsReflex = _VertexIsReflex(polygon, vertex); -// qCDebug(SurveyComplexItemLog) << "area " << area << " vertexIsReflex " << vertexIsReflex; if (!vertexIsReflex) continue; @@ -1407,7 +1401,6 @@ void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList< if (vertexAfter == vertexOther) continue; if (vertexBefore == vertexOther) continue; bool canSee = _VertexCanSeeOther(polygon, vertex, vertexOther); -// qCDebug(SurveyComplexItemLog) << "canSee " << canSee; if (!canSee) continue; QPolygonF polyLeft; @@ -1445,11 +1438,9 @@ void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList< // recursion QList polyLeftDecomposed{}; -// qCDebug(PolygonDecomposeLog) << " polyLeft "<< polyLeft; _PolygonDecomposeConvex(polyLeft, polyLeftDecomposed); QList polyRightDecomposed{}; -// qCDebug(PolygonDecomposeLog) << " polyRight "<< polyRight; _PolygonDecomposeConvex(polyRight, polyRightDecomposed); // compositon @@ -1463,13 +1454,6 @@ void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList< if (subSize < decompSize) { decompSize = subSize; decomposedPolygonsMin = polyLeftDecomposed + polyRightDecomposed; -// qCDebug(PolygonDecomposeLog) << "_PolygonDecomposeConvex polygon " << polygon; -// qCDebug(PolygonDecomposeLog) << "polyLeft.size() " << polyLeft.size() << " polyRight.size() " << polyRight.size() << " out of " << polygon.size(); -// qCDebug(PolygonDecomposeLog) << "vertex " << *vertex << " vertexOther " << *vertexOther << " vertexAfter " << *vertexAfter << " vertexBefore " << *vertexBefore; -// qCDebug(SurveyComplexItemLog) << "changing decomposedPolygonsMin"; - } - else { -// qCDebug(SurveyComplexItemLog) << "NOT changing decomposedPolygonsMin"; } } @@ -1477,13 +1461,9 @@ void SurveyComplexItem::_PolygonDecomposeConvex(const QPolygonF& polygon, QList< // assemble output if (decomposedPolygonsMin.size() > 0) { -// qCDebug(SurveyComplexItemLog) << "use decomposed polygon, decomposedPolygonsMin.size() " << decomposedPolygonsMin.size(); decomposedPolygons << decomposedPolygonsMin; -// qCDebug(PolygonDecomposeLog) << decomposedPolygonsMin; } else { -// qCDebug(SurveyComplexItemLog) << "use default polygon"; decomposedPolygons << polygon; -// qCDebug(PolygonDecomposeLog) << polygon << " empty polygon"; } return; From 8144d1f294d1329997daaf1aebb8f335cd45e8d9 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 8 Nov 2018 12:04:14 -0500 Subject: [PATCH 14/15] Move split survey polygons from app settings to survey settings. --- src/MissionManager/Survey.SettingsGroup.json | 6 +++ src/MissionManager/SurveyComplexItem.cc | 20 ++++++---- src/MissionManager/SurveyComplexItem.h | 5 +++ src/PlanView/SurveyItemEditor.qml | 2 +- src/Settings/App.SettingsGroup.json | 7 ---- src/Settings/AppSettings.cc | 58 +++++++++++----------------- src/Settings/AppSettings.h | 6 +-- 7 files changed, 49 insertions(+), 55 deletions(-) diff --git a/src/MissionManager/Survey.SettingsGroup.json b/src/MissionManager/Survey.SettingsGroup.json index 2690fdd..2f61020 100644 --- a/src/MissionManager/Survey.SettingsGroup.json +++ b/src/MissionManager/Survey.SettingsGroup.json @@ -14,5 +14,11 @@ "shortDescription": "Fly every other transect in each pass.", "type": "bool", "defaultValue": false +}, +{ + "name": "SplitConcavePolygons", + "shortDescription": "Split mission concave polygons into separate regular, convex polygons.", + "type": "bool", + "defaultValue": true } ] diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 8f4cc91..07bc974 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -28,6 +28,7 @@ const char* SurveyComplexItem::settingsGroup = "Survey"; const char* SurveyComplexItem::gridAngleName = "GridAngle"; const char* SurveyComplexItem::gridEntryLocationName = "GridEntryLocation"; const char* SurveyComplexItem::flyAlternateTransectsName = "FlyAlternateTransects"; +const char* SurveyComplexItem::splitConcavePolygonsName = "SplitConcavePolygons"; const char* SurveyComplexItem::_jsonGridAngleKey = "angle"; const char* SurveyComplexItem::_jsonEntryPointKey = "entryLocation"; @@ -58,12 +59,14 @@ const char* SurveyComplexItem::_jsonV3CameraOrientationLandscapeKey = "orienta const char* SurveyComplexItem::_jsonV3FixedValueIsAltitudeKey = "fixedValueIsAltitude"; const char* SurveyComplexItem::_jsonV3Refly90DegreesKey = "refly90Degrees"; const char* SurveyComplexItem::_jsonFlyAlternateTransectsKey = "flyAlternateTransects"; +const char* SurveyComplexItem::_jsonSplitConcavePolygonsKey = "splitConcavePolygons"; SurveyComplexItem::SurveyComplexItem(Vehicle* vehicle, bool flyView, const QString& kmlFile, QObject* parent) : TransectStyleComplexItem (vehicle, flyView, settingsGroup, parent) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/Survey.SettingsGroup.json"), this)) , _gridAngleFact (settingsGroup, _metaDataMap[gridAngleName]) , _flyAlternateTransectsFact(settingsGroup, _metaDataMap[flyAlternateTransectsName]) + , _splitConcavePolygonsFact (settingsGroup, _metaDataMap[splitConcavePolygonsName]) , _entryPoint (EntryLocationTopLeft) { _editorQml = "qrc:/qml/SurveyItemEditor.qml"; @@ -82,14 +85,14 @@ SurveyComplexItem::SurveyComplexItem(Vehicle* vehicle, bool flyView, const QStri connect(&_gridAngleFact, &Fact::valueChanged, this, &SurveyComplexItem::_setDirty); connect(&_flyAlternateTransectsFact,&Fact::valueChanged, this, &SurveyComplexItem::_setDirty); + connect(&_splitConcavePolygonsFact, &Fact::valueChanged, this, &SurveyComplexItem::_setDirty); connect(this, &SurveyComplexItem::refly90DegreesChanged, this, &SurveyComplexItem::_setDirty); connect(&_gridAngleFact, &Fact::valueChanged, this, &SurveyComplexItem::_rebuildTransects); connect(&_flyAlternateTransectsFact,&Fact::valueChanged, this, &SurveyComplexItem::_rebuildTransects); + connect(&_splitConcavePolygonsFact, &Fact::valueChanged, this, &SurveyComplexItem::_rebuildTransects); connect(this, &SurveyComplexItem::refly90DegreesChanged, this, &SurveyComplexItem::_rebuildTransects); - connect(qgcApp()->toolbox()->settingsManager()->appSettings()->splitConcavePolygons(), &Fact::valueChanged, this, &SurveyComplexItem::_rebuildTransects); - // FIXME: Shouldn't these be in TransectStyleComplexItem? They are also in CorridorScanComplexItem constructur connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::coordinateHasRelativeAltitudeChanged); connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::exitCoordinateHasRelativeAltitudeChanged); @@ -112,6 +115,7 @@ void SurveyComplexItem::save(QJsonArray& planItems) saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; saveObject[_jsonGridAngleKey] = _gridAngleFact.rawValue().toDouble(); saveObject[_jsonFlyAlternateTransectsKey] = _flyAlternateTransectsFact.rawValue().toBool(); + saveObject[_jsonSplitConcavePolygonsKey] = _splitConcavePolygonsFact.rawValue().toBool(); saveObject[_jsonEntryPointKey] = _entryPoint; // Polygon shape @@ -168,6 +172,7 @@ bool SurveyComplexItem::_loadV4(const QJsonObject& complexObject, int sequenceNu { _jsonEntryPointKey, QJsonValue::Double, true }, { _jsonGridAngleKey, QJsonValue::Double, true }, { _jsonFlyAlternateTransectsKey, QJsonValue::Bool, false }, + { _jsonSplitConcavePolygonsKey, QJsonValue::Bool, false }, }; if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) { return false; @@ -196,6 +201,7 @@ bool SurveyComplexItem::_loadV4(const QJsonObject& complexObject, int sequenceNu _gridAngleFact.setRawValue (complexObject[_jsonGridAngleKey].toDouble()); _flyAlternateTransectsFact.setRawValue (complexObject[_jsonFlyAlternateTransectsKey].toBool(false)); + _splitConcavePolygonsFact.setRawValue (complexObject[_jsonSplitConcavePolygonsKey].toBool(true)); _entryPoint = complexObject[_jsonEntryPointKey].toInt(); @@ -1062,7 +1068,7 @@ bool SurveyComplexItem::_hoverAndCaptureEnabled(void) const void SurveyComplexItem::_rebuildTransectsPhase1(void) { - bool split = qgcApp()->toolbox()->settingsManager()->appSettings()->splitConcavePolygons()->rawValue().toBool(); + bool split = splitConcavePolygons()->rawValue().toBool(); if (split) { _rebuildTransectsPhase1WorkerSplitPolygons(false /* refly */); } else { @@ -1087,7 +1093,7 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSinglePolygon(bool refly) if (_loadedMissionItemsParent) { _loadedMissionItems.clear(); _loadedMissionItemsParent->deleteLater(); - _loadedMissionItemsParent = NULL; + _loadedMissionItemsParent = nullptr; } // First pass will clear old transect data, refly will append to existing data @@ -1260,7 +1266,7 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSinglePolygon(bool refly) double transectLength = transect[0].distanceTo(transect[1]); double transectAzimuth = transect[0].azimuthTo(transect[1]); if (triggerDistance() < transectLength) { - int cInnerHoverPoints = floor(transectLength / triggerDistance()); + int cInnerHoverPoints = static_cast(floor(transectLength / triggerDistance())); qCDebug(SurveyComplexItemLog) << "cInnerHoverPoints" << cInnerHoverPoints; for (int i=0; ideleteLater(); - _loadedMissionItemsParent = NULL; + _loadedMissionItemsParent = nullptr; } // First pass will clear old transect data, refly will append to existing data @@ -1669,7 +1675,7 @@ void SurveyComplexItem::_rebuildTranscetsFromPolygon(bool refly, const QPolygonF double transectLength = transect[0].distanceTo(transect[1]); double transectAzimuth = transect[0].azimuthTo(transect[1]); if (triggerDistance() < transectLength) { - int cInnerHoverPoints = floor(transectLength / triggerDistance()); + int cInnerHoverPoints = static_cast(floor(transectLength / triggerDistance())); qCDebug(SurveyComplexItemLog) << "cInnerHoverPoints" << cInnerHoverPoints; for (int i=0; i("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); @@ -429,13 +427,3 @@ Fact* AppSettings::followTarget(void) return _followTargetFact; } - -Fact* AppSettings::splitConcavePolygons(void) -{ - if (!_splitConcavePolygonsFact) { - _splitConcavePolygonsFact = _createSettingsFact(splitConcavePolygonsName); - } - - return _splitConcavePolygonsFact; -} - diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index b12bdf5..2887a90 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -18,7 +18,7 @@ class AppSettings : public SettingsGroup Q_OBJECT public: - AppSettings(QObject* parent = NULL); + AppSettings(QObject* parent = nullptr); Q_PROPERTY(Fact* offlineEditingFirmwareType READ offlineEditingFirmwareType CONSTANT) Q_PROPERTY(Fact* offlineEditingVehicleType READ offlineEditingVehicleType CONSTANT) @@ -43,7 +43,6 @@ public: Q_PROPERTY(Fact* defaultFirmwareType READ defaultFirmwareType CONSTANT) Q_PROPERTY(Fact* gstDebug READ gstDebug CONSTANT) Q_PROPERTY(Fact* followTarget READ followTarget CONSTANT) - Q_PROPERTY(Fact* splitConcavePolygons READ splitConcavePolygons CONSTANT) Q_PROPERTY(QString missionSavePath READ missionSavePath NOTIFY savePathsChanged) Q_PROPERTY(QString parameterSavePath READ parameterSavePath NOTIFY savePathsChanged) @@ -83,7 +82,6 @@ public: Fact* defaultFirmwareType (void); Fact* gstDebug (void); Fact* followTarget (void); - Fact* splitConcavePolygons (void); QString missionSavePath (void); QString parameterSavePath (void); @@ -121,7 +119,6 @@ public: static const char* defaultFirmwareTypeName; static const char* gstDebugName; static const char* followTargetName; - static const char* splitConcavePolygonsName; // Application wide file extensions static const char* parameterFileExtension; @@ -173,7 +170,6 @@ private: SettingsFact* _defaultFirmwareTypeFact; SettingsFact* _gstDebugFact; SettingsFact* _followTargetFact; - SettingsFact* _splitConcavePolygonsFact; }; #endif From a2a82f30e76b4ccad786617a8d232b1d95818ccf Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 8 Nov 2018 16:06:47 -0500 Subject: [PATCH 15/15] Bump survey complex item version to 5 --- src/MissionManager/SurveyComplexItem.cc | 22 +++++++++++++++------- src/MissionManager/SurveyComplexItem.h | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 07bc974..c38f086 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -110,7 +110,7 @@ void SurveyComplexItem::save(QJsonArray& planItems) _save(saveObject); - saveObject[JsonHelper::jsonVersionKey] = 4; + saveObject[JsonHelper::jsonVersionKey] = 5; saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; saveObject[_jsonGridAngleKey] = _gridAngleFact.rawValue().toDouble(); @@ -135,13 +135,13 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe } int version = complexObject[JsonHelper::jsonVersionKey].toInt(); - if (version < 2 || version > 4) { + if (version < 2 || version > 5) { errorString = tr("Survey items do not support version %1").arg(version); return false; } - if (version == 4) { - if (!_loadV4(complexObject, sequenceNumber, errorString)) { + if (version == 4 || version == 5) { + if (!_loadV4V5(complexObject, sequenceNumber, errorString, version)) { return false; } } else { @@ -164,7 +164,7 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe return true; } -bool SurveyComplexItem::_loadV4(const QJsonObject& complexObject, int sequenceNumber, QString& errorString) +bool SurveyComplexItem::_loadV4V5(const QJsonObject& complexObject, int sequenceNumber, QString& errorString, int version) { QList keyInfoList = { { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, @@ -172,8 +172,13 @@ bool SurveyComplexItem::_loadV4(const QJsonObject& complexObject, int sequenceNu { _jsonEntryPointKey, QJsonValue::Double, true }, { _jsonGridAngleKey, QJsonValue::Double, true }, { _jsonFlyAlternateTransectsKey, QJsonValue::Bool, false }, - { _jsonSplitConcavePolygonsKey, QJsonValue::Bool, false }, }; + + if(version == 5) { + JsonHelper::KeyValidateInfo jSplitPolygon = { _jsonSplitConcavePolygonsKey, QJsonValue::Bool, true }; + keyInfoList.append(jSplitPolygon); + } + if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) { return false; } @@ -201,7 +206,10 @@ bool SurveyComplexItem::_loadV4(const QJsonObject& complexObject, int sequenceNu _gridAngleFact.setRawValue (complexObject[_jsonGridAngleKey].toDouble()); _flyAlternateTransectsFact.setRawValue (complexObject[_jsonFlyAlternateTransectsKey].toBool(false)); - _splitConcavePolygonsFact.setRawValue (complexObject[_jsonSplitConcavePolygonsKey].toBool(true)); + + if(version == 5) { + _splitConcavePolygonsFact.setRawValue (complexObject[_jsonSplitConcavePolygonsKey].toBool(true)); + } _entryPoint = complexObject[_jsonEntryPointKey].toInt(); diff --git a/src/MissionManager/SurveyComplexItem.h b/src/MissionManager/SurveyComplexItem.h index 4df07a5..6cdd2cc 100644 --- a/src/MissionManager/SurveyComplexItem.h +++ b/src/MissionManager/SurveyComplexItem.h @@ -112,7 +112,7 @@ private: double _turnaroundDistance(void) const; bool _hoverAndCaptureEnabled(void) const; bool _loadV3(const QJsonObject& complexObject, int sequenceNumber, QString& errorString); - bool _loadV4(const QJsonObject& complexObject, int sequenceNumber, QString& errorString); + bool _loadV4V5(const QJsonObject& complexObject, int sequenceNumber, QString& errorString, int version); void _rebuildTransectsPhase1Worker(bool refly); void _rebuildTransectsPhase1WorkerSinglePolygon(bool refly); void _rebuildTransectsPhase1WorkerSplitPolygons(bool refly);