diff --git a/Canvas.cpp b/Canvas.cpp index 1da4204..ed62825 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -7,7 +7,7 @@ static double dist_P2P(QPointF p1, QPointF p2) { template T makeOrderedPair(A* obj1, B* obj2) { - return (obj1->get_tag() < obj2->get_tag()) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1); + return (obj1 < obj2) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1); } void Canvas::changeMode(Mode _mode) @@ -46,9 +46,68 @@ bool Canvas::areCoincident(Point* p1, Point* p2) return P2Ppairs.count(makeOrderedPair(p1, p2)); } +// False - Horizonta, True - Vertical +bool Canvas::areHorizontalVertical(Point* p1, Point* p2, bool mode) +{ + if (!mode) + return HORIZ_pairs.count(makeOrderedPair(p1, p2)); + else + return VERT_pairs.count(makeOrderedPair(p1, p2)); + +} + +bool Canvas::isLineHorizontal(Line* line) +{ + if (!line || !line->start_ref || !line->end_ref) return false; + return HORIZ_pairs.count(makeOrderedPair(line->start_ref, line->end_ref)); +} + +bool Canvas::isLineVertical(Line* line) +{ + if (!line || !line->start_ref || !line->end_ref) return false; + return VERT_pairs.count(makeOrderedPair(line->start_ref, line->end_ref)); +} + bool Canvas::areAlreadyParallel(Line* l1, Line* l2) { - return parallelPairs.count(makeOrderedPair(l1, l2)) > 0; + return parallelPairs.count(makeOrderedPair(l1, l2)); +} + +void Canvas::remove_constraints() +{ + sys.clearByTag(constraints_count - 1); + + switch (lastConstraint.mode) { + case Mode::Horizontal: { + if (auto* pair = std::get_if(&lastConstraint.data)) { + HORIZ_pairs.erase(*pair); + } + break; + } + case Mode::Vertical: { + if (auto* pair = std::get_if(&lastConstraint.data)) { + VERT_pairs.erase(*pair); + } + break; + } + case Mode::Parallel: { + if (auto* pair = std::get_if(&lastConstraint.data)) { + parallelPairs.erase(*pair); + } + break; + } + case Mode::Coincedent: { + if (auto* pair = std::get_if(&lastConstraint.data)) { + P2Ppairs.erase(*pair); + } + break; + } + default: + break; + } + + lastConstraint.mode = Mode::None; + constraints_count--; } void Canvas::mousePressEvent(QMouseEvent* event) @@ -64,6 +123,18 @@ void Canvas::mousePressEvent(QMouseEvent* event) if (p) { draggedPoint = p; dragOffset = scene - QPointF(*p->x, *p->y); + return; + } + + Line* found = findAt(scene); + if (found) { + draggedLine = found; + QPointF lineCenter( + (*found->p1.x + *found->p2.x) / 2.0, + (*found->p1.y + *found->p2.y) / 2.0 + ); + dragOffset = scene - lineCenter; + return; } } @@ -71,14 +142,45 @@ void Canvas::mousePressEvent(QMouseEvent* event) Line* found = findAt(scene); if (found) { - if (mode == Mode::Horizontal) + if (mode == Mode::Horizontal && isLineVertical(found)) { + QMessageBox::warning(this, + QString("Невозможно"), + QString("Эта линия уже вертикальна и не может быть горизонтальной!"), + QMessageBox::Ok + ); + mode = Mode::None; + return; + } + + // Проверка: если линия уже горизонтальна, нельзя сделать её вертикальной + if (mode == Mode::Vertical && isLineHorizontal(found)) { + QMessageBox::warning(this, + QString("Невозможно"), + QString("Эта линия уже горизонтальна и не может быть вертикальной!"), + QMessageBox::Ok + ); + mode = Mode::None; + return; + } + + if (mode == Mode::Horizontal) { sys.addConstraintHorizontal(*found, constraints_count++); - else + auto pair = makeOrderedPair(found->start_ref, found->end_ref); + HORIZ_pairs.insert(pair); + lastConstraint.mode = Mode::Horizontal; + lastConstraint.data = pair; + } + else { sys.addConstraintVertical(*found, constraints_count++); + auto pair = makeOrderedPair(found->start_ref, found->end_ref); + VERT_pairs.insert(pair); + lastConstraint.mode = Mode::Vertical; + lastConstraint.data = pair; + } update(); + after_constraint = true; } mode = Mode::None; - after_constraint = true; } else if (mode == Mode::DrawingLine) { @@ -164,12 +266,16 @@ void Canvas::mousePressEvent(QMouseEvent* event) } if (!areAlreadyParallel(found, current_line)) { + auto pair = makeOrderedPair(found, current_line); sys.addConstraintParallel(*found, *current_line, constraints_count++); - parallelPairs.insert(makeOrderedPair(found, current_line)); + parallelPairs.insert(pair); + lastConstraint.mode = Mode::Parallel; + lastConstraint.data = pair; + current_line = nullptr; - update(); mode = Mode::None; after_constraint = true; + update(); } else { @@ -226,7 +332,11 @@ void Canvas::mousePressEvent(QMouseEvent* event) } sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++); - P2Ppairs.insert(makeOrderedPair(clickedPoint, firstPoint)); + auto pair = makeOrderedPair(clickedPoint, firstPoint); + P2Ppairs.insert(pair); + lastConstraint.mode = Mode::Coincedent; + lastConstraint.data = pair; + firstPoint = nullptr; mode = Mode::None; after_constraint = true; @@ -244,11 +354,47 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) *pair->x = pos.x(); *pair->y = pos.y(); } + if (areHorizontalVertical(draggedPoint, pair, true)) { + *pair->x = pos.x(); + } + if (areHorizontalVertical(draggedPoint, pair, false)) { + *pair->y = pos.y(); + } } *draggedPoint->x = pos.x(); *draggedPoint->y = pos.y(); update(); } + + else if (draggedLine) { + QPointF newCenter = event->pos() - dragOffset; + QPointF oldCenter( + (*draggedLine->p1.x + *draggedLine->p2.x) / 2.0, + (*draggedLine->p1.y + *draggedLine->p2.y) / 2.0 + ); + + // Смещение для перемещения + double dx = newCenter.x() - oldCenter.x(); + double dy = newCenter.y() - oldCenter.y(); + + Point* linePoints[2] = { draggedLine->start_ref, draggedLine->end_ref }; + + for (int i = 0; i < 2; i++) { + Point* currentPoint = linePoints[i]; + + *currentPoint->x += dx; + *currentPoint->y += dy; + + for (Point* pair : points) { + if (pair != currentPoint && areCoincident(currentPoint, pair)) { + *pair->x = *currentPoint->x; + *pair->y = *currentPoint->y; + } + } + } + + update(); + } } void Canvas::mouseReleaseEvent(QMouseEvent* event) @@ -257,6 +403,11 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event) draggedPoint = nullptr; update(); } + + if (draggedLine) { + draggedLine = nullptr; + update(); + } } void Canvas::paintEvent(QPaintEvent*) @@ -271,9 +422,8 @@ void Canvas::paintEvent(QPaintEvent*) sys.applySolution(); } else if (res == SolveStatus::Failed && after_constraint){ - QMessageBox::critical(this, QString("Error!"), QString("Last constraint is unavailable!")); - sys.removeConstraint(sys.get_last_constraint()); - constraints_count--; + QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!")); + remove_constraints(); } after_constraint = false; } @@ -311,18 +461,10 @@ void Canvas::paintEvent(QPaintEvent*) // === Текущая рисуемая линия (DrawingLine) === if (current_line && mode == Mode::DrawingLine) { - QPen pen(Qt::blue, 3, Qt::DashLine); - p.setPen(pen); - p.setBrush(Qt::transparent); - - p.drawLine(QPointF(*current_line->p1.x, *current_line->p1.y), - QPointF(*current_line->p2.x, *current_line->p2.y)); - // Концы текущей линии — синие точки p.setBrush(Qt::blue); p.setPen(Qt::NoPen); p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.y), 6, 6); - p.drawEllipse(QPointF(*current_line->p2.x, *current_line->p2.y), 6, 6); } } @@ -330,7 +472,6 @@ Canvas::Canvas(QWidget *parent) : QWidget(parent) { sys = System(); - current_line = nullptr; setMouseTracking(true); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); @@ -338,19 +479,26 @@ Canvas::Canvas(QWidget *parent) Canvas::~Canvas() { - for (Line* line : lines) { - delete line; - } - for (double* param : params) { delete param; } - lines.clear(); + 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; diff --git a/Canvas.h b/Canvas.h index ca234c9..ec40cd3 100644 --- a/Canvas.h +++ b/Canvas.h @@ -53,13 +53,19 @@ 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* draggedLine{ nullptr }; QPointF dragOffset; + // ====================== Работа с парами ограничений ====================== + void remove_constraints(); // ====================== Данные сцены ====================== System sys; // геометрический солвер QVector lines; // завершённые линии @@ -68,6 +74,8 @@ private: std::set parallelPairs; // уже запараллеленные пары (защита от дублей) std::set P2Ppairs; + std::set HORIZ_pairs; + std::set VERT_pairs; Line* current_line{ nullptr }; Point* firstPoint{ nullptr }; @@ -76,4 +84,10 @@ private: int obj_count{ 0 }; // тег для новых объектов int constraints_count{ 0 }; // тег для новых ограничений + + struct LastConstraint { + Mode mode{ Mode::None }; + std::variant data; + }; + LastConstraint lastConstraint; }; \ No newline at end of file