feat: Add horizontal/vertical line snapping with visual indicator
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -19,6 +19,8 @@
|
||||
#include <QApplication>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <cmath>
|
||||
#include <QtMath>
|
||||
|
||||
ViewportWidget::ViewportWidget(QWidget *parent)
|
||||
: QOpenGLWidget(parent)
|
||||
@@ -197,12 +199,55 @@ void ViewportWidget::paintGL()
|
||||
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.setY(m_firstLinePoint.Y());
|
||||
else if (m_currentPlane == SketchPlane::XZ) worldPos.setZ(m_firstLinePoint.Z());
|
||||
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());
|
||||
}
|
||||
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();
|
||||
|
||||
if (m_isSnappingHorizontal || m_isSnappingVertical) {
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QVector3D midPoint = (startPos + worldPos) / 2.0;
|
||||
const float indicatorSize = 0.02f * -m_zoom;
|
||||
const float indicatorOffset = 0.02f * -m_zoom;
|
||||
|
||||
glColor3f(1.0, 1.0, 0.0);
|
||||
glBegin(GL_LINES);
|
||||
|
||||
if (m_isSnappingHorizontal) {
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
glVertex3f(midPoint.x() - indicatorSize, midPoint.y() + indicatorOffset, midPoint.z());
|
||||
glVertex3f(midPoint.x() + indicatorSize, midPoint.y() + indicatorOffset, midPoint.z());
|
||||
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||
glVertex3f(midPoint.x() - indicatorSize, midPoint.y(), midPoint.z() + indicatorOffset);
|
||||
glVertex3f(midPoint.x() + indicatorSize, midPoint.y(), midPoint.z() + indicatorOffset);
|
||||
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||
glVertex3f(midPoint.x(), midPoint.y() - indicatorSize, midPoint.z() + indicatorOffset);
|
||||
glVertex3f(midPoint.x(), midPoint.y() + indicatorSize, midPoint.z() + indicatorOffset);
|
||||
}
|
||||
} else { // m_isSnappingVertical
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
glVertex3f(midPoint.x() + indicatorOffset, midPoint.y() - indicatorSize, midPoint.z());
|
||||
glVertex3f(midPoint.x() + indicatorOffset, midPoint.y() + indicatorSize, midPoint.z());
|
||||
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||
glVertex3f(midPoint.x() + indicatorOffset, midPoint.y(), midPoint.z() - indicatorSize);
|
||||
glVertex3f(midPoint.x() + indicatorOffset, midPoint.y(), midPoint.z() + indicatorSize);
|
||||
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||
glVertex3f(midPoint.x(), midPoint.y() + indicatorOffset, midPoint.z() - indicatorSize);
|
||||
glVertex3f(midPoint.x(), midPoint.y() + indicatorOffset, midPoint.z() + indicatorSize);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// View cube rendering
|
||||
@@ -241,6 +286,15 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
||||
p = m_snapVertex;
|
||||
} else {
|
||||
QVector3D worldPos = unproject(event->pos());
|
||||
if (m_isSnappingHorizontal) {
|
||||
if (m_currentPlane == SketchPlane::XY) worldPos.setY(m_firstLinePoint.Y());
|
||||
else if (m_currentPlane == SketchPlane::XZ) worldPos.setZ(m_firstLinePoint.Z());
|
||||
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());
|
||||
}
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
}
|
||||
|
||||
@@ -334,6 +388,40 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
update();
|
||||
}
|
||||
|
||||
bool oldIsSnappingHorizontal = m_isSnappingHorizontal;
|
||||
bool oldIsSnappingVertical = m_isSnappingVertical;
|
||||
m_isSnappingHorizontal = false;
|
||||
m_isSnappingVertical = false;
|
||||
|
||||
if (m_isDefiningLine && !m_isSnappingOrigin && !m_isSnappingVertex) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos);
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QVector3D delta = worldPos - startPos;
|
||||
|
||||
if (delta.length() > 1e-6) {
|
||||
const double snapAngleThreshold = qDegreesToRadians(2.0);
|
||||
double angle = 0;
|
||||
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
angle = atan2(delta.y(), delta.x());
|
||||
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||
angle = atan2(delta.z(), delta.x());
|
||||
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||
angle = atan2(delta.z(), delta.y());
|
||||
}
|
||||
|
||||
if (qAbs(sin(angle)) < sin(snapAngleThreshold)) {
|
||||
m_isSnappingHorizontal = true;
|
||||
} else if (qAbs(cos(angle)) < sin(snapAngleThreshold)) {
|
||||
m_isSnappingVertical = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldIsSnappingHorizontal != m_isSnappingHorizontal || oldIsSnappingVertical != m_isSnappingVertical) {
|
||||
update();
|
||||
}
|
||||
|
||||
int dx = event->pos().x() - lastPos.x();
|
||||
int dy = event->pos().y() - lastPos.y();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user