add editor layout occupancy management

This commit is contained in:
baiYue 2026-05-28 10:43:54 +08:00
parent 947c1a6858
commit 01ce273d68
11 changed files with 676 additions and 56 deletions

View File

@ -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<QString> componentOrder; // 组件顺序
QHash<QString, int> 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<QString> 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<QString,DiagramEditorRouteInfo> mapRoute; //线路信息
QMap<QString,DiagramEditorComponentInfo> mapComponent; //设备信息
QList<QString> routeOrder; //线路顺序
QMap<QString, QMap<int,DirectionOccupancyRecord>> directionOccupancyMap; //组件方向占用表 名称4个方向占用记录
QMap<QString, DirectionOccupancyRouteInfo> 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<QString,DiagramEditorRouteInfo> mapRoute; //中性点对应的线路结构
QMap<QString, QMap<int,DirectionOccupancyRecord>> directionOccupancyMap; //组件方向占用表 名称4个方向占用记录
QMap<QString, DirectionOccupancyRouteInfo> 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;
}
};

View File

@ -10,14 +10,32 @@ class DiagramLayoutEngine {
public:
struct Context {
int sourceId = 0;
QString currentRouteId; // 当前正在处理的线路ID
bool isRemovingRoute = false; // 是否为删除操作
bool saveToModel = false;
QMap<QString, QStandardItem*> itemCache;
QMap<QString, DiagramEditorComponentInfo> componentsCache;
QMap<QString, QSet<Direction>> branchUsedDirections; //key: 组件名 value: 已被支线占用的方向集合
QMap<QString, QMap<int,DirectionOccupancyRecord>> directionOccupancyMap;
QMap<QString, DirectionOccupancyRouteInfo> routeDirectionMap;
void initComponentsCache(const QMap<QString, DiagramEditorComponentInfo>& compos) {
componentsCache = compos;
}
void initDirectionOccupancyCache(const QMap<QString, QMap<int,DirectionOccupancyRecord>>& occus){
directionOccupancyMap = occus;
}
void initRouteDirectionMapCache(const QMap<QString, DirectionOccupancyRouteInfo>& routes){
routeDirectionMap = routes;
}
};
enum class DirectionOperation {
ADD, // 添加方向
REMOVE, // 移除方向
SET // 设置方向(替换)
};
explicit DiagramLayoutEngine(DiagramEditorModel* model)
@ -26,16 +44,19 @@ public:
QRectF executeLayout(
QMap<QString, DiagramEditorRouteInfo>& routes,
QMap<QString, DiagramEditorComponentInfo>& components,
QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,
QMap<QString, DirectionOccupancyRouteInfo>& routeDirectionMap,
const LayoutConfig& config,
Context& context
);
bool clearRouteDirectionOccupancy(const QString& routeId,Context& context);
private:
// 主线相关
QString findMainRoute(const QMap<QString, DiagramEditorRouteInfo>& 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<QString, DiagramEditorComponentInfo>& components);
Direction getRouteDirection(const QString& routeName,const QMap<QString, DiagramEditorRouteInfo>& 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;

View File

@ -68,8 +68,9 @@ public:
void generateItemByModel(QStandardItemModel* pModel,DiagramEditorBaseBlock* pBlock,int nFrom = 0,QPoint delta = QPoint(0,0)); //0间隔1变压器
QList<DiagramEditorComponentInfo> generateItemByInfo(QMap<QString,DiagramEditorRouteInfo>& mapRoute,QMap<QString,DiagramEditorComponentInfo>& mapCompo,QPointF delta = QPointF(0,0),DiagramEditorBaseBlock* pParent = nullptr); //根据data生成item parent:生成的对象添加到parent下(非拓扑计算)
QMultiMap<int,QUuid> generateOutConnection(QList<DiagramEditorComponentInfo>,QList<HierarchyItem>&,int nTypeTransCon,int nPos = 0,DiagramEditorBaseBlock* pParent = nullptr); //生成外部连接手动bind的连接relation:层级关系引用 nTypeTransCon变压器连线类型,1中性点连接2外部连接,nPos中性点连接时的位置
QRectF updateTarget(QMap<QString,DiagramEditorRouteInfo>&,QMap<QString,DiagramEditorComponentInfo>&,int nLayout,int nSource,bool saveToModel = true); //更新位置 nLayout主次朝向:8421,8421 上下左右,上下左右 nSource:0间隔1变压器 regenerate重新生成标志 saveToModel:生成到模型或map
void clearCompoDir(QMap<QString,DiagramEditorRouteInfo>&,QMap<QString,DiagramEditorComponentInfo>&,int nSource); //清空component中的dir(updateTarget前调用)
QRectF updateTarget(QMap<QString,DiagramEditorRouteInfo>&,QMap<QString,DiagramEditorComponentInfo>&,QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,QMap<QString, DirectionOccupancyRouteInfo>& routeDirectionMap,int nLayout,int nSource,bool saveToModel = true); //更新位置 nLayout主次朝向:8421,8421 上下左右,上下左右 nSource:0间隔1变压器 regenerate重新生成标志 saveToModel:生成到模型或map
void clearCompoDir(QMap<QString,DiagramEditorRouteInfo>&,QMap<QString,DiagramEditorComponentInfo>&,QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,QMap<QString, DirectionOccupancyRouteInfo>& routeDirectionMap,int nSource); //清空component中的dir(updateTarget前调用)
void removeRouteDir(QMap<QString,DiagramEditorRouteInfo>&,QMap<QString,DiagramEditorComponentInfo>&,QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,QMap<QString, DirectionOccupancyRouteInfo>& routeDirectionMap,int nSource); //删除某条线路上的方向占用
QList<DiagramEditorComponentInfo> getRouteItemInfoList(QMap<QString,DiagramEditorComponentInfo>,QMap<QString,DiagramEditorRouteInfo>); //返回线路中包含的设备信息列表

View File

@ -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();
}

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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下
}
}

