Blame gegl/gegl-dot.c

Packit Service 2781ba
/* This file is the public GEGL API
Packit Service 2781ba
 *
Packit Service 2781ba
 * GEGL is free software; you can redistribute it and/or
Packit Service 2781ba
 * modify it under the terms of the GNU Lesser General Public
Packit Service 2781ba
 * License as published by the Free Software Foundation; either
Packit Service 2781ba
 * version 3 of the License, or (at your option) any later version.
Packit Service 2781ba
 *
Packit Service 2781ba
 * GEGL is distributed in the hope that it will be useful,
Packit Service 2781ba
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 2781ba
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 2781ba
 * Lesser General Public License for more details.
Packit Service 2781ba
 *
Packit Service 2781ba
 * You should have received a copy of the GNU Lesser General Public
Packit Service 2781ba
 * License along with GEGL; if not, see <http://www.gnu.org/licenses>.
Packit Service 2781ba
 *
Packit Service 2781ba
 * 2006 © Øyvind Kolås.
Packit Service 2781ba
 */
Packit Service 2781ba
Packit Service 2781ba
/* FIXME: this file should be implemented using public API only */
Packit Service 2781ba
Packit Service 2781ba
#include "config.h"
Packit Service 2781ba
#include <stdlib.h>
Packit Service 2781ba
#include <stdio.h>
Packit Service 2781ba
#include <string.h>
Packit Service 2781ba
#include <glib-object.h>
Packit Service 2781ba
Packit Service 2781ba
#include "gegl.h"
Packit Service 2781ba
#include "gegl-types-internal.h"
Packit Service 2781ba
#include "graph/gegl-node.h"
Packit Service 2781ba
#include "graph/gegl-pad.h"
Packit Service 2781ba
#include "graph/gegl-connection.h"
Packit Service 2781ba
#include "graph/gegl-visitable.h"
Packit Service 2781ba
#include "graph/gegl-visitor.h"
Packit Service 2781ba
#include "gegl-dot.h"
Packit Service 2781ba
#include "gegl-dot-visitor.h"
Packit Service 2781ba
#include "gegl.h"
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_dot_util_add_node (GString  *string,
Packit Service 2781ba
                        GeglNode *node)
