/**
 * Function NormalizeAreaOutlines
 * Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
 * @param aNewPolygonList = a std::vector<CPolyLine*> reference where to store new CPolyLine
 * needed by the normalization
 * @return the polygon count (always >= 1, because there is at least one polygon)
 * There are new polygons only if the polygon count  is > 1
 */
int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
{

    SHAPE_POLY_SET polySet = ConvertPolyListToPolySet( m_CornersList );

    // We are expecting only one main outline, but this main outline can have holes
    // if holes: combine holes and remove them from the main outline.
    // Note also we are using SHAPE_POLY_SET::PM_STRICTLY_SIMPLE in polygon
    // calculations, but it is not mandatory. It is used mainly
    // because there is usually only very few vertices in area outlines
    SHAPE_POLY_SET::POLYGON& outline = polySet.Polygon( 0 );
    SHAPE_POLY_SET holesBuffer;

    // Move holes stored in outline to holesBuffer:
    // The first SHAPE_LINE_CHAIN is the main outline, others are holes
    while( outline.size() > 1 )
    {
        holesBuffer.AddOutline( outline.back() );
        outline.pop_back();
    }

    polySet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE);

    // If any hole, substract it to main outline
    if( holesBuffer.OutlineCount() )
    {
        holesBuffer.Simplify( SHAPE_POLY_SET::PM_FAST);
        polySet.BooleanSubtract( holesBuffer, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
    }

    RemoveAllContours();

    // Note: we can have more than outline, because a self intersecting outline will be
    // broken to non intersecting polygons, and removing holes can also create a few polygons
    for( int ii = 0; ii < polySet.OutlineCount(); ii++ )
    {
        CPolyLine* polyline = this;

        if( ii > 0 )
        {
            polyline = new CPolyLine;
            polyline->ImportSettings( this );
            aNewPolygonList->push_back( polyline );
        }

        SHAPE_POLY_SET pnew;
        pnew.NewOutline();
        pnew.Polygon( 0 ) = polySet.CPolygon( ii );

        polyline->m_CornersList = ConvertPolySetToPolyList( pnew );
    }

    return polySet.OutlineCount();
}
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 );
        }
    }
}
void Polygon_Calc_BBox_3DU( const SHAPE_POLY_SET &aPolysList,
                            CBBOX2D &aOutBBox ,
                            float aBiuTo3DunitsScale )
{
    aOutBBox.Reset();

    for( int idx = 0; idx < aPolysList.OutlineCount(); ++idx )
    {
        // Each polygon in aPolysList is a polygon with holes
        const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aPolysList.CPolygon( idx );

        for( unsigned ipoly = 0; ipoly < curr_polywithholes.size(); ++ipoly )
        {
            const SHAPE_LINE_CHAIN& path = curr_polywithholes[ipoly]; // a simple polygon

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

                 aOutBBox.Union( SFVEC2F( (float) a.x * aBiuTo3DunitsScale,
                                          (float)-a.y * aBiuTo3DunitsScale ) );
             }
        }
    }

    aOutBBox.ScaleNextUp();
}
/* This function is used to extract a board outlines (3D view, automatic zones build ...)
 * Any closed outline inside the main outline is a hole
 * All contours should be closed, i.e. valid closed polygon vertices
 */
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
        wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation )
{
    PCB_TYPE_COLLECTOR  items;

    // Get all the DRAWSEGMENTS and module graphics into 'items',
    // then keep only those on layer == Edge_Cuts.
    static const KICAD_T  scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT };
    items.Collect( aBoard, scan_graphics );

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

    for( int ii = 0; ii < items.GetCount(); ii++ )
    {
        if( items[ii]->GetLayer() == Edge_Cuts )
            segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) );
    }

    bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance, aErrorLocation );

    if( !success || !aOutlines.OutlineCount() )
    {
        // Creates a valid polygon outline is not possible.
        // So uses the board edge cuts bounding box to create a
        // rectangular outline
        // When no edge cuts items, build a contour
        // from global bounding box

        EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();

        // If null area, uses the global bounding box.
        if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
            bbbox = aBoard->ComputeBoundingBox();

        // Ensure non null area. If happen, gives a minimal size.
        if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
            bbbox.Inflate( Millimeter2iu( 1.0 ) );

        aOutlines.RemoveAllContours();
        aOutlines.NewOutline();

        wxPoint corner;
        aOutlines.Append( bbbox.GetOrigin() );

        corner.x = bbbox.GetOrigin().x;
        corner.y = bbbox.GetEnd().y;
        aOutlines.Append( corner );

        aOutlines.Append( bbbox.GetEnd() );

        corner.x = bbbox.GetEnd().x;
        corner.y = bbbox.GetOrigin().y;
        aOutlines.Append( corner );
    }

    return success;
}
/*
 * Function DrawApertureMacroShape
 * Draw the primitive shape for flashed items.
 * When an item is flashed, this is the shape of the item
 */
