diff options
| -rw-r--r-- | src/graphicsscene.cpp | 199 | ||||
| -rw-r--r-- | src/graphicsscene.h | 4 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 42 | ||||
| -rw-r--r-- | src/opticaldesign.h | 14 | ||||
| m--------- | third_party/goodies | 0 |
5 files changed, 254 insertions, 5 deletions
diff --git a/src/graphicsscene.cpp b/src/graphicsscene.cpp index 52416d5..e918ccc 100644 --- a/src/graphicsscene.cpp +++ b/src/graphicsscene.cpp @@ -4,6 +4,8 @@ #include <QGraphicsEllipseItem> #include <QGraphicsLineItem> #include <QGraphicsPolygonItem> +#include <QGraphicsTextItem> +#include <QGraphicsView> namespace orphex { @@ -31,7 +33,7 @@ GraphicsScene::GraphicsScene(QObject* parent) QLineF{QPointF{0, -50}, QPointF{0, 50}}, m_lenseItem }; - m_lineOfActionItem->setPen(QPen{Qt::gray, 0}); + m_lineOfActionItem->setPen(QPen{Qt::green, 0}); m_opticalAxisItem = new QGraphicsLineItem{ QLineF{QPointF{-1000, 0}, QPointF{1000, 0}}, @@ -72,10 +74,47 @@ GraphicsScene::GraphicsScene(QObject* parent) m_actualRangeAreaItem->setPen(QPen{m_sensorItem->pen().color(), 0}); m_actualRangeAreaItem->setBrush(QBrush{m_sensorItem->pen().color()}); m_actualRangeAreaItem->setOpacity(0.1); + + m_sensorLenseIntersectionItem = new QGraphicsEllipseItem{ + QRectF{QPointF{-1, -1}, QPointF{1, 1}}, + m_lenseItem + }; + m_sensorLenseIntersectionItem->setPen(QPen{Qt::red, 0.1}); + + const auto sensorLenseTextItem = new QGraphicsTextItem{ + tr("Scheimpflug: sensor/lense"), + m_sensorLenseIntersectionItem + }; + + sensorLenseTextItem->setPos(QPointF{-6, -8}); + auto font = sensorLenseTextItem->font(); + font.setPixelSize(2); + sensorLenseTextItem->setFont(font); + sensorLenseTextItem->setDefaultTextColor( + m_sensorLenseIntersectionItem->pen().color() + ); + + m_lenseLaserIntersectionItem = new QGraphicsEllipseItem{ + QRectF{QPointF{-1, -1}, QPointF{1, 1}}, + m_lenseItem + }; + m_lenseLaserIntersectionItem->setPen(QPen{Qt::blue, 0}); + + const auto lenseLaserTextItem = new QGraphicsTextItem{ + tr("Scheimpflug: lense/laser"), + m_lenseLaserIntersectionItem + }; + + lenseLaserTextItem->setPos(QPointF{-6, -2}); + lenseLaserTextItem->setFont(font); + lenseLaserTextItem->setDefaultTextColor( + m_lenseLaserIntersectionItem->pen().color() + ); } void GraphicsScene::update(OpticalDesign* design) { + qDebug() << "update"; if (!design) { qCritical() << Q_FUNC_INFO << "design is nullptr"; @@ -229,4 +268,162 @@ void GraphicsScene::update(OpticalDesign* design) ); // TODO: take back focal length into account + + // check Scheimpflug principle + // TODO: draw lines? + QPointF sensorLenseIntersection{}; + + if (m_sensorItem->line().intersects( + m_lineOfActionItem->line(), + &sensorLenseIntersection + ) == QLineF::NoIntersection) + { + qWarning() << "no intersection between sensor plane and lense plane"; + return; + } + + m_sensorLenseIntersectionItem->setPos(sensorLenseIntersection); + + QPointF lenseLaserIntersection{}; + + if (m_lineOfActionItem->line().intersects( + m_actualRangeItem->line(), + &lenseLaserIntersection + ) == QLineF::NoIntersection) + { + qWarning() << "no intersection between laser plane and lense plane"; + return; + } + + m_lenseLaserIntersectionItem->setPos(lenseLaserIntersection); + + if (!qFuzzyCompare(sensorLenseIntersection, lenseLaserIntersection)) + { + qWarning() << "The Scheimpflug principle is not observed:" << Qt::endl + << "sensor/lense intersection:" << sensorLenseIntersection + << Qt::endl + << "lense/laser intersection:" << lenseLaserIntersection; + } + + // TODO: move to settings + // doesn't work (doesn't zoom) + constexpr bool autoScale{false}; + + if (autoScale) + { + // region of interest + QRectF ROI{}; + + ROI |= m_actualRangeAreaItem->boundingRect(); + ROI |= m_desiredRangeAreaItem->boundingRect(); + ROI |= m_lenseItem->boundingRect(); + ROI |= m_sensorItem->boundingRect(); + ROI |= m_sensorLenseIntersectionItem->boundingRect(); + ROI |= m_lenseLaserIntersectionItem->boundingRect(); + + for (const auto& view : views()) + { + // TODO: move to settings + constexpr double margin{5.}; + view->setSceneRect(ROI); + } + } + + // calculate distance between sensor and lense on optical axis + QPointF sensorAxisIntersection{}; + + if (m_sensorItem->line().intersects( + m_opticalAxisItem->line(), + &sensorAxisIntersection + ) == QLineF::NoIntersection) + { + qCritical() << "no intersection between sensor and optical axis"; + return; + } + + design->set_lenseSensorDistanceMm( + QLineF{QPointF{0, 0}, sensorAxisIntersection}.length() + ); + + // calculate depth of field + QPointF axisLaserIntersection{}; + + if (m_opticalAxisItem->line().intersects( + m_actualRangeItem->line(), + &axisLaserIntersection + ) == QLineF::NoIntersection) + { + qCritical() << "no intersection between laser and optical axis"; + return; + } + + // const auto zM = std::min( + // design->get_sensorPixelsHeight(), + // design->get_sensorPixelsWidth() + // ) / + // 1000. / 1000.; + + // try z based on sensor cell size + // const auto zMm = std::min( + // design->get_sensorCellHeightUm(), + // design->get_sensorCellHeightUm() + // ) / + // 1000.; + // try z based on 1/16 sensor cell size (calibration step) + const auto zMm = std::min( + design->get_sensorCellHeightUm(), + design->get_sensorCellWidthUm() + ) / + 1000. / 16.; + + // try z based on E. Goldovsky method - 0.2% of sensor height + // const auto zMm = design->get_sensorHeightMm() * 0.2 / 100.; + // const auto RM = + // QLineF{QPointF{0., 0.}, axisLaserIntersection}.length() * 1000.; + const auto RMidMm = QLineF{QPointF{0., 0.}, axisLaserIntersection}.length(); + // const auto RFrontMm = QLineF{QPointF{0., 0.}, + // axisLaserIntersection}.length(); const auto fM = + // design->get_backFocalDistanceMm() * 1000.; + + // Gordijchuk & Pell, 1979, say to use focal distance. + // Wikipedia, based on the same book, says to use back focal distance. WTF? + // const auto fMm = design->get_backFocalDistanceMm(); + const auto fMm = design->get_focalDistanceMm(); + + const auto K = static_cast<int>(design->get_lenseAperture()) / 10.; + + using FromSharpDistMm = double; + using BackSharpDistMm = double; + using DofResult = std::tuple<FromSharpDistMm, BackSharpDistMm>; + const auto calculateDof = [zMm, fMm, K](const auto RMm) -> DofResult { + const auto ff = fMm * fMm; + const auto Rff = RMm * ff; + const auto Kfz = K * fMm * zMm; + const auto KRz = K * RMm * zMm; + + const auto R1Mm = Rff / (ff - Kfz + KRz); + const auto R2Mm = Rff / (ff + Kfz - KRz); + + return {R1Mm, R2Mm}; + }; + + const auto ff = fMm * fMm; + const auto Rff = RMidMm * ff; + const auto Kfz = K * fMm * zMm; + const auto KRz = K * RMidMm * zMm; + + // const auto R1M = Rff / (ff - Kfz + KRz); + // const auto R2M = Rff / (ff + Kfz - KRz); + + // const auto R1Mm = Rff / (ff - Kfz + KRz); + // const auto R2Mm = Rff / (ff + Kfz - KRz); + + const auto [R1Mm, R2Mm] = calculateDof(RMidMm); + + qDebug() << "AAAAA: update" << R1Mm << R2Mm + << design->get_sensorCellHeightUm(); + + design->set_frontSharpDistanceMm(R1Mm); + design->set_backSharpDistanceMm(R2Mm); + design->set_depthOfFieldMm((R2Mm - R1Mm)); } diff --git a/src/graphicsscene.h b/src/graphicsscene.h index 289deba..1c50c78 100644 --- a/src/graphicsscene.h +++ b/src/graphicsscene.h @@ -30,4 +30,8 @@ private: QGraphicsLineItem* m_actualRangeItem{nullptr}; QGraphicsPolygonItem* m_desiredRangeAreaItem{nullptr}; QGraphicsPolygonItem* m_actualRangeAreaItem{nullptr}; + + // debug + QGraphicsEllipseItem* m_sensorLenseIntersectionItem{nullptr}; + QGraphicsEllipseItem* m_lenseLaserIntersectionItem{nullptr}; }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 66274c1..e73df5a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -154,6 +154,13 @@ void MainWindow::initUi() } ); + connect( + m_design, + qOverload<>(&OpticalDesign::lenseApertureChanged), + m_scene, + std::bind(&GraphicsScene::update, m_scene, m_design) + ); + leftLayout->addRow(tr("Lense A&perture:"), comboBox); }; @@ -184,6 +191,14 @@ void MainWindow::initUi() MinValue{1.}, MaxValue{45.} ); + addFormPair( + tr("&Back Focal Distance (mm):"), + qOverload<double>(&OpticalDesign::backFocalDistanceMmChanged), + &OpticalDesign::get_backFocalDistanceMm, + &OpticalDesign::set_backFocalDistanceMm, + MinValue{1.}, + MaxValue{45.} + ); createLenseApertureComboBox(); @@ -271,6 +286,30 @@ void MainWindow::initUi() Decimals{2} ); + addFormLabel( + tr("Lense-Sensor Distance (mm):"), + qOverload<double>(&OpticalDesign::lenseSensorDistanceMmChanged), + Decimals{2} + ); + + addFormLabel( + tr("Front Sharp Distance (mm):"), + qOverload<double>(&OpticalDesign::frontSharpDistanceMmChanged), + Decimals{2} + ); + + addFormLabel( + tr("Back Sharp Distance (mm):"), + qOverload<double>(&OpticalDesign::backSharpDistanceMmChanged), + Decimals{2} + ); + + addFormLabel( + tr("Depth Of Field (mm):"), + qOverload<double>(&OpticalDesign::depthOfFieldMmChanged), + Decimals{2} + ); + // graphics view const auto openGlWidget = new QOpenGLWidget{centralWidget()}; QSurfaceFormat surfaceFormat{}; @@ -291,7 +330,8 @@ void MainWindow::initUi() ); m_view->setTransformationAnchor(GraphicsView::AnchorViewCenter); - m_view->scale(15, 15); + m_view->scale(5, 5); + m_view->setTransformationAnchor(GraphicsView::AnchorUnderMouse); m_view->centerOn({0, -m_design->get_lenseYPosMm()}); m_scene->update(m_design); diff --git a/src/opticaldesign.h b/src/opticaldesign.h index e6fb2c6..5a7ebac 100644 --- a/src/opticaldesign.h +++ b/src/opticaldesign.h @@ -36,13 +36,13 @@ Q_ENUM_NS(LenseAperture) // Q_DECLARE_METATYPE(orphex::LenseAperture) -class OpticalDesign : public G_Object +class OpticalDesign : public goodies::G_Object { Q_OBJECT - DEFAULT_CONSTRUCTOR(G_Object, OpticalDesign) + DEFAULT_CONSTRUCTOR(goodies::G_Object, OpticalDesign) DEFAULT_DESTRUCTOR(OpticalDesign); - G_OBJECT(G_Object, OpticalDesign) + G_OBJECT(goodies::G_Object, OpticalDesign) public: void calculate() const; @@ -51,12 +51,16 @@ protected: INI_PROPERTY(double, opticalAxisAngleDegrees, 45.); // the Y axis is directed from the laser to the lens INI_PROPERTY(double, lenseYPosMm, 50.); + // default values for CS2006ZM06 lense INI_PROPERTY(double, focalDistanceMm, 6.); + INI_PROPERTY(double, backFocalDistanceMm, 7.3); + // FIXME: loading doesn't work INI_PROPERTY_VAR( orphex::LenseAperture, lenseAperture, orphex::LenseAperture::F1_2 ); + // default values for IMX287 sensor INI_PROPERTY(double, sensorWidthMm, 4.98); INI_PROPERTY(double, sensorHeightMm, 3.74); INI_PROPERTY(double, sensorCellWidthUm, 6.9); @@ -71,4 +75,8 @@ protected: G_PROPERTY_DEFAULT(double, sensorLenseAngleDegrees, 0.); G_PROPERTY_DEFAULT(double, actualZBaseMm, 0.); G_PROPERTY_DEFAULT(double, actualZRangeMm, 0.); + G_PROPERTY_DEFAULT(double, lenseSensorDistanceMm, 0.); + G_PROPERTY_DEFAULT(double, frontSharpDistanceMm, 0.); + G_PROPERTY_DEFAULT(double, backSharpDistanceMm, 0.); + G_PROPERTY_DEFAULT(double, depthOfFieldMm, 0.); }; diff --git a/third_party/goodies b/third_party/goodies -Subproject e7019763076cbbe3d52c2a03133c3ded5558f01 +Subproject ffac2767127fe0bdb990187bbf9ec640e06933a |
