gegl-dot.c 9.33 KB
Newer Older
1
/* This file is the public GEGL API
2
 *
3 4 5
 * GEGL is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
6
 * version 3 of the License, or (at your option) any later version.
7
 *
8
 * GEGL is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
12
 *
13
 * You should have received a copy of the GNU Lesser General Public
14
 * License along with GEGL; if not, see <http://www.gnu.org/licenses>.
15
 *
16
 * 2006 © Øyvind Kolås.
17 18
 */

19
/* FIXME: this file should be implemented using public API only */
20

21
#include "config.h"
22
#include <stdlib.h>
23
#include <stdio.h>
24
#include <string.h>
25
#include <glib-object.h>
26 27

#include "gegl.h"
28
#include "gegl-types-internal.h"
29
#include "graph/gegl-node-private.h"
30 31
#include "graph/gegl-pad.h"
#include "graph/gegl-connection.h"
32 33
#include "graph/gegl-visitable.h"
#include "graph/gegl-visitor.h"
34
#include "gegl-dot.h"
35
#include "gegl-dot-visitor.h"
36
#include "gegl.h"
37

38
void
39 40 41
gegl_dot_util_add_node (GString  *string,
                        GeglNode *node)
{
42
  g_string_append_printf (string, "op_%p [fontsize=\"10\" label=\"", node);
43

44 45
  /* We build the record from top to bottom */
  g_string_append_printf (string, "{");
46

47
  /* The first row is a list of output pads */
48 49 50 51
  {
    GSList  *pads       = gegl_node_get_pads (node);
    GSList  *entry      = pads;
    gboolean got_output = FALSE;
52 53 54

    g_string_append_printf (string, "{");

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
    while (entry)
      {
        GeglPad *pad = entry->data;
        if (gegl_pad_is_output (pad))
          {
            if (got_output)
              {
                g_string_append (string, "|");
              }
            got_output = TRUE;
            g_string_append_printf (string, "<%s>%s",
                                    gegl_pad_get_name (pad),
                                    gegl_pad_get_name (pad));
          }
        entry = g_slist_next (entry);
      }
71 72

    g_string_append_printf (string, "}|");
73 74
  }

75
  /* The second row is the operation name such as gegl:translate */
76
  g_string_append_printf (string, "%s |", gegl_node_get_debug_name (node));
77

78
  /* The next rows are property names and their values */
79 80 81
  if (1)
    {
      guint        n_properties;
82
      GParamSpec **properties = gegl_operation_list_properties (gegl_node_get_operation (node), &n_properties);
83
      guint        i;
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
      for (i = 0; i < n_properties; i++)
        {
          const gchar *name   = properties[i]->name;
          GValue       tvalue = { 0, };
          GValue       svalue = { 0, };

          if (properties[i]->value_type == GEGL_TYPE_BUFFER)
            continue;

          g_value_init (&svalue, G_TYPE_STRING);
          g_value_init (&tvalue, properties[i]->value_type);

          gegl_node_get_property (node, name, &tvalue);

          if (g_value_transform (&tvalue, &svalue))
            {
              gchar *sval = g_value_dup_string (&svalue);
              if (sval && strlen (sval) > 30)
                {
                  sval[28] = '.';
                  sval[29] = '.';
                  sval[30] = '\0';
                }
              if (sval)
                {
109
                  g_string_append_printf (string, "%s=%s | ", name, sval);
110 111 112 113 114 115 116 117 118
                  g_free (sval);
                }
              g_value_unset (&svalue);
            }
          g_value_unset (&tvalue);
        }
      g_free (properties);
    }

119
  /* The last row is input pads */
120 121 122 123
  {
    GSList  *pads      = gegl_node_get_pads (node);
    GSList  *entry     = pads;
    gboolean got_input = FALSE;
124 125 126

    g_string_append_printf (string, "{");

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    while (entry)
      {
        GeglPad *pad = entry->data;
        if (gegl_pad_is_input (pad))
          {
            if (got_input)
              {
                g_string_append (string, "|");
              }
            got_input = TRUE;
            g_string_append_printf (string, "<%s>%s",
                                    gegl_pad_get_name (pad),
                                    gegl_pad_get_name (pad));
          }
        entry = g_slist_next (entry);
      }
143 144

    g_string_append_printf (string, "}");
145 146
  }

147 148
  g_string_append_printf (string, "}\"");
  g_string_append_printf (string, "shape=\"record\"];\n");
149 150
}

151
void
152 153 154 155 156 157 158 159 160
gegl_dot_util_add_node_sink_edges (GString  *string,
                                   GeglNode *node)
{
  GSList *connections = gegl_node_get_sinks (node);
  GSList *iter;

  for (iter = connections; iter; iter = g_slist_next (iter))
    {
      GeglConnection *connection = iter->data;
161
      gegl_dot_util_add_connection (string, connection);
162 163
    }
}
164

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
void
gegl_dot_util_add_connection (GString        *string,
                              GeglConnection *connection)
{
  GeglNode *source;
  GeglNode *sink;
  GeglPad  *source_pad;
  GeglPad  *sink_pad;

  source     = gegl_connection_get_source_node (connection);
  sink       = gegl_connection_get_sink_node (connection);
  source_pad = gegl_connection_get_source_pad (connection);
  sink_pad   = gegl_connection_get_sink_pad (connection);

  g_string_append_printf (string, "op_%p:%s -> op_%p:%s;\n",
                          source, gegl_pad_get_name (source_pad),
                          sink,   gegl_pad_get_name (sink_pad));
}

