1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-09 13:22:24 +03:00

Compare commits

...

2 Commits

Author SHA1 Message Date
ssv-occSolvers
d6dfeac244 Add documentation on XDE basics.
Documentation.


Add documentation on XDE basics.


Add documentation on XDE basics.
2017-08-23 22:23:07 +03:00
ssv
51bb1b8d6d 0029032: Provide XDE interface for exploration of assembly structure
Add assembly graph exploration tool with unit tests.
2017-08-22 16:27:26 +03:00
25 changed files with 1257 additions and 19 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -5,32 +5,116 @@
@section occt_xde_1 Introduction
This manual explains how to use the Extended Data Exchange (XDE). It provides basic documentation on setting up and using XDE. For advanced information on XDE and its applications, see our <a href="http://www.opencascade.com/content/tutorial-learning">E-learning & Training</a> offerings.
For hierarchically organizing engineering data, OCCT provides application framework (OCAF). A specialization of OCAF for representing CAD product information is XDE (eXtended Data Exchange) toolset. XDE is capable of associating geometry with metadata (PMI, names, colors, layers, etc.). This framework uses the component-level abstraction of an assembly (see \ref occt_xde_references_ShahMantyla95 "[Shah and Mantyla 1995]" for the overview of possible abstractions). The leaf nodes in the XDE hierarchy are the _part instances_. The interior nodes are the _subassemblies_. The root node represents the _entire assembly_ (several roots are allowed).
The Extended Data Exchange (XDE) module allows extending the scope of exchange by translating additional data attached to geometric BREP data, thereby improving the interoperability with external software.
Data types such as colors, layers, assembly descriptions and validation properties (i.e. center of gravity, etc.) are supported. These data are stored together with shapes in an XCAF document. It is also possible to add a new types of data taking the existing tools as prototypes.
Finally, the XDE provides reader and writer tools for reading and writing the data supported by XCAF to and from IGES and STEP files.
@figure{/user_guides/xde/images/646_xde_11_400.png,"Shape imported using XDE",240}
The XDE component requires @ref occt_user_guides__shape_healing "Shape Healing" toolkit for operation.
@figure{/user_guides/xde/images/646_xde_11_400.png,"Shape imported using XDE (courtesy of Dave Goetsch via GrabCAD).",240}
@subsection occt_xde_1_1 Basic terms
For better understanding of XDE, certain key terms are defined:
* **Shape** -- a standalone shape, which does not belong to the assembly structure.
* **Instance** -- a replication of another shape with a location that can be the same location or a different one.
* **Assembly** -- a construction that is either a root or a sub-assembly.
We use the following terminology throughout this documentation:
* **Assembly:** a hierarchy of elements representing a complex product.
* **Subassembly:** a group of elements inside an assembly. Typically, a subassembly is a set of components that are physically assembled into a unit and then assembled with other components.
* **Part:** a piece of data representing a simple individual product.
* **Instance:** a *usage occurrence* of a part or a subassembly. An instance is usually mounted to a parent component by specifying relative transformation. It should be noted that whenever term "instance" is used, it may refer to either a part or a subassembly. When an individual part is mentioned, we use a more precise "part instance" term.
* **Assembly item:** any element in the component hierarchy of an assembly. If visualization is employed, an assembly item typically corresponds to an element in a scene graph.
* **Component:** any *nested* assembly item.
The key terms are illustrated in the figure below.
@figure{/user_guides/xde/images/xde_assembly_design.png,"Parts and instances.",240}
The example above assumes that two parts (a plate and a cylinder) are created independently in some part design system. Then an assembly is constructed by instantiating these parts with some user-defined mutual placements (probably driven by assembly constraints). In this example, the root assembly item "MyAssembly" contains the following components:
* "Plate" as the only instance of the plate part.
* "Cylinders" as the subassembly which includes three instances of a cylinder part.
A CAD part in a design system has usually no link to other parts in a product unless it is dropped to an assembly structure. To become an assembly component, the part has to be instantiated. An instance is technically a reference to the part with a proper location. The distinction between *parts* and *instances* is one of the fundamental principles in assembly design. A single part may have multiple instances all sharing the same product information (geometry and metadata). It is also valid to associate metadata with the instances themselves, though the latter is not very common (and not fully supported in OCCT).
\note The important point is the distinction between parts and instances. A part is a single product which may several times occur in an assembly. An instance is a particular occurrence of a part in an assembly.
@subsection occt_xde_1_2 XDE Organization
The basis of XDE, called XCAF, is a framework based on OCAF (Open CASCADE Technology Application Framework) and is intended to be used with assemblies and with various kinds of attached data (attributes). Attributes can be Individual attributes for a shape, specifying some characteristics of a shape, or they can be Grouping attributes, specifying that a shape belongs to a given group whose definition is specified apart from the shapes.
@subsubsection occt_xde_1_2_1 Assemblies
XDE works in an OCAF document with a specific organization defined in a dedicated XCAF module. This organization is used by various functions of XDE to exchange standardized data other than shapes and geometry.
Assembly components are represented as nodes in a directed graph. The arcs of the graph represent "part-of" relations between the components (read like "component A is part of component B"). The following figure illustrates a conventional assembly from \ref occt_xde_references_cax_if "[CAx-IF STEP Library]" composed of five unique parts.
The Assembly Structure and attributes assigned to shapes are stored in the OCAF tree. It is possible to obtain TopoDS representation for each level of the assembly in the form of *TopoDS_Compound* or *TopoDS_Shape* using the API.
@figure{/user_guides/xde/images/xde_as1.png,"Sample assembly.",240}
Each part is defined in its local coordinate system and dropped to an assembly with a suitable placement. As such, the bolt has six instances, while a nut is instantiated eight times.
@figure{/user_guides/xde/images/xde_as1_parts.png,"Parts of sample assembly.",240}
The "part-of" graph is sketched in the following figure.
@figure{/user_guides/xde/images/xde_as1_part_of_graph.png,"\"Part-of\" assembly graph.",240}
There are as many component occurrences in an assembly as many paths exist to each leaf from the root nodes. Starting from the root node "as1", 18 unique ways are giving the total number of part occurrences in the component structure.
Technically, each node of the graph is represented by a label in the underlying OCAF structure. However, an OCAF label (TDF_Label) cannot be used as an identifier of an assembly item. In general case, one label can be reached from several "directions" of traversal. Thus, to address a single component occurrence unambiguously, one should use a full path to the occurrence in a component graph as an identifier. The following figure illustrates the result of assembly graph traversal which can be represented as a user-friendly tree (scene graph).
@figure{/user_guides/xde/images/xde_as1_part_of_tree.png,"\"Part-of\" assembly tree.",240}
Such a tree stands for the hierarchy of the components which can be shown to the user. A child-parent relation has "part-of" meaning. Notice that an individual node in the assembly tree is associated with a unique path to the item in the component graph. The following figure illustrates a simple bracket subassembly which is used twice as a component in the sample model.
@figure{/user_guides/xde/images/xde_as1_bracket_subassembly.png,"Bracket subassembly instantiated twice in sample model.",240}
The following figure outlines the component graph for this subassembly.
@figure{/user_guides/xde/images/xde_as1_bracket_subassembly_graph.png,"\"Part-of\" graph for bracket subassembly.",240}
Assembly structure is not directly related to any geometry. Therefore, assembly structure gives only a conceptual view of a product. According to \ref occt_xde_references_ShahMantyla95 "[Shah and Mantyla 1995]" (p. 9), many design tasks are not related to geometry at all, but to other characteristics of a product. Some problems involve idealized or simplified geometry, and it often happens that a detailed geometry is not even required (at least during some design phases). At the same time, the geometric model for a subassembly in XDE can be directly accessed at any time in the form of *TopoDS_Compound*.
XDE defines the specific organization of the assembly content. All elements are stored at sub-labels of label 0:1:1. There can be one or more roots. The hierarchy of labels is always two-levels nested. The first level (direct children of 0:1:1) declares all products within an assembly. The second level determines the "part-of" relations between the assembly elements.
@figure{/user_guides/xde/images/xde_assembly_ocaf.png,"Internal (OCAF) representation of XDE document.",240}
@subsubsection occt_xde_1_2_2 How to explore assembly structure
To explore the assembly structure, you may use a specialized *XCAFDoc_AssemblyGraph* tool. The following code snippet illustrates the construction of explicit assembly graph from XDE document.
\code
Handle(TDocStd_Document) doc = ...; // Your XDE document
// Prepare assembly graph
Handle(XCAFDoc_AssemblyGraph) asmGraph = new XCAFDoc_AssemblyGraph(doc, true);
\endcode
*XCAFDoc_AssemblyGraph* does not contain geometry of the assembly being explored. It is a formal graph structure which makes explicit the "part-of" relations between the assembly components. Each node in a graph is associated with one of the following types:
* **Root:**
* **Subassembly**
* **Part occurrence**
* **Part**
A node contains a persistent ID which points to the corresponding OCAF label. The persistent ID is a key to access geometric representations and metadata associated with the corresponding element of assembly.
@subsubsection occt_xde_1_2_3 Multiple component occurrences
Consider two parts: wheel and axle shown in the picture below.
@figure{/user_guides/xde/images/assm_wheel_axle.png,"Two parts: wheel and axle.",240}
Consider a wheel-axle subassembly composed of one axle and two wheels.
@figure{/user_guides/xde/images/assm_wheel_axle_sa.png,"Subassembly composed of two wheels and one axle.",240}
A higher-level chassis assembly is in turn composed of two wheel-axle subassemblies, the front, and the rear.
@figure{/user_guides/xde/images/assm_wheel_axle_root.png,"Chassis assembly with multiple component occurrences.",240}
There is a requirement to identify individual components such as the left-front wheel. However, a wheel part has only two instances in the owner wheel-axle subassembly. The necessity to uniquely identify all component occurrences requires introducing a *transient* key for the corresponding assembly item. This key is essentially a path in the component graph which unambiguously addresses the element in question.
\note As reported by \ref occt_xde_references_Ungerer02 "[Ungerer and Buchanan 2002]", STEP PDM Schema is capable of tracking specific usages of assembly components. However, OCCT makes no assumption on the used STEP schema, so multiple component occurrences should be implemented at the level of XDE-based application.
\todo mention Grab Instance
//////////////////////////////////////////////////////////
Basic elements used by XDE are introduced in the XCAF sub-module by the package XCAFDoc. These elements consist in descriptions of commonly used data structures (apart from the shapes themselves) in normalized data exchanges. They are not attached to specific applications and do not bring specific semantics, but are structured according to the use and needs of data exchanges.
The Document used by XDE usually starts as a *TDocStd_Document*.
@@ -45,9 +129,7 @@ For example, a mechanical assembly can be defined as follows:
@figure{/user_guides/xde/images/xde_image004.png,"Assembly View",240}
XDE defines the specific organization of the assembly content. Shapes are stored on sub-labels of label 0:1:1. There can be one or more roots (called free shapes) whether they are true trees or simple shapes. A shape can be considered to be an Assembly (such as AS1 under 0:1:1:1 in Figure1) if it is defined with Components (sub-shapes, located or not).
*XCAFDoc_ShapeTool* is a tool that allows managing the Shape section of the XCAF document. This tool is implemented as an attribute and located at the root label of the shape section.
@subsection occt_xde_1_4 Validation Properties
Validation properties are geometric characteristics of Shapes (volume, centroid, surface area) written to STEP files by the sending system. These characteristics are read by the receiving system to validate the quality of the translation. This is done by comparing the values computed by the original system with the same values computed by the receiving system on the resulting model.
@@ -685,3 +767,14 @@ As a result, if an application works on Assemblies, on Colors or Layers, on Vali
In addition, if an application has a data structure far from these notions, it can get data (such as Colors and Names on Shapes) according to its needs, but without having to consider the whole.
@section occt_xde_references References
-# \anchor occt_xde_references_ShahMantyla95
Shah J., Mantyla M. Parametric and Feature Based CAD/CAM. 1995.
-# \anchor occt_xde_references_cax_if
CAx-IF STEP File Library // URL: https://cax-if.org/library/index.html
-# \anchor occt_xde_references_Ungerer02
Ungerer, M. and Buchanan, K. 2002. Usage Guide for the STEP PDM Schema Release 4.3. PDM Implementor Forum, Darmstadt January.

