/*******************************************/ /* zones_convert_brd_items_to_polygons.cpp */ /*******************************************/ /* Functions to convert some board items to polygons (pads, tracks ..) This is used to calculate filled areas in copper zones. Filled areas are the full zone area sub all polygons calculated from these items and the clearance area */ using namespace std; #include #include #include "fctsys.h" #include "common.h" #include "pcbnew.h" #include "trigo.h" #include "zones.h" #include "PolyLine.h" extern void Test_For_Copper_Island_And_Remove( BOARD* aPcb, ZONE_CONTAINER* aZone_container ); // Local Functions: void AddTrackWithClearancePolygon( Bool_Engine* aBooleng, TRACK& aTrack, int aClearanceValue ); void AddPadWithClearancePolygon( Bool_Engine* aBooleng, D_PAD& aPad, int aClearanceValue ); void AddThermalReliefPadPolygon( Bool_Engine* aBooleng, D_PAD& aPad, int aThermalGap, int aCopperThickness, int aMinThicknessValue ); void AddRoundedEndsSegmentPolygon( Bool_Engine* aBooleng, wxPoint aStart, wxPoint aEnd, int aWidth ); void AddTextBoxWithClearancePolygon( Bool_Engine* aBooleng, TEXTE_PCB* aText, int aClearanceValue ); // Local Variables: /* how many segments are used to create a polygon from a circle: */ static int s_CircleToSegmentsCount = 16; /* default value. the real value will be changed to 32 * if g_Zone_Arc_Approximation == 1 */ double s_Correction; /* mult coeff used to enlarge rounded and oval pads (and vias) * because the segment approximation for arcs and circles * create a smaller gap than a true circle */ /** function AddClearanceAreasPolygonsToPolysList * Supports a min thickness area constraint. * Add non copper areas polygons (pads and tracks with clearence) * 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 clearence areas * to do that this function: * 1 - creates a Bool_Engine,with option: holes are linked to outer contours by double overlapping segments * this means the created polygons have no holes (hole are linked to outer outline by double overlapped segments * and are therefore compatible with draw functions (DC draw polygons and Gerber or PS outputs) * 2 - Add the main outline (zone outline) in group A * 3 - Creates a correction using BOOL_CORRECTION operation 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 wilm * match exactly the initial outlines * 4 - recreates the same Bool_Engine, with no correction * 3 - Add the main outline (zone outline) in group A * 4 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance + m_ZoneMinThickness/2 * 5 - calculates the polygon A - B * 6 - put resulting list of polygons (filled areas) in m_FilledPolysList * 7 - Remove insulated copper islands */ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) { // Set the number of segments in arc approximations if( m_ArcToSegmentsCount == 32 ) s_CircleToSegmentsCount = 32; else s_CircleToSegmentsCount = 16; /* 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 ) */ s_Correction = 1.0 / cos( 3.14159265 / s_CircleToSegmentsCount ); /* Uses a kbool engine to add holes in the m_FilledPolysList polygon. * Because this function is called just after creating the m_FilledPolysList, * only one polygon is in list. * (initial holes in zones are linked into outer contours by double overlapping segments). * because after adding holes, many polygons could be exist in this list. */ Bool_Engine* booleng = new Bool_Engine(); ArmBoolEng( booleng, true ); /* First, Add the main polygon (i.e. the filled area using only one outline) * in GroupA in Bool_Engine to do a BOOL_CORRECTION operation * to reserve a m_ZoneMinThickness/2 margind around the outlines and holes * the margind will be filled when redraw outilnes with segments having a whidth set to * m_ZoneMinThickness * so m_ZoneMinThickness is the min thickness of the filled zones areas */ CopyPolygonsFromFilledPolysListToBoolengine( booleng, GROUP_A ); booleng->SetCorrectionFactor( (double) -m_ZoneMinThickness/2 ); booleng->Do_Operation( BOOL_CORRECTION ); /* No copy the new outline in m_FilledPolysList */ m_FilledPolysList.clear(); CopyPolygonsFromBoolengineToFilledPolysList( booleng ); delete booleng; /* Second, Add the main (corrected) polygon (i.e. the filled area using only one outline) * in GroupA in Bool_Engine to do a BOOL_A_SUB_B operation * All areas to remove will be put in GroupB in Bool_Engine */ booleng = new Bool_Engine(); ArmBoolEng( booleng, true ); /* Add the main corrected polygon (i.e. the filled area using only one outline) * in GroupA in Bool_Engine */ CopyPolygonsFromFilledPolysListToBoolengine( booleng, GROUP_A ); // Calculates the clearance value that meet DRC requirements int clearance = max( m_ZoneClearance, g_DesignSettings.m_TrackClearence ); clearance += m_ZoneMinThickness/2; /* Add holes (i.e. tracks and pads areas as polygons outlines) * in GroupB in Bool_Engine */ /* items ouside the zone bounding box are skipped */ EDA_Rect item_boundingbox; EDA_Rect zone_boundingbox = GetBoundingBox(); zone_boundingbox.Inflate( m_ZoneClearance, clearance ); /* * First : Add pads */ for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->m_Pads; pad != NULL; pad = pad->Next() ) { if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNet() != GetNet() ) { item_boundingbox = pad->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) AddPadWithClearancePolygon( booleng, *pad, clearance ); continue; } switch( m_PadOption ) { case PAD_NOT_IN_ZONE: item_boundingbox = pad->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) AddPadWithClearancePolygon( booleng, *pad, clearance ); break; case THERMAL_PAD: item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( m_ThermalReliefGapValue, m_ThermalReliefGapValue ); if( item_boundingbox.Intersects( zone_boundingbox ) ) AddThermalReliefPadPolygon( booleng, *pad, m_ThermalReliefGapValue, m_ThermalReliefCopperBridgeValue, m_ZoneMinThickness); break; case PAD_IN_ZONE: break; } } } /* Add holes (i.e. tracks and pads areas as polygons outlines) * in GroupB in Bool_Engine * Next : Add tracks and vias */ for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNet() == GetNet() ) continue; item_boundingbox = track->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) AddTrackWithClearancePolygon( booleng, *track, clearance ); } // Draw graphic items (copper texts) and board edges // zone clearance is used here regardless of the g_DesignSettings.m_TrackClearence value for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->GetLayer() != GetLayer() && item->GetLayer() != EDGE_N ) continue; switch( item->Type() ) { case TYPEDRAWSEGMENT: AddRoundedEndsSegmentPolygon( booleng, ( (DRAWSEGMENT*) item )->m_Start, ( (DRAWSEGMENT*) item )->m_End, ( (DRAWSEGMENT*) item )->m_Width + (2 * m_ZoneClearance) ); break; case TYPETEXTE: if( ( (TEXTE_PCB*) item )->GetLength() == 0 ) break; AddTextBoxWithClearancePolygon( booleng, (TEXTE_PCB*) item, m_ZoneClearance ); break; default: break; } } /* compute copper areas */ booleng->Do_Operation( BOOL_A_SUB_B ); /* put these areas in m_FilledPolysList */ m_FilledPolysList.clear(); CopyPolygonsFromBoolengineToFilledPolysList( booleng ); delete booleng; // Remove insulated islands: if( GetNet() > 0 ) Test_For_Copper_Island_And_Remove_Insulated_Islands( aPcb ); } /** Function AddPadPolygonWithPadClearance * Add a polygon cutout for a pad in a zone area * Convert arcs and circles to multiple straight lines */ void AddPadWithClearancePolygon( Bool_Engine* aBooleng, D_PAD& aPad, int aClearanceValue ) { if( aBooleng->StartPolygonAdd( GROUP_B ) == 0 ) return; wxPoint corner_position; int ii, angle; int dx = (aPad.m_Size.x / 2) + aClearanceValue; int dy = (aPad.m_Size.y / 2) + aClearanceValue; int delta = 3600 / s_CircleToSegmentsCount; // rot angle in 0.1 degree wxPoint PadShapePos = aPad.ReturnShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ switch( aPad.m_PadShape ) { case PAD_CIRCLE: dx = (int) (dx * s_Correction); for( ii = 0; ii < s_CircleToSegmentsCount; ii++ ) { corner_position = wxPoint( dx, 0 ); angle = ii * delta; RotatePoint( &corner_position, angle ); corner_position += PadShapePos; aBooleng->AddPoint( corner_position.x, corner_position.y ); } break; case PAD_OVAL: angle = aPad.m_Orient; if( dy > dx ) // Oval pad X/Y ratio for choosing translation axles { dy = (int) (dy * s_Correction); int angle_pg; // Polygon angle wxPoint shape_offset = wxPoint( 0, (dy - dx) ); RotatePoint( &shape_offset, angle ); // Rotating shape offset vector with component for( ii = 0; ii < s_CircleToSegmentsCount / 2 + 1; ii++ ) // Half circle end cap... { corner_position = wxPoint( dx, 0 ); // Coordinate translation +dx RotatePoint( &corner_position, angle ); angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos - shape_offset; aBooleng->AddPoint( corner_position.x, corner_position.y ); } for( ii = 0; ii < s_CircleToSegmentsCount / 2 + 1; ii++ ) // Second half circle end cap... { corner_position = wxPoint( -dx, 0 ); // Coordinate translation -dx RotatePoint( &corner_position, angle ); angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos + shape_offset; aBooleng->AddPoint( corner_position.x, corner_position.y ); } break; } else //if( dy <= dx ) { dx = (int) (dx * s_Correction); int angle_pg; // Polygon angle wxPoint shape_offset = wxPoint( (dy - dx), 0 ); RotatePoint( &shape_offset, angle ); for( ii = 0; ii < s_CircleToSegmentsCount / 2 + 1; ii++ ) { corner_position = wxPoint( 0, dy ); RotatePoint( &corner_position, angle ); angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos - shape_offset; aBooleng->AddPoint( corner_position.x, corner_position.y ); } for( ii = 0; ii < s_CircleToSegmentsCount / 2 + 1; ii++ ) { corner_position = wxPoint( 0, -dy ); RotatePoint( &corner_position, angle ); angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos + shape_offset; aBooleng->AddPoint( corner_position.x, corner_position.y ); } break; } case PAD_RECT: angle = aPad.m_Orient; corner_position = wxPoint( -dx, -dy ); RotatePoint( &corner_position, angle ); corner_position += PadShapePos; aBooleng->AddPoint( corner_position.x, corner_position.y ); corner_position = wxPoint( -dx, +dy ); RotatePoint( &corner_position, angle ); corner_position += aPad.ReturnShapePos(); aBooleng->AddPoint( corner_position.x, corner_position.y ); corner_position = wxPoint( +dx, +dy ); RotatePoint( &corner_position, angle ); corner_position += PadShapePos; aBooleng->AddPoint( corner_position.x, corner_position.y ); corner_position = wxPoint( +dx, -dy ); RotatePoint( &corner_position, angle ); corner_position += PadShapePos; aBooleng->AddPoint( corner_position.x, corner_position.y ); break; } aBooleng->EndPolygonAdd(); } /** function AddThermalReliefPadPolygon * Add holes around a pad to create a thermal relief * copper tickness is min (dx/2, aCopperWitdh) or min (dy/2, aCopperWitdh) * @param aBooleng = current Bool_Engine * @param aPad = the current pad used to create the thermal shape * @param aThermalGap = gap in thermal shape * @param aMinThicknessValue = min copper thickness allowed */ /* thermal reliefs are created as 4 polygons. * each corner of a polygon if calculated for a pad at position 0, 0, orient 0, * and then moved and rotated acroding to the pad position and orientation */ /* WARNING: * When Kbool calculates the filled areas : * i.e when substarcting holes (thermal shapes) to the full zone area * under certains circumstances kboll drop some holes. * These circumstances are: * some identical holes (same thermal shape and size) are *exactly* on the same vertical line * And * nothing else between holes * And * angles less than 90 deg between 2 consecutive lines in hole outline * And a hole above the identical holes * * In fact, it is easy to find these conditions in pad arrays. * So to avoid this, the workaround is do not use holes outlines that include * angles less than 90 deg between 2 consecutive lines * this is made in round and oblong thermal reliefs * * Note: polygons are drawm using outlines witk a thickness = aMinThicknessValue * so shapes must keep in account this outline thickness */ void AddThermalReliefPadPolygon( Bool_Engine* aBooleng, D_PAD& aPad, int aThermalGap, int aCopperThickness, int aMinThicknessValue ) { wxPoint corner, corner_end; wxPoint PadShapePos = aPad.ReturnShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ int angle = 0; wxSize copper_tickness; int dx = aPad.m_Size.x / 2; int dy = aPad.m_Size.y / 2; int delta = 3600 / s_CircleToSegmentsCount; // rot angle in 0.1 degree /* Keep in account the polygon outline thickness * aThermalGap must be increased by aMinThicknessValue/2 because drawing external outline * with a thickness of aMinThicknessValue will reduce gap by aMinThicknessValue/2 */ aThermalGap += aMinThicknessValue/2; /* Keep in account the polygon outline thickness * copper_tickness must be decreased by aMinThicknessValue because drawing outlines * with a thickness of aMinThicknessValue will increase thickness by aMinThicknessValue */ aCopperThickness -= aMinThicknessValue; if ( aCopperThickness < 0 ) aCopperThickness = 0; copper_tickness.x = min( dx, aCopperThickness ); copper_tickness.y = min( dy, aCopperThickness ); if ( copper_tickness.x < aMinThicknessValue ) copper_tickness.x = 0; if ( copper_tickness.y < aMinThicknessValue ) copper_tickness.y = 0; switch( aPad.m_PadShape ) { case PAD_CIRCLE: // Add 4 similar holes { /* we create 4 copper holes and put them in position 1, 2, 3 and 4 * here is the area of the rectangular pad + its thermal gap * the 4 copper holes remove the copper in order to create the thermal gap * 4 ------ 1 * | | * | | * | | * | | * 3 ------ 2 * holes 2, 3, 4 are the same as hole 1, rotated 90, 180, 270 deg */ // Build the hole pattern, for the hole in the X >0, Y > 0 plane: // The pattern roughtly is a 90 deg arc pie std::vector corners_buffer; // Radius of outer arcs of the shape: int outer_radius = dx + aThermalGap; // The radius of the outer arc is pad radius + aThermalGap // Crosspoint of thermal spoke sides, the first point of polygon buffer corners_buffer.push_back( wxPoint( copper_tickness.x / 2, copper_tickness.y / 2 ) ); // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side and first seg of arc approx corner.x = copper_tickness.x / 2; int y = outer_radius - (aThermalGap / 4); corner.y = (int) sqrt( ( ( (double) y * y ) - (double) corner.x * corner.x ) ); corners_buffer.push_back( corner ); // calculate the starting point of the outter arc corner.x = copper_tickness.x / 2; double dtmp = sqrt( ( (double) outer_radius * outer_radius ) - ( (double) corner.x * corner.x ) ); corner.y = (int) dtmp; RotatePoint( &corner, 90 ); // calculate the ending point of the outter arc corner_end.x = corner.y; corner_end.y = corner.x; // calculate intermediate points (y coordinate from corner.y to corner_end.y while( (corner.y > corner_end.y) && (corner.x < corner_end.x) ) { corners_buffer.push_back( corner ); RotatePoint( &corner, delta ); } corners_buffer.push_back( corner_end ); /* add an intermediate point, to avoid angles < 90 deg between last arc approx line and radius line */ corner.x = corners_buffer[1].y; corner.y = corners_buffer[1].x; corners_buffer.push_back( corner ); // Now, add the 4 holes ( each is the pattern, rotated by 0, 90, 180 and 270 deg // WARNING: problems with kbool if angle = 0 (in fact when angle < 200): // bad filled polygon on some cases, when pads are on a same vertical line // this seems a bug in kbool polygon (exists in 1.9 kbool version) // angle = 450 (45.0 degrees orientation) seems work fine. // angle = 0 with thermal shapes without angle < 90 deg has problems in rare circumstances angle = 450; int angle_pad = aPad.m_Orient; // Pad orientation for( unsigned ihole = 0; ihole < 4; ihole++ ) { if( aBooleng->StartPolygonAdd( GROUP_B ) ) { for( unsigned ii = 0; ii < corners_buffer.size(); ii++ ) { corner = corners_buffer[ii]; RotatePoint( &corner, angle + angle_pad ); // Rotate by segment angle and pad orientation corner += PadShapePos; aBooleng->AddPoint( corner.x, corner.y ); } aBooleng->EndPolygonAdd(); angle += 900; // Note: angle in in 0.1 deg. } } } break; case PAD_OVAL: { // Oval pad support along the lines of round and rectangular pads std::vector corners_buffer; // Polygon buffer as vector int dx = (aPad.m_Size.x / 2) + aThermalGap; // Cutout radius x int dy = (aPad.m_Size.y / 2) + aThermalGap; // Cutout radius y wxPoint shape_offset; // We want to calculate an oval shape with dx > dy. // if this is not the case, exchange dx and dy, and rotate the shape 90 deg. int supp_angle = 0; if( dx < dy ) { EXCHG( dx, dy ); supp_angle = 900; EXCHG( copper_tickness.x, copper_tickness.y); } int deltasize = dx - dy; // = distance between shape position and the 2 demi-circle ends centre // here we have dx > dy // Radius of outer arcs of the shape: int outer_radius = dy; // The radius of the outer arc is radius end + aThermalGap // Some coordinate fiddling, depending on the shape offset direction shape_offset = wxPoint( deltasize, 0 ); // Crosspoint of thermal spoke sides, the first point of polygon buffer corners_buffer.push_back( wxPoint( copper_tickness.x / 2, copper_tickness.y / 2 ) ); // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge if( copper_tickness.x > deltasize ) // If copper thickness is more than shape offset, we need to calculate arc intercept point. { corner.x = copper_tickness.x / 2; corner.y = (int) sqrt( ((double)outer_radius * outer_radius ) - ( (double)( corner.x - delta ) * ( corner.x - deltasize ) ) ); corner.x -= deltasize; /* creates an intermediate point, to have a > 90 deg angle * between the side and the first segment of arc approximation */ wxPoint intpoint = corner; intpoint.y -= aThermalGap/4; corners_buffer.push_back( intpoint + shape_offset ); RotatePoint( &corner, 90 ); } else { corner.x = copper_tickness.x / 2; corner.y = outer_radius; corners_buffer.push_back( corner ); corner.x = ( deltasize - copper_tickness.x ) / 2; } // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side and first seg of arc approx wxPoint last_corner; last_corner.y = copper_tickness.y / 2; int px = outer_radius - (aThermalGap / 4); last_corner.x = (int) sqrt( ( ( (double) px * px ) - (double) last_corner.y * last_corner.y ) ); // Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge corner_end.y = copper_tickness.y / 2; corner_end.x = (int) sqrt( ( (double) outer_radius * outer_radius ) - ( (double) corner_end.y * corner_end.y ) ); RotatePoint( &corner_end, -90 ); // calculate intermediate arc points till limit is reached while( (corner.y > corner_end.y) && (corner.x < corner_end.x) ) { corners_buffer.push_back( corner + shape_offset ); RotatePoint( &corner, delta ); } //corners_buffer.push_back(corner + shape_offset); // TODO: about one mil geometry error forms somewhere. corners_buffer.push_back( corner_end + shape_offset ); corners_buffer.push_back( last_corner + shape_offset ); // Enabling the line above shows intersection point. /* Create 2 holes, rotated by pad rotation. */ angle = aPad.m_Orient + supp_angle; for( int irect = 0; irect < 2; irect++ ) { if( aBooleng->StartPolygonAdd( GROUP_B ) ) { for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint cpos = corners_buffer[ic]; RotatePoint( &cpos, angle ); cpos += PadShapePos; aBooleng->AddPoint( cpos.x, cpos.y ); } aBooleng->EndPolygonAdd(); angle += 1800; // this is calculate hole 3 if( angle >= 3600 ) angle -= 3600; } } // Create holes, that are the mirrored from the previous holes for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint swap = corners_buffer[ic]; swap.x = -swap.x; corners_buffer[ic] = swap; } // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg angle = aPad.m_Orient + supp_angle; for( int irect = 0; irect < 2; irect++ ) { if( aBooleng->StartPolygonAdd( GROUP_B ) ) { for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint cpos = corners_buffer[ic]; RotatePoint( &cpos, angle ); cpos += PadShapePos; aBooleng->AddPoint( cpos.x, cpos.y ); } aBooleng->EndPolygonAdd(); angle += 1800; if( angle >= 3600 ) angle -= 3600; } } } break; case PAD_RECT: // draw 4 Holes { /* we create 4 copper holes and put them in position 1, 2, 3 and 4 * here is the area of the rectangular pad + its thermal gap * the 4 copper holes remove the copper in order to create the thermal gap * 4 ------ 1 * | | * | | * | | * | | * 3 ------ 2 * hole 3 is the same as hole 1, rotated 180 deg * hole 4 is the same as hole 2, rotated 180 deg and is the same as hole 1, mirrored */ // First, create a rectangular hole for position 1 : // 2 ------- 3 // | | // | | // | | // 1 -------4 wxPoint corners_hole[4]; // buffer for 4 corners // Create 1 hole, for a pad centered at 0,0, orient 0 // Calculate coordinates for corner 1 to corner 4: corners_hole[0] = wxPoint( copper_tickness.x / 2, -copper_tickness.y / 2 ); corners_hole[1] = wxPoint( (copper_tickness.x / 2), -dy - aThermalGap ); corners_hole[2] = wxPoint( dx + aThermalGap, -dy - aThermalGap ); corners_hole[3] = wxPoint( dx + aThermalGap, -(copper_tickness.y / 2) ); /* Create 2 holes, rotated by pad rotation. */ angle = aPad.m_Orient; for( int irect = 0; irect < 2; irect++ ) { if( aBooleng->StartPolygonAdd( GROUP_B ) ) { for( int ic = 0; ic < 4; ic++ ) { wxPoint cpos = corners_hole[ic]; RotatePoint( &cpos, angle ); cpos += PadShapePos; aBooleng->AddPoint( cpos.x, cpos.y ); } aBooleng->EndPolygonAdd(); angle += 1800; // this is calculate hole 3 if( angle >= 3600 ) angle -= 3600; } } // Create a holes, that is the mirrored of the previous hole corners_hole[0].x = -corners_hole[0].x; corners_hole[1].x = -corners_hole[1].x; corners_hole[2].x = -corners_hole[2].x; corners_hole[3].x = -corners_hole[3].x; // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg angle = aPad.m_Orient; for( int irect = 0; irect < 2; irect++ ) { if( aBooleng->StartPolygonAdd( GROUP_B ) ) { for( int ic = 0; ic < 4; ic++ ) { wxPoint cpos = corners_hole[ic]; RotatePoint( &cpos, angle ); cpos += PadShapePos; aBooleng->AddPoint( cpos.x, cpos.y ); } aBooleng->EndPolygonAdd(); angle += 1800; if( angle >= 3600 ) angle -= 3600; } } } break; } } /** Function AddTrackWithClearancePolygon * Add a polygon cutout for a track in a zone area * Convert arcs and circles to multiple straight lines */ void AddTrackWithClearancePolygon( Bool_Engine* aBooleng, TRACK& aTrack, int aClearanceValue ) { wxPoint corner_position; int ii, angle; int dx = (aTrack.m_Width / 2) + aClearanceValue; int delta = 3600 / s_CircleToSegmentsCount; // rot angle in 0.1 degree switch( aTrack.Type() ) { case TYPEVIA: if( aBooleng->StartPolygonAdd( GROUP_B ) ) { dx = (int) (dx * s_Correction); for( ii = 0; ii < s_CircleToSegmentsCount; ii++ ) { corner_position = wxPoint( dx, 0 ); angle = ii * delta; RotatePoint( &corner_position, angle ); corner_position += aTrack.m_Start; aBooleng->AddPoint( corner_position.x, corner_position.y ); } aBooleng->EndPolygonAdd(); } break; default: AddRoundedEndsSegmentPolygon( aBooleng, aTrack.m_Start, aTrack.m_End, aTrack.m_Width + (2 * aClearanceValue) ); break; } } /** Function AddRoundedEndsSegmentPolygon * Add a polygon cutout for a segment (with rounded ends) in a zone area * Convert arcs to multiple straight lines */ void AddRoundedEndsSegmentPolygon( Bool_Engine* aBooleng, wxPoint aStart, wxPoint aEnd, int aWidth ) { int rayon = aWidth / 2; wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0) wxPoint startp = aStart; wxPoint corner; int seg_len; // normalize the position in order to have endp.x >= 0; if( endp.x < 0 ) { endp = aStart - aEnd; startp = aEnd; } int delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees seg_len = (int) sqrt( ( (double) endp.y * endp.y ) + ( (double) endp.x * endp.x ) ); if( !aBooleng->StartPolygonAdd( GROUP_B ) ) return; // error! int delta = 3600 / s_CircleToSegmentsCount; // rot angle in 0.1 degree // Compute the outlines of the segment, and creates a polygon corner = wxPoint( 0, rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; aBooleng->AddPoint( corner.x, corner.y ); corner = wxPoint( seg_len, rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; aBooleng->AddPoint( corner.x, corner.y ); // add right rounded end: for( int ii = delta; ii < 1800; ii += delta ) { corner = wxPoint( 0, rayon ); RotatePoint( &corner, ii ); corner.x += seg_len; RotatePoint( &corner, -delta_angle ); corner += startp; aBooleng->AddPoint( corner.x, corner.y ); } corner = wxPoint( seg_len, -rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; aBooleng->AddPoint( corner.x, corner.y ); corner = wxPoint( 0, -rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; aBooleng->AddPoint( corner.x, corner.y ); // add left rounded end: for( int ii = delta; ii < 1800; ii += delta ) { corner = wxPoint( 0, -rayon ); RotatePoint( &corner, ii ); RotatePoint( &corner, -delta_angle ); corner += startp; aBooleng->AddPoint( corner.x, corner.y ); } aBooleng->EndPolygonAdd(); } /** function AddTextBoxWithClearancePolygon * creates a polygon containing the text and add it to bool engine */ void AddTextBoxWithClearancePolygon( Bool_Engine* aBooleng, TEXTE_PCB* aText, int aClearanceValue ) { int corners[8]; // Buffer of coordinates int ii; int dx = aText->Pitch() * aText->GetLength(); int dy = aText->m_Size.y + aText->m_Width; /* Creates bounding box (rectangle) for an horizontal text */ dx /= 2; dy /= 2; /* dx et dy = demi dimensionx X et Y */ dx += aClearanceValue; dy += aClearanceValue; corners[0] = aText->m_Pos.x - dx; corners[1] = aText->m_Pos.y - dy; corners[2] = aText->m_Pos.x + dx; corners[3] = aText->m_Pos.y - dy; corners[4] = aText->m_Pos.x + dx; corners[5] = aText->m_Pos.y + dy; corners[6] = aText->m_Pos.x - dx; corners[7] = aText->m_Pos.y + dy; // Rotate rectangle RotatePoint( &corners[0], &corners[1], aText->m_Pos.x, aText->m_Pos.y, aText->m_Orient ); RotatePoint( &corners[2], &corners[3], aText->m_Pos.x, aText->m_Pos.y, aText->m_Orient ); RotatePoint( &corners[4], &corners[5], aText->m_Pos.x, aText->m_Pos.y, aText->m_Orient ); RotatePoint( &corners[6], &corners[7], aText->m_Pos.x, aText->m_Pos.y, aText->m_Orient ); if( aBooleng->StartPolygonAdd( GROUP_B ) ) { for( ii = 0; ii < 8; ii += 2 ) { aBooleng->AddPoint( corners[ii], corners[ii + 1] ); } aBooleng->EndPolygonAdd(); } } /***********************************************************************************************************/ int ZONE_CONTAINER::CopyPolygonsFromFilledPolysListToBoolengine( Bool_Engine* aBoolengine, GroupType aGroup ) /************************************************************************************************************/ /** Function CopyPolygonsFromFilledPolysListToBoolengine * Copy (Add) polygons created by kbool (after Do_Operation) to m_FilledPolysList * @param aBoolengine = kbool engine * @param aGroup = group in kbool engine (GROUP_A or GROUP_B only) * @return the corner count */ { unsigned corners_count = m_FilledPolysList.size(); int count = 0; unsigned ic = 0; if( aBoolengine->StartPolygonAdd( aGroup ) ) { for( ; ic < corners_count; ic++ ) { CPolyPt* corner = &m_FilledPolysList[ic]; aBoolengine->AddPoint( corner->x, corner->y ); count++; if( corner->end_contour ) break; } aBoolengine->EndPolygonAdd(); } return count; } /*****************************************************************************************/ int ZONE_CONTAINER::CopyPolygonsFromBoolengineToFilledPolysList( Bool_Engine* aBoolengine ) /*****************************************************************************************/ /** Function CopyPolygonsFromBoolengineToFilledPolysList * Copy (Add) polygons created by kbool (after Do_Operation) to m_FilledPolysList * @param aBoolengine = kbool engine * @return the corner count */ { int count = 0; while( aBoolengine->StartPolygonGet() ) { CPolyPt corner( 0, 0, false ); while( aBoolengine->PolygonHasMorePoints() ) { corner.x = (int) aBoolengine->GetPolygonXPoint(); corner.y = (int) aBoolengine->GetPolygonYPoint(); corner.end_contour = false; m_FilledPolysList.push_back( corner ); count++; } corner.end_contour = true; m_FilledPolysList.pop_back(); m_FilledPolysList.push_back( corner ); aBoolengine->EndPolygonGet(); } return count; }