feat: Implement in-viewport sketch plane selection
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -102,29 +102,32 @@ bool ApplicationController::saveDocumentAs()
|
|||||||
|
|
||||||
void ApplicationController::beginSketchCreation()
|
void ApplicationController::beginSketchCreation()
|
||||||
{
|
{
|
||||||
QStringList items;
|
emit planeSelectionModeStarted();
|
||||||
items << "Top (XY)" << "Front (XZ)" << "Right (YZ)";
|
}
|
||||||
|
|
||||||
bool ok;
|
void ApplicationController::onPlaneSelected(ViewportWidget::SketchPlane plane)
|
||||||
QString item = QInputDialog::getItem(m_mainWindow, "Select Sketch Plane",
|
{
|
||||||
"Plane:", items, 0, false, &ok);
|
|
||||||
if (ok && !item.isEmpty()) {
|
|
||||||
auto feature = new SketchFeature("Sketch");
|
auto feature = new SketchFeature("Sketch");
|
||||||
m_activeSketch = feature;
|
m_activeSketch = feature;
|
||||||
ViewportWidget::SketchPlane plane;
|
|
||||||
if (item == "Top (XY)") {
|
switch (plane) {
|
||||||
plane = ViewportWidget::SketchPlane::XY;
|
case ViewportWidget::SketchPlane::XY:
|
||||||
feature->setPlane(SketchFeature::SketchPlane::XY);
|
feature->setPlane(SketchFeature::SketchPlane::XY);
|
||||||
} else if (item == "Front (XZ)") {
|
break;
|
||||||
plane = ViewportWidget::SketchPlane::XZ;
|
case ViewportWidget::SketchPlane::XZ:
|
||||||
feature->setPlane(SketchFeature::SketchPlane::XZ);
|
feature->setPlane(SketchFeature::SketchPlane::XZ);
|
||||||
} else { // "Right (YZ)"
|
break;
|
||||||
plane = ViewportWidget::SketchPlane::YZ;
|
case ViewportWidget::SketchPlane::YZ:
|
||||||
feature->setPlane(SketchFeature::SketchPlane::YZ);
|
feature->setPlane(SketchFeature::SketchPlane::YZ);
|
||||||
|
break;
|
||||||
|
case ViewportWidget::SketchPlane::NONE:
|
||||||
|
delete feature;
|
||||||
|
m_activeSketch = nullptr;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_document->addFeature(feature);
|
m_document->addFeature(feature);
|
||||||
emit sketchModeStarted(plane);
|
emit sketchModeStarted(plane);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationController::addLine(const gp_Pnt& start, const gp_Pnt& end)
|
void ApplicationController::addLine(const gp_Pnt& start, const gp_Pnt& end)
|
||||||
|
|||||||
@@ -38,9 +38,11 @@ public slots:
|
|||||||
bool saveDocumentAs();
|
bool saveDocumentAs();
|
||||||
|
|
||||||
void beginSketchCreation();
|
void beginSketchCreation();
|
||||||
|
void onPlaneSelected(ViewportWidget::SketchPlane plane);
|
||||||
void endSketch();
|
void endSketch();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void planeSelectionModeStarted();
|
||||||
void sketchModeStarted(ViewportWidget::SketchPlane plane);
|
void sketchModeStarted(ViewportWidget::SketchPlane plane);
|
||||||
void sketchModeEnded();
|
void sketchModeEnded();
|
||||||
void currentFileChanged(const QString& path);
|
void currentFileChanged(const QString& path);
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ void ViewportWidget::paintGL()
|
|||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadMatrixf(model.constData());
|
glLoadMatrixf(model.constData());
|
||||||
|
|
||||||
|
if (m_isSelectingPlane) {
|
||||||
|
drawSelectionPlanes();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_currentPlane != SketchPlane::NONE) {
|
if (m_currentPlane != SketchPlane::NONE) {
|
||||||
m_sketchGrid->paintGL(static_cast<SketchGrid::SketchPlane>(m_currentPlane), projection, model);
|
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)) {
|
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) {
|
if (m_isSnappingOrigin) {
|
||||||
worldPos.setX(0);
|
worldPos.setX(0);
|
||||||
worldPos.setY(0);
|
worldPos.setY(0);
|
||||||
@@ -286,14 +290,25 @@ void ViewportWidget::resizeGL(int w, int h)
|
|||||||
|
|
||||||
void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton && m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
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;
|
gp_Pnt p;
|
||||||
if (m_isSnappingOrigin) {
|
if (m_isSnappingOrigin) {
|
||||||
p.SetCoord(0, 0, 0);
|
p.SetCoord(0, 0, 0);
|
||||||
} else if (m_isSnappingVertex) {
|
} else if (m_isSnappingVertex) {
|
||||||
p = m_snapVertex;
|
p = m_snapVertex;
|
||||||
} else {
|
} else {
|
||||||
QVector3D worldPos = unproject(event->pos());
|
QVector3D worldPos = unproject(event->pos(), m_currentPlane);
|
||||||
if (m_isSnappingHorizontal) {
|
if (m_isSnappingHorizontal) {
|
||||||
if (m_currentPlane == SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z());
|
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::XZ) worldPos.setY(m_firstLinePoint.Y());
|
||||||
@@ -314,6 +329,7 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
|||||||
m_firstLinePoint = p;
|
m_firstLinePoint = p;
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
lastPos = event->pos();
|
lastPos = event->pos();
|
||||||
}
|
}
|
||||||
@@ -323,9 +339,17 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
|||||||
{
|
{
|
||||||
m_currentMousePos = event->pos();
|
m_currentMousePos = event->pos();
|
||||||
|
|
||||||
|
if (m_isSelectingPlane) {
|
||||||
|
SketchPlane newHighlight = checkPlaneSelection(m_currentMousePos);
|
||||||
|
if (newHighlight != m_highlightedPlane) {
|
||||||
|
m_highlightedPlane = newHighlight;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool shouldSnap = false;
|
bool shouldSnap = false;
|
||||||
if (m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast<int>(ApplicationController::ToolType::None)) {
|
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;
|
const float snapRectHalfSize = 0.0075f * -m_zoom;
|
||||||
|
|
||||||
switch (m_currentPlane) {
|
switch (m_currentPlane) {
|
||||||
@@ -354,7 +378,7 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
|||||||
bool oldIsSnappingVertex = m_isSnappingVertex;
|
bool oldIsSnappingVertex = m_isSnappingVertex;
|
||||||
m_isSnappingVertex = false;
|
m_isSnappingVertex = false;
|
||||||
if (!m_isSnappingOrigin && m_document && m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast<int>(ApplicationController::ToolType::None)) {
|
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;
|
const float snapRectHalfSize = 0.0075f * -m_zoom;
|
||||||
|
|
||||||
for (Feature* feature : m_document->features()) {
|
for (Feature* feature : m_document->features()) {
|
||||||
@@ -402,7 +426,7 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
|||||||
m_isSnappingVertical = false;
|
m_isSnappingVertical = false;
|
||||||
|
|
||||||
if (m_isDefiningLine && !m_isSnappingOrigin && !m_isSnappingVertex) {
|
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 startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||||
QVector3D delta = worldPos - startPos;
|
QVector3D delta = worldPos - startPos;
|
||||||
|
|
||||||
@@ -538,6 +562,13 @@ void ViewportWidget::onSketchModeStarted(SketchPlane plane)
|
|||||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewportWidget::onPlaneSelectionModeStarted()
|
||||||
|
{
|
||||||
|
m_isSelectingPlane = true;
|
||||||
|
m_highlightedPlane = SketchPlane::NONE;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void ViewportWidget::onSketchModeEnded()
|
void ViewportWidget::onSketchModeEnded()
|
||||||
{
|
{
|
||||||
auto* animGroup = new QParallelAnimationGroup(this);
|
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;
|
QMatrix4x4 model;
|
||||||
model.translate(m_panX, m_panY, m_zoom);
|
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 rayDir = (QVector3D(farPoint_world) - rayOrigin).normalized();
|
||||||
|
|
||||||
QVector3D planeNormal;
|
QVector3D planeNormal;
|
||||||
switch (m_currentPlane) {
|
switch (plane) {
|
||||||
case SketchPlane::XY: planeNormal = QVector3D(0, 1, 0); break;
|
case SketchPlane::XY: planeNormal = QVector3D(0, 1, 0); break;
|
||||||
case SketchPlane::XZ: planeNormal = QVector3D(0, 0, 1); break;
|
case SketchPlane::XZ: planeNormal = QVector3D(0, 0, 1); break;
|
||||||
case SketchPlane::YZ: planeNormal = QVector3D(1, 0, 0); break;
|
case SketchPlane::YZ: planeNormal = QVector3D(1, 0, 0); break;
|
||||||
@@ -754,3 +785,105 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
|
|||||||
glEnable(GL_DEPTH_TEST);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onSketchModeStarted(SketchPlane plane);
|
void onSketchModeStarted(SketchPlane plane);
|
||||||
void onSketchModeEnded();
|
void onSketchModeEnded();
|
||||||
|
void onPlaneSelectionModeStarted();
|
||||||
void onActiveToolChanged(int tool);
|
void onActiveToolChanged(int tool);
|
||||||
|
|
||||||
float xRotation() const;
|
float xRotation() const;
|
||||||
@@ -62,6 +63,7 @@ public slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void lineAdded(const gp_Pnt& start, const gp_Pnt& end);
|
void lineAdded(const gp_Pnt& start, const gp_Pnt& end);
|
||||||
|
void planeSelected(SketchPlane plane);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initializeGL() override;
|
void initializeGL() override;
|
||||||
@@ -75,9 +77,11 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport);
|
QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport);
|
||||||
QVector3D unproject(const QPoint& screenPos);
|
QVector3D unproject(const QPoint& screenPos, SketchPlane plane);
|
||||||
void drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection);
|
void drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection);
|
||||||
void drawSketch(const SketchFeature* sketch);
|
void drawSketch(const SketchFeature* sketch);
|
||||||
|
void drawSelectionPlanes();
|
||||||
|
ViewportWidget::SketchPlane checkPlaneSelection(const QPoint& screenPos);
|
||||||
|
|
||||||
QMatrix4x4 projection;
|
QMatrix4x4 projection;
|
||||||
ViewCube* m_viewCube;
|
ViewCube* m_viewCube;
|
||||||
@@ -86,6 +90,9 @@ private:
|
|||||||
Document* m_document = nullptr;
|
Document* m_document = nullptr;
|
||||||
SketchPlane m_currentPlane = SketchPlane::NONE;
|
SketchPlane m_currentPlane = SketchPlane::NONE;
|
||||||
|
|
||||||
|
bool m_isSelectingPlane = false;
|
||||||
|
SketchPlane m_highlightedPlane = SketchPlane::NONE;
|
||||||
|
|
||||||
int m_activeTool = 0;
|
int m_activeTool = 0;
|
||||||
bool m_isDefiningLine = false;
|
bool m_isDefiningLine = false;
|
||||||
gp_Pnt m_firstLinePoint;
|
gp_Pnt m_firstLinePoint;
|
||||||
|
|||||||
Reference in New Issue
Block a user