diff --git a/src/CircleTool.cpp b/src/CircleTool.cpp index e69de29..cdd3804 100644 --- a/src/CircleTool.cpp +++ b/src/CircleTool.cpp @@ -0,0 +1,259 @@ +#include "CircleTool.h" +#include "ViewportWidget.h" +#include "Camera.h" +#include +#include +#include +#include +#include +#include +#include + +CircleTool::CircleTool(ViewportWidget* viewport) + : SketchTool(viewport) +{ +} + +void CircleTool::activate() +{ + SketchTool::activate(); + m_dimensionModes << "diameter"; + m_dimensionPropertyNames["diameter"] = "diameterInput"; + + m_viewport->setProperty("diameterInput", ""); + m_viewport->setProperty("dimensionEditMode", "diameter"); +} + +void CircleTool::mousePressEvent(QMouseEvent *event) +{ + gp_Pnt p; + if (!m_isDefining) { + if (m_viewport->isSnappingOrigin()) { + p.SetCoord(0, 0, 0); + } else if (m_viewport->isSnappingVertex()) { + p = m_viewport->snapVertex(); + } else { + QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane()); + p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z()); + } + m_centerPoint = p; + m_isDefining = true; + m_viewport->setProperty("diameterInput", ""); + m_viewport->setProperty("dimensionEditMode", "diameter"); + } else { + QVector3D worldPos; + QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z()); + + QString diameterInput = m_viewport->property("diameterInput").toString(); + bool diameterFromInput = false; + double inputDiameter = 0; + + if (!diameterInput.isEmpty()) { + bool ok; + inputDiameter = diameterInput.toDouble(&ok); + if (ok) diameterFromInput = true; + } + + if (diameterFromInput) { + QVector3D mousePos = m_viewport->unproject(event->pos(), m_viewport->currentPlane()); + QVector3D mouseDir = mousePos - centerPos; + if (mouseDir.lengthSquared() < 1e-9) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + mouseDir = QVector3D(1, 0, 0); + } else { // YZ + mouseDir = QVector3D(0, 1, 0); + } + } + double radius = inputDiameter / 2.0; + worldPos = centerPos + mouseDir.normalized() * radius; + } else { + if (m_viewport->isSnappingOrigin()) { + worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0); + } else if (m_viewport->isSnappingVertex()) { + worldPos = QVector3D(m_viewport->snapVertex().X(), m_viewport->snapVertex().Y(), m_viewport->snapVertex().Z()); + } else { + worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane()); + } + } + p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z()); + emit m_viewport->circleAdded(m_centerPoint, p); + deactivate(); + } +} + +void CircleTool::mouseMoveEvent(QMouseEvent *event) +{ + // To be implemented +} + +void CircleTool::finalizeCreation() +{ + QVector3D worldPos; + QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z()); + + QString diameterInput = m_viewport->property("diameterInput").toString(); + bool diameterFromInput = false; + double inputDiameter = 0; + + if (!diameterInput.isEmpty()) { + bool ok; + inputDiameter = diameterInput.toDouble(&ok); + if (ok) diameterFromInput = true; + } + + if (diameterFromInput) { + QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D mouseDir = mousePos - centerPos; + if (mouseDir.lengthSquared() < 1e-9) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + mouseDir = QVector3D(1, 0, 0); + } else { // YZ + mouseDir = QVector3D(0, 1, 0); + } + } + double radius = inputDiameter / 2.0; + worldPos = centerPos + mouseDir.normalized() * radius; + } else { + worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + } + + gp_Pnt p; + p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z()); + + emit m_viewport->circleAdded(m_centerPoint, p); + deactivate(); +} + +void CircleTool::paintGL() +{ + if (m_isDefining) { + QVector vertices; + QVector3D worldPos; + QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z()); + + QString diameterInput = m_viewport->property("diameterInput").toString(); + bool diameterFromInput = false; + double inputDiameter = 0; + + if (!diameterInput.isEmpty()) { + bool ok; + inputDiameter = diameterInput.toDouble(&ok); + if (ok) diameterFromInput = true; + } + + QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + + double radius; + if (diameterFromInput) { + radius = inputDiameter / 2.0; + } else { + worldPos = mousePos; + if (m_viewport->isSnappingOrigin()) { + worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0); + } else if (m_viewport->isSnappingVertex()) { + worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z()); + } + radius = (worldPos - centerPos).length(); + } + + const int segments = 64; + for (int i = 0; i < segments; ++i) { + double angle1 = i * 2.0 * M_PI / segments; + double angle2 = (i + 1) * 2.0 * M_PI / segments; + + QVector3D p1, p2; + + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + p1.setX(centerPos.x() + radius * qCos(angle1)); + p1.setY(centerPos.y() + radius * qSin(angle1)); + p1.setZ(centerPos.z()); + p2.setX(centerPos.x() + radius * qCos(angle2)); + p2.setY(centerPos.y() + radius * qSin(angle2)); + p2.setZ(centerPos.z()); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + p1.setX(centerPos.x() + radius * qCos(angle1)); + p1.setY(centerPos.y()); + p1.setZ(centerPos.z() + radius * qSin(angle1)); + p2.setX(centerPos.x() + radius * qCos(angle2)); + p2.setY(centerPos.y()); + p2.setZ(centerPos.z() + radius * qSin(angle2)); + } else { // YZ + p1.setX(centerPos.x()); + p1.setY(centerPos.y() + radius * qCos(angle1)); + p1.setZ(centerPos.z() + radius * qSin(angle1)); + p2.setX(centerPos.x()); + p2.setY(centerPos.y() + radius * qCos(angle2)); + p2.setZ(centerPos.z() + radius * qSin(angle2)); + } + vertices << p1.x() << p1.y() << p1.z(); + vertices << p2.x() << p2.y() << p2.z(); + } + + m_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(1.0f, 1.0f, 0.0f, 1.0f)); + m_viewport->vbo().bind(); + m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); + glDrawArrays(GL_LINES, 0, segments * 2); + } +} + +void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) +{ + if (m_isDefining) { + QVector3D worldPos; + QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z()); + + QString diameterInput = m_viewport->property("diameterInput").toString(); + bool diameterFromInput = false; + double inputDiameter = 0; + + if (!diameterInput.isEmpty()) { + bool ok; + inputDiameter = diameterInput.toDouble(&ok); + if (ok) diameterFromInput = true; + } + + QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D edgePos; + double diameter; + + if (diameterFromInput) { + diameter = inputDiameter; + QVector3D mouseDir = mousePos - centerPos; + if (mouseDir.lengthSquared() < 1e-9) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + mouseDir = QVector3D(1, 0, 0); + } else { // YZ + mouseDir = QVector3D(0, 1, 0); + } + } + edgePos = centerPos + mouseDir.normalized() * (diameter / 2.0); + } else { + edgePos = mousePos; + if (m_viewport->isSnappingOrigin()) { + edgePos.setX(0); edgePos.setY(0); edgePos.setZ(0); + } else if (m_viewport->isSnappingVertex()) { + edgePos.setX(m_viewport->snapVertex().X()); edgePos.setY(m_viewport->snapVertex().Y()); edgePos.setZ(m_viewport->snapVertex().Z()); + } + diameter = (edgePos - centerPos).length() * 2.0; + } + + painter.setRenderHint(QPainter::Antialiasing); + QFontMetrics fm(painter.font()); + + // Diameter dimension + QVector3D diameterTextPos3D = (centerPos + edgePos) / 2.0f; + QVector3D screenPosD = m_viewport->project(diameterTextPos3D, modelView, projection, m_viewport->rect()); + if (screenPosD.z() < 1.0f) { + QString diameterText = diameterFromInput ? diameterInput : QString::number(diameter, 'f', 2); + QRect textRect = fm.boundingRect(diameterText + "__"); + textRect.moveCenter(screenPosD.toPoint()); + if (m_viewport->property("dimensionEditMode").toString() == "diameter") { + painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255)); + } else { + painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50)); + } + painter.setPen(Qt::white); + painter.drawText(textRect, Qt::AlignCenter, diameterText); + } + } +} diff --git a/src/CircleTool.h b/src/CircleTool.h index e69de29..4d112ed 100644 --- a/src/CircleTool.h +++ b/src/CircleTool.h @@ -0,0 +1,28 @@ +#ifndef CIRCLETOOL_H +#define CIRCLETOOL_H + +#include "SketchTool.h" +#include + +class CircleTool : public SketchTool +{ + Q_OBJECT +public: + explicit CircleTool(ViewportWidget* viewport); + + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + + void paintGL() override; + void paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) override; + + void activate() override; + +protected: + void finalizeCreation() override; + +private: + gp_Pnt m_centerPoint; +}; + +#endif // CIRCLETOOL_H