diff --git a/gtsam/nonlinear/NonlinearFactorGraph.cpp b/gtsam/nonlinear/NonlinearFactorGraph.cpp index 3d51ef3ac..0c22f1365 100644 --- a/gtsam/nonlinear/NonlinearFactorGraph.cpp +++ b/gtsam/nonlinear/NonlinearFactorGraph.cpp @@ -18,9 +18,12 @@ */ #include +#include #include #include #include +#include +#include #include using namespace std; @@ -43,24 +46,110 @@ void NonlinearFactorGraph::print(const std::string& str, const KeyFormatter& key } /* ************************************************************************* */ -void NonlinearFactorGraph::saveGraph(std::ostream &stm, const KeyFormatter& keyFormatter) const { +void NonlinearFactorGraph::saveGraph(std::ostream &stm, const Values& values, + const GraphvizFormatting& graphvizFormatting, + const KeyFormatter& keyFormatter) const +{ stm << "graph {\n"; + stm << " size=\"" << graphvizFormatting.figureWidthInches << "," << + graphvizFormatting.figureHeightInches << "\";\n\n"; + + FastSet keys = this->keys(); + + // Local utility function to extract x and y coordinates + struct { boost::optional operator()( + const Value& value, const GraphvizFormatting& graphvizFormatting) + { + if(const Pose2* p = dynamic_cast(&value)) { + double x, y; + switch (graphvizFormatting.paperHorizontalAxis) { + case GraphvizFormatting::X: x = p->x(); break; + case GraphvizFormatting::Y: x = p->y(); break; + case GraphvizFormatting::Z: x = 0.0; break; + case GraphvizFormatting::NEGX: x = -p->x(); break; + case GraphvizFormatting::NEGY: x = -p->y(); break; + case GraphvizFormatting::NEGZ: x = 0.0; break; + } + switch (graphvizFormatting.paperVerticalAxis) { + case GraphvizFormatting::X: y = p->x(); break; + case GraphvizFormatting::Y: y = p->y(); break; + case GraphvizFormatting::Z: y = 0.0; break; + case GraphvizFormatting::NEGX: y = -p->x(); break; + case GraphvizFormatting::NEGY: y = -p->y(); break; + case GraphvizFormatting::NEGZ: y = 0.0; break; + } + return Point2(x,y); + } else if(const Pose3* p = dynamic_cast(&value)) { + double x, y; + switch (graphvizFormatting.paperHorizontalAxis) { + case GraphvizFormatting::X: x = p->x(); break; + case GraphvizFormatting::Y: x = p->y(); break; + case GraphvizFormatting::Z: x = p->z(); break; + case GraphvizFormatting::NEGX: x = -p->x(); break; + case GraphvizFormatting::NEGY: x = -p->y(); break; + case GraphvizFormatting::NEGZ: x = -p->z(); break; + } + switch (graphvizFormatting.paperVerticalAxis) { + case GraphvizFormatting::X: y = p->x(); break; + case GraphvizFormatting::Y: y = p->y(); break; + case GraphvizFormatting::Z: y = p->z(); break; + case GraphvizFormatting::NEGX: y = -p->x(); break; + case GraphvizFormatting::NEGY: y = -p->y(); break; + case GraphvizFormatting::NEGZ: y = -p->z(); break; + } + return Point2(x,y); + } else { + return boost::none; + } + }} getXY; // Create nodes for each variable in the graph - BOOST_FOREACH(Key key, this->keys()) { + BOOST_FOREACH(Key key, keys) { // Label the node with the label from the KeyFormatter - stm << " var" << key << "[label=\"" << keyFormatter(key) << "\"];\n"; } + stm << " var" << key << "[label=\"" << keyFormatter(key) << "\""; + if(values.exists(key)) { + boost::optional xy = getXY(values.at(key), graphvizFormatting); + if(xy) + stm << ", pos=\"" << xy->x() << "," << xy->y() << "\""; + } + stm << "];\n"; + } stm << "\n"; - // Create factors and variable connections - for(size_t i = 0; i < this->size(); ++i) { - // Make each factor a dot - stm << " factor" << i << "[label=\"\", shape=point];\n"; + if(graphvizFormatting.mergeSimilarFactors) { + // Remove duplicate factors + FastSet > structure; + BOOST_FOREACH(const sharedFactor& factor, *this) { + if(factor) { + vector factorKeys = factor->keys(); + std::sort(factorKeys.begin(), factorKeys.end()); + structure.insert(factorKeys); + } + } - // Make factor-variable connections - if(this->at(i)) { - BOOST_FOREACH(Key key, *this->at(i)) { - stm << " var" << key << "--" << "factor" << i << ";\n"; } } + // Create factors and variable connections + size_t i = 0; + BOOST_FOREACH(const vector& factorKeys, structure) { + // Make each factor a dot + stm << " factor" << i << "[label=\"\", shape=point];\n"; + + // Make factor-variable connections + BOOST_FOREACH(Key key, factorKeys) { + stm << " var" << key << "--" << "factor" << i << ";\n"; } + + ++ i; + } + } else { + // Create factors and variable connections + for(size_t i = 0; i < this->size(); ++i) { + // Make each factor a dot + stm << " factor" << i << "[label=\"\", shape=point];\n"; + + // Make factor-variable connections + if(this->at(i)) { + BOOST_FOREACH(Key key, *this->at(i)) { + stm << " var" << key << "--" << "factor" << i << ";\n"; } } + } } stm << "}\n"; diff --git a/gtsam/nonlinear/NonlinearFactorGraph.h b/gtsam/nonlinear/NonlinearFactorGraph.h index 945189a41..216e9e1c4 100644 --- a/gtsam/nonlinear/NonlinearFactorGraph.h +++ b/gtsam/nonlinear/NonlinearFactorGraph.h @@ -27,6 +27,26 @@ namespace gtsam { + /** + * Formatting options when saving in GraphViz format using + * NonlinearFactorGraph::saveGraph. + */ + struct GraphvizFormatting { + enum Axis { X, Y, Z, NEGX, NEGY, NEGZ }; ///< World axes to be assigned to paper axes + Axis paperHorizontalAxis; ///< The world axis assigned to the horizontal paper axis + Axis paperVerticalAxis; ///< The world axis assigned to the vertical paper axis + double figureWidthInches; ///< The figure width on paper in inches + double figureHeightInches; ///< The figure height on paper in inches + bool mergeSimilarFactors; ///< Merge multiple factors that have the same connectivity + /// Default constructor sets up robot coordinates. Paper horizontal is robot Y, + /// paper vertical is robot X. Default figure size of 5x5 in. + GraphvizFormatting() : + paperHorizontalAxis(Y), paperVerticalAxis(X), + figureWidthInches(5), figureHeightInches(5), + mergeSimilarFactors(false) {} + }; + + /** * A non-linear factor graph is a graph of non-Gaussian, i.e. non-linear factors, * which derive from NonlinearFactor. The values structures are typically (in SAM) more general @@ -47,7 +67,9 @@ namespace gtsam { void print(const std::string& str = "NonlinearFactorGraph: ", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const; /** Write the graph in GraphViz format for visualization */ - void saveGraph(std::ostream& stm, const KeyFormatter& keyFormatter = DefaultKeyFormatter) const; + void saveGraph(std::ostream& stm, const Values& values = Values(), + const GraphvizFormatting& graphvizFormatting = GraphvizFormatting(), + const KeyFormatter& keyFormatter = DefaultKeyFormatter) const; /** return keys as an ordered set - ordering is by key value */ FastSet keys() const;