From 2179685dd2dee963c6f55cf293738ce12659bfee Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Thu, 16 May 2024 01:33:03 +0300 Subject: [PATCH] STEP export: Use TDocStd_XLinkTool to copy model with colors instead of our DIY routine. Now supports older OCCT versions. Also puts components at a higher level in the hierarchy. Fixes https://gitlab.com/kicad/code/kicad/-/issues/17549 --- pcbnew/CMakeLists.txt | 1 + .../step/KI_XCAFDoc_AssemblyGraph.cxx | 270 ++++++++++++++++++ .../step/KI_XCAFDoc_AssemblyGraph.hxx | 220 ++++++++++++++ pcbnew/exporters/step/step_pcb_model.cpp | 253 ++++++++-------- 4 files changed, 627 insertions(+), 117 deletions(-) create mode 100644 pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.cxx create mode 100644 pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.hxx diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 977ca80698..ba081a9902 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -218,6 +218,7 @@ set( PCBNEW_EXPORTERS exporters/export_svg.cpp exporters/step/exporter_step.cpp exporters/step/step_pcb_model.cpp + exporters/step/KI_XCAFDoc_AssemblyGraph.cxx exporters/exporter_vrml.cpp exporters/place_file_exporter.cpp exporters/gen_drill_report_files.cpp diff --git a/pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.cxx b/pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.cxx new file mode 100644 index 0000000000..7cad410a0f --- /dev/null +++ b/pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.cxx @@ -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 +#include +#include +#include +#include +#include +#include +#include "KI_XCAFDoc_AssemblyGraph.hxx" +#include +#include + +// ======================================================================= +// function : KI_XCAFDoc_AssemblyGraph constructor +// purpose : Builds an assembly graph from the OCAF document +// ======================================================================= + +KI_XCAFDoc_AssemblyGraph::KI_XCAFDoc_AssemblyGraph(const Handle(TDocStd_Document)& theDoc) +{ + Standard_NullObject_Raise_if(theDoc.IsNull(), "Null document!"); + + myShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDoc->Main()); + Standard_NoSuchObject_Raise_if(myShapeTool.IsNull(), "No XCAFDoc_ShapeTool attribute!"); + + TDF_Label aDummy; + buildGraph(aDummy); +} + +// ======================================================================= +// function : KI_XCAFDoc_AssemblyGraph constructor +// purpose : Builds an assembly graph from the OCAF label +// ======================================================================= + +KI_XCAFDoc_AssemblyGraph::KI_XCAFDoc_AssemblyGraph(const TDF_Label& theLabel) +{ + Standard_NullObject_Raise_if(theLabel.IsNull(), "Null label!"); + + myShapeTool = XCAFDoc_DocumentTool::ShapeTool(theLabel); + Standard_NoSuchObject_Raise_if(myShapeTool.IsNull(), "No XCAFDoc_ShapeTool attribute!"); + + buildGraph(theLabel); +} + +// ======================================================================= +// function : IsDirectLink +// purpose : Checks if one node is the direct child of other one +// ======================================================================= + +Standard_Boolean KI_XCAFDoc_AssemblyGraph::IsDirectLink(const Standard_Integer theNode1, + const Standard_Integer theNode2) const +{ + if (!HasChildren(theNode1)) + return Standard_False; + + return GetChildren(theNode1).Contains(theNode2); +} + +// ======================================================================= +// function : GetNodeType +// purpose : Returns node type +// ======================================================================= + +KI_XCAFDoc_AssemblyGraph::NodeType +KI_XCAFDoc_AssemblyGraph::GetNodeType(const Standard_Integer theNode) const +{ + const NodeType* typePtr = myNodeTypes.Seek(theNode); + if (typePtr == NULL) + return NodeType_UNDEFINED; + + return (*typePtr); +} + +// ======================================================================= +// function : NbLinks +// purpose : Calculates and returns the number of links +// ======================================================================= + +Standard_Integer KI_XCAFDoc_AssemblyGraph::NbLinks() const +{ + Standard_Integer aNumLinks = 0; + for (AdjacencyMap::Iterator it(myAdjacencyMap); it.More(); it.Next()) + { + aNumLinks += it.Value().Extent(); + } + return aNumLinks; +} + +// ======================================================================= +// function : GetUsageOccurrenceQuantity +// purpose : +// ======================================================================= + +Standard_Integer KI_XCAFDoc_AssemblyGraph::NbOccurrences(const Standard_Integer theNode) const +{ + const Standard_Integer* aUsageOQPtr = myUsages.Seek(theNode); + if (aUsageOQPtr == NULL) + return 0; + + return (*aUsageOQPtr); +} + +// ======================================================================= +// function : buildGraph +// purpose : Builds an assembly graph from the OCAF document +// ======================================================================= + +void KI_XCAFDoc_AssemblyGraph::buildGraph(const TDF_Label& theLabel) +{ + // We start from those shapes which are "free" in terms of XDE. + TDF_LabelSequence aRoots; + if (theLabel.IsNull() || (myShapeTool->Label() == theLabel)) + myShapeTool->GetFreeShapes(aRoots); + else + aRoots.Append(theLabel); + + for (TDF_LabelSequence::Iterator it(aRoots); it.More(); it.Next()) + { + TDF_Label aLabel = it.Value(); + + TDF_Label anOriginal; + if (!myShapeTool->GetReferredShape(aLabel, anOriginal)) + anOriginal = aLabel; + + const Standard_Integer aRootId = addNode(anOriginal, 0); + if (aRootId == 0) + continue; + + myRoots.Add(aRootId); + + // Add components (the objects nested into the current one). + if (myShapeTool->IsAssembly(anOriginal)) + addComponents(anOriginal, aRootId); + } +} + +// ======================================================================= +// function : addComponents +// purpose : Adds components for the given parent to the graph structure +// ======================================================================= + +void KI_XCAFDoc_AssemblyGraph::addComponents(const TDF_Label& theParent, + const Standard_Integer theParentId) +{ + if (!myShapeTool->IsShape(theParent)) + { + return; // We have to return here in order to prevent iterating by + // sub-labels. For parts, sub-labels are used to encode + // metadata which is out of interest in conceptual design + // intent represented by assembly graph. + } + + // Loop over the children (persistent representation of "part-of" relation). + for (TDF_ChildIterator anIt(theParent); anIt.More(); anIt.Next()) + { + TDF_Label aComponent = anIt.Value(); + + // Add component + const Standard_Integer aComponentId = addNode(aComponent, theParentId); + if (aComponentId == 0) + continue; + + // Protection against deleted empty labels (after expand compounds, for example). + Handle(TDataStd_TreeNode) aJumpNode; + if (!aComponent.FindAttribute(XCAFDoc::ShapeRefGUID(), aJumpNode)) + continue; + + // Jump to the referred object (the original). + TDF_Label aChildOriginal; + if (!aJumpNode.IsNull() && aJumpNode->HasFather()) + aChildOriginal = aJumpNode->Father()->Label(); // Declaration-level origin. + + if (aChildOriginal.IsNull()) + continue; + + // Add child + const Standard_Integer aChildId = addNode(aChildOriginal, aComponentId); + if (aChildId == 0) + continue; + + // Process children: add components recursively. + addComponents(aChildOriginal, aChildId); + } +} + +// ======================================================================= +// function : addNode +// purpose : Adds node into the graph +// ======================================================================= + +Standard_Integer KI_XCAFDoc_AssemblyGraph::addNode(const TDF_Label& theLabel, + const Standard_Integer theParentId) +{ + NodeType aNodeType = NodeType_UNDEFINED; + if (myShapeTool->IsAssembly(theLabel)) + { + if (myShapeTool->IsFree(theLabel)) + aNodeType = NodeType_AssemblyRoot; + else + aNodeType = NodeType_Subassembly; + } + else if (myShapeTool->IsComponent(theLabel)) + { + aNodeType = NodeType_Occurrence; + } + else if (myShapeTool->IsSubShape(theLabel)) + { + aNodeType = NodeType_Subshape; + } + else if (myShapeTool->IsSimpleShape(theLabel)) + { + aNodeType = NodeType_Part; + } + + if (aNodeType == NodeType_UNDEFINED) + return 0; + + // Get ID of the insertion-level node in the abstract assembly graph. + const Standard_Integer aChildId = myNodes.Add(theLabel); + myNodeTypes.Bind(aChildId, aNodeType); + + if (aNodeType != NodeType_Occurrence) + { + // Bind usage occurrences. + Standard_Integer* aUsageOQPtr = myUsages.ChangeSeek(aChildId); + if (aUsageOQPtr == NULL) + aUsageOQPtr = myUsages.Bound(aChildId, 1); + else + ++(*aUsageOQPtr); + } + + if (theParentId > 0) + { + // Add link + TColStd_PackedMapOfInteger* aMapPtr = myAdjacencyMap.ChangeSeek(theParentId); + if (aMapPtr == NULL) + aMapPtr = myAdjacencyMap.Bound(theParentId, TColStd_PackedMapOfInteger()); + + (*aMapPtr).Add(aChildId); + } + + return aChildId; +} + +// ======================================================================= +// function : Iterator constructor +// purpose : Iteration starts from the specifid node. +// ======================================================================= + +KI_XCAFDoc_AssemblyGraph::Iterator::Iterator(const Handle(KI_XCAFDoc_AssemblyGraph)& theGraph, + const Standard_Integer theNode) +{ + Standard_NullObject_Raise_if(theGraph.IsNull(), "Null assembly graph!"); + Standard_NullObject_Raise_if(theNode < 1, "Node ID must be positive one-based integer!"); + + myGraph = theGraph; + myCurrentIndex = theNode; +} diff --git a/pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.hxx b/pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.hxx new file mode 100644 index 0000000000..e1899c63e1 --- /dev/null +++ b/pcbnew/exporters/step/KI_XCAFDoc_AssemblyGraph.hxx @@ -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 _KI_XCAFDoc_AssemblyGraph_HeaderFile +#define _KI_XCAFDoc_AssemblyGraph_HeaderFile + +#include +#include +#include +#include +#include +#include +#include + +class TDF_Label; +class TDocStd_Document; +class XCAFDoc_ShapeTool; + +class KI_XCAFDoc_AssemblyGraph; +DEFINE_STANDARD_HANDLE(KI_XCAFDoc_AssemblyGraph, Standard_Transient) + +// Assembly graph. +class KI_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 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(KI_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(KI_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 KI_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 KI_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 myNodeTypes; //!< Node types. + NCollection_DataMap myUsages; //!< Occurrences usage. + +}; + +#endif // _KI_XCAFDoc_AssemblyGraph_HeaderFile diff --git a/pcbnew/exporters/step/step_pcb_model.cpp b/pcbnew/exporters/step/step_pcb_model.cpp index 3db877c365..5c9402ab44 100644 --- a/pcbnew/exporters/step/step_pcb_model.cpp +++ b/pcbnew/exporters/step/step_pcb_model.cpp @@ -66,10 +66,11 @@ #include #include #include +#include #include #include #include -#include +#include #include #include #include @@ -77,6 +78,12 @@ #include #include #include +#include +#include +#include +#include + +#include "KI_XCAFDoc_AssemblyGraph.hxx" #include #include @@ -478,6 +485,113 @@ static TopoDS_Shape getOneShape( Handle( XCAFDoc_ShapeTool ) aShapeTool ) } +// Apply scaling to shapes within theLabel. +// Based on XCAFDoc_Editor::RescaleGeometry +static Standard_Boolean rescaleShapes( const TDF_Label& theLabel, const gp_XYZ& aScale ) +{ + if( theLabel.IsNull() ) + { + Message::SendFail( "Null label." ); + return Standard_False; + } + + if( Abs( aScale.X() ) <= gp::Resolution() || Abs( aScale.Y() ) <= gp::Resolution() + || Abs( aScale.Z() ) <= 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; + } + + Handle( KI_XCAFDoc_AssemblyGraph ) aG = new KI_XCAFDoc_AssemblyGraph( theLabel ); + if( aG.IsNull() ) + { + Message::SendFail( "Couldn't create assembly graph." ); + return Standard_False; + } + + Standard_Boolean anIsDone = Standard_True; + + // clang-format off + gp_GTrsf aGTrsf; + aGTrsf.SetVectorialPart( gp_Mat( aScale.X(), 0, 0, + 0, aScale.Y(), 0, + 0, 0, aScale.Z() ) ); + // clang-format on + + BRepBuilderAPI_GTransform aBRepTrsf( aGTrsf ); + + for( Standard_Integer idx = 1; idx <= aG->NbNodes(); idx++ ) + { + const KI_XCAFDoc_AssemblyGraph::NodeType aNodeType = aG->GetNodeType( idx ); + + if( ( aNodeType != KI_XCAFDoc_AssemblyGraph::NodeType_Part ) + && ( aNodeType != KI_XCAFDoc_AssemblyGraph::NodeType_Occurrence ) ) + { + continue; + } + + const TDF_Label& aLabel = aG->GetNode( idx ); + + if( aNodeType == KI_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 ); + } + + // These attributes will be recomputed eventually, but clear them just in case + aLabel.ForgetAttribute( XCAFDoc_Area::GetID() ); + aLabel.ForgetAttribute( XCAFDoc_Centroid::GetID() ); + aLabel.ForgetAttribute( XCAFDoc_Volume::GetID() ); + } + else if( aNodeType == KI_XCAFDoc_AssemblyGraph::NodeType_Occurrence ) + { + TopLoc_Location aLoc = aShapeTool->GetLocation( aLabel ); + gp_Trsf aTrsf = aLoc.Transformation(); + aTrsf.SetTranslationPart( aTrsf.TranslationPart().Multiplied( aScale ) ); + XCAFDoc_Location::Set( aLabel, aTrsf ); + } + } + + if( !anIsDone ) + { + return Standard_False; + } + + aShapeTool->UpdateAssemblies(); + + return anIsDone; +} + + STEP_PCB_MODEL::STEP_PCB_MODEL( const wxString& aPcbName ) { m_app = XCAFApp_Application::GetApplication(); @@ -2510,141 +2624,46 @@ bool STEP_PCB_MODEL::readSTEP( Handle( TDocStd_Document )& doc, const char* fnam } -TDF_Label STEP_PCB_MODEL::transferModel( Handle( TDocStd_Document )& source, - Handle( TDocStd_Document )& dest, VECTOR3D aScale ) +TDF_Label STEP_PCB_MODEL::transferModel( Handle( TDocStd_Document ) & source, + Handle( TDocStd_Document ) & dest, VECTOR3D aScale ) { // transfer data from Source into a top level component of Dest - gp_GTrsf scale_transform; - scale_transform.SetVectorialPart( gp_Mat( aScale.x, 0, 0, - 0, aScale.y, 0, - 0, 0, aScale.z ) ); - BRepBuilderAPI_GTransform brep( scale_transform ); - // s_assy = shape tool for the source - Handle(XCAFDoc_ShapeTool) s_assy = XCAFDoc_DocumentTool::ShapeTool( source->Main() ); + Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( source->Main() ); // retrieve all free shapes within the assembly TDF_LabelSequence frshapes; s_assy->GetFreeShapes( frshapes ); // d_assy = shape tool for the destination - Handle( XCAFDoc_ShapeTool ) d_assy = XCAFDoc_DocumentTool::ShapeTool ( dest->Main() ); + Handle( XCAFDoc_ShapeTool ) d_assy = XCAFDoc_DocumentTool::ShapeTool( dest->Main() ); // create a new shape within the destination and set the assembly tool to point to it - TDF_Label component = d_assy->NewShape(); + TDF_Label d_targetLabel = d_assy->NewShape(); - int nshapes = frshapes.Length(); - int id = 1; - Handle( XCAFDoc_ColorTool ) scolor = XCAFDoc_DocumentTool::ColorTool( source->Main() ); - Handle( XCAFDoc_ColorTool ) dcolor = XCAFDoc_DocumentTool::ColorTool( dest->Main() ); - TopExp_Explorer dtop; - TopExp_Explorer stop; - - while( id <= nshapes ) + if( frshapes.Size() == 1 ) { - const TDF_Label& s_shapeLabel = frshapes.Value( id ); - TopoDS_Shape shape = s_assy->GetShape( s_shapeLabel ); - - if( !shape.IsNull() ) + TDocStd_XLinkTool link; + link.Copy( d_targetLabel, frshapes.First() ); + } + else + { + // Rare case + for( TDF_Label& s_shapeLabel : frshapes ) { - Handle( TDataStd_Name ) s_nameAttr; - s_shapeLabel.FindAttribute( TDataStd_Name::GetID(), s_nameAttr ); + TDF_Label d_component = d_assy->NewShape(); - TCollection_ExtendedString s_labelName = - s_nameAttr ? s_nameAttr->Get() : TCollection_ExtendedString(); + TDocStd_XLinkTool link; + link.Copy( d_component, s_shapeLabel ); - TopoDS_Shape scaled_shape( shape ); - - if( aScale.x != 1.0 || aScale.y != 1.0 || aScale.z != 1.0 ) - { - brep.Perform( shape, Standard_False ); - - if( brep.IsDone() ) - { - scaled_shape = brep.Shape(); - } - else - { - ReportMessage( wxT( " * transfertModel(): failed to scale model\n" ) ); - - scaled_shape = shape; - } - } - - TDF_Label d_shapeLabel = d_assy->AddShape( scaled_shape, Standard_False ); - - if( s_labelName.Length() > 0 ) - TDataStd_Name::Set( d_shapeLabel, s_labelName ); - - TDF_Label niulab = d_assy->AddComponent( component, d_shapeLabel, TopLoc_Location() ); - - // check for per-surface colors - stop.Init( shape, TopAbs_FACE ); - dtop.Init( d_assy->GetShape( niulab ), TopAbs_FACE ); - - while( stop.More() && dtop.More() ) - { - Quantity_Color face_color; - - TDF_Label tl; - - // give priority to the base shape's color - if( s_assy->FindShape( stop.Current(), tl ) ) - { - if( scolor->GetColor( tl, XCAFDoc_ColorSurf, face_color ) - || scolor->GetColor( tl, XCAFDoc_ColorGen, face_color ) - || scolor->GetColor( tl, XCAFDoc_ColorCurv, face_color ) ) - { - dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorSurf ); - } - } - else if( scolor->GetColor( stop.Current(), XCAFDoc_ColorSurf, face_color ) - || scolor->GetColor( stop.Current(), XCAFDoc_ColorGen, face_color ) - || scolor->GetColor( stop.Current(), XCAFDoc_ColorCurv, face_color ) ) - { - dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorSurf ); - } - - stop.Next(); - dtop.Next(); - } - - // check for per-solid colors - stop.Init( shape, TopAbs_SOLID ); - dtop.Init( d_assy->GetShape( niulab ), TopAbs_SOLID, TopAbs_FACE ); - - while( stop.More() && dtop.More() ) - { - Quantity_Color face_color; - - TDF_Label tl; - - // give priority to the base shape's color - if( s_assy->FindShape( stop.Current(), tl ) ) - { - if( scolor->GetColor( tl, XCAFDoc_ColorSurf, face_color ) - || scolor->GetColor( tl, XCAFDoc_ColorGen, face_color ) - || scolor->GetColor( tl, XCAFDoc_ColorCurv, face_color ) ) - { - dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorGen ); - } - } - else if( scolor->GetColor( stop.Current(), XCAFDoc_ColorSurf, face_color ) - || scolor->GetColor( stop.Current(), XCAFDoc_ColorGen, face_color ) - || scolor->GetColor( stop.Current(), XCAFDoc_ColorCurv, face_color ) ) - { - dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorSurf ); - } - - stop.Next(); - dtop.Next(); - } + d_assy->AddComponent( d_targetLabel, d_component, TopLoc_Location() ); } + } - ++id; - }; + if( aScale.x != 1.0 || aScale.y != 1.0 || aScale.z != 1.0 ) + rescaleShapes( d_targetLabel, gp_XYZ( aScale.x, aScale.y, aScale.z ) ); - return component; + return d_targetLabel; }