void TransformOvalClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
                                wxPoint aStart, wxPoint aEnd, int aWidth,
                                int aCircleToSegmentsCount, double aCorrectionFactor )
{
    // To build the polygonal shape outside the actual shape, we use a bigger
    // radius to build rounded ends.
    // However, the width of the segment is too big.
    // so, later, we will clamp the polygonal shape with the bounding box
    // of the segment.
    int     radius  = aWidth / 2;

    // Note if we want to compensate the radius reduction of a circle due to
    // the segment approx, aCorrectionFactor must be calculated like this:
    // For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
    // aCorrectionFactor is 1 /cos( PI/s_CircleToSegmentsCount  )

    radius = radius * aCorrectionFactor;    // make segments outside the circles

    // end point is the coordinate relative to aStart
    wxPoint endp    = aEnd - aStart;
    wxPoint startp  = aStart;
    wxPoint corner;
    SHAPE_POLY_SET polyshape;

    polyshape.NewOutline();

    // normalize the position in order to have endp.x >= 0
    // it makes calculations more easy to understand
    if( endp.x < 0 )
    {
        endp    = aStart - aEnd;
        startp  = aEnd;
    }

    // delta_angle is in radian
    double delta_angle = atan2( (double)endp.y, (double)endp.x );
    int seg_len        = KiROUND( EuclideanNorm( endp ) );

    double delta = 3600.0 / aCircleToSegmentsCount;    // rot angle in 0.1 degree

    // Compute the outlines of the segment, and creates a polygon
    // Note: the polygonal shape is built from the equivalent horizontal
    // segment starting ar 0,0, and ending at seg_len,0

    // add right rounded end:
    for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ )
    {
        corner = wxPoint( 0, radius );
        RotatePoint( &corner, delta*ii );
        corner.x += seg_len;
        polyshape.Append( corner.x, corner.y );
    }

    // Finish arc:
    corner = wxPoint( seg_len, -radius );
    polyshape.Append( corner.x, corner.y );

    // add left rounded end:
    for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ )
    {
        corner = wxPoint( 0, -radius );
        RotatePoint( &corner, delta*ii );
        polyshape.Append( corner.x, corner.y );
    }

    // Finish arc:
    corner = wxPoint( 0, radius );
    polyshape.Append( corner.x, corner.y );

    // Now, clamp the polygonal shape (too big) with the segment bounding box
    // the polygonal shape bbox equivalent to the segment has a too big height,
    // and the right width
    if( aCorrectionFactor > 1.0 )
    {
        SHAPE_POLY_SET bbox;
        bbox.NewOutline();
        // Build the bbox (a horizontal rectangle).
        int halfwidth = aWidth / 2;     // Use the exact segment width for the bbox height
        corner.x = -radius - 2;         // use a bbox width slightly bigger to avoid
                                        // creating useless corner at segment ends
        corner.y = halfwidth;
        bbox.Append( corner.x, corner.y );
        corner.y = -halfwidth;
        bbox.Append( corner.x, corner.y );
        corner.x = radius + seg_len + 2;
        bbox.Append( corner.x, corner.y );
        corner.y = halfwidth;
        bbox.Append( corner.x, corner.y );

        // Now, clamp the shape
        polyshape.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
        // Note the final polygon is a simple, convex polygon with no hole
        // due to the shape of initial polygons
    }

    // Rotate and move the polygon to its right location
    polyshape.Rotate( delta_angle, VECTOR2I( 0, 0 ) );
    polyshape.Move( startp );

    aCornerBuffer.Append( polyshape);
}
Esempio n. 2
0
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
}
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;
}