STEP export: use unfractured polygons.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/15486
This commit is contained in:
Alex Shvartzkop 2023-08-24 03:47:51 +03:00
parent e118d2fca0
commit 458d5fbeb8
3 changed files with 170 additions and 169 deletions

View File

@ -333,7 +333,7 @@ void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin )
{
SHAPE_POLY_SET copper_shape;
zone->TransformSolidAreasShapesToPolygon( layer, copper_shape );
copper_shape.Fracture( SHAPE_POLY_SET::PM_FAST );
copper_shape.Unfracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
m_pcbModel->AddCopperPolygonShapes( &copper_shape, layer == F_Cu, aOrigin, false );
}
@ -427,9 +427,6 @@ bool EXPORTER_STEP::buildBoard3DShapes()
buildGraphic3DShape( item, origin );
}
m_top_copper_shapes.Fracture( SHAPE_POLY_SET::PM_FAST );
m_bottom_copper_shapes.Fracture( SHAPE_POLY_SET::PM_FAST );
m_pcbModel->AddCopperPolygonShapes( &m_top_copper_shapes, true, origin, true );
m_pcbModel->AddCopperPolygonShapes( &m_bottom_copper_shapes, false, origin, true );

View File

@ -234,10 +234,8 @@ bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin )
}
else
{
success = MakeShape( curr_shape, pad_shape.get()->COutline(0), m_copperThickness, Zpos, aOrigin );
if( success )
m_board_copper_pads.push_back( curr_shape );
success = MakeShapes( m_board_copper_pads, *pad_shape, m_copperThickness, Zpos,
aOrigin );
}
}
@ -257,25 +255,16 @@ bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes,
{
bool success = true;
for( int ii = 0; ii < aPolyShapes->OutlineCount(); ii++ )
{
TopoDS_Shape copper_shape;
double z_pos = aOnTop ? m_boardThickness : -m_copperThickness;
double z_pos = aOnTop ? m_boardThickness : -m_copperThickness;
if( MakeShape( copper_shape, aPolyShapes->COutline( ii ), m_copperThickness, z_pos, aOrigin ) )
{
if( aTrack )
m_board_copper_tracks.push_back( copper_shape );
else
m_board_copper_zones.push_back( copper_shape );
}
else
{
ReportMessage( wxString::Format( wxT( "Could not add shape (%d points) to copper layer on %s.\n" ),
aPolyShapes->COutline( ii ).PointCount(),
aOnTop ? wxT( "top" ) : wxT( "bottom" ) ) );
success = false;
}
if( !MakeShapes( aTrack ? m_board_copper_tracks : m_board_copper_zones, *aPolyShapes,
m_copperThickness, z_pos, aOrigin ) )
{
ReportMessage( wxString::Format(
wxT( "Could not add shape (%d points) to copper layer on %s.\n" ),
aPolyShapes->FullPointCount(), aOnTop ? wxT( "top" ) : wxT( "bottom" ) ) );
success = false;
}
return success;
@ -606,95 +595,136 @@ bool STEP_PCB_MODEL::MakeShapeAsThickSegment( TopoDS_Shape& aShape,
}
bool STEP_PCB_MODEL::MakeShape( TopoDS_Shape& aShape, const SHAPE_LINE_CHAIN& aChain,
double aThickness, double aZposition, const VECTOR2D& aOrigin )
bool STEP_PCB_MODEL::MakeShapes( std::vector<TopoDS_Shape>& aShapes, const SHAPE_POLY_SET& aPolySet,
double aThickness, double aZposition, const VECTOR2D& aOrigin )
{
if( !aShape.IsNull() )
return false; // there is already data in the shape object
SHAPE_POLY_SET simplified = aPolySet;
simplified.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
if( !aChain.IsClosed() )
return false; // the loop is not closed
// a SHAPE_LINE_CHAIN that is in fact a circle (one 360deg arc) is exported as cylinder
if( IsChainCircle( aChain ) )
return MakeShapeAsCylinder( aShape, aChain, aThickness, aZposition, aOrigin );
BRepBuilderAPI_MakeWire wire;
bool success = true;
gp_Pnt start = gp_Pnt( pcbIUScale.IUTomm( aChain.CPoint( 0 ).x - aOrigin.x ),
-pcbIUScale.IUTomm( aChain.CPoint( 0 ).y - aOrigin.y ), aZposition );
int items_count = 0;
for( int j = 0; j < aChain.PointCount(); j++ )
for( const SHAPE_POLY_SET::POLYGON& polygon : simplified.CPolygons() )
{
int next = j+1;
gp_Pnt end;
if( next >= aChain.PointCount() )
next = 0;
end = gp_Pnt( pcbIUScale.IUTomm( aChain.CPoint( next ).x - aOrigin.x ),
-pcbIUScale.IUTomm( aChain.CPoint( next ).y - aOrigin.y ), aZposition );
// Do not export too short segments: they create broken shape because OCC thinks
// start point and end point are at the same place
double seg_len = std::hypot( end.X() - start.X(), end.Y() - start.Y() );
if( seg_len <= m_mergeOCCMaxDist )
continue;
try
auto makeWireFromChain = [&]( BRepLib_MakeWire& aMkWire,
const SHAPE_LINE_CHAIN& aChain ) -> bool
{
TopoDS_Edge edge;
edge = BRepBuilderAPI_MakeEdge( start, end );
wire.Add( edge );
if( wire.Error() != BRepBuilderAPI_WireDone )
try
{
ReportMessage( wxT( "failed to add curve\n" ) );
// a SHAPE_LINE_CHAIN that is in fact a circle (one 360deg arc) is exported as cylinder
if( IsChainCircle( aChain ) )
{
const SHAPE_ARC& arc = aChain.Arc( 0 );
std::vector<VECTOR2D> coords = { arc.GetP0(), arc.GetArcMid(), arc.GetP1() };
std::vector<gp_Pnt> coords3D( coords.size() );
// Convert to 3D points
for( int ii = 0; ii < coords.size(); ii++ )
{
coords3D[ii] = gp_Pnt( pcbIUScale.IUTomm( coords[ii].x - aOrigin.x ),
-pcbIUScale.IUTomm( coords[ii].y - aOrigin.y ),
aZposition );
}
Handle( Geom_TrimmedCurve ) arcOfCircle =
GC_MakeArcOfCircle( coords3D[0], // start point
coords3D[1], // mid point
coords3D[2] // end point
);
aMkWire.Add( BRepBuilderAPI_MakeEdge( arcOfCircle ) );
if( aMkWire.Error() != BRepBuilderAPI_WireDone )
{
ReportMessage( wxT( "failed to add curve\n" ) );
return false;
}
return true;
}
gp_Pnt start = gp_Pnt( pcbIUScale.IUTomm( aChain.CPoint( 0 ).x - aOrigin.x ),
-pcbIUScale.IUTomm( aChain.CPoint( 0 ).y - aOrigin.y ),
aZposition );
for( int j = 0; j < aChain.PointCount(); j++ )
{
int next = ( j + 1 ) % aChain.PointCount();
gp_Pnt end = gp_Pnt( pcbIUScale.IUTomm( aChain.CPoint( next ).x - aOrigin.x ),
-pcbIUScale.IUTomm( aChain.CPoint( next ).y - aOrigin.y ),
aZposition );
// Do not export too short segments: they create broken shape because OCC thinks
// start point and end point are at the same place
double seg_len = std::hypot( end.X() - start.X(), end.Y() - start.Y() );
if( seg_len <= m_mergeOCCMaxDist )
continue;
BRepBuilderAPI_MakeEdge mkEdge( start, end );
aMkWire.Add( mkEdge );
if( aMkWire.Error() != BRepBuilderAPI_WireDone )
{
ReportMessage( wxT( "failed to add curve\n" ) );
return false;
}
start = end;
}
}
catch( const Standard_Failure& e )
{
ReportMessage( wxString::Format( wxT( "makeWireFromChain: OCC exception: %s\n" ),
e.GetMessageString() ) );
return false;
}
items_count++;
}
catch( const Standard_Failure& e )
return true;
};
BRepBuilderAPI_MakeFace mkFace;
for( int contId = 0; contId < polygon.size(); contId++ )
{
ReportMessage( wxString::Format( wxT( "MakeShape1: OCC exception: %s\n" ),
e.GetMessageString() ) );
success = false;
const SHAPE_LINE_CHAIN& contour = polygon[contId];
BRepLib_MakeWire mkWire;
try
{
if( !makeWireFromChain( mkWire, contour ) )
continue;
wxASSERT( mkWire.IsDone() );
if( contId == 0 ) // Outline
mkFace = BRepBuilderAPI_MakeFace( mkWire.Wire() );
else // Hole
mkFace.Add( mkWire );
}
catch( const Standard_Failure& e )
{
ReportMessage(
wxString::Format( wxT( "MakeShapes (contour %d): OCC exception: %s\n" ),
contId, e.GetMessageString() ) );
return false;
}
}
if( !success )
if( mkFace.IsDone() )
{
ReportMessage( wxS( "failed to add edge\n" ) );
return false;
TopoDS_Shape prism = BRepPrimAPI_MakePrism( mkFace, gp_Vec( 0, 0, aThickness ) );
aShapes.push_back( prism );
if( prism.IsNull() )
{
ReportMessage( wxT( "Failed to create a prismatic shape\n" ) );
return false;
}
}
else
{
wxASSERT( false );
}
start = end;
}
BRepBuilderAPI_MakeFace face;
try
{
face = BRepBuilderAPI_MakeFace( wire );
}
catch( const Standard_Failure& e )
{
ReportMessage(
wxString::Format( wxT( "MakeShape2 (items_count %d): OCC exception: %s\n" ),
items_count, e.GetMessageString() ) );
return false;
}
aShape = BRepPrimAPI_MakePrism( face, gp_Vec( 0, 0, aThickness ) );
if( aShape.IsNull() )
{
ReportMessage( wxT( "failed to create a prismatic shape\n" ) );
return false;
}
return true;
@ -721,37 +751,14 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
const SHAPE_LINE_CHAIN& outline = aOutline.COutline( cnt );
ReportMessage( wxString::Format( wxT( "Build board main outline %d with %d points.\n" ),
cnt+1, outline.PointCount() ) );
cnt + 1, outline.PointCount() ) );
TopoDS_Shape curr_brd;
if( !MakeShape( curr_brd, outline, m_boardThickness, 0.0, aOrigin ) )
if( !MakeShapes( m_board_outlines, aOutline, m_boardThickness, 0.0, aOrigin ) )
{
// Error
ReportMessage( wxString::Format(
wxT( "OCC error adding main outline polygon %d with %d points.\n" ),
cnt+1, outline.PointCount() ) );
}
else
m_board_outlines.push_back( curr_brd );
// Generate board cutouts in current main outline:
if( aOutline.HoleCount( cnt ) > 0 )
{
ReportMessage( wxString::Format( wxT( "Add cutouts in outline %d (%d cutout(s)).\n" ),
cnt+1, aOutline.HoleCount( cnt ) ) );
}
for( int ii = 0; ii < aOutline.HoleCount( cnt ); ii++ )
{
const SHAPE_LINE_CHAIN& holeOutline = aOutline.Hole( cnt, ii );
TopoDS_Shape hole;
if( MakeShape( hole, holeOutline, m_boardThickness + (m_copperThickness*4),
-m_copperThickness*2, aOrigin ) )
{
m_cutouts.push_back( hole );
}
wxT( "OCC error adding main outline polygon %d with %d points.\n" ), cnt + 1,
outline.PointCount() ) );
}
}
@ -805,48 +812,45 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
// push the board to the data structure
ReportMessage( wxT( "\nGenerate board full shape.\n" ) );
auto pushToAssembly = [&]( std::vector<TopoDS_Shape>& aShapesList, Quantity_Color aColor, const wxString& aShapeName )
{
int i = 1;
for( TopoDS_Shape& shape : aShapesList )
{
Handle( TDataStd_TreeNode ) node;
auto pushToAssembly = [&]( std::vector<TopoDS_Shape>& aShapesList, Quantity_Color aColor,
const wxString& aShapeName )
{
int i = 1;
for( TopoDS_Shape& shape : aShapesList )
{
Handle( TDataStd_TreeNode ) node;
// Dont expand the component or else coloring it gets hard
TDF_Label lbl =
m_assy->AddComponent( m_assy_label, shape, false );
m_pcb_labels.push_back( lbl );
// Dont expand the component or else coloring it gets hard
TDF_Label lbl = m_assy->AddComponent( m_assy_label, shape, false );
m_pcb_labels.push_back( lbl );
if( m_pcb_labels.back().IsNull() )
break;
if( m_pcb_labels.back().IsNull() )
break;
lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
TDF_Label shpLbl = node->Father()->Label();
if( !shpLbl.IsNull() )
{
colorTool->SetColor( shpLbl, aColor, XCAFDoc_ColorSurf );
wxString shapeName;
lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
TDF_Label shpLbl = node->Father()->Label();
if( !shpLbl.IsNull() )
{
colorTool->SetColor( shpLbl, aColor, XCAFDoc_ColorSurf );
wxString shapeName;
if( aShapesList.size() > 1 )
{
shapeName = wxString::Format(
wxT( "%s_%s_%d" ), m_pcbName, aShapeName, i );
}
else
{
shapeName = wxString::Format(
wxT( "%s_%s" ), m_pcbName, aShapeName );
}
if( aShapesList.size() > 1 )
{
shapeName = wxString::Format( wxT( "%s_%s_%d" ), m_pcbName, aShapeName, i );
}
else
{
shapeName = wxString::Format( wxT( "%s_%s" ), m_pcbName, aShapeName );
}
TCollection_ExtendedString partname(
shapeName.ToUTF8().data() );
TDataStd_Name::Set( shpLbl, partname );
}
TCollection_ExtendedString partname( shapeName.ToUTF8().data() );
TDataStd_Name::Set( shpLbl, partname );
}
i++;
}
};
i++;
}
};
// AddComponent adds a label that has a reference (not a parent/child relation) to the real
// label. We need to extract that real label to name it for the STEP output cleanly

View File

@ -114,15 +114,15 @@ public:
bool CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin );
/**
* Convert a SHAPE_LINE_CHAIN (closed) to a TopoDS_Shape (polygonal vertical prism)
* @param aShape is the TopoDS_Shape to initialize (must be empty)
* @param aChain is a closed SHAPE_LINE_CHAIN (a polygon)
* Convert a SHAPE_POLY_SET to TopoDS_Shape's (polygonal vertical prisms)
* @param aShapes is the TopoDS_Shape list to append to
* @param aPolySet is a polygon set
* @param aThickness is the height of the created prism
* @param aOrigin is the origin of the coordinates
* @return true if success
*/
bool MakeShape( TopoDS_Shape& aShape, const SHAPE_LINE_CHAIN& aChain, double aThickness,
double aZposition, const VECTOR2D& aOrigin );
bool MakeShapes( std::vector<TopoDS_Shape>& aShapes, const SHAPE_POLY_SET& aPolySet,
double aThickness, double aZposition, const VECTOR2D& aOrigin );
/**
* Convert a SHAPE_LINE_CHAIN containing only one 360 deg arc to a TopoDS_Shape