const EDA_RECT GERBER_DRAW_ITEM::GetBoundingBox() const
{
    // return a rectangle which is (pos,dim) in nature.  therefore the +1
    EDA_RECT bbox( m_Start, wxSize( 1, 1 ) );

    bbox.Inflate( m_Size.x / 2, m_Size.y / 2 );

    bbox.SetOrigin( GetABPosition( bbox.GetOrigin() ) );
    bbox.SetEnd( GetABPosition( bbox.GetEnd() ) );
    return bbox;
}
bool GERBER_DRAW_ITEM::HitTest( const EDA_RECT& aRefArea ) const
{
    wxPoint pos = GetABPosition( m_Start );

    if( aRefArea.Contains( pos ) )
        return true;

    pos = GetABPosition( m_End );

    if( aRefArea.Contains( pos ) )
        return true;

    return false;
}
const EDA_RECT GERBER_DRAW_ITEM::GetBoundingBox() const
{
    // return a rectangle which is (pos,dim) in nature.  therefore the +1
    EDA_RECT bbox( m_Start, wxSize( 1, 1 ) );

    bbox.Inflate( m_Size.x / 2, m_Size.y / 2 );

    // calculate the corners coordinates in current gerber axis orientations
    wxPoint org = GetABPosition( bbox.GetOrigin() );
    wxPoint end = GetABPosition( bbox.GetEnd() );

    // Set the corners position:
    bbox.SetOrigin( org );
    bbox.SetEnd( end );
    bbox.Normalize();

    return bbox;
}
bool GERBER_DRAW_ITEM::GetTextD_CodePrms( int& aSize, wxPoint& aPos, double& aOrientation )
{
    // calculate the best size and orientation of the D_Code text

    if( m_DCode <= 0 )
        return false;       // No D_Code for this item

    if( m_Flashed || m_Shape == GBR_ARC )
    {
        aPos = m_Start;
    }
    else    // it is a line:
    {
        aPos = ( m_Start + m_End) / 2;
    }

    aPos = GetABPosition( aPos );

    int size;   // the best size for the text

    if( GetDcodeDescr() )
        size = GetDcodeDescr()->GetShapeDim( this );
    else
        size = std::min( m_Size.x, m_Size.y );

    aOrientation = TEXT_ANGLE_HORIZ;

    if( m_Flashed )
    {
        // A reasonable size for text is min_dim/3 because most of time this text has 3 chars.
        aSize = size / 3;
    }
    else        // this item is a line
    {
        wxPoint delta = m_Start - m_End;

        aOrientation = RAD2DECIDEG( atan2( (double)delta.y, (double)delta.x ) );
        NORMALIZE_ANGLE_90( aOrientation );

        // A reasonable size for text is size/2 because text needs margin below and above it.
        // a margin = size/4 seems good, expecting the line len is large enough to show 3 chars,
        // that is the case most of time.
        aSize = size / 2;
    }

    return true;
}
void GERBER_DRAW_ITEM::DrawGbrPoly( EDA_RECT*      aClipBox,
                                    wxDC*          aDC,
                                    EDA_COLOR_T    aColor,
                                    const wxPoint& aOffset,
                                    bool           aFilledShape )
{
    std::vector<wxPoint> points;

    points = m_PolyCorners;
    for( unsigned ii = 0; ii < points.size(); ii++ )
    {
        points[ii] += aOffset;
        points[ii]  = GetABPosition( points[ii] );
    }

    GRClosedPoly( aClipBox, aDC, points.size(), &points[0], aFilledShape, aColor, aColor );
}
void GERBER_DRAW_ITEM::DrawGbrPoly( EDA_RECT*      aClipBox,
                                    wxDC*          aDC,
                                    COLOR4D        aColor,
                                    const wxPoint& aOffset,
                                    bool           aFilledShape )
{
    std::vector<wxPoint> points;
    SHAPE_LINE_CHAIN& poly = m_Polygon.Outline( 0 );
    int pointCount = poly.PointCount() - 1;

    points.reserve( pointCount );

    for( int ii = 0; ii < pointCount; ii++ )
    {
        wxPoint p( poly.Point( ii ).x, poly.Point( ii ).y );
        points[ii] = p + aOffset;
        points[ii] = GetABPosition( points[ii] );
    }

    GRClosedPoly( aClipBox, aDC, pointCount, &points[0], aFilledShape, aColor, aColor );
}
void GERBER_DRAW_ITEM::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode,
                             const wxPoint& aOffset, GBR_DISPLAY_OPTIONS* aDrawOptions )
{
    // used when a D_CODE is not found. default D_CODE to draw a flashed item
    static D_CODE dummyD_CODE( 0 );
    bool          isFilled;
    int           radius;
    int           halfPenWidth;
    static bool   show_err;
    D_CODE*       d_codeDescr = GetDcodeDescr();

    if( d_codeDescr == NULL )
        d_codeDescr = &dummyD_CODE;

    COLOR4D color = m_GerberImageFile->GetPositiveDrawColor();

    if( ( aDrawMode & GR_HIGHLIGHT ) && !( aDrawMode & GR_AND ) )
        color.SetToLegacyHighlightColor();

    /* isDark is true if flash is positive and should use a drawing
     *   color other than the background color, else use the background color
     *   when drawing so that an erasure happens.
     */
    bool isDark = !(m_LayerNegative ^ m_GerberImageFile->m_ImageNegative);

    if( !isDark )
    {
        // draw in background color ("negative" color)
        color = aDrawOptions->m_NegativeDrawColor;
    }

    GRSetDrawMode( aDC, aDrawMode );

    isFilled = aDrawOptions->m_DisplayLinesFill;

    switch( m_Shape )
    {
    case GBR_POLYGON:
        isFilled = aDrawOptions->m_DisplayPolygonsFill;

        if( !isDark )
            isFilled = true;

        DrawGbrPoly( aPanel->GetClipBox(), aDC, color, aOffset, isFilled );
        break;

    case GBR_CIRCLE:
        radius = KiROUND( GetLineLength( m_Start, m_End ) );

        halfPenWidth = m_Size.x >> 1;

        if( !isFilled )
        {
            // draw the border of the pen's path using two circles, each as narrow as possible
            GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                      radius - halfPenWidth, 0, color );
            GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                      radius + halfPenWidth, 0, color );
        }
        else    // Filled mode
        {
            GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                      radius, m_Size.x, color );
        }
        break;

    case GBR_ARC:
        // Currently, arcs plotted with a rectangular aperture are not supported.
        // a round pen only is expected.

