#pragma once constexpr auto EPS = 1e-9; #include #include #include #include #include #include #ifdef _DEBUG #include #endif // GCS — геометрический солвер из FreeCAD #include "GCS/Geo.h" #include "GCS/GCS.h" using namespace GCS; // =================================================================== // Типы и перечисления // =================================================================== /// Режимы работы с холстом enum class Mode : int { None = 0, ///< Режим отсутствия действия DrawingLine = 1, ///< Режим рисования линии Parallel = 2, ///< Режим задания параллельности Coincedent = 3, ///< Режим задания совпадения точек Horizontal = 4, ///< Режим задания горизонтальности Vertical = 5, ///< Режим задания вертикальности Perpendicular = 6 }; /// Удобный тип для хранения пары параллельных линий (порядок не важен) using LinePair = std::pair; /// Удобный тип для хранения пары точек (порядок не важен) using PointPair = std::pair; // =================================================================== // Основной класс холста // =================================================================== class Canvas : public QWidget { Q_OBJECT public: explicit Canvas(QWidget* parent = nullptr); ~Canvas() override; /// Изменить текущий режим работы void changeMode(Mode newMode); protected: // Обработчики событий Qt void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void paintEvent(QPaintEvent* event) override; void keyPressEvent(QKeyEvent* event); void leaveEvent(QEvent* event) override; private: #ifdef _DEBUG void showObjectTag(QPointF pos); #endif QPointF screenToLogical(const QPointF& screenPos) const; std::vector getCoincidentGroup(Point* p); // ====================== Методы поиска и выбора ====================== /// Найти линию под указанной позицией Line* findAt(QPointF& pos, qreal tolerance = 5.0); /// Найти точку в указанной позиции с заданной точностью 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); bool areAlreadyPerpendicular(Line* line1, Line* line2); // ====================== Методы работы с ограничениями ====================== /// Удалить последние добавленные ограничения при ошибке солвера void remove_constraint(int); // ====================== Данные для перемещения объектов ====================== 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 P2Ppairs; ///< Пары совпадающих точек std::set HORIZ_pairs; ///< Пары точек горизонтальных линий std::set 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 ConstraintInfo { Mode mode; std::variant data; }; std::map C_Info; void solve_for_canvas(); void clearCanvas(); };