]> OCCT Git - occt.git/commitdiff
0032954: Tool for applying transformation to OCAF document IR-2022-06-10
authorsnn <snn@opencascade.com>
Fri, 13 May 2022 09:53:03 +0000 (12:53 +0300)
committerafokin <afokin@opencascade.com>
Fri, 10 Jun 2022 15:56:40 +0000 (18:56 +0300)
New XCAFDoc classes:
- XCAFDoc_AssemblyIterator: iterator in depth along the assembly tree
- XCAFDoc_AssemblyGraph: assembly graph with iterator
- XCAFDoc_AssemblyTool: provides generic methods for traversing assembly tree and graph

A method for re-scaling (sub-)assembly geometry is added to XCAFDoc_Editor.

New DRAW commands:
- XDumpAssemblyTree: iterates through the assembly tree in depth up to the specified level, if any
- XDumpAssemblyGraph: prints assembly graph structure
- XDumpNomenclature: prints number of (sub-)assembly/part instances
- XRescaleGeometry: applies geometrical scale to (sub-)assembly

19 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_AssemblyIterator.cxx [new file with mode: 0644]
src/XCAFDoc/XCAFDoc_AssemblyIterator.hxx [new file with mode: 0644]
src/XCAFDoc/XCAFDoc_AssemblyTool.hxx [new file with mode: 0644]
src/XCAFDoc/XCAFDoc_Editor.cxx
src/XCAFDoc/XCAFDoc_Editor.hxx
src/XDEDRAW/XDEDRAW.cxx
tests/xcaf/end
tests/xcaf/grids.list
tests/xcaf/rescale/A1 [new file with mode: 0644]
tests/xcaf/rescale/A2 [new file with mode: 0644]
tests/xcaf/rescale/B1 [new file with mode: 0644]
tests/xcaf/rescale/C1 [new file with mode: 0644]
tests/xcaf/traverse/A1 [new file with mode: 0644]
tests/xcaf/traverse/A2 [new file with mode: 0644]
tests/xcaf/traverse/A3 [new file with mode: 0644]
tests/xcaf/traverse/B1 [new file with mode: 0644]

index f2af5883ac23a2f9225fde11d45d598399e818c2..2398d996436096e281957b2b5b01cc78fd24c762 100755 (executable)
@@ -6,6 +6,11 @@ XCAFDoc_AssemblyItemId.cxx
 XCAFDoc_AssemblyItemId.hxx
 XCAFDoc_AssemblyItemRef.cxx
 XCAFDoc_AssemblyItemRef.hxx
+XCAFDoc_AssemblyIterator.hxx
+XCAFDoc_AssemblyIterator.cxx
+XCAFDoc_AssemblyGraph.hxx
+XCAFDoc_AssemblyGraph.cxx
+XCAFDoc_AssemblyTool.hxx
 XCAFDoc_PartId.hxx
 XCAFDoc_Area.cxx
 XCAFDoc_Area.hxx
