feat: Add sketch creation with plane selection, oriented grid and labeled axes
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -15,6 +15,7 @@ add_executable(OpenCAD
|
|||||||
src/MainWindow.cpp
|
src/MainWindow.cpp
|
||||||
src/ViewportWidget.cpp
|
src/ViewportWidget.cpp
|
||||||
src/ViewCube.cpp
|
src/ViewCube.cpp
|
||||||
|
src/SketchGrid.cpp
|
||||||
resources.qrc
|
resources.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
: QMainWindow(parent)
|
: QMainWindow(parent)
|
||||||
@@ -34,6 +36,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
createSketchButton->setIcon(QIcon(":/icons/create-sketch.svg"));
|
createSketchButton->setIcon(QIcon(":/icons/create-sketch.svg"));
|
||||||
createSketchButton->setIconSize(QSize(48, 48));
|
createSketchButton->setIconSize(QSize(48, 48));
|
||||||
createSketchButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
|
createSketchButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
|
||||||
|
connect(createSketchButton, &QToolButton::clicked, this, &MainWindow::createSketch);
|
||||||
solidLayout->addWidget(createSketchButton);
|
solidLayout->addWidget(createSketchButton);
|
||||||
|
|
||||||
QToolButton *extrudeButton = new QToolButton();
|
QToolButton *extrudeButton = new QToolButton();
|
||||||
@@ -53,6 +56,25 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
QWidget *toolsTab = new QWidget();
|
QWidget *toolsTab = new QWidget();
|
||||||
tabWidget->addTab(toolsTab, "TOOLS");
|
tabWidget->addTab(toolsTab, "TOOLS");
|
||||||
|
|
||||||
ViewportWidget *viewport = new ViewportWidget;
|
m_viewport = new ViewportWidget;
|
||||||
setCentralWidget(viewport);
|
setCentralWidget(m_viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::createSketch()
|
||||||
|
{
|
||||||
|
QStringList items;
|
||||||
|
items << "XY-Plane" << "XZ-Plane" << "YZ-Plane";
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
QString item = QInputDialog::getItem(this, "Select Sketch Plane",
|
||||||
|
"Plane:", items, 0, false, &ok);
|
||||||
|
if (ok && !item.isEmpty()) {
|
||||||
|
if (item == "XY-Plane") {
|
||||||
|
m_viewport->startSketch(ViewportWidget::SketchPlane::XY);
|
||||||
|
} else if (item == "XZ-Plane") {
|
||||||
|
m_viewport->startSketch(ViewportWidget::SketchPlane::XZ);
|
||||||
|
} else if (item == "YZ-Plane") {
|
||||||
|
m_viewport->startSketch(ViewportWidget::SketchPlane::YZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,20 @@
|
|||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
class ViewportWidget;
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void createSketch();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ViewportWidget *m_viewport;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
112
src/SketchGrid.cpp
Normal file
112
src/SketchGrid.cpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#include "SketchGrid.h"
|
||||||
|
|
||||||
|
SketchGrid::SketchGrid()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SketchGrid::~SketchGrid()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SketchGrid::initializeGL()
|
||||||
|
{
|
||||||
|
initializeOpenGLFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SketchGrid::paintGL(SketchPlane plane, const QMatrix4x4& projection, const QMatrix4x4& modelview)
|
||||||
|
{
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadMatrixf(projection.constData());
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadMatrixf(modelview.constData());
|
||||||
|
|
||||||
|
glPushAttrib(GL_LINE_BIT | GL_POINT_BIT | GL_CURRENT_BIT);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
drawGridLines(plane);
|
||||||
|
drawAxes(plane);
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glPopAttrib();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SketchGrid::drawGridLines(SketchPlane plane)
|
||||||
|
{
|
||||||
|
const int gridSize = 50;
|
||||||
|
const float darkFactor = 0.8f;
|
||||||
|
|
||||||
|
// Lighter lines
|
||||||
|
glColor3f(0.5f, 0.5f, 0.5f);
|
||||||
|
glLineWidth(1.0f);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
for (int i = -gridSize; i <= gridSize; ++i)
|
||||||
|
{
|
||||||
|
if (i == 0 || i % 5 == 0) continue;
|
||||||
|
if (plane == XY) {
|
||||||
|
glVertex3f(i, -gridSize, 0); glVertex3f(i, gridSize, 0);
|
||||||
|
glVertex3f(-gridSize, i, 0); glVertex3f(gridSize, i, 0);
|
||||||
|
} else if (plane == XZ) {
|
||||||
|
glVertex3f(i, 0, -gridSize); glVertex3f(i, 0, gridSize);
|
||||||
|
glVertex3f(-gridSize, 0, i); glVertex3f(gridSize, 0, i);
|
||||||
|
} else { // YZ
|
||||||
|
glVertex3f(0, i, -gridSize); glVertex3f(0, i, gridSize);
|
||||||
|
glVertex3f(0, -gridSize, i); glVertex3f(0, gridSize, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
// Darker lines
|
||||||
|
glColor3f(0.5f * darkFactor, 0.5f * darkFactor, 0.5f * darkFactor);
|
||||||
|
glLineWidth(1.5f);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
for (int i = -gridSize; i <= gridSize; i += 5)
|
||||||
|
{
|
||||||
|
if (i == 0) continue;
|
||||||
|
if (plane == XY) {
|
||||||
|
glVertex3f(i, -gridSize, 0); glVertex3f(i, gridSize, 0);
|
||||||
|
glVertex3f(-gridSize, i, 0); glVertex3f(gridSize, i, 0);
|
||||||
|
} else if (plane == XZ) {
|
||||||
|
glVertex3f(i, 0, -gridSize); glVertex3f(i, 0, gridSize);
|
||||||
|
glVertex3f(-gridSize, 0, i); glVertex3f(gridSize, 0, i);
|
||||||
|
} else { // YZ
|
||||||
|
glVertex3f(0, i, -gridSize); glVertex3f(0, i, gridSize);
|
||||||
|
glVertex3f(0, -gridSize, i); glVertex3f(0, gridSize, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SketchGrid::drawAxes(SketchPlane plane)
|
||||||
|
{
|
||||||
|
const int axisLength = 50;
|
||||||
|
|
||||||
|
glLineWidth(2.0f);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
|
||||||
|
// X Axis (Red)
|
||||||
|
if (plane == XY || plane == XZ) {
|
||||||
|
glColor3f(1.0f, 0.0f, 0.0f);
|
||||||
|
glVertex3f(-axisLength, 0, 0);
|
||||||
|
glVertex3f(axisLength, 0, 0);
|
||||||
|
}
|
||||||
|
// Y Axis (Green)
|
||||||
|
if (plane == XY || plane == YZ) {
|
||||||
|
glColor3f(0.0f, 1.0f, 0.0f);
|
||||||
|
glVertex3f(0, -axisLength, 0);
|
||||||
|
glVertex3f(0, axisLength, 0);
|
||||||
|
}
|
||||||
|
// Z Axis (Blue)
|
||||||
|
if (plane == XZ || plane == YZ) {
|
||||||
|
glColor3f(0.0f, 0.0f, 1.0f);
|
||||||
|
glVertex3f(0, 0, -axisLength);
|
||||||
|
glVertex3f(0, 0, axisLength);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
// Origin dot
|
||||||
|
glPointSize(5.0f);
|
||||||
|
glColor3f(1.0f, 1.0f, 1.0f); // White
|
||||||
|
glBegin(GL_POINTS);
|
||||||
|
glVertex3f(0, 0, 0);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
27
src/SketchGrid.h
Normal file
27
src/SketchGrid.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef SKETCHGRID_H
|
||||||
|
#define SKETCHGRID_H
|
||||||
|
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QMatrix4x4>
|
||||||
|
|
||||||
|
class SketchGrid : protected QOpenGLFunctions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum SketchPlane {
|
||||||
|
XY = 1,
|
||||||
|
XZ = 2,
|
||||||
|
YZ = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
SketchGrid();
|
||||||
|
~SketchGrid();
|
||||||
|
|
||||||
|
void initializeGL();
|
||||||
|
void paintGL(SketchPlane plane, const QMatrix4x4& projection, const QMatrix4x4& modelview);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void drawGridLines(SketchPlane plane);
|
||||||
|
void drawAxes(SketchPlane plane);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SKETCHGRID_H
|
||||||
@@ -1,18 +1,24 @@
|
|||||||
#include "ViewportWidget.h"
|
#include "ViewportWidget.h"
|
||||||
#include "ViewCube.h"
|
#include "ViewCube.h"
|
||||||
|
#include "SketchGrid.h"
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QWheelEvent>
|
#include <QWheelEvent>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QWheelEvent>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
ViewportWidget::ViewportWidget(QWidget *parent)
|
ViewportWidget::ViewportWidget(QWidget *parent)
|
||||||
: QOpenGLWidget(parent)
|
: QOpenGLWidget(parent)
|
||||||
{
|
{
|
||||||
m_viewCube = new ViewCube();
|
m_viewCube = new ViewCube();
|
||||||
|
m_sketchGrid = new SketchGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewportWidget::~ViewportWidget()
|
ViewportWidget::~ViewportWidget()
|
||||||
{
|
{
|
||||||
delete m_viewCube;
|
delete m_viewCube;
|
||||||
|
delete m_sketchGrid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewportWidget::initializeGL()
|
void ViewportWidget::initializeGL()
|
||||||
@@ -21,6 +27,7 @@ void ViewportWidget::initializeGL()
|
|||||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
m_viewCube->initializeGL();
|
m_viewCube->initializeGL();
|
||||||
|
m_sketchGrid->initializeGL();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewportWidget::paintGL()
|
void ViewportWidget::paintGL()
|
||||||
@@ -42,6 +49,10 @@ void ViewportWidget::paintGL()
|
|||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadMatrixf(model.constData());
|
glLoadMatrixf(model.constData());
|
||||||
|
|
||||||
|
if (m_currentPlane != SketchPlane::NONE) {
|
||||||
|
m_sketchGrid->paintGL(static_cast<SketchGrid::SketchPlane>(m_currentPlane), projection, model);
|
||||||
|
}
|
||||||
|
|
||||||
// View cube rendering
|
// View cube rendering
|
||||||
QMatrix4x4 viewCubeModel;
|
QMatrix4x4 viewCubeModel;
|
||||||
viewCubeModel.rotate(xRot / 16.0f, 1, 0, 0);
|
viewCubeModel.rotate(xRot / 16.0f, 1, 0, 0);
|
||||||
@@ -49,6 +60,12 @@ void ViewportWidget::paintGL()
|
|||||||
m_viewCube->paintGL(viewCubeModel, width(), height());
|
m_viewCube->paintGL(viewCubeModel, width(), height());
|
||||||
|
|
||||||
glViewport(0, 0, width(), height());
|
glViewport(0, 0, width(), height());
|
||||||
|
|
||||||
|
if (m_currentPlane != SketchPlane::NONE) {
|
||||||
|
QPainter painter(this);
|
||||||
|
drawAxisLabels(painter, model, projection);
|
||||||
|
painter.end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewportWidget::resizeGL(int w, int h)
|
void ViewportWidget::resizeGL(int w, int h)
|
||||||
@@ -90,3 +107,77 @@ void ViewportWidget::wheelEvent(QWheelEvent *event)
|
|||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewportWidget::startSketch(SketchPlane plane)
|
||||||
|
{
|
||||||
|
m_currentPlane = plane;
|
||||||
|
panX = 0;
|
||||||
|
panY = 0;
|
||||||
|
zoom = -20.0f; // Zoom out to see the grid
|
||||||
|
|
||||||
|
switch (plane) {
|
||||||
|
case SketchPlane::XY: // Top view
|
||||||
|
xRot = -90 * 16;
|
||||||
|
yRot = 0;
|
||||||
|
break;
|
||||||
|
case SketchPlane::XZ: // Front view
|
||||||
|
xRot = 0;
|
||||||
|
yRot = 0;
|
||||||
|
break;
|
||||||
|
case SketchPlane::YZ: // Right view
|
||||||
|
xRot = 0;
|
||||||
|
yRot = 90 * 16;
|
||||||
|
break;
|
||||||
|
case SketchPlane::NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector3D ViewportWidget::project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport)
|
||||||
|
{
|
||||||
|
QVector4D clipCoord = projection * modelView * QVector4D(worldCoord, 1.0);
|
||||||
|
if (qFuzzyCompare(clipCoord.w(), 0.0f)) {
|
||||||
|
return QVector3D(-1, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector3D ndc(clipCoord.x() / clipCoord.w(), clipCoord.y() / clipCoord.w(), clipCoord.z() / clipCoord.w());
|
||||||
|
|
||||||
|
float winX = viewport.x() + viewport.width() * (ndc.x() + 1.0) / 2.0;
|
||||||
|
float winY = viewport.y() + viewport.height() * (1.0 - (ndc.y() + 1.0) / 2.0); // Y is inverted
|
||||||
|
|
||||||
|
return QVector3D(winX, winY, (ndc.z() + 1.0) / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewportWidget::drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
|
||||||
|
{
|
||||||
|
painter.setPen(Qt::white);
|
||||||
|
painter.setFont(QFont("Arial", 10));
|
||||||
|
|
||||||
|
const int range = 50;
|
||||||
|
const int step = 5;
|
||||||
|
|
||||||
|
auto drawLabelsForAxis = [&](int axis_idx) {
|
||||||
|
for (int i = -range; i <= range; i += step) {
|
||||||
|
if (i == 0) continue;
|
||||||
|
QVector3D worldCoord;
|
||||||
|
worldCoord[axis_idx] = i;
|
||||||
|
|
||||||
|
QVector3D screenPos = project(worldCoord, modelView, projection, rect());
|
||||||
|
if (screenPos.z() < 1.0f) { // Not clipped
|
||||||
|
painter.drawText(screenPos.toPoint(), QString::number(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_currentPlane == SketchPlane::XY) {
|
||||||
|
drawLabelsForAxis(0); // X
|
||||||
|
drawLabelsForAxis(1); // Y
|
||||||
|
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||||
|
drawLabelsForAxis(0); // X
|
||||||
|
drawLabelsForAxis(2); // Z
|
||||||
|
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||||
|
drawLabelsForAxis(1); // Y
|
||||||
|
drawLabelsForAxis(2); // Z
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,17 +5,29 @@
|
|||||||
#include <QOpenGLFunctions>
|
#include <QOpenGLFunctions>
|
||||||
#include <QMatrix4x4>
|
#include <QMatrix4x4>
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
|
#include <QVector3D>
|
||||||
|
#include <QRect>
|
||||||
|
|
||||||
class ViewCube;
|
class ViewCube;
|
||||||
|
class SketchGrid;
|
||||||
|
|
||||||
class ViewportWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
class ViewportWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum class SketchPlane {
|
||||||
|
NONE,
|
||||||
|
XY,
|
||||||
|
XZ,
|
||||||
|
YZ
|
||||||
|
};
|
||||||
|
|
||||||
explicit ViewportWidget(QWidget *parent = nullptr);
|
explicit ViewportWidget(QWidget *parent = nullptr);
|
||||||
~ViewportWidget();
|
~ViewportWidget();
|
||||||
|
|
||||||
|
void startSketch(SketchPlane plane);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initializeGL() override;
|
void initializeGL() override;
|
||||||
void paintGL() override;
|
void paintGL() override;
|
||||||
@@ -26,8 +38,13 @@ protected:
|
|||||||
void wheelEvent(QWheelEvent *event) override;
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport);
|
||||||
|
void drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection);
|
||||||
|
|
||||||
QMatrix4x4 projection;
|
QMatrix4x4 projection;
|
||||||
ViewCube* m_viewCube;
|
ViewCube* m_viewCube;
|
||||||
|
SketchGrid* m_sketchGrid = nullptr;
|
||||||
|
SketchPlane m_currentPlane = SketchPlane::NONE;
|
||||||
|
|
||||||
float xRot = 0;
|
float xRot = 0;
|
||||||
float yRot = 0;
|
float yRot = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user