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)
|
|
|
|
|
|
{
|
2026-03-02 20:15:22 +08:00
|
|
|
|
if (m_lstPoints.size() >= 2) {
|
|
|
|
|
|
// 保存终点
|
|
|
|
|
|
QPointF endPoint = m_lstPoints.last();
|
|
|
|
|
|
|
|
|
|
|
|
// 清空并设置新起点
|
|
|
|
|
|
m_lstPoints.clear();
|
|
|
|
|
|
m_lstPoints.append(p);
|
|
|
|
|
|
m_lstPoints.append(endPoint);
|
|
|
|
|
|
|
|
|
|
|
|
// 重新计算路径
|
|
|
|
|
|
calculatePath();
|
|
|
|
|
|
} else if (m_lstPoints.size() == 1) {
|
2024-12-13 18:08:00 +08:00
|
|
|
|
m_lstPoints[0] = p;
|
2026-03-02 20:15:22 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
m_lstPoints.append(p);
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
2026-03-02 20:15:22 +08:00
|
|
|
|
update();
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
void ElectricConnectLineItem::setEndPoint(const QPointF& p)
|
|
|
|
|
|
{
|
2026-03-02 20:15:22 +08:00
|
|
|
|
if (m_lstPoints.size() >= 2) {
|
|
|
|
|
|
// 保存起点
|
|
|
|
|
|
QPointF startPoint = m_lstPoints.first();
|
|
|
|
|
|
|
|
|
|
|
|
// 清空并设置新终点
|
|
|
|
|
|
m_lstPoints.clear();
|
|
|
|
|
|
m_lstPoints.append(startPoint);
|
|
|
|
|
|
m_lstPoints.append(p);
|
|
|
|
|
|
|
|
|
|
|
|
// 重新计算路径
|
|
|
|
|
|
calculatePath();
|
|
|
|
|
|
} else if (m_lstPoints.size() == 1) {
|
|
|
|
|
|
m_lstPoints.append(p);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
m_lstPoints.append(p);
|
|
|
|
|
|
}
|
|
|
|
|
|
update();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 开始拖拽
|
|
|
|
|
|
void ElectricConnectLineItem::startDrag(const QPointF& scenePos)
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "\n=== START DRAG ===";
|
|
|
|
|
|
|
|
|
|
|
|
// 重置状态
|
|
|
|
|
|
m_dragData.isActive = false;
|
|
|
|
|
|
m_dragData.segmentIndex = -1;
|
|
|
|
|
|
|
|
|
|
|
|
QPointF itemPos = mapFromScene(scenePos);
|
|
|
|
|
|
qDebug() << "Item pos:" << itemPos;
|
|
|
|
|
|
|
|
|
|
|
|
// 从路径提取点
|
|
|
|
|
|
QList<QPointF> points = extractPointsFromPath();
|
|
|
|
|
|
qDebug() << "Extracted" << points.size() << "points from path";
|
|
|
|
|
|
|
|
|
|
|
|
// 查找线段
|
|
|
|
|
|
int segmentIndex = findSegmentAt(points, itemPos);
|
|
|
|
|
|
|
|
|
|
|
|
if (segmentIndex >= 0) {
|
|
|
|
|
|
m_dragData.isActive = true;
|
|
|
|
|
|
m_dragData.segmentIndex = segmentIndex;
|
|
|
|
|
|
m_dragData.startScenePos = scenePos;
|
|
|
|
|
|
m_dragData.originalPath = m_points; // 保存原始路径
|
|
|
|
|
|
|
|
|
|
|
|
// 判断线段方向
|
|
|
|
|
|
if (segmentIndex < points.size() - 1) {
|
|
|
|
|
|
QPointF p1 = points[segmentIndex];
|
|
|
|
|
|
QPointF p2 = points[segmentIndex + 1];
|
|
|
|
|
|
m_dragData.isVertical = qFuzzyCompare(p1.x(), p2.x());
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
2026-03-02 20:15:22 +08:00
|
|
|
|
|
|
|
|
|
|
qDebug() << "✓ Dragging segment" << segmentIndex;
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 20:15:22 +08:00
|
|
|
|
// 更新拖拽
|
|
|
|
|
|
void ElectricConnectLineItem::updateDrag(const QPointF& scenePos)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_dragData.isActive) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int idx = m_dragData.segmentIndex;
|
|
|
|
|
|
|
|
|
|
|
|
// 从原始路径提取点
|
|
|
|
|
|
QList<QPointF> points = extractPointsFromPath(m_dragData.originalPath);
|
|
|
|
|
|
|
|
|
|
|
|
// 安全检查
|
|
|
|
|
|
if (idx < 0 || idx >= points.size() - 1) {
|
|
|
|
|
|
qWarning() << "Invalid segment index:" << idx;
|
|
|
|
|
|
m_dragData.isActive = false;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算移动距离
|
|
|
|
|
|
QPointF startItemPos = mapFromScene(m_dragData.startScenePos);
|
|
|
|
|
|
QPointF currentItemPos = mapFromScene(scenePos);
|
|
|
|
|
|
QPointF delta = currentItemPos - startItemPos;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前线段
|
|
|
|
|
|
QPointF& p1 = points[idx];
|
|
|
|
|
|
QPointF& p2 = points[idx + 1];
|
|
|
|
|
|
|
|
|
|
|
|
// 限制移动方向
|
|
|
|
|
|
if (m_dragData.isVertical) {
|
|
|
|
|
|
// 垂直线段:只能水平移动
|
|
|
|
|
|
delta.setY(0);
|
|
|
|
|
|
|
|
|
|
|
|
// 移动线段
|
|
|
|
|
|
p1.rx() += delta.x();
|
|
|
|
|
|
p2.rx() += delta.x();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 水平线段:只能垂直移动
|
|
|
|
|
|
delta.setX(0);
|
|
|
|
|
|
|
|
|
|
|
|
p1.ry() += delta.y();
|
|
|
|
|
|
p2.ry() += delta.y();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 修复连接
|
|
|
|
|
|
fixConnections(points, idx, m_dragData.isVertical);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新拖拽起点
|
|
|
|
|
|
m_dragData.startScenePos = scenePos;
|
|
|
|
|
|
|
|
|
|
|
|
// 应用修改后的点
|
|
|
|
|
|
applyPointsToPath(points);
|
|
|
|
|
|
|
|
|
|
|
|
// 重新计算路径(调用原有的 calculatePath)
|
|
|
|
|
|
calculatePath();
|
|
|
|
|
|
update();
|
|
|
|
|
|
}
|
|
|
|
|
|
// 结束拖拽
|
|
|
|
|
|
void ElectricConnectLineItem::endDrag()
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "\n" << QString(50, '=');
|
|
|
|
|
|
qDebug() << "END DRAG";
|
|
|
|
|
|
qDebug() << QString(50, '=');
|
|
|
|
|
|
|
|
|
|
|
|
if (!m_dragData.isActive) {
|
|
|
|
|
|
qDebug() << "No active drag to end";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "Drag was active on segment:" << m_dragData.segmentIndex;
|
|
|
|
|
|
|
|
|
|
|
|
// 验证和修复路径
|
|
|
|
|
|
validateAndFixPath();
|
|
|
|
|
|
|
|
|
|
|
|
// 重置状态
|
|
|
|
|
|
m_dragData.isActive = false;
|
|
|
|
|
|
m_dragData.segmentIndex = -1;
|
|
|
|
|
|
m_dragData.isVertical = false;
|
|
|
|
|
|
m_dragData.startScenePos = QPointF();
|
|
|
|
|
|
m_dragData.originalPath = QPainterPath();
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复光标
|
|
|
|
|
|
setCursor(Qt::ArrowCursor);
|
|
|
|
|
|
|
|
|
|
|
|
// 确保场景更新
|
|
|
|
|
|
update();
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "✓ Drag ended successfully";
|
|
|
|
|
|
qDebug() << QString(50, '=') << "\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::setLastPoint(const QPointF& point)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_lastPoint = point;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::setPath(const QPainterPath& path)
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "\n=== setPath ===";
|
|
|
|
|
|
|
|
|
|
|
|
if (m_points == path) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
prepareGeometryChange();
|
|
|
|
|
|
m_points = path; // 使用已有的 m_points
|
|
|
|
|
|
updateBoundingRect();
|
|
|
|
|
|
update();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-13 18:08:00 +08:00
|
|
|
|
QPainterPath ElectricConnectLineItem::shape() const
|
2024-12-07 17:24:36 +08:00
|
|
|
|
{
|
2026-03-02 20:15:22 +08:00
|
|
|
|
// 使用路径的轮廓
|
|
|
|
|
|
if (m_points.isEmpty()) {
|
|
|
|
|
|
return QPainterPath();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPainterPathStroker stroker;
|
|
|
|
|
|
stroker.setWidth(8.0); // 选择区域宽度
|
|
|
|
|
|
|
|
|
|
|
|
QPainterPath shape = stroker.createStroke(m_points);
|
|
|
|
|
|
return shape;
|
2024-12-07 17:24:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-13 18:08:00 +08:00
|
|
|
|
QRectF ElectricConnectLineItem::boundingRect() const
|
|
|
|
|
|
{
|
2026-03-02 20:15:22 +08:00
|
|
|
|
QRectF rect = shape().boundingRect();
|
|
|
|
|
|
|
|
|
|
|
|
// 如果shape为空,使用路径边界
|
|
|
|
|
|
if (rect.isNull() && !m_points.isEmpty()) {
|
|
|
|
|
|
rect = m_points.boundingRect().adjusted(-5, -5, 5, 5);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rect;
|
2024-12-13 18:08:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取元件
|
2026-03-02 20:15:22 +08:00
|
|
|
|
QList<QRectF> components = getAllComponentRects();
|
2026-02-04 20:44:59 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 单一线段与矩形相交检测
|
2026-03-02 20:15:22 +08:00
|
|
|
|
bool ElectricConnectLineItem::lineIntersectsRect(const QLineF& line, const QRectF& rect) const
|
2026-02-04 20:44:59 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 检查端点是否在矩形内
|
|
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 20:15:22 +08:00
|
|
|
|
double ElectricConnectLineItem::distancePointToLine(const QLineF& line, const QPointF& point) const
|
|
|
|
|
|
{
|
|
|
|
|
|
// 向量法计算点到线段的最短距离
|
|
|
|
|
|
|
|
|
|
|
|
if (line.isNull()) {
|
|
|
|
|
|
return QLineF(point, line.p1()).length();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPointF v = line.p2() - line.p1(); // 线段向量
|
|
|
|
|
|
QPointF w = point - line.p1(); // 点到线段起点的向量
|
|
|
|
|
|
|
|
|
|
|
|
// 计算点积
|
|
|
|
|
|
double c1 = w.x() * v.x() + w.y() * v.y(); // w·v
|
|
|
|
|
|
if (c1 <= 0.0) {
|
|
|
|
|
|
// 点在线段起点后方
|
|
|
|
|
|
return QLineF(point, line.p1()).length();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double c2 = v.x() * v.x() + v.y() * v.y(); // v·v
|
|
|
|
|
|
if (c2 <= c1) {
|
|
|
|
|
|
// 点在线段终点前方
|
|
|
|
|
|
return QLineF(point, line.p2()).length();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算投影点
|
|
|
|
|
|
double b = c1 / c2;
|
|
|
|
|
|
QPointF projection = line.p1() + b * v;
|
|
|
|
|
|
return QLineF(point, projection).length();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::ensureEnoughPointsForDrag()
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "\n=== ensureEnoughPointsForDrag ===";
|
|
|
|
|
|
qDebug() << "Current points:" << m_lstPoints.size();
|
|
|
|
|
|
|
|
|
|
|
|
if (m_lstPoints.size() < 2) {
|
|
|
|
|
|
qWarning() << "Not enough points!";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是只有2个点的直线,需要添加转折点
|
|
|
|
|
|
if (m_lstPoints.size() == 2) {
|
|
|
|
|
|
QPointF start = m_lstPoints[0];
|
|
|
|
|
|
QPointF end = m_lstPoints[1];
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是斜线,添加转折点
|
|
|
|
|
|
if (start.x() != end.x() && start.y() != end.y()) {
|
|
|
|
|
|
QPointF turnPoint;
|
|
|
|
|
|
|
|
|
|
|
|
// 选择转折方式
|
|
|
|
|
|
if (qAbs(end.x() - start.x()) < qAbs(end.y() - start.y())) {
|
|
|
|
|
|
// 接近垂直,先水平
|
|
|
|
|
|
turnPoint = QPointF(end.x(), start.y());
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 接近水平,先垂直
|
|
|
|
|
|
turnPoint = QPointF(start.x(), end.y());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_lstPoints.insert(1, turnPoint);
|
|
|
|
|
|
qDebug() << "Added turn point:" << turnPoint;
|
|
|
|
|
|
qDebug() << "Now have" << m_lstPoints.size() << "points";
|
|
|
|
|
|
|
|
|
|
|
|
// 重新计算路径
|
|
|
|
|
|
calculatePath();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::debugPathState(const QString& context) const
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "\n=== Debug Path State:" << context << "===";
|
|
|
|
|
|
qDebug() << "m_lstPoints count:" << m_lstPoints.size();
|
|
|
|
|
|
qDebug() << "m_dragState.isDragging:" << m_dragData.isActive;
|
|
|
|
|
|
qDebug() << "m_dragState.segmentIndex:" << m_dragData.segmentIndex;
|
|
|
|
|
|
qDebug() << "m_dragState.isVertical:" << m_dragData.isVertical;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_lstPoints.size(); i++) {
|
|
|
|
|
|
qDebug() << QString(" Point[%1]: (%2, %3)")
|
|
|
|
|
|
.arg(i)
|
|
|
|
|
|
.arg(m_lstPoints[i].x(), 0, 'f', 1)
|
|
|
|
|
|
.arg(m_lstPoints[i].y(), 0, 'f', 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查每个线段
|
|
|
|
|
|
for (int i = 0; i < m_lstPoints.size() - 1; i++) {
|
|
|
|
|
|
QPointF p1 = m_lstPoints[i];
|
|
|
|
|
|
QPointF p2 = m_lstPoints[i + 1];
|
|
|
|
|
|
|
|
|
|
|
|
bool isVertical = qFuzzyCompare(p1.x(), p2.x());
|
|
|
|
|
|
bool isHorizontal = qFuzzyCompare(p1.y(), p2.y());
|
|
|
|
|
|
|
|
|
|
|
|
QString type = "DIAGONAL";
|
|
|
|
|
|
if (isVertical) type = "VERTICAL";
|
|
|
|
|
|
else if (isHorizontal) type = "HORIZONTAL";
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << QString(" Segment[%1]: %2, length=%3")
|
|
|
|
|
|
.arg(i)
|
|
|
|
|
|
.arg(type)
|
|
|
|
|
|
.arg(QLineF(p1, p2).length(), 0, 'f', 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVector<QLineF> ElectricConnectLineItem::extractSegmentsFromPainterPath() const
|
|
|
|
|
|
{
|
|
|
|
|
|
QVector<QLineF> segments;
|
|
|
|
|
|
|
|
|
|
|
|
if (m_points.isEmpty()) {
|
|
|
|
|
|
return segments;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历QPainterPath的所有元素
|
|
|
|
|
|
QPointF lastPoint;
|
|
|
|
|
|
bool hasLastPoint = false;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_points.elementCount(); i++) {
|
|
|
|
|
|
QPainterPath::Element elem = m_points.elementAt(i);
|
|
|
|
|
|
QPointF currentPoint(elem.x, elem.y);
|
|
|
|
|
|
|
|
|
|
|
|
switch (elem.type) {
|
|
|
|
|
|
case QPainterPath::MoveToElement:
|
|
|
|
|
|
lastPoint = currentPoint;
|
|
|
|
|
|
hasLastPoint = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case QPainterPath::LineToElement:
|
|
|
|
|
|
if (hasLastPoint) {
|
|
|
|
|
|
segments.append(QLineF(lastPoint, currentPoint));
|
|
|
|
|
|
}
|
|
|
|
|
|
lastPoint = currentPoint;
|
|
|
|
|
|
hasLastPoint = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case QPainterPath::CurveToElement:
|
|
|
|
|
|
// 处理曲线(如果需要)
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case QPainterPath::CurveToDataElement:
|
|
|
|
|
|
// 曲线数据
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return segments;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ElectricConnectLineItem::findSegmentAt(const QList<QPointF>& points,
|
|
|
|
|
|
const QPointF& itemPos) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points.size() < 2) {
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const double HIT_TOLERANCE = 10.0;
|
|
|
|
|
|
int bestSegment = -1;
|
|
|
|
|
|
double bestDistance = HIT_TOLERANCE;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
|
|
|
|
|
QLineF segment(points[i], points[i + 1]);
|
|
|
|
|
|
|
|
|
|
|
|
// 计算距离
|
|
|
|
|
|
double distance = distanceToSegment(segment, itemPos);
|
|
|
|
|
|
|
|
|
|
|
|
if (distance < bestDistance) {
|
|
|
|
|
|
bestDistance = distance;
|
|
|
|
|
|
bestSegment = i;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return bestSegment;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double ElectricConnectLineItem::distanceToSegment(const QLineF& segment, const QPointF& point) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (segment.isNull()) {
|
|
|
|
|
|
return std::numeric_limits<double>::max();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是正交线段
|
|
|
|
|
|
bool isHorizontal = qFuzzyCompare(segment.y1(), segment.y2());
|
|
|
|
|
|
bool isVertical = qFuzzyCompare(segment.x1(), segment.x2());
|
|
|
|
|
|
|
|
|
|
|
|
if (isHorizontal) {
|
|
|
|
|
|
// 水平线段
|
|
|
|
|
|
double minX = qMin(segment.x1(), segment.x2());
|
|
|
|
|
|
double maxX = qMax(segment.x1(), segment.x2());
|
|
|
|
|
|
double y = segment.y1();
|
|
|
|
|
|
|
|
|
|
|
|
if (point.x() >= minX && point.x() <= maxX) {
|
|
|
|
|
|
return qAbs(point.y() - y);
|
|
|
|
|
|
} else if (point.x() < minX) {
|
|
|
|
|
|
return QLineF(point, QPointF(minX, y)).length();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return QLineF(point, QPointF(maxX, y)).length();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (isVertical) {
|
|
|
|
|
|
// 垂直线段
|
|
|
|
|
|
double minY = qMin(segment.y1(), segment.y2());
|
|
|
|
|
|
double maxY = qMax(segment.y1(), segment.y2());
|
|
|
|
|
|
double x = segment.x1();
|
|
|
|
|
|
|
|
|
|
|
|
if (point.y() >= minY && point.y() <= maxY) {
|
|
|
|
|
|
return qAbs(point.x() - x);
|
|
|
|
|
|
} else if (point.y() < minY) {
|
|
|
|
|
|
return QLineF(point, QPointF(x, minY)).length();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return QLineF(point, QPointF(x, maxY)).length();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
// 斜线(理论上不应该出现)
|
|
|
|
|
|
return QLineF(segment.p1(), point).length();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QList<QPointF> ElectricConnectLineItem::extractPointsFromPath() const
|
|
|
|
|
|
{
|
|
|
|
|
|
QList<QPointF> points;
|
|
|
|
|
|
|
|
|
|
|
|
if (m_points.isEmpty()) {
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从 QPainterPath 提取所有点
|
|
|
|
|
|
for (int i = 0; i < m_points.elementCount(); i++) {
|
|
|
|
|
|
QPainterPath::Element elem = m_points.elementAt(i);
|
|
|
|
|
|
if (elem.type == QPainterPath::MoveToElement ||
|
|
|
|
|
|
elem.type == QPainterPath::LineToElement) {
|
|
|
|
|
|
points.append(QPointF(elem.x, elem.y));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QList<QPointF> ElectricConnectLineItem::extractPointsFromPath(const QPainterPath& path) const
|
|
|
|
|
|
{
|
|
|
|
|
|
QList<QPointF> points;
|
|
|
|
|
|
|
|
|
|
|
|
if (path.isEmpty()) {
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < path.elementCount(); i++) {
|
|
|
|
|
|
QPainterPath::Element elem = path.elementAt(i);
|
|
|
|
|
|
if (elem.type == QPainterPath::MoveToElement ||
|
|
|
|
|
|
elem.type == QPainterPath::LineToElement) {
|
|
|
|
|
|
points.append(QPointF(elem.x, elem.y));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::applyPointsToPath(const QList<QPointF>& points)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points.size() < 2) {
|
|
|
|
|
|
m_points = QPainterPath();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPainterPath newPath;
|
|
|
|
|
|
newPath.moveTo(points.first());
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < points.size(); i++) {
|
|
|
|
|
|
newPath.lineTo(points[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_points = newPath;
|
|
|
|
|
|
setPath(m_points);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::fixConnections(QList<QPointF>& points,
|
|
|
|
|
|
int segmentIndex, bool isVertical)
|
|
|
|
|
|
{
|
|
|
|
|
|
int n = points.size();
|
|
|
|
|
|
if (n < 3) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isVertical) {
|
|
|
|
|
|
// 垂直线段移动
|
|
|
|
|
|
if (segmentIndex > 0) {
|
|
|
|
|
|
points[segmentIndex - 1].setX(points[segmentIndex].x());
|
|
|
|
|
|
}
|
|
|
|
|
|
if (segmentIndex + 2 < n) {
|
|
|
|
|
|
points[segmentIndex + 2].setX(points[segmentIndex + 1].x());
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 水平线段移动
|
|
|
|
|
|
if (segmentIndex > 0) {
|
|
|
|
|
|
points[segmentIndex - 1].setY(points[segmentIndex].y());
|
|
|
|
|
|
}
|
|
|
|
|
|
if (segmentIndex + 2 < n) {
|
|
|
|
|
|
points[segmentIndex + 2].setY(points[segmentIndex + 1].y());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::validateAndFixPath()
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "Validating and fixing path...";
|
|
|
|
|
|
|
|
|
|
|
|
QList<QPointF> points = extractPointsFromPath();
|
|
|
|
|
|
|
|
|
|
|
|
if (points.size() < 2) {
|
|
|
|
|
|
qWarning() << "Path has less than 2 points";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool needsFix = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查每个线段
|
|
|
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
|
|
|
|
|
QPointF& p1 = points[i];
|
|
|
|
|
|
QPointF& p2 = points[i + 1];
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否正交
|
|
|
|
|
|
if (!qFuzzyCompare(p1.x(), p2.x()) && !qFuzzyCompare(p1.y(), p2.y())) {
|
|
|
|
|
|
qDebug() << "Fixing non-orthogonal segment" << i;
|
|
|
|
|
|
|
|
|
|
|
|
// 选择主要方向
|
|
|
|
|
|
double dx = qAbs(p2.x() - p1.x());
|
|
|
|
|
|
double dy = qAbs(p2.y() - p1.y());
|
|
|
|
|
|
|
|
|
|
|
|
if (dx < dy) {
|
|
|
|
|
|
p2.setX(p1.x()); // 改为垂直
|
|
|
|
|
|
} else {
|
|
|
|
|
|
p2.setY(p1.y()); // 改为水平
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
needsFix = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 移除连续重复点
|
|
|
|
|
|
QList<QPointF> cleanedPoints;
|
|
|
|
|
|
cleanedPoints.append(points.first());
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < points.size(); i++) {
|
|
|
|
|
|
if (!points[i - 1].isNull() &&
|
|
|
|
|
|
qFuzzyCompare(points[i - 1].x(), points[i].x()) &&
|
|
|
|
|
|
qFuzzyCompare(points[i - 1].y(), points[i].y())) {
|
|
|
|
|
|
continue; // 跳过重复点
|
|
|
|
|
|
}
|
|
|
|
|
|
cleanedPoints.append(points[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (needsFix || cleanedPoints.size() != points.size()) {
|
|
|
|
|
|
qDebug() << "Applying fixes to path";
|
|
|
|
|
|
applyPointsToPath(cleanedPoints);
|
|
|
|
|
|
|
|
|
|
|
|
// 重新计算路径
|
|
|
|
|
|
calculatePath();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ElectricConnectLineItem::updateBoundingRect()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_points.isEmpty()) {
|
|
|
|
|
|
m_boundingRect = QRectF();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 使用路径的边界
|
|
|
|
|
|
m_boundingRect = m_points.boundingRect();
|
|
|
|
|
|
|
|
|
|
|
|
// 添加一些边距
|
|
|
|
|
|
const qreal MARGIN = 2.0;
|
|
|
|
|
|
m_boundingRect.adjust(-MARGIN, -MARGIN, MARGIN, MARGIN);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "Updated bounds:" << m_boundingRect;
|
|
|
|
|
|
}
|