Commit 86faa707 authored by Valentin Platzgummer's avatar Valentin Platzgummer

temporary

parent a0ab8f79
{
"AreaItems": [
{
"AreaType": "Service Area",
"BorderPolygonOffset": 6,
"DepotAltitude": 0,
"DepotLatitude": 47.76779586216649,
"DepotLongitude": 16.530510396830728,
"ShowBorderPolygon": 0,
"maxAltitude": 30,
"polygon": [
[
47.76780094967389,
16.530343715825353
],
[
47.76788238112663,
16.530625096151965
],
[
47.767783696604035,
16.530655731490725
],
[
47.76771642095403,
16.53041704413795
]
]
},
{
"AreaType": "Measurement Area",
"BorderPolygonOffset": 6,
"MinTileAreaPercent": 30,
"ShowBorderPolygon": 0,
"ShowTiles": true,
"TileHeight": 5,
"TileWidth": 5,
"maxAltitude": 30,
"polygon": [
[
47.76809580679245,
16.530246122817612
],
[
47.76823933601322,
16.53060087654427
],
[
47.7683711160486,
16.530967006195464
],
[
47.7680076482754,
16.531153949077463
],
[
47.7677855557718,
16.530403347547246
],
[
47.76812093862815,
16.53088714314225
]
]
}
],
"MissionItems": {
"fileType": "Plan",
"geoFence": {
"circles": [
],
"polygons": [
],
"version": 2
},
"groundStation": "QGroundControl",
"mission": {
"cruiseSpeed": 15,
"firmwareType": 3,
"hoverSpeed": 1,
"items": [
{
"autoContinue": true,
"command": 22,
"doJumpId": 1,
"frame": 3,
"params": [
15,
0,
0,
null,
47.76779586216649,
16.530510396830728,
10
],
"type": "SimpleItem"
},
{
"Alpha": 23,
"MinLength": 5,
"NumRuns": 1,
"ReferencePointAlt": 0,
"ReferencePointLat": 47.76810433066452,
"ReferencePointLong": 16.530712738301304,
"Run": 1,
"TransectDistance": 3,
"TransectStyleComplexItem": {
"CameraCalc": {
"AdjustedFootprintFrontal": 25,
"AdjustedFootprintSide": 25,
"CameraName": "Manual (no camera specs)",
"DistanceToSurface": 10,
"DistanceToSurfaceRelative": true,
"version": 1
},
"CameraShots": 0,
"CameraTriggerInTurnAround": true,
"FollowTerrain": false,
"HoverAndCapture": false,
"Items": [
{
"autoContinue": true,
"command": 16,
"doJumpId": 2,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76783728898401,
16.530481694784847,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 3,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76802613957926,
16.53114161450939,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 4,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768179446847086,
16.53106276402324,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 5,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76812171672615,
16.530861029906276,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 6,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76831125432985,
16.530806354689858,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 7,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768358305251056,
16.530970771201435,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 8,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76833275405663,
16.53098391307365,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 9,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76818906969824,
16.530481820523075,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 10,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76810775728066,
16.530504966391423,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 11,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76825610046365,
16.53102333861304,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 12,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768281651653936,
16.531010196779448,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 13,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76810310388368,
16.530386278613275,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 14,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76809845036399,
16.530267590829624,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 15,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76830720286059,
16.53099705493299,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 16,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76823054925376,
16.531036480433762,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 17,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768112410554885,
16.530623654217433,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 18,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76811706370638,
16.530742342037925,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 19,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76820499805124,
16.531049622228267,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 20,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76815389564133,
16.531075905805334,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 21,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768086884966515,
16.53084174152664,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 22,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76803696584865,
16.530769731894658,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 23,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76812834442496,
16.531089047574557,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 24,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76810279321597,
16.53110218933091,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 25,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76798704669417,
16.530697722387153,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 26,
"frame": 3,
"params": [
0,
0,
0,
null,
47.767937127512056,
16.530625713070812,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 27,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76807724201434,
16.531115331061052,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 28,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768051690793115,
16.531128472791657,
10
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 29,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76788720826634,
16.530553703838915,
10
],
"type": "SimpleItem"
}
],
"Refly90Degrees": false,
"TurnAroundDistance": 10,
"VisualTransectPoints": [
[
47.76783728898401,
16.530481694784847
],
[
47.76802613957926,
16.53114161450939
],
[
47.768179446847086,
16.53106276402324
],
[
47.76812171672615,
16.530861029906276
],
[
47.76831125432985,
16.530806354689858
],
[
47.768358305251056,
16.530970771201435
],
[
47.76833275405663,
16.53098391307365
],
[
47.76818906969824,
16.530481820523075
],
[
47.76810775728066,
16.530504966391423
],
[
47.76825610046365,
16.53102333861304
],
[
47.768281651653936,
16.531010196779448
],
[
47.76810310388368,
16.530386278613275
],
[
47.76809845036399,
16.530267590829624
],
[
47.76830720286059,
16.53099705493299
],
[
47.76823054925376,
16.531036480433762
],
[
47.768112410554885,
16.530623654217433
],
[
47.76811706370638,
16.530742342037925
],
[
47.76820499805124,
16.531049622228267
],
[
47.76815389564133,
16.531075905805334
],
[
47.768086884966515,
16.53084174152664
],
[
47.76803696584865,
16.530769731894658
],
[
47.76812834442496,
16.531089047574557
],
[
47.76810279321597,
16.53110218933091
],
[
47.76798704669417,
16.530697722387153
],
[
47.767937127512056,
16.530625713070812
],
[
47.76807724201434,
16.531115331061052
],
[
47.768051690793115,
16.531128472791657
],
[
47.76788720826634,
16.530553703838915
]
],
"version": 1
},
"Type": 1,
"Variant": 0,
"complexItemType": "CircularSurvey",
"polygon": [
[
50.54939570164431,
7.196328001507449
],
[
50.54939570164431,
7.238775845997314
],
[
50.522416083956976,
7.238775845997314
],
[
50.522416083956976,
7.196328001507449
]
],
"type": "ComplexItem",
"version": 1
},
{
"AMSLAltAboveTerrain": null,
"Altitude": 10,
"AltitudeMode": 1,
"autoContinue": true,
"command": 16,
"doJumpId": 33,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76807639917751,
16.530893624112736,
10
],
"type": "SimpleItem"
},
{
"AMSLAltAboveTerrain": null,
"Altitude": 10,
"AltitudeMode": 1,
"autoContinue": true,
"command": 16,
"doJumpId": 34,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76779586216649,
16.530510396830728,
10
],
"type": "SimpleItem"
},
{
"AMSLAltAboveTerrain": null,
"Altitude": 0,
"AltitudeMode": 1,
"autoContinue": true,
"command": 21,
"doJumpId": 35,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76779586216649,
16.530510396830728,
0
],
"type": "SimpleItem"
}
],
"plannedHomePosition": [
47.76779586216649,
16.530510396830728,
178
],
"vehicleType": 2,
"version": 2
},
"rallyPoints": {
"points": [
],
"version": 2
},
"version": 1
}
}
......@@ -418,6 +418,10 @@ HEADERS += \
src/Wima/GenericSingelton.h \
src/Wima/Geometry/GenericCircle.h \
src/Wima/RoutingThread.h \
src/Wima/Snake/CircularGenerator.h \
src/Wima/Snake/GeneratorBase.h \
src/Wima/Snake/GeneratorData.h \
src/Wima/Snake/StandardData.h \
src/Wima/Snake/clipper/clipper.hpp \
src/Wima/Snake/mapbox/feature.hpp \
src/Wima/Snake/mapbox/geometry.hpp \
......@@ -510,6 +514,9 @@ SOURCES += \
src/Wima/CircularSurvey.cc \
src/Wima/GenericSingelton.cpp \
src/Wima/RoutingThread.cpp \
src/Wima/Snake/CircularGenerator.cpp \
src/Wima/Snake/GeneratorBase.cc \
src/Wima/Snake/StandardData.cpp \
src/Wima/Snake/clipper/clipper.cpp \
src/Wima/Snake/snake.cpp \
src/Wima/Geometry/GeoPoint3D.cpp \
......
#include "CircularGenerator.h"
#include "QGCLoggingCategory.h"
QGC_LOGGING_CATEGORY(CircularGeneratorLog, "CircularGeneratorLog")
namespace routing {
bool circularTransects(const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length deltaR, snake::Angle deltaAlpha,
snake::Length minLength, snake::Transects &transects);
CircularGenerator::CircularGenerator() : GeneratorBase() {}
CircularGenerator::CircularGenerator(std::shared_ptr<GeneratorData> par,
QObject *parent)
: GeneratorBase(parent) {
if (qobject_cast<StandardData *>(par.get()) != nullptr) {
data_ = data;
} else {
qCWarning(CircularGeneratorLog)
<< "CircularGenerator(): CircularGenerator accepts only StandartData.";
}
}
QString CircularGenerator::editorQML() {
return QStringLiteral("CircularGeneratorEditor.qml");
}
QString CircularGenerator::mapVisualQML() {
return QStringLiteral("CircularGeneratorMapVisual.qml");
}
QString CircularGenerator::name() {
return QStringLiteral("Circular Generator");
}
QString CircularGenerator::abbreviation() { return QStringLiteral("C. Gen."); }
GeneratorBase::Generator CircularGenerator::get() {}
bool CircularGenerator::setData(std::shared_ptr<GeneratorData> data) {
if (qobject_cast<StandardData *>(par.get()) != nullptr) {
data_ = data;
} else {
qCWarning(CircularGeneratorLog)
<< "setData(): CircularGenerator accepts only StandartData.";
}
}
std::shared_ptr<GeneratorData> CircularGenerator::data() { return data_; }
bool circularTransects(const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length deltaR, snake::Angle deltaAlpha,
snake::Length minLength, snake::Transects &transects) {
auto s1 = std::chrono::high_resolution_clock::now();
// Check preconitions
if (polygon.outer().size() >= 3) {
using namespace boost::units;
// Convert geo polygon to ENU polygon.
snake::FPoint origin{0, 0};
std::string error;
// Check validity.
if (!bg::is_valid(polygon, error)) {
qCWarning(CircularSurveyLog) << "circularTransects(): "
"invalid polygon.";
qCWarning(CircularSurveyLog) << error.c_str();
std::stringstream ss;
ss << bg::wkt(polygon);
qCWarning(CircularSurveyLog) << ss.str().c_str();
} else {
// Calculate polygon distances and angles.
std::vector<snake::Length> distances;
distances.reserve(polygon.outer().size());
std::vector<snake::Angle> angles;
angles.reserve(polygon.outer().size());
//#ifdef DEBUG_CIRCULAR_SURVEY
// qCWarning(CircularSurveyLog) << "circularTransects():";
//#endif
for (const auto &p : polygon.outer()) {
snake::Length distance = bg::distance(origin, p) * si::meter;
distances.push_back(distance);
snake::Angle alpha = (std::atan2(p.get<1>(), p.get<0>())) * si::radian;
alpha = alpha < 0 * si::radian ? alpha + 2 * M_PI * si::radian : alpha;
angles.push_back(alpha);
//#ifdef DEBUG_CIRCULAR_SURVEY
// qCWarning(CircularSurveyLog) << "distances, angles,
// coordinates:"; qCWarning(CircularSurveyLog) <<
// to_string(distance).c_str(); qCWarning(CircularSurveyLog) <<
// to_string(snake::Degree(alpha)).c_str();
// qCWarning(CircularSurveyLog) << "x = " << p.get<0>() << "y = "
// << p.get<1>();
//#endif
}
auto rMin = deltaR; // minimal circle radius
snake::Angle alpha1(0 * degree::degree);
snake::Angle alpha2(360 * degree::degree);
// Determine r_min by successive approximation
if (!bg::within(origin, polygon.outer())) {
rMin = bg::distance(origin, polygon) * si::meter;
}
auto rMax = (*std::max_element(distances.begin(),
distances.end())); // maximal circle radius
// Scale parameters and coordinates.
const auto rMinScaled =
ClipperLib::cInt(std::round(rMin.value() * CLIPPER_SCALE));
const auto deltaRScaled =
ClipperLib::cInt(std::round(deltaR.value() * CLIPPER_SCALE));
auto originScaled =
ClipperLib::IntPoint{ClipperLib::cInt(std::round(origin.get<0>())),
ClipperLib::cInt(std::round(origin.get<1>()))};
// Generate circle sectors.
auto rScaled = rMinScaled;
const auto nTran = long(std::ceil(((rMax - rMin) / deltaR).value()));
vector<ClipperLib::Path> sectors(nTran, ClipperLib::Path());
const auto nSectors =
long(std::round(((alpha2 - alpha1) / deltaAlpha).value()));
//#ifdef DEBUG_CIRCULAR_SURVEY
// qCWarning(CircularSurveyLog) << "circularTransects(): sector
// parameres:"; qCWarning(CircularSurveyLog) << "alpha1: " <<
// to_string(snake::Degree(alpha1)).c_str();
// qCWarning(CircularSurveyLog) << "alpha2:
// "
// << to_string(snake::Degree(alpha2)).c_str();
// qCWarning(CircularSurveyLog) << "n: "
// << to_string((alpha2 - alpha1) / deltaAlpha).c_str();
// qCWarning(CircularSurveyLog)
// << "nSectors: " << nSectors; qCWarning(CircularSurveyLog) <<
// "rMin: " << to_string(rMin).c_str(); qCWarning(CircularSurveyLog)
// << "rMax: " << to_string(rMax).c_str();
// qCWarning(CircularSurveyLog) << "nTran: " << nTran;
//#endif
using ClipperCircle =
GenericCircle<ClipperLib::cInt, ClipperLib::IntPoint>;
for (auto &sector : sectors) {
ClipperCircle circle(rScaled, originScaled);
approximate(circle, nSectors, sector);
rScaled += deltaRScaled;
}
// Clip sectors to polygonENU.
ClipperLib::Path polygonClipper;
snake::FPolygon shrinked;
snake::offsetPolygon(polygon, shrinked, -0.3);
auto &outer = shrinked.outer();
polygonClipper.reserve(outer.size());
for (auto it = outer.begin(); it < outer.end() - 1; ++it) {
auto x = ClipperLib::cInt(std::round(it->get<0>() * CLIPPER_SCALE));
auto y = ClipperLib::cInt(std::round(it->get<1>() * CLIPPER_SCALE));
polygonClipper.push_back(ClipperLib::IntPoint{x, y});
}
ClipperLib::Clipper clipper;
clipper.AddPath(polygonClipper, ClipperLib::ptClip, true);
clipper.AddPaths(sectors, ClipperLib::ptSubject, false);
ClipperLib::PolyTree transectsClipper;
clipper.Execute(ClipperLib::ctIntersection, transectsClipper,
ClipperLib::pftNonZero, ClipperLib::pftNonZero);
// Subtract holes.
if (tiles.size() > 0) {
vector<ClipperLib::Path> processedTiles;
for (const auto &tile : tiles) {
ClipperLib::Path path;
for (const auto &v : tile.outer()) {
path.push_back(ClipperLib::IntPoint{
static_cast<ClipperLib::cInt>(v.get<0>() * CLIPPER_SCALE),
static_cast<ClipperLib::cInt>(v.get<1>() * CLIPPER_SCALE)});
}
processedTiles.push_back(std::move(path));
}
clipper.Clear();
for (const auto &child : transectsClipper.Childs) {
clipper.AddPath(child->Contour, ClipperLib::ptSubject, false);
}
clipper.AddPaths(processedTiles, ClipperLib::ptClip, true);
transectsClipper.Clear();
clipper.Execute(ClipperLib::ctDifference, transectsClipper,
ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
// Extract transects from PolyTree and convert them to
// BoostLineString
for (const auto &child : transectsClipper.Childs) {
snake::FLineString transect;
transect.reserve(child->Contour.size());
for (const auto &vertex : child->Contour) {
auto x = static_cast<double>(vertex.X) / CLIPPER_SCALE;
auto y = static_cast<double>(vertex.Y) / CLIPPER_SCALE;
transect.push_back(snake::FPoint(x, y));
}
transects.push_back(transect);
}
// Join sectors which where slit due to clipping.
const double th = 0.01;
for (auto ito = transects.begin(); ito < transects.end(); ++ito) {
for (auto iti = ito + 1; iti < transects.end(); ++iti) {
auto dist1 = bg::distance(ito->front(), iti->front());
if (dist1 < th) {
snake::FLineString temp;
for (auto it = iti->end() - 1; it >= iti->begin(); --it) {
temp.push_back(*it);
}
temp.insert(temp.end(), ito->begin(), ito->end());
*ito = temp;
transects.erase(iti);
break;
}
auto dist2 = bg::distance(ito->front(), iti->back());
if (dist2 < th) {
snake::FLineString temp;
temp.insert(temp.end(), iti->begin(), iti->end());
temp.insert(temp.end(), ito->begin(), ito->end());
*ito = temp;
transects.erase(iti);
break;
}
auto dist3 = bg::distance(ito->back(), iti->front());
if (dist3 < th) {
snake::FLineString temp;
temp.insert(temp.end(), ito->begin(), ito->end());
temp.insert(temp.end(), iti->begin(), iti->end());
*ito = temp;
transects.erase(iti);
break;
}
auto dist4 = bg::distance(ito->back(), iti->back());
if (dist4 < th) {
snake::FLineString temp;
temp.insert(temp.end(), ito->begin(), ito->end());
for (auto it = iti->end() - 1; it >= iti->begin(); --it) {
temp.push_back(*it);
}
*ito = temp;
transects.erase(iti);
break;
}
}
}
// Remove short transects
for (auto it = transects.begin(); it < transects.end();) {
if (bg::length(*it) < minLength.value()) {
it = transects.erase(it);
} else {
++it;
}
}
qCWarning(CircularSurveyLog)
<< "circularTransects(): transect gen. time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - s1)
.count()
<< " ms";
return true;
}
}
return false;
}
} // namespace routing
#include "GeneratorBase.h"
#include "StandardData.h"
namespace routing {
class CircularGenerator : public GeneratorBase {
Q_OBJECT
public:
CircularGenerator();
CircularGenerator(std::shared_ptr<GeneratorData> par,
QObject *parent = nullptr);
~CircularGenerator();
virtual QString editorQML();
virtual QString mapVisualQML();
virtual QString name();
virtual QString abbreviation();
virtual Generator get();
private:
std::shared_ptr<GeneratorData> _data;
};
} // namespace routing
#include "GeneratorBase.h"
namespace routing {
GeneratorBase::GeneratorBase(QObject *parent) : QObject(parent) {}
GeneratorBase::GeneratorBase(std::shared_ptr<GeneratorData> par,
QObject *parent)
: QObject(parent) {}
GeneratorBase::~GeneratorBase() {}
} // namespace routing
#include <QObject>
#include <functional>
#include <memory>
#include "snake.h"
#include "GeneratorData.h"
namespace routing {
class GeneratorBase : public QObject {
Q_OBJECT
public:
using Generator = std::function<bool(snake::Transects &)>;
explicit GeneratorBase(QObject *parent = nullptr);
GeneratorBase(std::shared_ptr<GeneratorData> par, QObject *parent = nullptr);
~GeneratorBase();
virtual QString editorQML() = 0;
virtual QString mapVisualQML() = 0;
virtual QString name() = 0;
virtual QString abbreviation() = 0;
virtual Generator get() = 0;
virtual bool setData(std::shared_ptr<GeneratorData> par) = 0;
virtual std::shared_ptr<GeneratorData> data() = 0;
};
} // namespace routing
#pragma once
#include <QObject>
class GeneratorData : public QObject {
Q_OBJECT
public:
explicit GeneratorData(QObject *parent = nullptr);
};
#include "StandardData.h"
StandardParameter::StandardParameter() {}
#pragma once
#include "GeneratorData.h"
namespace routing {
class StandardData : public GeneratorData {};
} // namespace routing
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