void CLAYER_TRIANGLES::AddToMiddleContourns( const SHAPE_POLY_SET &aPolySet,
                                             float zBot,
                                             float zTop,
                                             double aBiuTo3Du,
                                             bool aInvertFaceDirection )
{
    wxASSERT( aPolySet.OutlineCount() > 0 );

    if( aPolySet.OutlineCount() == 0 )
        return;

    // Calculate an estimation of points to reserve
    unsigned int nrContournPointsToReserve = 0;

    for( int i = 0; i < aPolySet.OutlineCount(); ++i )
    {
        const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i );

        nrContournPointsToReserve += pathOutline.PointCount();

        for( int h = 0; h < aPolySet.HoleCount( i ); ++h )
        {
            const SHAPE_LINE_CHAIN &hole = aPolySet.CHole( i, h );

            nrContournPointsToReserve += hole.PointCount();
        }
    }

    // Request to reserve more space
    m_layer_middle_contourns_quads->Reserve_More( nrContournPointsToReserve * 2,
                                                  true );

    #pragma omp parallel for
    for( signed int i = 0; i < aPolySet.OutlineCount(); ++i )
    {
        // Add outline
        const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i );

        AddToMiddleContourns( pathOutline, zBot, zTop, aBiuTo3Du, aInvertFaceDirection );

        // Add holes for this outline
        for( int h = 0; h < aPolySet.HoleCount( i ); ++h )
        {
            const SHAPE_LINE_CHAIN &hole = aPolySet.CHole( i, h );
            AddToMiddleContourns( hole, zBot, zTop, aBiuTo3Du, aInvertFaceDirection );
        }
    }
}
/* Build a pad outline as non filled polygon, to draw pads on silkscreen layer
 * Used only to draw pads outlines on silkscreen layers.
 */
void EDA_3D_CANVAS::buildPadShapeThickOutlineAsPolygon( const D_PAD*  aPad,
                                                SHAPE_POLY_SET& aCornerBuffer,
                                                int             aWidth,
                                                int             aCircleToSegmentsCount,
                                                double          aCorrectionFactor )
{
    if( aPad->GetShape() == PAD_SHAPE_CIRCLE )    // Draw a ring
    {
        TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(),
                                aPad->GetSize().x / 2, aCircleToSegmentsCount, aWidth );
        return;
    }

    // For other shapes, draw polygon outlines
    SHAPE_POLY_SET corners;
    aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ),
                                aCircleToSegmentsCount, aCorrectionFactor );

    // Add outlines as thick segments in polygon buffer

    const SHAPE_LINE_CHAIN& path = corners.COutline( 0 );

    for( int ii = 0; ii < path.PointCount(); ii++ )
    {
        const VECTOR2I& a = path.CPoint( ii );
        const VECTOR2I& b = path.CPoint( ii + 1 );

        TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
                                              wxPoint( a.x, a.y ),
                                              wxPoint( b.x, b.y ),
                                              aCircleToSegmentsCount, aWidth );
    }
}
void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int aWidth,
                                EDA_DRAW_MODE_T aPlotMode, void* aData )
{
    if( aPlotMode == SKETCH )
    {
        std::vector<wxPoint> cornerList;
        SHAPE_POLY_SET outlineBuffer;
        TransformOvalClearanceToPolygon( outlineBuffer,
                aStart, aEnd, aWidth, 32 , 1.0 );
        const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline(0 );

        for( int jj = 0; jj < path.PointCount(); jj++ )
            cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) );

        // Ensure the polygon is closed
        if( cornerList[0] != cornerList[cornerList.size() - 1] )
            cornerList.push_back( cornerList[0] );

        PlotPoly( cornerList, NO_FILL );
    }
    else
    {
        MoveTo( aStart );
        FinishTo( aEnd );
    }
}
void CINFO3D_VISU::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
                                                SHAPE_POLY_SET& aCornerBuffer,
                                                int             aWidth ) const
{
    if( aPad->GetShape() == PAD_SHAPE_CIRCLE )    // Draw a ring
    {
        unsigned int nr_sides_per_circle = GetNrSegmentsCircle( ( aPad->GetSize().x / 2 +
                                                                  aWidth / 2 ) * 2 );

        TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(),
                                aPad->GetSize().x / 2, nr_sides_per_circle, aWidth );
        return;
    }

    // For other shapes, draw polygon outlines
    SHAPE_POLY_SET corners;

    unsigned int nr_sides_per_circle = GetNrSegmentsCircle( glm::min( aPad->GetSize().x,
                                                                      aPad->GetSize().y) );

    buildPadShapePolygon( aPad, corners, wxSize( 0, 0 ),
                                nr_sides_per_circle, GetCircleCorrectionFactor( nr_sides_per_circle ) );

    // Add outlines as thick segments in polygon buffer

    const SHAPE_LINE_CHAIN& path = corners.COutline( 0 );

    for( int ii = 0; ii < path.PointCount(); ++ii )
    {
        const VECTOR2I& a = path.CPoint( ii );
        const VECTOR2I& b = path.CPoint( ii + 1 );

        TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
                                              wxPoint( a.x, a.y ),
                                              wxPoint( b.x, b.y ),
                                              nr_sides_per_circle,
                                              aWidth );
    }
}
Exemple #5
0
bool D_PAD::HitTest( const wxPoint& aPosition ) const
{
    int dx, dy;

    wxPoint shape_pos = ShapePos();

    wxPoint delta = aPosition - shape_pos;

    // first test: a test point must be inside a minimum sized bounding circle.
    int radius = GetBoundingRadius();

    if( ( abs( delta.x ) > radius ) || ( abs( delta.y ) > radius ) )
        return false;

    dx = m_Size.x >> 1; // dx also is the radius for rounded pads
    dy = m_Size.y >> 1;

    switch( GetShape() )
    {
    case PAD_SHAPE_CIRCLE:
        if( KiROUND( EuclideanNorm( delta ) ) <= dx )
            return true;

        break;

    case PAD_SHAPE_TRAPEZOID:
    {
        wxPoint poly[4];
        BuildPadPolygon( poly, wxSize(0,0), 0 );
        RotatePoint( &delta, -m_Orient );

        return TestPointInsidePolygon( poly, 4, delta );
    }

    case PAD_SHAPE_OVAL:
    {
        RotatePoint( &delta, -m_Orient );
        // An oval pad has the same shape as a segment with rounded ends
        // After rotation, the test point is relative to an horizontal pad
        int dist;
        wxPoint offset;
        if( dy > dx )   // shape is a vertical oval
        {
            offset.y = dy - dx;
            dist = dx;
        }
        else    //if( dy <= dx ) shape is an horizontal oval
        {
            offset.x = dy - dx;
            dist = dy;
        }
        return TestSegmentHit( delta, - offset, offset, dist );
    }
        break;

    case PAD_SHAPE_RECT:
        RotatePoint( &delta, -m_Orient );

        if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) )
            return true;

        break;

    case PAD_SHAPE_ROUNDRECT:
    {
        // Check for hit in polygon
        SHAPE_POLY_SET outline;
        const int segmentToCircleCount = 32;
        TransformRoundRectToPolygon( outline, wxPoint(0,0), GetSize(), m_Orient,
                                 GetRoundRectCornerRadius(), segmentToCircleCount );

        const SHAPE_LINE_CHAIN &poly = outline.COutline( 0 );
        return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta );
    }
        break;

    case PAD_SHAPE_CUSTOM:
        // Check for hit in polygon
        RotatePoint( &delta, -m_Orient );

        if( m_customShapeAsPolygon.OutlineCount() )
        {
            const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( 0 );
            return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta );
        }
        break;
    }

    return false;
}
/* test DRC between 2 pads.
 * this function can be also used to test DRC between a pad and a hole,
 * because a hole is like a round or oval pad.
 */
bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
{
    int     dist;
    double pad_angle;

    // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
    int     dist_min = aRefPad->GetClearance( aPad );

    // relativePadPos is the aPad shape position relative to the aRefPad shape position
    wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();

    dist = KiROUND( EuclideanNorm( relativePadPos ) );

    // Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
    if( (dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius()) >= dist_min )
        return true;

    /* Here, pads are near and DRC depend on the pad shapes
     * We must compare distance using a fine shape analysis
     * Because a circle or oval shape is the easier shape to test, try to have
     * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
     * if aRefPad = TRAP. and aPad = RECT, also swap pads
     * Swap aRefPad and aPad if needed
     */
    bool swap_pads;
    swap_pads = false;

    // swap pads to make comparisons easier
    // Note also a ROUNDRECT pad with a corner radius = r can be considered as
    // a smaller RECT (size - 2*r) with a clearance increased by r
    // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
    if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
    {
        // pad ref shape is here oval, rect, roundrect, trapezoid or custom
        switch( aPad->GetShape() )
        {
            case PAD_SHAPE_CIRCLE:
                swap_pads = true;
                break;

            case PAD_SHAPE_OVAL:
                swap_pads = true;
                break;

            case PAD_SHAPE_RECT:
            case PAD_SHAPE_ROUNDRECT:
                if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
                    swap_pads = true;
                break;

            default:
                break;
        }
    }

    if( swap_pads )
    {
        std::swap( aRefPad, aPad );
        relativePadPos = -relativePadPos;
    }

    // corners of aRefPad (used only for rect/roundrect/trap pad)
    wxPoint polyref[4];
    // corners of aRefPad (used only for custom pad)
    SHAPE_POLY_SET polysetref;

    // corners of aPad (used only for rect/roundrect/trap pad)
    wxPoint polycompare[4];
    // corners of aPad (used only custom pad)
    SHAPE_POLY_SET polysetcompare;

    /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
     * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
     * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
     * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
     */
    bool diag = true;

    switch( aRefPad->GetShape() )
    {
    case PAD_SHAPE_CIRCLE:

        /* One can use checkClearanceSegmToPad to test clearance
         * aRefPad is like a track segment with a null length and a witdth = GetSize().x
         */
        m_segmLength = 0;
        m_segmAngle  = 0;

        m_segmEnd.x = m_segmEnd.y = 0;

        m_padToTestPos = relativePadPos;
        diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
        break;

    case PAD_SHAPE_TRAPEZOID:
    case PAD_SHAPE_ROUNDRECT:
    case PAD_SHAPE_RECT:
        // pad_angle = pad orient relative to the aRefPad orient
        pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
        NORMALIZE_ANGLE_POS( pad_angle );

        if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
        {
            int padRadius = aRefPad->GetRoundRectCornerRadius();
            dist_min += padRadius;
            GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ),
                                aRefPad->GetSize(), aRefPad->GetOrientation() );
        }
        else
            aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );

        switch( aPad->GetShape() )
        {
        case PAD_SHAPE_ROUNDRECT:
        case PAD_SHAPE_RECT:
        case PAD_SHAPE_TRAPEZOID:
            if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
            {
                int padRadius = aPad->GetRoundRectCornerRadius();
                dist_min += padRadius;
                GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos,
                                    aPad->GetSize(), aPad->GetOrientation() );
            }
            else
            {
                aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );

                // Move aPad shape to relativePadPos
                for( int ii = 0; ii < 4; ii++ )
                    polycompare[ii] += relativePadPos;
            }

            // And now test polygons:
            if( polysetref.OutlineCount() )
            {
                const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
                // And now test polygons:
                if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
                            polycompare, 4, dist_min ) )
                    diag = false;
            }
            else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) )
                diag = false;
            break;

        default:
            wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
            break;
        }
        break;

    case PAD_SHAPE_OVAL:     /* an oval pad is like a track segment */
    {
        /* Create a track segment with same dimensions as the oval aRefPad
         * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
         */
        int segm_width;
        m_segmAngle = aRefPad->GetOrientation();                // Segment orient.

        if( aRefPad->GetSize().y < aRefPad->GetSize().x )     // Build an horizontal equiv segment
        {
            segm_width   = aRefPad->GetSize().y;
            m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y;
        }
        else        // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
        {
            segm_width   = aRefPad->GetSize().x;
            m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x;
            m_segmAngle += 900;
        }

        /* the start point must be 0,0 and currently relativePadPos
         * is relative the center of pad coordinate */
        wxPoint segstart;
        segstart.x = -m_segmLength / 2;                 // Start point coordinate of the horizontal equivalent segment

        RotatePoint( &segstart, m_segmAngle );          // actual start point coordinate of the equivalent segment
        // Calculate segment end position relative to the segment origin
        m_segmEnd.x = -2 * segstart.x;
        m_segmEnd.y = -2 * segstart.y;

        // Recalculate the equivalent segment angle in 0,1 degrees
        // to prepare a call to checkClearanceSegmToPad()
        m_segmAngle = ArcTangente( m_segmEnd.y, m_segmEnd.x );

        // move pad position relative to the segment origin
        m_padToTestPos = relativePadPos - segstart;

        // Use segment to pad check to test the second pad:
        diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
        break;
    }

    default:
        wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
        break;
    }

    return diag;
}
Exemple #7
0
bool ZONE_FILLER::fillZoneWithSegments( const ZONE_CONTAINER* aZone,
                                        const SHAPE_POLY_SET& aFilledPolys,
                                        ZONE_SEGMENT_FILL& aFillSegs ) const
{
    bool success = true;
    // segments are on something like a grid. Give it a minimal size
    // to avoid too many segments, and use the m_ZoneMinThickness when (this is usually the case)
    // the size is > mingrid_size.
    // This is not perfect, but the actual purpose of this code
    // is to allow filling zones on a grid, with grid size > m_ZoneMinThickness,
    // in order to have really a grid.
    //
    // Using a user selectable grid size is for future Kicad versions.
    // For now the area is fully filled.
    int mingrid_size = Millimeter2iu( 0.05 );
    int grid_size = std::max( mingrid_size, aZone->GetMinThickness() );
    // Make segments slightly overlapping to ensure a good full filling
    grid_size -= grid_size/20;

    // Creates the horizontal segments
    for ( int index = 0; index < aFilledPolys.OutlineCount(); index++ )
    {
        const SHAPE_LINE_CHAIN& outline0 = aFilledPolys.COutline( index );
        success = fillPolygonWithHorizontalSegments( outline0, aFillSegs, grid_size );

        if( !success )
            break;

        // Creates the vertical segments. Because the filling algo creates horizontal segments,
        // to reuse the fillPolygonWithHorizontalSegments function, we rotate the polygons to fill
        // then fill them, then inverse rotate the result
        SHAPE_LINE_CHAIN outline90;
        outline90.Append( outline0 );

        // Rotate 90 degrees the outline:
        for( int ii = 0; ii < outline90.PointCount(); ii++ )
        {
            VECTOR2I& point = outline90.Point( ii );
            std::swap( point.x, point.y );
            point.y = -point.y;
        }

        int first_point = aFillSegs.size();
        success = fillPolygonWithHorizontalSegments( outline90, aFillSegs, grid_size );

        if( !success )
            break;

        // Rotate -90 degrees the segments:
        for( unsigned ii = first_point; ii < aFillSegs.size(); ii++ )
        {
            SEG& segm = aFillSegs[ii];
            std::swap( segm.A.x, segm.A.y );
            std::swap( segm.B.x, segm.B.y );
            segm.A.x = - segm.A.x;
            segm.B.x = - segm.B.x;
        }
    }

    return success;
}
void Convert_shape_line_polygon_to_triangles( const SHAPE_POLY_SET &aPolyList,
                                              CGENERICCONTAINER2D &aDstContainer,
                                              float aBiuTo3DunitsScale ,
                                              const BOARD_ITEM &aBoardItem )
{
    unsigned int nOutlines = aPolyList.OutlineCount();


    for( unsigned int idx = 0; idx < nOutlines; ++idx )
    {
        const SHAPE_LINE_CHAIN &outlinePath = aPolyList.COutline( idx );

        wxASSERT( outlinePath.PointCount() >= 3 );

        std::vector<SFVEC2I64> scaledOutline;
        scaledOutline.resize( outlinePath.PointCount() );

        // printf("\nidx: %u\n", idx);

        // Apply a scale to the points
        for( unsigned int i = 0;
             i < (unsigned int)outlinePath.PointCount();
             ++i )
        {
            const VECTOR2I& a = outlinePath.CPoint( i );

#ifdef APPLY_EDGE_SHRINK
            scaledOutline[i] = SFVEC2I64( (glm::int64)a.x * POLY_SCALE_FACT,
                                          (glm::int64)a.y * POLY_SCALE_FACT );
#else
            scaledOutline[i] = SFVEC2I64( (glm::int64)a.x,
                                          (glm::int64)a.y );
#endif
        }

#ifdef APPLY_EDGE_SHRINK
        // Apply a modification to the points
        EdgeShrink( scaledOutline );
#endif
        // Copy to a array of pointers
        std::vector<p2t::Point*> polyline;
        polyline.resize( outlinePath.PointCount() );

        for( unsigned int i = 0;
             i < (unsigned int)scaledOutline.size();
             ++i )
        {
            const SFVEC2I64 &a = scaledOutline[i];

            //printf("%lu %lu\n", a.x, a.y);

            polyline[i] = new p2t::Point( (double)a.x,
                                          (double)a.y );
        }

        // Start creating the structured to be triangulated
        p2t::CDT* cdt = new p2t::CDT( polyline );

        // Add holes for this outline
        unsigned int nHoles = aPolyList.HoleCount( idx );

        std::vector< std::vector<p2t::Point*> > polylineHoles;

        polylineHoles.resize( nHoles );

        for( unsigned int idxHole = 0; idxHole < nHoles; ++idxHole )
        {
            const SHAPE_LINE_CHAIN &outlineHoles = aPolyList.CHole( idx,
                                                                    idxHole );

            wxASSERT( outlineHoles.PointCount() >= 3 );

            std::vector<SFVEC2I64> scaledHole;
            scaledHole.resize( outlineHoles.PointCount() );

            // Apply a scale to the points
            for( unsigned int i = 0;
                 i < (unsigned int)outlineHoles.PointCount();
                 ++i )
            {
                const VECTOR2I &h = outlineHoles.CPoint( i );
#ifdef APPLY_EDGE_SHRINK
                scaledHole[i] = SFVEC2I64( (glm::int64)h.x * POLY_SCALE_FACT,
                                           (glm::int64)h.y * POLY_SCALE_FACT );
#else
                scaledHole[i] = SFVEC2I64( (glm::int64)h.x,
                                           (glm::int64)h.y );
#endif
            }

#ifdef APPLY_EDGE_SHRINK
            // Apply a modification to the points
            EdgeShrink( scaledHole );
#endif

            // Resize and reserve space
            polylineHoles[idxHole].resize( outlineHoles.PointCount() );

            for( unsigned int i = 0;
                 i < (unsigned int)outlineHoles.PointCount();
                 ++i )
            {
                const SFVEC2I64 &h = scaledHole[i];

                polylineHoles[idxHole][i] = new p2t::Point( h.x, h.y );
            }

            cdt->AddHole( polylineHoles[idxHole] );
        }

        // Triangulate
        cdt->Triangulate();

        // Hint: if you find any crashes on the triangulation poly2tri library,
        // you can use the following site to debug the points and it will mark
        // the errors in the polygon:
        // http://r3mi.github.io/poly2tri.js/


        // Get and add triangles
        std::vector<p2t::Triangle*> triangles;
        triangles = cdt->GetTriangles();

#ifdef APPLY_EDGE_SHRINK
        const double conver_d = (double)aBiuTo3DunitsScale *
                                POLY_SCALE_FACT_INVERSE;
#else
        const double conver_d = (double)aBiuTo3DunitsScale;
#endif
        for( unsigned int i = 0; i < triangles.size(); ++i )
        {
            p2t::Triangle& t = *triangles[i];

            p2t::Point& a = *t.GetPoint( 0 );
            p2t::Point& b = *t.GetPoint( 1 );
            p2t::Point& c = *t.GetPoint( 2 );

            aDstContainer.Add( new CTRIANGLE2D( SFVEC2F( a.x * conver_d,
                                                        -a.y * conver_d ),
                                                SFVEC2F( b.x * conver_d,
                                                        -b.y * conver_d ),
                                                SFVEC2F( c.x * conver_d,
                                                        -c.y * conver_d ),
                                                aBoardItem ) );
        }

        // Delete created data
        delete cdt;

        // Free points
        FreeClear(polyline);

        for( unsigned int idxHole = 0; idxHole < nHoles; ++idxHole )
        {
            FreeClear( polylineHoles[idxHole] );
        }
    }
}
bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
{
    SHAPE_POLY_SET poly;

    if( !buildCustomPadPolygon( &poly, ARC_LOW_DEF ) )
        return false;

    const int minSteps = 10;
    const int maxSteps = 50;

    int stepsX, stepsY;

    auto bbox = poly.BBox();

    if( bbox.GetWidth() < bbox.GetHeight() )
    {
        stepsX = minSteps;
        stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
    }
    else
    {
        stepsY = minSteps;
        stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
    }

    stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
    stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );

    auto center = bbox.Centre();

    auto minDist = std::numeric_limits<int64_t>::max();
    int64_t minDistEdge;

    if( GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
    {
        minDistEdge = GetSize().x;
    }
    else
    {
        minDistEdge = std::max( GetSize().x, GetSize().y );
    }

    OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );

    for ( int y = 0; y < stepsY ; y++ )
    {
        for ( int x = 0; x < stepsX; x++ )
        {
            VECTOR2I p = bbox.GetPosition();
            p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
            p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );

            if ( poly.Contains(p) )
            {

                auto dist = (center - p).EuclideanNorm();
                auto distEdge = poly.COutline(0).Distance( p, true );
                if ( distEdge >= minDistEdge )
                {
                    if ( dist < minDist )
                    {
                        bestAnchor = p;
                        minDist = dist;
                    }
                }
            }
        }
    }

    if ( bestAnchor )
    {
        aPos = *bestAnchor;
        return true;
    }

    return false;
}
/**
 * DXF polygon: doesn't fill it but at least it close the filled ones
 * DXF does not know thick outline.
 * It does not know thhick segments, therefore filled polygons with thick outline
 * are converted to inflated polygon by aWidth/2
 */