diff --git a/src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx b/src/XCAFDoc/XCAFDoc_AssemblyGraph.cxx
new file mode 100644 (file)
index 0000000..b344fae
--- /dev/null
@@ -0,0 +1,270 @@
+// 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 <XCAFDoc_AssemblyGraph.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+
+// =======================================================================
+// function : XCAFDoc_AssemblyGraph constructor
+// purpose  : Builds an assembly graph from the OCAF document
+// =======================================================================
+
+XCAFDoc_AssemblyGraph::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 : XCAFDoc_AssemblyGraph constructor
+// purpose  : Builds an assembly graph from the OCAF label
+// =======================================================================
+
+XCAFDoc_AssemblyGraph::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 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
+// =======================================================================
+
+XCAFDoc_AssemblyGraph::NodeType 
+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 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 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 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 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 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.
+// =======================================================================
+
+XCAFDoc_AssemblyGraph::Iterator::Iterator(const Handle(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;
+}
diff --git a/src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx b/src/XCAFDoc/XCAFDoc_AssemblyGraph.hxx
new file mode 100644 (file)
index 0000000..96638b1
--- /dev/null
@@ -0,0 +1,220 @@
+// 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.
+
+#ifndef _XCAFDoc_AssemblyGraph_HeaderFile
+#define _XCAFDoc_AssemblyGraph_HeaderFile
+
+#include <NCollection_DataMap.hxx>
+#include <NCollection_IndexedMap.hxx>
+#include <Standard.hxx>
+#include <Standard_Type.hxx>
+#include <TCollection_AsciiString.hxx>
+#include <TColStd_PackedMapOfInteger.hxx>
+#include <TDF_LabelIndexedMap.hxx>
+
+class TDF_Label;
+class TDocStd_Document;
+class XCAFDoc_ShapeTool;
+
+class XCAFDoc_AssemblyGraph;
+DEFINE_STANDARD_HANDLE(XCAFDoc_AssemblyGraph, Standard_Transient)
+
+// Assembly graph.
+class XCAFDoc_AssemblyGraph : public Standard_Transient
+{
+public:
+
+  //! \brief Type of the graph node.
+  enum NodeType
+  {
+    NodeType_UNDEFINED = 0, //!< Undefined node type.
+    NodeType_AssemblyRoot,  //!< Root node.
+    NodeType_Subassembly,   //!< Intermediate node.
+    NodeType_Occurrence,    //!< Assembly/part occurrence node.
+    NodeType_Part,          //!< Leaf node to represent parts.
+    NodeType_Subshape       //!< Subshape node.
+  };
+
+  //! \brief Type definition for graph adjacency matrix. 
+  //! This is how parent-component links are realized in the assembly graph.
+  typedef NCollection_DataMap<Standard_Integer, TColStd_PackedMapOfInteger> AdjacencyMap;
+
+public:
+
+  //! \brief Graph iterator.
+  class Iterator
+  {
+  public:
+
+    //! \brief Accepting the assembly graph and starting node to iterate.
+    //! Iteration starts from the specified node.
+    //! \param [in] theGraph - assembly graph to iterate.
+    //! \param [in] theNode  - graph node ID.
+    Standard_EXPORT Iterator(const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+                             const Standard_Integer               theNode = 1);
+
+    //! Checks if there are more graph nodes to iterate.
+    //! \return true/false.
+    Standard_Boolean More() const
+    {
+      return myCurrentIndex <= myGraph->NbNodes();
+    }
+
+    //! \return 1-based ID of the current node.
+    Standard_Integer Current() const
+    {
+      return myCurrentIndex;
+    }
+
+    //! Moves iterator to the next position.
+    void Next()
+    {
+      ++myCurrentIndex;
+    }
+
+  private:
+
+    Handle(XCAFDoc_AssemblyGraph) myGraph;        //!< Assembly graph to iterate.
+    Standard_Integer              myCurrentIndex; //!< Current 1-based node ID.
+
+  };
+
+public:
+
+  //! \brief Constructs graph from XCAF document.
+  //! Construction of a formal graph will be done immediately.
+  //! \param [in] theDoc - document to iterate.
+  Standard_EXPORT XCAFDoc_AssemblyGraph(const Handle(TDocStd_Document)& theDoc);
+
+  //! \brief Constructs graph from XCAF label.
+  //! Construction of a formal graph will be done immediately. The specified
+  //! label is used as a starting position.
+  //! \param [in] theDoc   - document to iterate.
+  //! \param [in] theLabel - starting position.
+  Standard_EXPORT XCAFDoc_AssemblyGraph(const TDF_Label& theLabel);
+
+  //! \return Document shape tool.
+  const Handle(XCAFDoc_ShapeTool)& GetShapeTool() const
+  {
+    return myShapeTool;
+  }
+
+  //! \brief Returns IDs of the root nodes.
+  //! \return IDs of the root nodes.
+  const TColStd_PackedMapOfInteger& GetRoots() const
+  {
+    return myRoots;
+  }
+
+  //! \brief Checks whether the assembly graph contains (n1, n2) directed link.
+  //! \param [in] theNode1 - one-based ID of the first node.
+  //! \param [in] theNode2 - one-based ID of the second node.
+  //! \return true/false.
+  Standard_EXPORT Standard_Boolean IsDirectLink(const Standard_Integer theNode1,
+                                                const Standard_Integer theNode2) const;
+
+  //! \brief Checks whether direct children exist for the given node.
+  //! \param [in] theNode - one-based node ID.
+  //! \return true/false.
+  Standard_Boolean HasChildren(const Standard_Integer theNode) const
+  {
+    return myAdjacencyMap.IsBound(theNode);
+  }
+
+  //! \brief Returns IDs of child nodes for the given node.
+  //! \param [in] theNode - one-based node ID.
+  //! \return set of child IDs.
+  const TColStd_PackedMapOfInteger& GetChildren(const Standard_Integer theNode) const
+  {
+    return myAdjacencyMap(theNode);
+  }
+
+  //! \brief Returns the node type from \ref NodeType enum.
+  //! \param [in] theNode - one-based node ID.
+  //! \return node type.
+  //! \sa NodeType
+  Standard_EXPORT NodeType GetNodeType(const Standard_Integer theNode) const;
+
+  //! \brief returns object ID by node ID.
+  //! \param [in] theNode - one-based node ID.
+  //! \return persistent ID.
+  const TDF_Label& GetNode(const Standard_Integer theNode) const
+  {
+    return myNodes(theNode);
+  }
+
+  //! \brief Returns the unordered set of graph nodes.
+  //! \return graph nodes.
+  const TDF_LabelIndexedMap& GetNodes() const
+  {
+    return myNodes;
+  }
+
+  //! \brief Returns the number of graph nodes.
+  //! \return number of graph nodes.
+  Standard_Integer NbNodes() const
+  {
+    return myNodes.Extent();
+  }
+
+  //! \brief Returns the collection of graph links in the form of adjacency matrix.
+  //! \return graph links.
+  const AdjacencyMap& GetLinks() const
+  {
+    return myAdjacencyMap;
+  }
+
+  //! \brief Returns the number of graph links.
+  //! \return number of graph links.
+  Standard_EXPORT Standard_Integer NbLinks() const;
+
+  //! Returns quantity of part usage occurrences.
+  //! \param [in] theNode - one-based part ID.
+  //! \return usage occurrence quantity.
+  Standard_EXPORT Standard_Integer NbOccurrences(const Standard_Integer theNode) const;
+
+private:
+
+  //! Builds graph out of OCAF XDE structure.
+  //! \param [in] theLabel - optional starting position.
+  Standard_EXPORT void buildGraph(const TDF_Label& theLabel);
+
+  //! Adds components for the given parent to the graph structure.
+  //! \param [in] theParent   - OCAF label of the parent object.
+  //! \param [in] theParentId - ID of the already registered node representing
+  //!                           the parent object in the assembly graph
+  //!                           being populated.
+  Standard_EXPORT void addComponents(const TDF_Label&       theParent,
+                                     const Standard_Integer theParentId);
+
+  //! Adds node into the graph.
+  //! \param [in] theLabel    - label at insertion level.
+  //! \param [in] theParentId - parent one-based node IDS.
+  //! \return one-based internal ID of the node.
+  Standard_EXPORT Standard_Integer addNode(const TDF_Label&       theLabel,
+                                           const Standard_Integer theParentId);
+
+private:
+
+  Handle(XCAFDoc_ShapeTool)                       myShapeTool;    //!< Document shape tool.
+  TColStd_PackedMapOfInteger                      myRoots;        //!< IDs of the root nodes.
+  TDF_LabelIndexedMap                             myNodes;        //!< Maps assembly/part entries to graph node IDs.
+  AdjacencyMap                                    myAdjacencyMap; //!< "Part-of" relations.
+  NCollection_DataMap<Standard_Integer, NodeType> myNodeTypes;    //!< Node types.
+  NCollection_DataMap<Standard_Integer, 
+                      Standard_Integer>           myUsages;       //!< Occurrences usage.
+
+};
+
+#endif // _XCAFDoc_AssemblyGraph_HeaderFile
diff --git a/src/XCAFDoc/XCAFDoc_AssemblyIterator.cxx b/src/XCAFDoc/XCAFDoc_AssemblyIterator.cxx
new file mode 100644 (file)
index 0000000..83a319d
--- /dev/null
@@ -0,0 +1,193 @@
+// 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 <Standard_NoSuchObject.hxx>
+#include <Standard_RangeError.hxx>
+#include <TDF_LabelSequence.hxx>
+#include <TDF_Tool.hxx>
+#include <TDocStd_Document.hxx>
+#include <XCAFDoc.hxx>
+#include <XCAFDoc_AssemblyIterator.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+
+// =======================================================================
+// function : XCAFDoc_AssemblyIterator constructor
+// purpose  : Starts from free shapes
+// =======================================================================
+
+XCAFDoc_AssemblyIterator::XCAFDoc_AssemblyIterator(const Handle(TDocStd_Document)& theDoc,
+                                                   const Standard_Integer          theLevel)
+  : myMaxLevel(theLevel)
+  , mySeedLevel(1)
+{
+  Standard_NullObject_Raise_if(theDoc.IsNull(), "Null document!");
+
+  myShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDoc->Main());
+  Standard_NoSuchObject_Raise_if(myShapeTool.IsNull(), "No XCAFDoc_ShapeTool attribute!");
+
+  Standard_RangeError_Raise_if(myMaxLevel < 0, "Null document!");
+
+  TDF_LabelSequence aRoots;
+  myShapeTool->GetFreeShapes(aRoots);
+
+  AuxAssemblyItem anAuxItem;
+  TColStd_ListOfAsciiString aParentPath;
+  for (TDF_LabelSequence::Iterator anIt(aRoots); anIt.More(); anIt.Next())
+  {
+    createItem(anIt.Value(), aParentPath, anAuxItem);
+    myFringe.Append(anAuxItem);
+  }
+}
+
+// =======================================================================
+// function : XCAFDoc_AssemblyIterator constructor
+// purpose  : Starts from the specified root
+// =======================================================================
+
+XCAFDoc_AssemblyIterator::XCAFDoc_AssemblyIterator(const Handle(TDocStd_Document)& theDoc,
+                                                   const XCAFDoc_AssemblyItemId&   theRoot,
+                                                   const Standard_Integer          theLevel)
+  : myMaxLevel(theLevel)
+  , mySeedLevel(theRoot.GetPath().Size())
+{
+  Standard_NullObject_Raise_if(theDoc.IsNull(), "Null document!");
+
+  myShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDoc->Main());
+  Standard_NoSuchObject_Raise_if(myShapeTool.IsNull(), "No XCAFDoc_ShapeTool attribute!");
+
+  Standard_NullObject_Raise_if(theRoot.IsNull(), "Null assembly item!");
+
+  Standard_RangeError_Raise_if(myMaxLevel < 0, "Null document!");
+
+  AuxAssemblyItem aSeed;
+  aSeed.myItem = theRoot;
+  TDF_Tool::Label(theDoc->GetData(), theRoot.GetPath().Last(), aSeed.myLabel);
+
+  if (aSeed.myLabel.IsNull())
+    return;
+
+  TDF_Label anOriginal;
+  if (myShapeTool->GetReferredShape(aSeed.myLabel, anOriginal))
+  {
+    if (!myShapeTool->IsAssembly(aSeed.myLabel))
+    {
+      aSeed.myLabel = anOriginal;
+    }
+    else
+    {
+      TCollection_AsciiString aPathStr = theRoot.ToString();
+      Standard_Integer anIndex = aPathStr.SearchFromEnd("/");
+      if (anIndex != -1)
+      {
+        aPathStr.Remove(anIndex, aPathStr.Length() - anIndex + 1);
+      }
+      aSeed.myItem.Init(aPathStr);
+    }
+  }
+
+  myFringe.Append(aSeed);
+}
+
+// =======================================================================
+// function : More
+// purpose  : Checks possibility to continue iteration
+// =======================================================================
+
+Standard_Boolean XCAFDoc_AssemblyIterator::More() const
+{
+  return !myFringe.IsEmpty();
+}
+
+// =======================================================================
+// function : Next
+// purpose  : Moves to the next position
+// =======================================================================
+
+void XCAFDoc_AssemblyIterator::Next()
+{
+  if (!More())
+    return; // No next item.
+
+  // Pop item
+  AuxAssemblyItem aCurrent = myFringe.Last();
+  myFringe.Remove(myFringe.Size());
+
+  // Check current depth of iteration (root level is 0-level by convention)
+  const int aCurrentDepth = aCurrent.myItem.GetPath().Size() - mySeedLevel;
+
+  if (aCurrentDepth < myMaxLevel)
+  {
+    // If current item is an assembly, then the next items to iterate in
+    // depth-first order are the components of this assembly
+    TDF_LabelSequence aComponents;
+    if (myShapeTool->IsAssembly(aCurrent.myLabel))
+    {
+      myShapeTool->GetComponents(aCurrent.myLabel, aComponents);
+    }
+    else if (myShapeTool->IsComponent(aCurrent.myLabel))
+    {
+      aComponents.Append(aCurrent.myLabel);
+    }
+
+    // Put all labels pending for iteration to the fringe
+    AuxAssemblyItem anAuxItem;
+    for (Standard_Integer l = aComponents.Length(); l >= 1; --l)
+    {
+      TDF_Label aLabel = aComponents(l); // Insertion-level label
+      createItem(aLabel, aCurrent.myItem.GetPath(), anAuxItem);
+
+      // Set item to iterate
+      myFringe.Append(anAuxItem);
+    }
+  }
+}
+
+// =======================================================================
+// function : Current
+// purpose  : Returns current assembly item
+// =======================================================================
+
+XCAFDoc_AssemblyItemId XCAFDoc_AssemblyIterator::Current() const
+{
+  return myFringe.Last().myItem;
+}
+
+// =======================================================================
+// function : createItem
+// purpose  : Makes an assembly item id from the specified label
+// =======================================================================
+
+void XCAFDoc_AssemblyIterator::createItem(const TDF_Label&                 theLabel, 
+                                          const TColStd_ListOfAsciiString& theParentPath,
+                                          AuxAssemblyItem&                 theAuxItem) const
+{
+  TCollection_AsciiString anEntry;
+  TDF_Tool::Entry(theLabel, anEntry);
+
+  TDF_Label anOriginal;
+  if (myShapeTool->GetReferredShape(theLabel, anOriginal))
+  {
+    theAuxItem.myLabel = anOriginal;
+  }
+  else
+  {
+    theAuxItem.myLabel = theLabel;
+  }
+
+  TColStd_ListOfAsciiString aPath = theParentPath;
+  aPath.Append(anEntry);
+  theAuxItem.myItem.Init(aPath);
+}
diff --git a/src/XCAFDoc/XCAFDoc_AssemblyIterator.hxx b/src/XCAFDoc/XCAFDoc_AssemblyIterator.hxx
new file mode 100644 (file)
index 0000000..f2c5e2b
--- /dev/null
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef _XCAFDoc_AssemblyIterator_HeaderFile
+#define _XCAFDoc_AssemblyIterator_HeaderFile
+
+#include <NCollection_Sequence.hxx>
+#include <XCAFDoc_AssemblyItemId.hxx>
+
+class TDF_Label;
+class TDocStd_Document;
+class XCAFDoc_ShapeTool;
+
+//! Iterator in depth along the assembly tree.
+class XCAFDoc_AssemblyIterator
+{
+public:
+
+  //! Constructs iterator starting from assembly roots.
+  //! \param [in]      theDoc   - document to iterate.
+  //! \param [in, opt] theLevel - max level of hierarchy to reach (INT_MAX is for no limit).
+  Standard_EXPORT XCAFDoc_AssemblyIterator(const Handle(TDocStd_Document)& theDoc,
+                                           const Standard_Integer          theLevel = INT_MAX);
+
+  //! Constructs iterator starting from the specified position in the assembly tree.
+  //! \param [in]      theDoc   - document to iterate.
+  //! \param [in]      theRoot  - assembly item to start iterating from.
+  //! \param [in, opt] theLevel - max level of hierarchy to reach (INT_MAX is for no limit).
+  Standard_EXPORT XCAFDoc_AssemblyIterator(const Handle(TDocStd_Document)& theDoc,
+                                           const XCAFDoc_AssemblyItemId&   theRoot,
+                                           const Standard_Integer          theLevel = INT_MAX);
+
+  //! \return true if there is still something to iterate, false -- otherwise.
+  Standard_EXPORT Standard_Boolean More() const;
+
+  //! Moves depth-first iterator to the next position.
+  Standard_EXPORT void Next();
+
+  //! \return current item.
+  Standard_EXPORT XCAFDoc_AssemblyItemId Current() const;
+
+private:
+
+  struct AuxAssemblyItem
+  {
+    TDF_Label myLabel;
+    XCAFDoc_AssemblyItemId myItem;
+  };
+
+  void createItem(const TDF_Label& theLabel, const TColStd_ListOfAsciiString& theParentPath, 
+                  AuxAssemblyItem& theAuxItem) const;
+
+private:
+
+  Handle(XCAFDoc_ShapeTool)             myShapeTool; //!< Document shape tool.
+  NCollection_Sequence<AuxAssemblyItem> myFringe;    //!< Items pending for iteration.
+  Standard_Integer                      myMaxLevel;  //!< Limit on max depth of iteration.
+  Standard_Integer                      mySeedLevel; //!< Level of hierarchy where we start.
+};
+
+#endif // _XCAFDoc_AssemblyIterator_HeaderFile
diff --git a/src/XCAFDoc/XCAFDoc_AssemblyTool.hxx b/src/XCAFDoc/XCAFDoc_AssemblyTool.hxx
new file mode 100644 (file)
index 0000000..a7891ed
--- /dev/null
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef _XCAFDoc_AssemblyTool_HeaderFile
+#define _XCAFDoc_AssemblyTool_HeaderFile
+
+#include <Standard.hxx>
+#include <Standard_NullObject.hxx>
+#include <Standard_Type.hxx>
+#include <XCAFDoc_AssemblyIterator.hxx>
+#include <XCAFDoc_AssemblyGraph.hxx>
+
+class TDocStd_Document;
+class XCAFDoc_ShapeTool;
+
+//! Provides generic methods for traversing assembly tree and graph
+class XCAFDoc_AssemblyTool
+{
+public:
+
+  //! \brief Generic method for traversing assembly tree.
+  //! Performs in-depth traversing of the assembly tree and calls
+  //! user defined function for each assembly tree node.
+  //! User function takes single argument of XCAFDoc_AssemblyItemId type
+  //! and returns true/false to continue/break.
+  //! ~~~~~{.cpp}
+  //! Standard_Boolean Print(const XCAFDoc_AssemblyItemId& theItem)
+  //! {
+  //!   std::cout << theItem.ToString() << std::endl;
+  //!   return Standard_True;
+  //! }
+  //! ~~~~~
+  //! \param [in] theIterator - starting position in the assembly tree.
+  //! \param [in] theFunc     - user function called for each assembly tree node.
+  template <typename Func>
+  static void Traverse(XCAFDoc_AssemblyIterator theIterator,
+                       Func                     theFunc)
+  {
+    for (; theIterator.More(); theIterator.Next())
+    {
+      if (!theFunc(theIterator.Current()))
+        break;
+    }
+  }
+
+  //! \brief Generic method for traversing assembly graph.
+  //! Performs in-depth traversing of the assembly graph beginning from root nodes
+  //! and calls user defined function for each assembly graph node accepted
+  //! by the user defined filtering function. Filtering function takes
+  //! the assembly graph passed for traversing, current graph node ID 
+  //! and returns true/false to accept/reject node.
+  //! ~~~~~{.cpp}
+  //! Standard_Boolean AcceptPartOnly(const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+  //!                                 const Standard_Integer               theNode)
+  //! {
+  //!   return (theGraph->GetNodeType(theNode) == XCAFDoc_AssemblyGraph::NodeType_Part);
+  //! }
+  //! ~~~~~
+  //! User function theFunc takes the assembly graph passed for traversing, current
+  //! graph node ID and returns true/false to continue/break.
+  //! \param [in] theGraph  - assembly graph.
+  //! \param [in] theFilter - user filtering function called for each assembly graph node.
+  //! \param [in] theFunc   - user function called for accepted assembly graph node.
+  //! \param [in] theNode   - starting positive one-based graph node ID.
+  template <typename Func, typename Filter>
+  static void Traverse(const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+                       Filter                               theFilter,
+                       Func                                 theFunc,
+                       const Standard_Integer               theNode = 1)
+  {
+    Standard_NullObject_Raise_if(theGraph.IsNull(), "Null assembly graph!");
+
+    for (XCAFDoc_AssemblyGraph::Iterator anIt(theGraph, theNode); anIt.More(); anIt.Next())
+    {
+      const Standard_Integer aN = anIt.Current();
+      if (theFilter(theGraph, aN))
+      {
+        if (!theFunc(theGraph, aN))
+          break;
+      }
+    }
+  }
+
+};
+
+#endif // _XCAFDoc_AssemblyTool_HeaderFile
index 11cff3dc4481db53f2afb9ff6450b1468b3eb919..944351b7ae717df5c9ae9525415dfc74645dd314 100644 (file)
 #include <XCAFDoc_Editor.hxx>
 
 #include <BRep_Builder.hxx>
