2024-12-07 17:24:36 +08:00
|
|
|
|
#include "graphicsItem/electricConnectLineItem.h"
|
|
|
|
|
|
#include <QGraphicsScene>
|
|
|
|
|
|
#include <QPainter>
|
2024-12-13 18:08:00 +08:00
|
|
|
|
#include <QStyleOptionGraphicsItem>
|
2024-12-07 17:24:36 +08:00
|
|
|
|
|
|
|
|
|
|
ElectricConnectLineItem::ElectricConnectLineItem(QGraphicsItem *parent)
|
2025-06-13 19:00:05 +08:00
|
|
|
|
: GraphicsProjectModelItem(parent)
|
2025-11-14 19:31:09 +08:00
|
|
|
|
{
|
|
|
|
|
|
initial();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ElectricConnectLineItem::ElectricConnectLineItem(const ElectricConnectLineItem& obj)
|
|
|
|
|
|
: GraphicsProjectModelItem(obj)
|
|
|
|
|
|
{
|
|
|
|
|
|
initial();
|
|
|
|
|
|
m_lstPoints = obj.m_lstPoints;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ElectricConnectLineItem::~ElectricConnectLineItem()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ElectricConnectLineItem* ElectricConnectLineItem::clone() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ElectricConnectLineItem(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::initial()
|
2024-12-07 17:24:36 +08:00
|
|
|
|
{
|
2024-12-13 18:08:00 +08:00
|
|
|
|
m_boundingRect = QRectF();
|
2024-12-07 17:24:36 +08:00
|
|
|
|
m_pen = QPen(Qt::black);
|
|
|
|
|
|
m_brush = QBrush(Qt::NoBrush);
|
|
|
|
|
|
setHandleVisible(false);
|
|
|
|
|
|
setFunctionHandleIfShow(false);
|
|
|
|
|
|
setFunctionHandleEnaable(false);
|
2024-12-13 18:08:00 +08:00
|
|
|
|
m_lstPoints.push_back(QPointF()); //起点
|
|
|
|
|
|
m_lstPoints.push_back(QPointF()); //终点
|
|
|
|
|
|
_curLine = QPoint();
|
2024-12-07 17:24:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-12-13 18:08:00 +08:00
|
|
|
|
void ElectricConnectLineItem::setStartPoint(const QPointF& p)
|
|
|
|
|
|
{
|
|
|
|
|
|
int n = m_lstPoints.size();
|
|
|
|
|
|
if(n)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(n >2)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(m_lstPoints[0].x() == m_lstPoints[1].x()) //相邻点在垂直方向,水平移动,否则垂直移动
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints[1].setX(p.x());
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints[1].setY(p.y());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
m_lstPoints[0] = p;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void ElectricConnectLineItem::setEndPoint(const QPointF& p)
|
|
|
|
|
|
{
|
|
|
|
|
|
int n = m_lstPoints.size();
|
|
|
|
|
|
if(n)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(n >2)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(m_lstPoints[n-1].x() == m_lstPoints[n-2].x()) //相邻点在垂直方向,水平移动,否则垂直移动
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints[n-2].setX(p.x());
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints[n-2].setY(p.y());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
m_lstPoints[n-1] = p;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPainterPath ElectricConnectLineItem::shape() const
|
2024-12-07 17:24:36 +08:00
|
|
|
|
{
|
|
|
|
|
|
QPainterPath path;
|
2024-12-13 18:08:00 +08:00
|
|
|
|
//path.addPath(m_points);
|
|
|
|
|
|
path.addPath(m_pointsBoundingRect);
|
2024-12-07 17:24:36 +08:00
|
|
|
|
return path;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-13 18:08:00 +08:00
|
|
|
|
QRectF ElectricConnectLineItem::boundingRect() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_boundingRect;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-07 17:24:36 +08:00
|
|
|
|
void ElectricConnectLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
|
|
|
|
|
{
|
2024-12-13 18:08:00 +08:00
|
|
|
|
if (option->state & QStyle::State_Selected)
|
|
|
|
|
|
{
|
|
|
|
|
|
painter->setPen(Qt::red);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-12-03 18:27:49 +08:00
|
|
|
|
if(_curMonitorStateEnable){ //当前状态被设置
|
|
|
|
|
|
painter->setPen(QPen(_curMonitorStateColor));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
painter->setPen(m_pen);
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-07 17:24:36 +08:00
|
|
|
|
painter->setBrush(m_brush);
|
|
|
|
|
|
|
|
|
|
|
|
painter->drawPath(m_points);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-13 18:08:00 +08:00
|
|
|
|
void ElectricConnectLineItem::moveLine(QPointF pos)
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF delta = pos - m_lastPoint;
|
|
|
|
|
|
if(_curLine.isNull())
|
|
|
|
|
|
{
|
|
|
|
|
|
if(m_lstPoints.size() > 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
int n = m_lstPoints.size();
|
|
|
|
|
|
for(int i = 0;i < m_lstPoints.size() -1;++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF p1 = m_lstPoints[i];
|
|
|
|
|
|
QPointF p2 = m_lstPoints[i+1];
|
|
|
|
|
|
|
|
|
|
|
|
if(p1.x() == p2.x() && abs(p1.x() - pos.x()) < 2) //在相同竖线
|
|
|
|
|
|
{
|
|
|
|
|
|
if(p1.y() > p2.y())
|
|
|
|
|
|
{
|
|
|
|
|
|
if(pos.y() > p2.y() && pos.y() < p1.y()) //在竖线上
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if(i == 0 ) //起点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
|
|
|
|
|
|
i+=2;
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐后端
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(m_lstPoints.last());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if(i+1 == n-1) //终点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
|
|
|
|
|
|
m_lstPoints[i+1] = p2;
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐前端,移动后至少6个点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(m_lstPoints.first());
|
|
|
|
|
|
i+=1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//qDebug() << 1 <<" "<<i;
|
|
|
|
|
|
_curLine = QPoint(i,1);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(p1.y() < p2.y())
|
|
|
|
|
|
{
|
|
|
|
|
|
if(pos.y() < p2.y() && pos.y() > p1.y()) //在竖线上
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if(i == 0) //起点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
i+=2;
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐后端
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(m_lstPoints.last());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if(i+1 == n-1) //终点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
|
|
|
|
|
|
m_lstPoints[i+1] = p2;
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐前端
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(m_lstPoints.first());
|
|
|
|
|
|
i+=1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//qDebug() << 2 <<" "<<i;
|
|
|
|
|
|
_curLine = QPoint(i,1);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(p1.y() == p2.y() && abs(p1.y() - pos.y()) < 2) //在相同横线
|
|
|
|
|
|
{
|
|
|
|
|
|
if(p1.x() > p2.x())
|
|
|
|
|
|
{
|
|
|
|
|
|
if(pos.x() > p2.x() && pos.x() < p1.x())
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if(i == 0) //起点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
|
|
|
|
|
|
i+=2;
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐后端
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(m_lstPoints.last());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if(i+1 == n-1) //终点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐前端,移动后至少6个点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(m_lstPoints.first());
|
|
|
|
|
|
i+=1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//qDebug() << 3 <<" "<<i;
|
|
|
|
|
|
_curLine = QPoint(i,-1);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(p1.x() < p2.x())
|
|
|
|
|
|
{
|
|
|
|
|
|
if(pos.x() < p2.x() && pos.x() > p1.x())
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if(i == 0) //起点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
m_lstPoints.prepend(p1);
|
|
|
|
|
|
|
|
|
|
|
|
i+=2;
|
|
|
|
|
|
m_lstPoints[i] = p1;
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐后端
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(m_lstPoints.last());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if(i+1 == n-1) //终点是连接点,创建复制点
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
m_lstPoints.append(p2);
|
|
|
|
|
|
|
|
|
|
|
|
if(m_lstPoints.size() < 6) //补齐前端
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints.prepend(m_lstPoints.first());
|
|
|
|
|
|
i+=1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//qDebug() << 4 <<" "<<i;
|
|
|
|
|
|
_curLine = QPoint(i,-1);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
int n = _curLine.x();
|
|
|
|
|
|
//qDebug() << n <<" "<<_curLine.y();
|
|
|
|
|
|
//if(m_lstPoints.size() > 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if(_curLine.y() == 1) //竖线
|
|
|
|
|
|
{
|
|
|
|
|
|
if(n == 0) //起点是原点
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF p(0,delta.y()); //原点不动
|
|
|
|
|
|
m_lstPoints[n +1] = m_lstPoints[n +1] + p; //第二个点只竖直移动
|
|
|
|
|
|
m_lstPoints[n +2] = m_lstPoints[n +2] + p; //第三个点只竖直移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(n == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF p(delta.x(),0);
|
|
|
|
|
|
m_lstPoints[n] += p; //第一个点只水平移动
|
|
|
|
|
|
m_lstPoints[n +1] += p; //第二个点只水平移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(n == m_lstPoints.size()-2) //起点是倒数第二个点
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF p(0,delta.y());
|
|
|
|
|
|
m_lstPoints[n] += p; //起点只上下移动
|
|
|
|
|
|
m_lstPoints[n-1] += p; //上个点只竖直移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(n == m_lstPoints.size()-3) //起点是倒数第三个点
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF py(0,delta.y());
|
|
|
|
|
|
QPointF px(delta.x(),0);
|
|
|
|
|
|
m_lstPoints[n-1] += py; //上个点只垂直移动
|
|
|
|
|
|
m_lstPoints[n] += delta; //起点任意动
|
|
|
|
|
|
m_lstPoints[n+1] += px; //第二个点只水平移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints[n -1].setY(m_lstPoints[n ].y());
|
|
|
|
|
|
m_lstPoints[n] += delta;
|
|
|
|
|
|
m_lstPoints[n +1] += delta;
|
|
|
|
|
|
m_lstPoints[n +2].setY(m_lstPoints[n +1].y());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else //横线
|
|
|
|
|
|
{
|
|
|
|
|
|
if(n == 0) //起点是原点
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF p(delta.x(),0); //原点不动
|
|
|
|
|
|
m_lstPoints[n +1] = m_lstPoints[n +1] + p; //第二个点只水平移动
|
|
|
|
|
|
m_lstPoints[n +2] = m_lstPoints[n +2] + p; //第三个点只水平移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(n == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF p(0,delta.y());
|
|
|
|
|
|
m_lstPoints[n] = m_lstPoints[n] + p; //第一个点只竖直移动
|
|
|
|
|
|
m_lstPoints[n +1] = m_lstPoints[n +1] + p; //第二个点只竖直移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(n == m_lstPoints.size()-2) //起点是倒数第二个点
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF p(delta.x(),0);
|
|
|
|
|
|
m_lstPoints[n] += p; //起点水平移动
|
|
|
|
|
|
m_lstPoints[n-1] += p; //上个点只水平移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(n == m_lstPoints.size()-3) //起点是倒数第三个点
|
|
|
|
|
|
{
|
|
|
|
|
|
QPointF py(0,delta.y());
|
|
|
|
|
|
QPointF px(delta.x(),0);
|
|
|
|
|
|
m_lstPoints[n-1] += px; //上个点只水平移动
|
|
|
|
|
|
m_lstPoints[n] += delta; //起点任意动
|
|
|
|
|
|
m_lstPoints[n+1] += py; //第二个点只上下移动
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lstPoints[n -1].setX(m_lstPoints[n].x());
|
|
|
|
|
|
m_lstPoints[n] += delta;
|
|
|
|
|
|
m_lstPoints[n +1] += delta;
|
|
|
|
|
|
m_lstPoints[n +2].setX(m_lstPoints[n +1].x());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
calculatePath();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_lastPoint = pos;
|
|
|
|
|
|
}
|
2024-12-07 17:24:36 +08:00
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::calculatePath()
|
|
|
|
|
|
{
|
2026-02-04 20:44:59 +08:00
|
|
|
|
if (m_lstPoints.size() < 2) return;
|
|
|
|
|
|
|
|
|
|
|
|
QPointF start = m_lstPoints.first();
|
|
|
|
|
|
QPointF end = m_lstPoints.last();
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "\n=== calculatePath ===";
|
|
|
|
|
|
qDebug() << "Start:" << start << "End:" << end;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新路径
|
|
|
|
|
|
m_points = QPainterPath();
|
|
|
|
|
|
m_points.moveTo(start);
|
|
|
|
|
|
qDebug() << "Initialized path, current position:" << m_points.currentPosition();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取元件
|
|
|
|
|
|
QList<QRectF> components = getComponentCollisionRects();
|
|
|
|
|
|
qDebug() << "Found" << components.size() << "components";
|
|
|
|
|
|
|
|
|
|
|
|
// 检查起点终点距离
|
|
|
|
|
|
qDebug() << "Distance:" << QLineF(start, end).length();
|
|
|
|
|
|
|
|
|
|
|
|
// 如果距离很近,直接连接
|
|
|
|
|
|
if (QLineF(start, end).length() < 10) {
|
|
|
|
|
|
qDebug() << "Points are very close, direct connection";
|
|
|
|
|
|
m_points.lineTo(end);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 生成路径
|
|
|
|
|
|
generateAvoidancePath(start, end, components);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证路径
|
|
|
|
|
|
qDebug() << "After generation - Path element count:" << m_points.elementCount();
|
|
|
|
|
|
qDebug() << "After generation - Current position:" << m_points.currentPosition();
|
|
|
|
|
|
|
|
|
|
|
|
if (m_points.elementCount() <= 1) {
|
|
|
|
|
|
qWarning() << "Path has only" << m_points.elementCount() << "elements! Adding direct line";
|
|
|
|
|
|
m_points.lineTo(end);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保路径结束于终点
|
|
|
|
|
|
if (m_points.currentPosition() != end) {
|
|
|
|
|
|
qWarning() << "Path does not end at destination! Adding final segment";
|
|
|
|
|
|
qDebug() << "Expected end:" << end << "Actual end:" << m_points.currentPosition();
|
|
|
|
|
|
m_points.lineTo(end);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
update();
|
|
|
|
|
|
m_boundingRect = m_points.boundingRect();
|
|
|
|
|
|
prepareGeometryChange();
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "Final path bounds:" << m_boundingRect;
|
|
|
|
|
|
qDebug() << "=== calculatePath end ===\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用形状进行避障
|
|
|
|
|
|
void ElectricConnectLineItem::generateAvoidancePath(const QPointF& start, const QPointF& end,
|
|
|
|
|
|
const QList<QRectF>& components)
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "=== generateAvoidancePath (rectilinear) ===";
|
|
|
|
|
|
|
|
|
|
|
|
m_points = QPainterPath();
|
|
|
|
|
|
m_points.moveTo(start);
|
|
|
|
|
|
|
|
|
|
|
|
// 生成候选路径列表,按长度排序
|
|
|
|
|
|
QMultiMap<double, QList<QPointF>> candidatePaths;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 收集所有可能的直角路径
|
|
|
|
|
|
collectRectilinearPaths(start, end, components, candidatePaths);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 选择最短的安全路径
|
|
|
|
|
|
for (auto it = candidatePaths.begin(); it != candidatePaths.end(); ++it) {
|
|
|
|
|
|
const QList<QPointF>& path = it.value();
|
|
|
|
|
|
|
|
|
|
|
|
if (isPathSafe(path, components)) {
|
|
|
|
|
|
qDebug() << " Selected path with length" << it.key() << ":" << path;
|
|
|
|
|
|
|
|
|
|
|
|
// 绘制路径
|
|
|
|
|
|
for (int i = 1; i < path.size(); i++) {
|
|
|
|
|
|
m_points.lineTo(path[i]);
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
2026-02-04 20:44:59 +08:00
|
|
|
|
return;
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
2026-02-04 20:44:59 +08:00
|
|
|
|
}
|
2024-12-07 17:24:36 +08:00
|
|
|
|
|
2026-02-04 20:44:59 +08:00
|
|
|
|
// 3. 所有路径都失败,使用强制绕行
|
|
|
|
|
|
qDebug() << " All rectilinear paths failed, using forced bypass";
|
|
|
|
|
|
generateForcedRectilinearBypass(start, end, components);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算路径长度
|
|
|
|
|
|
double ElectricConnectLineItem::calculatePathLength(const QList<QPointF>& path)
|
|
|
|
|
|
{
|
|
|
|
|
|
double length = 0;
|
|
|
|
|
|
for (int i = 0; i < path.size() - 1; i++) {
|
|
|
|
|
|
length += QLineF(path[i], path[i+1]).length();
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
2026-02-04 20:44:59 +08:00
|
|
|
|
return length;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 单一线段与矩形相交检测
|
|
|
|
|
|
bool ElectricConnectLineItem::lineIntersectsRect(const QLineF& line, const QRectF& rect)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查端点是否在矩形内
|
|
|
|
|
|
if (rect.contains(line.p1()) || rect.contains(line.p2())) {
|
|
|
|
|
|
return true;
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
2026-02-04 20:44:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查是否与矩形的边相交
|
|
|
|
|
|
QLineF edges[4] = {
|
|
|
|
|
|
QLineF(rect.topLeft(), rect.topRight()),
|
|
|
|
|
|
QLineF(rect.topRight(), rect.bottomRight()),
|
|
|
|
|
|
QLineF(rect.bottomRight(), rect.bottomLeft()),
|
|
|
|
|
|
QLineF(rect.bottomLeft(), rect.topLeft())
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
QPointF intersection;
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
|
if (line.intersects(edges[i], &intersection) == QLineF::BoundedIntersection) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 辅助方法实现
|
|
|
|
|
|
bool ElectricConnectLineItem::isSegmentSafe(const QPointF& p1, const QPointF& p2,
|
|
|
|
|
|
const QList<QRectF>& components)
|
|
|
|
|
|
{
|
|
|
|
|
|
QLineF segment(p1, p2);
|
|
|
|
|
|
|
|
|
|
|
|
for (const QRectF& component : components) {
|
|
|
|
|
|
if (segmentPenetratesComponent(segment, component)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ElectricConnectLineItem::isPathSafe(const QList<QPointF>& path,
|
|
|
|
|
|
const QList<QRectF>& components)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < path.size() - 1; i++) {
|
|
|
|
|
|
if (!isSegmentSafe(path[i], path[i+1], components)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查线段是否穿透元件
|
|
|
|
|
|
bool ElectricConnectLineItem::segmentPenetratesComponent(const QLineF& segment,
|
|
|
|
|
|
const QRectF& component)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查线段端点是否在元件内
|
|
|
|
|
|
bool p1Inside = component.contains(segment.p1());
|
|
|
|
|
|
bool p2Inside = component.contains(segment.p2());
|
|
|
|
|
|
|
|
|
|
|
|
if (p1Inside && p2Inside) {
|
|
|
|
|
|
// 线段完全在元件内
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取与元件的交点
|
|
|
|
|
|
int intersectionCount = 0;
|
|
|
|
|
|
QLineF edges[4] = {
|
|
|
|
|
|
QLineF(component.topLeft(), component.topRight()),
|
|
|
|
|
|
QLineF(component.topRight(), component.bottomRight()),
|
|
|
|
|
|
QLineF(component.bottomRight(), component.bottomLeft()),
|
|
|
|
|
|
QLineF(component.bottomLeft(), component.topLeft())
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
QPointF intersection;
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
|
if (segment.intersects(edges[i], &intersection) == QLineF::BoundedIntersection) {
|
|
|
|
|
|
intersectionCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果线段与元件有2个或更多交点,说明穿透了元件
|
|
|
|
|
|
return intersectionCount >= 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有元件的总边界
|
|
|
|
|
|
QRectF ElectricConnectLineItem::getTotalComponentsBounds(const QList<QRectF>& components)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (components.isEmpty()) {
|
|
|
|
|
|
return QRectF();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QRectF total = components.first();
|
|
|
|
|
|
for (int i = 1; i < components.size(); i++) {
|
|
|
|
|
|
total = total.united(components[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return total;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 收集所有可能的直角路径
|
|
|
|
|
|
void ElectricConnectLineItem::collectRectilinearPaths(const QPointF& start, const QPointF& end,
|
|
|
|
|
|
const QList<QRectF>& components,
|
|
|
|
|
|
QMultiMap<double, QList<QPointF>>& paths)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 2段路径
|
|
|
|
|
|
QPointF turn1(start.x(), end.y()); // 水平-垂直
|
|
|
|
|
|
QPointF turn2(end.x(), start.y()); // 垂直-水平
|
|
|
|
|
|
|
|
|
|
|
|
addPathIfRectilinear({start, turn1, end}, paths);
|
|
|
|
|
|
addPathIfRectilinear({start, turn2, end}, paths);
|
|
|
|
|
|
|
|
|
|
|
|
// 3段路径
|
|
|
|
|
|
double midX = (start.x() + end.x()) / 2;
|
|
|
|
|
|
double midY = (start.y() + end.y()) / 2;
|
|
|
|
|
|
|
|
|
|
|
|
// 水平-垂直-水平
|
|
|
|
|
|
addPathIfRectilinear({start,
|
|
|
|
|
|
QPointF(start.x(), midY),
|
|
|
|
|
|
QPointF(end.x(), midY),
|
|
|
|
|
|
end}, paths);
|
|
|
|
|
|
|
|
|
|
|
|
// 垂直-水平-垂直
|
|
|
|
|
|
addPathIfRectilinear({start,
|
|
|
|
|
|
QPointF(midX, start.y()),
|
|
|
|
|
|
QPointF(midX, end.y()),
|
|
|
|
|
|
end}, paths);
|
|
|
|
|
|
|
|
|
|
|
|
// 4段路径(绕行)
|
|
|
|
|
|
collectBypassPaths(start, end, components, paths);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加路径(如果是直角)
|
|
|
|
|
|
void ElectricConnectLineItem::addPathIfRectilinear(const QList<QPointF>& path,
|
|
|
|
|
|
QMultiMap<double, QList<QPointF>>& paths)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否所有线段都是水平或垂直
|
|
|
|
|
|
for (int i = 0; i < path.size() - 1; i++) {
|
|
|
|
|
|
if (path[i].x() != path[i+1].x() && path[i].y() != path[i+1].y()) {
|
|
|
|
|
|
return; // 不是直角
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double length = calculatePathLength(path);
|
|
|
|
|
|
paths.insert(length, path);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 收集绕行路径
|
|
|
|
|
|
void ElectricConnectLineItem::collectBypassPaths(const QPointF& start, const QPointF& end,
|
|
|
|
|
|
const QList<QRectF>& components,
|
|
|
|
|
|
QMultiMap<double, QList<QPointF>>& paths)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (components.isEmpty()) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有元件的总边界
|
|
|
|
|
|
QRectF totalBounds = getTotalComponentsBounds(components);
|
|
|
|
|
|
if (!totalBounds.isValid()) return;
|
|
|
|
|
|
|
|
|
|
|
|
const double clearance = 20.0;
|
|
|
|
|
|
QRectF expanded = totalBounds.adjusted(-clearance, -clearance, clearance, clearance);
|
|
|
|
|
|
|
|
|
|
|
|
// 从上方绕行
|
|
|
|
|
|
addPathIfRectilinear({start,
|
|
|
|
|
|
QPointF(start.x(), expanded.top()),
|
|
|
|
|
|
QPointF(end.x(), expanded.top()),
|
|
|
|
|
|
end}, paths);
|
|
|
|
|
|
|
|
|
|
|
|
// 从下方绕行
|
|
|
|
|
|
addPathIfRectilinear({start,
|
|
|
|
|
|
QPointF(start.x(), expanded.bottom()),
|
|
|
|
|
|
QPointF(end.x(), expanded.bottom()),
|
|
|
|
|
|
end}, paths);
|
|
|
|
|
|
|
|
|
|
|
|
// 从左方绕行
|
|
|
|
|
|
addPathIfRectilinear({start,
|
|
|
|
|
|
QPointF(expanded.left(), start.y()),
|
|
|
|
|
|
QPointF(expanded.left(), end.y()),
|
|
|
|
|
|
end}, paths);
|
|
|
|
|
|
|
|
|
|
|
|
// 从右方绕行
|
|
|
|
|
|
addPathIfRectilinear({start,
|
|
|
|
|
|
QPointF(expanded.right(), start.y()),
|
|
|
|
|
|
QPointF(expanded.right(), end.y()),
|
|
|
|
|
|
end}, paths);
|
2024-12-07 17:24:36 +08:00
|
|
|
|
}
|
2026-02-04 20:44:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 强制直角绕行
|
|
|
|
|
|
void ElectricConnectLineItem::generateForcedRectilinearBypass(const QPointF& start, const QPointF& end,
|
|
|
|
|
|
const QList<QRectF>& components)
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << " In generateForcedRectilinearBypass";
|
|
|
|
|
|
|
|
|
|
|
|
// 找到阻挡的元件
|
|
|
|
|
|
QRectF blocking = findFirstBlockingComponent(start, end, components);
|
|
|
|
|
|
|
|
|
|
|
|
if (blocking.isValid()) {
|
|
|
|
|
|
qDebug() << " Blocking component:" << blocking;
|
|
|
|
|
|
|
|
|
|
|
|
// 从上方或下方绕过
|
|
|
|
|
|
const double clearance = 15.0;
|
|
|
|
|
|
double bypassY = (start.y() < blocking.center().y()) ?
|
|
|
|
|
|
blocking.top() - clearance : blocking.bottom() + clearance;
|
|
|
|
|
|
|
|
|
|
|
|
QList<QPointF> path = {start,
|
|
|
|
|
|
QPointF(start.x(), bypassY),
|
|
|
|
|
|
QPointF(end.x(), bypassY),
|
|
|
|
|
|
end};
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << " Forced bypass path:" << path;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < path.size(); i++) {
|
|
|
|
|
|
m_points.lineTo(path[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 没有阻挡,使用简单的直角路径
|
|
|
|
|
|
qDebug() << " No blocking component, using simple orthogonal path";
|
|
|
|
|
|
QPointF turnPoint = (qAbs(start.x() - end.x()) < qAbs(start.y() - end.y())) ?
|
|
|
|
|
|
QPointF(end.x(), start.y()) : QPointF(start.x(), end.y());
|
|
|
|
|
|
|
|
|
|
|
|
m_points.lineTo(turnPoint);
|
|
|
|
|
|
m_points.lineTo(end);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 找到第一个阻挡的元件
|
|
|
|
|
|
QRectF ElectricConnectLineItem::findFirstBlockingComponent(const QPointF& start, const QPointF& end,
|
|
|
|
|
|
const QList<QRectF>& components)
|
|
|
|
|
|
{
|
|
|
|
|
|
QLineF line(start, end);
|
|
|
|
|
|
|
|
|
|
|
|
for (const QRectF& rect : components) {
|
|
|
|
|
|
if (lineIntersectsRect(line, rect)) {
|
|
|
|
|
|
return rect;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return QRectF();
|
|
|
|
|
|
}
|
|
|
|
|
|
|