diff --git a/RoutesGenereren/RoutesGenereren.pro b/RoutesGenereren/RoutesGenereren.pro new file mode 100644 index 0000000..203165a --- /dev/null +++ b/RoutesGenereren/RoutesGenereren.pro @@ -0,0 +1,35 @@ +QT += core gui serialport + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++14 +QT += concurrent + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + console.cpp \ + main.cpp \ + mainwindow.cpp \ + routegenerator.cpp \ + settingsdialog.cpp + +HEADERS += \ + console.h \ + mainwindow.h \ + routegenerator.h \ + settingsdialog.h + +FORMS += \ + mainwindow.ui \ + settingsdialog.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +RESOURCES += \ + terminal.qrc diff --git a/RoutesGenereren/RoutesGenereren.pro.user b/RoutesGenereren/RoutesGenereren.pro.user new file mode 100644 index 0000000..edb4a0f --- /dev/null +++ b/RoutesGenereren/RoutesGenereren.pro.user @@ -0,0 +1,433 @@ + + + + + + EnvironmentId + {2de608a0-7d4e-4466-820b-332fa0018787} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 6 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop Qt 5.15.2 MinGW 32-bit + Desktop Qt 5.15.2 MinGW 32-bit + qt.qt5.5152.win32_mingw81_kit + 0 + 0 + 0 + + 0 + C:\Users\NPC\Documents\School\_ProjBlok11\QtApp - Copy\build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Debug + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Debug + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + C:\Users\NPC\Documents\School\_ProjBlok11\QtApp - Copy\build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Release + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Release + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + + + 0 + C:\Users\NPC\Documents\School\_ProjBlok11\QtApp - Copy\build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Profile + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Profile + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 0 + + 3 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + RoutesGenereren2 + Qt4ProjectManager.Qt4RunConfiguration:C:/Users/NPC/Documents/School/_ProjBlok11/QtApp2/RoutesGenereren/RoutesGenereren.pro + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp2/RoutesGenereren/RoutesGenereren.pro + true + true + true + true + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Debug + + 1 + + + + ProjectExplorer.Project.Target.1 + + Desktop + Qt 5.15.2 (mingw81_32) + Qt 5.15.2 (mingw81_32) + {db23b840-4fa8-4369-81d4-5c33831dd635} + 0 + 0 + 0 + + 0 + C:\Users\NPC\Documents\School\_ProjBlok11\QtApp\build-RoutesGenereren-Qt_5_15_2_mingw81_32-Debug + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp/build-RoutesGenereren-Qt_5_15_2_mingw81_32-Debug + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + C:\Users\NPC\Documents\School\_ProjBlok11\QtApp\build-RoutesGenereren-Qt_5_15_2_mingw81_32-Release + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp/build-RoutesGenereren-Qt_5_15_2_mingw81_32-Release + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + + + 0 + C:\Users\NPC\Documents\School\_ProjBlok11\QtApp\build-RoutesGenereren-Qt_5_15_2_mingw81_32-Profile + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp/build-RoutesGenereren-Qt_5_15_2_mingw81_32-Profile + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 0 + + 3 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + RoutesGenereren2 + Qt4ProjectManager.Qt4RunConfiguration:C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/RoutesGenereren/RoutesGenereren.pro + C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/RoutesGenereren/RoutesGenereren.pro + true + true + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 2 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/RoutesGenereren/RoutesGenereren.pro.user.6737c63 b/RoutesGenereren/RoutesGenereren.pro.user.6737c63 new file mode 100644 index 0000000..98a8268 --- /dev/null +++ b/RoutesGenereren/RoutesGenereren.pro.user.6737c63 @@ -0,0 +1,264 @@ + + + + + + EnvironmentId + {6737c63b-8d94-4d04-854a-df0b2ddb36db} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + -fno-delayed-template-parsing + + true + Builtin.BuildSystem + + true + true + Builtin.DefaultTidyAndClazy + 4 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop Qt 6.1.3 MSVC2019 64bit + Desktop Qt 6.1.3 MSVC2019 64bit + qt.qt6.613.win64_msvc2019_64_kit + 0 + 0 + 0 + + 0 + C:\Users\lars\OneDrive\Documenten\Avans Elektrotechniek\TechnischeInformatica2025\ProjectBlok11-12\build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Debug + C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Debug + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + C:\Users\lars\OneDrive\Documenten\Avans Elektrotechniek\TechnischeInformatica2025\ProjectBlok11-12\build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Release + C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Release + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + + + 0 + C:\Users\lars\OneDrive\Documenten\Avans Elektrotechniek\TechnischeInformatica2025\ProjectBlok11-12\build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Profile + C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Profile + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 0 + + 3 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + true + + 2 + + Qt4ProjectManager.Qt4RunConfiguration:C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/RoutesGenereren/RoutesGenereren.pro + C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/RoutesGenereren/RoutesGenereren.pro + false + true + true + false + true + C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Debug + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/RoutesGenereren/console.cpp b/RoutesGenereren/console.cpp new file mode 100644 index 0000000..b4c1ee1 --- /dev/null +++ b/RoutesGenereren/console.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2012 Denis Shienkov +// Copyright (C) 2012 Laszlo Papp +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "console.h" + +#include + +Console::Console(QWidget *parent) : + QPlainTextEdit(parent) +{ + document()->setMaximumBlockCount(100); + QPalette p = palette(); + p.setColor(QPalette::Base, Qt::black); + p.setColor(QPalette::Text, Qt::green); + setPalette(p); +} + +void Console::putData(const QByteArray &data) +{ + insertPlainText(data); + + QScrollBar *bar = verticalScrollBar(); + bar->setValue(bar->maximum()); +} + +void Console::setLocalEchoEnabled(bool set) +{ + m_localEchoEnabled = set; +} + +void Console::keyPressEvent(QKeyEvent *e) +{ + switch (e->key()) { + case Qt::Key_Backspace: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + break; + default: + if (m_localEchoEnabled) + QPlainTextEdit::keyPressEvent(e); + emit getData(e->text().toLocal8Bit()); + } +} + +void Console::mousePressEvent(QMouseEvent *e) +{ + Q_UNUSED(e); + setFocus(); +} + +void Console::mouseDoubleClickEvent(QMouseEvent *e) +{ + Q_UNUSED(e); +} + +void Console::contextMenuEvent(QContextMenuEvent *e) +{ + Q_UNUSED(e); +} diff --git a/RoutesGenereren/console.h b/RoutesGenereren/console.h new file mode 100644 index 0000000..d5a39ef --- /dev/null +++ b/RoutesGenereren/console.h @@ -0,0 +1,33 @@ +// Copyright (C) 2012 Denis Shienkov +// Copyright (C) 2012 Laszlo Papp +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef CONSOLE_H +#define CONSOLE_H + +#include + +class Console : public QPlainTextEdit +{ + Q_OBJECT + +signals: + void getData(const QByteArray &data); + +public: + explicit Console(QWidget *parent = nullptr); + + void putData(const QByteArray &data); + void setLocalEchoEnabled(bool set); + +protected: + void keyPressEvent(QKeyEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseDoubleClickEvent(QMouseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; + +private: + bool m_localEchoEnabled = false; +}; + +#endif // CONSOLE_H diff --git a/RoutesGenereren/images/application-exit.png b/RoutesGenereren/images/application-exit.png new file mode 100644 index 0000000..32be6b3 Binary files /dev/null and b/RoutesGenereren/images/application-exit.png differ diff --git a/RoutesGenereren/images/clear.png b/RoutesGenereren/images/clear.png new file mode 100644 index 0000000..aa612f1 Binary files /dev/null and b/RoutesGenereren/images/clear.png differ diff --git a/RoutesGenereren/images/connect.png b/RoutesGenereren/images/connect.png new file mode 100644 index 0000000..dd5a51e Binary files /dev/null and b/RoutesGenereren/images/connect.png differ diff --git a/RoutesGenereren/images/disconnect.png b/RoutesGenereren/images/disconnect.png new file mode 100644 index 0000000..fd58f7a Binary files /dev/null and b/RoutesGenereren/images/disconnect.png differ diff --git a/RoutesGenereren/images/settings.png b/RoutesGenereren/images/settings.png new file mode 100644 index 0000000..3d1042e Binary files /dev/null and b/RoutesGenereren/images/settings.png differ diff --git a/RoutesGenereren/locations.json b/RoutesGenereren/locations.json new file mode 100644 index 0000000..efc0cd5 --- /dev/null +++ b/RoutesGenereren/locations.json @@ -0,0 +1,200 @@ +[ + { + "cost": 2, + "mg_id": 0, + "x": 51688573, + "y": 5287210 + }, + { + "cost": 1.5, + "mg_id": 101, + "x": 51690224, + "y": 5296625 + }, + { + "cost": 2, + "mg_id": 112, + "x": 51688460, + "y": 5303150 + }, + { + "cost": 1.5, + "mg_id": 1, + "x": 51686200, + "y": 5304500 + }, + { + "cost": 2, + "mg_id": 9, + "x": 51685051, + "y": 5289156 + }, + { + "cost": 1.5, + "mg_id": 111, + "x": 51684258, + "y": 5302611 + }, + { + "cost": 2, + "mg_id": 5, + "x": 51691299, + "y": 5303950 + }, + { + "cost": 1.5, + "mg_id": 100, + "x": 51689428, + "y": 5310484 + }, + { + "cost": 2, + "mg_id": 113, + "x": 51695984, + "y": 5299074 + }, + { + "cost": 1.5, + "mg_id": 108, + "x": 51689124, + "y": 5303969 + }, + { + "cost": 2, + "mg_id": 114, + "x": 51689619, + "y": 5299065 + }, + { + "cost": 1.5, + "mg_id": 115, + "x": 51693002, + "y": 5301264 + }, + { + "cost": 2, + "mg_id": 102, + "x": 51697021, + "y": 5299328 + }, + { + "cost": 1.5, + "mg_id": 116, + "x": 51689724, + "y": 5300408 + }, + { + "cost": 2, + "mg_id": 110, + "x": 51686471, + "y": 5304106 + }, + { + "cost": 1.5, + "mg_id": 109, + "x": 51689471, + "y": 5303200 + }, + { + "cost": 2, + "mg_id": 117, + "x": 51689302, + "y": 5303396 + }, + { + "cost": 1.5, + "mg_id": 118, + "x": 51695457, + "y": 5297448 + }, + { + "cost": 2, + "mg_id": 119, + "x": 51694463, + "y": 5302862 + }, + { + "cost": 1.5, + "mg_id": 8, + "x": 51690467, + "y": 5294925 + }, + { + "cost": 2, + "mg_id": 11, + "x": 51683776, + "y": 5317938 + }, + { + "cost": 1.5, + "mg_id": 120, + "x": 51687561, + "y": 5305911 + }, + { + "cost": 1.5, + "mg_id": 121, + "x": 51698630, + "y": 5292803 + }, + { + "cost": 1.5, + "mg_id": 7, + "x": 51688691, + "y": 5309001 + }, + { + "cost": 1.5, + "mg_id": 122, + "x": 51687344, + "y": 5305871 + }, + { + "cost": 1.5, + "mg_id": 10, + "x": 51696264, + "y": 5307460 + }, + { + "cost": 1.5, + "mg_id": 123, + "x": 51696501, + "y": 5312884 + }, + { + "cost": 1.5, + "mg_id": 2, + "x": 51691911, + "y": 5286594 + }, + { + "cost": 1.5, + "mg_id": 124, + "x": 51690402, + "y": 5291740 + }, + { + "cost": 1.5, + "mg_id": 3, + "x": 51685400, + "y": 5289354 + }, + { + "cost": 1.5, + "mg_id": 106, + "x": 51690484, + "y": 5296206 + }, + { + "cost": 1.5, + "mg_id": 125, + "x": 51686618, + "y": 5308459 + }, + { + "cost": 1.5, + "mg_id": 6, + "x": 51686697, + "y": 5303137 + } +] diff --git a/RoutesGenereren/main.cpp b/RoutesGenereren/main.cpp new file mode 100644 index 0000000..fd3e533 --- /dev/null +++ b/RoutesGenereren/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/RoutesGenereren/mainwindow.cpp b/RoutesGenereren/mainwindow.cpp new file mode 100644 index 0000000..eec08d1 --- /dev/null +++ b/RoutesGenereren/mainwindow.cpp @@ -0,0 +1,688 @@ +// Copyright (C) 2012 Denis Shienkov +// Copyright (C) 2012 Laszlo Papp +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "console.h" +#include "routegenerator.h" +#include "settingsdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static constexpr std::chrono::seconds kWriteTimeout = std::chrono::seconds{5}; + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + m_ui(new Ui::MainWindow), + m_status(new QLabel), + m_console(new Console), + m_settings(new SettingsDialog(this)), + m_serial(new QSerialPort(this)) +{ + m_ui->setupUi(this); + + m_ui->tabWidget->setCurrentIndex(0); + + loadLocationsFromFile(); + // if there was nothing in the file, fall back to defaults + if (allPoints.empty()) + loadDefaultPoints(); + populateLocationsTable(); + + refreshRouteList(); + refreshSerialPorts(); + + scene = new QGraphicsScene(this); + m_ui->graphicsView->setScene(scene); + m_ui->graphicsView->setRenderHint(QPainter::Antialiasing, true); + + // SerialPort + console instellingen + //m_console->setEnabled(false); + //QWidget* tab1 = m_console; + //m_ui->tabWidget->addTab(tab1, "Developer mode"); + + m_ui->actionConnect->setEnabled(true); + m_ui->actionDisconnect->setEnabled(false); + m_ui->actionQuit->setEnabled(true); + m_ui->actionConfigure->setEnabled(true); + + m_ui->statusBar->addWidget(m_status); + + initActionsConnections(); + + connect(m_serial, &QSerialPort::errorOccurred, this, &MainWindow::handleError); + connect(m_serial, &QSerialPort::readyRead, this, &MainWindow::readData); + connect(m_console, &Console::getData, this, &MainWindow::writeData); + + //QObject::connect(m_ui->pushButton_2, SIGNAL(pressed()), this, SLOT(pushButton_2_clicked())); +} + +MainWindow::~MainWindow() +{ + delete m_settings; + delete m_ui; +} + +void MainWindow::drawRoutes() +{ + scene->clear(); + + int64_t minX = std::numeric_limits::max(), maxX = std::numeric_limits::lowest(); + int64_t minY = std::numeric_limits::max(), maxY = std::numeric_limits::lowest(); + for (auto& pts : selectedPoints) + for (auto& p : pts) { + minX = std::min(minX, p.x); + maxX = std::max(maxX, p.x); + minY = std::min(minY, p.y); + maxY = std::max(maxY, p.y); + } + + QSizeF viewSz = m_ui->graphicsView->viewport()->size(); + constexpr qreal M = 20.0; // marge in pixels + qreal scaleX = (viewSz.width() - 2*M) / qreal(maxX - minX); + qreal scaleY = (viewSz.height() - 2*M) / qreal(maxY - minY); + qreal scale = std::min(scaleX, scaleY); + + auto mapPt = [&](const Point& p) { + qreal x = (p.x - minX)*scale + M; + qreal y = (p.y - minY)*scale + M; + return QPointF(x,y); + }; + + const int radius = 4; + const QColor colors[] = { + Qt::red, Qt::blue, Qt::green, Qt::darkYellow, + Qt::magenta, Qt::cyan, Qt::darkGreen, Qt::darkRed, + Qt::gray, Qt::black + }; + + for (int r = 0; r < routeIndices.size(); ++r) { + QPen pen(colors[r%10]); + pen.setCosmetic(true); + pen.setWidth(0); + const auto& route = routeIndices[r]; + const auto& pts = selectedPoints[r]; + + // lijnen + for (int i = 0; i + 1 < route.size(); ++i) { + QPointF p1 = mapPt(pts[ route[i] ]); + QPointF p2 = mapPt(pts[ route[i+1] ]); + scene->addLine(p1.x(), p1.y(), p2.x(), p2.y(), pen); + } + // punten + for (auto& p : pts) { + QPointF c = mapPt(p); + scene->addEllipse(c.x()-radius, c.y()-radius, + radius*2, radius*2, + QPen(Qt::black), + QBrush(colors[r%10])); + } + } + + // 4) Scene instellen en fitten + scene->setSceneRect(0, 0, viewSz.width(), viewSz.height()); + m_ui->graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); +} + +void MainWindow::resizeEvent(QResizeEvent* event) +{ + QMainWindow::resizeEvent(event); + if (scene && !scene->items().isEmpty()) { + m_ui->graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); + } +} + +void MainWindow::on_pushButton_clicked() +{ + m_ui->statusLabel->setText("Bezig met genereren..."); + QApplication::processEvents(); + + QFuture future = QtConcurrent::run([this]() { + generate_routes(allPoints); + QMetaObject::invokeMethod(this, [this]() { + m_ui->statusLabel->setText("Routes succesvol gegenereerd!"); + drawRoutes(); + }, Qt::QueuedConnection); + }); +} + +void MainWindow::pushButton_2_clicked() +{ + +} + +void MainWindow::openSerialPort() +{ + const SettingsDialog::Settings p = m_settings->settings(); + m_serial->setPortName(p.name); + m_serial->setBaudRate(p.baudRate); + m_serial->setDataBits(p.dataBits); + m_serial->setParity(p.parity); + m_serial->setStopBits(p.stopBits); + m_serial->setFlowControl(p.flowControl); + if (m_serial->open(QIODevice::ReadWrite)) { + m_console->setEnabled(true); + m_console->setLocalEchoEnabled(p.localEchoEnabled); + m_ui->actionConnect->setEnabled(false); + m_ui->actionDisconnect->setEnabled(true); + m_ui->actionConfigure->setEnabled(false); + showStatusMessage(tr("Connected to %1 : %2, %3, %4, %5, %6") + .arg(p.name, p.stringBaudRate, p.stringDataBits, + p.stringParity, p.stringStopBits, p.stringFlowControl)); + } else { + QMessageBox::critical(this, tr("Error"), m_serial->errorString()); + showStatusMessage(tr("Open error")); + } +} + +void MainWindow::closeSerialPort() +{ + if (m_serial->isOpen()) + m_serial->close(); + m_console->setEnabled(false); + m_ui->actionConnect->setEnabled(true); + m_ui->actionDisconnect->setEnabled(false); + m_ui->actionConfigure->setEnabled(true); + showStatusMessage(tr("Disconnected")); +} + +void MainWindow::about() +{ + QMessageBox::about(this, tr("About Serial Terminal"), + tr("The Serial Terminal example demonstrates how to " + "use the Qt Serial Port module in modern GUI applications " + "using Qt, with a menu bar, toolbars, and a status bar.")); +} + +void MainWindow::writeData(const QByteArray &data) +{ + m_serial->write(data); +} + +void MainWindow::readData() +{ + const QByteArray data = m_serial->readAll(); + m_console->putData(data); +} + +void MainWindow::handleError(QSerialPort::SerialPortError error) +{ + if (error == QSerialPort::ResourceError) { + QMessageBox::critical(this, tr("Critical Error"), m_serial->errorString()); + closeSerialPort(); + } +} + +void MainWindow::handleBytesWritten(qint64 bytes) +{ + m_bytesToWrite -= bytes; + if (m_bytesToWrite == 0) + m_timer->stop(); +} + +void MainWindow::handleWriteTimeout() +{ + const QString error = tr("Write operation timed out for port %1.\n" + "Error: %2").arg(m_serial->portName(), + m_serial->errorString()); + showWriteError(error); +} + +void MainWindow::initActionsConnections() +{ + connect(m_ui->actionConnect, &QAction::triggered, this, &MainWindow::openSerialPort); + connect(m_ui->actionDisconnect, &QAction::triggered, this, &MainWindow::closeSerialPort); + connect(m_ui->actionQuit, &QAction::triggered, this, &MainWindow::close); + connect(m_ui->actionConfigure, &QAction::triggered, m_settings, &SettingsDialog::show); + connect(m_ui->actionClear, &QAction::triggered, m_console, &Console::clear); + connect(m_ui->actionAbout, &QAction::triggered, this, &MainWindow::about); + connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt); +} + +void MainWindow::showStatusMessage(const QString &message) +{ + m_status->setText(message); +} + +void MainWindow::showWriteError(const QString &message) +{ + QMessageBox::warning(this, tr("Warning"), message); +} + +void MainWindow::refreshRouteList() +{ + QDir buildDir(QCoreApplication::applicationDirPath()); + buildDir.setNameFilters(QStringList{"*.json"}); + buildDir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + + auto files = buildDir.entryList(); + m_ui->comboRouteFiles->clear(); + if (files.isEmpty()) { + m_ui->comboRouteFiles->addItem(tr("[geen routes]")); + } else { + m_ui->comboRouteFiles->addItems(files); + } +} + +void MainWindow::refreshSerialPorts() +{ + auto ports = QSerialPortInfo::availablePorts(); + m_ui->comboPorts->clear(); + for (const auto &info : ports) + m_ui->comboPorts->addItem(info.portName()); + if (ports.isEmpty()) + m_ui->comboPorts->addItem(tr("[geen poorten]")); +} + +void MainWindow::on_btnRefreshRoutes_clicked() +{ + refreshRouteList(); +} + +void MainWindow::on_btnRefreshPorts_clicked() +{ + refreshSerialPorts(); +} + +void MainWindow::on_btnUploadSerial_clicked() +{ + // Read json file + QString fileName = m_ui->comboRouteFiles->currentText(); + if (fileName.startsWith("[")) { + QMessageBox::warning(this, tr("Geen route"), + tr("Genereer eerst routes op tab 1.")); + return; + } + QDir buildDir(QCoreApplication::applicationDirPath()); + QString src = buildDir.filePath(fileName); + QFile f(src); + if (!f.open(QIODevice::ReadOnly)) { + QMessageBox::critical(this, tr("Fout"), + tr("Kan %1 niet openen").arg(src)); + return; + } + const QByteArray jsonData = f.readAll(); + f.close(); + + // Open serial port + QString portName = m_ui->comboPorts->currentText(); + if (portName.startsWith("[")) { + QMessageBox::warning(this, tr("Geen poort"), + tr("Sluit koffer aan en ververs poorten.")); + return; + } + QSerialPort port(portName, this); + port.setBaudRate(QSerialPort::Baud115200); + port.setDataBits(QSerialPort::Data8); + port.setParity(QSerialPort::NoParity); + port.setStopBits(QSerialPort::OneStop); + port.setFlowControl(QSerialPort::NoFlowControl); + if (!port.open(QIODevice::ReadWrite)) { + QMessageBox::critical(this, tr("Fout"), + tr("Kan poort %1 niet openen:\n%2") + .arg(portName, port.errorString())); + return; + } + + + // Delete loc.txt, progress.txt and score.txt + QByteArray resetCmd = "fs rm /SD:/loc.txt\r\n"; + port.write(resetCmd); + port.waitForBytesWritten(1000); + + QThread::msleep(300); + + resetCmd = "fs rm /SD:/progress.txt\r\n"; + port.write(resetCmd); + port.waitForBytesWritten(1000); + + QThread::msleep(300); + + resetCmd = "fs rm /SD:/score.txt\r\n"; + port.write(resetCmd); + port.waitForBytesWritten(1000); + + QThread::msleep(300); + + // upload to /SD:/loc.txt + const qint64 CHUNK_SIZE = 16; + const qint64 totalLen = jsonData.size(); + qint64 offset = 0; + const QByteArray remotePath("/SD:/loc.txt"); + + while (offset < totalLen) { + qint64 chunkLen = qMin(CHUNK_SIZE, totalLen - offset); + QByteArray chunk = jsonData.mid(offset, chunkLen); + + QString offsetHex = QString("%1") + .arg(offset, 2, 16, QChar('0')); + + // build hex-strings + QStringList hexBytes; + hexBytes.reserve(chunkLen); + for (uchar b : chunk) { + hexBytes << QString("%1") + .arg(b, 2, 16, QChar('0')); + } + QByteArray spacedHex = hexBytes.join(' ').toUtf8(); + + // build fs command + QByteArray cmd = "fs write " + + remotePath + " " + + spacedHex + + "\r\n"; + + qDebug() << ">>" << cmd.trimmed(); + + qint64 written = port.write(cmd); + if (written != cmd.size() || + !port.waitForBytesWritten(5000)) { + QMessageBox::critical(this, tr("Fout"), + tr("Chunk offset %1 niet verzonden").arg(offsetHex)); + port.close(); + return; + } + + QThread::msleep(300); + offset += chunkLen; + } + + // Reset progress.txt (ASCII ‘0’ = 0x30) + { + QByteArray resetCmd = "fs write /SD:/progress.txt 30\r\n"; + port.write(resetCmd); + port.waitForBytesWritten(500); + } + + // Reset score.txt + { + QByteArray resetCmd = "fs write /SD:/score.txt 30\r\n"; + port.write(resetCmd); + port.waitForBytesWritten(500); + } + + // (Optional feedback message) + if (port.waitForReadyRead(500)) { + QByteArray fb; + do { fb += port.readAll(); } + while (port.waitForReadyRead(100)); + qDebug() << "Feedback:" << fb.trimmed(); + QMessageBox::information(this, tr("Feedback"), + QString::fromUtf8(fb)); + } + + port.close(); + QMessageBox::information(this, tr("Klaar"), + tr("JSON (%1 bytes) in %2 chunks geüpload naar %3") + .arg(totalLen) + .arg((totalLen + CHUNK_SIZE - 1)/CHUNK_SIZE) + .arg(portName)); +} + +void MainWindow::on_btnAddLoc_clicked() +{ + bool ok = false; + // 1) Vraag X en Y (int64_t) + QString xs = QInputDialog::getText(this, tr("Nieuw punt"), tr("X (microdegrees):"), QLineEdit::Normal, + QString(), &ok); + if (!ok) return; + int64_t x = xs.toLongLong(&ok); + if (!ok) { + QMessageBox::warning(this, tr("Ongeldige invoer"), tr("Geef een geldig getal op.")); + return; + } + + QString ys = QInputDialog::getText(this, tr("Nieuw punt"), tr("Y (microdegrees):"), QLineEdit::Normal, + QString(), &ok); + if (!ok) return; + int64_t y = ys.toLongLong(&ok); + if (!ok) { + QMessageBox::warning(this, tr("Ongeldige invoer"), tr("Geef een geldig getal op.")); + return; + } + + // cost (float) + double cost = QInputDialog::getDouble(this, tr("Nieuw punt"), tr("Cost:"), + 1.0, 0.0, 1e6, 2, &ok); + if (!ok) return; + + // mg_id (int) + int mg = QInputDialog::getInt(this, tr("Nieuw punt"), tr("mg_id:"), + 0, 0, std::numeric_limits::max(), 1, &ok); + if (!ok) return; + + allPoints.emplace_back(x, y, static_cast(cost), mg); + + populateLocationsTable(); + saveLocationsToFile(); +} + +void MainWindow::on_btnEditLoc_clicked() +{ + auto *tbl = m_ui->tableLocations; + int row = tbl->currentRow(); + if (row < 0 || row >= int(allPoints.size())) { + QMessageBox::warning(this, tr("Bewerken"), tr("Selecteer eerst een rij.")); + return; + } + + Point &p = allPoints[row]; + bool ok = false; + int64_t newX = 0, newY = 0; + double newCost = 0.0; + int newMg = 0; + + do { + QString xs = QInputDialog::getText( + this, + tr("Bewerk punt"), + tr("X (microdegrees):"), + QLineEdit::Normal, + QString::number(p.x), + &ok + ); + if (!ok) return; + newX = xs.toLongLong(&ok); + if (!ok) { + QMessageBox::warning(this, + tr("Ongeldige invoer"), + tr("Voer een geldig geheel getal in voor X.")); + } + } while (!ok); + + do { + QString ys = QInputDialog::getText( + this, + tr("Bewerk punt"), + tr("Y (microdegrees):"), + QLineEdit::Normal, + QString::number(p.y), + &ok + ); + if (!ok) return; + newY = ys.toLongLong(&ok); + if (!ok) { + QMessageBox::warning(this, + tr("Ongeldige invoer"), + tr("Voer een geldig geheel getal in voor Y.")); + } + } while (!ok); + + do { + double c = QInputDialog::getDouble( + this, + tr("Bewerk punt"), + tr("Cost:"), + double(p.cost), + 0.0, 1e6, 2, + &ok + ); + if (!ok) return; + newCost = c; + } while (!ok); + + do { + int m = QInputDialog::getInt( + this, + tr("Bewerk punt"), + tr("mg_id:"), + p.mg_id, + 0, std::numeric_limits::max(), + 1, + &ok + ); + if (!ok) return; + newMg = m; + } while (!ok); + + p.x = newX; + p.y = newY; + p.cost = static_cast(newCost); + p.mg_id = newMg; + + populateLocationsTable(); + saveLocationsToFile(); +} + + +void MainWindow::on_btnRemoveLoc_clicked() +{ + auto *tbl = m_ui->tableLocations; + int row = tbl->currentRow(); + if (row < 0 || row >= int(allPoints.size())) { + QMessageBox::warning(this, tr("Verwijderen"), tr("Selecteer eerst een rij.")); + return; + } + + if (QMessageBox::question(this, tr("Verwijderen"), + tr("Weet je zeker dat je punt %1 wilt verwijderen?") + .arg(row+1)) + != QMessageBox::Yes) + { + return; + } + + allPoints.erase(allPoints.begin() + row); + populateLocationsTable(); + saveLocationsToFile(); +} + +void MainWindow::loadDefaultPoints() +{ + allPoints = { + {51688573, 5287210, 2.0f, 0}, // Avans + {51690224, 5296625, 1.5f, 101}, // Jan de Groot + {51688460, 5303150, 2.0f, 112}, // Stadhuis + {51686200, 5304500, 1.5f, 1}, // Sint Jans Kathedraal + {51685051, 5289156, 2.0f, 9}, // Paleisbrug + {51684258, 5302611, 1.5f, 111}, // Zuidwal + {51691299, 5303950, 2.0f, 5}, // Arena + {51689428, 5310484, 1.5f, 100}, // Nationaal Carnavalsmuseum + {51695984, 5299074, 2.0f, 113}, // Tramkade + {51689124, 5303969, 1.5f, 108}, // De Markt + {51689619, 5299065, 2.0f, 114}, // Bolwerk + {51693002, 5301264, 1.5f, 115}, // VUE Cinema + {51697021, 5299328, 2.0f, 102}, // Bossche Brouwers + {51689724, 5300408, 1.5f, 116}, // Café Bar le Duc + {51686471, 5304106, 2.0f, 110}, // Museumkwartier + {51689471, 5303200, 1.5f, 109}, // Moriaan + {51689302, 5303396, 2.0f, 117}, // ’t Opkikkertje + {51695457, 5297448, 1.5f, 118}, // Verkadefabriek + {51694463, 5302862, 2.0f, 119}, // BHIC + {51690467, 5294925, 1.5f, 8}, // Station + {51683776, 5317938, 2.0f, 11}, // Zuiderpark + {51687561, 5305911, 1.5f, 120}, // Korte Putstraat + {51698630, 5292803, 1.5f, 121}, // Brabanthallen + {51688691, 5309001, 1.5f, 7}, // Café de Palm + {51687344, 5305871, 1.5f, 122}, // Bistro Tante Pietje + {51696264, 5307460, 1.5f, 10}, // Taxandriaplein Park + {51696501, 5312884, 1.5f, 123}, // IJzeren Vrouw Park + {51691911, 5286594, 1.5f, 2}, // Simon Stevinweg + {51690402, 5291740, 1.5f, 124}, // Hugo de Grootplein + {51685400, 5289354, 1.5f, 3}, // Kinepolis + {51690484, 5296206, 1.5f, 106}, // Gouden Draak + {51686618, 5308459, 1.5f, 125}, // Theater aan de Parade + {51686697, 5303137, 1.5f, 6} // Gemeentehuis + }; +} + +void MainWindow::populateLocationsTable() +{ + auto *tbl = m_ui->tableLocations; + tbl->clearContents(); + tbl->setRowCount(int(allPoints.size())); + for (int i = 0; i < int(allPoints.size()); ++i) { + const Point &p = allPoints[i]; + tbl->setItem(i, 0, new QTableWidgetItem(QString::number(p.x))); + tbl->setItem(i, 1, new QTableWidgetItem(QString::number(p.y))); + tbl->setItem(i, 2, new QTableWidgetItem(QString::number(p.cost))); + tbl->setItem(i, 3, new QTableWidgetItem(QString::number(p.mg_id))); + } +} + +void MainWindow::loadLocationsFromFile() +{ + QString path = QCoreApplication::applicationDirPath() + "/locations.json"; + + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) + return; + auto raw = f.readAll(); + f.close(); + + QJsonDocument doc = QJsonDocument::fromJson(raw); + if (!doc.isArray()) + return; + + allPoints.clear(); + for (auto v : doc.array()) { + if (!v.isObject()) continue; + auto o = v.toObject(); + int64_t x = qint64(o.value("x").toDouble()); + int64_t y = qint64(o.value("y").toDouble()); + float cost = float(o.value("cost").toDouble()); + int mg = o.value("mg_id").toInt(); + allPoints.emplace_back(x,y,cost,mg); + } +} + +void MainWindow::saveLocationsToFile() +{ + QString path = QCoreApplication::applicationDirPath() + "/locations.json"; + + + QJsonArray arr; + for (auto &p : allPoints) { + QJsonObject o; + o["x"] = double(p.x); + o["y"] = double(p.y); + o["cost"] = double(p.cost); + o["mg_id"]= p.mg_id; + arr.append(o); + } + QJsonDocument doc(arr); + + QFile f(path); + if (!f.open(QIODevice::WriteOnly|QIODevice::Truncate)) { + QMessageBox::warning(this, tr("Error"), + tr("Kan locaties niet opslaan in %1").arg(path)); + return; + } + f.write(doc.toJson()); + f.close(); +} + diff --git a/RoutesGenereren/mainwindow.h b/RoutesGenereren/mainwindow.h new file mode 100644 index 0000000..de28b76 --- /dev/null +++ b/RoutesGenereren/mainwindow.h @@ -0,0 +1,98 @@ +// Copyright (C) 2012 Denis Shienkov +// Copyright (C) 2012 Laszlo Papp +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "routegenerator.h" + +QT_BEGIN_NAMESPACE + +class QLabel; +class QTimer; + +namespace Ui { +class MainWindow; +} + +QT_END_NAMESPACE + +class Console; +class SettingsDialog; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_pushButton_clicked(); + void pushButton_2_clicked(); + + void openSerialPort(); + void closeSerialPort(); + void about(); + void writeData(const QByteArray &data); + void readData(); + + void handleError(QSerialPort::SerialPortError error); + void handleBytesWritten(qint64 bytes); + void handleWriteTimeout(); + + void on_btnRefreshRoutes_clicked(); + void on_btnRefreshPorts_clicked(); + void on_btnUploadSerial_clicked(); + + void on_btnAddLoc_clicked(); + void on_btnEditLoc_clicked(); + void on_btnRemoveLoc_clicked(); + +private: + void initActionsConnections(); + void showStatusMessage(const QString &message); + void showWriteError(const QString &message); + void drawRoutes(); + void refreshRouteList(); + void refreshSerialPorts(); + void loadDefaultPoints(); + void populateLocationsTable(); + void loadLocationsFromFile(); + void saveLocationsToFile(); + +protected: + void resizeEvent(QResizeEvent* event) override; + +private: + Ui::MainWindow *m_ui = nullptr; + QLabel *m_status = nullptr; + Console *m_console = nullptr; + SettingsDialog *m_settings = nullptr; + qint64 m_bytesToWrite = 0; + QTimer *m_timer = nullptr; + QSerialPort *m_serial = nullptr; + QString dataRead; + QString selectedNode; + QGraphicsScene *scene = nullptr; + +}; + +#endif // MAINWINDOW_H diff --git a/RoutesGenereren/mainwindow.ui b/RoutesGenereren/mainwindow.ui new file mode 100644 index 0000000..f79d2c2 --- /dev/null +++ b/RoutesGenereren/mainwindow.ui @@ -0,0 +1,346 @@ + + + MainWindow + + + + 0 + 0 + 886 + 539 + + + + Serial Terminal + + + + + + + 0 + + + + Genereer routes + + + + + 10 + 370 + 191 + 29 + + + + Genereer Routes + + + + + + 210 + 370 + 191 + 31 + + + + status + + + + + + 10 + 10 + 821 + 351 + + + + + + + Overzetten naar koffer + + + + + 60 + 170 + 711 + 27 + + + + + + + Laad Routes in + + + + + + + + + + Refresh Poort + + + + + + + + + + Upload Route + + + + + + + + + Beheer locaties + + + + + 60 + 50 + 431 + 301 + + + + 4 + + + 99 + + + + X + + + + + Y + + + + + Cost + + + + + mg_id + + + + + + + 590 + 90 + 80 + 25 + + + + Toevoegen + + + + + + 590 + 160 + 80 + 25 + + + + Bewerken + + + + + + 590 + 240 + 80 + 25 + + + + Verwijderen + + + + + + + + + + + 0 + 0 + 886 + 22 + + + + + Calls + + + + + + + + + Tools + + + + + + + Help + + + + + + + + + + + TopToolBarArea + + + false + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + &About + + + About program + + + Alt+A + + + + + About Qt + + + + + + :/images/connect.png:/images/connect.png + + + C&onnect + + + Connect to serial port + + + Ctrl+O + + + + + + :/images/disconnect.png:/images/disconnect.png + + + &Disconnect + + + Disconnect from serial port + + + Ctrl+D + + + + + + :/images/settings.png:/images/settings.png + + + &Configure + + + Configure serial port + + + Alt+C + + + + + + :/images/clear.png:/images/clear.png + + + C&lear + + + Clear data + + + Alt+L + + + + + + :/images/application-exit.png:/images/application-exit.png + + + &Quit + + + Ctrl+Q + + + + + + + + + diff --git a/RoutesGenereren/routegenerator.cpp b/RoutesGenereren/routegenerator.cpp new file mode 100644 index 0000000..fc4e537 --- /dev/null +++ b/RoutesGenereren/routegenerator.cpp @@ -0,0 +1,226 @@ +#include "routegenerator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +const int NUM_HOTPOINTS = 5; +const int NUM_ROUTES = 10; +const float MIN_ROUTE_TIME_MINUTES = 1.0f * 60.0f; // 60 min +const float MAX_ROUTE_TIME_MINUTES = 2.0f * 60.0f; // 120 min + +constexpr double METERS_PER_DEGREE = 111320.0; // gemiddeld +constexpr double WALKING_SPEED_M_PER_S = 1.4; // meter per seconde + + +Point::Point(int64_t x, int64_t y, float cost, int mg_id) + : x(x), y(y), cost(cost), mg_id(mg_id) +{} + +bool Point::operator==(const Point& other) const { + return x == other.x + && y == other.y + && cost == other.cost + && mg_id == other.mg_id; +} + +vector> routeIndices; +vector> selectedPoints; +vector allPoints; + +struct Edge { + int u, v; + float weight; +}; + +// Calculate distance in metres between two microdegree points +double geoDistanceMeters(const Point& a, const Point& b) { + double dx_deg = (a.x - b.x) * 1e-6; + double dy_deg = (a.y - b.y) * 1e-6; + double dx_m = dx_deg * METERS_PER_DEGREE; + double dy_m = dy_deg * METERS_PER_DEGREE; + return sqrt(dx_m*dx_m + dy_m*dy_m); +} + +// Calculate time in minutes between two points +double stepTimeMinutes(const Point& a, const Point& b) { + double dist_m = geoDistanceMeters(a, b); + return (dist_m / WALKING_SPEED_M_PER_S) / 60.0; +} + +int findSet(int x, vector& parent) { + return parent[x] == x ? x : parent[x] = findSet(parent[x], parent); +} + +float totalRouteCost(const vector& route, const vector& points) { + float total = 0.0f; + for (size_t i = 0; i + 1 < route.size(); ++i) { + total += static_cast(stepTimeMinutes(points[route[i]], points[route[i+1]])) + + points[route[i]].cost; + } + total += points[route.back()].cost; + return total; +} + +vector computeMST(vector& points) { + size_t n = points.size(); + vector edges, mst; + vector parent(n); + for (size_t i = 0; i < n; ++i) parent[i] = i; + + for (size_t i = 0; i < n; ++i) { + for (size_t j = i + 1; j < n; ++j) { + double d = geoDistanceMeters(points[i], points[j]); + edges.push_back({ static_cast(i), static_cast(j), static_cast(d) }); + } + } + sort(edges.begin(), edges.end(), [](auto& a, auto& b){ return a.weight < b.weight; }); + for (auto& e : edges) { + int u = findSet(e.u, parent); + int v = findSet(e.v, parent); + if (u != v) { + mst.push_back(e); + parent[u] = v; + if (mst.size() == n - 1) break; + } + } + return mst; +} + +void optimizeRoute(vector& route, vector& points) { + bool improved = true; + while (improved) { + improved = false; + for (size_t i = 1; i + 2 < route.size(); ++i) { + for (size_t j = i + 1; j + 1 < route.size(); ++j) { + double d1 = geoDistanceMeters(points[route[i-1]], points[route[i]]) + + geoDistanceMeters(points[route[j]], points[route[j+1]]); + double d2 = geoDistanceMeters(points[route[i-1]], points[route[j]]) + + geoDistanceMeters(points[route[i]], points[route[j+1]]); + if (d2 < d1) { + reverse(route.begin()+i, route.begin()+j+1); + improved = true; + } + } + } + } +} + +vector christofides(vector& points) { + auto mst = computeMST(points); + vector degree(points.size(), 0); + for (auto& e : mst) { + degree[e.u]++; degree[e.v]++; + } + vector odd; + for (size_t i = 0; i < points.size(); ++i) + if (degree[i] % 2) odd.push_back(static_cast(i)); + + vector matching; + while (odd.size() >= 2) { + int u = odd.back(); odd.pop_back(); + int v = odd.back(); odd.pop_back(); + matching.push_back({u, v, static_cast(geoDistanceMeters(points[u], points[v]))}); + } + + vector eulerian = mst; + eulerian.insert(eulerian.end(), matching.begin(), matching.end()); + + vector route; + vector used(points.size(), false); + route.push_back(0); + used[0] = true; + for (auto& e : eulerian) { + if (!used[e.v]) { + route.push_back(e.v); + used[e.v] = true; + } + } + route.push_back(route.front()); + optimizeRoute(route, points); + return route; +} + +void saveRoutesToFiles(const vector>& routes, + const vector>& selPts, + const vector& allPts) +{ + for (size_t i = 0; i < routes.size(); ++i) { + string filename = "Route_" + to_string(i+1) + ".json"; + ofstream file(filename); + if (!file) { + cerr << "Kon bestand niet openen: " << filename << "\n"; + continue; + } + file << "[\n"; + for (size_t j = 0; j < routes[i].size(); ++j) { + const Point& p = selPts[i][routes[i][j]]; + file << " { \"x\": " << p.x + << ", \"y\": " << p.y + << ", \"mg_id\": " << p.mg_id << " }" + << (j+1 < routes[i].size() ? "," : "") << "\n"; + } + file << "]\n"; + cout << "Opgeslagen in '" << filename << "'.\n"; + } +} + +void generate_routes(const std::vector &inputPoints) { + + std::vector allPoints = inputPoints; + + vector hotpoints(allPoints.begin(), allPoints.begin() + NUM_HOTPOINTS); + vector waypoints(allPoints.begin() + NUM_HOTPOINTS, allPoints.end()); + + random_device rd; + mt19937 g(rd()); + + selectedPoints.clear(); + routeIndices.clear(); + vector routeTimes; + bool valid = false; + + while (!valid) { + selectedPoints = vector>(NUM_ROUTES, hotpoints); + routeIndices.clear(); + routeTimes.clear(); + + shuffle(waypoints.begin(), waypoints.end(), g); + for (size_t i = 0; i < waypoints.size(); ++i) { + int rid = static_cast(i % NUM_ROUTES); + selectedPoints[rid].push_back(waypoints[i]); + for (int r = 0; r < NUM_ROUTES; ++r) { + if (rand() % 10 < 3) + selectedPoints[r].push_back(waypoints[i]); + } + } + + for (int i = 0; i < NUM_ROUTES; ++i) { + routeIndices.push_back(christofides(selectedPoints[i])); + routeTimes.push_back(totalRouteCost(routeIndices[i], selectedPoints[i])); + } + + valid = true; + for (auto t : routeTimes) { + if (t < MIN_ROUTE_TIME_MINUTES || t > MAX_ROUTE_TIME_MINUTES) { + valid = false; + break; + } + } + } + + for (int i = 0; i < NUM_ROUTES; ++i) { + cout << "Route " << i+1 << " tijd: " + << fixed << setprecision(1) << routeTimes[i] << " min\n"; + } + + saveRoutesToFiles(routeIndices, selectedPoints, allPoints); +} diff --git a/RoutesGenereren/routegenerator.h b/RoutesGenereren/routegenerator.h new file mode 100644 index 0000000..d0f8dc7 --- /dev/null +++ b/RoutesGenereren/routegenerator.h @@ -0,0 +1,32 @@ +// routegenerator.h + + +#ifndef ROUTEGENERATOR_H +#define ROUTEGENERATOR_H + +#include +#include + +struct Point { + int64_t x; + int64_t y; + int mg_id; + + float cost; + + Point(int64_t x = 0, int64_t y = 0, float cost = 1.0f, int mg_id = -1); + + bool operator==(const Point& other) const; + +}; + + +extern std::vector> routeIndices; +extern std::vector> selectedPoints; +extern std::vector allPoints; + + +void generate_routes(const std::vector& inputPoints); + + +#endif // ROUTEGENERATOR_H diff --git a/RoutesGenereren/settingsdialog.cpp b/RoutesGenereren/settingsdialog.cpp new file mode 100644 index 0000000..4935c18 --- /dev/null +++ b/RoutesGenereren/settingsdialog.cpp @@ -0,0 +1,177 @@ +// Copyright (C) 2012 Denis Shienkov +// Copyright (C) 2012 Laszlo Papp +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "settingsdialog.h" +#include "ui_settingsdialog.h" + +#include +#include +#include + +static const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog", "N/A"); + +SettingsDialog::SettingsDialog(QWidget *parent) : + QDialog(parent), + m_ui(new Ui::SettingsDialog), + m_intValidator(new QIntValidator(0, 4000000, this)) +{ + m_ui->setupUi(this); + + m_ui->baudRateBox->setInsertPolicy(QComboBox::NoInsert); + + connect(m_ui->applyButton, &QPushButton::clicked, + this, &SettingsDialog::apply); + connect(m_ui->serialPortInfoListBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &SettingsDialog::showPortInfo); + connect(m_ui->baudRateBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &SettingsDialog::checkCustomBaudRatePolicy); + connect(m_ui->serialPortInfoListBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &SettingsDialog::checkCustomDevicePathPolicy); + + fillPortsParameters(); + fillPortsInfo(); + + updateSettings(); +} + + +SettingsDialog::~SettingsDialog() +{ + delete m_ui; +} + +SettingsDialog::Settings SettingsDialog::settings() const +{ + return m_currentSettings; +} + +void SettingsDialog::showPortInfo(int idx) +{ + if (idx == -1) + return; + + const QString blankString = tr(::blankString); + + const QStringList list = m_ui->serialPortInfoListBox->itemData(idx).toStringList(); + m_ui->descriptionLabel->setText(tr("Description: %1").arg(list.value(1, blankString))); + m_ui->manufacturerLabel->setText(tr("Manufacturer: %1").arg(list.value(2, blankString))); + m_ui->serialNumberLabel->setText(tr("Serial number: %1").arg(list.value(3, blankString))); + m_ui->locationLabel->setText(tr("Location: %1").arg(list.value(4, blankString))); + m_ui->vidLabel->setText(tr("Vendor Identifier: %1").arg(list.value(5, blankString))); + m_ui->pidLabel->setText(tr("Product Identifier: %1").arg(list.value(6, blankString))); +} + +void SettingsDialog::apply() +{ + updateSettings(); + hide(); +} + +void SettingsDialog::checkCustomBaudRatePolicy(int idx) +{ + const bool isCustomBaudRate = !m_ui->baudRateBox->itemData(idx).isValid(); + m_ui->baudRateBox->setEditable(isCustomBaudRate); + if (isCustomBaudRate) { + m_ui->baudRateBox->clearEditText(); + QLineEdit *edit = m_ui->baudRateBox->lineEdit(); + edit->setValidator(m_intValidator); + } +} + +void SettingsDialog::checkCustomDevicePathPolicy(int idx) +{ + const bool isCustomPath = !m_ui->serialPortInfoListBox->itemData(idx).isValid(); + m_ui->serialPortInfoListBox->setEditable(isCustomPath); + if (isCustomPath) + m_ui->serialPortInfoListBox->clearEditText(); +} + +void SettingsDialog::fillPortsParameters() +{ + m_ui->baudRateBox->addItem(QStringLiteral("9600"), QSerialPort::Baud9600); + m_ui->baudRateBox->addItem(QStringLiteral("19200"), QSerialPort::Baud19200); + m_ui->baudRateBox->addItem(QStringLiteral("38400"), QSerialPort::Baud38400); + m_ui->baudRateBox->addItem(QStringLiteral("115200"), QSerialPort::Baud115200); + m_ui->baudRateBox->addItem(tr("Custom")); + + m_ui->dataBitsBox->addItem(QStringLiteral("5"), QSerialPort::Data5); + m_ui->dataBitsBox->addItem(QStringLiteral("6"), QSerialPort::Data6); + m_ui->dataBitsBox->addItem(QStringLiteral("7"), QSerialPort::Data7); + m_ui->dataBitsBox->addItem(QStringLiteral("8"), QSerialPort::Data8); + m_ui->dataBitsBox->setCurrentIndex(3); + + m_ui->parityBox->addItem(tr("None"), QSerialPort::NoParity); + m_ui->parityBox->addItem(tr("Even"), QSerialPort::EvenParity); + m_ui->parityBox->addItem(tr("Odd"), QSerialPort::OddParity); + m_ui->parityBox->addItem(tr("Mark"), QSerialPort::MarkParity); + m_ui->parityBox->addItem(tr("Space"), QSerialPort::SpaceParity); + + m_ui->stopBitsBox->addItem(QStringLiteral("1"), QSerialPort::OneStop); +#ifdef Q_OS_WIN + m_ui->stopBitsBox->addItem(tr("1.5"), QSerialPort::OneAndHalfStop); +#endif + m_ui->stopBitsBox->addItem(QStringLiteral("2"), QSerialPort::TwoStop); + + m_ui->flowControlBox->addItem(tr("None"), QSerialPort::NoFlowControl); + m_ui->flowControlBox->addItem(tr("RTS/CTS"), QSerialPort::HardwareControl); + m_ui->flowControlBox->addItem(tr("XON/XOFF"), QSerialPort::SoftwareControl); +} + +void SettingsDialog::fillPortsInfo() +{ + m_ui->serialPortInfoListBox->clear(); + const QString blankString = tr(::blankString); + const auto infos = QSerialPortInfo::availablePorts(); + + for (const QSerialPortInfo &info : infos) { + QStringList list; + const QString description = info.description(); + const QString manufacturer = info.manufacturer(); + const QString serialNumber = info.serialNumber(); + const auto vendorId = info.vendorIdentifier(); + const auto productId = info.productIdentifier(); + list << info.portName() + << (!description.isEmpty() ? description : blankString) + << (!manufacturer.isEmpty() ? manufacturer : blankString) + << (!serialNumber.isEmpty() ? serialNumber : blankString) + << info.systemLocation() + << (vendorId ? QString::number(vendorId, 16) : blankString) + << (productId ? QString::number(productId, 16) : blankString); + + m_ui->serialPortInfoListBox->addItem(list.constFirst(), list); + } + + m_ui->serialPortInfoListBox->addItem(tr("Custom")); +} + +void SettingsDialog::updateSettings() +{ + m_currentSettings.name = m_ui->serialPortInfoListBox->currentText(); + + if (m_ui->baudRateBox->currentIndex() == 4) { + m_currentSettings.baudRate = m_ui->baudRateBox->currentText().toInt(); + } else { + const auto baudRateData = m_ui->baudRateBox->currentData(); + m_currentSettings.baudRate = baudRateData.value(); + } + m_currentSettings.stringBaudRate = QString::number(m_currentSettings.baudRate); + + const auto dataBitsData = m_ui->dataBitsBox->currentData(); + m_currentSettings.dataBits = dataBitsData.value(); + m_currentSettings.stringDataBits = m_ui->dataBitsBox->currentText(); + + const auto parityData = m_ui->parityBox->currentData(); + m_currentSettings.parity = parityData.value(); + m_currentSettings.stringParity = m_ui->parityBox->currentText(); + + const auto stopBitsData = m_ui->stopBitsBox->currentData(); + m_currentSettings.stopBits = stopBitsData.value(); + m_currentSettings.stringStopBits = m_ui->stopBitsBox->currentText(); + + const auto flowControlData = m_ui->flowControlBox->currentData(); + m_currentSettings.flowControl = flowControlData.value(); + m_currentSettings.stringFlowControl = m_ui->flowControlBox->currentText(); + + m_currentSettings.localEchoEnabled = m_ui->localEchoCheckBox->isChecked(); +} diff --git a/RoutesGenereren/settingsdialog.h b/RoutesGenereren/settingsdialog.h new file mode 100644 index 0000000..fac74c4 --- /dev/null +++ b/RoutesGenereren/settingsdialog.h @@ -0,0 +1,63 @@ +// Copyright (C) 2012 Denis Shienkov +// Copyright (C) 2012 Laszlo Papp +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace Ui { +class SettingsDialog; +} + +class QIntValidator; + +QT_END_NAMESPACE + +class SettingsDialog : public QDialog +{ + Q_OBJECT + +public: + struct Settings { + QString name; + qint32 baudRate; + QString stringBaudRate; + QSerialPort::DataBits dataBits; + QString stringDataBits; + QSerialPort::Parity parity; + QString stringParity; + QSerialPort::StopBits stopBits; + QString stringStopBits; + QSerialPort::FlowControl flowControl; + QString stringFlowControl; + bool localEchoEnabled; + }; + + explicit SettingsDialog(QWidget *parent = nullptr); + ~SettingsDialog(); + + Settings settings() const; + +private slots: + void showPortInfo(int idx); + void apply(); + void checkCustomBaudRatePolicy(int idx); + void checkCustomDevicePathPolicy(int idx); + +private: + void fillPortsParameters(); + void fillPortsInfo(); + void updateSettings(); + +private: + Ui::SettingsDialog *m_ui = nullptr; + Settings m_currentSettings; + QIntValidator *m_intValidator = nullptr; +}; + +#endif // SETTINGSDIALOG_H diff --git a/RoutesGenereren/settingsdialog.ui b/RoutesGenereren/settingsdialog.ui new file mode 100644 index 0000000..bf7753a --- /dev/null +++ b/RoutesGenereren/settingsdialog.ui @@ -0,0 +1,177 @@ + + + SettingsDialog + + + + 0 + 0 + 308 + 322 + + + + Settings + + + + + + Select Parameters + + + + + + BaudRate: + + + + + + + + + + Data bits: + + + + + + + + + + Parity: + + + + + + + + + + Stop bits: + + + + + + + + + + Flow control: + + + + + + + + + + + + + Select Serial Port + + + + + + + + + Description: + + + + + + + Manufacturer: + + + + + + + Serial number: + + + + + + + Location: + + + + + + + Vendor ID: + + + + + + + Product ID: + + + + + + + + + + + + Qt::Horizontal + + + + 96 + 20 + + + + + + + + Apply + + + + + + + + + Additional options + + + + + + Local echo + + + true + + + + + + + + + + + diff --git a/RoutesGenereren/terminal.qrc b/RoutesGenereren/terminal.qrc new file mode 100644 index 0000000..0b49879 --- /dev/null +++ b/RoutesGenereren/terminal.qrc @@ -0,0 +1,9 @@ + + + images/connect.png + images/disconnect.png + images/application-exit.png + images/settings.png + images/clear.png + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..4ba9d38 --- /dev/null +++ b/main.cpp @@ -0,0 +1,310 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace sf; + +const int WIDTH = 800; +const int HEIGHT = 600; +const int NUM_HOTPOINTS = 5; +const int NUM_ROUTES = 10; +const float MAX_DISTANCE_RATIO = 1.10f; + +struct Point { + float x, y; + float cost = 1.0f; // standaard tijd op locatie in minuten +}; + +bool operator==(const Point& a, const Point& b) { + return a.x == b.x && a.y == b.y; +} + +struct Edge { + int u, v; + float weight; +}; + +int find(int x, vector& parent) { + if (parent[x] == x) return x; + return parent[x] = find(parent[x], parent); +} + +float distance(const Point& a, const Point& b) { + return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); +} + +float totalRouteCost(const vector& route, const vector& points) { + float total = 0.0f; + for (size_t i = 0; i < route.size() - 1; ++i) { + float dist = distance(points[route[i]], points[route[i + 1]]); + float walkTime = dist / 84.0f; + total += walkTime + points[route[i]].cost; + } + total += points[route.back()].cost; + return total; +} + +vector computeMST(vector& points) { + int n = points.size(); + vector edges, mst; + vector parent(n); + for (int i = 0; i < n; i++) parent[i] = i; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + edges.push_back({ i, j, distance(points[i], points[j]) }); + } + } + + sort(edges.begin(), edges.end(), [](Edge a, Edge b) { + return a.weight < b.weight; + }); + + for (const auto& edge : edges) { + int setU = find(edge.u, parent); + int setV = find(edge.v, parent); + if (setU != setV) { + mst.push_back(edge); + parent[setU] = setV; + if (mst.size() == n - 1) break; + } + } + + return mst; +} + +void optimizeRoute(vector& route, vector& points) { + bool improved = true; + while (improved) { + improved = false; + for (size_t i = 1; i < route.size() - 2; ++i) { + for (size_t j = i + 1; j < route.size() - 1; ++j) { + float d1 = distance(points[route[i - 1]], points[route[i]]) + + distance(points[route[j]], points[route[j + 1]]); + float d2 = distance(points[route[i - 1]], points[route[j]]) + + distance(points[route[i]], points[route[j + 1]]); + if (d2 < d1) { + reverse(route.begin() + i, route.begin() + j + 1); + improved = true; + } + } + } + } +} + +vector christofides(vector& points) { + vector mst = computeMST(points); + vector degree(points.size(), 0); + + for (auto& edge : mst) { + degree[edge.u]++; + degree[edge.v]++; + } + + vector oddNodes; + for (int i = 0; i < points.size(); i++) { + if (degree[i] % 2 == 1) oddNodes.push_back(i); + } + + vector matching; + while (!oddNodes.empty()) { + int u = oddNodes.back(); oddNodes.pop_back(); + int v = oddNodes.back(); oddNodes.pop_back(); + matching.push_back({ u, v, distance(points[u], points[v]) }); + } + + vector eulerianGraph = mst; + eulerianGraph.insert(eulerianGraph.end(), matching.begin(), matching.end()); + + vector route; + vector visited(points.size(), false); + + route.push_back(0); + visited[0] = true; + + for (auto& edge : eulerianGraph) { + if (!visited[edge.v]) { + route.push_back(edge.v); + visited[edge.v] = true; + } + } + + for (int i = 0; i < NUM_HOTPOINTS; i++) { + if (find(route.begin(), route.end(), i) == route.end()) { + int bestInsertPos = 1; + float bestIncrease = FLT_MAX; + for (size_t j = 1; j < route.size(); j++) { + float increase = distance(points[route[j - 1]], points[i]) + + distance(points[i], points[route[j]]) - + distance(points[route[j - 1]], points[route[j]]); + if (increase < bestIncrease) { + bestIncrease = increase; + bestInsertPos = j; + } + } + route.insert(route.begin() + bestInsertPos, i); + } + } + + if (route.front() != 0) route.insert(route.begin(), 0); + if (route.back() != 0) route.push_back(0); + + optimizeRoute(route, points); + + return route; +} + +void saveRoutesToFile(const vector>& routes, const vector>& selectedPoints, const vector& allPoints, const string& filename) { + ofstream file(filename); + if (!file.is_open()) { + cerr << "Kon bestand niet openen: " << filename << endl; + return; + } + + for (int i = 0; i < routes.size(); ++i) { + file << "Route " << i + 1 << ": "; + for (int index : routes[i]) { + auto it = find(allPoints.begin(), allPoints.end(), selectedPoints[i][index]); + if (it != allPoints.end()) { + int locationID = distance(allPoints.begin(), it); + file << locationID << " "; + } + else { + file << "X "; + } + } + file << endl; + } + + file.close(); + cout << "Routes opgeslagen in '" << filename << "'." << endl; +} + +void drawGraph(RenderWindow& window, vector& allPoints, vector>& routes, vector>& selectedPoints) { + window.clear(Color::Black); + + for (auto& p : allPoints) { + CircleShape shape(5); + shape.setPosition(p.x - 3, p.y - 3); + shape.setFillColor(Color::White); + window.draw(shape); + } + + vector routeColors = { + Color::Red, Color::Green, Color::Blue, Color::Cyan, + Color::Magenta, Color::Yellow, Color(255, 165, 0), + Color(128, 0, 128), Color(0, 255, 127), Color(255, 105, 180) + }; + + for (int i = 0; i < NUM_ROUTES; ++i) { + auto& route = routes[i]; + auto& points = selectedPoints[i]; + Color color = routeColors[i % routeColors.size()]; + + for (size_t j = 0; j < points.size(); ++j) { + CircleShape shape(6); + shape.setPosition(points[j].x - 3, points[j].y - 3); + shape.setFillColor(j < NUM_HOTPOINTS ? Color::Blue : color); + window.draw(shape); + } + + for (size_t j = 0; j < route.size() - 1; ++j) { + Vertex line[] = { + Vertex(Vector2f(points[route[j]].x, points[route[j]].y), color), + Vertex(Vector2f(points[route[j + 1]].x, points[route[j + 1]].y), color) + }; + window.draw(line, 2, Lines); + } + } + + window.display(); +} + +int main() { + RenderWindow window(VideoMode(WIDTH, HEIGHT), "Christofides Algorithm Visualization"); + + vector allPoints = { + {100, 100, 2.0}, {300, 200, 1.5}, {500, 400, 3.5}, {200, 500, 2.5}, {600, 100, 4.0}, + {150, 300, 1.0}, {400, 350, 1.0}, {250, 50, 3.0}, {550, 250, 2.0}, {700, 150, 2.0}, + {120, 500, 1.5}, {320, 270, 1.0}, {530, 420, 2.5}, {260, 430, 2.0}, {580, 130, 3.0}, + {110, 220, 1.0}, {350, 180, 1.5}, {480, 380, 2.5}, {230, 510, 1.0}, {630, 170, 3.0}, + {50, 450, 1.5}, {450, 100, 2.0}, {370, 320, 1.0}, {670, 220, 2.5}, {280, 370, 1.0}, + {90, 290, 1.0}, {490, 470, 3.5}, {310, 150, 1.0}, {590, 290, 1.5}, {710, 410, 3.0}, + {400, 500, 2.5}, {540, 350, 2.0}, {620, 200, 1.0}, {260, 260, 1.0}, {150, 400, 1.0}, + {340, 530, 2.0}, {680, 300, 2.5}, {130, 140, 1.5}, {470, 240, 1.0}, {700, 500, 3.0}, + {330, 440, 1.0}, {210, 360, 1.0}, {570, 450, 2.0}, {90, 190, 1.0}, {610, 110, 3.0}, + {250, 480, 1.5}, {370, 210, 1.0}, {560, 310, 2.5}, {720, 180, 1.0}, {430, 280, 1.0} + }; + + vector hotpoints(allPoints.begin(), allPoints.begin() + NUM_HOTPOINTS); + vector waypoints(allPoints.begin() + NUM_HOTPOINTS, allPoints.end()); + + random_device rd; + mt19937 g(rd()); + + vector> selectedPoints; + vector> routes; + vector routeTimes; + bool valid = false; + + while (!valid) { + selectedPoints = vector>(NUM_ROUTES, vector(hotpoints)); + routes.clear(); + routeTimes.clear(); + + shuffle(waypoints.begin(), waypoints.end(), g); + + for (size_t i = 0; i < waypoints.size(); ++i) { + int routeIndex = i % NUM_ROUTES; + selectedPoints[routeIndex].push_back(waypoints[i]); + for (int r = 0; r < NUM_ROUTES; ++r) { + if (rand() % 10 < 3) { + selectedPoints[r].push_back(waypoints[i]); + } + } + } + + for (int i = 0; i < NUM_ROUTES; ++i) { + // Zorg dat schoolpunt (100, 100) in elke lijst zit + if (find(selectedPoints[i].begin(), selectedPoints[i].end(), allPoints[0]) == selectedPoints[i].end()) { + selectedPoints[i].insert(selectedPoints[i].begin(), allPoints[0]); + } + + sort(selectedPoints[i].begin(), selectedPoints[i].end(), [](const Point& a, const Point& b) { + return tie(a.x, a.y) < tie(b.x, b.y); + }); + selectedPoints[i].erase(unique(selectedPoints[i].begin(), selectedPoints[i].end(), [](const Point& a, const Point& b) { + return a == b && !(a.x == 100 && a.y == 100); + }), selectedPoints[i].end()); + + routes.push_back(christofides(selectedPoints[i])); + routeTimes.push_back(totalRouteCost(routes.back(), selectedPoints[i])); + } + + float minTime = *min_element(routeTimes.begin(), routeTimes.end()); + float maxTime = *max_element(routeTimes.begin(), routeTimes.end()); + valid = (maxTime / minTime) <= MAX_DISTANCE_RATIO; + } + + for (int i = 0; i < NUM_ROUTES; ++i) { + cout << "Route " << i + 1 << " tijd: " << routeTimes[i] << " min" << endl; + } + + saveRoutesToFile(routes, selectedPoints, allPoints, "routes.txt"); + + while (window.isOpen()) { + Event event; + while (window.pollEvent(event)) { + if (event.type == Event::Closed) + window.close(); + } + drawGraph(window, allPoints, routes, selectedPoints); + } + + return 0; +}