+#include <BRepBuilderAPI_Transform.hxx>
+#include <Message.hxx>
 #include <XCAFDoc.hxx>
+#include <XCAFDimTolObjects_DatumObject.hxx>
+#include <XCAFDimTolObjects_DimensionObject.hxx>
+#include <XCAFNoteObjects_NoteObject.hxx>
+#include <XCAFDoc_AssemblyItemRef.hxx>
+#include <XCAFDoc_AssemblyTool.hxx>
+#include <XCAFDoc_Area.hxx>
+#include <XCAFDoc_Centroid.hxx>
 #include <XCAFDoc_ColorTool.hxx>
+#include <XCAFDoc_Datum.hxx>
+#include <XCAFDoc_Dimension.hxx>
+#include <XCAFDoc_DimTol.hxx>
+#include <XCAFDoc_DimTolTool.hxx>
 #include <XCAFDoc_DocumentTool.hxx>
 #include <XCAFDoc_GraphNode.hxx>
 #include <XCAFDoc_Location.hxx>
 #include <XCAFDoc_LayerTool.hxx>
 #include <XCAFDoc_MaterialTool.hxx>
+#include <XCAFDoc_NoteBalloon.hxx>
+#include <XCAFDoc_NoteBinData.hxx>
+#include <XCAFDoc_NoteComment.hxx>
+#include <XCAFDoc_NotesTool.hxx>
 #include <XCAFDoc_ShapeMapTool.hxx>
 #include <XCAFDoc_ShapeTool.hxx>
 #include <XCAFDoc_VisMaterial.hxx>
 #include <XCAFDoc_VisMaterialTool.hxx>
+#include <XCAFDoc_Volume.hxx>
 #include <TDF_AttributeIterator.hxx>
 #include <TDF_ChildIterator.hxx>
 #include <TDF_RelocationTable.hxx>
 #include <TDF_Tool.hxx>
 #include <TDataStd_TreeNode.hxx>
+#include <TNaming_NamedShape.hxx>
+#include <TNaming_Builder.hxx>
 #include <TopLoc_Location.hxx>
 #include <TopoDS_Compound.hxx>
-#include <TNaming_NamedShape.hxx>
 
 //=======================================================================
 //function : Expand
@@ -475,3 +494,506 @@ void XCAFDoc_Editor::CloneMetaData(const TDF_Label& theSrcLabel,
     }
   }
 }
