From 1cbbaef68b24379421aede1f02288db0624083c3 Mon Sep 17 00:00:00 2001 From: ParkSuMin Date: Mon, 22 Dec 2025 23:21:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=20doxygen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Canvas.cpp | 52 ++++++++++---- Canvas.h | 199 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 200 insertions(+), 51 deletions(-) diff --git a/Canvas.cpp b/Canvas.cpp index 0157215..0d4a45e 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -8,13 +8,30 @@ std::set groups; // Вспомогательные функции // =================================================================== -/// Вычислить расстояние между двумя точками +/** + * @brief Вычислить расстояние между двумя точками + * @param p1 Первая точка + * @param p2 Вторая точка + * @return Расстояние между точками + */ static double dist_P2P(QPointF p1, QPointF p2) { return sqrt(pow(p2.x() - p1.x(), 2) + pow(p2.y() - p1.y(), 2)); } -/// Создать упорядоченную пару (чтобы pair(obj1, obj2) и pair(obj2, obj1) считались одинаковыми) +/** + * @brief Создать упорядоченную пару + * + * Создает пару, в которой элементы упорядочены по указателям, + * чтобы pair(obj1, obj2) и pair(obj2, obj1) считались одинаковыми + * + * @tparam T Тип возвращаемой пары + * @tparam A Тип первого элемента + * @tparam B Тип второго элемента + * @param obj1 Первый объект + * @param obj2 Второй объект + * @return Упорядоченная пара + */ template T makeOrderedPair(A* obj1, B* obj2) { @@ -41,7 +58,7 @@ Canvas::~Canvas() } // =================================================================== -// Методы изменения режима +// Методы изменения режима и масштаба // =================================================================== void Canvas::changeMode(Mode _mode) @@ -141,7 +158,6 @@ bool Canvas::areAlreadyPerpendicular(Line* line1, Line* line2) // Методы работы с ограничениями // =================================================================== -// TODO - переделать в bool для отображения статуса выполнения void Canvas::remove_constraint(int tag) { auto it = C_Info.find(tag); @@ -380,9 +396,9 @@ void Canvas::mousePressEvent(QMouseEvent* event) } else { // Линии уже параллельны - сообщаем об ошибке - #ifdef _DEBUG +#ifdef _DEBUG qDebug() << "Line" << current_line << "and" << found << "are parallel. Abort!"; - #endif +#endif QMessageBox::warning(this, QString("Wrong"), @@ -553,10 +569,10 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) } solve_for_canvas(); } - #ifdef _DEBUG +#ifdef _DEBUG else showObjectTag(WIDGET_POSITION); - #endif +#endif } void Canvas::mouseReleaseEvent(QMouseEvent* event) @@ -637,6 +653,10 @@ void Canvas::paintEvent(QPaintEvent* event) } } +// =================================================================== +// Обработчики событий клавиатуры и колесика мыши +// =================================================================== + void Canvas::keyPressEvent(QKeyEvent* event) { switch (event->key()) { @@ -734,11 +754,15 @@ void Canvas::showObjectTag(QPointF pos) .arg(info_line.length()); QToolTip::showText(mapToGlobal(pos.toPoint()), Text, this); } - else + else QToolTip::hideText(); } #endif +// =================================================================== +// Вспомогательные методы +// =================================================================== + QPointF Canvas::screenToLogical(const QPointF& screenPos) const { QPointF logical = screenPos; @@ -787,13 +811,13 @@ void Canvas::solve_for_canvas() } } } - + if (flag) { QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!")); - remove_constraint(constraints_count - 1); - C_Info.erase(constraints_count - 1); - constraints_count--; - } + remove_constraint(constraints_count - 1); + C_Info.erase(constraints_count - 1); + constraints_count--; + } after_constraint = false; update(); } diff --git a/Canvas.h b/Canvas.h index cd4ad82..3649af8 100644 --- a/Canvas.h +++ b/Canvas.h @@ -1,4 +1,5 @@ #pragma once + constexpr auto EPS = 1e-9; constexpr auto ZOOM_STEP = 0.05; @@ -22,7 +23,9 @@ using namespace GCS; // Типы и перечисления // =================================================================== -/// Режимы работы с холстом +/** + * @brief Режимы работы с холстом + */ enum class Mode : int { None = 0, ///< Режим отсутствия действия @@ -30,8 +33,8 @@ enum class Mode : int Parallel = 2, ///< Режим задания параллельности Coincedent = 3, ///< Режим задания совпадения точек Horizontal = 4, ///< Режим задания горизонтальности - Vertical = 5, ///< Режим задания вертикальности - Perpendicular = 6 + Vertical = 5, ///< Режим задания вертикальности + Perpendicular = 6 ///< Режим задания перпендикулярности }; /// Удобный тип для хранения пары параллельных линий (порядок не важен) @@ -44,123 +47,245 @@ using PointPair = std::pair; // Основной класс холста // =================================================================== +/** + * @brief Класс холста для рисования геометрических фигур с ограничениями + * + * Класс предоставляет функциональность для создания и редактирования + * геометрических объектов с применением ограничений через геометрический + * солвер FreeCAD GCS. + */ class Canvas : public QWidget { Q_OBJECT public: + /** + * @brief Конструктор класса Canvas + * @param parent Родительский виджет + */ explicit Canvas(QWidget* parent = nullptr); + + /** + * @brief Деструктор класса Canvas + */ ~Canvas() override; - /// Изменить текущий режим работы + /** + * @brief Изменить текущий режим работы + * @param newMode Новый режим работы + */ void changeMode(Mode newMode); - /// Увеличить масштаб + /** + * @brief Увеличить масштаб + * + * Увеличивает масштаб отображения на 5% + */ void zoomIn(); - /// Уменьшить масштаб + /** + * @brief Уменьшить масштаб + * + * Уменьшает масштаб отображения на 5% + */ void zoomOut(); - /// Сбросить масштаб к 100% + /** + * @brief Сбросить масштаб к 100% + */ void zoomReset(); protected: - // Обработчики событий Qt + /** + * @brief Обработчик нажатия кнопки мыши + * @param event Событие мыши + */ void mousePressEvent(QMouseEvent* event) override; + + /** + * @brief Обработчик перемещения мыши + * @param event Событие мыши + */ void mouseMoveEvent(QMouseEvent* event) override; + + /** + * @brief Обработчик отпускания кнопки мыши + * @param event Событие мыши + */ void mouseReleaseEvent(QMouseEvent* event) override; + + /** + * @brief Обработчик вращения колесика мыши + * @param event Событие колесика мыши + */ void wheelEvent(QWheelEvent* event) override; + + /** + * @brief Обработчик события отрисовки + * @param event Событие отрисовки + */ void paintEvent(QPaintEvent* event) override; - void keyPressEvent(QKeyEvent* event); + + /** + * @brief Обработчик нажатия клавиши клавиатуры + * @param event Событие клавиатуры + */ + void keyPressEvent(QKeyEvent* event) override; + + /** + * @brief Обработчик выхода курсора за пределы виджета + * @param event Событие + */ void leaveEvent(QEvent* event) override; private: - #ifdef _DEBUG + /** + * @brief Показать тег объекта под курсором (только в режиме отладки) + * @param pos Позиция курсора на экране + */ void showObjectTag(QPointF pos); #endif + /** + * @brief Преобразовать координаты экрана в логические координаты + * @param screenPos Координаты на экране + * @return Логические координаты + */ QPointF screenToLogical(const QPointF& screenPos) const; + + /** + * @brief Получить группу совпадающих точек + * @param p Базовая точка + */ void getCoincidentGroup(Point* p); + // ====================== Методы поиска и выбора ====================== - /// Найти линию под указанной позицией + /** + * @brief Найти линию под указанной позицией + * @param pos Позиция для поиска + * @param tolerance Допуск поиска + * @return Найденная линия или nullptr + */ Line* findAt(QPointF& pos, qreal tolerance = 5.0); - /// Найти точку в указанной позиции с заданной точностью + /** + * @brief Найти точку в указанной позиции + * @param position Позиция для поиска + * @param tolerance Допуск поиска + * @return Найденная точка или nullptr + */ Point* findPointAt(QPointF position, qreal tolerance = 5.0); - /// Проверить, совпадают ли две точки (ограничение P2P) + /** + * @brief Проверить, совпадают ли две точки (ограничение P2P) + * @param point1 Первая точка + * @param point2 Вторая точка + * @return true если точки совпадают, иначе false + */ bool areCoincident(Point* point1, Point* point2); - /// Проверить горизонтальность или вертикальность между двумя точками - /// @param mode: false - горизонтальность, true - вертикальность + /** + * @brief Проверить горизонтальность или вертикальность между двумя точками + * @param point1 Первая точка + * @param point2 Вторая точка + * @param mode false - проверка горизонтальности, true - проверка вертикальности + * @return true если точки имеют указанное ограничение, иначе false + */ bool areHorizontalVertical(Point* point1, Point* point2, bool mode); - /// Проверить, является ли линия горизонтальной + /** + * @brief Проверить, является ли линия горизонтальной + * @param line Линия для проверки + * @return true если линия горизонтальна, иначе false + */ bool isLineHorizontal(Line* line); - /// Проверить, является ли линия вертикальной + /** + * @brief Проверить, является ли линия вертикальной + * @param line Линия для проверки + * @return true если линия вертикальна, иначе false + */ bool isLineVertical(Line* line); - /// Проверить, являются ли две линии уже параллельными (дубликат ограничения) + /** + * @brief Проверить, являются ли две линии уже параллельными + * @param line1 Первая линия + * @param line2 Вторая линия + * @return true если линии уже параллельны, иначе false + */ bool areAlreadyParallel(Line* line1, Line* line2); + /** + * @brief Проверить, являются ли две линии уже перпендикулярными + * @param line1 Первая линия + * @param line2 Вторая линия + * @return true если линии уже перпендикулярны, иначе false + */ bool areAlreadyPerpendicular(Line* line1, Line* line2); // ====================== Методы работы с ограничениями ====================== - /// Удалить последние добавленные ограничения при ошибке солвера - void remove_constraint(int); + /** + * @brief Удалить ограничение по его тегу + * @param tag Тег ограничения + */ + void remove_constraint(int tag); + + /** + * @brief Решить геометрическую систему + * + * Вызывает солвер для решения системы ограничений и обновляет отображение + */ + void solve_for_canvas(); + + /** + * @brief Очистить холст + * + * Удаляет все объекты и ограничения, сбрасывает состояние + */ + void clearCanvas(); // ====================== Данные для перемещения объектов ====================== - Point* draggedPoint{ nullptr }; ///< Точка, которую перемещают Line* draggedLine{ nullptr }; ///< Линия, которую перемещают QPointF dragOffset; ///< Смещение при начале перемещения // ====================== Данные геометрической системы ====================== - System sys; ///< Геометрический солвер QVector lines; ///< Завершённые линии QVector points; ///< Все точки сцены std::vector params; ///< Все параметры, передаваемые в солвер // ====================== Коллекции ограничений ====================== - std::set parallelPairs; ///< Пары параллельных линий - std::set perpendicularPairs; + std::set perpendicularPairs; ///< Пары перпендикулярных линий std::set P2Ppairs; ///< Пары совпадающих точек std::set HORIZ_pairs; ///< Пары точек горизонтальных линий std::set VERT_pairs; ///< Пары точек вертикальных линий // ====================== Временные данные для режимов ====================== - Line* current_line{ nullptr }; ///< Текущая линия в режимах рисования/параллельности Point* firstPoint{ nullptr }; ///< Первая точка в режиме совпадения Mode mode{ Mode::None }; ///< Текущий режим работы // ====================== Флаги состояния ====================== - bool after_constraint{ false }; ///< Флаг, что только что добавлено ограничение - double scaleFactor { 1.0 }; + double scaleFactor{ 1.0 }; ///< Текущий масштаб отображения (1.0 = 100%) // ====================== Счётчики ====================== - int obj_count{ 0 }; ///< Счётчик объектов (для тегов) int constraints_count{ 0 }; ///< Счётчик ограничений (для тегов) // ====================== Информация о последнем ограничении ====================== - - /// Структура для хранения информации о последнем добавленном ограничении - /// (используется для отката при ошибке солвера) - + /** + * @brief Структура для хранения информации об ограничении + */ struct ConstraintInfo { - Mode mode; - std::variant data; + Mode mode; ///< Тип ограничения + std::variant data; ///< Данные ограничения }; - std::map C_Info; - void solve_for_canvas(); - void clearCanvas(); + std::map C_Info; ///< Карта информации об ограничениях }; \ No newline at end of file