void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
                            FILL_T aFill, int aWidth)
{
    if( aCornerList.size() <= 1 )
        return;

    unsigned last = aCornerList.size() - 1;

    // Plot outlines with lines (thickness = 0) to define the polygon
    if( aWidth <= 0  )
    {
        MoveTo( aCornerList[0] );

        for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
            LineTo( aCornerList[ii] );

        // Close polygon if 'fill' requested
        if( aFill )
        {
            if( aCornerList[last] != aCornerList[0] )
                LineTo( aCornerList[0] );
        }

        PenFinish();

        return;
    }


    // if the polygon outline has thickness, and is not filled
    // (i.e. is a polyline) plot outlines with thick segments
    if( aWidth > 0 && !aFill )
    {
        MoveTo( aCornerList[0] );

        for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
            ThickSegment( aCornerList[ii-1], aCornerList[ii],
                          aWidth, FILLED );

        return;
    }

    // The polygon outline has thickness, and is filled
    // Build and plot the polygon which contains the initial
    // polygon and its thick outline
    SHAPE_POLY_SET  bufferOutline;
    SHAPE_POLY_SET  bufferPolybase;
    const int circleToSegmentsCount = 16;

    bufferPolybase.NewOutline();

    // enter outline as polygon:
    for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
    {
        TransformRoundedEndsSegmentToPolygon( bufferOutline,
            aCornerList[ii-1], aCornerList[ii], circleToSegmentsCount, aWidth );
    }

    // enter the initial polygon:
    for( unsigned ii = 0; ii < aCornerList.size(); ii++ )
    {
        bufferPolybase.Append( aCornerList[ii] );
    }

    // Merge polygons to build the polygon which contains the initial
    // polygon and its thick outline

    bufferPolybase.BooleanAdd( bufferOutline ); // create the outline which contains thick outline
    bufferPolybase.Fracture();


    if( bufferPolybase.OutlineCount() < 1 )      // should not happen
        return;

    const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );

    if( path.PointCount() < 2 )           // should not happen
        return;

    // Now, output the final polygon to DXF file:
    last = path.PointCount() - 1;
	  VECTOR2I point = path.CPoint( 0 );

    wxPoint startPoint( point.x, point.y );
    MoveTo( startPoint );

    for( int ii = 1; ii < path.PointCount(); ii++ )
    {
        point = path.CPoint( ii );
        LineTo( wxPoint( point.x, point.y ) );
    }

    // Close polygon, if needed
    point = path.CPoint( last );
    wxPoint endPoint( point.x, point.y );

    if( endPoint != startPoint )
        LineTo( startPoint );

    PenFinish();
}
void C3D_RENDER_OGL_LEGACY::reload()
{
    m_reloadRequested = false;

    ogl_free_all_display_lists();

    COBJECT2D_STATS::Instance().ResetStats();

    m_settings.InitSettings();

    SFVEC3F camera_pos = m_settings.GetBoardCenter3DU();
    m_settings.CameraGet().SetBoardLookAtPos( camera_pos );

    // Create Board
    // /////////////////////////////////////////////////////////////////////////
    printf("Create board...\n");
    CCONTAINER2D boardContainer;
    Convert_shape_line_polygon_to_triangles( m_settings.GetBoardPoly(),
                                             boardContainer,
                                             m_settings.BiuTo3Dunits(),
                                             (const BOARD_ITEM &)*m_settings.GetBoard() );

    const LIST_OBJECT2D listBoardObject2d = boardContainer.GetList();

    if( listBoardObject2d.size() > 0 )
    {
        /*
        float layer_z_top = m_settings.GetLayerBottomZpos3DU( F_Cu );
        float layer_z_bot = m_settings.GetLayerBottomZpos3DU( B_Cu );
*/
        float layer_z_top = m_settings.GetLayerBottomZpos3DU( B_Mask );
        float layer_z_bot = m_settings.GetLayerTopZpos3DU( B_Mask );
        CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( listBoardObject2d.size() );

        for( LIST_OBJECT2D::const_iterator itemOnLayer = listBoardObject2d.begin();
             itemOnLayer != listBoardObject2d.end();
             itemOnLayer++ )
        {
            const COBJECT2D *object2d_A = static_cast<const COBJECT2D *>(*itemOnLayer);

            wxASSERT( object2d_A->GetObjectType() == OBJ2D_TRIANGLE );

            const CTRIANGLE2D *tri = (const CTRIANGLE2D *)object2d_A;
            const SFVEC2F &v1 = tri->GetP1();
            const SFVEC2F &v2 = tri->GetP2();
            const SFVEC2F &v3 = tri->GetP3();

            add_triangle_top_bot( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
        }

        const SHAPE_POLY_SET boardPoly = m_settings.GetBoardPoly();
        SHAPE_POLY_SET boardPolyCopy = boardPoly;

        boardPolyCopy.Simplify( SHAPE_POLY_SET::PM_FAST );

        if( boardPolyCopy.OutlineCount() == 1 )
        {
            const SHAPE_LINE_CHAIN& outlinePath = boardPolyCopy.COutline( 0 );

            std::vector< SFVEC2F > contournPoints;
            contournPoints.clear();
            contournPoints.reserve( outlinePath.PointCount() + 2 );

            for( unsigned int i = 0; i < (unsigned int)outlinePath.PointCount(); ++i )
            {
                const VECTOR2I& v = outlinePath.CPoint( i );
                contournPoints.push_back( SFVEC2F( v.x * m_settings.BiuTo3Dunits(),
                                                  -v.y * m_settings.BiuTo3Dunits() ) );
            }
            contournPoints.push_back( contournPoints[0] );

            if( contournPoints.size() > 4 )
            {
                // Calculate normals of each segment of the contourn
                std::vector< SFVEC2F > contournNormals;
                contournNormals.clear();
                contournNormals.reserve( contournPoints.size() );
                for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i )
                {
                    const SFVEC2F &v0 = contournPoints[i + 0];
                    const SFVEC2F &v1 = contournPoints[i + 1];

                    SFVEC2F n = glm::normalize( v1 - v0 );
                    contournNormals.push_back( SFVEC2F( -n.y, n.x ) );
                }

                SFVEC2F lastNormal = contournNormals[contournPoints.size() - 2];
                for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i )
                {
                    const SFVEC2F &v0 = contournPoints[i + 0];
                    const SFVEC2F &v1 = contournPoints[i + 1];

                    layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_top ) ),
                                                                             SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_top ) ),
                                                                             SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_bot ) ),
                                                                             SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_bot ) ) );

                    SFVEC2F n0 = contournNormals[i];

                    if( glm::dot( n0, lastNormal ) > 0.5f )
                        n0 += lastNormal;
                    else
                        n0 += contournNormals[i];

                    const SFVEC2F &nextNormal = contournNormals[ (i + 1) % (contournPoints.size() - 1) ];
                    SFVEC2F n1 = contournNormals[i];

                    if( glm::dot( n1, nextNormal ) > 0.5f )
                        n1 += nextNormal;
                    else
                        n1 += contournNormals[i];

                    n0 = glm::normalize( n0 );
                    n1 = glm::normalize( n1 );

                    const SFVEC3F n3d0 = SFVEC3F( n0.x, n0.y, 0.0f );
                    const SFVEC3F n3d1 = SFVEC3F( n1.x, n1.y, 0.0f );

                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d0, n3d1, n3d1, n3d0 );

                    lastNormal = contournNormals[i];