+
+//=======================================================================
+//function : rescaleDimensionRefLabels
+//purpose  : Applies geometrical scale to dimension's reference shapes 
+//           not belonging to the assembly graph
+//=======================================================================
+
+static void rescaleDimensionRefLabels(const TDF_LabelSequence& theRefLabels,
+                                      BRepBuilderAPI_Transform& theBRepTrsf,
+                                      const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+                                      const TCollection_AsciiString& theEntryDimension)
+{
+  for (TDF_LabelSequence::Iterator anIt(theRefLabels); anIt.More(); anIt.Next())
+  {
+    const TDF_Label& aL = anIt.Value();
+    if (!theGraph->GetNodes().Contains(aL))
+    {
+      Handle(TNaming_NamedShape) aNS;
+      if (aL.FindAttribute(TNaming_NamedShape::GetID(), aNS))
+      {
+        TopoDS_Shape aShape = aNS->Get();
+        theBRepTrsf.Perform(aShape, Standard_True);
+        if (!theBRepTrsf.IsDone())
+        {
+          Standard_SStream aSS;
+          aSS << "Dimmension PMI " << theEntryDimension << " is not scaled.";
+          Message::SendWarning(aSS.str().c_str());
+        }
+        else
+        {
+          TopoDS_Shape aScaledShape = theBRepTrsf.Shape();
+          TNaming_Builder aBuilder(aL);
+          aBuilder.Generated(aShape, aScaledShape);
+        }
+      }
+    }
+  }
+}
+
+//=======================================================================
+//function : shouldRescaleAndCheckRefLabels
+//purpose  : Checks if all PMI reference shapes belong to the assembly
+//           graph. Returns true if at least one reference shape belongs
+//           to the assembly graph.
+//=======================================================================
+
+static Standard_Boolean shouldRescaleAndCheckRefLabels(
+  const Handle(TDF_Data)& theData,
+  const TDF_LabelSequence& theRefLabels,
+  const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+  Standard_Boolean& theAllInG)
+{
+  theAllInG = Standard_True;
+  Standard_Boolean aShouldRescale = Standard_False;
+  for (TDF_LabelSequence::Iterator anIt1(theRefLabels); anIt1.More(); anIt1.Next())
+  {
+    const TDF_Label& aL = anIt1.Value();
+    if (theGraph->GetNodes().Contains(aL))
+    {
+      aShouldRescale = Standard_True;
+    }
+    else
+    {
+      Handle(XCAFDoc_AssemblyItemRef) anItemRefAttr;
+      if (!aL.FindAttribute(XCAFDoc_AssemblyItemRef::GetID(), anItemRefAttr))
+      {
+        theAllInG = Standard_False;
+        continue;
+      }
+      const XCAFDoc_AssemblyItemId& anItemId = anItemRefAttr->GetItem();
+      if (anItemId.IsNull())
+      {
+        theAllInG = Standard_False;
+        continue;
+      }
+      TDF_Label aLRef;
+      TDF_Tool::Label(theData, anItemId.GetPath().Last(), aLRef, Standard_False);
+      if (aLRef.IsNull() || !theGraph->GetNodes().Contains(aLRef))
+      {
+        theAllInG = Standard_False;
+        continue;
+      }
+      aShouldRescale = Standard_True;
+    }
+  }
+  return aShouldRescale;
+}
+
+//=======================================================================
+//function : RescaleGeometry
+//purpose  : Applies geometrical scale to all assembly parts, component
+//           locations and related attributes
+//=======================================================================
+
+Standard_Boolean XCAFDoc_Editor::RescaleGeometry(const TDF_Label& theLabel,
+                                                 const Standard_Real theScaleFactor,
+                                                 const Standard_Boolean theForceIfNotRoot)
+{
+  if (theLabel.IsNull())
+  {
+    Message::SendFail("Null label.");
+    return Standard_False;
+  }
+
+  if (Abs(theScaleFactor) <= gp::Resolution())
+  {
+    Message::SendFail("Scale factor is too small.");
+    return Standard_False;
+  }
+
+  Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(theLabel);
+  if (aShapeTool.IsNull())
+  {
+    Message::SendFail("Couldn't find XCAFDoc_ShapeTool attribute.");
+    return Standard_False;
+  }
+
+  if (!theForceIfNotRoot && aShapeTool->Label() != theLabel)
+  {
+    TDF_LabelSequence aFreeLabels;
+    aShapeTool->GetFreeShapes(aFreeLabels);
+    Standard_Boolean aFound = Standard_False;
+    for (TDF_LabelSequence::Iterator anIt(aFreeLabels); anIt.More(); anIt.Next())
+    {
+      if (theLabel == anIt.Value())
+      {
+        aFound = Standard_True;
+        break;
+      }
+    }
+    if (!aFound)
+    {
+      TCollection_AsciiString anEntry;
+      TDF_Tool::Entry(theLabel, anEntry);
+      Standard_SStream aSS;
+      aSS << "Label " << anEntry << " is not a root. Set ForceIfNotRoot true to rescale forcibly.";
+      Message::SendFail(aSS.str().c_str());
+      return Standard_False;
+    }
+  }
+
+  Handle(XCAFDoc_AssemblyGraph) aG = new XCAFDoc_AssemblyGraph(theLabel);
+  if (aG.IsNull())
+  {
+    Message::SendFail("Couldn't create assembly graph.");
+    return Standard_False;
+  }
+
+  Standard_Boolean anIsDone = Standard_True;
+
+  gp_Trsf aTrsf; aTrsf.SetScaleFactor(theScaleFactor);
+  BRepBuilderAPI_Transform aBRepTrsf(aTrsf);
+
+  XCAFDoc_AssemblyTool::Traverse(aG, 
+    [](const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+       const Standard_Integer               theNode) -> Standard_Boolean
+    {
+      const XCAFDoc_AssemblyGraph::NodeType aNodeType = theGraph->GetNodeType(theNode);
+      return (aNodeType == XCAFDoc_AssemblyGraph::NodeType_Part) ||
+             (aNodeType == XCAFDoc_AssemblyGraph::NodeType_Occurrence);
+    },
+    [&](const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+        const Standard_Integer               theNode) -> Standard_Boolean
+    {
+      const TDF_Label& aLabel = theGraph->GetNode(theNode);
+      const XCAFDoc_AssemblyGraph::NodeType aNodeType = theGraph->GetNodeType(theNode);
+
+      if (aNodeType == XCAFDoc_AssemblyGraph::NodeType_Part)
+      {
+        const TopoDS_Shape aShape = aShapeTool->GetShape(aLabel);
+        aBRepTrsf.Perform(aShape, Standard_True);
+        if (!aBRepTrsf.IsDone())
+        {
+          Standard_SStream aSS;
+          TCollection_AsciiString anEntry;
+          TDF_Tool::Entry(aLabel, anEntry);
+          aSS << "Shape " << anEntry << " is not scaled!";
+          Message::SendFail(aSS.str().c_str());
+          anIsDone = Standard_False;
+          return Standard_False;
+        }
+        TopoDS_Shape aScaledShape = aBRepTrsf.Shape();
+        aShapeTool->SetShape(aLabel, aScaledShape);
+
+        // Update sub-shapes
+        TDF_LabelSequence aSubshapes;
+        aShapeTool->GetSubShapes(aLabel, aSubshapes);
+        for (TDF_LabelSequence::Iterator anItSs(aSubshapes); anItSs.More(); anItSs.Next())
+        {
+          const TDF_Label& aLSs = anItSs.Value();
+          const TopoDS_Shape aSs = aShapeTool->GetShape(aLSs);
+          const TopoDS_Shape aSs1 = aBRepTrsf.ModifiedShape(aSs);
+          aShapeTool->SetShape(aLSs, aSs1);
+        }
+
+        Handle(XCAFDoc_Area) aArea;
+        if (aLabel.FindAttribute(XCAFDoc_Area::GetID(), aArea))
+        {
+          aArea->Set(aArea->Get() * theScaleFactor * theScaleFactor);
+        }
+
+        Handle(XCAFDoc_Centroid) aCentroid;
+        if (aLabel.FindAttribute(XCAFDoc_Centroid::GetID(), aCentroid))
+        {
+          aCentroid->Set(aCentroid->Get().XYZ() * theScaleFactor);
+        }
+
+        Handle(XCAFDoc_Volume) aVolume;
+        if (aLabel.FindAttribute(XCAFDoc_Volume::GetID(), aVolume))
+        {
+          aVolume->Set(aVolume->Get() * theScaleFactor * theScaleFactor * theScaleFactor);
+        }
+      }
+      else if (aNodeType == XCAFDoc_AssemblyGraph::NodeType_Occurrence)
+      {
+        TopLoc_Location aLoc = aShapeTool->GetLocation(aLabel);
+        gp_Trsf aTrsf = aLoc.Transformation();
+        aTrsf.SetTranslationPart(aTrsf.TranslationPart() * theScaleFactor);
+        XCAFDoc_Location::Set(aLabel, aTrsf);
+      }
+
+      return Standard_True;
+    }
+  );
+
+  if (!anIsDone)
+  {
+    return Standard_False;
+  }
+
+  aShapeTool->UpdateAssemblies();
+
+  Handle(XCAFDoc_DimTolTool) aDimTolTool = XCAFDoc_DocumentTool::DimTolTool(theLabel);
+  if (!aDimTolTool.IsNull())
+  {
+    TDF_LabelSequence aDimensions;
+    aDimTolTool->GetDimensionLabels(aDimensions);
+    for (TDF_LabelSequence::Iterator anItD(aDimensions); anItD.More(); anItD.Next())
+    {
+      const TDF_Label& aDimension = anItD.Value();
+
+      TCollection_AsciiString anEntryDimension;
+      TDF_Tool::Entry(aDimension, anEntryDimension);
+
+      Handle(XCAFDoc_Dimension) aDimAttr;
+      if (aDimension.FindAttribute(XCAFDoc_Dimension::GetID(), aDimAttr))
+      {
+        Standard_Boolean aShouldRescale = Standard_False;
+        Standard_Boolean aFirstLInG = Standard_True;
+        Standard_Boolean aSecondLInG = Standard_True;
+        TDF_LabelSequence aShapeLFirst, aShapeLSecond;
+        Standard_Boolean aHasShapeRefs = aDimTolTool->GetRefShapeLabel(aDimension, aShapeLFirst, aShapeLSecond);
+        if (aHasShapeRefs)
+        {
+          aShouldRescale = shouldRescaleAndCheckRefLabels(theLabel.Data(), aShapeLFirst, aG, aFirstLInG) ||
+                           shouldRescaleAndCheckRefLabels(theLabel.Data(), aShapeLSecond, aG, aSecondLInG);
+        }
+
+        if (!aShouldRescale)
+        {
+          Standard_SStream aSS;
+          aSS << "Dimension PMI " << anEntryDimension << " is not scaled!";
+          Message::SendWarning(aSS.str().c_str());
+          continue;
+        }
+
+        Handle(XCAFDimTolObjects_DimensionObject) aDimObj = aDimAttr->GetObject();
+
+        if (aDimObj->HasTextPoint())
+        {
+          aDimObj->SetPointTextAttach(aDimObj->GetPointTextAttach().XYZ() * theScaleFactor);
+        }
+
+        if (aDimObj->HasPoint())
+        {
+          aDimObj->SetPoint(aDimObj->GetPoint().XYZ() * theScaleFactor);
+        }
+
+        if (aDimObj->HasPoint2())
+        {
+          aDimObj->SetPoint2(aDimObj->GetPoint2().XYZ() * theScaleFactor);
+        }
+
+        if (aDimObj->HasPlane())
+        {
+          gp_Ax2 aPln = aDimObj->GetPlane();
+          aPln.SetLocation(aPln.Location().XYZ() * theScaleFactor);
+          aDimObj->SetPlane(aPln);
+        }
+
+        Handle(TColStd_HArray1OfReal) aValues = aDimObj->GetValues();
+        if (!aValues.IsNull())
+        {
+          if (!aFirstLInG || !aSecondLInG)
+          {
+            Standard_SStream aSS;
+            aSS << "Dimension PMI " << anEntryDimension << " base shapes do not belong to the rescaled assembly!";
+            Message::SendWarning(aSS.str().c_str());
+            continue;
+          }
+          Standard_Boolean aRescaleOtherValues = Standard_False;
+          TColStd_Array1OfReal& anArray = aValues->ChangeArray1();
+          switch (aDimObj->GetType())
+          {
+          case XCAFDimTolObjects_DimensionType_Location_None:
+          case XCAFDimTolObjects_DimensionType_Location_CurvedDistance:
+            {
+              Standard_SStream aSS;
+              aSS << "Dimension PMI " << anEntryDimension << " is not scaled.";
+              Message::SendWarning(aSS.str().c_str());
+            }
+            break;
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromCenterToOuter:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromCenterToInner:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromOuterToCenter:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromOuterToOuter:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromOuterToInner:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromInnerToCenter:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromInnerToOuter:
+          case XCAFDimTolObjects_DimensionType_Location_LinearDistance_FromInnerToInner:
+            anArray.ChangeFirst() *= theScaleFactor;
+            aRescaleOtherValues = Standard_True;
+            break;
+          case XCAFDimTolObjects_DimensionType_Location_Angular:
+          case XCAFDimTolObjects_DimensionType_Size_Angular:
+            break;
+          case XCAFDimTolObjects_DimensionType_Location_Oriented:
+          case XCAFDimTolObjects_DimensionType_Location_WithPath:
+            {
+              Standard_SStream aSS;
+              aSS << "Dimension PMI " << anEntryDimension << " is not scaled.";
+              Message::SendWarning(aSS.str().c_str());
+            }
+            break;
+          case XCAFDimTolObjects_DimensionType_Size_CurveLength:
+          case XCAFDimTolObjects_DimensionType_Size_Diameter:
+          case XCAFDimTolObjects_DimensionType_Size_SphericalDiameter:
+          case XCAFDimTolObjects_DimensionType_Size_Radius:
+          case XCAFDimTolObjects_DimensionType_Size_SphericalRadius:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalMinorDiameter:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalMajorDiameter:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalMinorRadius:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalMajorRadius:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalHighMajorDiameter:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalLowMajorDiameter:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalHighMajorRadius:
+          case XCAFDimTolObjects_DimensionType_Size_ToroidalLowMajorRadius:
+          case XCAFDimTolObjects_DimensionType_Size_Thickness:
+          case XCAFDimTolObjects_DimensionType_Size_WithPath:
+            anArray.ChangeFirst() *= theScaleFactor;
+            aRescaleOtherValues = Standard_True;
+            break;
+          case XCAFDimTolObjects_DimensionType_CommonLabel:
+          case XCAFDimTolObjects_DimensionType_DimensionPresentation:
+            {
+              Standard_SStream aSS;
+              aSS << "Dimension PMI " << anEntryDimension << " is not scaled.";
+              Message::SendWarning(aSS.str().c_str());
+            }
+            break;
+          default:
+            {
+              Standard_SStream aSS;
+              aSS << "Dimension PMI of unsupported type " << anEntryDimension << " is not scaled.";
+              Message::SendWarning(aSS.str().c_str());
+            }
+          }
+          rescaleDimensionRefLabels(aShapeLFirst, aBRepTrsf, aG, anEntryDimension);
+          rescaleDimensionRefLabels(aShapeLSecond, aBRepTrsf, aG, anEntryDimension);
+          if (aRescaleOtherValues)
+          {
+            for (Standard_Integer i = anArray.Lower() + 1; i <= anArray.Upper(); ++i)
+              anArray.ChangeValue(i) *= theScaleFactor;
+
+            Handle(TCollection_HAsciiString) aName = aDimObj->GetSemanticName();
+            if (!aName.IsNull())
+            {
+              aName->AssignCat(" (Rescaled to ");
+              Standard_SStream aSS; aSS << aValues->First();
+              aName->AssignCat(aSS.str().c_str());
+              aName->AssignCat(")");
+            }
+          }
+        }
+        else
+        {
+          Standard_SStream aSS;
+          aSS << "Dimension PMI values " << anEntryDimension << " are not scaled.";
+          Message::SendWarning(aSS.str().c_str());
+        }
+
+        aDimAttr->SetObject(aDimObj);
+      }
+    }
+  
+    TDF_LabelSequence aDatums;
+    aDimTolTool->GetDatumLabels(aDatums);
+    for (TDF_LabelSequence::Iterator anIt(aDatums); anIt.More(); anIt.Next())
+    {
+      const TDF_Label& aDatum = anIt.Value();
+
+      TCollection_AsciiString anEntryDatum;
+      TDF_Tool::Entry(aDatum, anEntryDatum);
+
+      Handle(XCAFDoc_Datum) aDatumAttr;
+      if (aDatum.FindAttribute(XCAFDoc_Datum::GetID(), aDatumAttr))
+      {
+        Handle(XCAFDimTolObjects_DatumObject) aDatumObj = aDatumAttr->GetObject();
+
+        if (aDatumObj->HasDatumTargetParams())
+        {
+          gp_Ax2 anAxis = aDatumObj->GetDatumTargetAxis();
+          anAxis.SetLocation(anAxis.Location().XYZ() * theScaleFactor);
+          aDatumObj->SetDatumTargetAxis(anAxis);
+          // TODO: Should we rescale target length and width?
+          Standard_SStream aSS;
+          aSS << "Datum PMI target length and width " << anEntryDatum << " are not scaled.";
+          Message::SendWarning(aSS.str().c_str());
+          //aDatumObj->SetDatumTargetLength(aDatumObj->GetDatumTargetLength() * theScaleFactor);
+          //aDatumObj->SetDatumTargetWidth(aDatumObj->GetDatumTargetWidth() * theScaleFactor);
+        }
+
+        if (aDatumObj->HasPointText())
+        {
+          aDatumObj->SetPointTextAttach(aDatumObj->GetPointTextAttach().XYZ() * theScaleFactor);
+        }
+
+        if (aDatumObj->HasPoint())
+        {
+          aDatumObj->SetPoint(aDatumObj->GetPoint().XYZ() * theScaleFactor);
+        }
+
+        if (aDatumObj->HasPlane())
+        {
+          gp_Ax2 aPln = aDatumObj->GetPlane();
+          aPln.SetLocation(aPln.Location().XYZ() * theScaleFactor);
+          aDatumObj->SetPlane(aPln);
+        }
+
+        aDatumAttr->SetObject(aDatumObj);
+      }
+    }
+  
+    TDF_LabelSequence aDimTols;
+    aDimTolTool->GetDimTolLabels(aDimTols);
+    for (TDF_LabelSequence::Iterator anIt(aDimTols); anIt.More(); anIt.Next())
+    {
+      const TDF_Label& aDimTol = anIt.Value();
+
+      TCollection_AsciiString anEntryDimTol;
+      TDF_Tool::Entry(aDimTol, anEntryDimTol);
+
+      Handle(XCAFDoc_DimTol) aDimTolAttr;
+      if (aDimTol.FindAttribute(XCAFDoc_DimTol::GetID(), aDimTolAttr))
+      {
+        Standard_SStream aSS;
+        aSS << "DimTol PMI " << anEntryDimTol << " is not scaled.";
+        Message::SendWarning(aSS.str().c_str());
+      }
+    }
+  }
+
+  Handle(XCAFDoc_NotesTool) aNotesTool = XCAFDoc_DocumentTool::NotesTool(theLabel);
+  if (!aNotesTool.IsNull())
+  {
+    TDF_LabelSequence aNotes;
+    aNotesTool->GetNotes(aNotes);
+    for (TDF_LabelSequence::Iterator anIt(aNotes); anIt.More(); anIt.Next())
+    {
+      const TDF_Label& aNote = anIt.Value();
+
+      Handle(XCAFDoc_Note) aNoteAttr;
+      if (aNote.FindAttribute(XCAFDoc_NoteComment::GetID(), aNoteAttr) ||
+          aNote.FindAttribute(XCAFDoc_NoteBalloon::GetID(), aNoteAttr) ||
+          aNote.FindAttribute(XCAFDoc_NoteBinData::GetID(), aNoteAttr))
+      {
+        Handle(XCAFNoteObjects_NoteObject) aNoteObj = aNoteAttr->GetObject();
+
+        if (aNoteObj->HasPointText())
+        {
+          aNoteObj->SetPointText(aNoteObj->GetPointText().XYZ() * theScaleFactor);
+        }
+
+        if (aNoteObj->HasPoint())
+        {
+          aNoteObj->SetPoint(aNoteObj->GetPoint().XYZ() * theScaleFactor);
+        }
+
+        if (aNoteObj->HasPlane())
+        {
+          gp_Ax2 aPln = aNoteObj->GetPlane();
+          aPln.SetLocation(aPln.Location().XYZ() * theScaleFactor);
+          aNoteObj->SetPlane(aPln);
+        }
+
+        aNoteAttr->SetObject(aNoteObj);
+      }
+    }
+  }
+
+  return anIsDone;
+}
index 89ac8ac7cf0c92a3f097f6cf59efcd50aaa49c55..dffc46bd1669ec21fa383a3a0dd9d25294babf37 100644 (file)
@@ -44,7 +44,6 @@ public:
 
   //! Converts all compounds shapes in the document to assembly
   //! @param[in] theDoc input document
