From dd4d2e2dbbc87e456c6519466d7498c87df00367 Mon Sep 17 00:00:00 2001 From: ParkSuMin Date: Thu, 25 Dec 2025 00:44:48 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=B0=D1=81=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=BE=D0=BA=D1=80=D1=83?= =?UTF-8?q?=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D0=B5=D0=B9=20=D0=B8=20=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D0=B8=20=D1=81=20=D0=BE=D0=BA=D1=80=D1=83?= =?UTF-8?q?=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Canvas.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++------- Canvas.h | 16 +++++- DRAWer_2_0.cpp | 6 +++ DRAWer_2_0.h | 2 + DRAWer_2_0.ui | 9 +++- 5 files changed, 141 insertions(+), 21 deletions(-) diff --git a/Canvas.cpp b/Canvas.cpp index f39bfb8..aa6c881 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -1,6 +1,4 @@ #include "Canvas.h" -#define WIDGET_POSITION event->pos() -#define UCS_POSITION screenToLogical(WIDGET_POSITION) std::set groups; @@ -93,12 +91,12 @@ void Canvas::zoomReset() Curve* Canvas::findAt(QPointF& pos, qreal tolerance) { for (Curve* curve : curves) { - if (Line* line = dynamic_cast(curve)) { + if (Line* line = CURVE_AS_LINE(curve)) { if (line->contains(pos, tolerance)) { return line; } } - else if (Circle* circle = dynamic_cast(curve)) { + else if (Circle* circle = CURVE_AS_CIRCLE(curve)) { QPointF center(*circle->center.x, *circle->center.y); double radius = *circle->rad; @@ -117,7 +115,7 @@ Point* Canvas::findPointAt(QPointF pos, qreal tolerance) Point* temp = nullptr; for (Curve* curve : curves) { - if (Line* line = dynamic_cast(curve)) { + if (Line* line = CURVE_AS_LINE(curve)) { QPointF p1(*line->p1.x, *line->p1.y); QPointF p2(*line->p2.x, *line->p2.y); @@ -126,7 +124,7 @@ Point* Canvas::findPointAt(QPointF pos, qreal tolerance) if (dist_P2P(p2, pos) <= tolerance) temp = line->end_ref; } - else if (Circle* circle = dynamic_cast(curve)) { + else if (Circle* circle = CURVE_AS_CIRCLE(curve)) { QPointF center(circle->center.get_X(), circle->center.get_Y()); if (dist_P2P(center, pos) <= tolerance) temp = circle->center_ref; @@ -174,6 +172,11 @@ bool Canvas::areAlreadyPerpendicular(Line* line1, Line* line2) return perpendicularPairs.count(makeOrderedPair(line1, line2)); } +bool Canvas::areAlreadyTangent(Curve* curve1, Curve* curve2) +{ + return tangentPairs.count(makeOrderedPair(curve1, curve2)); +} + // =================================================================== // Методы работы с ограничениями // =================================================================== @@ -248,7 +251,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) return; } - Line* found = dynamic_cast(findAt(scene)); + Line* found = CURVE_AS_LINE(findAt(scene)); if (found) { draggedCurve = found; QPointF lineCenter( @@ -259,7 +262,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) return; } - Circle* found_circle = dynamic_cast(findAt(scene)); + Circle* found_circle = CURVE_AS_CIRCLE(findAt(scene)); if (found_circle) { draggedCurve = found_circle; QPointF center(*found_circle->center.x, *found_circle->center.y); @@ -270,7 +273,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) // ====================== Режим Horizontal/Vertical ====================== else if (mode == Mode::Horizontal || mode == Mode::Vertical) { - Line* found = dynamic_cast(findAt(scene)); + Line* found = CURVE_AS_LINE(findAt(scene)); if (found) { // Проверка: если линия уже вертикальна, нельзя сделать её горизонтальной @@ -403,7 +406,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) current_circle->center_ref = points[points.size() - 1]; } else { - double *r = new double(dist_P2P(QPointF(current_circle->center.get_X(), current_circle->center.get_Y()), QPointF(scene.x(), scene.y()))); + double* r = new double(dist_P2P(QPointF(current_circle->center.get_X(), current_circle->center.get_Y()), QPointF(scene.x(), scene.y()))); current_circle->rad = r; params.push_back(r); @@ -417,7 +420,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) } // ====================== Режим Parallel: задание параллельности ====================== else if (mode == Mode::Parallel) { - Line* found = dynamic_cast(findAt(scene)); + Line* found = CURVE_AS_LINE(findAt(scene)); if (!found) { current_line = nullptr; @@ -496,7 +499,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) Line* l2 = nullptr; for (Curve* curve : curves) { - if (Line* l = dynamic_cast(curve)) { + if (Line* l = CURVE_AS_LINE(curve)) { if (l->start_ref == firstPoint || l->end_ref == firstPoint) l1 = l; if (l->start_ref == clickedPoint || l->end_ref == clickedPoint) @@ -527,7 +530,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) } else if (mode == Mode::Perpendicular) { - Line* found = dynamic_cast(findAt(scene)); + Line* found = CURVE_AS_LINE(findAt(scene)); if (!found) { current_line = nullptr; @@ -572,6 +575,96 @@ void Canvas::mousePressEvent(QMouseEvent* event) return; } } + + else if (mode == Mode::Tangent) { + // Ищем любой объект под курсором: линия или окружность + Curve* selected = findAt(scene); + + if (!selected) { + current_curve = nullptr; + update(); + return; + } + + // Если кликнули по уже выбранному объекту — сброс + if (current_curve && selected == current_curve) { + current_curve = nullptr; + update(); + return; + } + + // Первый выбор + if (!current_curve) { + current_curve = selected; + update(); // подсветим выбранный объект + return; + } + + // Второй выбор — теперь у нас два объекта + Curve* first = current_curve; + Curve* second = selected; + + // Проверка на уже существующее ограничение касательности + if (areAlreadyTangent(first, second)) { + QMessageBox::warning(this, "Ошибка", + "Эти объекты уже касательны!", QMessageBox::Ok); + current_curve = nullptr; + update(); + return; + } + + QString message; + bool constraintAdded = false; + + // Вариант 1: Линия → Окружность + if (Line* line = dynamic_cast(first)) { + if (Circle* circle = dynamic_cast(second)) { + message = "Выбрано: Линия → Окружность\nПрименено касание линии к окружности."; + sys.addConstraintTangent(*line, *circle, constraints_count++); + constraintAdded = true; + } + } + // Вариант 2: Окружность → Линия + else if (Circle* circle = dynamic_cast(first)) { + if (Line* line = dynamic_cast(second)) { + message = "Выбрано: Окружность → Линия\nПрименено касание линии к окружности."; + sys.addConstraintTangent(*line, *circle, constraints_count++); // порядок Line, Circle + constraintAdded = true; + } + } + // Вариант 3: Окружность → Окружность + if (Circle* c1 = dynamic_cast(first)) { + if (Circle* c2 = dynamic_cast(second)) { + message = "Выбрано: Окружность → Окружность\nПрименено касание двух окружностей."; + sys.addConstraintTangent(*c1, *c2, constraints_count++); + constraintAdded = true; + } + } + + // Если комбинация недопустима + if (!constraintAdded) { + message = "Недопустимая комбинация объектов для касания!\n" + "Поддерживаются: линия-окружность или окружность-окружность."; + QMessageBox::warning(this, "Ошибка", message, QMessageBox::Ok); + } + else { + // Сохраняем информацию об ограничении (для undo и удаления) + auto orderedPair = makeOrderedPair(first, second); + tangentPairs.insert(orderedPair); + + C_Info[constraints_count - 1] = { Mode::Tangent, orderedPair }; + + QMessageBox::information(this, "Касание применено", message, QMessageBox::Ok); + + after_constraint = true; + } + + // Сброс режима и выделения + current_curve = nullptr; + mode = Mode::None; + solve_for_canvas(); + update(); + } } void Canvas::mouseMoveEvent(QMouseEvent* event) @@ -598,7 +691,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) // ====================== Перемещение линии ====================== else if (draggedCurve) { - if (Line* draggedLine = dynamic_cast(draggedCurve)) { + if (Line* draggedLine = CURVE_AS_LINE(draggedCurve)) { QPointF newCenter = UCS_POSITION - dragOffset; QPointF oldCenter( (*draggedLine->p1.x + *draggedLine->p2.x) / 2.0, @@ -627,7 +720,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) } } } - else if (Circle* circle = dynamic_cast(draggedCurve)) { + else if (Circle* circle = CURVE_AS_CIRCLE(draggedCurve)) { QPointF newCenter = UCS_POSITION - dragOffset; *circle->center.x = newCenter.x(); *circle->center.y = newCenter.y(); @@ -687,7 +780,7 @@ void Canvas::paintEvent(QPaintEvent* event) // ====================== Отрисовка линий ====================== for (Curve* curve : curves) { - if (Line* line = dynamic_cast(curve)) { + if (Line* line = CURVE_AS_LINE(curve)) { bool isSelected = (mode == Mode::Parallel && line == current_line); // Настройка пера для линии @@ -708,7 +801,7 @@ void Canvas::paintEvent(QPaintEvent* event) p.drawEllipse(QPointF(*line->p1.x, *line->p1.y), 5, 5); p.drawEllipse(QPointF(*line->p2.x, *line->p2.y), 5, 5); } - else if (Circle* circle = dynamic_cast(curve)) { + else if (Circle* circle = CURVE_AS_CIRCLE(curve)) { p.setPen(QPen(Qt::black, 2)); p.setBrush(Qt::NoBrush); @@ -894,7 +987,7 @@ void Canvas::solve_for_canvas() else { sys.applySolution(); for (Curve* curve : curves) { - if (Line* line = dynamic_cast(curve)) { + if (Line* line = CURVE_AS_LINE(curve)) { if (abs(*line->p1.x - *line->p2.x) < EPS && abs(*line->p1.y - *line->p2.y) < EPS && after_constraint) { sys.undoSolution(); flag = true; diff --git a/Canvas.h b/Canvas.h index 0f444cc..b4857d1 100644 --- a/Canvas.h +++ b/Canvas.h @@ -3,6 +3,11 @@ constexpr auto EPS = 1e-9; constexpr auto ZOOM_STEP = 0.05; +#define WIDGET_POSITION event->pos() +#define UCS_POSITION screenToLogical(WIDGET_POSITION) +#define CURVE_AS_LINE(CURVE) dynamic_cast(CURVE) +#define CURVE_AS_CIRCLE(CURVE) dynamic_cast(CURVE) + #include #include #include @@ -35,7 +40,8 @@ enum class Mode : int Horizontal = 4, ///< Режим задания горизонтальности Vertical = 5, ///< Режим задания вертикальности Perpendicular = 6, ///< Режим задания перпендикулярности - DrawingCircle = 7 ///< Режим рисования окружности + DrawingCircle = 7, ///< Режим рисования окружности + Tangent = 8 //< Режим задания касательности }; /// Удобный тип для хранения пары параллельных линий (порядок не важен) @@ -44,6 +50,8 @@ using LinePair = std::pair; /// Удобный тип для хранения пары точек (порядок не важен) using PointPair = std::pair; +using CurvePair = std::pair; + // =================================================================== // Основной класс холста // =================================================================== @@ -226,6 +234,8 @@ private: */ bool areAlreadyPerpendicular(Line* line1, Line* line2); + bool areAlreadyTangent(Curve* curve1, Curve* curve2); + // ====================== Методы работы с ограничениями ====================== /** @@ -263,6 +273,7 @@ private: // ====================== Коллекции ограничений ====================== std::set parallelPairs; ///< Пары параллельных линий std::set perpendicularPairs; ///< Пары перпендикулярных линий + std::set tangentPairs; std::set P2Ppairs; ///< Пары совпадающих точек std::set HORIZ_pairs; ///< Пары точек горизонтальных линий std::set VERT_pairs; ///< Пары точек вертикальных линий @@ -270,6 +281,7 @@ private: // ====================== Временные данные для режимов ====================== Line* current_line{ nullptr }; ///< Текущая линия в режимах рисования/параллельности Circle* current_circle{ nullptr }; + Curve* current_curve { nullptr }; Point* firstPoint{ nullptr }; ///< Первая точка в режиме совпадения Mode mode{ Mode::None }; ///< Текущий режим работы @@ -287,7 +299,7 @@ private: */ struct ConstraintInfo { Mode mode; ///< Тип ограничения - std::variant data; ///< Данные ограничения + std::variant data; ///< Данные ограничения }; std::map C_Info; ///< Карта информации об ограничениях diff --git a/DRAWer_2_0.cpp b/DRAWer_2_0.cpp index 9a95dad..3b854dd 100644 --- a/DRAWer_2_0.cpp +++ b/DRAWer_2_0.cpp @@ -49,3 +49,9 @@ void DRAWer_2_0::on_Circle_Button_clicked() ui.widget->changeMode(Mode::DrawingCircle); } + +void DRAWer_2_0::on_Tangent_Button_clicked() +{ + ui.widget->changeMode(Mode::Tangent); +} + diff --git a/DRAWer_2_0.h b/DRAWer_2_0.h index df1232a..0a0f929 100644 --- a/DRAWer_2_0.h +++ b/DRAWer_2_0.h @@ -26,6 +26,8 @@ private slots: void on_Circle_Button_clicked(); + void on_Tangent_Button_clicked(); + private: Ui::DRAWer_2_0Class ui; }; diff --git a/DRAWer_2_0.ui b/DRAWer_2_0.ui index 66f3857..ba60d81 100644 --- a/DRAWer_2_0.ui +++ b/DRAWer_2_0.ui @@ -122,6 +122,13 @@ + + + Tangent + + + + Vertical @@ -170,7 +177,7 @@ 0 0 776 - 22 + 21