#include "graphicsItem/electricConnectLineItem.h" #include #include #include 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) { 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) { m_lstPoints[0] = p; } else { m_lstPoints.append(p); } update(); } void ElectricConnectLineItem::setEndPoint(const QPointF& p) { 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 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()); } qDebug() << "✓ Dragging segment" << segmentIndex; } } // 更新拖拽 void ElectricConnectLineItem::updateDrag(const QPointF& scenePos) { if (!m_dragData.isActive) { return; } int idx = m_dragData.segmentIndex; // 从原始路径提取点 QList 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(); } QPainterPath ElectricConnectLineItem::shape() const { // 使用路径的轮廓 if (m_points.isEmpty()) { return QPainterPath(); } QPainterPathStroker stroker; stroker.setWidth(8.0); // 选择区域宽度 QPainterPath shape = stroker.createStroke(m_points); return shape; } QRectF ElectricConnectLineItem::boundingRect() const { QRectF rect = shape().boundingRect(); // 如果shape为空,使用路径边界 if (rect.isNull() && !m_points.isEmpty()) { rect = m_points.boundingRect().adjusted(-5, -5, 5, 5); } return rect; } 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::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 components = getAllComponentRects(); 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& components) { qDebug() << "=== generateAvoidancePath (rectilinear) ==="; m_points = QPainterPath(); m_points.moveTo(start); // 生成候选路径列表,按长度排序 QMultiMap> candidatePaths; // 1. 收集所有可能的直角路径 collectRectilinearPaths(start, end, components, candidatePaths); // 2. 选择最短的安全路径 for (auto it = candidatePaths.begin(); it != candidatePaths.end(); ++it) { const QList& 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& 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) const { // 检查端点是否在矩形内 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& components) { QLineF segment(p1, p2); for (const QRectF& component : components) { if (segmentPenetratesComponent(segment, component)) { return false; } } return true; } bool ElectricConnectLineItem::isPathSafe(const QList& path, const QList& 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& 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& components, QMultiMap>& 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& path, QMultiMap>& 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& components, QMultiMap>& 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& 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 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& components) { QLineF line(start, end); for (const QRectF& rect : components) { if (lineIntersectsRect(line, rect)) { return rect; } } return QRectF(); } 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 ElectricConnectLineItem::extractSegmentsFromPainterPath() const { QVector 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& 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::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 ElectricConnectLineItem::extractPointsFromPath() const { QList 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 ElectricConnectLineItem::extractPointsFromPath(const QPainterPath& path) const { QList 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& 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& 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 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 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; }