diff --git a/src/ViewportWidget.cpp b/src/ViewportWidget.cpp index d843ffc..e3ccd96 100644 --- a/src/ViewportWidget.cpp +++ b/src/ViewportWidget.cpp @@ -198,20 +198,48 @@ void ViewportWidget::paintGL() if (m_isDefiningLine && m_activeTool == static_cast(ApplicationController::ToolType::Line)) { vertices.clear(); - QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane); - if (m_isSnappingOrigin) { - worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0); - } else if (m_isSnappingVertex) { - worldPos.setX(m_snapVertex.X()); worldPos.setY(m_snapVertex.Y()); worldPos.setZ(m_snapVertex.Z()); - } else if (m_isSnappingHorizontal) { - if (m_currentPlane == SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z()); - else if (m_currentPlane == SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y()); - else if (m_currentPlane == SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z()); - } else if (m_isSnappingVertical) { - if (m_currentPlane == SketchPlane::XY) worldPos.setX(m_firstLinePoint.X()); - else if (m_currentPlane == SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X()); - else if (m_currentPlane == SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y()); + QVector3D worldPos; + QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); + + QString dimInput = property("dimensionInput").toString(); + bool lengthFromInput = false; + if (!dimInput.isEmpty()) { + bool ok; + double inputLength = dimInput.toDouble(&ok); + if (ok) { + lengthFromInput = true; + QVector3D currentMouseWorldPos = unproject(m_currentMousePos, m_currentPlane); + QVector3D dir = (currentMouseWorldPos - startPos); + if (dir.length() > 1e-6) { + dir.normalize(); + worldPos = startPos + inputLength * dir; + } else { + if (m_currentPlane == SketchPlane::XY || m_currentPlane == SketchPlane::XZ) { + worldPos = startPos + QVector3D(inputLength, 0, 0); + } else { + worldPos = startPos + QVector3D(0, inputLength, 0); + } + } + } } + + if (!lengthFromInput) { + worldPos = unproject(m_currentMousePos, m_currentPlane); + if (m_isSnappingOrigin) { + worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0); + } else if (m_isSnappingVertex) { + worldPos.setX(m_snapVertex.X()); worldPos.setY(m_snapVertex.Y()); worldPos.setZ(m_snapVertex.Z()); + } else if (m_isSnappingHorizontal) { + if (m_currentPlane == SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z()); + else if (m_currentPlane == SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y()); + else if (m_currentPlane == SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z()); + } else if (m_isSnappingVertical) { + if (m_currentPlane == SketchPlane::XY) worldPos.setX(m_firstLinePoint.X()); + else if (m_currentPlane == SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X()); + else if (m_currentPlane == SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y()); + } + } + vertices << m_firstLinePoint.X() << m_firstLinePoint.Y() << m_firstLinePoint.Z(); vertices << worldPos.x() << worldPos.y() << worldPos.z(); @@ -220,7 +248,58 @@ void ViewportWidget::paintGL() m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); glDrawArrays(GL_LINES, 0, 2); - if (m_isSnappingHorizontal || m_isSnappingVertical) { + // Draw dimension line + QVector3D lineVec = worldPos - startPos; + float lineLength = lineVec.length(); + + if (lineLength > 1e-6) { + vertices.clear(); + + QVector3D perpVec; + if (m_currentPlane == SketchPlane::XY) { + perpVec = QVector3D(-lineVec.z(), 0, lineVec.x()).normalized(); + } else if (m_currentPlane == SketchPlane::XZ) { + perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized(); + } else if (m_currentPlane == SketchPlane::YZ) { + perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized(); + } + + float offset = 0.05f * -m_camera->zoom(); + QVector3D dimStart = startPos + offset * perpVec; + QVector3D dimEnd = worldPos + offset * perpVec; + + vertices << dimStart.x() << dimStart.y() << dimStart.z(); + vertices << dimEnd.x() << dimEnd.y() << dimEnd.z(); + + float arrowLength = 0.02f * -m_camera->zoom(); + float arrowWidth = 0.005f * -m_camera->zoom(); + QVector3D lineDir = lineVec.normalized(); + + QVector3D arrow_base_end = dimEnd - arrowLength * lineDir; + QVector3D arrowP1_end = arrow_base_end + arrowWidth * perpVec; + QVector3D arrowP2_end = arrow_base_end - arrowWidth * perpVec; + vertices << dimEnd.x() << dimEnd.y() << dimEnd.z(); + vertices << arrowP1_end.x() << arrowP1_end.y() << arrowP1_end.z(); + vertices << dimEnd.x() << dimEnd.y() << dimEnd.z(); + vertices << arrowP2_end.x() << arrowP2_end.y() << arrowP2_end.z(); + + QVector3D arrow_base_start = dimStart + arrowLength * lineDir; + QVector3D arrowP1_start = arrow_base_start + arrowWidth * perpVec; + QVector3D arrowP2_start = arrow_base_start - arrowWidth * perpVec; + vertices << dimStart.x() << dimStart.y() << dimStart.z(); + vertices << arrowP1_start.x() << arrowP1_start.y() << arrowP1_start.z(); + vertices << dimStart.x() << dimStart.y() << dimStart.z(); + vertices << arrowP2_start.x() << arrowP2_start.y() << arrowP2_start.z(); + + m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(0.7f, 0.7f, 0.7f, 1.0f)); + glLineWidth(1.0f); + m_vbo.bind(); + m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); + glDrawArrays(GL_LINES, 0, vertices.size() / 3); + glLineWidth(2.0f); + } + + if (!lengthFromInput && (m_isSnappingHorizontal || m_isSnappingVertical)) { vertices.clear(); QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); QVector3D midPoint = (startPos + worldPos) / 2.0; @@ -273,6 +352,91 @@ void ViewportWidget::paintGL() } m_featureBrowser->paint(painter, width(), height()); + if (m_isDefiningLine && m_activeTool == static_cast(ApplicationController::ToolType::Line)) { + QVector3D worldPos; + QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); + QString dimText; + + QString dimInput = property("dimensionInput").toString(); + bool lengthFromInput = false; + double lineLength = 0; + + if (!dimInput.isEmpty()) { + bool ok; + double inputLength = dimInput.toDouble(&ok); + if (ok) { + lengthFromInput = true; + lineLength = inputLength; + QVector3D currentMouseWorldPos = unproject(m_currentMousePos, m_currentPlane); + QVector3D dir = (currentMouseWorldPos - startPos); + if (dir.length() > 1e-6) { + dir.normalize(); + worldPos = startPos + inputLength * dir; + } else { + if (m_currentPlane == SketchPlane::XY || m_currentPlane == SketchPlane::XZ) { + worldPos = startPos + QVector3D(inputLength, 0, 0); + } else { + worldPos = startPos + QVector3D(0, inputLength, 0); + } + } + } + } + + if (!lengthFromInput) { + worldPos = unproject(m_currentMousePos, m_currentPlane); + if (m_isSnappingOrigin) { + worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0); + } else if (m_isSnappingVertex) { + worldPos.setX(m_snapVertex.X()); worldPos.setY(m_snapVertex.Y()); worldPos.setZ(m_snapVertex.Z()); + } else if (m_isSnappingHorizontal) { + if (m_currentPlane == SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z()); + else if (m_currentPlane == SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y()); + else if (m_currentPlane == SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z()); + } else if (m_isSnappingVertical) { + if (m_currentPlane == SketchPlane::XY) worldPos.setX(m_firstLinePoint.X()); + else if (m_currentPlane == SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X()); + else if (m_currentPlane == SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y()); + } + lineLength = (worldPos - startPos).length(); + } + + QVector3D lineVec = worldPos - startPos; + if (lineVec.length() > 1e-6) { + QVector3D perpVec; + if (m_currentPlane == SketchPlane::XY) { + perpVec = QVector3D(-lineVec.z(), 0, lineVec.x()).normalized(); + } else if (m_currentPlane == SketchPlane::XZ) { + perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized(); + } else if (m_currentPlane == SketchPlane::YZ) { + perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized(); + } + + float offset = 0.05f * -m_camera->zoom(); + QVector3D dimStart = startPos + offset * perpVec; + QVector3D dimEnd = worldPos + offset * perpVec; + QVector3D textPos3D = (dimStart + dimEnd) / 2.0f + 0.015f * -m_camera->zoom() * perpVec; + + QVector3D screenPos = project(textPos3D, model, projection, rect()); + + if (screenPos.z() < 1.0f) { + dimText = lengthFromInput ? dimInput : QString::number(lineLength, 'f', 2); + + painter.setRenderHint(QPainter::Antialiasing); + QFontMetrics fm(painter.font()); + QRect textRect = fm.boundingRect(dimText + "_"); + textRect.moveCenter(screenPos.toPoint()); + + if (lengthFromInput) { + 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, dimText); + } + } + } + painter.end(); } @@ -318,9 +482,11 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event) if (!m_isDefiningLine) { m_firstLinePoint = p; m_isDefiningLine = true; + setProperty("dimensionInput", QVariant("")); } else { emit lineAdded(m_firstLinePoint, p); m_firstLinePoint = p; + setProperty("dimensionInput", QVariant("")); } update(); } @@ -462,9 +628,53 @@ void ViewportWidget::wheelEvent(QWheelEvent *event) void ViewportWidget::keyPressEvent(QKeyEvent *event) { + if (m_isDefiningLine && m_activeTool == static_cast(ApplicationController::ToolType::Line)) { + QString currentInput = property("dimensionInput").toString(); + if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9) { + currentInput += event->text(); + setProperty("dimensionInput", currentInput); + update(); + return; + } else if (event->key() == Qt::Key_Period) { + if (!currentInput.contains('.')) { + currentInput += '.'; + setProperty("dimensionInput", currentInput); + update(); + return; + } + } else if (event->key() == Qt::Key_Backspace) { + if (!currentInput.isEmpty()) { + currentInput.chop(1); + setProperty("dimensionInput", currentInput); + update(); + return; + } + } else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + bool ok; + double length = currentInput.toDouble(&ok); + if (ok && length > 0) { + QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane); + QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); + QVector3D dir = (worldPos - startPos); + if (dir.length() > 1e-6) { + dir.normalize(); + QVector3D endPos = startPos + length * dir; + gp_Pnt p; + p.SetCoord(endPos.x(), endPos.y(), endPos.z()); + emit lineAdded(m_firstLinePoint, p); + m_firstLinePoint = p; // for chained lines + setProperty("dimensionInput", QVariant("")); + update(); + return; + } + } + } + } + if (event->key() == Qt::Key_Escape) { if (m_isDefiningLine) { m_isDefiningLine = false; + setProperty("dimensionInput", QVariant("")); emit toolDeactivated(); update(); return;