void APERTURE_MACRO::DrawApertureMacroShape( GERBER_DRAW_ITEM* aParent,
                                             EDA_RECT* aClipBox, wxDC* aDC,
                                             COLOR4D aColor,
                                             wxPoint aShapePos, bool aFilledShape )
{
    SHAPE_POLY_SET* shapeBuffer = GetApertureMacroShape( aParent, aShapePos );

    if( shapeBuffer->OutlineCount() == 0 )
        return;

    for( int ii = 0; ii < shapeBuffer->OutlineCount(); ii++ )
    {
        SHAPE_LINE_CHAIN& poly = shapeBuffer->Outline( ii );

        GRClosedPoly( aClipBox, aDC,
                      poly.PointCount(), (wxPoint*)&poly.Point( 0 ), aFilledShape, aColor, aColor );
    }
}
void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
                                     SHAPE_POLY_SET* aPolygons,
                                     EDA_DRAW_MODE_T aTraceMode, void* aData )

{
    // A Pad custom is plotted as polygon.

    // A flashed circle @aPadPos is added (anchor pad)
    // However, because the anchor pad can be circle or rect, we use only
    // a circle not bigger than the rect.
    // the main purpose is to print a flashed DCode as pad anchor
    if( aTraceMode == FILLED )
        FlashPadCircle( aPadPos, std::min( aSize.x, aSize.y ), aTraceMode, aData );

    GBR_METADATA gbr_metadata;

    if( aData )
    {
        gbr_metadata = *static_cast<GBR_METADATA*>( aData );
        // If the pad is drawn on a copper layer,
        // set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR
        if( gbr_metadata.IsCopper() )
            gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );

        wxString attrname( ".P" );
        gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname );   // not allowed on inner layers
    }

    SHAPE_POLY_SET polyshape = *aPolygons;

    if( aTraceMode != FILLED )
    {
        SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
        polyshape.Inflate( -GetCurrentLineWidth()/2, 16 );
    }

    std::vector< wxPoint > cornerList;

    for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
    {
        SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );

        cornerList.clear();

        for( int ii = 0; ii < poly.PointCount(); ++ii )
            cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) );

        // Close polygon
        cornerList.push_back( cornerList[0] );

        PlotPoly( cornerList,
                  aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL,
                  aTraceMode == FILLED ? 0 : GetCurrentLineWidth(), &gbr_metadata );
    }
}
bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_ref,
                          ZONE_CONTAINER* area_to_combine )
{
    if( area_ref == area_to_combine )
    {
        wxASSERT( 0 );
        return false;
    }

    SHAPE_POLY_SET mergedOutlines = *area_ref->Outline();
    SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline();

    mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST  );
    mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );

    // We should have one polygon with hole
    // We can have 2 polygons with hole, if the 2 initial polygons have only one common corner
    // and therefore cannot be merged (they are dectected as intersecting)
    // but we should never have more than 2 polys
    if( mergedOutlines.OutlineCount() > 2 )
    {
        wxLogMessage(wxT("BOARD::CombineAreas error: more than 2 polys after merging") );
        return false;
    }

    if( mergedOutlines.OutlineCount() > 1 )
        return false;

    // Update the area with the new merged outline
    delete area_ref->Outline();
    area_ref->SetOutline( new SHAPE_POLY_SET( mergedOutlines ) );

    RemoveArea( aDeletedList, area_to_combine );

    area_ref->SetLocalFlags( 1 );
    area_ref->Hatch();

    return true;
}
SHAPE_POLY_SET* APERTURE_MACRO::GetApertureMacroShape( const GERBER_DRAW_ITEM* aParent,
                                                       wxPoint aShapePos )
{
    SHAPE_POLY_SET holeBuffer;
    bool hasHole = false;

    m_shape.RemoveAllContours();

    for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
         prim_macro != primitives.end(); ++prim_macro )
    {
        if( prim_macro->primitive_id == AMP_COMMENT )
            continue;

        if( prim_macro->IsAMPrimitiveExposureOn( aParent ) )
            prim_macro->DrawBasicShape( aParent, m_shape, aShapePos );
        else
        {
            prim_macro->DrawBasicShape( aParent, holeBuffer, aShapePos );

            if( holeBuffer.OutlineCount() )     // we have a new hole in shape: remove the hole
            {
                m_shape.BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST );
                holeBuffer.RemoveAllContours();
                hasHole = true;
            }
        }
    }

    // If a hole is defined inside a polygon, we must fracture the polygon
    // to be able to drawn it (i.e link holes by overlapping edges)
    if( hasHole )
        m_shape.Fracture( SHAPE_POLY_SET::PM_FAST );

    m_boundingBox = EDA_RECT( wxPoint( 0, 0 ), wxSize( 1, 1 ) );
    auto bb = m_shape.BBox();
    wxPoint center( bb.Centre().x, bb.Centre().y );
    m_boundingBox.Move( aParent->GetABPosition( center ) );
    m_boundingBox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );

    return &m_shape;
}
void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
                                      REPORTER* aErrorMessages, REPORTER* aActivity  )
{
    BOARD* pcb = GetBoard();

    // If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas.
    // If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas,
    // but the calculation time is twice shorter.
    bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES );

    bool realistic_mode = isRealisticMode();
    bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES );

    // Number of segments to convert a circle to polygon
    // We use 2 values: the first gives a good shape (for instanes rond pads)
    // the second is used to speed up calculations, when a poor approximation is acceptable (holes)
    const int       segcountforcircle   = 18;
    double          correctionFactor    = 1.0 / cos( M_PI / (segcountforcircle * 2.0) );
    const int       segcountLowQuality  = 12;   // segments to draw a circle with low quality
                                                // to reduce time calculations
                                                // for holes and items which do not need
                                                // a fine representation
    double          correctionFactorLQ  = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) );

    SHAPE_POLY_SET  bufferPolys;        // copper areas: tracks, pads and filled zones areas
                                        // when holes are removed from zones
    SHAPE_POLY_SET  bufferPcbOutlines;  // stores the board main outlines
    SHAPE_POLY_SET  bufferZonesPolys;   // copper filled zones areas
                                        // when holes are not removed from zones
    SHAPE_POLY_SET  currLayerHoles;     // Contains holes for the current layer
    SHAPE_POLY_SET  allLayerHoles;      // Contains holes for all layers

    // Build a polygon from edge cut items
    wxString msg;

    if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) )
    {
        if( aErrorMessages )
        {
            msg << wxT("\n") << _("Unable to calculate the board outlines.\n"
                                  "Therefore use the board boundary box.") << wxT("\n\n");

            aErrorMessages->Report( msg, REPORTER::RPT_WARNING );
        }
    }

    // Build board holes, with optimization of large holes shape.
    buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true );

    LSET            cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount );

    glNewList( aBoardList, GL_COMPILE );

    for( LSEQ cu = cu_set.CuStack();  cu;  ++cu )
    {
        LAYER_ID layer = *cu;

        // Skip non enabled layers in normal mode,
        // and internal layers in realistic mode
        if( !is3DLayerEnabled( layer ) )
            continue;

        if( aActivity )
            aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) );

        bufferPolys.RemoveAllContours();
        bufferZonesPolys.RemoveAllContours();
        currLayerHoles.RemoveAllContours();

        // Draw track shapes:
        for( TRACK* track = pcb->m_Track;  track;  track = track->Next() )
        {
            if( !track->IsOnLayer( layer ) )
                continue;

            track->TransformShapeWithClearanceToPolygon( bufferPolys,
                                                         0, segcountforcircle,
                                                         correctionFactor );

            // Add blind/buried via holes
            if( track->Type() == PCB_VIA_T )
            {
                VIA *via = static_cast<VIA*>( track );

                if( via->GetViaType() == VIA_THROUGH )
                    continue;   // already done

                int holediameter = via->GetDrillValue();
                int thickness = GetPrm3DVisu().GetCopperThicknessBIU();
                int hole_outer_radius = (holediameter + thickness) / 2;

                TransformCircleToPolygon( currLayerHoles,
                                          via->GetStart(), hole_outer_radius,
                                          segcountLowQuality );
            }
        }

        // draw pad shapes
        for( MODULE* module = pcb->m_Modules;  module;  module = module->Next() )
        {
            // Note: NPTH pads are not drawn on copper layers when the pad
            // has same shape as its hole
            module->TransformPadsShapesWithClearanceToPolygon( layer,
                                                               bufferPolys,
                                                               0,
                                                               segcountforcircle,
                                                               correctionFactor, true );

            // Micro-wave modules may have items on copper layers
            module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
                                                                     bufferPolys,
                                                                     0,
                                                                     segcountforcircle,
                                                                     correctionFactor );

            // pad holes are already in list.
        }

        // Draw copper zones. Note:
        // * if the holes are removed from copper zones
        // the polygons are stored in bufferPolys (which contains all other polygons)
        // * if the holes are NOT removed from copper zones
        // the polygons are stored in bufferZonesPolys
        if( isEnabled( FL_ZONE ) )
        {
            for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
            {
                ZONE_CONTAINER* zone = pcb->GetArea( ii );
                LAYER_NUM       zonelayer = zone->GetLayer();

                if( zonelayer == layer )
                {
                    zone->TransformSolidAreasShapesToPolygonSet(
                        remove_Holes ? bufferPolys : bufferZonesPolys,
                        segcountLowQuality, correctionFactorLQ );
                }
            }
        }

        // draw graphic items on copper layers (texts)
        for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
        {
            if( !item->IsOnLayer( layer ) )
                continue;

            switch( item->Type() )
            {
            case PCB_LINE_T:    // should not exist on copper layers
                ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
                    bufferPolys, 0, segcountforcircle, correctionFactor );
                break;

            case PCB_TEXT_T:
                ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
                    bufferPolys, 0, segcountLowQuality, correctionFactor );
                break;

            default:
                break;
            }
        }

        // bufferPolys contains polygons to merge. Many overlaps .
        // Calculate merged polygons
        if( bufferPolys.IsEmpty() )
            continue;

        // Use Clipper lib to subtract holes to copper areas
        if( currLayerHoles.OutlineCount() )
        {
            currLayerHoles.Append(allLayerHoles);
            currLayerHoles.Simplify();
            bufferPolys.BooleanSubtract( currLayerHoles );
        }
        else
            bufferPolys.BooleanSubtract( allLayerHoles );

        int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
        int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );

        float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)

        // If we are not using thickness, then the z-normal has to match the layer direction
        // because just one plane will be drawn
        if( !thickness )
            zNormal = Get3DLayer_Z_Orientation( layer );

        if( realistic_mode )
        {
            setGLCopperColor();
        }
        else
        {
            EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer );
            SetGLColor( color );
        }

        // If holes are removed from copper zones, bufferPolys contains all polygons
        // to draw (tracks+zones+texts).
        Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness,
                                            GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                            zNormal );

        // If holes are not removed from copper zones (for calculation time reasons,
        // the zone polygons are stored in bufferZonesPolys and have to be drawn now:
        if( !bufferZonesPolys.IsEmpty() )
        {
            Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness,
                                    GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                    zNormal );
        }
    }

    if( aActivity )
        aActivity->Report( _( "Build board body" ) );

    // Draw plated vertical holes inside the board, but not always. They are drawn:
    // - if the board body is not shown, to show the holes.
    // - or if the copper thickness is shown
    if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) )
    {
        // Draw vias holes (vertical cylinders)
        for( const TRACK* track = pcb->m_Track;  track;  track = track->Next() )
        {
            if( track->Type() == PCB_VIA_T )
            {
                const VIA *via = static_cast<const VIA*>(track);
                draw3DViaHole( via );
            }
        }

        // Draw pads holes (vertical cylinders)
        for( const MODULE* module = pcb->m_Modules;  module;  module = module->Next() )
        {
            for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
                if( pad->GetAttribute () != PAD_HOLE_NOT_PLATED )
                    draw3DPadHole( pad );
        }
    }

    glEndList();

    // Build the body board:
    glNewList( aBodyOnlyList, GL_COMPILE );

    if( isRealisticMode() )
    {
        setGLEpoxyColor( 1.00 );
    }
    else
    {
        EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts );
        SetGLColor( color, 0.7 );
    }

    float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU();

    // a small offset between substrate and external copper layer to avoid artifacts
    // when drawing copper items on board
    float epsilon = Millimeter2iu( 0.01 );
    float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
    float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu )
                        - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );

    // items on copper layers and having a thickness = copper_thickness
    // are drawn from zpos - copper_thickness/2 to zpos + copper_thickness
    // therefore substrate position is copper_thickness/2 to
    // substrate_height - copper_thickness/2
    zpos += (copper_thickness + epsilon) / 2.0f;
    board_thickness -= copper_thickness + epsilon;

    bufferPcbOutlines.BooleanSubtract( allLayerHoles );

    if( !bufferPcbOutlines.IsEmpty() )
    {
        Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0,
                                            board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                            1.0f );
    }

    glEndList();
}
/* 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;
}
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] );
        }
    }
}
Exemple #12
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;
}
/* 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 );
            }
        }
    }
}
/**
 * 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();
}
bool DRC_COURTYARD_OVERLAP::RunDRC( BOARD& aBoard ) const
{
    wxLogTrace( DRC_COURTYARD_TRACE, "Running DRC: Courtyard" );

    // Detects missing (or malformed) footprint courtyard,
    // and for footprint with courtyard, courtyards overlap.
    wxString msg;
    bool     success = true;

    const DRC_MARKER_FACTORY& marker_factory = GetMarkerFactory();

    // Update courtyard polygons, and test for missing courtyard definition:
    for( MODULE* footprint = aBoard.m_Modules; footprint; footprint = footprint->Next() )
    {
        wxPoint pos = footprint->GetPosition();
        bool    is_ok = footprint->BuildPolyCourtyard();

        if( !is_ok && aBoard.GetDesignSettings().m_ProhibitOverlappingCourtyards )
        {
            auto marker = std::unique_ptr<MARKER_PCB>( marker_factory.NewMarker(
                    pos, footprint, DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT ) );
            HandleMarker( std::move( marker ) );
            success = false;
        }

        if( !aBoard.GetDesignSettings().m_RequireCourtyards )
            continue;

        if( footprint->GetPolyCourtyardFront().OutlineCount() == 0
                && footprint->GetPolyCourtyardBack().OutlineCount() == 0 && is_ok )
        {
            auto marker = std::unique_ptr<MARKER_PCB>( marker_factory.NewMarker(
                    pos, footprint, DRCE_MISSING_COURTYARD_IN_FOOTPRINT ) );
            HandleMarker( std::move( marker ) );
            success = false;
        }
    }

    if( !aBoard.GetDesignSettings().m_ProhibitOverlappingCourtyards )
        return success;

    wxLogTrace( DRC_COURTYARD_TRACE, "Checking for courtyard overlap" );

    // Now test for overlapping on top layer:
    SHAPE_POLY_SET courtyard; // temporary storage of the courtyard of current footprint

    for( MODULE* footprint = aBoard.m_Modules; footprint; footprint = footprint->Next() )
    {
        if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 )
            continue; // No courtyard defined

        for( MODULE* candidate = footprint->Next(); candidate; candidate = candidate->Next() )
        {
            if( candidate->GetPolyCourtyardFront().OutlineCount() == 0 )
                continue; // No courtyard defined

            courtyard.RemoveAllContours();
            courtyard.Append( footprint->GetPolyCourtyardFront() );

            // Build the common area between footprint and the candidate:
            courtyard.BooleanIntersection(
                    candidate->GetPolyCourtyardFront(), SHAPE_POLY_SET::PM_FAST );

            // If no overlap, courtyard is empty (no common area).
            // Therefore if a common polygon exists, this is a DRC error
            if( courtyard.OutlineCount() )
            {
                //Overlap between footprint and candidate
                VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
                auto      marker = std::unique_ptr<MARKER_PCB>(
                        marker_factory.NewMarker( wxPoint( pos.x, pos.y ), footprint, candidate,
                                DRCE_OVERLAPPING_FOOTPRINTS ) );
                HandleMarker( std::move( marker ) );
                success = false;
            }
        }
    }

    // Test for overlapping on bottom layer:
    for( MODULE* footprint = aBoard.m_Modules; footprint; footprint = footprint->Next() )
    {
        if( footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
            continue; // No courtyard defined

        for( MODULE* candidate = footprint->Next(); candidate; candidate = candidate->Next() )
        {
            if( candidate->GetPolyCourtyardBack().OutlineCount() == 0 )
                continue; // No courtyard defined

            courtyard.RemoveAllContours();
            courtyard.Append( footprint->GetPolyCourtyardBack() );

            // Build the common area between footprint and the candidate:
            courtyard.BooleanIntersection(
                    candidate->GetPolyCourtyardBack(), SHAPE_POLY_SET::PM_FAST );

            // If no overlap, courtyard is empty (no common area).
            // Therefore if a common polygon exists, this is a DRC error
            if( courtyard.OutlineCount() )
            {
                //Overlap between footprint and candidate
                VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
                auto      marker = std::unique_ptr<MARKER_PCB>(
                        marker_factory.NewMarker( wxPoint( pos.x, pos.y ), footprint, candidate,
                                DRCE_OVERLAPPING_FOOTPRINTS ) );
                HandleMarker( std::move( marker ) );
                success = false;
            }
        }
    }

    return success;
}
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

}
void Convert_path_polygon_to_polygon_blocks_and_dummy_blocks(
        const SHAPE_POLY_SET &aMainPath,
        CGENERICCONTAINER2D &aDstContainer,
        float aBiuTo3DunitsScale,
        float aDivFactor,
        const BOARD_ITEM &aBoardItem )
{
    BOX2I pathBounds = aMainPath.BBox();

    // Get the path

    wxASSERT( aMainPath.OutlineCount() == 1 );
    const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aMainPath.CPolygon( 0 );

    wxASSERT( curr_polywithholes.size() == 1 );
    const SHAPE_LINE_CHAIN& path = curr_polywithholes[0];   // a simple polygon

    // Convert the points to segments class
    CBBOX2D bbox;
    bbox.Reset();

    // Contains the main list of segments and each segment normal interpolated
    SEGMENTS_WIDTH_NORMALS segments_and_normals;

    // Contains a closed polygon used to calc if points are inside
    SEGMENTS segments;

    segments_and_normals.resize( path.PointCount() );
    segments.resize( path.PointCount() );

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

        const SFVEC2F point ( (float)( a.x) * aBiuTo3DunitsScale,
                              (float)(-a.y) * aBiuTo3DunitsScale );

        bbox.Union( point );
        segments_and_normals[i].m_Start = point;
        segments[i].m_Start = point;
    }

    bbox.ScaleNextUp();


    // Calc the slopes, normals and some statistics about this polygon
    unsigned int i;
    unsigned int j = segments_and_normals.size() - 1;

    // Temporary normal to the segment, it will later be used for interpolation
    std::vector< SFVEC2F >  tmpSegmentNormals;
    tmpSegmentNormals.resize( segments_and_normals.size() );

    float medOfTheSquaresSegmentLength = 0.0f;
#ifdef PRINT_STATISTICS_3D_VIEWER
    float minLength = FLT_MAX;
#endif

    for( i = 0; i < segments_and_normals.size(); j = i++ )
    {
        const SFVEC2F slope = segments_and_normals[j].m_Start -
                              segments_and_normals[i].m_Start;

        segments_and_normals[i].m_Precalc_slope = slope;

        // Calculate constants for each segment
        segments[i].m_inv_JY_minus_IY = 1.0f / ( segments_and_normals[j].m_Start.y -
                                                 segments_and_normals[i].m_Start.y );

        segments[i].m_JX_minus_IX = ( segments_and_normals[j].m_Start.x -
                                      segments_and_normals[i].m_Start.x );

        // The normal orientation expect a fixed polygon orientation (!TODO: which one?)
        //tmpSegmentNormals[i] = glm::normalize( SFVEC2F( -slope.y, +slope.x ) );
        tmpSegmentNormals[i] = glm::normalize( SFVEC2F( slope.y, -slope.x ) );

        const float length = slope.x * slope.x + slope.y * slope.y;

#ifdef PRINT_STATISTICS_3D_VIEWER
        if( length < minLength )
            minLength = length;
#endif

        medOfTheSquaresSegmentLength += length;
    }

#ifdef PRINT_STATISTICS_3D_VIEWER
    float minSegmentLength = sqrt( minLength );
#endif

    // This calc an approximation of medium lengths, that will be used to calc
    // the size of the division.
    medOfTheSquaresSegmentLength /= segments_and_normals.size();
    medOfTheSquaresSegmentLength = sqrt( medOfTheSquaresSegmentLength );


    // Compute the normal interpolation
    // If calculate the dot between the segments, if they are above/below some
    // threshould it will not interpolated it (ex: if you are in a edge corner
    // or in a smooth transaction)
    j = segments_and_normals.size() - 1;
    for( i = 0; i < segments_and_normals.size(); j = i++ )
    {
        const SFVEC2F normalBeforeSeg = tmpSegmentNormals[j];
        const SFVEC2F normalSeg       = tmpSegmentNormals[i];
        const SFVEC2F normalAfterSeg  = tmpSegmentNormals[ (i + 1) %
                                                           segments_and_normals.size() ];

        const float dotBefore = glm::dot( normalBeforeSeg, normalSeg );
        const float dotAfter  = glm::dot( normalAfterSeg,  normalSeg );

        if( dotBefore < 0.7f )
            segments_and_normals[i].m_Normals.m_Start = normalSeg;
        else
            segments_and_normals[i].m_Normals.m_Start =
                glm::normalize( (((normalBeforeSeg * dotBefore ) + normalSeg) * 0.5f) );

        if( dotAfter < 0.7f )
            segments_and_normals[i].m_Normals.m_End = normalSeg;
        else
            segments_and_normals[i].m_Normals.m_End =
                glm::normalize( (((normalAfterSeg  * dotAfter  ) + normalSeg) * 0.5f) );
    }

    if( aDivFactor == 0.0f )
        aDivFactor = medOfTheSquaresSegmentLength;

    SFVEC2UI grid_divisions;
    grid_divisions.x = (unsigned int)( (bbox.GetExtent().x / aDivFactor) );
    grid_divisions.y = (unsigned int)( (bbox.GetExtent().y / aDivFactor) );

    grid_divisions = glm::clamp( grid_divisions ,
                                 SFVEC2UI( 1, 1 ),
                                 SFVEC2UI( MAX_NR_DIVISIONS, MAX_NR_DIVISIONS ) );

    // Calculate the steps advance of the grid
    SFVEC2F blockAdvance;

    blockAdvance.x = bbox.GetExtent().x / (float)grid_divisions.x;
    blockAdvance.y = bbox.GetExtent().y / (float)grid_divisions.y;

    wxASSERT( blockAdvance.x > 0.0f );
    wxASSERT( blockAdvance.y > 0.0f );

    const int leftToRight_inc = (pathBounds.GetRight()  - pathBounds.GetLeft()) /
                                grid_divisions.x;

    const int topToBottom_inc = (pathBounds.GetBottom() - pathBounds.GetTop())  /
                                grid_divisions.y;

    // Statistics
    unsigned int stats_n_empty_blocks = 0;
    unsigned int stats_n_dummy_blocks = 0;
    unsigned int stats_n_poly_blocks = 0;
    unsigned int stats_sum_size_of_polygons = 0;


    // Step by each block of a grid trying to extract segments and create
    // polygon blocks

    int topToBottom = pathBounds.GetTop();
    float blockY = bbox.Max().y;

    for( unsigned int iy = 0; iy < grid_divisions.y; iy++ )
    {

        int leftToRight = pathBounds.GetLeft();
        float blockX = bbox.Min().x;

        for( unsigned int ix = 0; ix < grid_divisions.x; ix++ )
        {
            CBBOX2D blockBox( SFVEC2F( blockX,
                                       blockY - blockAdvance.y ),
                              SFVEC2F( blockX + blockAdvance.x,
                                       blockY                  ) );

            // Make the box large to it will catch (intersect) the edges
            blockBox.ScaleNextUp();
            blockBox.ScaleNextUp();
            blockBox.ScaleNextUp();

            SEGMENTS_WIDTH_NORMALS extractedSegments;

            extractPathsFrom( segments_and_normals, blockBox, extractedSegments );


            if( extractedSegments.empty() )
            {

                SFVEC2F p1( blockBox.Min().x, blockBox.Min().y );
                SFVEC2F p2( blockBox.Max().x, blockBox.Min().y );
                SFVEC2F p3( blockBox.Max().x, blockBox.Max().y );
                SFVEC2F p4( blockBox.Min().x, blockBox.Max().y );

                if( polygon_IsPointInside( segments, p1 ) ||
                    polygon_IsPointInside( segments, p2 ) ||
                    polygon_IsPointInside( segments, p3 ) ||
                    polygon_IsPointInside( segments, p4 ) )
                {
                    // In this case, the segments are not intersecting the
                    // polygon, so it means that if any point is inside it,
                    // then all other are inside the polygon.
                    // This is a full bbox inside, so add a dummy box

                    aDstContainer.Add( new CDUMMYBLOCK2D( blockBox, aBoardItem ) );
                    stats_n_dummy_blocks++;
                }
                else
                {
                    // Points are outside, so this block complety missed the polygon
                    // In this case, no objects need to be added
                    stats_n_empty_blocks++;
                }
            }
            else
            {
                // At this point, the borders of polygon were intersected by the
                // bounding box, so we must calculate a new polygon that will
                // close that small block.
                // This block will be used to calculate if points are inside
                // the (sub block) polygon.

                SHAPE_POLY_SET subBlockPoly;

                SHAPE_LINE_CHAIN sb = SHAPE_LINE_CHAIN(
                                        VECTOR2I( leftToRight,
                                                  topToBottom ),
                                        VECTOR2I( leftToRight + leftToRight_inc,
                                                  topToBottom ),
                                        VECTOR2I( leftToRight + leftToRight_inc,
                                                  topToBottom + topToBottom_inc ),
                                        VECTOR2I( leftToRight,
                                                  topToBottom + topToBottom_inc ) );

                //sb.Append( leftToRight, topToBottom );
                sb.SetClosed( true );

                subBlockPoly.AddOutline( sb );

                // We need here a strictly simple polygon with outlines and holes
                SHAPE_POLY_SET solution;
                solution.BooleanIntersection( aMainPath,
                                              subBlockPoly,
                                              SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );

                OUTERS_AND_HOLES outersAndHoles;

                outersAndHoles.m_Holes.clear();
                outersAndHoles.m_Outers.clear();

                for( int idx = 0; idx < solution.OutlineCount(); idx++ )
                {
                    const SHAPE_LINE_CHAIN & outline = solution.Outline( idx );

                    SEGMENTS solutionSegment;

                    polygon_Convert( outline, solutionSegment, aBiuTo3DunitsScale );
                    outersAndHoles.m_Outers.push_back( solutionSegment );

                    stats_sum_size_of_polygons += solutionSegment.size();

                    for( int holeIdx = 0;
                         holeIdx < solution.HoleCount( idx );
                         holeIdx++ )
                    {
                        const SHAPE_LINE_CHAIN & hole = solution.Hole( idx, holeIdx );

                        polygon_Convert( hole, solutionSegment, aBiuTo3DunitsScale );
                        outersAndHoles.m_Holes.push_back( solutionSegment );
                        stats_sum_size_of_polygons += solutionSegment.size();
                    }

                }

                if( !outersAndHoles.m_Outers.empty() )
                {
                    aDstContainer.Add( new CPOLYGONBLOCK2D( extractedSegments,
                                                            outersAndHoles,
                                                            aBoardItem ) );
                    stats_n_poly_blocks++;
                }
            }

            blockX += blockAdvance.x;
            leftToRight += leftToRight_inc;
        }

        blockY -= blockAdvance.y;
        topToBottom += topToBottom_inc;
    }

#ifdef PRINT_STATISTICS_3D_VIEWER
    printf( "////////////////////////////////////////////////////////////////////////////////\n" );
    printf( "Convert_path_polygon_to_polygon_blocks_and_dummy_blocks\n" );
    printf( "  grid_divisions (%u, %u)\n", grid_divisions.x, grid_divisions.y );
    printf( "  N Total Blocks %u\n", grid_divisions.x * grid_divisions.y );
    printf( "  N Empty Blocks %u\n", stats_n_empty_blocks );
    printf( "  N Dummy Blocks %u\n", stats_n_dummy_blocks );
    printf( "  N Polyg Blocks %u\n", stats_n_poly_blocks );
    printf( "  Med N Seg Poly %u\n", stats_sum_size_of_polygons / stats_n_poly_blocks );
    printf( "  medOfTheSquaresSegmentLength %f\n", medOfTheSquaresSegmentLength );
    printf( "  minSegmentLength             %f\n", minSegmentLength );
    printf( "  aDivFactor                   %f\n", aDivFactor );
    printf( "////////////////////////////////////////////////////////////////////////////////\n" );
#endif
}