#include "diagramEditor/editorDiagramLayoutEngine.h" #include "diagramEditor/editorDirectionManager.h" #include "graphicsDataModel/diagramEditorModel.h" // 主入口函数 QRectF DiagramLayoutEngine::executeLayout( QMap& routes, QMap& components, QMap>& directionOccupancyMap, QMap& routeDirectionMap, const LayoutConfig& config, Context& context) { context.initComponentsCache(components); context.initDirectionOccupancyCache(directionOccupancyMap); context.initRouteDirectionMapCache(routeDirectionMap); context.itemCache.clear(); QString mainRouteName = findMainRoute(routes); if (mainRouteName.isEmpty()) { qWarning() << "No main route found"; return QRectF(); } // 1. 布局主线 layoutMainRoute(routes[mainRouteName], config, context); // 设置一级支线父方向 for (auto it = routes.begin(); it != routes.end(); ++it) { if (it->bMainRoute) continue; if(it->sParentRoute == mainRouteName) //一级支线方向 it->preferDirection = config.subDirection(); } for (auto it = routes.begin(); it != routes.end(); ++it) { if (it->bMainRoute) continue; if(it->sParentRoute == mainRouteName) continue; it->preferDirection = config.mainDirection(); //二级支线方向(与主线同向) } // 2. 布局所有支线(✅ 此时方向数据必须是最新的) for (auto it = routes.begin(); it != routes.end(); ++it) { if (it->sRouteName == mainRouteName) continue; layoutBranchRoute(*it, config, context); } // ✅ 3. 所有布局完成之后,才更新 components if (!context.saveToModel) { for(auto& compo:components){ //从compo缓存中读取计算过的数据 auto mapCatch = context.componentsCache; compo.nUsedDirection = mapCatch[compo.sName].nUsedDirection; compo.nRotate = mapCatch[compo.sName].nRotate; compo.deltaPos = mapCatch[compo.sName].deltaPos; } } // 4. 计算边界 if (!context.saveToModel) { return calculateBoundingRect(components); } return QRectF(); } // 查找主线 QString DiagramLayoutEngine::findMainRoute( const QMap& routes) { QString mainRoute; bool hasMain = false; // 查找标记为主线 for (auto it = routes.begin(); it != routes.end(); ++it) { if (it->bMainRoute) { mainRoute = it->sRouteName; hasMain = true; break; } } // 未设置主线,选择设备最多的线路 if (!hasMain) { int maxCount = 0; for (auto it = routes.begin(); it != routes.end(); ++it) { int count = it->lstCompo.size(); if (count > maxCount) { maxCount = count; mainRoute = it->sRouteName; } } // 标记为主线 if (!mainRoute.isEmpty()) { const_cast&>(routes)[mainRoute].bMainRoute = true; } } return mainRoute; } // 布局主线 void DiagramLayoutEngine::layoutMainRoute( DiagramEditorRouteInfo& route, const LayoutConfig& config, Context& context) { // 1. 设置当前线路上下文 QString originalRouteId = context.currentRouteId; bool originalIsRemoving = context.isRemovingRoute; context.currentRouteId = route.sRouteName; context.isRemovingRoute = false; // 2. 执行布局计算 layoutMainRouteInternal(route, config, context); // 4. 恢复上下文 context.currentRouteId = originalRouteId; context.isRemovingRoute = originalIsRemoving; qInfo() << "Main route" << route.sRouteName << "layout completed"; /*Direction mainDir = config.mainDirection(); auto& components = route.lstCompo; if (components.isEmpty()) return; int nSeg = components.size() / 2; int nSegIndex = (mainDir == Direction::Down || mainDir == Direction::Right) ? -nSeg : nSeg; for (int i = 0; i < components.size(); ++i) { DiagramEditorComponentInfo& compo = components[i]; Direction dir = mainDir; int mainDirFlag = static_cast(mainDir); int oppositeFlag = static_cast(DirectionManager::getOpposite(mainDir)); if (i == 0) { // 队首:只占用出方向 compo.nUsedDirection |= mainDirFlag; } else if (i == components.size() - 1) { // 队尾:只占用入方向 compo.nUsedDirection |= oppositeFlag; } else { // 中间节点:入方向 + 出方向(一次性占满) compo.nUsedDirection |= (mainDirFlag | oppositeFlag); } QPoint delta(0, 0); int spacing = DirectionManager::isHorizontal(mainDir) ? config.horizontalSpacing() : config.verticalSpacing(); if (DirectionManager::isHorizontal(mainDir)) delta.setX(nSegIndex * spacing); else delta.setY(nSegIndex * spacing); int rotate = DirectionManager::getRotationAngle(mainDir); // ✅ 主线方向 → 统一交给 updateComponent updateComponent(compo, compo.nUsedDirection, delta, rotate, context); //**方向赋值重复,待修改 nSegIndex += (mainDir == Direction::Up || mainDir == Direction::Left) ? -1 : 1; }*/ } void DiagramLayoutEngine::layoutMainRouteInternal( DiagramEditorRouteInfo& route, const LayoutConfig& config, Context& context) { QString routeId = route.sRouteName; auto& components = route.lstCompo; if (components.isEmpty()) return; Direction mainDir = config.mainDirection(); int mainDirFlag = static_cast(mainDir); int oppositeFlag = static_cast(DirectionManager::getOpposite(mainDir)); bool isHorizontal = DirectionManager::isHorizontal(mainDir); int spacing = isHorizontal ? config.horizontalSpacing() : config.verticalSpacing(); // 计算分段索引 int nSeg = components.size() - 1; // 实际段数 int nSegIndex = (mainDir == Direction::Down || mainDir == Direction::Right) ? -nSeg : nSeg; for (int i = 0; i < components.size(); ++i) { DiagramEditorComponentInfo& compo = components[i]; const QString& compoName = compo.sName; int dirMask = 0; // 计算方向占用掩码 if (i == 0) { dirMask = mainDirFlag; // 队首:出方向 } else if (i == components.size() - 1) { dirMask = oppositeFlag; // 队尾:入方向 } else { dirMask = mainDirFlag | oppositeFlag; // 中间:入方向 + 出方向 } // 计算位置偏移 QPoint delta(0, 0); if (isHorizontal) { delta.setX(nSegIndex * spacing); } else { delta.setY(nSegIndex * spacing); } int rotate = DirectionManager::getRotationAngle(mainDir); // 更新组件 updateComponent(compo, dirMask, delta, rotate, context); //recordRouteDirectionOccupancy(routeId, compoName, dirMask ,context); // 更新索引 nSegIndex += (mainDir == Direction::Up || mainDir == Direction::Left) ? -1 : 1; } } // 拆分支线 void DiagramLayoutEngine::splitBranchRoute( DiagramEditorRouteInfo& route, Context& context) { route.lstOrder.clear(); route.lstReverse.clear(); if (route.lstCompo.isEmpty()) { return; } // 检查首尾元件 int firstDir = getComponentDirection(route.lstCompo.first().sName, context); int lastDir = getComponentDirection(route.lstCompo.last().sName, context); if (firstDir != 0) { // 情况1: 首元件是节点 route.lstOrder = route.lstCompo; } else if (lastDir != 0) { // 情况2: 末元件是节点 for (auto it = route.lstCompo.rbegin(); it != route.lstCompo.rend(); ++it) { route.lstReverse.append(*it); } } else { // 情况3: 查找中间节点 int nodeIndex = -1; for (int i = 0; i < route.lstCompo.size(); ++i) { int dir = getComponentDirection(route.lstCompo[i].sName, context); if (dir != 0) { nodeIndex = i; break; } } if (nodeIndex == -1) { // 没有节点,默认为正序 route.lstOrder = route.lstCompo; return; } // 反向序列: 从节点到开头 for (int i = nodeIndex; i >= 0; --i) { route.lstReverse.append(route.lstCompo[i]); } // 正序序列: 从节点到结尾 for (int i = nodeIndex; i < route.lstCompo.size(); ++i) { route.lstOrder.append(route.lstCompo[i]); } } } // 布局支线 void DiagramLayoutEngine::layoutBranchRoute( DiagramEditorRouteInfo& route, const LayoutConfig& config, Context& context) { // 1. 设置当前线路上下文 QString originalRouteId = context.currentRouteId; bool originalIsRemoving = context.isRemovingRoute; context.currentRouteId = route.sRouteName; context.isRemovingRoute = false; splitBranchRoute(route, context); if (route.lstOrder.size() > 1) { layoutBranchSequence(route.lstOrder, route.preferDirection, config, context, true, 1); } // 4. 布局反序序列 if (route.lstReverse.size() > 1) { layoutBranchSequence(route.lstReverse, route.preferDirection, config, context, false, -1); } // 6. 恢复上下文 context.currentRouteId = originalRouteId; context.isRemovingRoute = originalIsRemoving; qInfo() << "Branch route" << route.sRouteName << "layout completed"; /*if (route.lstOrder.size() > 1) { // ✅ 第一个元件的真实方向,由 determineBranchDirection 决定 Direction startDir = determineBranchDirection(route.lstOrder[0], route.preferDirection, context); int polarity = 1; layoutBranchSequence(route.lstOrder, startDir, config, context, true, polarity); } if (route.lstReverse.size() > 1) { Direction startDir = determineBranchDirection(route.lstReverse[0], route.preferDirection, context); int polarity = -1; layoutBranchSequence(route.lstReverse, startDir, config, context, false, polarity); }*/ } // 布局分支序列 void DiagramLayoutEngine::layoutBranchSequence( QList& sequence, Direction branchDir, const LayoutConfig& config, Context& context, bool isOrder, int polarity) { if (sequence.size() < 2) return; QString routeId = context.currentRouteId; bool isVertical = DirectionManager::isVertical(branchDir); // ✅ 2. 间距由主线方向决定 int spacing = isVertical ? config.verticalSpacing() : config.horizontalSpacing(); for (int i = 0; i < sequence.size(); ++i) { // ✅ 当前元件作为“起点” QPoint basePos = getComponentPosition(sequence[i].sName, context); Direction dir = determineBranchDirection(sequence[i], branchDir, context); // ✅ 如果后面还有元件,才计算偏移 if (i + 1 < sequence.size()) { DiagramEditorComponentInfo& next = sequence[i + 1]; int offset = (i + 1) * polarity * spacing; QPoint nextPos = basePos; if (isVertical) { nextPos.setY(basePos.y() + offset); } else { nextPos.setX(basePos.x() + offset); } QPoint deltaPos = nextPos - basePos; Direction nextConnectionDir = DirectionManager::getOpposite(dir); int rotate = DirectionManager::getRotationAngle(dir); updateComponent(sequence[i], int(dir), basePos, rotate, context); updateComponent(next, int(nextConnectionDir), nextPos, rotate, context); } } } // 确定支线方向 Direction DiagramLayoutEngine::determineBranchDirection( const DiagramEditorComponentInfo& currentNode, Direction preferredDir, Context& context) { int usedDirections = getComponentDirection(currentNode.sName, context); bool horizontal = DirectionManager::isHorizontal(preferredDir); if (horizontal) { bool left = DirectionManager::isDirectionOccupied(usedDirections, Direction::Left); bool right = DirectionManager::isDirectionOccupied(usedDirections, Direction::Right); if (!left && right) return Direction::Left; if (left && !right) return Direction::Right; } else { bool up = DirectionManager::isDirectionOccupied(usedDirections, Direction::Up); bool down = DirectionManager::isDirectionOccupied(usedDirections, Direction::Down); if (!up && down) return Direction::Up; if (up && !down) return Direction::Down; } return preferredDir; } // 获取组件位置 QPoint DiagramLayoutEngine::getComponentPosition( const QString& componentName, Context& context) { if (context.saveToModel) { QStandardItem* item = getNameItem(componentName, context); if (item) { QVariant posData = item->data(Qt::UserRole + 2); if (posData.isValid() && posData.canConvert()) { return posData.toPoint(); } } return QPoint(0, 0); } else { // ✅ 核心修复:优先使用 componentsCache if (context.componentsCache.contains(componentName)) { return context.componentsCache[componentName].deltaPos; } // ✅ 终极兜底:如果 cache 里也没有,从原始 components 取 QStandardItem* item = getNameItem(componentName, context); if (item) { QVariant posData = item->data(Qt::UserRole + 2); if (posData.isValid() && posData.canConvert()) { return posData.toPoint(); // ✅ 返回真实的 deltaPos } } return QPoint(0, 0); } } // 获取组件方向 int DiagramLayoutEngine::getComponentDirection( const QString& compoName, Context& context) { if (context.saveToModel) { QStandardItem* item = getNameItem(compoName, context); return item ? item->data().toInt() : 0; } else { if (context.componentsCache.contains(compoName)) { return context.componentsCache[compoName].nUsedDirection; } return 0; } } // 获取名称项 QStandardItem* DiagramLayoutEngine::getNameItem( const QString& name, Context& context) { if (!m_model) return nullptr; return m_model->getNameItem(name, context.sourceId); } // 更新组件 void DiagramLayoutEngine::updateComponent( DiagramEditorComponentInfo& compo, int dir, const QPoint& position, int rotate, Context& context) { // 1. 如果是添加操作,记录方向占用 if (dir != 0 && !context.isRemovingRoute) { QString routeId = context.currentRouteId; if (!routeId.isEmpty()) { // 记录方向占用 bool success = recordRouteDirectionOccupancy(routeId, compo.sName, dir,context); if (success) { // 使用统一的缓存更新函数 int nTotalDir = getComponentTotalOccupiedDirections(compo.sName,context); updateComponentDirections(compo.sName, nTotalDir, DirectionOperation::ADD, context); } } } if (context.saveToModel) { QStandardItem* item = getNameItem(compo.sName, context); if (item) { //int currentDir = item->data().toInt(); //int newDir = DirectionManager::markDirectionOccupied(currentDir, dir); //item->setData(QString::number(newDir)); //int nUsedDir = item->data().toInt(); //item->setData(dir | nUsedDir); item->setData(position, Qt::UserRole + 2); item->setData(rotate, Qt::UserRole + 5); } } else { //compo.nUsedDirection = DirectionManager::markDirectionOccupied(compo.nUsedDirection, dir); compo.deltaPos = position; compo.nRotate = rotate; // 更新缓存 //context.componentsCache[compo.sName].nUsedDirection |= dir; context.componentsCache[compo.sName].deltaPos = compo.deltaPos; context.componentsCache[compo.sName].nRotate = compo.nRotate; } } bool DiagramLayoutEngine::recordRouteDirectionOccupancy( const QString& routeId, const QString& compoName, int directionMask, Context& context) { if (directionMask == 0 || routeId.isEmpty() || compoName.isEmpty()) { return false; } // 1. 检查冲突 auto& dirRecords = context.directionOccupancyMap[compoName]; bool allRecorded = true; for (int dirIndex = 0; dirIndex < 4; ++dirIndex) { int dirBit = 1 << dirIndex; if ((directionMask & dirBit) == 0) { continue; // 不是要记录的方向 } // 检查方向冲突 if (!dirRecords[dirIndex].routeId.isEmpty() && dirRecords[dirIndex].routeId != routeId) { qWarning() << "方向冲突: 组件" << compoName << "方向" << getDirectionName(dirBit) << "已被线路" << dirRecords[dirIndex].routeId << "占用"; allRecorded = false; continue; } } if (!allRecorded) { return false; // 有冲突,不记录 } // 2. 记录到全局表 for (int dirIndex = 0; dirIndex < 4; ++dirIndex) { int dirBit = 1 << dirIndex; if ((directionMask & dirBit) == 0) { continue; } dirRecords[dirIndex] = DirectionOccupancyRecord{ .routeId = routeId, .directionMask = dirBit, .timestamp = QDateTime::currentMSecsSinceEpoch() }; qDebug() << "记录: 线路" << routeId << "占用组件" << compoName << "方向" << getDirectionName(dirBit); } // 3. 更新线路的routeInfo(关键变化:只存储实际占用的方向) DirectionOccupancyRouteInfo& routeInfo = context.routeDirectionMap[routeId]; if (routeInfo.contains(compoName)) { // 合并方向 int existingMask = routeInfo.getDirection(compoName); routeInfo.updateDirection(compoName, existingMask | directionMask); } else { // 添加组件 routeInfo.addComponent(compoName, directionMask); } return true; } bool DiagramLayoutEngine::updateComponentDirections(const QString& compoName, int directionMask, DirectionOperation operation, Context& context) { if (compoName.isEmpty()) { qWarning() << "Component name is empty"; return false; } if (directionMask == 0) { qDebug() << "Direction mask is zero, skipping update for" << compoName; return true; // 方向掩码为0也算成功 } // 验证方向掩码 /*if (!isValidDirectionMask(directionMask)) { qWarning() << "Invalid direction mask:" << directionMask << "for component" << compoName; return false; }*/ // 1. 更新缓存 bool cacheUpdated = updateComponentCacheInternal(compoName, directionMask, operation, context); // 2. 如果需要,更新模型 bool modelUpdated = true; // 默认成功 if (context.saveToModel) { modelUpdated = updateComponentModel(compoName, directionMask, operation, context); } return cacheUpdated && modelUpdated; } bool DiagramLayoutEngine::updateComponentCacheInternal(const QString& compoName, int directionMask, DirectionOperation operation, Context& context) { auto it = context.componentsCache.find(compoName); switch (operation) { case DirectionOperation::ADD: { if (it == context.componentsCache.end()) { // 创建新缓存条目 DiagramEditorComponentInfo info; info.nUsedDirection = directionMask; context.componentsCache[compoName] = info; qDebug() << "catch create compo" << compoName << "used dir:" << directionMask; } else { int oldDir = it->nUsedDirection; it->nUsedDirection |= directionMask; if (oldDir != it->nUsedDirection) { qDebug() << "catch add compo" << compoName << "direction:" << oldDir << "→" << it->nUsedDirection; } } break; } case DirectionOperation::REMOVE: { if (it == context.componentsCache.end()) { qWarning() << compoName << "does't exist"; return false; } int oldDir = it->nUsedDirection; it->nUsedDirection &= ~directionMask; qDebug() << "catch remove compo" << compoName << "direction:" << oldDir << "→" << it->nUsedDirection; // 如果没有占用的方向,清理缓存 /*if (it->nUsedDirection == 0) { context.componentsCache.erase(it); qDebug() << "[缓存] 移除组件" << compoName << "(无占用方向)"; }*/ break; } case DirectionOperation::SET: { if (it == context.componentsCache.end()) { DiagramEditorComponentInfo info; info.nUsedDirection = directionMask; context.componentsCache[compoName] = info; } else { it->nUsedDirection = directionMask; } qDebug() << "catch set compo" << compoName << "direction:" << directionMask; break; } default: qWarning() << "catch unknown"; return false; } return true; } bool DiagramLayoutEngine::updateComponentModel(const QString& compoName, int directionMask, DirectionOperation operation, Context& context) { QStandardItem* item = getNameItem(compoName, context); if (!item) { qWarning() << "model can't find" << compoName << " model"; return false; } int currentDir = item->data().toInt(); // 当前模型中的方向 int newDir = currentDir; switch (operation) { case DirectionOperation::ADD: newDir = currentDir | directionMask; break; case DirectionOperation::REMOVE: newDir = currentDir & ~directionMask; break; case DirectionOperation::SET: newDir = directionMask; break; default: qWarning() << "[model] unknown"; return false; } if (currentDir != newDir) { item->setData(newDir); qDebug() << "[model] update compo" << compoName << "direction:" << currentDir << "→" << newDir; } return true; } // 计算边界矩形 QRectF DiagramLayoutEngine::calculateBoundingRect( const QMap& components) { if (components.isEmpty()) { return QRectF(); } qreal minX = 0, maxX = 0, minY = 0, maxY = 0; bool first = true; for (auto it = components.begin(); it != components.end(); ++it) { if (it->nUsedDirection == 0) continue; // 未使用的组件 QPointF pos = it->deltaPos; if (first) { minX = pos.x(); maxX = pos.x() + m_compoWidth; minY = pos.y(); maxY = pos.y() + m_compoHeight; first = false; } else { minX = qMin(minX, pos.x()); maxX = qMax(maxX, pos.x() + m_compoWidth); minY = qMin(minY, pos.y()); maxY = qMax(maxY, pos.y() + m_compoHeight); } } if (first) { return QRectF(); // 没有使用的组件 } return QRectF(minX, minY, maxX - minX, maxY - minY); } Direction DiagramLayoutEngine::getRouteDirection( const QString& routeName, const QMap& routes) { if (!routes.contains(routeName)) return Direction::Invalid; const auto& route = routes[routeName]; if (route.bMainRoute) return Direction::Invalid; // 主线没有父方向 // ✅ 主线 → 支线 if (route.preferDirection == Direction::Invalid) return Direction::Invalid; // 等待填充 return route.preferDirection; } int DiagramLayoutEngine::getComponentTotalOccupiedDirections(const QString& compoName, Context& context) { auto it = context.directionOccupancyMap.find(compoName); if (it == context.directionOccupancyMap.end()) { return 0; } int totalMask = 0; for (int dirIndex = 0; dirIndex < 4; ++dirIndex) { if (!it.value()[dirIndex].routeId.isEmpty()) { totalMask |= it.value()[dirIndex].directionMask; } } return totalMask; } int DiagramLayoutEngine::getComponentDirectionFromCache(const QString& compoName,const QString& routeId,Context& context) { if (!context.routeDirectionMap.contains(routeId)) { qDebug() << "Route" << routeId << "not found in direction map"; return 0; } // 4. 从全局方向占用表中查找 if (context.directionOccupancyMap.contains(compoName)) { const auto& dirRecords = context.directionOccupancyMap[compoName]; int directionMask = 0; // 遍历4个方向,检查哪些被指定线路占用 for (int i = 0; i < dirRecords.size(); ++i) { if (dirRecords[i].routeId == routeId) { directionMask |= dirRecords[i].directionMask; } } if (directionMask != 0) { qDebug() << "Found direction mask" << directionMask << "for component" << compoName << "in route" << routeId << "from global occupancy map"; return directionMask; } } // 5. 未找到 qDebug() << "Component" << compoName << "not found in route" << routeId; return 0; } bool DiagramLayoutEngine::clearRouteDirectionOccupancy(const QString& routeId,Context& context) { // 1. 查找要清理的线路 auto routeIt = context.routeDirectionMap.find(routeId); if (routeIt == context.routeDirectionMap.end()) { return false; } DirectionOccupancyRouteInfo& routeToClear = routeIt.value(); qDebug() << "=== 清理线路:" << routeId << "==="; // 2. 从全局表中清理这个线路的所有占用 for (const QString& compoName : routeToClear.getAllComponents()) { int routeDirection = routeToClear.getDirection(compoName); qDebug() << "clear compo" << compoName << "direction:" << routeDirection; // 从全局表中清理 auto& dirRecords = context.directionOccupancyMap[compoName]; for (int dirIndex = 0; dirIndex < 4; ++dirIndex) { int dirBit = 1 << dirIndex; if ((routeDirection & dirBit) == 0) { continue; // 不是这个线路占用的方向 } if (!dirRecords[dirIndex].routeId.isEmpty() && dirRecords[dirIndex].routeId == routeId) { qDebug() << " clear direction" << getDirectionName(dirBit); dirRecords[dirIndex] = DirectionOccupancyRecord{}; } } // 更新组件缓存 updateComponentDirections(compoName, routeDirection, DirectionOperation::REMOVE, context); } // 3. 从routeDirectionMap中删除这个线路 context.routeDirectionMap.erase(routeIt); // 4. 清理空的组件条目(可选) //cleanupEmptyComponents(context); qDebug() << "=== route" << routeId << "clear ==="; return true; }