Packit Service 2781ba
{
Packit Service 2781ba
  g_string_append_printf (string, "op_%p [fontsize=\"10\" label=\"", node);
Packit Service 2781ba
Packit Service 2781ba
  /* We build the record from top to bottom */
Packit Service 2781ba
  g_string_append_printf (string, "{");
Packit Service 2781ba
Packit Service 2781ba
  /* The first row is a list of output pads */
Packit Service 2781ba
  {
Packit Service 2781ba
    GSList  *pads       = gegl_node_get_pads (node);
Packit Service 2781ba
    GSList  *entry      = pads;
Packit Service 2781ba
    gboolean got_output = FALSE;
Packit Service 2781ba
Packit Service 2781ba
    g_string_append_printf (string, "{");
Packit Service 2781ba
Packit Service 2781ba
    while (entry)
Packit Service 2781ba
      {
Packit Service 2781ba
        GeglPad *pad = entry->data;
Packit Service 2781ba
        if (gegl_pad_is_output (pad))
Packit Service 2781ba
          {
Packit Service 2781ba
            if (got_output)
Packit Service 2781ba
              {
Packit Service 2781ba
                g_string_append (string, "|");
Packit Service 2781ba
              }
Packit Service 2781ba
            got_output = TRUE;
Packit Service 2781ba
            g_string_append_printf (string, "<%s>%s",
Packit Service 2781ba
                                    gegl_pad_get_name (pad),
Packit Service 2781ba
                                    gegl_pad_get_name (pad));
Packit Service 2781ba
          }
Packit Service 2781ba
        entry = g_slist_next (entry);
Packit Service 2781ba
      }
Packit Service 2781ba
Packit Service 2781ba
    g_string_append_printf (string, "}|");
Packit Service 2781ba
  }
Packit Service 2781ba
Packit Service 2781ba
  /* The second row is the operation name such as gegl:translate */
Packit Service 2781ba
  g_string_append_printf (string, "%s |", gegl_node_get_debug_name (node));
Packit Service 2781ba
Packit Service 2781ba
  /* The next rows are property names and their values */
Packit Service 2781ba
  if (1)
Packit Service 2781ba
    {
Packit Service 2781ba
      guint        n_properties;
Packit Service 2781ba
      GParamSpec **properties = gegl_operation_list_properties (gegl_node_get_operation (node), &n_properties);
Packit Service 2781ba
      guint        i;
Packit Service 2781ba
      for (i = 0; i < n_properties; i++)
Packit Service 2781ba
        {
Packit Service 2781ba
          const gchar *name   = properties[i]->name;
Packit Service 2781ba
          GValue       tvalue = { 0, };
Packit Service 2781ba
          GValue       svalue = { 0, };
Packit Service 2781ba
Packit Service 2781ba
          if (properties[i]->value_type == GEGL_TYPE_BUFFER)
Packit Service 2781ba
            continue;
Packit Service 2781ba
Packit Service 2781ba
          g_value_init (&svalue, G_TYPE_STRING);
Packit Service 2781ba
          g_value_init (&tvalue, properties[i]->value_type);
Packit Service 2781ba
Packit Service 2781ba
          gegl_node_get_property (node, name, &tvalue);
Packit Service 2781ba
Packit Service 2781ba
          if (g_value_transform (&tvalue, &svalue))
Packit Service 2781ba
            {
Packit Service 2781ba
              gchar *sval = g_value_dup_string (&svalue);
Packit Service 2781ba
              if (sval && strlen (sval) > 30)
Packit Service 2781ba
                {
Packit Service 2781ba
                  sval[28] = '.';
Packit Service 2781ba
                  sval[29] = '.';
Packit Service 2781ba
                  sval[30] = '\0';
Packit Service 2781ba
                }
Packit Service 2781ba
              if (sval)
Packit Service 2781ba
                {
Packit Service 2781ba
                  g_string_append_printf (string, "%s=%s | ", name, sval);
Packit Service 2781ba
                  g_free (sval);
Packit Service 2781ba
                }
Packit Service 2781ba
              g_value_unset (&svalue);
Packit Service 2781ba
            }
Packit Service 2781ba
          g_value_unset (&tvalue);
Packit Service 2781ba
        }
Packit Service 2781ba
      g_free (properties);
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  /* The last row is input pads */
Packit Service 2781ba
  {
Packit Service 2781ba
    GSList  *pads      = gegl_node_get_pads (node);
Packit Service 2781ba
    GSList  *entry     = pads;
Packit Service 2781ba
    gboolean got_input = FALSE;
Packit Service 2781ba
Packit Service 2781ba
    g_string_append_printf (string, "{");
Packit Service 2781ba
Packit Service 2781ba
    while (entry)
Packit Service 2781ba
      {
Packit Service 2781ba
        GeglPad *pad = entry->data;
Packit Service 2781ba
        if (gegl_pad_is_input (pad))
Packit Service 2781ba
          {
Packit Service 2781ba
            if (got_input)
Packit Service 2781ba
              {
Packit Service 2781ba
                g_string_append (string, "|");
Packit Service 2781ba
              }
Packit Service 2781ba
            got_input = TRUE;
Packit Service 2781ba
            g_string_append_printf (string, "<%s>%s",
Packit Service 2781ba
                                    gegl_pad_get_name (pad),
Packit Service 2781ba
                                    gegl_pad_get_name (pad));
Packit Service 2781ba
          }
Packit Service 2781ba
        entry = g_slist_next (entry);
Packit Service 2781ba
      }
Packit Service 2781ba
Packit Service 2781ba
    g_string_append_printf (string, "}");
Packit Service 2781ba
  }
Packit Service 2781ba
Packit Service 2781ba
  g_string_append_printf (string, "}\"");
Packit Service 2781ba
  g_string_append_printf (string, "shape=\"record\"];\n");
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_dot_util_add_node_sink_edges (GString  *string,
Packit Service 2781ba
                                   GeglNode *node)
Packit Service 2781ba
{
Packit Service 2781ba
  GSList *connections = gegl_node_get_sinks (node);
Packit Service 2781ba
  GSList *iter;
Packit Service 2781ba
Packit Service 2781ba
  for (iter = connections; iter; iter = g_slist_next (iter))
Packit Service 2781ba
    {
Packit Service 2781ba
      GeglConnection *connection = iter->data;
Packit Service 2781ba
      gegl_dot_util_add_connection (string, connection);
Packit Service 2781ba
    }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_dot_util_add_connection (GString        *string,
Packit Service 2781ba
                              GeglConnection *connection)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglNode *source;
Packit Service 2781ba
  GeglNode *sink;
Packit Service 2781ba
  GeglPad  *source_pad;
Packit Service 2781ba
  GeglPad  *sink_pad;
Packit Service 2781ba
Packit Service 2781ba
  source     = gegl_connection_get_source_node (connection);
Packit Service 2781ba
  sink       = gegl_connection_get_sink_node (connection);
Packit Service 2781ba
  source_pad = gegl_connection_get_source_pad (connection);
Packit Service 2781ba
  sink_pad   = gegl_connection_get_sink_pad (connection);
Packit Service 2781ba
Packit Service 2781ba
  g_string_append_printf (string, "op_%p:%s -> op_%p:%s;\n",
Packit Service 2781ba
                          source, gegl_pad_get_name (source_pad),
Packit Service 2781ba
                          sink,   gegl_pad_get_name (sink_pad));
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_dot_add_graph (GString     *string,
Packit Service 2781ba
                    GeglNode    *node,
Packit Service 2781ba
                    const gchar *label)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglNode *graph = node;
Packit Service 2781ba
Packit Service 2781ba
  g_string_append_printf (string, "subgraph cluster_%s%p { graph [ label=\"%s %p\" fontsize=\"10\" ranksep=\"0.3\" nodesep=\"0.3\"]; node [ fontsize=\"10\" ];\n", label, node, label, node);
Packit Service 2781ba
Packit Service 2781ba
  {
Packit Service 2781ba
    GSList *nodes = gegl_node_get_children (graph);
Packit Service 2781ba
    GSList *entry = nodes;
Packit Service 2781ba
Packit Service 2781ba
    while (entry)
Packit Service 2781ba
      {
Packit Service 2781ba
        GeglNode *node = entry->data;
Packit Service 2781ba
Packit Service 2781ba
        if (node->is_graph)
Packit Service 2781ba
          {
Packit Service 2781ba
            gchar *name = g_strdup (gegl_node_get_debug_name (node));
Packit Service 2781ba
            gchar *p    = name;
Packit Service 2781ba
            while (*p)
Packit Service 2781ba
              {
Packit Service 2781ba
                if (*p == ' ' ||
Packit Service 2781ba
                    *p == '-')
Packit Service 2781ba
                  *p = '_';
Packit Service 2781ba
                p++;
Packit Service 2781ba
              }
Packit Service 2781ba
            gegl_dot_add_graph (string, node, name);
Packit Service 2781ba
            g_free (name);
Packit Service 2781ba
          }
Packit Service 2781ba
        else
Packit Service 2781ba
          {
Packit Service 2781ba
            gegl_dot_util_add_node (string, node);
Packit Service 2781ba
          }
Packit Service 2781ba
Packit Service 2781ba
        entry = g_slist_next (entry);
Packit Service 2781ba
      }
Packit Service 2781ba
Packit Service 2781ba
    g_slist_free (nodes);
Packit Service 2781ba
  }
Packit Service 2781ba
Packit Service 2781ba
  {
Packit Service 2781ba
    GSList *nodes = gegl_node_get_children (graph);
Packit Service 2781ba
    GSList *entry = nodes;
Packit Service 2781ba
Packit Service 2781ba
    while (entry)
Packit Service 2781ba
      {
Packit Service 2781ba
        GeglNode *node = entry->data;
Packit Service 2781ba
Packit Service 2781ba
        gegl_dot_util_add_node_sink_edges (string, node);
Packit Service 2781ba
Packit Service 2781ba
        entry = g_slist_next (entry);
Packit Service 2781ba
      }
Packit Service 2781ba
    g_slist_free (nodes);
Packit Service 2781ba
  }
Packit Service 2781ba
  g_string_append_printf (string, "}\n");
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * gegl_dot_add_node_and_dependencies:
Packit Service 2781ba
 * @string:
Packit Service 2781ba
 * @node:
Packit Service 2781ba
 *
Packit Service 2781ba
 * Adds @node to the graph, and all nodes that @node depends on both
Packit Service 2781ba
 * directly and indirectly. There is no grouping of subgraphs.
Packit Service 2781ba
 **/
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_dot_add_node_and_dependencies (GString  *string,
Packit Service 2781ba
                                    GeglNode *node)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglDotVisitor *dot_visitor;
Packit Service 2781ba
  GeglPad        *pad;
Packit Service 2781ba
  gpointer        context_id = string;
Packit Service 2781ba
Packit Service 2781ba
  dot_visitor = g_object_new (GEGL_TYPE_DOT_VISITOR,
Packit Service 2781ba
                              "id", context_id,
Packit Service 2781ba
                              NULL);
Packit Service 2781ba
Packit Service 2781ba
  gegl_dot_visitor_set_string_to_append (dot_visitor,
Packit Service 2781ba
                                         string);
Packit Service 2781ba
Packit Service 2781ba
  /* Add the nodes */
Packit Service 2781ba
  gegl_visitor_bfs_traverse (GEGL_VISITOR (dot_visitor),
Packit Service 2781ba
                             GEGL_VISITABLE (node));
Packit Service 2781ba
Packit Service 2781ba
  /* Add the edges */
Packit Service 2781ba
  pad = gegl_node_get_pad (node, "output");
Packit Service 2781ba
  if (! pad)
Packit Service 2781ba
    {
Packit Service 2781ba
      pad = gegl_node_get_pad (node, "input");
Packit Service 2781ba
Packit Service 2781ba
      if (pad)
Packit Service 2781ba
        {
Packit Service 2781ba
          /* This is a sink node, we need to add these edges manually
Packit Service 2781ba
           * since no pad depends on this input pad */
Packit Service 2781ba
          GSList *iter;
Packit Service 2781ba
          for (iter = pad->connections; iter; iter = g_slist_next (iter))
Packit Service 2781ba
            {
Packit Service 2781ba
              GeglConnection *connection = iter->data;
Packit Service 2781ba
              gegl_dot_util_add_connection (string, connection);
Packit Service 2781ba
            }
Packit Service 2781ba
        }
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
  gegl_visitor_bfs_traverse (GEGL_VISITOR (dot_visitor),
Packit Service 2781ba
                             GEGL_VISITABLE (pad));
Packit Service 2781ba
Packit Service 2781ba
  g_object_unref (dot_visitor);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
gchar *
Packit Service 2781ba
gegl_to_dot (GeglNode *node)
Packit Service 2781ba
{
Packit Service 2781ba
  GString *string;
Packit Service 2781ba
Packit Service 2781ba
  string = g_string_new ("digraph gegl { graph [ rankdir = \"BT\" fontsize = \"10\" ];\n");
Packit Service 2781ba
Packit Service 2781ba
  if (node->is_graph)
Packit Service 2781ba
    gegl_dot_add_graph (string, node, "GEGL");
Packit Service 2781ba
  else
Packit Service 2781ba
    gegl_dot_add_node_and_dependencies (string, node);
Packit Service 2781ba
Packit Service 2781ba
  g_string_append (string, "}\n");
Packit Service 2781ba
Packit Service 2781ba
  return g_string_free (string, FALSE);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * gegl_dot_node_to_png_default:
Packit Service 2781ba
 * @node:
Packit Service 2781ba
 *
Packit Service 2781ba
 * Calls gegl_dot_node_to_png() with `png_path' set to "/tmp/node.png".
Packit Service 2781ba
 **/
Packit Service 2781ba
void
Packit Service 2781ba
gegl_dot_node_to_png_default (GeglNode *node)
Packit Service 2781ba
{
Packit Service 2781ba
  gegl_dot_node_to_png (node, "/tmp/node.png");
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * gegl_dot_node_to_png:
Packit Service 2781ba
 * @node: Node to depict graph for.
Packit Service 2781ba
 * @png_path: Path of the png to write.
Packit Service 2781ba
 *
Packit Service 2781ba
 * This is for debug purposes, meant to be invoked directly from a
Packit Service 2781ba
 * debugger.
Packit Service 2781ba
 **/
Packit Service 2781ba
void
Packit Service 2781ba
gegl_dot_node_to_png (GeglNode    *node,
Packit Service 2781ba
                      const gchar *png_path)
Packit Service 2781ba
{
Packit Service 2781ba
  gchar      *dot_string   = NULL;
Packit Service 2781ba
  gchar      *dot_filename = NULL;
Packit Service 2781ba
  gchar      *dot_cmd      = NULL;
Packit Service 2781ba
Packit Service 2781ba
  /* Get dot string */
Packit Service 2781ba
  dot_string = gegl_to_dot (node);
Packit Service 2781ba
Packit Service 2781ba
  /* Write it to a file */
Packit Service 2781ba
  dot_filename = g_build_filename (g_get_tmp_dir (), "gegl-dot.dot", NULL);
Packit Service 2781ba
  g_file_set_contents (dot_filename, dot_string, -1, NULL);
Packit Service 2781ba
Packit Service 2781ba
  /* Create a png from it */
Packit Service 2781ba
  dot_cmd = g_strdup_printf ("dot -o %s -Tpng %s", png_path, dot_filename);
Packit Service 2781ba
  if (system (dot_cmd) == -1)
Packit Service 2781ba
    g_warning ("Error executing GraphViz dot program");
Packit Service 2781ba
}