#if 0   // for arc debug only
        GRLine( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                GetABPosition( m_ArcCentre ), 0, color );
        GRLine( aPanel->GetClipBox(), aDC, GetABPosition( m_End ),
                GetABPosition( m_ArcCentre ), 0, color );
#endif

        if( !isFilled )
        {
            GRArc1( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                    GetABPosition( m_End ), GetABPosition( m_ArcCentre ),
                    0, color );
        }
        else
        {
            GRArc1( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                    GetABPosition( m_End ), GetABPosition( m_ArcCentre ),
                    m_Size.x, color );
        }

        break;

    case GBR_SPOT_CIRCLE:
    case GBR_SPOT_RECT:
    case GBR_SPOT_OVAL:
    case GBR_SPOT_POLY:
    case GBR_SPOT_MACRO:
        isFilled = aDrawOptions->m_DisplayFlashedItemsFill;
        d_codeDescr->DrawFlashedShape( this, aPanel->GetClipBox(), aDC, color,
                                       m_Start, isFilled );
        break;

    case GBR_SEGMENT:
        /* Plot a line from m_Start to m_End.
         * Usually, a round pen is used, but some gerber files use a rectangular pen
         * In fact, any aperture can be used to plot a line.
         * currently: only a square pen is handled (I believe using a polygon gives a strange plot).
         */
        if( d_codeDescr->m_Shape == APT_RECT )
        {
            if( m_Polygon.OutlineCount() == 0 )
                ConvertSegmentToPolygon();

            DrawGbrPoly( aPanel->GetClipBox(), aDC, color, aOffset, isFilled );
        }
        else
        {
            if( !isFilled )
            {
                    GRCSegm( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                             GetABPosition( m_End ), m_Size.x, color );
            }
            else
            {
                GRFilledSegment( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ),
                                 GetABPosition( m_End ), m_Size.x, color );
            }
        }

        break;

    default:
        if( !show_err )
        {
            wxMessageBox( wxT( "Trace_Segment() type error" ) );
            show_err = true;
        }

        break;
    }
}
const EDA_RECT GERBER_DRAW_ITEM::GetBoundingBox() const
{
    // return a rectangle which is (pos,dim) in nature.  therefore the +1
    EDA_RECT bbox( m_Start, wxSize( 1, 1 ) );
    D_CODE* code = GetDcodeDescr();

    // TODO(JE) GERBER_DRAW_ITEM maybe should actually be a number of subclasses.
    // Until/unless that is changed, we need to do different things depending on
    // what is actually being represented by this GERBER_DRAW_ITEM.

    switch( m_Shape )
    {
    case GBR_POLYGON:
    {
        auto bb = m_Polygon.BBox();
        bbox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
        bbox.SetOrigin( bb.GetOrigin().x, bb.GetOrigin().y );
        break;
    }

    case GBR_CIRCLE:
    {
        double radius = GetLineLength( m_Start, m_End );
        bbox.Inflate( radius, radius );
        break;
    }

    case GBR_ARC:
    {
        // Note: using a larger-than-necessary BB to simplify computation
        double radius = GetLineLength( m_Start, m_ArcCentre );
        bbox.Move( m_ArcCentre - m_Start );
        bbox.Inflate( radius + m_Size.x, radius + m_Size.x );
        break;
    }

    case GBR_SPOT_CIRCLE:
    {
        if( code )
        {
            int radius = code->m_Size.x >> 1;
            bbox.Inflate( radius, radius );
        }
        break;
    }

    case GBR_SPOT_RECT:
    {
        if( code )
            bbox.Inflate( code->m_Size.x / 2, code->m_Size.y / 2 );
        break;
    }

    case GBR_SPOT_OVAL:
    {
        if( code )
            bbox.Inflate( code->m_Size.x, code->m_Size.y );
        break;
    }

    case GBR_SPOT_POLY:
    {
        if( code )
        {
            if( code->m_Polygon.OutlineCount() == 0 )
                code->ConvertShapeToPolygon();

            bbox.Inflate( code->m_Polygon.BBox().GetWidth() / 2,
                          code->m_Polygon.BBox().GetHeight() / 2 );
        }
        break;
    }
    case GBR_SPOT_MACRO:
    {
        if( code )
        {
            // Update the shape drawings and the bounding box coordiantes:
            code->GetMacro()->GetApertureMacroShape( this, m_Start );
            // now the bounding box is valid:
            bbox = code->GetMacro()->GetBoundingBox();
        }
        break;
    }

    case GBR_SEGMENT:
    {
        if( code && code->m_Shape == APT_RECT )
        {
            if( m_Polygon.OutlineCount() > 0 )
            {
                auto bb = m_Polygon.BBox();
                bbox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
                bbox.SetOrigin( bb.GetOrigin().x, bb.GetOrigin().y );
            }
        }
        else
        {
            int radius = ( m_Size.x + 1 ) / 2;

            int ymax = std::max( m_Start.y, m_End.y ) + radius;
            int xmax = std::max( m_Start.x, m_End.x ) + radius;

            int ymin = std::min( m_Start.y, m_End.y ) - radius;
            int xmin = std::min( m_Start.x, m_End.x ) - radius;

            bbox = EDA_RECT( wxPoint( xmin, ymin ), wxSize( xmax - xmin + 1, ymax - ymin + 1 ) );
        }

        break;
    }
    default:
        wxASSERT_MSG( false, wxT( "GERBER_DRAW_ITEM shape is unknown!" ) );
        break;
    }

    // calculate the corners coordinates in current gerber axis orientations
    wxPoint org = GetABPosition( bbox.GetOrigin() );
    wxPoint end = GetABPosition( bbox.GetEnd() );

    // Set the corners position:
    bbox.SetOrigin( org );
    bbox.SetEnd( end );
    bbox.Normalize();

    return bbox;
}