View File

@ -6,10 +6,14 @@
QRectF DiagramLayoutEngine::executeLayout(
QMap<QString, DiagramEditorRouteInfo>& routes,
QMap<QString, DiagramEditorComponentInfo>& components,
QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,
QMap<QString, DirectionOccupancyRouteInfo>& 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<int>(mainDir);
int oppositeFlag = static_cast<int>(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<int>(dir);
item->setData(updated);
} else {
context.componentsCache[compoName].nUsedDirection |=
static_cast<int>(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<QString, DiagramEditorComponentInfo>& 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;
}

View File

@ -1325,8 +1325,11 @@ QMultiMap<int,QUuid> DiagramEditorModel::generateOutConnection(QList<DiagramEdit
return bindId;
}
void DiagramEditorModel::clearCompoDir(QMap<QString,DiagramEditorRouteInfo>& data,QMap<QString,DiagramEditorComponentInfo>& compos,int nSource)
void DiagramEditorModel::clearCompoDir(QMap<QString,DiagramEditorRouteInfo>& data,QMap<QString,DiagramEditorComponentInfo>& compos,
QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,QMap<QString, DirectionOccupancyRouteInfo>& 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<QString,DiagramEditorRouteInfo>& dat
}
}
void DiagramEditorModel::removeRouteDir(QMap<QString,DiagramEditorRouteInfo>&,QMap<QString,DiagramEditorComponentInfo>&,QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,
QMap<QString, DirectionOccupancyRouteInfo>& routeDirectionMap,int nSource)
{
DiagramLayoutEngine engine(this);
DiagramLayoutEngine::Context context;
//engine.clearRouteDirectionOccupancy(routeId,context);
}
QRectF DiagramEditorModel::updateTarget(
QMap<QString, DiagramEditorRouteInfo>& data,
QMap<QString, DiagramEditorComponentInfo>& compos,
QMap<QString, QMap<int,DirectionOccupancyRecord>>& directionOccupancyMap,
QMap<QString, DirectionOccupancyRouteInfo>& 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;
}

View File

@ -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);