Улучшение читабельности кода

This commit is contained in:
2025-12-15 12:07:31 +03:00
parent cc6dfbc4a2
commit e2ce3a0e48
2 changed files with 650 additions and 526 deletions

View File

@@ -1,23 +1,84 @@
#include "Canvas.h" #include "Canvas.h"
static double dist_P2P(QPointF p1, QPointF p2) { // ===================================================================
// Вспомогательные функции
// ===================================================================
/// Вычислить расстояние между двумя точками
static double dist_P2P(QPointF p1, QPointF p2)
{
return sqrt(pow(p2.x() - p1.x(), 2) + pow(p2.y() - p1.y(), 2)); return sqrt(pow(p2.x() - p1.x(), 2) + pow(p2.y() - p1.y(), 2));
} }
/// Создать упорядоченную пару (чтобы pair(obj1, obj2) и pair(obj2, obj1) считались одинаковыми)
template <typename T, typename A, typename B> template <typename T, typename A, typename B>
T makeOrderedPair(A* obj1, B* obj2) T makeOrderedPair(A* obj1, B* obj2)
{ {
return (obj1 < obj2) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1); return (obj1 < obj2) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1);
} }
// ===================================================================
// Конструктор и деструктор
// ===================================================================
Canvas::Canvas(QWidget* parent)
: QWidget(parent)
{
sys = System();
setMouseTracking(true);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
}
Canvas::~Canvas()
{
// Очистка динамически выделенной памяти
for (double* param : params) {
delete param;
}
for (Point* pt : points) {
delete pt;
}
for (Line* line : lines) {
delete line;
}
// Очистка контейнеров
params.clear();
points.clear();
lines.clear();
parallelPairs.clear();
P2Ppairs.clear();
VERT_pairs.clear();
HORIZ_pairs.clear();
// Очистка временных указателей
if (current_line)
delete current_line;
if (firstPoint)
delete firstPoint;
}
// ===================================================================
// Методы изменения режима
// ===================================================================
void Canvas::changeMode(Mode _mode) void Canvas::changeMode(Mode _mode)
{ {
mode = _mode; mode = _mode;
} }
// TODO // ===================================================================
// Методы поиска и проверки
// ===================================================================
Line* Canvas::findAt(QPointF& pos) Line* Canvas::findAt(QPointF& pos)
{ {
// TODO: реализовать проверку, находится ли точка на линии
for (Line* line : lines) { for (Line* line : lines) {
if (line->contains(pos)) { if (line->contains(pos)) {
return line; return line;
@@ -46,25 +107,27 @@ bool Canvas::areCoincident(Point* p1, Point* p2)
return P2Ppairs.count(makeOrderedPair<PointPair>(p1, p2)); return P2Ppairs.count(makeOrderedPair<PointPair>(p1, p2));
} }
// False - Horizonta, True - Vertical
bool Canvas::areHorizontalVertical(Point* p1, Point* p2, bool mode) bool Canvas::areHorizontalVertical(Point* p1, Point* p2, bool mode)
{ {
// mode = false - проверка горизонтальности
// mode = true - проверка вертикальности
if (!mode) if (!mode)
return HORIZ_pairs.count(makeOrderedPair<PointPair>(p1, p2)); return HORIZ_pairs.count(makeOrderedPair<PointPair>(p1, p2));
else else
return VERT_pairs.count(makeOrderedPair<PointPair>(p1, p2)); return VERT_pairs.count(makeOrderedPair<PointPair>(p1, p2));
} }
bool Canvas::isLineHorizontal(Line* line) bool Canvas::isLineHorizontal(Line* line)
{ {
if (!line || !line->start_ref || !line->end_ref) return false; if (!line || !line->start_ref || !line->end_ref)
return false;
return HORIZ_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref)); return HORIZ_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref));
} }
bool Canvas::isLineVertical(Line* line) bool Canvas::isLineVertical(Line* line)
{ {
if (!line || !line->start_ref || !line->end_ref) return false; if (!line || !line->start_ref || !line->end_ref)
return false;
return VERT_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref)); return VERT_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref));
} }
@@ -73,10 +136,16 @@ bool Canvas::areAlreadyParallel(Line* l1, Line* l2)
return parallelPairs.count(makeOrderedPair<LinePair>(l1, l2)); return parallelPairs.count(makeOrderedPair<LinePair>(l1, l2));
} }
// ===================================================================
// Методы работы с ограничениями
// ===================================================================
void Canvas::remove_constraints() void Canvas::remove_constraints()
{ {
// Удаляем ограничение из солвера
sys.clearByTag(constraints_count - 1); sys.clearByTag(constraints_count - 1);
// Удаляем из соответствующих контейнеров в зависимости от типа ограничения
switch (lastConstraint.mode) { switch (lastConstraint.mode) {
case Mode::Horizontal: { case Mode::Horizontal: {
if (auto* pair = std::get_if<PointPair>(&lastConstraint.data)) { if (auto* pair = std::get_if<PointPair>(&lastConstraint.data)) {
@@ -106,19 +175,25 @@ void Canvas::remove_constraints()
break; break;
} }
// Сбрасываем информацию о последнем ограничении
lastConstraint.mode = Mode::None; lastConstraint.mode = Mode::None;
constraints_count--; constraints_count--;
} }
// ===================================================================
// Обработчики событий мыши
// ===================================================================
void Canvas::mousePressEvent(QMouseEvent* event) void Canvas::mousePressEvent(QMouseEvent* event)
{ {
QPointF scene = event->pos(); QPointF scene = event->pos();
#ifdef _DEBUG #ifdef _DEBUG
qDebug() << "Scene point in" << scene.x() << scene.y(); qDebug() << "Scene point in" << scene.x() << scene.y();
#endif #endif
if (mode == Mode::None){ // ====================== Режим None: перемещение объектов ======================
if (mode == Mode::None) {
Point* p = findPointAt(scene); Point* p = findPointAt(scene);
if (p) { if (p) {
draggedPoint = p; draggedPoint = p;
@@ -138,10 +213,12 @@ void Canvas::mousePressEvent(QMouseEvent* event)
} }
} }
// ====================== Режим Horizontal/Vertical ======================
else if (mode == Mode::Horizontal || mode == Mode::Vertical) { else if (mode == Mode::Horizontal || mode == Mode::Vertical) {
Line* found = findAt(scene); Line* found = findAt(scene);
if (found) { if (found) {
// Проверка: если линия уже вертикальна, нельзя сделать её горизонтальной
if (mode == Mode::Horizontal && isLineVertical(found)) { if (mode == Mode::Horizontal && isLineVertical(found)) {
QMessageBox::warning(this, QMessageBox::warning(this,
QString("Невозможно"), QString("Невозможно"),
@@ -183,19 +260,24 @@ void Canvas::mousePressEvent(QMouseEvent* event)
mode = Mode::None; mode = Mode::None;
} }
// ====================== Режим DrawingLine: рисование линий ======================
else if (mode == Mode::DrawingLine) { else if (mode == Mode::DrawingLine) {
if (!current_line) { if (!current_line) {
// Первый клик: создаем новую линию
current_line = new Line(); current_line = new Line();
current_line->set_tag(obj_count); current_line->set_tag(obj_count);
// Создаем координаты для точек линии
double* x1 = new double(scene.x()); double* x1 = new double(scene.x());
double* y1 = new double(scene.y()); double* y1 = new double(scene.y());
double* x2 = new double(scene.x()); double* x2 = new double(scene.x());
double* y2 = new double(scene.y()); double* y2 = new double(scene.y());
// Добавляем параметры в солвер
params.push_back(x1); params.push_back(x1);
params.push_back(y1); params.push_back(y1);
// Создаем точки и привязываем к линии
points.push_back(new Point(x1, y1, obj_count)); points.push_back(new Point(x1, y1, obj_count));
points.push_back(new Point(x2, y2, obj_count)); points.push_back(new Point(x2, y2, obj_count));
@@ -207,15 +289,20 @@ void Canvas::mousePressEvent(QMouseEvent* event)
current_line->end_ref = points[points.size() - 1]; current_line->end_ref = points[points.size() - 1];
} }
else { else {
// Второй клик: завершаем рисование линии
*current_line->p2.x = scene.x(); *current_line->p2.x = scene.x();
*current_line->p2.y = scene.y(); *current_line->p2.y = scene.y();
// Временный ограничитель длины линии // Проверка минимальной длины линии
double len = sqrt(pow(*current_line->p2.x - *current_line->p1.x, 2) + pow(*current_line->p2.y - *current_line->p1.y, 2)); double len = sqrt(pow(*current_line->p2.x - *current_line->p1.x, 2) +
pow(*current_line->p2.y - *current_line->p1.y, 2));
if (len < 10) { if (len < 10) {
// Линия слишком короткая - отменяем создание
delete current_line; delete current_line;
current_line = nullptr; current_line = nullptr;
// Удаляем созданные точки и параметры
delete points.back(); delete points.back();
points.pop_back(); points.pop_back();
delete points.back(); delete points.back();
@@ -226,14 +313,17 @@ void Canvas::mousePressEvent(QMouseEvent* event)
params.pop_back(); params.pop_back();
mode = Mode::None; mode = Mode::None;
QMessageBox::critical(this, "WHOOPS", "Sorry, your line is very short", QMessageBox::Ok); QMessageBox::critical(this, "WHOOPS",
"Sorry, your line is very short", QMessageBox::Ok);
update(); update();
return; return;
} }
// Добавляем координаты второй точки в параметры
params.push_back(current_line->p2.x); params.push_back(current_line->p2.x);
params.push_back(current_line->p2.y); params.push_back(current_line->p2.y);
// Завершаем создание линии
lines.append(current_line); lines.append(current_line);
current_line = nullptr; current_line = nullptr;
mode = Mode::None; mode = Mode::None;
@@ -244,6 +334,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
return; return;
} }
// ====================== Режим Parallel: задание параллельности ======================
else if (mode == Mode::Parallel) { else if (mode == Mode::Parallel) {
Line* found = findAt(scene); Line* found = findAt(scene);
@@ -253,18 +344,21 @@ void Canvas::mousePressEvent(QMouseEvent* event)
return; return;
} }
// Первый клик: выбираем первую линию
if (!current_line) { if (!current_line) {
current_line = found; current_line = found;
update(); update();
return; return;
} }
if (found == current_line){ // Повторный клик на ту же линию: сброс выбора
if (found == current_line) {
current_line = nullptr; current_line = nullptr;
update(); update();
return; return;
} }
// Второй клик на другую линию: добавляем ограничение параллельности
if (!areAlreadyParallel(found, current_line)) { if (!areAlreadyParallel(found, current_line)) {
auto pair = makeOrderedPair<LinePair>(found, current_line); auto pair = makeOrderedPair<LinePair>(found, current_line);
sys.addConstraintParallel(*found, *current_line, constraints_count++); sys.addConstraintParallel(*found, *current_line, constraints_count++);
@@ -278,10 +372,11 @@ void Canvas::mousePressEvent(QMouseEvent* event)
update(); update();
} }
else { else {
// Линии уже параллельны - сообщаем об ошибке
#ifdef _DEBUG #ifdef _DEBUG
qDebug() << "Line" << current_line << "and" << found << "are parallel. Abort!"; qDebug() << "Line" << current_line << "and" << found << "are parallel. Abort!";
#endif #endif
QMessageBox::warning(this, QMessageBox::warning(this,
QString("Wrong"), QString("Wrong"),
QString("Parallel lines can not be more parallel!"), QString("Parallel lines can not be more parallel!"),
@@ -291,10 +386,9 @@ void Canvas::mousePressEvent(QMouseEvent* event)
update(); update();
return; return;
} }
update();
} }
// ====================== Режим Coincedent: совпадение точек ======================
else if (mode == Mode::Coincedent) { else if (mode == Mode::Coincedent) {
Point* clickedPoint = findPointAt(scene); Point* clickedPoint = findPointAt(scene);
if (!clickedPoint) { if (!clickedPoint) {
@@ -303,27 +397,32 @@ void Canvas::mousePressEvent(QMouseEvent* event)
return; return;
} }
// Первый клик: выбираем первую точку
if (!firstPoint) { if (!firstPoint) {
firstPoint = clickedPoint; firstPoint = clickedPoint;
update(); update();
return; return;
} }
// Повторный клик на ту же точку: сброс выбора
if (clickedPoint == firstPoint) { if (clickedPoint == firstPoint) {
firstPoint = nullptr; firstPoint = nullptr;
update(); update();
return; return;
} }
Line *l1 = nullptr, *l2 = nullptr; // Находим линии, к которым принадлежат точки
Line* l1 = nullptr;
Line* l2 = nullptr;
for (Line* l : lines) { for (Line* l : lines) {
if (l->start_ref == firstPoint || l->end_ref == firstPoint) l1 = l; if (l->start_ref == firstPoint || l->end_ref == firstPoint)
if (l->start_ref == clickedPoint || l->end_ref == clickedPoint) l2 = l; l1 = l;
if (l->start_ref == clickedPoint || l->end_ref == clickedPoint)
l2 = l;
} }
if (l1 == l2 && l1 && l2 || // Проверка на невозможность ограничения
(areCoincident(firstPoint, clickedPoint))) if ((l1 == l2 && l1 && l2) || areCoincident(firstPoint, clickedPoint)) {
{
QMessageBox::critical(this, QString("NO!"), QString("P2P failed")); QMessageBox::critical(this, QString("NO!"), QString("P2P failed"));
firstPoint = nullptr; firstPoint = nullptr;
mode = Mode::None; mode = Mode::None;
@@ -331,6 +430,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
return; return;
} }
// Добавляем ограничение совпадения точек
sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++); sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++);
auto pair = makeOrderedPair<PointPair>(clickedPoint, firstPoint); auto pair = makeOrderedPair<PointPair>(clickedPoint, firstPoint);
P2Ppairs.insert(pair); P2Ppairs.insert(pair);
@@ -347,25 +447,30 @@ void Canvas::mousePressEvent(QMouseEvent* event)
void Canvas::mouseMoveEvent(QMouseEvent* event) void Canvas::mouseMoveEvent(QMouseEvent* event)
{ {
// ====================== Перемещение точки ======================
if (draggedPoint) { if (draggedPoint) {
QPointF pos = event->pos() - dragOffset; QPointF pos = event->pos() - dragOffset;
// Обновляем все связанные точки (совпадающие, горизонтальные, вертикальные)
for (Point* pair : points) { for (Point* pair : points) {
if (areCoincident(draggedPoint, pair)) { if (areCoincident(draggedPoint, pair)) {
*pair->x = pos.x(); *pair->x = pos.x();
*pair->y = pos.y(); *pair->y = pos.y();
} }
if (areHorizontalVertical(draggedPoint, pair, true)) { if (areHorizontalVertical(draggedPoint, pair, true)) { // vertical
*pair->x = pos.x(); *pair->x = pos.x();
} }
if (areHorizontalVertical(draggedPoint, pair, false)) { if (areHorizontalVertical(draggedPoint, pair, false)) { // horizontal
*pair->y = pos.y(); *pair->y = pos.y();
} }
} }
*draggedPoint->x = pos.x(); *draggedPoint->x = pos.x();
*draggedPoint->y = pos.y(); *draggedPoint->y = pos.y();
update(); update();
} }
// ====================== Перемещение линии ======================
else if (draggedLine) { else if (draggedLine) {
QPointF newCenter = event->pos() - dragOffset; QPointF newCenter = event->pos() - dragOffset;
QPointF oldCenter( QPointF oldCenter(
@@ -373,10 +478,11 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
(*draggedLine->p1.y + *draggedLine->p2.y) / 2.0 (*draggedLine->p1.y + *draggedLine->p2.y) / 2.0
); );
// Смещение для перемещения // Вычисляем смещение
double dx = newCenter.x() - oldCenter.x(); double dx = newCenter.x() - oldCenter.x();
double dy = newCenter.y() - oldCenter.y(); double dy = newCenter.y() - oldCenter.y();
// Перемещаем обе точки линии
Point* linePoints[2] = { draggedLine->start_ref, draggedLine->end_ref }; Point* linePoints[2] = { draggedLine->start_ref, draggedLine->end_ref };
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
@@ -385,6 +491,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
*currentPoint->x += dx; *currentPoint->x += dx;
*currentPoint->y += dy; *currentPoint->y += dy;
// Обновляем совпадающие точки
for (Point* pair : points) { for (Point* pair : points) {
if (pair != currentPoint && areCoincident(currentPoint, pair)) { if (pair != currentPoint && areCoincident(currentPoint, pair)) {
*pair->x = *currentPoint->x; *pair->x = *currentPoint->x;
@@ -399,6 +506,8 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
void Canvas::mouseReleaseEvent(QMouseEvent* event) void Canvas::mouseReleaseEvent(QMouseEvent* event)
{ {
Q_UNUSED(event);
if (draggedPoint) { if (draggedPoint) {
draggedPoint = nullptr; draggedPoint = nullptr;
update(); update();
@@ -410,44 +519,56 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event)
} }
} }
void Canvas::paintEvent(QPaintEvent*) // ===================================================================
// Метод отрисовки
// ===================================================================
void Canvas::paintEvent(QPaintEvent* event)
{ {
Q_UNUSED(event);
QPainter p(this); QPainter p(this);
p.setRenderHint(QPainter::Antialiasing, true); p.setRenderHint(QPainter::Antialiasing, true);
// === Решаем систему один раз === // ====================== Решение системы уравнений ======================
if (!params.empty()) { if (!params.empty()) {
int res = sys.solve(params); int res = sys.solve(params);
if (res == SolveStatus::Success || res == SolveStatus::Converged) { if (res == SolveStatus::Success || res == SolveStatus::Converged) {
sys.applySolution(); sys.applySolution();
} }
else if (res == SolveStatus::Failed && after_constraint){ else if (res == SolveStatus::Failed && after_constraint) {
QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!")); // Ошибка решения: удаляем последнее добавленное ограничение
QMessageBox::warning(this, QString("Error!"),
QString("Last constraint is unavailable!"));
remove_constraints(); remove_constraints();
} }
after_constraint = false; after_constraint = false;
} }
// ====================== Отрисовка линий ======================
for (Line* line : lines) { for (Line* line : lines) {
bool isSelected = (mode == Mode::Parallel && line == current_line); bool isSelected = (mode == Mode::Parallel && line == current_line);
// Настройка пера для линии
QPen linePen = isSelected ? QPen(Qt::red, 4) : QPen(Qt::black, 2); QPen linePen = isSelected ? QPen(Qt::red, 4) : QPen(Qt::black, 2);
p.setPen(linePen); p.setPen(linePen);
// Рисуем саму линию // Рисуем линию
p.drawLine(QPointF(*line->p1.x, *line->p1.y), p.drawLine(
QPointF(*line->p2.x, *line->p2.y)); QPointF(*line->p1.x, *line->p1.y),
QPointF(*line->p2.x, *line->p2.y)
);
// Рисуем концы — с учётом выделения // Рисуем конечные точки линии
QBrush pointBrush = isSelected ? QBrush(Qt::red) : QBrush(Qt::darkBlue); QBrush pointBrush = isSelected ? QBrush(Qt::red) : QBrush(Qt::darkBlue);
p.setBrush(pointBrush); p.setBrush(pointBrush);
p.setPen(Qt::NoPen); // убираем обводку у кружков p.setPen(Qt::NoPen);
p.drawEllipse(QPointF(*line->p1.x, *line->p1.y), 5, 5); p.drawEllipse(QPointF(*line->p1.x, *line->p1.y), 5, 5);
p.drawEllipse(QPointF(*line->p2.x, *line->p2.y), 5, 5); p.drawEllipse(QPointF(*line->p2.x, *line->p2.y), 5, 5);
} }
// === Подсветка выбранной точки в режиме Coincident === // ====================== Подсветка выбранной точки (режим Coincedent) ======================
if (mode == Mode::Coincedent && firstPoint) { if (mode == Mode::Coincedent && firstPoint) {
QPointF pt(*firstPoint->x, *firstPoint->y); QPointF pt(*firstPoint->x, *firstPoint->y);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
@@ -459,50 +580,11 @@ void Canvas::paintEvent(QPaintEvent*)
p.drawEllipse(pt, 4, 4); // маленький центр p.drawEllipse(pt, 4, 4); // маленький центр
} }
// === Текущая рисуемая линия (DrawingLine) === // ====================== Отрисовка текущей линии (режим DrawingLine) ======================
if (current_line && mode == Mode::DrawingLine) { if (current_line && mode == Mode::DrawingLine) {
// Концы текущей линии — синие точки // Рисуем начальную точку текущей линии
p.setBrush(Qt::blue); p.setBrush(Qt::blue);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.y), 6, 6); p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.y), 6, 6);
} }
} }
Canvas::Canvas(QWidget *parent)
: QWidget(parent)
{
sys = System();
setMouseTracking(true);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
}
Canvas::~Canvas()
{
for (double* param : params) {
delete param;
}
for (Point* pt : points) {
delete pt;
}
for (Line* line : lines) {
delete line;
}
params.clear();
points.clear();
lines.clear();
parallelPairs.clear();
P2Ppairs.clear();
VERT_pairs.clear();
HORIZ_pairs.clear();
if (current_line)
delete current_line;
if (firstPoint)
delete firstPoint;
}

120
Canvas.h
View File

@@ -14,18 +14,25 @@
#include "GCS/GCS.h" #include "GCS/GCS.h"
using namespace GCS; using namespace GCS;
// ===================================================================
// Типы и перечисления
// ===================================================================
/// Режимы работы с холстом
enum class Mode : int enum class Mode : int
{ {
None = 0, None = 0, ///< Режим отсутствия действия
DrawingLine = 1, DrawingLine = 1, ///< Режим рисования линии
Parallel = 2, Parallel = 2, ///< Режим задания параллельности
Coincedent = 3, Coincedent = 3, ///< Режим задания совпадения точек
Horizontal = 4, Horizontal = 4, ///< Режим задания горизонтальности
Vertical = 5 Vertical = 5 ///< Режим задания вертикальности
}; };
// Удобный тип для хранения пары параллельных линий (порядок не важен) /// Удобный тип для хранения пары параллельных линий (порядок не важен)
using LinePair = std::pair<Line*, Line*>; using LinePair = std::pair<Line*, Line*>;
/// Удобный тип для хранения пары точек (порядок не важен)
using PointPair = std::pair<Point*, Point*>; using PointPair = std::pair<Point*, Point*>;
// =================================================================== // ===================================================================
@@ -40,54 +47,89 @@ public:
explicit Canvas(QWidget* parent = nullptr); explicit Canvas(QWidget* parent = nullptr);
~Canvas() override; ~Canvas() override;
/// Изменить текущий режим работы
void changeMode(Mode newMode); void changeMode(Mode newMode);
protected: protected:
// Обработчики событий Qt
void mousePressEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override; void paintEvent(QPaintEvent* event) override;
private: private:
// ====================== Поиск и выбор ====================== // ====================== Методы поиска и выбора ======================
Line* findAt(QPointF&); // ищет линию под курсором
Point* findPointAt(QPointF, qreal tolerance = 5.0);
bool areCoincident(Point*, Point*);
bool areHorizontalVertical(Point*, Point*, bool);
bool isLineHorizontal(Line* line); // проверяет, горизонтальна ли линия
bool isLineVertical(Line* line); // проверяет, вертикальна ли линия
// ====================== Параллельность ======================
bool areAlreadyParallel(Line* l1, Line* l2); // проверка на дубликат
// ====================== Перемещение ====================== /// Найти линию под указанной позицией
Point* draggedPoint{ nullptr }; Line* findAt(QPointF& position);
Line* draggedLine{ nullptr };
QPointF dragOffset;
// ====================== Работа с парами ограничений ====================== /// Найти точку в указанной позиции с заданной точностью
Point* findPointAt(QPointF position, qreal tolerance = 5.0);
/// Проверить, совпадают ли две точки (ограничение P2P)
bool areCoincident(Point* point1, Point* point2);
/// Проверить горизонтальность или вертикальность между двумя точками
/// @param mode: false - горизонтальность, true - вертикальность
bool areHorizontalVertical(Point* point1, Point* point2, bool mode);
/// Проверить, является ли линия горизонтальной
bool isLineHorizontal(Line* line);
/// Проверить, является ли линия вертикальной
bool isLineVertical(Line* line);
/// Проверить, являются ли две линии уже параллельными (дубликат ограничения)
bool areAlreadyParallel(Line* line1, Line* line2);
// ====================== Методы работы с ограничениями ======================
/// Удалить последние добавленные ограничения при ошибке солвера
void remove_constraints(); void remove_constraints();
// ====================== Данные сцены ======================
System sys; // геометрический солвер
QVector<Line*> lines; // завершённые линии
QVector<Point*> points; // все точки (для удобного доступа)
std::vector<double*> params; // все параметры, передаваемые в солвер
std::set<LinePair> parallelPairs; // уже запараллеленные пары (защита от дублей) // ====================== Данные для перемещения объектов ======================
std::set<PointPair> P2Ppairs;
std::set<PointPair> HORIZ_pairs;
std::set<PointPair> VERT_pairs;
Line* current_line{ nullptr }; Point* draggedPoint{ nullptr }; ///< Точка, которую перемещают
Point* firstPoint{ nullptr }; Line* draggedLine{ nullptr }; ///< Линия, которую перемещают
Mode mode{ Mode::None }; QPointF dragOffset; ///< Смещение при начале перемещения
bool after_constraint{ false };
int obj_count{ 0 }; // тег для новых объектов // ====================== Данные геометрической системы ======================
int constraints_count{ 0 }; // тег для новых ограничений
System sys; ///< Геометрический солвер
QVector<Line*> lines; ///< Завершённые линии
QVector<Point*> points; ///< Все точки сцены
std::vector<double*> params; ///< Все параметры, передаваемые в солвер
// ====================== Коллекции ограничений ======================
std::set<LinePair> parallelPairs; ///< Пары параллельных линий
std::set<PointPair> P2Ppairs; ///< Пары совпадающих точек
std::set<PointPair> HORIZ_pairs; ///< Пары точек горизонтальных линий
std::set<PointPair> VERT_pairs; ///< Пары точек вертикальных линий
// ====================== Временные данные для режимов ======================
Line* current_line{ nullptr }; ///< Текущая линия в режимах рисования/параллельности
Point* firstPoint{ nullptr }; ///< Первая точка в режиме совпадения
Mode mode{ Mode::None }; ///< Текущий режим работы
// ====================== Флаги состояния ======================
bool after_constraint{ false }; ///< Флаг, что только что добавлено ограничение
// ====================== Счётчики ======================
int obj_count{ 0 }; ///< Счётчик объектов (для тегов)
int constraints_count{ 0 }; ///< Счётчик ограничений (для тегов)
// ====================== Информация о последнем ограничении ======================
/// Структура для хранения информации о последнем добавленном ограничении
/// (используется для отката при ошибке солвера)
struct LastConstraint { struct LastConstraint {
Mode mode{ Mode::None }; Mode mode{ Mode::None }; ///< Тип последнего ограничения
std::variant<LinePair, PointPair> data; std::variant<LinePair, PointPair> data; ///< Данные ограничения
}; };
LastConstraint lastConstraint;
LastConstraint lastConstraint; ///< Информация о последнем добавленном ограничении
}; };