Better routing

This commit is contained in:
Joey Yakimowich-Payne 2026-02-06 13:24:18 -07:00
commit 07e2fb4c5a
3 changed files with 99 additions and 27 deletions

View file

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