current revision
This commit is contained in:
46
ui/LoggerUI.h
Normal file
46
ui/LoggerUI.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef LOGGERUI_H
|
||||
#define LOGGERUI_H
|
||||
|
||||
#include <QApplication>
|
||||
#include "debug/InfoWidget.h"
|
||||
#include <Indoor/misc/log/Logger.h>
|
||||
|
||||
/** send all log-messages to the UI */
|
||||
class LoggerUI : public Logger {
|
||||
|
||||
private:
|
||||
|
||||
InfoWidget* iw;
|
||||
|
||||
std::vector<QString> lines;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the main-menu to show the log within */
|
||||
LoggerUI(InfoWidget* iw) : iw(iw) {
|
||||
lines.push_back("");
|
||||
}
|
||||
|
||||
void add(const std::string& str, const bool nl) override {
|
||||
lines.back() += QString(str.c_str());
|
||||
if (nl) {lines.push_back("");}
|
||||
while(lines.size() > 4) {lines.erase(lines.begin());}
|
||||
QString qs = getStr();
|
||||
QMetaObject::invokeMethod(iw, "showLog", Qt::QueuedConnection, Q_ARG(const QString&, qs));
|
||||
QApplication::processEvents();
|
||||
//mm->showActivity(getStr());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
QString getStr() const {
|
||||
QString str;
|
||||
for (const QString& line : lines) {str += line + "\n";}
|
||||
str.remove(str.length()-1, 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // LOGGERUI_H
|
||||
@@ -2,33 +2,58 @@
|
||||
|
||||
#include <QResizeEvent>
|
||||
|
||||
#include "map/MapView.h"
|
||||
#include "map/3D/MapView3D.h"
|
||||
#include "map/2D/MapView2D.h"
|
||||
|
||||
#include "menu/MainMenu.h"
|
||||
#include "debug/SensorDataWidget.h"
|
||||
#include "debug/InfoWidget.h"
|
||||
#include "UIHelper.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
|
||||
setMinimumHeight(500);
|
||||
setMinimumWidth(500);
|
||||
|
||||
mapView = new MapView(this);
|
||||
mapView3D = new MapView3D(this);
|
||||
mapView2D = new MapView2D(this);
|
||||
|
||||
mainMenu = new MainMenu(this);
|
||||
sensorWidget = new SensorDataWidget(this);
|
||||
infoWidget = new InfoWidget(this);
|
||||
sensorWidget = new SensorDataWidget(this); sensorWidget->setVisible(false);
|
||||
|
||||
//sensorWidget->setVisible(false);
|
||||
showMaximized();
|
||||
|
||||
sleep(1);
|
||||
emit resizeEvent(nullptr);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent* event) {
|
||||
|
||||
const int w = event->size().width();
|
||||
const int h = event->size().height();
|
||||
const int w = this->width();
|
||||
const int h = this->height();
|
||||
|
||||
const int menuH = UIHelper::getMainMenuHeight(this);
|
||||
const int infoH = UIHelper::getInfoHeight(this);
|
||||
|
||||
int y = 0;
|
||||
|
||||
mainMenu->setGeometry(0,y,w,menuH); y += menuH;
|
||||
infoWidget->setGeometry(0,y,w,infoH); y += infoH;
|
||||
|
||||
mapView3D->setGeometry(0,y,w,h-y);
|
||||
mapView2D->setGeometry(0,y,w,h-y);
|
||||
|
||||
sensorWidget->setGeometry(0,y,w,h-y);
|
||||
|
||||
mainMenu->resizeEvent(event);
|
||||
mapView2D->resizeEvent(event);
|
||||
// infoWidget->resizeEvent(event);
|
||||
|
||||
mapView->setGeometry(0,0,w,h);
|
||||
mainMenu->setGeometry(0,0,w,64);
|
||||
sensorWidget->setGeometry(0,64,w,h-64);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class MapView;
|
||||
class MapView3D;
|
||||
class MapView2D;
|
||||
|
||||
class MainMenu;
|
||||
class SensorDataWidget;
|
||||
class InfoWidget;
|
||||
|
||||
class MainWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
@@ -17,21 +20,22 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
MapView* mapView = nullptr;
|
||||
MapView3D* mapView3D = nullptr;
|
||||
MapView2D* mapView2D = nullptr;
|
||||
|
||||
MainMenu* mainMenu = nullptr;
|
||||
InfoWidget* infoWidget = nullptr;
|
||||
SensorDataWidget* sensorWidget = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
MapView* getMapView() const {return mapView;}
|
||||
MapView3D* getMapView3D() const {return mapView3D;}
|
||||
MapView2D* getMapView2D() const {return mapView2D;}
|
||||
|
||||
MainMenu* getMainMenu() const {return mainMenu;}
|
||||
InfoWidget* getInfoWidget() const {return infoWidget;}
|
||||
SensorDataWidget* getSensorDataWidget() const {return sensorWidget;}
|
||||
|
||||
|
||||
// void setMapView(QWidget* widget) {mapView = widget; mapView->setParent(this);}
|
||||
// void setMainMenu(QWidget* widget) {mainMenu = widget; mainMenu->setParent(this);}
|
||||
// void setSensorWidget(QWidget* widget) {sensorWidget = widget; sensorWidget->setParent(this);}
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
39
ui/UIHelper.h
Normal file
39
ui/UIHelper.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef UIHELPER_H
|
||||
#define UIHELPER_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class UIHelper {
|
||||
|
||||
public:
|
||||
|
||||
static int getButtonSize(const QObject* window) {
|
||||
return isLarge(window) ? (48*2) : (48);
|
||||
}
|
||||
|
||||
static int getMainMenuHeight(const QObject* window) {
|
||||
return isLarge(window) ? (64*2) : (64);
|
||||
}
|
||||
|
||||
static int getInfoHeight(const QObject* window) {
|
||||
return isLarge(window) ? (70*2) : (70);
|
||||
}
|
||||
|
||||
static int getPlotHeight(const QObject* window) {
|
||||
return isLarge(window) ? (90*2) : (90);
|
||||
}
|
||||
|
||||
static int getWifiLabelDistY(const QObject* window) {
|
||||
return isLarge(window) ? (13*2) : (13);
|
||||
}
|
||||
static int getWifiLabelDistX(const QObject* window) {
|
||||
return isLarge(window) ? (150*2) : (150);
|
||||
}
|
||||
|
||||
static bool isLarge(const QObject* window) {
|
||||
return (((QWidget*)window)->height() > 1000);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // UIHELPER_H
|
||||
67
ui/debug/InfoWidget.cpp
Normal file
67
ui/debug/InfoWidget.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "InfoWidget.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include <Indoor/Assertions.h>
|
||||
|
||||
InfoWidget::InfoWidget(QWidget *parent) : QWidget(parent) {
|
||||
|
||||
//setMinimumHeight(32);
|
||||
//setMaximumHeight(32);
|
||||
|
||||
QGridLayout* lay = new QGridLayout(this);
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
lblActivity = new QLabel();
|
||||
lblActivity->setText("-");
|
||||
//lblActivity->setStyleSheet("QLabel { color : white; }");
|
||||
//lblActivity->setFont(QFont("courier", 9));
|
||||
lay->addWidget(lblActivity, row, col, 1,1,Qt::AlignLeft); ++row;
|
||||
lblActivity->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
|
||||
lblFilterTime = new QLabel();
|
||||
lblFilterTime->setText("-");
|
||||
//lblFilterTime->setStyleSheet("QLabel { color : white; }");
|
||||
//lblFilterTime->setFont(QFont("courier", 9));
|
||||
lay->addWidget(lblFilterTime, row, col, 1,1,Qt::AlignLeft); ++row;
|
||||
lblFilterTime->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
|
||||
// lblMapViewTime = new QLabel();
|
||||
// lblMapViewTime->setText("-");
|
||||
// //lblMapViewTime->setStyleSheet("QLabel { color : white; }");
|
||||
// //lblMapViewTime->setFont(QFont("courier", 9));
|
||||
// lay->addWidget(lblMapViewTime, row, col, 1,1,Qt::AlignLeft); ++row;
|
||||
// lblMapViewTime->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
|
||||
col = 1;
|
||||
row = 0;
|
||||
|
||||
lblLog = new QLabel(this);
|
||||
lblLog->setText("-");
|
||||
//lblLog->setStyleSheet("QLabel { color : white; }");
|
||||
lblLog->setFont(QFont("Arial", 8));
|
||||
lay->addWidget(lblLog, row, col, 3, 1);
|
||||
lblLog->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void InfoWidget::showActivity(const QString& act) {
|
||||
lblActivity->setText("Activity: " + act);
|
||||
}
|
||||
|
||||
void InfoWidget::showFilterTime(const QString& act) {
|
||||
lblFilterTime->setText("Filtering: " + act);
|
||||
}
|
||||
|
||||
void InfoWidget::showMapViewTime(const QString& act) {
|
||||
lblMapViewTime->setText("MapView: " + act);
|
||||
}
|
||||
|
||||
void InfoWidget::showLog(const QString& info) {
|
||||
lblLog->setText(info);
|
||||
}
|
||||
|
||||
35
ui/debug/InfoWidget.h
Normal file
35
ui/debug/InfoWidget.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef INFOWIDGET_H
|
||||
#define INFOWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLabel;
|
||||
|
||||
class InfoWidget : public QWidget {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
QLabel* lblActivity;
|
||||
QLabel* lblFilterTime;
|
||||
QLabel* lblMapViewTime;
|
||||
QLabel* lblLog;
|
||||
|
||||
public:
|
||||
|
||||
explicit InfoWidget(QWidget *parent = 0);
|
||||
|
||||
Q_INVOKABLE void showActivity(const QString& act);
|
||||
Q_INVOKABLE void showFilterTime(const QString& act);
|
||||
Q_INVOKABLE void showMapViewTime(const QString& act);
|
||||
Q_INVOKABLE void showLog(const QString& info);
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
};
|
||||
|
||||
#endif // INFOWIDGET_H
|
||||
@@ -6,7 +6,9 @@ PlotTurns::PlotTurns(QWidget *parent) : QWidget(parent) {
|
||||
setMinimumWidth(96);
|
||||
setMinimumHeight(96);
|
||||
|
||||
resize(96, 96);
|
||||
//setSizeIncrement(QSize(1,1));
|
||||
//setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding));
|
||||
// resize(96, 96);
|
||||
|
||||
// setMaximumWidth(64);
|
||||
// setMaximumHeight(64);
|
||||
|
||||
@@ -19,6 +19,8 @@ public:
|
||||
|
||||
void add(const Timestamp ts, const TurnData& data);
|
||||
|
||||
//QSize sizeHint() const {return QSize(96, 96);}
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -3,12 +3,18 @@
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStaticText>
|
||||
#include "../UIHelper.h"
|
||||
|
||||
PlotWiFiScan::PlotWiFiScan(QWidget *parent) : QWidget(parent) {
|
||||
|
||||
setMinimumWidth(96);
|
||||
setMinimumHeight(96);
|
||||
|
||||
|
||||
//setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
// setMaximumHeight(300);
|
||||
// sets
|
||||
//setAutoFillBackground(false);
|
||||
|
||||
}
|
||||
@@ -24,13 +30,15 @@ void PlotWiFiScan::paintEvent(QPaintEvent* evt) {
|
||||
(void) evt;
|
||||
QPainter p(this);
|
||||
|
||||
const int x0 = 4; const int xw = 150;
|
||||
const int x0 = 4; const int xw = UIHelper::getWifiLabelDistX(this->parent());
|
||||
const int y0 = 3;
|
||||
const int lh = 13;
|
||||
const int lh = UIHelper::getWifiLabelDistY(this->parent());
|
||||
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
|
||||
int w = width();
|
||||
int h = height();
|
||||
|
||||
p.fillRect(0,0,width(),height(),QColor(255,255,255,192));
|
||||
p.setPen(Qt::black);
|
||||
@@ -45,7 +53,7 @@ void PlotWiFiScan::paintEvent(QPaintEvent* evt) {
|
||||
std::string str = mac + ": " + std::to_string((int)m.getRSSI());
|
||||
p.drawStaticText(x, y, QStaticText(str.c_str()));
|
||||
y += lh;
|
||||
if (y > 90) {y = y0; x += xw;}
|
||||
if (y > this->height()-10) {y = y0; x += xw;}
|
||||
}
|
||||
|
||||
p.end();
|
||||
|
||||
@@ -9,10 +9,15 @@
|
||||
#include "PlotTurns.h"
|
||||
#include "PlotWiFiScan.h"
|
||||
|
||||
#include "../Settings.h"
|
||||
#include "../UIHelper.h"
|
||||
|
||||
template <typename Data> void removeOld(Data& data, const Timestamp limit) {
|
||||
if (data.size() < 2) {return;}
|
||||
while ( (data.back().key - data.front().key) > limit.ms()) {
|
||||
/** helper method to remove old entries */
|
||||
template <typename Data> void removeOld(Data& data, const Data& dataRef, const Timestamp limit) {
|
||||
if (data.size() == 0) {return;}
|
||||
if (dataRef.size() == 0) {return;}
|
||||
while ( (dataRef.back().key - data.front().key) > limit.ms()) {
|
||||
if (data.size() == 0) {return;}
|
||||
data.remove(0);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +46,7 @@ public:
|
||||
Timestamp lastRefresh;
|
||||
bool needsRefresh(const Timestamp ts) {
|
||||
const Timestamp diff = ts - lastRefresh;
|
||||
return (diff > Timestamp::fromMS(100));
|
||||
return (diff > Settings::SensorDebug::updateEvery);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +73,7 @@ public:
|
||||
steps.setColor(colors[2]);
|
||||
steps.setPointSize(8);
|
||||
pc.addPlot(&steps);
|
||||
const float s = 4.2;
|
||||
const float s = 4.8;
|
||||
const float ref = 9.81;
|
||||
pc.setValRange(Range(ref-s, ref+s));
|
||||
}
|
||||
@@ -89,10 +94,10 @@ public:
|
||||
|
||||
void limit() {
|
||||
const Timestamp limit = Timestamp::fromMS(3000);
|
||||
removeOld(line[0].getData(), limit);
|
||||
removeOld(line[1].getData(), limit);
|
||||
removeOld(line[2].getData(), limit);
|
||||
removeOld(steps.getData(), limit - Timestamp::fromMS(100)); // remove steps a little before. prevents errors
|
||||
removeOld(line[0].getData(), line[0].getData(), limit);
|
||||
removeOld(line[1].getData(), line[0].getData(), limit);
|
||||
removeOld(line[2].getData(), line[0].getData(), limit);
|
||||
removeOld( steps.getData(), line[0].getData(), limit); // remove steps a little before. prevents errors
|
||||
}
|
||||
|
||||
};
|
||||
@@ -102,7 +107,7 @@ class PlotGyro : public PlotXLines<3> {
|
||||
public:
|
||||
|
||||
PlotGyro(QWidget* parent) : PlotXLines(parent) {
|
||||
const float s = 1;
|
||||
const float s = 1.5;
|
||||
const float ref = 0;
|
||||
pc.setValRange(Range(ref-s, ref+s));
|
||||
}
|
||||
@@ -119,14 +124,14 @@ public:
|
||||
|
||||
void limit() {
|
||||
const Timestamp limit = Timestamp::fromMS(3000);
|
||||
removeOld(line[0].getData(), limit);
|
||||
removeOld(line[1].getData(), limit);
|
||||
removeOld(line[2].getData(), limit);
|
||||
removeOld(line[0].getData(), line[0].getData(), limit);
|
||||
removeOld(line[1].getData(), line[0].getData(), limit);
|
||||
removeOld(line[2].getData(), line[0].getData(), limit);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PlotBaro : public PlotXLines<1> {
|
||||
class PlotBaro : public PlotXLines<2> {
|
||||
|
||||
public:
|
||||
|
||||
@@ -135,55 +140,92 @@ public:
|
||||
}
|
||||
|
||||
void add(const Timestamp ts, const BarometerData& data) {
|
||||
|
||||
static int skip = 0;
|
||||
if ((++skip % 8) != 0) {return;}
|
||||
|
||||
addLineNode(ts, data.hPa, 0);
|
||||
if (needsRefresh(ts)) {
|
||||
limit();
|
||||
refresh(ts);
|
||||
}
|
||||
const float s = 0.5;
|
||||
const float s = 1.0;
|
||||
const float ref = line[0].getData().front().val;
|
||||
pc.setValRange(Range(ref-s, ref+s));
|
||||
|
||||
}
|
||||
|
||||
void add(const Timestamp ts, const ActivityData& data) {
|
||||
|
||||
static int skip = 0;
|
||||
if ((++skip % 8) != 0) {return;}
|
||||
|
||||
float offset = 0;
|
||||
switch(data.curActivity) {
|
||||
case ActivityButterPressure::Activity::DOWN: offset = -0.5; break;
|
||||
case ActivityButterPressure::Activity::UP: offset = +0.5; break;
|
||||
case ActivityButterPressure::Activity::STAY: offset = +0.1; break;
|
||||
}
|
||||
|
||||
addLineNode(ts, line[0].getData().front().val + offset, 1);
|
||||
if (needsRefresh(ts)) {
|
||||
limit();
|
||||
refresh(ts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void limit() {
|
||||
removeOld(line[0].getData(), Timestamp::fromMS(8000));
|
||||
// no limit!
|
||||
//removeOld(line[0].getData(), Timestamp::fromMS(15000)); // 15 second values
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PlotTurn : public QWidget {
|
||||
//class PlotTurn : public QWidget {
|
||||
|
||||
};
|
||||
//};
|
||||
|
||||
|
||||
|
||||
SensorDataWidget::SensorDataWidget(QWidget* parent) : QWidget(parent) {
|
||||
|
||||
QGridLayout* lay = new QGridLayout(this);
|
||||
|
||||
plotGyro = new PlotGyro(this);
|
||||
plotAcc = new PlotAcc(this);
|
||||
plotBaro = new PlotBaro(this);
|
||||
plotTurn = new PlotTurns(this);
|
||||
plotWiFi = new PlotWiFiScan(this);
|
||||
|
||||
lay->addWidget(plotGyro, 0, 0, 1, 4, Qt::AlignTop);
|
||||
lay->addWidget(plotAcc, 1, 0, 1, 4, Qt::AlignTop);
|
||||
lay->addWidget(plotBaro, 2, 0, 1, 4, Qt::AlignTop);
|
||||
lay->addWidget(plotTurn, 3, 0, 1, 1, Qt::AlignTop);
|
||||
lay->addWidget(plotWiFi, 3, 1, 1, 3, Qt::AlignTop);
|
||||
// layout setup
|
||||
lay = new QGridLayout(this);
|
||||
lay->addWidget(plotGyro, 0, 0, 1, 4);
|
||||
lay->addWidget(plotAcc, 1, 0, 1, 4);
|
||||
lay->addWidget(plotBaro, 2, 0, 1, 4);
|
||||
lay->addWidget(plotTurn, 3, 0, 1, 1);
|
||||
lay->addWidget(plotWiFi, 3, 1, 1, 3);
|
||||
|
||||
// lay->setRowStretch(0, 1);
|
||||
// lay->setRowStretch(1, 1);
|
||||
// lay->setRowStretch(2, 10);
|
||||
// lay->setRowStretch(3, 10);
|
||||
// lay->setVerticalSpacing(5);
|
||||
// lay->setHorizontalSpacing(5);
|
||||
// lay->setSizeConstraint(QGridLayout::SetDefaultConstraint);
|
||||
|
||||
// attach as listener to all sensors we want to debug
|
||||
SensorFactory::get().getAccelerometer().addListener(this);
|
||||
SensorFactory::get().getGyroscope().addListener(this);
|
||||
SensorFactory::get().getBarometer().addListener(this);
|
||||
SensorFactory::get().getSteps().addListener(this);
|
||||
SensorFactory::get().getTurns().addListener(this);
|
||||
SensorFactory::get().getWiFi().addListener(this);
|
||||
|
||||
//setAutoFillBackground(false);
|
||||
SensorFactory::get().getActivity().addListener(this);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SensorDataWidget::onSensorData(Sensor<AccelerometerData>* sensor, const Timestamp ts, const AccelerometerData& data) {
|
||||
(void) sensor;
|
||||
((PlotAcc*)plotAcc)->add(ts, data);
|
||||
@@ -205,6 +247,11 @@ void SensorDataWidget::onSensorData(Sensor<BarometerData>* sensor, const Timesta
|
||||
((PlotBaro*)plotBaro)->add(ts, data);
|
||||
}
|
||||
|
||||
void SensorDataWidget::onSensorData(Sensor<ActivityData>* sensor, const Timestamp ts, const ActivityData& data) {
|
||||
(void) sensor;
|
||||
((PlotBaro*)plotBaro)->add(ts, data);
|
||||
}
|
||||
|
||||
void SensorDataWidget::onSensorData(Sensor<TurnData>* sensor, const Timestamp ts, const TurnData& data) {
|
||||
(void) sensor;
|
||||
((PlotTurns*)plotTurn)->add(ts, data);
|
||||
@@ -214,4 +261,3 @@ void SensorDataWidget::onSensorData(Sensor<WiFiMeasurements>* sensor, const Time
|
||||
(void) sensor;
|
||||
((PlotWiFiScan*)plotWiFi)->add(ts, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
#include "../sensors/StepSensor.h"
|
||||
#include "../sensors/TurnSensor.h"
|
||||
#include "../sensors/WiFiSensor.h"
|
||||
#include "../sensors/ActivitySensor.h"
|
||||
|
||||
class PlotWidget;
|
||||
class QGridLayout;
|
||||
|
||||
/** debug display for sensor data */
|
||||
class SensorDataWidget :
|
||||
@@ -21,6 +23,7 @@ class SensorDataWidget :
|
||||
public SensorListener<AccelerometerData>,
|
||||
public SensorListener<GyroscopeData>,
|
||||
public SensorListener<BarometerData>,
|
||||
public SensorListener<ActivityData>,
|
||||
public SensorListener<StepData>,
|
||||
public SensorListener<TurnData>,
|
||||
public SensorListener<WiFiMeasurements> {
|
||||
@@ -28,6 +31,7 @@ class SensorDataWidget :
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
|
||||
SensorDataWidget(QWidget* parent);
|
||||
@@ -38,6 +42,7 @@ public:
|
||||
void onSensorData(Sensor<StepData>* sensor, const Timestamp ts, const StepData& data) override;
|
||||
void onSensorData(Sensor<TurnData>* sensor, const Timestamp ts, const TurnData& data) override;
|
||||
void onSensorData(Sensor<WiFiMeasurements>* sensor, const Timestamp ts, const WiFiMeasurements& data) override;
|
||||
void onSensorData(Sensor<ActivityData>* sensor, const Timestamp ts, const ActivityData& data) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -47,6 +52,8 @@ private:
|
||||
QWidget* plotTurn;
|
||||
QWidget* plotWiFi;
|
||||
|
||||
QGridLayout* lay;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "LoadSetupDialog.h"
|
||||
#include "../misc/fixc11.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
@@ -10,13 +11,12 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <Indoor/Assertions.h>
|
||||
|
||||
#include "../Config.h"
|
||||
#include "../Settings.h"
|
||||
|
||||
LoadSetupDialog::LoadSetupDialog() {
|
||||
|
||||
// the folder all map-setups reside within
|
||||
const std::string base = Config::getMapDir();
|
||||
const std::string base = Settings::Data::getMapDir();
|
||||
QDir mapFolder(QString(base.c_str()));
|
||||
|
||||
// sanity check. folder must exist
|
||||
|
||||
120
ui/map/2D/ColorPoints2D.h
Normal file
120
ui/map/2D/ColorPoints2D.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef COLORPOINTS2D_H
|
||||
#define COLORPOINTS2D_H
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
#include "Renderable2D.h"
|
||||
#include <Indoor/geo/Point3.h>
|
||||
|
||||
#include <Indoor/grid/Grid.h>
|
||||
|
||||
#include <KLib/math/filter/particles/Particle.h>
|
||||
#include "../nav/Node.h"
|
||||
#include "../nav/State.h"
|
||||
|
||||
/**
|
||||
* debug color points
|
||||
*/
|
||||
class ColorPoints2D : public Renderable2D {
|
||||
|
||||
private:
|
||||
|
||||
struct PT {
|
||||
Point3 pos;
|
||||
QColor color;
|
||||
PT(const Point3 pos, const QColor color) : pos(pos), color(color) {;}
|
||||
};
|
||||
|
||||
std::vector<PT> points;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
ColorPoints2D() {
|
||||
|
||||
}
|
||||
|
||||
void showGridImportance(Grid<MyGridNode>* grid) {
|
||||
|
||||
float min = +INFINITY;
|
||||
float max = -INFINITY;
|
||||
|
||||
for (const MyGridNode& n : *grid) {
|
||||
const float f = n.getWalkImportance();
|
||||
if (f < min) {min = f;}
|
||||
if (f > max) {max = f;}
|
||||
}
|
||||
|
||||
max = 1.2;
|
||||
|
||||
for (const MyGridNode& n : *grid) {
|
||||
const Point3 pt(n.x_cm/100.0f, n.y_cm/100.0f + 0.1f, n.z_cm/100.0f);
|
||||
const float f = n.getWalkImportance();
|
||||
float h = 0.66 - ((f-min)/(max-min)) * 0.66; // 0.66 is blue on the HSV-scale
|
||||
if (h < 0) {h = 0;}
|
||||
if (h > 1) {h = 1;}
|
||||
const QColor color = QColor::fromHsvF(h, 1, 1);
|
||||
points.push_back(PT(pt, color));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** NOTE: must be called from Qt's main thread! */
|
||||
template <typename T> void setFromParticles(const std::vector<K::Particle<T>>& particles) {
|
||||
|
||||
points.clear();
|
||||
|
||||
// group particles by grid-point
|
||||
std::unordered_map<GridPoint, float> weights;
|
||||
for (const K::Particle<T>& p : particles) {
|
||||
const GridPoint gp = p.state.position;
|
||||
if (weights.find(gp) != weights.end()) {continue;}
|
||||
weights[gp] += p.weight;
|
||||
}
|
||||
|
||||
// find min/max
|
||||
float min = +INFINITY;
|
||||
float max = -INFINITY;
|
||||
for (auto it : weights) {
|
||||
if (it.second > max) {max = it.second;}
|
||||
if (it.second < min) {min = it.second;}
|
||||
}
|
||||
|
||||
// draw colored
|
||||
for (auto it : weights) {
|
||||
const GridPoint gp = it.first;
|
||||
const float w = it.second;
|
||||
const float p = (w-min) / (max-min); // [0:1]
|
||||
const Point3 pt(gp.x_cm/100.0f, gp.y_cm/100.0f + 0.1f, gp.z_cm/100.0f);
|
||||
float h = 0.66 - (p*0.66); // 0.66 is blue on the HSV-scale
|
||||
const QColor color = QColor::fromHsvF(h, 1, 1);
|
||||
points.push_back(PT(pt, color));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void doRender(QPainter& qp, const Scaler2D& s, const RenderParams2D& r) override {
|
||||
|
||||
QPen pen;
|
||||
pen.setWidth(4);
|
||||
|
||||
for (const PT& pt : points) {
|
||||
|
||||
if (pt.pos.z < r.clip.belowHeight_m) {continue;}
|
||||
if (pt.pos.z > r.clip.aboveHeight_m) {continue;}
|
||||
|
||||
const Point2 p2 = s.mapToScreen(pt.pos.xy());
|
||||
|
||||
pen.setColor(pt.color);
|
||||
qp.setPen(pen);
|
||||
qp.drawPoint(p2.x, p2.y);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // COLORPOINTS2D_H
|
||||
62
ui/map/2D/Floor2D.h
Normal file
62
ui/map/2D/Floor2D.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef FLOOR2D_H
|
||||
#define FLOOR2D_H
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
#include "Renderable2D.h"
|
||||
|
||||
/**
|
||||
* draw the floor itself (outline, obstacles)
|
||||
*/
|
||||
class Floor2D : public Renderable2D {
|
||||
|
||||
private:
|
||||
|
||||
Floorplan::Floor* floor;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Floor2D(Floorplan::Floor* floor) : floor(floor) {
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void doRender(QPainter& qp, const Scaler2D& s, const RenderParams2D& r) override {
|
||||
|
||||
if (floor->atHeight < r.clip.belowHeight_m) {return;}
|
||||
if (floor->atHeight > r.clip.aboveHeight_m) {return;}
|
||||
|
||||
qp.setPen(Qt::black);
|
||||
for (const Floorplan::FloorObstacle* obs : floor->obstacles) {
|
||||
const Floorplan::FloorObstacleLine* line = dynamic_cast<const Floorplan::FloorObstacleLine*>(obs);
|
||||
if (line) {drawLine(qp, s, line);}
|
||||
}
|
||||
|
||||
qp.setPen(Qt::gray);
|
||||
for (const Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
drawOutline(qp, s, poly);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void drawLine(QPainter& qp, const Scaler2D& s, const Floorplan::FloorObstacleLine* line) {
|
||||
const Point2 pt1 = s.mapToScreen(line->from);
|
||||
const Point2 pt2 = s.mapToScreen(line->to);
|
||||
qp.drawLine(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||
}
|
||||
|
||||
void drawOutline(QPainter& qp, const Scaler2D& s, const Floorplan::FloorOutlinePolygon* poly) {
|
||||
const int num = poly->poly.points.size();
|
||||
for (int i = 0; i < num; ++i) {
|
||||
const Point2 pt1 = s.mapToScreen(poly->poly.points[(i+0)]);
|
||||
const Point2 pt2 = s.mapToScreen(poly->poly.points[(i+1)%num]);
|
||||
qp.drawLine(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // FLOOR2D_H
|
||||
20
ui/map/2D/HasSelectableNodes.h
Normal file
20
ui/map/2D/HasSelectableNodes.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef HASSELECTABLENODES_H
|
||||
#define HASSELECTABLENODES_H
|
||||
|
||||
#include <vector>
|
||||
#include <KLib/geo/Point2.h>
|
||||
|
||||
class HasSelectableNodes {
|
||||
|
||||
|
||||
public:
|
||||
|
||||
virtual ~HasSelectableNodes() {;}
|
||||
|
||||
virtual std::vector<Point2> getNodes() const = 0;
|
||||
|
||||
virtual void selectNode(const int idx) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // HASSELECTABLENODES_H
|
||||
184
ui/map/2D/MapView2D.cpp
Normal file
184
ui/map/2D/MapView2D.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "MapView2D.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include <QSlider>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
#include "Floor2D.h"
|
||||
#include "ColorPoints2D.h"
|
||||
#include "Path2D.h"
|
||||
#include "WiFiCalibTool.h"
|
||||
|
||||
#include "../../Icons.h"
|
||||
#include "../../UIHelper.h"
|
||||
|
||||
|
||||
MapView2D::MapView2D(QWidget *parent) : QWidget(parent) {
|
||||
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setRenderHeight(0);
|
||||
|
||||
colorPoints = new ColorPoints2D();
|
||||
elements.push_back(colorPoints);
|
||||
|
||||
pathToDest = new Path2D();
|
||||
pathToDest->setColor(Qt::blue);
|
||||
pathToDest->setWidth(10);
|
||||
elements.push_back(pathToDest);
|
||||
|
||||
pathWalked = new Path2D();
|
||||
pathToDest->setColor(Qt::black);
|
||||
pathToDest->setWidth(5);
|
||||
elements.push_back(pathWalked);
|
||||
|
||||
|
||||
// buttons
|
||||
//menu = new QWidget(this);
|
||||
QGridLayout* lay = new QGridLayout(this);
|
||||
int row = 0;
|
||||
|
||||
|
||||
lay->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding), row, 0, 1, 1);
|
||||
|
||||
++ row;
|
||||
|
||||
// // map-layer slider
|
||||
// sldLayer = new QSlider();
|
||||
// sldLayer->setOrientation(Qt::Horizontal);
|
||||
// connect(sldLayer, &QSlider::sliderMoved, this, &MapView2D::onLayerSelect);
|
||||
// connect(sldLayer, &QSlider::sliderReleased, this, &MapView2D::onLayerSelect);
|
||||
// lay->addWidget(sldLayer, row, 0, 1, 1);
|
||||
|
||||
// show/hide button
|
||||
const int bs = UIHelper::getButtonSize(this);
|
||||
btnColorPoints = new QPushButton(Icons::getIcon("dots", bs), "");
|
||||
btnColorPoints->connect(btnColorPoints, &QPushButton::clicked, [&] () {colorPoints->setVisible(!colorPoints->isVisible()); emit update();} );
|
||||
lay->addWidget(btnColorPoints, row, 0, 1, 1);
|
||||
|
||||
btnLayerMinus = new QPushButton("-");
|
||||
connect(btnLayerMinus, &QPushButton::clicked, this, &MapView2D::onLayerMinus);
|
||||
lay->addWidget(btnLayerMinus, row, 1, 1, 1);
|
||||
|
||||
btnLayerPlus = new QPushButton("+");
|
||||
connect(btnLayerPlus, &QPushButton::clicked, this, &MapView2D::onLayerPlus);
|
||||
lay->addWidget(btnLayerPlus, row, 2, 1, 1);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void MapView2D::onLayerSelect() {
|
||||
setRenderHeight(sldLayer->value());
|
||||
}
|
||||
|
||||
void MapView2D::onLayerMinus() {
|
||||
if (layerHeight_m <= 0) {return;}
|
||||
layerHeight_m -= 1;
|
||||
setRenderHeight(layerHeight_m);
|
||||
}
|
||||
void MapView2D::onLayerPlus() {
|
||||
if (layerHeight_m >= 16) {return;}
|
||||
layerHeight_m += 1;
|
||||
setRenderHeight(layerHeight_m);
|
||||
}
|
||||
|
||||
void MapView2D::setMap(WiFiCalibrationDataModel* mdl, Floorplan::IndoorMap* map) {
|
||||
|
||||
for (Floorplan::Floor* floor : map->floors) {
|
||||
Floor2D* f = new Floor2D(floor);
|
||||
elements.push_back(f);
|
||||
}
|
||||
|
||||
wifiCalib = new WiFiCalibTool(mdl, map);
|
||||
elements.push_back(wifiCalib);
|
||||
|
||||
scaler.setCenterM(Point2(70, 35));
|
||||
|
||||
}
|
||||
|
||||
void MapView2D::showParticles(const std::vector<K::Particle<MyState>>* particles) {
|
||||
this->colorPoints->setFromParticles(*particles);
|
||||
}
|
||||
|
||||
void MapView2D::setCurrentEstimation(const Point3 pos_m, const Point3 dir) {
|
||||
(void) dir;
|
||||
setRenderHeight(pos_m.z);
|
||||
scaler.setCenterM(pos_m.xy());
|
||||
}
|
||||
|
||||
void MapView2D::setRenderHeight(const float height_m) {
|
||||
renderParams.clip.aboveHeight_m = height_m + 1.5;
|
||||
renderParams.clip.belowHeight_m = height_m - 1.5;
|
||||
emit update();
|
||||
}
|
||||
|
||||
void MapView2D::showGridImportance(Grid<MyGridNode>* grid) {
|
||||
colorPoints->showGridImportance(grid);
|
||||
}
|
||||
|
||||
void MapView2D::resizeEvent(QResizeEvent* evt) {
|
||||
(void) evt;
|
||||
int s = UIHelper::getButtonSize(this->parent()) * 1.5;
|
||||
//sldLayer->setMinimumHeight(s);
|
||||
//sldLayer->setMinimum(0);
|
||||
//sldLayer->setMaximum(16);
|
||||
btnColorPoints->setMinimumHeight(s);
|
||||
btnColorPoints->setMinimumWidth(s);
|
||||
btnLayerMinus->setMinimumHeight(s);
|
||||
btnLayerMinus->setMinimumWidth(s);
|
||||
btnLayerPlus->setMinimumHeight(s);
|
||||
btnLayerPlus->setMinimumWidth(s);
|
||||
|
||||
scaler.setScreenSize(width(), height());
|
||||
scaler.setScale( UIHelper::isLarge(this->parent()) ? 2 : 1 );
|
||||
|
||||
}
|
||||
|
||||
void MapView2D::mousePressEvent(QMouseEvent* evt) {
|
||||
move.startCenter_px = scaler.getCenterPX();
|
||||
move.startMouse_px = Point2(evt->x(), evt->y());
|
||||
}
|
||||
|
||||
void MapView2D::mouseMoveEvent(QMouseEvent* evt) {
|
||||
Point2 pt(evt->x(), evt->y());
|
||||
pt -= move.startMouse_px;
|
||||
pt.x = -pt.x;
|
||||
pt += move.startCenter_px;
|
||||
scaler.setCenterPX(pt);
|
||||
emit update();
|
||||
}
|
||||
|
||||
void MapView2D::mouseReleaseEvent(QMouseEvent* evt) {
|
||||
|
||||
if (!wifiCalib) {return;}
|
||||
|
||||
const Point2 p1(evt->x(), evt->y());
|
||||
|
||||
int idx = 0;
|
||||
for (const Point2 p2 : wifiCalib->getNodes()) {
|
||||
const float dist = p1.getDistance(p2);
|
||||
if (dist < 25) { wifiCalib->selectNode(idx); emit update(); break; }
|
||||
++idx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MapView2D::paintEvent(QPaintEvent*) {
|
||||
|
||||
QPainter qp(this);
|
||||
|
||||
// clear
|
||||
qp.fillRect(0, 0, width(), height(), Qt::white);
|
||||
|
||||
// render elements
|
||||
for (Renderable2D* r : elements) {
|
||||
r->render(qp, scaler, renderParams);
|
||||
}
|
||||
|
||||
qp.end();
|
||||
|
||||
}
|
||||
131
ui/map/2D/MapView2D.h
Normal file
131
ui/map/2D/MapView2D.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#ifndef MAPVIEW2D_H
|
||||
#define MAPVIEW2D_H
|
||||
|
||||
#include "../misc/fixc11.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "Scaler2D.h"
|
||||
#include "Path2D.h"
|
||||
#include "Renderable2D.h"
|
||||
|
||||
#include <Indoor/nav/dijkstra/DijkstraPath.h>
|
||||
#include <Indoor/geo/Point3.h>
|
||||
|
||||
namespace Floorplan {
|
||||
class IndoorMap;
|
||||
}
|
||||
|
||||
template <typename T> class Grid;
|
||||
class MyGridNode;
|
||||
class Renderable2D;
|
||||
class QSlider;
|
||||
class QPushButton;
|
||||
class ColorPoints2D;
|
||||
class Path2D;
|
||||
|
||||
template <typename T> class DijkstraPath;
|
||||
namespace K {
|
||||
template <typename T> class Particle;
|
||||
}
|
||||
class MyState;
|
||||
|
||||
class WiFiCalibTool;
|
||||
class WiFiCalibrationDataModel;
|
||||
|
||||
class MapView2D : public QWidget {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
std::vector<Renderable2D*> elements;
|
||||
ColorPoints2D* colorPoints = nullptr;
|
||||
Path2D* pathToDest = nullptr;
|
||||
Path2D* pathWalked = nullptr;
|
||||
WiFiCalibTool* wifiCalib = nullptr;
|
||||
|
||||
Scaler2D scaler;
|
||||
RenderParams2D renderParams;
|
||||
float layerHeight_m = 0;
|
||||
|
||||
struct Move {
|
||||
Point2 startCenter_px;
|
||||
Point2 startMouse_px;
|
||||
} move;
|
||||
|
||||
QWidget* menu = nullptr;
|
||||
QSlider* sldLayer = nullptr;
|
||||
QPushButton* btnColorPoints = nullptr;
|
||||
QPushButton* btnLayerPlus = nullptr;
|
||||
QPushButton* btnLayerMinus = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
explicit MapView2D(QWidget *parent = 0);
|
||||
|
||||
/** set the to-be-shown map */
|
||||
void setMap(WiFiCalibrationDataModel* mdl, Floorplan::IndoorMap* map);
|
||||
|
||||
/** show importance factors for the grid */
|
||||
void showGridImportance(Grid<MyGridNode>* grid);
|
||||
|
||||
/** set the height-slice to be visible */
|
||||
void setRenderHeight(const float height_m);
|
||||
|
||||
|
||||
|
||||
/** set the path to the destination MUST BE CALLED FROM THE MAIN THREAD */
|
||||
void setPathToDestination(const std::vector<Point3>& path);
|
||||
|
||||
/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD */
|
||||
template <typename Node> void setPathToDestination(const DijkstraPath<Node>* path) {this->pathToDest->set(*path);}
|
||||
|
||||
/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD*/
|
||||
Q_INVOKABLE void setPathToDestination(const void* path) { setPathToDestination( (const DijkstraPath<MyGridNode>*) path); }
|
||||
|
||||
|
||||
|
||||
/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
|
||||
void setPathWalked(const std::vector<Point3>& path) {this->pathWalked->set(path);}
|
||||
|
||||
/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
|
||||
Q_INVOKABLE void setPathWalked(const void* path) { this->pathWalked->set( *((const std::vector<Point3>*) path)); }
|
||||
|
||||
|
||||
|
||||
/** NOTE: must be called from Qt's main thread! */
|
||||
Q_INVOKABLE void showParticles(const void* particles) {
|
||||
showParticles((const std::vector<K::Particle<MyState>>*) particles);
|
||||
}
|
||||
|
||||
/** NOTE: must be called from Qt's main thread! */
|
||||
void showParticles(const std::vector<K::Particle<MyState>>* particles);
|
||||
|
||||
|
||||
|
||||
/** set the currently estimated position */
|
||||
void setCurrentEstimation(const Point3 pos, const Point3 dir);
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
protected slots:
|
||||
|
||||
void onLayerSelect();
|
||||
void onLayerPlus();
|
||||
void onLayerMinus();
|
||||
|
||||
public slots:
|
||||
|
||||
void resizeEvent(QResizeEvent*);
|
||||
|
||||
void paintEvent(QPaintEvent*);
|
||||
|
||||
void mousePressEvent(QMouseEvent*);
|
||||
void mouseMoveEvent(QMouseEvent*);
|
||||
void mouseReleaseEvent(QMouseEvent*);
|
||||
|
||||
};
|
||||
|
||||
#endif // MAPVIEW2D_H
|
||||
123
ui/map/2D/Path2D.h
Normal file
123
ui/map/2D/Path2D.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef PATH2D_H
|
||||
#define PATH2D_H
|
||||
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
#include "Renderable2D.h"
|
||||
#include <Indoor/geo/Point3.h>
|
||||
#include <Indoor/nav/dijkstra/DijkstraPath.h>
|
||||
#include <Indoor/grid/Grid.h>
|
||||
|
||||
#include "../nav/Node.h"
|
||||
|
||||
|
||||
class Path2D : public Renderable2D {
|
||||
|
||||
private:
|
||||
|
||||
/** the path */
|
||||
std::vector<Point3> path;
|
||||
|
||||
float width = 8;
|
||||
QColor color = Qt::blue;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Path2D() {
|
||||
|
||||
}
|
||||
|
||||
template <typename Node> void set(const DijkstraPath<Node>& path) {
|
||||
|
||||
std::vector<Point3> out;
|
||||
for (const DijkstraNode<Node>* node : path.getVector()) {
|
||||
if (!node) {break;}
|
||||
const Node* elem = node->element;
|
||||
out.push_back(Point3(elem->x_cm/100.0f, elem->y_cm/100.0f, elem->z_cm/100.0f));
|
||||
}
|
||||
|
||||
set(out);
|
||||
|
||||
}
|
||||
|
||||
/** MUST BE CALLED FROM THE MAIN THREAD */
|
||||
void set(const std::vector<Point3>& path) {
|
||||
this->path = simplify(path);
|
||||
}
|
||||
|
||||
|
||||
/** combine nodes while the direction stays the same (many small quads -> one large quad) */
|
||||
std::vector<Point3> simplify(const std::vector<Point3>& path) {
|
||||
|
||||
// copy
|
||||
std::vector<Point3> out = path;
|
||||
|
||||
// remove unneccesary nodes
|
||||
for (int i = 1; i < (int) out.size() - 1; ++i) {
|
||||
|
||||
const Point3 pa = out[i-1];
|
||||
const Point3 pb = out[i-0];
|
||||
const Point3 pc = out[i+1];
|
||||
|
||||
// same direction as last segment? combine segments!
|
||||
const float dir1 = std::atan2(pb.y-pa.y, pb.x-pa.x); // last edge
|
||||
const float dir2 = std::atan2(pc.y-pb.y, pc.x-pb.x); // next edge
|
||||
const bool isSameDir = std::abs(dir1-dir2) < 0.03; // last-edge and next-edge have (approx) the same direction?
|
||||
if (isSameDir) {out.erase(out.begin()+i); --i; continue;} // no additional information! remove the center node
|
||||
|
||||
// too many changes in a small space? -> remove some!
|
||||
const float d1 = pb.getDistance(pa); // distance to last node
|
||||
const float d2 = pb.getDistance(pc); // distance to next node
|
||||
const float min = 1.0;
|
||||
const bool isPackedChange = d1 < min && d2 < min; // both distances below a threshold?
|
||||
if (isPackedChange) {out.erase(out.begin()+i); --i; continue;} // -> many changes in a small area -> remove current node!
|
||||
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** the color to use */
|
||||
void setColor(const QColor color) {
|
||||
this->color = color;
|
||||
}
|
||||
|
||||
/** the width to use */
|
||||
void setWidth(const float w) {
|
||||
this->width = w;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
void doRender(QPainter& qp, const Scaler2D& s, const RenderParams2D& r) override {
|
||||
|
||||
QPen pen;
|
||||
pen.setWidth(this->width);
|
||||
pen.setColor(color);
|
||||
qp.setPen(pen);
|
||||
|
||||
for (int i = 0; i < (int)path.size() - 1; ++i) {
|
||||
|
||||
const Point3 p1 = path[i];
|
||||
const Point3 p2 = path[i+1];
|
||||
|
||||
if (p1.z < r.clip.belowHeight_m && p2.z < r.clip.belowHeight_m) {continue;}
|
||||
if (p1.z > r.clip.aboveHeight_m && p2.z > r.clip.aboveHeight_m) {continue;}
|
||||
|
||||
const Point2 pa1 = s.mapToScreen(p1.xy());
|
||||
const Point2 pa2 = s.mapToScreen(p2.xy());
|
||||
|
||||
qp.drawLine(pa1.x, pa1.y, pa2.x, pa2.y);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // PATH2D_H
|
||||
13
ui/map/2D/RenderParams2D.h
Normal file
13
ui/map/2D/RenderParams2D.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef RENDERPARAMS2D_H
|
||||
#define RENDERPARAMS2D_H
|
||||
|
||||
struct RenderParams2D {
|
||||
|
||||
struct Clipping {
|
||||
float belowHeight_m;
|
||||
float aboveHeight_m;
|
||||
} clip;
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERPARAMS2D_H
|
||||
37
ui/map/2D/Renderable2D.h
Normal file
37
ui/map/2D/Renderable2D.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef RENDERABLE2D_H
|
||||
#define RENDERABLE2D_H
|
||||
|
||||
#include <QPainter>
|
||||
#include "Scaler2D.h"
|
||||
#include "RenderParams2D.h"
|
||||
|
||||
class Renderable2D {
|
||||
|
||||
private:
|
||||
|
||||
bool visible = true;
|
||||
|
||||
public:
|
||||
|
||||
virtual ~Renderable2D() {;}
|
||||
|
||||
/** show/hide this element */
|
||||
void setVisible(const bool visible) {this->visible = visible;}
|
||||
|
||||
/** is this element currently visible? */
|
||||
bool isVisible() const {return this->visible;}
|
||||
|
||||
/** render this element */
|
||||
void render(QPainter& qp, const Scaler2D& s, const RenderParams2D& p) {
|
||||
if (!visible) {return;}
|
||||
doRender(qp, s, p);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/** subclasses render themselves here */
|
||||
virtual void doRender(QPainter& qp, const Scaler2D& s, const RenderParams2D& p) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERABLE2D_H
|
||||
106
ui/map/2D/Scaler2D.h
Normal file
106
ui/map/2D/Scaler2D.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef SCALER2D_H
|
||||
#define SCALER2D_H
|
||||
|
||||
#include <QPoint>
|
||||
#include <Indoor/geo/Point2.h>
|
||||
|
||||
class Scaler2D {
|
||||
|
||||
private:
|
||||
|
||||
Point2 screenSize = Point2(400,400);
|
||||
Point2 center_m = Point2(0,0);
|
||||
float rotation_rad = 0.0f;
|
||||
float scaleFactor = 1;
|
||||
|
||||
public:
|
||||
|
||||
Scaler2D() {
|
||||
;
|
||||
}
|
||||
|
||||
/** set the screen's size (in pixel) */
|
||||
void setScreenSize(const float w_px, const float h_px) {
|
||||
this->screenSize = Point2(w_px, h_px);
|
||||
}
|
||||
|
||||
/** change the point displayed within the center of the screen */
|
||||
void setCenter(const float x_m, const float y_m) {
|
||||
this->center_m = Point2(x_m, y_m);
|
||||
}
|
||||
|
||||
/** change the point displayed within the center of the screen */
|
||||
void setCenterM(const Point2 center_m) {
|
||||
this->center_m = center_m;
|
||||
}
|
||||
|
||||
/** change the point displayed within the center of the screen */
|
||||
void setCenterPX(const Point2 center_px) {
|
||||
this->center_m = pxToM(center_px);
|
||||
}
|
||||
|
||||
Point2 getCenterPX() const {
|
||||
return mToPX(this->center_m);
|
||||
}
|
||||
|
||||
/** set the map's rotation in radians */
|
||||
void setRotation(const float rad) {
|
||||
this->rotation_rad = rad;
|
||||
}
|
||||
|
||||
void setScale(const float scale) {
|
||||
this->scaleFactor = scale;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
float mToPX(const float m) const {
|
||||
return m * scaleFactor * (screenSize.x * 0.01);
|
||||
}
|
||||
|
||||
float pxToM(const float px) const {
|
||||
return px / scaleFactor / (screenSize.x * 0.01);
|
||||
}
|
||||
|
||||
Point2 pxToM(const Point2 pt) const {
|
||||
return Point2(pxToM(pt.x), pxToM(pt.y));
|
||||
}
|
||||
|
||||
Point2 mToPX(const Point2 pt) const {
|
||||
return Point2(mToPX(pt.x), mToPX(pt.y));
|
||||
}
|
||||
|
||||
/** convert map to screen coordinates */
|
||||
Point2 mapToScreen(const Point2 pt_m) const {
|
||||
|
||||
Point2 pt = pt_m;
|
||||
|
||||
// move to (0,0)
|
||||
pt -= center_m;
|
||||
|
||||
// rotate
|
||||
pt = Point2(
|
||||
std::cos(rotation_rad) * pt.x - std::sin(rotation_rad) * pt.y,
|
||||
std::sin(rotation_rad) * pt.x + std::cos(rotation_rad) * pt.y
|
||||
);
|
||||
|
||||
// scale
|
||||
pt.x = mToPX(pt.x);
|
||||
pt.y = mToPX(pt.y);
|
||||
|
||||
|
||||
// add screen-center
|
||||
pt += screenSize/2;
|
||||
|
||||
// negate y
|
||||
pt.y = screenSize.y - pt.y;
|
||||
|
||||
// done
|
||||
return pt;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // SCALER2D_H
|
||||
129
ui/map/2D/WiFiCalibTool.h
Normal file
129
ui/map/2D/WiFiCalibTool.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef WIFICALIBTOOL_H
|
||||
#define WIFICALIBTOOL_H
|
||||
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
#include "Renderable2D.h"
|
||||
|
||||
#include "../../../Settings.h"
|
||||
#include "HasSelectableNodes.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPushButton>
|
||||
#include "../../../tools/calibration/WiFiCalibrationDataModel.h"
|
||||
#include "../../../tools/calibration/WiFiCalibrationScanDialog.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include "../ui/UIHelper.h"
|
||||
|
||||
#include "../../../sensors/SensorFactory.h"
|
||||
|
||||
struct WiFiCalibPoint {
|
||||
std::string name; // title
|
||||
Point3 pos_m; // map position + smartphone height
|
||||
Point2 pos_px; // screen position
|
||||
WiFiCalibPoint(const std::string& name, const Point3 pos_m, const Point2 pos_px) : name(name), pos_m(pos_m), pos_px(pos_px) {;}
|
||||
WiFiCalibPoint() {;}
|
||||
};
|
||||
|
||||
/**
|
||||
* helper for wifi calibration
|
||||
*/
|
||||
class WiFiCalibTool : public Renderable2D {
|
||||
|
||||
private:
|
||||
|
||||
WiFiCalibrationDataModel* mdl;
|
||||
Floorplan::IndoorMap* map;
|
||||
|
||||
std::vector<WiFiCalibPoint> currentlyVisible;
|
||||
WiFiCalibPoint currentlySelected;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
WiFiCalibTool(WiFiCalibrationDataModel* mdl, Floorplan::IndoorMap* map) : mdl(mdl), map(map) {
|
||||
|
||||
}
|
||||
|
||||
virtual void selectNode(const int idx) {
|
||||
currentlySelected = currentlyVisible[idx];
|
||||
showCalib(currentlySelected);
|
||||
}
|
||||
|
||||
/** get all selectable caliration nodes */
|
||||
virtual std::vector<Point2> getNodes() const {
|
||||
std::vector<Point2> pts;
|
||||
for (const WiFiCalibPoint& cp : currentlyVisible) {pts.push_back(cp.pos_px);}
|
||||
return pts;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void doRender(QPainter& qp, const Scaler2D& s, const RenderParams2D& r) override {
|
||||
|
||||
currentlyVisible.clear();
|
||||
const QFont font("Arial", 10);
|
||||
qp.setFont(font);
|
||||
|
||||
for (const Floorplan::Floor* floor : map->floors) {
|
||||
for (const Floorplan::Beacon* beacon : floor->beacons) {
|
||||
|
||||
const Point3 p = beacon->pos + Point3(0,0,floor->atHeight) + Point3(0,0,Settings::smartphoneAboveGround);
|
||||
const Point2 pt = s.mapToScreen(p.xy());
|
||||
|
||||
if (floor->atHeight < r.clip.belowHeight_m) {continue;}
|
||||
if (floor->atHeight > r.clip.aboveHeight_m) {continue;}
|
||||
|
||||
const WiFiCalibPoint cp(beacon->name, p, pt);
|
||||
currentlyVisible.push_back(cp);
|
||||
|
||||
const WiFiFingerprint& fp = mdl->getFingerprint(cp.pos_m);
|
||||
const QString txt1(beacon->name.c_str());
|
||||
const QString txt2 = QString::number(fp.measurements.entries.size());
|
||||
qp.setPen(Qt::black);
|
||||
|
||||
if (currentlySelected.name == cp.name) {
|
||||
qp.setBrush(Qt::blue);
|
||||
} else {
|
||||
qp.setBrush(Qt::NoBrush);
|
||||
}
|
||||
|
||||
//FONT SIZE??
|
||||
|
||||
|
||||
int s = 20;
|
||||
qp.drawEllipse(pt.x-s, pt.y-s, s*2, s*2);
|
||||
qp.drawText(pt.x+s*2, pt.y-s, txt1);
|
||||
qp.drawText(pt.x+s*2, pt.y+s, txt2);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
void showCalib(const WiFiCalibPoint& cp) {
|
||||
|
||||
// get (or create an empty one) the fingerprint for this location
|
||||
WiFiFingerprint& fp = mdl->getFingerprint(cp.pos_m);
|
||||
|
||||
// edit it (blocking!)
|
||||
WiFiCalibrationScanDialog::get(fp);
|
||||
|
||||
// save the model
|
||||
mdl->save();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // WIFICALIBTOOL_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "MapView.h"
|
||||
#include "MapView3D.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QGLShaderProgram>
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "elements/ColorPoints.h"
|
||||
#include "elements/Object.h"
|
||||
|
||||
#include "../Settings.h"
|
||||
|
||||
#include <Indoor/data/Timestamp.h>
|
||||
#include <QDebug>
|
||||
|
||||
@@ -22,34 +24,47 @@
|
||||
* which is already visible!
|
||||
*/
|
||||
|
||||
MapView::MapView(QWidget* parent) : QOpenGLWidget(parent) {
|
||||
MapView3D::MapView3D(QWidget* parent) : QOpenGLWidget(parent) {
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
void MapView::clear() {
|
||||
void MapView3D::clear() {
|
||||
|
||||
for (Renderable* r : elements) {delete r;}
|
||||
elements.clear();
|
||||
|
||||
}
|
||||
|
||||
void MapView::setMap(Floorplan::IndoorMap* map) {
|
||||
void MapView3D::setMap(Floorplan::IndoorMap* map) {
|
||||
|
||||
clear();
|
||||
|
||||
if (!isGLInitialized) {throw Exception("openGL is not yet initialized. add mapView to a visible window!");}
|
||||
|
||||
// first to be rendered
|
||||
// first to be rendered -> the colored debug points
|
||||
this->colorPoints = new ColorPoints();
|
||||
elements.push_back(this->colorPoints);
|
||||
|
||||
//leDude = new Object("/mnt/firma/tmp/3D/minion/minion.obj", "/mnt/firma/tmp/3D/minion/minion.png", "", 0.35);
|
||||
leDude = new Object("/mnt/firma/tmp/3D/gnome/gnome.obj", "/mnt/firma/tmp/3D/gnome/gnome_diffuse.jpg", "/mnt/firma/tmp/3D/gnome/gnome_normal.jpg", 0.033);
|
||||
//leDude = new Object("/mnt/firma/tmp/3D/squirrel/squirrel.obj", "/mnt/firma/tmp/3D/squirrel/squirrel.jpg", "/mnt/firma/tmp/3D/squirrel/squirrel_normal.jpg", 0.033);
|
||||
elements.push_back(leDude);
|
||||
// 2nd is the path to the destination (if any)
|
||||
this->pathToDest = new Path();
|
||||
elements.push_back(this->pathToDest);
|
||||
|
||||
this->pathWalked = new Path();
|
||||
this->pathWalked->setWidth(0.3);
|
||||
this->pathWalked->setColor(Qt::yellow);
|
||||
elements.push_back(this->pathWalked);
|
||||
|
||||
|
||||
|
||||
// 3rd is the dude, walking along the path
|
||||
//leDude = new Object("/mnt/firma/tmp/3D/minion/minion.obj", "/mnt/firma/tmp/3D/minion/minion.png", "", 0.35);
|
||||
//leDude = new Object("/mnt/firma/tmp/3D/gnome/gnome.obj", "/mnt/firma/tmp/3D/gnome/gnome_diffuse.jpg", "/mnt/firma/tmp/3D/gnome/gnome_normal.jpg", 0.033);
|
||||
//leDude = new Object("/mnt/firma/tmp/3D/squirrel/squirrel.obj", "/mnt/firma/tmp/3D/squirrel/squirrel.jpg", "/mnt/firma/tmp/3D/squirrel/squirrel_normal.jpg", 0.033);
|
||||
//elements.push_back(leDude);
|
||||
|
||||
// hereafter follows the floorplan (floors, doors, walls, stairs, ..)
|
||||
for (Floorplan::Floor* floor : map->floors) {
|
||||
elements.push_back(new Ground(floor));
|
||||
elements.push_back(new Walls(floor));
|
||||
@@ -58,8 +73,7 @@ void MapView::setMap(Floorplan::IndoorMap* map) {
|
||||
elements.push_back(new Doors(floor));
|
||||
}
|
||||
|
||||
this->path = new Path();
|
||||
elements.push_back(this->path);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -73,16 +87,20 @@ void MapView::setMap(Floorplan::IndoorMap* map) {
|
||||
|
||||
}
|
||||
|
||||
void MapView::setPath(const std::vector<Point3>& path) {
|
||||
this->path->set(path);
|
||||
void MapView3D::setPathToDestination(const std::vector<Point3>& path) {
|
||||
this->pathToDest->set(path);
|
||||
}
|
||||
|
||||
void MapView3D::setPathWalked(const std::vector<Point3>& path) {
|
||||
this->pathWalked->set(path);
|
||||
}
|
||||
|
||||
|
||||
void MapView::timerEvent(QTimerEvent *) {
|
||||
void MapView3D::timerEvent(QTimerEvent *) {
|
||||
update();
|
||||
}
|
||||
|
||||
void MapView::initializeGL() {
|
||||
void MapView3D::initializeGL() {
|
||||
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
@@ -91,21 +109,19 @@ void MapView::initializeGL() {
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
// start background update timer
|
||||
const int fps = 25;
|
||||
const int interval = 1000 / fps;
|
||||
timer.start(interval, this);
|
||||
timer.start(Settings::MapView::msPerFrame.ms(), this);
|
||||
|
||||
// OpenGL is now initialized
|
||||
isGLInitialized = true;
|
||||
|
||||
}
|
||||
|
||||
void MapView::paintGL() {
|
||||
void MapView3D::paintGL() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
draw();
|
||||
}
|
||||
|
||||
void MapView::resizeGL(int w, int h) {
|
||||
void MapView3D::resizeGL(int w, int h) {
|
||||
|
||||
// Calculate aspect ratio
|
||||
qreal aspect = qreal(w) / qreal(h ? h : 1);
|
||||
@@ -123,7 +139,7 @@ void MapView::resizeGL(int w, int h) {
|
||||
|
||||
}
|
||||
|
||||
void MapView::rebuildLookat() {
|
||||
void MapView3D::rebuildLookat() {
|
||||
// QVector3D qDir(lookAt.dir.x, lookAt.dir.z, lookAt.dir.y);
|
||||
// QVector3D at = QVector3D(lookAt.pos.x, lookAt.pos.z, lookAt.pos.y);
|
||||
// QVector3D eye = at + qDir * 0.1;
|
||||
@@ -136,12 +152,12 @@ void MapView::rebuildLookat() {
|
||||
// lightPos = eye + QVector3D(0.0, 4.0, 0.0);
|
||||
// eyePos = eye;
|
||||
|
||||
const Point3 dir = lookAt.getDir();
|
||||
const QVector3D qDir = lookAt.getDir();
|
||||
|
||||
QVector3D qDir(dir.x, dir.z, dir.y);
|
||||
QVector3D eye(lookAt.eye_m.x, lookAt.eye_m.z, lookAt.eye_m.y);
|
||||
QVector3D at = eye + qDir * 0.5;
|
||||
QVector3D up = QVector3D(0,1,0);
|
||||
//QVector3D qDir(dir.x, dir.z, dir.y);
|
||||
const QVector3D eye = lookAt.eye_m;//(lookAt.eye_m.x, lookAt.eye_m.z, lookAt.eye_m.y);
|
||||
const QVector3D at = eye + qDir * 0.5;
|
||||
const QVector3D up = QVector3D(0,1,0);
|
||||
matView.setToIdentity();
|
||||
matView.lookAt(eye, at, up);
|
||||
lightPos = eye + QVector3D(0.0, 0.5, 0.0) + qDir * 1.2;
|
||||
@@ -151,7 +167,7 @@ void MapView::rebuildLookat() {
|
||||
|
||||
}
|
||||
|
||||
void MapView::setCurrentEstimation(const Point3 pos, const Point3 dir) {
|
||||
void MapView3D::setCurrentEstimation(const Point3 pos, const Point3 dir) {
|
||||
const float angle = std::atan2(dir.y, dir.x) * 180 / M_PI;
|
||||
if (leDude) {
|
||||
leDude->setPosition(pos.x, pos.y, pos.z);
|
||||
@@ -159,72 +175,100 @@ void MapView::setCurrentEstimation(const Point3 pos, const Point3 dir) {
|
||||
}
|
||||
}
|
||||
|
||||
void MapView::setLookAt(const Point3 pos_m, const Point3 dir) {
|
||||
lookAt.eye_m = pos_m + dir * 0.1;
|
||||
lookAt.dir = dir;
|
||||
void MapView3D::setLookAt(const Point3 pos_m, const Point3 dir) {
|
||||
lookAt.eye_m = QVector3D(pos_m.x, pos_m.z, pos_m.y) + QVector3D(dir.x, dir.z, dir.y) * 0.1;
|
||||
lookAt.dir = QVector3D(dir.x, dir.z, dir.y);
|
||||
rebuildLookat();
|
||||
}
|
||||
|
||||
void MapView::setLookDir(const Point3 dir) {
|
||||
lookAt.dir = dir;
|
||||
void MapView3D::setLookDir(const Point3 dir) {
|
||||
lookAt.dir = QVector3D(dir.x, dir.z, dir.y);
|
||||
rebuildLookat();
|
||||
}
|
||||
|
||||
void MapView::setLookEye(const Point3 eye_m) {
|
||||
lookAt.eye_m = eye_m;
|
||||
void MapView3D::setLookEye(const Point3 eye_m) {
|
||||
lookAt.eye_m = QVector3D(eye_m.x, eye_m.z, eye_m.y);
|
||||
rebuildLookat();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MapView::mousePressEvent(QMouseEvent* evt) {
|
||||
void MapView3D::mousePressEvent(QMouseEvent* evt) {
|
||||
mouseState.down = true;
|
||||
mouseState.x = evt->x();
|
||||
mouseState.y = evt->y();
|
||||
}
|
||||
|
||||
void MapView::mouseMoveEvent(QMouseEvent* evt) {
|
||||
void MapView3D::mouseMoveEvent(QMouseEvent* evt) {
|
||||
|
||||
const float dx = evt->x() - mouseState.x;
|
||||
const float dy = evt->y() - mouseState.y;
|
||||
mouseState.x = evt->x();
|
||||
mouseState.y = evt->y();
|
||||
|
||||
// PI*0.3 head movement left/right and up/down
|
||||
const float yFac = (this->height() / 2) / (M_PI * 0.3);
|
||||
const float xFac = (this->width() / 2) / (M_PI * 0.3);
|
||||
//const float yFac = (this->height() / 2) / (M_PI * 0.3);
|
||||
//const float xFac = (this->width() / 2) / (M_PI * 0.3);
|
||||
|
||||
|
||||
const float angleX = dx/3.0f;
|
||||
const float angleY = dy/3.0f;
|
||||
lookAt.ax += angleX;
|
||||
lookAt.ay += angleY;
|
||||
lookAt.dirRot.setToIdentity();
|
||||
lookAt.dirRot.rotate(lookAt.ax, QVector3D(0,1,0));
|
||||
lookAt.dirRot.rotate(lookAt.ay, QVector3D(0,0,1));
|
||||
|
||||
//lookAt.dirRot.rotate(angleX, QVector3D(0,1,0));
|
||||
//lookAt.dirRot.rotate(angleY, QVector3D(0,0,1));
|
||||
|
||||
lookAt.dirOffset = Point3(0, std::sin(dx/xFac), std::sin(-dy/yFac));
|
||||
rebuildLookat();
|
||||
|
||||
}
|
||||
|
||||
void MapView::mouseReleaseEvent(QMouseEvent* evt) {
|
||||
void MapView3D::mouseReleaseEvent(QMouseEvent* evt) {
|
||||
(void) evt;
|
||||
mouseState.down = false;
|
||||
}
|
||||
|
||||
|
||||
void MapView::keyPressEvent(QKeyEvent* evt) {
|
||||
void MapView3D::keyPressEvent(QKeyEvent* evt) {
|
||||
|
||||
if (evt->key() == Qt::Key_W) {lookAt.eye_m += lookAt.getDir(); rebuildLookat();}
|
||||
if (evt->key() == Qt::Key_S) {lookAt.eye_m -= lookAt.getDir(); rebuildLookat();}
|
||||
const QVector3D dir = lookAt.getDir() * 0.5;
|
||||
const QVector3D side(-dir.z(), 0, dir.x());
|
||||
|
||||
if (evt->key() == Qt::Key_W) {lookAt.eye_m += dir; rebuildLookat();}
|
||||
if (evt->key() == Qt::Key_S) {lookAt.eye_m -= dir; rebuildLookat();}
|
||||
|
||||
if (evt->key() == Qt::Key_A) {lookAt.eye_m += side; rebuildLookat();}
|
||||
if (evt->key() == Qt::Key_D) {lookAt.eye_m -= side; rebuildLookat();}
|
||||
|
||||
}
|
||||
|
||||
void MapView::toggleRenderMode() {
|
||||
void MapView3D::toggleRenderMode() {
|
||||
|
||||
renderMode = (RenderMode) (((int)renderMode + 1) % 3);
|
||||
renderMode = (RenderMode) (((int)renderMode + 1) % 2); // normally %3 but lines crash the smartphone
|
||||
|
||||
for (Renderable* r : elements) {
|
||||
if (renderMode == RenderMode::OUTLINE) {
|
||||
r->setOutlineOnly(true);
|
||||
} else {
|
||||
r->setOutlineOnly(false);
|
||||
switch (renderMode) {
|
||||
case RenderMode::OUTLINE:
|
||||
r->setOutlineOnly(true);
|
||||
r->setTransparent(false);
|
||||
break;
|
||||
case RenderMode::TRANSPARENT:
|
||||
r->setOutlineOnly(false);
|
||||
r->setTransparent(true);
|
||||
break;
|
||||
case RenderMode::NORMAL:
|
||||
r->setOutlineOnly(false);
|
||||
r->setTransparent(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MapView::draw() {
|
||||
void MapView3D::draw() {
|
||||
|
||||
//const Timestamp ts1 = Timestamp::fromUnixTime();
|
||||
|
||||
@@ -232,17 +276,9 @@ void MapView::draw() {
|
||||
glClearColor(0,0,0,1);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
if (renderMode == RenderMode::TRANSPARENT) {
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
|
||||
|
||||
|
||||
for (Renderable* r : elements) {
|
||||
|
||||
QOpenGLShaderProgram& program = r->getProgram();
|
||||
@@ -255,7 +291,7 @@ void MapView::draw() {
|
||||
program.setUniformValue("lightWorldPos", lightPos);
|
||||
program.setUniformValue("eyeWorldPos", eyePos);
|
||||
|
||||
r->render();
|
||||
r->render(renderParams);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class Renderable;
|
||||
class Path;
|
||||
|
||||
|
||||
class MapView : public QOpenGLWidget, protected QOpenGLFunctions {
|
||||
class MapView3D : public QOpenGLWidget, protected QOpenGLFunctions {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
@@ -38,19 +38,26 @@ private:
|
||||
QVector3D lightPos;
|
||||
QVector3D eyePos;
|
||||
|
||||
RenderParams renderParams;
|
||||
|
||||
QBasicTimer timer;
|
||||
|
||||
std::vector<Renderable*> elements;
|
||||
Path* path = nullptr;
|
||||
Path* pathToDest = nullptr;
|
||||
Path* pathWalked = nullptr;
|
||||
ColorPoints* colorPoints = nullptr;
|
||||
Object* leDude = nullptr;
|
||||
|
||||
struct LookAt {
|
||||
Point3 eye_m = Point3(0,0,1);
|
||||
Point3 dir = Point3(1,0,-0.1);
|
||||
Point3 dirOffset = Point3(0,0,0);
|
||||
Point3 getDir() const {return dir + dirOffset;}
|
||||
QVector3D eye_m = QVector3D(0,0,1);
|
||||
QVector3D dir = QVector3D(1,0,-0.1);
|
||||
float ax = 0;
|
||||
float ay = 0;
|
||||
QMatrix4x4 dirRot;
|
||||
QVector3D getDir() const {return this->dirRot * this->dir;}
|
||||
LookAt() {
|
||||
dirRot.setToIdentity();
|
||||
}
|
||||
} lookAt;
|
||||
|
||||
struct MouseState {
|
||||
@@ -63,9 +70,10 @@ private:
|
||||
|
||||
void clear();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
MapView(QWidget* parent = 0);
|
||||
MapView3D(QWidget* parent = 0);
|
||||
|
||||
/** set the map to display */
|
||||
void setMap(Floorplan::IndoorMap* map);
|
||||
@@ -79,24 +87,33 @@ public:
|
||||
/** set the eye's position (looking from here) */
|
||||
void setLookEye(const Point3 eye_m);
|
||||
|
||||
/** do not render elements higher than the given height */
|
||||
void setClipAbove(const float height_m) {renderParams.clipAboveHeight_m = height_m;}
|
||||
|
||||
|
||||
/** set the currently estimated position */
|
||||
void setCurrentEstimation(const Point3 pos, const Point3 dir);
|
||||
|
||||
/** set the path to disply */
|
||||
void setPath(const std::vector<Point3>& path);
|
||||
|
||||
/** NOTE: must be called from Qt's main thread! */
|
||||
/** set the path to disply */
|
||||
Q_INVOKABLE void setPath(const void* path) {
|
||||
setPath( (const DijkstraPath<MyGridNode>*) path);
|
||||
}
|
||||
/** set the path to the destination MUST BE CALLED FROM THE MAIN THREAD */
|
||||
void setPathToDestination(const std::vector<Point3>& path);
|
||||
|
||||
/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD */
|
||||
template <typename Node> void setPathToDestination(const DijkstraPath<Node>* path) {this->pathToDest->set(*path);}
|
||||
|
||||
/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD*/
|
||||
Q_INVOKABLE void setPathToDestination(const void* path) { setPathToDestination( (const DijkstraPath<MyGridNode>*) path); }
|
||||
|
||||
|
||||
/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
|
||||
void setPathWalked(const std::vector<Point3>& path);
|
||||
|
||||
/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
|
||||
Q_INVOKABLE void setPathWalked(const void* path) { this->pathWalked->set( *((const std::vector<Point3>*) path)); }
|
||||
|
||||
|
||||
|
||||
|
||||
/** NOTE: must be called from Qt's main thread! */
|
||||
/** set the path to disply */
|
||||
template <typename Node> void setPath(const DijkstraPath<Node>* path) {
|
||||
this->path->set(*path);
|
||||
}
|
||||
|
||||
/** NOTE: must be called from Qt's main thread! */
|
||||
void showGridImportance(Grid<MyGridNode>* grid) {
|
||||
11
ui/map/3D/RenderParams.h
Normal file
11
ui/map/3D/RenderParams.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef RENDERPARAMS_H
|
||||
#define RENDERPARAMS_H
|
||||
|
||||
struct RenderParams {
|
||||
|
||||
/** clip (do not render) everything above this height */
|
||||
float clipAboveHeight_m = 99999;
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERPARAMS_H
|
||||
@@ -2,6 +2,7 @@
|
||||
#define RENDERABLE_H
|
||||
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include "RenderParams.h"
|
||||
|
||||
class Renderable {
|
||||
|
||||
@@ -18,9 +19,9 @@ public:
|
||||
QOpenGLShaderProgram& getProgram() {return program;}
|
||||
|
||||
/** render the renderable */
|
||||
void render() {
|
||||
void render(const RenderParams& params) {
|
||||
program.bind();
|
||||
_render();
|
||||
_render(params);
|
||||
}
|
||||
|
||||
struct ModelMatrix {
|
||||
@@ -54,11 +55,13 @@ public:
|
||||
modelMatrix.update();
|
||||
}
|
||||
|
||||
virtual void setOutlineOnly(const bool outline) {;}
|
||||
virtual void setOutlineOnly(const bool outline) { (void) outline; }
|
||||
|
||||
virtual void setTransparent(const bool transparent) { (void) transparent; }
|
||||
|
||||
virtual void initGL() = 0;
|
||||
|
||||
virtual void _render() = 0;
|
||||
virtual void _render(const RenderParams& params) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
|
||||
#include "../gl/GLHelper.h"
|
||||
#include "../gl/GLPoints.h"
|
||||
//#include "../gl/GLTriangles.h"
|
||||
#include "../Renderable.h"
|
||||
|
||||
#include "../../../nav/Node.h"
|
||||
#include "../../../../nav/Node.h"
|
||||
|
||||
class ColorPoints : public Renderable {
|
||||
|
||||
@@ -30,17 +31,28 @@ public:
|
||||
|
||||
points.clear();
|
||||
|
||||
float min = +INFINITY;
|
||||
float max = -INFINITY;
|
||||
|
||||
for (const MyGridNode& n : *grid) {
|
||||
const float f = n.getWalkImportance();
|
||||
if (f < min) {min = f;}
|
||||
if (f > max) {max = f;}
|
||||
}
|
||||
|
||||
max = 1.2;
|
||||
|
||||
for (const MyGridNode& n : *grid) {
|
||||
const QVector3D pt(n.x_cm/100.0f, n.z_cm/100.0f + 0.1f, n.y_cm/100.0f); // swap z and y
|
||||
const float f = n.getNavImportance();
|
||||
float h = 0.66 - (f*0.20); // 0.66 is blue on the HSV-scale
|
||||
const float f = n.getWalkImportance();
|
||||
float h = 0.66 - ((f-min)/(max-min)) * 0.66; // 0.66 is blue on the HSV-scale
|
||||
if (h < 0) {h = 0;}
|
||||
if (h > 1) {h = 1;}
|
||||
const QColor color = QColor::fromHsvF(h, 1, 1);
|
||||
points.addPoint(pt, color);
|
||||
}
|
||||
|
||||
size = 3.0f;
|
||||
size = 8.0f;
|
||||
points.rebuild();
|
||||
|
||||
}
|
||||
@@ -54,6 +66,7 @@ public:
|
||||
std::unordered_map<GridPoint, float> weights;
|
||||
for (const K::Particle<T>& p : particles) {
|
||||
const GridPoint gp = p.state.position;
|
||||
if (weights.find(gp) != weights.end()) {continue;}
|
||||
weights[gp] += p.weight;
|
||||
}
|
||||
|
||||
@@ -84,11 +97,23 @@ public:
|
||||
// points.addPoint(pt, color);
|
||||
// }
|
||||
|
||||
size = 6.0f;
|
||||
size = 8.0f;
|
||||
points.rebuild();
|
||||
|
||||
}
|
||||
|
||||
// void addPoint(const QVector3D pt, const QColor color) {
|
||||
|
||||
// const float s = 5 / 100.0f;
|
||||
// const QVector3D col(color.redF(), color.greenF(), color.blueF());
|
||||
// const VertColor vc1(pt + QVector3D(-s, -s, 0), col);
|
||||
// const VertColor vc2(pt + QVector3D(+s, -s, 0), col);
|
||||
// const VertColor vc3(pt + QVector3D(+s, +s, 0), col);
|
||||
// const VertColor vc4(pt + QVector3D(-s, +s, 0), col);
|
||||
|
||||
// points.addQuad(vc1, vc2, vc3, vc4);
|
||||
|
||||
// }
|
||||
|
||||
void initGL() override {
|
||||
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentColorPoint.glsl");
|
||||
@@ -97,9 +122,11 @@ public:
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
void _render(const RenderParams& params) override {
|
||||
|
||||
(void) params;
|
||||
//glDisable(GL_DEPTH_TEST);
|
||||
//glPointSize()
|
||||
|
||||
#ifndef ANDROID
|
||||
glPointSize(size);
|
||||
#endif
|
||||
@@ -37,8 +37,13 @@ public:
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
void _render(const RenderParams& params) override {
|
||||
|
||||
// skip me?
|
||||
if (params.clipAboveHeight_m < floor->atHeight) {return;}
|
||||
|
||||
doors.render(&program);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "../gl/GLLines.h"
|
||||
#include "../Renderable.h"
|
||||
|
||||
#include "../../../lib/gpc/Polygon.h"
|
||||
#include "../../../../lib/gpc/Polygon.h"
|
||||
|
||||
class Ground : public Renderable {
|
||||
|
||||
@@ -19,7 +19,9 @@ private:
|
||||
GLTriangles<VertNormTexTan> ceiling;
|
||||
|
||||
GLLines outline;
|
||||
|
||||
bool outlineOnly = false;
|
||||
bool transparent = false;
|
||||
|
||||
public:
|
||||
|
||||
@@ -50,7 +52,19 @@ public:
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
void _render(const RenderParams& params) override {
|
||||
|
||||
// skip me?
|
||||
if (params.clipAboveHeight_m < floor->atHeight) {return;}
|
||||
|
||||
if (transparent) {
|
||||
program.setUniformValue("alpha", 0.5f);
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
program.setUniformValue("alpha", 1.0f);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
if (outlineOnly) {
|
||||
glLineWidth(5);
|
||||
outline.render(&program);
|
||||
@@ -58,10 +72,14 @@ public:
|
||||
flooring.render(&program);
|
||||
ceiling.render(&program);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
}
|
||||
|
||||
/** render only the outline? */
|
||||
void setOutlineOnly(const bool outline) override {
|
||||
(void) outline;
|
||||
// this->outlineOnly = outline;
|
||||
// if (outlineOnly) {
|
||||
// loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl");
|
||||
@@ -73,6 +91,10 @@ public:
|
||||
// }
|
||||
}
|
||||
|
||||
void setTransparent(const bool transparent) override {
|
||||
this->transparent = transparent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
@@ -32,7 +32,8 @@ public:
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
void _render(const RenderParams& params) override {
|
||||
if (params.clipAboveHeight_m < floor->atHeight) {return;}
|
||||
glLineWidth(2);
|
||||
lines.render(&program);
|
||||
}
|
||||
@@ -66,7 +66,8 @@ public:
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
void _render(const RenderParams& params) override {
|
||||
(void) params;
|
||||
triangles.render(&program);
|
||||
}
|
||||
|
||||
@@ -16,19 +16,24 @@ private:
|
||||
|
||||
GLTriangles<VertNormTex> lines;
|
||||
|
||||
/** path's width (in meter) */
|
||||
float width;
|
||||
|
||||
/** the path's color */
|
||||
QVector4D color;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Path() {
|
||||
;
|
||||
setColor(QVector4D(0.0, 0.4, 1.0, 0.6));
|
||||
setWidth(0.8);
|
||||
}
|
||||
|
||||
|
||||
void initGL() override {
|
||||
|
||||
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl");
|
||||
program.setUniformValue("color", QVector4D(0.0, 0.4, 1.0, 0.6));
|
||||
|
||||
//loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTexSimple.glsl");
|
||||
|
||||
lines.setDiffuse(":/res/gl/tex/arrows.png");
|
||||
@@ -36,15 +41,32 @@ public:
|
||||
|
||||
}
|
||||
|
||||
/** set the path's color */
|
||||
void setColor(const QVector4D& color) {
|
||||
this->color = color;
|
||||
}
|
||||
|
||||
/** set the path's color */
|
||||
void setColor(const QColor& color) {
|
||||
setColor(QVector4D(color.redF(), color.greenF(), color.blueF(), color.alphaF()));
|
||||
}
|
||||
|
||||
/** set the path's width (in meter) */
|
||||
void setWidth(const float width) {
|
||||
this->width = width;
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
lines.rebuild();
|
||||
glLineWidth(30);
|
||||
glEnable(GL_BLEND);
|
||||
void _render(const RenderParams& params) override {
|
||||
|
||||
(void) params;
|
||||
program.setUniformValue("color", color);
|
||||
|
||||
//glEnable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
lines.render(&program);
|
||||
glDisable(GL_BLEND);
|
||||
//glDisable(GL_BLEND);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
}
|
||||
@@ -59,95 +81,15 @@ public:
|
||||
out.push_back(Point3(elem->x_cm/100.0f, elem->y_cm/100.0f, elem->z_cm/100.0f));
|
||||
}
|
||||
|
||||
out = simplify(out);
|
||||
set(out);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
void setSimple(const std::vector<Point3>& path) {
|
||||
|
||||
|
||||
lines.clear();
|
||||
const float s = 0.5;
|
||||
|
||||
Point3 lastDir(0,0,0);
|
||||
std::vector<Floorplan::Quad3> quads;
|
||||
|
||||
for (int i = 1; i < (int) path.size(); ++i) {
|
||||
|
||||
Point3 pa = path[i-1];
|
||||
Point3 pb = path[i-0];
|
||||
const Point3 pc(0, 0, 1);
|
||||
|
||||
Point3 dir = pb - pa; dir /= dir.length();
|
||||
Point3 perb = cross(pa-pb, pc); perb /= perb.length();
|
||||
|
||||
const Point3 p1 = pa - perb*s;
|
||||
const Point3 p2 = pa + perb*s;
|
||||
const Point3 p3 = pb + perb*s;
|
||||
const Point3 p4 = pb - perb*s;
|
||||
|
||||
if (dir == lastDir) {
|
||||
quads.back().p3 = p3;
|
||||
quads.back().p4 = p4;
|
||||
} else {
|
||||
quads.push_back(Floorplan::Quad3(p1,p2,p3,p4));
|
||||
}
|
||||
|
||||
lastDir = dir;
|
||||
|
||||
// // produce a small gap between path-lines [will be filled with another quad!]
|
||||
// pa += dir * 0.6;
|
||||
// pb -= dir * 0.6;
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int) quads.size(); ++i) {
|
||||
|
||||
// add the line-segment
|
||||
const Floorplan::Quad3 q1 = quads[i];
|
||||
addQuad(q1);
|
||||
|
||||
// // construct the quad between adjacent segments
|
||||
// if (i < (int) quads.size() - 1) {
|
||||
// const Floorplan::Quad3 q2 = quads[i+1];
|
||||
// const Floorplan::Quad3 q3(q1.p3, q2.p2, q2.p1, q1.p4);
|
||||
// addQuad(q3);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/** combine nodes while the direction stays the same (many small quads -> one large quad) */
|
||||
std::vector<Point3> simplify(const std::vector<Point3>& path) {
|
||||
|
||||
std::vector<Point3> out;
|
||||
|
||||
Point3 lastDir(0,0,0);
|
||||
if (!path.empty()) {
|
||||
out.push_back(path.back());
|
||||
}
|
||||
|
||||
for (int i = path.size() - 1; i >= 1; --i) {
|
||||
|
||||
const Point3 pa = path[i-0];
|
||||
const Point3 pb = path[i-1];
|
||||
const Point3 dir = (pb - pa).normalized();
|
||||
|
||||
if (dir == lastDir) {
|
||||
out[out.size()-1] = pb;
|
||||
} else {
|
||||
out.push_back(pb);
|
||||
}
|
||||
|
||||
lastDir = dir;
|
||||
|
||||
}
|
||||
// copy
|
||||
std::vector<Point3> out = path;
|
||||
|
||||
// remove unneccesary nodes
|
||||
for (int i = 1; i < (int) out.size() - 1; ++i) {
|
||||
@@ -156,13 +98,18 @@ public:
|
||||
const Point3 pb = out[i-0];
|
||||
const Point3 pc = out[i+1];
|
||||
|
||||
const float min = 0.6;
|
||||
const float d1 = pb.getDistance(pa);
|
||||
const float d2 = pb.getDistance(pc);
|
||||
// same direction as last segment? combine segments!
|
||||
const float dir1 = std::atan2(pb.y-pa.y, pb.x-pa.x); // last edge
|
||||
const float dir2 = std::atan2(pc.y-pb.y, pc.x-pb.x); // next edge
|
||||
const bool isSameDir = std::abs(dir1-dir2) < 0.03; // last-edge and next-edge have (approx) the same direction?
|
||||
if (isSameDir) {out.erase(out.begin()+i); --i; continue;} // no additional information! remove the center node
|
||||
|
||||
if (d1 < min || d2 < min) {
|
||||
out.erase(out.begin() + i);
|
||||
}
|
||||
// too many changes in a small space? -> remove some!
|
||||
const float d1 = pb.getDistance(pa); // distance to last node
|
||||
const float d2 = pb.getDistance(pc); // distance to next node
|
||||
const float min = 0.8;
|
||||
const bool isPackedChange = d1 < min && d2 < min; // both distances below a threshold?
|
||||
if (isPackedChange) {out.erase(out.begin()+i); --i; continue;} // -> many changes in a small area -> remove current node!
|
||||
|
||||
}
|
||||
|
||||
@@ -170,61 +117,16 @@ public:
|
||||
|
||||
}
|
||||
|
||||
// void set(const std::vector<Point3>& path) {
|
||||
|
||||
// lines.clear();
|
||||
|
||||
// const float s = 0.4;
|
||||
// std::vector<Point3> pts;
|
||||
|
||||
// for (int i = 0; i < (int) path.size(); ++i) {
|
||||
|
||||
// const Point3 pa = path[i-1];
|
||||
// const Point3 pb = path[i-0];
|
||||
// const Point3 pc(0, 0, 1);
|
||||
|
||||
// const Point3 perb = cross(pa-pb, pc).normalized();
|
||||
|
||||
// // quad's edges
|
||||
// const Point3 p1 = pa - perb*s;
|
||||
// const Point3 p2 = pa + perb*s;
|
||||
// const Point3 p3 = pb + perb*s;
|
||||
// const Point3 p4 = pb - perb*s;
|
||||
|
||||
// pts.push_back(p1);
|
||||
// pts.push_back(p2);
|
||||
|
||||
// }
|
||||
|
||||
// std::vector<Floorplan::Quad3> quads;
|
||||
// for (int i = 0; i < (int) pts.size(); i+=2) {
|
||||
// quads.push_back(Floorplan::Quad3(pts[i+0], pts[i+1], pts[i+3], pts[i+2]));
|
||||
// }
|
||||
|
||||
// float l1 = 0;
|
||||
// float l2 = 0;
|
||||
|
||||
// for (int i = 0; i < (int) quads.size(); ++i) {
|
||||
|
||||
// // add the line-segment
|
||||
// const Floorplan::Quad3 q1 = quads[i];
|
||||
// l2 += ((q1.p1 + q1.p2) / 2).getDistance( (q1.p3 + q1.p4) / 2 );
|
||||
// addQuad(q1, l1, l2);
|
||||
// l1 = l2;
|
||||
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
void set(const std::vector<Point3>& path) {
|
||||
/** MUST BE CALLED FROM THE MAIN THREAD */
|
||||
void set(const std::vector<Point3>& _path) {
|
||||
|
||||
std::vector<Point3> path = simplify(_path);
|
||||
|
||||
// reset
|
||||
lines.clear();
|
||||
|
||||
// half the width of the path
|
||||
const float s = 0.4;
|
||||
const float s = this->width * 0.5;
|
||||
std::vector<Floorplan::Quad3> quads;
|
||||
|
||||
for (int i = 1; i < (int) path.size(); ++i) {
|
||||
@@ -275,6 +177,8 @@ public:
|
||||
|
||||
}
|
||||
|
||||
lines.rebuild();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -306,7 +210,6 @@ private:
|
||||
const VertNormTex vnt3(v3, n, tex3);
|
||||
const VertNormTex vnt4(v4, n, tex4);
|
||||
|
||||
|
||||
lines.addQuadCCW(vnt1, vnt2, vnt3, vnt4);
|
||||
|
||||
}
|
||||
@@ -40,7 +40,8 @@ public:
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
void _render(const RenderParams& params) override {
|
||||
(void) params;
|
||||
parts.render(&program);
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@ private:
|
||||
|
||||
GLTriangles<VertNormTexTan> triangles;
|
||||
GLLines outlines;
|
||||
|
||||
bool outlineOnly = false;
|
||||
bool transparent = false;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Walls(Floorplan::Floor* floor) : floor(floor) {
|
||||
|
||||
setOutlineOnly(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -42,13 +42,28 @@ public:
|
||||
}
|
||||
|
||||
/** render the floor */
|
||||
void _render() override {
|
||||
void _render(const RenderParams& params) override {
|
||||
|
||||
// skip me?
|
||||
if (params.clipAboveHeight_m < floor->atHeight) {return;}
|
||||
|
||||
if (transparent) {
|
||||
program.setUniformValue("alpha", 0.5f);
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
program.setUniformValue("alpha", 1.0f);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
if (outlineOnly) {
|
||||
glLineWidth(1);
|
||||
outlines.render(&program);
|
||||
} else {
|
||||
triangles.render(&program);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
}
|
||||
|
||||
/** render only the outline? */
|
||||
@@ -64,6 +79,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void setTransparent(const bool transparent) override {
|
||||
this->transparent = transparent;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
@@ -20,9 +20,13 @@ struct VertColor {
|
||||
int getVertOffset() const {return 0;}
|
||||
int getColorOffset() const {return sizeof(QVector3D);}
|
||||
int getTanOffset() const {throw "error";}
|
||||
int getNormOffset() const {throw "error";}
|
||||
int getTexOffset() const {throw "error";}
|
||||
bool operator == (const VertColor& o) const {return (vert == o.vert) && (color == o.color);}
|
||||
static bool hasTangent() {return false;}
|
||||
static bool hasColor() {return true;}
|
||||
static bool hasNormal() {return false;}
|
||||
static bool hasTexCoord() {return false;}
|
||||
};
|
||||
|
||||
struct VertNorm {
|
||||
@@ -32,9 +36,12 @@ struct VertNorm {
|
||||
int getVertOffset() const {return 0;}
|
||||
int getNormOffset() const {return sizeof(QVector3D);}
|
||||
int getTanOffset() const {throw "error";}
|
||||
int getColorOffset() const {throw "error";}
|
||||
bool operator == (const VertNorm& o) const {return (vert == o.vert) && (norm == o.norm);}
|
||||
static bool hasTangent() {return false;}
|
||||
|
||||
static bool hasNormal() {return true;}
|
||||
static bool hasTexCoord() {return false;}
|
||||
static bool hasColor() {return false;}
|
||||
};
|
||||
|
||||
struct VertNormTex {
|
||||
@@ -46,8 +53,12 @@ struct VertNormTex {
|
||||
int getNormOffset() const {return sizeof(QVector3D);}
|
||||
int getTexOffset() const {return sizeof(QVector3D)*2;}
|
||||
int getTanOffset() const {throw "error";}
|
||||
int getColorOffset() const {throw "error";}
|
||||
bool operator == (const VertNormTex& o) const {return (vert == o.vert) && (norm == o.norm) && (tex == o.tex);}
|
||||
static bool hasTangent() {return false;}
|
||||
static bool hasNormal() {return true;}
|
||||
static bool hasTexCoord() {return true;}
|
||||
static bool hasColor() {return false;}
|
||||
};
|
||||
|
||||
struct VertNormTexTan {
|
||||
@@ -60,8 +71,12 @@ struct VertNormTexTan {
|
||||
int getNormOffset() const {return sizeof(QVector3D);}
|
||||
int getTexOffset() const {return sizeof(QVector3D)*2;}
|
||||
int getTanOffset() const {return sizeof(QVector3D)*2 + sizeof(QVector2D);}
|
||||
int getColorOffset() const {throw "error";}
|
||||
bool operator == (const VertNormTexTan& o) const {return (vert == o.vert) && (norm == o.norm) && (tex == o.tex) && (tan == o.tan);}
|
||||
static bool hasTangent() {return true;}
|
||||
static bool hasNormal() {return true;}
|
||||
static bool hasTexCoord() {return true;}
|
||||
static bool hasColor() {return false;}
|
||||
};
|
||||
|
||||
#endif // HELPER_GL_H
|
||||
@@ -18,7 +18,8 @@ private:
|
||||
std::vector<VertColor> vertices;
|
||||
std::vector<GLuint> indices;
|
||||
|
||||
int mode = GL_POINTS;
|
||||
// use rectangles instead of GL_POINTS
|
||||
int mode = GL_TRIANGLES;
|
||||
bool initOnce = true;
|
||||
|
||||
public:
|
||||
@@ -35,9 +36,31 @@ public:
|
||||
|
||||
/** add a new face to this element */
|
||||
void addPoint(const QVector3D& pt, const QColor& color) {
|
||||
indices.push_back(vertices.size());
|
||||
QVector3D c(color.redF(), color.greenF(), color.blueF());
|
||||
vertices.push_back(VertColor(pt, c));
|
||||
|
||||
const QVector3D c(color.redF(), color.greenF(), color.blueF());
|
||||
|
||||
float s = 10 / 100.0f;
|
||||
|
||||
const VertColor vc1(pt + QVector3D(-s, 0, -s), c);
|
||||
const VertColor vc2(pt + QVector3D(+s, 0, -s), c);
|
||||
const VertColor vc3(pt + QVector3D(+s, 0, +s), c);
|
||||
const VertColor vc4(pt + QVector3D(-s, 0, +s), c);
|
||||
|
||||
const int start = vertices.size();
|
||||
|
||||
vertices.push_back(vc1);
|
||||
vertices.push_back(vc2);
|
||||
vertices.push_back(vc3);
|
||||
vertices.push_back(vc4);
|
||||
|
||||
indices.push_back(start + 3);
|
||||
indices.push_back(start + 2);
|
||||
indices.push_back(start + 1);
|
||||
|
||||
indices.push_back(start + 3);
|
||||
indices.push_back(start + 1);
|
||||
indices.push_back(start + 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ private:
|
||||
QOpenGLTexture* textures[4];
|
||||
|
||||
std::vector<T> vertices;
|
||||
std::vector<GLushort> indices;
|
||||
std::vector<GLuint> indices;
|
||||
|
||||
public:
|
||||
|
||||
@@ -123,6 +123,9 @@ public:
|
||||
/** render the element */
|
||||
void render(QOpenGLShaderProgram* program) {
|
||||
|
||||
// nothing to-do?
|
||||
if (indices.empty()) {return;}
|
||||
|
||||
// Tell OpenGL which VBOs to use
|
||||
arrayBuf.bind();
|
||||
indexBuf.bind();
|
||||
@@ -137,13 +140,17 @@ public:
|
||||
program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0]));
|
||||
|
||||
// Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
|
||||
int normLoc = program->attributeLocation("a_normal");
|
||||
program->enableAttributeArray(normLoc);
|
||||
program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0]));
|
||||
if (T::hasNormal()) {
|
||||
int normLoc = program->attributeLocation("a_normal");
|
||||
program->enableAttributeArray(normLoc);
|
||||
program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0]));
|
||||
}
|
||||
|
||||
int texcoordLocation = program->attributeLocation("a_texcoord");
|
||||
program->enableAttributeArray(texcoordLocation);
|
||||
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0]));
|
||||
if (T::hasTexCoord()) {
|
||||
int texcoordLocation = program->attributeLocation("a_texcoord");
|
||||
program->enableAttributeArray(texcoordLocation);
|
||||
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0]));
|
||||
}
|
||||
|
||||
// bind tangent data?
|
||||
if (T::hasTangent()) {
|
||||
@@ -152,12 +159,18 @@ public:
|
||||
program->setAttributeBuffer(tanLocation, GL_FLOAT, vertices[0].getTanOffset(), 3, sizeof(vertices[0]));
|
||||
}
|
||||
|
||||
// bind color data?
|
||||
if (T::hasColor()) {
|
||||
int colorLoc = program->attributeLocation("a_color");
|
||||
program->enableAttributeArray(colorLoc);
|
||||
program->setAttributeBuffer(colorLoc, GL_FLOAT, vertices[0].getColorOffset(), 3, sizeof(vertices[0]));
|
||||
}
|
||||
|
||||
// texture
|
||||
program->setUniformValue("texture", 0);
|
||||
|
||||
// Draw cube geometry using indices from VBO 1
|
||||
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0);
|
||||
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include <Indoor/Assertions.h>
|
||||
#include "../UIHelper.h"
|
||||
|
||||
MainMenu::MainMenu(QWidget* parent) : QWidget(parent) {
|
||||
|
||||
setMinimumHeight(64);
|
||||
|
||||
QGridLayout* lay = new QGridLayout(this);
|
||||
int row = 0;
|
||||
@@ -27,28 +28,50 @@ MainMenu::MainMenu(QWidget* parent) : QWidget(parent) {
|
||||
Assert::isTrue(connect(btnCamera, &QPushButton::clicked, this, &MainMenu::onCameraButton), "connect() failed");
|
||||
lay->addWidget(btnCamera, row, col, 1,1,Qt::AlignTop); ++col;
|
||||
|
||||
btnTransparent = getButton("cube");
|
||||
btnTransparent = getButton("wall");
|
||||
Assert::isTrue(connect(btnTransparent, &QPushButton::clicked, this, &MainMenu::onTransparentButton), "connect() failed");
|
||||
lay->addWidget(btnTransparent, row, col, 1,1,Qt::AlignTop); ++col;
|
||||
lay->addWidget(btnTransparent, row, col, 1,1,Qt::AlignTop); ++col;
|
||||
|
||||
btn3D = getButton("cube");
|
||||
Assert::isTrue(connect(btn3D, &QPushButton::clicked, this, &MainMenu::on3DButton), "connect() failed");
|
||||
lay->addWidget(btn3D, row, col, 1,1,Qt::AlignTop); ++col;
|
||||
|
||||
|
||||
btnStart = getButton("run");
|
||||
Assert::isTrue(connect(btnStart, &QPushButton::clicked, this, &MainMenu::onStartButton), "connect() failed");
|
||||
lay->addWidget(btnStart, row, col, 1,1,Qt::AlignTop); ++col;
|
||||
|
||||
}
|
||||
|
||||
static inline void setButtonSize(QPushButton* btn, int size) {
|
||||
const int border = 4;
|
||||
btn->setMinimumHeight(size+border);
|
||||
btn->setMaximumHeight(size+border);
|
||||
btn->setMinimumWidth(size+border);
|
||||
btn->setMaximumWidth(size+border);
|
||||
}
|
||||
|
||||
void MainMenu::resizeEvent(QEvent* evt) {
|
||||
|
||||
const int s = UIHelper::getButtonSize(this->parent());
|
||||
setButtonSize(btnLoadMap, s);
|
||||
setButtonSize(btnDebug, s);
|
||||
setButtonSize(btnCamera, s);
|
||||
setButtonSize(btnTransparent, s);
|
||||
setButtonSize(btnStart, s);
|
||||
setButtonSize(btn3D, s);
|
||||
|
||||
|
||||
}
|
||||
|
||||
QPushButton* MainMenu::getButton(const std::string& icon) {
|
||||
|
||||
const int size = 48;
|
||||
const int border = 4;
|
||||
const int size = UIHelper::getButtonSize(this->parent());
|
||||
|
||||
|
||||
QPushButton* btn = new QPushButton(Icons::getIcon(icon, size), "");
|
||||
btn->setIconSize(QSize(size,size));
|
||||
btn->setMinimumHeight(size+border);
|
||||
btn->setMaximumHeight(size+border);
|
||||
btn->setMinimumWidth(size+border);
|
||||
btn->setMaximumWidth(size+border);
|
||||
setButtonSize(btn, size);
|
||||
|
||||
return btn;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class MainMenu : public QWidget {
|
||||
@@ -13,6 +14,10 @@ public:
|
||||
/** ctor */
|
||||
explicit MainMenu(QWidget* parent);
|
||||
|
||||
public slots:
|
||||
|
||||
void resizeEvent(QEvent*);
|
||||
|
||||
signals:
|
||||
|
||||
void onLoadButton();
|
||||
@@ -20,6 +25,7 @@ signals:
|
||||
void onDebugButton();
|
||||
void onCameraButton();
|
||||
void onTransparentButton();
|
||||
void on3DButton();
|
||||
|
||||
private:
|
||||
|
||||
@@ -30,6 +36,8 @@ private:
|
||||
QPushButton* btnDebug;
|
||||
QPushButton* btnCamera;
|
||||
QPushButton* btnTransparent;
|
||||
QPushButton* btn3D;
|
||||
QLabel* lblLog;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user