STEP export: Use TDocStd_XLinkTool to copy model with colors instead of our DIY routine.

Also puts components at a higher level in the hierarchy.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17549
This commit is contained in:
Alex Shvartzkop 2024-05-16 01:33:03 +03:00
parent f17b13655b
commit f735c9bdc2
1 changed files with 142 additions and 117 deletions

View File

@ -66,17 +66,24 @@
#include <Standard_Version.hxx>
#include <TCollection_ExtendedString.hxx>
#include <TDocStd_Document.hxx>
#include <TDocStd_XLinkTool.hxx>
#include <TDataStd_Name.hxx>
#include <TDataStd_TreeNode.hxx>
#include <TDF_LabelSequence.hxx>
#include <TDF_ChildIterator.hxx>
#include <TDF_Tool.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <XCAFApp_Application.hxx>
#include <XCAFDoc.hxx>
#include <XCAFDoc_AssemblyTool.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ColorTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <XCAFDoc_Area.hxx>
#include <XCAFDoc_AssemblyGraph.hxx>
#include <XCAFDoc_Centroid.hxx>
#include <XCAFDoc_Location.hxx>
#include <XCAFDoc_Volume.hxx>
#include <BRep_Tool.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
@ -478,6 +485,119 @@ 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( 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;
// clang-format off
gp_GTrsf aTrsf;
aTrsf.SetVectorialPart( gp_Mat( aScale.X(), 0, 0,
0, aScale.Y(), 0,
0, 0, aScale.Z() ) );
// clang-format on
BRepBuilderAPI_GTransform 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 );
}
// 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 == 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 );
}
return Standard_True;
} );
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 +2630,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;
}