diff --git a/DFTFringe.pro b/DFTFringe.pro
index a2383a1d..f722d1bd 100644
--- a/DFTFringe.pro
+++ b/DFTFringe.pro
@@ -212,6 +212,7 @@ SOURCES += SingleApplication/singleapplication.cpp \
plotcolor.cpp \
profileplot.cpp \
profileplotpicker.cpp \
+ ronchicomparedialog.cpp \
psfplot.cpp \
psi_dlg.cpp \
psiphasedisplay.cpp \
@@ -243,6 +244,7 @@ SOURCES += SingleApplication/singleapplication.cpp \
surfacegraph.cpp \
surfacelightingproxy.cpp \
surfacemanager.cpp \
+ wavefrontloaderworker.cpp \
transformwavefrontdlg.cpp \
unwraperrorsview.cpp \
usercolormapdlg.cpp \
@@ -330,6 +332,7 @@ HEADERS += bezier/bezier.h \
plotcolor.h \
profileplot.h \
profileplotpicker.h \
+ ronchicomparedialog.h \
psfplot.h \
psi_dlg.h \
psiphasedisplay.h \
@@ -361,6 +364,7 @@ HEADERS += bezier/bezier.h \
surfacegraph.h \
surfacelightingproxy.h \
surfacemanager.h \
+ wavefrontloaderworker.h \
transformwavefrontdlg.h \
unwraperrorsview.h \
usercolormapdlg.h \
diff --git a/DFTFringe_Dale.pro b/DFTFringe_Dale.pro
index 17d54447..e0c6cb34 100644
--- a/DFTFringe_Dale.pro
+++ b/DFTFringe_Dale.pro
@@ -47,6 +47,7 @@ SOURCES += main.cpp \
percentcorrectiondlg.cpp \
profileplot.cpp \
profileplotpicker.cpp \
+ ronchicomparedialog.cpp \
settingsigramimportconfig.cpp \
startestmoviedlg.cpp \
surface3dcontrolsdlg.cpp \
@@ -59,6 +60,7 @@ SOURCES += main.cpp \
dftcolormap.cpp \
surfaceanalysistools.cpp \
surfacemanager.cpp \
+ wavefrontloaderworker.cpp \
zernikedlg.cpp \
zernikepolar.cpp \
zernikeprocess.cpp \
@@ -163,6 +165,7 @@ HEADERS += mainwindow.h \
percentcorrectiondlg.h \
profileplot.h \
profileplotpicker.h \
+ ronchicomparedialog.h \
settingsigramimportconfig.h \
startestmoviedlg.h \
surface3dcontrolsdlg.h \
@@ -175,6 +178,7 @@ HEADERS += mainwindow.h \
dftcolormap.h \
surfaceanalysistools.h \
surfacemanager.h \
+ wavefrontloaderworker.h \
zernikedlg.h \
zernikepolar.h \
zernikeprocess.h \
diff --git a/DFTFringe_QT5.pro b/DFTFringe_QT5.pro
index c3d9b979..e7c3fbdf 100644
--- a/DFTFringe_QT5.pro
+++ b/DFTFringe_QT5.pro
@@ -211,6 +211,7 @@ SOURCES += SingleApplication/singleapplication.cpp \
plotcolor.cpp \
profileplot.cpp \
profileplotpicker.cpp \
+ ronchicomparedialog.cpp \
psfplot.cpp \
psi_dlg.cpp \
psiphasedisplay.cpp \
@@ -242,6 +243,7 @@ SOURCES += SingleApplication/singleapplication.cpp \
surfacegraph.cpp \
surfacelightingproxy.cpp \
surfacemanager.cpp \
+ wavefrontloaderworker.cpp \
transformwavefrontdlg.cpp \
unwraperrorsview.cpp \
usercolormapdlg.cpp \
@@ -329,6 +331,7 @@ HEADERS += bezier/bezier.h \
plotcolor.h \
profileplot.h \
profileplotpicker.h \
+ ronchicomparedialog.h \
psfplot.h \
psi_dlg.h \
psiphasedisplay.h \
@@ -360,6 +363,7 @@ HEADERS += bezier/bezier.h \
surfacegraph.h \
surfacelightingproxy.h \
surfacemanager.h \
+ wavefrontloaderworker.h \
transformwavefrontdlg.h \
unwraperrorsview.h \
usercolormapdlg.h \
diff --git a/autoinvertdlg.cpp b/autoinvertdlg.cpp
index 817d4130..ef36c479 100644
--- a/autoinvertdlg.cpp
+++ b/autoinvertdlg.cpp
@@ -53,3 +53,9 @@ void autoInvertDlg::setMainLabel(const QString & str) {
void autoInvertDlg::enableConic(bool b) {
ui->btnUseConic->setEnabled(b);
}
+
+void autoInvertDlg::on_InvertWithoutAskingRb_clicked(bool checked)
+{
+
+}
+
diff --git a/autoinvertdlg.h b/autoinvertdlg.h
index dd41699f..647af4fd 100644
--- a/autoinvertdlg.h
+++ b/autoinvertdlg.h
@@ -27,6 +27,8 @@ private slots:
void on_btnOutside_clicked();
+ void on_InvertWithoutAskingRb_clicked(bool checked);
+
private:
Ui::autoInvertDlg *ui;
};
diff --git a/autoinvertdlg.ui b/autoinvertdlg.ui
index 2c4bfbcd..66f818e8 100644
--- a/autoinvertdlg.ui
+++ b/autoinvertdlg.ui
@@ -6,8 +6,8 @@
0
0
- 1078
- 464
+ 1295
+ 565
@@ -25,7 +25,7 @@
true
-
+
-
@@ -137,6 +137,48 @@
+ -
+
+
-
+
+
+ <html><head/><body><p><span style=" font-weight:700;">What to do if invertions may be detected</span></p></body></html>
+
+
+ true
+
+
+
+ -
+
+
-
+
+
+ Don't Ask but Invert
+
+
+
+ -
+
+
+ Ask but Don't Invert
+
+
+ true
+
+
+
+ -
+
+
+ Don't ask or invert
+
+
+
+
+
+
+
-
diff --git a/foucaultview.cpp b/foucaultview.cpp
index e31d2bd7..0d6fda2e 100644
--- a/foucaultview.cpp
+++ b/foucaultview.cpp
@@ -8,7 +8,7 @@
#include
#include
#include "zernikeprocess.h"
-
+#include "ronchicomparedialog.h"
extern double outputLambda;
foucaultView::foucaultView(QWidget *parent, SurfaceManager *sm) :
@@ -74,10 +74,27 @@ void foucaultView::showContextMenu(QPoint pos)
QMenu myMenu;
myMenu.addAction("Save Ronchi image", this, &foucaultView::saveRonchiImage);
myMenu.addAction("Save Foucault Image", this, &foucaultView::saveFoucaultImage);
+ QAction *showAllRonchi = new QAction("Show all Selected Wave Fronts using Ronchi");
+ connect (showAllRonchi, &QAction::triggered,this, &foucaultView::showSelectedRonchiImages);
+ myMenu.addAction(showAllRonchi);
// Show context menu at handling position
myMenu.exec(globalPos);
}
+
+void foucaultView::showSelectedRonchiImages(){
+
+ surfaceAnalysisTools *saTools = surfaceAnalysisTools::get_Instance();
+ QList list = saTools->SelectedWaveFronts();
+
+ QList wfs;
+
+ for (int i = 0; i < list.size(); ++i){
+ wfs << m_sm->m_wavefronts.at(list[i]);
+ }
+ generateBatchRonchiImage(wfs);
+}
+
QImage *foucaultView::render(){
on_makePb_clicked();
QSize imsize = ui->foucaultViewLb->size();
@@ -152,280 +169,351 @@ QVector scaleProfile(QPolygonF points, int width,
return results;
}
-void foucaultView::on_makePb_clicked()
+
+
+QImage foucaultView::generateOpticalTestImage(OpticalTestType type, wavefront* wf, const OpticalTestSettings& s)
{
- m_guiTimer.stop();
- if (m_wf == 0 ||( m_wf->data.cols == 0))
- return;
- if (mirrorDlg::get_Instance()->isEllipse()){
- QMessageBox::warning(0,"warning","Foucaualt is not suppported for flat surfaces");
- return;
- }
- qDebug() << "slider" << ui->rocOffsetSlider->value();
- QApplication::setOverrideCursor(Qt::WaitCursor);
+ if (!wf || wf->data.cols == 0) return QImage();
+ // 1. Setup Constants
double pad = 1.1;
- int size = m_wf->data.cols * pad;
- size = size/2;
- size *= 2;
-
- pad = (double)size/m_wf->data.cols;
- double moving_constant = (ui->movingSourceRb->isChecked()) ? 1. : 2.;
+ int size = (int(wf->data.cols * pad) / 2) * 2;
+ double actualPad = (double)size / wf->data.cols;
+ double moving_constant = (s.movingSource) ? 1.0 : 2.0;
- double gamma = ui->gammaSb->value();
mirrorDlg *md = mirrorDlg::get_Instance();
- double Radius = md->diameter/2.;
- double r2 = Radius * Radius;
- double Fnumber = .5 * md->roc/md->diameter; //ROC is twice FL
- double unitMultiplyer = 1.;
- if (!ui->useMM->isChecked()){
- unitMultiplyer = 25.4;
- }
-
-
- double coc_offset_mm = ui->rocOffsetSb->value() * unitMultiplyer;
-
- double b = (md->roc) + coc_offset_mm;
-
- double pv = ( sqrt((r2)+(md->roc * md->roc))
- - (sqrt(r2+ b * b) - coc_offset_mm) )/ (md->lambda* 1.E-6);
+ double unitMultiplyer = s.useMM ? 1.0 : 25.4;
+ double coc_offset_mm = s.rocOffset * unitMultiplyer;
+
+ // Physics geometry
+ double r2 = (md->diameter / 2.0) * (md->diameter / 2.0);
+ double b = md->roc + coc_offset_mm;
+ double pv = (sqrt(r2 + (md->roc * md->roc)) - (sqrt(r2 + b * b) - coc_offset_mm)) / (md->lambda * 1.E-6);
+ double z3 = pv / moving_constant;
+ double effectiveZ3 = (type == OpticalTestType::Ronchi) ? (s.ronchiX * z3) : z3;
+
+ // 2. Wavefront Prep
+ std::vector originalZerns = wf->InputZerns;
+ std::vector tempZerns = originalZerns;
+ tempZerns[3] -= 3 * tempZerns[8];
+ wf->InputZerns = tempZerns;
- std::vector zerns = m_wf->InputZerns;
- std::vector newZerns = zerns;
- double z3 = pv / ( moving_constant);
-
- bool oldDoNull = md->doNull;
- if (!ui->autocollimation->isChecked()){
- md->doNull = false;
- }
-
- cv::Mat surf_fft;
SimulationsView *sv = SimulationsView::getInstance(0);
- newZerns[3] = newZerns[3] - 3 * newZerns[8];
- m_wf->InputZerns = newZerns;
- sv->setSurface(m_wf);
-
- cv::Mat surf_fftRonchi;
- surf_fft = sv->computeStarTest( heightMultiply * sv->nulledSurface(z3), size, pad ,true);
- surf_fftRonchi = sv->computeStarTest( heightMultiply *
- sv->nulledSurface(ui->RonchiX->value() * z3), size, pad ,true);
- //showMag(surf_fft, true, "star ", true, gamma);
- size = surf_fft.cols;
-
- int hx = (size -1)/2 + lateralOffset;
- m_wf->InputZerns = zerns;
-
- md->doNull = oldDoNull;
-
- double hy = hx;
-
- cv::Mat vknife[] = {cv::Mat::zeros(size,size,CV_64FC1)
- ,cv::Mat::zeros(cv::Size(size,size),CV_64FC1)};
-
- cv::Mat ronchiGrid[] = {cv::Mat::zeros(size,size,CV_64FC1)
- ,cv::Mat::zeros(cv::Size(size,size),CV_64FC1)};
-
- cv::Mat slit[] = {cv::Mat::zeros(size,size,CV_64FC1)
- ,cv::Mat::zeros(cv::Size(size,size),CV_64FC1)};
-
-
- cv::Mat ronchiSlit[] = {cv::Mat::zeros(size,size,CV_64FC1)
- ,cv::Mat::zeros(cv::Size(size,size),CV_64FC1)};
-
- // compute real world pixel width.
- double pixwidth = outputLambda * 1.E-6* Fnumber * 2./(25.4 * pad);
-
- double lpi = ui->lpiSb->value() * ((ui->useMM->isChecked()) ? 25.4 : 1.);
-
- double linewidth = .5/lpi;
- int ppl = linewidth/pixwidth; // pixels per line
- if (ppl <= 0)
- ppl = 1;
-
- int start = ((double)(size)/2.) -(double)ppl/2.;
- bool even = ((start / ppl) % 2) == 0;
-
- if (ui->clearCenterCb->isChecked())
- even = !even;
-
- start = ppl - start % (ppl);// + ui->lateralKnifeSb->value() * unitMultiplyer;
-
- double pixels_per_thou = .001 / pixwidth;
-
- double slitWidthHalf = pixels_per_thou * ui->slitWidthSb->value() * 1000 * ((ui->useMM->isChecked()) ? 1./25.4 : 1.);
- if (slitWidthHalf < .75){
- QString msg = QString("warning the slit width of %1 may too small. Using one pixel slit instead").arg(ui->slitWidthSb->value(), 6, 'f', 5);
- QMessageBox::warning(0,"warning", msg);
-
- }
- QString parms = QString(" Pixel width %1 slit size in pixels %2").arg(pixwidth, 6, 'f', 5).arg((int)(2 * slitWidthHalf));
- ui->pixeParms->setText(parms);
- // compute offset so that line is at center
- for (int y = 0; y < size; ++y)
- {
- double ry = double(y - hy)/(double)hy;
- int roffset = start;
- int line_no = 0;
- for (int x = 0; x < size; ++x)
- {
- double rx = double(x - hx)/(double)hx;
- double r = sqrt(rx * rx + ry *ry);
-
- // foucault setup
- if (r <= 1.)
- {
- //slit width is in inches convert to 1/1000 s.
- if ((x > hx -slitWidthHalf) && (x < hx + slitWidthHalf))
- {
- slit[0].at(y,x) = 255.;
- }
- }
-
- int knife_side = x;
- if (ui->knifeOnLeftCb->isChecked())
- knife_side = size - x;
-
- if (knife_side > hx )
- {
- vknife[0].at(y,x) = 255.;
- }
-
-
- // ronchi setup
- if ((even && (line_no%2 == 0)) || (!even && (line_no%2 != 0)))
- {
- ronchiGrid[0].at(y,x) = 1;
- }
- if(++roffset >= ppl)
- {
- ++line_no;
- roffset = 0;
- }
-
-
- if (x> hx - ppl/2. && x < hx + ppl/2.)
- {
- ronchiSlit[0].at(y,x) = 1;
- }
-
+ sv->setSurface(wf);
+ cv::Mat surf_fft = sv->computeStarTest(s.heightMultiply * sv->nulledSurface(effectiveZ3), size, actualPad, true);
+ wf->InputZerns = originalZerns; // Restore state immediately
+
+ // 3. Mask Generation
+ cv::Mat mask = cv::Mat::zeros(size, size, CV_64FC1);
+ cv::Mat sourceSlit = cv::Mat::zeros(size, size, CV_64FC1);
+ int hx = (size - 1) / 2 + s.lateralOffset;
+ double pixwidth = s.outputLambda * 1.E-6 * (0.5 * md->roc / md->diameter) * 2. / (25.4 * actualPad);
+
+ if (type == OpticalTestType::Ronchi) {
+ double lpi_val = s.lpi * (s.useMM ? 25.4 : 1.0);
+ int ppl = std::max(1, (int)((0.5 / lpi_val) / pixwidth));
+ int start = (size / 2) - (ppl / 2);
+ bool even = ((start / ppl) % 2 == 0) ^ s.clearCenter;
+ int roffset_start = ppl - (start % ppl);
+
+ for (int y = 0; y < size; ++y) {
+ int line_no = 0, roffset = roffset_start;
+ for (int x = 0; x < size; ++x) {
+ if (((even && (line_no % 2 == 0)) || (!even && (line_no % 2 != 0)))) mask.at(y, x) = 1.0;
+ if (++roffset >= ppl) { line_no++; roffset = 0; }
+ if (x > hx - ppl / 2. && x < hx + ppl / 2.) sourceSlit.at(y, x) = 1.0;
+ }
+ }
+ } else {
+ double slitWidthHalf = (.001 / pixwidth) * s.slitWidth * 1000 * (s.useMM ? 1./25.4 : 1.0);
+ for (int y = 0; y < size; ++y) {
+ double ry = double(y - hx) / hx;
+ for (int x = 0; x < size; ++x) {
+ if (sqrt(pow(double(x - hx)/hx, 2) + ry*ry) <= 1.0 && std::abs(x - hx) < slitWidthHalf) sourceSlit.at(y, x) = 255.0;
+ int k_side = s.knifeOnLeft ? (size - x) : x;
+ if (k_side > hx) mask.at(y, x) = 255.0;
}
}
+ }
- cv::Mat FFT1, FFT2;
- //fftw_plan p;
- cv::Mat complexIn;
- cv::Mat complexIn2;
-
- merge(ronchiGrid, 2, complexIn);
- merge(ronchiSlit,2,complexIn2);
-
- //showData("grid", ronchiGrid[0]);
- //showData("rslit", ronchiSlit[0]);
-
-
- dft(complexIn, FFT1, cv::DFT_REAL_OUTPUT);
- shiftDFT(FFT1);
- dft(complexIn2, FFT2, cv::DFT_REAL_OUTPUT);
- shiftDFT(FFT2);
- cv::Mat knifeSlit;
- mulSpectrums(FFT1, FFT2, knifeSlit, 0, true);
- idft(knifeSlit, knifeSlit, cv::DFT_SCALE); // gives us the correlation result...
- shiftDFT(knifeSlit);
- cv::Mat knifeSurf;
-
- mulSpectrums(knifeSlit, surf_fftRonchi, knifeSurf,0,true);
- idft(knifeSurf, knifeSurf, cv::DFT_SCALE);
- shiftDFT(knifeSurf);
-
- QImage ronchi = showMag(knifeSurf, false,"", false, gamma);
- int startx = size - m_wf->data.cols;
- ronchi = ronchi.copy(startx,startx,m_wf->data.cols, m_wf->data.cols);
-
- ronchi = ronchi.mirrored(true,false);
- QSize s = ui->ronchiViewLb->size();
- QPixmap rp = QPixmap::fromImage(ronchi.scaledToWidth(s.width()));
-
- QPainter painter(&rp);
- painter.save();
- painter.drawPixmap(0, 0, rp);
- painter.setPen(QPen(QColor(Qt::white)));
- painter.setFont(QFont("Arial", 15));
- QString zoffsetStr = QString("%1 %2").arg(ui->RonchiX->value() * ui->rocOffsetSb->value(), 6, 'f', 3)
- .arg(ui->useMM->isChecked()? "mm" : "in");
- painter.drawText(20, 40, zoffsetStr);
- QVector profilePoints;
- if (ui->overlayProfile->isChecked()){
- // overlay profile onto ronchi plot
- QPolygonF profile = m_sm->m_profilePlot->createProfile(1.,m_wf);
- profilePoints= scaleProfile(profile, rp.width(), M_PI/4.);
- painter.setPen(QPen(QColor(Qt::yellow),3));
- painter.drawLines(profilePoints);
+ // 4. DFT Pipeline
+ cv::Mat FFT1, FFT2, complexMask, complexSlit, combinedFilter, finalResult;
+ cv::Mat planesM[] = {mask, cv::Mat::zeros(mask.size(), CV_64FC1)};
+ cv::Mat planesS[] = {sourceSlit, cv::Mat::zeros(sourceSlit.size(), CV_64FC1)};
+ cv::merge(planesM, 2, complexMask);
+ cv::merge(planesS, 2, complexSlit);
- }
+ cv::dft(complexMask, FFT1, cv::DFT_REAL_OUTPUT);
+ cv::dft(complexSlit, FFT2, cv::DFT_REAL_OUTPUT);
+ if (type == OpticalTestType::Ronchi) { shiftDFT(FFT1); shiftDFT(FFT2); }
- painter.restore();
+ cv::mulSpectrums(FFT1, FFT2, combinedFilter, 0, true);
+ cv::idft(combinedFilter, combinedFilter, cv::DFT_SCALE);
+ if (type == OpticalTestType::Ronchi) shiftDFT(combinedFilter);
+ cv::mulSpectrums(combinedFilter, surf_fft, finalResult, 0, true);
+ cv::idft(finalResult, finalResult, cv::DFT_SCALE);
+ if (type == OpticalTestType::Ronchi) shiftDFT(finalResult);
- ui->ronchiViewLb->setPixmap(rp);
+ // 5. Output Image
+ QImage res = showMag(finalResult, false, "", false, s.gamma);
+ int startx = size - wf->data.cols;
+ return res.copy(startx, startx, wf->data.cols, wf->data.cols).mirrored(true, false);
+}
- merge(vknife, 2, complexIn);
- merge(slit,2,complexIn2);
+void foucaultView::on_makePb_clicked()
+{
+ m_guiTimer.stop();
+ if (m_wf == nullptr || m_wf->data.cols == 0)
+ return;
- dft(complexIn, FFT1, cv::DFT_REAL_OUTPUT);
- dft(complexIn2, FFT2, cv::DFT_REAL_OUTPUT);
+ if (mirrorDlg::get_Instance()->isEllipse()){
+ QMessageBox::warning(0,"warning","Foucaualt is not suppported for flat surfaces");
+ return;
+ }
- mulSpectrums(FFT1, FFT2, knifeSlit, 0, true);
- idft(knifeSlit, knifeSlit, cv::DFT_SCALE); // gives us the correlation result...
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ // 1. Pack the current UI state into the settings struct
+ OpticalTestSettings settings;
+ settings.rocOffset = ui->rocOffsetSb->value();
+ settings.ronchiX = ui->RonchiX->value();
+ settings.lpi = ui->lpiSb->value();
+ settings.gamma = ui->gammaSb->value();
+ settings.slitWidth = ui->slitWidthSb->value();
+ settings.useMM = ui->useMM->isChecked();
+ settings.movingSource = ui->movingSourceRb->isChecked();
+ settings.knifeOnLeft = ui->knifeOnLeftCb->isChecked();
+ settings.clearCenter = ui->clearCenterCb->isChecked();
+
+ // Pass the class members that aren't driven by UI widgets
+ settings.heightMultiply = this->heightMultiply;
+ settings.lateralOffset = this->lateralOffset;
+ // Note: outputLambda should be defined/accessible in your scope
+ settings.outputLambda = 550.0;
+
+ // 2. Call the refactored static engine for both images
+ QImage ronchiImg = generateOpticalTestImage(OpticalTestType::Ronchi, m_wf, settings);
+ QImage foucaultImg = generateOpticalTestImage(OpticalTestType::Foucault, m_wf, settings);
+
+ // Store for potential saving/external access
+ m_foucultQimage = foucaultImg;
+
+ // 3. UI Painting Helper (to avoid duplicating the Painter logic)
+ auto paintAndDisplay = [&](QLabel* label, QImage img, double offsetValue) {
+ if (img.isNull()) return;
+
+ QSize s = label->size();
+ QPixmap pix = QPixmap::fromImage(img.scaledToWidth(s.width()));
+
+ QPainter painter(&pix);
+ painter.save();
+ painter.setPen(QPen(QColor(Qt::white)));
+ painter.setFont(QFont("Arial", 15));
+
+ QString unit = ui->useMM->isChecked() ? "mm" : "in";
+ QString zStr = QString("%1 %2").arg(offsetValue, 6, 'f', 3).arg(unit);
+ painter.drawText(20, 40, zStr);
+
+ if (ui->overlayProfile->isChecked()) {
+ QPolygonF profile = m_sm->m_profilePlot->createProfile(1.0, m_wf);
+ // Assuming scaleProfile is a helper accessible in this scope
+ QVector profilePoints = scaleProfile(profile, pix.width(), M_PI/4.0);
+ painter.setPen(QPen(QColor(Qt::yellow), 3));
+ painter.drawLines(profilePoints);
+ }
+ painter.restore();
+ label->setPixmap(pix);
+ };
- mulSpectrums(knifeSlit, surf_fft, knifeSurf,0,true);
- idft(knifeSurf, knifeSurf, cv::DFT_SCALE);
+ // 4. Update the actual labels
+ paintAndDisplay(ui->ronchiViewLb, ronchiImg, settings.ronchiX * settings.rocOffset);
+ paintAndDisplay(ui->foucaultViewLb, foucaultImg, settings.rocOffset);
- m_foucultQimage = showMag(knifeSurf, false,"", false, gamma);
+ QApplication::restoreOverrideCursor();
+}
- startx = size - m_wf->data.cols;
- QImage foucault = m_foucultQimage.copy(startx,startx,m_wf->data.cols, m_wf->data.cols);
- qDebug() << "foucult" << foucault.size();
-/*
- cv::Mat iMat(foucault.height(), foucault.width(), CV_8UC3, foucault.bits(), foucault.bytesPerLine());
- cv::Mat flipped;
- cv::flip(iMat,flipped, 1);
- cv::Mat diffed;
- cv::absdiff(iMat, flipped, diffed);
- diffed = diffed * 10;
- cv::imshow("diffed", diffed);
- cv::waitKey(1);
- foucault = QImage((uchar*)diffed.data, diffed.cols, diffed.rows, diffed.step, QImage::Format_RGB888).copy();
-*/
- foucault = foucault.mirrored(true, false);
- s = ui->foucaultViewLb->size();
- QPixmap rpf = QPixmap::fromImage(foucault.scaledToWidth(s.width()));
+void foucaultView::generateBatchRonchiImage(const QList& wavefrontList)
+{
+ // 1. Initial Checks
+ if (wavefrontList.isEmpty() || !m_wf) return;
+
+ // 2. Ask user for Grid Layout
+ bool ok;
+ int cols = QInputDialog::getInt(this, tr("Batch Ronchi"),
+ tr("Number of columns:"), 2, 1, 10, 1, &ok);
+ if (!ok) return;
+
+ // 3. Prepare Optical Settings from UI
+ OpticalTestSettings s;
+ s.rocOffset = ui->rocOffsetSb->value();
+ s.ronchiX = ui->RonchiX->value();
+ s.lpi = ui->lpiSb->value();
+ s.gamma = ui->gammaSb->value();
+ s.slitWidth = ui->slitWidthSb->value();
+ s.useMM = ui->useMM->isChecked();
+ s.movingSource = ui->movingSourceRb->isChecked();
+ s.knifeOnLeft = ui->knifeOnLeftCb->isChecked();
+ s.clearCenter = ui->clearCenterCb->isChecked();
+ s.heightMultiply = this->heightMultiply;
+ s.lateralOffset = this->lateralOffset;
+ s.outputLambda = 550.0;
+
+ // 4. Calculate Grid and Canvas Geometry
+ int count = wavefrontList.size();
+ int rows = (count + cols - 1) / cols;
+ int imgDim = m_wf->data.cols;
+
+ int headerHeight = 70;
+ int textBuffer = 40;
+
+ int cellW = imgDim;
+ int cellH = imgDim + textBuffer;
+
+ // Total Canvas size
+ QImage canvas(cellW * cols, (cellH * rows) + headerHeight, QImage::Format_RGB32);
+ canvas.fill(Qt::black);
+
+ QPainter painter(&canvas);
+ painter.setRenderHint(QPainter::Antialiasing);
+ QApplication::setOverrideCursor(Qt::WaitCursor);
- QPainter painterf(&rpf);
- painterf.save();
- painterf.drawPixmap(0, 0, rpf);
- painterf.setPen(QPen(QColor(Qt::white)));
- painterf.setFont(QFont("Arial", 15));
- zoffsetStr = QString("%1 %2").arg(ui->rocOffsetSb->value(), 6 , 'f', 3)
- .arg(ui->useMM->isChecked()? "mm" : "in");
- painterf.drawText(20, 40, zoffsetStr);
- if (ui->overlayProfile->isChecked()){
- // overlay profile onto ronchi plot
- painterf.setPen(QPen(QColor(Qt::yellow),3));
- painterf.drawLines(profilePoints);
+ // NEW: Container for individual Ronchi images to be used in comparison
+ QList individualRonchis;
+ QList names;
+ // 5. Draw Simulation Header
+ painter.setPen(Qt::white);
+ painter.setFont(QFont("Arial", 12, QFont::Bold));
+ QString unit = s.useMM ? "mm" : "in";
+ QString headerText = QString("Ronchi Analysis | LPI: %1 | Offset: %2 %3")
+ .arg(s.lpi).arg(s.rocOffset).arg(unit);
+ painter.drawText(20, 35, headerText);
+
+ painter.setPen(QPen(Qt::gray, 2));
+ painter.drawLine(10, headerHeight - 15, canvas.width() - 10, headerHeight - 15);
+
+ // 6. Loop through and Render Ronchi Patterns
+ painter.setFont(QFont("Arial", 11, QFont::Bold));
+ QFontMetrics fm(painter.font());
+
+ for (int i = 0; i < count; ++i) {
+ wavefront* currentWf = wavefrontList[i];
+ int row = i / cols;
+ int col = i % cols;
+
+ QImage ronchi = generateOpticalTestImage(OpticalTestType::Ronchi, currentWf, s);
+
+ if (!ronchi.isNull()) {
+ // Store a copy for the comparison feature
+ individualRonchis.append(ronchi);
+
+ int xPos = col * cellW;
+ int yPos = headerHeight + (row * cellH);
+
+ painter.drawImage(xPos, yPos, ronchi);
+
+ QFileInfo fileInfo(currentWf->name);
+ QString displayName = fileInfo.baseName();
+ names << displayName;
+ int textWidth = fm.horizontalAdvance(displayName);
+ int xText = xPos + (cellW - textWidth) / 2;
+ int yText = yPos + imgDim + (textBuffer / 2) + (fm.ascent() / 2);
+
+ painter.setPen(Qt::yellow);
+ painter.drawText(xText, yText, displayName);
+ }
}
+ painter.end();
+ QApplication::restoreOverrideCursor();
- painterf.restore();
-
+ // 7. Configure Preview Dialog
+ QScreen *screen = QGuiApplication::primaryScreen();
+ int dlgW = static_cast(screen->availableGeometry().width() * 0.75);
+ int dlgH = static_cast(screen->availableGeometry().height() * 0.85);
+
+ QDialog previewDlg(this);
+ previewDlg.setWindowTitle(tr("Batch Ronchi Analysis Preview"));
+ previewDlg.resize(dlgW, dlgH);
+
+ QVBoxLayout *layout = new QVBoxLayout(&previewDlg);
+ QScrollArea *scroll = new QScrollArea(&previewDlg);
+ scroll->setWidgetResizable(true);
+ scroll->setAlignment(Qt::AlignCenter);
+ //scroll->setStyleSheet("background-color: #1a1a1a;");
+
+ QLabel *imgLabel = new QLabel();
+ imgLabel->setAlignment(Qt::AlignCenter);
+ scroll->setWidget(imgLabel);
+ layout->addWidget(scroll);
+
+ // 8. Zoom Slider Integration
+ QPixmap previewPixmap = QPixmap::fromImage(canvas);
+ QHBoxLayout *zoomLayout = new QHBoxLayout();
+ QSlider *slider = new QSlider(Qt::Horizontal);
+ slider->setRange(10, 400);
+ slider->setValue(100);
+
+ QLabel *zoomValueLabel = new QLabel("100%");
+ zoomLayout->addWidget(new QLabel(tr("Zoom: ")));
+ zoomLayout->addWidget(slider);
+ zoomLayout->addWidget(zoomValueLabel);
+ layout->addLayout(zoomLayout);
+
+ auto updateZoom = [previewPixmap, imgLabel, zoomValueLabel, dlgW](int val) {
+ int targetWidth = (dlgW - 80) * val / 100;
+ QPixmap scaled = previewPixmap.scaledToWidth(targetWidth, Qt::SmoothTransformation);
+ imgLabel->setPixmap(scaled);
+ imgLabel->setFixedSize(scaled.size());
+ zoomValueLabel->setText(QString("%1%").arg(val));
+ };
+
+ connect(slider, &QSlider::valueChanged, updateZoom);
+ updateZoom(100);
+
+ // 9. Navigation and Comparison Buttons
+ QHBoxLayout *btns = new QHBoxLayout();
+
+ // NEW: Comparison button
+ QPushButton *compareBtn = new QPushButton(tr("Compare Top Two Patterns"));
+ compareBtn->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));
+ // Feature only enabled if 2 or more wavefronts were processed
+ compareBtn->setEnabled(individualRonchis.size() >= 2);
+
+ QPushButton *saveBtn = new QPushButton(tr("Save Grid Image"));
+ QPushButton *cancelBtn = new QPushButton(tr("Close"));
+
+ btns->addWidget(compareBtn);
+ btns->addStretch();
+ btns->addWidget(saveBtn);
+ btns->addWidget(cancelBtn);
+ layout->addLayout(btns);
+
+ // Connect the comparison trigger
+ connect(compareBtn, &QPushButton::clicked, [=, &previewDlg]() {
+ // [=] copies individualRonchis and names so they stay
+ // valid even after generateBatchRonchiImage() returns.
+ if (individualRonchis.size() >= 2) {
+ RonchiCompareDialog compDlg(individualRonchis[0], names[0],
+ individualRonchis[1], names[1], &previewDlg);
+ compDlg.exec();
+ }
+ });
- ui->foucaultViewLb->setPixmap(rpf);
- //ui->foucaultViewLb->setPixmap(QPixmap::fromImage(foucault.scaledToWidth(s.width())));
+ connect(saveBtn, &QPushButton::clicked, &previewDlg, &QDialog::accept);
+ connect(cancelBtn, &QPushButton::clicked, &previewDlg, &QDialog::reject);
- QApplication::restoreOverrideCursor();
+ // 10. Execute Dialog and Save Grid
+ if (previewDlg.exec() == QDialog::Accepted) {
+ QString path = QFileDialog::getSaveFileName(this, tr("Save Ronchi Grid"),
+ imageDir, tr("Images (*.png *.jpg)"));
+ if (!path.isEmpty()) {
+ canvas.save(path);
+ }
+ }
}
void foucaultView::on_gammaSb_valueChanged(double /*arg1*/)
diff --git a/foucaultview.h b/foucaultview.h
index 23e4be92..8c57d703 100644
--- a/foucaultview.h
+++ b/foucaultview.h
@@ -2,8 +2,37 @@
#define FOUCAULTVIEW_H
#include
-#include "surfacemanager.h"
+#include
+#include
+#include
#include
+#include
+#include "surfacemanager.h"
+
+// Enum to specify which optical test to simulate
+enum class OpticalTestType {
+ Foucault,
+ Ronchi
+};
+
+// Parameter container so this can be called without needing the UI pointers
+struct OpticalTestSettings {
+ double rocOffset = 0.0;
+ double ronchiX = 1.0;
+ double lpi = 100.0;
+ double gamma = 1.0;
+ double slitWidth = 0.001;
+ bool useMM = true;
+ bool movingSource = true;
+ bool knifeOnLeft = false;
+ bool clearCenter = false;
+
+ // Internal state variables used by the original logic
+ int heightMultiply = 1;
+ int lateralOffset = 0;
+ double outputLambda = 550.0;
+};
+
namespace Ui {
class foucaultView;
}
@@ -22,63 +51,42 @@ class foucaultView : public QWidget
bool saveOnlyFoucault();
bool needsDrawing;
+ // The Refactored Static Engine - Callable from other classes
+ static QImage generateOpticalTestImage(OpticalTestType type, wavefront* wf, const OpticalTestSettings& settings);
+
public slots:
void on_makePb_clicked();
-
QImage *render();
private slots:
void showContextMenu(QPoint pos);
-
void on_gammaSb_valueChanged(double arg1);
-
void on_lpiSb_valueChanged(double arg1);
-
void on_movingSourceRb_clicked(bool /*unused*/);
-
void on_radioButton_2_clicked();
-
void on_knifeOnLeftCb_clicked();
-
void on_lpiSb_editingFinished();
-
void on_rocOffsetSb_editingFinished();
-
void on_slitWidthSb_editingFinished();
-
void on_useMM_clicked(bool checked);
-
void on_scanPb_clicked();
-
void on_h1x_clicked();
-
void on_h2x_clicked();
-
void on_h4x_clicked();
-
void on_rocOffsetSlider_valueChanged(int value);
-
void on_clearCenterCb_clicked();
-
void on_autoStepSize_clicked(bool checked);
-
void on_rocStepSize_editingFinished();
-
void on_lateralOffset_valueChanged(int arg1);
-
void on_SaveImageCB_clicked(bool checked);
-
void saveRonchiImage();
-
void saveFoucaultImage();
-
void on_overlayProfile_stateChanged(int arg1);
-
void on_RonchiX_valueChanged(double arg1);
-
void on_pushButton_clicked();
-
void on_autocollimation_clicked(bool checked);
+ void showSelectedRonchiImages();
+ void generateBatchRonchiImage(const QList& wavefrontList);
private:
Ui::foucaultView *ui;
@@ -96,7 +104,6 @@ private slots:
cv::Mat compute_star_test(int pupil_size, double defocus, double pad, bool use_OPD);
double getStep();
void draw_ROC_Scale();
-
};
#endif // FOUCAULTVIEW_H
diff --git a/foucaultview.ui b/foucaultview.ui
index 9d5c24e4..613c55e9 100644
--- a/foucaultview.ui
+++ b/foucaultview.ui
@@ -13,15 +13,6 @@
Form
-
- QPushButton{ border-style: outset;
- background-color: rgb(0, 0, 0);
- border-width: 3px;
- border-radius: 7px;
- border-color: darkgray;
- font: normal 12px;
- padding: 2px; }
-
-
@@ -523,7 +514,6 @@
- -1
false
false
diff --git a/plotcolor.cpp b/plotcolor.cpp
index ca265b1e..601a01f8 100644
--- a/plotcolor.cpp
+++ b/plotcolor.cpp
@@ -1,7 +1,7 @@
#include "plotcolor.h"
const char *plotColors[]=
-{ "Red",
- "Black",
+{ "Black",
+ "Red",
"Green",
"LightSalmon",
"SteelBlue",
diff --git a/profileplot.cpp b/profileplot.cpp
index 3a75b537..482e5e15 100644
--- a/profileplot.cpp
+++ b/profileplot.cpp
@@ -50,13 +50,17 @@
#include "zernikeprocess.h"
#include
#include "plotcolor.h"
-#include "profileplotpicker.h"
+
extern double outputLambda;
#include
#include
#include
+
+#include
#include
+#include
+#include
Profile line colors
-
-
-
- 210
- 260
- 111
- 32
-
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::NoButton
-
-
-
-
-
- 30
- 0
- 231
- 16
-
-
-
- These colors are used for the profile line plots
-
-
-
-
-
- 180
- 10
- 191
- 61
-
-
-
- When displaying multiple profiles these colors are cycled through for each wavefront.
-
-
- true
-
-
-
-
-
- 30
- 20
- 121
- 266
-
-
-
- -
-
-
- Color 1:
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- Color 2:
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- Color 3:
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- Color 4:
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- Color 5:
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- Color 6:
-
-
-
- -
-
-
- Color 7:
-
-
-
- -
-
-
- Color 8:
-
-
-
- -
-
-
- Color 9:
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
- 190
- 100
- 95
- 22
-
-
-
- -
-
-
- Line Width
-
-
-
- -
-
-
- 2
-
-
-
-
-
-
-
-
- 190
- 140
- 150
- 22
-
-
-
- -
-
-
- Selected Profile Width
-
-
-
- -
-
-
- 5
-
-
-
-
-
+
+ -
+
+
-
+
+
+ Color 1:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Color 2:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Color 3:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Color 4:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Color 5:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Color 6:
+
+
+
+ -
+
+
+ Color 7:
+
+
+
+ -
+
+
+ Color 8:
+
+
+
+ -
+
+
+ Color 9:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ These colors are used for the profile line plots
+
+
+
+ -
+
+
+ When displaying multiple profiles these colors are cycled through for each wavefront.
+
+
+ true
+
+
+
+ -
+
+
-
+
+
+ Line Width
+
+
+
+ -
+
+
+ 2
+
+
+
+
+
+ -
+
+
-
+
+
+ Selected Profile Width
+
+
+
+ -
+
+
+ 5
+
+
+
+
+
+ -
+
+
-
+
+
+ Slope Error Line Width
+
+
+
+ -
+
+
+ 1
+
+
+
+
+
+ -
+
+
-
+
+
+ Average Profile Width
+
+
+
+ -
+
+
+ 1
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 17
+ 13
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::NoButton
+
+
+
+
diff --git a/surfacemanager.cpp b/surfacemanager.cpp
index acb6c010..2d1d6899 100644
--- a/surfacemanager.cpp
+++ b/surfacemanager.cpp
@@ -1109,6 +1109,7 @@ void SurfaceManager::createSurfaceFromPhaseMap(cv::Mat phase, CircleOutline outs
m_surfaceTools->addWaveFront(wf->name);
m_currentNdx = m_wavefronts.size()-1;
+ m_surfaceTools->select(m_currentNdx);
}
wf->m_origin = origin;
wf->m_outside = outside;
diff --git a/surfacemanager.h b/surfacemanager.h
index ac19c46b..ba1df11a 100644
--- a/surfacemanager.h
+++ b/surfacemanager.h
@@ -45,6 +45,7 @@
#include "surfacegraph.h"
enum configRESPONSE { YES, NO, ASK};
enum AutoInvertMode {invNOTSET, invMANUAL, invCONIC, invINSIDE, invOUTSIDE};
+enum askAboutInversion {ask, doNotAskDoNotInvert, doNotAskbutInvert};
struct textres {
QTextEdit *Edit;
QList res;
@@ -112,6 +113,7 @@ class SurfaceManager : public QObject
void subtractWavefronts();
AutoInvertMode m_inverseMode;
+ askAboutInversion m_askAboutInvert;
bool m_ignoreInverse;
bool m_surface_finished;
configRESPONSE diamResp;
diff --git a/wavefrontloaderworker.cpp b/wavefrontloaderworker.cpp
new file mode 100644
index 00000000..f41a38fb
--- /dev/null
+++ b/wavefrontloaderworker.cpp
@@ -0,0 +1,44 @@
+#include "wavefrontloaderworker.h"
+#include
+#include "surfacemanager.h"
+// Ensure your SurfaceManager header is included here
+// #include "surfacemanager.h"
+
+WavefrontLoaderWorker::WavefrontLoaderWorker(QObject *parent)
+ : QObject(parent), m_isCancelled(false)
+{
+}
+
+void WavefrontLoaderWorker::process(QStringList args, SurfaceManager* manager) {
+ m_isCancelled = false; // Reset the flag for this run
+ int cnt = 0;
+
+ for (const QString &arg : args) {
+ // Safe check for cross-thread cancellation
+ if (m_isCancelled.load()) {
+ break;
+ }
+
+ if (arg.endsWith(".wft", Qt::CaseInsensitive)) {
+ // Signal the UI thread to update the label
+ emit progressLabelChanged(arg);
+
+ try {
+ // The actual heavy task
+ manager->loadWavefront(arg);
+ } catch (...) {
+ // Handle potential exceptions during load
+ break;
+ }
+
+ // Signal the UI thread to update the progress bar
+ emit progressValueChanged(++cnt);
+ }
+ }
+
+ emit finished();
+}
+
+void WavefrontLoaderWorker::cancel() {
+ m_isCancelled.store(true);
+}
diff --git a/wavefrontloaderworker.h b/wavefrontloaderworker.h
new file mode 100644
index 00000000..ee579c75
--- /dev/null
+++ b/wavefrontloaderworker.h
@@ -0,0 +1,36 @@
+#ifndef WAVEFRONTLOADERWORKER_H
+#define WAVEFRONTLOADERWORKER_H
+
+#include
+#include
+#include
+
+// Forward declaration of SurfaceManager to keep header clean
+class SurfaceManager;
+
+class WavefrontLoaderWorker : public QObject {
+ Q_OBJECT
+public:
+ explicit WavefrontLoaderWorker(QObject *parent = nullptr);
+
+public slots:
+ /**
+ * @brief The main loop that processes the wavefront files.
+ */
+ void process(QStringList args, SurfaceManager* manager);
+
+ /**
+ * @brief Slot to trigger a safe exit of the loop.
+ */
+ void cancel();
+
+signals:
+ void progressLabelChanged(const QString &text);
+ void progressValueChanged(int value);
+ void finished();
+
+private:
+ std::atomic m_isCancelled;
+};
+
+#endif // WAVEFRONTLOADERWORKER_H