View File

@@ -4,6 +4,8 @@ XCAFDoc.cxx
XCAFDoc.hxx
XCAFDoc_Area.cxx
XCAFDoc_Area.hxx
XCAFDoc_AssemblyGraph.cxx
XCAFDoc_AssemblyGraph.hxx
XCAFDoc_Centroid.cxx
XCAFDoc_Centroid.hxx
XCAFDoc_ClippingPlaneTool.cxx
@@ -40,6 +42,7 @@ XCAFDoc_Material.cxx
XCAFDoc_Material.hxx
XCAFDoc_MaterialTool.cxx
XCAFDoc_MaterialTool.hxx
XCAFDoc_ObjectId.hxx
XCAFDoc_ShapeMapTool.cxx
XCAFDoc_ShapeMapTool.hxx
XCAFDoc_ShapeTool.cxx

View File

@@ -0,0 +1,259 @@
// Created on: 2017-08-22
// Created by: Sergey SLYADNEV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
// Own include
#include <XCAFDoc_AssemblyGraph.hxx>
// OCCT includes
#include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
#include <TDataStd_Name.hxx>
#include <TDataStd_TreeNode.hxx>
#include <TDF_ChildIterator.hxx>
#include <TDF_LabelSequence.hxx>
#include <TDF_Tool.hxx>
#include <XCAFDoc.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
//-----------------------------------------------------------------------------
#define NodeLetter "N"
#define Whitespace " "
//-----------------------------------------------------------------------------
XCAFDoc_AssemblyGraph::XCAFDoc_AssemblyGraph(const Handle(TDocStd_Document)& M,
const bool withParts)
: Standard_Transient (),
m_model (M),
m_bWithParts (withParts)
{
this->buildGraph();
}
//-----------------------------------------------------------------------------
void XCAFDoc_AssemblyGraph::Dump(Standard_OStream& out) const
{
// Directed graph header
out << "digraph core_AssemblyGraph {\n";
out << "\n";
// Dump nodes with attributes
const NCollection_IndexedMap<XCAFDoc_ObjectId>& nodes = this->GetNodes();
//
for ( int n = 1; n <= nodes.Extent(); ++n )
{
// Get name of persistent object
TCollection_ExtendedString name;
this->getObjectName(nodes(n), name);
// Generate label
TCollection_AsciiString label(name);
label += "\\n"; label += nodes(n);
// Dump node with label
out << Whitespace << NodeLetter << n << " [label=\"" << label.ToCString() << "\"];\n";
}
out << "\n";
// Dump arcs
for ( t_adjacency::Iterator it(m_arcs); it.More(); it.Next() )
{
const int parentId = it.Key();
const TColStd_PackedMapOfInteger& children = it.Value();
// Loop over the children
for ( TColStd_MapIteratorOfPackedMapOfInteger cit(children); cit.More(); cit.Next() )
{
const int childId = cit.Key();
out << Whitespace
<< NodeLetter << parentId
<< " -> "
<< NodeLetter << childId
<< ";\n";
}
}
out << "\n";
out << "}\n";
}
//-----------------------------------------------------------------------------
void XCAFDoc_AssemblyGraph::CalculateSummary(int& numRoots,
int& numSubassemblies,
int& numPartOccurrences,
int& numParts) const
{
numRoots = 0;
numSubassemblies = 0;
numPartOccurrences = 0;
numParts = 0;
// Loop over the nodes
for ( int n = 1; n <= this->GetNodes().Extent(); ++n )
{
if ( !m_nodeTypes.IsBound(n) ) continue; // This should never happen
switch ( m_nodeTypes(n) )
{
case NodeType_Root: ++numRoots; break;
case NodeType_Subassembly: ++numSubassemblies; break;
case NodeType_PartOccurrence: ++numPartOccurrences; break;
case NodeType_Part: ++numParts; break;
default: break;
}
}
}
//-----------------------------------------------------------------------------
void XCAFDoc_AssemblyGraph::buildGraph()
{
// Get shape tool
Handle(XCAFDoc_ShapeTool)
shapeTool = XCAFDoc_DocumentTool::ShapeTool( m_model->Main() );
// We start from those shapes which are "free" in terms of XDE
TDF_LabelSequence roots;
//
shapeTool->GetFreeShapes(roots);
//
for ( TDF_LabelSequence::Iterator it(roots); it.More(); it.Next() )
{
const TDF_Label& label = it.Value();
// Get entry of the current label
XCAFDoc_ObjectId objectId;
TDF_Tool::Entry(label, objectId);
// Free shapes are root nodes of the assembly graph
const int iObjectId = m_nodes.Add(objectId);
// Mark as root
m_nodeTypes.Bind(iObjectId, NodeType_Root);
//
m_roots.Add(iObjectId);
// Add components (the objects nested into the current one)
this->addComponents(label, iObjectId);
}
}
//-----------------------------------------------------------------------------
void XCAFDoc_AssemblyGraph::addComponents(const TDF_Label& parent,
const int iParentId)
{
// Get shape tool
Handle(XCAFDoc_ShapeTool)
shapeTool = XCAFDoc_DocumentTool::ShapeTool( m_model->Main() );
const bool isSubassembly = shapeTool->IsAssembly(parent);
// Bind topological type. We check that no attribute is associated to
// prevent multiple tagging of the same node from different directions
// of traversal.
if ( !m_nodeTypes.IsBound(iParentId) )
{
if ( isSubassembly )
m_nodeTypes.Bind(iParentId, NodeType_Subassembly);
else
{
m_nodeTypes.Bind(iParentId, NodeType_PartOccurrence);
// If parts are requested to participate in the graph, we add more nodes
if ( m_bWithParts )
{
// Get entry of the current label which is the original label
XCAFDoc_ObjectId partId;
TDF_Tool::Entry(parent, partId);
// Add node
const int iPartId = m_nodes.Add(partId);
// Add arc
if ( !m_arcs.IsBound(iParentId) )
m_arcs.Bind( iParentId, TColStd_PackedMapOfInteger() );
//
m_arcs(iParentId).Add(iPartId);
// Bind type
if ( !m_nodeTypes.IsBound(iPartId) )
m_nodeTypes.Bind(iPartId, NodeType_Part);
}
}
}
if ( !isSubassembly )
return; // We have to return here in order to prevent iterating by
// sub-labels. For parts, sub-labels are used to encode
// metadata which is out of interest in conceptual design
// intent represented by assembly graph.
// Loop over the children (persistent representation of "part-of" relation)
for ( TDF_ChildIterator cit(parent); cit.More(); cit.Next() )
{
TDF_Label child = cit.Value();
// Get entry of the current label
XCAFDoc_ObjectId childId;
TDF_Tool::Entry(child, childId);
// Add node
const int iChildId = m_nodes.Add(childId);
// Add arc
if ( !m_arcs.IsBound(iParentId) )
m_arcs.Bind( iParentId, TColStd_PackedMapOfInteger() );
//
m_arcs(iParentId).Add(iChildId);
// Jump to the referred object (the original)
TDF_Label childOriginal;
Handle(TDataStd_TreeNode) jumpTreeNode;
child.FindAttribute(XCAFDoc::ShapeRefGUID(), jumpTreeNode);
//
if ( !jumpTreeNode.IsNull() && jumpTreeNode->HasFather() )
childOriginal = jumpTreeNode->Father()->Label(); // Declaration-level origin
// Process children: add components recursively
if ( !childOriginal.IsNull() )
this->addComponents(childOriginal, iChildId);
}
}
//-----------------------------------------------------------------------------
bool XCAFDoc_AssemblyGraph::getObjectName(const XCAFDoc_ObjectId& id,
TCollection_ExtendedString& name) const
{
// Get label for object ID
TDF_Label label;
TDF_Tool::Label(m_model->GetData(), id, label);
// Access name
Handle(TDataStd_Name) nameAttr;
if ( !label.FindAttribute(TDataStd_Name::GetID(), nameAttr) )
{
name = "";
return false;
}
//
name = nameAttr->Get();
return true;
}

