STEP export: use unfractured polygons.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/15486
This commit is contained in:
parent
e118d2fca0
commit
458d5fbeb8
|
@ -333,7 +333,7 @@ void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin )
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET copper_shape;
|
SHAPE_POLY_SET copper_shape;
|
||||||
zone->TransformSolidAreasShapesToPolygon( layer, 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 );
|
m_pcbModel->AddCopperPolygonShapes( &copper_shape, layer == F_Cu, aOrigin, false );
|
||||||
}
|
}
|
||||||
|
@ -427,9 +427,6 @@ bool EXPORTER_STEP::buildBoard3DShapes()
|
||||||
buildGraphic3DShape( item, origin );
|
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_top_copper_shapes, true, origin, true );
|
||||||
m_pcbModel->AddCopperPolygonShapes( &m_bottom_copper_shapes, false, origin, true );
|
m_pcbModel->AddCopperPolygonShapes( &m_bottom_copper_shapes, false, origin, true );
|
||||||
|
|
||||||
|
|
|
@ -234,10 +234,8 @@ bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin )
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
success = MakeShape( curr_shape, pad_shape.get()->COutline(0), m_copperThickness, Zpos, aOrigin );
|
success = MakeShapes( m_board_copper_pads, *pad_shape, m_copperThickness, Zpos,
|
||||||
|
aOrigin );
|
||||||
if( success )
|
|
||||||
m_board_copper_pads.push_back( curr_shape );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,25 +255,16 @@ bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes,
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
for( int ii = 0; ii < aPolyShapes->OutlineCount(); ii++ )
|
double z_pos = aOnTop ? m_boardThickness : -m_copperThickness;
|
||||||
{
|
|
||||||
TopoDS_Shape copper_shape;
|
|
||||||
double z_pos = aOnTop ? m_boardThickness : -m_copperThickness;
|
|
||||||
|
|
||||||
if( MakeShape( copper_shape, aPolyShapes->COutline( ii ), m_copperThickness, z_pos, aOrigin ) )
|
if( !MakeShapes( aTrack ? m_board_copper_tracks : m_board_copper_zones, *aPolyShapes,
|
||||||
{
|
m_copperThickness, z_pos, aOrigin ) )
|
||||||
if( aTrack )
|
{
|
||||||
m_board_copper_tracks.push_back( copper_shape );
|
ReportMessage( wxString::Format(
|
||||||
else
|
wxT( "Could not add shape (%d points) to copper layer on %s.\n" ),
|
||||||
m_board_copper_zones.push_back( copper_shape );
|
aPolyShapes->FullPointCount(), aOnTop ? wxT( "top" ) : wxT( "bottom" ) ) );
|
||||||
}
|
|
||||||
else
|
success = false;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
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,
|
bool STEP_PCB_MODEL::MakeShapes( std::vector<TopoDS_Shape>& aShapes, const SHAPE_POLY_SET& aPolySet,
|
||||||
double aThickness, double aZposition, const VECTOR2D& aOrigin )
|
double aThickness, double aZposition, const VECTOR2D& aOrigin )
|
||||||
{
|
{
|
||||||
if( !aShape.IsNull() )
|
SHAPE_POLY_SET simplified = aPolySet;
|
||||||
return false; // there is already data in the shape object
|
simplified.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||||
|
|
||||||
if( !aChain.IsClosed() )
|
for( const SHAPE_POLY_SET::POLYGON& polygon : simplified.CPolygons() )
|
||||||
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++ )
|
|
||||||
{
|
{
|
||||||
int next = j+1;
|
auto makeWireFromChain = [&]( BRepLib_MakeWire& aMkWire,
|
||||||
|
const SHAPE_LINE_CHAIN& aChain ) -> bool
|
||||||
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
|
|
||||||
{
|
{
|
||||||
TopoDS_Edge edge;
|
try
|
||||||
edge = BRepBuilderAPI_MakeEdge( start, end );
|
|
||||||
wire.Add( edge );
|
|
||||||
|
|
||||||
if( wire.Error() != BRepBuilderAPI_WireDone )
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
items_count++;
|
return true;
|
||||||
}
|
};
|
||||||
catch( const Standard_Failure& e )
|
|
||||||
|
BRepBuilderAPI_MakeFace mkFace;
|
||||||
|
|
||||||
|
for( int contId = 0; contId < polygon.size(); contId++ )
|
||||||
{
|
{
|
||||||
ReportMessage( wxString::Format( wxT( "MakeShape1: OCC exception: %s\n" ),
|
const SHAPE_LINE_CHAIN& contour = polygon[contId];
|
||||||
e.GetMessageString() ) );
|
BRepLib_MakeWire mkWire;
|
||||||
success = false;
|
|
||||||
|
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" ) );
|
TopoDS_Shape prism = BRepPrimAPI_MakePrism( mkFace, gp_Vec( 0, 0, aThickness ) );
|
||||||
return false;
|
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;
|
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 );
|
const SHAPE_LINE_CHAIN& outline = aOutline.COutline( cnt );
|
||||||
|
|
||||||
ReportMessage( wxString::Format( wxT( "Build board main outline %d with %d points.\n" ),
|
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( !MakeShapes( m_board_outlines, aOutline, m_boardThickness, 0.0, aOrigin ) )
|
||||||
|
|
||||||
if( !MakeShape( curr_brd, outline, m_boardThickness, 0.0, aOrigin ) )
|
|
||||||
{
|
{
|
||||||
// Error
|
// Error
|
||||||
ReportMessage( wxString::Format(
|
ReportMessage( wxString::Format(
|
||||||
wxT( "OCC error adding main outline polygon %d with %d points.\n" ),
|
wxT( "OCC error adding main outline polygon %d with %d points.\n" ), cnt + 1,
|
||||||
cnt+1, outline.PointCount() ) );
|
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -805,48 +812,45 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
|
||||||
// push the board to the data structure
|
// push the board to the data structure
|
||||||
ReportMessage( wxT( "\nGenerate board full shape.\n" ) );
|
ReportMessage( wxT( "\nGenerate board full shape.\n" ) );
|
||||||
|
|
||||||
auto pushToAssembly = [&]( std::vector<TopoDS_Shape>& aShapesList, Quantity_Color aColor, const wxString& aShapeName )
|
auto pushToAssembly = [&]( std::vector<TopoDS_Shape>& aShapesList, Quantity_Color aColor,
|
||||||
{
|
const wxString& aShapeName )
|
||||||
int i = 1;
|
{
|
||||||
for( TopoDS_Shape& shape : aShapesList )
|
int i = 1;
|
||||||
{
|
for( TopoDS_Shape& shape : aShapesList )
|
||||||
Handle( TDataStd_TreeNode ) node;
|
{
|
||||||
|
Handle( TDataStd_TreeNode ) node;
|
||||||
|
|
||||||
// Dont expand the component or else coloring it gets hard
|
// Dont expand the component or else coloring it gets hard
|
||||||
TDF_Label lbl =
|
TDF_Label lbl = m_assy->AddComponent( m_assy_label, shape, false );
|
||||||
m_assy->AddComponent( m_assy_label, shape, false );
|
m_pcb_labels.push_back( lbl );
|
||||||
m_pcb_labels.push_back( lbl );
|
|
||||||
|
|
||||||
if( m_pcb_labels.back().IsNull() )
|
if( m_pcb_labels.back().IsNull() )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
|
lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
|
||||||
TDF_Label shpLbl = node->Father()->Label();
|
TDF_Label shpLbl = node->Father()->Label();
|
||||||
if( !shpLbl.IsNull() )
|
if( !shpLbl.IsNull() )
|
||||||
{
|
{
|
||||||
colorTool->SetColor( shpLbl, aColor, XCAFDoc_ColorSurf );
|
colorTool->SetColor( shpLbl, aColor, XCAFDoc_ColorSurf );
|
||||||
wxString shapeName;
|
wxString shapeName;
|
||||||
|
|
||||||
if( aShapesList.size() > 1 )
|
if( aShapesList.size() > 1 )
|
||||||
{
|
{
|
||||||
shapeName = wxString::Format(
|
shapeName = wxString::Format( wxT( "%s_%s_%d" ), m_pcbName, aShapeName, i );
|
||||||
wxT( "%s_%s_%d" ), m_pcbName, aShapeName, i );
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
shapeName = wxString::Format( wxT( "%s_%s" ), m_pcbName, aShapeName );
|
||||||
shapeName = wxString::Format(
|
}
|
||||||
wxT( "%s_%s" ), m_pcbName, aShapeName );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TCollection_ExtendedString partname(
|
TCollection_ExtendedString partname( shapeName.ToUTF8().data() );
|
||||||
shapeName.ToUTF8().data() );
|
TDataStd_Name::Set( shpLbl, partname );
|
||||||
TDataStd_Name::Set( shpLbl, partname );
|
}
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// AddComponent adds a label that has a reference (not a parent/child relation) to the real
|
// 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
|
// label. We need to extract that real label to name it for the STEP output cleanly
|
||||||
|
|
|
@ -114,15 +114,15 @@ public:
|
||||||
bool CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin );
|
bool CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a SHAPE_LINE_CHAIN (closed) to a TopoDS_Shape (polygonal vertical prism)
|
* Convert a SHAPE_POLY_SET to TopoDS_Shape's (polygonal vertical prisms)
|
||||||
* @param aShape is the TopoDS_Shape to initialize (must be empty)
|
* @param aShapes is the TopoDS_Shape list to append to
|
||||||
* @param aChain is a closed SHAPE_LINE_CHAIN (a polygon)
|
* @param aPolySet is a polygon set
|
||||||
* @param aThickness is the height of the created prism
|
* @param aThickness is the height of the created prism
|
||||||
* @param aOrigin is the origin of the coordinates
|
* @param aOrigin is the origin of the coordinates
|
||||||
* @return true if success
|
* @return true if success
|
||||||
*/
|
*/
|
||||||
bool MakeShape( TopoDS_Shape& aShape, const SHAPE_LINE_CHAIN& aChain, double aThickness,
|
bool MakeShapes( std::vector<TopoDS_Shape>& aShapes, const SHAPE_POLY_SET& aPolySet,
|
||||||
double aZposition, const VECTOR2D& aOrigin );
|
double aThickness, double aZposition, const VECTOR2D& aOrigin );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a SHAPE_LINE_CHAIN containing only one 360 deg arc to a TopoDS_Shape
|
* Convert a SHAPE_LINE_CHAIN containing only one 360 deg arc to a TopoDS_Shape
|
||||||
|
|
Loading…
Reference in New Issue