From 9cd6813b079d65c5eada038f33d80ff145e1aa8b Mon Sep 17 00:00:00 2001
From: Joseph Mirabel <jmirabel@laas.fr>
Date: Tue, 18 Nov 2014 10:18:02 +0100
Subject: [PATCH] Enhance DOT print functionality of the graph.

---
 CMakeLists.txt                                |  1 +
 include/hpp/manipulation/graph/dot.hh         | 51 +++++++++++++++++++
 include/hpp/manipulation/graph/edge.hh        |  6 +--
 .../hpp/manipulation/graph/graph-component.hh |  3 +-
 include/hpp/manipulation/graph/graph.hh       |  2 +-
 .../hpp/manipulation/graph/node-selector.hh   |  2 +-
 include/hpp/manipulation/graph/node.hh        |  2 +-
 src/CMakeLists.txt                            |  2 +
 src/graph/dot.cc                              | 44 ++++++++++++++++
 src/graph/edge.cc                             | 27 +++++++---
 src/graph/graph-component.cc                  |  2 +-
 src/graph/graph.cc                            |  4 +-
 src/graph/node-selector.cc                    |  2 +-
 src/graph/node.cc                             | 18 +++++--
 14 files changed, 144 insertions(+), 22 deletions(-)
 create mode 100644 include/hpp/manipulation/graph/dot.hh
 create mode 100644 src/graph/dot.cc

diff --git a/CMakeLists.txt b/CMakeLists.txt
index cea373b..dc4b724 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,6 +66,7 @@ SET (${PROJECT_NAME}_HEADERS
   include/hpp/manipulation/graph/statistics.hh
   include/hpp/manipulation/graph/graph-component.hh
   include/hpp/manipulation/graph/fwd.hh
+  include/hpp/manipulation/graph/dot.hh
   )
 
 # Add dependency toward hpp-model library in pkg-config file.
diff --git a/include/hpp/manipulation/graph/dot.hh b/include/hpp/manipulation/graph/dot.hh
new file mode 100644
index 0000000..ecf503c
--- /dev/null
+++ b/include/hpp/manipulation/graph/dot.hh
@@ -0,0 +1,51 @@
+// Copyright (c) 2014, LAAS-CNRS
+// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
+//
+// This file is part of hpp-manipulation.
+// hpp-manipulation 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 version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef HPP_MANIPULATION_GRAPH_DOT_HH
+# define HPP_MANIPULATION_GRAPH_DOT_HH
+
+# include <ostream>
+# include <map>
+
+namespace hpp {
+  namespace manipulation {
+    namespace graph {
+      namespace dot {
+        struct DrawingAttributes {
+          typedef std::pair <std::string, std::string> Pair;
+          typedef std::map <std::string, std::string> Map;
+          Map attr;
+
+          inline void insertWithQuote (const std::string& K, const std::string& V) {
+            attr.insert (Pair (K, "\"" + V + "\""));
+          }
+          inline void insert (const std::string& K, const std::string& V) {
+            attr.insert (Pair (K, V));
+          }
+          std::string& operator [] (const std::string& K) {
+            return attr [K];
+          }
+        };
+
+        std::ostream& insertComments (std::ostream& os, const std::string& c);
+
+        std::ostream& operator<< (std::ostream& os, const DrawingAttributes& da);
+      } // namespace dot
+    } // namespace graph
+  } // namespace manipulation
+} // namespace hpp
+
+#endif // HPP_MANIPULATION_GRAPH_DOT_HH
diff --git a/include/hpp/manipulation/graph/edge.hh b/include/hpp/manipulation/graph/edge.hh
index 65ce4e2..351f742 100644
--- a/include/hpp/manipulation/graph/edge.hh
+++ b/include/hpp/manipulation/graph/edge.hh
@@ -88,7 +88,7 @@ namespace hpp {
           }
 
           /// Print the object in a stream.
-          virtual std::ostream& dotPrint (std::ostream& os) const;
+          virtual std::ostream& dotPrint (std::ostream& os, dot::DrawingAttributes da = dot::DrawingAttributes ()) const;
 
         protected:
           /// Initialization of the object.
