예제 #1
0
/**
 * Function idf_export_outline
 * retrieves line segment information from the edge layer and compiles
 * the data into a form which can be output as an IDFv3 compliant
 * BOARD_OUTLINE section.
 */
static void idf_export_outline( BOARD* aPcb, IDF3_BOARD& aIDFBoard )
{
    double scale = aIDFBoard.GetUserScale();

    DRAWSEGMENT* graphic;               // KiCad graphical item
    IDF_POINT sp, ep;                   // start and end points from KiCad item

    std::list< IDF_SEGMENT* > lines;    // IDF intermediate form of KiCad graphical item
    IDF_OUTLINE* outline = NULL;        // graphical items forming an outline or cutout

    // NOTE: IMPLEMENTATION
    // If/when component cutouts are allowed, we must implement them separately. Cutouts
    // must be added to the board outline section and not to the Other Outline section.
    // The module cutouts should be handled via the idf_export_module() routine.

    double offX, offY;
    aIDFBoard.GetUserOffset( offX, offY );

    // Retrieve segments and arcs from the board
    for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() )
    {
        if( item->Type() != PCB_LINE_T || item->GetLayer() != Edge_Cuts )
            continue;

        graphic = (DRAWSEGMENT*) item;

        switch( graphic->GetShape() )
        {
        case S_SEGMENT:
            {
                if( ( graphic->GetStart().x == graphic->GetEnd().x )
                    && ( graphic->GetStart().y == graphic->GetEnd().y ) )
                    break;

                sp.x    = graphic->GetStart().x * scale + offX;
                sp.y    = -graphic->GetStart().y * scale + offY;
                ep.x    = graphic->GetEnd().x * scale + offX;
                ep.y    = -graphic->GetEnd().y * scale + offY;
                IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep );

                if( seg )
                    lines.push_back( seg );
            }
            break;

        case S_ARC:
            {
                if( ( graphic->GetCenter().x == graphic->GetArcStart().x )
                    && ( graphic->GetCenter().y == graphic->GetArcStart().y ) )
                    break;

                sp.x = graphic->GetCenter().x * scale + offX;
                sp.y = -graphic->GetCenter().y * scale + offY;
                ep.x = graphic->GetArcStart().x * scale + offX;
                ep.y = -graphic->GetArcStart().y * scale + offY;
                IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, -graphic->GetAngle() / 10.0, true );

                if( seg )
                    lines.push_back( seg );
            }
            break;

        case S_CIRCLE:
            {
                if( graphic->GetRadius() == 0 )
                    break;

                sp.x = graphic->GetCenter().x * scale + offX;
                sp.y = -graphic->GetCenter().y * scale + offY;
                ep.x = sp.x - graphic->GetRadius() * scale;
                ep.y = sp.y;
                // Circles must always have an angle of +360 deg. to appease
                // quirky MCAD implementations of IDF.
                IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, 360.0, true );

                if( seg )
                    lines.push_back( seg );
            }
            break;

        default:
            break;
        }
    }

    // if there is no outline then use the bounding box
    if( lines.empty() )
    {
        goto UseBoundingBox;
    }

    // get the board outline and write it out
    // note: we do not use a try/catch block here since we intend
    // to simply ignore unclosed loops and continue processing
    // until we're out of segments to process
    outline = new IDF_OUTLINE;
    IDF3::GetOutline( lines, *outline );

    if( outline->empty() )
        goto UseBoundingBox;

    aIDFBoard.AddBoardOutline( outline );
    outline = NULL;

    // get all cutouts and write them out
    while( !lines.empty() )
    {
        if( !outline )
            outline = new IDF_OUTLINE;

        IDF3::GetOutline( lines, *outline );

        if( outline->empty() )
        {
            outline->Clear();
            continue;
        }

        aIDFBoard.AddBoardOutline( outline );
        outline = NULL;
    }

    return;

