From 51bb1b8d6d1798974d1ff452ff5a1c1f3dec1de3 Mon Sep 17 00:00:00 2001 From: ssv Date: Tue, 22 Aug 2017 16:27:26 +0300 Subject: [PATCH] 0029032: Provide XDE interface for exploration of assembly structure Add assembly graph exploration tool with unit tests. --- src/XCAFDoc/FILES | 3 + src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx | 259 +++++++++++++++ src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx | 292 +++++++++++++++++ src/XCAFDoc/XCAFDoc_ObjectId.hxx | 25 ++ src/XDEDRAW/FILES | 4 + src/XDEDRAW/XDEDRAW.cxx | 2 + src/XDEDRAW/XDEDRAW_Assemblies.cxx | 308 ++++++++++++++++++ src/XDEDRAW/XDEDRAW_Assemblies.hxx | 32 ++ src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.cxx | 52 +++ src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.hxx | 63 ++++ tests/bugs/xde/bug29032_1 | 69 ++++ tests/bugs/xde/bug29032_2 | 36 ++ 12 files changed, 1145 insertions(+) create mode 100644 src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx create mode 100644 src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx create mode 100644 src/XCAFDoc/XCAFDoc_ObjectId.hxx create mode 100644 src/XDEDRAW/XDEDRAW_Assemblies.cxx create mode 100644 src/XDEDRAW/XDEDRAW_Assemblies.hxx create mode 100644 src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.cxx create mode 100644 src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.hxx create mode 100644 tests/bugs/xde/bug29032_1 create mode 100644 tests/bugs/xde/bug29032_2 diff --git a/src/XCAFDoc/FILES b/src/XCAFDoc/FILES index a30837c6fe..32d0b2b722 100755 --- a/src/XCAFDoc/FILES +++ b/src/XCAFDoc/FILES @@ -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 index 0000000000..ab77244ce4 --- /dev/null +++ b/src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx @@ -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 + +// OCCT includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- + +#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& 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 index 0000000000..74fda9bb82 --- /dev/null +++ b/src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx @@ -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 + +// Other OCCT includes +#include +#include +#include + +//! \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. +//! +//!
+//!   G = 
+//! 
+//! +//! 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 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& 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 m_nodes; //!< Graph nodes. + t_adjacency m_arcs; //!< "Part-of" relations. + NCollection_DataMap 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 index 0000000000..648f2f91c1 --- /dev/null +++ b/src/XCAFDoc/XCAFDoc_ObjectId.hxx @@ -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 + +//! Persistent object ID. +typedef TCollection_AsciiString XCAFDoc_ObjectId; + +#endif diff --git a/src/XDEDRAW/FILES b/src/XDEDRAW/FILES index 9787e20eac..a480f8152a 100644 --- a/src/XDEDRAW/FILES +++ b/src/XDEDRAW/FILES @@ -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 diff --git a/src/XDEDRAW/XDEDRAW.cxx b/src/XDEDRAW/XDEDRAW.cxx index c70cbfb3aa..1945ab02fa 100644 --- a/src/XDEDRAW/XDEDRAW.cxx +++ b/src/XDEDRAW/XDEDRAW.cxx @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -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 index 0000000000..e0a66e3be9 --- /dev/null +++ b/src/XDEDRAW/XDEDRAW_Assemblies.cxx @@ -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 + +// Other OCCT includes +#include +#include +#include +#include + +// Standard includes +#include + +//----------------------------------------------------------------------------- + +std::vector< std::pair > + 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 index 0000000000..361fcb9fd2 --- /dev/null +++ b/src/XDEDRAW/XDEDRAW_Assemblies.hxx @@ -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 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 index 0000000000..3e6751b8e9 --- /dev/null +++ b/src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.cxx @@ -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::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 index 0000000000..01683a545b --- /dev/null +++ b/src/XDEDRAW/XDEDRAW_DrawableAssemblyGraph.hxx @@ -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 + +// OCCT includes +#include + +//! 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 index 0000000000..5de5934690 --- /dev/null +++ b/tests/bugs/xde/bug29032_1 @@ -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 index 0000000000..5e8b0ff82f --- /dev/null +++ b/tests/bugs/xde/bug29032_2 @@ -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" +} -- 2.39.5