]> OCCT Git - occt-copy.git/commitdiff
0029032: Provide XDE interface for exploration of assembly structure
authorssv <sergey.slyadnev@gmail.com>
Tue, 22 Aug 2017 13:27:26 +0000 (16:27 +0300)
committerssv <sergey.slyadnev@gmail.com>
Tue, 22 Aug 2017 13:27:26 +0000 (16:27 +0300)
Add assembly graph exploration tool with unit tests.

12 files changed:
src/XCAFDoc/FILES
src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx [new file with mode: 0644]
src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx [new file with mode: 0644]
src/XCAFDoc/XCAFDoc_ObjectId.hxx [new file with mode: 0644]
src/XDEDRAW/FILES
src/XDEDRAW/XDEDRAW.cxx
src/XDEDRAW/XDEDRAW_Assemblies.cxx [new file with mode: 0644]
src/XDEDRAW/XDEDRAW_Assemblies.hxx [new file with mode: 0644]
src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.cxx [new file with mode: 0644]
src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.hxx [new file with mode: 0644]
tests/bugs/xde/bug29032_1 [new file with mode: 0644]
tests/bugs/xde/bug29032_2 [new file with mode: 0644]

index a30837c6fed1ac812fc8b4ae5b3644e4b38633d8..32d0b2b722176e7cbf51fff1b41b3bd3b2a35eac 100755 (executable)
@@ -4,6 +4,8 @@ XCAFDoc.cxx
 XCAFDoc.hxx
 XCAFDoc_Area.cxx
 XCAFDoc_Area.hxx
+XCAFDoc_AssemblyGraph.cxx
+XCAFDoc_AssemblyGraph.hxx
 XCAFDoc_Centroid.cxx
 XCAFDoc_Centroid.hxx
 XCAFDoc_ClippingPlaneTool.cxx
@@ -40,6 +42,7 @@ XCAFDoc_Material.cxx
 XCAFDoc_Material.hxx
 XCAFDoc_MaterialTool.cxx
 XCAFDoc_MaterialTool.hxx
+XCAFDoc_ObjectId.hxx
 XCAFDoc_ShapeMapTool.cxx
 XCAFDoc_ShapeMapTool.hxx
 XCAFDoc_ShapeTool.cxx
