diff --git a/src/LineTool.cpp b/src/LineTool.cpp index 52dbb0b..c8928b9 100644 --- a/src/LineTool.cpp +++ b/src/LineTool.cpp @@ -1,8 +1,11 @@ #include "LineTool.h" #include "ViewportWidget.h" +#include "Camera.h" #include #include #include +#include +#include LineTool::LineTool(ViewportWidget* viewport) : SketchTool(viewport) @@ -42,10 +45,572 @@ void LineTool::keyPressEvent(QKeyEvent *event) void LineTool::paintGL() { - // To be implemented + if (m_isDefiningLine) { + QVector vertices; + QVector3D worldPos; + QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); + + QString dimInput = m_viewport->property("dimensionInput").toString(); + QString angleInput = m_viewport->property("angleInput").toString(); + bool lengthFromInput = false; + bool angleFromInput = false; + double inputLength = 0; + double inputAngleDegrees = 0; + + if (!dimInput.isEmpty()) { + bool ok; + inputLength = dimInput.toDouble(&ok); + if (ok) lengthFromInput = true; + } + if (!angleInput.isEmpty()) { + bool ok; + inputAngleDegrees = angleInput.toDouble(&ok); + if (ok) angleFromInput = true; + } + + if (angleFromInput) { + QVector3D refDir; + if (m_viewport->property("isChainedLine").toBool()) { + refDir = m_viewport->property("previousLineDirection").value(); + } else { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + refDir = QVector3D(1, 0, 0); + } else { // YZ + refDir = QVector3D(0, 0, -1); + } + } + + QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D mouseVec = currentMouseWorldPos - startPos; + + // Quadrant snapping + double mouseAngle; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x())); + else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y())); + + double refAngle; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); + else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); + + double relativeMouseAngle = mouseAngle - refAngle; + while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; + while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; + + double snappedAngle = 0; + if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1 + snappedAngle = inputAngleDegrees; + } else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2 + snappedAngle = 180.0 - inputAngleDegrees; + } else if (relativeMouseAngle < -90) { // Quadrant 3 + snappedAngle = -180.0 + inputAngleDegrees; + } else { // Quadrant 4 + snappedAngle = -inputAngleDegrees; + } + + double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle); + QVector3D finalDir; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad)); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0); + else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad)); + + double lineLength; + if (lengthFromInput) { + lineLength = inputLength; + } else { + lineLength = QVector3D::dotProduct(mouseVec, finalDir); + if (lineLength < 0) lineLength = 0; + } + worldPos = startPos + lineLength * finalDir; + + } else if (lengthFromInput) { + QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D dir = (currentMouseWorldPos - startPos); + if (dir.length() > 1e-6) { + dir.normalize(); + worldPos = startPos + inputLength * dir; + } else { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + worldPos = startPos + QVector3D(inputLength, 0, 0); + } else { + worldPos = startPos + QVector3D(0, inputLength, 0); + } + } + } else { + worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + 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()); + } else if (m_viewport->isSnappingHorizontal()) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z()); + } else if (m_viewport->isSnappingVertical()) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setX(m_firstLinePoint.X()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y()); + } + } + + vertices << m_firstLinePoint.X() << m_firstLinePoint.Y() << m_firstLinePoint.Z(); + vertices << worldPos.x() << worldPos.y() << worldPos.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, 2); + + // Draw dimension line + QVector3D lineVec = worldPos - startPos; + float lineLength = lineVec.length(); + + if (lineLength > 1e-6) { + double refAngle, lineAngle, angleDiff; + { + QVector3D refDir; + if (m_viewport->property("isChainedLine").toBool()) { + refDir = m_viewport->property("previousLineDirection").value(); + } else { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refDir = QVector3D(1, 0, 0); + else refDir = QVector3D(0, 0, -1); + } + + if (angleFromInput) { + QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D mouseVec = currentMouseWorldPos - startPos; + + double mouseAngle; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x())); + else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y())); + + double refAngleForQuadrant; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); + else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); + + double relativeMouseAngle = mouseAngle - refAngleForQuadrant; + while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; + while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; + + if (relativeMouseAngle >= 90 || relativeMouseAngle < -90) { + refDir = -refDir; + } + } else { + if (m_viewport->property("isChainedLine").toBool()) { + refDir = -refDir; + } + } + + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + refAngle = atan2(refDir.z(), refDir.x()); + lineAngle = atan2(lineVec.z(), lineVec.x()); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + refAngle = atan2(refDir.y(), refDir.x()); + lineAngle = atan2(lineVec.y(), lineVec.x()); + } else { // YZ + refAngle = atan2(refDir.z(), refDir.y()); + lineAngle = atan2(lineVec.z(), lineVec.y()); + } + + angleDiff = lineAngle - refAngle; + while (angleDiff <= -M_PI) angleDiff += 2 * M_PI; + while (angleDiff > M_PI) angleDiff -= 2 * M_PI; + lineAngle = refAngle + angleDiff; + } + + vertices.clear(); + + QVector3D perpVec; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + perpVec = QVector3D(-lineVec.z(), 0, lineVec.x()).normalized(); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized(); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) { + perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized(); + } + + if (angleDiff < 0) { + perpVec = -perpVec; + } + + float offset = 0.05f * -m_viewport->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_viewport->camera()->zoom(); + float arrowWidth = 0.005f * -m_viewport->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_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(0.7f, 0.7f, 0.7f, 1.0f)); + glLineWidth(1.0f); + m_viewport->vbo().bind(); + m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); + glDrawArrays(GL_LINES, 0, vertices.size() / 3); + glLineWidth(2.0f); + + // Draw angle dimension + vertices.clear(); + const int numSegments = 30; + const float radius = 0.1f * -m_viewport->camera()->zoom(); + for (int i = 0; i <= numSegments; ++i) { + double angle = refAngle + (lineAngle - refAngle) * i / numSegments; + QVector3D p; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) p = startPos + radius * QVector3D(cos(angle), 0, sin(angle)); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) p = startPos + radius * QVector3D(cos(angle), sin(angle), 0); + else p = startPos + radius * QVector3D(0, cos(angle), sin(angle)); + vertices << p.x() << p.y() << p.z(); + } + glLineWidth(1.0f); + m_viewport->vbo().bind(); + m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); + glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3); + + // Arrowheads for arc + QVector arrowVertices; + float arcArrowLength = 0.02f * -m_viewport->camera()->zoom(); + float arcArrowWidth = 0.005f * -m_viewport->camera()->zoom(); + + double sign = (angleDiff >= 0) ? 1.0 : -1.0; + + // End arrowhead + QVector3D endPoint(vertices[vertices.size()-3], vertices[vertices.size()-2], vertices[vertices.size()-1]); + double endAngle = lineAngle; + QVector3D radialDir_end, tangentDir_end; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + radialDir_end = QVector3D(cos(endAngle), 0, sin(endAngle)); + tangentDir_end = QVector3D(-sin(endAngle), 0, cos(endAngle)); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + radialDir_end = QVector3D(cos(endAngle), sin(endAngle), 0); + tangentDir_end = QVector3D(-sin(endAngle), cos(endAngle), 0); + } else { + radialDir_end = QVector3D(0, cos(endAngle), sin(endAngle)); + tangentDir_end = QVector3D(0, -sin(endAngle), cos(endAngle)); + } + QVector3D arc_arrow_base_end = endPoint - sign * arcArrowLength * tangentDir_end; + QVector3D arc_arrowP1_end = arc_arrow_base_end + arcArrowWidth * radialDir_end; + QVector3D arc_arrowP2_end = arc_arrow_base_end - arcArrowWidth * radialDir_end; + arrowVertices << endPoint.x() << endPoint.y() << endPoint.z() << arc_arrowP1_end.x() << arc_arrowP1_end.y() << arc_arrowP1_end.z(); + arrowVertices << endPoint.x() << endPoint.y() << endPoint.z() << arc_arrowP2_end.x() << arc_arrowP2_end.y() << arc_arrowP2_end.z(); + + // Start arrowhead + QVector3D startPoint(vertices[0], vertices[1], vertices[2]); + double startAngle = refAngle; + QVector3D radialDir_start, tangentDir_start; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + radialDir_start = QVector3D(cos(startAngle), 0, sin(startAngle)); + tangentDir_start = QVector3D(-sin(startAngle), 0, cos(startAngle)); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + radialDir_start = QVector3D(cos(startAngle), sin(startAngle), 0); + tangentDir_start = QVector3D(-sin(startAngle), cos(startAngle), 0); + } else { + radialDir_start = QVector3D(0, cos(startAngle), sin(startAngle)); + tangentDir_start = QVector3D(0, -sin(startAngle), cos(startAngle)); + } + QVector3D arc_arrow_base_start = startPoint + sign * arcArrowLength * tangentDir_start; + QVector3D arc_arrowP1_start = arc_arrow_base_start + arcArrowWidth * radialDir_start; + QVector3D arc_arrowP2_start = arc_arrow_base_start - arcArrowWidth * radialDir_start; + arrowVertices << startPoint.x() << startPoint.y() << startPoint.z() << arc_arrowP1_start.x() << arc_arrowP1_start.y() << arc_arrowP1_start.z(); + arrowVertices << startPoint.x() << startPoint.y() << startPoint.z() << arc_arrowP2_start.x() << arc_arrowP2_start.y() << arc_arrowP2_start.z(); + + m_viewport->vbo().bind(); + m_viewport->vbo().allocate(arrowVertices.constData(), arrowVertices.size() * sizeof(GLfloat)); + glDrawArrays(GL_LINES, 0, arrowVertices.size() / 3); + glLineWidth(2.0f); + } + + if (!lengthFromInput && !angleFromInput && (m_viewport->isSnappingHorizontal() || m_viewport->isSnappingVertical())) { + vertices.clear(); + QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); + QVector3D midPoint = (startPos + worldPos) / 2.0; + const float indicatorSize = 0.02f * -m_viewport->camera()->zoom(); + const float indicatorOffset = 0.02f * -m_viewport->camera()->zoom(); + if (m_viewport->isSnappingHorizontal()) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + vertices << midPoint.x() - indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset; + vertices << midPoint.x() + indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset; + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + vertices << midPoint.x() - indicatorSize << midPoint.y() + indicatorOffset << midPoint.z(); + vertices << midPoint.x() + indicatorSize << midPoint.y() + indicatorOffset << midPoint.z(); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) { + vertices << midPoint.x() << midPoint.y() - indicatorSize << midPoint.z() + indicatorOffset; + vertices << midPoint.x() << midPoint.y() + indicatorSize << midPoint.z() + indicatorOffset; + } + } else { // m_isSnappingVertical + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() - indicatorSize; + vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() + indicatorSize; + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + vertices << midPoint.x() + indicatorOffset << midPoint.y() - indicatorSize << midPoint.z(); + vertices << midPoint.x() + indicatorOffset << midPoint.y() + indicatorSize << midPoint.z(); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) { + vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() - indicatorSize; + vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() + indicatorSize; + } + } + m_viewport->vbo().bind(); + m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); + glDrawArrays(GL_LINES, 0, 2); + } + } } void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) { - // To be implemented + if (m_isDefiningLine) { + QVector3D worldPos; + QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); + QString dimText; + QString angleText; + + QString dimInput = m_viewport->property("dimensionInput").toString(); + QString angleInput = m_viewport->property("angleInput").toString(); + bool lengthFromInput = false; + bool angleFromInput = false; + double inputLength = 0; + double inputAngleDegrees = 0; + double lineLength = 0; + + if (!dimInput.isEmpty()) { + bool ok; + inputLength = dimInput.toDouble(&ok); + if (ok) lengthFromInput = true; + } + if (!angleInput.isEmpty()) { + bool ok; + inputAngleDegrees = angleInput.toDouble(&ok); + if (ok) angleFromInput = true; + } + + if (angleFromInput) { + QVector3D refDir; + if (m_viewport->property("isChainedLine").toBool()) { + refDir = m_viewport->property("previousLineDirection").value(); + } else { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + refDir = QVector3D(1, 0, 0); + } else { // YZ + refDir = QVector3D(0, 0, -1); + } + } + + QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D mouseVec = currentMouseWorldPos - startPos; + + double mouseAngle; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x())); + else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y())); + + double refAngle; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); + else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); + + double relativeMouseAngle = mouseAngle - refAngle; + while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; + while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; + + double snappedAngle = 0; + if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1 + snappedAngle = inputAngleDegrees; + } else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2 + snappedAngle = 180.0 - inputAngleDegrees; + } else if (relativeMouseAngle < -90) { // Quadrant 3 + snappedAngle = -180.0 + inputAngleDegrees; + } else { // Quadrant 4 + snappedAngle = -inputAngleDegrees; + } + + double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle); + QVector3D finalDir; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad)); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0); + else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad)); + + if (lengthFromInput) { + lineLength = inputLength; + } else { + lineLength = QVector3D::dotProduct(mouseVec, finalDir); + if (lineLength < 0) lineLength = 0; + } + worldPos = startPos + lineLength * finalDir; + + } else if (lengthFromInput) { + lineLength = inputLength; + QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D dir = (currentMouseWorldPos - startPos); + if (dir.length() > 1e-6) { + dir.normalize(); + worldPos = startPos + inputLength * dir; + } else { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + worldPos = startPos + QVector3D(inputLength, 0, 0); + } else { + worldPos = startPos + QVector3D(0, inputLength, 0); + } + } + } else { + worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + 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()); + } else if (m_viewport->isSnappingHorizontal()) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z()); + } else if (m_viewport->isSnappingVertical()) { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setX(m_firstLinePoint.X()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X()); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y()); + } + lineLength = (worldPos - startPos).length(); + } + + QVector3D lineVec = worldPos - startPos; + if (lineVec.length() > 1e-6) { + double refAngle, lineAngle; + QVector3D refDir; + if (m_viewport->property("isChainedLine").toBool()) { + refDir = m_viewport->property("previousLineDirection").value(); + } else { + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refDir = QVector3D(1, 0, 0); + else refDir = QVector3D(0, 0, -1); + } + + QVector3D currentMouseWorldPosForText = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); + QVector3D mouseVecForText = currentMouseWorldPosForText - startPos; + + if (angleFromInput) { + if (mouseVecForText.length() > 1e-6) { + double mouseAngle; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.y(), mouseVecForText.x())); + else mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.y())); + + double refAngleForQuadrant; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); + else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); + + double relativeMouseAngle = mouseAngle - refAngleForQuadrant; + while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; + while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; + + if (relativeMouseAngle >= 90 || relativeMouseAngle < -90) { + refDir = -refDir; + } + } + } else { + if (m_viewport->property("isChainedLine").toBool()) { + refDir = -refDir; + } + } + + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + refAngle = atan2(refDir.z(), refDir.x()); + lineAngle = atan2(lineVec.z(), lineVec.x()); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + refAngle = atan2(refDir.y(), refDir.x()); + lineAngle = atan2(lineVec.y(), lineVec.x()); + } else { // YZ + refAngle = atan2(refDir.z(), refDir.y()); + lineAngle = atan2(lineVec.z(), lineVec.y()); + } + + double angleDiff = lineAngle - refAngle; + while (angleDiff <= -M_PI) angleDiff += 2 * M_PI; + while (angleDiff > M_PI) angleDiff -= 2 * M_PI; + lineAngle = refAngle + angleDiff; + + QVector3D perpVec; + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { + perpVec = QVector3D(-lineVec.z(), 0, lineVec.x()).normalized(); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { + perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized(); + } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) { + perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized(); + } + + if (angleDiff < 0) { + perpVec = -perpVec; + } + + float offset = 0.05f * -m_viewport->camera()->zoom(); + QVector3D dimStart = startPos + offset * perpVec; + QVector3D dimEnd = worldPos + offset * perpVec; + QVector3D textPos3D = (dimStart + dimEnd) / 2.0f + 0.015f * -m_viewport->camera()->zoom() * perpVec; + + QVector3D screenPos = m_viewport->project(textPos3D, modelView, projection, m_viewport->rect()); + + painter.setRenderHint(QPainter::Antialiasing); + QFontMetrics fm(painter.font()); + + if (screenPos.z() < 1.0f) { + dimText = lengthFromInput ? dimInput : QString::number(lineLength, 'f', 2); + + QRect textRect = fm.boundingRect(dimText + "_"); + textRect.moveCenter(screenPos.toPoint()); + + if (m_viewport->property("dimensionEditMode").toString() == "length") { + 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); + } + + // Angle dimension text + double angleDiffDegrees = qRadiansToDegrees(angleDiff); + while (angleDiffDegrees <= -180.0) angleDiffDegrees += 360.0; + while (angleDiffDegrees > 180.0) angleDiffDegrees -= 360.0; + + angleText = angleFromInput ? angleInput : QString::number(qAbs(angleDiffDegrees), 'f', 1) + QChar(0x00B0); + + const float radius = 0.1f * -m_viewport->camera()->zoom(); + double midAngle = refAngle + (lineAngle - refAngle) / 2.0; + QVector3D textPos3DAngle; + float textOffset = 0.035f * -m_viewport->camera()->zoom(); + + if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), 0, sin(midAngle)); + else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), sin(midAngle), 0); + else textPos3DAngle = startPos + (radius + textOffset) * QVector3D(0, cos(midAngle), sin(midAngle)); + + QVector3D screenPosAngle = m_viewport->project(textPos3DAngle, modelView, projection, m_viewport->rect()); + if (screenPosAngle.z() < 1.0f) { + QRect angleTextRect = fm.boundingRect(angleText + "_"); + angleTextRect.moveCenter(screenPosAngle.toPoint()); + if (m_viewport->property("dimensionEditMode").toString() == "angle") { + painter.fillRect(angleTextRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255)); + } else { + painter.fillRect(angleTextRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50)); + } + painter.setPen(Qt::white); + painter.drawText(angleTextRect, Qt::AlignCenter, angleText); + } + } + } } diff --git a/src/ViewportWidget.cpp b/src/ViewportWidget.cpp index 3737327..7139b9e 100644 --- a/src/ViewportWidget.cpp +++ b/src/ViewportWidget.cpp @@ -289,334 +289,8 @@ void ViewportWidget::paintGL() glDrawArrays(GL_LINES, 0, 8); } - if (m_isDefiningLine && m_activeTool == static_cast(ApplicationController::ToolType::Line)) { - vertices.clear(); - QVector3D worldPos; - QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); - - QString dimInput = property("dimensionInput").toString(); - QString angleInput = property("angleInput").toString(); - bool lengthFromInput = false; - bool angleFromInput = false; - double inputLength = 0; - double inputAngleDegrees = 0; - - if (!dimInput.isEmpty()) { - bool ok; - inputLength = dimInput.toDouble(&ok); - if (ok) lengthFromInput = true; - } - if (!angleInput.isEmpty()) { - bool ok; - inputAngleDegrees = angleInput.toDouble(&ok); - if (ok) angleFromInput = true; - } - - if (angleFromInput) { - QVector3D refDir; - if (property("isChainedLine").toBool()) { - refDir = property("previousLineDirection").value(); - } else { - if (m_currentPlane == SketchPlane::XY || m_currentPlane == SketchPlane::XZ) { - refDir = QVector3D(1, 0, 0); - } else { // YZ - refDir = QVector3D(0, 0, -1); - } - } - - QVector3D currentMouseWorldPos = unproject(m_currentMousePos, m_currentPlane); - QVector3D mouseVec = currentMouseWorldPos - startPos; - - // Quadrant snapping - double mouseAngle; - if (m_currentPlane == SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x())); - else if (m_currentPlane == SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x())); - else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y())); - - double refAngle; - if (m_currentPlane == SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); - else if (m_currentPlane == SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); - else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); - - double relativeMouseAngle = mouseAngle - refAngle; - while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; - while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; - - double snappedAngle = 0; - if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1 - snappedAngle = inputAngleDegrees; - } else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2 - snappedAngle = 180.0 - inputAngleDegrees; - } else if (relativeMouseAngle < -90) { // Quadrant 3 - snappedAngle = -180.0 + inputAngleDegrees; - } else { // Quadrant 4 - snappedAngle = -inputAngleDegrees; - } - - double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle); - QVector3D finalDir; - if (m_currentPlane == SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad)); - else if (m_currentPlane == SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0); - else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad)); - - double lineLength; - if (lengthFromInput) { - lineLength = inputLength; - } else { - lineLength = QVector3D::dotProduct(mouseVec, finalDir); - if (lineLength < 0) lineLength = 0; - } - worldPos = startPos + lineLength * finalDir; - - } else if (lengthFromInput) { - 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); - } - } - } else { - 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(); - - m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(1.0f, 1.0f, 0.0f, 1.0f)); - m_vbo.bind(); - m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); - glDrawArrays(GL_LINES, 0, 2); - - // Draw dimension line - QVector3D lineVec = worldPos - startPos; - float lineLength = lineVec.length(); - - if (lineLength > 1e-6) { - double refAngle, lineAngle, angleDiff; - { - QVector3D refDir; - if (property("isChainedLine").toBool()) { - refDir = property("previousLineDirection").value(); - } else { - if (m_currentPlane == SketchPlane::XY || m_currentPlane == SketchPlane::XZ) refDir = QVector3D(1, 0, 0); - else refDir = QVector3D(0, 0, -1); - } - - if (angleFromInput) { - QVector3D currentMouseWorldPos = unproject(m_currentMousePos, m_currentPlane); - QVector3D mouseVec = currentMouseWorldPos - startPos; - - double mouseAngle; - if (m_currentPlane == SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x())); - else if (m_currentPlane == SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x())); - else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y())); - - double refAngleForQuadrant; - if (m_currentPlane == SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); - else if (m_currentPlane == SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); - else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); - - double relativeMouseAngle = mouseAngle - refAngleForQuadrant; - while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; - while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; - - if (relativeMouseAngle >= 90 || relativeMouseAngle < -90) { - refDir = -refDir; - } - } else { - if (property("isChainedLine").toBool()) { - refDir = -refDir; - } - } - - if (m_currentPlane == SketchPlane::XY) { - refAngle = atan2(refDir.z(), refDir.x()); - lineAngle = atan2(lineVec.z(), lineVec.x()); - } else if (m_currentPlane == SketchPlane::XZ) { - refAngle = atan2(refDir.y(), refDir.x()); - lineAngle = atan2(lineVec.y(), lineVec.x()); - } else { // YZ - refAngle = atan2(refDir.z(), refDir.y()); - lineAngle = atan2(lineVec.z(), lineVec.y()); - } - - angleDiff = lineAngle - refAngle; - while (angleDiff <= -M_PI) angleDiff += 2 * M_PI; - while (angleDiff > M_PI) angleDiff -= 2 * M_PI; - lineAngle = refAngle + angleDiff; - } - - 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(); - } - - if (angleDiff < 0) { - perpVec = -perpVec; - } - - 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); - - // Draw angle dimension - vertices.clear(); - const int numSegments = 30; - const float radius = 0.1f * -m_camera->zoom(); - for (int i = 0; i <= numSegments; ++i) { - double angle = refAngle + (lineAngle - refAngle) * i / numSegments; - QVector3D p; - if (m_currentPlane == SketchPlane::XY) p = startPos + radius * QVector3D(cos(angle), 0, sin(angle)); - else if (m_currentPlane == SketchPlane::XZ) p = startPos + radius * QVector3D(cos(angle), sin(angle), 0); - else p = startPos + radius * QVector3D(0, cos(angle), sin(angle)); - vertices << p.x() << p.y() << p.z(); - } - glLineWidth(1.0f); - m_vbo.bind(); - m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); - glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3); - - // Arrowheads for arc - QVector arrowVertices; - float arcArrowLength = 0.02f * -m_camera->zoom(); - float arcArrowWidth = 0.005f * -m_camera->zoom(); - - double sign = (angleDiff >= 0) ? 1.0 : -1.0; - - // End arrowhead - QVector3D endPoint(vertices[vertices.size()-3], vertices[vertices.size()-2], vertices[vertices.size()-1]); - double endAngle = lineAngle; - QVector3D radialDir_end, tangentDir_end; - if (m_currentPlane == SketchPlane::XY) { - radialDir_end = QVector3D(cos(endAngle), 0, sin(endAngle)); - tangentDir_end = QVector3D(-sin(endAngle), 0, cos(endAngle)); - } else if (m_currentPlane == SketchPlane::XZ) { - radialDir_end = QVector3D(cos(endAngle), sin(endAngle), 0); - tangentDir_end = QVector3D(-sin(endAngle), cos(endAngle), 0); - } else { - radialDir_end = QVector3D(0, cos(endAngle), sin(endAngle)); - tangentDir_end = QVector3D(0, -sin(endAngle), cos(endAngle)); - } - QVector3D arc_arrow_base_end = endPoint - sign * arcArrowLength * tangentDir_end; - QVector3D arc_arrowP1_end = arc_arrow_base_end + arcArrowWidth * radialDir_end; - QVector3D arc_arrowP2_end = arc_arrow_base_end - arcArrowWidth * radialDir_end; - arrowVertices << endPoint.x() << endPoint.y() << endPoint.z() << arc_arrowP1_end.x() << arc_arrowP1_end.y() << arc_arrowP1_end.z(); - arrowVertices << endPoint.x() << endPoint.y() << endPoint.z() << arc_arrowP2_end.x() << arc_arrowP2_end.y() << arc_arrowP2_end.z(); - - // Start arrowhead - QVector3D startPoint(vertices[0], vertices[1], vertices[2]); - double startAngle = refAngle; - QVector3D radialDir_start, tangentDir_start; - if (m_currentPlane == SketchPlane::XY) { - radialDir_start = QVector3D(cos(startAngle), 0, sin(startAngle)); - tangentDir_start = QVector3D(-sin(startAngle), 0, cos(startAngle)); - } else if (m_currentPlane == SketchPlane::XZ) { - radialDir_start = QVector3D(cos(startAngle), sin(startAngle), 0); - tangentDir_start = QVector3D(-sin(startAngle), cos(startAngle), 0); - } else { - radialDir_start = QVector3D(0, cos(startAngle), sin(startAngle)); - tangentDir_start = QVector3D(0, -sin(startAngle), cos(startAngle)); - } - QVector3D arc_arrow_base_start = startPoint + sign * arcArrowLength * tangentDir_start; - QVector3D arc_arrowP1_start = arc_arrow_base_start + arcArrowWidth * radialDir_start; - QVector3D arc_arrowP2_start = arc_arrow_base_start - arcArrowWidth * radialDir_start; - arrowVertices << startPoint.x() << startPoint.y() << startPoint.z() << arc_arrowP1_start.x() << arc_arrowP1_start.y() << arc_arrowP1_start.z(); - arrowVertices << startPoint.x() << startPoint.y() << startPoint.z() << arc_arrowP2_start.x() << arc_arrowP2_start.y() << arc_arrowP2_start.z(); - - m_vbo.bind(); - m_vbo.allocate(arrowVertices.constData(), arrowVertices.size() * sizeof(GLfloat)); - glDrawArrays(GL_LINES, 0, arrowVertices.size() / 3); - glLineWidth(2.0f); - } - - if (!lengthFromInput && !angleFromInput && (m_isSnappingHorizontal || m_isSnappingVertical)) { - vertices.clear(); - QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z()); - QVector3D midPoint = (startPos + worldPos) / 2.0; - const float indicatorSize = 0.02f * -m_camera->zoom(); - const float indicatorOffset = 0.02f * -m_camera->zoom(); - if (m_isSnappingHorizontal) { - if (m_currentPlane == SketchPlane::XY) { - vertices << midPoint.x() - indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset; - vertices << midPoint.x() + indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset; - } else if (m_currentPlane == SketchPlane::XZ) { - vertices << midPoint.x() - indicatorSize << midPoint.y() + indicatorOffset << midPoint.z(); - vertices << midPoint.x() + indicatorSize << midPoint.y() + indicatorOffset << midPoint.z(); - } else if (m_currentPlane == SketchPlane::YZ) { - vertices << midPoint.x() << midPoint.y() - indicatorSize << midPoint.z() + indicatorOffset; - vertices << midPoint.x() << midPoint.y() + indicatorSize << midPoint.z() + indicatorOffset; - } - } else { // m_isSnappingVertical - if (m_currentPlane == SketchPlane::XY) { - vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() - indicatorSize; - vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() + indicatorSize; - } else if (m_currentPlane == SketchPlane::XZ) { - vertices << midPoint.x() + indicatorOffset << midPoint.y() - indicatorSize << midPoint.z(); - vertices << midPoint.x() + indicatorOffset << midPoint.y() + indicatorSize << midPoint.z(); - } else if (m_currentPlane == SketchPlane::YZ) { - vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() - indicatorSize; - vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() + indicatorSize; - } - } - m_vbo.bind(); - m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); - glDrawArrays(GL_LINES, 0, 2); - } + if (m_activeSketchTool) { + m_activeSketchTool->paintGL(); } m_shaderProgram->release(); @@ -755,240 +429,8 @@ void ViewportWidget::paintGL() } } - 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 angleText; - - QString dimInput = property("dimensionInput").toString(); - QString angleInput = property("angleInput").toString(); - bool lengthFromInput = false; - bool angleFromInput = false; - double inputLength = 0; - double inputAngleDegrees = 0; - double lineLength = 0; - - if (!dimInput.isEmpty()) { - bool ok; - inputLength = dimInput.toDouble(&ok); - if (ok) lengthFromInput = true; - } - if (!angleInput.isEmpty()) { - bool ok; - inputAngleDegrees = angleInput.toDouble(&ok); - if (ok) angleFromInput = true; - } - - if (angleFromInput) { - QVector3D refDir; - if (property("isChainedLine").toBool()) { - refDir = property("previousLineDirection").value(); - } else { - if (m_currentPlane == SketchPlane::XY || m_currentPlane == SketchPlane::XZ) { - refDir = QVector3D(1, 0, 0); - } else { // YZ - refDir = QVector3D(0, 0, -1); - } - } - - QVector3D currentMouseWorldPos = unproject(m_currentMousePos, m_currentPlane); - QVector3D mouseVec = currentMouseWorldPos - startPos; - - double mouseAngle; - if (m_currentPlane == SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x())); - else if (m_currentPlane == SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x())); - else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y())); - - double refAngle; - if (m_currentPlane == SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); - else if (m_currentPlane == SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); - else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); - - double relativeMouseAngle = mouseAngle - refAngle; - while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; - while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; - - double snappedAngle = 0; - if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1 - snappedAngle = inputAngleDegrees; - } else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2 - snappedAngle = 180.0 - inputAngleDegrees; - } else if (relativeMouseAngle < -90) { // Quadrant 3 - snappedAngle = -180.0 + inputAngleDegrees; - } else { // Quadrant 4 - snappedAngle = -inputAngleDegrees; - } - - double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle); - QVector3D finalDir; - if (m_currentPlane == SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad)); - else if (m_currentPlane == SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0); - else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad)); - - if (lengthFromInput) { - lineLength = inputLength; - } else { - lineLength = QVector3D::dotProduct(mouseVec, finalDir); - if (lineLength < 0) lineLength = 0; - } - worldPos = startPos + lineLength * finalDir; - - } else if (lengthFromInput) { - 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); - } - } - } else { - 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) { - double refAngle, lineAngle; - QVector3D refDir; - if (property("isChainedLine").toBool()) { - refDir = property("previousLineDirection").value(); - } else { - if (m_currentPlane == SketchPlane::XY || m_currentPlane == SketchPlane::XZ) refDir = QVector3D(1, 0, 0); - else refDir = QVector3D(0, 0, -1); - } - - QVector3D currentMouseWorldPosForText = unproject(m_currentMousePos, m_currentPlane); - QVector3D mouseVecForText = currentMouseWorldPosForText - startPos; - - if (angleFromInput) { - if (mouseVecForText.length() > 1e-6) { - double mouseAngle; - if (m_currentPlane == SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.x())); - else if (m_currentPlane == SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.y(), mouseVecForText.x())); - else mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.y())); - - double refAngleForQuadrant; - if (m_currentPlane == SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x())); - else if (m_currentPlane == SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x())); - else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y())); - - double relativeMouseAngle = mouseAngle - refAngleForQuadrant; - while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0; - while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0; - - if (relativeMouseAngle >= 90 || relativeMouseAngle < -90) { - refDir = -refDir; - } - } - } else { - if (property("isChainedLine").toBool()) { - refDir = -refDir; - } - } - - if (m_currentPlane == SketchPlane::XY) { - refAngle = atan2(refDir.z(), refDir.x()); - lineAngle = atan2(lineVec.z(), lineVec.x()); - } else if (m_currentPlane == SketchPlane::XZ) { - refAngle = atan2(refDir.y(), refDir.x()); - lineAngle = atan2(lineVec.y(), lineVec.x()); - } else { // YZ - refAngle = atan2(refDir.z(), refDir.y()); - lineAngle = atan2(lineVec.z(), lineVec.y()); - } - - double angleDiff = lineAngle - refAngle; - while (angleDiff <= -M_PI) angleDiff += 2 * M_PI; - while (angleDiff > M_PI) angleDiff -= 2 * M_PI; - lineAngle = refAngle + angleDiff; - - 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(); - } - - if (angleDiff < 0) { - perpVec = -perpVec; - } - - 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()); - - painter.setRenderHint(QPainter::Antialiasing); - QFontMetrics fm(painter.font()); - - if (screenPos.z() < 1.0f) { - dimText = lengthFromInput ? dimInput : QString::number(lineLength, 'f', 2); - - QRect textRect = fm.boundingRect(dimText + "_"); - textRect.moveCenter(screenPos.toPoint()); - - if (property("dimensionEditMode").toString() == "length") { - 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); - } - - // Angle dimension text - double angleDiffDegrees = qRadiansToDegrees(angleDiff); - while (angleDiffDegrees <= -180.0) angleDiffDegrees += 360.0; - while (angleDiffDegrees > 180.0) angleDiffDegrees -= 360.0; - - angleText = angleFromInput ? angleInput : QString::number(qAbs(angleDiffDegrees), 'f', 1) + QChar(0x00B0); - - const float radius = 0.1f * -m_camera->zoom(); - double midAngle = refAngle + (lineAngle - refAngle) / 2.0; - QVector3D textPos3DAngle; - float textOffset = 0.035f * -m_camera->zoom(); - - if (m_currentPlane == SketchPlane::XY) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), 0, sin(midAngle)); - else if (m_currentPlane == SketchPlane::XZ) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), sin(midAngle), 0); - else textPos3DAngle = startPos + (radius + textOffset) * QVector3D(0, cos(midAngle), sin(midAngle)); - - QVector3D screenPosAngle = project(textPos3DAngle, model, projection, rect()); - if (screenPosAngle.z() < 1.0f) { - QRect angleTextRect = fm.boundingRect(angleText + "_"); - angleTextRect.moveCenter(screenPosAngle.toPoint()); - if (property("dimensionEditMode").toString() == "angle") { - painter.fillRect(angleTextRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255)); - } else { - painter.fillRect(angleTextRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50)); - } - painter.setPen(Qt::white); - painter.drawText(angleTextRect, Qt::AlignCenter, angleText); - } - } + if (m_activeSketchTool) { + m_activeSketchTool->paint2D(painter, model, projection); } painter.end(); diff --git a/src/ViewportWidget.h b/src/ViewportWidget.h index a631f92..50f9be0 100644 --- a/src/ViewportWidget.h +++ b/src/ViewportWidget.h @@ -39,6 +39,20 @@ public: void setDocument(Document* document); + QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport); + QVector3D unproject(const QPoint& screenPos, SketchPlane plane); + QOpenGLShaderProgram* shaderProgram() { return m_shaderProgram; } + QOpenGLBuffer& vbo() { return m_vbo; } + int colorLoc() const { return m_colorLoc; } + Camera* camera() const { return m_camera; } + SketchPlane currentPlane() const { return m_currentPlane; } + const QPoint& currentMousePos() const { return m_currentMousePos; } + bool isSnappingOrigin() const { return m_isSnappingOrigin; } + bool isSnappingVertex() const { return m_isSnappingVertex; } + const gp_Pnt& snapVertex() const { return m_snapVertex; } + bool isSnappingHorizontal() const { return m_isSnappingHorizontal; } + bool isSnappingVertical() const { return m_isSnappingVertical; } + public slots: void onSketchModeStarted(SketchPlane plane); void onSketchModeEnded(); @@ -64,8 +78,6 @@ protected: private: void initShaders(); - QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport); - QVector3D unproject(const QPoint& screenPos, SketchPlane plane); void drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection); void drawSketch(const SketchFeature* sketch); void drawSelectionPlanes();