feat: Implement interactive dimension input for line drawing
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -198,7 +198,33 @@ void ViewportWidget::paintGL()
|
||||
|
||||
if (m_isDefiningLine && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||
vertices.clear();
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
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) {
|
||||
@@ -212,6 +238,8 @@ void ViewportWidget::paintGL()
|
||||
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<int>(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<int>(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;
|
||||
|
||||
Reference in New Issue
Block a user