diff --git a/src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx b/src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx
new file mode 100644 (file)
index 0000000..ab77244
--- /dev/null
@@ -0,0 +1,259 @@
+// Created on: 2017-08-22
+// Created by: Sergey SLYADNEV
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+// Own include
+#include <XCAFDoc_AssemblyGraph.hxx>
+
+// OCCT includes
+#include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
+#include <TDataStd_Name.hxx>
+#include <TDataStd_TreeNode.hxx>
+#include <TDF_ChildIterator.hxx>
+#include <TDF_LabelSequence.hxx>
+#include <TDF_Tool.hxx>
+#include <XCAFDoc.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+
+//-----------------------------------------------------------------------------
+
+#define NodeLetter "N"
+#define Whitespace "    "
+
+//-----------------------------------------------------------------------------
+
+XCAFDoc_AssemblyGraph::XCAFDoc_AssemblyGraph(const Handle(TDocStd_Document)& M,
+                                             const bool                      withParts)
+: Standard_Transient (),
+  m_model            (M),
+  m_bWithParts       (withParts)
+{
+  this->buildGraph();
+}
+
+//-----------------------------------------------------------------------------
+
+void XCAFDoc_AssemblyGraph::Dump(Standard_OStream& out) const
+{
+  // Directed graph header
+  out << "digraph core_AssemblyGraph {\n";
+  out << "\n";
+
+  // Dump nodes with attributes
+  const NCollection_IndexedMap<XCAFDoc_ObjectId>& nodes = this->GetNodes();
+  //
+  for ( int n = 1; n <= nodes.Extent(); ++n )
+  {
+    // Get name of persistent object
+    TCollection_ExtendedString name;
+    this->getObjectName(nodes(n), name);
+
+    // Generate label
+    TCollection_AsciiString label(name);
+    label += "\\n"; label += nodes(n);
+
+    // Dump node with label
+    out << Whitespace << NodeLetter << n << " [label=\"" << label.ToCString() << "\"];\n";
+  }
+  out << "\n";
+
+  // Dump arcs
+  for ( t_adjacency::Iterator it(m_arcs); it.More(); it.Next() )
+  {
+    const int                         parentId = it.Key();
+    const TColStd_PackedMapOfInteger& children = it.Value();
+
+    // Loop over the children
+    for ( TColStd_MapIteratorOfPackedMapOfInteger cit(children); cit.More(); cit.Next() )
+    {
+      const int childId = cit.Key();
+
+      out << Whitespace
+          << NodeLetter << parentId
+          << " -> "
+          << NodeLetter << childId
+          << ";\n";
+    }
+  }
+
+  out << "\n";
+  out << "}\n";
+}
+
+//-----------------------------------------------------------------------------
+
+void XCAFDoc_AssemblyGraph::CalculateSummary(int& numRoots,
+                                             int& numSubassemblies,
+                                             int& numPartOccurrences,
+                                             int& numParts) const
+{
+  numRoots           = 0;
+  numSubassemblies   = 0;
+  numPartOccurrences = 0;
+  numParts           = 0;
+
+  // Loop over the nodes
+  for ( int n = 1; n <= this->GetNodes().Extent(); ++n )
+  {
+    if ( !m_nodeTypes.IsBound(n) ) continue; // This should never happen
+
+    switch ( m_nodeTypes(n) )
+    {
+      case NodeType_Root:           ++numRoots;           break;
+      case NodeType_Subassembly:    ++numSubassemblies;   break;
+      case NodeType_PartOccurrence: ++numPartOccurrences; break;
+      case NodeType_Part:           ++numParts;           break;
+      default: break;
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+
+void XCAFDoc_AssemblyGraph::buildGraph()
+{
+  // Get shape tool
+  Handle(XCAFDoc_ShapeTool)
+    shapeTool = XCAFDoc_DocumentTool::ShapeTool( m_model->Main() );
+
+  // We start from those shapes which are "free" in terms of XDE
+  TDF_LabelSequence roots;
+  //
+  shapeTool->GetFreeShapes(roots);
+  //
+  for ( TDF_LabelSequence::Iterator it(roots); it.More(); it.Next() )
+  {
+    const TDF_Label& label = it.Value();
+
+    // Get entry of the current label
+    XCAFDoc_ObjectId objectId;
+    TDF_Tool::Entry(label, objectId);
+
+    // Free shapes are root nodes of the assembly graph
+    const int iObjectId = m_nodes.Add(objectId);
+
+    // Mark as root
+    m_nodeTypes.Bind(iObjectId, NodeType_Root);
+    //
+    m_roots.Add(iObjectId);
+
+    // Add components (the objects nested into the current one)
+    this->addComponents(label, iObjectId);
+  }
+}
+
+//-----------------------------------------------------------------------------
+
+void XCAFDoc_AssemblyGraph::addComponents(const TDF_Label& parent,
+                                          const int        iParentId)
+{
+  // Get shape tool
+  Handle(XCAFDoc_ShapeTool)
+    shapeTool = XCAFDoc_DocumentTool::ShapeTool( m_model->Main() );
+
+  const bool isSubassembly = shapeTool->IsAssembly(parent);
+
+  // Bind topological type. We check that no attribute is associated to
+  // prevent multiple tagging of the same node from different directions
+  // of traversal.
+  if ( !m_nodeTypes.IsBound(iParentId) )
+  {
+    if ( isSubassembly )
+      m_nodeTypes.Bind(iParentId, NodeType_Subassembly);
+    else
+    {
+      m_nodeTypes.Bind(iParentId, NodeType_PartOccurrence);
+
+      // If parts are requested to participate in the graph, we add more nodes
+      if ( m_bWithParts )
+      {
+        // Get entry of the current label which is the original label
+        XCAFDoc_ObjectId partId;
+        TDF_Tool::Entry(parent, partId);
+
+        // Add node
+        const int iPartId = m_nodes.Add(partId);
+
+        // Add arc
+        if ( !m_arcs.IsBound(iParentId) )
+          m_arcs.Bind( iParentId, TColStd_PackedMapOfInteger() );
+        //
+        m_arcs(iParentId).Add(iPartId);
+
+        // Bind type
+        if ( !m_nodeTypes.IsBound(iPartId) )
+          m_nodeTypes.Bind(iPartId, NodeType_Part);
+      }
+    }
+  }
+
+  if ( !isSubassembly )
+    return; // We have to return here in order to prevent iterating by
+            // sub-labels. For parts, sub-labels are used to encode
+            // metadata which is out of interest in conceptual design
+            // intent represented by assembly graph.
+
+  // Loop over the children (persistent representation of "part-of" relation)
+  for ( TDF_ChildIterator cit(parent); cit.More(); cit.Next() )
+  {
+    TDF_Label child = cit.Value();
+
+    // Get entry of the current label
+    XCAFDoc_ObjectId childId;
+    TDF_Tool::Entry(child, childId);
+
+    // Add node
+    const int iChildId = m_nodes.Add(childId);
+
+    // Add arc
+    if ( !m_arcs.IsBound(iParentId) )
+      m_arcs.Bind( iParentId, TColStd_PackedMapOfInteger() );
+    //
+    m_arcs(iParentId).Add(iChildId);
+
+    // Jump to the referred object (the original)
+    TDF_Label childOriginal;
+    Handle(TDataStd_TreeNode) jumpTreeNode;
+    child.FindAttribute(XCAFDoc::ShapeRefGUID(), jumpTreeNode);
+    //
+    if ( !jumpTreeNode.IsNull() && jumpTreeNode->HasFather() )
+      childOriginal = jumpTreeNode->Father()->Label(); // Declaration-level origin
+
+    // Process children: add components recursively
+    if ( !childOriginal.IsNull() )
+      this->addComponents(childOriginal, iChildId);
+  }
+}
+
+//-----------------------------------------------------------------------------
+
+bool XCAFDoc_AssemblyGraph::getObjectName(const XCAFDoc_ObjectId&     id,
+                                          TCollection_ExtendedString& name) const
+{
+  // Get label for object ID
+  TDF_Label label;
+  TDF_Tool::Label(m_model->GetData(), id, label);
+
+  // Access name
+  Handle(TDataStd_Name) nameAttr;
+  if ( !label.FindAttribute(TDataStd_Name::GetID(), nameAttr) )
+  {
+    name = "";
+    return false;
+  }
+  //
+  name = nameAttr->Get();
+  return true;
+}
diff --git a/src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx b/src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx
new file mode 100644 (file)
index 0000000..74fda9b
--- /dev/null
@@ -0,0 +1,292 @@
+// Created on: 2017-08-22
+// Created by: Sergey SLYADNEV
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _XCAFDoc_AssemblyGraph_HeaderFile
+#define _XCAFDoc_AssemblyGraph_HeaderFile
+
+// XDE includes
+#include <XCAFDoc_ObjectId.hxx>
+
+// Other OCCT includes
+#include <NCollection_DataMap.hxx>
+#include <TColStd_PackedMapOfInteger.hxx>
+#include <TDocStd_Document.hxx>
+
+//! \brief Assembly graph.
+//!
+//! This tool gives clear OCAF-agnostic interface to
+//! the assembly structure of a product. A graph is essentially a set of
+//! nodes {N} and a set of arcs {A} as defined formally.
+//!
+//! <pre>
+//!   G = <N, A>
+//! </pre>
+//!
+//! Using this tool, you can map XDE assembly items to a formal graph
+//! structure. Each node in the graph preserves a link to the data storage
+//! (OCAF document) by means of \ref XCAFDoc_ObjectId.
+//!
+//! \sa XCAFDoc_ObjectId
+class XCAFDoc_AssemblyGraph : public Standard_Transient
+{
+public:
+
+  //! \brief Type of the graph node.
+  enum NodeType
+  {
+    NodeType_UNDEFINED = 0,  //!< Undefined node type.
+    //
+    NodeType_Root,           //!< Root node (has no entry arcs).
+    NodeType_Subassembly,    //!< Intermediate node (non-leaf node which has entry arcs).
+    NodeType_PartOccurrence, //!< Part usage occurrence.
+    NodeType_Part            //!< Optional leaf node to represent parts. Note that
+                             //!< this node type is activated by a dedicated flag in
+                             //!< the constructor. If activated, the part occurrence nodes
+                             //!< are not leafs anymore.
+  };
+
+public:
+
+  // OCCT RTTI
+  DEFINE_STANDARD_RTTI_INLINE(XCAFDoc_AssemblyGraph, Standard_Transient)
+
+public:
+
+  //! \brief Graph iterator.
+  class Iterator
+  {
+  public:
+
+    //! Default ctor.
+    Iterator() : m_iCurrentIndex(0) {}
+
+    //! ctor accepting the assembly graph to iterate.
+    //! \param[in] asmGraph assembly graph to iterate.
+    Iterator(const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
+    {
+      this->Init(asmGraph);
+    }
+
+  public:
+
+    //! Initializes iterator with assembly graph.
+    //! \param[in] asmGraph assembly graph to iterate.
+    void Init(const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
+    {
+      m_graph         = asmGraph;
+      m_iCurrentIndex = 1;
+    }
+
+    //! Checks if there are more graph nodes to iterate.
+    //! \return true/false.
+    bool More() const
+    {
+      return m_iCurrentIndex <= m_graph->GetNodes().Extent();
+    }
+
+    //! \return 1-based ID of the current node.
+    int GetCurrentNode() const
+    {
+      return m_iCurrentIndex;
+    }
+
+    //! Moves iterator to the next position.
+    void Next()
+    {
+      ++m_iCurrentIndex;
+    }
+
+  protected:
+
+    Handle(XCAFDoc_AssemblyGraph) m_graph;         //!< Assembly graph to iterate.
+    int                           m_iCurrentIndex; //!< Current 1-based node ID.
+
+  };
+
+public:
+
+  //! Type definition for graph adjacency matrix. This is how parent-component
+  //! links are realized in the assembly graph.
+  typedef NCollection_DataMap<int, TColStd_PackedMapOfInteger> t_adjacency;
+
+public:
+
+  //! \brief Initializes graph from Data Model.
+  //!
+  //! Construction of a formal graph will be done immediately at ctor.
+  //!
+  //! \param[in] M         Data Model to iterate.
+  //! \param[in] withParts indicates whether to add nodes representing the
+  //!                      instanced parts to the assembly graph.
+  Standard_EXPORT
+    XCAFDoc_AssemblyGraph(const Handle(TDocStd_Document)& M,
+                          const bool                      withParts = false);
+
+public:
+
+  //! \brief Dumps graph structure to output stream.
+  //!
+  //! The output format is DOT. You may use graph rendering tools like
+  //! Graphviz to parse the output.
+  //!
+  //! \param[out] out output stream.
+  Standard_EXPORT void
+    Dump(Standard_OStream& out) const;
+
+  //! \brief Calculates short summary for the assembly.
+  //!
+  //! Short summary gives you the total number of nodes of a particular
+  //! type. Note that the number of parts will be calculated only if parts
+  //! are available in the graph (which is the option by construction).
+  //!
+  //! \param[out] numRoots           number of root nodes.
+  //! \param[out] numSubassemblies   number of subassembly nodes.
+  //! \param[out] numPartOccurrences number of part usage occurrence nodes.
+  //! \param[out] numParts           number of parts (if available).
+  Standard_EXPORT void
+    CalculateSummary(int& numRoots,
+                     int& numSubassemblies,
+                     int& numPartOccurrences,
+                     int& numParts) const;
+
+public:
+
+  //! \brief Returns IDs of the root nodes.
+  //! \return IDs of the root nodes.
+  const TColStd_PackedMapOfInteger& GetRoots() const
+  {
+    return m_roots;
+  }
+
+  //! \brief Checks whether the assembly graph contains (n1, n2) directed arc.
+  //! \param[in] n1 one-based ID of the first node.
+  //! \param[in] n2 one-based ID of the second node.
+  //! \return true/false.
+  bool HasArc(const int n1, const int n2) const
+  {
+    if ( !this->HasChildren(n1) )
+      return false;
+
+    return this->GetChildren(n1).Contains(n2);
+  }
+
+  //! \brief Checks whether children exist for the given node.
+  //! \param[in] oneBasedNodeId one-based node ID.
+  //! \return true/false.
+  bool HasChildren(const int oneBasedNodeId) const
+  {
+    return m_arcs.IsBound(oneBasedNodeId);
+  }
+
+  //! \brief Returns IDs of child nodes for the given node.
+  //! \param[in] oneBasedNodeId one-based node ID.
+  //! \return set of child IDs.
+  const TColStd_PackedMapOfInteger& GetChildren(const int oneBasedNodeId) const
+  {
+    return m_arcs(oneBasedNodeId);
+  }
+
+  //! \brief Returns the node type from \ref NodeType enum.
+  //! \param[in] oneBasedNodeId one-based node ID.
+  //! \return node type.
+  //! \sa NodeType
+  NodeType GetNodeType(const int oneBasedNodeId) const
+  {
+    if ( !m_nodeTypes.IsBound(oneBasedNodeId) )
+      return NodeType_UNDEFINED;
+
+    return m_nodeTypes(oneBasedNodeId);
+  }
+
+  //! \brief returns object ID by node ID.
+  //! \param[in] oneBasedNodeId one-based node ID.
+  //! \return persistent ID.
+  const XCAFDoc_ObjectId& GetPersistentId(const int oneBasedNodeId) const
+  {
+    return m_nodes(oneBasedNodeId);
+  }
+
+  //! \brief Returns the unordered set of graph nodes.
+  //! \return graph nodes.
+  const NCollection_IndexedMap<XCAFDoc_ObjectId>& GetNodes() const
+  {
+    return m_nodes;
+  }
+
+  //! \brief Returns the number of graph nodes.
+  //! \return number of graph nodes.
+  int GetNumberOfNodes() const
+  {
+    return m_nodes.Extent();
+  }
+
+  //! \brief Returns the collection of graph arcs in form of adjacency matrix.
+  //! \return graph arcs.
+  const t_adjacency& GetArcs() const
+  {
+    return m_arcs;
+  }
+
+  //! \brief Returns the number of graph arcs.
+  //! \return number of graph arcs.
+  int GetNumberOfArcs() const
+  {
+    int numArcs = 0;
+    //
+    for ( t_adjacency::Iterator it(m_arcs); it.More(); it.Next() )
+      numArcs += it.Value().Extent();
+
+    return numArcs;
+  }
+
+protected:
+
+  //! Builds graph out of OCAF XDE structure.
+  Standard_EXPORT void
+    buildGraph();
+
+  //! Adds components for the given root to the graph structure.
+  //! \param[in] parent    OCAF label of the parent object.
+  //! \param[in] iParentId ID of the already registered node
+  //!                      representing the parent object in the assembly
+  //!                      graph being populated.
+  Standard_EXPORT void
+    addComponents(const TDF_Label& parent,
+                  const int        iParentId);
+
+  //! Returns object name for the given persistent ID.
+  //! \param[in]  id   persistent ID.
+  //! \param[out] name object name.
+  //! \return false if no name is associated with the object or the object
+  //!         does not exist.
+  Standard_EXPORT bool
+    getObjectName(const XCAFDoc_ObjectId&     id,
+                  TCollection_ExtendedString& name) const;
+
+protected:
+
+  // INPUTS
+  Handle(TDocStd_Document) m_model;      //!< Data Model instance.
+  bool                     m_bWithParts; //!< Indicates whether to include parts to the graph.
+
+  // OUTPUTS
+  TColStd_PackedMapOfInteger               m_roots;     //!< IDs of the root nodes.
+  NCollection_IndexedMap<XCAFDoc_ObjectId> m_nodes;     //!< Graph nodes.
+  t_adjacency                              m_arcs;      //!< "Part-of" relations.
+  NCollection_DataMap<int, NodeType>       m_nodeTypes; //!< Node types (cached for efficiency).
+
+};
+
+#endif
diff --git a/src/XCAFDoc/XCAFDoc_ObjectId.hxx b/src/XCAFDoc/XCAFDoc_ObjectId.hxx
new file mode 100644 (file)
index 0000000..648f2f9
--- /dev/null
@@ -0,0 +1,25 @@
+// Created on: 2017-08-22
+// Created by: Sergey SLYADNEV
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _XCAFDoc_ObjectId_HeaderFile
+#define _XCAFDoc_ObjectId_HeaderFile
+
+// OCCT includes
+#include <TCollection_AsciiString.hxx>
+
+//! Persistent object ID.
+typedef TCollection_AsciiString XCAFDoc_ObjectId;
+
+#endif
index 9787e20eac47ae35482c3f5c6fc38e436d524517..a480f8152af543a0aeadaf3a7c71c0376b8ede7b 100644 (file)
@@ -1,9 +1,13 @@
 XDEDRAW.cxx
 XDEDRAW.hxx
+XDEDRAW_Assemblies.hxx
+XDEDRAW_Assemblies.cxx
 XDEDRAW_Colors.cxx
 XDEDRAW_Colors.hxx
 XDEDRAW_Common.cxx
 XDEDRAW_Common.hxx
+XDEDRAW_DrawableAssemblyGraph.cxx
+XDEDRAW_DrawableAssemblyGraph.hxx
 XDEDRAW_Layers.cxx
 XDEDRAW_Layers.hxx
 XDEDRAW_Props.cxx
index c70cbfb3aa51a8d940a998795b75dd9ad46d41fb..1945ab02fab171092819d9ef09113e790fb39c36 100644 (file)
@@ -80,6 +80,7 @@
 #include <XCAFPrs.hxx>
 #include <XCAFPrs_Driver.hxx>
 #include <XDEDRAW.hxx>
+#include <XDEDRAW_Assemblies.hxx>
 #include <XDEDRAW_Colors.hxx>
 #include <XDEDRAW_Common.hxx>
 #include <XDEDRAW_Layers.hxx>
@@ -1165,6 +1166,7 @@ void XDEDRAW::Init(Draw_Interpretor& di)
    di.Add ("XTestDoc", "XTestDoc shape", __FILE__, testDoc, g);
 
   // Specialized commands
+  XDEDRAW_Assemblies::InitCommands ( di );
   XDEDRAW_Shapes::InitCommands ( di );
   XDEDRAW_Colors::InitCommands ( di );
   XDEDRAW_Layers::InitCommands ( di );
diff --git a/src/XDEDRAW/XDEDRAW_Assemblies.cxx b/src/XDEDRAW/XDEDRAW_Assemblies.cxx
new file mode 100644 (file)
index 0000000..e0a66e3
--- /dev/null
@@ -0,0 +1,308 @@
+// Created on: 2017-08-22
+// Created by: Sergey SLYADNEV
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+// Own include
+#include <XDEDRAW_Assemblies.hxx>
+
+// Other OCCT includes
+#include <DDocStd.hxx>
+#include <Draw.hxx>
+#include <NCollection_Vector.hxx>
+#include <XDEDRAW_DrawableAssemblyGraph.hxx>
+
+// Standard includes
+#include <vector>
+
+//-----------------------------------------------------------------------------
+
+std::vector< std::pair<TCollection_AsciiString, XCAFDoc_AssemblyGraph::NodeType> >
+  XAssembly_NodeTypes = {
+    {"-roots",           XCAFDoc_AssemblyGraph::NodeType_Root},
+    {"-subassemblies",   XCAFDoc_AssemblyGraph::NodeType_Subassembly},
+    {"-partoccurrences", XCAFDoc_AssemblyGraph::NodeType_PartOccurrence},
+    {"-parts",           XCAFDoc_AssemblyGraph::NodeType_Part}
+  };
+
+//-----------------------------------------------------------------------------
+
+//! Returns true if the passed command line option specifies node type.
+//! \param[in] opt option to check.
+//! \return true/false.
+bool XAssembly_IsKeywordNodeType(const TCollection_AsciiString& opt)
+{
+  for ( size_t s = 0; s < XAssembly_NodeTypes.size(); ++s )
+    if ( opt == XAssembly_NodeTypes[s].first )
+      return true;
+
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+
+//! Checks if the given graph node is of expected type.
+//! \param[in] what     node to check.
+//! \param[in] opt      expected type.
+//! \param[in] asmGraph assembly graph.
+//! \return true/false.
+bool XAssembly_IsOfExpectedType(const int                            n,
+                                const TCollection_AsciiString&       opt,
+                                const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
+{
+  const XCAFDoc_AssemblyGraph::NodeType actualType = asmGraph->GetNodeType(n);
+
+  // Check against registered types
+  TCollection_AsciiString expectedOpt;
+  for ( size_t t = 0; t < XAssembly_NodeTypes.size(); ++t )
+  {
+    if ( actualType == XAssembly_NodeTypes[t].second )
+    {
+      expectedOpt = XAssembly_NodeTypes[t].first;
+      break;
+    }
+  }
+  if ( expectedOpt.IsEmpty() )
+    return false;
+
+  return (expectedOpt == opt);
+}
+
+//-----------------------------------------------------------------------------
+
+//! Builds assembly graph for the given model.
+//! \param[in] di   Draw interpreter instance.
+//! \param[in] argc number of arguments.
+//! \param[in] argv list of arguments.
+//! \return execution result.
+static int XAssemblyGraph(Draw_Interpretor& di, int argc, const char** argv)
+{
+  // Preliminary checks
+  if ( argc != 4 )
+  {
+    std::cout << "Incorrect number of arguments. Check help for details..." << std::endl;
+    return 1; // Failure
+  }
+
+  // Get document
+  Handle(TDocStd_Document) doc;
+  DDocStd::GetDocument(argv[1], doc);
+  if ( doc.IsNull() )
+  {
+    di << argv[1] << " is not a document\n"; return 1;
+  }
+
+  // Whether to include parts to assembly graph or not
+  const bool withParts = ( atoi(argv[2]) > 0 );
+
+  // Construct assembly graph
+  Handle(XCAFDoc_AssemblyGraph) asmGraph = new XCAFDoc_AssemblyGraph(doc, withParts);
+
+  // Register assembly graph in Draw
+  Draw::Set( argv[3], new XDEDRAW_DrawableAssemblyGraph(asmGraph) );
+
+  return 0; // Success
+}
+
+//-----------------------------------------------------------------------------
+
+//! Checks assembly graph.
+//! \param[in] di   Draw interpreter instance.
+//! \param[in] argc number of arguments.
+//! \param[in] argv list of arguments.
+//! \return execution result.
+static int XAssemblyGraphCheck(Draw_Interpretor& di, int argc, const char** argv)
+{
+  // Preliminary checks
+  if ( argc < 3 )
+  {
+    std::cout << "Incorrect number of arguments. Check help for details..." << std::endl;
+    return 1; // Failure
+  }
+
+  // Get assembly graph
+  Handle(XDEDRAW_DrawableAssemblyGraph)
+    DAG = Handle(XDEDRAW_DrawableAssemblyGraph)::DownCast( Draw::Get(argv[1]) );
+  //
+  if ( DAG.IsNull() )
+  {
+    std::cout << "Error: Drawable Assembly Graph is NULL" << std::endl;
+    return 1; // Failure
+  }
+  //
+  Handle(XCAFDoc_AssemblyGraph) asmGraph = DAG->GetGraph();
+
+  // Get summary
+  int numRoots         = 0;
+  int numSubassemblies = 0;
+  int numPartInstances = 0;
+  int numParts         = 0;
+  //
+  asmGraph->CalculateSummary(numRoots, numSubassemblies, numPartInstances, numParts);
+
+  // Check according to the argument keys
+  for ( int i = 2; i < argc; ++i )
+  {
+    TCollection_AsciiString opt(argv[i]);
+    opt.LowerCase();
+
+    // Check the number of graph nodes
+    if ( opt == "-numnodes" )
+    {
+      const int num = atoi( argv[++i] );
+      //
+      if ( num != asmGraph->GetNumberOfNodes() )
+      {
+        di << 0; // FALSE
+        std::cout << "Error: unexpected number of nodes" << std::endl;
+        return 0;
+      }
+    }
+
+    // Check the number of graph arcs
+    else if ( opt == "-numarcs" )
+    {
+      const int num = atoi( argv[++i] );
+      //
+      if ( num != asmGraph->GetNumberOfArcs() )
+      {
+        di << 0; // FALSE
+        std::cout << "Error: unexpected number of arcs" << std::endl;
+        return 0;
+      }
+    }
+
+    // Check the number of roots
+    else if ( opt == "-numroots" )
+    {
+      const int num = atoi( argv[++i] );
+      //
+      if ( num != numRoots )
+      {
+        di << 0; // FALSE
+        std::cout << "Error: unexpected number of roots" << std::endl;
+        return 0;
+      }
+    }
+
+    // Check the number of subassemblies
+    else if ( opt == "-numsubassemblies" )
+    {
+      const int num = atoi( argv[++i] );
+      //
+      if ( num != numSubassemblies )
+      {
+        di << 0; // FALSE
+        std::cout << "Error: unexpected number of subassemblies" << std::endl;
+        return 0;
+      }
+    }
+
+    // Check the number of part occurrences
+    else if ( opt == "-numpartoccurrences" )
+    {
+      const int num = atoi( argv[++i] );
+      //
+      if ( num != numPartInstances )
+      {
+        di << 0; // FALSE
+        std::cout << "Error: unexpected number of part occurrences" << std::endl;
+        return 0;
+      }
+    }
+
+    // Check the number of parts
+    else if ( opt == "-numparts" )
+    {
+      const int num = atoi( argv[++i] );
+      //
+      if ( num != numParts )
+      {
+        di << 0; // FALSE
+        std::cout << "Error: unexpected number of parts" << std::endl;
+        return 0;
+      }
+    }
+
+    // Check individual arc
+    else if ( opt == "-arc" )
+    {
+      const int n1 = atoi( argv[++i] );
+      const int n2 = atoi( argv[++i] );
+      //
+      if ( !asmGraph->HasArc(n1, n2) )
+      {
+        di << 0; // FALSE
+        std::cout << "Error: arc (" << n1 << ", " << n2 << ") does not exist" << std::endl;
+        return 0;
+      }
+    }
+
+    // Check individual node types
+    else if ( XAssembly_IsKeywordNodeType(opt) )
+    {
+      do
+      {
+        // Get node index
+        const int n = atoi( argv[++i] );
+
+        // Check type
+        if ( !XAssembly_IsOfExpectedType(n, opt, asmGraph) )
+        {
+          di << 0; // FALSE
+          std::cout << "Error: unexpected type for node " << n << std::endl;
+          return 0;
+        }
+      }
+      while ( i < (argc - 1) && !XAssembly_IsKeywordNodeType(argv[i + 1]) );
+    }
+  }
+  di << 1; // TRUE
+
+  return 0; // Success
+}
+
+//=======================================================================
+//function : InitCommands
+//purpose  : 
+//=======================================================================
+
+void XDEDRAW_Assemblies::InitCommands(Draw_Interpretor& di)
+{
+  static Standard_Boolean initactor = Standard_False;
+  if (initactor)
+  {
+    return;
+  }
+  initactor = Standard_True;
+
+  Standard_CString grp = "XDE assembly commands";
+
+  di.Add("XAssemblyGraph", "XAssemblyGraph doc withParts graph",
+    __FILE__, XAssemblyGraph, grp);
+
+  di.Add("XAssemblyGraphCheck", "XAssemblyGraphCheck [-numnodes           num] "
+                                                    "[-numarcs            num] "
+                                                    "[-numroots           num] "
+                                                    "[-numsubassemblies   num] "
+                                                    "[-numpartoccurrences num] "
+                                                    "[-numparts           num] "
+                                                    "[-arc                n1 n2] "
+                                                    "[-arc                n2 n3] "
+                                                    " ... "
+                                                    "[-roots              n1 n2 ...] "
+                                                    "[-subassemblies      n3 n4 ...] "
+                                                    "[-partoccurrences    n5 n6 ...] "
+                                                    "[-parts              n7 n8 ...] ",
+    __FILE__, XAssemblyGraphCheck, grp);
+}
diff --git a/src/XDEDRAW/XDEDRAW_Assemblies.hxx b/src/XDEDRAW/XDEDRAW_Assemblies.hxx
new file mode 100644 (file)
index 0000000..361fcb9
--- /dev/null
@@ -0,0 +1,32 @@
+// Created on: 2017-08-22
+// Created by: Sergey SLYADNEV
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _XDEDRAW_Assemblies_HeaderFile
+#define _XDEDRAW_Assemblies_HeaderFile
+
+#include <Draw_Interpretor.hxx>
+
+//! Draw commands for new XDE interface dedicated to assemblies.
+class XDEDRAW_Assemblies
+{
+public:
+
+  DEFINE_STANDARD_ALLOC
+
+  Standard_EXPORT static void InitCommands(Draw_Interpretor& theCommands);
+
+};
+
+#endif // _XDEDRAW_Assemblies_HeaderFile
diff --git a/src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.cxx b/src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.cxx
new file mode 100644 (file)
index 0000000..3e6751b
--- /dev/null
@@ -0,0 +1,52 @@
+// Created on: 2017-08-22
+// Created by: Sergey SLYADNEV
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+// Own include
+#include <XDEDRAW_DrawableAssemblyGraph.hxx>
+
+//-----------------------------------------------------------------------------
+
+XDEDRAW_DrawableAssemblyGraph::XDEDRAW_DrawableAssemblyGraph(const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
+: Draw_Drawable3D (),
+  m_graph         (asmGraph)
+{}
+
+//-----------------------------------------------------------------------------
+
+inline const Handle(XCAFDoc_AssemblyGraph)& XDEDRAW_DrawableAssemblyGraph::GetGraph() const
+{
+  return m_graph;
+}
+
+//-----------------------------------------------------------------------------
+
+void XDEDRAW_DrawableAssemblyGraph::DrawOn(Draw_Display&) const
+{
+  this->Dump(std::cout);
+}
+
+//-----------------------------------------------------------------------------
+
+void XDEDRAW_DrawableAssemblyGraph::Dump(Standard_OStream& out) const
+{
+  m_graph->Dump(out);
+}
+
+//-----------------------------------------------------------------------------
+
+void XDEDRAW_DrawableAssemblyGraph::Whatis(Draw_Interpretor& di) const
+{ 
+  di << "Assembly graph";
+}
diff --git a/src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.hxx b/src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.hxx
new file mode 100644 (file)
index 0000000..01683a5
--- /dev/null
@@ -0,0 +1,63 @@
+// Created on: 2017-08-22
+// Created by: Sergey SLYADNEV
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _XDEDRAW_DrawableAssemblyGraph_HeaderFile
+#define _XDEDRAW_DrawableAssemblyGraph_HeaderFile
+
+// XDE includes
+#include <XCAFDoc_AssemblyGraph.hxx>
+
+// OCCT includes
+#include <Draw_Drawable3D.hxx>
+
+//! Drawable container for assembly graph.
+class XDEDRAW_DrawableAssemblyGraph : public Draw_Drawable3D
+{
+public:
+
+  // OCCT RTTI
+  DEFINE_STANDARD_RTTI_INLINE(XDEDRAW_DrawableAssemblyGraph, Draw_Drawable3D)
+
+public:
+
+  //! Ctor accepting an assembly graph.
+  //! \param[in] asmGraph assembly graph to have as drawable.
+  Standard_EXPORT
+    XDEDRAW_DrawableAssemblyGraph(const Handle(XCAFDoc_AssemblyGraph)& asmGraph);
+
+public:
+
+  //! \return stored assembly graph.
+  Standard_EXPORT const Handle(XCAFDoc_AssemblyGraph)&
+    GetGraph() const;
+
+public:
+
+  Standard_EXPORT void
+    DrawOn(Draw_Display& dis) const Standard_OVERRIDE;
+
+  Standard_EXPORT virtual void
+    Dump(Standard_OStream& out) const Standard_OVERRIDE;
+
+  Standard_EXPORT virtual void
+    Whatis(Draw_Interpretor& di) const Standard_OVERRIDE;
+
+private:
+
+  Handle(XCAFDoc_AssemblyGraph) m_graph; //!< Assembly graph to "draw".
+
+};
+
+#endif
diff --git a/tests/bugs/xde/bug29032_1 b/tests/bugs/xde/bug29032_1
new file mode 100644 (file)
index 0000000..5de5934
--- /dev/null
@@ -0,0 +1,69 @@
+puts "============"
+puts "CR29032"
+puts "============"
+puts ""
+
+pload VISUALIZATION
+
+##########################################################################
+# Provide XDE interface for exploration of assembly structure
+##########################################################################
+
+ReadStep d [locate_data_file trj3_as1-tc-214.stp]
+XShow d
+vfit
+vsetdispmode 1
+
+checkview -screenshot -3d -path ${imagedir}/${::casename}_1.png
+
+# Build assembly graph with parts
+if { [XAssemblyGraph d 1 DAG] == 0 } {
+  puts "Error: cannot build assembly graph"
+}
+
+dump DAG
+
+# Check assembly graph
+set ret [XAssemblyGraphCheck DAG \
+                            -numnodes           19 \
+                            -numarcs            28 \
+                            -numroots           1 \
+                            -numsubassemblies   6 \
+                            -numpartoccurrences 7 \
+                            -numparts           5 \
+                            -arc                1 2 \
+                            -arc                1 12 \
+                            -arc                1 14 \
+                            -arc                1 15 \
+                            -arc                2 3 \
+                            -arc                2 5 \
+                            -arc                2 10 \
+                            -arc                2 11 \
+                            -arc                3 4 \
+                            -arc                5 6 \
+                            -arc                5 8 \
+                            -arc                6 7 \
+                            -arc                8 9 \
+                            -arc                10 6 \
+                            -arc                10 8 \
+                            -arc                11 6 \
+                            -arc                11 8 \
+                            -arc                12 13 \
+                            -arc                14 3 \
+                            -arc                14 5 \
+                            -arc                14 10 \
+                            -arc                14 11 \
+                            -arc                15 16 \
+                            -arc                15 18 \
+                            -arc                15 19 \
+                            -arc                16 17 \
+                            -arc                18 7 \
+                            -arc                19 7 \
+                            -roots              1 \
+                            -subassemblies      2 5 10 11 14 15 \
+                            -partoccurrences    3 8 6 12 16 18 19 \
+                            -parts              4 7 9 13 17]
+
+if { $ret == 0 } {
+  puts "Error: unexpected contents of assembly graph"
+}
diff --git a/tests/bugs/xde/bug29032_2 b/tests/bugs/xde/bug29032_2
new file mode 100644 (file)
index 0000000..5e8b0ff
--- /dev/null
@@ -0,0 +1,36 @@
+puts "============"
+puts "CR29032"
+puts "============"
+puts ""
+
+pload VISUALIZATION
+
+##########################################################################
+# Provide XDE interface for exploration of assembly structure
+##########################################################################
+
+ReadStep d [locate_data_file OCC137-ANC101-Solid.stp]
+XShow d
+vfit
+vsetdispmode 1
+
+checkview -screenshot -3d -path ${imagedir}/${::casename}_1.png
+
+# Build assembly graph with parts
+if { [XAssemblyGraph d 1 DAG] == 0 } {
+  puts "Error: cannot build assembly graph"
+}
+
+dump DAG
+
+# Check assembly graph
+set ret [XAssemblyGraphCheck DAG \
+                            -numnodes           1 \
+                            -numarcs            0 \
+                            -numroots           1 \
+                            -numsubassemblies   0 \
+                            -roots              1]
+
+if { $ret == 0 } {
+  puts "Error: unexpected contents of assembly graph"
+}