diff --git a/src/Camera.cpp b/src/Camera.cpp new file mode 100644 index 0000000..2de01ce --- /dev/null +++ b/src/Camera.cpp @@ -0,0 +1,111 @@ +#include "Camera.h" +#include +#include + +Camera::Camera(QObject *parent) : QObject(parent) +{ + // Set initial view to an isometric angle on the XY plane + m_xRot = 30 * 16; + m_yRot = -45 * 16; + m_zoom = -20.0f; + m_panX = 0.0f; + m_panY = 0.0f; +} + +void Camera::processMouseMovement(QMouseEvent* event, const QPoint& lastPos) +{ + int dx = event->pos().x() - lastPos.x(); + int dy = event->pos().y() - lastPos.y(); + + if (event->buttons() & Qt::MiddleButton) { + if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { + // Pan + setPanX(m_panX + dx / 100.0f); + setPanY(m_panY - dy / 100.0f); + } else { + // Rotate + setXRotation(m_xRot + 8 * dy); + setYRotation(m_yRot + 8 * dx); + } + } +} + +void Camera::processWheel(QWheelEvent* event) +{ + QPoint numDegrees = event->angleDelta() / 8; + if (!numDegrees.isNull()) { + setZoom(m_zoom + numDegrees.y() / 5.0f); + } +} + +QMatrix4x4 Camera::modelViewMatrix() const +{ + QMatrix4x4 model; + model.translate(m_panX, m_panY, m_zoom); + model.rotate(m_xRot / 16.0f, 1, 0, 0); + model.rotate(m_yRot / 16.0f, 0, 1, 0); + return model; +} + +float Camera::xRotation() const { return m_xRot; } +void Camera::setXRotation(float angle) +{ + if (angle != m_xRot) { + m_xRot = angle; + emit cameraChanged(); + } +} + +float Camera::yRotation() const { return m_yRot; } +void Camera::setYRotation(float angle) +{ + if (angle != m_yRot) { + m_yRot = angle; + emit cameraChanged(); + } +} + +float Camera::zoom() const { return m_zoom; } +void Camera::setZoom(float value) +{ + if (value != m_zoom) { + m_zoom = value; + emit cameraChanged(); + } +} + +float Camera::panX() const { return m_panX; } +void Camera::setPanX(float value) +{ + if (value != m_panX) { + m_panX = value; + emit cameraChanged(); + } +} + +float Camera::panY() const { return m_panY; } +void Camera::setPanY(float value) +{ + if (value != m_panY) { + m_panY = value; + emit cameraChanged(); + } +} + +void Camera::saveState() +{ + m_savedXRot = m_xRot; + m_savedYRot = m_yRot; + m_savedPanX = m_panX; + m_savedPanY = m_panY; + m_savedZoom = m_zoom; +} + +void Camera::restoreState() +{ + setXRotation(m_savedXRot); + setYRotation(m_savedYRot); + setPanX(m_savedPanX); + setPanY(m_savedPanY); + setZoom(m_savedZoom); +} diff --git a/src/Camera.h b/src/Camera.h new file mode 100644 index 0000000..5b42f03 --- /dev/null +++ b/src/Camera.h @@ -0,0 +1,64 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include +#include +#include +#include + +class Camera : public QObject +{ + Q_OBJECT + Q_PROPERTY(float xRotation READ xRotation WRITE setXRotation) + Q_PROPERTY(float yRotation READ yRotation WRITE setYRotation) + Q_PROPERTY(float zoom READ zoom WRITE setZoom) + Q_PROPERTY(float panX READ panX WRITE setPanX) + Q_PROPERTY(float panY READ panY WRITE setPanY) + +public: + explicit Camera(QObject *parent = nullptr); + + void processMouseMovement(QMouseEvent* event, const QPoint& lastPos); + void processWheel(QWheelEvent* event); + + QMatrix4x4 modelViewMatrix() const; + + float xRotation() const; + void setXRotation(float angle); + float yRotation() const; + void setYRotation(float angle); + float zoom() const; + void setZoom(float value); + float panX() const; + void setPanX(float value); + float panY() const; + void setPanY(float value); + + void saveState(); + void restoreState(); + + float savedXRot() const { return m_savedXRot; } + float savedYRot() const { return m_savedYRot; } + float savedZoom() const { return m_savedZoom; } + float savedPanX() const { return m_savedPanX; } + float savedPanY() const { return m_savedPanY; } + +signals: + void cameraChanged(); + +private: + float m_xRot; + float m_yRot; + float m_zoom; + float m_panX; + float m_panY; + + float m_savedXRot = 0; + float m_savedYRot = 0; + float m_savedZoom = -5.0f; + float m_savedPanX = 0; + float m_savedPanY = 0; +}; + +#endif // CAMERA_H diff --git a/src/ViewportWidget.cpp b/src/ViewportWidget.cpp index 4e508be..9fe053d 100644 --- a/src/ViewportWidget.cpp +++ b/src/ViewportWidget.cpp @@ -1,4 +1,5 @@ #include "ViewportWidget.h" +#include "Camera.h" #include "ViewCube.h" #include "SketchGrid.h" #include "Document.h" @@ -25,18 +26,14 @@ ViewportWidget::ViewportWidget(QWidget *parent) : QOpenGLWidget(parent) { + m_camera = new Camera(this); + connect(m_camera, &Camera::cameraChanged, this, QOverload<>::of(&QWidget::update)); m_viewCube = new ViewCube(); m_sketchGrid = new SketchGrid(); m_featureBrowser = new FeatureBrowser(); setMouseTracking(true); setFocusPolicy(Qt::StrongFocus); - // Set initial view to an isometric angle on the XY plane and show grid - m_xRot = 30 * 16; - m_yRot = -45 * 16; - m_zoom = -20.0f; - m_panX = 0.0f; - m_panY = 0.0f; m_currentPlane = SketchPlane::XY; m_toolIcons.insert(static_cast(ApplicationController::ToolType::Line), new QSvgRenderer(QString(":/icons/line.svg"), this)); @@ -59,51 +56,6 @@ void ViewportWidget::setDocument(Document* document) m_featureBrowser->setDocument(document); } -float ViewportWidget::xRotation() const { return m_xRot; } -void ViewportWidget::setXRotation(float angle) -{ - if (angle != m_xRot) { - m_xRot = angle; - update(); - } -} - -float ViewportWidget::yRotation() const { return m_yRot; } -void ViewportWidget::setYRotation(float angle) -{ - if (angle != m_yRot) { - m_yRot = angle; - update(); - } -} - -float ViewportWidget::zoom() const { return m_zoom; } -void ViewportWidget::setZoom(float value) -{ - if (value != m_zoom) { - m_zoom = value; - update(); - } -} - -float ViewportWidget::panX() const { return m_panX; } -void ViewportWidget::setPanX(float value) -{ - if (value != m_panX) { - m_panX = value; - update(); - } -} - -float ViewportWidget::panY() const { return m_panY; } -void ViewportWidget::setPanY(float value) -{ - if (value != m_panY) { - m_panY = value; - update(); - } -} - void ViewportWidget::initializeGL() { initializeOpenGLFunctions(); @@ -122,10 +74,7 @@ void ViewportWidget::paintGL() glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); - QMatrix4x4 model; - model.translate(m_panX, m_panY, m_zoom); - model.rotate(m_xRot / 16.0f, 1, 0, 0); - model.rotate(m_yRot / 16.0f, 0, 1, 0); + QMatrix4x4 model = m_camera->modelViewMatrix(); // For simplicity, we'll use a fixed-function pipeline style for drawing. // In a real app, this would use shaders. @@ -155,7 +104,7 @@ void ViewportWidget::paintGL() } if (m_isSnappingOrigin) { - const float rectSize = 0.0075f * -m_zoom; + const float rectSize = 0.0075f * -m_camera->zoom(); glColor4f(1.0, 1.0, 0.0, 0.5f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -179,7 +128,7 @@ void ViewportWidget::paintGL() glEnd(); glDisable(GL_BLEND); } else if (m_isSnappingVertex) { - const float rectSize = 0.0075f * -m_zoom; + const float rectSize = 0.0075f * -m_camera->zoom(); glColor4f(1.0, 1.0, 0.0, 0.5f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -232,8 +181,8 @@ void ViewportWidget::paintGL() if (m_isSnappingHorizontal || m_isSnappingVertical) { QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); QVector3D midPoint = (startPos + worldPos) / 2.0; - const float indicatorSize = 0.02f * -m_zoom; - const float indicatorOffset = 0.02f * -m_zoom; + const float indicatorSize = 0.02f * -m_camera->zoom(); + const float indicatorOffset = 0.02f * -m_camera->zoom(); glColor3f(1.0, 1.0, 0.0); glBegin(GL_LINES); @@ -267,8 +216,8 @@ void ViewportWidget::paintGL() // View cube rendering QMatrix4x4 viewCubeModel; - viewCubeModel.rotate(m_xRot / 16.0f, 1, 0, 0); - viewCubeModel.rotate(m_yRot / 16.0f, 0, 1, 0); + viewCubeModel.rotate(m_camera->xRotation() / 16.0f, 1, 0, 0); + viewCubeModel.rotate(m_camera->yRotation() / 16.0f, 0, 1, 0); m_viewCube->paintGL(viewCubeModel, width(), height()); glViewport(0, 0, width(), height()); @@ -353,7 +302,7 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event) bool shouldSnap = false; if (m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast(ApplicationController::ToolType::None)) { QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane); - const float snapRectHalfSize = 0.0075f * -m_zoom; + const float snapRectHalfSize = 0.0075f * -m_camera->zoom(); switch (m_currentPlane) { case SketchPlane::XY: @@ -382,7 +331,7 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event) m_isSnappingVertex = false; if (!m_isSnappingOrigin && m_document && m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast(ApplicationController::ToolType::None)) { QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane); - const float snapRectHalfSize = 0.0075f * -m_zoom; + const float snapRectHalfSize = 0.0075f * -m_camera->zoom(); for (Feature* feature : m_document->features()) { if (auto sketch = dynamic_cast(feature)) { @@ -457,19 +406,8 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event) update(); } - int dx = event->pos().x() - lastPos.x(); - int dy = event->pos().y() - lastPos.y(); - if (event->buttons() & Qt::MiddleButton) { - if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { - // Pan - m_panX += dx / 100.0f; - m_panY -= dy / 100.0f; - } else { - // Rotate - m_xRot += 8 * dy; - m_yRot += 8 * dx; - } + m_camera->processMouseMovement(event, lastPos); } lastPos = event->pos(); update(); @@ -477,11 +415,7 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event) void ViewportWidget::wheelEvent(QWheelEvent *event) { - QPoint numDegrees = event->angleDelta() / 8; - if (!numDegrees.isNull()) { - m_zoom += numDegrees.y() / 5.0f; - } - update(); + m_camera->processWheel(event); } void ViewportWidget::keyPressEvent(QKeyEvent *event) @@ -507,14 +441,10 @@ void ViewportWidget::onSketchModeStarted(SketchPlane plane) { m_currentPlane = plane; - m_savedXRot = m_xRot; - m_savedYRot = m_yRot; - m_savedPanX = m_panX; - m_savedPanY = m_panY; - m_savedZoom = m_zoom; + m_camera->saveState(); - float targetXRot = m_xRot; - float targetYRot = m_yRot; + float targetXRot = m_camera->xRotation(); + float targetYRot = m_camera->yRotation(); switch (plane) { case SketchPlane::XY: // Top view targetXRot = 90 * 16; @@ -534,37 +464,37 @@ void ViewportWidget::onSketchModeStarted(SketchPlane plane) auto* animGroup = new QParallelAnimationGroup(this); - auto* xRotAnim = new QPropertyAnimation(this, "xRotation"); + auto* xRotAnim = new QPropertyAnimation(m_camera, "xRotation"); xRotAnim->setDuration(300); - xRotAnim->setStartValue(m_xRot); + xRotAnim->setStartValue(m_camera->xRotation()); xRotAnim->setEndValue(targetXRot); xRotAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(xRotAnim); - auto* yRotAnim = new QPropertyAnimation(this, "yRotation"); + auto* yRotAnim = new QPropertyAnimation(m_camera, "yRotation"); yRotAnim->setDuration(300); - yRotAnim->setStartValue(m_yRot); + yRotAnim->setStartValue(m_camera->yRotation()); yRotAnim->setEndValue(targetYRot); yRotAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(yRotAnim); - auto* panXAnim = new QPropertyAnimation(this, "panX"); + auto* panXAnim = new QPropertyAnimation(m_camera, "panX"); panXAnim->setDuration(300); - panXAnim->setStartValue(m_panX); + panXAnim->setStartValue(m_camera->panX()); panXAnim->setEndValue(0.0f); panXAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(panXAnim); - auto* panYAnim = new QPropertyAnimation(this, "panY"); + auto* panYAnim = new QPropertyAnimation(m_camera, "panY"); panYAnim->setDuration(300); - panYAnim->setStartValue(m_panY); + panYAnim->setStartValue(m_camera->panY()); panYAnim->setEndValue(0.0f); panYAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(panYAnim); - auto* zoomAnim = new QPropertyAnimation(this, "zoom"); + auto* zoomAnim = new QPropertyAnimation(m_camera, "zoom"); zoomAnim->setDuration(300); - zoomAnim->setStartValue(m_zoom); + zoomAnim->setStartValue(m_camera->zoom()); zoomAnim->setEndValue(-20.0f); zoomAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(zoomAnim); @@ -584,38 +514,38 @@ void ViewportWidget::onSketchModeEnded() { auto* animGroup = new QParallelAnimationGroup(this); - auto* xRotAnim = new QPropertyAnimation(this, "xRotation"); + auto* xRotAnim = new QPropertyAnimation(m_camera, "xRotation"); xRotAnim->setDuration(300); - xRotAnim->setStartValue(m_xRot); - xRotAnim->setEndValue(m_savedXRot); + xRotAnim->setStartValue(m_camera->xRotation()); + xRotAnim->setEndValue(m_camera->savedXRot()); xRotAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(xRotAnim); - auto* yRotAnim = new QPropertyAnimation(this, "yRotation"); + auto* yRotAnim = new QPropertyAnimation(m_camera, "yRotation"); yRotAnim->setDuration(300); - yRotAnim->setStartValue(m_yRot); - yRotAnim->setEndValue(m_savedYRot); + yRotAnim->setStartValue(m_camera->yRotation()); + yRotAnim->setEndValue(m_camera->savedYRot()); yRotAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(yRotAnim); - auto* panXAnim = new QPropertyAnimation(this, "panX"); + auto* panXAnim = new QPropertyAnimation(m_camera, "panX"); panXAnim->setDuration(300); - panXAnim->setStartValue(m_panX); - panXAnim->setEndValue(m_savedPanX); + panXAnim->setStartValue(m_camera->panX()); + panXAnim->setEndValue(m_camera->savedPanX()); panXAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(panXAnim); - auto* panYAnim = new QPropertyAnimation(this, "panY"); + auto* panYAnim = new QPropertyAnimation(m_camera, "panY"); panYAnim->setDuration(300); - panYAnim->setStartValue(m_panY); - panYAnim->setEndValue(m_savedPanY); + panYAnim->setStartValue(m_camera->panY()); + panYAnim->setEndValue(m_camera->savedPanY()); panYAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(panYAnim); - auto* zoomAnim = new QPropertyAnimation(this, "zoom"); + auto* zoomAnim = new QPropertyAnimation(m_camera, "zoom"); zoomAnim->setDuration(300); - zoomAnim->setStartValue(m_zoom); - zoomAnim->setEndValue(m_savedZoom); + zoomAnim->setStartValue(m_camera->zoom()); + zoomAnim->setEndValue(m_camera->savedZoom()); zoomAnim->setEasingCurve(QEasingCurve::InOutQuad); animGroup->addAnimation(zoomAnim); @@ -722,10 +652,7 @@ void ViewportWidget::onActiveToolChanged(int tool) QVector3D ViewportWidget::unproject(const QPoint& screenPos, SketchPlane plane) { - QMatrix4x4 model; - model.translate(m_panX, m_panY, m_zoom); - model.rotate(m_xRot / 16.0f, 1, 0, 0); - model.rotate(m_yRot / 16.0f, 0, 1, 0); + QMatrix4x4 model = m_camera->modelViewMatrix(); bool invertible; QMatrix4x4 inv = (projection * model).inverted(&invertible); diff --git a/src/ViewportWidget.h b/src/ViewportWidget.h index d7059b1..6bf6e0c 100644 --- a/src/ViewportWidget.h +++ b/src/ViewportWidget.h @@ -16,17 +16,12 @@ class SketchGrid; class Document; class FeatureBrowser; class SketchFeature; +class Camera; class ViewportWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT - Q_PROPERTY(float xRotation READ xRotation WRITE setXRotation) - Q_PROPERTY(float yRotation READ yRotation WRITE setYRotation) - Q_PROPERTY(float zoom READ zoom WRITE setZoom) - Q_PROPERTY(float panX READ panX WRITE setPanX) - Q_PROPERTY(float panY READ panY WRITE setPanY) - public: enum class SketchPlane { NONE, @@ -46,21 +41,6 @@ public slots: void onPlaneSelectionModeStarted(); void onActiveToolChanged(int tool); - float xRotation() const; - void setXRotation(float angle); - - float yRotation() const; - void setYRotation(float angle); - - float zoom() const; - void setZoom(float value); - - float panX() const; - void setPanX(float value); - - float panY() const; - void setPanY(float value); - signals: void lineAdded(const gp_Pnt& start, const gp_Pnt& end); void planeSelected(SketchPlane plane); @@ -84,6 +64,7 @@ private: ViewportWidget::SketchPlane checkPlaneSelection(const QPoint& screenPos); QMatrix4x4 projection; + Camera* m_camera = nullptr; ViewCube* m_viewCube; SketchGrid* m_sketchGrid = nullptr; FeatureBrowser* m_featureBrowser = nullptr; @@ -106,18 +87,7 @@ private: QMap m_toolIcons; QSvgRenderer* m_cursorRenderer = nullptr; - float m_xRot = 35.264f * 16.0f; // Default to isometric view - float m_yRot = -45.0f * 16.0f; // Default to isometric view - float m_zoom = -5.0f; - float m_panX = 0; - float m_panY = 0; QPoint lastPos; - - float m_savedXRot = 0; - float m_savedYRot = 0; - float m_savedZoom = -5.0f; - float m_savedPanX = 0; - float m_savedPanY = 0; }; #endif // VIEWPORTWIDGET_H