DiagramDesigner/diagramCavas/source/graphicsItem/electricConnectLineItem.cpp

693 lines
24 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 "graphicsItem/electricConnectLineItem.h"
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
ElectricConnectLineItem::ElectricConnectLineItem(QGraphicsItem *parent)
: GraphicsProjectModelItem(parent)
{
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()
{
m_boundingRect = QRectF();
m_pen = QPen(Qt::black);
m_brush = QBrush(Qt::NoBrush);
setHandleVisible(false);
setFunctionHandleIfShow(false);
setFunctionHandleEnaable(false);
m_lstPoints.push_back(QPointF()); //起点
m_lstPoints.push_back(QPointF()); //终点
_curLine = QPoint();
}
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
{
QPainterPath path;
//path.addPath(m_points);
path.addPath(m_pointsBoundingRect);
return path;
}
QRectF ElectricConnectLineItem::boundingRect() const
{
return m_boundingRect;
}
void ElectricConnectLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
if (option->state & QStyle::State_Selected)
{
painter->setPen(Qt::red);
}
else
{
if(_curMonitorStateEnable){ //当前状态被设置
painter->setPen(QPen(_curMonitorStateColor));
}
else
painter->setPen(m_pen);
}
painter->setBrush(m_brush);
painter->drawPath(m_points);
}
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;
}
void ElectricConnectLineItem::calculatePath()
{
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]);
}
return;
}
}
// 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();
}
return length;
}
// 单一线段与矩形相交检测
bool ElectricConnectLineItem::lineIntersectsRect(const QLineF& line, const QRectF& rect)
{
// 检查端点是否在矩形内
if (rect.contains(line.p1()) || rect.contains(line.p2())) {
return true;
}
// 检查是否与矩形的边相交
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);
}
// 强制直角绕行
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();
}