/*
                    const SFVEC2F n0 = glm::normalize( v0 - center );
                    const SFVEC2F n1 = glm::normalize( v1 - center );
                    const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f );
                    const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f );
                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );*/
                }
            }
            contournPoints.clear();
        }

        m_ogl_disp_list_board = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, m_ogl_circle_texture, SFVEC3F(0.65f,0.55f,0.05f) );

        delete layerTriangles;
    }


    float calc_sides_min_factor = (float)(   10.0 * IU_PER_MILS * m_settings.BiuTo3Dunits() );
    float calc_sides_max_factor = (float)( 1000.0 * IU_PER_MILS * m_settings.BiuTo3Dunits() );


    // Add layers maps (except B_Mask and F_Mask)
    // /////////////////////////////////////////////////////////////////////////
    printf("Add layers maps...\n");
    for( MAP_CONTAINER_2D::const_iterator it = m_settings.GetMapLayers().begin();
         it != m_settings.GetMapLayers().end(); it++ )
    {
        LAYER_ID layer_id = static_cast<LAYER_ID>(it->first);

        if( !m_settings.Is3DLayerEnabled( layer_id ) )
            continue;

        const CBVHCONTAINER2D *container2d = static_cast<const CBVHCONTAINER2D *>(it->second);
        const LIST_OBJECT2D listObject2d = container2d->GetList();

        if( listObject2d.size() == 0 )
            continue;

        //CMATERIAL *materialLayer = &m_materials.m_SilkS;
        SFVEC3F layerColor = SFVEC3F( 0.3f, 0.4f, 0.5f );

        float layer_z_bot = m_settings.GetLayerBottomZpos3DU( layer_id );
        float layer_z_top = m_settings.GetLayerTopZpos3DU( layer_id );

        if( layer_z_top < layer_z_bot )
        {
            float tmpFloat = layer_z_bot;
            layer_z_bot = layer_z_top;
            layer_z_top = tmpFloat;
        }

        layer_z_bot -= m_settings.GetNonCopperLayerThickness3DU();
        layer_z_top += m_settings.GetNonCopperLayerThickness3DU();

        if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) )
        {
            switch( layer_id )
            {
                case B_Adhes:
                case F_Adhes:

                break;

                case B_Paste:
                case F_Paste:
    //                materialLayer = &m_materials.m_Paste;
                break;

                case B_SilkS:
                case F_SilkS:

    //                materialLayer = &m_materials.m_SilkS;
    //                layerColor = g_silkscreenColor;
                break;

                case Dwgs_User:
                case Cmts_User:
                case Eco1_User:
                case Eco2_User:
                case Edge_Cuts:
                case Margin:
                break;

                case B_CrtYd:
                case F_CrtYd:
                break;

                case B_Fab:
                case F_Fab:
                break;

                default:
                    //materialLayer = &m_materials.m_Copper;

                        //layerColor = g_copperColor;


                break;
            }
        }
        else
        {
            layerColor = m_settings.GetLayerColor( layer_id );

        }


        // Calculate an estiation for then nr of triangles based on the nr of objects
        unsigned int nrTrianglesEstimation = listObject2d.size() * 8;

        CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( nrTrianglesEstimation );

        m_triangles[layer_id] = layerTriangles;

        for( LIST_OBJECT2D::const_iterator itemOnLayer = listObject2d.begin();
             itemOnLayer != listObject2d.end(); itemOnLayer++ )
        {
            const COBJECT2D *object2d_A = static_cast<const COBJECT2D *>(*itemOnLayer);

            switch( object2d_A->GetObjectType() )
            {

                case OBJ2D_FILLED_CIRCLE:
                {
                    const CFILLEDCIRCLE2D *filledCircle = (const CFILLEDCIRCLE2D *)object2d_A;
                    const SFVEC2F &center = filledCircle->GetCenter();
                    float radius = filledCircle->GetRadius() * 2.0f;            // Double because the render triangle
                    float radiusSquared = radius * radius;

                    const float f = (sqrtf(2.0f) / 2.0f) * radius * 0.9;// * texture_factor;

                    layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, layer_z_top ),
                                                                           SFVEC3F( center.x - f, center.y, layer_z_top ),
                                                                           SFVEC3F( center.x,
                                                                                    center.y - f, layer_z_top ) );

                    layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, layer_z_top ),
                                                                           SFVEC3F( center.x + f, center.y, layer_z_top ),
                                                                           SFVEC3F( center.x,
                                                                                    center.y + f, layer_z_top ) );

                    layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, layer_z_bot ),
                                                                           SFVEC3F( center.x + f, center.y, layer_z_bot ),
                                                                           SFVEC3F( center.x,
                                                                                    center.y - f, layer_z_bot ) );

                    layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, layer_z_bot ),
                                                                           SFVEC3F( center.x - f, center.y, layer_z_bot ),
                                                                           SFVEC3F( center.x,
                                                                                    center.y + f, layer_z_bot ) );

                    unsigned int nr_sides_per_circle = (unsigned int)mapf( radiusSquared,
                                                                           calc_sides_min_factor, calc_sides_max_factor,
                                                                           24.0f, 256.0f );

                    wxASSERT( nr_sides_per_circle >= 24 );

                    // Normal radius for the circle
                    radius = filledCircle->GetRadius();

                    std::vector< SFVEC2F > contournPoints;

                    contournPoints.clear();
                    contournPoints.reserve( nr_sides_per_circle + 2 );
                    int    delta       = 3600 / nr_sides_per_circle;
                    for( int ii = 0; ii < 3600; ii += delta )
                    {
                        const SFVEC2F rotatedDir = glm::rotate( SFVEC2F( 0.0f, 1.0f ),  (float)ii  * 2.0f * 3.14f / 3600.0f );
                        contournPoints.push_back(   SFVEC2F( center.x - rotatedDir.y * radius, center.y + rotatedDir.x * radius ) );
                    }
                    contournPoints.push_back( contournPoints[0] );

                    if( contournPoints.size() > 1 )
                    {
                        for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i )
                        {
                            const SFVEC2F &v0 = contournPoints[i + 0];
                            const SFVEC2F &v1 = contournPoints[i + 1];

                            layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ),
                                                                                     SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ),
                                                                                     SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ),
                                                                                     SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ) );


                            const SFVEC2F n0 = glm::normalize( v0 - center );
                            const SFVEC2F n1 = glm::normalize( v1 - center );
                            const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f );
                            const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f );
                            layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );
                        }
                    }
                    contournPoints.clear();

                }
                break;

                case OBJ2D_DUMMYBLOCK:
                {
                }
                break;

                case OBJ2D_POLYGON4PT:
                {
                    const CPOLYGON4PTS2D *poly = (const CPOLYGON4PTS2D *)object2d_A;
                    const SFVEC2F &v0 = poly->GetV0();
                    const SFVEC2F &v1 = poly->GetV1();
                    const SFVEC2F &v2 = poly->GetV2();
                    const SFVEC2F &v3 = poly->GetV3();

                    add_triangle_top_bot( layerTriangles, v0, v2, v1, layer_z_top, layer_z_bot );
                    add_triangle_top_bot( layerTriangles, v2, v0, v3, layer_z_top, layer_z_bot );

                    const SFVEC2F &n0 = poly->GetN0();
                    const SFVEC2F &n1 = poly->GetN1();
                    const SFVEC2F &n2 = poly->GetN2();
                    const SFVEC2F &n3 = poly->GetN3();

                    const SFVEC3F n3d0 = SFVEC3F(-n0.y, n0.x, 0.0f );
                    const SFVEC3F n3d1 = SFVEC3F(-n1.y, n1.x, 0.0f );
                    const SFVEC3F n3d2 = SFVEC3F(-n2.y, n2.x, 0.0f );
                    const SFVEC3F n3d3 = SFVEC3F(-n3.y, n3.x, 0.0f );

                    layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_bot ),
                                                                             SFVEC3F( v1.x, v1.y, layer_z_bot ),
                                                                             SFVEC3F( v1.x, v1.y, layer_z_top ),
                                                                             SFVEC3F( v0.x, v0.y, layer_z_top ) );
                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d0, n3d0, n3d0, n3d0 );


                    layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v2.x, v2.y, layer_z_top ),
                                                                             SFVEC3F( v1.x, v1.y, layer_z_top ),
                                                                             SFVEC3F( v1.x, v1.y, layer_z_bot ),
                                                                             SFVEC3F( v2.x, v2.y, layer_z_bot ) );
                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d1, n3d1, n3d1, n3d1 );


                    layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v3.x, v3.y, layer_z_top ),
                                                                             SFVEC3F( v2.x, v2.y, layer_z_top ),
                                                                             SFVEC3F( v2.x, v2.y, layer_z_bot ),
                                                                             SFVEC3F( v3.x, v3.y, layer_z_bot ) );
                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d2, n3d2, n3d2, n3d2 );


                    layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_top ),
                                                                             SFVEC3F( v3.x, v3.y, layer_z_top ),
                                                                             SFVEC3F( v3.x, v3.y, layer_z_bot ),
                                                                             SFVEC3F( v0.x, v0.y, layer_z_bot ) );
                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d3, n3d3, n3d3, n3d3 );
                }
                break;


                case OBJ2D_RING:
                {
                    const CRING2D *ring = (const CRING2D *)object2d_A;
                    const SFVEC2F &center = ring->GetCenter();
                    float inner = ring->GetInnerRadius();
                    float outer = ring->GetOuterRadius();

                    unsigned int nr_sides_per_circle = (unsigned int)mapf( outer,
                                                                           calc_sides_min_factor, calc_sides_max_factor,
                                                                           24.0f, 256.0f );

                    wxASSERT( nr_sides_per_circle >= 24 );


                    std::vector< SFVEC2F > innerContour;
                    std::vector< SFVEC2F > outerContour;
                    innerContour.clear();
                    innerContour.reserve( nr_sides_per_circle + 2 );

                    outerContour.clear();
                    outerContour.reserve( nr_sides_per_circle + 2 );

                    int    delta       = 3600 / nr_sides_per_circle;
                    for( int ii = 0; ii < 3600; ii += delta )
                    {
                        const SFVEC2F rotatedDir = glm::rotate( SFVEC2F( 0.0f, 1.0f),  (float)        ii  * 2.0f * 3.14f / 3600.0f );

                        innerContour.push_back( SFVEC2F( center.x - rotatedDir.y * inner, center.y + rotatedDir.x * inner ) );
                        outerContour.push_back( SFVEC2F( center.x - rotatedDir.y * outer, center.y + rotatedDir.x * outer ) );
                    }

                    innerContour.push_back( innerContour[0] );
                    outerContour.push_back( outerContour[0] );

                    wxASSERT( innerContour.size() == outerContour.size() );

                    for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
                    {
                        const SFVEC2F &vi0 = innerContour[i + 0];
                        const SFVEC2F &vi1 = innerContour[i + 1];
                        const SFVEC2F &vo0 = outerContour[i + 0];
                        const SFVEC2F &vo1 = outerContour[i + 1];

                        layerTriangles->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, layer_z_top ),
                                                                        SFVEC3F( vi0.x, vi0.y, layer_z_top ),
                                                                        SFVEC3F( vo0.x, vo0.y, layer_z_top ),
                                                                        SFVEC3F( vo1.x, vo1.y, layer_z_top ) );

                        layerTriangles->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, layer_z_bot ),
                                                                        SFVEC3F( vo1.x, vo1.y, layer_z_bot ),
                                                                        SFVEC3F( vo0.x, vo0.y, layer_z_bot ),
                                                                        SFVEC3F( vi0.x, vi0.y, layer_z_bot ) );
                    }

                    for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
                    {
                        const SFVEC2F &v0 = innerContour[i + 0];
                        const SFVEC2F &v1 = innerContour[i + 1];

                        layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ),
                                                                                 SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ),
                                                                                 SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ),
                                                                                 SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ) );


                        const SFVEC2F n0 = glm::normalize( v0 - center );
                        const SFVEC2F n1 = glm::normalize( v1 - center );
                        const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f );
                        const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f );
                        layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );
                    }


                    for( unsigned int i = 0; i < ( outerContour.size() - 1 ); ++i )
                    {
                        const SFVEC2F &v0 = outerContour[i + 0];
                        const SFVEC2F &v1 = outerContour[i + 1];

                        layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ),
                                                                                 SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ),
                                                                                 SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ),
                                                                                 SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ) );


                        const SFVEC2F n0 = glm::normalize( v0 - center );
                        const SFVEC2F n1 = glm::normalize( v1 - center );
                        const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f );
                        const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f );
                        layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );
                    }

                }
                break;


                case OBJ2D_TRIANGLE:
                {
                    const CTRIANGLE2D *tri = (const CTRIANGLE2D *)object2d_A;
                    const SFVEC2F &v1 = tri->GetP1();
                    const SFVEC2F &v2 = tri->GetP2();
                    const SFVEC2F &v3 = tri->GetP3();

                    add_triangle_top_bot( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
                }
                break;

                case OBJ2D_ROUNDSEG:
                {
                    const CROUNDSEGMENT2D &roundSeg = (const CROUNDSEGMENT2D &) *object2d_A;
                    unsigned int nr_sides_per_circle = (unsigned int)mapf( roundSeg.GetWidth(),
                                                                           calc_sides_min_factor, calc_sides_max_factor,
                                                                           24.0f, 256.0f );

                    wxASSERT( nr_sides_per_circle >= 24 );

                    SFVEC2F leftStart   = roundSeg.GetLeftStar();
                    SFVEC2F leftEnd     = roundSeg.GetLeftEnd();
                    SFVEC2F leftDir     = roundSeg.GetLeftDir();

                    SFVEC2F rightStart  = roundSeg.GetRightStar();
                    SFVEC2F rightEnd    = roundSeg.GetRightEnd();
                    SFVEC2F rightDir    = roundSeg.GetRightDir();
                    float   radius      = roundSeg.GetRadius();

                    SFVEC2F start       = roundSeg.GetStart();
                    SFVEC2F end         = roundSeg.GetEnd();

                    float texture_factor = (12.0f/(float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f;
                    float texture_factorF= ( 4.0f/(float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f;

                    const float radius_of_the_square = sqrtf( roundSeg.GetRadiusSquared() * 2.0f );
                    const float radius_triangle_factor = (radius_of_the_square - radius) / radius;

                    const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor, rightDir.x * radius * radius_triangle_factor );
                    const SFVEC2F factorE = SFVEC2F( -leftDir.y  * radius * radius_triangle_factor, leftDir.x  * radius * radius_triangle_factor );

                    // Top end segment triangles
                    layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( rightEnd.x   + texture_factor * factorS.x, rightEnd.y  + texture_factor * factorS.y, layer_z_top ),
                                                                           SFVEC3F( leftStart.x  + texture_factor * factorE.x, leftStart.y + texture_factor * factorE.y, layer_z_top ),
                                                                           SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf(2.0f),
                                                                                    start.y - texture_factorF * leftDir.y * radius * sqrtf(2.0f), layer_z_top ) );

                    layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( leftEnd.x    + texture_factor * factorE.x, leftEnd.y    + texture_factor * factorE.y, layer_z_top ),
                                                                           SFVEC3F( rightStart.x + texture_factor * factorS.x, rightStart.y + texture_factor * factorS.y, layer_z_top ),
                                                                           SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf(2.0f),
                                                                                    end.y - texture_factorF * rightDir.y * radius * sqrtf(2.0f), layer_z_top ) );

                    // Bot end segment triangles
                    layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( leftStart.x  + texture_factor * factorE.x, leftStart.y + texture_factor * factorE.y, layer_z_bot ),
                                                                           SFVEC3F( rightEnd.x   + texture_factor * factorS.x, rightEnd.y  + texture_factor * factorS.y, layer_z_bot ),
                                                                           SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf(2.0f),
                                                                                    start.y - texture_factorF * leftDir.y * radius * sqrtf(2.0f), layer_z_bot ) );

                    layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( rightStart.x + texture_factor * factorS.x, rightStart.y + texture_factor * factorS.y, layer_z_bot ),
                                                                           SFVEC3F( leftEnd.x    + texture_factor * factorE.x, leftEnd.y    + texture_factor * factorE.y, layer_z_bot ),
                                                                           SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf(2.0f),
                                                                                    end.y - texture_factorF * rightDir.y * radius * sqrtf(2.0f), layer_z_bot ) );

                    // Segment top and bot planes
                    layerTriangles->m_layer_top_triangles->AddQuad( SFVEC3F( rightEnd.x,   rightEnd.y,   layer_z_top ),
                                                                    SFVEC3F( rightStart.x, rightStart.y, layer_z_top ),
                                                                    SFVEC3F( leftEnd.x,    leftEnd.y,    layer_z_top ),
                                                                    SFVEC3F( leftStart.x,  leftStart.y,  layer_z_top ) );

                    layerTriangles->m_layer_bot_triangles->AddQuad( SFVEC3F( rightEnd.x,   rightEnd.y,   layer_z_bot ),
                                                                    SFVEC3F( leftStart.x,  leftStart.y,  layer_z_bot ),
                                                                    SFVEC3F( leftEnd.x,    leftEnd.y,    layer_z_bot ),
                                                                    SFVEC3F( rightStart.x, rightStart.y, layer_z_bot ) );

                    // Middle contourns (two sides of the segment)
                    layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( leftStart.x, leftStart.y, layer_z_top ),
                                                                             SFVEC3F( leftEnd.x,   leftEnd.y,   layer_z_top ),
                                                                             SFVEC3F( leftEnd.x,   leftEnd.y,   layer_z_bot ),
                                                                             SFVEC3F( leftStart.x, leftStart.y, layer_z_bot ) );
                    const SFVEC3F leftNormal = SFVEC3F( -leftDir.y, leftDir.x, 0.0f );
                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( leftNormal, leftNormal, leftNormal, leftNormal );


                    layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( rightStart.x, rightStart.y, layer_z_top ),
                                                                             SFVEC3F( rightEnd.x,   rightEnd.y,   layer_z_top ),
                                                                             SFVEC3F( rightEnd.x,   rightEnd.y,   layer_z_bot ),
                                                                             SFVEC3F( rightStart.x, rightStart.y, layer_z_bot ) );
                    const SFVEC3F rightNormal = SFVEC3F( -rightDir.y, rightDir.x, 0.0f );
                    layerTriangles->m_layer_middle_contourns_quads->AddNormal( rightNormal, rightNormal, rightNormal, rightNormal );


                    // Compute the outlines of the segment, and creates a polygon
                    // add right rounded end:

                    std::vector< SFVEC2F > roundedEndPointsStart;
                    std::vector< SFVEC2F > roundedEndPointsEnd;
                    roundedEndPointsStart.clear();
                    roundedEndPointsStart.reserve( nr_sides_per_circle + 2 );

                    roundedEndPointsEnd.clear();
                    roundedEndPointsEnd.reserve( nr_sides_per_circle + 2 );

                    roundedEndPointsStart.push_back( SFVEC2F( leftStart.x, leftStart.y ) );
                    roundedEndPointsEnd.push_back(   SFVEC2F( leftEnd.x,   leftEnd.y ) );

                    int    delta       = 3600 / nr_sides_per_circle;
                    for( int ii = delta; ii < 1800; ii += delta )
                    {
                        const SFVEC2F rotatedDirL = glm::rotate( leftDir,  (float)        ii  * 2.0f * 3.14f / 3600.0f );
                        const SFVEC2F rotatedDirR = glm::rotate( rightDir, (float)(1800 - ii) * 2.0f * 3.14f / 3600.0f );
                        roundedEndPointsStart.push_back( SFVEC2F( start.x - rotatedDirL.y * radius, start.y + rotatedDirL.x * radius ) );
                        roundedEndPointsEnd.push_back(   SFVEC2F( end.x   - rotatedDirR.y * radius, end.y   + rotatedDirR.x * radius ) );
                    }
                    roundedEndPointsStart.push_back( SFVEC2F( rightEnd.x, rightEnd.y ) );
                    roundedEndPointsEnd.push_back(   SFVEC2F( rightStart.x, rightStart.y ) );

                    if( roundedEndPointsStart.size() > 1 )
                    {
                        for( unsigned int i = 0; i < ( roundedEndPointsStart.size() - 1 ); ++i )
                        {
                            const SFVEC2F &v0 = roundedEndPointsStart[i + 0];
                            const SFVEC2F &v1 = roundedEndPointsStart[i + 1];

                            layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_bot ),
                                                                                     SFVEC3F( v1.x, v1.y, layer_z_bot ),
                                                                                     SFVEC3F( v1.x, v1.y, layer_z_top ),
                                                                                     SFVEC3F( v0.x, v0.y, layer_z_top ) );


                            const SFVEC2F n0 = glm::normalize( v0 - start );
                            const SFVEC2F n1 = glm::normalize( v1 - start );
                            const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f );
                            const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f );
                            layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );
                        }
                    }
                    roundedEndPointsStart.clear();

                    if( roundedEndPointsEnd.size() > 1 )
                    {
                        for( unsigned int i = 0; i < ( roundedEndPointsEnd.size() - 1 ); ++i )
                        {
                            const SFVEC2F &v0 = roundedEndPointsEnd[i + 0];
                            const SFVEC2F &v1 = roundedEndPointsEnd[i + 1];

                            layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_top ),
                                                                                     SFVEC3F( v1.x, v1.y, layer_z_top ),
                                                                                     SFVEC3F( v1.x, v1.y, layer_z_bot ),
                                                                                     SFVEC3F( v0.x, v0.y, layer_z_bot ) );


                            const SFVEC2F n0 = glm::normalize( v0 - end );
                            const SFVEC2F n1 = glm::normalize( v1 - end );
                            const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f );
                            const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f );
                            layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );
                        }
                    }
                    roundedEndPointsEnd.clear();
                }
                break;

                default:
                {
                }
                break;
            }
