From f842e6ac7a527055ae2d00add4e4a02548a3ffc8 Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Thu, 24 Aug 2023 21:49:34 +0300 Subject: [PATCH] STEP: export arcs in board contours as curves, not polygons. Fixes https://gitlab.com/kicad/code/kicad/-/issues/13825 --- pcbnew/convert_shape_list_to_polygon.cpp | 38 +++--- pcbnew/exporters/step/step_pcb_model.cpp | 163 ++++++++++++++++------- 2 files changed, 135 insertions(+), 66 deletions(-) diff --git a/pcbnew/convert_shape_list_to_polygon.cpp b/pcbnew/convert_shape_list_to_polygon.cpp index 80bc982b46..10dcc8173a 100644 --- a/pcbnew/convert_shape_list_to_polygon.cpp +++ b/pcbnew/convert_shape_list_to_polygon.cpp @@ -326,40 +326,36 @@ bool ConvertOutlineToPolygon( std::vector& aShapeList, SHAPE_POLY_SE break; case SHAPE_T::ARC: - // We do not support arcs in polygons, so approximate an arc with a series of - // short lines and put those line segments into the !same! PATH. { - VECTOR2I pstart = graphic->GetStart(); - VECTOR2I pend = graphic->GetEnd(); + VECTOR2I pstart = graphic->GetStart(); + VECTOR2I pmid = graphic->GetArcMid(); + VECTOR2I pend = graphic->GetEnd(); VECTOR2I pcenter = graphic->GetCenter(); - EDA_ANGLE angle = -graphic->GetArcAngle(); - int radius = graphic->GetRadius(); - int steps = GetArcToSegmentCount( radius, aErrorMax, angle ); if( !close_enough( prevPt, pstart, aChainingEpsilon ) ) { wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) ); - angle = -angle; std::swap( pstart, pend ); } - // Create intermediate points between start and end: - for( int step = 1; step < steps; ++step ) + SHAPE_ARC sarc( pstart, pmid, pend, 0 ); + + SHAPE_LINE_CHAIN arcChain; + arcChain.Append( sarc, aErrorMax ); + + if( !aAllowUseArcsInPolygons ) + arcChain.ClearArcs(); + + // set shapeOwners for arcChain points created by appending the sarc: + for( int ii = 1; ii < arcChain.PointCount(); ++ii ) { - EDA_ANGLE rotation = ( angle * step ) / steps; - VECTOR2I pt = pstart; - - RotatePoint( pt, pcenter, rotation ); - - currContour.Append( pt ); - shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic; - prevPt = pt; + shapeOwners[std::make_pair( arcChain.CPoint( ii - 1 ), + arcChain.CPoint( ii ) )] = graphic; } - // Append the last arc end point - currContour.Append( pend ); - shapeOwners[ std::make_pair( prevPt, pend ) ] = graphic; + currContour.Append( arcChain ); + prevPt = pend; } break; diff --git a/pcbnew/exporters/step/step_pcb_model.cpp b/pcbnew/exporters/step/step_pcb_model.cpp index 62a0b73587..9e969f82f6 100644 --- a/pcbnew/exporters/step/step_pcb_model.cpp +++ b/pcbnew/exporters/step/step_pcb_model.cpp @@ -620,67 +620,140 @@ bool STEP_PCB_MODEL::MakeShapes( std::vector& aShapes, const SHAPE { try { - // a SHAPE_LINE_CHAIN that is in fact a circle (one 360deg arc) is exported as cylinder - if( IsChainCircle( aChain ) ) + auto addSegment = [&]( const VECTOR2D& aPt0, const VECTOR2D& aPt1 ) -> bool { - const SHAPE_ARC& arc = aChain.Arc( 0 ); - - Handle( Geom_Curve ) curve; - - if( arc.GetCentralAngle() == ANGLE_360 ) - { - gp_Ax2 axis = gp::XOY(); - axis.SetLocation( toPoint( arc.GetCenter() ) ); - - curve = GC_MakeCircle( axis, pcbIUScale.IUTomm( arc.GetRadius() ) ).Value(); - } - else - { - curve = GC_MakeArcOfCircle( toPoint( arc.GetP0() ), - toPoint( arc.GetArcMid() ), - toPoint( arc.GetP1() ) ) - .Value(); - } - - if( !curve.IsNull() ) - { - aMkWire.Add( BRepBuilderAPI_MakeEdge( curve ) ); - - if( aMkWire.Error() != BRepLib_WireDone ) - { - ReportMessage( wxT( "failed to add curve\n" ) ); - return false; - } - - return true; - } - } - - gp_Pnt start = toPoint( aChain.CPoint( 0 ) ); - - for( int j = 0; j < aChain.PointCount(); j++ ) - { - int next = ( j + 1 ) % aChain.PointCount(); - gp_Pnt end = toPoint( aChain.CPoint( next ) ); + gp_Pnt start = toPoint( aPt0 ); + gp_Pnt end = toPoint( aPt1 ); // 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; + return false; BRepBuilderAPI_MakeEdge mkEdge( start, end ); - aMkWire.Add( mkEdge ); - if( aMkWire.Error() != BRepLib_WireDone ) + if( !mkEdge.IsDone() || mkEdge.Edge().IsNull() ) + { + ReportMessage( wxT( "failed to make edge, skipping\n" ) ); + } + else + { + aMkWire.Add( mkEdge.Edge() ); + + if( aMkWire.Error() != BRepLib_WireDone ) + { + ReportMessage( wxT( "failed to add edge to wire\n" ) ); + return false; + } + } + + return true; + }; + + auto addArc = [&]( const SHAPE_ARC& aArc ) -> bool + { + // Do not export too short segments: they create broken shape because OCC thinks + Handle( Geom_Curve ) curve; + + if( aArc.GetCentralAngle() == ANGLE_360 ) + { + gp_Ax2 axis = gp::XOY(); + axis.SetLocation( toPoint( aArc.GetCenter() ) ); + + curve = GC_MakeCircle( axis, pcbIUScale.IUTomm( aArc.GetRadius() ) ) + .Value(); + } + else + { + curve = GC_MakeArcOfCircle( toPoint( aArc.GetP0() ), + toPoint( aArc.GetArcMid() ), + toPoint( aArc.GetP1() ) ) + .Value(); + } + + if( curve.IsNull() ) + return false; + + aMkWire.Add( BRepBuilderAPI_MakeEdge( curve ) ); + + if( !aMkWire.IsDone() ) { ReportMessage( wxT( "failed to add curve\n" ) ); return false; } - start = end; + return true; + }; + + VECTOR2I firstPt; + VECTOR2I lastPt; + bool isFirstShape = true; + + for( int i = 0; i <= aChain.PointCount() && i != -1; i = aChain.NextShape( i ) ) + { + if( i == 0 ) + { + if( aChain.IsArcSegment( 0 ) + && aChain.IsArcSegment( aChain.PointCount() - 1 ) + && aChain.ArcIndex( 0 ) == aChain.ArcIndex( aChain.PointCount() - 1 ) ) + { + std::cout << "Skip looping arc" << std::endl; + + // Skip first arc (we should encounter it later) + int nextShape = aChain.NextShape( i ); + + // If nextShape points to the end, then we have a circle. + if( nextShape != aChain.PointCount() - 1 ) + i = nextShape; + } + } + + if( isFirstShape ) + lastPt = aChain.CPoint( i ); + + bool isArc = aChain.IsArcSegment( i ); + int arcindex = isArc ? aChain.ArcIndex( i ) : -1; + + if( aChain.IsArcStart( i ) ) + { + const SHAPE_ARC& currentArc = aChain.Arc( aChain.ArcIndex( i ) ); + int nextShape = aChain.NextShape( i ); + bool isLastShape = nextShape < 0; + + if( isFirstShape ) + { + firstPt = currentArc.GetP0(); + lastPt = firstPt; + } + + if( lastPt != currentArc.GetP0() ) + addSegment( lastPt, currentArc.GetP0() ); + + addArc( currentArc ); + + lastPt = currentArc.GetP1(); + } + else if( !isArc ) + { + const SEG& seg = aChain.CSegment( i ); + + if( isFirstShape ) + { + firstPt = seg.A; + lastPt = firstPt; + } + + if( addSegment( lastPt, seg.B ) ) + lastPt = seg.B; + } + + isFirstShape = false; } + + if( lastPt != firstPt ) + addSegment( lastPt, firstPt ); } catch( const Standard_Failure& e ) {