Improve SHAPE_LINE_CHAIN arc plotting.
It still doesn't get it quite right as it restarts the lineStyle pattern at each segment/arc transition.
This commit is contained in:
@@ -851,14 +851,7 @@ void GERBER_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill,
|
||||
int aCornerRadius )
|
||||
{
|
||||
if( aCornerRadius > 0 )
|
||||
{
|
||||
BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
|
||||
box.Normalize();
|
||||
SHAPE_RECT rect( box );
|
||||
rect.SetRadius( aCornerRadius );
|
||||
PlotPoly( rect.Outline(), fill, width, nullptr );
|
||||
return;
|
||||
}
|
||||
wxFAIL_MSG( wxT( "GERBER_PLOTTER must use PlotPolyAsRegion() for rounded-corner rectangles!" ) );
|
||||
|
||||
std::vector<VECTOR2I> cornerList;
|
||||
|
||||
|
||||
@@ -386,18 +386,10 @@ void PDF_PLOTTER::Circle( const VECTOR2I& pos, int diametre, FILL_T aFill, int w
|
||||
}
|
||||
|
||||
|
||||
void PDF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
std::string PDF_PLOTTER::arcPath( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aAngle, double aRadius )
|
||||
{
|
||||
wxASSERT( m_workFile );
|
||||
|
||||
SetCurrentLineWidth( aWidth );
|
||||
|
||||
if( aRadius <= 0 )
|
||||
{
|
||||
Circle( aCenter, GetCurrentLineWidth(), FILL_T::FILLED_SHAPE, 0 );
|
||||
return;
|
||||
}
|
||||
std::string path;
|
||||
|
||||
/*
|
||||
* Arcs are not so easily approximated by beziers (in the general case), so we approximate
|
||||
@@ -416,20 +408,39 @@ void PDF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
start.x = KiROUND( aCenter.x + aRadius * ( -startAngle ).Cos() );
|
||||
start.y = KiROUND( aCenter.y + aRadius * ( -startAngle ).Sin() );
|
||||
VECTOR2D pos_dev = userToDeviceCoordinates( start );
|
||||
fmt::print( m_workFile, "{:g} {:g} m ", pos_dev.x, pos_dev.y );
|
||||
path += fmt::format( "{:g} {:g} m ", pos_dev.x, pos_dev.y );
|
||||
|
||||
for( EDA_ANGLE ii = startAngle + delta; ii < endAngle; ii += delta )
|
||||
{
|
||||
end.x = KiROUND( aCenter.x + aRadius * ( -ii ).Cos() );
|
||||
end.y = KiROUND( aCenter.y + aRadius * ( -ii ).Sin() );
|
||||
pos_dev = userToDeviceCoordinates( end );
|
||||
fmt::print( m_workFile, "{:g} {:g} l ", pos_dev.x, pos_dev.y );
|
||||
path += fmt::format( "{:g} {:g} l ", pos_dev.x, pos_dev.y );
|
||||
}
|
||||
|
||||
end.x = KiROUND( aCenter.x + aRadius * ( -endAngle ).Cos() );
|
||||
end.y = KiROUND( aCenter.y + aRadius * ( -endAngle ).Sin() );
|
||||
pos_dev = userToDeviceCoordinates( end );
|
||||
fmt::print( m_workFile, "{:g} {:g} l ", pos_dev.x, pos_dev.y );
|
||||
path += fmt::format( "{:g} {:g} l ", pos_dev.x, pos_dev.y );
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
void PDF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
{
|
||||
wxASSERT( m_workFile );
|
||||
|
||||
SetCurrentLineWidth( aWidth );
|
||||
|
||||
if( aRadius <= 0 )
|
||||
{
|
||||
Circle( aCenter, GetCurrentLineWidth(), FILL_T::FILLED_SHAPE, 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
fmt::print( m_workFile, "{}", arcPath( aCenter, aStartAngle, aAngle, aRadius ) );
|
||||
|
||||
// The arc is drawn... if not filled we stroke it, otherwise we finish
|
||||
// closing the pie at the center
|
||||
@@ -439,7 +450,7 @@ void PDF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
}
|
||||
else
|
||||
{
|
||||
pos_dev = userToDeviceCoordinates( aCenter );
|
||||
VECTOR2D pos_dev = userToDeviceCoordinates( aCenter );
|
||||
fmt::println( m_workFile, "{:g} {:g} l b", pos_dev.x, pos_dev.y );
|
||||
}
|
||||
}
|
||||
@@ -480,16 +491,43 @@ void PDF_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFi
|
||||
void PDF_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill, int aWidth,
|
||||
void* aData )
|
||||
{
|
||||
std::vector<VECTOR2I> cornerList;
|
||||
cornerList.reserve( aCornerList.PointCount() );
|
||||
SetCurrentLineWidth( aWidth );
|
||||
|
||||
for( int ii = 0; ii < aCornerList.PointCount(); ii++ )
|
||||
cornerList.emplace_back( aCornerList.CPoint( ii ) );
|
||||
std::set<size_t> handledArcs;
|
||||
|
||||
if( aCornerList.IsClosed() && cornerList.front() != cornerList.back() )
|
||||
cornerList.emplace_back( aCornerList.CPoint( 0 ) );
|
||||
for( int ii = 0; ii < aCornerList.SegmentCount(); ++ii )
|
||||
{
|
||||
if( aCornerList.IsArcSegment( ii ) )
|
||||
{
|
||||
size_t arcIndex = aCornerList.ArcIndex( ii );
|
||||
|
||||
PlotPoly( cornerList, aFill, aWidth, aData );
|
||||
if( !handledArcs.contains( arcIndex ) )
|
||||
{
|
||||
handledArcs.insert( arcIndex );
|
||||
const SHAPE_ARC& arc( aCornerList.Arc( arcIndex ) );
|
||||
fmt::print( m_workFile, "{}", arcPath( arc.GetCenter(), arc.GetStartAngle(),
|
||||
arc.GetCentralAngle(), arc.GetRadius() ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const SEG& seg( aCornerList.Segment( ii ) );
|
||||
|
||||
VECTOR2D pos = userToDeviceCoordinates( seg.A );
|
||||
fmt::println( m_workFile, "{:f} {:f} m", pos.x, pos.y );
|
||||
|
||||
pos = userToDeviceCoordinates( seg.B );
|
||||
fmt::println( m_workFile, "{:f} {:f} l", pos.x, pos.y );
|
||||
}
|
||||
}
|
||||
|
||||
// Close path and stroke and/or fill
|
||||
if( aFill == FILL_T::NO_FILL )
|
||||
fmt::println( m_workFile, "S" );
|
||||
else if( aWidth == 0 )
|
||||
fmt::println( m_workFile, "f" );
|
||||
else
|
||||
fmt::println( m_workFile, "b" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -60,9 +60,13 @@ public:
|
||||
double aScale, bool aMirror ) override;
|
||||
|
||||
// Basic plot primitives
|
||||
|
||||
// Note: do not use Rect() to plot rounded-corner rectangles. Use PlotPolyAsRegion() instead.
|
||||
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
|
||||
int aCornerRadius = 0 ) override;
|
||||
|
||||
virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width ) override;
|
||||
|
||||
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth ) override;
|
||||
|
||||
|
||||
@@ -418,6 +418,9 @@ private:
|
||||
void drawOverbars( const std::vector<OverbarInfo>& aOverbars,
|
||||
const EDA_ANGLE& aOrient, const KIFONT::METRICS& aFontMetrics );
|
||||
|
||||
std::string arcPath( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aAngle, double aRadius );
|
||||
|
||||
public:
|
||||
virtual void PlotText( const VECTOR2I& aPos,
|
||||
const COLOR4D& aColor,
|
||||
|
||||
@@ -1099,18 +1099,18 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
|
||||
BOX2I box( aShape->GetStart(), VECTOR2I( aShape->GetEnd().x - aShape->GetStart().x,
|
||||
aShape->GetEnd().y - aShape->GetStart().y ) );
|
||||
box.Normalize();
|
||||
|
||||
if( margin < 0 )
|
||||
{
|
||||
box.Inflate( margin );
|
||||
radius += margin;
|
||||
}
|
||||
|
||||
SHAPE_RECT rect( box );
|
||||
rect.SetRadius( radius );
|
||||
|
||||
SHAPE_LINE_CHAIN outline = rect.Outline();
|
||||
SHAPE_POLY_SET poly;
|
||||
poly.NewOutline();
|
||||
|
||||
for( int ii = 0; ii < outline.PointCount(); ++ii )
|
||||
poly.Append( outline.CPoint( ii ) );
|
||||
|
||||
if( margin < 0 )
|
||||
poly.Inflate( margin / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, aShape->GetMaxError() );
|
||||
SHAPE_POLY_SET poly( outline );
|
||||
|
||||
FILL_T fill_mode = isSolidFill ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
|
||||
|
||||
@@ -1119,11 +1119,11 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
|
||||
if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
|
||||
{
|
||||
GERBER_PLOTTER* gbr_plotter = static_cast<GERBER_PLOTTER*>( m_plotter );
|
||||
gbr_plotter->PlotPolyAsRegion( poly.COutline( 0 ), fill_mode, thickness,
|
||||
&gbr_metadata );
|
||||
gbr_plotter->PlotPolyAsRegion( poly.COutline( 0 ), fill_mode, thickness, &gbr_metadata );
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: PlotPoly needs to handle arcs...
|
||||
m_plotter->PlotPoly( poly.COutline( 0 ), fill_mode, thickness, getMetadata() );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user