@@ -169,7 +169,7 @@ namespace hpp {
           boost::shared_ptr <EdgeType> waypoint () const;
 
           /// Print the object in a stream.
-          virtual std::ostream& dotPrint (std::ostream& os) const;
+          virtual std::ostream& dotPrint (std::ostream& os, dot::DrawingAttributes da = dot::DrawingAttributes ()) const;
 
           /// Create inner waypoints.
           /// \param depth the number of waypoints between from() and to()
@@ -219,7 +219,7 @@ namespace hpp {
           void insertConfigConstraint (const LockedDofPtr_t lockedDof);
 
           /// Print the object in a stream.
-          virtual std::ostream& dotPrint (std::ostream& os) const;
+          virtual std::ostream& dotPrint (std::ostream& os, dot::DrawingAttributes da = dot::DrawingAttributes ()) const;
 
         protected:
           /// Initialization of the object.
diff --git a/include/hpp/manipulation/graph/graph-component.hh b/include/hpp/manipulation/graph/graph-component.hh
index 1644fe4..74f66ee 100644
--- a/include/hpp/manipulation/graph/graph-component.hh
+++ b/include/hpp/manipulation/graph/graph-component.hh
@@ -24,6 +24,7 @@
 # include "hpp/manipulation/config.hh"
 # include "hpp/manipulation/fwd.hh"
 # include "hpp/manipulation/graph/fwd.hh"
+# include "hpp/manipulation/graph/dot.hh"
 
 namespace hpp {
   namespace manipulation {
@@ -74,7 +75,7 @@ namespace hpp {
           void parentGraph(const GraphWkPtr_t& parent);
 
           /// Print the component in DOT language.
-          virtual std::ostream& dotPrint (std::ostream& os) const;
+          virtual std::ostream& dotPrint (std::ostream& os, dot::DrawingAttributes da = dot::DrawingAttributes ()) const;
 
         protected:
           /// Initialize the component
diff --git a/include/hpp/manipulation/graph/graph.hh b/include/hpp/manipulation/graph/graph.hh
index 55e7f76..77050d1 100644
--- a/include/hpp/manipulation/graph/graph.hh
+++ b/include/hpp/manipulation/graph/graph.hh
@@ -84,7 +84,7 @@ namespace hpp {
           const RobotPtr_t& robot () const;
 
           /// Print the component in DOT language.
-          virtual std::ostream& dotPrint (std::ostream& os) const;
+          virtual std::ostream& dotPrint (std::ostream& os, dot::DrawingAttributes da = dot::DrawingAttributes ()) const;
 
         protected:
           /// Initialization of the object.
diff --git a/include/hpp/manipulation/graph/node-selector.hh b/include/hpp/manipulation/graph/node-selector.hh
index 875b99f..de0e0cf 100644
--- a/include/hpp/manipulation/graph/node-selector.hh
+++ b/include/hpp/manipulation/graph/node-selector.hh
@@ -55,7 +55,7 @@ namespace hpp {
           }
 
           /// Print the object in a stream.
-          std::ostream& dotPrint (std::ostream& os) const;
+          std::ostream& dotPrint (std::ostream& os, dot::DrawingAttributes da = dot::DrawingAttributes ()) const;
 
         protected:
           /// Initialization of the object.
diff --git a/include/hpp/manipulation/graph/node.hh b/include/hpp/manipulation/graph/node.hh
index 15c83e8..6dde6d6 100644
--- a/include/hpp/manipulation/graph/node.hh
+++ b/include/hpp/manipulation/graph/node.hh
@@ -111,7 +111,7 @@ namespace hpp {
           }
 
           /// Print the object in a stream.
-          std::ostream& dotPrint (std::ostream& os) const;
+          std::ostream& dotPrint (std::ostream& os, dot::DrawingAttributes da = dot::DrawingAttributes ()) const;
 
         protected:
           /// Initialize the object.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f1cff32..3073519 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -35,6 +35,8 @@ ADD_LIBRARY(${LIBRARY_NAME} SHARED
   graph/graph-component.cc
   graph/node-selector.cc
   graph/statistics.cc
+
+  graph/dot.cc
 )
 
 PKG_CONFIG_USE_DEPENDENCY(${LIBRARY_NAME} hpp-core)
diff --git a/src/graph/dot.cc b/src/graph/dot.cc
new file mode 100644
index 0000000..dfe592c
--- /dev/null
+++ b/src/graph/dot.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2014, LAAS-CNRS
+// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
+//
+// This file is part of hpp-manipulation.
+// hpp-manipulation 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 version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>.
+
+#include "hpp/manipulation/graph/dot.hh"
+
+namespace hpp {
+  namespace manipulation {
+    namespace graph {
+      namespace dot {
+        std::ostream& operator<< (std::ostream& os, const DrawingAttributes& da)
+        {
+          if (da.attr.empty ()) return os;
+          os << "[";
+          size_t i = da.attr.size ();
+          for (DrawingAttributes::Map::const_iterator it = da.attr.begin ();
+              it != da.attr.end (); ++it) {
+            os << it->first << "=" << it->second; 
+            i--;
+            if (i > 0) os << ", ";
+          }
+          return os << "]";
+        }
+
+        std::ostream& insertComments (std::ostream& os, const std::string& c)
+        {
+          return os << "/*" << std::endl << c << std::endl << "*/";
+        }
+      } // namespace dot
+    } // namespace graph
+  } // namespace manipulation
+} // namespace hpp
diff --git a/src/graph/edge.cc b/src/graph/edge.cc
index feb5d8a..d099b92 100644
--- a/src/graph/edge.cc
+++ b/src/graph/edge.cc
@@ -82,9 +82,11 @@ namespace hpp {
         return os;
       }
 
-      std::ostream& Edge::dotPrint (std::ostream& os) const
+      std::ostream& Edge::dotPrint (std::ostream& os, dot::DrawingAttributes da) const
       {
-        os << from()->id () << " -> " << to()->id () << " [shape=onormal,label=\"" << name () << "\"];";
+        da.insertWithQuote ("label", name ());
+        da.insert ("shape", "onormal");
+        os << from()->id () << " -> " << to()->id () << " " << da << ";";
         return os;
       }
 
@@ -277,9 +279,19 @@ namespace hpp {
         return os;
       }
 
-      std::ostream& WaypointEdge::dotPrint (std::ostream& os) const
+      std::ostream& WaypointEdge::dotPrint (std::ostream& os, dot::DrawingAttributes da) const
       {
-        os << from()->id () << " -> " << to()->id () << " [shape=onormal,label=\"" << name () << "\"];";
+        // First print the waypoint node, then the first edge.
+        da ["style"]="dashed";
+        waypoint_.second->dotPrint (os, da);
+        da ["style"]="solid";
+        waypoint_.first->dotPrint (os, da) << std::endl;
+        da ["style"]="dotted";
+        da ["dir"] = "both";
+        da ["arrowtail"]="dot";
+        da.insert ("shape", "onormal");
+        da.insertWithQuote ("label", name());
+        os << waypoint_.second->id () << " -> " << to()->id () << " " << da << ";";
         return os;
       }
 
@@ -291,10 +303,11 @@ namespace hpp {
         return os;
       }
 
-      std::ostream& LevelSetEdge::dotPrint (std::ostream& os) const
+      std::ostream& LevelSetEdge::dotPrint (std::ostream& os, dot::DrawingAttributes da) const
       {
-        os << from()->id () << " -> " << to()->id () << " [shape=onormal,label=\"" << name () << "\"];";
-        return os;
+        da.insert ("shape", "onormal");
+        da.insert ("style", "dashed");
+        return Edge::dotPrint (os, da);
       }
 
       bool LevelSetEdge::applyConstraints (ConfigurationIn_t, ConfigurationOut_t) const
diff --git a/src/graph/graph-component.cc b/src/graph/graph-component.cc
index e7b6f36..acc4877 100644
--- a/src/graph/graph-component.cc
+++ b/src/graph/graph-component.cc
@@ -55,7 +55,7 @@ namespace hpp {
         return os;
       }
 
-      std::ostream& GraphComponent::dotPrint (std::ostream& os) const
+      std::ostream& GraphComponent::dotPrint (std::ostream& os, dot::DrawingAttributes) const
       {
         os << id ();
         return os;
diff --git a/src/graph/graph.cc b/src/graph/graph.cc
index 5f42a22..e455cd9 100644
--- a/src/graph/graph.cc
+++ b/src/graph/graph.cc
@@ -108,9 +108,9 @@ namespace hpp {
         return edge->pathConstraint ();
       }
 
-      std::ostream& Graph::dotPrint (std::ostream& os) const
+      std::ostream& Graph::dotPrint (std::ostream& os, dot::DrawingAttributes da) const
       {
-        os << "digraph " << id() << " {" << std::endl;
+        os << "digraph " << id() << " " << da << " {" << std::endl;
         nodeSelector_->dotPrint (os);
         os << "}" << std::endl;
         return os;
diff --git a/src/graph/node-selector.cc b/src/graph/node-selector.cc
index 2b7e15e..418f470 100644
--- a/src/graph/node-selector.cc
+++ b/src/graph/node-selector.cc
@@ -59,7 +59,7 @@ namespace hpp {
         return neighborPicker ();
       }
 
-      std::ostream& NodeSelector::dotPrint (std::ostream& os) const
+      std::ostream& NodeSelector::dotPrint (std::ostream& os, dot::DrawingAttributes) const
       {
         for (Nodes_t::const_iterator it = orderedStates_.begin();
             orderedStates_.end() != it; ++it)
diff --git a/src/graph/node.cc b/src/graph/node.cc
index 24b3b62..b7e30bf 100644
--- a/src/graph/node.cc
+++ b/src/graph/node.cc
@@ -58,12 +58,22 @@ namespace hpp {
         return configConstraint()->isSatisfied (config);
       }
 
-      std::ostream& Node::dotPrint (std::ostream& os) const
+      std::ostream& Node::dotPrint (std::ostream& os, dot::DrawingAttributes da) const
       {
-        os << id () << " [label=\"" << name () << "\"];" << std::endl;
+        da.insertWithQuote ("label", name ());
+        da.insert ("style","filled");
+        os << id () << " " << da << ";" << std::endl;
+
+        dot::DrawingAttributes dac;
+        std::vector <double> p = neighbors_.probabilities ();
+        size_t i = 0;
         for (Neighbors_t::const_iterator it = neighbors_.begin();
-            it != neighbors_.end(); ++it)
-          it->second->dotPrint (os) << std::endl;
+            it != neighbors_.end(); ++it) {
+          std::ostringstream oss; oss << (p[i] * 3 + 0.5);
+          dac ["penwidth"] = oss.str ();
+          i++;
+          it->second->dotPrint (os, dac) << std::endl;
+        }
         return os;
       }
 
-- 
GitLab