Merge pull request 'Perpendicular' (#7) from Perpendicular into master
Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
198
Canvas.cpp
198
Canvas.cpp
@@ -1,4 +1,6 @@
|
||||
#include "Canvas.h"
|
||||
#define WIDGET_POSITION event->pos()
|
||||
#define UCS_POSITION screenToLogical(WIDGET_POSITION)
|
||||
|
||||
// ===================================================================
|
||||
// Вспомогательные функции
|
||||
@@ -78,7 +80,6 @@ void Canvas::changeMode(Mode _mode)
|
||||
|
||||
Line* Canvas::findAt(QPointF& pos, qreal tolerance)
|
||||
{
|
||||
// TODO: реализовать проверку, находится ли точка на линии
|
||||
for (Line* line : lines) {
|
||||
if (line->contains(pos, tolerance)) {
|
||||
return line;
|
||||
@@ -136,48 +137,62 @@ 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));
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Методы работы с ограничениями
|
||||
// ===================================================================
|
||||
|
||||
void Canvas::remove_constraints()
|
||||
// TODO - переделать в bool для отображения статуса выполнения
|
||||
void Canvas::remove_constraint(int tag)
|
||||
{
|
||||
auto it = C_Info.find(tag);
|
||||
if (it == C_Info.end()) return;
|
||||
|
||||
ConstraintInfo info = it->second;
|
||||
|
||||
// Удаляем ограничение из солвера
|
||||
sys.clearByTag(constraints_count - 1);
|
||||
sys.clearByTag(tag);
|
||||
|
||||
// Удаляем из соответствующих контейнеров в зависимости от типа ограничения
|
||||
switch (lastConstraint.mode) {
|
||||
switch (info.mode) {
|
||||
case Mode::Horizontal: {
|
||||
if (auto* pair = std::get_if<PointPair>(&lastConstraint.data)) {
|
||||
if (auto* pair = std::get_if<PointPair>(&info.data)) {
|
||||
HORIZ_pairs.erase(*pair);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::Vertical: {
|
||||
if (auto* pair = std::get_if<PointPair>(&lastConstraint.data)) {
|
||||
if (auto* pair = std::get_if<PointPair>(&info.data)) {
|
||||
VERT_pairs.erase(*pair);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::Parallel: {
|
||||
if (auto* pair = std::get_if<LinePair>(&lastConstraint.data)) {
|
||||
if (auto* pair = std::get_if<LinePair>(&info.data)) {
|
||||
parallelPairs.erase(*pair);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::Coincedent: {
|
||||
if (auto* pair = std::get_if<PointPair>(&lastConstraint.data)) {
|
||||
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;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Сбрасываем информацию о последнем ограничении
|
||||
lastConstraint.mode = Mode::None;
|
||||
constraints_count--;
|
||||
C_Info.erase(it);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -244,17 +259,15 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
sys.addConstraintHorizontal(*found, constraints_count++);
|
||||
auto pair = makeOrderedPair<PointPair>(found->start_ref, found->end_ref);
|
||||
HORIZ_pairs.insert(pair);
|
||||
lastConstraint.mode = Mode::Horizontal;
|
||||
lastConstraint.data = 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);
|
||||
lastConstraint.mode = Mode::Vertical;
|
||||
lastConstraint.data = pair;
|
||||
C_Info[constraints_count - 1] = { Mode::Vertical, pair };
|
||||
}
|
||||
update();
|
||||
solve_for_canvas();
|
||||
after_constraint = true;
|
||||
}
|
||||
mode = Mode::None;
|
||||
@@ -315,7 +328,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
mode = Mode::None;
|
||||
QMessageBox::critical(this, "WHOOPS",
|
||||
"Sorry, your line is very short", QMessageBox::Ok);
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -330,7 +343,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
obj_count++;
|
||||
after_constraint = true;
|
||||
}
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -340,21 +353,21 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
|
||||
if (!found) {
|
||||
current_line = nullptr;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
// Первый клик: выбираем первую линию
|
||||
if (!current_line) {
|
||||
current_line = found;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
// Повторный клик на ту же линию: сброс выбора
|
||||
if (found == current_line) {
|
||||
current_line = nullptr;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -363,13 +376,12 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
auto pair = makeOrderedPair<LinePair>(found, current_line);
|
||||
sys.addConstraintParallel(*found, *current_line, constraints_count++);
|
||||
parallelPairs.insert(pair);
|
||||
lastConstraint.mode = Mode::Parallel;
|
||||
lastConstraint.data = pair;
|
||||
C_Info[constraints_count - 1] = { Mode::Parallel, pair };
|
||||
|
||||
current_line = nullptr;
|
||||
mode = Mode::None;
|
||||
after_constraint = true;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
}
|
||||
else {
|
||||
// Линии уже параллельны - сообщаем об ошибке
|
||||
@@ -383,7 +395,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
QMessageBox::Ok
|
||||
);
|
||||
current_line = nullptr;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -393,21 +405,21 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
Point* clickedPoint = findPointAt(scene);
|
||||
if (!clickedPoint) {
|
||||
firstPoint = nullptr;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
// Первый клик: выбираем первую точку
|
||||
if (!firstPoint) {
|
||||
firstPoint = clickedPoint;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
// Повторный клик на ту же точку: сброс выбора
|
||||
if (clickedPoint == firstPoint) {
|
||||
firstPoint = nullptr;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -426,7 +438,7 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
QMessageBox::critical(this, QString("NO!"), QString("P2P failed"));
|
||||
firstPoint = nullptr;
|
||||
mode = Mode::None;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -434,15 +446,61 @@ void Canvas::mousePressEvent(QMouseEvent* event)
|
||||
sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++);
|
||||
auto pair = makeOrderedPair<PointPair>(clickedPoint, firstPoint);
|
||||
P2Ppairs.insert(pair);
|
||||
lastConstraint.mode = Mode::Coincedent;
|
||||
lastConstraint.data = pair;
|
||||
C_Info[constraints_count - 1] = { Mode::Coincedent, pair };
|
||||
|
||||
firstPoint = nullptr;
|
||||
mode = Mode::None;
|
||||
after_constraint = true;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
return;
|
||||
}
|
||||
|
||||
else if (mode == Mode::Perpendicular) {
|
||||
Line* found = 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::mouseMoveEvent(QMouseEvent* event)
|
||||
@@ -451,8 +509,18 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
|
||||
if (draggedPoint) {
|
||||
QPointF pos = UCS_POSITION - dragOffset;
|
||||
|
||||
auto coincidentGroup = getCoincidentGroup(draggedPoint);
|
||||
*draggedPoint->x = pos.x();
|
||||
*draggedPoint->y = pos.y();
|
||||
|
||||
// TODO
|
||||
for (Point* pair : points) {
|
||||
if (areCoincident(draggedPoint, pair)) {
|
||||
*pair->x = pos.x();
|
||||
*pair->y = pos.y();
|
||||
}
|
||||
}
|
||||
|
||||
auto coincidentGroup = getCoincidentGroup(draggedPoint);
|
||||
for (Point* pt : coincidentGroup) {
|
||||
*pt->x = pos.x();
|
||||
*pt->y = pos.y();
|
||||
@@ -469,9 +537,16 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
|
||||
}
|
||||
}
|
||||
|
||||
*draggedPoint->x = pos.x();
|
||||
*draggedPoint->y = pos.y();
|
||||
update();
|
||||
for (Point* other : points) {
|
||||
if (areHorizontalVertical(draggedPoint, other, true)) {
|
||||
*other->x = pos.x();
|
||||
}
|
||||
if (areHorizontalVertical(draggedPoint, other, false)) {
|
||||
*other->y = pos.y();
|
||||
}
|
||||
}
|
||||
|
||||
solve_for_canvas();
|
||||
}
|
||||
|
||||
// ====================== Перемещение линии ======================
|
||||
@@ -503,7 +578,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
|
||||
}
|
||||
}
|
||||
}
|
||||
update();
|
||||
solve_for_canvas();
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
else
|
||||
@@ -517,12 +592,12 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event)
|
||||
|
||||
if (draggedPoint) {
|
||||
draggedPoint = nullptr;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
}
|
||||
|
||||
if (draggedLine) {
|
||||
draggedLine = nullptr;
|
||||
update();
|
||||
solve_for_canvas();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,22 +618,6 @@ void Canvas::paintEvent(QPaintEvent* event)
|
||||
p.drawLine(-5, 0, 5, 0);
|
||||
p.drawLine(0, -5, 0, 5);
|
||||
|
||||
|
||||
// ====================== Решение системы уравнений ======================
|
||||
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::warning(this, QString("Error!"),
|
||||
QString("Last constraint is unavailable!"));
|
||||
remove_constraints();
|
||||
}
|
||||
after_constraint = false;
|
||||
}
|
||||
|
||||
// ====================== Отрисовка линий ======================
|
||||
for (Line* line : lines) {
|
||||
bool isSelected = (mode == Mode::Parallel && line == current_line);
|
||||
@@ -651,3 +710,30 @@ std::vector<Point*> Canvas::getCoincidentGroup(Point* p)
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
QMessageBox::warning(this, QString("Error!"), QString("Last constraint is unavailable!"));
|
||||
remove_constraint(constraints_count - 1);
|
||||
constraints_count--;
|
||||
}
|
||||
after_constraint = false;
|
||||
update();
|
||||
}
|
||||
24
Canvas.h
24
Canvas.h
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define WIDGET_POSITION event->pos()
|
||||
#define UCS_POSITION screenToLogical(WIDGET_POSITION)
|
||||
constexpr auto EPS = 1e-9;
|
||||
|
||||
#include <QWidget>
|
||||
#include <QMouseEvent>
|
||||
@@ -30,7 +28,8 @@ enum class Mode : int
|
||||
Parallel = 2, ///< Режим задания параллельности
|
||||
Coincedent = 3, ///< Режим задания совпадения точек
|
||||
Horizontal = 4, ///< Режим задания горизонтальности
|
||||
Vertical = 5 ///< Режим задания вертикальности
|
||||
Vertical = 5, ///< Режим задания вертикальности
|
||||
Perpendicular = 6
|
||||
};
|
||||
|
||||
/// Удобный тип для хранения пары параллельных линий (порядок не важен)
|
||||
@@ -94,10 +93,12 @@ private:
|
||||
/// Проверить, являются ли две линии уже параллельными (дубликат ограничения)
|
||||
bool areAlreadyParallel(Line* line1, Line* line2);
|
||||
|
||||
bool areAlreadyPerpendicular(Line* line1, Line* line2);
|
||||
|
||||
// ====================== Методы работы с ограничениями ======================
|
||||
|
||||
/// Удалить последние добавленные ограничения при ошибке солвера
|
||||
void remove_constraints();
|
||||
void remove_constraint(int);
|
||||
|
||||
// ====================== Данные для перемещения объектов ======================
|
||||
|
||||
@@ -115,6 +116,7 @@ private:
|
||||
// ====================== Коллекции ограничений ======================
|
||||
|
||||
std::set<LinePair> parallelPairs; ///< Пары параллельных линий
|
||||
std::set<LinePair> perpendicularPairs;
|
||||
std::set<PointPair> P2Ppairs; ///< Пары совпадающих точек
|
||||
std::set<PointPair> HORIZ_pairs; ///< Пары точек горизонтальных линий
|
||||
std::set<PointPair> VERT_pairs; ///< Пары точек вертикальных линий
|
||||
@@ -138,10 +140,12 @@ private:
|
||||
|
||||
/// Структура для хранения информации о последнем добавленном ограничении
|
||||
/// (используется для отката при ошибке солвера)
|
||||
struct LastConstraint {
|
||||
Mode mode{ Mode::None }; ///< Тип последнего ограничения
|
||||
std::variant<LinePair, PointPair> data; ///< Данные ограничения
|
||||
};
|
||||
|
||||
LastConstraint lastConstraint; ///< Информация о последнем добавленном ограничении
|
||||
struct ConstraintInfo {
|
||||
Mode mode;
|
||||
std::variant<LinePair, PointPair> data;
|
||||
};
|
||||
std::map<int, ConstraintInfo> C_Info;
|
||||
|
||||
void solve_for_canvas();
|
||||
};
|
||||
@@ -40,3 +40,9 @@ void DRAWer_2_0::on_Vertical_Button_clicked()
|
||||
ui.widget->changeMode(Mode::Vertical);
|
||||
}
|
||||
|
||||
|
||||
void DRAWer_2_0::on_Perpendicular_Button_clicked()
|
||||
{
|
||||
ui.widget->changeMode(Mode::Perpendicular);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ private slots:
|
||||
|
||||
void on_Vertical_Button_clicked();
|
||||
|
||||
void on_Perpendicular_Button_clicked();
|
||||
|
||||
private:
|
||||
Ui::DRAWer_2_0Class ui;
|
||||
int counter;
|
||||
|
||||
@@ -52,6 +52,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="Perpendicular_Button">
|
||||
<property name="text">
|
||||
<string>Perpendicular</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="P2P_Button">
|
||||
<property name="minimumSize">
|
||||
|
||||
Reference in New Issue
Block a user