#pragma once constexpr auto EPS = 1e-9; constexpr auto ZOOM_STEP = 0.05; #include #include #include #include #include #include #ifdef _DEBUG #include #endif // GCS — геометрический солвер из FreeCAD #include "GCS/Geo.h" #include "GCS/GCS.h" using namespace GCS; // =================================================================== // Типы и перечисления // =================================================================== /** * @brief Режимы работы с холстом */ enum class Mode : int { None = 0, ///< Режим отсутствия действия DrawingLine = 1, ///< Режим рисования линии Parallel = 2, ///< Режим задания параллельности Coincedent = 3, ///< Режим задания совпадения точек Horizontal = 4, ///< Режим задания горизонтальности Vertical = 5, ///< Режим задания вертикальности Perpendicular = 6, ///< Режим задания перпендикулярности DrawingCircle = 7 ///< Режим рисования окружности }; /// Удобный тип для хранения пары параллельных линий (порядок не важен) using LinePair = std::pair; /// Удобный тип для хранения пары точек (порядок не важен) 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(); /** * @brief Сбросить масштаб к 100% */ void zoomReset(); protected: /** * @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; /** * @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); /** * @brief Проверить, совпадают ли две точки (ограничение P2P) * @param point1 Первая точка * @param point2 Вторая точка * @return true если точки совпадают, иначе false */ bool areCoincident(Point* point1, Point* point2); /** * @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); // ====================== Методы работы с ограничениями ====================== /** * @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 circles; ///< Завершённые окружности QVector curves; QVector points; ///< Все точки сцены std::vector params; ///< Все параметры, передаваемые в солвер // ====================== Коллекции ограничений ====================== std::set parallelPairs; ///< Пары параллельных линий 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 }; ///< Текущий масштаб отображения (1.0 = 100%) // ====================== Счётчики ====================== int obj_count{ 0 }; ///< Счётчик объектов (для тегов) int constraints_count{ 0 }; ///< Счётчик ограничений (для тегов) // ====================== Информация о последнем ограничении ====================== /** * @brief Структура для хранения информации об ограничении */ struct ConstraintInfo { Mode mode; ///< Тип ограничения std::variant data; ///< Данные ограничения }; std::map C_Info; ///< Карта информации об ограничениях };