void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures )
{
    int segsPerCircle;
    double correctionFactor;

    // Set the number of segments in arc approximations
    if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF  )
        segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
    else
        segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;

    /* calculates the coeff to compensate radius reduction of holes clearance
     * due to the segment approx.
     * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
     * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount  )
     */
    correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );

    aFeatures.RemoveAllContours();

    int outline_half_thickness = m_ZoneMinThickness / 2;

    int zone_clearance = std::max( m_ZoneClearance, GetClearance() );
    zone_clearance += outline_half_thickness;

    /* store holes (i.e. tracks and pads areas as polygons outlines)
     * in a polygon list
     */

    /* items ouside the zone bounding box are skipped
     * the bounding box is the zone bounding box + the biggest clearance found in Netclass list
     */
    EDA_RECT item_boundingbox;
    EDA_RECT zone_boundingbox  = GetBoundingBox();
    int      biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue();
    biggest_clearance = std::max( biggest_clearance, zone_clearance );
    zone_boundingbox.Inflate( biggest_clearance );

    /*
     * First : Add pads. Note: pads having the same net as zone are left in zone.
     * Thermal shapes will be created later if necessary
     */
    int item_clearance;

    /* Use a dummy pad to calculate hole clerance when a pad is not on all copper layers
     * and this pad has a hole
     * This dummy pad has the size and shape of the hole
    * Therefore, this dummy pad is a circle or an oval.
     * A pad must have a parent because some functions expect a non null parent
     * to find the parent board, and some other data
     */
    MODULE dummymodule( aPcb );    // Creates a dummy parent
    D_PAD dummypad( &dummymodule );

    for( MODULE* module = aPcb->m_Modules;  module;  module = module->Next() )
    {
        D_PAD* nextpad;

        for( D_PAD* pad = module->Pads(); pad != NULL; pad = nextpad )
        {
            nextpad = pad->Next();  // pad pointer can be modified by next code, so
                                    // calculate the next pad here

            if( !pad->IsOnLayer( GetLayer() ) )
            {
                /* Test for pads that are on top or bottom only and have a hole.
                 * There are curious pads but they can be used for some components that are
                 * inside the board (in fact inside the hole. Some photo diodes and Leds are
                 * like this)
                 */
                if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
                    continue;

                // Use a dummy pad to calculate a hole shape that have the same dimension as
                // the pad hole
                dummypad.SetSize( pad->GetDrillSize() );
                dummypad.SetOrientation( pad->GetOrientation() );
                dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
                                   PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
                dummypad.SetPosition( pad->GetPosition() );

                pad = &dummypad;
            }

            // Note: netcode <=0 means not connected item
            if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) )
            {
                item_clearance   = pad->GetClearance() + outline_half_thickness;
                item_boundingbox = pad->GetBoundingBox();
                item_boundingbox.Inflate( item_clearance );

                if( item_boundingbox.Intersects( zone_boundingbox ) )
                {
                    int clearance = std::max( zone_clearance, item_clearance );
                    pad->TransformShapeWithClearanceToPolygon( aFeatures,
                                                               clearance,
                                                               segsPerCircle,
                                                               correctionFactor );
                }

                continue;
            }

            // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE
            if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE )
            {
                int gap = zone_clearance;
                int thermalGap = GetThermalReliefGap( pad );
                gap = std::max( gap, thermalGap );
                item_boundingbox = pad->GetBoundingBox();
                item_boundingbox.Inflate( gap );

                if( item_boundingbox.Intersects( zone_boundingbox ) )
                {
                    pad->TransformShapeWithClearanceToPolygon( aFeatures,
                                                               gap,
                                                               segsPerCircle,
                                                               correctionFactor );
                }
            }
        }
    }

    /* Add holes (i.e. tracks and vias areas as polygons outlines)
     * in cornerBufferPolysToSubstract
     */
    for( TRACK* track = aPcb->m_Track;  track;  track = track->Next() )
    {
        if( !track->IsOnLayer( GetLayer() ) )
            continue;

        if( track->GetNetCode() == GetNetCode()  && (GetNetCode() != 0) )
            continue;

        item_clearance   = track->GetClearance() + outline_half_thickness;
        item_boundingbox = track->GetBoundingBox();

        if( item_boundingbox.Intersects( zone_boundingbox ) )
        {
            int clearance = std::max( zone_clearance, item_clearance );
            track->TransformShapeWithClearanceToPolygon( aFeatures,
                                                         clearance,
                                                         segsPerCircle,
                                                         correctionFactor );
        }
    }

    /* Add module edge items that are on copper layers
     * Pcbnew allows these items to be on copper layers in microwave applictions
     * This is a bad thing, but must be handled here, until a better way is found
     */
    for( MODULE* module = aPcb->m_Modules;  module;  module = module->Next() )
    {
        for( BOARD_ITEM* item = module->GraphicalItems();  item;  item = item->Next() )
        {
            if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) )
                continue;

            if( item->Type() != PCB_MODULE_EDGE_T )
                continue;

            item_boundingbox = item->GetBoundingBox();

            if( item_boundingbox.Intersects( zone_boundingbox ) )
            {
                ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon(
                    aFeatures, zone_clearance,
                    segsPerCircle, correctionFactor );
            }
        }
    }

    // Add graphic items (copper texts) and board edges
    for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() )
    {
        if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts )
            continue;

        switch( item->Type() )
        {
        case PCB_LINE_T:
            ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
                aFeatures,
                zone_clearance, segsPerCircle, correctionFactor );
            break;

        case PCB_TEXT_T:
            ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon(
                aFeatures, zone_clearance );
            break;

        default:
            break;
        }
    }

    // Add zones outlines having an higher priority and keepout
    for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ )
    {
        ZONE_CONTAINER* zone = GetBoard()->GetArea( ii );
        if( zone->GetLayer() != GetLayer() )
            continue;

        if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() )
            continue;

        if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() )
            continue;

        // A highter priority zone or keepout area is found: remove this area
        item_boundingbox = zone->GetBoundingBox();
        if( !item_boundingbox.Intersects( zone_boundingbox ) )
            continue;

        // Add the zone outline area.
        // However if the zone has the same net as the current zone,
        // do not add any clearance.
        // the zone will be connected to the current zone, but filled areas
        // will use different parameters (clearance, thermal shapes )
        bool same_net = GetNetCode() == zone->GetNetCode();
        bool use_net_clearance = true;
        int min_clearance = zone_clearance;

        // Do not forget to make room to draw the thick outlines
        // of the hole created by the area of the zone to remove
        int holeclearance = zone->GetClearance() + outline_half_thickness;

        // The final clearance is obviously the max value of each zone clearance
        min_clearance = std::max( min_clearance, holeclearance );

        if( zone->GetIsKeepout() || same_net )
        {
            // Just take in account the fact the outline has a thickness, so
            // the actual area to substract is inflated to take in account this fact
            min_clearance = outline_half_thickness;
            use_net_clearance = false;
        }

        zone->TransformOutlinesShapeWithClearanceToPolygon(
                    aFeatures,
                    min_clearance, use_net_clearance );
    }

   // Remove thermal symbols
    for( MODULE* module = aPcb->m_Modules;  module;  module = module->Next() )
    {
        for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() )
        {
            // Rejects non-standard pads with tht-only thermal reliefs
            if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL
             && pad->GetAttribute() != PAD_ATTRIB_STANDARD )
                continue;

            if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL
             && GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL )
                continue;

            if( !pad->IsOnLayer( GetLayer() ) )
                continue;

            if( pad->GetNetCode() != GetNetCode() )
                continue;
            item_boundingbox = pad->GetBoundingBox();
            int thermalGap = GetThermalReliefGap( pad );
            item_boundingbox.Inflate( thermalGap, thermalGap );

            if( item_boundingbox.Intersects( zone_boundingbox ) )
            {
                CreateThermalReliefPadPolygon( aFeatures,
                                               *pad, thermalGap,
                                               GetThermalReliefCopperBridge( pad ),
                                               m_ZoneMinThickness,
                                               segsPerCircle,
                                               correctionFactor, s_thermalRot );
            }
        }
    }

}
Exemple #2
0
void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone,
        SHAPE_POLY_SET& aFeatures ) const
{
    int segsPerCircle;
    double correctionFactor;

    // Set the number of segments in arc approximations
    if( aZone->GetArcSegmentCount() == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF  )
        segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
    else
        segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;

    /* calculates the coeff to compensate radius reduction of holes clearance
     * due to the segment approx.
     * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
     * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount  )
     */
    correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );

    aFeatures.RemoveAllContours();

    int outline_half_thickness = aZone->GetMinThickness() / 2;

    // When removing holes, the holes must be expanded by outline_half_thickness
    // to take in account the thickness of the zone outlines
    int zone_clearance = aZone->GetClearance() + outline_half_thickness;

    // When holes are created by non copper items (edge cut items), use only
    // the m_ZoneClearance parameter (zone clearance with no netclass clearance)
    int zone_to_edgecut_clearance = aZone->GetZoneClearance() + outline_half_thickness;

    /* store holes (i.e. tracks and pads areas as polygons outlines)
     * in a polygon list
     */

    /* items ouside the zone bounding box are skipped
     * the bounding box is the zone bounding box + the biggest clearance found in Netclass list
     */
    EDA_RECT    item_boundingbox;
    EDA_RECT    zone_boundingbox = aZone->GetBoundingBox();
    int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
    biggest_clearance = std::max( biggest_clearance, zone_clearance );
    zone_boundingbox.Inflate( biggest_clearance );

    /*
     * First : Add pads. Note: pads having the same net as zone are left in zone.
     * Thermal shapes will be created later if necessary
     */

    /* Use a dummy pad to calculate hole clearance when a pad is not on all copper layers
     * and this pad has a hole
     * This dummy pad has the size and shape of the hole
     * Therefore, this dummy pad is a circle or an oval.
     * A pad must have a parent because some functions expect a non null parent
     * to find the parent board, and some other data
     */
    MODULE  dummymodule( m_board );   // Creates a dummy parent
    D_PAD   dummypad( &dummymodule );

    for( MODULE* module = m_board->m_Modules; module; module = module->Next() )
    {
        D_PAD* nextpad;

        for( D_PAD* pad = module->PadsList(); pad != NULL; pad = nextpad )
        {
            nextpad = pad->Next();      // pad pointer can be modified by next code, so
                                        // calculate the next pad here

            if( !pad->IsOnLayer( aZone->GetLayer() ) )
            {
                /* Test for pads that are on top or bottom only and have a hole.
                 * There are curious pads but they can be used for some components that are
                 * inside the board (in fact inside the hole. Some photo diodes and Leds are
                 * like this)
                 */
                if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
                    continue;

                // Use a dummy pad to calculate a hole shape that have the same dimension as
                // the pad hole
                dummypad.SetSize( pad->GetDrillSize() );
                dummypad.SetOrientation( pad->GetOrientation() );
                dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
                        PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
                dummypad.SetPosition( pad->GetPosition() );

                pad = &dummypad;
            }

            // Note: netcode <=0 means not connected item
            if( ( pad->GetNetCode() != aZone->GetNetCode() ) || ( pad->GetNetCode() <= 0 ) )
            {
                int item_clearance = pad->GetClearance() + outline_half_thickness;
                item_boundingbox = pad->GetBoundingBox();
                item_boundingbox.Inflate( item_clearance );

                if( item_boundingbox.Intersects( zone_boundingbox ) )
                {
                    int clearance = std::max( zone_clearance, item_clearance );

                    // PAD_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape
                    if( pad->GetShape() == PAD_SHAPE_CUSTOM
                        && pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
                    {
                        // the pad shape in zone can be its convex hull or
                        // the shape itself
                        SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
                        outline.Inflate( KiROUND( clearance * correctionFactor ), segsPerCircle );
                        pad->CustomShapeAsPolygonToBoardPosition( &outline,
                                pad->GetPosition(), pad->GetOrientation() );

                        if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
                        {
                            std::vector<wxPoint> convex_hull;
                            BuildConvexHull( convex_hull, outline );

                            aFeatures.NewOutline();

                            for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
                                aFeatures.Append( convex_hull[ii] );
                        }
                        else
                            aFeatures.Append( outline );
                    }
                    else
                        pad->TransformShapeWithClearanceToPolygon( aFeatures,
                                clearance,
                                segsPerCircle,
                                correctionFactor );
                }

                continue;
            }

            // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE
            // or if they have a custom shape, because a thermal relief will break
            // the shape
            if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_NONE
                || pad->GetShape() == PAD_SHAPE_CUSTOM )
            {
                int gap = zone_clearance;
                int thermalGap = aZone->GetThermalReliefGap( pad );
                gap = std::max( gap, thermalGap );
                item_boundingbox = pad->GetBoundingBox();
                item_boundingbox.Inflate( gap );

                if( item_boundingbox.Intersects( zone_boundingbox ) )
                {
                    // PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape
                    // the pad shape in zone can be its convex hull or the shape itself
                    if( pad->GetShape() == PAD_SHAPE_CUSTOM
                        && pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
                    {
                        // the pad shape in zone can be its convex hull or
                        // the shape itself
                        SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
                        outline.Inflate( KiROUND( gap * correctionFactor ), segsPerCircle );
                        pad->CustomShapeAsPolygonToBoardPosition( &outline,
                                pad->GetPosition(), pad->GetOrientation() );

                        std::vector<wxPoint> convex_hull;
                        BuildConvexHull( convex_hull, outline );

                        aFeatures.NewOutline();

                        for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
                            aFeatures.Append( convex_hull[ii] );
                    }
                    else
                        pad->TransformShapeWithClearanceToPolygon( aFeatures,
                                gap, segsPerCircle, correctionFactor );
                }
            }
        }
    }

    /* Add holes (i.e. tracks and vias areas as polygons outlines)
     * in cornerBufferPolysToSubstract
     */
    for( auto track : m_board->Tracks() )
    {
        if( !track->IsOnLayer( aZone->GetLayer() ) )
            continue;

        if( track->GetNetCode() == aZone->GetNetCode()  && ( aZone->GetNetCode() != 0) )
            continue;

        int item_clearance = track->GetClearance() + outline_half_thickness;
        item_boundingbox = track->GetBoundingBox();

        if( item_boundingbox.Intersects( zone_boundingbox ) )
        {
            int clearance = std::max( zone_clearance, item_clearance );
            track->TransformShapeWithClearanceToPolygon( aFeatures,
                    clearance,
                    segsPerCircle,
                    correctionFactor );
        }
    }

    /* Add module edge items that are on copper layers
     * Pcbnew allows these items to be on copper layers in microwave applictions
     * This is a bad thing, but must be handled here, until a better way is found
     */
    for( auto module : m_board->Modules() )
    {
        for( auto item : module->GraphicalItems() )
        {
            if( !item->IsOnLayer( aZone->GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) )
                continue;

            if( item->Type() != PCB_MODULE_EDGE_T )
                continue;

            item_boundingbox = item->GetBoundingBox();

            if( item_boundingbox.Intersects( zone_boundingbox ) )
            {
                int zclearance = zone_clearance;

                if( item->IsOnLayer( Edge_Cuts ) )
                    // use only the m_ZoneClearance, not the clearance using
                    // the netclass value, because we do not have a copper item
                    zclearance = zone_to_edgecut_clearance;

                ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon(
                        aFeatures, zclearance, segsPerCircle, correctionFactor );
            }
        }
    }

    // Add graphic items (copper texts) and board edges
    // Currently copper texts have no net, so only the zone_clearance
    // is used.
    for( auto item : m_board->Drawings() )
    {
        if( item->GetLayer() != aZone->GetLayer() && item->GetLayer() != Edge_Cuts )
            continue;

        int zclearance = zone_clearance;

        if( item->GetLayer() == Edge_Cuts )
            // use only the m_ZoneClearance, not the clearance using
            // the netclass value, because we do not have a copper item
            zclearance = zone_to_edgecut_clearance;

        switch( item->Type() )
        {
        case PCB_LINE_T:
            ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
                    aFeatures,
                    zclearance, segsPerCircle, correctionFactor );
            break;

        case PCB_TEXT_T:
            ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon(
                    aFeatures, zclearance );
            break;

        default:
            break;
        }
    }

    // Add zones outlines having an higher priority and keepout
    for( int ii = 0; ii < m_board->GetAreaCount(); ii++ )
    {
        ZONE_CONTAINER* zone = m_board->GetArea( ii );

        // If the zones share no common layers
        if( !aZone->CommonLayerExists( zone->GetLayerSet() ) )
            continue;

        if( !zone->GetIsKeepout() && zone->GetPriority() <= aZone->GetPriority() )
            continue;

        if( zone->GetIsKeepout() && !zone->GetDoNotAllowCopperPour() )
            continue;

        // A highter priority zone or keepout area is found: remove this area
        item_boundingbox = zone->GetBoundingBox();

        if( !item_boundingbox.Intersects( zone_boundingbox ) )
            continue;

        // Add the zone outline area.
        // However if the zone has the same net as the current zone,
        // do not add any clearance.
        // the zone will be connected to the current zone, but filled areas
        // will use different parameters (clearance, thermal shapes )
        bool    same_net = aZone->GetNetCode() == zone->GetNetCode();
        bool    use_net_clearance = true;
        int     min_clearance = zone_clearance;

        // Do not forget to make room to draw the thick outlines
        // of the hole created by the area of the zone to remove
        int holeclearance = zone->GetClearance() + outline_half_thickness;

        // The final clearance is obviously the max value of each zone clearance
        min_clearance = std::max( min_clearance, holeclearance );

        if( zone->GetIsKeepout() || same_net )
        {
            // Just take in account the fact the outline has a thickness, so
            // the actual area to substract is inflated to take in account this fact
            min_clearance = outline_half_thickness;
            use_net_clearance = false;
        }

        zone->TransformOutlinesShapeWithClearanceToPolygon(
                aFeatures, min_clearance, use_net_clearance );
    }

    // Remove thermal symbols
    for( auto module : m_board->Modules() )
    {
        for( auto pad : module->Pads() )
        {
            // Rejects non-standard pads with tht-only thermal reliefs
            if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL
                && pad->GetAttribute() != PAD_ATTRIB_STANDARD )
                continue;

            if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL
                && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL )
                continue;

            if( !pad->IsOnLayer( aZone->GetLayer() ) )
                continue;

            if( pad->GetNetCode() != aZone->GetNetCode() )
                continue;

            item_boundingbox = pad->GetBoundingBox();
            int thermalGap = aZone->GetThermalReliefGap( pad );
            item_boundingbox.Inflate( thermalGap, thermalGap );

            if( item_boundingbox.Intersects( zone_boundingbox ) )
            {
                CreateThermalReliefPadPolygon( aFeatures,
                        *pad, thermalGap,
                        aZone->GetThermalReliefCopperBridge( pad ),
                        aZone->GetMinThickness(),
                        segsPerCircle,
                        correctionFactor, s_thermalRot );
            }
        }
    }
}