Plotters: add plot Arcs using EDA_SHAPE or center, start point and end point.

It avoid trying to calculate arc angles (start, end or arc angle) that
frequently create issues due to reverse Y axis, plot mirrored and/or
angle normalization with different criteria.
Fixes #10914
https://gitlab.com/kicad/code/kicad/issues/10914
This commit is contained in:
jean-pierre charras 2022-02-20 14:59:11 +01:00
parent d0749c4e9a
commit d2cf68bcdd
9 changed files with 162 additions and 34 deletions

View File

@ -5,7 +5,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2017-2022 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
@ -1124,9 +1124,9 @@ void GERBER_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, i
} }
} }
void GERBER_PLOTTER::ThickArc( const VECTOR2I& centre, const EDA_ANGLE& aStartAngle, void GERBER_PLOTTER::ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aEndAngle, int aRadius, int aWidth, const EDA_ANGLE& aEndAngle, int aRadius, int aWidth,
OUTLINE_MODE tracemode, void* aData ) OUTLINE_MODE aTraceMode, void* aData )
{ {
GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ); GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
SetCurrentLineWidth( aWidth, gbr_metadata ); SetCurrentLineWidth( aWidth, gbr_metadata );
@ -1134,21 +1134,59 @@ void GERBER_PLOTTER::ThickArc( const VECTOR2I& centre, const EDA_ANGLE& aStartAn
if( gbr_metadata ) if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
if( tracemode == FILLED ) if( aTraceMode == FILLED )
{ {
Arc( centre, aStartAngle, aEndAngle, aRadius, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH ); Arc( aCentre, aStartAngle, aEndAngle, aRadius, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
} }
else else
{ {
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
Arc( centre, aStartAngle, aEndAngle, aRadius - ( aWidth - m_currentPenWidth ) / 2, Arc( aCentre, aStartAngle, aEndAngle, aRadius - ( aWidth - m_currentPenWidth ) / 2,
FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH ); FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
Arc( centre, aStartAngle, aEndAngle, aRadius + ( aWidth - m_currentPenWidth ) / 2, Arc( aCentre, aStartAngle, aEndAngle, aRadius + ( aWidth - m_currentPenWidth ) / 2,
FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH ); FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
} }
} }
void GERBER_PLOTTER::ThickArc( const VECTOR2I& aCentre, const VECTOR2I& aStart,
const VECTOR2I& aEnd, int aWidth,
OUTLINE_MODE aTraceMode, void* aData )
{
EDA_ANGLE start_angle( aStart - aCentre );
EDA_ANGLE end_angle( aEnd - aCentre );
if( start_angle > end_angle )
{
if( end_angle < ANGLE_0 )
end_angle.Normalize();
else
start_angle = start_angle.Normalize() - ANGLE_360;
}
int radius = (aStart - aCentre).EuclideanNorm();
if( !m_yaxisReversed ) // should be always the case
{
std::swap( end_angle, start_angle );
end_angle = -end_angle;
start_angle = -start_angle;
}
ThickArc( aCentre, start_angle, end_angle, radius, aWidth, aTraceMode, aData );
}
void GERBER_PLOTTER::ThickArc( const EDA_SHAPE& aArcShape,
OUTLINE_MODE aTraceMode, void* aData )
{
wxASSERT( aArcShape.GetShape() == SHAPE_T::ARC );
ThickArc( aArcShape.getCenter(), aArcShape.GetStart(), aArcShape.GetEnd(),
aArcShape.GetWidth(), aTraceMode, aData );
}
void GERBER_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void GERBER_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width,
OUTLINE_MODE tracemode, void* aData ) OUTLINE_MODE tracemode, void* aData )
{ {

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -570,8 +570,6 @@ void HPGL_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end,
void HPGL_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, void HPGL_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth ) const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth )
{ {
wxASSERT( m_outputFile );
if( aRadius <= 0 ) if( aRadius <= 0 )
return; return;
@ -611,6 +609,18 @@ void HPGL_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
} }
void HPGL_PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart,
const VECTOR2I& aEnd,
FILL_T aFill, int aWidth, int aMaxError )
{
EDA_ANGLE startAngle( aStart - aCenter );
EDA_ANGLE endAngle( aEnd - aCenter );
int radius = ( aStart - aCenter ).EuclideanNorm();
Arc( aCenter, -endAngle, -startAngle, radius, aFill, aWidth );
}
void HPGL_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize, void HPGL_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData ) const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData )
{ {

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2017-2022 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -159,6 +159,9 @@ void PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR
EDA_ANGLE startAngle( aStart - aCenter ); EDA_ANGLE startAngle( aStart - aCenter );
EDA_ANGLE endAngle( aEnd - aCenter ); EDA_ANGLE endAngle( aEnd - aCenter );
int radius = ( aStart - aCenter ).EuclideanNorm(); int radius = ( aStart - aCenter ).EuclideanNorm();
#if 0
// Approximate arc by segments:
int numSegs = GetArcToSegmentCount( radius, aMaxError, FULL_CIRCLE ); int numSegs = GetArcToSegmentCount( radius, aMaxError, FULL_CIRCLE );
EDA_ANGLE delta = ANGLE_360 / std::max( 8, numSegs ); EDA_ANGLE delta = ANGLE_360 / std::max( 8, numSegs );
VECTOR2I start( aStart ); VECTOR2I start( aStart );
@ -193,6 +196,25 @@ void PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR
LineTo( end ); LineTo( end );
FinishTo( aCenter ); FinishTo( aCenter );
} }
#else
if( startAngle > endAngle )
{
if( endAngle < ANGLE_0 )
endAngle.Normalize();
else
startAngle = startAngle.Normalize() - ANGLE_360;
}
if( m_yaxisReversed )
{
std::swap( startAngle, endAngle );
startAngle = -startAngle;
endAngle = -endAngle;
}
Arc( aCenter, startAngle, endAngle, radius, aFill, aWidth );
#endif
} }
@ -607,6 +629,42 @@ void PLOTTER::ThickArc( const VECTOR2I& centre, const EDA_ANGLE& aStartAngle,
} }
void PLOTTER::ThickArc( const VECTOR2I& aCentre, const VECTOR2I& aStart,
const VECTOR2I& aEnd, int aWidth,
OUTLINE_MODE aTraceMode, void* aData )
{
if( aTraceMode == FILLED )
{
Arc( aCentre, aStart, aEnd, FILL_T::NO_FILL, aWidth, GetPlotterArcHighDef() );
}
else
{
SetCurrentLineWidth( -1 );
int radius = ( aStart - aCentre ).EuclideanNorm();
int new_radius = radius - ( aWidth - m_currentPenWidth ) / 2;
VECTOR2I start = ( aStart - aCentre ).Resize( new_radius ) + aCentre;
VECTOR2I end = ( aEnd - aCentre ).Resize( new_radius ) + aCentre;
Arc( aCentre, start, end, FILL_T::NO_FILL, -1, GetPlotterArcHighDef() );
new_radius = radius + ( aWidth - m_currentPenWidth ) / 2;
start = ( aStart - aCentre ).Resize( new_radius ) + aCentre;
end = ( aEnd - aCentre ).Resize( new_radius ) + aCentre;
Arc( aCentre, start, end, FILL_T::NO_FILL, -1, GetPlotterArcHighDef() );
}
}
void PLOTTER::ThickArc( const EDA_SHAPE& aArcShape,
OUTLINE_MODE aTraceMode, void* aData )
{
ThickArc( aArcShape.getCenter(),aArcShape.GetStart(), aArcShape.GetEnd(),
aArcShape.GetWidth(), aTraceMode, aData );
}
void PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width,
OUTLINE_MODE tracemode, void* aData ) OUTLINE_MODE tracemode, void* aData )
{ {

View File

@ -309,13 +309,24 @@ public:
// Higher level primitives -- can be drawn as line, sketch or 'filled' // Higher level primitives -- can be drawn as line, sketch or 'filled'
virtual void ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width, virtual void ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width,
OUTLINE_MODE tracemode, void* aData ); OUTLINE_MODE tracemode, void* aData );
virtual void ThickArc( const VECTOR2I& centre, const EDA_ANGLE& StAngle,
const EDA_ANGLE& EndAngle, int rayon, int width, virtual void ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& StAngle,
OUTLINE_MODE tracemode, void* aData ); const EDA_ANGLE& EndAngle, int aRadius, int aWidth,
OUTLINE_MODE aTraceMode, void* aData );
virtual void ThickArc( const VECTOR2I& aCentre, const VECTOR2I& aStart,
const VECTOR2I& aEnd, int aWidth,
OUTLINE_MODE aTraceMode, void* aData );
virtual void ThickArc( const EDA_SHAPE& aArcShape,
OUTLINE_MODE aTraceMode, void* aData );
virtual void ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, OUTLINE_MODE tracemode, virtual void ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, OUTLINE_MODE tracemode,
void* aData ); void* aData );
virtual void ThickCircle( const VECTOR2I& pos, int diametre, int width, OUTLINE_MODE tracemode, virtual void ThickCircle( const VECTOR2I& pos, int diametre, int width, OUTLINE_MODE tracemode,
void* aData ); void* aData );
virtual void FilledCircle( const VECTOR2I& pos, int diametre, OUTLINE_MODE tracemode, virtual void FilledCircle( const VECTOR2I& pos, int diametre, OUTLINE_MODE tracemode,
void* aData ); void* aData );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2020 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2020 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
@ -77,13 +77,23 @@ public:
virtual void ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width, virtual void ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width,
OUTLINE_MODE tracemode, void* aData ) override; OUTLINE_MODE tracemode, void* aData ) override;
virtual void ThickArc( const VECTOR2I& centre, const EDA_ANGLE& aStartAngle, virtual void ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aEndAngle, int aRadius, int aWidth, const EDA_ANGLE& aEndAngle, int aRadius, int aWidth,
OUTLINE_MODE tracemode, void* aData ) override; OUTLINE_MODE aTraceMode, void* aData ) override;
virtual void ThickArc( const VECTOR2I& aCentre, const VECTOR2I& aStart,
const VECTOR2I& aEnd, int aWidth,
OUTLINE_MODE aTraceMode, void* aData ) override;
virtual void ThickArc( const EDA_SHAPE& aArcShape,
OUTLINE_MODE aTraceMode, void* aData ) override;
virtual void ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, virtual void ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width,
OUTLINE_MODE tracemode, void* aData ) override; OUTLINE_MODE tracemode, void* aData ) override;
virtual void ThickCircle( const VECTOR2I& pos, int diametre, int width, virtual void ThickCircle( const VECTOR2I& pos, int diametre, int width,
OUTLINE_MODE tracemode, void* aData ) override; OUTLINE_MODE tracemode, void* aData ) override;
virtual void FilledCircle( const VECTOR2I& pos, int diametre, virtual void FilledCircle( const VECTOR2I& pos, int diametre,
OUTLINE_MODE tracemode, void* aData ) override; OUTLINE_MODE tracemode, void* aData ) override;

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
@ -121,7 +121,12 @@ public:
virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill,
int aWidth = USE_DEFAULT_LINE_WIDTH ) override; int aWidth = USE_DEFAULT_LINE_WIDTH ) override;
virtual void Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR2I& aEnd,
FILL_T aFill, int aWidth, int aMaxError ) override;
virtual void PenTo( const VECTOR2I& pos, char plume ) override; virtual void PenTo( const VECTOR2I& pos, char plume ) override;
virtual void FlashPadCircle( const VECTOR2I& aPadPos, int aDiameter, virtual void FlashPadCircle( const VECTOR2I& aPadPos, int aDiameter,
OUTLINE_MODE aTraceMode, void* aData ) override; OUTLINE_MODE aTraceMode, void* aData ) override;
virtual void FlashPadOval( const VECTOR2I& aPadPos, const VECTOR2I& aSize, virtual void FlashPadOval( const VECTOR2I& aPadPos, const VECTOR2I& aSize,

View File

@ -579,13 +579,14 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
if( track->Type() == PCB_ARC_T ) if( track->Type() == PCB_ARC_T )
{ {
const PCB_ARC* arc = static_cast<const PCB_ARC*>( track ); const PCB_ARC* arc = static_cast<const PCB_ARC*>( track );
VECTOR2D center( arc->GetCenter() ); PCB_SHAPE arc_shape( nullptr, SHAPE_T::ARC );
int radius = arc->GetRadius(); arc_shape.SetWidth( width );
EDA_ANGLE start_angle = arc->GetArcAngleStart(); arc_shape.SetStart( arc->GetStart() );
EDA_ANGLE end_angle = start_angle + arc->GetAngle(); arc_shape.SetEnd( arc->GetEnd() );
arc_shape.SetCenter( arc->GetCenter() );
aPlotter->ThickArc( center, -end_angle, -start_angle, radius, width, plotMode, aPlotter->ThickArc( arc->GetCenter(), arc->GetStart(), arc->GetEnd(),
&gbr_metadata ); width, plotMode, &gbr_metadata );
} }
else else
{ {

View File

@ -673,10 +673,9 @@ void BRDITEMS_PLOTTER::PlotFootprintShape( const FP_SHAPE* aShape )
case SHAPE_T::ARC: case SHAPE_T::ARC:
{ {
radius = KiROUND( GetLineLength( aShape->GetCenter(), aShape->GetStart() ) ); radius = KiROUND( GetLineLength( aShape->GetCenter(), aShape->GetStart() ) );
EDA_ANGLE startAngle( aShape->GetStart() - aShape->GetCenter() );
EDA_ANGLE endAngle = startAngle - aShape->GetArcAngle();
// when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg // when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg
// but it is a circle
if( std::abs( aShape->GetArcAngle().AsDegrees() ) == 360.0 ) if( std::abs( aShape->GetArcAngle().AsDegrees() ) == 360.0 )
{ {
m_plotter->ThickCircle( aShape->GetCenter(), radius * 2, thickness, GetPlotMode(), m_plotter->ThickCircle( aShape->GetCenter(), radius * 2, thickness, GetPlotMode(),
@ -684,8 +683,7 @@ void BRDITEMS_PLOTTER::PlotFootprintShape( const FP_SHAPE* aShape )
} }
else else
{ {
m_plotter->ThickArc( aShape->GetCenter(), -startAngle, -endAngle, radius, m_plotter->ThickArc( *aShape, GetPlotMode(), &gbr_metadata );
thickness, GetPlotMode(), &gbr_metadata );
} }
} }
break; break;
@ -945,10 +943,8 @@ void BRDITEMS_PLOTTER::PlotPcbShape( const PCB_SHAPE* aShape )
case SHAPE_T::ARC: case SHAPE_T::ARC:
{ {
EDA_ANGLE startAngle( aShape->GetStart() - aShape->GetCenter() );
EDA_ANGLE endAngle = startAngle - aShape->GetArcAngle();
// when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg // when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg
// but it is a circle
if( std::abs( aShape->GetArcAngle().AsDegrees() ) == 360.0 ) if( std::abs( aShape->GetArcAngle().AsDegrees() ) == 360.0 )
{ {
m_plotter->ThickCircle( aShape->GetCenter(), aShape->GetRadius() * 2, thickness, m_plotter->ThickCircle( aShape->GetCenter(), aShape->GetRadius() * 2, thickness,
@ -956,8 +952,7 @@ void BRDITEMS_PLOTTER::PlotPcbShape( const PCB_SHAPE* aShape )
} }
else else
{ {
m_plotter->ThickArc( aShape->GetCenter(), -startAngle, -endAngle, m_plotter->ThickArc( *aShape, GetPlotMode(), &gbr_metadata );
aShape->GetRadius(), thickness, GetPlotMode(), &gbr_metadata );
} }
break; break;