Graphviz 16.1.1~dev.20260926.2046
Loading...
Searching...
No Matches
flat.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <assert.h>
14#include <dotgen/dot.h>
15#include <stdbool.h>
16#include <stddef.h>
17#include <util/alloc.h>
18#include <util/gv_math.h>
19
20static node_t *make_vn_slot(graph_t * g, int r, int pos)
21{
22 int i;
23 node_t *n;
24
25 assert(GD_rank(g)[r].av == GD_rank(g)[r].v);
26 GD_rank(g)[r].av = gv_recalloc(GD_rank(g)[r].av, GD_rank(g)[r].n + 1,
27 GD_rank(g)[r].n + 2, sizeof(node_t *));
28 GD_rank(g)[r].v = GD_rank(g)[r].av;
29 node_t **const v = GD_rank(g)[r].v;
30 for (i = GD_rank(g)[r].n; i > pos; i--) {
31 v[i] = v[i - 1];
32 ND_order(v[i])++;
33 }
34 n = v[pos] = virtual_node(g);
35 ND_order(n) = pos;
36 ND_rank(n) = r;
37 v[++GD_rank(g)[r].n] = NULL;
38 return v[pos];
39}
40
41#define HLB 0 /* hard left bound */
42#define HRB 1 /* hard right bound */
43#define SLB 2 /* soft left bound */
44#define SRB 3 /* soft right bound */
45
46static void findlr(node_t * u, node_t * v, int *lp, int *rp)
47{
48 int l, r;
49 l = ND_order(u);
50 r = ND_order(v);
51 if (l > r) {
52 SWAP(&l, &r);
53 }
54 *lp = l;
55 *rp = r;
56}
57
58static void setbounds(node_t * v, int *bounds, int lpos, int rpos)
59{
60 int i, l, r, ord;
61 edge_t *f;
62
63 if (ND_node_type(v) == VIRTUAL) {
64 ord = ND_order(v);
65 if (ND_in(v).size == 0) { /* flat */
66 assert(ND_out(v).size == 2);
67 findlr(aghead(ND_out(v).list[0]), aghead(ND_out(v).list[1]), &l,
68 &r);
69 /* the other flat edge could be to the left or right */
70 if (r <= lpos)
71 bounds[SLB] = bounds[HLB] = ord;
72 else if (l >= rpos)
73 bounds[SRB] = bounds[HRB] = ord;
74 /* could be spanning this one */
75 else if (l < lpos && r > rpos); // ignore
76 /* must have intersecting ranges */
77 else {
78 if (l < lpos || (l == lpos && r < rpos))
79 bounds[SLB] = ord;
80 if (r > rpos || (r == rpos && l > lpos))
81 bounds[SRB] = ord;
82 }
83 } else { /* forward */
84 bool onleft, onright;
85 onleft = onright = false;
86 for (i = 0; (f = ND_out(v).list[i]); i++) {
87 if (ND_order(aghead(f)) <= lpos) {
88 onleft = true;
89 continue;
90 }
91 if (ND_order(aghead(f)) >= rpos) {
92 onright = true;
93 continue;
94 }
95 }
96 if (onleft && !onright)
97 bounds[HLB] = ord + 1;
98 if (onright && !onleft)
99 bounds[HRB] = ord - 1;
100 }
101 }
102}
103
104static int flat_limits(graph_t * g, edge_t * e)
105{
106 int lnode, rnode, bounds[4], lpos, rpos, pos;
107
108 const int r = ND_rank(agtail(e)) - 1;
109 node_t **const rank = GD_rank(g)[r].v;
110 lnode = 0;
111 rnode = GD_rank(g)[r].n - 1;
112 bounds[HLB] = bounds[SLB] = lnode - 1;
113 bounds[HRB] = bounds[SRB] = rnode + 1;
114 findlr(agtail(e), aghead(e), &lpos, &rpos);
115 while (lnode <= rnode) {
116 setbounds(rank[lnode], bounds, lpos, rpos);
117 if (lnode != rnode)
118 setbounds(rank[rnode], bounds, lpos, rpos);
119 lnode++;
120 rnode--;
121 if (bounds[HRB] - bounds[HLB] <= 1)
122 break;
123 }
124 if (bounds[HLB] <= bounds[HRB])
125 pos = (bounds[HLB] + bounds[HRB] + 1) / 2;
126 else
127 pos = (bounds[SLB] + bounds[SRB] + 1) / 2;
128 return pos;
129}
130
131/* Create virtual node representing edge label between
132 * actual ends of edge e.
133 * This node is characterized by being virtual and having a non-NULL
134 * ND_alg pointing to e.
135 */
136static void
138{
139 double ypos, h2;
140 graph_t *g;
141 node_t *n, *vn;
142 edge_t *ve;
143 pointf dimen;
144
145 if (ED_label(e) == NULL)
146 return;
147 g = dot_root(agtail(e));
148 const int r = ND_rank(agtail(e));
149
150 const int place = flat_limits(g, e);
151 /* grab ypos = LL.y of label box before make_vn_slot() */
152 if ((n = GD_rank(g)[r - 1].v[0]))
153 ypos = ND_coord(n).y - GD_rank(g)[r - 1].ht1;
154 else {
155 n = GD_rank(g)[r].v[0];
156 ypos = ND_coord(n).y + GD_rank(g)[r].ht2 + GD_ranksep(g);
157 }
158 vn = make_vn_slot(g, r - 1, place);
159 dimen = ED_label(e)->dimen;
160 if (GD_flip(g)) {
161 SWAP(&dimen.x, &dimen.y);
162 }
163 ND_ht(vn) = dimen.y;
164 h2 = ND_ht(vn) / 2;
165 ND_lw(vn) = ND_rw(vn) = dimen.x / 2;
166 ND_label(vn) = ED_label(e);
167 ND_coord(vn).y = ypos + h2;
168 ve = virtual_edge(vn, agtail(e), e); /* was NULL? */
169 ED_tail_port(ve).p.x = -ND_lw(vn);
170 ED_head_port(ve).p.x = ND_rw(agtail(e));
172 ve = virtual_edge(vn, aghead(e), e);
173 ED_tail_port(ve).p.x = ND_rw(vn);
174 ED_head_port(ve).p.x = ND_lw(aghead(e));
176 /* another assumed symmetry of ht1/ht2 of a label node */
177 if (GD_rank(g)[r - 1].ht1 < h2)
178 GD_rank(g)[r - 1].ht1 = h2;
179 if (GD_rank(g)[r - 1].ht2 < h2)
180 GD_rank(g)[r - 1].ht2 = h2;
181 ND_alg(vn) = e;
182}
183
184static void abomination(graph_t * g)
185{
186 int r;
187
188 assert(GD_minrank(g) == 0);
189 /* 3 = one for new rank, one for sentinel, one for off-by-one */
190 r = GD_maxrank(g) + 3;
191 rank_t *rptr = gv_recalloc(GD_rank(g), GD_maxrank(g) + 1, r,
192 sizeof(rank_t));
193 GD_rank(g) = rptr + 1;
194 for (r = GD_maxrank(g); r >= 0; r--)
195 GD_rank(g)[r] = GD_rank(g)[r - 1];
196 GD_rank(g)[r].n = GD_rank(g)[r].an = 0;
197 GD_rank(g)[r].v = GD_rank(g)[r].av = gv_calloc(2, sizeof(node_t *));
198 GD_rank(g)[r].flat = NULL;
199 GD_rank(g)[r].ht1 = GD_rank(g)[r].ht2 = 1;
200 GD_rank(g)[r].pht1 = GD_rank(g)[r].pht2 = 1;
201 GD_minrank(g)--;
202}
203
204/* Check if tn and hn are adjacent.
205 * If so, set adjacent bit on all related edges.
206 * Assume e is flat.
207 */
208static void
210{
211 node_t* tn = agtail(e);
212 node_t* hn = aghead(e);
213 int i, lo, hi;
214 node_t* n;
215 rank_t *rank;
216
217 if (ND_order(tn) < ND_order(hn)) {
218 lo = ND_order(tn);
219 hi = ND_order(hn);
220 }
221 else {
222 lo = ND_order(hn);
223 hi = ND_order(tn);
224 }
225 rank = &GD_rank(dot_root(tn))[ND_rank(tn)];
226 for (i = lo + 1; i < hi; i++) {
227 n = rank->v[i];
228 if ((ND_node_type(n) == VIRTUAL && ND_label(n)) ||
229 ND_node_type(n) == NORMAL)
230 break;
231 }
232 if (i == hi) { /* adjacent edge */
233 do {
234 ED_adjacent(e) = 1;
235 e = ED_to_virt(e);
236 } while (e);
237 }
238}
239
240/* Process flat edges.
241 * First, mark flat edges as having adjacent endpoints or not.
242 *
243 * Second, if there are edge labels, nodes are placed on ranks 0,2,4,...
244 * If we have a labeled flat edge on rank 0, add a rank -1.
245 *
246 * Finally, create label information. Add a virtual label node in the
247 * previous rank for each labeled, non-adjacent flat edge. If this is
248 * done for any edge, return true, so that main code will reset y coords.
249 * For labeled adjacent flat edges, store label width in representative edge.
250 * FIX: We should take into account any extra height needed for the latter
251 * labels.
252 *
253 * We leave equivalent flat edges in ND_other. Their ED_virt field should
254 * still point to the class representative.
255 */
256int
258{
259 edge_t *e;
260
261 for (node_t *n = GD_nlist(g); n; n = ND_next(n)) {
262 if (ND_flat_out(n).list) {
263 for (size_t j = 0; (e = ND_flat_out(n).list[j]); j++) {
265 }
266 }
267 for (size_t j = 0; j < ND_other(n).size; j++) {
268 e = ND_other(n).list[j];
269 if (ND_rank(aghead(e)) == ND_rank(agtail(e)))
271 }
272 }
273
274 if (GD_rank(g)[0].flat || GD_n_cluster(g) > 0) {
275 bool found = false;
276 node_t *n;
277 for (size_t i = 0; !found && (n = GD_rank(g)[0].v[i]); i++) {
278 for (size_t j = 0; !found && (e = ND_flat_in(n).list[j]); j++) {
279 if (ED_label(e) && !ED_adjacent(e)) {
280 abomination(g);
281 found = true;
282 }
283 }
284 for (size_t j = 0; !found && j < ND_other(n).size; j++) {
285 e = ND_other(n).list[j];
286 if (ED_label(e) && !ED_adjacent(e)) {
287 abomination(g);
288 found = true;
289 }
290 }
291 }
292 }
293
295 bool reset = false;
296 for (node_t *n = GD_nlist(g); n; n = ND_next(n)) {
297 /* if n is the tail of any flat edge, one will be in flat_out */
298 if (ND_flat_out(n).list) {
299 for (size_t i = 0; (e = ND_flat_out(n).list[i]); i++) {
300 if (ED_label(e)) {
301 if (ED_adjacent(e)) {
302 ED_dist(e) = GD_flip(g) ? ED_label(e)->dimen.y : ED_label(e)->dimen.x;
303 }
304 else {
305 reset = true;
306 flat_node(e);
307 }
308 }
309 }
310 /* look for other flat edges with labels */
311 for (size_t j = 0; j < ND_other(n).size; j++) {
312 e = ND_other(n).list[j];
313 if (ND_rank(agtail(e)) != ND_rank(aghead(e))) continue;
314 if (agtail(e) == aghead(e)) continue; /* skip loops */
315 edge_t *le = e;
316 while (ED_to_virt(le)) le = ED_to_virt(le);
318 if (ED_label(e)) {
319 if (ED_adjacent(e)) {
320 const double lw = GD_flip(g) ? ED_label(e)->dimen.y : ED_label(e)->dimen.x;
321 ED_dist(le) = MAX(lw,ED_dist(le));
322 }
323 else {
324 reset = true;
325 flat_node(e);
326 }
327 }
328 }
329 }
330 }
331 if (reset) {
334 }
335 return reset;
336}
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define MAX(a, b)
Definition arith.h:33
#define NORMAL
Definition const.h:24
#define FLATORDER
Definition const.h:28
#define VIRTUAL
Definition const.h:25
Agraph_t * dot_root(void *p)
Definition dotinit.c:483
Agedge_t * virtual_edge(Agnode_t *, Agnode_t *, Agedge_t *)
Definition fastgr.c:170
void checkLabelOrder(graph_t *g)
Definition mincross.c:297
void rec_reset_vlists(Agraph_t *)
Definition mincross.c:930
void rec_save_vlists(Agraph_t *)
Definition mincross.c:922
Agnode_t * virtual_node(Agraph_t *)
Definition fastgr.c:200
#define le
Definition edges.h:29
static void flat_node(edge_t *e)
Definition flat.c:137
static int flat_limits(graph_t *g, edge_t *e)
Definition flat.c:104
#define HLB
Definition flat.c:41
int flat_edges(graph_t *g)
Definition flat.c:257
static void checkFlatAdjacent(edge_t *e)
Definition flat.c:209
static void abomination(graph_t *g)
Definition flat.c:184
#define HRB
Definition flat.c:42
static node_t * make_vn_slot(graph_t *g, int r, int pos)
Definition flat.c:20
#define SRB
Definition flat.c:44
#define SLB
Definition flat.c:43
static void findlr(node_t *u, node_t *v, int *lp, int *rp)
Definition flat.c:46
static void setbounds(node_t *v, int *bounds, int lpos, int rpos)
Definition flat.c:58
node NULL
Definition grammar.y:181
#define ED_dist(e)
Definition types.h:602
#define agtail(e)
Definition cgraph.h:982
#define ED_edge_type(e)
Definition types.h:582
#define ED_adjacent(e)
Definition types.h:584
#define aghead(e)
Definition cgraph.h:983
#define ED_head_port(e)
Definition types.h:588
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
#define ED_to_virt(e)
Definition types.h:599
#define GD_minrank(g)
Definition types.h:384
#define GD_maxrank(g)
Definition types.h:382
#define GD_rank(g)
Definition types.h:395
#define GD_nlist(g)
Definition types.h:393
#define GD_n_cluster(g)
Definition types.h:389
#define GD_flip(g)
Definition types.h:378
#define GD_ranksep(g)
Definition types.h:397
#define ND_rank(n)
Definition types.h:523
#define ND_ht(n)
Definition types.h:500
#define ND_next(n)
Definition types.h:510
#define ND_other(n)
Definition types.h:514
#define ND_label(n)
Definition types.h:502
#define ND_alg(n)
Definition types.h:484
#define ND_flat_out(n)
Definition types.h:493
#define ND_rw(n)
Definition types.h:525
#define ND_node_type(n)
Definition types.h:511
#define ND_lw(n)
Definition types.h:506
#define ND_order(n)
Definition types.h:513
#define ND_flat_in(n)
Definition types.h:492
#define ND_coord(n)
Definition types.h:490
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:137
int rank(graph_t *g, int balance, int maxiter)
Definition ns.c:1029
void reset(sgraph *G)
Definition sgraph.c:30
graph or subgraph
Definition cgraph.h:424
double x
Definition geom.h:29
double y
Definition geom.h:29
int n
Definition types.h:201