View File

@@ -0,0 +1,292 @@
// Created on: 2017-08-22
// Created by: Sergey SLYADNEV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _XCAFDoc_AssemblyGraph_HeaderFile
#define _XCAFDoc_AssemblyGraph_HeaderFile
// XDE includes
#include <XCAFDoc_ObjectId.hxx>
// Other OCCT includes
#include <NCollection_DataMap.hxx>
#include <TColStd_PackedMapOfInteger.hxx>
#include <TDocStd_Document.hxx>
//! \brief Assembly graph.
//!
//! This tool gives clear OCAF-agnostic interface to
//! the assembly structure of a product. A graph is essentially a set of
//! nodes {N} and a set of arcs {A} as defined formally.
//!
//! <pre>
//! G = <N, A>
//! </pre>
//!
//! Using this tool, you can map XDE assembly items to a formal graph
//! structure. Each node in the graph preserves a link to the data storage
//! (OCAF document) by means of \ref XCAFDoc_ObjectId.
//!
//! \sa XCAFDoc_ObjectId
class XCAFDoc_AssemblyGraph : public Standard_Transient
{
public:
//! \brief Type of the graph node.
enum NodeType
{
NodeType_UNDEFINED = 0, //!< Undefined node type.
//
NodeType_Root, //!< Root node (has no entry arcs).
NodeType_Subassembly, //!< Intermediate node (non-leaf node which has entry arcs).
NodeType_PartOccurrence, //!< Part usage occurrence.
NodeType_Part //!< Optional leaf node to represent parts. Note that
//!< this node type is activated by a dedicated flag in
//!< the constructor. If activated, the part occurrence nodes
//!< are not leafs anymore.
};
public:
// OCCT RTTI
DEFINE_STANDARD_RTTI_INLINE(XCAFDoc_AssemblyGraph, Standard_Transient)
public:
//! \brief Graph iterator.
class Iterator
{
public:
//! Default ctor.
Iterator() : m_iCurrentIndex(0) {}
//! ctor accepting the assembly graph to iterate.
//! \param[in] asmGraph assembly graph to iterate.
Iterator(const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
{
this->Init(asmGraph);
}
public:
//! Initializes iterator with assembly graph.
//! \param[in] asmGraph assembly graph to iterate.
void Init(const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
{
m_graph = asmGraph;
m_iCurrentIndex = 1;
}
//! Checks if there are more graph nodes to iterate.
//! \return true/false.
bool More() const
{
return m_iCurrentIndex <= m_graph->GetNodes().Extent();
}
//! \return 1-based ID of the current node.
int GetCurrentNode() const
{
return m_iCurrentIndex;
}
//! Moves iterator to the next position.
void Next()
{
++m_iCurrentIndex;
}
protected:
Handle(XCAFDoc_AssemblyGraph) m_graph; //!< Assembly graph to iterate.
int m_iCurrentIndex; //!< Current 1-based node ID.
};
public:
//! Type definition for graph adjacency matrix. This is how parent-component
//! links are realized in the assembly graph.
typedef NCollection_DataMap<int, TColStd_PackedMapOfInteger> t_adjacency;
public:
//! \brief Initializes graph from Data Model.
//!
//! Construction of a formal graph will be done immediately at ctor.
//!
//! \param[in] M Data Model to iterate.
//! \param[in] withParts indicates whether to add nodes representing the
//! instanced parts to the assembly graph.
Standard_EXPORT
XCAFDoc_AssemblyGraph(const Handle(TDocStd_Document)& M,
const bool withParts = false);
public:
//! \brief Dumps graph structure to output stream.
//!
//! The output format is DOT. You may use graph rendering tools like
//! Graphviz to parse the output.
//!
//! \param[out] out output stream.
Standard_EXPORT void
Dump(Standard_OStream& out) const;
//! \brief Calculates short summary for the assembly.
//!
//! Short summary gives you the total number of nodes of a particular
//! type. Note that the number of parts will be calculated only if parts
//! are available in the graph (which is the option by construction).
//!
//! \param[out] numRoots number of root nodes.
//! \param[out] numSubassemblies number of subassembly nodes.
//! \param[out] numPartOccurrences number of part usage occurrence nodes.
//! \param[out] numParts number of parts (if available).
Standard_EXPORT void
CalculateSummary(int& numRoots,
int& numSubassemblies,
int& numPartOccurrences,
int& numParts) const;
public:
//! \brief Returns IDs of the root nodes.
//! \return IDs of the root nodes.
const TColStd_PackedMapOfInteger& GetRoots() const
{
return m_roots;
}
//! \brief Checks whether the assembly graph contains (n1, n2) directed arc.
//! \param[in] n1 one-based ID of the first node.
//! \param[in] n2 one-based ID of the second node.
//! \return true/false.
bool HasArc(const int n1, const int n2) const
{
if ( !this->HasChildren(n1) )
return false;
return this->GetChildren(n1).Contains(n2);
}
//! \brief Checks whether children exist for the given node.
//! \param[in] oneBasedNodeId one-based node ID.
//! \return true/false.
bool HasChildren(const int oneBasedNodeId) const
{
return m_arcs.IsBound(oneBasedNodeId);
}
//! \brief Returns IDs of child nodes for the given node.
//! \param[in] oneBasedNodeId one-based node ID.
//! \return set of child IDs.
const TColStd_PackedMapOfInteger& GetChildren(const int oneBasedNodeId) const
{
return m_arcs(oneBasedNodeId);
}
//! \brief Returns the node type from \ref NodeType enum.
//! \param[in] oneBasedNodeId one-based node ID.
//! \return node type.
//! \sa NodeType
NodeType GetNodeType(const int oneBasedNodeId) const
{
if ( !m_nodeTypes.IsBound(oneBasedNodeId) )
return NodeType_UNDEFINED;
return m_nodeTypes(oneBasedNodeId);
}
//! \brief returns object ID by node ID.
//! \param[in] oneBasedNodeId one-based node ID.
//! \return persistent ID.
const XCAFDoc_ObjectId& GetPersistentId(const int oneBasedNodeId) const
{
return m_nodes(oneBasedNodeId);
}
//! \brief Returns the unordered set of graph nodes.
//! \return graph nodes.
const NCollection_IndexedMap<XCAFDoc_ObjectId>& GetNodes() const
{
return m_nodes;
}
//! \brief Returns the number of graph nodes.
//! \return number of graph nodes.
int GetNumberOfNodes() const
{
return m_nodes.Extent();
}
//! \brief Returns the collection of graph arcs in form of adjacency matrix.
//! \return graph arcs.
const t_adjacency& GetArcs() const
{
return m_arcs;
}
//! \brief Returns the number of graph arcs.
//! \return number of graph arcs.
int GetNumberOfArcs() const
{
int numArcs = 0;
//
for ( t_adjacency::Iterator it(m_arcs); it.More(); it.Next() )
numArcs += it.Value().Extent();
return numArcs;
}
protected:
//! Builds graph out of OCAF XDE structure.
Standard_EXPORT void
buildGraph();
//! Adds components for the given root to the graph structure.
//! \param[in] parent OCAF label of the parent object.
//! \param[in] iParentId ID of the already registered node
//! representing the parent object in the assembly
//! graph being populated.
Standard_EXPORT void
addComponents(const TDF_Label& parent,
const int iParentId);
//! Returns object name for the given persistent ID.
//! \param[in] id persistent ID.
//! \param[out] name object name.
//! \return false if no name is associated with the object or the object
//! does not exist.
Standard_EXPORT bool
getObjectName(const XCAFDoc_ObjectId& id,
TCollection_ExtendedString& name) const;
protected:
// INPUTS
Handle(TDocStd_Document) m_model; //!< Data Model instance.
bool m_bWithParts; //!< Indicates whether to include parts to the graph.
// OUTPUTS
TColStd_PackedMapOfInteger m_roots; //!< IDs of the root nodes.
NCollection_IndexedMap<XCAFDoc_ObjectId> m_nodes; //!< Graph nodes.
t_adjacency m_arcs; //!< "Part-of" relations.
NCollection_DataMap<int, NodeType> m_nodeTypes; //!< Node types (cached for efficiency).
};
#endif

View File

@@ -0,0 +1,25 @@
// Created on: 2017-08-22
// Created by: Sergey SLYADNEV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _XCAFDoc_ObjectId_HeaderFile
#define _XCAFDoc_ObjectId_HeaderFile
// OCCT includes
#include <TCollection_AsciiString.hxx>
//! Persistent object ID.
typedef TCollection_AsciiString XCAFDoc_ObjectId;
#endif

View File

@@ -1,9 +1,13 @@
XDEDRAW.cxx
XDEDRAW.hxx
XDEDRAW_Assemblies.hxx
XDEDRAW_Assemblies.cxx
XDEDRAW_Colors.cxx
XDEDRAW_Colors.hxx
XDEDRAW_Common.cxx
XDEDRAW_Common.hxx
XDEDRAW_DrawableAssemblyGraph.cxx
XDEDRAW_DrawableAssemblyGraph.hxx
XDEDRAW_Layers.cxx
XDEDRAW_Layers.hxx
XDEDRAW_Props.cxx

View File

@@ -80,6 +80,7 @@
#include <XCAFPrs.hxx>
#include <XCAFPrs_Driver.hxx>
#include <XDEDRAW.hxx>
#include <XDEDRAW_Assemblies.hxx>
#include <XDEDRAW_Colors.hxx>
#include <XDEDRAW_Common.hxx>
#include <XDEDRAW_Layers.hxx>
@@ -1165,6 +1166,7 @@ void XDEDRAW::Init(Draw_Interpretor& di)
di.Add ("XTestDoc", "XTestDoc shape", __FILE__, testDoc, g);
// Specialized commands
XDEDRAW_Assemblies::InitCommands ( di );
XDEDRAW_Shapes::InitCommands ( di );
XDEDRAW_Colors::InitCommands ( di );
XDEDRAW_Layers::InitCommands ( di );

View File

@@ -0,0 +1,308 @@
// Created on: 2017-08-22
// Created by: Sergey SLYADNEV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
// Own include
#include <XDEDRAW_Assemblies.hxx>
// Other OCCT includes
#include <DDocStd.hxx>
#include <Draw.hxx>
#include <NCollection_Vector.hxx>
#include <XDEDRAW_DrawableAssemblyGraph.hxx>
// Standard includes
#include <vector>
//-----------------------------------------------------------------------------
std::vector< std::pair<TCollection_AsciiString, XCAFDoc_AssemblyGraph::NodeType> >
XAssembly_NodeTypes = {
{"-roots", XCAFDoc_AssemblyGraph::NodeType_Root},
{"-subassemblies", XCAFDoc_AssemblyGraph::NodeType_Subassembly},
{"-partoccurrences", XCAFDoc_AssemblyGraph::NodeType_PartOccurrence},
{"-parts", XCAFDoc_AssemblyGraph::NodeType_Part}
};
//-----------------------------------------------------------------------------
//! Returns true if the passed command line option specifies node type.
//! \param[in] opt option to check.
//! \return true/false.
bool XAssembly_IsKeywordNodeType(const TCollection_AsciiString& opt)
{
for ( size_t s = 0; s < XAssembly_NodeTypes.size(); ++s )
if ( opt == XAssembly_NodeTypes[s].first )
return true;
return false;
}
//-----------------------------------------------------------------------------
//! Checks if the given graph node is of expected type.
//! \param[in] what node to check.
//! \param[in] opt expected type.
//! \param[in] asmGraph assembly graph.
//! \return true/false.
bool XAssembly_IsOfExpectedType(const int n,
const TCollection_AsciiString& opt,
const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
{
const XCAFDoc_AssemblyGraph::NodeType actualType = asmGraph->GetNodeType(n);
// Check against registered types
TCollection_AsciiString expectedOpt;
for ( size_t t = 0; t < XAssembly_NodeTypes.size(); ++t )
{
if ( actualType == XAssembly_NodeTypes[t].second )
{
expectedOpt = XAssembly_NodeTypes[t].first;
break;
}
}
if ( expectedOpt.IsEmpty() )
return false;
return (expectedOpt == opt);
}
//-----------------------------------------------------------------------------
//! Builds assembly graph for the given model.
//! \param[in] di Draw interpreter instance.
//! \param[in] argc number of arguments.
//! \param[in] argv list of arguments.
//! \return execution result.
static int XAssemblyGraph(Draw_Interpretor& di, int argc, const char** argv)
{
// Preliminary checks
if ( argc != 4 )
{
std::cout << "Incorrect number of arguments. Check help for details..." << std::endl;
return 1; // Failure
}
// Get document
Handle(TDocStd_Document) doc;
DDocStd::GetDocument(argv[1], doc);
if ( doc.IsNull() )
{
di << argv[1] << " is not a document\n"; return 1;
}
// Whether to include parts to assembly graph or not
const bool withParts = ( atoi(argv[2]) > 0 );
// Construct assembly graph
Handle(XCAFDoc_AssemblyGraph) asmGraph = new XCAFDoc_AssemblyGraph(doc, withParts);
// Register assembly graph in Draw
Draw::Set( argv[3], new XDEDRAW_DrawableAssemblyGraph(asmGraph) );
return 0; // Success
}
//-----------------------------------------------------------------------------
//! Checks assembly graph.
//! \param[in] di Draw interpreter instance.
//! \param[in] argc number of arguments.
//! \param[in] argv list of arguments.
//! \return execution result.
static int XAssemblyGraphCheck(Draw_Interpretor& di, int argc, const char** argv)
{
// Preliminary checks
if ( argc < 3 )
{
std::cout << "Incorrect number of arguments. Check help for details..." << std::endl;
return 1; // Failure
}
// Get assembly graph
Handle(XDEDRAW_DrawableAssemblyGraph)
DAG = Handle(XDEDRAW_DrawableAssemblyGraph)::DownCast( Draw::Get(argv[1]) );
//
if ( DAG.IsNull() )
{
std::cout << "Error: Drawable Assembly Graph is NULL" << std::endl;
return 1; // Failure
}
//
Handle(XCAFDoc_AssemblyGraph) asmGraph = DAG->GetGraph();
// Get summary
int numRoots = 0;
int numSubassemblies = 0;
int numPartInstances = 0;
int numParts = 0;
//
asmGraph->CalculateSummary(numRoots, numSubassemblies, numPartInstances, numParts);
// Check according to the argument keys
for ( int i = 2; i < argc; ++i )
{
TCollection_AsciiString opt(argv[i]);
opt.LowerCase();
// Check the number of graph nodes
if ( opt == "-numnodes" )
{
const int num = atoi( argv[++i] );
//
if ( num != asmGraph->GetNumberOfNodes() )
{
di << 0; // FALSE
std::cout << "Error: unexpected number of nodes" << std::endl;
return 0;
}
}
// Check the number of graph arcs
else if ( opt == "-numarcs" )
{
const int num = atoi( argv[++i] );
//
if ( num != asmGraph->GetNumberOfArcs() )
{
di << 0; // FALSE
std::cout << "Error: unexpected number of arcs" << std::endl;
return 0;
}
}
// Check the number of roots
else if ( opt == "-numroots" )
{
const int num = atoi( argv[++i] );
//
if ( num != numRoots )
{
di << 0; // FALSE
std::cout << "Error: unexpected number of roots" << std::endl;
return 0;
}
}
// Check the number of subassemblies
else if ( opt == "-numsubassemblies" )
{
const int num = atoi( argv[++i] );
//
if ( num != numSubassemblies )
{
di << 0; // FALSE
std::cout << "Error: unexpected number of subassemblies" << std::endl;
return 0;
}
}
// Check the number of part occurrences
else if ( opt == "-numpartoccurrences" )
{
const int num = atoi( argv[++i] );
//
if ( num != numPartInstances )
{
di << 0; // FALSE
std::cout << "Error: unexpected number of part occurrences" << std::endl;
return 0;
}
}
// Check the number of parts
else if ( opt == "-numparts" )
{
const int num = atoi( argv[++i] );
//
if ( num != numParts )
{
di << 0; // FALSE
std::cout << "Error: unexpected number of parts" << std::endl;
return 0;
}
}
// Check individual arc
else if ( opt == "-arc" )
{
const int n1 = atoi( argv[++i] );
const int n2 = atoi( argv[++i] );
//
if ( !asmGraph->HasArc(n1, n2) )
{
di << 0; // FALSE
std::cout << "Error: arc (" << n1 << ", " << n2 << ") does not exist" << std::endl;
return 0;
}
}
// Check individual node types
else if ( XAssembly_IsKeywordNodeType(opt) )
{
do
{
// Get node index
const int n = atoi( argv[++i] );
// Check type
if ( !XAssembly_IsOfExpectedType(n, opt, asmGraph) )
{
di << 0; // FALSE
std::cout << "Error: unexpected type for node " << n << std::endl;
return 0;
}
}
while ( i < (argc - 1) && !XAssembly_IsKeywordNodeType(argv[i + 1]) );
}
}
di << 1; // TRUE
return 0; // Success
}
//=======================================================================
//function : InitCommands
//purpose :
//=======================================================================
void XDEDRAW_Assemblies::InitCommands(Draw_Interpretor& di)
{
static Standard_Boolean initactor = Standard_False;
if (initactor)
{
return;
}
initactor = Standard_True;
Standard_CString grp = "XDE assembly commands";
di.Add("XAssemblyGraph", "XAssemblyGraph doc withParts graph",
__FILE__, XAssemblyGraph, grp);
di.Add("XAssemblyGraphCheck", "XAssemblyGraphCheck [-numnodes num] "
"[-numarcs num] "
"[-numroots num] "
"[-numsubassemblies num] "
"[-numpartoccurrences num] "
"[-numparts num] "
"[-arc n1 n2] "
"[-arc n2 n3] "
" ... "
"[-roots n1 n2 ...] "
"[-subassemblies n3 n4 ...] "
"[-partoccurrences n5 n6 ...] "
"[-parts n7 n8 ...] ",
__FILE__, XAssemblyGraphCheck, grp);
}

View File

@@ -0,0 +1,32 @@
// Created on: 2017-08-22
// Created by: Sergey SLYADNEV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _XDEDRAW_Assemblies_HeaderFile
#define _XDEDRAW_Assemblies_HeaderFile
#include <Draw_Interpretor.hxx>
//! Draw commands for new XDE interface dedicated to assemblies.
class XDEDRAW_Assemblies
{
public:
DEFINE_STANDARD_ALLOC
Standard_EXPORT static void InitCommands(Draw_Interpretor& theCommands);
};
#endif // _XDEDRAW_Assemblies_HeaderFile

View File

@@ -0,0 +1,52 @@
// Created on: 2017-08-22
// Created by: Sergey SLYADNEV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
// Own include
#include <XDEDRAW_DrawableAssemblyGraph.hxx>
//-----------------------------------------------------------------------------
XDEDRAW_DrawableAssemblyGraph::XDEDRAW_DrawableAssemblyGraph(const Handle(XCAFDoc_AssemblyGraph)& asmGraph)
: Draw_Drawable3D (),
m_graph (asmGraph)
{}
//-----------------------------------------------------------------------------
inline const Handle(XCAFDoc_AssemblyGraph)& XDEDRAW_DrawableAssemblyGraph::GetGraph() const
{
return m_graph;
}
//-----------------------------------------------------------------------------
void XDEDRAW_DrawableAssemblyGraph::DrawOn(Draw_Display&) const
{
this->Dump(std::cout);
}
//-----------------------------------------------------------------------------
void XDEDRAW_DrawableAssemblyGraph::Dump(Standard_OStream& out) const
{
m_graph->Dump(out);
}
//-----------------------------------------------------------------------------
void XDEDRAW_DrawableAssemblyGraph::Whatis(Draw_Interpretor& di) const
{
di << "Assembly graph";
}

View File

@@ -0,0 +1,63 @@
// Created on: 2017-08-22
// Created by: Sergey SLYADNEV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _XDEDRAW_DrawableAssemblyGraph_HeaderFile
#define _XDEDRAW_DrawableAssemblyGraph_HeaderFile
// XDE includes
#include <XCAFDoc_AssemblyGraph.hxx>
// OCCT includes
#include <Draw_Drawable3D.hxx>
//! Drawable container for assembly graph.
class XDEDRAW_DrawableAssemblyGraph : public Draw_Drawable3D
{
public:
// OCCT RTTI
DEFINE_STANDARD_RTTI_INLINE(XDEDRAW_DrawableAssemblyGraph, Draw_Drawable3D)
public:
//! Ctor accepting an assembly graph.
//! \param[in] asmGraph assembly graph to have as drawable.
Standard_EXPORT
XDEDRAW_DrawableAssemblyGraph(const Handle(XCAFDoc_AssemblyGraph)& asmGraph);
public:
//! \return stored assembly graph.
Standard_EXPORT const Handle(XCAFDoc_AssemblyGraph)&
GetGraph() const;
public:
Standard_EXPORT void
DrawOn(Draw_Display& dis) const Standard_OVERRIDE;
Standard_EXPORT virtual void
Dump(Standard_OStream& out) const Standard_OVERRIDE;
Standard_EXPORT virtual void
Whatis(Draw_Interpretor& di) const Standard_OVERRIDE;
private:
Handle(XCAFDoc_AssemblyGraph) m_graph; //!< Assembly graph to "draw".
};
#endif

69
tests/bugs/xde/bug29032_1 Normal file
View File

@@ -0,0 +1,69 @@
puts "============"
puts "CR29032"
puts "============"
puts ""
pload VISUALIZATION
##########################################################################
# Provide XDE interface for exploration of assembly structure
##########################################################################
ReadStep d [locate_data_file trj3_as1-tc-214.stp]
XShow d
vfit
vsetdispmode 1
checkview -screenshot -3d -path ${imagedir}/${::casename}_1.png
# Build assembly graph with parts
if { [XAssemblyGraph d 1 DAG] == 0 } {
puts "Error: cannot build assembly graph"
}
dump DAG
# Check assembly graph
set ret [XAssemblyGraphCheck DAG \
-numnodes 19 \
-numarcs 28 \
-numroots 1 \
-numsubassemblies 6 \
-numpartoccurrences 7 \
-numparts 5 \
-arc 1 2 \
-arc 1 12 \
-arc 1 14 \
-arc 1 15 \
-arc 2 3 \
-arc 2 5 \
-arc 2 10 \
-arc 2 11 \
-arc 3 4 \
-arc 5 6 \
-arc 5 8 \
-arc 6 7 \
-arc 8 9 \
-arc 10 6 \
-arc 10 8 \
-arc 11 6 \
-arc 11 8 \
-arc 12 13 \
-arc 14 3 \
-arc 14 5 \
-arc 14 10 \
-arc 14 11 \
-arc 15 16 \
-arc 15 18 \
-arc 15 19 \
-arc 16 17 \
-arc 18 7 \
-arc 19 7 \
-roots 1 \
-subassemblies 2 5 10 11 14 15 \
-partoccurrences 3 8 6 12 16 18 19 \
-parts 4 7 9 13 17]
if { $ret == 0 } {
puts "Error: unexpected contents of assembly graph"
}

36
tests/bugs/xde/bug29032_2 Normal file
View File

@@ -0,0 +1,36 @@
puts "============"
puts "CR29032"
puts "============"
puts ""
pload VISUALIZATION
##########################################################################
# Provide XDE interface for exploration of assembly structure
##########################################################################
ReadStep d [locate_data_file OCC137-ANC101-Solid.stp]
XShow d
vfit
vsetdispmode 1
checkview -screenshot -3d -path ${imagedir}/${::casename}_1.png
# Build assembly graph with parts
if { [XAssemblyGraph d 1 DAG] == 0 } {
puts "Error: cannot build assembly graph"
}
dump DAG
# Check assembly graph
set ret [XAssemblyGraphCheck DAG \
-numnodes 1 \
-numarcs 0 \
-numroots 1 \
-numsubassemblies 0 \
-roots 1]
if { $ret == 0 } {
puts "Error: unexpected contents of assembly graph"
}