Better routing
This commit is contained in:
parent
916965488c
commit
07e2fb4c5a
3 changed files with 99 additions and 27 deletions
|
|
@ -66,7 +66,17 @@ QPainterPath SquareConnectionPainter::orthogonalPath(
|
|||
constexpr double kNodePad = 15.0;
|
||||
|
||||
auto const cId = cgo.connectionId();
|
||||
double const spread = static_cast<double>(cId.outPortIndex) * kSpacing;
|
||||
|
||||
double spread = static_cast<double>(cId.outPortIndex) * kSpacing;
|
||||
auto *sceneForChannel = cgo.nodeScene();
|
||||
if (sceneForChannel) {
|
||||
auto *mdl = dynamic_cast<WarpGraphModel *>(&sceneForChannel->graphModel());
|
||||
if (mdl) {
|
||||
auto ch = mdl->connectionChannel(cId);
|
||||
spread = (static_cast<double>(ch.index) - (ch.count - 1) / 2.0)
|
||||
* kSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
double const dy = in.y() - out.y();
|
||||
|
||||
|
|
@ -114,51 +124,52 @@ QPainterPath SquareConnectionPainter::orthogonalPath(
|
|||
// out -------+ seg 1 + corner 1
|
||||
//
|
||||
|
||||
double rightX = out.x() + kMinStub + spread;
|
||||
double leftX = in.x() - kMinStub - spread;
|
||||
double midY = (out.y() + in.y()) / 2.0;
|
||||
double railOffset = 0.0;
|
||||
if (sceneForChannel) {
|
||||
auto *mdl2 = dynamic_cast<WarpGraphModel *>(&sceneForChannel->graphModel());
|
||||
if (mdl2) {
|
||||
auto ch = mdl2->connectionChannel(cId);
|
||||
railOffset = static_cast<double>(ch.index) * kSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
double rightX = out.x() + kMinStub + railOffset;
|
||||
double leftX = in.x() - kMinStub - railOffset;
|
||||
double midY = (out.y() + in.y()) / 2.0 + spread;
|
||||
|
||||
// Use actual node geometry when available so the path routes cleanly
|
||||
// around both nodes instead of cutting through them.
|
||||
auto *scene = cgo.nodeScene();
|
||||
if (scene) {
|
||||
auto *outNGO = scene->nodeGraphicsObject(cId.outNodeId);
|
||||
auto *inNGO = scene->nodeGraphicsObject(cId.inNodeId);
|
||||
if (outNGO && inNGO) {
|
||||
// Map node scene bounds into the CGO's local coordinate space
|
||||
// (endPoint() values live there too).
|
||||
QRectF const outRect =
|
||||
cgo.mapRectFromScene(outNGO->sceneBoundingRect());
|
||||
QRectF const inRect =
|
||||
cgo.mapRectFromScene(inNGO->sceneBoundingRect());
|
||||
|
||||
// Push vertical rails outside both nodes.
|
||||
double const rightEdge = std::max(outRect.right(), inRect.right());
|
||||
rightX = std::max(rightX, rightEdge + kNodePad + spread);
|
||||
rightX = std::max(rightX, rightEdge + kNodePad + railOffset);
|
||||
|
||||
double const leftEdge = std::min(outRect.left(), inRect.left());
|
||||
leftX = std::min(leftX, leftEdge - kNodePad - spread);
|
||||
leftX = std::min(leftX, leftEdge - kNodePad - railOffset);
|
||||
|
||||
// Place the horizontal crossover in the gap between nodes when
|
||||
// they don't overlap vertically; otherwise route above or below.
|
||||
double const topInner =
|
||||
std::min(outRect.bottom(), inRect.bottom());
|
||||
double const botInner =
|
||||
std::max(outRect.top(), inRect.top());
|
||||
|
||||
if (botInner > topInner + 2.0 * kNodePad) {
|
||||
// Vertical gap exists — clamp midY into the gap.
|
||||
midY = std::clamp(midY, topInner + kNodePad,
|
||||
botInner - kNodePad);
|
||||
} else {
|
||||
// Nodes overlap vertically — pick the shorter detour.
|
||||
double const above =
|
||||
std::min(outRect.top(), inRect.top()) - kNodePad;
|
||||
double const below =
|
||||
std::max(outRect.bottom(), inRect.bottom()) + kNodePad;
|
||||
midY = (std::abs(midY - above) < std::abs(midY - below))
|
||||
? above
|
||||
: below;
|
||||
double baseMidY = (out.y() + in.y()) / 2.0;
|
||||
midY = (std::abs(baseMidY - above) < std::abs(baseMidY - below))
|
||||
? above + spread
|
||||
: below + spread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -565,22 +565,38 @@ void WarpGraphModel::refreshFromClient() {
|
|||
QtNodes::PortIndex inPortIdx = 0;
|
||||
bool found = false;
|
||||
|
||||
bool outPortFound = false;
|
||||
bool inPortFound = false;
|
||||
|
||||
for (const auto &[qtId, nodeData] : m_nodes) {
|
||||
if (!outPortFound) {
|
||||
for (size_t i = 0; i < nodeData.outputPorts.size(); ++i) {
|
||||
if (nodeData.outputPorts[i].id.value == link.output_port.value) {
|
||||
if (nodeData.outputPorts[i].id.value ==
|
||||
link.output_port.value) {
|
||||
outNodeIt = m_pwToQt.find(nodeData.info.id.value);
|
||||
outPortIdx = static_cast<QtNodes::PortIndex>(i);
|
||||
outPortFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!inPortFound) {
|
||||
for (size_t i = 0; i < nodeData.inputPorts.size(); ++i) {
|
||||
if (nodeData.inputPorts[i].id.value == link.input_port.value) {
|
||||
if (nodeData.inputPorts[i].id.value ==
|
||||
link.input_port.value) {
|
||||
inNodeIt = m_pwToQt.find(nodeData.info.id.value);
|
||||
inPortIdx = static_cast<QtNodes::PortIndex>(i);
|
||||
inPortFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (outPortFound && inPortFound)
|
||||
break;
|
||||
}
|
||||
|
||||
if (outNodeIt != m_pwToQt.end() && inNodeIt != m_pwToQt.end()) {
|
||||
if (outPortFound && inPortFound && outNodeIt != m_pwToQt.end() &&
|
||||
inNodeIt != m_pwToQt.end()) {
|
||||
found = true;
|
||||
}
|
||||
|
||||
|
|
@ -710,6 +726,8 @@ void WarpGraphModel::refreshFromClient() {
|
|||
Q_EMIT nodeVolumeChanged(qtId, previous, cached);
|
||||
}
|
||||
|
||||
recomputeConnectionChannels();
|
||||
|
||||
m_refreshing = false;
|
||||
if (sceneChanged) {
|
||||
Q_EMIT endBatchUpdate();
|
||||
|
|
@ -976,6 +994,40 @@ float WarpGraphModel::nodePeakLevel(QtNodes::NodeId nodeId) const {
|
|||
return it != m_peakLevels.end() ? it->second : 0.0f;
|
||||
}
|
||||
|
||||
void WarpGraphModel::recomputeConnectionChannels() {
|
||||
m_connectionChannels.clear();
|
||||
|
||||
std::unordered_map<QtNodes::NodeId, std::vector<QtNodes::ConnectionId>> byTarget;
|
||||
for (const auto &cId : m_connections)
|
||||
byTarget[cId.inNodeId].push_back(cId);
|
||||
|
||||
for (auto &[targetId, conns] : byTarget) {
|
||||
std::sort(conns.begin(), conns.end(),
|
||||
[this](const auto &a, const auto &b) {
|
||||
auto posA = m_positions.count(a.outNodeId)
|
||||
? m_positions.at(a.outNodeId).y()
|
||||
: 0.0;
|
||||
auto posB = m_positions.count(b.outNodeId)
|
||||
? m_positions.at(b.outNodeId).y()
|
||||
: 0.0;
|
||||
if (posA != posB)
|
||||
return posA < posB;
|
||||
return a.outPortIndex < b.outPortIndex;
|
||||
});
|
||||
|
||||
int count = static_cast<int>(conns.size());
|
||||
for (int i = 0; i < count; ++i)
|
||||
m_connectionChannels[conns[i]] = {i, count};
|
||||
}
|
||||
}
|
||||
|
||||
WarpGraphModel::ConnectionChannel
|
||||
WarpGraphModel::connectionChannel(QtNodes::ConnectionId cId) const {
|
||||
auto it = m_connectionChannels.find(cId);
|
||||
return it != m_connectionChannels.end() ? it->second
|
||||
: ConnectionChannel{0, 1};
|
||||
}
|
||||
|
||||
void WarpGraphModel::saveLayout(const QString &path) const {
|
||||
ViewState vs{};
|
||||
saveLayout(path, vs);
|
||||
|
|
|
|||
|
|
@ -98,6 +98,12 @@ public:
|
|||
void setNodePeakLevel(QtNodes::NodeId nodeId, float level);
|
||||
float nodePeakLevel(QtNodes::NodeId nodeId) const;
|
||||
|
||||
struct ConnectionChannel {
|
||||
int index = 0;
|
||||
int count = 1;
|
||||
};
|
||||
ConnectionChannel connectionChannel(QtNodes::ConnectionId cId) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void beginBatchUpdate();
|
||||
void endBatchUpdate();
|
||||
|
|
@ -173,4 +179,7 @@ private:
|
|||
std::unordered_map<QtNodes::NodeId, QPointer<QWidget>> m_volumeWidgets;
|
||||
mutable std::unordered_map<QtNodes::NodeId, QVariant> m_styleCache;
|
||||
std::unordered_map<QtNodes::NodeId, float> m_peakLevels;
|
||||
std::unordered_map<QtNodes::ConnectionId, ConnectionChannel> m_connectionChannels;
|
||||
|
||||
void recomputeConnectionChannels();
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue