diff --git a/common/core_model/diagram.h b/common/core_model/diagram.h index f58a5be..073b8ce 100644 --- a/common/core_model/diagram.h +++ b/common/core_model/diagram.h @@ -129,6 +129,122 @@ enum class Direction : int { Up = 8 }; +// 方向占用记录 +struct DirectionOccupancyRecord { + QString routeId; // 占用此方向的线路ID + int directionMask; // 占用的方向位掩码 + qint64 timestamp; // 占用时间 + friend QDataStream &operator<<(QDataStream &out, const DirectionOccupancyRecord &data) { + out << data.routeId << data.directionMask << data.timestamp; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DirectionOccupancyRecord &data) { + in >> data.routeId >> data.directionMask >> data.timestamp; + return in; + } +}; + +// 线路方向占用记录 +struct DirectionOccupancyRouteInfo { + QList componentOrder; // 组件顺序 + QHash componentDirections; // 组件->方向掩码 + + friend QDataStream &operator<<(QDataStream &out, const DirectionOccupancyRouteInfo &data) { + out << data.componentOrder << data.componentDirections; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DirectionOccupancyRouteInfo &data) { + in >> data.componentOrder >> data.componentDirections; + return in; + } + + // 添加组件 + bool addComponent(const QString& compoName, int direction, int position = -1) { + if (componentDirections.contains(compoName)) { + return false; // 组件已存在 + } + + if (position < 0 || position >= componentOrder.size()) { + componentOrder.append(compoName); + } else { + componentOrder.insert(position, compoName); + } + + componentDirections[compoName] = direction; + return true; + } + + // 移除组件 + bool removeComponent(const QString& compoName) { + if (!componentDirections.contains(compoName)) { + return false; + } + + componentOrder.removeAll(compoName); + componentDirections.remove(compoName); + return true; + } + + // 获取组件方向 + int getDirection(const QString& compoName) const { + return componentDirections.value(compoName, 0); + } + + // 更新组件方向 + bool updateDirection(const QString& compoName, int newDirection) { + if (!componentDirections.contains(compoName)) { + return false; + } + + componentDirections[compoName] = newDirection; + return true; + } + + // 获取前一个组件 + QString getPreviousComponent(const QString& compoName) const { + int index = componentOrder.indexOf(compoName); + if (index > 0) { + return componentOrder.at(index - 1); + } + return QString(); + } + + // 获取后一个组件 + QString getNextComponent(const QString& compoName) const { + int index = componentOrder.indexOf(compoName); + if (index >= 0 && index < componentOrder.size() - 1) { + return componentOrder.at(index + 1); + } + return QString(); + } + + // 检查是否包含组件 + bool contains(const QString& compoName) const { + return componentDirections.contains(compoName); + } + + // 获取所有组件(按顺序) + QList getAllComponents() const { + return componentOrder; + } + + // 获取路由大小 + int size() const { + return componentOrder.size(); + } + + // 是否为空 + bool isEmpty() const { + return componentOrder.isEmpty(); + } + + // 清空 + void clear() { + componentOrder.clear(); + componentDirections.clear(); + } +}; + inline uint qHash(const DiagramEditorComponentInfo &key, uint seed = 0) { return qHash(key.uid, seed); } @@ -163,13 +279,15 @@ struct DiagramEditorBayInfo //组态编辑间隔信息 QMap mapRoute; //线路信息 QMap mapComponent; //设备信息 QList routeOrder; //线路顺序 + QMap> directionOccupancyMap; //组件方向占用表 名称,4个方向占用记录 + QMap routeDirectionMap; //线路方向映射表 key: 线路名, value: 组件->方向占用映射 friend QDataStream &operator<<(QDataStream &out, const DiagramEditorBayInfo &data) { - out << data.name << data.nLayout << data.lstFrom << data.lstTo << data.mapRoute << data.mapComponent << data.routeOrder; + out << data.name << data.nLayout << data.lstFrom << data.lstTo << data.mapRoute << data.mapComponent << data.routeOrder << data.directionOccupancyMap << data.routeDirectionMap; return out; } friend QDataStream &operator>>(QDataStream &in, DiagramEditorBayInfo &data) { - in >> data.name >> data.nLayout >> data.lstFrom >> data.lstTo >> data.mapRoute >> data.mapComponent >> data.routeOrder; + in >> data.name >> data.nLayout >> data.lstFrom >> data.lstTo >> data.mapRoute >> data.mapComponent >> data.routeOrder >> data.directionOccupancyMap >> data.routeDirectionMap; return in; } }; @@ -182,13 +300,15 @@ struct DiagramEditorTransNeutralInfo //组态编辑变压器中性点信息 int nType = 0; //中性点类型 0高1中2低 QPointF delPoint; //相对变压器偏移量 QMap mapRoute; //中性点对应的线路结构 + QMap> directionOccupancyMap; //组件方向占用表 名称,4个方向占用记录 + QMap routeDirectionMap; //线路方向映射表 key: 线路名, value: 组件->方向占用映射 friend QDataStream &operator<<(QDataStream &out, const DiagramEditorTransNeutralInfo &data) { - out << data.name << data.nType << data.delPoint << data.mapRoute; + out << data.name << data.nType << data.delPoint << data.mapRoute << data.directionOccupancyMap << data.directionOccupancyMap; return out; } friend QDataStream &operator>>(QDataStream &in, DiagramEditorTransNeutralInfo &data) { - in >> data.name >> data.nType >> data.delPoint >> data.mapRoute; + in >> data.name >> data.nType >> data.delPoint >> data.mapRoute >> data.directionOccupancyMap >> data.routeDirectionMap; return in; } }; diff --git a/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h b/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h index 5206cd8..899a5fc 100644 --- a/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h +++ b/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h @@ -10,14 +10,32 @@ class DiagramLayoutEngine { public: struct Context { int sourceId = 0; + QString currentRouteId; // 当前正在处理的线路ID + bool isRemovingRoute = false; // 是否为删除操作 bool saveToModel = false; QMap itemCache; QMap componentsCache; QMap> branchUsedDirections; //key: 组件名 value: 已被支线占用的方向集合 + QMap> directionOccupancyMap; + QMap routeDirectionMap; void initComponentsCache(const QMap& compos) { componentsCache = compos; } + + void initDirectionOccupancyCache(const QMap>& occus){ + directionOccupancyMap = occus; + } + + void initRouteDirectionMapCache(const QMap& routes){ + routeDirectionMap = routes; + } + }; + + enum class DirectionOperation { + ADD, // 添加方向 + REMOVE, // 移除方向 + SET // 设置方向(替换) }; explicit DiagramLayoutEngine(DiagramEditorModel* model) @@ -26,16 +44,19 @@ public: QRectF executeLayout( QMap& routes, QMap& components, + QMap>& directionOccupancyMap, + QMap& routeDirectionMap, const LayoutConfig& config, Context& context ); - + bool clearRouteDirectionOccupancy(const QString& routeId,Context& context); private: // 主线相关 QString findMainRoute(const QMap& routes); void layoutMainRoute(DiagramEditorRouteInfo& route, const LayoutConfig& config, Context& context); + void layoutMainRouteInternal(DiagramEditorRouteInfo& route,const LayoutConfig& config,Context& context); // 支线相关 void layoutBranchRoute(DiagramEditorRouteInfo& route, @@ -49,7 +70,6 @@ private: Context& context, bool isOrder, int polarity); - void markDirectionUsed(const QString& compoName,Direction dir,Context& context); // 组件相关 Direction determineBranchDirection(const DiagramEditorComponentInfo& currentNode, Direction preferredDir, @@ -61,13 +81,56 @@ private: const QPoint& position, int rotate, Context& context); + //记录线路在组件上的方向占用 + bool recordRouteDirectionOccupancy(const QString& routeId,const QString& compoName,int directionMask,Context& context); //线路ID,组件名称,方向位掩码 + /** + * @brief 统一更新组件方向占用 + * @param compoName 组件名称 + * @param directionMask 方向掩码 (8上,4下,2左,1右) + * @param operation 操作类型: 添加、移除、设置 + * @param context 上下文 + * @return 是否成功 + */ + bool updateComponentDirections(const QString& compoName,int directionMask,DirectionOperation operation,Context& context); + bool updateComponentCacheInternal(const QString& compoName,int directionMask,DirectionOperation operation,Context& context); + bool updateComponentModel(const QString& compoName,int directionMask,DirectionOperation operation,Context& context); // 辅助函数 QStandardItem* getNameItem(const QString& name, Context& context); int getComponentDirection(const QString& compoName, Context& context); QRectF calculateBoundingRect(const QMap& components); Direction getRouteDirection(const QString& routeName,const QMap& routes); + int getComponentTotalOccupiedDirections(const QString& compoName, Context& context); //从总占用表中获取元件占用方向 + int getComponentDirectionFromCache(const QString& compoName,const QString& routeId,Context& context); //从缓存获取方向占用 + QString getDirectionName(int dirBit) { + switch (dirBit) { + case 8: return "上"; + case 4: return "下"; + case 2: return "左"; + case 1: return "右"; + default: return QString("未知(%1)").arg(dirBit); + } + } + int getOppositeDirection(int dirBit) { + switch (dirBit) { + case 8: return 4; // 上 ↔ 下 + case 4: return 8; // 下 ↔ 上 + case 2: return 1; // 左 ↔ 右 + case 1: return 2; // 右 ↔ 左 + default: return 0; + } + } + + int getDirectionIndex(int dirBit) { + switch (dirBit) { + case 1: return 0; // 右 + case 2: return 1; // 左 + case 4: return 2; // 下 + case 8: return 3; // 上 + default: return -1; + } + } private: int m_compoWidth = 50; int m_compoHeight = 30; diff --git a/diagramCavas/include/graphicsDataModel/diagramEditorModel.h b/diagramCavas/include/graphicsDataModel/diagramEditorModel.h index 11b2ddd..99ab957 100644 --- a/diagramCavas/include/graphicsDataModel/diagramEditorModel.h +++ b/diagramCavas/include/graphicsDataModel/diagramEditorModel.h @@ -68,8 +68,9 @@ public: void generateItemByModel(QStandardItemModel* pModel,DiagramEditorBaseBlock* pBlock,int nFrom = 0,QPoint delta = QPoint(0,0)); //0间隔1变压器 QList generateItemByInfo(QMap& mapRoute,QMap& mapCompo,QPointF delta = QPointF(0,0),DiagramEditorBaseBlock* pParent = nullptr); //根据data生成item parent:生成的对象添加到parent下(非拓扑计算) QMultiMap generateOutConnection(QList,QList&,int nTypeTransCon,int nPos = 0,DiagramEditorBaseBlock* pParent = nullptr); //生成外部连接(手动bind的连接)relation:层级关系引用 nTypeTransCon变压器连线类型,1中性点连接2外部连接,nPos中性点连接时的位置 - QRectF updateTarget(QMap&,QMap&,int nLayout,int nSource,bool saveToModel = true); //更新位置 nLayout主次朝向:8421,8421 上下左右,上下左右 nSource:0间隔1变压器 regenerate重新生成标志 saveToModel:生成到模型或map - void clearCompoDir(QMap&,QMap&,int nSource); //清空component中的dir(updateTarget前调用) + QRectF updateTarget(QMap&,QMap&,QMap>& directionOccupancyMap,QMap& routeDirectionMap,int nLayout,int nSource,bool saveToModel = true); //更新位置 nLayout主次朝向:8421,8421 上下左右,上下左右 nSource:0间隔1变压器 regenerate重新生成标志 saveToModel:生成到模型或map + void clearCompoDir(QMap&,QMap&,QMap>& directionOccupancyMap,QMap& routeDirectionMap,int nSource); //清空component中的dir(updateTarget前调用) + void removeRouteDir(QMap&,QMap&,QMap>& directionOccupancyMap,QMap& routeDirectionMap,int nSource); //删除某条线路上的方向占用 QList getRouteItemInfoList(QMap,QMap); //返回线路中包含的设备信息列表 diff --git a/diagramCavas/source/diagramCavas.cpp b/diagramCavas/source/diagramCavas.cpp index b6fec92..3254908 100644 --- a/diagramCavas/source/diagramCavas.cpp +++ b/diagramCavas/source/diagramCavas.cpp @@ -719,8 +719,9 @@ void DiagramCavas::onSignal_unloadProject(const QString& sName) for(auto &pair:m_mapEditPanel){ EditPanel* pPanel = pair.first; - this->removeSubWindow(pPanel); - delete pPanel; + this->removeSubWindow(pair.second); + delete pPanel; //只删除界面会留下无效sub + delete pair.second; } m_mapEditPanel.clear(); } diff --git a/diagramCavas/source/diagramEditor/diagramEditorBayDetailSettingDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorBayDetailSettingDlg.cpp index 8c1dad9..abc4013 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorBayDetailSettingDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorBayDetailSettingDlg.cpp @@ -263,12 +263,16 @@ void DiagramEditorBayDetailSettingDlg::onOkClicked() getModel()->clearCompoDir( _curBayInfo.mapRoute, _curBayInfo.mapComponent, + _curBayInfo.directionOccupancyMap, + _curBayInfo.routeDirectionMap, 0); QRectF recBounding = getModel()->updateTarget( _curBayInfo.mapRoute, _curBayInfo.mapComponent, + _curBayInfo.directionOccupancyMap, + _curBayInfo.routeDirectionMap, nDir, 0, false); // ✅ 结果写回 mapComponent diff --git a/diagramCavas/source/diagramEditor/diagramEditorBayPreviewDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorBayPreviewDlg.cpp index 90b7a17..ed86c29 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorBayPreviewDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorBayPreviewDlg.cpp @@ -48,8 +48,9 @@ void DiagramEditorBayPreviewDlg::showDlg(int nLayout) else if(nLayout == 1){ //横,右下 nDir = 14; } - _pParent->getModel()->clearCompoDir(_pParent->getBayInfo().mapRoute,_pParent->getBayInfo().mapComponent,0); - _pParent->getModel()->updateTarget(_pParent->getBayInfo().mapRoute,_pParent->getBayInfo().mapComponent,nDir,0); + auto& bayInfo = _pParent->getBayInfo(); + _pParent->getModel()->clearCompoDir(bayInfo.mapRoute,bayInfo.mapComponent,bayInfo.directionOccupancyMap,bayInfo.routeDirectionMap,0); + _pParent->getModel()->updateTarget(_pParent->getBayInfo().mapRoute,_pParent->getBayInfo().mapComponent,bayInfo.directionOccupancyMap,bayInfo.routeDirectionMap,nDir,0); } void DiagramEditorBayPreviewDlg::setSceneRect(const QRect rec) diff --git a/diagramCavas/source/diagramEditor/diagramEditorTransDetailSettingDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorTransDetailSettingDlg.cpp index baf9e68..af6bb3a 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorTransDetailSettingDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorTransDetailSettingDlg.cpp @@ -258,17 +258,17 @@ void DiagramEditorTransDetailSettingDlg::onOkClicked() QRectF result; if(_transInfo.mapNeutral.contains(0)) - getModel()->clearCompoDir(_transInfo.mapNeutral[0].mapRoute,_transInfo.mapComponent,1); + getModel()->clearCompoDir(_transInfo.mapNeutral[0].mapRoute,_transInfo.mapComponent,_transInfo.mapNeutral[0].directionOccupancyMap,_transInfo.mapNeutral[0].routeDirectionMap,1); if(_transInfo.mapNeutral.contains(1)) - getModel()->clearCompoDir(_transInfo.mapNeutral[1].mapRoute,_transInfo.mapComponent,1); + getModel()->clearCompoDir(_transInfo.mapNeutral[1].mapRoute,_transInfo.mapComponent,_transInfo.mapNeutral[1].directionOccupancyMap,_transInfo.mapNeutral[1].routeDirectionMap,1); if(_transInfo.mapNeutral.contains(2)) - getModel()->clearCompoDir(_transInfo.mapNeutral[2].mapRoute,_transInfo.mapComponent,1); + getModel()->clearCompoDir(_transInfo.mapNeutral[2].mapRoute,_transInfo.mapComponent,_transInfo.mapNeutral[2].directionOccupancyMap,_transInfo.mapNeutral[2].routeDirectionMap,1); if(_transInfo.mapNeutral.contains(0)){ QPointF delta = QPointF(100,-25); _transInfo.mapNeutral[0].nType = 0; _transInfo.mapNeutral[0].delPoint = delta; - rec1 = getModel()->updateTarget(_transInfo.mapNeutral[0].mapRoute,_transInfo.mapComponent,14,1,false); //1右2下 + rec1 = getModel()->updateTarget(_transInfo.mapNeutral[0].mapRoute,_transInfo.mapComponent,_transInfo.mapNeutral[0].directionOccupancyMap,_transInfo.mapNeutral[0].routeDirectionMap,14,1,false); //1右2下 rec1.translate(delta); //相对trans偏移 result = result.united(rec1); } @@ -276,7 +276,7 @@ void DiagramEditorTransDetailSettingDlg::onOkClicked() QPointF delta = QPointF(-150,0); _transInfo.mapNeutral[1].nType = 1; _transInfo.mapNeutral[1].delPoint = delta; - rec2 = getModel()->updateTarget(_transInfo.mapNeutral[1].mapRoute,_transInfo.mapComponent,24,1,false); //1左2下 + rec2 = getModel()->updateTarget(_transInfo.mapNeutral[1].mapRoute,_transInfo.mapComponent,_transInfo.mapNeutral[1].directionOccupancyMap,_transInfo.mapNeutral[1].routeDirectionMap,24,1,false); //1左2下 rec2.translate(delta); result = result.united(rec2); } @@ -284,7 +284,7 @@ void DiagramEditorTransDetailSettingDlg::onOkClicked() QPointF delta = QPointF(100,25); _transInfo.mapNeutral[2].nType = 2; _transInfo.mapNeutral[2].delPoint = delta; - rec3 = getModel()->updateTarget(_transInfo.mapNeutral[2].mapRoute,_transInfo.mapComponent,14,1,false); //1右2下 + rec3 = getModel()->updateTarget(_transInfo.mapNeutral[2].mapRoute,_transInfo.mapComponent,_transInfo.mapNeutral[2].directionOccupancyMap,_transInfo.mapNeutral[2].routeDirectionMap,14,1,false); //1右2下 rec3.translate(delta); result = result.united(rec3); } diff --git a/diagramCavas/source/diagramEditor/diagramEditorTransPreviewDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorTransPreviewDlg.cpp index 8bf3322..caa7e3d 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorTransPreviewDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorTransPreviewDlg.cpp @@ -44,21 +44,22 @@ void DiagramEditorTransPreviewDlg::showDlg(int nType) void DiagramEditorTransPreviewDlg::updateModelData(int nType) { + auto& transInfo = _pParent->getTransInfo(); if(nType == 0 || nType == 1 || nType == 2){ - _pParent->getModel()->clearCompoDir(_pParent->getTransInfo().mapNeutral[nType].mapRoute,_pParent->getTransInfo().mapComponent,1); + _pParent->getModel()->clearCompoDir(transInfo.mapNeutral[nType].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[nType].directionOccupancyMap,transInfo.mapNeutral[nType].routeDirectionMap,1); if(nType == 0 || nType == 2) - _pParent->getModel()->updateTarget(_pParent->getTransInfo().mapNeutral[nType].mapRoute,_pParent->getTransInfo().mapComponent,14,1); //1右2下 + _pParent->getModel()->updateTarget(transInfo.mapNeutral[nType].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[nType].directionOccupancyMap,transInfo.mapNeutral[nType].routeDirectionMap,14,1); //1右2下 else if(nType == 1) - _pParent->getModel()->updateTarget(_pParent->getTransInfo().mapNeutral[nType].mapRoute,_pParent->getTransInfo().mapComponent,24,1); //1左2下 + _pParent->getModel()->updateTarget(transInfo.mapNeutral[nType].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[nType].directionOccupancyMap,transInfo.mapNeutral[nType].routeDirectionMap,24,1); //1左2下 } else if(nType == 3){ //整个变压器 - _pParent->getModel()->clearCompoDir(_pParent->getTransInfo().mapNeutral[0].mapRoute,_pParent->getTransInfo().mapComponent,1); - _pParent->getModel()->clearCompoDir(_pParent->getTransInfo().mapNeutral[1].mapRoute,_pParent->getTransInfo().mapComponent,1); - _pParent->getModel()->clearCompoDir(_pParent->getTransInfo().mapNeutral[2].mapRoute,_pParent->getTransInfo().mapComponent,1); + _pParent->getModel()->clearCompoDir(transInfo.mapNeutral[0].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[0].directionOccupancyMap,transInfo.mapNeutral[0].routeDirectionMap,1); + _pParent->getModel()->clearCompoDir(transInfo.mapNeutral[1].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[1].directionOccupancyMap,transInfo.mapNeutral[1].routeDirectionMap,1); + _pParent->getModel()->clearCompoDir(transInfo.mapNeutral[2].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[2].directionOccupancyMap,transInfo.mapNeutral[2].routeDirectionMap,1); - _pParent->getModel()->updateTarget(_pParent->getTransInfo().mapNeutral[0].mapRoute,_pParent->getTransInfo().mapComponent,14,1); //1右2下 - _pParent->getModel()->updateTarget(_pParent->getTransInfo().mapNeutral[1].mapRoute,_pParent->getTransInfo().mapComponent,24,1); //1左2下 - _pParent->getModel()->updateTarget(_pParent->getTransInfo().mapNeutral[2].mapRoute,_pParent->getTransInfo().mapComponent,14,1); //1右2下 + _pParent->getModel()->updateTarget(transInfo.mapNeutral[0].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[0].directionOccupancyMap,transInfo.mapNeutral[0].routeDirectionMap,14,1); //1右2下 + _pParent->getModel()->updateTarget(transInfo.mapNeutral[1].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[1].directionOccupancyMap,transInfo.mapNeutral[1].routeDirectionMap,24,1); //1左2下 + _pParent->getModel()->updateTarget(transInfo.mapNeutral[2].mapRoute,transInfo.mapComponent,transInfo.mapNeutral[2].directionOccupancyMap,transInfo.mapNeutral[2].routeDirectionMap,14,1); //1右2下 } } diff --git a/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp b/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp index daf15c4..6ee0621 100644 --- a/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp +++ b/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp @@ -6,10 +6,14 @@ 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); @@ -100,7 +104,22 @@ void DiagramLayoutEngine::layoutMainRoute( const LayoutConfig& config, Context& context) { - Direction mainDir = config.mainDirection(); + // 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; @@ -149,6 +168,61 @@ void DiagramLayoutEngine::layoutMainRoute( 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; } } @@ -211,9 +285,32 @@ void DiagramLayoutEngine::layoutBranchRoute( 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], @@ -241,7 +338,7 @@ void DiagramLayoutEngine::layoutBranchRoute( context, false, polarity); - } + }*/ } // 布局分支序列 @@ -255,8 +352,8 @@ void DiagramLayoutEngine::layoutBranchSequence( if (sequence.size() < 2) return; - bool isVertical = - DirectionManager::isVertical(branchDir); + QString routeId = context.currentRouteId; + bool isVertical = DirectionManager::isVertical(branchDir); // ✅ 2. 间距由主线方向决定 int spacing = isVertical @@ -290,30 +387,15 @@ void DiagramLayoutEngine::layoutBranchSequence( int rotate = DirectionManager::getRotationAngle(dir); + updateComponent(sequence[i], int(dir), + basePos, rotate, context); + updateComponent(next, int(nextConnectionDir), nextPos, rotate, context); } } } -void DiagramLayoutEngine::markDirectionUsed( - const QString& compoName, - Direction dir, - Context& context) -{ - if (context.saveToModel) { - QStandardItem* item = getNameItem(compoName, context); - if (!item) return; - - int old = item->data().toInt(); - int updated = old | static_cast(dir); - item->setData(updated); - } else { - context.componentsCache[compoName].nUsedDirection |= - static_cast(dir); - } -} - // 确定支线方向 Direction DiagramLayoutEngine::determineBranchDirection( const DiagramEditorComponentInfo& currentNode, @@ -408,27 +490,261 @@ void DiagramLayoutEngine::updateComponent( 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 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.nUsedDirection = DirectionManager::markDirectionOccupied(compo.nUsedDirection, dir); compo.deltaPos = position; compo.nRotate = rotate; // 更新缓存 - context.componentsCache[compo.sName].nUsedDirection = compo.nUsedDirection; + //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) { @@ -484,3 +800,102 @@ Direction DiagramLayoutEngine::getRouteDirection( 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; +} diff --git a/diagramCavas/source/graphicsDataModel/diagramEditorModel.cpp b/diagramCavas/source/graphicsDataModel/diagramEditorModel.cpp index 5e2d878..17e7bc1 100644 --- a/diagramCavas/source/graphicsDataModel/diagramEditorModel.cpp +++ b/diagramCavas/source/graphicsDataModel/diagramEditorModel.cpp @@ -1325,8 +1325,11 @@ QMultiMap DiagramEditorModel::generateOutConnection(QList& data,QMap& compos,int nSource) +void DiagramEditorModel::clearCompoDir(QMap& data,QMap& compos, + QMap>& directionOccupancyMap,QMap& routeDirectionMap,int nSource) { + directionOccupancyMap.clear(); + routeDirectionMap.clear(); for(auto &routeInfo:data){ routeInfo.lstOrder.clear(); routeInfo.lstReverse.clear(); @@ -1349,9 +1352,19 @@ void DiagramEditorModel::clearCompoDir(QMap& dat } } +void DiagramEditorModel::removeRouteDir(QMap&,QMap&,QMap>& directionOccupancyMap, + QMap& routeDirectionMap,int nSource) +{ + DiagramLayoutEngine engine(this); + DiagramLayoutEngine::Context context; + //engine.clearRouteDirectionOccupancy(routeId,context); +} + QRectF DiagramEditorModel::updateTarget( QMap& data, QMap& compos, + QMap>& directionOccupancyMap, + QMap& routeDirectionMap, int nLayout, int nSource, bool saveToModel) { @@ -1368,7 +1381,7 @@ QRectF DiagramEditorModel::updateTarget( context.saveToModel = saveToModel; // 3. 执行布局 - QRectF boundingRect = engine.executeLayout(data, compos, config, context); + QRectF boundingRect = engine.executeLayout(data, compos, directionOccupancyMap, routeDirectionMap, config, context); return boundingRect; } diff --git a/source/mainwindow.cpp b/source/mainwindow.cpp index 4b0097b..cbc8921 100644 --- a/source/mainwindow.cpp +++ b/source/mainwindow.cpp @@ -335,6 +335,7 @@ void CMainWindow::onSignal_loadProject() m_pLoadPageDlg = new LoadPageDlg(this); connect(&ProjectManager::instance(),&ProjectManager::prepareLoadBaseSetting,m_pDiagramCavas,&DiagramCavas::onSignal_loadEdit); connect(m_pLoadPageDlg,&LoadPageDlg::selectedProject,m_pDiagramView,&DiagramView::onEditorLoaded); + connect(m_pLoadPageDlg,&LoadPageDlg::selectedProject,m_pDiagramCavas,&DiagramCavas::onSignal_unloadProject); connect(&ProjectManager::instance(),&ProjectManager::createNewEditor,m_pDiagramCavas,&DiagramCavas::onSignal_createEditPanel); connect(&ProjectManager::instance(),&ProjectManager::prepareSaveEditor,m_pDiagramCavas,&DiagramCavas::onSignal_prepareSaveEdit); connect(&ProjectManager::instance(),&ProjectManager::prepareDeleteBaseSetting,m_pDiagramCavas,&DiagramCavas::onSignal_prepareDeleteEditor);