#if 0
            // not yet used / implemented (can be used in future to clip the objects in the board borders
            COBJECT2D *object2d_C = CSGITEM_FULL;

            std::vector<const COBJECT2D *> *object2d_B = CSGITEM_EMPTY;

            if( m_settings.GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) )
            {
                object2d_B = new std::vector<const COBJECT2D *>();

                // Check if there are any layerhole that intersects this object
                // Eg: a segment is cutted by a via hole or THT hole.
                // /////////////////////////////////////////////////////////////
                const MAP_CONTAINER_2D &layerHolesMap = m_settings.GetMapLayersHoles();
                if( layerHolesMap.find(layer_id) != layerHolesMap.end() )
                {
                    MAP_CONTAINER_2D::const_iterator ii_hole = layerHolesMap.find(layer_id);
                    const CBVHCONTAINER2D *containerLayerHoles2d = static_cast<const CBVHCONTAINER2D *>(ii_hole->second);


                    CONST_LIST_OBJECT2D intersectionList;
                    containerLayerHoles2d->GetListObjectsIntersects( object2d_A->GetBBox(), intersectionList );

                    if( !intersectionList.empty() )
                    {
                        for( CONST_LIST_OBJECT2D::const_iterator holeOnLayer = intersectionList.begin();
                             holeOnLayer != intersectionList.end();
                             holeOnLayer++ )
                        {
                            const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*holeOnLayer);

                            //if( object2d_A->Intersects( hole2d->GetBBox() ) )
                                //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) )
                                    object2d_B->push_back( hole2d );
                        }
                    }
                }

                // Check if there are any THT that intersects this object
                // /////////////////////////////////////////////////////////////
                if( !m_settings.GetThroughHole_Inflated().GetList().empty() )
                {
                    CONST_LIST_OBJECT2D intersectionList;
                    m_settings.GetThroughHole_Inflated().GetListObjectsIntersects( object2d_A->GetBBox(), intersectionList );

                    if( !intersectionList.empty() )
                    {
                        for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin();
                             hole != intersectionList.end();
                             hole++ )
                        {
                            const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*hole);

                            //if( object2d_A->Intersects( hole2d->GetBBox() ) )
                                //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) )
                                    object2d_B->push_back( hole2d );
                        }
                    }
                }

                if( object2d_B->empty() )
                {
                    delete object2d_B;
                    object2d_B = CSGITEM_EMPTY;
                }
            }

            if( (object2d_B == CSGITEM_EMPTY) &&
                (object2d_C == CSGITEM_FULL) )
            {
#if 0
               create_3d_object_from( m_object_container, object2d_A, m_settings.GetLayerBottomZpos3DU( layer_id ),
                                                                       m_settings.GetLayerTopZpos3DU( layer_id ),
                                       materialLayer,
                                       layerColor );
#else
                CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, m_settings.GetLayerBottomZpos3DU( layer_id ),
                                                                 m_settings.GetLayerTopZpos3DU( layer_id ) );
                objPtr->SetMaterial( materialLayer );
                objPtr->SetColor( layerColor );
                m_object_container.Add( objPtr );
