GridFrame/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp

1099 lines
35 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "diagramEditor/editorDiagramLayoutEngine.h"
#include "diagramEditor/editorDirectionManager.h"
#include "graphicsDataModel/diagramEditorModel.h"
// 主入口函数
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);
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);
}
auto mapSameDirNode = getCommonDirectionNode(routes,context); //获取相同占位的点(特殊处理)
if(!mapSameDirNode.isEmpty()){
updateConfilicNode(routes,context,config,mapSameDirNode);
}
// ✅ 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;
}
}
directionOccupancyMap = context.directionOccupancyMap;
routeDirectionMap = context.routeDirectionMap;
// 4. 计算边界
if (!context.saveToModel) {
return calculateBoundingRect(components);
}
return QRectF();
}
// 查找主线
QString DiagramLayoutEngine::findMainRoute(
const QMap<QString, DiagramEditorRouteInfo>& 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<QMap<QString, DiagramEditorRouteInfo>&>(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<int>(mainDir);
int oppositeFlag = static_cast<int>(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<int>(mainDir);
int oppositeFlag = static_cast<int>(DirectionManager::getOpposite(mainDir));
bool isHorizontal = DirectionManager::isHorizontal(mainDir);
int spacing = isHorizontal ? config.horizontalSpacing() : config.verticalSpacing();
if(components.size() == 2) //两个元件的主线拉长(特殊处理)
spacing *= 2;
// 计算分段索引
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 = 0;
if(i == 0 && (compo.nType == 6)) //单port元件队首反向
rotate = DirectionManager::getRotationAngle(DirectionManager::getOpposite(mainDir));
else
DirectionManager::getOpposite(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, DirectionManager::getOpposite(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<DiagramEditorComponentInfo>& sequence,
Direction branchDir,
const LayoutConfig& config,
Context& context,
bool isOrder,
int polarity,
double sizeFacotr) {
if (sequence.size() < 2) return;
QString routeId = context.currentRouteId;
bool isVertical = DirectionManager::isVertical(branchDir);
// ✅ 2. 间距由主线方向决定
int spacing = isVertical
? config.verticalSpacing()
: config.horizontalSpacing();
spacing *= sizeFacotr;
for (int i = 0; i < sequence.size(); ++i) {
DiagramEditorComponentInfo& compo = sequence[i];
// ✅ 当前元件作为“起点”
QPoint basePos = getComponentPosition(sequence[i].sName, context);
Direction dir =
determineBranchDirection(sequence[i], branchDir, context, polarity);
// ✅ 如果后面还有元件,才计算偏移
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 nextRotate =
DirectionManager::getRotationAngle(dir);
int curRotate = nextRotate; //当前元件旋转,默认与下个相同
//if(i == 0 && (compo.nType == 11 || compo.nType == 12)) //单port元件队首反向(避雷器,带电指示器)
// curRotate = DirectionManager::getRotationAngle(DirectionManager::getOpposite(dir));
updateComponent(sequence[i], int(dir),
basePos, curRotate, context);
updateComponent(next, int(nextConnectionDir),
nextPos, nextRotate, context);
}
}
}
// 确定支线方向
Direction DiagramLayoutEngine::determineBranchDirection(
const DiagramEditorComponentInfo& currentNode,
Direction preferredDir,
Context& context,
int forward) {
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<QPoint>()) {
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<QPoint>()) {
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) {
auto it = context.componentsCache.find(compo.sName);
if(it != context.componentsCache.end())
item->setData(it->nUsedDirection, Qt::UserRole + 1); //同步占用到standardModel
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<QString, DiagramEditorComponentInfo>& 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<QString, DiagramEditorRouteInfo>& 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;
}
QMap<int, QList<QString>> DiagramLayoutEngine::getCommonDirectionNode(QMap<QString, DiagramEditorRouteInfo>& routes,Context& context)
{
QMap<int, QList<QString>> directionToNames;
QList<QString> lstMain; //主线设备
for(auto& route:routes){
if(!route.bMainRoute)
continue;
for(auto& compo:route.lstCompo){
lstMain.append(compo.sName);
}
}
for (auto it = context.componentsCache.constBegin(); //寻找相同方位占用的node
it != context.componentsCache.constEnd();
++it)
{
const DiagramEditorComponentInfo& info = it.value();
//if (info.nCategory != 1) //新建时catch中分类无信息
// continue;
directionToNames[info.nUsedDirection].append(it.key());
}
//从 directionToNames 中移除出现在 lstMain 的node
for (auto it = directionToNames.begin(); it != directionToNames.end(); )
{
QList<QString>& nameList = it.value();
// 移除在 lstMain 中出现的node
nameList.erase(
std::remove_if(nameList.begin(), nameList.end(),
[&](const QString& name) {
return lstMain.contains(name);
}),
nameList.end()
);
// 如果该方向已无设备,删除该方向
if (nameList.isEmpty())
it = directionToNames.erase(it);
else
++it;
}
QSet<int> invalidDirections = {
8, 4, 2, 1, // 单方向
12, 3 // 直线组合
};
// 只保留拐角node
for (auto it = directionToNames.begin(); it != directionToNames.end(); )
{
int dir = it.key();
if(it.value().size() == 1){ //删除孤立数据
it = directionToNames.erase(it);
continue;
}
if (invalidDirections.contains(dir) || it.value().isEmpty())
it = directionToNames.erase(it);
else
++it;
}
return directionToNames;
}
void DiagramLayoutEngine::updateConfilicNode(QMap<QString, DiagramEditorRouteInfo>& routes,Context& context,const LayoutConfig& config,QMap<int, QList<QString>> directionToNames)
{
QMap<int, QList<QPair<QString, QString>>> directionConflicts; //key占用方向分支 value:first→占用方向相同的设备 second→ 共线lstMain中设备
QList<QString> lstMain; //主线设备
QMap<QString,QList<QString>> mapSub; //支线设备
for(auto& route:routes){
if(!route.bMainRoute)
{
QList<QString> lstSub;
for(auto& compo:route.lstCompo){
lstSub.append(compo.sName);
}
mapSub.insert(route.sRouteName,lstSub);
continue;
}
for(auto& compo:route.lstCompo){
lstMain.append(compo.sName);
}
}
// 遍历 directionToNames 的每个方向分支
for (auto dirIt = directionToNames.constBegin();
dirIt != directionToNames.constEnd();
++dirIt)
{
int direction = dirIt.key();
const QList<QString>& devList = dirIt.value();
QList<QPair<QString, QString>> conflicts;
// 遍历当前方向分支中的每个设备
for (const QString& dev : devList)
{
// 遍历所有 sub 线路
for (const QList<QString>& subDevs : mapSub)
{
if (!subDevs.contains(dev))
continue;
// 查找 lstMain 中同时存在于该 sub 的设备
for (const QString& mainDev : lstMain)
{
if (subDevs.contains(mainDev))
{
conflicts.append(qMakePair(dev, mainDev));
break;
}
}
if (!conflicts.isEmpty() &&
conflicts.last().first == dev)
{
break;
}
}
}
if (!conflicts.isEmpty())
directionConflicts[direction] = conflicts;
}
if(!directionConflicts.isEmpty()){
const QString lastMainDev = lstMain.last();
for(auto& lstPair:directionConflicts){
QPair<QString, QString> targetPair;
for (const auto& pair : lstPair)
{
if (pair.second != lastMainDev)
{
targetPair = pair;
break;
}
}
// 如果找到了,才处理
if (!targetPair.first.isEmpty())
{
relayoutTargetRoute(targetPair.first,routes,context,config);
}
}
}
}
void DiagramLayoutEngine::relayoutTargetRoute(const QString& obj,QMap<QString, DiagramEditorRouteInfo>& routes,Context& context,const LayoutConfig& config)
{
for(auto& route:routes){ //对包含本node的线路重绘制
bool containObj = false;
for(auto& compo:route.lstCompo){
if(obj == compo.sName){
containObj = true;
break;
}
}
if(containObj){
clearRouteDirectionOccupancy(route.sRouteName,context); //重构时先移除占用
if (route.lstOrder.size() > 1) {
layoutBranchSequence(route.lstOrder, route.preferDirection,
config, context, true, 1, 2.0);
}
if (route.lstReverse.size() > 1) {
layoutBranchSequence(route.lstReverse, DirectionManager::getOpposite(route.preferDirection),
config, context, false, -1, 2.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;
}