mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
193 lines
8.3 KiB
C++
193 lines
8.3 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "Helpers.h"
|
|
#include "NodeClassification.h"
|
|
|
|
/// Prepares the graph for further processing.
|
|
template<RankingStrategy Strategy>
|
|
std::tuple<RankContainer, MaybeClassifier<Strategy>>
|
|
prepareGraph(InternalGraph &Graph, bool OmitClassification);
|
|
|
|
/// Approximates an optimal permutation selection.
|
|
template<RankingStrategy Strategy>
|
|
LayerContainer selectPermutation(InternalGraph &Graph,
|
|
RankContainer &Ranks,
|
|
const MaybeClassifier<Strategy> &Classifier);
|
|
|
|
/// A simplified permutation selection to only be used with simple tree.
|
|
LayerContainer selectSimpleTreePermutation(InternalGraph &Graph,
|
|
RankContainer &Ranks);
|
|
|
|
/// Topologically orders nodes of an augmented graph generated based on a
|
|
/// layered version of the graph.
|
|
std::vector<NodeView>
|
|
extractAugmentedTopologicalOrder(InternalGraph &Graph,
|
|
const LayerContainer &Layers);
|
|
|
|
/// Looks for the linear segments and ensures an optimal combination of them
|
|
/// is selected. It uses an algorithm from the Sander's paper.
|
|
/// The worst case complexity is O(N^2) in the cases where jump table is huge,
|
|
/// but the common case is very far from that because normally both
|
|
/// entry and exit edge count is low (intuitively, our layouts are tall rather
|
|
/// than wide).
|
|
///
|
|
/// \note: it's probably a good idea to think about loosening the dependence
|
|
/// on tall graph layouts since we will want to also lay more generic graphs
|
|
/// out.
|
|
SegmentContainer selectLinearSegments(InternalGraph &Graph,
|
|
const RankContainer &Ranks,
|
|
const LayerContainer &Layers,
|
|
const std::vector<NodeView> &Order);
|
|
|
|
/// Builds an empty linear segments map.
|
|
SegmentContainer emptyLinearSegments(InternalGraph &Graph);
|
|
|
|
/// "Levels up" a `LayerContainer` to a `LayoutContainer`.
|
|
LayoutContainer convertToLayout(const LayerContainer &Layers);
|
|
|
|
/// Calculates horizontal coordinates based on a finalized layout and segments.
|
|
void setHorizontalCoordinates(const LayerContainer &Layers,
|
|
const std::vector<NodeView> &Order,
|
|
const SegmentContainer &LinearSegments,
|
|
const LayoutContainer &Layout,
|
|
float MarginSize,
|
|
float VirtualNodeWeight = 0.1f);
|
|
|
|
/// Simplified horizontal coordinate calculation based on layers only.
|
|
void setStaticOffsetHorizontalCoordinates(const LayerContainer &Layers,
|
|
float MarginSize);
|
|
|
|
/// Distributes "touching" edges across lanes to minimize the crossing count.
|
|
LaneContainer assignLanes(InternalGraph &Graph,
|
|
const SegmentContainer &LinearSegments,
|
|
const LayoutContainer &Layout);
|
|
|
|
/// Calculates vertical coordinates based on layer and lane data.
|
|
void setVerticalCoordinates(const LayerContainer &Layers,
|
|
const LaneContainer &Lanes,
|
|
float MarginSize,
|
|
float EdgeDistance);
|
|
|
|
/// Routes edges that form backwards facing corners. For their indication,
|
|
/// V-shaped structures were added to the graph when the backwards edges
|
|
/// were partitioned.
|
|
CornerContainer routeBackwardsCorners(InternalGraph &Graph,
|
|
const RankContainer &Ranks,
|
|
const LaneContainer &Lanes,
|
|
float MarginSize,
|
|
float EdgeDistance);
|
|
|
|
/// Restores the original edge direction by flipping all the edges marked with
|
|
/// `IsBackwards == true`.
|
|
void restoreEdgeDirections(InternalGraph &Graph);
|
|
|
|
/// Produce the optimal routing order.
|
|
OrderedEdgeContainer orderEdges(InternalGraph &Graph,
|
|
CornerContainer &&Prerouted,
|
|
const RankContainer &Ranks,
|
|
const LaneContainer &Lanes);
|
|
|
|
void route(const OrderedEdgeContainer &OrderedListOfEdges,
|
|
float MarginSize,
|
|
float EdgeDistance);
|
|
|
|
void routeWithStraightLines(const OrderedEdgeContainer &OrderedListOfEdges);
|
|
|
|
/// Computes the layout given a graph and the configuration.
|
|
///
|
|
/// \note: it only works with `MutableEdgeNode`s.
|
|
template<yield::layout::sugiyama::RankingStrategy RS>
|
|
bool computeInternal(InternalGraph &Graph, const Configuration &Configuration) {
|
|
static_assert(StrictSpecializationOfMutableEdgeNode<InternalNode>,
|
|
"LayouterSugiyama requires mutable edge nodes.");
|
|
|
|
// There's nothing we can do for a graph without any nodes.
|
|
if (Graph.size() == 0)
|
|
return true;
|
|
|
|
// Prepare the graph for the layouter: this converts `Graph` into
|
|
// a DAG, guaranteeing that it's has no loops (some of the edges might have to
|
|
// be temporarily inverted to ensure this), with every node having a rank
|
|
// assigned based on the strategy specified by the template parameter.
|
|
//
|
|
// All the long edges (edges where the ranks of `From` and `To` nodes differ
|
|
// by more than 1) are split into `1-rank` sized chunks by inserting "virtual"
|
|
// nodes in-between.
|
|
//
|
|
// On top of that, all the backwards facing edges, are also split into
|
|
// multiple chunks and are marked by 'v'-shapes in order to be easily noticed
|
|
// by the router so they can be treated in a special way.
|
|
// For more details on this, see `routeBackwardsCorners` function.
|
|
bool ShouldClassify = !Configuration.UseSimpleTreeOptimization;
|
|
auto &&[Ranks, Classified] = prepareGraph<RS>(Graph, !ShouldClassify);
|
|
|
|
// Try to select an optimal node permutation per layer.
|
|
// NOTE: since this is the part with the highest complexity, it needs extra
|
|
// care for the layouter to perform well.
|
|
// Maybe we should consider something more optimal instead of a simple hill
|
|
// climbing algorithm.
|
|
auto Layers = Configuration.UseSimpleTreeOptimization ?
|
|
selectSimpleTreePermutation(Graph, Ranks) :
|
|
selectPermutation<RS>(Graph, Ranks, *Classified);
|
|
|
|
// Compute an augmented topological ordering of the nodes of the graph.
|
|
auto Order = extractAugmentedTopologicalOrder(Graph, Layers);
|
|
|
|
// Decide on which segments of the graph can be made linear, e.g. each edge
|
|
// within the same linear segment is a straight line.
|
|
SegmentContainer LinearSegments;
|
|
if (Configuration.PreserveLinearSegments)
|
|
LinearSegments = selectLinearSegments(Graph, Ranks, Layers, Order);
|
|
else
|
|
LinearSegments = emptyLinearSegments(Graph);
|
|
|
|
// Finalize the logical positions for each of the nodes.
|
|
const auto Final = convertToLayout(Layers);
|
|
|
|
// Finalize the horizontal node positions.
|
|
const auto &Margin = Configuration.NodeMarginSize;
|
|
if (Configuration.UseSimpleTreeOptimization) {
|
|
size_t MaximumNodeWidth = 0;
|
|
for (auto *Node : Graph.nodes())
|
|
if (Node->Size.W > MaximumNodeWidth)
|
|
MaximumNodeWidth = Node->Size.W;
|
|
setStaticOffsetHorizontalCoordinates(Layers, MaximumNodeWidth + Margin);
|
|
} else {
|
|
const auto &W = Configuration.VirtualNodeWeight;
|
|
setHorizontalCoordinates(Layers, Order, LinearSegments, Final, Margin, W);
|
|
}
|
|
|
|
// Distribute edge lanes in a way that minimizes the number of crossings.
|
|
auto Lanes = assignLanes(Graph, LinearSegments, Final);
|
|
|
|
// Set the rest of the coordinates. Node layouting is complete after this.
|
|
const auto &EdgeGap = Configuration.EdgeMarginSize;
|
|
setVerticalCoordinates(Layers, Lanes, Margin, EdgeGap);
|
|
|
|
// Route edges forming backwards facing corners.
|
|
CornerContainer Prerouted;
|
|
if (Configuration.UseOrthogonalBends)
|
|
Prerouted = routeBackwardsCorners(Graph, Ranks, Lanes, Margin, EdgeGap);
|
|
|
|
// Restore the original edge directions.
|
|
// The graph will no longer be a DAG.
|
|
restoreEdgeDirections(Graph);
|
|
|
|
// To simplify routing, order the edges first. This consumes the prerouted
|
|
// corners to build an ordered list of edges with all the information
|
|
// necessary for them to get routed (see `OrderedEdgeContainer`).
|
|
auto Edges = orderEdges(Graph, std::move(Prerouted), Ranks, Lanes);
|
|
|
|
// Route the edges.
|
|
if (Configuration.UseOrthogonalBends)
|
|
route(Edges, Margin, EdgeGap);
|
|
else
|
|
routeWithStraightLines(Edges);
|
|
|
|
return true;
|
|
}
|