Улучшение читабельности кода
This commit is contained in:
240
Canvas.cpp
240
Canvas.cpp
@@ -1,23 +1,84 @@
|
|||||||
#include "Canvas.h"
|
#include "Canvas.h"
|
||||||
|
|
||||||
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) считались одинаковыми)
|
||||||
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)
|
||||||
{
|
{
|
||||||
return (obj1 < obj2) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1);
|
return (obj1 < obj2) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Конструктор и деструктор
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
Canvas::Canvas(QWidget* parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
sys = System();
|
||||||
|
setMouseTracking(true);
|
||||||
|
setBackgroundRole(QPalette::Base);
|
||||||
|
setAutoFillBackground(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas::~Canvas()
|
||||||
|
{
|
||||||
|
// Очистка динамически выделенной памяти
|
||||||
|
|
||||||
|
for (double* param : params) {
|
||||||
|
delete param;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Point* pt : points) {
|
||||||
|
delete pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Line* line : lines) {
|
||||||
|
delete line;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Очистка контейнеров
|
||||||
|
params.clear();
|
||||||
|
points.clear();
|
||||||
|
lines.clear();
|
||||||
|
|
||||||
|
parallelPairs.clear();
|
||||||
|
P2Ppairs.clear();
|
||||||
|
VERT_pairs.clear();
|
||||||
|
HORIZ_pairs.clear();
|
||||||
|
|
||||||
|
// Очистка временных указателей
|
||||||
|
if (current_line)
|
||||||
|
delete current_line;
|
||||||
|
if (firstPoint)
|
||||||
|
delete firstPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Методы изменения режима
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
void Canvas::changeMode(Mode _mode)
|
void Canvas::changeMode(Mode _mode)
|
||||||
{
|
{
|
||||||
mode = _mode;
|
mode = _mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// ===================================================================
|
||||||
|
// Методы поиска и проверки
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
Line* Canvas::findAt(QPointF& pos)
|
Line* Canvas::findAt(QPointF& pos)
|
||||||
{
|
{
|
||||||
|
// TODO: реализовать проверку, находится ли точка на линии
|
||||||
for (Line* line : lines) {
|
for (Line* line : lines) {
|
||||||
if (line->contains(pos)) {
|
if (line->contains(pos)) {
|
||||||
return line;
|
return line;
|
||||||
@@ -46,25 +107,27 @@ bool Canvas::areCoincident(Point* p1, Point* p2)
|
|||||||
return P2Ppairs.count(makeOrderedPair<PointPair>(p1, p2));
|
return P2Ppairs.count(makeOrderedPair<PointPair>(p1, p2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// False - Horizonta, True - Vertical
|
|
||||||
bool Canvas::areHorizontalVertical(Point* p1, Point* p2, bool mode)
|
bool Canvas::areHorizontalVertical(Point* p1, Point* p2, bool mode)
|
||||||
{
|
{
|
||||||
|
// mode = false - проверка горизонтальности
|
||||||
|
// mode = true - проверка вертикальности
|
||||||
if (!mode)
|
if (!mode)
|
||||||
return HORIZ_pairs.count(makeOrderedPair<PointPair>(p1, p2));
|
return HORIZ_pairs.count(makeOrderedPair<PointPair>(p1, p2));
|
||||||
else
|
else
|
||||||
return VERT_pairs.count(makeOrderedPair<PointPair>(p1, p2));
|
return VERT_pairs.count(makeOrderedPair<PointPair>(p1, p2));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::isLineHorizontal(Line* line)
|
bool Canvas::isLineHorizontal(Line* line)
|
||||||
{
|
{
|
||||||
if (!line || !line->start_ref || !line->end_ref) return false;
|
if (!line || !line->start_ref || !line->end_ref)
|
||||||
|
return false;
|
||||||
return HORIZ_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref));
|
return HORIZ_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::isLineVertical(Line* line)
|
bool Canvas::isLineVertical(Line* line)
|
||||||
{
|
{
|
||||||
if (!line || !line->start_ref || !line->end_ref) return false;
|
if (!line || !line->start_ref || !line->end_ref)
|
||||||
|
return false;
|
||||||
return VERT_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref));
|
return VERT_pairs.count(makeOrderedPair<PointPair>(line->start_ref, line->end_ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,10 +136,16 @@ bool Canvas::areAlreadyParallel(Line* l1, Line* l2)
|
|||||||
return parallelPairs.count(makeOrderedPair<LinePair>(l1, l2));
|
return parallelPairs.count(makeOrderedPair<LinePair>(l1, l2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Методы работы с ограничениями
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
void Canvas::remove_constraints()
|
void Canvas::remove_constraints()
|
||||||
{
|
{
|
||||||
|
// Удаляем ограничение из солвера
|
||||||
sys.clearByTag(constraints_count - 1);
|
sys.clearByTag(constraints_count - 1);
|
||||||
|
|
||||||
|
// Удаляем из соответствующих контейнеров в зависимости от типа ограничения
|
||||||
switch (lastConstraint.mode) {
|
switch (lastConstraint.mode) {
|
||||||
case Mode::Horizontal: {
|
case Mode::Horizontal: {
|
||||||
if (auto* pair = std::get_if<PointPair>(&lastConstraint.data)) {
|
if (auto* pair = std::get_if<PointPair>(&lastConstraint.data)) {
|
||||||
@@ -106,19 +175,25 @@ void Canvas::remove_constraints()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Сбрасываем информацию о последнем ограничении
|
||||||
lastConstraint.mode = Mode::None;
|
lastConstraint.mode = Mode::None;
|
||||||
constraints_count--;
|
constraints_count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Обработчики событий мыши
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
void Canvas::mousePressEvent(QMouseEvent* event)
|
void Canvas::mousePressEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
QPointF scene = event->pos();
|
QPointF scene = event->pos();
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
qDebug() << "Scene point in" << scene.x() << scene.y();
|
qDebug() << "Scene point in" << scene.x() << scene.y();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (mode == Mode::None){
|
// ====================== Режим None: перемещение объектов ======================
|
||||||
|
if (mode == Mode::None) {
|
||||||
Point* p = findPointAt(scene);
|
Point* p = findPointAt(scene);
|
||||||
if (p) {
|
if (p) {
|
||||||
draggedPoint = p;
|
draggedPoint = p;
|
||||||
@@ -138,10 +213,12 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== Режим Horizontal/Vertical ======================
|
||||||
else if (mode == Mode::Horizontal || mode == Mode::Vertical) {
|
else if (mode == Mode::Horizontal || mode == Mode::Vertical) {
|
||||||
Line* found = findAt(scene);
|
Line* found = findAt(scene);
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
|
// Проверка: если линия уже вертикальна, нельзя сделать её горизонтальной
|
||||||
if (mode == Mode::Horizontal && isLineVertical(found)) {
|
if (mode == Mode::Horizontal && isLineVertical(found)) {
|
||||||
QMessageBox::warning(this,
|
QMessageBox::warning(this,
|
||||||
QString("Невозможно"),
|
QString("Невозможно"),
|
||||||
@@ -183,19 +260,24 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
mode = Mode::None;
|
mode = Mode::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== Режим DrawingLine: рисование линий ======================
|
||||||
else if (mode == Mode::DrawingLine) {
|
else if (mode == Mode::DrawingLine) {
|
||||||
if (!current_line) {
|
if (!current_line) {
|
||||||
|
// Первый клик: создаем новую линию
|
||||||
current_line = new Line();
|
current_line = new Line();
|
||||||
current_line->set_tag(obj_count);
|
current_line->set_tag(obj_count);
|
||||||
|
|
||||||
|
// Создаем координаты для точек линии
|
||||||
double* x1 = new double(scene.x());
|
double* x1 = new double(scene.x());
|
||||||
double* y1 = new double(scene.y());
|
double* y1 = new double(scene.y());
|
||||||
|
|
||||||
double* x2 = new double(scene.x());
|
double* x2 = new double(scene.x());
|
||||||
double* y2 = new double(scene.y());
|
double* y2 = new double(scene.y());
|
||||||
|
|
||||||
|
// Добавляем параметры в солвер
|
||||||
params.push_back(x1);
|
params.push_back(x1);
|
||||||
params.push_back(y1);
|
params.push_back(y1);
|
||||||
|
|
||||||
|
// Создаем точки и привязываем к линии
|
||||||
points.push_back(new Point(x1, y1, obj_count));
|
points.push_back(new Point(x1, y1, obj_count));
|
||||||
points.push_back(new Point(x2, y2, obj_count));
|
points.push_back(new Point(x2, y2, obj_count));
|
||||||
|
|
||||||
@@ -207,15 +289,20 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
current_line->end_ref = points[points.size() - 1];
|
current_line->end_ref = points[points.size() - 1];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// Второй клик: завершаем рисование линии
|
||||||
*current_line->p2.x = scene.x();
|
*current_line->p2.x = scene.x();
|
||||||
*current_line->p2.y = scene.y();
|
*current_line->p2.y = scene.y();
|
||||||
|
|
||||||
// Временный ограничитель длины линии
|
// Проверка минимальной длины линии
|
||||||
double len = sqrt(pow(*current_line->p2.x - *current_line->p1.x, 2) + pow(*current_line->p2.y - *current_line->p1.y, 2));
|
double len = sqrt(pow(*current_line->p2.x - *current_line->p1.x, 2) +
|
||||||
|
pow(*current_line->p2.y - *current_line->p1.y, 2));
|
||||||
|
|
||||||
if (len < 10) {
|
if (len < 10) {
|
||||||
|
// Линия слишком короткая - отменяем создание
|
||||||
delete current_line;
|
delete current_line;
|
||||||
current_line = nullptr;
|
current_line = nullptr;
|
||||||
|
|
||||||
|
// Удаляем созданные точки и параметры
|
||||||
delete points.back();
|
delete points.back();
|
||||||
points.pop_back();
|
points.pop_back();
|
||||||
delete points.back();
|
delete points.back();
|
||||||
@@ -226,14 +313,17 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
params.pop_back();
|
params.pop_back();
|
||||||
|
|
||||||
mode = Mode::None;
|
mode = Mode::None;
|
||||||
QMessageBox::critical(this, "WHOOPS", "Sorry, your line is very short", QMessageBox::Ok);
|
QMessageBox::critical(this, "WHOOPS",
|
||||||
|
"Sorry, your line is very short", QMessageBox::Ok);
|
||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Добавляем координаты второй точки в параметры
|
||||||
params.push_back(current_line->p2.x);
|
params.push_back(current_line->p2.x);
|
||||||
params.push_back(current_line->p2.y);
|
params.push_back(current_line->p2.y);
|
||||||
|
|
||||||
|
// Завершаем создание линии
|
||||||
lines.append(current_line);
|
lines.append(current_line);
|
||||||
current_line = nullptr;
|
current_line = nullptr;
|
||||||
mode = Mode::None;
|
mode = Mode::None;
|
||||||
@@ -244,6 +334,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== Режим Parallel: задание параллельности ======================
|
||||||
else if (mode == Mode::Parallel) {
|
else if (mode == Mode::Parallel) {
|
||||||
Line* found = findAt(scene);
|
Line* found = findAt(scene);
|
||||||
|
|
||||||
@@ -253,18 +344,21 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Первый клик: выбираем первую линию
|
||||||
if (!current_line) {
|
if (!current_line) {
|
||||||
current_line = found;
|
current_line = found;
|
||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found == current_line){
|
// Повторный клик на ту же линию: сброс выбора
|
||||||
|
if (found == current_line) {
|
||||||
current_line = nullptr;
|
current_line = nullptr;
|
||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Второй клик на другую линию: добавляем ограничение параллельности
|
||||||
if (!areAlreadyParallel(found, current_line)) {
|
if (!areAlreadyParallel(found, current_line)) {
|
||||||
auto pair = makeOrderedPair<LinePair>(found, current_line);
|
auto pair = makeOrderedPair<LinePair>(found, current_line);
|
||||||
sys.addConstraintParallel(*found, *current_line, constraints_count++);
|
sys.addConstraintParallel(*found, *current_line, constraints_count++);
|
||||||
@@ -278,10 +372,11 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
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"),
|
||||||
QString("Parallel lines can not be more parallel!"),
|
QString("Parallel lines can not be more parallel!"),
|
||||||
@@ -291,10 +386,9 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== Режим Coincedent: совпадение точек ======================
|
||||||
else if (mode == Mode::Coincedent) {
|
else if (mode == Mode::Coincedent) {
|
||||||
Point* clickedPoint = findPointAt(scene);
|
Point* clickedPoint = findPointAt(scene);
|
||||||
if (!clickedPoint) {
|
if (!clickedPoint) {
|
||||||
@@ -303,27 +397,32 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Первый клик: выбираем первую точку
|
||||||
if (!firstPoint) {
|
if (!firstPoint) {
|
||||||
firstPoint = clickedPoint;
|
firstPoint = clickedPoint;
|
||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Повторный клик на ту же точку: сброс выбора
|
||||||
if (clickedPoint == firstPoint) {
|
if (clickedPoint == firstPoint) {
|
||||||
firstPoint = nullptr;
|
firstPoint = nullptr;
|
||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Line *l1 = nullptr, *l2 = nullptr;
|
// Находим линии, к которым принадлежат точки
|
||||||
|
Line* l1 = nullptr;
|
||||||
|
Line* l2 = nullptr;
|
||||||
for (Line* l : lines) {
|
for (Line* l : lines) {
|
||||||
if (l->start_ref == firstPoint || l->end_ref == firstPoint) l1 = l;
|
if (l->start_ref == firstPoint || l->end_ref == firstPoint)
|
||||||
if (l->start_ref == clickedPoint || l->end_ref == clickedPoint) l2 = l;
|
l1 = l;
|
||||||
|
if (l->start_ref == clickedPoint || l->end_ref == clickedPoint)
|
||||||
|
l2 = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (l1 == l2 && l1 && l2 ||
|
// Проверка на невозможность ограничения
|
||||||
(areCoincident(firstPoint, clickedPoint)))
|
if ((l1 == l2 && l1 && l2) || areCoincident(firstPoint, clickedPoint)) {
|
||||||
{
|
|
||||||
QMessageBox::critical(this, QString("NO!"), QString("P2P failed"));
|
QMessageBox::critical(this, QString("NO!"), QString("P2P failed"));
|
||||||
firstPoint = nullptr;
|
firstPoint = nullptr;
|
||||||
mode = Mode::None;
|
mode = Mode::None;
|
||||||
@@ -331,6 +430,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Добавляем ограничение совпадения точек
|
||||||
sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++);
|
sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++);
|
||||||
auto pair = makeOrderedPair<PointPair>(clickedPoint, firstPoint);
|
auto pair = makeOrderedPair<PointPair>(clickedPoint, firstPoint);
|
||||||
P2Ppairs.insert(pair);
|
P2Ppairs.insert(pair);
|
||||||
@@ -347,25 +447,30 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
|||||||
|
|
||||||
void Canvas::mouseMoveEvent(QMouseEvent* event)
|
void Canvas::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
|
// ====================== Перемещение точки ======================
|
||||||
if (draggedPoint) {
|
if (draggedPoint) {
|
||||||
QPointF pos = event->pos() - dragOffset;
|
QPointF pos = event->pos() - dragOffset;
|
||||||
|
|
||||||
|
// Обновляем все связанные точки (совпадающие, горизонтальные, вертикальные)
|
||||||
for (Point* pair : points) {
|
for (Point* pair : points) {
|
||||||
if (areCoincident(draggedPoint, pair)) {
|
if (areCoincident(draggedPoint, pair)) {
|
||||||
*pair->x = pos.x();
|
*pair->x = pos.x();
|
||||||
*pair->y = pos.y();
|
*pair->y = pos.y();
|
||||||
}
|
}
|
||||||
if (areHorizontalVertical(draggedPoint, pair, true)) {
|
if (areHorizontalVertical(draggedPoint, pair, true)) { // vertical
|
||||||
*pair->x = pos.x();
|
*pair->x = pos.x();
|
||||||
}
|
}
|
||||||
if (areHorizontalVertical(draggedPoint, pair, false)) {
|
if (areHorizontalVertical(draggedPoint, pair, false)) { // horizontal
|
||||||
*pair->y = pos.y();
|
*pair->y = pos.y();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*draggedPoint->x = pos.x();
|
*draggedPoint->x = pos.x();
|
||||||
*draggedPoint->y = pos.y();
|
*draggedPoint->y = pos.y();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== Перемещение линии ======================
|
||||||
else if (draggedLine) {
|
else if (draggedLine) {
|
||||||
QPointF newCenter = event->pos() - dragOffset;
|
QPointF newCenter = event->pos() - dragOffset;
|
||||||
QPointF oldCenter(
|
QPointF oldCenter(
|
||||||
@@ -373,10 +478,11 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
|
|||||||
(*draggedLine->p1.y + *draggedLine->p2.y) / 2.0
|
(*draggedLine->p1.y + *draggedLine->p2.y) / 2.0
|
||||||
);
|
);
|
||||||
|
|
||||||
// Смещение для перемещения
|
// Вычисляем смещение
|
||||||
double dx = newCenter.x() - oldCenter.x();
|
double dx = newCenter.x() - oldCenter.x();
|
||||||
double dy = newCenter.y() - oldCenter.y();
|
double dy = newCenter.y() - oldCenter.y();
|
||||||
|
|
||||||
|
// Перемещаем обе точки линии
|
||||||
Point* linePoints[2] = { draggedLine->start_ref, draggedLine->end_ref };
|
Point* linePoints[2] = { draggedLine->start_ref, draggedLine->end_ref };
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
@@ -385,6 +491,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
|
|||||||
*currentPoint->x += dx;
|
*currentPoint->x += dx;
|
||||||
*currentPoint->y += dy;
|
*currentPoint->y += dy;
|
||||||
|
|
||||||
|
// Обновляем совпадающие точки
|
||||||
for (Point* pair : points) {
|
for (Point* pair : points) {
|
||||||
if (pair != currentPoint && areCoincident(currentPoint, pair)) {
|
if (pair != currentPoint && areCoincident(currentPoint, pair)) {
|
||||||
*pair->x = *currentPoint->x;
|
*pair->x = *currentPoint->x;
|
||||||
@@ -399,6 +506,8 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
|
|||||||
|
|
||||||
void Canvas::mouseReleaseEvent(QMouseEvent* event)
|
void Canvas::mouseReleaseEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
|
||||||
if (draggedPoint) {
|
if (draggedPoint) {
|
||||||
draggedPoint = nullptr;
|
draggedPoint = nullptr;
|
||||||
update();
|
update();
|
||||||
@@ -410,44 +519,56 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::paintEvent(QPaintEvent*)
|
// ===================================================================
|
||||||
|
// Метод отрисовки
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
void Canvas::paintEvent(QPaintEvent* event)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
p.setRenderHint(QPainter::Antialiasing, true);
|
p.setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
|
||||||
// === Решаем систему один раз ===
|
// ====================== Решение системы уравнений ======================
|
||||||
if (!params.empty()) {
|
if (!params.empty()) {
|
||||||
int res = sys.solve(params);
|
int res = sys.solve(params);
|
||||||
if (res == SolveStatus::Success || res == SolveStatus::Converged) {
|
if (res == SolveStatus::Success || res == SolveStatus::Converged) {
|
||||||
sys.applySolution();
|
sys.applySolution();
|
||||||
}
|
}
|
||||||
else if (res == SolveStatus::Failed && after_constraint){
|
else if (res == SolveStatus::Failed && after_constraint) {
|
||||||
QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!"));
|
// Ошибка решения: удаляем последнее добавленное ограничение
|
||||||
|
QMessageBox::warning(this, QString("Error!"),
|
||||||
|
QString("Last constraint is unavailable!"));
|
||||||
remove_constraints();
|
remove_constraints();
|
||||||
}
|
}
|
||||||
after_constraint = false;
|
after_constraint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== Отрисовка линий ======================
|
||||||
for (Line* line : lines) {
|
for (Line* line : lines) {
|
||||||
bool isSelected = (mode == Mode::Parallel && line == current_line);
|
bool isSelected = (mode == Mode::Parallel && line == current_line);
|
||||||
|
|
||||||
|
// Настройка пера для линии
|
||||||
QPen linePen = isSelected ? QPen(Qt::red, 4) : QPen(Qt::black, 2);
|
QPen linePen = isSelected ? QPen(Qt::red, 4) : QPen(Qt::black, 2);
|
||||||
p.setPen(linePen);
|
p.setPen(linePen);
|
||||||
|
|
||||||
// Рисуем саму линию
|
// Рисуем линию
|
||||||
p.drawLine(QPointF(*line->p1.x, *line->p1.y),
|
p.drawLine(
|
||||||
QPointF(*line->p2.x, *line->p2.y));
|
QPointF(*line->p1.x, *line->p1.y),
|
||||||
|
QPointF(*line->p2.x, *line->p2.y)
|
||||||
|
);
|
||||||
|
|
||||||
// Рисуем концы — с учётом выделения
|
// Рисуем конечные точки линии
|
||||||
QBrush pointBrush = isSelected ? QBrush(Qt::red) : QBrush(Qt::darkBlue);
|
QBrush pointBrush = isSelected ? QBrush(Qt::red) : QBrush(Qt::darkBlue);
|
||||||
p.setBrush(pointBrush);
|
p.setBrush(pointBrush);
|
||||||
p.setPen(Qt::NoPen); // убираем обводку у кружков
|
p.setPen(Qt::NoPen);
|
||||||
|
|
||||||
p.drawEllipse(QPointF(*line->p1.x, *line->p1.y), 5, 5);
|
p.drawEllipse(QPointF(*line->p1.x, *line->p1.y), 5, 5);
|
||||||
p.drawEllipse(QPointF(*line->p2.x, *line->p2.y), 5, 5);
|
p.drawEllipse(QPointF(*line->p2.x, *line->p2.y), 5, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Подсветка выбранной точки в режиме Coincident ===
|
// ====================== Подсветка выбранной точки (режим Coincedent) ======================
|
||||||
if (mode == Mode::Coincedent && firstPoint) {
|
if (mode == Mode::Coincedent && firstPoint) {
|
||||||
QPointF pt(*firstPoint->x, *firstPoint->y);
|
QPointF pt(*firstPoint->x, *firstPoint->y);
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
@@ -459,50 +580,11 @@ void Canvas::paintEvent(QPaintEvent*)
|
|||||||
p.drawEllipse(pt, 4, 4); // маленький центр
|
p.drawEllipse(pt, 4, 4); // маленький центр
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Текущая рисуемая линия (DrawingLine) ===
|
// ====================== Отрисовка текущей линии (режим DrawingLine) ======================
|
||||||
if (current_line && mode == Mode::DrawingLine) {
|
if (current_line && mode == Mode::DrawingLine) {
|
||||||
// Концы текущей линии — синие точки
|
// Рисуем начальную точку текущей линии
|
||||||
p.setBrush(Qt::blue);
|
p.setBrush(Qt::blue);
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.y), 6, 6);
|
p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.y), 6, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas::Canvas(QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
sys = System();
|
|
||||||
setMouseTracking(true);
|
|
||||||
setBackgroundRole(QPalette::Base);
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Canvas::~Canvas()
|
|
||||||
{
|
|
||||||
for (double* param : params) {
|
|
||||||
delete param;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Point* pt : points) {
|
|
||||||
delete pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Line* line : lines) {
|
|
||||||
delete line;
|
|
||||||
}
|
|
||||||
|
|
||||||
params.clear();
|
|
||||||
points.clear();
|
|
||||||
lines.clear();
|
|
||||||
|
|
||||||
parallelPairs.clear();
|
|
||||||
P2Ppairs.clear();
|
|
||||||
VERT_pairs.clear();
|
|
||||||
HORIZ_pairs.clear();
|
|
||||||
|
|
||||||
if (current_line)
|
|
||||||
delete current_line;
|
|
||||||
if (firstPoint)
|
|
||||||
delete firstPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
120
Canvas.h
120
Canvas.h
@@ -14,18 +14,25 @@
|
|||||||
#include "GCS/GCS.h"
|
#include "GCS/GCS.h"
|
||||||
using namespace GCS;
|
using namespace GCS;
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Типы и перечисления
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
/// Режимы работы с холстом
|
||||||
enum class Mode : int
|
enum class Mode : int
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0, ///< Режим отсутствия действия
|
||||||
DrawingLine = 1,
|
DrawingLine = 1, ///< Режим рисования линии
|
||||||
Parallel = 2,
|
Parallel = 2, ///< Режим задания параллельности
|
||||||
Coincedent = 3,
|
Coincedent = 3, ///< Режим задания совпадения точек
|
||||||
Horizontal = 4,
|
Horizontal = 4, ///< Режим задания горизонтальности
|
||||||
Vertical = 5
|
Vertical = 5 ///< Режим задания вертикальности
|
||||||
};
|
};
|
||||||
|
|
||||||
// Удобный тип для хранения пары параллельных линий (порядок не важен)
|
/// Удобный тип для хранения пары параллельных линий (порядок не важен)
|
||||||
using LinePair = std::pair<Line*, Line*>;
|
using LinePair = std::pair<Line*, Line*>;
|
||||||
|
|
||||||
|
/// Удобный тип для хранения пары точек (порядок не важен)
|
||||||
using PointPair = std::pair<Point*, Point*>;
|
using PointPair = std::pair<Point*, Point*>;
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
@@ -40,54 +47,89 @@ public:
|
|||||||
explicit Canvas(QWidget* parent = nullptr);
|
explicit Canvas(QWidget* parent = nullptr);
|
||||||
~Canvas() override;
|
~Canvas() override;
|
||||||
|
|
||||||
|
/// Изменить текущий режим работы
|
||||||
void changeMode(Mode newMode);
|
void changeMode(Mode newMode);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Обработчики событий Qt
|
||||||
void mousePressEvent(QMouseEvent* event) override;
|
void mousePressEvent(QMouseEvent* event) override;
|
||||||
void mouseMoveEvent(QMouseEvent* event) override;
|
void mouseMoveEvent(QMouseEvent* event) override;
|
||||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
void paintEvent(QPaintEvent* event) override;
|
void paintEvent(QPaintEvent* event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ====================== Поиск и выбор ======================
|
// ====================== Методы поиска и выбора ======================
|
||||||
Line* findAt(QPointF&); // ищет линию под курсором
|
|
||||||
Point* findPointAt(QPointF, qreal tolerance = 5.0);
|
|
||||||
bool areCoincident(Point*, Point*);
|
|
||||||
bool areHorizontalVertical(Point*, Point*, bool);
|
|
||||||
bool isLineHorizontal(Line* line); // проверяет, горизонтальна ли линия
|
|
||||||
bool isLineVertical(Line* line); // проверяет, вертикальна ли линия
|
|
||||||
// ====================== Параллельность ======================
|
|
||||||
bool areAlreadyParallel(Line* l1, Line* l2); // проверка на дубликат
|
|
||||||
|
|
||||||
// ====================== Перемещение ======================
|
/// Найти линию под указанной позицией
|
||||||
Point* draggedPoint{ nullptr };
|
Line* findAt(QPointF& position);
|
||||||
Line* draggedLine{ nullptr };
|
|
||||||
QPointF dragOffset;
|
|
||||||
|
|
||||||
// ====================== Работа с парами ограничений ======================
|
/// Найти точку в указанной позиции с заданной точностью
|
||||||
|
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);
|
||||||
|
|
||||||
|
// ====================== Методы работы с ограничениями ======================
|
||||||
|
|
||||||
|
/// Удалить последние добавленные ограничения при ошибке солвера
|
||||||
void remove_constraints();
|
void remove_constraints();
|
||||||
// ====================== Данные сцены ======================
|
|
||||||
System sys; // геометрический солвер
|
|
||||||
QVector<Line*> lines; // завершённые линии
|
|
||||||
QVector<Point*> points; // все точки (для удобного доступа)
|
|
||||||
std::vector<double*> params; // все параметры, передаваемые в солвер
|
|
||||||
|
|
||||||
std::set<LinePair> parallelPairs; // уже запараллеленные пары (защита от дублей)
|
// ====================== Данные для перемещения объектов ======================
|
||||||
std::set<PointPair> P2Ppairs;
|
|
||||||
std::set<PointPair> HORIZ_pairs;
|
|
||||||
std::set<PointPair> VERT_pairs;
|
|
||||||
|
|
||||||
Line* current_line{ nullptr };
|
Point* draggedPoint{ nullptr }; ///< Точка, которую перемещают
|
||||||
Point* firstPoint{ nullptr };
|
Line* draggedLine{ nullptr }; ///< Линия, которую перемещают
|
||||||
Mode mode{ Mode::None };
|
QPointF dragOffset; ///< Смещение при начале перемещения
|
||||||
bool after_constraint{ false };
|
|
||||||
|
|
||||||
int obj_count{ 0 }; // тег для новых объектов
|
// ====================== Данные геометрической системы ======================
|
||||||
int constraints_count{ 0 }; // тег для новых ограничений
|
|
||||||
|
|
||||||
|
System sys; ///< Геометрический солвер
|
||||||
|
QVector<Line*> lines; ///< Завершённые линии
|
||||||
|
QVector<Point*> points; ///< Все точки сцены
|
||||||
|
std::vector<double*> params; ///< Все параметры, передаваемые в солвер
|
||||||
|
|
||||||
|
// ====================== Коллекции ограничений ======================
|
||||||
|
|
||||||
|
std::set<LinePair> parallelPairs; ///< Пары параллельных линий
|
||||||
|
std::set<PointPair> P2Ppairs; ///< Пары совпадающих точек
|
||||||
|
std::set<PointPair> HORIZ_pairs; ///< Пары точек горизонтальных линий
|
||||||
|
std::set<PointPair> 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 LastConstraint {
|
struct LastConstraint {
|
||||||
Mode mode{ Mode::None };
|
Mode mode{ Mode::None }; ///< Тип последнего ограничения
|
||||||
std::variant<LinePair, PointPair> data;
|
std::variant<LinePair, PointPair> data; ///< Данные ограничения
|
||||||
};
|
};
|
||||||
LastConstraint lastConstraint;
|
|
||||||
|
LastConstraint lastConstraint; ///< Информация о последнем добавленном ограничении
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user