184
static void
185 186 187
gegl_dot_add_graph (GString     *string,
                    GeglNode    *node,
                    const gchar *label)
188
{
189
  GeglNode *graph = node;
Øyvind Kolås's avatar
Øyvind Kolås committed
190

191
  g_string_append_printf (string, "subgraph cluster_%p { graph [ label=\"%s %p\" fontsize=\"10\" ranksep=\"0.3\" nodesep=\"0.3\"]; node [ fontsize=\"10\" ];\n", node, label, node);
192

193 194 195
  {
    GSList *nodes = gegl_node_get_children (graph);
    GSList *entry = nodes;
196

197 198 199
    while (entry)
      {
        GeglNode *node = entry->data;
Øyvind Kolås's avatar
Øyvind Kolås committed
200

201 202 203 204 205 206 207 208 209 210 211
        if (node->is_graph)
          {
            gchar *name = g_strdup (gegl_node_get_debug_name (node));
            gchar *p    = name;
            while (*p)
              {
                if (*p == ' ' ||
                    *p == '-')
                  *p = '_';
                p++;
              }
212
            gegl_dot_add_graph (string, node, name);
213 214
            g_free (name);
          }
215 216 217 218
        else
          {
            gegl_dot_util_add_node (string, node);
          }
Øyvind Kolås's avatar
Øyvind Kolås committed
219

220 221
        entry = g_slist_next (entry);
      }
222

223 224
    g_slist_free (nodes);
  }
225

226 227 228
  {
    GSList *nodes = gegl_node_get_children (graph);
    GSList *entry = nodes;
229

230 231 232 233
    while (entry)
      {
        GeglNode *node = entry->data;

234 235
        gegl_dot_util_add_node_sink_edges (string, node);

236 237 238 239
        entry = g_slist_next (entry);
      }
    g_slist_free (nodes);
  }
Øyvind Kolås's avatar
Øyvind Kolås committed
240 241 242
  g_string_append_printf (string, "}\n");
}

243 244 245 246 247 248 249 250 251 252 253 254 255 256

/**
 * gegl_dot_add_node_and_dependencies:
 * @string:
 * @node:
 *
 * Adds @node to the graph, and all nodes that @node depends on both
 * directly and indirectly. There is no grouping of subgraphs.
 **/
static void
gegl_dot_add_node_and_dependencies (GString  *string,
                                    GeglNode *node)
{
  GeglDotVisitor *dot_visitor;
257
  GeglPad        *pad;
258

Ell's avatar
Ell committed
259
  dot_visitor = g_object_new (GEGL_TYPE_DOT_VISITOR, NULL);
260 261 262 263

  gegl_dot_visitor_set_string_to_append (dot_visitor,
                                         string);

264
  /* Add the nodes */
265
  gegl_visitor_traverse (GEGL_VISITOR (dot_visitor), GEGL_VISITABLE (node));
266

267
  /* Add the edges */
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
  pad = gegl_node_get_pad (node, "output");
  if (! pad)
    {
      pad = gegl_node_get_pad (node, "input");

      if (pad)
        {
          /* This is a sink node, we need to add these edges manually
           * since no pad depends on this input pad */
          GSList *iter;
          for (iter = pad->connections; iter; iter = g_slist_next (iter))
            {
              GeglConnection *connection = iter->data;
              gegl_dot_util_add_connection (string, connection);
            }
        }
    }


287
  gegl_visitor_traverse (GEGL_VISITOR (dot_visitor), GEGL_VISITABLE (pad));
288

289 290 291
  g_object_unref (dot_visitor);
}

Øyvind Kolås's avatar
Øyvind Kolås committed
292 293 294
gchar *
gegl_to_dot (GeglNode *node)
{
295
  GString *string;
296

297
  string = g_string_new ("digraph gegl { graph [ rankdir = \"BT\" fontsize = \"10\" ];\n");
298 299 300 301 302 303 304

  if (node->is_graph)
    gegl_dot_add_graph (string, node, "GEGL");
  else
    gegl_dot_add_node_and_dependencies (string, node);

  g_string_append (string, "}\n");
305

306 307
  return g_string_free (string, FALSE);
}
308

309 310 311 312 313 314 315 316 317 318 319 320
/**
 * gegl_dot_node_to_png_default:
 * @node:
 *
 * Calls gegl_dot_node_to_png() with `png_path' set to "/tmp/node.png".
 **/
void
gegl_dot_node_to_png_default (GeglNode *node)
{
  gegl_dot_node_to_png (node, "/tmp/node.png");
}

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
/**
 * gegl_dot_node_to_png:
 * @node: Node to depict graph for.
 * @png_path: Path of the png to write.
 *
 * This is for debug purposes, meant to be invoked directly from a
 * debugger.
 **/
void
gegl_dot_node_to_png (GeglNode    *node,
                      const gchar *png_path)
{
  gchar      *dot_string   = NULL;
  gchar      *dot_filename = NULL;
  gchar      *dot_cmd      = NULL;

  /* Get dot string */
  dot_string = gegl_to_dot (node);

  /* Write it to a file */
  dot_filename = g_build_filename (g_get_tmp_dir (), "gegl-dot.dot", NULL);
  g_file_set_contents (dot_filename, dot_string, -1, NULL);

  /* Create a png from it */
  dot_cmd = g_strdup_printf ("dot -o %s -Tpng %s", png_path, dot_filename);
346 347
  if (system (dot_cmd) == -1)
    g_warning ("Error executing GraphViz dot program");
348
  g_free (dot_cmd);
349
}