ADDED support for reading/writing arcs in polys

This adds support for reading and writing arc elements in polygons.
This commit is contained in:
Seth Hillbrand 2020-08-14 01:10:40 +01:00 committed by Jon Evans
parent 252bf76452
commit 4f4deece6d
8 changed files with 308 additions and 130 deletions

View File

@ -152,17 +152,7 @@ public:
m_shapes = std::vector<ssize_t>( 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<ssize_t>( 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()
{}

View File

@ -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.
*

View File

@ -48,6 +48,43 @@ SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<int>& 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
{

View File

@ -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++ )

View File

@ -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

View File

@ -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

View File

@ -41,6 +41,8 @@
#include <pcb_group.h>
#include <pcb_target.h>
#include <footprint.h>
#include <geometry/shape_line_chain.h>
#include <netclass.h>
#include <pad.h>
#include <pcb_track.h>
@ -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();

View File

@ -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<wxString, wxString> 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.
*