diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6075fde --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dependencies/eigen"] + path = dependencies/eigen + url = https://gitlab.com/libeigen/eigen.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cc9cb3..fea0c1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # PlaneGCS PART set(PlaneGCS_project_name "PlaneGCS") set(INCLUDE_BOOST_DIR "C:/local/boost_1_89_0") -set(INCLUDE_EIGEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/eigen-5.0.0) +set(INCLUDE_EIGEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/eigen) add_library(${PlaneGCS_project_name} STATIC GCS/GCS.cpp diff --git a/Canvas.cpp b/Canvas.cpp index 0d4a45e..f39bfb8 100644 --- a/Canvas.cpp +++ b/Canvas.cpp @@ -90,11 +90,23 @@ void Canvas::zoomReset() // Методы поиска и проверки // =================================================================== -Line* Canvas::findAt(QPointF& pos, qreal tolerance) +Curve* Canvas::findAt(QPointF& pos, qreal tolerance) { - for (Line* line : lines) { - if (line->contains(pos, tolerance)) { - return line; + for (Curve* curve : curves) { + if (Line* line = dynamic_cast(curve)) { + if (line->contains(pos, tolerance)) { + return line; + } + } + else if (Circle* circle = dynamic_cast(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; @@ -103,14 +115,22 @@ Line* Canvas::findAt(QPointF& pos, qreal tolerance) 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; + for (Curve* curve : curves) { + if (Line* line = dynamic_cast(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 = dynamic_cast(curve)) { + QPointF center(circle->center.get_X(), circle->center.get_Y()); + if (dist_P2P(center, pos) <= tolerance) + temp = circle->center_ref; + } } return temp; } @@ -228,9 +248,9 @@ void Canvas::mousePressEvent(QMouseEvent* event) return; } - Line* found = findAt(scene); + Line* found = dynamic_cast(findAt(scene)); if (found) { - draggedLine = found; + draggedCurve = found; QPointF lineCenter( (*found->p1.x + *found->p2.x) / 2.0, (*found->p1.y + *found->p2.y) / 2.0 @@ -238,11 +258,19 @@ void Canvas::mousePressEvent(QMouseEvent* event) dragOffset = scene - lineCenter; return; } + + Circle* found_circle = dynamic_cast(findAt(scene)); + if (found_circle) { + draggedCurve = found_circle; + QPointF center(*found_circle->center.x, *found_circle->center.y); + dragOffset = scene - center; + return; + } } // ====================== Режим Horizontal/Vertical ====================== else if (mode == Mode::Horizontal || mode == Mode::Vertical) { - Line* found = findAt(scene); + Line* found = dynamic_cast(findAt(scene)); if (found) { // Проверка: если линия уже вертикальна, нельзя сделать её горизонтальной @@ -349,18 +377,47 @@ void Canvas::mousePressEvent(QMouseEvent* event) current_line->set_tag(obj_count++); // Завершаем создание линии - lines.append(current_line); + curves.append(current_line); current_line = nullptr; mode = Mode::None; - after_constraint = true; } solve_for_canvas(); return; } + else if (mode == Mode::DrawingCircle) { + 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; + } // ====================== Режим Parallel: задание параллельности ====================== else if (mode == Mode::Parallel) { - Line* found = findAt(scene); + Line* found = dynamic_cast(findAt(scene)); if (!found) { current_line = nullptr; @@ -437,11 +494,14 @@ void Canvas::mousePressEvent(QMouseEvent* event) // Находим линии, к которым принадлежат точки Line* l1 = nullptr; Line* 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; + + for (Curve* curve : curves) { + if (Line* l = dynamic_cast(curve)) { + if (l->start_ref == firstPoint || l->end_ref == firstPoint) + l1 = l; + if (l->start_ref == clickedPoint || l->end_ref == clickedPoint) + l2 = l; + } } // Проверка на невозможность ограничения @@ -467,7 +527,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) } else if (mode == Mode::Perpendicular) { - Line* found = findAt(scene); + Line* found = dynamic_cast(findAt(scene)); if (!found) { current_line = nullptr; @@ -534,45 +594,60 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) } } } - - solve_for_canvas(); } // ====================== Перемещение линии ====================== - else if (draggedLine) { - QPointF newCenter = UCS_POSITION - dragOffset; - QPointF oldCenter( - (*draggedLine->p1.x + *draggedLine->p2.x) / 2.0, - (*draggedLine->p1.y + *draggedLine->p2.y) / 2.0 - ); + else if (draggedCurve) { + if (Line* draggedLine = dynamic_cast(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(); + // Вычисляем смещение + double dx = newCenter.x() - oldCenter.x(); + 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++) { - Point* currentPoint = linePoints[i]; + for (int i = 0; i < 2; i++) { + Point* currentPoint = linePoints[i]; - *currentPoint->x += dx; - *currentPoint->y += dy; + *currentPoint->x += dx; + *currentPoint->y += dy; - // Обновляем совпадающие точки - for (Point* pair : points) { - if (pair != currentPoint && areCoincident(currentPoint, pair)) { - *pair->x = *currentPoint->x; - *pair->y = *currentPoint->y; + // Обновляем совпадающие точки + for (Point* pair : points) { + if (pair != currentPoint && areCoincident(currentPoint, pair)) { + *pair->x = *currentPoint->x; + *pair->y = *currentPoint->y; + } + } + } + } + else if (Circle* circle = dynamic_cast(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(); } } } - solve_for_canvas(); } #ifdef _DEBUG else showObjectTag(WIDGET_POSITION); #endif + solve_for_canvas(); } void Canvas::mouseReleaseEvent(QMouseEvent* event) @@ -584,8 +659,8 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event) solve_for_canvas(); } - if (draggedLine) { - draggedLine = nullptr; + if (draggedCurve) { + draggedCurve = nullptr; solve_for_canvas(); } @@ -610,26 +685,41 @@ void Canvas::paintEvent(QPaintEvent* event) p.drawLine(0, -5, 0, 5); // ====================== Отрисовка линий ====================== - 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); + for (Curve* curve : curves) { + if (Line* line = dynamic_cast(curve)) { + bool isSelected = (mode == Mode::Parallel && line == current_line); - // Рисуем линию - p.drawLine( - QPointF(*line->p1.x, *line->p1.y), - QPointF(*line->p2.x, *line->p2.y) - ); + // Настройка пера для линии + QPen linePen = isSelected ? QPen(Qt::red, 4) : QPen(Qt::black, 2); + p.setPen(linePen); - // Рисуем конечные точки линии - QBrush pointBrush = isSelected ? QBrush(Qt::red) : QBrush(Qt::darkBlue); - p.setBrush(pointBrush); - p.setPen(Qt::NoPen); + // Рисуем линию + p.drawLine( + QPointF(*line->p1.x, *line->p1.y), + QPointF(*line->p2.x, *line->p2.y) + ); - p.drawEllipse(QPointF(*line->p1.x, *line->p1.y), 5, 5); - p.drawEllipse(QPointF(*line->p2.x, *line->p2.y), 5, 5); + // Рисуем конечные точки линии + 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 = dynamic_cast(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) ====================== @@ -743,19 +833,19 @@ void Canvas::leaveEvent(QEvent* event) #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(); + //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 @@ -803,21 +893,25 @@ void Canvas::solve_for_canvas() } else { sys.applySolution(); - for (Line* line : lines) { - if (abs(*line->p1.x - *line->p2.x) < EPS && abs(*line->p1.y - *line->p2.y) < EPS && after_constraint) { - sys.undoSolution(); - flag = true; - break; + for (Curve* curve : curves) { + if (Line* line = dynamic_cast(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 (flag) { + 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(); } @@ -840,14 +934,15 @@ void Canvas::clearCanvas() delete pt; } - for (Line* line : lines) { - delete line; + for (Curve* curve : curves){ + delete curve; } + // Очистка контейнеров params.clear(); points.clear(); - lines.clear(); + curves.clear(); // Очистка контейнеров ограничений parallelPairs.clear(); diff --git a/Canvas.h b/Canvas.h index 3649af8..0f444cc 100644 --- a/Canvas.h +++ b/Canvas.h @@ -34,7 +34,8 @@ enum class Mode : int Coincedent = 3, ///< Режим задания совпадения точек Horizontal = 4, ///< Режим задания горизонтальности Vertical = 5, ///< Режим задания вертикальности - Perpendicular = 6 ///< Режим задания перпендикулярности + Perpendicular = 6, ///< Режим задания перпендикулярности + DrawingCircle = 7 ///< Режим рисования окружности }; /// Удобный тип для хранения пары параллельных линий (порядок не важен) @@ -168,7 +169,7 @@ private: * @param tolerance Допуск поиска * @return Найденная линия или nullptr */ - Line* findAt(QPointF& pos, qreal tolerance = 5.0); + Curve* findAt(QPointF& pos, qreal tolerance = 5.0); /** * @brief Найти точку в указанной позиции @@ -250,11 +251,12 @@ private: // ====================== Данные для перемещения объектов ====================== Point* draggedPoint{ nullptr }; ///< Точка, которую перемещают Line* draggedLine{ nullptr }; ///< Линия, которую перемещают + Curve* draggedCurve { nullptr }; QPointF dragOffset; ///< Смещение при начале перемещения // ====================== Данные геометрической системы ====================== System sys; ///< Геометрический солвер - QVector lines; ///< Завершённые линии + QVector curves; ///< Геометрические кривые QVector points; ///< Все точки сцены std::vector params; ///< Все параметры, передаваемые в солвер @@ -267,6 +269,7 @@ private: // ====================== Временные данные для режимов ====================== Line* current_line{ nullptr }; ///< Текущая линия в режимах рисования/параллельности + Circle* current_circle{ nullptr }; Point* firstPoint{ nullptr }; ///< Первая точка в режиме совпадения Mode mode{ Mode::None }; ///< Текущий режим работы diff --git a/DRAWer_2_0.cpp b/DRAWer_2_0.cpp index 7c62787..9a95dad 100644 --- a/DRAWer_2_0.cpp +++ b/DRAWer_2_0.cpp @@ -43,3 +43,9 @@ void DRAWer_2_0::on_Perpendicular_Button_clicked() ui.widget->changeMode(Mode::Perpendicular); } + +void DRAWer_2_0::on_Circle_Button_clicked() +{ + ui.widget->changeMode(Mode::DrawingCircle); +} + diff --git a/DRAWer_2_0.h b/DRAWer_2_0.h index a4e5f87..df1232a 100644 --- a/DRAWer_2_0.h +++ b/DRAWer_2_0.h @@ -24,6 +24,8 @@ private slots: void on_Perpendicular_Button_clicked(); + void on_Circle_Button_clicked(); + private: Ui::DRAWer_2_0Class ui; }; diff --git a/DRAWer_2_0.ui b/DRAWer_2_0.ui index 8bc1f52..66f3857 100644 --- a/DRAWer_2_0.ui +++ b/DRAWer_2_0.ui @@ -6,14 +6,14 @@ 0 0 - 691 - 615 + 776 + 701 1080 - 1080 + 1060 @@ -27,64 +27,110 @@ 23 - - - - 75 - 24 - - - - Line - - + + + + + + + Primitives + + + Qt::AlignmentFlag::AlignCenter + + + true + + + + + + + + + + 75 + 24 + + + + Line + + + + + + + Circle + + + + - - - - 75 - 24 - - - - Parallel - - - - - - - Perpendicular - - - - - - - - 75 - 24 - - - - P2P - - - - - - - Vertical - - - - - - - Horizontal - - + + + + + Constraints + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + 75 + 24 + + + + P2P + + + + + + + Perpendicular + + + + + + + Horizontal + + + + + + + + 75 + 24 + + + + Parallel + + + + + + + Vertical + + + + + + @@ -108,6 +154,9 @@ 16777215 + + CrossCursor + Canvas @@ -120,8 +169,8 @@ 0 0 - 691 - 21 + 776 + 22 diff --git a/GCS/Geo.cpp b/GCS/Geo.cpp index c8eae86..5c7a3b5 100644 --- a/GCS/Geo.cpp +++ b/GCS/Geo.cpp @@ -149,7 +149,7 @@ void Line::set_tag(int _tag) { p2.set_tag(_tag); } -int Line::get_tag() +int Curve::get_tag() { return tag; } @@ -275,6 +275,11 @@ Circle* Circle::Copy() return crv; } +void Circle::set_tag(int _tag) { + tag = _tag; + center.set_tag(tag); +} + //------------arc int Arc::PushOwnParams(VEC_pD& pvec) { diff --git a/GCS/Geo.h b/GCS/Geo.h index 42e183d..2b5ca35 100644 --- a/GCS/Geo.h +++ b/GCS/Geo.h @@ -173,6 +173,8 @@ public: /// A base class for all curve-based objects (line, circle/arc, ellipse/arc). class Curve { +protected: + int tag; public: virtual ~Curve() {} @@ -212,12 +214,13 @@ public: // DeepSOIC: I haven't found a way to simply copy a curve object provided pointer to a curve // object. virtual Curve* Copy() = 0; + + void set_tag(int); + int get_tag(); }; class Line: public Curve { -private: - int tag; public: Line() {} @@ -238,7 +241,6 @@ public: Line* Copy() override; void set_tag(int); - int get_tag(); bool contains(QPointF, qreal tol) const; }; @@ -253,12 +255,14 @@ public: ~Circle() override {} Point center; + Point* center_ref; double* rad; DeriVector2 CalculateNormal(const Point& p, const double* derivparam = nullptr) const override; DeriVector2 Value(double u, double du, const double* derivparam = nullptr) const override; int PushOwnParams(VEC_pD& pvec) override; void ReconstructOnNewPvec(VEC_pD& pvec, int& cnt) override; Circle* Copy() override; + void set_tag(int); }; class Arc: public Circle diff --git a/dependencies/eigen b/dependencies/eigen new file mode 160000 index 0000000..9164d3f --- /dev/null +++ b/dependencies/eigen @@ -0,0 +1 @@ +Subproject commit 9164d3f16ad21c1546d5fc99cd28fffc8ac3c1ee