Add assembly graph exploration tool with unit tests.
XCAFDoc.hxx
XCAFDoc_Area.cxx
XCAFDoc_Area.hxx
+XCAFDoc_AssemblyGraph.cxx
+XCAFDoc_AssemblyGraph.hxx
XCAFDoc_Centroid.cxx
XCAFDoc_Centroid.hxx
XCAFDoc_ClippingPlaneTool.cxx
XCAFDoc_Material.hxx
XCAFDoc_MaterialTool.cxx
XCAFDoc_MaterialTool.hxx
+XCAFDoc_ObjectId.hxx
XCAFDoc_ShapeMapTool.cxx
XCAFDoc_ShapeMapTool.hxx
XCAFDoc_ShapeTool.cxx
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
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
#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>
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 );
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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
--- /dev/null
+// 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";
+}
--- /dev/null
+// 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
--- /dev/null
+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"
+}
--- /dev/null
+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"
+}