diff --git a/src/ApplicationController.cpp b/src/ApplicationController.cpp index c47e927..99a9f89 100644 --- a/src/ApplicationController.cpp +++ b/src/ApplicationController.cpp @@ -1,6 +1,7 @@ #include "ApplicationController.h" #include "Document.h" #include "SketchFeature.h" +#include "SketchLine.h" #include "MainWindow.h" #include @@ -126,6 +127,13 @@ void ApplicationController::beginSketchCreation() } } +void ApplicationController::addLine(const gp_Pnt& start, const gp_Pnt& end) +{ + if (m_activeSketch) { + m_activeSketch->addObject(new SketchLine(start, end)); + } +} + void ApplicationController::endSketch() { m_activeSketch = nullptr; diff --git a/src/ApplicationController.h b/src/ApplicationController.h index 1c3db43..15031d7 100644 --- a/src/ApplicationController.h +++ b/src/ApplicationController.h @@ -3,6 +3,7 @@ #include #include "ViewportWidget.h" // For SketchPlane enum +#include class Document; class MainWindow; @@ -30,6 +31,7 @@ public: public slots: void setActiveTool(ToolType tool); + void addLine(const gp_Pnt& start, const gp_Pnt& end); void newDocument(); bool openDocument(); bool saveDocument(); diff --git a/src/ViewportWidget.cpp b/src/ViewportWidget.cpp index 1ccb859..0f2798c 100644 --- a/src/ViewportWidget.cpp +++ b/src/ViewportWidget.cpp @@ -3,6 +3,10 @@ #include "SketchGrid.h" #include "Document.h" #include "FeatureBrowser.h" +#include "SketchFeature.h" +#include "SketchLine.h" +#include "SketchObject.h" +#include "ApplicationController.h" #include #include #include @@ -18,6 +22,7 @@ ViewportWidget::ViewportWidget(QWidget *parent) m_viewCube = new ViewCube(); m_sketchGrid = new SketchGrid(); m_featureBrowser = new FeatureBrowser(); + setMouseTracking(true); } ViewportWidget::~ViewportWidget() @@ -29,6 +34,7 @@ ViewportWidget::~ViewportWidget() void ViewportWidget::setDocument(Document* document) { + m_document = document; m_featureBrowser->setDocument(document); } @@ -112,6 +118,23 @@ void ViewportWidget::paintGL() m_sketchGrid->paintGL(static_cast(m_currentPlane), projection, model); } + if (m_document) { + for (Feature* feature : m_document->features()) { + if (auto sketch = dynamic_cast(feature)) { + drawSketch(sketch); + } + } + } + + if (m_isDefiningLine && m_activeTool == static_cast(ApplicationController::ToolType::Line)) { + QVector3D worldPos = unproject(m_currentMousePos); + glBegin(GL_LINES); + glColor3f(1.0, 1.0, 0.0); + glVertex3d(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); + glVertex3d(worldPos.x(), worldPos.y(), worldPos.z()); + glEnd(); + } + // View cube rendering QMatrix4x4 viewCubeModel; viewCubeModel.rotate(m_xRot / 16.0f, 1, 0, 0); @@ -136,11 +159,26 @@ void ViewportWidget::resizeGL(int w, int h) void ViewportWidget::mousePressEvent(QMouseEvent *event) { - lastPos = event->pos(); + if (event->button() == Qt::LeftButton && m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast(ApplicationController::ToolType::Line)) { + QVector3D worldPos = unproject(event->pos()); + gp_Pnt p(worldPos.x(), worldPos.y(), worldPos.z()); + + if (!m_isDefiningLine) { + m_firstLinePoint = p; + m_isDefiningLine = true; + } else { + emit lineAdded(m_firstLinePoint, p); + m_isDefiningLine = false; + } + update(); + } else { + lastPos = event->pos(); + } } void ViewportWidget::mouseMoveEvent(QMouseEvent *event) { + m_currentMousePos = event->pos(); int dx = event->pos().x() - lastPos.x(); int dy = event->pos().y() - lastPos.y(); @@ -150,9 +188,11 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event) m_panX += dx / 100.0f; m_panY -= dy / 100.0f; } else { - // Rotate - m_xRot += 8 * dy; - m_yRot += 8 * dx; + if (m_currentPlane == SketchPlane::NONE) { + // Rotate + m_xRot += 8 * dy; + m_yRot += 8 * dx; + } } } lastPos = event->pos(); @@ -332,3 +372,79 @@ void ViewportWidget::drawAxisLabels(QPainter& painter, const QMatrix4x4& modelVi } } +void ViewportWidget::onActiveToolChanged(int tool) +{ + m_activeTool = tool; + m_isDefiningLine = false; +} + +QVector3D ViewportWidget::unproject(const QPoint& screenPos) +{ + 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); + + bool invertible; + QMatrix4x4 inv = (projection * model).inverted(&invertible); + if (!invertible) { + return QVector3D(); + } + + float ndcX = (2.0f * screenPos.x()) / width() - 1.0f; + float ndcY = 1.0f - (2.0f * screenPos.y()) / height(); + + QVector4D nearPoint_ndc(ndcX, ndcY, -1.0f, 1.0f); + QVector4D farPoint_ndc(ndcX, ndcY, 1.0f, 1.0f); + + QVector4D nearPoint_world = inv * nearPoint_ndc; + QVector4D farPoint_world = inv * farPoint_ndc; + + if (qFuzzyCompare(nearPoint_world.w(), 0.0f) || qFuzzyCompare(farPoint_world.w(), 0.0f)) { + return QVector3D(); + } + + nearPoint_world /= nearPoint_world.w(); + farPoint_world /= farPoint_world.w(); + + QVector3D rayOrigin(nearPoint_world); + QVector3D rayDir = (QVector3D(farPoint_world) - rayOrigin).normalized(); + + QVector3D planeNormal; + switch (m_currentPlane) { + case SketchPlane::XY: planeNormal = QVector3D(0, 0, 1); break; + case SketchPlane::XZ: planeNormal = QVector3D(0, 1, 0); break; + case SketchPlane::YZ: planeNormal = QVector3D(1, 0, 0); break; + case SketchPlane::NONE: return QVector3D(); + } + + float denom = QVector3D::dotProduct(planeNormal, rayDir); + if (qAbs(denom) > 1e-6) { + QVector3D p0(0,0,0); + float t = QVector3D::dotProduct(p0 - rayOrigin, planeNormal) / denom; + return rayOrigin + t * rayDir; + } + + return QVector3D(); +} + +void ViewportWidget::drawSketch(const SketchFeature* sketch) +{ + glDisable(GL_DEPTH_TEST); + glLineWidth(2.0f); + glColor3f(1.0, 1.0, 1.0); + + for (const auto& obj : sketch->objects()) { + if (obj->type() == SketchObject::ObjectType::Line) { + auto line = static_cast(obj); + const auto& start = line->startPoint(); + const auto& end = line->endPoint(); + glBegin(GL_LINES); + glVertex3d(start.X(), start.Y(), start.Z()); + glVertex3d(end.X(), end.Y(), end.Z()); + glEnd(); + } + } + glEnable(GL_DEPTH_TEST); +} + diff --git a/src/ViewportWidget.h b/src/ViewportWidget.h index 04c8999..099cc93 100644 --- a/src/ViewportWidget.h +++ b/src/ViewportWidget.h @@ -7,11 +7,13 @@ #include #include #include +#include class ViewCube; class SketchGrid; class Document; class FeatureBrowser; +class SketchFeature; class ViewportWidget : public QOpenGLWidget, protected QOpenGLFunctions { @@ -39,6 +41,7 @@ public: public slots: void onSketchModeStarted(SketchPlane plane); void onSketchModeEnded(); + void onActiveToolChanged(int tool); float xRotation() const; void setXRotation(float angle); @@ -55,6 +58,9 @@ public slots: float panY() const; void setPanY(float value); +signals: + void lineAdded(const gp_Pnt& start, const gp_Pnt& end); + protected: void initializeGL() override; void paintGL() override; @@ -66,14 +72,22 @@ protected: private: QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport); + QVector3D unproject(const QPoint& screenPos); void drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection); + void drawSketch(const SketchFeature* sketch); QMatrix4x4 projection; ViewCube* m_viewCube; SketchGrid* m_sketchGrid = nullptr; FeatureBrowser* m_featureBrowser = nullptr; + Document* m_document = nullptr; SketchPlane m_currentPlane = SketchPlane::NONE; + int m_activeTool = 0; + bool m_isDefiningLine = false; + gp_Pnt m_firstLinePoint; + QPoint m_currentMousePos; + 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;