feat: Implement in-viewport sketch plane selection
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -135,6 +135,10 @@ void ViewportWidget::paintGL()
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadMatrixf(model.constData());
|
||||
|
||||
if (m_isSelectingPlane) {
|
||||
drawSelectionPlanes();
|
||||
}
|
||||
|
||||
if (m_currentPlane != SketchPlane::NONE) {
|
||||
m_sketchGrid->paintGL(static_cast<SketchGrid::SketchPlane>(m_currentPlane), projection, model);
|
||||
}
|
||||
@@ -198,7 +202,7 @@ void ViewportWidget::paintGL()
|
||||
}
|
||||
|
||||
if (m_isDefiningLine && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos);
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
if (m_isSnappingOrigin) {
|
||||
worldPos.setX(0);
|
||||
worldPos.setY(0);
|
||||
@@ -286,16 +290,27 @@ void ViewportWidget::resizeGL(int w, int h)
|
||||
|
||||
void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton && m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||
gp_Pnt p;
|
||||
if (m_isSnappingOrigin) {
|
||||
p.SetCoord(0, 0, 0);
|
||||
} else if (m_isSnappingVertex) {
|
||||
p = m_snapVertex;
|
||||
} else {
|
||||
QVector3D worldPos = unproject(event->pos());
|
||||
if (m_isSnappingHorizontal) {
|
||||
if (m_currentPlane == SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z());
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (m_isSelectingPlane) {
|
||||
if (m_highlightedPlane != SketchPlane::NONE) {
|
||||
emit planeSelected(m_highlightedPlane);
|
||||
m_isSelectingPlane = false;
|
||||
m_highlightedPlane = SketchPlane::NONE;
|
||||
update();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||
gp_Pnt p;
|
||||
if (m_isSnappingOrigin) {
|
||||
p.SetCoord(0, 0, 0);
|
||||
} else if (m_isSnappingVertex) {
|
||||
p = m_snapVertex;
|
||||
} else {
|
||||
QVector3D worldPos = unproject(event->pos(), m_currentPlane);
|
||||
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) {
|
||||
@@ -312,8 +327,9 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
||||
} else {
|
||||
emit lineAdded(m_firstLinePoint, p);
|
||||
m_firstLinePoint = p;
|
||||
}
|
||||
update();
|
||||
}
|
||||
update();
|
||||
} else {
|
||||
lastPos = event->pos();
|
||||
}
|
||||
@@ -323,9 +339,17 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
m_currentMousePos = event->pos();
|
||||
|
||||
if (m_isSelectingPlane) {
|
||||
SketchPlane newHighlight = checkPlaneSelection(m_currentMousePos);
|
||||
if (newHighlight != m_highlightedPlane) {
|
||||
m_highlightedPlane = newHighlight;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldSnap = false;
|
||||
if (m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos);
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
const float snapRectHalfSize = 0.0075f * -m_zoom;
|
||||
|
||||
switch (m_currentPlane) {
|
||||
@@ -354,7 +378,7 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
bool oldIsSnappingVertex = m_isSnappingVertex;
|
||||
m_isSnappingVertex = false;
|
||||
if (!m_isSnappingOrigin && m_document && m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos);
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
const float snapRectHalfSize = 0.0075f * -m_zoom;
|
||||
|
||||
for (Feature* feature : m_document->features()) {
|
||||
@@ -402,7 +426,7 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
m_isSnappingVertical = false;
|
||||
|
||||
if (m_isDefiningLine && !m_isSnappingOrigin && !m_isSnappingVertex) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos);
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QVector3D delta = worldPos - startPos;
|
||||
|
||||
@@ -538,6 +562,13 @@ void ViewportWidget::onSketchModeStarted(SketchPlane plane)
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void ViewportWidget::onPlaneSelectionModeStarted()
|
||||
{
|
||||
m_isSelectingPlane = true;
|
||||
m_highlightedPlane = SketchPlane::NONE;
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewportWidget::onSketchModeEnded()
|
||||
{
|
||||
auto* animGroup = new QParallelAnimationGroup(this);
|
||||
@@ -678,7 +709,7 @@ void ViewportWidget::onActiveToolChanged(int tool)
|
||||
}
|
||||
}
|
||||
|
||||
QVector3D ViewportWidget::unproject(const QPoint& screenPos)
|
||||
QVector3D ViewportWidget::unproject(const QPoint& screenPos, SketchPlane plane)
|
||||
{
|
||||
QMatrix4x4 model;
|
||||
model.translate(m_panX, m_panY, m_zoom);
|
||||
@@ -711,7 +742,7 @@ QVector3D ViewportWidget::unproject(const QPoint& screenPos)
|
||||
QVector3D rayDir = (QVector3D(farPoint_world) - rayOrigin).normalized();
|
||||
|
||||
QVector3D planeNormal;
|
||||
switch (m_currentPlane) {
|
||||
switch (plane) {
|
||||
case SketchPlane::XY: planeNormal = QVector3D(0, 1, 0); break;
|
||||
case SketchPlane::XZ: planeNormal = QVector3D(0, 0, 1); break;
|
||||
case SketchPlane::YZ: planeNormal = QVector3D(1, 0, 0); break;
|
||||
@@ -754,3 +785,105 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void ViewportWidget::drawSelectionPlanes()
|
||||
{
|
||||
const float planeSize = 5.0f;
|
||||
const float planeOffset = 1.0f;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glLineWidth(2.0f);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
// Draw back to front for proper blending
|
||||
|
||||
// XY Plane (Top), normal is Y (green)
|
||||
glColor4f(0.0f, 1.0f, 0.0f, m_highlightedPlane == SketchPlane::XY ? 0.5f : 0.2f);
|
||||
if (m_highlightedPlane == SketchPlane::XY) {
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f(planeOffset, 0, planeOffset);
|
||||
glVertex3f(planeOffset + planeSize, 0, planeOffset);
|
||||
glVertex3f(planeOffset + planeSize, 0, planeOffset + planeSize);
|
||||
glVertex3f(planeOffset, 0, planeOffset + planeSize);
|
||||
glEnd();
|
||||
}
|
||||
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glVertex3f(planeOffset, 0, planeOffset);
|
||||
glVertex3f(planeOffset + planeSize, 0, planeOffset);
|
||||
glVertex3f(planeOffset + planeSize, 0, planeOffset + planeSize);
|
||||
glVertex3f(planeOffset, 0, planeOffset + planeSize);
|
||||
glEnd();
|
||||
|
||||
// XZ Plane (Front), normal is Z (blue)
|
||||
glColor4f(0.0f, 0.0f, 1.0f, m_highlightedPlane == SketchPlane::XZ ? 0.5f : 0.2f);
|
||||
if (m_highlightedPlane == SketchPlane::XZ) {
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f(planeOffset, planeOffset, 0);
|
||||
glVertex3f(planeOffset + planeSize, planeOffset, 0);
|
||||
glVertex3f(planeOffset + planeSize, planeOffset + planeSize, 0);
|
||||
glVertex3f(planeOffset, planeOffset + planeSize, 0);
|
||||
glEnd();
|
||||
}
|
||||
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glVertex3f(planeOffset, planeOffset, 0);
|
||||
glVertex3f(planeOffset + planeSize, planeOffset, 0);
|
||||
glVertex3f(planeOffset + planeSize, planeOffset + planeSize, 0);
|
||||
glVertex3f(planeOffset, planeOffset + planeSize, 0);
|
||||
glEnd();
|
||||
|
||||
// YZ Plane (Right), normal is X (red)
|
||||
glColor4f(1.0f, 0.0f, 0.0f, m_highlightedPlane == SketchPlane::YZ ? 0.5f : 0.2f);
|
||||
if (m_highlightedPlane == SketchPlane::YZ) {
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f(0, planeOffset, planeOffset);
|
||||
glVertex3f(0, planeOffset + planeSize, planeOffset);
|
||||
glVertex3f(0, planeOffset + planeSize, planeOffset + planeSize);
|
||||
glVertex3f(0, planeOffset, planeOffset + planeSize);
|
||||
glEnd();
|
||||
}
|
||||
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glVertex3f(0, planeOffset, planeOffset);
|
||||
glVertex3f(0, planeOffset + planeSize, planeOffset);
|
||||
glVertex3f(0, planeOffset + planeSize, planeOffset + planeSize);
|
||||
glVertex3f(0, planeOffset, planeOffset + planeSize);
|
||||
glEnd();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
ViewportWidget::SketchPlane ViewportWidget::checkPlaneSelection(const QPoint& screenPos)
|
||||
{
|
||||
const float planeSize = 5.0f;
|
||||
const float planeOffset = 1.0f;
|
||||
|
||||
QVector3D intersection;
|
||||
|
||||
// Check front to back to handle overlaps
|
||||
// YZ plane (Right)
|
||||
intersection = unproject(screenPos, SketchPlane::YZ);
|
||||
if (intersection.y() >= planeOffset && intersection.y() <= planeOffset + planeSize &&
|
||||
intersection.z() >= planeOffset && intersection.z() <= planeOffset + planeSize) {
|
||||
return SketchPlane::YZ;
|
||||
}
|
||||
|
||||
// XZ plane (Front)
|
||||
intersection = unproject(screenPos, SketchPlane::XZ);
|
||||
if (intersection.x() >= planeOffset && intersection.x() <= planeOffset + planeSize &&
|
||||
intersection.y() >= planeOffset && intersection.y() <= planeOffset + planeSize) {
|
||||
return SketchPlane::XZ;
|
||||
}
|
||||
|
||||
// XY plane (Top)
|
||||
intersection = unproject(screenPos, SketchPlane::XY);
|
||||
if (intersection.x() >= planeOffset && intersection.x() <= planeOffset + planeSize &&
|
||||
intersection.z() >= planeOffset && intersection.z() <= planeOffset + planeSize) {
|
||||
return SketchPlane::XY;
|
||||
}
|
||||
|
||||
return SketchPlane::NONE;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user