GridFrame/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp

476 lines
15 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,
const LayoutConfig& config,
Context& context) {
context.initComponentsCache(components);
context.itemCache.clear();
QString mainRouteName = findMainRoute(routes);
if (mainRouteName.isEmpty()) {
qWarning() << "No main route found";
return QRectF();
}
// 1. 布局主线
layoutMainRoute(routes[mainRouteName], config, context);
// 2. 布局所有支线(✅ 此时方向数据必须是最新的)
for (auto it = routes.begin(); it != routes.end(); ++it) {
if (it->sRouteName == mainRouteName) continue;
layoutBranchRoute(*it, config, context);
}
// ✅ 3. 所有布局完成之后,才更新 components
if (!context.saveToModel) {
for(auto& compo:components){ //从compo缓存中读取计算过的数据
auto mapCatch = context.componentsCache;
compo.nUsedDirection = mapCatch[compo.sName].nUsedDirection;
compo.nRotate = mapCatch[compo.sName].nRotate;
compo.deltaPos = mapCatch[compo.sName].deltaPos;
}
}
// 4. 计算边界
if (!context.saveToModel) {
return calculateBoundingRect(components);
}
return QRectF();
}
// 查找主线
QString DiagramLayoutEngine::findMainRoute(
const QMap<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) {
Direction mainDir = config.mainDirection();
auto& components = route.lstCompo;
if (components.isEmpty()) return;
// 计算分段
int nSeg = components.size() / 2;
int nSegIndex = 0;
// 确定起始索引方向
if (mainDir == Direction::Down || mainDir == Direction::Right) {
nSegIndex = -nSeg; // 正向从负半开始
} else {
nSegIndex = nSeg; // 反向从正半开始
}
// 遍历所有组件
for (int i = 0; i < components.size(); ++i) {
DiagramEditorComponentInfo& compo = components[i];
// 确定方向占用
Direction dir = mainDir;
if (i == 0) {
// 队首保持主线方向
} else if (i == components.size() - 1) {
// 队尾取反方向
dir = DirectionManager::getOpposite(mainDir);
} else {
// 中间元件双向占用
compo.nUsedDirection = static_cast<int>(mainDir) |
static_cast<int>(DirectionManager::getOpposite(mainDir));
}
// 计算位置增量
QPoint delta = QPoint(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);
if (!context.saveToModel) {
context.componentsCache[compo.sName] = compo;
}
// 更新组件
updateComponent(compo, dir, delta, rotate, context);
// 更新索引
if (mainDir == Direction::Up || mainDir == Direction::Left) {
nSegIndex -= 1;
} else {
nSegIndex += 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) {
splitBranchRoute(route, context);
if (route.lstOrder.size() > 1) {
// ✅ 第一个元件的真实方向,由 determineBranchDirection 决定
Direction startDir =
determineBranchDirection(route.lstOrder[0],
config.subDirection(),
context);
int polarity = 1;
layoutBranchSequence(route.lstOrder,
startDir,
config,
context,
true,
polarity);
}
if (route.lstReverse.size() > 1) {
Direction startDir =
determineBranchDirection(route.lstReverse[0],
config.subDirection(),
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) {
if (sequence.size() < 2) return;
// ✅ 1. 根据主线方向决定展开轴
bool mainIsVertical =
DirectionManager::isVertical(config.mainDirection());
// ✅ 2. 间距由主线方向决定
int spacing = mainIsVertical
? config.horizontalSpacing()
: config.verticalSpacing();
for (int i = 0; i < sequence.size(); ++i) {
// ✅ 当前元件作为“起点”
QPoint basePos = getComponentPosition(sequence[i].sName, context);
Direction dir =
determineBranchDirection(sequence[i], branchDir, context);
// ✅ 如果后面还有元件,才计算偏移
if (i + 1 < sequence.size()) {
DiagramEditorComponentInfo& next = sequence[i + 1];
int offset = (i + 1) * polarity * spacing;
QPoint nextPos = basePos;
if (mainIsVertical) {
// ✅ 只在 X 轴应用偏移Y 轴纹丝不动
nextPos.setX(basePos.x() + offset);
// nextPos.setY(basePos.y()); // ✅ 隐式成立,无需再写
} else {
// ✅ 只在 Y 轴应用偏移X 轴纹丝不动
nextPos.setY(basePos.y() + offset);
// nextPos.setX(basePos.x()); // ✅ 隐式成立,无需再写
}
QPoint deltaPos = nextPos - basePos;
Direction nextConnectionDir =
DirectionManager::getOpposite(dir);
int rotate =
DirectionManager::getRotationAngle(dir);
updateComponent(next, nextConnectionDir,
nextPos, rotate, context);
}
}
}
// 确定支线方向
Direction DiagramLayoutEngine::determineBranchDirection(
const DiagramEditorComponentInfo& currentNode,
Direction preferredDir,
Context& context) {
int usedDirections = getComponentDirection(currentNode.sName, context);
bool isHorizontalLayout = DirectionManager::isHorizontal(preferredDir);
if (isHorizontalLayout) {
// 水平布局,检查左右
bool leftOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Left);
bool rightOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Right);
if (leftOccupied && rightOccupied) {
qWarning() << "Component" << currentNode.sName
<< "has both left and right directions occupied";
return preferredDir;
} else if (leftOccupied && !rightOccupied) {
return Direction::Right;
} else if (rightOccupied && !leftOccupied) {
return Direction::Left;
} else {
return preferredDir;
}
} else {
// 垂直布局,检查上下
bool upOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Up);
bool downOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Down);
if (upOccupied && downOccupied) {
qWarning() << "Component" << currentNode.sName
<< "has both up and down directions occupied";
return preferredDir;
} else if (upOccupied && !downOccupied) {
return Direction::Down;
} else if (downOccupied && !upOccupied) {
return Direction::Up;
} else {
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,
Direction dir,
const QPoint& position,
int rotate,
Context& 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));
item->setData(position, Qt::UserRole + 2);
item->setData(rotate, Qt::UserRole + 5);
//compo.nUsedDirection = DirectionManager::markDirectionOccupied(compo.nUsedDirection, dir);
//compo.deltaPos = position;
//compo.nRotate = rotate;
// 更新缓存
//context.componentsCache[compo.sName] = compo;
}
} else {
compo.nUsedDirection = DirectionManager::markDirectionOccupied(compo.nUsedDirection, dir);
compo.deltaPos = position;
compo.nRotate = rotate;
// 更新缓存
context.componentsCache[compo.sName].nUsedDirection = compo.nUsedDirection;
context.componentsCache[compo.sName].deltaPos = compo.deltaPos;
context.componentsCache[compo.sName].nRotate = compo.nRotate;
}
}
// 计算边界矩形
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);
}