#endif
            }
            else
            {
#if 1
                CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A, object2d_B, object2d_C,
                                                                  object2d_A->GetBoardItem() );
                m_containerWithObjectsToDelete.Add( itemCSG2d );

                CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, m_settings.GetLayerBottomZpos3DU( layer_id ),
                                                     m_settings.GetLayerTopZpos3DU( layer_id ) );

                objPtr->SetMaterial( materialLayer );
                objPtr->SetColor( layerColor );
                m_object_container.Add( objPtr );
#endif
            }
#endif
        }

        // Create display list
        // /////////////////////////////////////////////////////////////////////
        m_ogl_disp_lists_layers[layer_id] = new CLAYERS_OGL_DISP_LISTS( *layerTriangles,
                                                                        m_ogl_circle_texture,
                                                                        layerColor );
    }// for each layer on map

}
/* Plot outlines of copper, for copper layer
 */
void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
                        LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
{

    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
    itemplotter.SetLayerSet( aLayerMask );

    SHAPE_POLY_SET outlines;

    for( LSEQ seq = aLayerMask.Seq( plot_seq, DIM( plot_seq ) );  seq;  ++seq )
    {
        LAYER_ID layer = *seq;

        outlines.RemoveAllContours();
        aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines );

        outlines.Simplify();

        // Plot outlines
        std::vector< wxPoint > cornerList;

        // Now we have one or more basic polygons: plot each polygon
        for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
        {
            for(int kk = 0; kk <= outlines.HoleCount (ii); kk++ )
            {
                cornerList.clear();
                const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 );

                for( int jj = 0; jj < path.PointCount(); jj++ )
                    cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) );


                // Ensure the polygon is closed
                if( cornerList[0] != cornerList[cornerList.size() - 1] )
                    cornerList.push_back( cornerList[0] );

                aPlotter->PlotPoly( cornerList, NO_FILL );
            }
        }

        // Plot pad holes
        if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
        {
            for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
            {
                for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
                {
                    wxSize hole = pad->GetDrillSize();

                    if( hole.x == 0 || hole.y == 0 )
                        continue;

                    if( hole.x == hole.y )
                        aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL );
                    else
                    {
                        wxPoint drl_start, drl_end;
                        int width;
                        pad->GetOblongDrillGeometry( drl_start, drl_end, width );
                        aPlotter->ThickSegment( pad->GetPosition() + drl_start,
                                                pad->GetPosition() + drl_end, width, SKETCH );
                    }
                }
            }
        }

        // Plot vias holes
        for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
        {
            const VIA* via = dyn_cast<const VIA*>( track );

            if( via && via->IsOnLayer( layer ) )    // via holes can be not through holes
            {
                aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL );
            }
        }
    }
}