diff options
Diffstat (limited to 'pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp')
-rw-r--r-- | pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp new file mode 100644 index 0000000..c02d570 --- /dev/null +++ b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp @@ -0,0 +1,508 @@ +/** + * @file zones_convert_brd_items_to_polygons_with_Boost.cpp + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Functions to convert some board items to polygons + * (pads, tracks ..) + * This is used to calculate filled areas in copper zones. + * Filled areas are areas remainder of the full zone area after removed all polygons + * calculated from these items shapes and the clearance area + * + * Important note: + * Because filled areas must have a minimum thickness to match with Design rule, they are + * draw in 2 step: + * 1 - filled polygons are drawn + * 2 - polygon outlines are drawn with a "minimum thickness width" ( or with a minimum + * thickness pen ) + * So outlines of filled polygons are calculated with the constraint they match with clearance, + * taking in account outlines have thickness + * This ensures: + * - areas meet the minimum thickness requirement. + * - shapes are smoothed. + */ + +#include <cmath> +#include <sstream> + +#include <fctsys.h> +#include <wxPcbStruct.h> +#include <trigo.h> + +#include <class_board.h> +#include <class_module.h> +#include <class_track.h> +#include <class_edge_mod.h> +#include <class_drawsegment.h> +#include <class_pcb_text.h> +#include <class_zone.h> +#include <project.h> + +#include <pcbnew.h> +#include <zones.h> +#include <convert_basic_shapes_to_polygon.h> + +#include <geometry/shape_poly_set.h> +#include <geometry/shape_file_io.h> + +#include <boost/foreach.hpp> + +extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, + BOARD* aPcb, ZONE_CONTAINER* aZone, + double aArcCorrection, + double aRoundPadThermalRotation); + +extern void Test_For_Copper_Island_And_Remove( BOARD* aPcb, + ZONE_CONTAINER* aZone_container ); + +extern void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer, + D_PAD& aPad, + int aThermalGap, + int aCopperThickness, + int aMinThicknessValue, + int aCircleToSegmentsCount, + double aCorrectionFactor, + double aThermalRot ); + +// Local Variables: +static double s_thermalRot = 450; // angle of stubs in thermal reliefs for round pads + +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; + } + + if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE ) + { + int gap = zone_clearance; + int thermalGap = GetThermalReliefGap( pad ); + gap = std::max( gap, thermalGap ); + item_boundingbox = pad->GetBoundingBox(); + + 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 ); + } + } + } + +} + + +/** + * Function AddClearanceAreasPolygonsToPolysList + * Supports a min thickness area constraint. + * Add non copper areas polygons (pads and tracks with clearance) + * to the filled copper area found + * in BuildFilledPolysListData after calculating filled areas in a zone + * Non filled copper areas are pads and track and their clearance areas + * The filled copper area must be computed just before. + * BuildFilledPolysListData() call this function just after creating the + * filled copper area polygon (without clearance areas) + * to do that this function: + * 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area + * with m_ZoneMinThickness/2 value. + * The result is areas with a margin of m_ZoneMinThickness/2 + * When drawing outline with segments having a thickness of m_ZoneMinThickness, the + * outlines will match exactly the initial outlines + * 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance + + * m_ZoneMinThickness/2 + * in a buffer + * - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes + * 4 - calculates the polygon A - B + * 5 - put resulting list of polygons (filled areas) in m_FilledPolysList + * This zone contains pads with the same net. + * 6 - Remove insulated copper islands + * 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes: + * creates a buffer of polygons corresponding to stubs to remove + * sub them to the filled areas. + * Remove new insulated copper islands + */ + +void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ) +{ + int segsPerCircle; + double correctionFactor; + int outline_half_thickness = m_ZoneMinThickness / 2; + + + std::auto_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO( + g_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) ); + + // 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 ); + + CPOLYGONS_LIST tmp; + + if(g_DumpZonesWhenFilling) + dumper->BeginGroup("clipper-zone"); + + SHAPE_POLY_SET solidAreas = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ); + + solidAreas.Inflate( -outline_half_thickness, segsPerCircle ); + solidAreas.Simplify(); + + SHAPE_POLY_SET holes; + + if(g_DumpZonesWhenFilling) + dumper->Write( &solidAreas, "solid-areas" ); + + tmp.RemoveAllContours(); + buildFeatureHoleList( aPcb, holes ); + + if(g_DumpZonesWhenFilling) + dumper->Write( &holes, "feature-holes" ); + + holes.Simplify( true ); + + if (g_DumpZonesWhenFilling) + dumper->Write( &holes, "feature-holes-postsimplify" ); + + // Generate the filled areas (currently, without thermal shapes, which will + // be created later). + // Generate strictly simple polygons needed by Gerber files and Fracture() + solidAreas.BooleanSubtract( holes, false ); + + if (g_DumpZonesWhenFilling) + dumper->Write( &solidAreas, "solid-areas-minus-holes" ); + + SHAPE_POLY_SET fractured = solidAreas; + fractured.Fracture(); + + if (g_DumpZonesWhenFilling) + dumper->Write( &fractured, "fractured" ); + + m_FilledPolysList = fractured; + + // Remove insulated islands: + if( GetNetCode() > 0 ) + TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); + + SHAPE_POLY_SET thermalHoles; + + // Test thermal stubs connections and add polygons to remove unconnected stubs. + // (this is a refinement for thermal relief shapes) + if( GetNetCode() > 0 ) + BuildUnconnectedThermalStubsPolygonList( thermalHoles, aPcb, this, + correctionFactor, s_thermalRot ); + + // remove copper areas corresponding to not connected stubs + if( !thermalHoles.IsEmpty() ) + { + thermalHoles.Simplify(); + // Remove unconnected stubs. + // Generate strictly simple polygons needed by Gerber files and Fracture() + solidAreas.BooleanSubtract( thermalHoles, false ); + + if( g_DumpZonesWhenFilling ) + dumper->Write( &thermalHoles, "thermal-holes" ); + + // put these areas in m_FilledPolysList + SHAPE_POLY_SET fractured = solidAreas; + fractured.Fracture(); + + if( g_DumpZonesWhenFilling ) + dumper->Write ( &fractured, "fractured" ); + + m_FilledPolysList = fractured; + + if( GetNetCode() > 0 ) + TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); + } + + if(g_DumpZonesWhenFilling) + dumper->EndGroup(); +} + +void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) +{ +} |