Depsgraph: Comb code to a better state all over
[blender.git] / source / blender / depsgraph / intern / debug / deg_debug_relations_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_relations_graphviz.cc
28  *  \ingroup depsgraph
29  *
30  * Implementation of tools for debugging the depsgraph
31  */
32
33 #include <cstdarg>
34
35 #include "BLI_utildefines.h"
36 #include "BLI_ghash.h"
37
38 extern "C" {
39 #include "DNA_listBase.h"
40 }  /* extern "C" */
41
42 #include "DEG_depsgraph.h"
43 #include "DEG_depsgraph_debug.h"
44
45 #include "intern/depsgraph.h"
46 #include "intern/node/deg_node_component.h"
47 #include "intern/node/deg_node_id.h"
48 #include "intern/node/deg_node_operation.h"
49 #include "intern/node/deg_node_time.h"
50
51 /* ****************** */
52 /* Graphviz Debugging */
53
54 namespace DEG {
55
56 #define NL "\r\n"
57
58 /* Only one should be enabled, defines whether graphviz nodes
59  * get colored by individual types or classes.
60  */
61 #define COLOR_SCHEME_NODE_CLASS 1
62 //#define COLOR_SCHEME_NODE_TYPE  2
63
64 static const char *deg_debug_graphviz_fontname = "helvetica";
65 static float deg_debug_graphviz_graph_label_size = 20.0f;
66 static float deg_debug_graphviz_node_label_size = 14.0f;
67 static const int deg_debug_max_colors = 12;
68 #ifdef COLOR_SCHEME_NODE_TYPE
69 static const char *deg_debug_colors[] = {
70     "#a6cee3", "#1f78b4", "#b2df8a",
71     "#33a02c", "#fb9a99", "#e31a1c",
72     "#fdbf6f", "#ff7f00", "#cab2d6",
73     "#6a3d9a", "#ffff99", "#b15928",
74     "#ff00ff",
75 };
76 #endif
77 static const char *deg_debug_colors_light[] = {
78     "#8dd3c7", "#ffffb3", "#bebada",
79     "#fb8072", "#80b1d3", "#fdb462",
80     "#b3de69", "#fccde5", "#d9d9d9",
81     "#bc80bd", "#ccebc5", "#ffed6f",
82     "#ff00ff",
83 };
84
85 #ifdef COLOR_SCHEME_NODE_TYPE
86 static const int deg_debug_node_type_color_map[][2] = {
87     {NodeType::TIMESOURCE,   0},
88     {NodeType::ID_REF,       1},
89
90     /* Outer Types */
91     {NodeType::PARAMETERS,         2},
92     {NodeType::PROXY,              3},
93     {NodeType::ANIMATION,          4},
94     {NodeType::TRANSFORM,          5},
95     {NodeType::GEOMETRY,           6},
96     {NodeType::SEQUENCER,          7},
97     {NodeType::SHADING,            8},
98     {NodeType::SHADING_PARAMETERS, 9},
99     {NodeType::CACHE,              10},
100     {NodeType::POINT_CACHE,        11},
101     {NodeType::LAYER_COLLECTIONS,  12},
102     {NodeType::COPY_ON_WRITE,      13},
103     {-1,                               0}
104 };
105 #endif
106
107 static int deg_debug_node_color_index(const Node *node)
108 {
109 #ifdef COLOR_SCHEME_NODE_CLASS
110         /* Some special types. */
111         switch (node->type) {
112                 case NodeType::ID_REF:
113                         return 5;
114                 case NodeType::OPERATION:
115                 {
116                         OperationNode *op_node = (OperationNode *)node;
117                         if (op_node->is_noop())
118                                 return 8;
119                         break;
120                 }
121
122                 default:
123                         break;
124         }
125         /* Do others based on class. */
126         switch (node->get_class()) {
127                 case NodeClass::OPERATION:
128                         return 4;
129                 case NodeClass::COMPONENT:
130                         return 1;
131                 default:
132                         return 9;
133         }
134 #endif
135
136 #ifdef COLOR_SCHEME_NODE_TYPE
137         const int (*pair)[2];
138         for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) {
139                 if ((*pair)[0] == node->type) {
140                         return (*pair)[1];
141                 }
142         }
143         return -1;
144 #endif
145 }
146
147 struct DebugContext {
148         FILE *file;
149         bool show_tags;
150 };
151
152 static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3);
153 static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
154 {
155         va_list args;
156         va_start(args, fmt);
157         vfprintf(ctx.file, fmt, args);
158         va_end(args);
159 }
160
161 static void deg_debug_graphviz_legend_color(const DebugContext &ctx,
162                                             const char *name,
163                                             const char *color)
164 {
165         deg_debug_fprintf(ctx, "<TR>");
166         deg_debug_fprintf(ctx, "<TD>%s</TD>", name);
167         deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color);
168         deg_debug_fprintf(ctx, "</TR>" NL);
169 }
170
171 static void deg_debug_graphviz_legend(const DebugContext &ctx)
172 {
173         deg_debug_fprintf(ctx, "{" NL);
174         deg_debug_fprintf(ctx, "rank = sink;" NL);
175         deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL);
176         deg_debug_fprintf(ctx, "  <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL);
177         deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL);
178
179 #ifdef COLOR_SCHEME_NODE_CLASS
180         const char **colors = deg_debug_colors_light;
181         deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]);
182         deg_debug_graphviz_legend_color(ctx, "Component", colors[1]);
183         deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]);
184         deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]);
185 #endif
186
187 #ifdef COLOR_SCHEME_NODE_TYPE
188         const int (*pair)[2];
189         for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) {
190                 DepsNodeFactory *nti = type_get_factory((NodeType)(*pair)[0]);
191                 deg_debug_graphviz_legend_color(ctx,
192                                                 nti->tname().c_str(),
193                                                 deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]);
194         }
195 #endif
196
197         deg_debug_fprintf(ctx, "</TABLE>" NL);
198         deg_debug_fprintf(ctx, ">" NL);
199         deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname);
200         deg_debug_fprintf(ctx, "];" NL);
201         deg_debug_fprintf(ctx, "}" NL);
202 }
203
204 static void deg_debug_graphviz_node_color(const DebugContext &ctx,
205                                           const Node *node)
206 {
207         const char *color_default = "black";
208         const char *color_modified = "orangered4";
209         const char *color_update = "dodgerblue3";
210         const char *color = color_default;
211         if (ctx.show_tags) {
212                 if (node->get_class() == NodeClass::OPERATION) {
213                         OperationNode *op_node = (OperationNode *)node;
214                         if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
215                                 color = color_modified;
216                         }
217                         else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
218                                 color = color_update;
219                         }
220                 }
221         }
222         deg_debug_fprintf(ctx, "\"%s\"", color);
223 }
224
225 static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx,
226                                              const Node *node)
227 {
228         float penwidth_default = 1.0f;
229         float penwidth_modified = 4.0f;
230         float penwidth_update = 4.0f;
231         float penwidth = penwidth_default;
232         if (ctx.show_tags) {
233                 if (node->get_class() == NodeClass::OPERATION) {
234                         OperationNode *op_node = (OperationNode *)node;
235                         if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
236                                 penwidth = penwidth_modified;
237                         }
238                         else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
239                                 penwidth = penwidth_update;
240                         }
241                 }
242         }
243         deg_debug_fprintf(ctx, "\"%f\"", penwidth);
244 }
245
246 static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx,
247                                               const Node *node)
248 {
249         const char *defaultcolor = "gainsboro";
250         int color_index = deg_debug_node_color_index(node);
251         const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors];
252         deg_debug_fprintf(ctx, "\"%s\"", fillcolor);
253 }
254
255 static void deg_debug_graphviz_relation_color(const DebugContext &ctx,
256                                               const Relation *rel)
257 {
258         const char *color_default = "black";
259         const char *color_cyclic = "red4";  /* The color of crime scene. */
260         const char *color_godmode = "blue4";  /* The color of beautiful sky. */
261         const char *color = color_default;
262         if (rel->flag & RELATION_FLAG_CYCLIC) {
263                 color = color_cyclic;
264         }
265         else if (rel->flag & RELATION_FLAG_GODMODE) {
266                 color = color_godmode;
267         }
268         deg_debug_fprintf(ctx, "%s", color);
269 }
270
271 static void deg_debug_graphviz_relation_style(const DebugContext &ctx,
272                                               const Relation *rel)
273 {
274         const char *style_default = "solid";
275         const char *style_no_flush = "dashed";
276         const char *style_flush_user_only = "dotted";
277         const char *style = style_default;
278         if (rel->flag & RELATION_FLAG_NO_FLUSH) {
279                 style = style_no_flush;
280         }
281         if (rel->flag & RELATION_FLAG_FLUSH_USER_EDIT_ONLY) {
282                 style = style_flush_user_only;
283         }
284         deg_debug_fprintf(ctx, "%s", style);
285 }
286
287 static void deg_debug_graphviz_relation_arrowhead(const DebugContext &ctx,
288                                                   const Relation *rel)
289 {
290         const char *shape_default = "normal";
291         const char *shape_no_cow = "box";
292         const char *shape = shape_default;
293         if (rel->from->get_class() == NodeClass::OPERATION &&
294             rel->to->get_class() == NodeClass::OPERATION)
295         {
296                 OperationNode *op_from = (OperationNode *)rel->from;
297                 OperationNode *op_to = (OperationNode *)rel->to;
298                 if (op_from->owner->type == NodeType::COPY_ON_WRITE &&
299                     !op_to->owner->need_tag_cow_before_update())
300                 {
301                         shape = shape_no_cow;
302                 }
303         }
304         deg_debug_fprintf(ctx, "%s", shape);
305 }
306
307 static void deg_debug_graphviz_node_style(const DebugContext &ctx, const Node *node)
308 {
309         const char *base_style = "filled"; /* default style */
310         if (ctx.show_tags) {
311                 if (node->get_class() == NodeClass::OPERATION) {
312                         OperationNode *op_node = (OperationNode *)node;
313                         if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) {
314                                 base_style = "striped";
315                         }
316                 }
317         }
318         switch (node->get_class()) {
319                 case NodeClass::GENERIC:
320                         deg_debug_fprintf(ctx, "\"%s\"", base_style);
321                         break;
322                 case NodeClass::COMPONENT:
323                         deg_debug_fprintf(ctx, "\"%s\"", base_style);
324                         break;
325                 case NodeClass::OPERATION:
326                         deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style);
327                         break;
328         }
329 }
330
331 static void deg_debug_graphviz_node_single(const DebugContext &ctx,
332                                            const Node *node)
333 {
334         const char *shape = "box";
335         string name = node->identifier();
336         deg_debug_fprintf(ctx, "// %s\n", name.c_str());
337         deg_debug_fprintf(ctx, "\"node_%p\"", node);
338         deg_debug_fprintf(ctx, "[");
339 //      deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name);
340         deg_debug_fprintf(ctx, "label=<%s>", name.c_str());
341         deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname);
342         deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size);
343         deg_debug_fprintf(ctx, ",shape=%s", shape);
344         deg_debug_fprintf(ctx, ",style="); deg_debug_graphviz_node_style(ctx, node);
345         deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_node_color(ctx, node);
346         deg_debug_fprintf(ctx, ",fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node);
347         deg_debug_fprintf(ctx, ",penwidth="); deg_debug_graphviz_node_penwidth(ctx, node);
348         deg_debug_fprintf(ctx, "];" NL);
349         deg_debug_fprintf(ctx, NL);
350 }
351
352 static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx,
353                                                   const Node *node)
354 {
355         string name = node->identifier();
356         deg_debug_fprintf(ctx, "// %s\n", name.c_str());
357         deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node);
358 //      deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name);
359         deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str());
360         deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname);
361         deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size);
362         deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16);
363         deg_debug_fprintf(ctx, "style="); deg_debug_graphviz_node_style(ctx, node); deg_debug_fprintf(ctx, ";" NL);
364         deg_debug_fprintf(ctx, "color="); deg_debug_graphviz_node_color(ctx, node); deg_debug_fprintf(ctx, ";" NL);
365         deg_debug_fprintf(ctx, "fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); deg_debug_fprintf(ctx, ";" NL);
366         deg_debug_fprintf(ctx, "penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); deg_debug_fprintf(ctx, ";" NL);
367         /* dummy node, so we can add edges between clusters */
368         deg_debug_fprintf(ctx, "\"node_%p\"", node);
369         deg_debug_fprintf(ctx, "[");
370         deg_debug_fprintf(ctx, "shape=%s", "point");
371         deg_debug_fprintf(ctx, ",style=%s", "invis");
372         deg_debug_fprintf(ctx, "];" NL);
373         deg_debug_fprintf(ctx, NL);
374 }
375
376 static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx)
377 {
378         deg_debug_fprintf(ctx, "}" NL);
379         deg_debug_fprintf(ctx, NL);
380 }
381
382 static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx,
383                                            const Depsgraph *graph);
384 static void deg_debug_graphviz_graph_relations(const DebugContext &ctx,
385                                                const Depsgraph *graph);
386
387 static void deg_debug_graphviz_node(const DebugContext &ctx,
388                                     const Node *node)
389 {
390         switch (node->type) {
391                 case NodeType::ID_REF:
392                 {
393                         const IDNode *id_node = (const IDNode *)node;
394                         if (BLI_ghash_len(id_node->components) == 0) {
395                                 deg_debug_graphviz_node_single(ctx, node);
396                         }
397                         else {
398                                 deg_debug_graphviz_node_cluster_begin(ctx, node);
399                                 GHASH_FOREACH_BEGIN(const ComponentNode *, comp, id_node->components)
400                                 {
401                                         deg_debug_graphviz_node(ctx, comp);
402                                 }
403                                 GHASH_FOREACH_END();
404                                 deg_debug_graphviz_node_cluster_end(ctx);
405                         }
406                         break;
407                 }
408                 case NodeType::PARAMETERS:
409                 case NodeType::ANIMATION:
410                 case NodeType::TRANSFORM:
411                 case NodeType::PROXY:
412                 case NodeType::GEOMETRY:
413                 case NodeType::SEQUENCER:
414                 case NodeType::EVAL_POSE:
415                 case NodeType::BONE:
416                 case NodeType::SHADING:
417                 case NodeType::SHADING_PARAMETERS:
418                 case NodeType::CACHE:
419                 case NodeType::POINT_CACHE:
420                 case NodeType::LAYER_COLLECTIONS:
421                 case NodeType::PARTICLE_SYSTEM:
422                 case NodeType::PARTICLE_SETTINGS:
423                 case NodeType::COPY_ON_WRITE:
424                 case NodeType::OBJECT_FROM_LAYER:
425                 case NodeType::BATCH_CACHE:
426                 case NodeType::DUPLI:
427                 case NodeType::SYNCHRONIZE:
428                 case NodeType::GENERIC_DATABLOCK:
429                 {
430                         ComponentNode *comp_node = (ComponentNode *)node;
431                         if (!comp_node->operations.empty()) {
432                                 deg_debug_graphviz_node_cluster_begin(ctx, node);
433                                 for (Node *op_node : comp_node->operations) {
434                                         deg_debug_graphviz_node(ctx, op_node);
435                                 }
436                                 deg_debug_graphviz_node_cluster_end(ctx);
437                         }
438                         else {
439                                 deg_debug_graphviz_node_single(ctx, node);
440                         }
441                         break;
442                 }
443                 case NodeType::UNDEFINED:
444                 case NodeType::TIMESOURCE:
445                 case NodeType::OPERATION:
446                         deg_debug_graphviz_node_single(ctx, node);
447                         break;
448                 case NodeType::NUM_TYPES:
449                         break;
450         }
451 }
452
453 static bool deg_debug_graphviz_is_cluster(const Node *node)
454 {
455         switch (node->type) {
456                 case NodeType::ID_REF:
457                 {
458                         const IDNode *id_node = (const IDNode *)node;
459                         return BLI_ghash_len(id_node->components) > 0;
460                 }
461                 case NodeType::PARAMETERS:
462                 case NodeType::ANIMATION:
463                 case NodeType::TRANSFORM:
464                 case NodeType::PROXY:
465                 case NodeType::GEOMETRY:
466                 case NodeType::SEQUENCER:
467                 case NodeType::EVAL_POSE:
468                 case NodeType::BONE:
469                 {
470                         ComponentNode *comp_node = (ComponentNode *)node;
471                         return !comp_node->operations.empty();
472                 }
473                 default:
474                         return false;
475         }
476 }
477
478 static bool deg_debug_graphviz_is_owner(const Node *node,
479                                         const Node *other)
480 {
481         switch (node->get_class()) {
482                 case NodeClass::COMPONENT:
483                 {
484                         ComponentNode *comp_node = (ComponentNode *)node;
485                         if (comp_node->owner == other)
486                                 return true;
487                         break;
488                 }
489                 case NodeClass::OPERATION:
490                 {
491                         OperationNode *op_node = (OperationNode *)node;
492                         if (op_node->owner == other)
493                                 return true;
494                         else if (op_node->owner->owner == other)
495                                 return true;
496                         break;
497                 }
498                 default: break;
499         }
500         return false;
501 }
502
503 static void deg_debug_graphviz_node_relations(const DebugContext &ctx,
504                                               const Node *node)
505 {
506         for (Relation *rel : node->inlinks) {
507                 float penwidth = 2.0f;
508
509                 const Node *tail = rel->to; /* same as node */
510                 const Node *head = rel->from;
511                 deg_debug_fprintf(ctx, "// %s -> %s\n",
512                                  head->identifier().c_str(),
513                                  tail->identifier().c_str());
514                 deg_debug_fprintf(ctx, "\"node_%p\"", head);
515                 deg_debug_fprintf(ctx, " -> ");
516                 deg_debug_fprintf(ctx, "\"node_%p\"", tail);
517
518                 deg_debug_fprintf(ctx, "[");
519                 /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */
520                 deg_debug_fprintf(ctx, "id=\"%s\"", rel->name);
521                 // deg_debug_fprintf(ctx, "label=\"%s\"", rel->name);
522                 deg_debug_fprintf(ctx, ",color=");
523                 deg_debug_graphviz_relation_color(ctx, rel);
524                 deg_debug_fprintf(ctx, ",style=");
525                 deg_debug_graphviz_relation_style(ctx, rel);
526                 deg_debug_fprintf(ctx, ",arrowhead=");
527                 deg_debug_graphviz_relation_arrowhead(ctx, rel);
528                 deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth);
529                 /* NOTE: edge from node to own cluster is not possible and gives graphviz
530                  * warning, avoid this here by just linking directly to the invisible
531                  * placeholder node. */
532                 if (deg_debug_graphviz_is_cluster(tail) &&
533                     !deg_debug_graphviz_is_owner(head, tail))
534                 {
535                         deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail);
536                 }
537                 if (deg_debug_graphviz_is_cluster(head) &&
538                     !deg_debug_graphviz_is_owner(tail, head))
539                 {
540                         deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head);
541                 }
542                 deg_debug_fprintf(ctx, "];" NL);
543                 deg_debug_fprintf(ctx, NL);
544         }
545 }
546
547 static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx,
548                                            const Depsgraph *graph)
549 {
550         for (Node *node : graph->id_nodes) {
551                 deg_debug_graphviz_node(ctx, node);
552         }
553         TimeSourceNode *time_source = graph->find_time_source();
554         if (time_source != NULL) {
555                 deg_debug_graphviz_node(ctx, time_source);
556         }
557 }
558
559 static void deg_debug_graphviz_graph_relations(const DebugContext &ctx,
560                                                const Depsgraph *graph)
561 {
562         for (IDNode *id_node : graph->id_nodes) {
563                 GHASH_FOREACH_BEGIN(ComponentNode *, comp_node, id_node->components)
564                 {
565                         for (OperationNode *op_node : comp_node->operations) {
566                                 deg_debug_graphviz_node_relations(ctx, op_node);
567                         }
568                 }
569                 GHASH_FOREACH_END();
570         }
571
572         TimeSourceNode *time_source = graph->find_time_source();
573         if (time_source != NULL) {
574                 deg_debug_graphviz_node_relations(ctx, time_source);
575         }
576 }
577
578 }  // namespace DEG
579
580 void DEG_debug_relations_graphviz(const Depsgraph *graph,
581                                   FILE *f,
582                                   const char *label)
583 {
584         if (!graph) {
585                 return;
586         }
587
588         const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
589
590         DEG::DebugContext ctx;
591         ctx.file = f;
592
593         DEG::deg_debug_fprintf(ctx, "digraph depgraph {" NL);
594         DEG::deg_debug_fprintf(ctx, "rankdir=LR;" NL);
595         DEG::deg_debug_fprintf(ctx, "graph [");
596         DEG::deg_debug_fprintf(ctx, "compound=true");
597         DEG::deg_debug_fprintf(ctx, ",labelloc=\"t\"");
598         DEG::deg_debug_fprintf(ctx, ",fontsize=%f", DEG::deg_debug_graphviz_graph_label_size);
599         DEG::deg_debug_fprintf(ctx, ",fontname=\"%s\"", DEG::deg_debug_graphviz_fontname);
600         DEG::deg_debug_fprintf(ctx, ",label=\"%s\"", label);
601         DEG::deg_debug_fprintf(ctx, ",splines=ortho");
602         DEG::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato
603         DEG::deg_debug_fprintf(ctx, "];" NL);
604
605         DEG::deg_debug_graphviz_graph_nodes(ctx, deg_graph);
606         DEG::deg_debug_graphviz_graph_relations(ctx, deg_graph);
607
608         DEG::deg_debug_graphviz_legend(ctx);
609
610         DEG::deg_debug_fprintf(ctx, "}" NL);
611 }
612
613 #undef NL