refactor: Implement Rectangle tool with snapping and dimensions
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -38,6 +38,7 @@ add_executable(OpenCAD
|
|||||||
src/Feature.cpp
|
src/Feature.cpp
|
||||||
src/SketchFeature.cpp
|
src/SketchFeature.cpp
|
||||||
src/SketchLine.cpp
|
src/SketchLine.cpp
|
||||||
|
src/SketchRectangle.cpp
|
||||||
src/FeatureBrowser.cpp
|
src/FeatureBrowser.cpp
|
||||||
src/ApplicationController.cpp
|
src/ApplicationController.cpp
|
||||||
src/Camera.cpp
|
src/Camera.cpp
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "Document.h"
|
#include "Document.h"
|
||||||
#include "SketchFeature.h"
|
#include "SketchFeature.h"
|
||||||
#include "SketchLine.h"
|
#include "SketchLine.h"
|
||||||
|
#include "SketchRectangle.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
|
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
@@ -140,6 +141,13 @@ void ApplicationController::addLine(const gp_Pnt& start, const gp_Pnt& end)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApplicationController::addRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2)
|
||||||
|
{
|
||||||
|
if (m_activeSketch) {
|
||||||
|
m_activeSketch->addObject(new SketchRectangle(corner1, corner2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ApplicationController::endSketch()
|
void ApplicationController::endSketch()
|
||||||
{
|
{
|
||||||
m_activeSketch = nullptr;
|
m_activeSketch = nullptr;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void setActiveTool(ToolType tool);
|
void setActiveTool(ToolType tool);
|
||||||
void addLine(const gp_Pnt& start, const gp_Pnt& end);
|
void addLine(const gp_Pnt& start, const gp_Pnt& end);
|
||||||
|
void addRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2);
|
||||||
void newDocument();
|
void newDocument();
|
||||||
bool openDocument();
|
bool openDocument();
|
||||||
bool saveDocument();
|
bool saveDocument();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "SketchFeature.h"
|
#include "SketchFeature.h"
|
||||||
#include "SketchObject.h"
|
#include "SketchObject.h"
|
||||||
#include "SketchLine.h"
|
#include "SketchLine.h"
|
||||||
|
#include "SketchRectangle.h"
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
|
||||||
@@ -66,6 +67,10 @@ void SketchFeature::read(const QJsonObject& json)
|
|||||||
auto line = new SketchLine();
|
auto line = new SketchLine();
|
||||||
line->read(objectJson);
|
line->read(objectJson);
|
||||||
m_objects.append(line);
|
m_objects.append(line);
|
||||||
|
} else if (type == "Rectangle") {
|
||||||
|
auto rect = new SketchRectangle();
|
||||||
|
rect->read(objectJson);
|
||||||
|
m_objects.append(rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ class SketchObject
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class ObjectType {
|
enum class ObjectType {
|
||||||
Line
|
Line,
|
||||||
|
Rectangle
|
||||||
};
|
};
|
||||||
|
|
||||||
SketchObject() = default;
|
SketchObject() = default;
|
||||||
|
|||||||
61
src/SketchRectangle.cpp
Normal file
61
src/SketchRectangle.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include "SketchRectangle.h"
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void pointToJson(const gp_Pnt& p, QJsonArray& arr)
|
||||||
|
{
|
||||||
|
arr.append(p.X());
|
||||||
|
arr.append(p.Y());
|
||||||
|
arr.append(p.Z());
|
||||||
|
}
|
||||||
|
|
||||||
|
gp_Pnt jsonToPoint(const QJsonArray& arr)
|
||||||
|
{
|
||||||
|
return gp_Pnt(arr[0].toDouble(), arr[1].toDouble(), arr[2].toDouble());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SketchRectangle::SketchRectangle()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SketchRectangle::SketchRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2)
|
||||||
|
: m_corner1(corner1), m_corner2(corner2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SketchObject::ObjectType SketchRectangle::type() const
|
||||||
|
{
|
||||||
|
return ObjectType::Rectangle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SketchRectangle::read(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
if (json.contains("corner1") && json["corner1"].isArray()) {
|
||||||
|
m_corner1 = jsonToPoint(json["corner1"].toArray());
|
||||||
|
}
|
||||||
|
if (json.contains("corner2") && json["corner2"].isArray()) {
|
||||||
|
m_corner2 = jsonToPoint(json["corner2"].toArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SketchRectangle::write(QJsonObject& json) const
|
||||||
|
{
|
||||||
|
json["type"] = "Rectangle";
|
||||||
|
QJsonArray c1, c2;
|
||||||
|
pointToJson(m_corner1, c1);
|
||||||
|
pointToJson(m_corner2, c2);
|
||||||
|
json["corner1"] = c1;
|
||||||
|
json["corner2"] = c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gp_Pnt& SketchRectangle::corner1() const
|
||||||
|
{
|
||||||
|
return m_corner1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gp_Pnt& SketchRectangle::corner2() const
|
||||||
|
{
|
||||||
|
return m_corner2;
|
||||||
|
}
|
||||||
26
src/SketchRectangle.h
Normal file
26
src/SketchRectangle.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef SKETCHRECTANGLE_H
|
||||||
|
#define SKETCHRECTANGLE_H
|
||||||
|
|
||||||
|
#include "SketchObject.h"
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
|
||||||
|
class SketchRectangle : public SketchObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SketchRectangle();
|
||||||
|
SketchRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2);
|
||||||
|
|
||||||
|
ObjectType type() const override;
|
||||||
|
|
||||||
|
void read(const QJsonObject& json) override;
|
||||||
|
void write(QJsonObject& json) const override;
|
||||||
|
|
||||||
|
const gp_Pnt& corner1() const;
|
||||||
|
const gp_Pnt& corner2() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
gp_Pnt m_corner1;
|
||||||
|
gp_Pnt m_corner2;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SKETCHRECTANGLE_H
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "FeatureBrowser.h"
|
#include "FeatureBrowser.h"
|
||||||
#include "SketchFeature.h"
|
#include "SketchFeature.h"
|
||||||
#include "SketchLine.h"
|
#include "SketchLine.h"
|
||||||
|
#include "SketchRectangle.h"
|
||||||
#include "SketchObject.h"
|
#include "SketchObject.h"
|
||||||
#include "ApplicationController.h"
|
#include "ApplicationController.h"
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
@@ -196,6 +197,47 @@ void ViewportWidget::paintGL()
|
|||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_isDefiningRectangle && m_activeTool == static_cast<int>(ApplicationController::ToolType::Rectangle)) {
|
||||||
|
vertices.clear();
|
||||||
|
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||||
|
QVector3D startPos(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
|
||||||
|
|
||||||
|
if (m_isSnappingOrigin) {
|
||||||
|
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||||
|
} else if (m_isSnappingVertex) {
|
||||||
|
worldPos.setX(m_snapVertex.X()); worldPos.setY(m_snapVertex.Y()); worldPos.setZ(m_snapVertex.Z());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector3D p1 = startPos;
|
||||||
|
QVector3D p2, p3, p4;
|
||||||
|
p3 = worldPos;
|
||||||
|
|
||||||
|
if (m_currentPlane == SketchPlane::XY) {
|
||||||
|
p2.setX(p3.x()); p2.setY(p1.y()); p2.setZ(p1.z());
|
||||||
|
p4.setX(p1.x()); p4.setY(p1.y()); p4.setZ(p3.z());
|
||||||
|
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||||
|
p2.setX(p3.x()); p2.setY(p1.y()); p2.setZ(p1.z());
|
||||||
|
p4.setX(p1.x()); p4.setY(p3.y()); p4.setZ(p1.z());
|
||||||
|
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||||
|
p2.setX(p1.x()); p2.setY(p3.y()); p2.setZ(p1.z());
|
||||||
|
p4.setX(p1.x()); p4.setY(p1.y()); p4.setZ(p3.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices << p1.x() << p1.y() << p1.z();
|
||||||
|
vertices << p2.x() << p2.y() << p2.z();
|
||||||
|
vertices << p2.x() << p2.y() << p2.z();
|
||||||
|
vertices << p3.x() << p3.y() << p3.z();
|
||||||
|
vertices << p3.x() << p3.y() << p3.z();
|
||||||
|
vertices << p4.x() << p4.y() << p4.z();
|
||||||
|
vertices << p4.x() << p4.y() << p4.z();
|
||||||
|
vertices << p1.x() << p1.y() << p1.z();
|
||||||
|
|
||||||
|
m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(1.0f, 1.0f, 0.0f, 1.0f));
|
||||||
|
m_vbo.bind();
|
||||||
|
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||||
|
glDrawArrays(GL_LINES, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_isDefiningLine && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
if (m_isDefiningLine && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||||
vertices.clear();
|
vertices.clear();
|
||||||
QVector3D worldPos;
|
QVector3D worldPos;
|
||||||
@@ -544,6 +586,65 @@ void ViewportWidget::paintGL()
|
|||||||
}
|
}
|
||||||
m_featureBrowser->paint(painter, width(), height());
|
m_featureBrowser->paint(painter, width(), height());
|
||||||
|
|
||||||
|
if (m_isDefiningRectangle && m_activeTool == static_cast<int>(ApplicationController::ToolType::Rectangle)) {
|
||||||
|
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||||
|
if (m_isSnappingOrigin) {
|
||||||
|
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||||
|
} else if (m_isSnappingVertex) {
|
||||||
|
worldPos.setX(m_snapVertex.X()); worldPos.setY(m_snapVertex.Y()); worldPos.setZ(m_snapVertex.Z());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector3D p1_3d(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
|
||||||
|
QVector3D p3_3d = worldPos;
|
||||||
|
QVector3D p2_3d, p4_3d;
|
||||||
|
|
||||||
|
double w, h;
|
||||||
|
|
||||||
|
if (m_currentPlane == SketchPlane::XY) {
|
||||||
|
p2_3d.setX(p3_3d.x()); p2_3d.setY(p1_3d.y()); p2_3d.setZ(p1_3d.z());
|
||||||
|
p4_3d.setX(p1_3d.x()); p4_3d.setY(p1_3d.y()); p4_3d.setZ(p3_3d.z());
|
||||||
|
w = qAbs(p3_3d.x() - p1_3d.x());
|
||||||
|
h = qAbs(p3_3d.z() - p1_3d.z());
|
||||||
|
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||||
|
p2_3d.setX(p3_3d.x()); p2_3d.setY(p1_3d.y()); p2_3d.setZ(p1_3d.z());
|
||||||
|
p4_3d.setX(p1_3d.x()); p4_3d.setY(p3_3d.y()); p4_3d.setZ(p1_3d.z());
|
||||||
|
w = qAbs(p3_3d.x() - p1_3d.x());
|
||||||
|
h = qAbs(p3_3d.y() - p1_3d.y());
|
||||||
|
} else { // YZ
|
||||||
|
p2_3d.setX(p1_3d.x()); p2_3d.setY(p3_3d.y()); p2_3d.setZ(p1_3d.z());
|
||||||
|
p4_3d.setX(p1_3d.x()); p4_3d.setY(p1_3d.y()); p4_3d.setZ(p3_3d.z());
|
||||||
|
w = qAbs(p3_3d.y() - p1_3d.y());
|
||||||
|
h = qAbs(p3_3d.z() - p1_3d.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
QFontMetrics fm(painter.font());
|
||||||
|
|
||||||
|
// Width dimension
|
||||||
|
QVector3D widthTextPos3D = (p1_3d + p2_3d) / 2.0f;
|
||||||
|
QVector3D screenPosW = project(widthTextPos3D, model, projection, rect());
|
||||||
|
if (screenPosW.z() < 1.0f) {
|
||||||
|
QString widthText = QString::number(w, 'f', 2);
|
||||||
|
QRect textRect = fm.boundingRect(widthText + "__");
|
||||||
|
textRect.moveCenter(screenPosW.toPoint() + QPoint(0, (p3_3d.z() > p1_3d.z() || p3_3d.y() > p1_3d.y()) ? -15 : 15));
|
||||||
|
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||||
|
painter.setPen(Qt::white);
|
||||||
|
painter.drawText(textRect, Qt::AlignCenter, widthText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Height dimension
|
||||||
|
QVector3D heightTextPos3D = (p2_3d + p3_3d) / 2.0f;
|
||||||
|
QVector3D screenPosH = project(heightTextPos3D, model, projection, rect());
|
||||||
|
if (screenPosH.z() < 1.0f) {
|
||||||
|
QString heightText = QString::number(h, 'f', 2);
|
||||||
|
QRect textRect = fm.boundingRect(heightText + "__");
|
||||||
|
textRect.moveCenter(screenPosH.toPoint() + QPoint((p3_3d.x() > p1_3d.x()) ? 15 : -15, 0));
|
||||||
|
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||||
|
painter.setPen(Qt::white);
|
||||||
|
painter.drawText(textRect, Qt::AlignCenter, heightText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_isDefiningLine && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
if (m_isDefiningLine && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||||
QVector3D worldPos;
|
QVector3D worldPos;
|
||||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||||
@@ -802,6 +903,28 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast<int>(ApplicationController::ToolType::Rectangle)) {
|
||||||
|
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);
|
||||||
|
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_isDefiningRectangle) {
|
||||||
|
m_firstRectanglePoint = p;
|
||||||
|
m_isDefiningRectangle = true;
|
||||||
|
} else {
|
||||||
|
emit rectangleAdded(m_firstRectanglePoint, p);
|
||||||
|
m_isDefiningRectangle = false;
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
if (m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||||
gp_Pnt p;
|
gp_Pnt p;
|
||||||
QString dimInput = property("dimensionInput").toString();
|
QString dimInput = property("dimensionInput").toString();
|
||||||
@@ -1202,6 +1325,12 @@ void ViewportWidget::keyPressEvent(QKeyEvent *event)
|
|||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (m_isDefiningRectangle) {
|
||||||
|
m_isDefiningRectangle = false;
|
||||||
|
emit toolDeactivated();
|
||||||
|
update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_isSelectingPlane) {
|
if (m_isSelectingPlane) {
|
||||||
m_isSelectingPlane = false;
|
m_isSelectingPlane = false;
|
||||||
m_highlightedPlane = SketchPlane::NONE;
|
m_highlightedPlane = SketchPlane::NONE;
|
||||||
@@ -1394,6 +1523,7 @@ void ViewportWidget::onActiveToolChanged(int tool)
|
|||||||
{
|
{
|
||||||
m_activeTool = tool;
|
m_activeTool = tool;
|
||||||
m_isDefiningLine = false;
|
m_isDefiningLine = false;
|
||||||
|
m_isDefiningRectangle = false;
|
||||||
|
|
||||||
if (tool == static_cast<int>(ApplicationController::ToolType::None)) {
|
if (tool == static_cast<int>(ApplicationController::ToolType::None)) {
|
||||||
unsetCursor();
|
unsetCursor();
|
||||||
@@ -1499,6 +1629,36 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
|
|||||||
|
|
||||||
vertexCounts[start]++;
|
vertexCounts[start]++;
|
||||||
vertexCounts[end]++;
|
vertexCounts[end]++;
|
||||||
|
} else if (obj->type() == SketchObject::ObjectType::Rectangle) {
|
||||||
|
auto rect = static_cast<const SketchRectangle*>(obj);
|
||||||
|
const auto& p1 = rect->corner1();
|
||||||
|
const auto& p3 = rect->corner2();
|
||||||
|
|
||||||
|
gp_Pnt p2, p4;
|
||||||
|
if (sketch->plane() == SketchFeature::SketchPlane::XY) {
|
||||||
|
p2.SetCoord(p3.X(), p1.Y(), p1.Z());
|
||||||
|
p4.SetCoord(p1.X(), p1.Y(), p3.Z());
|
||||||
|
} else if (sketch->plane() == SketchFeature::SketchPlane::XZ) {
|
||||||
|
p2.SetCoord(p3.X(), p1.Y(), p1.Z());
|
||||||
|
p4.SetCoord(p1.X(), p3.Y(), p1.Z());
|
||||||
|
} else if (sketch->plane() == SketchFeature::SketchPlane::YZ) {
|
||||||
|
p2.SetCoord(p1.X(), p3.Y(), p1.Z());
|
||||||
|
p4.SetCoord(p1.X(), p1.Y(), p3.Z());
|
||||||
|
}
|
||||||
|
|
||||||
|
lineVertices << p1.X() << p1.Y() << p1.Z();
|
||||||
|
lineVertices << p2.X() << p2.Y() << p2.Z();
|
||||||
|
lineVertices << p2.X() << p2.Y() << p2.Z();
|
||||||
|
lineVertices << p3.X() << p3.Y() << p3.Z();
|
||||||
|
lineVertices << p3.X() << p3.Y() << p3.Z();
|
||||||
|
lineVertices << p4.X() << p4.Y() << p4.Z();
|
||||||
|
lineVertices << p4.X() << p4.Y() << p4.Z();
|
||||||
|
lineVertices << p1.X() << p1.Y() << p1.Z();
|
||||||
|
|
||||||
|
vertexCounts[p1] += 2;
|
||||||
|
vertexCounts[p2] += 2;
|
||||||
|
vertexCounts[p3] += 2;
|
||||||
|
vertexCounts[p4] += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,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 rectangleAdded(const gp_Pnt& corner1, const gp_Pnt& corner2);
|
||||||
void planeSelected(SketchPlane plane);
|
void planeSelected(SketchPlane plane);
|
||||||
void toolDeactivated();
|
void toolDeactivated();
|
||||||
|
|
||||||
@@ -91,6 +92,8 @@ private:
|
|||||||
int m_activeTool = 0;
|
int m_activeTool = 0;
|
||||||
bool m_isDefiningLine = false;
|
bool m_isDefiningLine = false;
|
||||||
gp_Pnt m_firstLinePoint;
|
gp_Pnt m_firstLinePoint;
|
||||||
|
bool m_isDefiningRectangle = false;
|
||||||
|
gp_Pnt m_firstRectanglePoint;
|
||||||
QPoint m_currentMousePos;
|
QPoint m_currentMousePos;
|
||||||
bool m_isSnappingOrigin = false;
|
bool m_isSnappingOrigin = false;
|
||||||
bool m_isSnappingVertex = false;
|
bool m_isSnappingVertex = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user