// Created on: 2022-05-11 // Copyright (c) 2022 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. #include #include #include #include #include #include #include #include "KI_XCAFDoc_AssemblyGraph.hxx" #include #include // ======================================================================= // function : KI_XCAFDoc_AssemblyGraph constructor // purpose : Builds an assembly graph from the OCAF document // ======================================================================= KI_XCAFDoc_AssemblyGraph::KI_XCAFDoc_AssemblyGraph(const Handle(TDocStd_Document)& theDoc) { Standard_NullObject_Raise_if(theDoc.IsNull(), "Null document!"); myShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDoc->Main()); Standard_NoSuchObject_Raise_if(myShapeTool.IsNull(), "No XCAFDoc_ShapeTool attribute!"); TDF_Label aDummy; buildGraph(aDummy); } // ======================================================================= // function : KI_XCAFDoc_AssemblyGraph constructor // purpose : Builds an assembly graph from the OCAF label // ======================================================================= KI_XCAFDoc_AssemblyGraph::KI_XCAFDoc_AssemblyGraph(const TDF_Label& theLabel) { Standard_NullObject_Raise_if(theLabel.IsNull(), "Null label!"); myShapeTool = XCAFDoc_DocumentTool::ShapeTool(theLabel); Standard_NoSuchObject_Raise_if(myShapeTool.IsNull(), "No XCAFDoc_ShapeTool attribute!"); buildGraph(theLabel); } // ======================================================================= // function : IsDirectLink // purpose : Checks if one node is the direct child of other one // ======================================================================= Standard_Boolean KI_XCAFDoc_AssemblyGraph::IsDirectLink(const Standard_Integer theNode1, const Standard_Integer theNode2) const { if (!HasChildren(theNode1)) return Standard_False; return GetChildren(theNode1).Contains(theNode2); } // ======================================================================= // function : GetNodeType // purpose : Returns node type // ======================================================================= KI_XCAFDoc_AssemblyGraph::NodeType KI_XCAFDoc_AssemblyGraph::GetNodeType(const Standard_Integer theNode) const { const NodeType* typePtr = myNodeTypes.Seek(theNode); if (typePtr == NULL) return NodeType_UNDEFINED; return (*typePtr); } // ======================================================================= // function : NbLinks // purpose : Calculates and returns the number of links // ======================================================================= Standard_Integer KI_XCAFDoc_AssemblyGraph::NbLinks() const { Standard_Integer aNumLinks = 0; for (AdjacencyMap::Iterator it(myAdjacencyMap); it.More(); it.Next()) { aNumLinks += it.Value().Extent(); } return aNumLinks; } // ======================================================================= // function : GetUsageOccurrenceQuantity // purpose : // ======================================================================= Standard_Integer KI_XCAFDoc_AssemblyGraph::NbOccurrences(const Standard_Integer theNode) const { const Standard_Integer* aUsageOQPtr = myUsages.Seek(theNode); if (aUsageOQPtr == NULL) return 0; return (*aUsageOQPtr); } // ======================================================================= // function : buildGraph // purpose : Builds an assembly graph from the OCAF document // ======================================================================= void KI_XCAFDoc_AssemblyGraph::buildGraph(const TDF_Label& theLabel) { // We start from those shapes which are "free" in terms of XDE. TDF_LabelSequence aRoots; if (theLabel.IsNull() || (myShapeTool->Label() == theLabel)) myShapeTool->GetFreeShapes(aRoots); else aRoots.Append(theLabel); for (TDF_LabelSequence::Iterator it(aRoots); it.More(); it.Next()) { TDF_Label aLabel = it.Value(); TDF_Label anOriginal; if (!myShapeTool->GetReferredShape(aLabel, anOriginal)) anOriginal = aLabel; const Standard_Integer aRootId = addNode(anOriginal, 0); if (aRootId == 0) continue; myRoots.Add(aRootId); // Add components (the objects nested into the current one). if (myShapeTool->IsAssembly(anOriginal)) addComponents(anOriginal, aRootId); } } // ======================================================================= // function : addComponents // purpose : Adds components for the given parent to the graph structure // ======================================================================= void KI_XCAFDoc_AssemblyGraph::addComponents(const TDF_Label& theParent, const Standard_Integer theParentId) { if (!myShapeTool->IsShape(theParent)) { 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 anIt(theParent); anIt.More(); anIt.Next()) { TDF_Label aComponent = anIt.Value(); // Add component const Standard_Integer aComponentId = addNode(aComponent, theParentId); if (aComponentId == 0) continue; // Protection against deleted empty labels (after expand compounds, for example). Handle(TDataStd_TreeNode) aJumpNode; if (!aComponent.FindAttribute(XCAFDoc::ShapeRefGUID(), aJumpNode)) continue; // Jump to the referred object (the original). TDF_Label aChildOriginal; if (!aJumpNode.IsNull() && aJumpNode->HasFather()) aChildOriginal = aJumpNode->Father()->Label(); // Declaration-level origin. if (aChildOriginal.IsNull()) continue; // Add child const Standard_Integer aChildId = addNode(aChildOriginal, aComponentId); if (aChildId == 0) continue; // Process children: add components recursively. addComponents(aChildOriginal, aChildId); } } // ======================================================================= // function : addNode // purpose : Adds node into the graph // ======================================================================= Standard_Integer KI_XCAFDoc_AssemblyGraph::addNode(const TDF_Label& theLabel, const Standard_Integer theParentId) { NodeType aNodeType = NodeType_UNDEFINED; if (myShapeTool->IsAssembly(theLabel)) { if (myShapeTool->IsFree(theLabel)) aNodeType = NodeType_AssemblyRoot; else aNodeType = NodeType_Subassembly; } else if (myShapeTool->IsComponent(theLabel)) { aNodeType = NodeType_Occurrence; } else if (myShapeTool->IsSubShape(theLabel)) { aNodeType = NodeType_Subshape; } else if (myShapeTool->IsSimpleShape(theLabel)) { aNodeType = NodeType_Part; } if (aNodeType == NodeType_UNDEFINED) return 0; // Get ID of the insertion-level node in the abstract assembly graph. const Standard_Integer aChildId = myNodes.Add(theLabel); myNodeTypes.Bind(aChildId, aNodeType); if (aNodeType != NodeType_Occurrence) { // Bind usage occurrences. Standard_Integer* aUsageOQPtr = myUsages.ChangeSeek(aChildId); if (aUsageOQPtr == NULL) aUsageOQPtr = myUsages.Bound(aChildId, 1); else ++(*aUsageOQPtr); } if (theParentId > 0) { // Add link TColStd_PackedMapOfInteger* aMapPtr = myAdjacencyMap.ChangeSeek(theParentId); if (aMapPtr == NULL) aMapPtr = myAdjacencyMap.Bound(theParentId, TColStd_PackedMapOfInteger()); (*aMapPtr).Add(aChildId); } return aChildId; } // ======================================================================= // function : Iterator constructor // purpose : Iteration starts from the specifid node. // ======================================================================= KI_XCAFDoc_AssemblyGraph::Iterator::Iterator(const Handle(KI_XCAFDoc_AssemblyGraph)& theGraph, const Standard_Integer theNode) { Standard_NullObject_Raise_if(theGraph.IsNull(), "Null assembly graph!"); Standard_NullObject_Raise_if(theNode < 1, "Node ID must be positive one-based integer!"); myGraph = theGraph; myCurrentIndex = theNode; }