From 104097dfc74cd45f678bf4ef893ba707fe9c483a Mon Sep 17 00:00:00 2001 From: ParkSuMin Date: Mon, 15 Dec 2025 19:37:40 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D1=82=D0=B5=D0=B3=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Но как Canvas, а не PlaneGCS --- Canvas.cpp | 41 ++++++++++++++++++++--------------------- Canvas.h | 12 ++++++------ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/Canvas.cpp b/Canvas.cpp index 556dbf6..a0393de 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -78,7 +78,6 @@ void Canvas::changeMode(Mode _mode) Line* Canvas::findAt(QPointF& pos, qreal tolerance) { - // TODO: реализовать проверку, находится ли точка на линии for (Line* line : lines) { if (line->contains(pos, tolerance)) { return line; @@ -140,33 +139,39 @@ bool Canvas::areAlreadyParallel(Line* l1, Line* l2) // Методы работы с ограничениями // =================================================================== -void Canvas::remove_constraints() +// TODO - переделать в bool для отображения статуса выполнения +void Canvas::remove_constraint(int tag) { + auto it = C_Info.find(tag); + if (it == C_Info.end()) return; + + ConstraintInfo info = it->second; + // Удаляем ограничение из солвера - sys.clearByTag(constraints_count - 1); + sys.clearByTag(tag); // Удаляем из соответствующих контейнеров в зависимости от типа ограничения - switch (lastConstraint.mode) { + switch (info.mode) { case Mode::Horizontal: { - if (auto* pair = std::get_if(&lastConstraint.data)) { + if (auto* pair = std::get_if(&info.data)) { HORIZ_pairs.erase(*pair); } break; } case Mode::Vertical: { - if (auto* pair = std::get_if(&lastConstraint.data)) { + if (auto* pair = std::get_if(&info.data)) { VERT_pairs.erase(*pair); } break; } case Mode::Parallel: { - if (auto* pair = std::get_if(&lastConstraint.data)) { + if (auto* pair = std::get_if(&info.data)) { parallelPairs.erase(*pair); } break; } case Mode::Coincedent: { - if (auto* pair = std::get_if(&lastConstraint.data)) { + if (auto* pair = std::get_if(&info.data)) { P2Ppairs.erase(*pair); } break; @@ -174,10 +179,7 @@ void Canvas::remove_constraints() default: break; } - - // Сбрасываем информацию о последнем ограничении - lastConstraint.mode = Mode::None; - constraints_count--; + C_Info.erase(it); } // =================================================================== @@ -244,15 +246,13 @@ void Canvas::mousePressEvent(QMouseEvent* event) sys.addConstraintHorizontal(*found, constraints_count++); auto pair = makeOrderedPair(found->start_ref, found->end_ref); HORIZ_pairs.insert(pair); - lastConstraint.mode = Mode::Horizontal; - lastConstraint.data = pair; + C_Info[constraints_count - 1] = { Mode::Horizontal, pair }; } else { sys.addConstraintVertical(*found, constraints_count++); auto pair = makeOrderedPair(found->start_ref, found->end_ref); VERT_pairs.insert(pair); - lastConstraint.mode = Mode::Vertical; - lastConstraint.data = pair; + C_Info[constraints_count - 1] = { Mode::Vertical, pair }; } update(); after_constraint = true; @@ -363,8 +363,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) auto pair = makeOrderedPair(found, current_line); sys.addConstraintParallel(*found, *current_line, constraints_count++); parallelPairs.insert(pair); - lastConstraint.mode = Mode::Parallel; - lastConstraint.data = pair; + C_Info[constraints_count - 1] = { Mode::Parallel, pair }; current_line = nullptr; mode = Mode::None; @@ -434,8 +433,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++); auto pair = makeOrderedPair(clickedPoint, firstPoint); P2Ppairs.insert(pair); - lastConstraint.mode = Mode::Coincedent; - lastConstraint.data = pair; + C_Info[constraints_count - 1] = { Mode::Coincedent, pair }; firstPoint = nullptr; mode = Mode::None; @@ -554,7 +552,8 @@ void Canvas::paintEvent(QPaintEvent* event) // Ошибка решения: удаляем последнее добавленное ограничение QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!")); - remove_constraints(); + remove_constraint(constraints_count - 1); + constraints_count--; } after_constraint = false; } diff --git a/Canvas.h b/Canvas.h index e3b9ba6..b5e1525 100644 --- a/Canvas.h +++ b/Canvas.h @@ -97,7 +97,7 @@ private: // ====================== Методы работы с ограничениями ====================== /// Удалить последние добавленные ограничения при ошибке солвера - void remove_constraints(); + void remove_constraint(int); // ====================== Данные для перемещения объектов ====================== @@ -138,10 +138,10 @@ private: /// Структура для хранения информации о последнем добавленном ограничении /// (используется для отката при ошибке солвера) - struct LastConstraint { - Mode mode{ Mode::None }; ///< Тип последнего ограничения - std::variant data; ///< Данные ограничения - }; - LastConstraint lastConstraint; ///< Информация о последнем добавленном ограничении + struct ConstraintInfo { + Mode mode; + std::variant data; + }; + std::map C_Info; }; \ No newline at end of file -- 2.49.1 From f719694580751786e835b855f4e071b739f1e728 Mon Sep 17 00:00:00 2001 From: ParkSuMin Date: Mon, 15 Dec 2025 19:52:23 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=AD=D0=BA=D1=81=D1=82=D1=80=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=84=D0=B8=D0=BA=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Не менялось положение точек при объединении 3 линий и более --- Canvas.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Canvas.cpp b/Canvas.cpp index a0393de..faa94e9 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -449,8 +449,15 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) if (draggedPoint) { QPointF pos = UCS_POSITION - dragOffset; - auto coincidentGroup = getCoincidentGroup(draggedPoint); + // TODO + for (Point* pair : points) { + if (areCoincident(draggedPoint, pair)) { + *pair->x = pos.x(); + *pair->y = pos.y(); + } + } + auto coincidentGroup = getCoincidentGroup(draggedPoint); for (Point* pt : coincidentGroup) { *pt->x = pos.x(); *pt->y = pos.y(); -- 2.49.1 From 68451b426157a824f32fb5e8c36ded9ac5099863 Mon Sep 17 00:00:00 2001 From: ParkSuMin Date: Mon, 15 Dec 2025 20:44:23 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=BF=D0=B5=D0=BD=D0=B4=D0=B8=D0=BA=D1=83?= =?UTF-8?q?=D0=BB=D1=8F=D1=80=D0=BD=D0=BE=D1=81=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Canvas.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ Canvas.h | 6 +++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Canvas.cpp b/Canvas.cpp index 556dbf6..c5f0497 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -136,6 +136,11 @@ bool Canvas::areAlreadyParallel(Line* l1, Line* l2) return parallelPairs.count(makeOrderedPair(l1, l2)); } +bool Canvas::areAlreadyPerpendicular(Line* line1, Line* line2) +{ + return perpendicularPairs.count(makeOrderedPair(line1, line2)); +} + // =================================================================== // Методы работы с ограничениями // =================================================================== @@ -443,6 +448,52 @@ void Canvas::mousePressEvent(QMouseEvent* event) update(); return; } + + else if (mode == Mode::Perpendicular) { + Line* found = findAt(scene); + + if (!found) { + current_line = nullptr; + update(); + return; + } + + // Первый клик: выбираем первую линию + if (!current_line) { + current_line = found; + update(); + return; + } + + // Повторный клик на ту же линию: сброс выбора + if (found == current_line) { + current_line = nullptr; + update(); + return; + } + + if (!areAlreadyPerpendicular(found, current_line)) { + auto pair = makeOrderedPair(found, current_line); + sys.addConstraintPerpendicular(*found, *current_line, constraints_count++); + perpendicularPairs.insert(pair); + + current_line = nullptr; + mode = Mode::None; + after_constraint = true; + update(); + } + else { + + QMessageBox::warning(this, + QString("Wrong"), + QString("Perpendicular lines can not be more perpendicular!"), + QMessageBox::Ok + ); + current_line = nullptr; + update(); + return; + } + } } void Canvas::mouseMoveEvent(QMouseEvent* event) diff --git a/Canvas.h b/Canvas.h index e3b9ba6..1a86f60 100644 --- a/Canvas.h +++ b/Canvas.h @@ -30,7 +30,8 @@ enum class Mode : int Parallel = 2, ///< Режим задания параллельности Coincedent = 3, ///< Режим задания совпадения точек Horizontal = 4, ///< Режим задания горизонтальности - Vertical = 5 ///< Режим задания вертикальности + Vertical = 5, ///< Режим задания вертикальности + Perpendicular = 6 }; /// Удобный тип для хранения пары параллельных линий (порядок не важен) @@ -94,6 +95,8 @@ private: /// Проверить, являются ли две линии уже параллельными (дубликат ограничения) bool areAlreadyParallel(Line* line1, Line* line2); + bool areAlreadyPerpendicular(Line* line1, Line* line2); + // ====================== Методы работы с ограничениями ====================== /// Удалить последние добавленные ограничения при ошибке солвера @@ -115,6 +118,7 @@ private: // ====================== Коллекции ограничений ====================== std::set parallelPairs; ///< Пары параллельных линий + std::set perpendicularPairs; std::set P2Ppairs; ///< Пары совпадающих точек std::set HORIZ_pairs; ///< Пары точек горизонтальных линий std::set VERT_pairs; ///< Пары точек вертикальных линий -- 2.49.1 From 72c0c243185b0a69c1fc76a3029b045dc868d262 Mon Sep 17 00:00:00 2001 From: ParkSuMin Date: Mon, 15 Dec 2025 22:45:11 +0300 Subject: [PATCH 4/4] =?UTF-8?q?=D0=9E=D1=82=D0=BB=D0=B0=D0=B4=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=BF=D0=B5=D0=BD=D0=B4=D0=B8=D0=BA=D1=83?= =?UTF-8?q?=D0=BB=D1=8F=D1=80=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Перенос солвера для canvas в отдельную функцию (краш на QPen) --- Canvas.cpp | 111 +++++++++++++++++++++++++++++++------------------ Canvas.h | 6 +-- DRAWer_2_0.cpp | 6 +++ DRAWer_2_0.h | 2 + DRAWer_2_0.ui | 7 ++++ 5 files changed, 88 insertions(+), 44 deletions(-) diff --git a/Canvas.cpp b/Canvas.cpp index d268241..c613cbb 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -1,4 +1,6 @@ #include "Canvas.h" +#define WIDGET_POSITION event->pos() +#define UCS_POSITION screenToLogical(WIDGET_POSITION) // =================================================================== // Вспомогательные функции @@ -181,6 +183,12 @@ void Canvas::remove_constraint(int tag) } break; } + case Mode::Perpendicular: { + if (auto* pair = std::get_if(&info.data)) { + perpendicularPairs.erase(*pair); + } + break; + } default: break; } @@ -259,7 +267,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) VERT_pairs.insert(pair); C_Info[constraints_count - 1] = { Mode::Vertical, pair }; } - update(); + solve_for_canvas(); after_constraint = true; } mode = Mode::None; @@ -320,7 +328,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) mode = Mode::None; QMessageBox::critical(this, "WHOOPS", "Sorry, your line is very short", QMessageBox::Ok); - update(); + solve_for_canvas(); return; } @@ -335,7 +343,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) obj_count++; after_constraint = true; } - update(); + solve_for_canvas(); return; } @@ -345,21 +353,21 @@ void Canvas::mousePressEvent(QMouseEvent* event) if (!found) { current_line = nullptr; - update(); + solve_for_canvas(); return; } // Первый клик: выбираем первую линию if (!current_line) { current_line = found; - update(); + solve_for_canvas(); return; } // Повторный клик на ту же линию: сброс выбора if (found == current_line) { current_line = nullptr; - update(); + solve_for_canvas(); return; } @@ -373,7 +381,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) current_line = nullptr; mode = Mode::None; after_constraint = true; - update(); + solve_for_canvas(); } else { // Линии уже параллельны - сообщаем об ошибке @@ -387,7 +395,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) QMessageBox::Ok ); current_line = nullptr; - update(); + solve_for_canvas(); return; } } @@ -397,21 +405,21 @@ void Canvas::mousePressEvent(QMouseEvent* event) Point* clickedPoint = findPointAt(scene); if (!clickedPoint) { firstPoint = nullptr; - update(); + solve_for_canvas(); return; } // Первый клик: выбираем первую точку if (!firstPoint) { firstPoint = clickedPoint; - update(); + solve_for_canvas(); return; } // Повторный клик на ту же точку: сброс выбора if (clickedPoint == firstPoint) { firstPoint = nullptr; - update(); + solve_for_canvas(); return; } @@ -430,7 +438,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) QMessageBox::critical(this, QString("NO!"), QString("P2P failed")); firstPoint = nullptr; mode = Mode::None; - update(); + solve_for_canvas(); return; } @@ -443,7 +451,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) firstPoint = nullptr; mode = Mode::None; after_constraint = true; - update(); + solve_for_canvas(); return; } @@ -452,21 +460,21 @@ void Canvas::mousePressEvent(QMouseEvent* event) if (!found) { current_line = nullptr; - update(); + solve_for_canvas(); return; } // Первый клик: выбираем первую линию if (!current_line) { current_line = found; - update(); + solve_for_canvas(); return; } // Повторный клик на ту же линию: сброс выбора if (found == current_line) { current_line = nullptr; - update(); + solve_for_canvas(); return; } @@ -474,11 +482,12 @@ void Canvas::mousePressEvent(QMouseEvent* event) auto pair = makeOrderedPair(found, current_line); sys.addConstraintPerpendicular(*found, *current_line, constraints_count++); perpendicularPairs.insert(pair); + C_Info[constraints_count - 1] = { Mode::Perpendicular, pair }; current_line = nullptr; mode = Mode::None; after_constraint = true; - update(); + solve_for_canvas(); } else { @@ -488,7 +497,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) QMessageBox::Ok ); current_line = nullptr; - update(); + solve_for_canvas(); return; } } @@ -500,6 +509,9 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) if (draggedPoint) { QPointF pos = UCS_POSITION - dragOffset; + *draggedPoint->x = pos.x(); + *draggedPoint->y = pos.y(); + // TODO for (Point* pair : points) { if (areCoincident(draggedPoint, pair)) { @@ -525,9 +537,16 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) } } - *draggedPoint->x = pos.x(); - *draggedPoint->y = pos.y(); - update(); + for (Point* other : points) { + if (areHorizontalVertical(draggedPoint, other, true)) { + *other->x = pos.x(); + } + if (areHorizontalVertical(draggedPoint, other, false)) { + *other->y = pos.y(); + } + } + + solve_for_canvas(); } // ====================== Перемещение линии ====================== @@ -559,7 +578,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) } } } - update(); + solve_for_canvas(); } #ifdef _DEBUG else @@ -573,12 +592,12 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event) if (draggedPoint) { draggedPoint = nullptr; - update(); + solve_for_canvas(); } if (draggedLine) { draggedLine = nullptr; - update(); + solve_for_canvas(); } } @@ -599,23 +618,6 @@ void Canvas::paintEvent(QPaintEvent* event) p.drawLine(-5, 0, 5, 0); p.drawLine(0, -5, 0, 5); - - // ====================== Решение системы уравнений ====================== - if (!params.empty()) { - int res = sys.solve(params); - if (res == SolveStatus::Success || res == SolveStatus::Converged) { - sys.applySolution(); - } - else if (res == SolveStatus::Failed && after_constraint) { - // Ошибка решения: удаляем последнее добавленное ограничение - QMessageBox::warning(this, QString("Error!"), - QString("Last constraint is unavailable!")); - remove_constraint(constraints_count - 1); - constraints_count--; - } - after_constraint = false; - } - // ====================== Отрисовка линий ====================== for (Line* line : lines) { bool isSelected = (mode == Mode::Parallel && line == current_line); @@ -708,3 +710,30 @@ std::vector Canvas::getCoincidentGroup(Point* p) } return group; } + +void Canvas::solve_for_canvas() +{ + bool flag = false; + int res = sys.solve(params); + if (res != SolveStatus::Success && res != SolveStatus::Converged) { + flag = true; + } + else { + sys.applySolution(); + for (Line* line : lines) { + if (abs(*line->p1.x - *line->p2.x) < EPS && abs(*line->p1.y - *line->p2.y) < EPS && after_constraint) { + sys.undoSolution(); + flag = true; + break; + } + } + } + + if (flag) { + QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!")); + remove_constraint(constraints_count - 1); + constraints_count--; + } + after_constraint = false; + update(); +} \ No newline at end of file diff --git a/Canvas.h b/Canvas.h index 4f6f31a..fc2bfb2 100644 --- a/Canvas.h +++ b/Canvas.h @@ -1,7 +1,5 @@ #pragma once - -#define WIDGET_POSITION event->pos() -#define UCS_POSITION screenToLogical(WIDGET_POSITION) +constexpr auto EPS = 1e-9; #include #include @@ -148,4 +146,6 @@ private: std::variant data; }; std::map C_Info; + + void solve_for_canvas(); }; \ No newline at end of file diff --git a/DRAWer_2_0.cpp b/DRAWer_2_0.cpp index 4458cb3..1575b58 100644 --- a/DRAWer_2_0.cpp +++ b/DRAWer_2_0.cpp @@ -40,3 +40,9 @@ void DRAWer_2_0::on_Vertical_Button_clicked() ui.widget->changeMode(Mode::Vertical); } + +void DRAWer_2_0::on_Perpendicular_Button_clicked() +{ + ui.widget->changeMode(Mode::Perpendicular); +} + diff --git a/DRAWer_2_0.h b/DRAWer_2_0.h index 9d40eca..218e250 100644 --- a/DRAWer_2_0.h +++ b/DRAWer_2_0.h @@ -22,6 +22,8 @@ private slots: void on_Vertical_Button_clicked(); + void on_Perpendicular_Button_clicked(); + private: Ui::DRAWer_2_0Class ui; int counter; diff --git a/DRAWer_2_0.ui b/DRAWer_2_0.ui index fe011c0..a906a54 100644 --- a/DRAWer_2_0.ui +++ b/DRAWer_2_0.ui @@ -52,6 +52,13 @@ + + + + Perpendicular + + + -- 2.49.1