-  //! @param[in] theShape input shape label
   //! @param[in] theRecursively recursively expand a compound subshape
   //! @return True if shape successfully expanded
   Standard_EXPORT static Standard_Boolean Expand(const TDF_Label& theDoc,
@@ -98,6 +97,23 @@ public:
                                             const Standard_Boolean theToCopyVisMaterial = Standard_True,
                                             const Standard_Boolean theToCopyAttributes = Standard_True);
 
+  //! Applies geometrical scaling to the following assembly components:
+  //! - part geometry
+  //! - sub-assembly/part occurrence location
+  //! - part's centroid, area and volume attributes
+  //! - PMIs (warnings and errors are reported if it is impossible to make changes)
+  //! Normally, should start from a root sub-assembly, but if theForceIfNotRoot true
+  //! scaling will be applied forcibly. If theLabel corresponds to the shape tool
+  //! scaling is applied to the whole assembly.
+  //! @param[in] theLabel starting label
+  //! @param[in] theScaleFactor scale factor, should be positive
+  //! @param[in] theForceIfNotRoot allows scaling of a non root assembly if true,
+  //!                              otherwise - returns false
+  //! @return true in case of success, otherwise - false.
+  Standard_EXPORT static Standard_Boolean RescaleGeometry(const TDF_Label& theLabel,
+                                                          const Standard_Real theScaleFactor,
+                                                          const Standard_Boolean theForceIfNotRoot = Standard_False);
+
 };
 
 #endif // _XCAFDoc_Editor_HeaderFile
index 083ed649d44f6b5d4c50a08f7b4a5b63ad333ea9..dd69cab6f860837edee2f31d580f7081f16df8ca 100644 (file)
@@ -19,6 +19,7 @@
 #include <AIS_InteractiveObject.hxx>
 #include <AIS_Trihedron.hxx>
 #include <Aspect_TypeOfLine.hxx>
+#include <BRepBuilderAPI_Transform.hxx>
 #include <DBRep.hxx>
 #include <DDF_Browser.hxx>
 #include <DDocStd.hxx>
@@ -37,6 +38,7 @@
 #include <TColStd_HArray1OfInteger.hxx>
 #include <TColStd_HArray1OfReal.hxx>
 #include <TColStd_HSequenceOfExtendedString.hxx>
+#include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
 #include <TDataStd_AsciiString.hxx>
 #include <TDataStd_ByteArray.hxx>
 #include <TDataStd_Comment.hxx>
@@ -69,6 +71,9 @@
 #include <ViewerTest.hxx>
 #include <ViewerTest_AutoUpdater.hxx>
 #include <XCAFDoc.hxx>
+#include <XCAFDoc_AssemblyIterator.hxx>
+#include <XCAFDoc_AssemblyGraph.hxx>
+#include <XCAFDoc_AssemblyTool.hxx>
 #include <XCAFDoc_Area.hxx>
 #include <XCAFDoc_Centroid.hxx>
 #include <XCAFDoc_Color.hxx>
@@ -76,6 +81,7 @@
 #include <XCAFDoc_DimTol.hxx>
 #include <XCAFDoc_Dimension.hxx>
 #include <XCAFDoc_Datum.hxx>
+#include <XCAFDoc_Editor.hxx>
 #include <XCAFDoc_GeomTolerance.hxx>
 #include <XCAFDoc_DocumentTool.hxx>
 #include <XCAFDoc_GraphNode.hxx>
@@ -1299,6 +1305,360 @@ static Standard_Integer XShowFaceBoundary (Draw_Interpretor& di,
   return 0;
 }
 
