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
ParkSuMin 7c24270e6e Horizontal and vertical points
Теперь можно обе точки при ограничении дёргать
2025-12-14 20:30:08 +03:00

372 lines
8.6 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"
static double dist_P2P(QPointF p1, QPointF p2) {
return sqrt(pow(p2.x() - p1.x(), 2) + pow(p2.y() - p1.y(), 2));
}
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);
}
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<PointPair>(p1, p2));
}
// False - Horizonta, True - Vertical
bool Canvas::areHorizontalVertical(Point* p1, Point* p2, bool mode)
{
if (!mode)
return HORIZ_pairs.count(makeOrderedPair<PointPair>(p1, p2));
else
return VERT_pairs.count(makeOrderedPair<PointPair>(p1, p2));
}
bool Canvas::areAlreadyParallel(Line* l1, Line* l2)
{
return parallelPairs.count(makeOrderedPair<LinePair>(l1, l2));
}
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++);
HORIZ_pairs.insert(makeOrderedPair<PointPair>(found->start_ref, found->end_ref));
}
else {
sys.addConstraintVertical(*found, constraints_count++);
VERT_pairs.insert(makeOrderedPair<PointPair>(found->start_ref, found->end_ref));
}
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<LinePair>(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::warning(this, QString("NO!"), QString("P2P failed"));
firstPoint = nullptr;
mode = Mode::None;
update();
return;
}
sys.addConstraintP2PCoincident(*clickedPoint, *firstPoint, constraints_count++);
P2Ppairs.insert(makeOrderedPair<PointPair>(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();
}
if (areHorizontalVertical(draggedPoint, pair, true)) {
*pair->x = pos.x();
}
if (areHorizontalVertical(draggedPoint, pair, false)) {
*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::warning(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) {
// Концы текущей линии — синие точки
p.setBrush(Qt::blue);
p.setPen(Qt::NoPen);
p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.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;
}