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 <QApplication>
|
||||||
#include <QPropertyAnimation>
|
#include <QPropertyAnimation>
|
||||||
#include <QParallelAnimationGroup>
|
#include <QParallelAnimationGroup>
|
||||||
|
#include <cmath>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
ViewportWidget::ViewportWidget(QWidget *parent)
|
ViewportWidget::ViewportWidget(QWidget *parent)
|
||||||
: QOpenGLWidget(parent)
|
: QOpenGLWidget(parent)
|
||||||
@@ -197,12 +199,55 @@ void ViewportWidget::paintGL()
|
|||||||
worldPos.setX(m_snapVertex.X());
|
worldPos.setX(m_snapVertex.X());
|
||||||
worldPos.setY(m_snapVertex.Y());
|
worldPos.setY(m_snapVertex.Y());
|
||||||
worldPos.setZ(m_snapVertex.Z());
|
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);
|
glBegin(GL_LINES);
|
||||||
glColor3f(1.0, 1.0, 0.0);
|
glColor3f(1.0, 1.0, 0.0);
|
||||||
glVertex3d(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
glVertex3d(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||||
glVertex3d(worldPos.x(), worldPos.y(), worldPos.z());
|
glVertex3d(worldPos.x(), worldPos.y(), worldPos.z());
|
||||||
glEnd();
|
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
|
// View cube rendering
|
||||||
@@ -241,6 +286,15 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
|||||||
p = m_snapVertex;
|
p = m_snapVertex;
|
||||||
} else {
|
} else {
|
||||||
QVector3D worldPos = unproject(event->pos());
|
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());
|
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,6 +388,40 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
|||||||
update();
|
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 dx = event->pos().x() - lastPos.x();
|
||||||
int dy = event->pos().y() - lastPos.y();
|
int dy = event->pos().y() - lastPos.y();
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ private:
|
|||||||
bool m_isSnappingOrigin = false;
|
bool m_isSnappingOrigin = false;
|
||||||
bool m_isSnappingVertex = false;
|
bool m_isSnappingVertex = false;
|
||||||
gp_Pnt m_snapVertex;
|
gp_Pnt m_snapVertex;
|
||||||
|
bool m_isSnappingHorizontal = false;
|
||||||
|
bool m_isSnappingVertical = false;
|
||||||
|
|
||||||
QMap<int, QSvgRenderer*> m_toolIcons;
|
QMap<int, QSvgRenderer*> m_toolIcons;
|
||||||
QSvgRenderer* m_cursorRenderer = nullptr;
|
QSvgRenderer* m_cursorRenderer = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user