pcbnew: GRAPHICS_IMPORTER can now recognize multi-path shapes (and postprocess polygons with holes into Kicad-compatible fractured polysets)

This commit is contained in:
Tomasz Wlostowski 2021-12-30 03:47:54 +01:00
parent 78968f75c0
commit 5d87b37f52
4 changed files with 144 additions and 0 deletions

View File

@ -78,3 +78,9 @@ bool GRAPHICS_IMPORTER::Import( double aScale )
return success; return success;
} }
void GRAPHICS_IMPORTER::NewShape( POLY_FILL_RULE aFillRule )
{
m_shapeFillRules.push_back( aFillRule );
}

View File

@ -45,6 +45,12 @@ class EDA_ITEM;
class GRAPHICS_IMPORTER class GRAPHICS_IMPORTER
{ {
public: public:
enum POLY_FILL_RULE
{
PF_NONZERO = 0,
PF_EVEN_ODD
};
GRAPHICS_IMPORTER(); GRAPHICS_IMPORTER();
virtual ~GRAPHICS_IMPORTER() virtual ~GRAPHICS_IMPORTER()
@ -186,6 +192,8 @@ public:
///< Default line thickness (in mm) ///< Default line thickness (in mm)
static constexpr unsigned int DEFAULT_LINE_WIDTH_DFX = 1; static constexpr unsigned int DEFAULT_LINE_WIDTH_DFX = 1;
virtual void NewShape( POLY_FILL_RULE aFillRule = PF_NONZERO );
/** /**
* Create an object representing a line segment. * Create an object representing a line segment.
* *
@ -261,6 +269,8 @@ protected:
///< Offset (in mm) for imported coordinates ///< Offset (in mm) for imported coordinates
VECTOR2D m_offsetCoordmm; VECTOR2D m_offsetCoordmm;
std::vector<POLY_FILL_RULE> m_shapeFillRules;
private: private:
///< List of imported items ///< List of imported items
std::list<std::unique_ptr<EDA_ITEM>> m_items; std::list<std::unique_ptr<EDA_ITEM>> m_items;

View File

@ -86,3 +86,119 @@ void GRAPHICS_IMPORTER_BUFFER::ImportTo( GRAPHICS_IMPORTER& aImporter )
for( std::unique_ptr<IMPORTED_SHAPE>& shape : m_shapes ) for( std::unique_ptr<IMPORTED_SHAPE>& shape : m_shapes )
shape->ImportTo( aImporter ); shape->ImportTo( aImporter );
} }
// converts a single SVG-style polygon (multiple outlines, hole detection based on orientation, custom fill rule) to a format that can be digested by KiCad (single outline, fractured)
static void convertPolygon( std::list<std::unique_ptr<IMPORTED_SHAPE>>& aShapes,
std::vector<IMPORTED_POLYGON*>& aPaths,
GRAPHICS_IMPORTER::POLY_FILL_RULE aFillRule,
int aWidth )
{
double minX = std::numeric_limits<double>::max();
double minY = minX;
double maxX = std::numeric_limits<double>::min();
double maxY = maxX;
// as Clipper/SHAPE_POLY_SET uses ints we first need to upscale to a reasonably large size (in integer coordinates)
// to avoid loosing accuracy
const double convert_scale = 1000000000.0;
for( IMPORTED_POLYGON* path : aPaths )
{
for( VECTOR2D& v : path->Vertices() )
{
minX = std::min( minX, v.x );
minY = std::min( minY, v.y );
maxX = std::max( maxX, v.x );
maxY = std::max( maxY, v.y );
}
}
double origW = ( maxX - minX );
double origH = ( maxY - minY );
double upscaledW, upscaledH;
if( origW > origH )
{
upscaledW = convert_scale;
upscaledH = ( origH == 0.0f ? 0.0 : origH * convert_scale / origW );
}
else
{
upscaledH = convert_scale;
upscaledW = ( origW == 0.0f ? 0.0 : origW * convert_scale / origH );
}
std::vector<SHAPE_LINE_CHAIN> upscaledPaths;
for( IMPORTED_POLYGON* path : aPaths )
{
SHAPE_LINE_CHAIN lc;
for( VECTOR2D& v : path->Vertices() )
{
int xp = KiROUND( ( v.x - minX ) * ( upscaledW / origW ) );
int yp = KiROUND( ( v.y - minY ) * ( upscaledH / origH ) );
lc.Append( xp, yp );
}
lc.SetClosed( true );
upscaledPaths.push_back( lc );
}
SHAPE_POLY_SET result = SHAPE_POLY_SET::BuildPolysetFromOrientedPaths(
upscaledPaths, false, aFillRule == GRAPHICS_IMPORTER::PF_EVEN_ODD );
result.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
for( int outl = 0; outl < result.OutlineCount(); outl++ )
{
const SHAPE_LINE_CHAIN& ro = result.COutline( outl );
std::vector<VECTOR2D> pts;
for( int i = 0; i < ro.PointCount(); i++ )
{
double xp = (double) ro.CPoint( i ).x * ( origW / upscaledW ) + minX;
double yp = (double) ro.CPoint( i ).y * ( origH / upscaledH ) + minY;
pts.emplace_back( VECTOR2D( xp, yp ) );
}
aShapes.push_back( std::make_unique<IMPORTED_POLYGON>( pts, aWidth ) );
}
}
void GRAPHICS_IMPORTER_BUFFER::PostprocessNestedPolygons()
{
int curShapeIdx = 0;
int lastWidth = 1;
std::list<std::unique_ptr<IMPORTED_SHAPE>> newShapes;
std::vector<IMPORTED_POLYGON*> polypaths;
for( auto& shape : m_shapes )
{
IMPORTED_POLYGON* poly = dynamic_cast<IMPORTED_POLYGON*>( shape.get() );
if( !poly || poly->GetParentShapeIndex() < 0 )
{
newShapes.push_back( shape->clone() );
continue;
}
lastWidth = poly->GetWidth();
int index = poly->GetParentShapeIndex();
if( index == curShapeIdx )
{
polypaths.push_back( poly );
}
else if( index == curShapeIdx + 1 )
{
convertPolygon( newShapes, polypaths, m_shapeFillRules[curShapeIdx], lastWidth );
curShapeIdx++;
polypaths.clear();
polypaths.push_back( poly );
}
}
convertPolygon( newShapes, polypaths, m_shapeFillRules[curShapeIdx], lastWidth );
m_shapes.swap( newShapes );
}

View File

@ -41,6 +41,12 @@ public:
virtual void Translate( const VECTOR2D& aVec ) = 0; virtual void Translate( const VECTOR2D& aVec ) = 0;
virtual void Scale( double scaleX, double scaleY ) = 0; virtual void Scale( double scaleX, double scaleY ) = 0;
void SetParentShapeIndex( int aIndex ) { m_parentShapeIndex = aIndex; }
int GetParentShapeIndex() const { return m_parentShapeIndex; }
protected:
int m_parentShapeIndex = -1;
}; };
@ -208,6 +214,10 @@ public:
} }
} }
std::vector<VECTOR2D>& Vertices() { return m_vertices; }
int GetWidth() const { return m_width; }
private: private:
std::vector<VECTOR2D> m_vertices; std::vector<VECTOR2D> m_vertices;
double m_width; double m_width;
@ -344,6 +354,8 @@ public:
return m_shapes; return m_shapes;
} }
void PostprocessNestedPolygons();
protected: protected:
///< List of imported shapes ///< List of imported shapes
std::list< std::unique_ptr< IMPORTED_SHAPE > > m_shapes; std::list< std::unique_ptr< IMPORTED_SHAPE > > m_shapes;