+//=======================================================================
+//function : XAssemblyTreeDump
+//purpose  : Prints assembly tree structure up to the specified level
+//=======================================================================
+
+static Standard_Integer XDumpAssemblyTree(Draw_Interpretor& di,
+                                          Standard_Integer argc,
+                                          const char ** argv)
+{
+  if (argc < 2)
+  {
+    di << "Usage :\n " << argv[0] << " Doc [-root label] [-level l] [-names]\n"
+      << "   Doc         - document name. \n"
+      << "   -root label - starting root label. \n"
+      << "   -level l    - depth level (infinite by default). \n"
+      << "   -names      - prints names instead of entries. \n";
+
+    return 1;
+  }
+
+  // get specified document
+  Handle(TDocStd_Document) aDoc;
+  DDocStd::GetDocument(argv[1], aDoc);
+  if (aDoc.IsNull())
+  {
+    di << argv[1] << " is not a document\n";
+    return 1;
+  }
+
+  XCAFDoc_AssemblyItemId aRoot;
+  Standard_Integer aLevel = INT_MAX;
+  Standard_Boolean aPrintNames = Standard_False;
+  for (Standard_Integer iarg = 2; iarg < argc; ++iarg)
+  {
+    if (strcmp(argv[iarg], "-root") == 0)
+    {
+      Standard_ProgramError_Raise_if(iarg + 1 >= argc, "Root is expected!");
+      aRoot.Init(argv[++iarg]);
+    }
+    else if (strcmp(argv[iarg], "-level") == 0)
+    {
+      Standard_ProgramError_Raise_if(iarg + 1 >= argc, "Level is expected!");
+      TCollection_AsciiString anArg = argv[++iarg];
+      Standard_ProgramError_Raise_if(!anArg.IsIntegerValue(), "Integer value is expected!");
+      aLevel = anArg.IntegerValue();
+    }
+    else if (strcmp(argv[iarg], "-names") == 0)
+    {
+      aPrintNames = Standard_True;
+    }
+  }
+
+  Standard_SStream aSS;
+
+  XCAFDoc_AssemblyIterator anIt = aRoot.IsNull() ? XCAFDoc_AssemblyIterator(aDoc, aLevel) 
+                                                 : XCAFDoc_AssemblyIterator(aDoc, aRoot, aLevel);
+  XCAFDoc_AssemblyTool::Traverse(anIt, [&](const XCAFDoc_AssemblyItemId& theItem) -> Standard_Boolean
+  {
+    if (aPrintNames)
+    {
+      Standard_Boolean aFirst = Standard_True;
+      for (TColStd_ListOfAsciiString::Iterator anIt(theItem.GetPath()); anIt.More(); 
+           anIt.Next(), aFirst = Standard_False)
+      {
+        if (!aFirst) aSS << "/";
+        TDF_Label aL;
+        TDF_Tool::Label(aDoc->GetData(), anIt.Value(), aL, Standard_False);
+        if (!aL.IsNull())
+        {
+          TCollection_ExtendedString aName;
+          Handle(TDataStd_Name) aNameAttr;
+          if (aL.FindAttribute(TDataStd_Name::GetID(), aNameAttr))
+          {
+            aName = aNameAttr->Get();
+            aSS << aName;
+            continue;
+          }
+        }
+        aSS << anIt.Value();
+      }
+      aSS << std::endl;
+    }
+    else
+    {
+      aSS << theItem.ToString() << std::endl;
+    }
+    return Standard_True;
+  });
+
+  di << aSS.str().c_str();
+  return 0;
+}
+
+//=======================================================================
+//function : graphNodeTypename
+//purpose  : Returns node type name
+//=======================================================================
+
+static 
+const char* graphNodeTypename(const XCAFDoc_AssemblyGraph::NodeType theNodeType)
+{
+  switch (theNodeType)
+  {
+  case XCAFDoc_AssemblyGraph::NodeType_AssemblyRoot: return "R";
+  case XCAFDoc_AssemblyGraph::NodeType_Subassembly:  return "A";
+  case XCAFDoc_AssemblyGraph::NodeType_Occurrence:   return "O";
+  case XCAFDoc_AssemblyGraph::NodeType_Part:         return "P";
+  case XCAFDoc_AssemblyGraph::NodeType_Subshape:     return "S";
+  default:                                           return "?";
+  }
+}
+
+//=======================================================================
+//function : XAssemblyGraphDump
+//purpose  : Prints assembly graph structure
+//=======================================================================
+
+static Standard_Integer XDumpAssemblyGraph(Draw_Interpretor& di,
+                                           Standard_Integer argc,
+                                           const char ** argv)
+{
+  if (argc < 2)
+  {
+    di << "Usage :\n " << argv[0] << " Doc [-root label] [-verbose] \n"
+      << "   Doc         - is the document name. \n"
+      << "   -root label - is the optional starting label. \n"
+      << "   -names      - prints names instead of entries. \n";
+
+    return 1;
+  }
+
+  // get specified document
+  Handle(TDocStd_Document) aDoc;
+  DDocStd::GetDocument(argv[1], aDoc);
+  if (aDoc.IsNull())
+  {
+    di << argv[1] << " is not a document\n";
+    return 1;
+  }
+
+  Standard_Boolean aPrintNames = Standard_False;
+  TDF_Label aLabel = XCAFDoc_DocumentTool::ShapesLabel(aDoc->Main());
+  for (Standard_Integer iarg = 2; iarg < argc; ++iarg)
+  {
+    if (strcmp(argv[iarg], "-root") == 0)
+    {
+      Standard_ProgramError_Raise_if(iarg + 1 >= argc, "Root is expected!");
+      TDF_Tool::Label(aDoc->GetData(), argv[++iarg], aLabel, Standard_False);
+    }
+    else if (strcmp(argv[iarg], "-names") == 0)
+    {
+      aPrintNames = Standard_True;
+    }
+  }
+
+  Handle(XCAFDoc_AssemblyGraph) aG = new XCAFDoc_AssemblyGraph(aLabel);
+
+  Standard_SStream aSS;
+
+  XCAFDoc_AssemblyTool::Traverse(aG, 
+    [](const Handle(XCAFDoc_AssemblyGraph)& /*theGraph*/,
+       const Standard_Integer               /*theNode*/) -> Standard_Boolean
+    {
+      return Standard_True;
+    },
+    [&](const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+        const Standard_Integer               theNode) -> Standard_Boolean
+    {
+      const TDF_Label& aLabel = theGraph->GetNode(theNode);
+
+      const XCAFDoc_AssemblyGraph::NodeType aNodeType = theGraph->GetNodeType(theNode);
+
+      TCollection_AsciiString aNodeEntry;
+      if (aPrintNames)
+      {
+        Handle(TDataStd_Name) aNameAttr;
+        if (aLabel.FindAttribute(TDataStd_Name::GetID(), aNameAttr))
+        {
+          aNodeEntry.AssignCat("'");
+          aNodeEntry.AssignCat(aNameAttr->Get());
+          aNodeEntry.AssignCat("'");
+        }
+      }
+      if (aNodeEntry.IsEmpty())
+      {
+        TDF_Tool::Entry(aLabel, aNodeEntry);
+      }
+
+      aSS << theNode << " " << graphNodeTypename(aNodeType) << " " << aNodeEntry;
+      const XCAFDoc_AssemblyGraph::AdjacencyMap& anAdjacencyMap = theGraph->GetLinks();
+      const TColStd_PackedMapOfInteger* aLinksPtr = anAdjacencyMap.Seek(theNode);
+      if (aLinksPtr != NULL)
+      {
+        for (TColStd_MapIteratorOfPackedMapOfInteger anIt1(*aLinksPtr); anIt1.More(); anIt1.Next())
+        {
+          aSS << " " << anIt1.Key();
+        }
+      }
+      aSS << std::endl;
+
+      return Standard_True;
+    }
+  );
+
+  di << aSS.str().c_str();
+  return 0;
+}
+
+//=======================================================================
+//function : XDumpNomenclature
+//purpose  : Prints number of assembly instances
+//=======================================================================
+
+static Standard_Integer XDumpNomenclature(Draw_Interpretor& di,
+                                          Standard_Integer argc,
+                                          const char ** argv)
+{
+  if (argc < 2)
+  {
+    di << "Usage :\n " << argv[0] << " Doc [-names] \n"
+      << "   Doc    - is the document name. \n"
+      << "   -names - prints names instead of entries. \n";
+
+    return 1;
+  }
+
+  // get specified document
+  Handle(TDocStd_Document) aDoc;
+  DDocStd::GetDocument(argv[1], aDoc);
+  if (aDoc.IsNull())
+  {
+    di << argv[1] << " is not a document\n";
+    return 1;
+  }
+
+  Standard_Boolean aPrintNames = Standard_False;
+  for (Standard_Integer iarg = 2; iarg < argc; ++iarg)
+  {
+    if (strcmp(argv[iarg], "-names") == 0)
+    {
+      aPrintNames = Standard_True;
+    }
+  }
+
+  Handle(XCAFDoc_AssemblyGraph) aG = new XCAFDoc_AssemblyGraph(aDoc);
+
+  Standard_SStream aSS;
+
+  XCAFDoc_AssemblyTool::Traverse(aG, 
+    [](const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+       const Standard_Integer               theNode) -> Standard_Boolean
+    {
+      const XCAFDoc_AssemblyGraph::NodeType aNodeType = theGraph->GetNodeType(theNode);
+      return (aNodeType == XCAFDoc_AssemblyGraph::NodeType_AssemblyRoot) ||
+             (aNodeType == XCAFDoc_AssemblyGraph::NodeType_Subassembly) ||
+             (aNodeType == XCAFDoc_AssemblyGraph::NodeType_Part);
+    },
+    [&](const Handle(XCAFDoc_AssemblyGraph)& theGraph,
+        const Standard_Integer               theNode) -> Standard_Boolean
+    {
+      const TDF_Label& aLabel = theGraph->GetNode(theNode);
+
+      const XCAFDoc_AssemblyGraph::NodeType aNodeType = theGraph->GetNodeType(theNode);
+
+      TCollection_AsciiString aNodeEntry;
+      if (aPrintNames)
+      {
+        Handle(TDataStd_Name) aNameAttr;
+        if (aLabel.FindAttribute(TDataStd_Name::GetID(), aNameAttr))
+        {
+          aNodeEntry.AssignCat("'");
+          aNodeEntry.AssignCat(aNameAttr->Get());
+          aNodeEntry.AssignCat("'");
+        }
+      }
+      if (aNodeEntry.IsEmpty())
+      {
+        TDF_Tool::Entry(aLabel, aNodeEntry);
+      }
+
+      aSS << theNode << " " << graphNodeTypename(aNodeType) << " " << aNodeEntry << " "
+          << theGraph->NbOccurrences(theNode) << std::endl;
+
+      return Standard_True;
+    }
+  );
+
+  di << aSS.str().c_str();
+
+  return 0;
+}
+
+//=======================================================================
+//function : XRescaleGeometry
+//purpose  : Applies geometrical scale to all assembly components
+//=======================================================================
+
+static Standard_Integer XRescaleGeometry(Draw_Interpretor& di,
+                                         Standard_Integer argc,
+                                         const char ** argv)
+{
+  if (argc < 3)
+  {
+    di << "Usage :\n " << argv[0] << " Doc factor [-root label] [-force]\n"
+      << "   Doc         - is the document name. \n"
+      << "   factor      - is the scale factor. \n"
+      << "   -root label - is the starting label to apply rescaling. \n"
+      << "   -force      - forces rescaling even if the starting label\n"
+      << "                 is not a root. \n";
+
+    return 1;
+  }
+
+  // get specified document
+  Handle(TDocStd_Document) aDoc;
+  DDocStd::GetDocument(argv[1], aDoc);
+  if (aDoc.IsNull())
+  {
+    di << argv[1] << " is not a document\n";
+    return 1;
+  }
+
+  // get scale factor
+  Standard_Real aScaleFactor = Draw::Atof(argv[2]);
+  if (aScaleFactor <= 0)
+  {
+    di << "Scale factor must be positive\n";
+    return 1;
+  }
+
+  Standard_Boolean aForce = Standard_False;
+  TDF_Label aLabel = XCAFDoc_DocumentTool::ShapesLabel(aDoc->Main());
+  for (Standard_Integer iarg = 3; iarg < argc; ++iarg)
+  {
+    if (strcmp(argv[iarg], "-root") == 0)
+    {
+      Standard_ProgramError_Raise_if(iarg + 1 >= argc, "Root is expected!");
+      TDF_Tool::Label(aDoc->GetData(), argv[++iarg], aLabel, Standard_False);
+    }
+    else if (strcmp(argv[iarg], "-force") == 0)
+    {
+      aForce = Standard_True;
+    }
+  }
+
+  if (!XCAFDoc_Editor::RescaleGeometry(aLabel, aScaleFactor, aForce))
+  {
+    di << "Geometry rescale failed\n";
+    return 1;
+  }
+
+  return 0;
+}
+
 //=======================================================================
 //function : testDoc
 //purpose  : Method to test destruction of document
@@ -1456,6 +1816,19 @@ void XDEDRAW::Init(Draw_Interpretor& di)
           __FILE__, XShowFaceBoundary, g);
    di.Add ("XTestDoc", "XTestDoc shape", __FILE__, testDoc, g);
 
+  di.Add("XDumpAssemblyTree",
+         "Doc [-root label] [-level l] [-names]: Iterates through the assembly tree in depth up to the specified level, if any",
+         __FILE__, XDumpAssemblyTree, g);
+  di.Add("XDumpAssemblyGraph",
+         "Doc [-root label] [-names]: Prints assembly graph structure",
+         __FILE__, XDumpAssemblyGraph, g);
+  di.Add("XDumpNomenclature",
+         "Doc [-names]: Prints number of assembly instances",
+         __FILE__, XDumpNomenclature, g);
+  di.Add("XRescaleGeometry",
+         "Doc -scale factor [-root label]: Applies geometrical scale to assembly",
+         __FILE__, XRescaleGeometry, g);
+
   // Specialized commands
   XDEDRAW_Shapes::InitCommands ( di );
   XDEDRAW_Colors::InitCommands ( di );
