feat: Implement CircleTool with diameter input and preview
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
#include "CircleTool.h"
|
||||
#include "ViewportWidget.h"
|
||||
#include "Camera.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QVector>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <cmath>
|
||||
#include <QtMath>
|
||||
|
||||
CircleTool::CircleTool(ViewportWidget* viewport)
|
||||
: SketchTool(viewport)
|
||||
{
|
||||
}
|
||||
|
||||
void CircleTool::activate()
|
||||
{
|
||||
SketchTool::activate();
|
||||
m_dimensionModes << "diameter";
|
||||
m_dimensionPropertyNames["diameter"] = "diameterInput";
|
||||
|
||||
m_viewport->setProperty("diameterInput", "");
|
||||
m_viewport->setProperty("dimensionEditMode", "diameter");
|
||||
}
|
||||
|
||||
void CircleTool::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
gp_Pnt p;
|
||||
if (!m_isDefining) {
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
p.SetCoord(0, 0, 0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
p = m_viewport->snapVertex();
|
||||
} else {
|
||||
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
}
|
||||
m_centerPoint = p;
|
||||
m_isDefining = true;
|
||||
m_viewport->setProperty("diameterInput", "");
|
||||
m_viewport->setProperty("dimensionEditMode", "diameter");
|
||||
} else {
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
if (diameterFromInput) {
|
||||
QVector3D mousePos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
QVector3D mouseDir = mousePos - centerPos;
|
||||
if (mouseDir.lengthSquared() < 1e-9) {
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
mouseDir = QVector3D(1, 0, 0);
|
||||
} else { // YZ
|
||||
mouseDir = QVector3D(0, 1, 0);
|
||||
}
|
||||
}
|
||||
double radius = inputDiameter / 2.0;
|
||||
worldPos = centerPos + mouseDir.normalized() * radius;
|
||||
} else {
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
worldPos = QVector3D(m_viewport->snapVertex().X(), m_viewport->snapVertex().Y(), m_viewport->snapVertex().Z());
|
||||
} else {
|
||||
worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
}
|
||||
}
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
emit m_viewport->circleAdded(m_centerPoint, p);
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void CircleTool::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
// To be implemented
|
||||
}
|
||||
|
||||
void CircleTool::finalizeCreation()
|
||||
{
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
if (diameterFromInput) {
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D mouseDir = mousePos - centerPos;
|
||||
if (mouseDir.lengthSquared() < 1e-9) {
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
mouseDir = QVector3D(1, 0, 0);
|
||||
} else { // YZ
|
||||
mouseDir = QVector3D(0, 1, 0);
|
||||
}
|
||||
}
|
||||
double radius = inputDiameter / 2.0;
|
||||
worldPos = centerPos + mouseDir.normalized() * radius;
|
||||
} else {
|
||||
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
}
|
||||
|
||||
gp_Pnt p;
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
|
||||
emit m_viewport->circleAdded(m_centerPoint, p);
|
||||
deactivate();
|
||||
}
|
||||
|
||||
void CircleTool::paintGL()
|
||||
{
|
||||
if (m_isDefining) {
|
||||
QVector<GLfloat> vertices;
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
|
||||
double radius;
|
||||
if (diameterFromInput) {
|
||||
radius = inputDiameter / 2.0;
|
||||
} else {
|
||||
worldPos = mousePos;
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
|
||||
}
|
||||
radius = (worldPos - centerPos).length();
|
||||
}
|
||||
|
||||
const int segments = 64;
|
||||
for (int i = 0; i < segments; ++i) {
|
||||
double angle1 = i * 2.0 * M_PI / segments;
|
||||
double angle2 = (i + 1) * 2.0 * M_PI / segments;
|
||||
|
||||
QVector3D p1, p2;
|
||||
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
p1.setX(centerPos.x() + radius * qCos(angle1));
|
||||
p1.setY(centerPos.y() + radius * qSin(angle1));
|
||||
p1.setZ(centerPos.z());
|
||||
p2.setX(centerPos.x() + radius * qCos(angle2));
|
||||
p2.setY(centerPos.y() + radius * qSin(angle2));
|
||||
p2.setZ(centerPos.z());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
p1.setX(centerPos.x() + radius * qCos(angle1));
|
||||
p1.setY(centerPos.y());
|
||||
p1.setZ(centerPos.z() + radius * qSin(angle1));
|
||||
p2.setX(centerPos.x() + radius * qCos(angle2));
|
||||
p2.setY(centerPos.y());
|
||||
p2.setZ(centerPos.z() + radius * qSin(angle2));
|
||||
} else { // YZ
|
||||
p1.setX(centerPos.x());
|
||||
p1.setY(centerPos.y() + radius * qCos(angle1));
|
||||
p1.setZ(centerPos.z() + radius * qSin(angle1));
|
||||
p2.setX(centerPos.x());
|
||||
p2.setY(centerPos.y() + radius * qCos(angle2));
|
||||
p2.setZ(centerPos.z() + radius * qSin(angle2));
|
||||
}
|
||||
vertices << p1.x() << p1.y() << p1.z();
|
||||
vertices << p2.x() << p2.y() << p2.z();
|
||||
}
|
||||
|
||||
m_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(1.0f, 1.0f, 0.0f, 1.0f));
|
||||
m_viewport->vbo().bind();
|
||||
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, segments * 2);
|
||||
}
|
||||
}
|
||||
|
||||
void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
|
||||
{
|
||||
if (m_isDefining) {
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D edgePos;
|
||||
double diameter;
|
||||
|
||||
if (diameterFromInput) {
|
||||
diameter = inputDiameter;
|
||||
QVector3D mouseDir = mousePos - centerPos;
|
||||
if (mouseDir.lengthSquared() < 1e-9) {
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
mouseDir = QVector3D(1, 0, 0);
|
||||
} else { // YZ
|
||||
mouseDir = QVector3D(0, 1, 0);
|
||||
}
|
||||
}
|
||||
edgePos = centerPos + mouseDir.normalized() * (diameter / 2.0);
|
||||
} else {
|
||||
edgePos = mousePos;
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
edgePos.setX(0); edgePos.setY(0); edgePos.setZ(0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
edgePos.setX(m_viewport->snapVertex().X()); edgePos.setY(m_viewport->snapVertex().Y()); edgePos.setZ(m_viewport->snapVertex().Z());
|
||||
}
|
||||
diameter = (edgePos - centerPos).length() * 2.0;
|
||||
}
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QFontMetrics fm(painter.font());
|
||||
|
||||
// Diameter dimension
|
||||
QVector3D diameterTextPos3D = (centerPos + edgePos) / 2.0f;
|
||||
QVector3D screenPosD = m_viewport->project(diameterTextPos3D, modelView, projection, m_viewport->rect());
|
||||
if (screenPosD.z() < 1.0f) {
|
||||
QString diameterText = diameterFromInput ? diameterInput : QString::number(diameter, 'f', 2);
|
||||
QRect textRect = fm.boundingRect(diameterText + "__");
|
||||
textRect.moveCenter(screenPosD.toPoint());
|
||||
if (m_viewport->property("dimensionEditMode").toString() == "diameter") {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
|
||||
} else {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||
}
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(textRect, Qt::AlignCenter, diameterText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef CIRCLETOOL_H
|
||||
#define CIRCLETOOL_H
|
||||
|
||||
#include "SketchTool.h"
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
class CircleTool : public SketchTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CircleTool(ViewportWidget* viewport);
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
void paintGL() override;
|
||||
void paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) override;
|
||||
|
||||
void activate() override;
|
||||
|
||||
protected:
|
||||
void finalizeCreation() override;
|
||||
|
||||
private:
|
||||
gp_Pnt m_centerPoint;
|
||||
};
|
||||
|
||||
#endif // CIRCLETOOL_H
|
||||
|
||||
Reference in New Issue
Block a user