pcbnew: GRAPHICS_IMPORTER can now recognize multi-path shapes (and postprocess polygons with holes into Kicad-compatible fractured polysets)
This commit is contained in:
parent
78968f75c0
commit
5d87b37f52
|
@ -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 );
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue