refactor: Use gp_Ax2 for all sketch plane calculations in CircleTool

Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
2026-03-03 15:58:17 -07:00
parent 228540e095
commit e363595eb5
+125 -131
View File
@@ -15,6 +15,10 @@
#include <QOpenGLShaderProgram> #include <QOpenGLShaderProgram>
#include <cmath> #include <cmath>
#include <QtMath> #include <QtMath>
#include <optional>
#include <gp_Ax2.hxx>
#include <gp_Pnt2d.hxx>
#include <ElCLib.hxx>
CircleTool::CircleTool(ViewportWidget* viewport) CircleTool::CircleTool(ViewportWidget* viewport)
: SketchTool(viewport) : SketchTool(viewport)
@@ -35,14 +39,18 @@ void CircleTool::activate()
void CircleTool::mousePressEvent(QMouseEvent *event) void CircleTool::mousePressEvent(QMouseEvent *event)
{ {
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
gp_Pnt p; gp_Pnt p;
if (!m_isDefining) { if (!m_isDefining) {
if (m_viewport->isSnappingOrigin()) { if (m_viewport->isSnappingOrigin()) {
p.SetCoord(0, 0, 0); p = plane.Location();
} else if (m_viewport->isSnappingVertex()) { } else if (m_viewport->isSnappingVertex()) {
p = m_viewport->snapVertex(); p = m_viewport->snapVertex();
} else { } else {
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane()); QVector3D worldPos = m_viewport->unproject(event->pos(), plane);
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z()); p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
} }
m_centerPoint = p; m_centerPoint = p;
@@ -70,24 +78,22 @@ void CircleTool::mousePressEvent(QMouseEvent *event)
} }
if (diameterFromInput) { if (diameterFromInput) {
QVector3D mousePos = m_viewport->unproject(event->pos(), m_viewport->currentPlane()); QVector3D mousePos = m_viewport->unproject(event->pos(), plane);
QVector3D mouseDir = mousePos - centerPos; QVector3D mouseDir = mousePos - centerPos;
if (mouseDir.lengthSquared() < 1e-9) { if (mouseDir.lengthSquared() < 1e-9) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { const auto& xDir = plane.XDirection();
mouseDir = QVector3D(1, 0, 0); mouseDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
} else { // YZ
mouseDir = QVector3D(0, 1, 0);
}
} }
double radius = inputDiameter / 2.0; double radius = inputDiameter / 2.0;
worldPos = centerPos + mouseDir.normalized() * radius; worldPos = centerPos + mouseDir.normalized() * radius;
} else { } else {
if (m_viewport->isSnappingOrigin()) { if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0); const auto& origin = plane.Location();
worldPos.setX(origin.X()); worldPos.setY(origin.Y()); worldPos.setZ(origin.Z());
} else if (m_viewport->isSnappingVertex()) { } else if (m_viewport->isSnappingVertex()) {
worldPos = QVector3D(m_viewport->snapVertex().X(), m_viewport->snapVertex().Y(), m_viewport->snapVertex().Z()); worldPos = QVector3D(m_viewport->snapVertex().X(), m_viewport->snapVertex().Y(), m_viewport->snapVertex().Z());
} else { } else {
worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane()); worldPos = m_viewport->unproject(event->pos(), plane);
} }
} }
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z()); p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
@@ -103,6 +109,10 @@ void CircleTool::mouseMoveEvent(QMouseEvent *event)
void CircleTool::finalizeCreation() void CircleTool::finalizeCreation()
{ {
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector3D worldPos; QVector3D worldPos;
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z()); QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
@@ -117,19 +127,16 @@ void CircleTool::finalizeCreation()
} }
if (diameterFromInput) { if (diameterFromInput) {
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D mouseDir = mousePos - centerPos; QVector3D mouseDir = mousePos - centerPos;
if (mouseDir.lengthSquared() < 1e-9) { if (mouseDir.lengthSquared() < 1e-9) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { const auto& xDir = plane.XDirection();
mouseDir = QVector3D(1, 0, 0); mouseDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
} else { // YZ
mouseDir = QVector3D(0, 1, 0);
}
} }
double radius = inputDiameter / 2.0; double radius = inputDiameter / 2.0;
worldPos = centerPos + mouseDir.normalized() * radius; worldPos = centerPos + mouseDir.normalized() * radius;
} else { } else {
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); worldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
} }
gp_Pnt p; gp_Pnt p;
@@ -141,134 +148,121 @@ void CircleTool::finalizeCreation()
void CircleTool::paintGL() void CircleTool::paintGL()
{ {
if (m_isDefining) { auto currentPlaneOpt = m_viewport->currentPlane();
QVector<GLfloat> vertices; if (!m_isDefining || !currentPlaneOpt) return;
QVector3D worldPos; const auto& plane = currentPlaneOpt.value();
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
QString diameterInput = m_viewport->property("diameterInput").toString(); QVector<GLfloat> vertices;
bool diameterFromInput = false; QVector3D worldPos;
double inputDiameter = 0; QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
if (!diameterInput.isEmpty()) { QString diameterInput = m_viewport->property("diameterInput").toString();
bool ok; bool diameterFromInput = false;
inputDiameter = diameterInput.toDouble(&ok); double inputDiameter = 0;
if (ok) diameterFromInput = true;
}
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); if (!diameterInput.isEmpty()) {
bool ok;
double radius; inputDiameter = diameterInput.toDouble(&ok);
if (diameterFromInput) { if (ok) diameterFromInput = true;
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);
} }
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
double radius;
if (diameterFromInput) {
radius = inputDiameter / 2.0;
} else {
worldPos = mousePos;
if (m_viewport->isSnappingOrigin()) {
const auto& origin = plane.Location();
worldPos.setX(origin.X()); worldPos.setY(origin.Y()); worldPos.setZ(origin.Z());
} 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;
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D u_axis(xDir.X(), xDir.Y(), xDir.Z());
QVector3D v_axis(yDir.X(), yDir.Y(), yDir.Z());
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 = centerPos + radius * (qCos(angle1) * u_axis + qSin(angle1) * v_axis);
QVector3D p2 = centerPos + radius * (qCos(angle2) * u_axis + qSin(angle2) * v_axis);
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) void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
{ {
if (m_isDefining) { auto currentPlaneOpt = m_viewport->currentPlane();
QVector3D worldPos; if (!m_isDefining || !currentPlaneOpt) return;
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z()); const auto& plane = currentPlaneOpt.value();
QString diameterInput = m_viewport->property("diameterInput").toString(); QVector3D worldPos;
bool diameterFromInput = false; QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
double inputDiameter = 0;
if (!diameterInput.isEmpty()) { QString diameterInput = m_viewport->property("diameterInput").toString();
bool ok; bool diameterFromInput = false;
inputDiameter = diameterInput.toDouble(&ok); double inputDiameter = 0;
if (ok) diameterFromInput = true;
if (!diameterInput.isEmpty()) {
bool ok;
inputDiameter = diameterInput.toDouble(&ok);
if (ok) diameterFromInput = true;
}
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D edgePos;
double diameter;
if (diameterFromInput) {
diameter = inputDiameter;
QVector3D mouseDir = mousePos - centerPos;
if (mouseDir.lengthSquared() < 1e-9) {
const auto& xDir = plane.XDirection();
mouseDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
} }
edgePos = centerPos + mouseDir.normalized() * (diameter / 2.0);
} else {
edgePos = mousePos;
if (m_viewport->isSnappingOrigin()) {
const auto& origin = plane.Location();
edgePos.setX(origin.X()); edgePos.setY(origin.Y()); edgePos.setZ(origin.Z());
} 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;
}
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane()); painter.setRenderHint(QPainter::Antialiasing);
QVector3D edgePos; QFontMetrics fm(painter.font());
double diameter;
if (diameterFromInput) { // Diameter dimension
diameter = inputDiameter; QVector3D diameterTextPos3D = (centerPos + edgePos) / 2.0f;
QVector3D mouseDir = mousePos - centerPos; QVector3D screenPosD = m_viewport->project(diameterTextPos3D, modelView, projection, m_viewport->rect());
if (mouseDir.lengthSquared() < 1e-9) { if (screenPosD.z() < 1.0f) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { QString diameterText = diameterFromInput ? diameterInput : QString::number(diameter, 'f', 2);
mouseDir = QVector3D(1, 0, 0); QRect textRect = fm.boundingRect(diameterText + "__");
} else { // YZ textRect.moveCenter(screenPosD.toPoint());
mouseDir = QVector3D(0, 1, 0); if (m_viewport->property("dimensionEditMode").toString() == "diameter") {
} painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
}
edgePos = centerPos + mouseDir.normalized() * (diameter / 2.0);
} else { } else {
edgePos = mousePos; painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
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);
} }
painter.setPen(Qt::white);
painter.drawText(textRect, Qt::AlignCenter, diameterText);
} }
} }