#include "Canvas.h" static double dist_P2P(QPointF p1, QPointF p2) { return sqrt(pow(p2.x() - p1.x(), 2) + pow(p2.y() - p1.y(), 2)); } template T makeOrderedPair(A* obj1, B* obj2) { return (obj1->get_tag() < obj2->get_tag()) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1); } void Canvas::changeMode(Mode _mode) { mode = _mode; } // TODO Line* Canvas::findAt(QPointF& pos) { for (Line* line : lines) { if (line->contains(pos)) { return line; } } return nullptr; } Point* Canvas::findPointAt(QPointF pos, qreal tolerance) { Point* temp = nullptr; for (Line* line : lines) { QPointF p1(*line->p1.x, *line->p1.y); QPointF p2(*line->p2.x, *line->p2.y); if (dist_P2P(p1, pos) <= tolerance) temp = line->start_ref; if (dist_P2P(p2, pos) <= tolerance) temp = line->end_ref; } return temp; } bool Canvas::areCoincident(Point* p1, Point* p2) { return P2Ppairs.count(makeOrderedPair(p1, p2)); } bool Canvas::areAlreadyParallel(Line* l1, Line* l2) { return parallelPairs.count(makeOrderedPair(l1, l2)) > 0; } void Canvas::mousePressEvent(QMouseEvent* event) { QPointF scene = event->pos(); #ifdef _DEBUG qDebug() << "Scene point in" << scene.x() << scene.y(); #endif if (mode == Mode::None){ Point* p = findPointAt(scene); if (p) { draggedPoint = p; dragOffset = scene - QPointF(*p->x, *p->y); } } else if (mode == Mode::Horizontal || mode == Mode::Vertical) { Line* found = findAt(scene); if (found) { if (mode == Mode::Horizontal) sys.addConstraintHorizontal(*found, constraints_count++); else sys.addConstraintVertical(*found, constraints_count++); update(); } mode = Mode::None; after_constraint = true; } else if (mode == Mode::DrawingLine) { if (!current_line) { current_line = new Line(); current_line->set_tag(obj_count); double* x1 = new double(scene.x()); double* y1 = new double(scene.y()); double* x2 = new double(scene.x()); double* y2 = new double(scene.y()); params.push_back(x1); params.push_back(y1); points.push_back(new Point(x1, y1, obj_count)); points.push_back(new Point(x2, y2, obj_count)); current_line->p1.x = x1; current_line->p1.y = y1; current_line->p2.x = x2; current_line->p2.y = y2; current_line->start_ref = points[points.size() - 2]; current_line->end_ref = points[points.size() - 1]; } else { *current_line->p2.x = scene.x(); *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)); if (len < 10) { delete current_line; current_line = nullptr; delete points.back(); points.pop_back(); delete points.back(); points.pop_back(); delete params.back(); params.pop_back(); delete params.back(); params.pop_back(); mode = Mode::None; QMessageBox::critical(this, "WHOOPS", "Sorry, your line is very short", QMessageBox::Ok); update(); return; } params.push_back(current_line->p2.x); params.push_back(current_line->p2.y); lines.append(current_line); current_line = nullptr; mode = Mode::None; obj_count++; after_constraint = true; } update(); return; } else if (mode == Mode::Parallel) { Line* found = findAt(scene); if (!found) { current_line = nullptr; update(); return; } if (!current_line) { current_line = found; update(); return; } if (found == current_line){ current_line = nullptr; update(); return; } if (!areAlreadyParallel(found, current_line)) { sys.addConstraintParallel(*found, *current_line, constraints_count++); parallelPairs.insert(makeOrderedPair(found, current_line)); current_line = nullptr; update(); mode = Mode::None; after_constraint = true; } else { #ifdef _DEBUG qDebug() << "Line" << current_line << "and" << found << "are parallel. Abort!"; #endif QMessageBox::warning(this, QString("Wrong"), QString("Parallel lines can not be more parallel!"), QMessageBox::Ok ); current_line = nullptr; update(); return; } update(); } else if (mode == Mode::Coincedent) { Point* clickedPoint = findPointAt(scene); if (!clickedPoint) { firstPoint = nullptr; update(); return; } if (!firstPoint) { firstPoint = clickedPoint; update(); return; } if (clickedPoint == firstPoint) { firstPoint = nullptr; update(); return; } Line *l1 = nullptr, *l2 = nullptr; for (Line* l : lines) { if (l->start_ref == firstPoint || l->end_ref == firstPoint) l1 = l; if (l->start_ref == clickedPoint || l->end_ref == clickedPoint) l2 = l; } if (l1 == l2 && l1 && l2 || (areCoincident(firstPoint, clickedPoint))) { QMessageBox::critical(this, QString("NO!"), QString("P2P failed")); firstPoint = nullptr; mode = Mode::None; update(); return; } sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++); P2Ppairs.insert(makeOrderedPair(clickedPoint, firstPoint)); firstPoint = nullptr; mode = Mode::None; after_constraint = true; update(); return; } } void Canvas::mouseMoveEvent(QMouseEvent* event) { if (draggedPoint) { QPointF pos = event->pos() - dragOffset; for (Point* pair : points) { if (areCoincident(draggedPoint, pair)) { *pair->x = pos.x(); *pair->y = pos.y(); } } *draggedPoint->x = pos.x(); *draggedPoint->y = pos.y(); update(); } } void Canvas::mouseReleaseEvent(QMouseEvent* event) { if (draggedPoint) { draggedPoint = nullptr; update(); } } void Canvas::paintEvent(QPaintEvent*) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); // === Решаем систему один раз === if (!params.empty()) { int res = sys.solve(params); if (res == SolveStatus::Success || res == SolveStatus::Converged) { 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--; } after_constraint = false; } for (Line* line : lines) { bool isSelected = (mode == Mode::Parallel && line == current_line); QPen linePen = isSelected ? QPen(Qt::red, 4) : QPen(Qt::black, 2); p.setPen(linePen); // Рисуем саму линию p.drawLine(QPointF(*line->p1.x, *line->p1.y), QPointF(*line->p2.x, *line->p2.y)); // Рисуем концы — с учётом выделения QBrush pointBrush = isSelected ? QBrush(Qt::red) : QBrush(Qt::darkBlue); p.setBrush(pointBrush); p.setPen(Qt::NoPen); // убираем обводку у кружков p.drawEllipse(QPointF(*line->p1.x, *line->p1.y), 5, 5); p.drawEllipse(QPointF(*line->p2.x, *line->p2.y), 5, 5); } // === Подсветка выбранной точки в режиме Coincident === if (mode == Mode::Coincedent && firstPoint) { QPointF pt(*firstPoint->x, *firstPoint->y); p.setPen(Qt::NoPen); p.setBrush(Qt::red); p.drawEllipse(pt, 12, 12); p.setBrush(Qt::white); p.drawEllipse(pt, 8, 8); p.setBrush(Qt::red); p.drawEllipse(pt, 4, 4); // маленький центр } // === Текущая рисуемая линия (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); } } Canvas::Canvas(QWidget *parent) : QWidget(parent) { sys = System(); current_line = nullptr; setMouseTracking(true); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); } Canvas::~Canvas() { for (Line* line : lines) { delete line; } for (double* param : params) { delete param; } lines.clear(); params.clear(); points.clear(); parallelPairs.clear(); P2Ppairs.clear(); if (current_line) delete current_line; if (firstPoint) delete firstPoint; }