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 24316cdd4f P2P constraint fix
Но по-хорошему стоит придумать, как после P2P соединить линии одной точкой
2025-12-08 23:01:02 +03:00

348 lines
7.9 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));
}
void Canvas::changeMode(Mode _mode)
{
mode = _mode;
}
template <typename T, typename A, typename B>
T makeOrderedPair(A* obj1, B* obj2)
{
return (obj1->get_tag() < obj2->get_tag()) ? std::make_pair(obj1, obj2) : std::make_pair(obj2, obj1);
}
// 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->p1;
else if (dist_P2P(p2, pos) <= tolerance)
temp = &line->p2;
}
return temp;
}
bool Canvas::areCoincident(Point* p1, Point* p2, bool mode)
{
int tag_0 = p1->get_tag(), tag_1 = p2->get_tag();
if (!mode){
return (tag_0 != tag_1 && *p1 == *p2);
}
else {
for (PointPair pair : P2Ppairs) {
int pair_tag_0 = pair.first->get_tag();
int pair_tag_1 = pair.second->get_tag();
if (pair_tag_0 == tag_0 && pair_tag_1 == tag_1 ||
pair_tag_0 == tag_1 && pair_tag_1 == tag_0)
return true;
}
return false;
}
}
bool Canvas::areAlreadyParallel(Line* l1, Line* l2)
{
return parallelPairs.count(makeOrderedPair<LinePair>(l1, l2)) > 0;
}
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);
return;
}
}
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(Point(x1, y1, obj_count));
points.push_back(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;
}
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;
points.pop_back();
points.pop_back();
params.pop_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->p1 == firstPoint || &l->p2 == firstPoint) l1 = l;
if (&l->p1 == clickedPoint || &l->p2 == clickedPoint) l2 = l;
}
if (l1 == l2 ||
(areCoincident(firstPoint, clickedPoint, true)))
{
QMessageBox::warning(this, QString("NO!"), QString("P2P failed"));
firstPoint = nullptr;
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;
*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) {
QPen pen(Qt::blue, 3, Qt::DashLine);
p.setPen(pen);
p.setBrush(Qt::transparent);
p.drawLine(QPointF(*current_line->p1.x, *current_line->p1.y),
QPointF(*current_line->p2.x, *current_line->p2.y));
// Концы текущей линии — синие точки
p.setBrush(Qt::blue);
p.setPen(Qt::NoPen);
p.drawEllipse(QPointF(*current_line->p1.x, *current_line->p1.y), 6, 6);
p.drawEllipse(QPointF(*current_line->p2.x, *current_line->p2.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;
}