Fix for T53599: OpenGL render with transparent background
[blender.git] / source / blender / depsgraph / intern / debug / deg_debug_graphviz.cc
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2014 Blender Foundation.
19  * All rights reserved.
20  *
21  * Original Author: Lukas Toenne
22  * Contributor(s): None Yet
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/depsgraph/intern/debug/deg_debug_graphviz.cc
28  *  \ingroup depsgraph
29  *
30  * Implementation of tools for debugging the depsgraph
31  */
32
33 #include "BLI_utildefines.h"
34 #include "BLI_ghash.h"
35
36 extern "C" {
37 #include "DNA_listBase.h"
38 }  /* extern "C" */
39
40 #include "DEG_depsgraph.h"
41 #include "DEG_depsgraph_debug.h"
42
43 #include "intern/depsgraph_intern.h"
44 #include "intern/nodes/deg_node_id.h"
45 #include "intern/nodes/deg_node_time.h"
46
47 #include "util/deg_util_foreach.h"
48
49 /* ****************** */
50 /* Graphviz Debugging */
51
52 namespace DEG {
53
54 #define NL "\r\n"
55
56 /* Only one should be enabled, defines whether graphviz nodes
57  * get colored by individual types or classes.
58  */
59 #define COLOR_SCHEME_NODE_CLASS 1
60 //#define COLOR_SCHEME_NODE_TYPE  2
61
62 static const char *deg_debug_graphviz_fontname = "helvetica";
63 static float deg_debug_graphviz_graph_label_size = 20.0f;
64 static float deg_debug_graphviz_node_label_size = 14.0f;
65 static const int deg_debug_max_colors = 12;
66 #ifdef COLOR_SCHEME_NODE_TYPE
67 static const char *deg_debug_colors[] = {
68     "#a6cee3", "#1f78b4", "#b2df8a",
69     "#33a02c", "#fb9a99", "#e31a1c",
70     "#fdbf6f", "#ff7f00", "#cab2d6",
71     "#6a3d9a", "#ffff99", "#b15928",
72 };
73 #endif
74 static const char *deg_debug_colors_light[] = {
75     "#8dd3c7", "#ffffb3", "#bebada",
76     "#fb8072", "#80b1d3", "#fdb462",
77     "#b3de69", "#fccde5", "#d9d9d9",
78     "#bc80bd", "#ccebc5", "#ffed6f",
79 };
80
81 #ifdef COLOR_SCHEME_NODE_TYPE
82 static const int deg_debug_node_type_color_map[][2] = {
83     {DEG_NODE_TYPE_TIMESOURCE,   0},
84     {DEG_NODE_TYPE_ID_REF,       1},
85
86     /* Outer Types */
87     {DEG_NODE_TYPE_PARAMETERS,        2},
88     {DEG_NODE_TYPE_PROXY,             3},
89     {DEG_NODE_TYPE_ANIMATION,         4},
90     {DEG_NODE_TYPE_TRANSFORM,         5},
91     {DEG_NODE_TYPE_GEOMETRY,          6},
92     {DEG_NODE_TYPE_SEQUENCER,         7},
93     {DEG_NODE_TYPE_SHADING,           8},
94     {DEG_NODE_TYPE_SHADING_PARAMETERS, 9},
95     {DEG_NODE_TYPE_CACHE,             10},
96     {DEG_NODE_TYPE_LAYER_COLLECTIONS, 11},
97     {DEG_NODE_TYPE_COPY_ON_WRITE,     12},
98     {-1,                              0}
99 };
100 #endif
101
102 static int deg_debug_node_color_index(const DepsNode *node)
103 {
104 #ifdef COLOR_SCHEME_NODE_CLASS
105         /* Some special types. */
106         switch (node->type) {
107                 case DEG_NODE_TYPE_ID_REF:
108                         return 5;
109                 case DEG_NODE_TYPE_OPERATION:
110                 {
111                         OperationDepsNode *op_node = (OperationDepsNode *)node;
112                         if (op_node->is_noop())
113                                 return 8;
114                         break;
115                 }
116
117                 default:
118                         break;
119         }
120         /* Do others based on class. */
121         switch (node->tclass) {
122                 case DEG_NODE_CLASS_OPERATION:
123                         return 4;
124                 case DEG_NODE_CLASS_COMPONENT:
125                         return 1;
126                 default:
127                         return 9;
128         }
129 #endif
130
131 #ifdef COLOR_SCHEME_NODE_TYPE
132         const int (*pair)[2];
133         for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) {
134                 if ((*pair)[0] == node->type) {
135                         return (*pair)[1];
136                 }
137         }
138         return -1;
139 #endif
140 }
141
142 struct DebugContext {
143         FILE *file;
144         bool show_tags;
145         bool show_eval_priority;
146 };
147
148 static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3);
149 static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
150 {
151         va_list args;
152         va_start(args, fmt);
153         vfprintf(ctx.file, fmt, args);
154         va_end(args);
155 }
156
157 static void deg_debug_graphviz_legend_color(const DebugContext &ctx,
158                                             const char *name,
159                                             const char *color)
160 {
161         deg_debug_fprintf(ctx, "<TR>");
162         deg_debug_fprintf(ctx, "<TD>%s</TD>", name);
163         deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color);
164         deg_debug_fprintf(ctx, "</TR>" NL);
165 }
166
167 static void deg_debug_graphviz_legend(const DebugContext &ctx)
168 {
169         deg_debug_fprintf(ctx, "{" NL);
170         deg_debug_fprintf(ctx, "rank = sink;" NL);
171         deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL);
172         deg_debug_fprintf(ctx, "  <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL);
173         deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL);
174
175 #ifdef COLOR_SCHEME_NODE_CLASS
176         const char **colors = deg_debug_colors_light;
177         deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]);
178         deg_debug_graphviz_legend_color(ctx, "Component", colors[1]);
179         deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]);
180         deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]);
181 #endif
182
183 #ifdef COLOR_SCHEME_NODE_TYPE
184         const int (*pair)[2];
185         for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) {
186                 DepsNodeFactory *nti = deg_type_get_factory((eDepsNode_Type)(*pair)[0]);
187                 deg_debug_graphviz_legend_color(ctx,
188                                                 nti->tname().c_str(),
189                                                 deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]);
190         }
191 #endif
192
193         deg_debug_fprintf(ctx, "</TABLE>" NL);
194         deg_debug_fprintf(ctx, ">" NL);
195         deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname);
196         deg_debug_fprintf(ctx, "];" NL);
197         deg_debug_fprintf(ctx, "}" NL);
198 }
199
200 static void deg_debug_graphviz_node_color(const DebugContext &ctx,
201                                           const DepsNode *node)
202 {
203         const char *color_default = "black";
204         const char *color_modified = "orangered4";
205         const char *color_update = "dodgerblue3";
206         const char *color = color_default;
207         if (ctx.show_tags) {
208                 if (node->tclass == DEG_NODE_CLASS_OPERATION) {
209                         OperationDepsNode *op_node = (OperationDepsNode *)node;
210                         if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
211                                 color = color_modified;
212                         }
213                         else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
214                                 color = color_update;
215                         }
216                 }
217         }
218         deg_debug_fprintf(ctx, "\"%s\"", color);
219 }
220
221 static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx,
222                                              const DepsNode *node)
223 {
224         float penwidth_default = 1.0f;
225         float penwidth_modified = 4.0f;
226         float penwidth_update = 4.0f;
227         float penwidth = penwidth_default;
228         if (ctx.show_tags) {
229                 if (node->tclass == DEG_NODE_CLASS_OPERATION) {
230                         OperationDepsNode *op_node = (OperationDepsNode *)node;
231                         if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
232                                 penwidth = penwidth_modified;
233                         }
234                         else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
235                                 penwidth = penwidth_update;
236                         }
237                 }
238         }
239         deg_debug_fprintf(ctx, "\"%f\"", penwidth);
240 }
241
242 static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx,
243                                               const DepsNode *node)
244 {
245         const char *defaultcolor = "gainsboro";
246         int color_index = deg_debug_node_color_index(node);
247         const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors];
248         deg_debug_fprintf(ctx, "\"%s\"", fillcolor);
249 }
250
251 static void deg_debug_graphviz_relation_color(const DebugContext &ctx,
252                                               const DepsRelation *rel)
253 {
254         const char *color_default = "black";
255         const char *color_error = "red4";
256         const char *color = color_default;
257         if (rel->flag & DEPSREL_FLAG_CYCLIC) {
258                 color = color_error;
259         }
260         deg_debug_fprintf(ctx, "%s", color);
261 }
262
263 static void deg_debug_graphviz_node_style(const DebugContext &ctx, const DepsNode *node)
264 {
265         const char *base_style = "filled"; /* default style */
266         if (ctx.show_tags) {
267                 if (node->tclass == DEG_NODE_CLASS_OPERATION) {
268                         OperationDepsNode *op_node = (OperationDepsNode *)node;
269                         if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) {
270                                 base_style = "striped";
271                         }
272                 }
273         }
274         switch (node->tclass) {
275                 case DEG_NODE_CLASS_GENERIC:
276                         deg_debug_fprintf(ctx, "\"%s\"", base_style);
277                         break;
278                 case DEG_NODE_CLASS_COMPONENT:
279                         deg_debug_fprintf(ctx, "\"%s\"", base_style);
280                         break;
281                 case DEG_NODE_CLASS_OPERATION:
282                         deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style);
283                         break;
284         }
285 }
286
287 static void deg_debug_graphviz_node_single(const DebugContext &ctx,
288                                            const DepsNode *node)
289 {
290         const char *shape = "box";
291         string name = node->identifier();
292         float priority = -1.0f;
293         if (ctx.show_eval_priority && node->tclass == DEG_NODE_CLASS_OPERATION) {
294                 priority = ((OperationDepsNode *)node)->eval_priority;
295         }
296         deg_debug_fprintf(ctx, "// %s\n", name.c_str());
297         deg_debug_fprintf(ctx, "\"node_%p\"", node);
298         deg_debug_fprintf(ctx, "[");
299 //      deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name);
300         if (priority >= 0.0f) {
301                 deg_debug_fprintf(ctx, "label=<%s<BR/>(<I>%.2f</I>)>",
302                                  name.c_str(),
303                                  priority);
304         }
305         else {
306                 deg_debug_fprintf(ctx, "label=<%s>", name.c_str());
307         }
308         deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname);
309         deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size);
310         deg_debug_fprintf(ctx, ",shape=%s", shape);
311         deg_debug_fprintf(ctx, ",style="); deg_debug_graphviz_node_style(ctx, node);
312         deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_node_color(ctx, node);
313         deg_debug_fprintf(ctx, ",fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node);
314         deg_debug_fprintf(ctx, ",penwidth="); deg_debug_graphviz_node_penwidth(ctx, node);
315         deg_debug_fprintf(ctx, "];" NL);
316         deg_debug_fprintf(ctx, NL);
317 }
318
319 static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx,
320                                                   const DepsNode *node)
321 {
322         string name = node->identifier();
323         deg_debug_fprintf(ctx, "// %s\n", name.c_str());
324         deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node);
325 //      deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name);
326         deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str());
327         deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname);
328         deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size);
329         deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16);
330         deg_debug_fprintf(ctx, "style="); deg_debug_graphviz_node_style(ctx, node); deg_debug_fprintf(ctx, ";" NL);
331         deg_debug_fprintf(ctx, "color="); deg_debug_graphviz_node_color(ctx, node); deg_debug_fprintf(ctx, ";" NL);
332         deg_debug_fprintf(ctx, "fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); deg_debug_fprintf(ctx, ";" NL);
333         deg_debug_fprintf(ctx, "penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); deg_debug_fprintf(ctx, ";" NL);
334         /* dummy node, so we can add edges between clusters */
335         deg_debug_fprintf(ctx, "\"node_%p\"", node);
336         deg_debug_fprintf(ctx, "[");
337         deg_debug_fprintf(ctx, "shape=%s", "point");
338         deg_debug_fprintf(ctx, ",style=%s", "invis");
339         deg_debug_fprintf(ctx, "];" NL);
340         deg_debug_fprintf(ctx, NL);
341 }
342
343 static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx)
344 {
345         deg_debug_fprintf(ctx, "}" NL);
346         deg_debug_fprintf(ctx, NL);
347 }
348
349 static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx,
350                                            const Depsgraph *graph);
351 static void deg_debug_graphviz_graph_relations(const DebugContext &ctx,
352                                                const Depsgraph *graph);
353
354 static void deg_debug_graphviz_node(const DebugContext &ctx,
355                                     const DepsNode *node)
356 {
357         switch (node->type) {
358                 case DEG_NODE_TYPE_ID_REF:
359                 {
360                         const IDDepsNode *id_node = (const IDDepsNode *)node;
361                         if (BLI_ghash_size(id_node->components) == 0) {
362                                 deg_debug_graphviz_node_single(ctx, node);
363                         }
364                         else {
365                                 deg_debug_graphviz_node_cluster_begin(ctx, node);
366                                 GHASH_FOREACH_BEGIN(const ComponentDepsNode *, comp, id_node->components)
367                                 {
368                                         deg_debug_graphviz_node(ctx, comp);
369                                 }
370                                 GHASH_FOREACH_END();
371                                 deg_debug_graphviz_node_cluster_end(ctx);
372                         }
373                         break;
374                 }
375                 case DEG_NODE_TYPE_PARAMETERS:
376                 case DEG_NODE_TYPE_ANIMATION:
377                 case DEG_NODE_TYPE_TRANSFORM:
378                 case DEG_NODE_TYPE_PROXY:
379                 case DEG_NODE_TYPE_GEOMETRY:
380                 case DEG_NODE_TYPE_SEQUENCER:
381                 case DEG_NODE_TYPE_EVAL_POSE:
382                 case DEG_NODE_TYPE_BONE:
383                 case DEG_NODE_TYPE_SHADING:
384                 case DEG_NODE_TYPE_SHADING_PARAMETERS:
385                 case DEG_NODE_TYPE_CACHE:
386                 case DEG_NODE_TYPE_LAYER_COLLECTIONS:
387                 case DEG_NODE_TYPE_EVAL_PARTICLES:
388                 case DEG_NODE_TYPE_COPY_ON_WRITE:
389                 case DEG_NODE_TYPE_BATCH_CACHE:
390                 {
391                         ComponentDepsNode *comp_node = (ComponentDepsNode *)node;
392                         if (!comp_node->operations.empty()) {
393                                 deg_debug_graphviz_node_cluster_begin(ctx, node);
394                                 foreach (DepsNode *op_node, comp_node->operations) {
395                                         deg_debug_graphviz_node(ctx, op_node);
396                                 }
397                                 deg_debug_graphviz_node_cluster_end(ctx);
398                         }
399                         else {
400                                 deg_debug_graphviz_node_single(ctx, node);
401                         }
402                         break;
403                 }
404                 case DEG_NODE_TYPE_UNDEFINED:
405                 case DEG_NODE_TYPE_TIMESOURCE:
406                 case DEG_NODE_TYPE_OPERATION:
407                         deg_debug_graphviz_node_single(ctx, node);
408                         break;
409                 case NUM_DEG_NODE_TYPES:
410                         break;
411         }
412 }
413
414 static bool deg_debug_graphviz_is_cluster(const DepsNode *node)
415 {
416         switch (node->type) {
417                 case DEG_NODE_TYPE_ID_REF:
418                 {
419                         const IDDepsNode *id_node = (const IDDepsNode *)node;
420                         return BLI_ghash_size(id_node->components) > 0;
421                 }
422                 case DEG_NODE_TYPE_PARAMETERS:
423                 case DEG_NODE_TYPE_ANIMATION:
424                 case DEG_NODE_TYPE_TRANSFORM:
425                 case DEG_NODE_TYPE_PROXY:
426                 case DEG_NODE_TYPE_GEOMETRY:
427                 case DEG_NODE_TYPE_SEQUENCER:
428                 case DEG_NODE_TYPE_EVAL_POSE:
429                 case DEG_NODE_TYPE_BONE:
430                 {
431                         ComponentDepsNode *comp_node = (ComponentDepsNode *)node;
432                         return !comp_node->operations.empty();
433                 }
434                 default:
435                         return false;
436         }
437 }
438
439 static bool deg_debug_graphviz_is_owner(const DepsNode *node,
440                                         const DepsNode *other)
441 {
442         switch (node->tclass) {
443                 case DEG_NODE_CLASS_COMPONENT:
444                 {
445                         ComponentDepsNode *comp_node = (ComponentDepsNode *)node;
446                         if (comp_node->owner == other)
447                                 return true;
448                         break;
449                 }
450                 case DEG_NODE_CLASS_OPERATION:
451                 {
452                         OperationDepsNode *op_node = (OperationDepsNode *)node;
453                         if (op_node->owner == other)
454                                 return true;
455                         else if (op_node->owner->owner == other)
456                                 return true;
457                         break;
458                 }
459                 default: break;
460         }
461         return false;
462 }
463
464 static void deg_debug_graphviz_node_relations(const DebugContext &ctx,
465                                               const DepsNode *node)
466 {
467         foreach (DepsRelation *rel, node->inlinks) {
468                 float penwidth = 2.0f;
469
470                 const DepsNode *tail = rel->to; /* same as node */
471                 const DepsNode *head = rel->from;
472                 deg_debug_fprintf(ctx, "// %s -> %s\n",
473                                  head->identifier().c_str(),
474                                  tail->identifier().c_str());
475                 deg_debug_fprintf(ctx, "\"node_%p\"", head);
476                 deg_debug_fprintf(ctx, " -> ");
477                 deg_debug_fprintf(ctx, "\"node_%p\"", tail);
478
479                 deg_debug_fprintf(ctx, "[");
480                 /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */
481                 deg_debug_fprintf(ctx, "id=\"%s\"", rel->name);
482                 deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_relation_color(ctx, rel);
483                 deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth);
484                 /* NOTE: edge from node to own cluster is not possible and gives graphviz
485                  * warning, avoid this here by just linking directly to the invisible
486                  * placeholder node
487                  */
488                 if (deg_debug_graphviz_is_cluster(tail) && !deg_debug_graphviz_is_owner(head, tail)) {
489                         deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail);
490                 }
491                 if (deg_debug_graphviz_is_cluster(head) && !deg_debug_graphviz_is_owner(tail, head)) {
492                         deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head);
493                 }
494                 deg_debug_fprintf(ctx, "];" NL);
495                 deg_debug_fprintf(ctx, NL);
496         }
497 }
498
499 static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx,
500                                            const Depsgraph *graph)
501 {
502         foreach (DepsNode *node, graph->id_nodes) {
503                 deg_debug_graphviz_node(ctx, node);
504         }
505         TimeSourceDepsNode *time_source = graph->find_time_source();
506         if (time_source != NULL) {
507                 deg_debug_graphviz_node(ctx, time_source);
508         }
509 }
510
511 static void deg_debug_graphviz_graph_relations(const DebugContext &ctx,
512                                                const Depsgraph *graph)
513 {
514         foreach (IDDepsNode *id_node, graph->id_nodes) {
515                 GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components)
516                 {
517                         foreach (OperationDepsNode *op_node, comp_node->operations) {
518                                 deg_debug_graphviz_node_relations(ctx, op_node);
519                         }
520                 }
521                 GHASH_FOREACH_END();
522         }
523
524         TimeSourceDepsNode *time_source = graph->find_time_source();
525         if (time_source != NULL) {
526                 deg_debug_graphviz_node_relations(ctx, time_source);
527         }
528 }
529
530 }  // namespace DEG
531
532 void DEG_debug_graphviz(const Depsgraph *graph, FILE *f, const char *label, bool show_eval)
533 {
534         if (!graph) {
535                 return;
536         }
537
538         const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
539
540         DEG::DebugContext ctx;
541         ctx.file = f;
542         ctx.show_tags = show_eval;
543         ctx.show_eval_priority = show_eval;
544
545         DEG::deg_debug_fprintf(ctx, "digraph depgraph {" NL);
546         DEG::deg_debug_fprintf(ctx, "rankdir=LR;" NL);
547         DEG::deg_debug_fprintf(ctx, "graph [");
548         DEG::deg_debug_fprintf(ctx, "compound=true");
549         DEG::deg_debug_fprintf(ctx, ",labelloc=\"t\"");
550         DEG::deg_debug_fprintf(ctx, ",fontsize=%f", DEG::deg_debug_graphviz_graph_label_size);
551         DEG::deg_debug_fprintf(ctx, ",fontname=\"%s\"", DEG::deg_debug_graphviz_fontname);
552         DEG::deg_debug_fprintf(ctx, ",label=\"%s\"", label);
553         DEG::deg_debug_fprintf(ctx, ",splines=ortho");
554         DEG::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato
555         DEG::deg_debug_fprintf(ctx, "];" NL);
556
557         DEG::deg_debug_graphviz_graph_nodes(ctx, deg_graph);
558         DEG::deg_debug_graphviz_graph_relations(ctx, deg_graph);
559
560         DEG::deg_debug_graphviz_legend(ctx);
561
562         DEG::deg_debug_fprintf(ctx, "}" NL);
563 }
564
565 #undef NL