From 4f4deece6d083382388de461cdbe6d08cf2df43d Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Fri, 14 Aug 2020 01:10:40 +0100 Subject: [PATCH] ADDED support for reading/writing arcs in polys This adds support for reading and writing arc elements in polygons. --- .../include/geometry/shape_line_chain.h | 12 +- libs/kimath/include/geometry/shape_poly_set.h | 10 + libs/kimath/src/geometry/shape_line_chain.cpp | 37 +++ libs/kimath/src/geometry/shape_poly_set.cpp | 24 ++ pcbnew/plugins/kicad/kicad_plugin.cpp | 217 ++++++++++-------- pcbnew/plugins/kicad/kicad_plugin.h | 7 +- pcbnew/plugins/kicad/pcb_parser.cpp | 121 ++++++++-- pcbnew/plugins/kicad/pcb_parser.h | 10 + 8 files changed, 308 insertions(+), 130 deletions(-) diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h index b09291c622..dfe171689f 100644 --- a/libs/kimath/include/geometry/shape_line_chain.h +++ b/libs/kimath/include/geometry/shape_line_chain.h @@ -152,17 +152,7 @@ public: m_shapes = std::vector( m_points.size(), 0 ); } - SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) : - SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), - m_closed( true ), - m_width( 0 ) - { - m_points.reserve( aPath.size() ); - m_shapes = std::vector( aPath.size(), ssize_t( SHAPE_IS_PT ) ); - - for( const auto& point : aPath ) - m_points.emplace_back( point.X, point.Y ); - } + SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ); virtual ~SHAPE_LINE_CHAIN() {} diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index 2bb3c970e4..f49261d4d9 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -571,6 +571,16 @@ public: ///< Append a vertex at the end of the given outline/hole (default: the last outline) void Append( const VECTOR2I& aP, int aOutline = -1, int aHole = -1 ); + /** + * Append a new arc to the contour indexed by \p aOutline and \p aHole (defaults to the + * outline of the last polygon). + * @param aArc The arc to be inserted + * @param aOutline Index of the polygon + * @param aHole Index of the hole (-1 for the main outline) + * @return the number of points in the arc (including the interpolated points from the arc) + */ + int Append( SHAPE_ARC& aArc, int aOutline = -1, int aHole = -1 ); + /** * Adds a vertex in the globally indexed position \a aGlobalIndex. * diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp index a8b5c8d266..82bad3c9b1 100644 --- a/libs/kimath/src/geometry/shape_line_chain.cpp +++ b/libs/kimath/src/geometry/shape_line_chain.cpp @@ -48,6 +48,43 @@ SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector& aV ) } } +SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) : + SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), + m_closed( true ), + m_width( 0 ) +{ + int started_mid = 0; + + m_points.reserve( aPath.size() ); + m_shapes.reserve( aPath.size() ); + + for( size_t ii = 0; ii < aPath.size() - started_mid; ++ii ) + { + if( ii == 0 && aPath[ii].type == ClipperLib::pttMid ) + { + started_mid = 1; + VECTOR2I start( aPath.back().X, aPath.back().Y ); + VECTOR2I mid( aPath[0].X, aPath[0].Y ); + VECTOR2I end( aPath[1].X, aPath[1].Y ); + + Append( SHAPE_ARC( start, mid, end, 0 ) ); + ii += 1; + } + else if( ( aPath[ii].type == ClipperLib::pttStart ) + && ii < aPath.size() - 2 ) + { + VECTOR2I start( aPath[ii].X, aPath[ii].Y ); + VECTOR2I mid( aPath[ii + 1].X, aPath[ii + 1].Y ); + VECTOR2I end( aPath[ii + 2].X, aPath[ii + 2].Y ); + + Append( SHAPE_ARC( start, mid, end, 0 ) ); + ii += 2; + } + else + Append( aPath[ii].X, aPath[ii].Y ); + + } +} ClipperLib::Path SHAPE_LINE_CHAIN::convertToClipper( bool aRequiredOrientation ) const { diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index 0ba1398571..9468c5d707 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -235,6 +235,29 @@ int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole, bool aAllowDu } +int SHAPE_POLY_SET::Append( SHAPE_ARC& aArc, int aOutline, int aHole ) +{ + assert( m_polys.size() ); + + if( aOutline < 0 ) + aOutline += m_polys.size(); + + int idx; + + if( aHole < 0 ) + idx = 0; + else + idx = aHole + 1; + + assert( aOutline < (int) m_polys.size() ); + assert( idx < (int) m_polys[aOutline].size() ); + + m_polys[aOutline][idx].Append( aArc ); + + return m_polys[aOutline][idx].PointCount(); +} + + void SHAPE_POLY_SET::InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex ) { VERTEX_INDEX index; @@ -643,6 +666,7 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree ) { POLYGON paths; paths.reserve( n->Childs.size() + 1 ); + paths.push_back( n->Contour ); for( unsigned int i = 0; i < n->Childs.size(); i++ ) diff --git a/pcbnew/plugins/kicad/kicad_plugin.cpp b/pcbnew/plugins/kicad/kicad_plugin.cpp index f3d4a2f339..3438d521da 100644 --- a/pcbnew/plugins/kicad/kicad_plugin.cpp +++ b/pcbnew/plugins/kicad/kicad_plugin.cpp @@ -835,7 +835,7 @@ void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const m_out->Print( aNestLevel, "(gr_poly%s (pts\n", locked.c_str() ); - for( int ii = 0; ii < pointsCount; ++ii ) + for( int ii = 0; ii < outline.PointCount(); ++ii ) { int nestLevel = 0; @@ -846,9 +846,29 @@ void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const m_out->Print( 0, "\n" ); } - m_out->Print( nestLevel, "%s(xy %s)", - nestLevel ? "" : " ", - FormatInternalUnits( outline.CPoint( ii ) ).c_str() ); + int ind = outline.ArcIndex( ii ); + + if( ind < 0 ) + { + m_out->Print( nestLevel, "%s(xy %s)", + nestLevel ? "" : " ", FormatInternalUnits( outline.CPoint( ii ) ).c_str() ); + } + else + { + auto& arc = outline.Arc( ind ); + m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s)", + nestLevel ? "" : " ", + FormatInternalUnits( arc.GetP0() ).c_str(), + FormatInternalUnits( arc.GetArcMid() ).c_str(), + FormatInternalUnits( arc.GetP1() ).c_str() ); + + do + { + ++ii; + } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind ); + + --ii; + } } m_out->Print( 0, ")" ); @@ -937,12 +957,11 @@ void PCB_IO::format( const FP_SHAPE* aFPShape, int aNestLevel ) const { const SHAPE_POLY_SET& poly = aFPShape->GetPolyShape(); const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 ); - int pointsCount = outline.PointCount(); m_out->Print( aNestLevel, "(fp_poly%s (pts", locked.c_str() ); - for( int ii = 0; ii < pointsCount; ++ii ) + for( int ii = 0; ii < outline.PointCount(); ++ii ) { int nestLevel = 0; @@ -953,9 +972,29 @@ void PCB_IO::format( const FP_SHAPE* aFPShape, int aNestLevel ) const m_out->Print( 0, "\n" ); } - m_out->Print( nestLevel, "%s(xy %s)", - nestLevel ? "" : " ", - FormatInternalUnits( outline.CPoint( ii ) ).c_str() ); + int ind = outline.ArcIndex( ii ); + + if( ind < 0 ) + { + m_out->Print( nestLevel, "%s(xy %s)", + nestLevel ? "" : " ", FormatInternalUnits( outline.CPoint( ii ) ).c_str() ); + } + else + { + auto& arc = outline.Arc( ind ); + m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s))", + nestLevel ? "" : " ", + FormatInternalUnits( arc.GetP0() ).c_str(), + FormatInternalUnits( arc.GetArcMid() ).c_str(), + FormatInternalUnits( arc.GetP1() ).c_str() ); + + do + { + ++ii; + } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind ); + + --ii; + } } m_out->Print( 0, ")" ); @@ -2019,63 +2058,53 @@ void PCB_IO::format( const ZONE* aZone, int aNestLevel ) const m_out->Print( 0, ")\n" ); - int newLine = 0; - if( aZone->GetNumCorners() ) { - bool new_polygon = true; - bool is_closed = false; + SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0); - for( auto iterator = aZone->CIterateWithHoles(); iterator; ++iterator ) + for( auto& chain : poly ) { - if( new_polygon ) - { - newLine = 0; - m_out->Print( aNestLevel + 1, "(polygon\n" ); - m_out->Print( aNestLevel + 2, "(pts\n" ); - new_polygon = false; - is_closed = false; - } + m_out->Print( aNestLevel + 1, "(polygon\n" ); + m_out->Print( aNestLevel + 2, "(pts" ); - if( newLine == 0 ) - m_out->Print( aNestLevel + 3, "(xy %s %s)", - FormatInternalUnits( iterator->x ).c_str(), - FormatInternalUnits( iterator->y ).c_str() ); - else - m_out->Print( 0, " (xy %s %s)", - FormatInternalUnits( iterator->x ).c_str(), - FormatInternalUnits( iterator->y ).c_str() ); - - if( newLine < 4 && ADVANCED_CFG::GetCfg().m_CompactSave ) + for( int ii = 0; ii < chain.PointCount(); ++ii ) { - newLine += 1; - } - else - { - newLine = 0; - m_out->Print( 0, "\n" ); - } + int nestLevel = 0; - if( iterator.IsEndContour() ) - { - is_closed = true; - - if( newLine != 0 ) + if( !( ii % 4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) // newline every 4 pts + { + nestLevel = aNestLevel + 3; m_out->Print( 0, "\n" ); + } - m_out->Print( aNestLevel + 2, ")\n" ); - m_out->Print( aNestLevel + 1, ")\n" ); - new_polygon = true; + int ind = chain.ArcIndex( ii ); + + if( ind < 0 ) + { + m_out->Print( nestLevel, "%s(xy %s)", + nestLevel ? "" : " ", FormatInternalUnits( chain.CPoint( ii ) ).c_str() ); + } + else + { + auto& arc = chain.Arc( ind ); + m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s))", + nestLevel ? "" : " ", + FormatInternalUnits( arc.GetP0() ).c_str(), + FormatInternalUnits( arc.GetArcMid() ).c_str(), + FormatInternalUnits( arc.GetP1() ).c_str() ); + + do + { + ++ii; + } while( ii < chain.PointCount() && chain.ArcIndex( ii ) == ind ); + + --ii; + } } - } - - if( !is_closed ) // Should not happen, but... - { - if( newLine != 0 ) - m_out->Print( 0, "\n" ); m_out->Print( aNestLevel + 2, ")\n" ); m_out->Print( aNestLevel + 1, ")\n" ); + } } @@ -2083,65 +2112,57 @@ void PCB_IO::format( const ZONE* aZone, int aNestLevel ) const for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() ) { const SHAPE_POLY_SET& fv = aZone->GetFilledPolysList( layer ); - newLine = 0; - if( !fv.IsEmpty() ) + for( int ii = 0; ii < fv.OutlineCount(); ++ii ) { - int poly_index = 0; - bool new_polygon = true; - bool is_closed = false; + m_out->Print( aNestLevel + 1, "(filled_polygon\n" ); + m_out->Print( aNestLevel + 2, "(layer %s)\n", + m_out->Quotew( LSET::Name( layer ) ).c_str() ); - for( auto it = fv.CIterate(); it; ++it ) + if( aZone->IsIsland( layer, ii ) ) + m_out->Print( aNestLevel + 2, "(island)\n" ); + + m_out->Print( aNestLevel + 2, "(pts" ); + + const SHAPE_LINE_CHAIN& chain = fv.COutline( ii ); + + for( int jj = 0; jj < chain.PointCount(); ++jj ) { - if( new_polygon ) + int nestLevel = 0; + + if( !( jj%4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) // newline every 4 pts { - newLine = 0; - m_out->Print( aNestLevel + 1, "(filled_polygon\n" ); - m_out->Print( aNestLevel + 2, "(layer %s)\n", - m_out->Quotew( LSET::Name( layer ) ).c_str() ); - - if( aZone->IsIsland( layer, poly_index ) ) - m_out->Print( aNestLevel + 2, "(island)\n" ); - - m_out->Print( aNestLevel + 2, "(pts\n" ); - new_polygon = false; - is_closed = false; - poly_index++; - } - - if( newLine == 0 ) - m_out->Print( aNestLevel + 3, "(xy %s %s)", - FormatInternalUnits( it->x ).c_str(), - FormatInternalUnits( it->y ).c_str() ); - else - m_out->Print( 0, " (xy %s %s)", FormatInternalUnits( it->x ).c_str(), - FormatInternalUnits( it->y ).c_str() ); - - if( newLine < 4 && ADVANCED_CFG::GetCfg().m_CompactSave ) - { - newLine += 1; - } - else - { - newLine = 0; + nestLevel = aNestLevel + 3; m_out->Print( 0, "\n" ); } - if( it.IsEndContour() ) + int ind = chain.ArcIndex( jj ); + + if( ind < 0 ) { - is_closed = true; + m_out->Print( nestLevel, "%s(xy %s)", + nestLevel ? "" : " ", FormatInternalUnits( chain.CPoint( jj ) ).c_str() ); + } + else + { + auto& arc = chain.Arc( ind ); + m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s))", + nestLevel ? "" : " ", + FormatInternalUnits( arc.GetP0() ).c_str(), + FormatInternalUnits( arc.GetArcMid() ).c_str(), + FormatInternalUnits( arc.GetP1() ).c_str() ); - if( newLine != 0 ) - m_out->Print( 0, "\n" ); + do + { + ++jj; + } while( jj < chain.PointCount() && chain.ArcIndex( jj ) == ind ); - m_out->Print( aNestLevel + 2, ")\n" ); - m_out->Print( aNestLevel + 1, ")\n" ); - new_polygon = true; + --jj; } } - if( !is_closed ) // Should not happen, but... - m_out->Print( aNestLevel + 1, ")\n" ); + m_out->Print( aNestLevel+2, ")\n" ); + m_out->Print( aNestLevel+1, ")\n" ); } // Save the filling segments list diff --git a/pcbnew/plugins/kicad/kicad_plugin.h b/pcbnew/plugins/kicad/kicad_plugin.h index 330a6b2ac5..a2df9b4e10 100644 --- a/pcbnew/plugins/kicad/kicad_plugin.h +++ b/pcbnew/plugins/kicad/kicad_plugin.h @@ -95,9 +95,10 @@ class PCB_TEXT; //#define SEXPR_BOARD_FILE_VERSION 20201220 // Add free via token //#define SEXPR_BOARD_FILE_VERSION 20210108 // Pad locking moved from footprint to pads //#define SEXPR_BOARD_FILE_VERSION 20210126 // Store pintype alongside pinfunction (in pads). -//#define SEXPR_BOARD_FILE_VERSION 20210228 // Move global margins back to board file -//#define SEXPR_BOARD_FILE_VERSION 20210424 // Correct locked flag syntax (remove parens). -#define SEXPR_BOARD_FILE_VERSION 20210606 // Change overbar syntax from `~...~` to `~{...}`. +//#define SEXPR_BOARD_FILE_VERSION 20210228 // Move global margins back to board file +//#define SEXPR_BOARD_FILE_VERSION 20210424 // Correct locked flag syntax (remove parens). +//#define SEXPR_BOARD_FILE_VERSION 20210606 // Change overbar syntax from `~...~` to `~{...}`. +#define SEXPR_BOARD_FILE_VERSION 20210623 // Add support for reading/writing arcs in polygons #define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp index fa164c89bf..8bdf0dcfba 100644 --- a/pcbnew/plugins/kicad/pcb_parser.cpp +++ b/pcbnew/plugins/kicad/pcb_parser.cpp @@ -41,6 +41,8 @@ #include #include #include +#include + #include #include #include @@ -283,6 +285,89 @@ wxPoint PCB_PARSER::parseXY() } +void PCB_PARSER::parseOutlinePoints( SHAPE_LINE_CHAIN& aPoly ) +{ + if( CurTok() != T_LEFT ) + NeedLEFT(); + + T token = NextTok(); + + switch( token ) + { + case T_xy: + { + int x = parseBoardUnits( "X coordinate" ); + int y = parseBoardUnits( "Y coordinate" ); + + NeedRIGHT(); + + aPoly.Append( x, y ); + break; + } + case T_arc: + { + bool has_start = false; + bool has_mid = false; + bool has_end = false; + + VECTOR2I arc_start, arc_mid, arc_end; + + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) + { + if( token != T_LEFT ) + Expecting( T_LEFT ); + + token = NextTok(); + + switch( token ) + { + case T_start: + arc_start.x = parseBoardUnits( "start x" ); + arc_start.y = parseBoardUnits( "start y" ); + has_start = true; + break; + + case T_mid: + arc_mid.x = parseBoardUnits( "mid x" ); + arc_mid.y = parseBoardUnits( "mid y" ); + has_mid = true; + break; + + case T_end: + arc_end.x = parseBoardUnits( "end x" ); + arc_end.y = parseBoardUnits( "end y" ); + has_end = true; + break; + + default: + Expecting( "start, mid or end" ); + } + + NeedRIGHT(); + } + + if( !has_start ) + Expecting( "start" ); + + if( !has_mid ) + Expecting( "mid" ); + + if( !has_end ) + Expecting( "end" ); + + SHAPE_ARC arc( arc_start, arc_mid, arc_end, 0 ); + + aPoly.Append( arc ); + NeedRIGHT(); + + break; + } + default: + Expecting( "xy or arc" ); + } +} + + void PCB_PARSER::parseXY( int* aX, int* aY ) { wxPoint pt = parseXY(); @@ -2408,6 +2493,10 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE() { shape->SetShape( PCB_SHAPE_TYPE::POLYGON ); shape->SetWidth( 0 ); // this is the default value. will be (perhaps) modified later + shape->SetPolyPoints( {} ); + + SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 ); + token = NextTok(); if( token == T_locked ) @@ -2424,12 +2513,10 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE() if( token != T_pts ) Expecting( T_pts ); - std::vector< wxPoint > pts; - while( (token = NextTok() ) != T_RIGHT ) - pts.push_back( parseXY() ); - - shape->SetPolyPoints( pts ); + { + parseOutlinePoints( outline ); + } } break; @@ -3665,6 +3752,9 @@ FP_SHAPE* PCB_PARSER::parseFP_SHAPE() case T_fp_poly: { shape->SetShape( PCB_SHAPE_TYPE::POLYGON ); + shape->SetPolyPoints( {} ); + SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 ); + token = NextTok(); if( token == T_locked ) @@ -3681,12 +3771,8 @@ FP_SHAPE* PCB_PARSER::parseFP_SHAPE() if( token != T_pts ) Expecting( T_pts ); - std::vector< wxPoint > pts; - while( (token = NextTok() ) != T_RIGHT ) - pts.push_back( parseXY() ); - - shape->SetPolyPoints( pts ); + parseOutlinePoints( outline ); } break; @@ -5076,7 +5162,7 @@ ZONE* PCB_PARSER::parseZONE( BOARD_ITEM_CONTAINER* aParent ) case T_polygon: { - std::vector< wxPoint > corners; + SHAPE_LINE_CHAIN outline; NeedLEFT(); token = NextTok(); @@ -5085,15 +5171,15 @@ ZONE* PCB_PARSER::parseZONE( BOARD_ITEM_CONTAINER* aParent ) Expecting( T_pts ); for( token = NextTok(); token != T_RIGHT; token = NextTok() ) - { - corners.push_back( parseXY() ); - } + parseOutlinePoints( outline ); NeedRIGHT(); + outline.SetClosed( true ); + // Remark: The first polygon is the main outline. // Others are holes inside the main outline. - zone->AddPolygon( corners ); + zone->AddPolygon( outline ); } break; @@ -5138,14 +5224,13 @@ ZONE* PCB_PARSER::parseZONE( BOARD_ITEM_CONTAINER* aParent ) SHAPE_POLY_SET& poly = pts.at( filledLayer ); int idx = poly.NewOutline(); + SHAPE_LINE_CHAIN& chain = poly.Outline( idx ); if( island ) zone->SetIsIsland( filledLayer, idx ); for( token = NextTok(); token != T_RIGHT; token = NextTok() ) - { - poly.Append( parseXY() ); - } + parseOutlinePoints( chain ); NeedRIGHT(); diff --git a/pcbnew/plugins/kicad/pcb_parser.h b/pcbnew/plugins/kicad/pcb_parser.h index 6f8091710c..7344a6a473 100644 --- a/pcbnew/plugins/kicad/pcb_parser.h +++ b/pcbnew/plugins/kicad/pcb_parser.h @@ -59,6 +59,7 @@ class PCB_TARGET; class PCB_VIA; class ZONE; class FP_3DMODEL; +class SHAPE_LINE_CHAIN; struct LAYER; class PROGRESS_REPORTER; @@ -271,6 +272,15 @@ private: std::pair parseProperty(); + /** + * Parses possible outline points and stores them into \p aPoly. This accepts points + * for DRAWSEGMENT polygons, EDGEMODULE polygons and ZONE_CONTAINER polygons. Points + * and arcs are added to the most recent outline + * + * @param aPoly polygon container to add points and arcs + */ + void parseOutlinePoints( SHAPE_LINE_CHAIN& aPoly ); + /** * Parse the common settings for any object derived from #EDA_TEXT. *