2019-04-04 05:54:42 +00:00
|
|
|
#ifndef GRAPHGRIDLAYOUT_H
|
|
|
|
#define GRAPHGRIDLAYOUT_H
|
|
|
|
|
|
|
|
#include "core/Cutter.h"
|
|
|
|
#include "GraphLayout.h"
|
2020-06-03 15:36:44 +00:00
|
|
|
#include "common/LinkedListPool.h"
|
2019-04-04 05:54:42 +00:00
|
|
|
|
2020-06-03 15:36:44 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Graph layout algorithm on layered graph layout approach. For simplicity all the nodes are placed in a grid.
|
|
|
|
*/
|
2019-04-04 05:54:42 +00:00
|
|
|
class GraphGridLayout : public GraphLayout
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum class LayoutType {
|
|
|
|
Medium,
|
|
|
|
Wide,
|
|
|
|
Narrow,
|
|
|
|
};
|
|
|
|
|
|
|
|
GraphGridLayout(LayoutType layoutType = LayoutType::Medium);
|
2020-07-03 17:09:37 +00:00
|
|
|
virtual void CalculateLayout(Graph &blocks,
|
2019-04-04 05:54:42 +00:00
|
|
|
ut64 entry,
|
|
|
|
int &width,
|
|
|
|
int &height) const override;
|
2020-07-03 17:09:37 +00:00
|
|
|
void setTightSubtreePlacement(bool enabled) { tightSubtreePlacement = enabled; }
|
|
|
|
void setParentBetweenDirectChild(bool enabled) { parentBetweenDirectChild = enabled; }
|
|
|
|
void setverticalBlockAlignmentMiddle(bool enabled) { verticalBlockAlignmentMiddle = enabled; }
|
|
|
|
void setLayoutOptimization(bool enabled) { useLayoutOptimization = enabled; }
|
2019-04-04 05:54:42 +00:00
|
|
|
private:
|
2020-06-03 15:36:44 +00:00
|
|
|
/// 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;
|
2020-07-03 17:09:37 +00:00
|
|
|
bool useLayoutOptimization = true;
|
2019-04-04 05:54:42 +00:00
|
|
|
|
|
|
|
struct GridBlock {
|
|
|
|
ut64 id;
|
2020-06-03 15:36:44 +00:00
|
|
|
std::vector<ut64> tree_edge; //!< subset of outgoing edges that form a tree
|
|
|
|
std::vector<ut64> dag_edge; //!< subset of outgoing edges that form a dag
|
2019-04-06 19:37:49 +00:00
|
|
|
std::size_t has_parent = false;
|
2020-06-03 15:36:44 +00:00
|
|
|
int inputCount = 0;
|
|
|
|
int outputCount = 0;
|
2019-04-04 05:54:42 +00:00
|
|
|
|
2020-06-03 15:36:44 +00:00
|
|
|
/// Number of rows in subtree
|
2019-04-04 05:54:42 +00:00
|
|
|
int row_count = 0;
|
2020-06-03 15:36:44 +00:00
|
|
|
/// Column in which the block is
|
2019-04-04 05:54:42 +00:00
|
|
|
int col = 0;
|
2020-06-03 15:36:44 +00:00
|
|
|
/// Row in which the block is
|
2019-04-04 05:54:42 +00:00
|
|
|
int row = 0;
|
2020-06-03 15:36:44 +00:00
|
|
|
|
2020-07-03 17:09:37 +00:00
|
|
|
ut64 mergeBlock = 0;
|
|
|
|
|
2020-06-03 15:36:44 +00:00
|
|
|
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<int>::List leftSideShape;
|
|
|
|
LinkedListPool<int>::List rightSideShape;
|
2019-04-04 05:54:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Point {
|
2020-06-03 15:36:44 +00:00
|
|
|
int row;
|
|
|
|
int col;
|
|
|
|
int offset;
|
|
|
|
int16_t kind;
|
|
|
|
int16_t spacingOverride;
|
2019-04-04 05:54:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct GridEdge {
|
|
|
|
ut64 dest;
|
2020-06-03 15:36:44 +00:00
|
|
|
int mainColumn = -1;
|
2019-04-04 05:54:42 +00:00
|
|
|
std::vector<Point> points;
|
2020-06-03 15:36:44 +00:00
|
|
|
int secondaryPriority;
|
2019-04-04 05:54:42 +00:00
|
|
|
|
2020-06-03 15:36:44 +00:00
|
|
|
void addPoint(int row, int col, int16_t kind = 0)
|
2019-04-04 05:54:42 +00:00
|
|
|
{
|
2020-06-03 15:36:44 +00:00
|
|
|
this->points.push_back({row, col, 0, kind, 0});
|
2019-04-04 05:54:42 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct LayoutState {
|
|
|
|
std::unordered_map<ut64, GridBlock> grid_blocks;
|
|
|
|
std::unordered_map<ut64, GraphBlock> *blocks = nullptr;
|
|
|
|
std::unordered_map<ut64, std::vector<GridEdge>> edge;
|
2020-06-03 15:36:44 +00:00
|
|
|
size_t rows = -1;
|
|
|
|
size_t columns = -1;
|
|
|
|
std::vector<int> columnWidth;
|
|
|
|
std::vector<int> rowHeight;
|
|
|
|
std::vector<int> edgeColumnWidth;
|
|
|
|
std::vector<int> edgeRowHeight;
|
|
|
|
|
|
|
|
std::vector<int> columnOffset;
|
|
|
|
std::vector<int> rowOffset;
|
|
|
|
std::vector<int> edgeColumnOffset;
|
|
|
|
std::vector<int> edgeRowOffset;
|
2019-04-04 05:54:42 +00:00
|
|
|
};
|
|
|
|
|
2019-04-06 19:37:49 +00:00
|
|
|
using GridBlockMap = std::unordered_map<ut64, GridBlock>;
|
|
|
|
|
2020-06-03 15:36:44 +00:00
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
*/
|
2019-04-06 19:37:49 +00:00
|
|
|
void computeAllBlockPlacement(const std::vector<ut64> &blockOrder,
|
|
|
|
LayoutState &layoutState) const;
|
2020-06-03 15:36:44 +00:00
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
*/
|
2019-04-04 05:54:42 +00:00
|
|
|
static std::vector<ut64> topoSort(LayoutState &state, ut64 entry);
|
|
|
|
|
2020-06-03 15:36:44 +00:00
|
|
|
/**
|
|
|
|
* @brief Assign row positions to nodes.
|
|
|
|
* @param state
|
|
|
|
* @param blockOrder reverse topological ordering of nodes
|
|
|
|
*/
|
|
|
|
static void assignRows(LayoutState &state, const std::vector<ut64> &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<int> &columnWidth, std::vector<int> &edgeColumnWidth,
|
|
|
|
std::vector<int> &columnOffset, std::vector<int> &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;
|
2020-07-03 17:09:37 +00:00
|
|
|
/**
|
|
|
|
* @brief Move the graph content to top left corner and update dimensions.
|
|
|
|
* @param graph
|
|
|
|
* @param width width after cropping
|
|
|
|
* @param height height after cropping
|
|
|
|
*/
|
|
|
|
void cropToContent(Graph &graph, int &width, int &height) const;
|
|
|
|
/**
|
|
|
|
* @brief Connect edge ends to blocks by changing y.
|
|
|
|
* @param graph
|
|
|
|
*/
|
|
|
|
void connectEdgeEnds(Graph &graph) const;
|
|
|
|
/**
|
|
|
|
* @brief Reduce spacing between nodes and edges by pushing everything together ignoring the grid.
|
|
|
|
* @param state
|
|
|
|
*/
|
|
|
|
void optimizeLayout(LayoutState &state) const;
|
2019-04-04 05:54:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // GRAPHGRIDLAYOUT_H
|