index c3f5dc48e12435c64e92fab948cee82b3d42c23c..7b42e7c65c438b5fd45dae087ea15bc731c4c00e 100644 (file)
@@ -524,6 +524,84 @@ if { [regexp "LAYERS" $CompareDocumentsMode] || [regexp "ALL" $CompareDocumentsM
     set DocLayerLabels_First [XGetLayerLabels D_First]
     set DocShapeLabels_First [XGetTopLevelShapes D_First]
 }
+# Traverse assembly tree
+if {[regexp "TRAVERSE_ASSEMBLY_TREE" $CompareDocumentsMode]} {
+    if {[info exists TRAVERSE_ASSEMBLY_TREE_ARGS]} {
+      set traverse_assembly_tree_result [XDumpAssemblyTree D_First {*}$TRAVERSE_ASSEMBLY_TREE_ARGS]
+    } else {
+        set traverse_assembly_tree_result [XDumpAssemblyTree D_First]
+    }
+    set traverse_assembly_tree_result [string trim $traverse_assembly_tree_result]
+    if {$TRAVERSE_ASSEMBLY_TREE_RESULT != $traverse_assembly_tree_result} {
+        puts "ERROR: Not expected traverse assembly tree result"
+    }
+}
+# Traverse assembly graph
+if {[regexp "TRAVERSE_ASSEMBLY_GRAPH" $CompareDocumentsMode]} {
+    if {[info exists TRAVERSE_ASSEMBLY_GRAPH_ARGS]} {
+        set traverse_assembly_graph_result [XDumpAssemblyGraph D_First {*}$TRAVERSE_ASSEMBLY_GRAPH_ARGS]
+    } else {
+        set traverse_assembly_graph_result [XDumpAssemblyGraph D_First]
+    }
+    set traverse_assembly_graph_result [string trim $traverse_assembly_graph_result]
+    if {$TRAVERSE_ASSEMBLY_GRAPH_RESULT != $traverse_assembly_graph_result} {
+        puts "ERROR: Not expected traverse assembly graph result"
+    }
+}
+# Assembly nomenclature
+if {[regexp "ASSEMBLY_NOMENCLATURE" $CompareDocumentsMode]} {
+    set assembly_nomenclature_result [XDumpNomenclature D_First {*}$ASSEMBLY_NOMENCLATURE_ARGS]
+    set assembly_nomenclature_result [string trim $assembly_nomenclature_result]
+    if {$ASSEMBLY_NOMENCLATURE_RESULT != $assembly_nomenclature_result} {
+        puts "ERROR: Not expected assembly nomenclature result"
+    }
+}
+# Rescale assembly
+if {[regexp "RESCALE_ASSEMBLY" $CompareDocumentsMode]} {
+    if {[info exists RESCALE_ASSEMBLY_CHECK_BOUNDING] && $RESCALE_ASSEMBLY_CHECK_BOUNDING} {
+        XGetOneShape S_First D_First
+        bounding S_First -noTriangulation -save xmin ymin zmin xmax ymax zmax
+        set xmin_First [format "%.2f" [dval xmin]]
+        set ymin_First [format "%.2f" [dval ymin]]
+        set zmin_First [format "%.2f" [dval zmin]]
+        set xmax_First [format "%.2f" [dval xmax]]
+        set ymax_First [format "%.2f" [dval ymax]]
+        set zmax_First [format "%.2f" [dval zmax]]
+        set dx_First [expr $xmax_First - $xmin_First]
+        set dy_First [expr $ymax_First - $ymin_First]
+        set dz_First [expr $zmax_First - $zmin_First]
+    }
+    if {[info exists RESCALE_ASSEMBLY_ARGS]} {
+        XRescaleGeometry D_First $RESCALE_ASSEMBLY_FACTOR {*}$RESCALE_ASSEMBLY_ARGS
+    } else {
+        XRescaleGeometry D_First $RESCALE_ASSEMBLY_FACTOR
+    }
+    if {[info exists RESCALE_ASSEMBLY_CHECK_BOUNDING] && $RESCALE_ASSEMBLY_CHECK_BOUNDING} {
+        XGetOneShape S_Second D_First
+        bounding S_Second -noTriangulation -save xmin ymin zmin xmax ymax zmax
+        set xmin_Second [format "%.2f" [dval xmin]]
+        set ymin_Second [format "%.2f" [dval ymin]]
+        set zmin_Second [format "%.2f" [dval zmin]]
+        set xmax_Second [format "%.2f" [dval xmax]]
+        set ymax_Second [format "%.2f" [dval ymax]]
+        set zmax_Second [format "%.2f" [dval zmax]]
+        set dx_Second [expr $xmax_Second - $xmin_Second]
+        set dy_Second [expr $ymax_Second - $ymin_Second]
+        set dz_Second [expr $zmax_Second - $zmin_Second]
+        set dx_Scale [expr $dx_Second / $dx_First]
+        set dy_Scale [expr $dy_Second / $dy_First]
+        set dz_Scale [expr $dz_Second / $dz_First]
+        if { $dx_Scale != $RESCALE_ASSEMBLY_FACTOR } {
+            puts [format "Error : Compared X scale %f differs from %f specified one" $dx_Scale $RESCALE_ASSEMBLY_FACTOR]
+        }
+        if { $dy_Scale != $RESCALE_ASSEMBLY_FACTOR } {
+            puts [format "Error : Compared Y scale %f differs from %f specified one" $dy_Scale $RESCALE_ASSEMBLY_FACTOR]
+        }
+        if { $dz_Scale != $RESCALE_ASSEMBLY_FACTOR } {
+            puts [format "Error : Compared Z scale %f differs from %f specified one" $dz_Scale $RESCALE_ASSEMBLY_FACTOR]
+        }
+    }
+}
 ################## WRITING FILE ##################"
 ###Open temporary file
 if { [string compare ${TypeOfFile} ""] == 0  } {
@@ -917,9 +995,11 @@ if { [regexp "LAYERS" $CompareDocumentsMode] || [regexp "ALL" $CompareDocumentsM
     }
 }    
 
-XGetOneShape result D_Second
-if {[isdraw result]} {
-  checkview -display result -2d -path ${imagedir}/${test_image}.png
+if {![regexp "SKIP_CHECKVIEW" $CompareDocumentsMode]} {
+    XGetOneShape result D_Second
+    if {[isdraw result]} {
+      checkview -display result -2d -path ${imagedir}/${test_image}.png
+    }
 }
 
 if {[expr $ErrorCode == 2]} { 
index e0cefc8d227b8ebd0007ac5803ee7a821639ce75..b1b5ef2bc1b75dcf83a084cb0acb735dde198671 100644 (file)
@@ -9,4 +9,6 @@
 009 brep_to_stp_add_CL
 010 brep_to_xbf
 011 add_ACL_brep
-012 brep_add_CL
\ No newline at end of file
+012 brep_add_CL
+013 traverse
+014 rescale
\ No newline at end of file
diff --git a/tests/xcaf/rescale/A1 b/tests/xcaf/rescale/A1
new file mode 100644 (file)
index 0000000..5632196
--- /dev/null
@@ -0,0 +1,11 @@
+
+ReadStep D_First [locate_data_file "as1-oc-214-mat.stp"]
+
+set RESCALE_ASSEMBLY_FACTOR 0.5
+set RESCALE_ASSEMBLY_CHECK_BOUNDING 1
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "RESCALE_ASSEMBLY"
diff --git a/tests/xcaf/rescale/A2 b/tests/xcaf/rescale/A2
new file mode 100644 (file)
index 0000000..6dac670
--- /dev/null
@@ -0,0 +1,11 @@
+
+ReadStep D_First [locate_data_file "as1-oc-214-mat.stp"]
+
+set RESCALE_ASSEMBLY_FACTOR 0.5
+set RESCALE_ASSEMBLY_ARGS "-root 0:1:1:9 -force"
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "RESCALE_ASSEMBLY"
diff --git a/tests/xcaf/rescale/B1 b/tests/xcaf/rescale/B1
new file mode 100644 (file)
index 0000000..af85e8a
--- /dev/null
@@ -0,0 +1,11 @@
+
+ReadStep D_First [locate_data_file "as1_motor.step"]
+
+set RESCALE_ASSEMBLY_FACTOR 0.001
+set RESCALE_ASSEMBLY_ARGS "-root 0:1:1:1"
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "RESCALE_ASSEMBLY"
diff --git a/tests/xcaf/rescale/C1 b/tests/xcaf/rescale/C1
new file mode 100644 (file)
index 0000000..786fdfb
--- /dev/null
@@ -0,0 +1,11 @@
+
+XOpen [locate_data_file "as1_pmi.xbf"] D_First
+
+set RESCALE_ASSEMBLY_FACTOR 0.5
+set RESCALE_ASSEMBLY_CHECK_BOUNDING 1
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "RESCALE_ASSEMBLY"
diff --git a/tests/xcaf/traverse/A1 b/tests/xcaf/traverse/A1
new file mode 100644 (file)
index 0000000..02bffe8
--- /dev/null
@@ -0,0 +1,73 @@
+
+ReadStep D_First [locate_data_file "as1-oc-214-mat.stp"]
+
+set TRAVERSE_ASSEMBLY_TREE_ARGS "-names"
+set TRAVERSE_ASSEMBLY_TREE_RESULT "as1
+as1/rod-assembly_1
+as1/rod-assembly_1/nut_1
+as1/rod-assembly_1/nut_2
+as1/rod-assembly_1/rod_1
+as1/l-bracket-assembly_1
+as1/l-bracket-assembly_1/nut-bolt-assembly_1
+as1/l-bracket-assembly_1/nut-bolt-assembly_1/bolt_1
+as1/l-bracket-assembly_1/nut-bolt-assembly_1/nut_3
+as1/l-bracket-assembly_1/nut-bolt-assembly_2
+as1/l-bracket-assembly_1/nut-bolt-assembly_2/bolt_1
+as1/l-bracket-assembly_1/nut-bolt-assembly_2/nut_3
+as1/l-bracket-assembly_1/nut-bolt-assembly_3
+as1/l-bracket-assembly_1/nut-bolt-assembly_3/bolt_1
+as1/l-bracket-assembly_1/nut-bolt-assembly_3/nut_3
+as1/l-bracket-assembly_1/l-bracket_1
+as1/plate_1
+as1/l-bracket-assembly_2
+as1/l-bracket-assembly_2/nut-bolt-assembly_1
+as1/l-bracket-assembly_2/nut-bolt-assembly_1/bolt_1
+as1/l-bracket-assembly_2/nut-bolt-assembly_1/nut_3
+as1/l-bracket-assembly_2/nut-bolt-assembly_2
+as1/l-bracket-assembly_2/nut-bolt-assembly_2/bolt_1
+as1/l-bracket-assembly_2/nut-bolt-assembly_2/nut_3
+as1/l-bracket-assembly_2/nut-bolt-assembly_3
+as1/l-bracket-assembly_2/nut-bolt-assembly_3/bolt_1
+as1/l-bracket-assembly_2/nut-bolt-assembly_3/nut_3
+as1/l-bracket-assembly_2/l-bracket_1"
+
+set TRAVERSE_ASSEMBLY_GRAPH_ARGS "-names"
+set TRAVERSE_ASSEMBLY_GRAPH_RESULT "1 R 'as1' 2 9 20 22
+2 O 'rod-assembly_1' 3
+3 A 'rod-assembly' 4 6 7
+4 O 'nut_1' 5
+5 P 'nut'
+6 O 'nut_2' 5
+7 O 'rod_1' 8
+8 P 'rod'
+9 O 'l-bracket-assembly_1' 10
+10 A 'l-bracket-assembly' 11 16 17 18
+11 O 'nut-bolt-assembly_1' 12
+12 A 'nut-bolt-assembly' 13 15
+13 O 'bolt_1' 14
+14 P 'bolt'
+15 O 'nut_3' 5
+16 O 'nut-bolt-assembly_2' 12
+17 O 'nut-bolt-assembly_3' 12
+18 O 'l-bracket_1' 19
+19 P 'l-bracket'
+20 O 'plate_1' 21
+21 P 'plate'
+22 O 'l-bracket-assembly_2' 10"
+
+set ASSEMBLY_NOMENCLATURE_ARGS "-names"
+set ASSEMBLY_NOMENCLATURE_RESULT "1 R 'as1' 1
+3 A 'rod-assembly' 1
+5 P 'nut' 8
+8 P 'rod' 1
+10 A 'l-bracket-assembly' 2
+12 A 'nut-bolt-assembly' 6
+14 P 'bolt' 6
+19 P 'l-bracket' 2
+21 P 'plate' 1"
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "TRAVERSE_ASSEMBLY_TREE TRAVERSE_ASSEMBLY_GRAPH ASSEMBLY_NOMENCLATURE SKIP_CHECKVIEW"
diff --git a/tests/xcaf/traverse/A2 b/tests/xcaf/traverse/A2
new file mode 100644 (file)
index 0000000..bb7f7b0
--- /dev/null
@@ -0,0 +1,22 @@
+
+ReadStep D_First [locate_data_file "as1-oc-214-mat.stp"]
+
+set TRAVERSE_ASSEMBLY_TREE_ARGS "-root 0:1:1:1/0:1:1:2 -names"
+set TRAVERSE_ASSEMBLY_TREE_RESULT "as1/rod-assembly
+as1/rod-assembly/nut_1
+as1/rod-assembly/nut_2
+as1/rod-assembly/rod_1"
+
+set TRAVERSE_ASSEMBLY_GRAPH_ARGS "-root 0:1:1:2 -names"
+set TRAVERSE_ASSEMBLY_GRAPH_RESULT "1 A 'rod-assembly' 2 4 5
+2 O 'nut_1' 3
+3 P 'nut'
+4 O 'nut_2' 3
+5 O 'rod_1' 6
+6 P 'rod'"
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "TRAVERSE_ASSEMBLY_TREE TRAVERSE_ASSEMBLY_GRAPH SKIP_CHECKVIEW"
diff --git a/tests/xcaf/traverse/A3 b/tests/xcaf/traverse/A3
new file mode 100644 (file)
index 0000000..1302543
--- /dev/null
@@ -0,0 +1,35 @@
+
+XOpen [locate_data_file "as1_pmi.xbf"] D_First
+
+set TRAVERSE_ASSEMBLY_GRAPH_ARGS "-names"
+set TRAVERSE_ASSEMBLY_GRAPH_RESULT "1 R 'as1' 2 9 22 25
+2 O 'rod-assembly_1' 3
+3 A 'rod-assembly' 4 6 7
+4 O 'nut_1' 5
+5 P 'nut'
+6 O 'nut_2' 5
+7 O 'rod_1' 8
+8 P 'rod'
+9 O 'l-bracket-assembly_1' 10
+10 A 'l-bracket-assembly' 11 16 17 18
+11 O 'nut-bolt-assembly_1' 12
+12 A 'nut-bolt-assembly' 13 15
+13 O 'bolt_1' 14
+14 P 'bolt'
+15 O 'nut_3' 5
+16 O 'nut-bolt-assembly_2' 12
+17 O 'nut-bolt-assembly_3' 12
+18 O 'l-bracket_1' 19
+19 P 'l-bracket' 20 21
+20 S 0:1:1:8:1
+21 S 0:1:1:8:2
+22 O 'plate_1' 23
+23 P 'plate' 24
+24 S 0:1:1:9:1
+25 O 'l-bracket-assembly_2' 10"
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "TRAVERSE_ASSEMBLY_GRAPH SKIP_CHECKVIEW"
diff --git a/tests/xcaf/traverse/B1 b/tests/xcaf/traverse/B1
new file mode 100644 (file)
index 0000000..3423d87
--- /dev/null
@@ -0,0 +1,192 @@
+
+ReadStep D_First [locate_data_file "as1_motor.step"]
+
+set TRAVERSE_ASSEMBLY_TREE_ARGS "-names"
+set TRAVERSE_ASSEMBLY_TREE_RESULT "Product 2
+Product 2/as1
+Product 2/as1/rod-assembly_1
+Product 2/as1/rod-assembly_1/nut_1
+Product 2/as1/rod-assembly_1/nut_2
+Product 2/as1/rod-assembly_1/rod_1
+Product 2/as1/l-bracket-assembly_1
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_1
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_1/bolt_1
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_1/nut_3
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_2
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_2/bolt_1
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_2/nut_3
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_3
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_3/bolt_1
+Product 2/as1/l-bracket-assembly_1/nut-bolt-assembly_3/nut_3
+Product 2/as1/l-bracket-assembly_1/l-bracket_1
+Product 2/as1/plate_1
+Product 2/as1/l-bracket-assembly_2
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_1
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_1/bolt_1
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_1/nut_3
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_2
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_2/bolt_1
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_2/nut_3
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_3
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_3/bolt_1
+Product 2/as1/l-bracket-assembly_2/nut-bolt-assembly_3/nut_3
+Product 2/as1/l-bracket-assembly_2/l-bracket_1
+Product 1
+Product 1/35
+Product 1/35/Item_0
+Product 1/35/Item_0/Item_0
+Product 1/35/Item_1
+Product 1/35/Item_1/Item_0
+Product 1/35/Item_2
+Product 1/35/Item_2/Item_0
+Product 1/35/Item_3
+Product 1/35/Item_3/Item_0
+Product 1/35/Item_4
+Product 1/35/Item_4/Item_0
+Product 1/35/Item_5
+Product 1/35/Item_5/Item_0
+Product 1/35/Item_6
+Product 1/35/Item_6/Item_0
+Product 1/35/Item_7
+Product 1/35/Item_7/Item_0
+Product 1/35/Item_8
+Product 1/35/Item_8/Item_0
+Product 1/35/Item_9
+Product 1/35/Item_9/Item_0
+Product 1/35/Item_10
+Product 1/35/Item_10/Item_0
+Product 1/35/Item_11
+Product 1/35/Item_11/Item_0
+Product 1/35/Item_12
+Product 1/35/Item_12/Item_0
+Product 1/35/Item_13
+Product 1/35/Item_13/Item_0
+Product 1/35/Item_14
+Product 1/35/Item_14/Item_0
+Product 1/35/Item_15
+Product 1/35/Item_15/Item_0
+Product 1/35/Item_16
+Product 1/35/Item_16/Item_0"
+
+set TRAVERSE_ASSEMBLY_GRAPH_ARGS "-names"
+set TRAVERSE_ASSEMBLY_GRAPH_RESULT "1 R 'Product 1' 2
+2 O '35' 3
+3 A 'Product 1.1' 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68
+4 O 'Item_0' 5
+5 A 'Product 1.1.1' 6
+6 O 'Item_0' 7
+7 P 'Product 1.1.1.1'
+8 O 'Item_1' 9
+9 A 'Product 1.1.2' 10
+10 O 'Item_0' 11
+11 P 'Product 1.1.2.1'
+12 O 'Item_2' 13
+13 A 'Product 1.1.3' 14
+14 O 'Item_0' 15
+15 P 'Product 1.1.3.1'
+16 O 'Item_3' 17
+17 A 'Product 1.1.4' 18
+18 O 'Item_0' 19
+19 P 'Product 1.1.4.1'
+20 O 'Item_4' 21
+21 A 'Product 1.1.5' 22
+22 O 'Item_0' 23
+23 P 'Product 1.1.5.1'
+24 O 'Item_5' 25
+25 A 'Product 1.1.6' 26
+26 O 'Item_0' 27
+27 P 'Product 1.1.6.1'
+28 O 'Item_6' 29
+29 A 'Product 1.1.7' 30
+30 O 'Item_0' 31
+31 P 'Product 1.1.7.1'
+32 O 'Item_7' 33
+33 A 'Product 1.1.8' 34
+34 O 'Item_0' 35
+35 P 'Product 1.1.8.1'
+36 O 'Item_8' 37
+37 A 'Product 1.1.9' 38
+38 O 'Item_0' 39
+39 P 'Product 1.1.9.1'
+40 O 'Item_9' 41
+41 A 'Product 1.1.10' 42
+42 O 'Item_0' 43
+43 P 'Product 1.1.10.1'
+44 O 'Item_10' 45
+45 A 'Product 1.1.11' 46
+46 O 'Item_0' 47
+47 P 'Product 1.1.11.1'
+48 O 'Item_11' 49
+49 A 'Product 1.1.12' 50
+50 O 'Item_0' 51
+51 P 'Product 1.1.12.1'
+52 O 'Item_12' 53
+53 A 'Product 1.1.13' 54
+54 O 'Item_0' 55
+55 P 'Product 1.1.13.1'
+56 O 'Item_13' 57
+57 A 'Product 1.1.14' 58
+58 O 'Item_0' 59
+59 P 'Product 1.1.14.1'
+60 O 'Item_14' 61
+61 A 'Product 1.1.15' 62
+62 O 'Item_0' 63
+63 P 'Product 1.1.15.1'
+64 O 'Item_15' 65
+65 A 'Product 1.1.16' 66
+66 O 'Item_0' 67
+67 P 'Product 1.1.16.1'
+68 O 'Item_16' 69
+69 A 'Product 1.1.17' 70
+70 O 'Item_0' 71
+71 P 'Product 1.1.17.1'
+72 R 'Product 2' 73
+73 O 'as1' 74
+74 A 'Product 2.1' 75 82 99 101
+75 O 'rod-assembly_1' 76
+76 A 'rod-assembly' 77 79 80
+77 O 'nut_1' 78
+78 P 'nut'
+79 O 'nut_2' 78
+80 O 'rod_1' 81
+81 P 'rod'
+82 O 'l-bracket-assembly_1' 83
+83 A 'l-bracket-assembly' 84 89 93 97
+84 O 'nut-bolt-assembly_1' 85
+85 A 'nut-bolt-assembly' 86 88
+86 O 'bolt_1' 87
+87 P 'bolt'
+88 O 'nut_3' 78
+89 O 'nut-bolt-assembly_2' 90
+90 A 'nut-bolt-assembly' 91 92
+91 O 'bolt_1' 87
+92 O 'nut_3' 78
+93 O 'nut-bolt-assembly_3' 94
+94 A 'nut-bolt-assembly' 95 96
+95 O 'bolt_1' 87
+96 O 'nut_3' 78
+97 O 'l-bracket_1' 98
+98 P 'l-bracket'
+99 O 'plate_1' 100
+100 P 'plate'
+101 O 'l-bracket-assembly_2' 102
+102 A 'l-bracket-assembly' 103 107 111 115
+103 O 'nut-bolt-assembly_1' 104
+104 A 'nut-bolt-assembly' 105 106
+105 O 'bolt_1' 87
+106 O 'nut_3' 78
+107 O 'nut-bolt-assembly_2' 108
+108 A 'nut-bolt-assembly' 109 110
+109 O 'bolt_1' 87
+110 O 'nut_3' 78
+111 O 'nut-bolt-assembly_3' 112
+112 A 'nut-bolt-assembly' 113 114
+113 O 'bolt_1' 87
+114 O 'nut_3' 78
+115 O 'l-bracket_1' 98"
+
+set TypeOfFile ""
+
+set AddToDocument ""
+
+set CompareDocumentsMode "TRAVERSE_ASSEMBLY_TREE TRAVERSE_ASSEMBLY_GRAPH SKIP_CHECKVIEW"