From 1c3e619fe58aea3e0db818ef1d92fae7f186963f Mon Sep 17 00:00:00 2001 From: Paul Leroy Date: Fri, 28 Feb 2025 15:14:18 +0100 Subject: [PATCH] Wolman + Angles (dip and azimuth) implemented --- CMakeLists.txt | 7 +- images/dbImageSymbol.png | Bin 0 -> 471 bytes images/smallCSVFile.png | Bin 0 -> 421 bytes include/AnglesCustomPlot.h | 44 +++++ include/CMakeLists.txt | 6 +- include/{ActionA.h => G3PointAction.h} | 10 +- include/G3PointDialog.h | 6 +- ...3PointDisclaimer.h => G3PointDisclaimer.h} | 12 +- include/G3PointPlots.h | 37 ++++ include/GrainsAsEllipsoids.h | 1 + include/WolmanCustomPlot.h | 66 ++++++- qG3POINT.qrc | 2 + src/AnglesCustomPlot.cpp | 131 +++++++++++++ src/CMakeLists.txt | 6 +- src/G3Point.cpp | 2 +- src/{ActionA.cpp => G3PointAction.cpp} | 178 +++++------------- src/G3PointDialog.cpp | 31 ++- ...ntDisclaimer.cpp => G3PointDisclaimer.cpp} | 14 +- src/G3PointPlots.cpp | 166 ++++++++++++++++ src/GrainsAsEllipsoids.cpp | 8 +- src/WolmanCustomPlot.cpp | 27 ++- ui/AnglesCustomPlot.ui | 19 ++ ui/CMakeLists.txt | 7 +- ui/{qG3PointDialog.ui => G3PointDialog.ui} | 111 +++++++++-- ...ointDisclaimer.ui => G3PointDisclaimer.ui} | 0 ui/G3PointPlots.ui | 69 +++++++ ui/WolmanCustomPlot.ui | 13 ++ 27 files changed, 787 insertions(+), 186 deletions(-) create mode 100644 images/dbImageSymbol.png create mode 100644 images/smallCSVFile.png create mode 100644 include/AnglesCustomPlot.h rename include/{ActionA.h => G3PointAction.h} (93%) rename include/{qG3PointDisclaimer.h => G3PointDisclaimer.h} (59%) create mode 100644 include/G3PointPlots.h create mode 100644 src/AnglesCustomPlot.cpp rename src/{ActionA.cpp => G3PointAction.cpp} (92%) rename src/{qG3PointDisclaimer.cpp => G3PointDisclaimer.cpp} (73%) create mode 100644 src/G3PointPlots.cpp create mode 100644 ui/AnglesCustomPlot.ui rename ui/{qG3PointDialog.ui => G3PointDialog.ui} (80%) rename ui/{qG3PointDisclaimer.ui => G3PointDisclaimer.ui} (100%) create mode 100644 ui/G3PointPlots.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a7822e..09e9e7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,12 @@ if ( PLUGIN_G3POINT ) AddPlugin( NAME ${PROJECT_NAME} ) - set(QG3POINT_PLUGIN_VERSION "0.3+") +target_sources(G3PointPlugin + PRIVATE + ui/WolmanCustomPlot.ui +) + + set(QG3POINT_PLUGIN_VERSION "0.4") add_subdirectory( include ) add_subdirectory( src ) diff --git a/images/dbImageSymbol.png b/images/dbImageSymbol.png new file mode 100644 index 0000000000000000000000000000000000000000..bcafc7164d07a310b11bf0668b5c206f971fa5e1 GIT binary patch literal 471 zcmV;|0Vw{7P)@X087cVYWFFsscT`VqEi@{(tSPWK+!C-Z8aBy@mI5;n9FTC!hm;7HO z=Re=f&2C1lEcJv$IxIw7c51&@j>{ri!Hi5*%}%OXL0R^?O!O(V#>!6VWfIL;k5g5f zFee%#{t}p#N5h-qo%dNQ)FxDuYQR=dW7sYNu~=Oc28>_m?vy5BZfUMcRgvveKw>k2 z)#vxOxJP>4!|YLd`V>O^6sW2Z@fxFl4DR`i0YXBMo^HgLuL6=VL9Z`mFFO`NGWd_PS%qRPf`b*_m>_1i@({|FCtTSm-PSu N002ovPDHLkV1m|>$}9i? literal 0 HcmV?d00001 diff --git a/images/smallCSVFile.png b/images/smallCSVFile.png new file mode 100644 index 0000000000000000000000000000000000000000..37d95b0530946ca47b63d26a2e413891d8df0f77 GIT binary patch literal 421 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Y$ZW{!C;AX&hNDNfGqYBPhVH| z>)h;|%F0&XH!%Z+I14-?iy0WW1wojxJ|%u4P>?0v(btiIVPik{pF~y$21X@M7srqa z#@@iKjx2!!bEh(CH1JI4C@=rc^g+yH0~^Ojmh!U&ry9&YtUvQsp`NvJ`|j_@&Nl5} z{!mag>*t?S64QIQYxbRx7G^N8{l*~|^P^?Ls>MpqnQKilcu!Ra%!u4OM`|sr#c~Pm zya)Cdo>nF4i9N16x$#Bx=_ej9ysK6{R{9;px`Wfl{|Fm%qmo+NuT^YSt0(M;zsh{% z4OfF$LQdxvHO`I~z1$+FiWWX@2>E>aqiyNUV}61z4SObXq)L|ubnP+Md#}(kZIOxz zXC{lsN{!U`)FKP>%o&?%7_Xc>_tm!LLd1lYFui6*ju(3Ve9ymiJZqFZv8`S@aG{>Q zpl_4=78Y66lMnB>R3yCExmH>wCUS?JV&h|ROS?l(OGEe?{_;7MN|zt;-=+f$JqAx# KKbLh*2~7Y_@SV>9 literal 0 HcmV?d00001 diff --git a/include/AnglesCustomPlot.h b/include/AnglesCustomPlot.h new file mode 100644 index 0000000..f487340 --- /dev/null +++ b/include/AnglesCustomPlot.h @@ -0,0 +1,44 @@ +#ifndef ANGLESCUSTOMPLOT_H +#define ANGLESCUSTOMPLOT_H + +#include + +#include .h> + +namespace Ui { +class AnglesCustomPlot; +} + +class AnglesCustomPlot : public QCustomPlot +{ + +public: + explicit AnglesCustomPlot(const QVector& data, const QString& xLabel, int nbBins, + QWidget *parent = nullptr); + ~AnglesCustomPlot(); + + bool computeHistogram(const QVector& data); + + void createQCPBars(const QString &xLabel); + + void rescale(); + + void mouseDoubleClickEvent(QMouseEvent *event) override; + + QSharedPointer dataContainer(){return m_bars->data();} + +private: + Ui::AnglesCustomPlot *ui; + + QVector m_axis; + + QVector m_histogram; + + QString m_label; + + QCPBars* m_bars; + + int m_nbBins; +}; + +#endif // ANGLESCUSTOMPLOT_H diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index d89e9e2..eccd856 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,12 +1,14 @@ target_sources( ${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/ActionA.h + ${CMAKE_CURRENT_LIST_DIR}/G3PointAction.h ${CMAKE_CURRENT_LIST_DIR}/G3Point.h ${CMAKE_CURRENT_LIST_DIR}/G3PointDialog.h ${CMAKE_CURRENT_LIST_DIR}/GrainsAsEllipsoids.h - ${CMAKE_CURRENT_LIST_DIR}/qG3PointDisclaimer.h + ${CMAKE_CURRENT_LIST_DIR}/G3PointDisclaimer.h + ${CMAKE_CURRENT_LIST_DIR}/AnglesCustomPlot.h ${CMAKE_CURRENT_LIST_DIR}/WolmanCustomPlot.h + ${CMAKE_CURRENT_LIST_DIR}/G3PointPlots.h ) target_include_directories( ${PROJECT_NAME} diff --git a/include/ActionA.h b/include/G3PointAction.h similarity index 93% rename from include/ActionA.h rename to include/G3PointAction.h index c336834..ae9fb5b 100644 --- a/include/ActionA.h +++ b/include/G3PointAction.h @@ -11,6 +11,10 @@ #include +#include + +#include + #pragma once class ccMainAppInterface; @@ -36,6 +40,8 @@ public: bool cluster(); void fit(); void exportResults(); + void plots(); + void showWolman(const Eigen::ArrayXf& d_sample); bool wolman(); bool angles(); bool processNewStacks(std::vector>& newStacks, int pointCount); @@ -107,6 +113,8 @@ private: static std::shared_ptr s_g3PointAction; - QSharedPointer m_grainsAsEllipsoids; + static std::shared_ptr s_g3PointPlots; + + GrainsAsEllipsoids* m_grainsAsEllipsoids; }; } diff --git a/include/G3PointDialog.h b/include/G3PointDialog.h index d17f40b..c90550d 100644 --- a/include/G3PointDialog.h +++ b/include/G3PointDialog.h @@ -4,7 +4,7 @@ #include namespace Ui { -class qG3PointDialog; +class G3PointDialog; } class G3PointDialog : public QDialog @@ -43,6 +43,8 @@ public: double getMinFlatness(); int getNMin(); int getkNN(); + int getWolmanNbIter(); + int getAnglesNbBins(); double getRadiusFactor(); bool isSteepestSlope(); @@ -73,7 +75,7 @@ signals: void glPointSize(int size); private: - Ui::qG3PointDialog *ui; + Ui::G3PointDialog *ui; }; #endif // QG3POINTDIALOG_H diff --git a/include/qG3PointDisclaimer.h b/include/G3PointDisclaimer.h similarity index 59% rename from include/qG3PointDisclaimer.h rename to include/G3PointDisclaimer.h index 06a5cee..75f5514 100644 --- a/include/qG3PointDisclaimer.h +++ b/include/G3PointDisclaimer.h @@ -1,5 +1,5 @@ -#ifndef QG3POINTDISCLAIMER_H -#define QG3POINTDISCLAIMER_H +#ifndef G3POINTDISCLAIMER_H +#define G3POINTDISCLAIMER_H #include @@ -9,13 +9,13 @@ namespace Ui { class G3PointDisclaimer; } -class qG3PointDisclaimer : public QDialog +class G3PointDisclaimer : public QDialog { Q_OBJECT public: - explicit qG3PointDisclaimer(QWidget *parent = nullptr); - ~qG3PointDisclaimer(); + explicit G3PointDisclaimer(QWidget *parent = nullptr); + ~G3PointDisclaimer(); static bool show(ccMainAppInterface* app); @@ -26,4 +26,4 @@ private: Ui::G3PointDisclaimer *ui; }; -#endif // QG3POINTDISCLAIMER_H +#endif // G3POINTDISCLAIMER_H diff --git a/include/G3PointPlots.h b/include/G3PointPlots.h new file mode 100644 index 0000000..217475f --- /dev/null +++ b/include/G3PointPlots.h @@ -0,0 +1,37 @@ +#ifndef G3POINTPLOTS_H +#define G3POINTPLOTS_H + +#include + +#include + +namespace Ui { +class G3PointPlots; +} + +class G3PointPlots : public QWidget +{ + Q_OBJECT + +public: + explicit G3PointPlots(QString title, QWidget *parent = nullptr); + ~G3PointPlots(); + + void readSettings(); + + void addToTabWidget(QWidget* widget); + + template + bool exportToCSV(QString filename, SharedDataContainer container) const; + + void onExportToCSV(); + + void onExportToImage(); + + void closeEvent(QCloseEvent *event) override; + +private: + Ui::G3PointPlots *ui; +}; + +#endif // G3POINTPLOTS_H diff --git a/include/GrainsAsEllipsoids.h b/include/GrainsAsEllipsoids.h index a576803..9173900 100644 --- a/include/GrainsAsEllipsoids.h +++ b/include/GrainsAsEllipsoids.h @@ -34,6 +34,7 @@ public: GrainsAsEllipsoids(ccMainAppInterface *app); GrainsAsEllipsoids(ccPointCloud *cloud, ccMainAppInterface* app, const std::vector >& stacks, const RGBAColorsTableType& colors); + ~GrainsAsEllipsoids(); //! Set the path for shaders void setShaderPath(); diff --git a/include/WolmanCustomPlot.h b/include/WolmanCustomPlot.h index fc49781..ec7489c 100644 --- a/include/WolmanCustomPlot.h +++ b/include/WolmanCustomPlot.h @@ -1,21 +1,77 @@ #ifndef WOLMANCUSTOMPLOT_H #define WOLMANCUSTOMPLOT_H -#include - #include +// Eigen +#include + namespace Ui { class WolmanCustomPlot; } -class WolmanCustomPlot : public QCustomPlot +//! QCustomPlot: vertical bar with text along side +class QCPBarsWithText : public QCPBars { Q_OBJECT public: - explicit WolmanCustomPlot(QWidget *parent = nullptr); - ~WolmanCustomPlot(); + + QCPBarsWithText(QCPAxis* keyAxis, QCPAxis* valueAxis) : QCPBars(keyAxis,valueAxis), m_textOnTheLeft(false) {} + + void setText(QString text) { m_text = QStringList(text); } + void appendText(QString text) { m_text.append(text); } + void setTextAlignment(bool left) { m_textOnTheLeft = left; } + +protected: + + QStringList m_text; + bool m_textOnTheLeft; + + // reimplemented virtual draw method + virtual void draw(QCPPainter *painter) + { + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + //switch to standard display + QCPBars::draw(painter); + + int fontHeight = painter->fontMetrics().height(); + + if (!data()->isEmpty()) + { + double& key = data()->begin()->key; + double& value = data()->begin()->value; + QPointF P = coordsToPixels(key, value); + //apply a small shift + int margin = 5; //in pixels + if (m_textOnTheLeft) + margin = -margin; + P.setX(P.x() + margin); + //we draw at the 'base' line + P.setY(P.y() + fontHeight); + + for (int i=0; ifontMetrics().width(m_text[i])); + painter->drawText(Pstart,m_text[i]); + P.setY(P.y() + fontHeight); + } + } + } + +}; + +class WolmanCustomPlot : public QWidget +{ +public: + WolmanCustomPlot(const Eigen::ArrayXf& d_sample); + + QSharedPointer dataContainer(){return m_graph->data();} + + QCPGraph* m_graph; private: Ui::WolmanCustomPlot *ui; diff --git a/qG3POINT.qrc b/qG3POINT.qrc index 4444ea4..5796aeb 100644 --- a/qG3POINT.qrc +++ b/qG3POINT.qrc @@ -2,5 +2,7 @@ info.json images/icon.png + images/smallCSVFile.png + images/dbImageSymbol.png diff --git a/src/AnglesCustomPlot.cpp b/src/AnglesCustomPlot.cpp new file mode 100644 index 0000000..ea4a2da --- /dev/null +++ b/src/AnglesCustomPlot.cpp @@ -0,0 +1,131 @@ +#include "AnglesCustomPlot.h" +#include "ui_AnglesCustomPlot.h" + +#include +#include + +AnglesCustomPlot::AnglesCustomPlot(const QVector &data, const QString &xLabel, int nbBins, QWidget *parent): + ui(new Ui::AnglesCustomPlot), + m_label(xLabel), + m_bars(nullptr), + m_nbBins(nbBins) +{ + setProperty("TypeOfCustomPlot", "AnglesCustomPlot"); + + ui->setupUi(this); + + setWindowTitle(m_label); + + computeHistogram(data); + + createQCPBars(m_label + " [°]"); + + m_bars->rescaleAxes(); +} + +AnglesCustomPlot::~AnglesCustomPlot() +{ + delete ui; +} + +//! Default number of classes for associated histogram +static const unsigned MAX_HISTOGRAM_SIZE = 512; + +bool AnglesCustomPlot::computeHistogram(const QVector& data) +{ + double minData = *std::min_element(data.begin(), data.end()); + double maxData = *std::max_element(data.begin(), data.end()); + double range = maxData - minData; + + if (range == 0 || data.size() == 0) + { + //can't build histogram of a flat field + return false; + } + else + { + unsigned count = data.size(); + unsigned numberOfBins; + if (m_nbBins == -1) // compute number of bins automatically + { + numberOfBins = static_cast(ceil(sqrt(static_cast(count)))); + numberOfBins = std::max(std::min(numberOfBins, MAX_HISTOGRAM_SIZE), 4); + } + else // use number of bins + { + numberOfBins = m_nbBins; + } + + m_axis.resize(numberOfBins); + for (unsigned i = 0; i < numberOfBins; i++) + { + m_axis[i] = minData + i * range / numberOfBins; + } + + //reserve memory + try + { + m_histogram.resize(numberOfBins); + } + catch (const std::bad_alloc&) + { + ccLog::Warning("[computeHistogram] Failed to allocate histogram!"); + } + + std::fill(m_histogram.begin(), m_histogram.end(), 0); + + //compute histogram + ScalarType step = static_cast(numberOfBins) / range; + for (unsigned i = 0; i < count; ++i) + { + const ScalarType& val = data[i]; + + if (CCCoreLib::ScalarField::ValidValue(val)) + { + unsigned bin = static_cast((val - minData) * step); + ++m_histogram[std::min(bin, numberOfBins - 1)]; + } + } + } + + return true; +} + +void AnglesCustomPlot::createQCPBars(const QString& xLabel) +{ + //clear previous display + if (m_bars) + { + this->removePlottable(m_bars); + m_bars = nullptr; + } + + m_bars = new QCPBars(xAxis, yAxis); + m_bars->setAntialiased(false); // gives more crisp, pixel aligned bar borders + m_bars->setPen(QPen(QColor(0, 168, 140).lighter(130))); + m_bars->setBrush(QColor(0, 168, 140)); + m_bars->setWidth(m_axis[1] - m_axis[0]); + m_bars->setData(m_axis, m_histogram); + xAxis->setLabel(xLabel); + yAxis->setLabel("Counts"); + setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); +} + +void AnglesCustomPlot::rescale() +{ + // set ranges appropriate to show data + if (m_bars) + { + m_bars->rescaleAxes(); + replot(); + } +} + +void AnglesCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + { + rescale(); + } + QCustomPlot::mouseDoubleClickEvent(event); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 930c0b3..cb7d3b8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,12 @@ target_sources( ${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/ActionA.cpp + ${CMAKE_CURRENT_LIST_DIR}/G3PointAction.cpp ${CMAKE_CURRENT_LIST_DIR}/G3Point.cpp ${CMAKE_CURRENT_LIST_DIR}/G3PointDialog.cpp ${CMAKE_CURRENT_LIST_DIR}/GrainsAsEllipsoids.cpp - ${CMAKE_CURRENT_LIST_DIR}/qG3PointDisclaimer.cpp + ${CMAKE_CURRENT_LIST_DIR}/G3PointDisclaimer.cpp + ${CMAKE_CURRENT_LIST_DIR}/AnglesCustomPlot.cpp ${CMAKE_CURRENT_LIST_DIR}/WolmanCustomPlot.cpp + ${CMAKE_CURRENT_LIST_DIR}/G3PointPlots.cpp ) diff --git a/src/G3Point.cpp b/src/G3Point.cpp index fb29c66..426f266 100644 --- a/src/G3Point.cpp +++ b/src/G3Point.cpp @@ -35,7 +35,7 @@ #include "G3Point.h" -#include "ActionA.h" +#include "G3PointAction.h" ccHObject* G3PointFactory::buildObject(const QString& metaName) { diff --git a/src/ActionA.cpp b/src/G3PointAction.cpp similarity index 92% rename from src/ActionA.cpp rename to src/G3PointAction.cpp index 174d6ff..0cfcfbe 100644 --- a/src/ActionA.cpp +++ b/src/G3PointAction.cpp @@ -1,4 +1,4 @@ -#include "ActionA.h" +#include "G3PointAction.h" // CCPluginAPI #include @@ -42,13 +42,15 @@ #include #include -#include +#include #include namespace G3Point { std::shared_ptr G3PointAction::s_g3PointAction; +std::shared_ptr G3PointAction::s_g3PointPlots; + G3PointAction::G3PointAction(ccPointCloud *cloud, ccMainAppInterface *app) : m_app(app) , m_dlg(nullptr) @@ -71,9 +73,11 @@ G3PointAction::~G3PointAction() void G3PointAction::GetG3PointAction(ccPointCloud *cloud, ccMainAppInterface *app) { //disclaimer accepted? - if (!qG3PointDisclaimer::show(app)) + if (!G3PointDisclaimer::show(app)) return; + s_g3PointPlots.reset(new G3PointPlots(cloud->getName())); + if (!s_g3PointAction) // create the singleton if needed { s_g3PointAction.reset(new G3PointAction(cloud, app)); @@ -613,8 +617,12 @@ bool G3PointAction::exportLocalMaximaAsCloud(const Eigen::ArrayXi& localMaximumI m_app->removeFromDB(child, true); } - parent->addChild(cloud, ccHObject::DP_PARENT_OF_OTHER, 0); + // parent->addChild(cloud, ccHObject::DP_PARENT_OF_OTHER, 0); + // m_app->addToDB(cloud); + m_cloud->addChild(cloud); m_app->addToDB(cloud); + + cloud->setEnabled(false); return true; @@ -1008,24 +1016,26 @@ void G3PointAction::fit() // plot display grains as ellipsoids m_grainColors.reset(new RGBAColorsTableType(getRandomColors(m_localMaximumIndexes.size()))); - m_grainsAsEllipsoids.reset(new GrainsAsEllipsoids(m_cloud, m_app, m_stacks, *m_grainColors)); + m_grainsAsEllipsoids = new GrainsAsEllipsoids(m_cloud, m_app, m_stacks, *m_grainColors); m_grainsAsEllipsoids->setName("g3point_ellipsoids"); // add connections with the dialog - connect(m_dlg, &G3PointDialog::onlyOneClicked, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::showOnlyOne); - connect(m_dlg, &G3PointDialog::allClicked, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::showAll); - connect(m_dlg, &G3PointDialog::onlyOneChanged, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::setOnlyOne); - connect(m_dlg, &G3PointDialog::transparencyChanged, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::setTransparency); - connect(m_dlg, &G3PointDialog::drawSurfaces, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::drawSurfaces); - connect(m_dlg, &G3PointDialog::drawLines, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::drawLines); - connect(m_dlg, &G3PointDialog::drawPoints, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::drawPoints); - connect(m_dlg, &G3PointDialog::glPointSize, m_grainsAsEllipsoids.data(), &GrainsAsEllipsoids::setGLPointSize); + connect(m_dlg, &G3PointDialog::onlyOneClicked, m_grainsAsEllipsoids, &GrainsAsEllipsoids::showOnlyOne); + connect(m_dlg, &G3PointDialog::allClicked, m_grainsAsEllipsoids, &GrainsAsEllipsoids::showAll); + connect(m_dlg, &G3PointDialog::onlyOneChanged, m_grainsAsEllipsoids, &GrainsAsEllipsoids::setOnlyOne); + connect(m_dlg, &G3PointDialog::transparencyChanged, m_grainsAsEllipsoids, &GrainsAsEllipsoids::setTransparency); + connect(m_dlg, &G3PointDialog::drawSurfaces, m_grainsAsEllipsoids, &GrainsAsEllipsoids::drawSurfaces); + connect(m_dlg, &G3PointDialog::drawLines, m_grainsAsEllipsoids, &GrainsAsEllipsoids::drawLines); + connect(m_dlg, &G3PointDialog::drawPoints, m_grainsAsEllipsoids, &GrainsAsEllipsoids::drawPoints); + connect(m_dlg, &G3PointDialog::glPointSize, m_grainsAsEllipsoids, &GrainsAsEllipsoids::setGLPointSize); m_dlg->setOnlyOneMax(m_stacks.size()); m_dlg->emitSignals(); // force to send parameters to m_grainsAsEllipsoids - m_cloud->getParent()->addChild(m_grainsAsEllipsoids.data()); - m_app->addToDB(m_cloud); + // m_cloud->getParent()->addChild(m_grainsAsEllipsoids.get()); + // m_app->addToDB(m_cloud); + m_cloud->addChild(m_grainsAsEllipsoids); + m_app->addToDB(m_grainsAsEllipsoids); m_app->updateUI(); } @@ -1084,33 +1094,25 @@ double std_dev(const T &vec) return std::sqrt((vec - vec.mean()).square().sum() / (vec.size() - 1)); } -void showWolman(const Eigen::ArrayXf& d_sample) +void G3PointAction::plots() +{ + angles(); +} + +void G3PointAction::showWolman(const Eigen::ArrayXf& d_sample) { // QCustomPlot - WolmanCustomPlot* wolmanCustomPlot = new WolmanCustomPlot(); - QCPGraph* graph = wolmanCustomPlot->addGraph(); - QVector x_data(d_sample.size()); - QVector y_data(d_sample.size()); - for (int k = 0; k < d_sample.size(); k++) - { - x_data[k] = d_sample(k); - y_data[k] = (static_cast(k)) / static_cast(d_sample.size()); - } - std::sort(x_data.begin(), x_data.end()); - graph->setData(x_data, y_data); - graph->rescaleAxes(); - // give the axes some labels: - wolmanCustomPlot->xAxis->setScaleType(QCPAxis::stLogarithmic); - wolmanCustomPlot->xAxis->setLabel("Diameter [mm]"); - wolmanCustomPlot->yAxis->setLabel("CDF"); - wolmanCustomPlot->show(); + if (!s_g3PointPlots) + s_g3PointPlots.reset(new G3PointPlots(m_cloud->getName())); + s_g3PointPlots->addToTabWidget(new WolmanCustomPlot(d_sample)); + s_g3PointPlots->show(); } bool G3PointAction::wolman() { - int n_iter = 10; + int n_iter = m_dlg->getWolmanNbIter(); - if (m_grainsAsEllipsoids.isNull()) + if (!m_grainsAsEllipsoids) { ccLog::Error("[G3PointAction::wolman] no ellipsoid, not possible to do Wolman analysis"); return false; @@ -1169,7 +1171,7 @@ bool G3PointAction::wolman() { float r0 = dist(urbg); float r1 = dist(urbg); - std::cout << r0 << ", " << r1 << "," << std::endl; + // std::cout << r0 << ", " << r1 << "," << std::endl; x_grid = arange(x.minCoeff() - r0 * dx, x.maxCoeff(), dx); y_grid = arange(y.minCoeff() - r1 * dx, y.maxCoeff(), dx); int nx = x_grid.size(); @@ -1246,93 +1248,20 @@ bool G3PointAction::wolman() return true; } -//! Default number of classes for associated histogram -static const unsigned MAX_HISTOGRAM_SIZE = 512; - -bool computeHistogram(const QVector& data, QVector& axis, QVector& histogram) -{ - double minData = *std::min_element(data.begin(), data.end()); - double maxData = *std::max_element(data.begin(), data.end()); - double range = maxData - minData; - - if (range == 0 || data.size() == 0) - { - //can't build histogram of a flat field - return false; - } - else - { - unsigned count = data.size(); - unsigned numberOfBins = static_cast(ceil(sqrt(static_cast(count)))); - // numberOfBins = std::max(std::min(numberOfBins, MAX_HISTOGRAM_SIZE), 4); - numberOfBins = 10; - - axis.resize(numberOfBins); - for (int i = 0; i < numberOfBins; i++) - { - axis[i] = minData + i * range / numberOfBins; - } - - //reserve memory - try - { - histogram.resize(numberOfBins); - } - catch (const std::bad_alloc&) - { - ccLog::Warning("[computeHistogram] Failed to allocate histogram!"); - } - - std::fill(histogram.begin(), histogram.end(), 0); - - //compute histogram - ScalarType step = static_cast(numberOfBins) / range; - for (unsigned i = 0; i < count; ++i) - { - const ScalarType& val = data[i]; - - if (CCCoreLib::ScalarField::ValidValue(val)) - { - unsigned bin = static_cast((val - minData) * step); - ++histogram[std::min(bin, numberOfBins - 1)]; - } - } - } - - return true; -} - -void showHistogram(const QVector& data) -{ - // QCustomPlot - WolmanCustomPlot* anglesCustomPlot = new WolmanCustomPlot(); - QVector axis; - QVector histogram; - computeHistogram(data, axis, histogram); - QCPBars *regen = new QCPBars(anglesCustomPlot->xAxis, anglesCustomPlot->yAxis); - regen->setData(axis, histogram); - regen->rescaleAxes(); - regen->setPen(QPen(QColor(0, 168, 140).lighter(130))); - regen->setBrush(QColor(0, 168, 140)); - anglesCustomPlot->xAxis->setLabel("Azimut [°]"); - anglesCustomPlot->yAxis->setLabel("Counts"); - anglesCustomPlot->show(); -} - bool G3PointAction::angles() { - if (m_grainsAsEllipsoids.isNull()) + if (!m_grainsAsEllipsoids) { ccLog::Error("[G3PointAction::angles] no ellipsoid, not possible to do angles analysis"); return false; } float delta = 1e32; - int n_ellipsoids = m_grainsAsEllipsoids->m_rotationMatrix.size(); + size_t n_ellipsoids = m_grainsAsEllipsoids->m_rotationMatrix.size(); QVector granuloAngleMView(n_ellipsoids); QVector granuloAngleXView(n_ellipsoids); - for (int i = 0; i < n_ellipsoids; i++) + for (size_t i = 0; i < n_ellipsoids; i++) { float u, v, w; @@ -1370,18 +1299,12 @@ bool G3PointAction::angles() } // QCustomPlot - WolmanCustomPlot* anglesCustomPlot = new WolmanCustomPlot(); - QVector axis; - QVector histogram; - computeHistogram(granuloAngleMView, axis, histogram); - QCPBars *regen = new QCPBars(anglesCustomPlot->xAxis, anglesCustomPlot->yAxis); - regen->setData(axis, histogram); - regen->rescaleAxes(); - regen->setPen(QPen(QColor(0, 168, 140).lighter(130))); - regen->setBrush(QColor(0, 168, 140)); - anglesCustomPlot->xAxis->setLabel("Azimut [°]"); - anglesCustomPlot->yAxis->setLabel("Counts"); - anglesCustomPlot->show(); + if (!s_g3PointPlots) + s_g3PointPlots.reset(new G3PointPlots(m_cloud->getName())); + int nbBins = m_dlg->getAnglesNbBins(); + s_g3PointPlots->addToTabWidget(new AnglesCustomPlot(granuloAngleMView, "Azimut", nbBins)); + s_g3PointPlots->addToTabWidget(new AnglesCustomPlot(granuloAngleXView, "Dip", nbBins)); + s_g3PointPlots->show(); return true; } @@ -2017,7 +1940,7 @@ void G3PointAction::showDlg() connect(m_dlg, &G3PointDialog::fit, s_g3PointAction.get(), &G3Point::G3PointAction::fit); connect(m_dlg, &G3PointDialog::exportResults, s_g3PointAction.get(), &G3Point::G3PointAction::exportResults); connect(m_dlg, &G3PointDialog::wolman, s_g3PointAction.get(), &G3Point::G3PointAction::wolman); - connect(m_dlg, &G3PointDialog::angles, s_g3PointAction.get(), &G3Point::G3PointAction::angles); + connect(m_dlg, &G3PointDialog::angles, s_g3PointAction.get(), &G3Point::G3PointAction::plots); connect(m_dlg, &QDialog::finished, s_g3PointAction.get(), &G3Point::G3PointAction::clean); connect(m_dlg, &QDialog::finished, s_g3PointAction.get(), &G3Point::G3PointAction::resetDlg); // dialog is defined with Qt::WA_DeleteOnClose @@ -2116,7 +2039,11 @@ bool G3PointAction::setCloud(ccPointCloud *cloud) + QString::number(nPointsInGrains) + "/" + QString::number(cloud->size()) + " points belonging to " + QString::number(newStacks.size()) + " grains"); + std::cout << "[d] processNewStacks" << std::endl; + processNewStacks(newStacks, nPointsInGrains); + + std::cout << "[e]" << std::endl; } return true; @@ -2125,7 +2052,6 @@ bool G3PointAction::setCloud(ccPointCloud *cloud) void G3PointAction::setKNN() { m_kNN = m_dlg->getkNN(); - std::cout << "kNN " << m_kNN << std::endl; } void G3PointAction::createAction(ccMainAppInterface *appInterface) diff --git a/src/G3PointDialog.cpp b/src/G3PointDialog.cpp index ac42445..7f40e2f 100644 --- a/src/G3PointDialog.cpp +++ b/src/G3PointDialog.cpp @@ -1,11 +1,11 @@ #include "G3PointDialog.h" -#include "ui_qG3PointDialog.h" +#include "ui_G3PointDialog.h" #include G3PointDialog::G3PointDialog(QString cloudName, QWidget *parent) : QDialog(parent) - , ui(new Ui::qG3PointDialog) + , ui(new Ui::G3PointDialog) { ui->setupUi(this); @@ -54,6 +54,12 @@ void G3PointDialog::readSettings() // max angle 1 double maxAngle1 = settings.value("maxAngle1", 60).toDouble(); this->ui->doubleSpinBoxRadiusFactor->setValue(maxAngle1); + // wolmanNbIter + int wolmanNbIter = settings.value("wolmanNbIter", 20).toInt(); + this->ui->spinBoxWolmanNbIter->setValue(wolmanNbIter); + // anglesNbBins + int anglesNbBins = settings.value("anglesNbBins", 10).toInt(); + this->ui->spinBoxAnglesNbBins->setValue(anglesNbBins); } void G3PointDialog::writeSettings() @@ -67,6 +73,10 @@ void G3PointDialog::writeSettings() settings.setValue("radiusFactor", ui->doubleSpinBoxRadiusFactor->value()); // max angle 1 settings.setValue("maxAngle1", ui->doubleSpinBoxMaxAngle1->value()); + // wolmanNbIter + settings.setValue("wolmanNbIter", ui->spinBoxWolmanNbIter->value()); + // anglesNbBins + settings.setValue("anglesNbBins", ui->spinBoxAnglesNbBins->value()); } double G3PointDialog::getMaxAngle1() @@ -94,6 +104,23 @@ int G3PointDialog::getkNN() return this->ui->spinBoxkNN->value(); } +int G3PointDialog::getWolmanNbIter() +{ + return this->ui->spinBoxWolmanNbIter->value(); +} + +int G3PointDialog::getAnglesNbBins() +{ + if (this->ui->radioButtonAutoNbBins->isChecked()) + { + return -1; + } + else + { + return this->ui->spinBoxAnglesNbBins->value(); + } +} + double G3PointDialog::getRadiusFactor() { return ui->doubleSpinBoxRadiusFactor->value(); diff --git a/src/qG3PointDisclaimer.cpp b/src/G3PointDisclaimer.cpp similarity index 73% rename from src/qG3PointDisclaimer.cpp rename to src/G3PointDisclaimer.cpp index d0cbce8..1425251 100644 --- a/src/qG3PointDisclaimer.cpp +++ b/src/G3PointDisclaimer.cpp @@ -1,5 +1,5 @@ -#include "qG3PointDisclaimer.h" -#include "ui_qG3PointDisclaimer.h" +#include "G3PointDisclaimer.h" +#include "ui_G3PointDisclaimer.h" //qCC_plugins #include @@ -7,9 +7,9 @@ //Qt #include -bool qG3PointDisclaimer::s_disclaimerAccepted = false; +bool G3PointDisclaimer::s_disclaimerAccepted = false; -qG3PointDisclaimer::qG3PointDisclaimer(QWidget *parent) +G3PointDisclaimer::G3PointDisclaimer(QWidget *parent) : QDialog(parent) , ui(new Ui::G3PointDisclaimer) { @@ -34,17 +34,17 @@ qG3PointDisclaimer::qG3PointDisclaimer(QWidget *parent) ui->labelCompilationInformation->setText(compilationInfo); } -qG3PointDisclaimer::~qG3PointDisclaimer() +G3PointDisclaimer::~G3PointDisclaimer() { delete ui; } -bool qG3PointDisclaimer::show(ccMainAppInterface *app) +bool G3PointDisclaimer::show(ccMainAppInterface *app) { if ( !s_disclaimerAccepted ) { //if the user "cancels" it, then he refuses the disclaimer - s_disclaimerAccepted = qG3PointDisclaimer(app ? app->getMainWindow() : 0).exec(); + s_disclaimerAccepted = G3PointDisclaimer(app ? app->getMainWindow() : 0).exec(); } return s_disclaimerAccepted; diff --git a/src/G3PointPlots.cpp b/src/G3PointPlots.cpp new file mode 100644 index 0000000..c322745 --- /dev/null +++ b/src/G3PointPlots.cpp @@ -0,0 +1,166 @@ +#include "G3PointPlots.h" +#include "ui_G3PointPlots.h" + +// CCPluginAPI +#include + +/// qCC_db +#include + +// qcc_io +#include + +#include + +#include + +#include + +#include + +G3PointPlots::G3PointPlots(QString title, QWidget *parent) + : QWidget(parent) + , ui(new Ui::G3PointPlots) +{ + ui->setupUi(this); + + readSettings(); + + setWindowTitle(title); + + connect(this->ui->exportCSVToolButton, &QToolButton::clicked, this, &G3PointPlots::onExportToCSV); + connect(this->ui->exportImage, &QToolButton::clicked, this, &G3PointPlots::onExportToImage); +} + +G3PointPlots::~G3PointPlots() +{ + delete ui; +} + +void G3PointPlots::closeEvent(QCloseEvent *event) +{ + QSettings settings("OSERen", "qG3Point"); + settings.setValue("G3PointPlots/geometry", saveGeometry()); + // settings.setValue("windowState", saveState()); + QWidget::closeEvent(event); +} + +void G3PointPlots::readSettings() +{ + QSettings settings("OSERen", "qG3Point"); + restoreGeometry(settings.value("G3PointPlots/geometry").toByteArray()); +} + +void G3PointPlots::addToTabWidget(QWidget* widget) +{ + this->ui->tabWidget->addTab(widget, widget->windowTitle()); + this->ui->tabWidget->setCurrentWidget(widget); +} + +template +bool G3PointPlots::exportToCSV(QString filename, SharedDataContainer container) const +{ + QFile file(filename); + if (!file.open(QFile::WriteOnly | QFile::Text)) + { + ccLog::Warning(QString("[SF/SF] Failed to save plot to file '%1'").arg(filename)); + return false; + } + + QTextStream stream(&file); + stream.setRealNumberPrecision(12); + stream.setRealNumberNotation(QTextStream::FixedNotation); + + //header + stream << "angle [°], counts" << endl; + + //data + { + for (auto item : *container) + { + stream << item.key << " " << item.value << endl; + } + } + + file.close(); + + ccLog::Print(QString("[SF/SF] File '%1' saved").arg(filename)); + + return true; +} + +typedef QSharedPointer SharedBarsDataContainer; +typedef QSharedPointer SharedGraphDataContainer; + +void G3PointPlots::onExportToCSV() +{ + // get current tab widget + QWidget* currentWidget = this->ui->tabWidget->currentWidget(); + + //persistent settings + QSettings settings; + settings.beginGroup(ccPS::SaveFile()); + QString currentPath = settings.value(ccPS::CurrentPath(), ccFileUtils::defaultDocPath()).toString(); + + currentPath += QString("/") + currentWidget->windowTitle() + ".csv"; + + //ask for a filename + QString filename = QFileDialog::getSaveFileName(this, "Select output file", currentPath, "*.csv"); + if (filename.isEmpty()) + { + //process cancelled by user + return; + } + + //save last saving location + settings.setValue(ccPS::CurrentPath(), QFileInfo(filename).absolutePath()); + settings.endGroup(); + + if (currentWidget->property("TypeOfCustomPlot").toString() == "AnglesCustomPlot") + { + exportToCSV(filename, + static_cast(currentWidget)->dataContainer()); + } + else if (currentWidget->property("TypeOfCustomPlot").toString() == "WolmanCustomPlot") + { + exportToCSV(filename, + static_cast(currentWidget)->dataContainer()); + } +} + +void G3PointPlots::onExportToImage() +{ + QWidget* currentWidget = this->ui->tabWidget->currentWidget(); + + //persistent settings + QSettings settings; + settings.beginGroup(ccPS::SaveFile()); + QString currentPath = settings.value(ccPS::CurrentPath(), ccFileUtils::defaultDocPath()).toString(); + + QString outputFilename = ImageFileFilter::GetSaveFilename("Select output file", + currentWidget->windowTitle(), + currentPath, + this); + + if (outputFilename.isEmpty()) + { + //process cancelled by user (or error) + return; + } + + //save current export path to persistent settings + settings.setValue(ccPS::CurrentPath(), QFileInfo(outputFilename).absolutePath()); + settings.endGroup(); + + //save the widget as an image file + QPixmap image = currentWidget->grab(); + if (image.save(outputFilename)) + { + ccLog::Print(QString("[SF/SF] Image '%1' successfully saved").arg(outputFilename)); + } + else + { + ccLog::Error(QString("Failed to save file '%1'").arg(outputFilename)); + } +} + diff --git a/src/GrainsAsEllipsoids.cpp b/src/GrainsAsEllipsoids.cpp index 3a2d432..4e5fedb 100644 --- a/src/GrainsAsEllipsoids.cpp +++ b/src/GrainsAsEllipsoids.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include // from GeometricTools/GTE // #include @@ -30,6 +30,8 @@ GrainsAsEllipsoids::GrainsAsEllipsoids(ccMainAppInterface *app) setShaderPath(); } +GrainsAsEllipsoids::~GrainsAsEllipsoids(){} + GrainsAsEllipsoids::GrainsAsEllipsoids(ccPointCloud *cloud, ccMainAppInterface *app, const std::vector >& stacks, const RGBAColorsTableType& colors) : m_cloud(cloud) , m_app(app) @@ -237,7 +239,9 @@ bool GrainsAsEllipsoids::exportResultsAsCloud() cloud->showColors(true); cloud->setPointSize(9); - m_cloud->getParent()->addChild(cloud, ccHObject::DP_PARENT_OF_OTHER, 0); + // m_cloud->getParent()->addChild(cloud, ccHObject::DP_PARENT_OF_OTHER, 0); + // m_app->addToDB(cloud); + m_cloud->addChild(cloud); m_app->addToDB(cloud); return true; diff --git a/src/WolmanCustomPlot.cpp b/src/WolmanCustomPlot.cpp index 3ae67b3..2899c85 100644 --- a/src/WolmanCustomPlot.cpp +++ b/src/WolmanCustomPlot.cpp @@ -1,13 +1,28 @@ #include "WolmanCustomPlot.h" #include "ui_WolmanCustomPlot.h" -WolmanCustomPlot::WolmanCustomPlot(QWidget *parent): +WolmanCustomPlot::WolmanCustomPlot(const Eigen::ArrayXf &d_sample): ui(new Ui::WolmanCustomPlot) { - ui->setupUi(this); -} + setProperty("TypeOfCustomPlot", "WolmanCustomPlot"); -WolmanCustomPlot::~WolmanCustomPlot() -{ - delete ui; + ui->setupUi(this); + + setWindowTitle("Wolman"); + + m_graph = this->ui->customPlot->addGraph(); + QVector x_data(d_sample.size()); + QVector y_data(d_sample.size()); + for (int k = 0; k < d_sample.size(); k++) + { + x_data[k] = d_sample(k); + y_data[k] = (static_cast(k)) / static_cast(d_sample.size()); + } + std::sort(x_data.begin(), x_data.end()); + m_graph->setData(x_data, y_data); + m_graph->rescaleAxes(); + // give the axes some labels: + this->ui->customPlot->xAxis->setScaleType(QCPAxis::stLogarithmic); + this->ui->customPlot->xAxis->setLabel("Diameter [mm]"); + this->ui->customPlot->yAxis->setLabel("CDF"); } diff --git a/ui/AnglesCustomPlot.ui b/ui/AnglesCustomPlot.ui new file mode 100644 index 0000000..7cb7539 --- /dev/null +++ b/ui/AnglesCustomPlot.ui @@ -0,0 +1,19 @@ + + + AnglesCustomPlot + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index cc15379..46908f7 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources( ${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/qG3PointDialog.ui - ${CMAKE_CURRENT_LIST_DIR}/qG3PointDisclaimer.ui - ${CMAKE_CURRENT_LIST_DIR}/WolmanCustomPlot.ui + ${CMAKE_CURRENT_LIST_DIR}/G3PointDialog.ui + ${CMAKE_CURRENT_LIST_DIR}/G3PointDisclaimer.ui + ${CMAKE_CURRENT_LIST_DIR}/AnglesCustomPlot.ui + ${CMAKE_CURRENT_LIST_DIR}/G3PointPlots.ui ) diff --git a/ui/qG3PointDialog.ui b/ui/G3PointDialog.ui similarity index 80% rename from ui/qG3PointDialog.ui rename to ui/G3PointDialog.ui index 8a729fc..5da4234 100644 --- a/ui/qG3PointDialog.ui +++ b/ui/G3PointDialog.ui @@ -1,12 +1,12 @@ - qG3PointDialog - + G3PointDialog + 0 0 - 357 + 384 608 @@ -347,24 +347,95 @@ - - - Export results - - - - - - - Wolman - - - - - - - Angles + + + Outputs + + + + + Export results as a cloud + + + + + + + Angles + + + + + + + + false + + + + Nb bins: + + + + + + + Auto + + + true + + + + + + + Fix + + + + + + + QAbstractSpinBox::ButtonSymbols::NoButtons + + + 10 + + + 512 + + + + + + + QAbstractSpinBox::ButtonSymbols::NoButtons + + + 3 + + + 1000000 + + + + + + + Nb iterations + + + + + + + Wolman + + + + diff --git a/ui/qG3PointDisclaimer.ui b/ui/G3PointDisclaimer.ui similarity index 100% rename from ui/qG3PointDisclaimer.ui rename to ui/G3PointDisclaimer.ui diff --git a/ui/G3PointPlots.ui b/ui/G3PointPlots.ui new file mode 100644 index 0000000..1b29ffb --- /dev/null +++ b/ui/G3PointPlots.ui @@ -0,0 +1,69 @@ + + + G3PointPlots + + + + 0 + 0 + 751 + 641 + + + + G3Point plots + + + + + + -1 + + + + + + + + + ... + + + + :/CC/plugin/G3PointPlugin/images/smallCSVFile.png:/CC/plugin/G3PointPlugin/images/smallCSVFile.png + + + + + + + ... + + + + :/CC/plugin/G3PointPlugin/images/dbImageSymbol.png:/CC/plugin/G3PointPlugin/images/dbImageSymbol.png + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 560 + + + + + + + + + + + diff --git a/ui/WolmanCustomPlot.ui b/ui/WolmanCustomPlot.ui index 6d1db30..e9c3c00 100644 --- a/ui/WolmanCustomPlot.ui +++ b/ui/WolmanCustomPlot.ui @@ -13,7 +13,20 @@ Form + + + + + + + + QCustomPlot + QWidget +
qcustomplot.h
+ 1 +
+