UseBoundingBox:

    // clean up if necessary
    while( !lines.empty() )
    {
        delete lines.front();
        lines.pop_front();
    }

    if( outline )
        outline->Clear();
    else
        outline = new IDF_OUTLINE;

    // fetch a rectangular bounding box for the board;
    // there is always some uncertainty in the board dimensions
    // computed via ComputeBoundingBox() since this depends on the
    // individual module entities.
    EDA_RECT bbbox = aPcb->ComputeBoundingBox( true );

    // convert to mm and compensate for an assumed LINE_WIDTH line thickness
    double  x   = ( bbbox.GetOrigin().x + LINE_WIDTH / 2 ) * scale + offX;
    double  y   = ( bbbox.GetOrigin().y + LINE_WIDTH / 2 ) * scale + offY;
    double  dx  = ( bbbox.GetSize().x - LINE_WIDTH ) * scale;
    double  dy  = ( bbbox.GetSize().y - LINE_WIDTH ) * scale;

    double px[4], py[4];
    px[0]   = x;
    py[0]   = y;

    px[1]   = x;
    py[1]   = y + dy;

    px[2]   = x + dx;
    py[2]   = y + dy;

    px[3]   = x + dx;
    py[3]   = y;

    IDF_POINT p1, p2;

    p1.x    = px[3];
    p1.y    = py[3];
    p2.x    = px[0];
    p2.y    = py[0];

    outline->push( new IDF_SEGMENT( p1, p2 ) );

    for( int i = 1; i < 4; ++i )
    {
        p1.x    = px[i - 1];
        p1.y    = py[i - 1];
        p2.x    = px[i];
        p2.y    = py[i];

        outline->push( new IDF_SEGMENT( p1, p2 ) );
    }

    aIDFBoard.AddBoardOutline( outline );
}
void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos )
{
    VECTOR2I origin;

    switch( aItem->Type() )
    {
        case PCB_MODULE_T:
        {
            MODULE* mod = static_cast<MODULE*>( aItem );
            addAnchor( mod->GetPosition(), ORIGIN | SNAPPABLE, mod );

            for( D_PAD* pad = mod->Pads(); pad; pad = pad->Next() )
                addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad );

            break;
        }


        case PCB_PAD_T:
        {
            D_PAD* pad = static_cast<D_PAD*>( aItem );
            addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad );

            break;
        }

        case PCB_MODULE_EDGE_T:
        case PCB_LINE_T:
        {
            DRAWSEGMENT* dseg = static_cast<DRAWSEGMENT*>( aItem );
            VECTOR2I start = dseg->GetStart();
            VECTOR2I end = dseg->GetEnd();
            //LAYER_ID layer = dseg->GetLayer();

            switch( dseg->GetShape() )
            {
                case S_CIRCLE:
                {
                    int r = ( start - end ).EuclideanNorm();

                    addAnchor( start, ORIGIN | SNAPPABLE, dseg );
                    addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, dseg );
                    addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, dseg );
                    addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, dseg );
                    addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, dseg );
                    break;
                }

                case S_ARC:
                {
                    origin = dseg->GetCenter();
                    addAnchor( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg );
                    addAnchor( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg );
                    addAnchor( origin, ORIGIN | SNAPPABLE, dseg );
                    break;
                }

                case S_SEGMENT:
                {
                    origin.x = start.x + ( start.x - end.x ) / 2;
                    origin.y = start.y + ( start.y - end.y ) / 2;
                    addAnchor( start, CORNER | SNAPPABLE, dseg );
                    addAnchor( end, CORNER | SNAPPABLE, dseg );
                    addAnchor( origin, ORIGIN, dseg );
                    break;
                }

                default:
                {
                    origin = dseg->GetStart();
                    addAnchor( origin, ORIGIN | SNAPPABLE, dseg );
                    break;
                }
            }
            break;
        }

        case PCB_TRACE_T:
        {
            TRACK* track = static_cast<TRACK*>( aItem );
            VECTOR2I start = track->GetStart();
            VECTOR2I end = track->GetEnd();
            origin.x = start.x + ( start.x - end.x ) / 2;
            origin.y = start.y + ( start.y - end.y ) / 2;
            addAnchor( start, CORNER | SNAPPABLE, track );
            addAnchor( end, CORNER | SNAPPABLE, track );
            addAnchor( origin, ORIGIN, track);
            break;
        }

        case PCB_VIA_T:
            addAnchor( aItem->GetPosition(), CORNER | SNAPPABLE, aItem );
            break;

        case PCB_ZONE_AREA_T:
        {
            const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
            int cornersCount = outline->GetCornersCount();

            SHAPE_LINE_CHAIN lc;
            lc.SetClosed( true );

            for( int i = 0; i < cornersCount; ++i )
            {
                const VECTOR2I p ( outline->GetPos( i ) );
                addAnchor( p, CORNER, aItem );
                lc.Append( p );
            }

            addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );

            break;
        }

        case PCB_MODULE_TEXT_T:
        case PCB_TEXT_T:
            addAnchor( aItem->GetPosition(), ORIGIN, aItem );
        default:

        break;
   }
}
예제 #3
0
void POINT_EDITOR::updateItem() const
{
    EDA_ITEM* item = m_editPoints->GetParent();

    switch( item->Type() )
    {
    case PCB_LINE_T:
    case PCB_MODULE_EDGE_T:
    {
        DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
        switch( segment->GetShape() )
        {
        case S_SEGMENT:
            if( isModified( m_editPoints->Point( SEG_START ) ) )
                segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
                                            m_editPoints->Point( SEG_START ).GetPosition().y ) );

            else if( isModified( m_editPoints->Point( SEG_END ) ) )
                segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
                                          m_editPoints->Point( SEG_END ).GetPosition().y ) );

            break;

        case S_ARC:
        {
            const VECTOR2I& center = m_editPoints->Point( ARC_CENTER ).GetPosition();
            const VECTOR2I& start = m_editPoints->Point( ARC_START ).GetPosition();
            const VECTOR2I& end = m_editPoints->Point( ARC_END ).GetPosition();

            if( center != segment->GetCenter() )
            {
                wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
                segment->Move( moveVector );

                m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
                m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
            }

            else
            {
                segment->SetArcStart( wxPoint( start.x, start.y ) );

                VECTOR2D startLine = start - center;
                VECTOR2I endLine = end - center;
                double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );

                // Adjust the new angle to (counter)clockwise setting
                bool clockwise = ( segment->GetAngle() > 0 );

                if( clockwise && newAngle < 0.0 )
                    newAngle += 3600.0;
                else if( !clockwise && newAngle > 0.0 )
                    newAngle -= 3600.0;

                segment->SetAngle( newAngle );
            }

            break;
        }

        case S_CIRCLE:
        {
            const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
            const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();

            if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
            {
                wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
                segment->Move( moveVector );
            }
            else
            {
                segment->SetEnd( wxPoint( end.x, end.y ) );
            }

            break;
        }

        default:        // suppress warnings
            break;
        }

        // Update relative coordinates for module edges
        if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) )
            edge->SetLocalCoord();

        break;
    }

    case PCB_ZONE_AREA_T:
    {
        ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
        zone->ClearFilledPolysList();
        CPolyLine* outline = zone->Outline();

        for( int i = 0; i < outline->GetCornersCount(); ++i )
        {
            VECTOR2I point = m_editPoints->Point( i ).GetPosition();
            outline->SetX( i, point.x );
            outline->SetY( i, point.y );
        }

        break;
    }

    case PCB_DIMENSION_T:
    {
        DIMENSION* dimension = static_cast<DIMENSION*>( item );

        // Check which point is currently modified and updated dimension's points respectively
        if( isModified( m_editPoints->Point( DIM_CROSSBARO ) ) )
        {
            VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetOrigin() );
            VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );

            if( featureLine.Cross( crossBar ) > 0 )
                dimension->SetHeight( -featureLine.EuclideanNorm() );
            else
                dimension->SetHeight( featureLine.EuclideanNorm() );
        }

        else if( isModified( m_editPoints->Point( DIM_CROSSBARF ) ) )
        {
            VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
            VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );

            if( featureLine.Cross( crossBar ) > 0 )
                dimension->SetHeight( -featureLine.EuclideanNorm() );
            else
                dimension->SetHeight( featureLine.EuclideanNorm() );
        }

        else if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
        {
            dimension->SetOrigin( wxPoint( m_editedPoint->GetPosition().x, m_editedPoint->GetPosition().y ) );
            m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
                                                                             m_editPoints->Point( DIM_FEATUREGO ) ) );
            m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
                                                                             m_editPoints->Point( DIM_FEATUREDO ) ) );
        }

        else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
        {
            dimension->SetEnd( wxPoint( m_editedPoint->GetPosition().x, m_editedPoint->GetPosition().y ) );
            m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
                                                                             m_editPoints->Point( DIM_FEATUREGO ) ) );
            m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
                                                                             m_editPoints->Point( DIM_FEATUREDO ) ) );
        }

        break;
    }

    default:
        break;
    }
}
/**
 * Function ConvertOutlineToPolygon
 * build a polygon (with holes) from a DRAWSEGMENT list, which is expected to be
 * a outline, therefore a closed main outline with perhaps closed inner outlines.
 * These closed inner outlines are considered as holes in the main outline
 * @param aSegList the initial list of drawsegments (only lines, circles and arcs).
 * @param aPolygons will contain the complex polygon.
 * @param aTolerance is the max distance between points that is still accepted as connected (internal units)
 * @param aErrorText is a wxString to return error message.
 * @param aErrorLocation is the optional position of the error in the outline
 */
bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SET& aPolygons,
                              wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation )
{
    if( aSegList.size() == 0 )
        return true;

    wxString msg;

    // Make a working copy of aSegList, because the list is modified during calculations
    std::vector< DRAWSEGMENT* > segList = aSegList;

    DRAWSEGMENT* graphic;
    wxPoint prevPt;

    // Find edge point with minimum x, this should be in the outer polygon
    // which will define the perimeter Edge.Cuts polygon.
    wxPoint xmin    = wxPoint( INT_MAX, 0 );
    int     xmini   = 0;

    for( size_t i = 0; i < segList.size(); i++ )
    {
        graphic = (DRAWSEGMENT*) segList[i];

        switch( graphic->GetShape() )
        {
        case S_SEGMENT:
            {
                if( graphic->GetStart().x < xmin.x )
                {
                    xmin    = graphic->GetStart();
                    xmini   = i;
                }

                if( graphic->GetEnd().x < xmin.x )
                {
                    xmin    = graphic->GetEnd();
                    xmini   = i;
                }
            }
            break;

        case S_ARC:
            // Freerouter does not yet understand arcs, so approximate
            // an arc with a series of short lines and put those
            // line segments into the !same! PATH.
            {
                wxPoint  pstart = graphic->GetArcStart();
                wxPoint  center = graphic->GetCenter();
                double   angle  = -graphic->GetAngle();
                double   radius = graphic->GetRadius();
                int      steps  = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 );
                wxPoint  pt;

                for( int step = 1; step<=steps; ++step )
                {
                    double rotation = ( angle * step ) / steps;

                    pt = pstart;

                    RotatePoint( &pt, center, rotation );

                    if( pt.x < xmin.x )
                    {
                        xmin  = pt;
                        xmini = i;
                    }
                }
            }
            break;

        case S_CIRCLE:
            {
                wxPoint pt = graphic->GetCenter();

                // pt has minimum x point
                pt.x -= graphic->GetRadius();

                // when the radius <= 0, this is a mal-formed circle. Skip it
                if( graphic->GetRadius() > 0 && pt.x < xmin.x )
                {
                    xmin  = pt;
                    xmini = i;
                }
            }
            break;

        case S_CURVE:
            {
                graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() );

                for( unsigned int jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
                {
                    wxPoint pt = graphic->GetBezierPoints()[jj];

                    if( pt.x < xmin.x )
                    {
                        xmin  = pt;
                        xmini = i;
                    }
                }
            }
            break;

        case S_POLYGON:
            {
                const auto poly = graphic->GetPolyShape();
                MODULE* module = aSegList[0]->GetParentModule();
                double orientation = module ? module->GetOrientation() : 0.0;
                VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );

                for( auto iter = poly.CIterate(); iter; iter++ )
                {
                    auto pt = *iter;
                    RotatePoint( pt, orientation );
                    pt += offset;

                    if( pt.x < xmin.x )
                    {
                        xmin.x = pt.x;
                        xmin.y = pt.y;
                        xmini = i;
                    }
                }
            }
            break;
        default:
            break;
        }
    }

    // Grab the left most point, assume its on the board's perimeter, and see if we
    // can put enough graphics together by matching endpoints to formulate a cohesive
    // polygon.

    graphic = (DRAWSEGMENT*) segList[xmini];

    // The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items'
    segList.erase( segList.begin() + xmini );

    // Output the Edge.Cuts perimeter as circle or polygon.
    if( graphic->GetShape() == S_CIRCLE )
    {
        int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_LOW_DEF, 360.0 );
        TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), steps );
    }
    else if( graphic->GetShape() == S_POLYGON )
    {
        MODULE* module = graphic->GetParentModule();     // NULL for items not in footprints
        double orientation = module ? module->GetOrientation() : 0.0;
        VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );

        aPolygons.NewOutline();

        for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
        {
            auto pt = *it;
            RotatePoint( pt, orientation );
            pt += offset;
            aPolygons.Append( pt );
        }
    }
    else
    {
        // Polygon start point. Arbitrarily chosen end of the
        // segment and build the poly from here.

        wxPoint startPt = wxPoint( graphic->GetEnd() );
        prevPt = graphic->GetEnd();
        aPolygons.NewOutline();
        aPolygons.Append( prevPt );

        // Do not append the other end point yet of this 'graphic', this first
        // 'graphic' might be an arc or a curve.

        for(;;)
        {
            switch( graphic->GetShape() )
            {
            case S_SEGMENT:
                {
                    wxPoint  nextPt;

                    // Use the line segment end point furthest away from
                    // prevPt as we assume the other end to be ON prevPt or
                    // very close to it.

                    if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
                        nextPt = graphic->GetEnd();
                    else
                        nextPt = graphic->GetStart();

                    aPolygons.Append( nextPt );
                    prevPt = nextPt;
                }
                break;

            case S_ARC:
                // We do not support arcs in polygons, so approximate
                // an arc with a series of short lines and put those
                // line segments into the !same! PATH.
                {
                    wxPoint pstart  = graphic->GetArcStart();
                    wxPoint pend    = graphic->GetArcEnd();
                    wxPoint pcenter = graphic->GetCenter();
                    double  angle   = -graphic->GetAngle();
                    double  radius  = graphic->GetRadius();
                    int     steps   = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 );

                    if( !close_enough( prevPt, pstart, aTolerance ) )
                    {
                        wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );

                        angle = -angle;
                        std::swap( pstart, pend );
                    }

                    wxPoint nextPt;

                    for( int step = 1; step<=steps; ++step )
                    {
                        double rotation = ( angle * step ) / steps;
                        nextPt = pstart;
                        RotatePoint( &nextPt, pcenter, rotation );

                        aPolygons.Append( nextPt );
                    }

                    prevPt = nextPt;
                }
                break;

            case S_CURVE:
                // We do not support Bezier curves in polygons, so approximate
                // with a series of short lines and put those
                // line segments into the !same! PATH.
                {
                    wxPoint  nextPt;
                    bool reverse = false;

                    // Use the end point furthest away from
                    // prevPt as we assume the other end to be ON prevPt or
                    // very close to it.

                    if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
                        nextPt = graphic->GetEnd();
                    else
                    {
                        nextPt = graphic->GetStart();
                        reverse = true;
                    }

                    if( reverse )
                    {
                        for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
                            aPolygons.Append( graphic->GetBezierPoints()[jj] );
                    }
                    else
                    {
                        for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
                            aPolygons.Append( graphic->GetBezierPoints()[jj] );
                    }

                    prevPt = nextPt;
                }
                break;

            default:
                if( aErrorText )
                {
                    msg.Printf( "Unsupported DRAWSEGMENT type %s.",
                                BOARD_ITEM::ShowShape( graphic->GetShape() ) );

                    *aErrorText << msg << "\n";
                }

                if( aErrorLocation )
                    *aErrorLocation = graphic->GetPosition();

                return false;
            }

            // Get next closest segment.

            graphic = findPoint( prevPt, segList, aTolerance );

            // If there are no more close segments, check if the board
            // outline polygon can be closed.

            if( !graphic )
            {
                if( close_enough( startPt, prevPt, aTolerance ) )
                {
                    // Close the polygon back to start point
                    // aPolygons.Append( startPt ); // not needed
                }
                else
                {
                    if( aErrorText )
                    {
                        msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ),
                                    StringFromValue( MILLIMETRES, prevPt.x, true ),
                                    StringFromValue( MILLIMETRES, prevPt.y, true ) );

                        *aErrorText << msg << "\n";
                    }

                    if( aErrorLocation )
                        *aErrorLocation = prevPt;

                    return false;
                }
                break;
            }
        }
    }

    while( segList.size() )
    {
        // emit a signal layers keepout for every interior polygon left...
        int hole = aPolygons.NewHole();

        graphic = (DRAWSEGMENT*) segList[0];
        segList.erase( segList.begin() );

        // Both circles and polygons on the edge cuts layer are closed items that
        // do not connect to other elements, so we process them independently
        if( graphic->GetShape() == S_POLYGON )
        {
            MODULE* module = graphic->GetParentModule();     // NULL for items not in footprints
            double orientation = module ? module->GetOrientation() : 0.0;
            VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );

            for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
            {
                auto val = *it;
                RotatePoint( val, orientation );
                val += offset;

                aPolygons.Append( val, -1, hole );
            }
        }
        else if( graphic->GetShape() == S_CIRCLE )
        {
            // make a circle by segments;
            wxPoint  center  = graphic->GetCenter();
            double   angle   = 3600.0;
            wxPoint  start   = center;
            int      radius  = graphic->GetRadius();
            int      steps   = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 );
            wxPoint  nextPt;

            start.x += radius;

            for( int step = 0; step < steps; ++step )
            {
                double rotation = ( angle * step ) / steps;
                nextPt = start;
                RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
                aPolygons.Append( nextPt, -1, hole );
            }
        }
        else
        {
            // Polygon start point. Arbitrarily chosen end of the
            // segment and build the poly from here.

            wxPoint startPt( graphic->GetEnd() );
            prevPt = graphic->GetEnd();
            aPolygons.Append( prevPt, -1, hole );

            // do not append the other end point yet, this first 'graphic' might be an arc
            for(;;)
            {
                switch( graphic->GetShape() )
                {
                case S_SEGMENT:
                    {
                        wxPoint nextPt;

                        // Use the line segment end point furthest away from
                        // prevPt as we assume the other end to be ON prevPt or
                        // very close to it.

                        if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
                        {
                            nextPt = graphic->GetEnd();
                        }
                        else
                        {
                            nextPt = graphic->GetStart();
                        }

                        prevPt = nextPt;
                        aPolygons.Append( prevPt, -1, hole );
                    }
                    break;

                case S_ARC:
                    // Freerouter does not yet understand arcs, so approximate
                    // an arc with a series of short lines and put those
                    // line segments into the !same! PATH.
                    {
                        wxPoint pstart  = graphic->GetArcStart();
                        wxPoint pend    = graphic->GetArcEnd();
                        wxPoint pcenter = graphic->GetCenter();
                        double  angle   = -graphic->GetAngle();
                        int     radius  = graphic->GetRadius();
                        int     steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 );

                        if( !close_enough( prevPt, pstart, aTolerance ) )
                        {
                            wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );

                            angle = -angle;
                            std::swap( pstart, pend );
                        }

                        wxPoint nextPt;

                        for( int step = 1; step <= steps; ++step )
                        {
                            double rotation = ( angle * step ) / steps;

                            nextPt = pstart;
                            RotatePoint( &nextPt, pcenter, rotation );

                            aPolygons.Append( nextPt, -1, hole );
                        }

                        prevPt = nextPt;
                    }
                    break;

                case S_CURVE:
                    // We do not support Bezier curves in polygons, so approximate
                    // with a series of short lines and put those
                    // line segments into the !same! PATH.
                    {
                        wxPoint  nextPt;
                        bool reverse = false;

                        // Use the end point furthest away from
                        // prevPt as we assume the other end to be ON prevPt or
                        // very close to it.

                        if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
                            nextPt = graphic->GetEnd();
                        else
                        {
                            nextPt = graphic->GetStart();
                            reverse = true;
                        }

                        if( reverse )
                        {
                            for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
                                aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
                        }
                        else
                        {
                            for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
                                aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
                        }

                        prevPt = nextPt;
                    }
                    break;

                default:
                    if( aErrorText )
                    {
                        msg.Printf( "Unsupported DRAWSEGMENT type %s.",
                                    BOARD_ITEM::ShowShape( graphic->GetShape() ) );

                        *aErrorText << msg << "\n";
                    }

                    if( aErrorLocation )
                        *aErrorLocation = graphic->GetPosition();

                    return false;
                }

                // Get next closest segment.

                graphic = findPoint( prevPt, segList, aTolerance );

                // If there are no more close segments, check if polygon
                // can be closed.

                if( !graphic )
                {
                    if( close_enough( startPt, prevPt, aTolerance ) )
                    {
                        // Close the polygon back to start point
                        // aPolygons.Append( startPt, -1, hole );   // not needed
                    }
                    else
                    {
                        if( aErrorText )
                        {
                            msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ),
                                        StringFromValue( MILLIMETRES, prevPt.x, true ),
                                        StringFromValue( MILLIMETRES, prevPt.y, true ) );

                            *aErrorText << msg << "\n";
                        }

                        if( aErrorLocation )
                            *aErrorLocation = prevPt;

                        return false;
                    }
                    break;
                }
            }
        }
    }

    return true;
}