LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
tikz_layout.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_IO_TIKZ_TIKZ_LAYOUT_H
6#define LINE_IO_TIKZ_TIKZ_LAYOUT_H
7
8/**
9 * @file
10 * @ingroup line_io
11 * Port of `jline.io.tikz.TikZLayoutEngine`: a layered (Sugiyama-style) layout.
12 *
13 * Three steps, each the JAR's: layers by a breadth-first topological sweep from
14 * the Sources and the nodes nobody feeds (a cycle is broken by taking the first
15 * unassigned node in model order), Sinks moved to the last layer; four
16 * forward/backward barycenter passes to reduce crossings, each a STABLE sort as
17 * `Collections.sort` is; then x = layer * layerSpacing and each layer centred
18 * on y = 0 with nodeSpacing between its members.
19 */
20
21#include <algorithm>
22#include <limits>
23#include <vector>
24
27
28namespace line {
29namespace io {
30
31/** Node positions in cm, indexed like `TikzGraph::nodes`, and the layers that produced them. */
32struct TikzLayout {
33 std::vector<double> x, y;
34 std::vector<std::vector<std::size_t>> layers; ///< 0-based node indices, in drawing order
35};
36
37namespace tikz_detail {
38
39/** `reorderLayerByBarycenter`: sort a layer by the mean position of its neighbours in the reference layer. */
40inline void reorder_by_barycenter(std::vector<std::size_t>& layer,
41 const std::vector<std::vector<std::size_t>>& connections,
42 const std::vector<std::size_t>& reference, std::size_t nnodes) {
43 std::vector<long> refpos(nnodes, -1);
44 for (std::size_t i = 0; i < reference.size(); ++i) refpos[reference[i]] = static_cast<long>(i);
45 std::vector<double> bary(nnodes, std::numeric_limits<double>::max());
46 for (std::size_t k = 0; k < layer.size(); ++k) {
47 const std::size_t v = layer[k];
48 double sum = 0;
49 int count = 0;
50 for (std::size_t c : connections[v]) {
51 if (refpos[c] >= 0) {
52 sum += static_cast<double>(refpos[c]);
53 ++count;
54 }
55 }
56 if (count > 0) bary[v] = sum / count;
57 }
58 std::stable_sort(layer.begin(), layer.end(),
59 [&](std::size_t a, std::size_t b) { return bary[a] < bary[b]; });
60}
61
62} // namespace tikz_detail
63
64/** `TikZLayoutEngine.computeLayout`. */
66 TikzLayout L;
67 const std::size_t n = g.nodes.size();
68 L.x.assign(n, 0.0);
69 L.y.assign(n, 0.0);
70 if (n == 0) return L;
71
72 std::vector<std::vector<std::size_t>> succ(n), pred(n);
73 for (std::size_t i = 0; i < n; ++i)
74 for (std::size_t j = 0; j < n; ++j)
75 if (g.conn[i][j]) {
76 succ[i].push_back(j);
77 pred[j].push_back(i);
78 }
79
80 // Step 1: layers
81 std::vector<std::vector<std::size_t>>& layers = L.layers;
82 std::vector<bool> assigned(n, false);
83 std::size_t nassigned = 0;
84 std::vector<std::size_t> layer0;
85 for (std::size_t v = 0; v < n; ++v)
86 if (g.nodes[v].type == lang::NodeType::Source || pred[v].empty()) {
87 layer0.push_back(v);
88 assigned[v] = true;
89 ++nassigned;
90 }
91 if (layer0.empty()) {
92 layer0.push_back(0);
93 assigned[0] = true;
94 ++nassigned;
95 }
96 layers.push_back(layer0);
97 std::size_t cur = 0;
98 while (nassigned < n) {
99 std::vector<std::size_t> next;
100 for (std::size_t v = 0; v < n; ++v) {
101 if (assigned[v]) continue;
102 bool all = true;
103 for (std::size_t p : pred[v])
104 if (!assigned[p]) {
105 all = false;
106 break;
107 }
108 if (all) next.push_back(v);
109 }
110 if (next.empty()) // a cycle: take the first unassigned node
111 for (std::size_t v = 0; v < n; ++v)
112 if (!assigned[v]) {
113 next.push_back(v);
114 break;
115 }
116 if (!next.empty()) {
117 for (std::size_t v : next) {
118 assigned[v] = true;
119 ++nassigned;
120 }
121 layers.push_back(next);
122 }
123 ++cur;
124 if (cur > n) break; // the JAR's own guard
125 }
126 if (layers.size() > 1) {
127 std::vector<std::size_t>& last = layers.back();
128 for (std::size_t i = 0; i + 1 < layers.size(); ++i) {
129 std::vector<std::size_t>& layer = layers[i];
130 for (std::size_t k = 0; k < layer.size();) {
131 const std::size_t v = layer[k];
132 if (g.nodes[v].type == lang::NodeType::Sink) {
133 layer.erase(layer.begin() + static_cast<long>(k));
134 if (std::find(last.begin(), last.end(), v) == last.end()) last.push_back(v);
135 } else {
136 ++k;
137 }
138 }
139 }
140 std::vector<std::vector<std::size_t>> kept;
141 for (std::size_t i = 0; i < layers.size(); ++i)
142 if (!layers[i].empty()) kept.push_back(layers[i]);
143 layers.swap(kept);
144 }
145
146 // Step 2: crossing reduction
147 for (int pass = 0; pass < 4; ++pass) {
148 for (std::size_t i = 1; i < layers.size(); ++i)
149 tikz_detail::reorder_by_barycenter(layers[i], pred, layers[i - 1], n);
150 for (std::size_t i = layers.size() >= 2 ? layers.size() - 1 : 0; i-- > 0;)
151 tikz_detail::reorder_by_barycenter(layers[i], succ, layers[i + 1], n);
152 }
153
154 // Step 3: coordinates
155 for (std::size_t li = 0; li < layers.size(); ++li) {
156 const std::vector<std::size_t>& layer = layers[li];
157 const double x = static_cast<double>(li) * opt.layer_spacing;
158 const double total = static_cast<double>(layer.size() - 1) * opt.node_spacing;
159 const double start = total / 2.0;
160 for (std::size_t k = 0; k < layer.size(); ++k) {
161 L.x[layer[k]] = x;
162 L.y[layer[k]] = start - static_cast<double>(k) * opt.node_spacing;
163 }
164 }
165 return L;
166}
167
168} // namespace io
169} // namespace line
170
171#endif // LINE_IO_TIKZ_TIKZ_LAYOUT_H
void reorder_by_barycenter(std::vector< std::size_t > &layer, const std::vector< std::vector< std::size_t > > &connections, const std::vector< std::size_t > &reference, std::size_t nnodes)
reorderLayerByBarycenter: sort a layer by the mean position of its neighbours in the reference layer.
Definition tikz_layout.h:40
TikzLayout tikz_layout(const TikzGraph &g, const TikzOptions &opt)
TikZLayoutEngine.computeLayout.
Definition tikz_layout.h:65
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Nodes in model order plus sn.connmatrix over them.
Definition tikz_graph.h:54
std::vector< std::vector< bool > > conn
conn[i][j]: node i is linked to node j, 0-based
Definition tikz_graph.h:57
std::vector< TikzNode > nodes
Definition tikz_graph.h:56
Node positions in cm, indexed like TikzGraph::nodes, and the layers that produced them.
Definition tikz_layout.h:32
std::vector< double > y
Definition tikz_layout.h:33
std::vector< std::vector< std::size_t > > layers
0-based node indices, in drawing order
Definition tikz_layout.h:34
std::vector< double > x
Definition tikz_layout.h:33
Layout and rendering options of the network TikZ exporter (TikZOptions).
The part of a network the TikZ exporter draws, and the text helpers the JAR's exporter uses to write ...
Port of jline.io.tikz.TikZOptions: the knobs of the network TikZ exporter.