271 lines
9.0 KiB
C++
271 lines
9.0 KiB
C++
|
// 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 <Standard_NullObject.hxx>
|
||
|
#include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
|
||
|
#include <TDataStd_TreeNode.hxx>
|
||
|
#include <TDF_ChildIterator.hxx>
|
||
|
#include <TDF_Tool.hxx>
|
||
|
#include <TDocStd_Document.hxx>
|
||
|
#include <XCAFDoc.hxx>
|
||
|
#include "KI_XCAFDoc_AssemblyGraph.hxx"
|
||
|
#include <XCAFDoc_DocumentTool.hxx>
|
||
|
#include <XCAFDoc_ShapeTool.hxx>
|
||
|
|
||
|
// =======================================================================
|
||
|
// 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;
|
||
|
}
|