void Convert_shape_line_polygon_to_triangles( SHAPE_POLY_SET &aPolyList,
                                              CGENERICCONTAINER2D &aDstContainer,
                                              float aBiuTo3DunitsScale ,
                                              const BOARD_ITEM &aBoardItem )
{

    aPolyList.CacheTriangulation();
    const double conver_d = (double)aBiuTo3DunitsScale;

    for( unsigned int j = 0; j < aPolyList.TriangulatedPolyCount(); j++ )
    {
        auto triPoly = aPolyList.TriangulatedPolygon( j );

        for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
        {
            VECTOR2I a;
            VECTOR2I b;
            VECTOR2I c;
            triPoly->GetTriangle( i, a, b, c );

            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 ) );
        }

    }
}
CROUNDSEGMENT2D::CROUNDSEGMENT2D( const SFVEC2F &aStart, const SFVEC2F &aEnd, float aWidth,
                                  const BOARD_ITEM &aBoardItem ) :
    COBJECT2D( OBJ2D_ROUNDSEG, aBoardItem ),
    m_segment( aStart, aEnd )
{
    m_radius = (aWidth / 2.0f);
    m_radius_squared = m_radius * m_radius;
    m_width = aWidth;

    SFVEC2F leftRadiusOffset( -m_segment.m_Dir.y * m_radius, m_segment.m_Dir.x * m_radius);
    m_leftStart = aStart + leftRadiusOffset;
    m_leftEnd   = aEnd   + leftRadiusOffset;
    m_leftEnd_minus_start = m_leftEnd - m_leftStart;
    m_leftDir   = glm::normalize( m_leftEnd_minus_start );

    SFVEC2F rightRadiusOffset( -leftRadiusOffset.x, -leftRadiusOffset.y );
    m_rightStart = aEnd   + rightRadiusOffset;
    m_rightEnd   = aStart + rightRadiusOffset;
    m_rightEnd_minus_start = m_rightEnd - m_rightStart;
    m_rightDir   = glm::normalize( m_rightEnd_minus_start );

    m_bbox.Reset();
    m_bbox.Set( aStart, aEnd );
    m_bbox.Set( m_bbox.Min() - SFVEC2F( m_radius, m_radius ), m_bbox.Max() + SFVEC2F( m_radius, m_radius ) );
    m_bbox.ScaleNextUp();
    m_centroid = m_bbox.GetCenter();

    wxASSERT( m_bbox.IsInitialized() );
}
INTERSECTION_RESULT CROUNDSEGMENT2D::IsBBoxInside( const CBBOX2D &aBBox ) const
{
    if( !m_bbox.Intersects( aBBox ) )
        return INTR_MISSES;

    SFVEC2F v[4];

    v[0] = aBBox.Min();
    v[1] = aBBox.Max();
    v[2] = SFVEC2F( aBBox.Min().x, aBBox.Max().y );
    v[3] = SFVEC2F( aBBox.Max().x, aBBox.Min().y );

    bool isInside[4];

    isInside[0] = IsPointInside( v[0] );
    isInside[1] = IsPointInside( v[1] );
    isInside[2] = IsPointInside( v[2] );
    isInside[3] = IsPointInside( v[3] );

    // Check if all points are inside the circle
    if( isInside[0] &&
        isInside[1] &&
        isInside[2] &&
        isInside[3] )
        return INTR_FULL_INSIDE;

    // Check if any point is inside the circle
    if( isInside[0] ||
        isInside[1] ||
        isInside[2] ||
        isInside[3] )
        return INTR_INTERSECTS;

    return INTR_MISSES;
}
void C3D_RENDER_OGL_LEGACY::generate_ring_contour( const SFVEC2F &aCenter,
                                                   float aInnerRadius,
                                                   float aOuterRadius,
                                                   unsigned int aNr_sides_per_circle,
                                                   std::vector< SFVEC2F > &aInnerContourResult,
                                                   std::vector< SFVEC2F > &aOuterContourResult,
                                                   bool aInvertOrder )
{
    aInnerContourResult.clear();
    aInnerContourResult.reserve( aNr_sides_per_circle + 2 );

    aOuterContourResult.clear();
    aOuterContourResult.reserve( aNr_sides_per_circle + 2 );

    const int delta = 3600 / aNr_sides_per_circle;

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

        aInnerContourResult.push_back( SFVEC2F( aCenter.x + rotatedDir.x * aInnerRadius,
                                                aCenter.y + rotatedDir.y * aInnerRadius ) );

        aOuterContourResult.push_back( SFVEC2F( aCenter.x + rotatedDir.x * aOuterRadius,
                                                aCenter.y + rotatedDir.y * aOuterRadius ) );
    }

    aInnerContourResult.push_back( aInnerContourResult[0] );
    aOuterContourResult.push_back( aOuterContourResult[0] );

    wxASSERT( aInnerContourResult.size() == aOuterContourResult.size() );
}
static void polygon_Convert( const SHAPE_LINE_CHAIN &aPath,
                             SEGMENTS &aOutSegment,
                             float aBiuTo3DunitsScale )
{
    aOutSegment.resize( aPath.PointCount() );

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

        aOutSegment[j].m_Start = SFVEC2F( (float) a.x * aBiuTo3DunitsScale,
                                          (float)-a.y * aBiuTo3DunitsScale );
    }

    unsigned int i;
    unsigned int j = aOutSegment.size () - 1;

    for( i = 0; i < aOutSegment.size (); j = i++ )
    {
        // Calculate constants for each segment
        aOutSegment[i].m_inv_JY_minus_IY = 1.0f / ( aOutSegment[j].m_Start.y -
                                                    aOutSegment[i].m_Start.y );

        aOutSegment[i].m_JX_minus_IX = (aOutSegment[j].m_Start.x -
                                        aOutSegment[i].m_Start.x);
    }
}
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();
}
void CLAYER_TRIANGLES::AddToMiddleContourns( const SHAPE_LINE_CHAIN &outlinePath,
                                             float zBot,
                                             float zTop,
                                             double aBiuTo3Du,
                                             bool aInvertFaceDirection )
{
    std::vector< SFVEC2F >contournPoints;

    contournPoints.clear();
    contournPoints.reserve( outlinePath.PointCount() + 2 );

    const VECTOR2I &firstV = outlinePath.CPoint( 0 );

    SFVEC2F lastV = SFVEC2F( firstV.x * aBiuTo3Du,
                            -firstV.y * aBiuTo3Du );

    contournPoints.push_back( lastV );

    for( unsigned int i = 1; i < (unsigned int)outlinePath.PointCount(); ++i )
    {
        const VECTOR2I & v = outlinePath.CPoint( i );

        const SFVEC2F vf = SFVEC2F(  v.x * aBiuTo3Du,
                                    -v.y * aBiuTo3Du );

        if( vf != lastV ) // Do not add repeated points
        {
            lastV = vf;
            contournPoints.push_back( vf );
        }
    }

    // Add first position fo the list to close the path
    if( lastV != contournPoints[0] )
        contournPoints.push_back( contournPoints[0] );

    AddToMiddleContourns( contournPoints, zBot, zTop, aInvertFaceDirection );
}
bool CROUNDSEGMENT2D::Intersects( const CBBOX2D &aBBox ) const
{
    if( !m_bbox.Intersects( aBBox ) )
        return false;

    if( (aBBox.Max().x > m_bbox.Max().x) &&
        (aBBox.Max().y > m_bbox.Max().x) &&
        (aBBox.Min().x < m_bbox.Min().x) &&
        (aBBox.Min().y < m_bbox.Min().y)
        )
        return true;

    SFVEC2F v[4];

    v[0] = aBBox.Min();
    v[1] = SFVEC2F( aBBox.Min().x, aBBox.Max().y );
    v[2] = aBBox.Max();
    v[3] = SFVEC2F( aBBox.Max().x, aBBox.Min().y );

    // Test against the main rectangle segment
    if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[0], v[1] - v[0] ) ) return true;
    if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[1], v[2] - v[1] ) ) return true;
    if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[2], v[3] - v[2] ) ) return true;
    if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[3], v[0] - v[3] ) ) return true;

    if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[0], v[1] - v[0] ) ) return true;
    if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[1], v[2] - v[1] ) ) return true;
    if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[2], v[3] - v[2] ) ) return true;
    if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[3], v[0] - v[3] ) ) return true;

    // Test the two circles
    if( aBBox.Intersects( m_segment.m_Start, m_radius_squared ) ) return true;
    if( aBBox.Intersects( m_segment.m_End,   m_radius_squared ) ) return true;

    return false;
}
static void polygon_Convert( const ClipperLib::Path &aPath,
                             SEGMENTS &aOutSegment,
                             float aBiuTo3DunitsScale )
{
    aOutSegment.resize( aPath.size() );

    for( unsigned i = 0; i < aPath.size(); i++ )
    {
        aOutSegment[i].m_Start = SFVEC2F( (float) aPath[i].X * aBiuTo3DunitsScale,
                                          (float)-aPath[i].Y * aBiuTo3DunitsScale );
    }

    unsigned int i;
    unsigned int j = aOutSegment.size () - 1;

    for( i = 0; i < aOutSegment.size (); j = i++ )
    {
        // Calculate constants for each segment
        aOutSegment[i].m_inv_JY_minus_IY = 1.0f / ( aOutSegment[j].m_Start.y -
                                                    aOutSegment[i].m_Start.y );
        aOutSegment[i].m_JX_minus_IX = (aOutSegment[j].m_Start.x - aOutSegment[i].m_Start.x);
    }
}
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 C3D_RENDER_OGL_LEGACY::add_object_to_triangle_layer( const CROUNDSEGMENT2D * aSeg,
                                                          CLAYER_TRIANGLES *aDstLayer,
                                                          float aZtop,
                                                          float aZbot )
{
    const SFVEC2F leftStart   = aSeg->GetLeftStar();
    const SFVEC2F leftEnd     = aSeg->GetLeftEnd();
    const SFVEC2F leftDir     = aSeg->GetLeftDir();

    const SFVEC2F rightStart  = aSeg->GetRightStar();
    const SFVEC2F rightEnd    = aSeg->GetRightEnd();
    const SFVEC2F rightDir    = aSeg->GetRightDir();
    const float   radius      = aSeg->GetRadius();

    const SFVEC2F start       = aSeg->GetStart();
    const SFVEC2F end         = aSeg->GetEnd();

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

    const float radius_of_the_square = sqrtf( aSeg->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 (semi-circles)
    aDstLayer->m_layer_top_segment_ends->AddTriangle(
                SFVEC3F( rightEnd.x  + texture_factor * factorS.x,
                         rightEnd.y  + texture_factor * factorS.y,
                         aZtop ),
                SFVEC3F( leftStart.x + texture_factor * factorE.x,
                         leftStart.y + texture_factor * factorE.y,
                         aZtop ),
                SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
                         start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
                         aZtop ) );

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

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

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

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

    aDstLayer->m_layer_bot_triangles->AddQuad(
                SFVEC3F( rightEnd.x,   rightEnd.y,   aZbot ),
                SFVEC3F( leftStart.x,  leftStart.y,  aZbot ),
                SFVEC3F( leftEnd.x,    leftEnd.y,    aZbot ),
                SFVEC3F( rightStart.x, rightStart.y, aZbot ) );
}
bool CVCYLINDER::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const
{
    // Based on:
    // http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp
    // Ray-sphere intersection: geometric
    // /////////////////////////////////////////////////////////////////////////
    const double OCx_Start = aRay.m_Origin.x - m_center.x;
    const double OCy_Start = aRay.m_Origin.y - m_center.y;

    const double p_dot_p = OCx_Start * OCx_Start + OCy_Start * OCy_Start;

    const double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x +
                     (double)aRay.m_Dir.y * (double)aRay.m_Dir.y;
    const double b = (double)aRay.m_Dir.x * (double)OCx_Start +
                     (double)aRay.m_Dir.y * (double)OCy_Start;
    const double c = p_dot_p - m_radius_squared;

    const float delta = (float)(b * b - a * c);

    bool hitResult = false;

    if( delta > FLT_EPSILON )
    {
        const float inv_a = 1.0 / a;

        const float sdelta = sqrtf( delta );
        const float t = (-b - sdelta) * inv_a;
        const float z = aRay.m_Origin.z + t * aRay.m_Dir.z;

        if( (z >= m_bbox.Min().z) &&
            (z <= m_bbox.Max().z) )
        {
            if( t < aHitInfo.m_tHit )
            {
                hitResult = true;
                aHitInfo.m_tHit = t;
            }
        }

        if( !hitResult )
        {
            const float t1 = (-b + sdelta) * inv_a;
            const float z1 = aRay.m_Origin.z + t1 * aRay.m_Dir.z;

            if( (z1 > m_bbox.Min().z ) &&
                (z1 < m_bbox.Max().z ) )
            {
                if( t1 < aHitInfo.m_tHit )
                {
                    hitResult = true;
                    aHitInfo.m_tHit = t1;
                }
            }
        }
    }

    if( hitResult )
    {
        aHitInfo.m_HitPoint = aRay.at( aHitInfo.m_tHit );

        const SFVEC2F hitPoint2D = SFVEC2F( aHitInfo.m_HitPoint.x,
                                            aHitInfo.m_HitPoint.y );

        aHitInfo.m_HitNormal = SFVEC3F( -(hitPoint2D.x - m_center.x) * m_inv_radius,
                                        -(hitPoint2D.y - m_center.y) * m_inv_radius,
                                        0.0f );

        m_material->PerturbeNormal( aHitInfo.m_HitNormal, aRay, aHitInfo );

        aHitInfo.pHitObject = this;
    }

    return hitResult;
}
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
}
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] );
        }
    }
}
GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_seg_ends(
        const CLAYER_TRIANGLE_CONTAINER *aTriangleContainer,
        bool aIsNormalUp,
        GLuint aTextureId ) const
{
    wxASSERT( aTriangleContainer != NULL );

    wxASSERT( (aTriangleContainer->GetVertexSize() % 3) == 0 );

    // Top and Bot dont have normals array stored in container
    wxASSERT( aTriangleContainer->GetNormalsSize() == 0 );

    if( (aTriangleContainer->GetVertexSize() > 0) &&
        ((aTriangleContainer->GetVertexSize() % 3) == 0) )
    {
        GLuint listIdx = glGenLists( 1 );

        if( glIsList( listIdx ) )
        {
            // Prepare an array of UV text coordinates
            SFVEC2F *uvArray = new SFVEC2F[aTriangleContainer->GetVertexSize()];

            for( unsigned int i = 0;
                 i < aTriangleContainer->GetVertexSize();
                 i += 3 )
            {
                uvArray[i + 0] = SFVEC2F( 1.0f, 0.0f );
                uvArray[i + 1] = SFVEC2F( 0.0f, 1.0f );
                uvArray[i + 2] = SFVEC2F( 0.0f, 0.0f );
            }

            glEnableClientState( GL_TEXTURE_COORD_ARRAY );
            glDisableClientState( GL_COLOR_ARRAY );
            glDisableClientState( GL_NORMAL_ARRAY );
            glEnableClientState( GL_VERTEX_ARRAY );
            glVertexPointer( 3, GL_FLOAT, 0, aTriangleContainer->GetVertexPointer() );
            glTexCoordPointer( 2, GL_FLOAT, 0, uvArray );

            glNewList( listIdx, GL_COMPILE );

            glDisable( GL_COLOR_MATERIAL );

            glEnable( GL_TEXTURE_2D );
            glBindTexture( GL_TEXTURE_2D, aTextureId );

            setBlendfunction();

            glAlphaFunc( GL_GREATER, 0.2f );
            glEnable( GL_ALPHA_TEST );

            glNormal3f( 0.0f, 0.0f, aIsNormalUp?1.0f:-1.0f );

            glDrawArrays( GL_TRIANGLES, 0, aTriangleContainer->GetVertexSize() );

            glDisable( GL_TEXTURE_2D );
            glDisable( GL_ALPHA_TEST );
            glDisable( GL_BLEND );

            glEndList();

            glDisableClientState( GL_VERTEX_ARRAY );
            glDisableClientState( GL_TEXTURE_COORD_ARRAY );

            delete [] uvArray;
            return listIdx;
        }
    }

    return 0;
}
void CLAYER_TRIANGLES::AddToMiddleContourns( const std::vector< SFVEC2F > &aContournPoints,
                                             float zBot,
                                             float zTop,
                                             bool aInvertFaceDirection )
{
    if( aContournPoints.size() > 4 )
    {
        // Calculate normals of each segment of the contourn
        std::vector< SFVEC2F > contournNormals;

        contournNormals.clear();
        contournNormals.resize( aContournPoints.size() - 1 );

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

                const SFVEC2F n = glm::normalize( v1 - v0 );

                contournNormals[i] = SFVEC2F( n.y,-n.x );
            }
        }
        else
        {
            for( unsigned int i = 0; i < ( aContournPoints.size() - 1 ); ++i )
            {
                const SFVEC2F &v0 = aContournPoints[i + 0];
                const SFVEC2F &v1 = aContournPoints[i + 1];

                const SFVEC2F n = glm::normalize( v1 - v0 );

                contournNormals[i] = SFVEC2F( -n.y, n.x );
            }
        }


        if( aInvertFaceDirection )
            std::swap( zBot, zTop );

        const unsigned int nContournsToProcess = ( aContournPoints.size() - 1 );

        for( unsigned int i = 0; i < nContournsToProcess; ++i )
        {
            SFVEC2F lastNormal;

            if( i > 0 )
                lastNormal = contournNormals[i - 1];
            else
                lastNormal = contournNormals[nContournsToProcess - 1];

            SFVEC2F n0 = contournNormals[i];

            // Only interpolate the normal if the angle is closer
            if( glm::dot( n0, lastNormal ) > 0.5f )
                n0 = glm::normalize( n0 + lastNormal );

            SFVEC2F nextNormal;

            if( i < (nContournsToProcess - 1) )
                nextNormal = contournNormals[i + 1];
            else
                nextNormal = contournNormals[0];

            SFVEC2F n1 = contournNormals[i];

            if( glm::dot( n1, nextNormal ) > 0.5f )
                n1 = glm::normalize( n1 + nextNormal );

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

            const SFVEC2F &v0 = aContournPoints[i + 0];
            const SFVEC2F &v1 = aContournPoints[i + 1];

            #pragma omp critical
            {
                m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, zTop ),
                                                         SFVEC3F( v1.x, v1.y, zTop ),
                                                         SFVEC3F( v1.x, v1.y, zBot ),
                                                         SFVEC3F( v0.x, v0.y, zBot ) );

                m_layer_middle_contourns_quads->AddNormal( n3d0, n3d1, n3d1, n3d0 );
            }
        }
    }
}
void Polygon2d_TestModule()
{
    // "This structure contains a sequence of IntPoint vertices defining a
    // single contour"
    ClipperLib::Path aPath;

    SEGMENTS aSegments;

    aPath.resize( 4 );

    aPath[0] = ClipperLib::IntPoint( -2, -2 );
    aPath[1] = ClipperLib::IntPoint(  2, -2 );
    aPath[2] = ClipperLib::IntPoint(  2,  2 );
    aPath[3] = ClipperLib::IntPoint( -2,  2 );

    // It must be an outter polygon
    wxASSERT( ClipperLib::Orientation( aPath ) );

    polygon_Convert( aPath, aSegments, 1.0f );

    wxASSERT( aPath.size() == aSegments.size() );

    wxASSERT( aSegments[0].m_Start == SFVEC2F( -2.0f,  2.0f ) );
    wxASSERT( aSegments[1].m_Start == SFVEC2F(  2.0f,  2.0f ) );
    wxASSERT( aSegments[2].m_Start == SFVEC2F(  2.0f, -2.0f ) );
    wxASSERT( aSegments[3].m_Start == SFVEC2F( -2.0f, -2.0f ) );

    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  0.0f,  0.0f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f, -1.9f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f,  1.9f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  1.9f,  1.9f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  1.9f, -1.9f ) ) );

    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f, -2.0f ) ) == false );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f,  2.0f ) ) == false );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  2.1f,  2.0f ) ) == false );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  2.1f, -2.0f ) ) == false );
}
bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const
{
    wxASSERT( aOutT );
    wxASSERT( aNormalOut );

    bool start_is_inside = IsPointInside( aSegRay.m_Start );
    bool end_is_inside = IsPointInside( aSegRay.m_End );

    // If segment if inside there are no hits
    if( start_is_inside && end_is_inside )
        return false;

    bool hitted = false;

    float closerHitT = FLT_MAX;
    float farHitT = FLT_MAX;

    SFVEC2F closerHitNormal;
    SFVEC2F farHitNormal;

    float leftSegT;
    bool leftSegmentHit = aSegRay.IntersectSegment( m_leftStart, m_leftEnd_minus_start, &leftSegT );

    if( leftSegmentHit )
    {
        hitted = true;
        closerHitT = leftSegT;
        farHitT    = leftSegT;

        closerHitNormal = SFVEC2F( -m_leftDir.y,  m_leftDir.x );
        farHitNormal    = SFVEC2F( -m_leftDir.y,  m_leftDir.x );
    }

    float rightSegT;
    bool rightSegmentHit = aSegRay.IntersectSegment( m_rightStart, m_rightEnd_minus_start, &rightSegT );

    if( rightSegmentHit )
    {
        if( !start_is_inside )
        if( (hitted == false) || (rightSegT < closerHitT) )
        {
            closerHitT = rightSegT;
            closerHitNormal = SFVEC2F( -m_rightDir.y,  m_rightDir.x );
        }

        if( start_is_inside )
        if( (hitted == false) || (rightSegT > farHitT) )
        {
            farHitT = rightSegT;
            farHitNormal = SFVEC2F( -m_rightDir.y,  m_rightDir.x );
        }

        hitted = true;
    }

    float   circleStart_T0;
    float   circleStart_T1;
    SFVEC2F circleStart_N0;
    SFVEC2F circleStart_N1;

    bool startCircleHit = aSegRay.IntersectCircle( m_segment.m_Start, m_radius,
                                                   &circleStart_T0, &circleStart_T1,
                                                   &circleStart_N0, &circleStart_N1 );

    if( startCircleHit )
    {
        if( circleStart_T0 > 0.0f )
        {
            if( !start_is_inside )
            if( (hitted == false) || (circleStart_T0 < closerHitT) )
            {
                closerHitT = circleStart_T0;
                closerHitNormal = circleStart_N0;
            }

            if( start_is_inside )
            if( (hitted == false) || (circleStart_T1 > farHitT) )
            {
                farHitT = circleStart_T1;
                farHitNormal = circleStart_N1;
            }
        }
        else
        {
            // This can only happen if the ray starts inside
            if( (hitted == false) || (circleStart_T1 > farHitT) )
            {
                farHitT = circleStart_T1;
                farHitNormal = circleStart_N1;
            }
        }

        hitted = true;
    }

    float   circleEnd_T0;
    float   circleEnd_T1;
    SFVEC2F circleEnd_N0;
    SFVEC2F circleEnd_N1;

    bool rightCircleHit = aSegRay.IntersectCircle( m_segment.m_End, m_radius,
                                                   &circleEnd_T0, &circleEnd_T1,
                                                   &circleEnd_N0, &circleEnd_N1 );
    if( rightCircleHit )
    {
        if( circleEnd_T0 > 0.0f )
        {
            if( !start_is_inside )
            if( (hitted == false) || (circleEnd_T0 < closerHitT) )
            {
                closerHitT = circleEnd_T0;
                closerHitNormal = circleEnd_N0;
            }

            if( start_is_inside )
            if( (hitted == false) || (circleEnd_T1 > farHitT) )
            {
                farHitT = circleEnd_T1;
                farHitNormal = circleEnd_N1;
            }
        }
        else
        {
            // This can only happen if the ray starts inside
            if( (hitted == false) || (circleEnd_T1 > farHitT) )
            {
                farHitT = circleEnd_T1;
                farHitNormal = circleEnd_N1;
            }
        }

        hitted = true;
    }

    if( hitted )
    {
        if( !start_is_inside )
        {
            *aOutT = closerHitT;
            //wxASSERT( (closerHitT > 0.0f) && (closerHitT <= 1.0f) );
            *aNormalOut = closerHitNormal;
        }
        else
        {
            wxASSERT( (farHitT > 0.0f) && (farHitT <= 1.0f) );
            *aOutT = farHitT;
            *aNormalOut = farHitNormal;
        }
    }

    return hitted;
}