diff options
| author | Nikita Kostovsky <nikita@kostovsky.me> | 2025-09-19 14:28:21 +0200 |
|---|---|---|
| committer | Nikita Kostovsky <nikita@kostovsky.me> | 2025-09-19 21:04:04 +0200 |
| commit | ad001563fda4a9061909bd09dcf51238138014d6 (patch) | |
| tree | 5394cc0436d6ef811b6b791c37a233047c99247d /src | |
initial commit
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 45 | ||||
| -rw-r--r-- | src/graphicsscene.cpp | 232 | ||||
| -rw-r--r-- | src/graphicsscene.h | 33 | ||||
| -rw-r--r-- | src/graphicsview.cpp | 47 | ||||
| -rw-r--r-- | src/graphicsview.h | 21 | ||||
| -rw-r--r-- | src/main.cpp | 19 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 303 | ||||
| -rw-r--r-- | src/mainwindow.h | 27 | ||||
| -rw-r--r-- | src/opticaldesign.cpp | 14 | ||||
| -rw-r--r-- | src/opticaldesign.h | 74 |
10 files changed, 815 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..9ef55bb --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 4.1) + +project(optical_design LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets) + +qt_standard_project_setup() + +qt_add_executable(${PROJECT_NAME} + WIN32 MACOSX_BUNDLE + main.cpp + mainwindow.cpp + mainwindow.h + opticaldesign.h + opticaldesign.cpp + graphicsscene.h + graphicsscene.cpp + graphicsview.h + graphicsview.cpp +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + Qt::Core + Qt::Widgets + goodies +) + +include(GNUInstallDirs) + +install(TARGETS ${PROJECT_NAME} + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET ${PROJECT_NAME} + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) diff --git a/src/graphicsscene.cpp b/src/graphicsscene.cpp new file mode 100644 index 0000000..52416d5 --- /dev/null +++ b/src/graphicsscene.cpp @@ -0,0 +1,232 @@ +#include "graphicsscene.h" + +// qt +#include <QGraphicsEllipseItem> +#include <QGraphicsLineItem> +#include <QGraphicsPolygonItem> + +namespace orphex +{ +namespace constants +{ +const QLineF laserPlane{QPointF{-1000, 0}, QPointF{1000, 0}}; +} +} // namespace orphex + +GraphicsScene::GraphicsScene(QObject* parent) + : QGraphicsScene{parent} +{ + setSceneRect(QRectF{QPointF{-1000, -1000}, QPointF{1000, 1000}}); + + m_lenseItem = addEllipse( + QRectF{QPointF{-1, -15}, QPointF{1, 15}}, + QPen{Qt::yellow, 0} + ); + m_lenseItem->setTransformOriginPoint(m_lenseItem->boundingRect().center()); + + m_sensorItem = new QGraphicsLineItem(QLineF{}, m_lenseItem); + m_sensorItem->setPen(QPen{Qt::red, 0}); + + m_lineOfActionItem = new QGraphicsLineItem{ + QLineF{QPointF{0, -50}, QPointF{0, 50}}, + m_lenseItem + }; + m_lineOfActionItem->setPen(QPen{Qt::gray, 0}); + + m_opticalAxisItem = new QGraphicsLineItem{ + QLineF{QPointF{-1000, 0}, QPointF{1000, 0}}, + m_lenseItem + }; + m_opticalAxisItem->setPen(QPen{Qt::gray, 0}); + + using namespace orphex::constants; + m_laserPlaneItem = addLine(laserPlane, QPen{Qt::black, 0}); + m_laserPlaneItem->stackBefore(m_lenseItem); + + m_reverseLaserPlaneItem = new QGraphicsLineItem(QLineF{}, m_lenseItem); + m_reverseLaserPlaneItem->setPen(QPen{Qt::magenta, 2}); + m_reverseLaserPlaneItem->setVisible(false); + + m_desiredLaserPlaneItem = new QGraphicsLineItem(QLineF{}, m_lenseItem); + m_desiredLaserPlaneItem->setPen(QPen{Qt::yellow, 0.1}); + m_desiredLaserPlaneItem->setOpacity(0.5); + + m_desiredImagePlaneItem = new QGraphicsLineItem(QLineF{}, m_lenseItem); + m_desiredImagePlaneItem->setPen(QPen{Qt::yellow, 0.15}); + m_desiredImagePlaneItem->stackBefore(m_sensorItem); + + m_desiredRangeAreaItem = new QGraphicsPolygonItem{m_lenseItem}; + m_desiredRangeAreaItem->setPen( + QPen{m_desiredLaserPlaneItem->pen().color(), 0} + ); + m_desiredRangeAreaItem->setBrush( + QBrush{m_desiredLaserPlaneItem->pen().color()} + ); + m_desiredRangeAreaItem->setOpacity(0.1); + + m_actualRangeItem = new QGraphicsLineItem(QLineF{}, m_lenseItem); + m_actualRangeItem->setPen(QPen{Qt::red, 0.1}); + m_actualRangeItem->setOpacity(0.5); + + m_actualRangeAreaItem = new QGraphicsPolygonItem{m_lenseItem}; + m_actualRangeAreaItem->setPen(QPen{m_sensorItem->pen().color(), 0}); + m_actualRangeAreaItem->setBrush(QBrush{m_sensorItem->pen().color()}); + m_actualRangeAreaItem->setOpacity(0.1); +} + +void GraphicsScene::update(OpticalDesign* design) +{ + if (!design) + { + qCritical() << Q_FUNC_INFO << "design is nullptr"; + return; + } + + using namespace orphex::constants; + + const auto F = design->get_focalDistanceMm(); + const auto H = design->get_lenseYPosMm(); + const auto w = design->get_sensorPixelsWidth(); + const auto h = design->get_sensorPixelsHeight(); + const auto wMm = design->get_sensorWidthMm(); + const auto hMm = design->get_sensorHeightMm(); + const auto oAngle = design->get_opticalAxisAngleDegrees(); + + m_lenseItem->setRotation(m_lenseItem->rotation()); + m_lenseItem->setRotation(-oAngle); + m_lenseItem->setPos(QPointF{0, -H}); + + const auto objectToImage = [F](const QPointF& objectPoint) -> QPointF { + // please note that object X is negative + const auto imagePointX = 1. / (1. / F - 1. / -objectPoint.x()); + const auto magnification = imagePointX / objectPoint.x(); + const auto imagePointY = magnification * objectPoint.y(); + + return QPointF{imagePointX, imagePointY}; + }; + + const auto imageToObject = [F](const QPointF& imagePoint) -> QPointF { + const auto objectPointX = 1. / (1. / imagePoint.x() - 1. / F); + const auto magnification = imagePoint.x() / objectPointX; + const auto objectPointY = imagePoint.y() / magnification; + + return QPointF{objectPointX, objectPointY}; + }; + + // calculate sensor plane + const auto& oai = m_opticalAxisItem; + const auto& oail = m_opticalAxisItem->line(); + const QLineF opticalAxisScene{ + oai->mapToScene(oail.p1()), + oai->mapToScene(oail.p2()) + }; + + QPointF intersection{}; + const auto type = + m_laserPlaneItem->line().intersects(opticalAxisScene, &intersection); + + if (type == QLineF::NoIntersection) + { + qCritical() << "no intersection between laser plane and optical axis"; + return; + } + + if (!qFuzzyCompare(intersection.y(), 0.)) + { + qCritical() << "laser plane/optical axis intersection point y != 0"; + return; + } + + // take 2 points - to the left and to the right from intersection point; + const auto leftP = intersection / 4; + // const auto rightP = intersection + leftP; + const auto rightP = intersection * 1.5; + + // create desired line on laser plane + m_desiredLaserPlaneItem->setLine( + {m_lenseItem->mapFromScene(QPointF{-design->get_zBaseMm(), 0}), + m_lenseItem->mapFromScene( + QPointF{-(design->get_zBaseMm() + design->get_zRangeMm()), 0} + )} + ); + + // calculate image plane + const auto debugL = m_desiredLaserPlaneItem->line(); + const QLineF debugImageL{ + objectToImage(debugL.p1()), + objectToImage(debugL.p2()) + }; + + m_desiredImagePlaneItem->setLine(debugImageL); + + // create sensor line on image plane + QPointF sensorCenterPos{}; + + if (m_opticalAxisItem->line().intersects( + m_desiredImagePlaneItem->line(), + &sensorCenterPos + ) == QLineF::NoIntersection) + { + qCritical() << "no intersection between sensor plane and optical axis"; + return; + } + + const auto sensorHalfHeight = design->get_sensorHeightMm() / 2.; + QLineF sensorLine{QPointF{0, 0}, QPointF{0, sensorHalfHeight}}; + sensorLine.translate(sensorCenterPos); + sensorLine.setAngle(m_desiredImagePlaneItem->line().angle()); + sensorLine.setP1(sensorCenterPos + (sensorCenterPos - sensorLine.p2())); + // at this momet sensor is centered and rotated. apply vertical offset + const auto offsetRatio = + design->get_sensorVerticalOffsetMm() / sensorLine.length(); + const auto offset = sensorLine.pointAt(1 + offsetRatio) - sensorLine.p2(); + sensorLine.setPoints(sensorLine.p1() + offset, sensorLine.p2() + offset); + + m_sensorItem->setLine(sensorLine); + + // find laser plane range which corresponds to sensor position + m_actualRangeItem->setLine({ + imageToObject(m_sensorItem->line().p1()), + imageToObject(m_sensorItem->line().p2()), + }); + + // not that object area has negative coords + design->set_actualZBaseMm( + -m_actualRangeItem->mapToScene(m_actualRangeItem->line().p1()).x() + ); + design->set_actualZRangeMm(m_actualRangeItem->line().length()); + + // convert debug image plane to laser plane just to verify they are the same + // TODO: check with code and log errors? + const QLineF reverseLaserPlaneLine{ + imageToObject(m_desiredImagePlaneItem->line().p1()), + imageToObject(m_desiredImagePlaneItem->line().p2()) + }; + m_reverseLaserPlaneItem->setLine(reverseLaserPlaneLine); + + // fill desired range area + m_desiredRangeAreaItem->setPolygon( + QPolygonF{ + {m_desiredImagePlaneItem->line().p1(), + m_desiredImagePlaneItem->line().p2(), + m_desiredLaserPlaneItem->line().p2(), + m_desiredLaserPlaneItem->line().p1()} + } + ); + + // fill actual range area + m_actualRangeAreaItem->setPolygon( + QPolygonF{ + {m_sensorItem->line().p1(), + m_sensorItem->line().p2(), + m_actualRangeItem->line().p2(), + m_actualRangeItem->line().p1()} + } + ); + + design->set_sensorLenseAngleDegrees( + m_desiredImagePlaneItem->line().angleTo(m_lineOfActionItem->line()) + ); + + // TODO: take back focal length into account +} diff --git a/src/graphicsscene.h b/src/graphicsscene.h new file mode 100644 index 0000000..289deba --- /dev/null +++ b/src/graphicsscene.h @@ -0,0 +1,33 @@ +#pragma once + +// qt +#include <QGraphicsScene> + +// optical design +#include "opticaldesign.h" + +class GraphicsScene : public QGraphicsScene +{ + Q_OBJECT + +public: + explicit GraphicsScene(QObject* parent = nullptr); + ~GraphicsScene() override = default; + +public slots: + // TODO: store design in scene? + void update(OpticalDesign* design); + +private: + QGraphicsLineItem* m_lineOfActionItem{nullptr}; + QGraphicsEllipseItem* m_lenseItem{nullptr}; + QGraphicsLineItem* m_opticalAxisItem{nullptr}; + QGraphicsLineItem* m_laserPlaneItem{nullptr}; + QGraphicsLineItem* m_desiredLaserPlaneItem{nullptr}; + QGraphicsLineItem* m_reverseLaserPlaneItem{nullptr}; + QGraphicsLineItem* m_desiredImagePlaneItem{nullptr}; + QGraphicsLineItem* m_sensorItem{nullptr}; + QGraphicsLineItem* m_actualRangeItem{nullptr}; + QGraphicsPolygonItem* m_desiredRangeAreaItem{nullptr}; + QGraphicsPolygonItem* m_actualRangeAreaItem{nullptr}; +}; diff --git a/src/graphicsview.cpp b/src/graphicsview.cpp new file mode 100644 index 0000000..b81cff4 --- /dev/null +++ b/src/graphicsview.cpp @@ -0,0 +1,47 @@ +#include "graphicsview.h" + +// qt +#include <QWheelEvent> + +GraphicsView::GraphicsView(QWidget* parent) + : QGraphicsView{parent} +{ + init(); +} + +GraphicsView::GraphicsView(QGraphicsScene* scene, QWidget* parent) + : QGraphicsView{scene, parent} +{ + init(); +} + +void GraphicsView::wheelEvent(QWheelEvent* event) +{ + setTransformationAnchor(GraphicsView::AnchorUnderMouse); + + QPoint pixels = event->pixelDelta(); + QPoint degrees = event->angleDelta(); + const auto oldScenePos = event->scenePosition(); + const auto oldMapped = mapToScene(event->position().toPoint()); + + if (pixels.isNull() && degrees.isNull()) + return; + + const double scaleMultiplier{1.1}; + const auto steps = double(pixels.isNull() ? degrees.y() : pixels.y()); + auto actualScale = std::abs(steps) * scaleMultiplier / + double(QWheelEvent::DefaultDeltasPerStep); + + if (steps < 0) + actualScale = 1 / actualScale; + + scale(actualScale, actualScale); + + event->accept(); +} + +void GraphicsView::init() +{ + setDragMode(QGraphicsView::ScrollHandDrag); + setRenderHint(QPainter::Antialiasing, true); +} diff --git a/src/graphicsview.h b/src/graphicsview.h new file mode 100644 index 0000000..b7672ee --- /dev/null +++ b/src/graphicsview.h @@ -0,0 +1,21 @@ +#pragma once + +// qt +#include <QGraphicsView> + +class GraphicsView : public QGraphicsView +{ + Q_OBJECT + +public: + explicit GraphicsView(QWidget* parent = nullptr); + explicit GraphicsView(QGraphicsScene* scene, QWidget* parent = nullptr); + ~GraphicsView() override = default; + + // QGraphicsView +public: + void wheelEvent(QWheelEvent* event) override; + +private: + void init(); +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..96ec1e3 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,19 @@ +#include "mainwindow.h" + +#include <QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + a.setApplicationName("OpticalDesign"); + a.setApplicationDisplayName("Optical Design"); + // TODO: move to cmake + a.setApplicationVersion(QStringLiteral("0.1")); + a.setOrganizationName("Orphex"); + a.setOrganizationDomain("kostovsky.me"); + + MainWindow w; + w.showMaximized(); + return a.exec(); +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 0000000..66274c1 --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,303 @@ +#include "mainwindow.h" + +// qt +#include <QComboBox> +#include <QDoubleSpinBox> +#include <QFormLayout> +#include <QHBoxLayout> +#include <QLabel> +#include <QOpenGLWidget> +#include <QShortcut> +#include <QStyle> + +// optical design +#include "graphicsscene.h" +#include "graphicsview.h" +#include "opticaldesign.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow{parent} + , m_scene{new GraphicsScene{this}} + , m_view{new GraphicsView{m_scene, this}} + , m_design{new OpticalDesign{this}} +{ + initUi(); + initShortcuts(); +} + +MainWindow::~MainWindow() {} + +void MainWindow::initUi() +{ + setMinimumSize({640, 480}); + + qDebug() << "global settings path:" + << GlobalSettings::instance()->fileName(); + + const auto mainWidget = new QWidget{this}; + + const auto mainLayout = new QHBoxLayout{mainWidget}; + mainWidget->setLayout(mainLayout); + // addLayout sets parent below + const auto leftLayout = new QFormLayout{}; + leftLayout->setLabelAlignment(Qt::AlignVCenter | Qt::AlignRight); + mainLayout->addLayout(leftLayout); + + // left panel + const auto connectWidgetToProperty = [this]( + const auto widget, + const auto widgetSignal, + const auto widgetSetter, + const auto propertyObject, + const auto propertySignal, + const auto propertySetter + ) { + connect(widget, widgetSignal, propertyObject, propertySetter); + connect(propertyObject, propertySignal, widget, widgetSetter); + }; + + using MinValue = double; + using MaxValue = double; + using SingleStep = double; + auto addFormPair = [this, leftLayout, connectWidgetToProperty]( + const QString text, + const auto propertySignal, + const auto propertyGetter, + const auto propertySetter, + const MinValue minValue, + const MaxValue maxValue, + const SingleStep singleStep = 0.1 + ) { + const auto spinBox = new QDoubleSpinBox{centralWidget()}; + spinBox->setRange(minValue, maxValue); + spinBox->setSingleStep(singleStep); + spinBox->setValue(std::bind(propertyGetter, m_design)()); + + connect( + spinBox, + &QDoubleSpinBox::valueChanged, + m_design, + propertySetter + ); + connect(m_design, propertySignal, spinBox, &QDoubleSpinBox::setValue); + connect( + m_design, + propertySignal, + m_scene, + std::bind(&GraphicsScene::update, m_scene, m_design) + ); + + leftLayout->addRow(text, spinBox); + }; + + using Decimals = int; + auto addFormLabel = [this, leftLayout]( + const QString text, + const auto propertySignal, + const Decimals decimals = 2 + ) { + const auto label = new QLabel{centralWidget()}; + + connect( + m_design, + propertySignal, + label, + [label, decimals](const auto value) { + label->setText(QString::number(value, 'F', decimals)); + } + ); + + leftLayout->addRow(text, label); + }; + + const auto createLenseApertureComboBox = [this, leftLayout]() { + const auto comboBox = new QComboBox{centralWidget()}; + + using namespace orphex; + const auto metaEnum = QMetaEnum::fromType<LenseAperture>(); + + for (int i{0}; i < metaEnum.keyCount(); ++i) + { + const auto key = metaEnum.key(i); + const auto value = metaEnum.value(i); + const auto text = + QString{key} + .replace(QStringLiteral("F"), QStringLiteral("1/")) + .replace(QChar{'_'}, QChar{'.'}); + + comboBox->addItem(text, value); + } + + // update property on combobox change + connect( + comboBox, + &QComboBox::currentIndexChanged, + m_design, + [this, metaEnum, comboBox](const auto index) { + m_design->set_lenseAperture( + comboBox->currentData().value<LenseAperture>() + ); + + qDebug() << m_design->get_lenseAperture(); + } + ); + + // update combobox on property change + connect( + m_design, + qOverload<LenseAperture>(&OpticalDesign::lenseApertureChanged), + comboBox, + [comboBox](const auto value) { + comboBox->setCurrentIndex( + comboBox->findData(QVariant::fromValue(value)) + ); + } + ); + + leftLayout->addRow(tr("Lense A&perture:"), comboBox); + }; + + addFormPair( + tr("&Axis Angle (°):"), + qOverload<double>(&OpticalDesign::opticalAxisAngleDegreesChanged), + &OpticalDesign::get_opticalAxisAngleDegrees, + &OpticalDesign::set_opticalAxisAngleDegrees, + MinValue{10.}, + MaxValue{85.} + ); + + addFormPair( + tr("Lense &Y pos (mm):"), + qOverload<double>(&OpticalDesign::lenseYPosMmChanged), + &OpticalDesign::get_lenseYPosMm, + &OpticalDesign::set_lenseYPosMm, + MinValue{10.}, + MaxValue{500.}, + SingleStep{1.} + ); + + addFormPair( + tr("&Focal Distance (mm):"), + qOverload<double>(&OpticalDesign::focalDistanceMmChanged), + &OpticalDesign::get_focalDistanceMm, + &OpticalDesign::set_focalDistanceMm, + MinValue{1.}, + MaxValue{45.} + ); + + createLenseApertureComboBox(); + + addFormPair( + tr("Sensor &Width (mm):"), + qOverload<double>(&OpticalDesign::sensorWidthMmChanged), + &OpticalDesign::get_sensorWidthMm, + &OpticalDesign::set_sensorWidthMm, + MinValue{1.}, + MaxValue{30.} + ); + + addFormPair( + tr("Sensor &Height (mm):"), + qOverload<double>(&OpticalDesign::sensorHeightMmChanged), + &OpticalDesign::get_sensorHeightMm, + &OpticalDesign::set_sensorHeightMm, + MinValue{1.}, + MaxValue{30.} + ); + + addFormPair( + tr("Sensor Cell W&idth (mm):"), + qOverload<double>(&OpticalDesign::sensorCellWidthUmChanged), + &OpticalDesign::get_sensorCellWidthUm, + &OpticalDesign::set_sensorCellWidthUm, + MinValue{1.}, + MaxValue{30.} + ); + + addFormPair( + tr("Sensor Cell H&eight (mm):"), + qOverload<double>(&OpticalDesign::sensorCellHeightUmChanged), + &OpticalDesign::get_sensorCellHeightUm, + &OpticalDesign::set_sensorCellHeightUm, + MinValue{1.}, + MaxValue{30.} + ); + + addFormPair( + tr("Sensor Vertical &Offset (mm):"), + qOverload<double>(&OpticalDesign::sensorVerticalOffsetMmChanged), + &OpticalDesign::get_sensorVerticalOffsetMm, + &OpticalDesign::set_sensorVerticalOffsetMm, + MinValue{-50.}, + MaxValue{50.}, + SingleStep{0.1} + ); + + addFormPair( + tr("Z &Base (mm):"), + qOverload<double>(&OpticalDesign::zBaseMmChanged), + &OpticalDesign::get_zBaseMm, + &OpticalDesign::set_zBaseMm, + MinValue{1.}, + MaxValue{10000.}, + SingleStep{1.} + ); + + addFormPair( + tr("Z &Range (mm):"), + qOverload<double>(&OpticalDesign::zRangeMmChanged), + &OpticalDesign::get_zRangeMm, + &OpticalDesign::set_zRangeMm, + MinValue{1.}, + MaxValue{10000.}, + SingleStep{1.} + ); + + addFormLabel( + tr("Sensor/Lense Angle (°):"), + qOverload<double>(&OpticalDesign::sensorLenseAngleDegreesChanged), + Decimals{3} + ); + + addFormLabel( + tr("Actual Z Base (mm):"), + qOverload<double>(&OpticalDesign::actualZBaseMmChanged), + Decimals{2} + ); + + addFormLabel( + tr("Actual Z Range (mm):"), + qOverload<double>(&OpticalDesign::actualZRangeMmChanged), + Decimals{2} + ); + + // graphics view + const auto openGlWidget = new QOpenGLWidget{centralWidget()}; + QSurfaceFormat surfaceFormat{}; + surfaceFormat.setSamples(4); + openGlWidget->setFormat(surfaceFormat); + m_view->setViewport(openGlWidget); + // m_view->centerOn({0, 0}); + // m_view->ensureVisible(QRectF{m_scene->sceneRect()}); + + mainLayout->addWidget(m_view); + setCentralWidget(mainWidget); + + connect( + m_design, + qOverload<double>(&OpticalDesign::opticalAxisAngleDegreesChanged), + this, + [](const auto value) { qDebug() << "angle changed:" << value; } + ); + + m_view->setTransformationAnchor(GraphicsView::AnchorViewCenter); + m_view->scale(15, 15); + m_view->centerOn({0, -m_design->get_lenseYPosMm()}); + + m_scene->update(m_design); +} + +void MainWindow::initShortcuts() +{ + new QShortcut{Qt::CTRL | Qt::Key_Q, this, this, &MainWindow::close}; +} diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 0000000..3b4922a --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,27 @@ +#pragma once + +// qt +#include <QMainWindow> + +// optical design +class GraphicsScene; +class GraphicsView; +class OpticalDesign; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + void initUi(); + void initShortcuts(); + +private: + GraphicsScene* m_scene{nullptr}; + GraphicsView* m_view{nullptr}; + OpticalDesign* m_design{nullptr}; +}; diff --git a/src/opticaldesign.cpp b/src/opticaldesign.cpp new file mode 100644 index 0000000..01f6050 --- /dev/null +++ b/src/opticaldesign.cpp @@ -0,0 +1,14 @@ +#include "opticaldesign.h" + +// qt +#include <QSize> + +void OpticalDesign::calculate() const +{ + const auto F = get_focalDistanceMm(); + const auto w = get_sensorPixelsWidth(); + const auto h = get_sensorPixelsHeight(); + const auto wMm = get_sensorWidthMm(); + const auto hMm = get_sensorHeightMm(); + const auto oAngle = get_opticalAxisAngleDegrees(); +} diff --git a/src/opticaldesign.h b/src/opticaldesign.h new file mode 100644 index 0000000..e6fb2c6 --- /dev/null +++ b/src/opticaldesign.h @@ -0,0 +1,74 @@ +#pragma once + +// goodies +#include <g_object.h> +#include <g_property.h> + +namespace orphex +{ +Q_NAMESPACE +// multiplied by 10 +enum class LenseAperture +{ + F0_5 = 05, + F0_7 = 07, + F1_0 = 10, + F1_2 = 12, + F1_4 = 14, + F2_0 = 20, + F2_8 = 28, + F4_0 = 40, + F5_6 = 56, + F8_0 = 80, + F11_0 = 110, + F16_0 = 160, + F22_0 = 220, + F32_0 = 320, + F45_0 = 450, + F64_0 = 640, + F90_0 = 900, + F128_0 = 1280, + F180_0 = 1800, + F256_0 = 2560 +}; +Q_ENUM_NS(LenseAperture) +} // namespace orphex + +// Q_DECLARE_METATYPE(orphex::LenseAperture) + +class OpticalDesign : public G_Object +{ + Q_OBJECT + + DEFAULT_CONSTRUCTOR(G_Object, OpticalDesign) + DEFAULT_DESTRUCTOR(OpticalDesign); + G_OBJECT(G_Object, OpticalDesign) + +public: + void calculate() const; + +protected: + INI_PROPERTY(double, opticalAxisAngleDegrees, 45.); + // the Y axis is directed from the laser to the lens + INI_PROPERTY(double, lenseYPosMm, 50.); + INI_PROPERTY(double, focalDistanceMm, 6.); + INI_PROPERTY_VAR( + orphex::LenseAperture, + lenseAperture, + orphex::LenseAperture::F1_2 + ); + INI_PROPERTY(double, sensorWidthMm, 4.98); + INI_PROPERTY(double, sensorHeightMm, 3.74); + INI_PROPERTY(double, sensorCellWidthUm, 6.9); + INI_PROPERTY(double, sensorCellHeightUm, 6.9); + // sensor vertical offset along the image plane + INI_PROPERTY(double, sensorVerticalOffsetMm, 0.); + INI_PROPERTY(uint16_t, sensorPixelsHeight, 720); + INI_PROPERTY(uint16_t, sensorPixelsWidth, 544); + INI_PROPERTY(double, zBaseMm, 90); + INI_PROPERTY(double, zRangeMm, 250); + + G_PROPERTY_DEFAULT(double, sensorLenseAngleDegrees, 0.); + G_PROPERTY_DEFAULT(double, actualZBaseMm, 0.); + G_PROPERTY_DEFAULT(double, actualZRangeMm, 0.); +}; |
