#ifndef GRAPHGRIDLAYOUT_H #define GRAPHGRIDLAYOUT_H #include "core/Cutter.h" #include "GraphLayout.h" #include "common/LinkedListPool.h" /** * @brief Graph layout algorithm on layered graph layout approach. For simplicity all the nodes are placed in a grid. */ class GraphGridLayout : public GraphLayout { public: enum class LayoutType { Medium, Wide, Narrow, }; GraphGridLayout(LayoutType layoutType = LayoutType::Medium); virtual void CalculateLayout(std::unordered_map &blocks, ut64 entry, int &width, int &height) const override; private: LayoutType layoutType; /// false - use bounding box for smallest subtree when placing them side by side bool tightSubtreePlacement = false; /// true if code should try to place parent between direct children as much as possible bool parentBetweenDirectChild = false; /// false if blocks in rows should be aligned at top, true for middle alignment bool verticalBlockAlignmentMiddle = false; struct GridBlock { ut64 id; std::vector tree_edge; //!< subset of outgoing edges that form a tree std::vector dag_edge; //!< subset of outgoing edges that form a dag std::size_t has_parent = false; int inputCount = 0; int outputCount = 0; /// Number of rows in subtree int row_count = 0; /// Column in which the block is int col = 0; /// Row in which the block is int row = 0; int lastRowLeft; //!< left side of subtree last row int lastRowRight; //!< right side of subtree last row int leftPosition; //!< left side of subtree int rightPosition; //!< right side of subtree LinkedListPool::List leftSideShape; LinkedListPool::List rightSideShape; }; struct Point { int row; int col; int offset; int16_t kind; int16_t spacingOverride; }; struct GridEdge { ut64 dest; int mainColumn = -1; std::vector points; int secondaryPriority; void addPoint(int row, int col, int16_t kind = 0) { this->points.push_back({row, col, 0, kind, 0}); } }; struct LayoutState { std::unordered_map grid_blocks; std::unordered_map *blocks = nullptr; std::unordered_map> edge; size_t rows = -1; size_t columns = -1; std::vector columnWidth; std::vector rowHeight; std::vector edgeColumnWidth; std::vector edgeRowHeight; std::vector columnOffset; std::vector rowOffset; std::vector edgeColumnOffset; std::vector edgeRowOffset; }; using GridBlockMap = std::unordered_map; /** * @brief Find nodes where control flow merges after splitting. * Sets node column offset so that after computing placement merge point is centered bellow nodes above. */ void findMergePoints(LayoutState &state) const; /** * @brief Compute node rows and columns within grid. * @param blockOrder Nodes in the reverse topological order. */ void computeAllBlockPlacement(const std::vector &blockOrder, LayoutState &layoutState) const; /** * @brief Perform the topological sorting of graph nodes. * If the graph contains loops, a subset of edges is selected. Subset of edges forming DAG are stored in * GridBlock::dag_edge. * @param state Graph layout state including the input graph. * @param entry Entrypoint node. When removing loops prefer placing this node at top. * @return Reverse topological ordering. */ static std::vector topoSort(LayoutState &state, ut64 entry); /** * @brief Assign row positions to nodes. * @param state * @param blockOrder reverse topological ordering of nodes */ static void assignRows(LayoutState &state, const std::vector &blockOrder); /** * @brief Select subset of DAG edges that form tree. * @param state */ static void selectTree(LayoutState &state); /** * @brief routeEdges Route edges, expects node positions to be calculated previously. */ void routeEdges(LayoutState &state) const; /** * @brief Choose which column to use for transition from start node row to target node row. */ void calculateEdgeMainColumn(LayoutState &state) const; /** * @brief Do rough edge routing within grid using up to 5 segments. */ void roughRouting(LayoutState &state) const; /** * @brief Calculate segment placement relative to their columns. */ void elaborateEdgePlacement(LayoutState &state) const; /** * @brief Recalculate column widths, trying to compensate for the space taken by edge columns. */ void adjustColumnWidths(LayoutState &state) const; /** * @brief Calculate position of each column(or row) based on widths. * It is assumed that columnWidth.size() + 1 = edgeColumnWidth.size() and they are interleaved. * @param columnWidth * @param edgeColumnWidth * @param columnOffset * @param edgeColumnOffset * @return total width of all the columns */ static int calculateColumnOffsets(const std::vector &columnWidth, std::vector &edgeColumnWidth, std::vector &columnOffset, std::vector &edgeColumnOffset); /** * @brief Final graph layout step. Convert grids cell relative positions to absolute pixel positions. * @param state * @param width image width output argument * @param height image height output argument */ void convertToPixelCoordinates(LayoutState &state, int &width, int &height) const; }; #endif // GRAPHGRIDLAYOUT_H