Касательность окружностей и линии с окружностью

This commit is contained in:
2025-12-25 00:44:48 +03:00
parent a31ef22f0f
commit dd4d2e2dbb
5 changed files with 141 additions and 21 deletions

View File

@@ -1,6 +1,4 @@
#include "Canvas.h"
#define WIDGET_POSITION event->pos()
#define UCS_POSITION screenToLogical(WIDGET_POSITION)
std::set<Point*> groups;
@@ -93,12 +91,12 @@ void Canvas::zoomReset()
Curve* Canvas::findAt(QPointF& pos, qreal tolerance)
{
for (Curve* curve : curves) {
if (Line* line = dynamic_cast<Line*>(curve)) {
if (Line* line = CURVE_AS_LINE(curve)) {
if (line->contains(pos, tolerance)) {
return line;
}
}
else if (Circle* circle = dynamic_cast<Circle*>(curve)) {
else if (Circle* circle = CURVE_AS_CIRCLE(curve)) {
QPointF center(*circle->center.x, *circle->center.y);
double radius = *circle->rad;
@@ -117,7 +115,7 @@ Point* Canvas::findPointAt(QPointF pos, qreal tolerance)
Point* temp = nullptr;
for (Curve* curve : curves) {
if (Line* line = dynamic_cast<Line*>(curve)) {
if (Line* line = CURVE_AS_LINE(curve)) {
QPointF p1(*line->p1.x, *line->p1.y);
QPointF p2(*line->p2.x, *line->p2.y);
@@ -126,7 +124,7 @@ Point* Canvas::findPointAt(QPointF pos, qreal tolerance)
if (dist_P2P(p2, pos) <= tolerance)
temp = line->end_ref;
}
else if (Circle* circle = dynamic_cast<Circle*>(curve)) {
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;
@@ -174,6 +172,11 @@ 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));
}
// ===================================================================
// Методы работы с ограничениями
// ===================================================================
@@ -248,7 +251,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
return;
}
Line* found = dynamic_cast<Line*>(findAt(scene));
Line* found = CURVE_AS_LINE(findAt(scene));
if (found) {
draggedCurve = found;
QPointF lineCenter(
@@ -259,7 +262,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
return;
}
Circle* found_circle = dynamic_cast<Circle*>(findAt(scene));
Circle* found_circle = CURVE_AS_CIRCLE(findAt(scene));
if (found_circle) {
draggedCurve = found_circle;
QPointF center(*found_circle->center.x, *found_circle->center.y);
@@ -270,7 +273,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
// ====================== Режим Horizontal/Vertical ======================
else if (mode == Mode::Horizontal || mode == Mode::Vertical) {
Line* found = dynamic_cast<Line*>(findAt(scene));
Line* found = CURVE_AS_LINE(findAt(scene));
if (found) {
// Проверка: если линия уже вертикальна, нельзя сделать её горизонтальной
@@ -403,7 +406,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
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())));
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);
@@ -417,7 +420,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
}
// ====================== Режим Parallel: задание параллельности ======================
else if (mode == Mode::Parallel) {
Line* found = dynamic_cast<Line*>(findAt(scene));
Line* found = CURVE_AS_LINE(findAt(scene));
if (!found) {
current_line = nullptr;
@@ -496,7 +499,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
Line* l2 = nullptr;
for (Curve* curve : curves) {
if (Line* l = dynamic_cast<Line*>(curve)) {
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)
@@ -527,7 +530,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
}
else if (mode == Mode::Perpendicular) {
Line* found = dynamic_cast<Line*>(findAt(scene));
Line* found = CURVE_AS_LINE(findAt(scene));
if (!found) {
current_line = nullptr;
@@ -572,6 +575,96 @@ void Canvas::mousePressEvent(QMouseEvent* event)
return;
}
}
else if (mode == Mode::Tangent) {
// Ищем любой объект под курсором: линия или окружность
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;
}
QString message;
bool constraintAdded = false;
// Вариант 1: Линия → Окружность
if (Line* line = dynamic_cast<Line*>(first)) {
if (Circle* circle = dynamic_cast<Circle*>(second)) {
message = "Выбрано: Линия → Окружность\nПрименено касание линии к окружности.";
sys.addConstraintTangent(*line, *circle, constraints_count++);
constraintAdded = true;
}
}
// Вариант 2: Окружность → Линия
else if (Circle* circle = dynamic_cast<Circle*>(first)) {
if (Line* line = dynamic_cast<Line*>(second)) {
message = "Выбрано: Окружность → Линия\nПрименено касание линии к окружности.";
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)) {
message = "Выбрано: Окружность → Окружность\nПрименено касание двух окружностей.";
sys.addConstraintTangent(*c1, *c2, constraints_count++);
constraintAdded = true;
}
}
// Если комбинация недопустима
if (!constraintAdded) {
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 };
QMessageBox::information(this, "Касание применено", message, QMessageBox::Ok);
after_constraint = true;
}
// Сброс режима и выделения
current_curve = nullptr;
mode = Mode::None;
solve_for_canvas();
update();
}
}
void Canvas::mouseMoveEvent(QMouseEvent* event)
@@ -598,7 +691,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
// ====================== Перемещение линии ======================
else if (draggedCurve) {
if (Line* draggedLine = dynamic_cast<Line*>(draggedCurve)) {
if (Line* draggedLine = CURVE_AS_LINE(draggedCurve)) {
QPointF newCenter = UCS_POSITION - dragOffset;
QPointF oldCenter(
(*draggedLine->p1.x + *draggedLine->p2.x) / 2.0,
@@ -627,7 +720,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
}
}
}
else if (Circle* circle = dynamic_cast<Circle*>(draggedCurve)) {
else if (Circle* circle = CURVE_AS_CIRCLE(draggedCurve)) {
QPointF newCenter = UCS_POSITION - dragOffset;
*circle->center.x = newCenter.x();
*circle->center.y = newCenter.y();
@@ -687,7 +780,7 @@ void Canvas::paintEvent(QPaintEvent* event)
// ====================== Отрисовка линий ======================
for (Curve* curve : curves) {
if (Line* line = dynamic_cast<Line*>(curve)) {
if (Line* line = CURVE_AS_LINE(curve)) {
bool isSelected = (mode == Mode::Parallel && line == current_line);
// Настройка пера для линии
@@ -708,7 +801,7 @@ void Canvas::paintEvent(QPaintEvent* event)
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 = dynamic_cast<Circle*>(curve)) {
else if (Circle* circle = CURVE_AS_CIRCLE(curve)) {
p.setPen(QPen(Qt::black, 2));
p.setBrush(Qt::NoBrush);
@@ -894,7 +987,7 @@ void Canvas::solve_for_canvas()
else {
sys.applySolution();
for (Curve* curve : curves) {
if (Line* line = dynamic_cast<Line*>(curve)) {
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;