Форматирование под doxygen
This commit is contained in:
52
Canvas.cpp
52
Canvas.cpp
@@ -8,13 +8,30 @@ std::set<Point*> groups;
|
|||||||
// Вспомогательные функции
|
// Вспомогательные функции
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
/// Вычислить расстояние между двумя точками
|
/**
|
||||||
|
* @brief Вычислить расстояние между двумя точками
|
||||||
|
* @param p1 Первая точка
|
||||||
|
* @param p2 Вторая точка
|
||||||
|
* @return Расстояние между точками
|
||||||
|
*/
|
||||||
static double dist_P2P(QPointF p1, QPointF p2)
|
static double dist_P2P(QPointF p1, QPointF p2)
|
||||||
{
|
{
|
||||||
return sqrt(pow(p2.x() - p1.x(), 2) + pow(p2.y() - p1.y(), 2));
|
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 <typename T, typename A, typename B>
|
template <typename T, typename A, typename B>
|
||||||
T makeOrderedPair(A* obj1, B* obj2)
|
T makeOrderedPair(A* obj1, B* obj2)
|
||||||
{
|
{
|
||||||
@@ -41,7 +58,7 @@ Canvas::~Canvas()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// Методы изменения режима
|
// Методы изменения режима и масштаба
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
void Canvas::changeMode(Mode _mode)
|
void Canvas::changeMode(Mode _mode)
|
||||||
@@ -141,7 +158,6 @@ bool Canvas::areAlreadyPerpendicular(Line* line1, Line* line2)
|
|||||||
// Методы работы с ограничениями
|
// Методы работы с ограничениями
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
// TODO - переделать в bool для отображения статуса выполнения
|
|
||||||
void Canvas::remove_constraint(int tag)
|
void Canvas::remove_constraint(int tag)
|
||||||
{
|
{
|
||||||
auto it = C_Info.find(tag);
|
auto it = C_Info.find(tag);
|
||||||
@@ -380,9 +396,9 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Линии уже параллельны - сообщаем об ошибке
|
// Линии уже параллельны - сообщаем об ошибке
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
qDebug() << "Line" << current_line << "and" << found << "are parallel. Abort!";
|
qDebug() << "Line" << current_line << "and" << found << "are parallel. Abort!";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QMessageBox::warning(this,
|
QMessageBox::warning(this,
|
||||||
QString("Wrong"),
|
QString("Wrong"),
|
||||||
@@ -553,10 +569,10 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
|
|||||||
}
|
}
|
||||||
solve_for_canvas();
|
solve_for_canvas();
|
||||||
}
|
}
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
else
|
else
|
||||||
showObjectTag(WIDGET_POSITION);
|
showObjectTag(WIDGET_POSITION);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::mouseReleaseEvent(QMouseEvent* event)
|
void Canvas::mouseReleaseEvent(QMouseEvent* event)
|
||||||
@@ -637,6 +653,10 @@ void Canvas::paintEvent(QPaintEvent* event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Обработчики событий клавиатуры и колесика мыши
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
void Canvas::keyPressEvent(QKeyEvent* event)
|
void Canvas::keyPressEvent(QKeyEvent* event)
|
||||||
{
|
{
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
@@ -734,11 +754,15 @@ void Canvas::showObjectTag(QPointF pos)
|
|||||||
.arg(info_line.length());
|
.arg(info_line.length());
|
||||||
QToolTip::showText(mapToGlobal(pos.toPoint()), Text, this);
|
QToolTip::showText(mapToGlobal(pos.toPoint()), Text, this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
QToolTip::hideText();
|
QToolTip::hideText();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Вспомогательные методы
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
QPointF Canvas::screenToLogical(const QPointF& screenPos) const
|
QPointF Canvas::screenToLogical(const QPointF& screenPos) const
|
||||||
{
|
{
|
||||||
QPointF logical = screenPos;
|
QPointF logical = screenPos;
|
||||||
@@ -787,13 +811,13 @@ void Canvas::solve_for_canvas()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag) {
|
if (flag) {
|
||||||
QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!"));
|
QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!"));
|
||||||
remove_constraint(constraints_count - 1);
|
remove_constraint(constraints_count - 1);
|
||||||
C_Info.erase(constraints_count - 1);
|
C_Info.erase(constraints_count - 1);
|
||||||
constraints_count--;
|
constraints_count--;
|
||||||
}
|
}
|
||||||
after_constraint = false;
|
after_constraint = false;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|||||||
199
Canvas.h
199
Canvas.h
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
constexpr auto EPS = 1e-9;
|
constexpr auto EPS = 1e-9;
|
||||||
constexpr auto ZOOM_STEP = 0.05;
|
constexpr auto ZOOM_STEP = 0.05;
|
||||||
|
|
||||||
@@ -22,7 +23,9 @@ using namespace GCS;
|
|||||||
// Типы и перечисления
|
// Типы и перечисления
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
/// Режимы работы с холстом
|
/**
|
||||||
|
* @brief Режимы работы с холстом
|
||||||
|
*/
|
||||||
enum class Mode : int
|
enum class Mode : int
|
||||||
{
|
{
|
||||||
None = 0, ///< Режим отсутствия действия
|
None = 0, ///< Режим отсутствия действия
|
||||||
@@ -30,8 +33,8 @@ enum class Mode : int
|
|||||||
Parallel = 2, ///< Режим задания параллельности
|
Parallel = 2, ///< Режим задания параллельности
|
||||||
Coincedent = 3, ///< Режим задания совпадения точек
|
Coincedent = 3, ///< Режим задания совпадения точек
|
||||||
Horizontal = 4, ///< Режим задания горизонтальности
|
Horizontal = 4, ///< Режим задания горизонтальности
|
||||||
Vertical = 5, ///< Режим задания вертикальности
|
Vertical = 5, ///< Режим задания вертикальности
|
||||||
Perpendicular = 6
|
Perpendicular = 6 ///< Режим задания перпендикулярности
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Удобный тип для хранения пары параллельных линий (порядок не важен)
|
/// Удобный тип для хранения пары параллельных линий (порядок не важен)
|
||||||
@@ -44,123 +47,245 @@ using PointPair = std::pair<Point*, Point*>;
|
|||||||
// Основной класс холста
|
// Основной класс холста
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Класс холста для рисования геометрических фигур с ограничениями
|
||||||
|
*
|
||||||
|
* Класс предоставляет функциональность для создания и редактирования
|
||||||
|
* геометрических объектов с применением ограничений через геометрический
|
||||||
|
* солвер FreeCAD GCS.
|
||||||
|
*/
|
||||||
class Canvas : public QWidget
|
class Canvas : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Конструктор класса Canvas
|
||||||
|
* @param parent Родительский виджет
|
||||||
|
*/
|
||||||
explicit Canvas(QWidget* parent = nullptr);
|
explicit Canvas(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Деструктор класса Canvas
|
||||||
|
*/
|
||||||
~Canvas() override;
|
~Canvas() override;
|
||||||
|
|
||||||
/// Изменить текущий режим работы
|
/**
|
||||||
|
* @brief Изменить текущий режим работы
|
||||||
|
* @param newMode Новый режим работы
|
||||||
|
*/
|
||||||
void changeMode(Mode newMode);
|
void changeMode(Mode newMode);
|
||||||
|
|
||||||
/// Увеличить масштаб
|
/**
|
||||||
|
* @brief Увеличить масштаб
|
||||||
|
*
|
||||||
|
* Увеличивает масштаб отображения на 5%
|
||||||
|
*/
|
||||||
void zoomIn();
|
void zoomIn();
|
||||||
|
|
||||||
/// Уменьшить масштаб
|
/**
|
||||||
|
* @brief Уменьшить масштаб
|
||||||
|
*
|
||||||
|
* Уменьшает масштаб отображения на 5%
|
||||||
|
*/
|
||||||
void zoomOut();
|
void zoomOut();
|
||||||
|
|
||||||
/// Сбросить масштаб к 100%
|
/**
|
||||||
|
* @brief Сбросить масштаб к 100%
|
||||||
|
*/
|
||||||
void zoomReset();
|
void zoomReset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Обработчики событий Qt
|
/**
|
||||||
|
* @brief Обработчик нажатия кнопки мыши
|
||||||
|
* @param event Событие мыши
|
||||||
|
*/
|
||||||
void mousePressEvent(QMouseEvent* event) override;
|
void mousePressEvent(QMouseEvent* event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик перемещения мыши
|
||||||
|
* @param event Событие мыши
|
||||||
|
*/
|
||||||
void mouseMoveEvent(QMouseEvent* event) override;
|
void mouseMoveEvent(QMouseEvent* event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик отпускания кнопки мыши
|
||||||
|
* @param event Событие мыши
|
||||||
|
*/
|
||||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик вращения колесика мыши
|
||||||
|
* @param event Событие колесика мыши
|
||||||
|
*/
|
||||||
void wheelEvent(QWheelEvent* event) override;
|
void wheelEvent(QWheelEvent* event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик события отрисовки
|
||||||
|
* @param event Событие отрисовки
|
||||||
|
*/
|
||||||
void paintEvent(QPaintEvent* event) override;
|
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;
|
void leaveEvent(QEvent* event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
|
/**
|
||||||
|
* @brief Показать тег объекта под курсором (только в режиме отладки)
|
||||||
|
* @param pos Позиция курсора на экране
|
||||||
|
*/
|
||||||
void showObjectTag(QPointF pos);
|
void showObjectTag(QPointF pos);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Преобразовать координаты экрана в логические координаты
|
||||||
|
* @param screenPos Координаты на экране
|
||||||
|
* @return Логические координаты
|
||||||
|
*/
|
||||||
QPointF screenToLogical(const QPointF& screenPos) const;
|
QPointF screenToLogical(const QPointF& screenPos) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить группу совпадающих точек
|
||||||
|
* @param p Базовая точка
|
||||||
|
*/
|
||||||
void getCoincidentGroup(Point* p);
|
void getCoincidentGroup(Point* p);
|
||||||
|
|
||||||
// ====================== Методы поиска и выбора ======================
|
// ====================== Методы поиска и выбора ======================
|
||||||
|
|
||||||
/// Найти линию под указанной позицией
|
/**
|
||||||
|
* @brief Найти линию под указанной позицией
|
||||||
|
* @param pos Позиция для поиска
|
||||||
|
* @param tolerance Допуск поиска
|
||||||
|
* @return Найденная линия или nullptr
|
||||||
|
*/
|
||||||
Line* findAt(QPointF& pos, qreal tolerance = 5.0);
|
Line* findAt(QPointF& pos, qreal tolerance = 5.0);
|
||||||
|
|
||||||
/// Найти точку в указанной позиции с заданной точностью
|
/**
|
||||||
|
* @brief Найти точку в указанной позиции
|
||||||
|
* @param position Позиция для поиска
|
||||||
|
* @param tolerance Допуск поиска
|
||||||
|
* @return Найденная точка или nullptr
|
||||||
|
*/
|
||||||
Point* findPointAt(QPointF position, qreal tolerance = 5.0);
|
Point* findPointAt(QPointF position, qreal tolerance = 5.0);
|
||||||
|
|
||||||
/// Проверить, совпадают ли две точки (ограничение P2P)
|
/**
|
||||||
|
* @brief Проверить, совпадают ли две точки (ограничение P2P)
|
||||||
|
* @param point1 Первая точка
|
||||||
|
* @param point2 Вторая точка
|
||||||
|
* @return true если точки совпадают, иначе false
|
||||||
|
*/
|
||||||
bool areCoincident(Point* point1, Point* point2);
|
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);
|
bool areHorizontalVertical(Point* point1, Point* point2, bool mode);
|
||||||
|
|
||||||
/// Проверить, является ли линия горизонтальной
|
/**
|
||||||
|
* @brief Проверить, является ли линия горизонтальной
|
||||||
|
* @param line Линия для проверки
|
||||||
|
* @return true если линия горизонтальна, иначе false
|
||||||
|
*/
|
||||||
bool isLineHorizontal(Line* line);
|
bool isLineHorizontal(Line* line);
|
||||||
|
|
||||||
/// Проверить, является ли линия вертикальной
|
/**
|
||||||
|
* @brief Проверить, является ли линия вертикальной
|
||||||
|
* @param line Линия для проверки
|
||||||
|
* @return true если линия вертикальна, иначе false
|
||||||
|
*/
|
||||||
bool isLineVertical(Line* line);
|
bool isLineVertical(Line* line);
|
||||||
|
|
||||||
/// Проверить, являются ли две линии уже параллельными (дубликат ограничения)
|
/**
|
||||||
|
* @brief Проверить, являются ли две линии уже параллельными
|
||||||
|
* @param line1 Первая линия
|
||||||
|
* @param line2 Вторая линия
|
||||||
|
* @return true если линии уже параллельны, иначе false
|
||||||
|
*/
|
||||||
bool areAlreadyParallel(Line* line1, Line* line2);
|
bool areAlreadyParallel(Line* line1, Line* line2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Проверить, являются ли две линии уже перпендикулярными
|
||||||
|
* @param line1 Первая линия
|
||||||
|
* @param line2 Вторая линия
|
||||||
|
* @return true если линии уже перпендикулярны, иначе false
|
||||||
|
*/
|
||||||
bool areAlreadyPerpendicular(Line* line1, Line* line2);
|
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 }; ///< Точка, которую перемещают
|
Point* draggedPoint{ nullptr }; ///< Точка, которую перемещают
|
||||||
Line* draggedLine{ nullptr }; ///< Линия, которую перемещают
|
Line* draggedLine{ nullptr }; ///< Линия, которую перемещают
|
||||||
QPointF dragOffset; ///< Смещение при начале перемещения
|
QPointF dragOffset; ///< Смещение при начале перемещения
|
||||||
|
|
||||||
// ====================== Данные геометрической системы ======================
|
// ====================== Данные геометрической системы ======================
|
||||||
|
|
||||||
System sys; ///< Геометрический солвер
|
System sys; ///< Геометрический солвер
|
||||||
QVector<Line*> lines; ///< Завершённые линии
|
QVector<Line*> lines; ///< Завершённые линии
|
||||||
QVector<Point*> points; ///< Все точки сцены
|
QVector<Point*> points; ///< Все точки сцены
|
||||||
std::vector<double*> params; ///< Все параметры, передаваемые в солвер
|
std::vector<double*> params; ///< Все параметры, передаваемые в солвер
|
||||||
|
|
||||||
// ====================== Коллекции ограничений ======================
|
// ====================== Коллекции ограничений ======================
|
||||||
|
|
||||||
std::set<LinePair> parallelPairs; ///< Пары параллельных линий
|
std::set<LinePair> parallelPairs; ///< Пары параллельных линий
|
||||||
std::set<LinePair> perpendicularPairs;
|
std::set<LinePair> perpendicularPairs; ///< Пары перпендикулярных линий
|
||||||
std::set<PointPair> P2Ppairs; ///< Пары совпадающих точек
|
std::set<PointPair> P2Ppairs; ///< Пары совпадающих точек
|
||||||
std::set<PointPair> HORIZ_pairs; ///< Пары точек горизонтальных линий
|
std::set<PointPair> HORIZ_pairs; ///< Пары точек горизонтальных линий
|
||||||
std::set<PointPair> VERT_pairs; ///< Пары точек вертикальных линий
|
std::set<PointPair> VERT_pairs; ///< Пары точек вертикальных линий
|
||||||
|
|
||||||
// ====================== Временные данные для режимов ======================
|
// ====================== Временные данные для режимов ======================
|
||||||
|
|
||||||
Line* current_line{ nullptr }; ///< Текущая линия в режимах рисования/параллельности
|
Line* current_line{ nullptr }; ///< Текущая линия в режимах рисования/параллельности
|
||||||
Point* firstPoint{ nullptr }; ///< Первая точка в режиме совпадения
|
Point* firstPoint{ nullptr }; ///< Первая точка в режиме совпадения
|
||||||
Mode mode{ Mode::None }; ///< Текущий режим работы
|
Mode mode{ Mode::None }; ///< Текущий режим работы
|
||||||
|
|
||||||
// ====================== Флаги состояния ======================
|
// ====================== Флаги состояния ======================
|
||||||
|
|
||||||
bool after_constraint{ false }; ///< Флаг, что только что добавлено ограничение
|
bool after_constraint{ false }; ///< Флаг, что только что добавлено ограничение
|
||||||
double scaleFactor { 1.0 };
|
double scaleFactor{ 1.0 }; ///< Текущий масштаб отображения (1.0 = 100%)
|
||||||
|
|
||||||
// ====================== Счётчики ======================
|
// ====================== Счётчики ======================
|
||||||
|
|
||||||
int obj_count{ 0 }; ///< Счётчик объектов (для тегов)
|
int obj_count{ 0 }; ///< Счётчик объектов (для тегов)
|
||||||
int constraints_count{ 0 }; ///< Счётчик ограничений (для тегов)
|
int constraints_count{ 0 }; ///< Счётчик ограничений (для тегов)
|
||||||
|
|
||||||
// ====================== Информация о последнем ограничении ======================
|
// ====================== Информация о последнем ограничении ======================
|
||||||
|
/**
|
||||||
/// Структура для хранения информации о последнем добавленном ограничении
|
* @brief Структура для хранения информации об ограничении
|
||||||
/// (используется для отката при ошибке солвера)
|
*/
|
||||||
|
|
||||||
struct ConstraintInfo {
|
struct ConstraintInfo {
|
||||||
Mode mode;
|
Mode mode; ///< Тип ограничения
|
||||||
std::variant<LinePair, PointPair> data;
|
std::variant<LinePair, PointPair> data; ///< Данные ограничения
|
||||||
};
|
};
|
||||||
std::map<int, ConstraintInfo> C_Info;
|
|
||||||
|
|
||||||
void solve_for_canvas();
|
std::map<int, ConstraintInfo> C_Info; ///< Карта информации об ограничениях
|
||||||
void clearCanvas();
|
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user