This repository has been archived on 2026-05-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
DRAwer_2_0/Canvas.cpp

1115 lines
34 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "Canvas.h"
std::set<Point*> groups;
// ===================================================================
// Вспомогательные функции
// ===================================================================
/**
* @brief Вычислить расстояние между двумя точками
* @param p1 Первая точка
* @param p2 Вторая точка
* @return Расстояние между точками
*/
static double dist_P2P(QPointF p1, QPointF p2)
{
return sqrt(pow(p2.x() - p1.x(), 2) + pow(p2.y() - p1.y(), 2));
}
/**
* @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>
T makeOrderedPair(A* obj1, B* obj2)
{
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);
setFocusPolicy(Qt::StrongFocus);
setAutoFillBackground(true);
}
Canvas::~Canvas()
{
clearCanvas();
}
// ===================================================================
// Методы изменения режима и масштаба
// ===================================================================
void Canvas::changeMode(Mode _mode)
{
mode = _mode;
}
void Canvas::zoomIn()
{
scaleFactor += ZOOM_STEP;
update();
}
void Canvas::zoomOut()
{
scaleFactor -= ZOOM_STEP;
if (scaleFactor < 0.1) // Минимальный масштаб 10%
scaleFactor = 0.1;
update();
}
void Canvas::zoomReset()
{
scaleFactor = 1.0;
update();
}
// ===================================================================
// Методы поиска и проверки
// ===================================================================
Curve* Canvas::findAt(QPointF& pos, qreal tolerance)
{
for (Curve* curve : curves) {
if (Line* line = CURVE_AS_LINE(curve)) {
if (line->contains(pos, tolerance)) {
return line;
}
}
else if (Circle* circle = CURVE_AS_CIRCLE(curve)) {
QPointF center(*circle->center.x, *circle->center.y);
double radius = *circle->rad;
double distToCenter = dist_P2P(center, pos);
// Проверяем, лежит ли точка вблизи окружности (с допуском)
if (std::abs(distToCenter - radius) <= tolerance) {
return circle;
}
}
}
return nullptr;
}
Point* Canvas::findPointAt(QPointF pos, qreal tolerance)
{
Point* temp = nullptr;
for (Curve* curve : curves) {
if (Line* line = CURVE_AS_LINE(curve)) {
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;
}
else if (Circle* circle = CURVE_AS_CIRCLE(curve)) {
QPointF center(circle->center.get_X(), circle->center.get_Y());
if (dist_P2P(center, pos) <= tolerance)
temp = circle->center_ref;
}
}
return temp;
}
bool Canvas::areCoincident(Point* p1, Point* p2)
{
return P2Ppairs.count(makeOrderedPair<PointPair>(p1, p2));
}
bool Canvas::areHorizontalVertical(Point* p1, Point* p2, bool mode)
{
// mode = false - проверка горизонтальности
// mode = true - проверка вертикальности
if (!mode)
return HORIZ_pairs.count(makeOrderedPair<PointPair>(p1, p2));
else
return VERT_pairs.count(makeOrderedPair<PointPair>(p1, p2));
}
bool Canvas::isLineHorizontal(Line* line)
{
if (!line || !line->start_ref || !line->end_ref)
return false;
return HORIZ_pairs.count(makeOrderedPair<PointPair>(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<PointPair>(line->start_ref, line->end_ref));
}
bool Canvas::areAlreadyParallel(Line* l1, Line* l2)
{
return parallelPairs.count(makeOrderedPair<LinePair>(l1, l2));
}
bool Canvas::areAlreadyPerpendicular(Line* line1, Line* line2)
{
return perpendicularPairs.count(makeOrderedPair<LinePair>(line1, line2));
}
bool Canvas::areAlreadyTangent(Curve* curve1, Curve* curve2)
{
return tangentPairs.count(makeOrderedPair<CurvePair>(curve1, curve2));
}
// ===================================================================
// Методы работы с ограничениями
// ===================================================================
void Canvas::remove_constraint(int tag)
{
auto it = C_Info.find(tag);
if (it == C_Info.end()) return;
ConstraintInfo info = it->second;
// Удаляем ограничение из солвера
sys.clearByTag(tag);
// Удаляем из соответствующих контейнеров в зависимости от типа ограничения
switch (info.mode) {
case Mode::Horizontal: {
if (auto* pair = std::get_if<PointPair>(&info.data)) {
HORIZ_pairs.erase(*pair);
}
break;
}
case Mode::Vertical: {
if (auto* pair = std::get_if<PointPair>(&info.data)) {
VERT_pairs.erase(*pair);
}
break;
}
case Mode::Parallel: {
if (auto* pair = std::get_if<LinePair>(&info.data)) {
parallelPairs.erase(*pair);
}
break;
}
case Mode::Coincedent: {
if (auto* pair = std::get_if<PointPair>(&info.data)) {
P2Ppairs.erase(*pair);
}
break;
}
case Mode::Perpendicular: {
if (auto* pair = std::get_if<LinePair>(&info.data)) {
perpendicularPairs.erase(*pair);
}
break;
}
case Mode::Tangent: {
if (auto* pair = std::get_if<CurvePair>(&info.data)) {
tangentPairs.erase(*pair);
}
break;
}
default:
break;
}
C_Info.erase(it);
}
// ===================================================================
// Обработчики событий мыши
// ===================================================================
void Canvas::mousePressEvent(QMouseEvent* event)
{
QPointF scene = UCS_POSITION;
#ifdef _DEBUG
qDebug() << "Scene point in" << scene.x() << scene.y();
#endif
switch (mode)
{
case Mode::None:
ON_NONE(&scene);
break;
case Mode::DrawingLine:
ON_LINE(&scene);
break;
case Mode::Parallel:
ON_PARALLEL(&scene);
break;
case Mode::Coincedent:
ON_P2P(&scene);
break;
case Mode::Horizontal:
ON_HORIZ_VERT(&scene);
break;
case Mode::Vertical:
ON_HORIZ_VERT(&scene);
break;
case Mode::Perpendicular:
ON_PERPENDICULAR(&scene);
break;
case Mode::DrawingCircle:
ON_CIRCLE(&scene);
break;
case Mode::Tangent:
ON_TANGENT(&scene);
break;
default:
break;
}
}
void Canvas::mouseMoveEvent(QMouseEvent* event)
{
// ====================== Перемещение точки ======================
if (draggedPoint) {
QPointF pos = UCS_POSITION - dragOffset;
for (Point* pt : groups) {
*pt->x = pos.x();
*pt->y = pos.y();
}
for (Point* basePt : groups) {
for (Point* other : points) {
if (areHorizontalVertical(basePt, other, true)) {
*other->x = pos.x();
}
if (areHorizontalVertical(basePt, other, false)) {
*other->y = pos.y();
}
}
}
}
// ====================== Перемещение линии ======================
else if (draggedCurve) {
if (Line* draggedLine = CURVE_AS_LINE(draggedCurve)) {
QPointF newCenter = UCS_POSITION - 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;
}
}
}
}
else if (Circle* circle = CURVE_AS_CIRCLE(draggedCurve)) {
QPointF newCenter = UCS_POSITION - dragOffset;
*circle->center.x = newCenter.x();
*circle->center.y = newCenter.y();
*circle->center_ref->x = newCenter.x();
*circle->center_ref->y = newCenter.y();
// Также обновляем все совпадающие точки (если центр совпадает с другими точками через P2P)
for (Point* other : points) {
if (other != circle->center_ref && areCoincident(circle->center_ref, other)) {
*other->x = newCenter.x();
*other->y = newCenter.y();
}
}
}
}
#ifdef _DEBUG
else
showObjectTag(WIDGET_POSITION);
#endif
solve_for_canvas();
}
void Canvas::mouseReleaseEvent(QMouseEvent* event)
{
Q_UNUSED(event);
if (draggedPoint) {
draggedPoint = nullptr;
solve_for_canvas();
}
if (draggedCurve) {
draggedCurve = nullptr;
solve_for_canvas();
}
groups.clear();
}
// ===================================================================
// Метод отрисовки
// ===================================================================
void Canvas::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing, true);
p.translate(width() / 2.0, height() / 2.0);
p.scale(scaleFactor, -scaleFactor);
p.setPen(Qt::red);
p.drawLine(-5, 0, 5, 0);
p.drawLine(0, -5, 0, 5);
// ====================== Отрисовка линий ======================
for (Curve* curve : curves) {
if (Line* line = CURVE_AS_LINE(curve)) {
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);
}
else if (Circle* circle = CURVE_AS_CIRCLE(curve)) {
p.setPen(QPen(Qt::black, 2));
p.setBrush(Qt::NoBrush);
p.drawEllipse(QPointF(*circle->center.x, *circle->center.y),
*circle->rad, *circle->rad);
p.setBrush(QBrush(Qt::darkBlue));
p.setPen(Qt::NoPen);
p.drawEllipse(QPointF(*circle->center.x, *circle->center.y), 5, 5);
p.setBrush(Qt::NoBrush);
}
}
// ====================== Подсветка выбранной точки (режим Coincedent) ======================
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) {
// Рисуем начальную точку текущей линии
p.setBrush(Qt::blue);
p.setPen(Qt::NoPen);
p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.y), 6, 6);
}
}
// ===================================================================
// Обработчики событий клавиатуры и колесика мыши
// ===================================================================
void Canvas::keyPressEvent(QKeyEvent* event)
{
switch (event->key()) {
case Qt::Key_Delete:
case Qt::Key_Clear: {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this,
"Очистка canvas",
"Удалить все объекты и ограничения?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
clearCanvas();
}
break;
}
case Qt::Key_Escape: {
// Отмена текущего действия
if (mode == Mode::DrawingLine && current_line) {
// Отмена рисования линии
delete current_line;
current_line = nullptr;
// Удаляем созданные точки и параметры
if (points.size() >= 2) {
delete points.back();
points.pop_back();
delete points.back();
points.pop_back();
}
if (params.size() >= 4) {
delete params.back();
params.pop_back();
delete params.back();
params.pop_back();
delete params.back();
params.pop_back();
delete params.back();
params.pop_back();
}
mode = Mode::None;
update();
}
else if (mode != Mode::None) {
// Отмена любого другого режима
mode = Mode::None;
current_line = nullptr;
firstPoint = nullptr;
groups.clear();
update();
}
break;
}
case Qt::Key_Z: {
if (constraints_count > 0) {
remove_constraint(constraints_count - 1);
constraints_count--;
solve_for_canvas();
}
break;
}
default:
QWidget::keyPressEvent(event);
}
}
void Canvas::wheelEvent(QWheelEvent* event)
{
if (event->angleDelta().y() > 0) {
zoomIn();
}
else {
zoomOut();
}
event->accept();
}
void Canvas::leaveEvent(QEvent* event)
{
Q_UNUSED(event);
QToolTip::hideText();
}
#ifdef _DEBUG
void Canvas::showObjectTag(QPointF pos)
{
//QPointF l = screenToLogical(pos);
//Line* lineUnderCursor = findAt(l, 1.0);
//if (lineUnderCursor && lineUnderCursor != draggedLine) {
// QPointF p1(*lineUnderCursor->p1.x, *lineUnderCursor->p1.y);
// QPointF p2(*lineUnderCursor->p2.x, *lineUnderCursor->p2.y);
// QLineF info_line(p1, p2);
// QString Text = QString("Line %1\nLength = %2mm")
// .arg(lineUnderCursor->get_tag() + 1)
// .arg(info_line.length());
// QToolTip::showText(mapToGlobal(pos.toPoint()), Text, this);
//}
//else
// QToolTip::hideText();
}
#endif
// ===================================================================
// Вспомогательные методы
// ===================================================================
QPointF Canvas::screenToLogical(const QPointF& screenPos) const
{
QPointF logical = screenPos;
logical.rx() -= width() / 2.0;
logical.ry() -= height() / 2.0;
logical.rx() /= scaleFactor;
logical.ry() /= -scaleFactor;
return logical;
}
void Canvas::getCoincidentGroup(Point* p)
{
for (Point* other : points) {
if (other != p && areCoincident(p, other)) {
groups.insert(other);
break;
}
}
for (size_t i = 0; i < points.size(); ++i) {
for (size_t j = i + 1; j < points.size(); j++) {
if (*points[i] == *points[j] && groups.contains(points[j])) {
groups.insert(points[i]);
}
}
}
groups.insert(p);
}
void Canvas::solve_for_canvas()
{
bool flag = false;
int res = sys.solve(params);
if (res != SolveStatus::Success && res != SolveStatus::Converged) {
flag = true;
}
else {
sys.applySolution();
for (Curve* curve : curves) {
if (Line* line = CURVE_AS_LINE(curve)) {
if (abs(*line->p1.x - *line->p2.x) < EPS && abs(*line->p1.y - *line->p2.y) < EPS && after_constraint) {
sys.undoSolution();
flag = true;
break;
}
}
if (Circle * circle = CURVE_AS_CIRCLE(curve)) {
if (*circle->rad < EPS && after_constraint) {
sys.undoSolution();
flag = true;
break;
}
}
}
}
if (flag && after_constraint) {
QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!"));
remove_constraint(constraints_count - 1);
C_Info.erase(constraints_count - 1);
constraints_count--;
}
else if (flag) {
sys.undoSolution();
}
after_constraint = false;
update();
}
// ===================================================================
// Очистка всего canvas
// ===================================================================
void Canvas::clearCanvas()
{
// Очистка солвера
sys.clear();
// Очистка динамически выделенной памяти
for (double* param : params) {
delete param;
}
for (Point* pt : points) {
delete pt;
}
for (Curve* curve : curves){
delete curve;
}
// Очистка контейнеров
params.clear();
points.clear();
curves.clear();
// Очистка контейнеров ограничений
parallelPairs.clear();
P2Ppairs.clear();
VERT_pairs.clear();
HORIZ_pairs.clear();
perpendicularPairs.clear();
tangentPairs.clear();
// Очистка информации об ограничениях
C_Info.clear();
// Сброс счетчиков
obj_count = 0;
constraints_count = 0;
// Очистка временных указателей
if (current_line) {
delete current_line;
current_line = nullptr;
}
if (firstPoint) {
delete firstPoint;
firstPoint = nullptr;
}
draggedPoint = nullptr;
draggedLine = nullptr;
// Сброс режима
mode = Mode::None;
// Обновление отображения
update();
groups.clear();
}
void Canvas::ON_NONE(QPointF* scene)
{
Point* p = findPointAt(*scene);
if (p) {
draggedPoint = p;
getCoincidentGroup(draggedPoint);
dragOffset = *scene - QPointF(*p->x, *p->y);
return;
}
Line* found = CURVE_AS_LINE(findAt(*scene));
if (found) {
draggedCurve = found;
QPointF lineCenter(
(*found->p1.x + *found->p2.x) / 2.0,
(*found->p1.y + *found->p2.y) / 2.0
);
dragOffset = *scene - lineCenter;
return;
}
Circle* found_circle = CURVE_AS_CIRCLE(findAt(*scene));
if (found_circle) {
draggedCurve = found_circle;
QPointF center(*found_circle->center.x, *found_circle->center.y);
dragOffset = *scene - center;
return;
}
return;
}
void Canvas::ON_LINE(QPointF* pos)
{
QPointF scene = *pos;
if (!current_line) {
// Первый клик: создаем новую линию
current_line = new Line();
// Создаем координаты для точек линии
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);
solve_for_canvas();
return;
}
// Добавляем координаты второй точки в параметры
params.push_back(current_line->p2.x);
params.push_back(current_line->p2.y);
current_line->set_tag(obj_count++);
// Завершаем создание линии
curves.append(current_line);
current_line = nullptr;
mode = Mode::None;
}
solve_for_canvas();
return;
}
void Canvas::ON_CIRCLE(QPointF* pos)
{
QPointF scene = *pos;
if (!current_circle) {
current_circle = new Circle();
// Создаем координаты для центра окружности
double* x = new double(scene.x());
double* y = new double(scene.y());
points.push_back(new Point(x, y, obj_count++));
// Добавляем параметры в солвер
params.push_back(x);
params.push_back(y);
current_circle->center.x = x;
current_circle->center.y = y;
current_circle->center_ref = points[points.size() - 1];
}
else {
double* r = new double(dist_P2P(QPointF(current_circle->center.get_X(), current_circle->center.get_Y()), QPointF(scene.x(), scene.y())));
current_circle->rad = r;
params.push_back(r);
curves.append(current_circle);
current_circle->set_tag(obj_count++);
current_circle = nullptr;
mode = Mode::None;
}
solve_for_canvas();
return;
}
void Canvas::ON_PARALLEL(QPointF* scene)
{
Line* found = CURVE_AS_LINE(findAt(*scene));
if (!found) {
current_line = nullptr;
solve_for_canvas();
return;
}
// Первый клик: выбираем первую линию
if (!current_line) {
current_line = found;
solve_for_canvas();
return;
}
// Повторный клик на ту же линию: сброс выбора
if (found == current_line) {
current_line = nullptr;
solve_for_canvas();
return;
}
// Второй клик на другую линию: добавляем ограничение параллельности
if (!areAlreadyParallel(found, current_line)) {
auto pair = makeOrderedPair<LinePair>(found, current_line);
sys.addConstraintParallel(*found, *current_line, constraints_count++);
parallelPairs.insert(pair);
C_Info[constraints_count - 1] = { Mode::Parallel, pair };
current_line = nullptr;
mode = Mode::None;
after_constraint = true;
solve_for_canvas();
}
else {
// Линии уже параллельны - сообщаем об ошибке
QMessageBox::warning(this,
QString("Wrong"),
QString("Parallel lines can not be more parallel!"),
QMessageBox::Ok
);
current_line = nullptr;
solve_for_canvas();
return;
}
}
void Canvas::ON_PERPENDICULAR(QPointF* scene)
{
Line* found = CURVE_AS_LINE(findAt(*scene));
if (!found) {
current_line = nullptr;
solve_for_canvas();
return;
}
// Первый клик: выбираем первую линию
if (!current_line) {
current_line = found;
solve_for_canvas();
return;
}
// Повторный клик на ту же линию: сброс выбора
if (found == current_line) {
current_line = nullptr;
solve_for_canvas();
return;
}
if (!areAlreadyPerpendicular(found, current_line)) {
auto pair = makeOrderedPair<LinePair>(found, current_line);
sys.addConstraintPerpendicular(*found, *current_line, constraints_count++);
perpendicularPairs.insert(pair);
C_Info[constraints_count - 1] = { Mode::Perpendicular, pair };
current_line = nullptr;
mode = Mode::None;
after_constraint = true;
solve_for_canvas();
}
else {
QMessageBox::warning(this,
QString("Wrong"),
QString("Perpendicular lines can not be more perpendicular!"),
QMessageBox::Ok
);
current_line = nullptr;
solve_for_canvas();
return;
}
}
void Canvas::ON_P2P(QPointF* scene)
{
Point* clickedPoint = findPointAt(*scene);
if (!clickedPoint) {
firstPoint = nullptr;
solve_for_canvas();
return;
}
// Первый клик: выбираем первую точку
if (!firstPoint) {
firstPoint = clickedPoint;
solve_for_canvas();
return;
}
// Повторный клик на ту же точку: сброс выбора
if (clickedPoint == firstPoint) {
firstPoint = nullptr;
solve_for_canvas();
return;
}
// Находим линии, к которым принадлежат точки
Line* l1 = nullptr;
Line* l2 = nullptr;
for (Curve* curve : curves) {
if (Line* l = CURVE_AS_LINE(curve)) {
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;
solve_for_canvas();
return;
}
// Добавляем ограничение совпадения точек
sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++);
auto pair = makeOrderedPair<PointPair>(clickedPoint, firstPoint);
P2Ppairs.insert(pair);
C_Info[constraints_count - 1] = { Mode::Coincedent, pair };
firstPoint = nullptr;
mode = Mode::None;
after_constraint = true;
solve_for_canvas();
return;
}
void Canvas::ON_TANGENT(QPointF* scene)
{
// Ищем любой объект под курсором: линия или окружность
Curve* selected = findAt(*scene);
if (!selected) {
current_curve = nullptr;
update();
return;
}
// Если кликнули по уже выбранному объекту — сброс
if (current_curve && selected == current_curve) {
current_curve = nullptr;
update();
return;
}
// Первый выбор
if (!current_curve) {
current_curve = selected;
update(); // подсветим выбранный объект
return;
}
// Второй выбор — теперь у нас два объекта
Curve* first = current_curve;
Curve* second = selected;
// Проверка на уже существующее ограничение касательности
if (areAlreadyTangent(first, second)) {
QMessageBox::warning(this, "Ошибка",
"Эти объекты уже касательны!", QMessageBox::Ok);
current_curve = nullptr;
update();
return;
}
bool constraintAdded = false;
// Вариант 1: Линия → Окружность
if (Line* line = dynamic_cast<Line*>(first)) {
if (Circle* circle = dynamic_cast<Circle*>(second)) {
sys.addConstraintTangent(*line, *circle, constraints_count++);
constraintAdded = true;
}
}
// Вариант 2: Окружность → Линия
else if (Circle* circle = dynamic_cast<Circle*>(first)) {
if (Line* line = dynamic_cast<Line*>(second)) {
sys.addConstraintTangent(*line, *circle, constraints_count++); // порядок Line, Circle
constraintAdded = true;
}
}
// Вариант 3: Окружность → Окружность
if (Circle* c1 = dynamic_cast<Circle*>(first)) {
if (Circle* c2 = dynamic_cast<Circle*>(second)) {
sys.addConstraintTangent(*c1, *c2, constraints_count++);
constraintAdded = true;
}
}
// Если комбинация недопустима
if (!constraintAdded) {
QString message = "Недопустимая комбинация объектов для касания!\n"
"Поддерживаются: линия-окружность или окружность-окружность.";
QMessageBox::warning(this, "Ошибка", message, QMessageBox::Ok);
}
else {
// Сохраняем информацию об ограничении (для undo и удаления)
auto orderedPair = makeOrderedPair<CurvePair>(first, second);
tangentPairs.insert(orderedPair);
C_Info[constraints_count - 1] = { Mode::Tangent, orderedPair };
after_constraint = true;
}
// Сброс режима и выделения
current_curve = nullptr;
mode = Mode::None;
solve_for_canvas();
}
void Canvas::ON_HORIZ_VERT(QPointF* scene)
{
Line* found = CURVE_AS_LINE(findAt(*scene));
if (found) {
// Проверка: если линия уже вертикальна, нельзя сделать её горизонтальной
if (mode == Mode::Horizontal && isLineVertical(found)) {
QMessageBox::warning(this,
QString("Невозможно"),
QString("Эта линия уже вертикальна и не может быть горизонтальной!"),
QMessageBox::Ok
);
mode = Mode::None;
return;
}
// Проверка: если линия уже горизонтальна, нельзя сделать её вертикальной
else 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++);
auto pair = makeOrderedPair<PointPair>(found->start_ref, found->end_ref);
HORIZ_pairs.insert(pair);
C_Info[constraints_count - 1] = { Mode::Horizontal, pair };
}
else {
sys.addConstraintVertical(*found, constraints_count++);
auto pair = makeOrderedPair<PointPair>(found->start_ref, found->end_ref);
VERT_pairs.insert(pair);
C_Info[constraints_count - 1] = { Mode::Vertical, pair };
}
solve_for_canvas();
after_constraint = true;
}
mode = Mode::None;
return;
}