diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index b3b20e7b82..256f574c1a 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -21,16 +21,151 @@ * @param aCornerBuffer = a buffer to store the polygon * @param aStart = the segment start point coordinate * @param aEnd = the segment end point coordinate - * @param aWidth = the segment width * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = the segment width */ -void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, +void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aCircleToSegmentsCount, int aWidth ); -/** Function TransformTrackWithClearanceToPolygon +/** Function TransformArcToPolygon + * Creates a polygon from an Arc + * Convert arcs to multiple straight segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aCentre = centre of the arc or circle + * @param aStart = start point of the arc, or a point on the circle + * @param aArcAngle = arc angle in 0.1 degrees. For a circle, aArcAngle = 3600 + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = width (thickness) of the line + */ +void TransformArcToPolygon( std::vector & aCornerBuffer, + wxPoint aCentre, wxPoint aStart, int aArcAngle, + int aCircleToSegmentsCount, int aWidth ) +{ + wxPoint arc_start, arc_end; + int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree + + arc_end = arc_start = aStart; + if( aArcAngle != 3600 ) + { + RotatePoint( &arc_end, aCentre, -aArcAngle ); + } + + if( aArcAngle < 0 ) + { + EXCHG( arc_start, arc_end ); + NEGATE( aArcAngle ); + } + + // Compute the ends of segments and creates poly + wxPoint curr_end = arc_start; + wxPoint curr_start = arc_start; + for( int ii = delta; ii < aArcAngle; ii += delta ) + { + curr_end = arc_start; + RotatePoint( &curr_end, aCentre, -ii ); + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, + curr_start, curr_end, aCircleToSegmentsCount, aWidth ); + curr_start = curr_end; + } + + if( curr_end != arc_end ) + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, + curr_end, arc_end, aCircleToSegmentsCount, aWidth ); +} + + +/** Function TEXTE_PCB::TransformShapeWithClearanceToPolygon + * Convert the track shape to a closed polygon + * Used in filling zones calculations + * Circles and arcs are approximated by segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aClearanceValue = the clearance around the pad + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aCorrectionFactor = the correction to apply to circles radius to keep + * clearance when the circle is approximated by segment bigger or equal + * to the real clearance value (usually near from 1.0) + */ +void TEXTE_PCB::TransformShapeWithClearanceToPolygon( + std::vector & aCornerBuffer, + int aClearanceValue, + int aCircleToSegmentsCount, + double aCorrectionFactor ) +{ + if( GetLength() == 0 ) + return; + + CPolyPt corners[4]; // Buffer of polygon corners + + EDA_Rect rect = GetTextBox( -1 ); + rect.Inflate( aClearanceValue, aClearanceValue ); + corners[0].x = rect.GetOrigin().x; + corners[0].y = rect.GetOrigin().y; + corners[1].y = corners[0].y; + corners[1].x = rect.GetRight(); + corners[2].x = corners[1].x; + corners[2].y = rect.GetBottom(); + corners[3].y = corners[2].y; + corners[3].x = corners[0].x; + + for( int ii = 0; ii < 4; ii++ ) + { + // Rotate polygon + RotatePoint( &corners[ii].x, &corners[ii].y, + m_Pos.x, m_Pos.y, + m_Orient ); + aCornerBuffer.push_back( corners[ii] ); + } + + aCornerBuffer.back().end_contour = true; +} + + +/** Function DRAWSEGMENT::TransformShapeWithClearanceToPolygon + * Convert the track shape to a closed polygon + * Used in filling zones calculations + * Circles and arcs are approximated by segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aClearanceValue = the clearance around the pad + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aCorrectionFactor = the correction to apply to circles radius to keep + * clearance when the circle is approxiamted by segment bigger or equal + * to the real clearance value (usually near from 1.0) + */ +void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( + std::vector & aCornerBuffer, + int aClearanceValue, + int aCircleToSegmentsCount, + double aCorrectionFactor ) +{ + switch( m_Shape ) + { + case S_CIRCLE: + TransformArcToPolygon( aCornerBuffer, m_Start, // Circle centre + m_End, 3600, + aCircleToSegmentsCount, + m_Width + (2 * aClearanceValue) ); + break; + + case S_ARC: + TransformArcToPolygon( aCornerBuffer, m_Start, + m_End, m_Angle, + aCircleToSegmentsCount, + m_Width + (2 * aClearanceValue) ); + break; + + default: + TransformRoundedEndsSegmentToPolygon( + aCornerBuffer, m_Start, m_End, + aCircleToSegmentsCount, m_Width + (2 * aClearanceValue) ); + break; + } +} + + +/** Function TRACK::TransformShapeWithClearanceToPolygon * Convert the track shape to a closed polygon * Used in filling zones calculations * Circles (vias) and arcs (ends of tracks) are approximated by segments @@ -41,16 +176,15 @@ void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, * clearance when the circle is approxiamted by segment bigger or equal * to the real clearance value (usually near from 1.0) */ -void TRACK::TransformTrackWithClearanceToPolygon( std::vector & aCornerBuffer, - int aClearanceValue, - int aCircleToSegmentsCount, - double aCorrectionFactor ) +void TRACK:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCornerBuffer, + int aClearanceValue, + int aCircleToSegmentsCount, + double aCorrectionFactor ) { wxPoint corner_position; int ii, angle; - int dx = (m_Width / 2) + aClearanceValue; - - int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree + int dx = (m_Width / 2) + aClearanceValue; + int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree switch( Type() ) { @@ -59,19 +193,25 @@ void TRACK::TransformTrackWithClearanceToPolygon( std::vector & aCorner for( ii = 0; ii < aCircleToSegmentsCount; ii++ ) { corner_position = wxPoint( dx, 0 ); - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); + RotatePoint( &corner_position.x, &corner_position.y, + (1800 / aCircleToSegmentsCount) ); angle = ii * delta; - RotatePoint( &corner_position, angle ); - corner_position += m_Start; - aCornerBuffer.push_back( corner_position ); + RotatePoint( &corner_position.x, &corner_position.y, angle ); + corner_position.x += m_Start.x; + corner_position.y += m_Start.y; + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } + aCornerBuffer.back().end_contour = true; break; default: - TransformRoundedEndsSegmentToPolygon( aCornerBuffer, - m_Start, m_End, aCircleToSegmentsCount, - m_Width + (2 * aClearanceValue) ); + TransformRoundedEndsSegmentToPolygon( + aCornerBuffer, + m_Start, m_End, + aCircleToSegmentsCount, + m_Width + ( 2 * aClearanceValue) ); break; } } @@ -79,7 +219,7 @@ void TRACK::TransformTrackWithClearanceToPolygon( std::vector & aCorner /* Function TransformRoundedEndsSegmentToPolygon */ -void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, +void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aCircleToSegmentsCount, int aWidth ) @@ -89,6 +229,7 @@ void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, wxPoint startp = aStart; wxPoint corner; int seg_len; + CPolyPt polypoint; // normalize the position in order to have endp.x >= 0; if( endp.x < 0 ) @@ -105,12 +246,16 @@ void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, corner = wxPoint( 0, rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; - aCornerBuffer.push_back( corner ); + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); corner = wxPoint( seg_len, rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; - aCornerBuffer.push_back( corner ); + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); // add right rounded end: for( int ii = delta; ii < 1800; ii += delta ) @@ -120,18 +265,24 @@ void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, corner.x += seg_len; RotatePoint( &corner, -delta_angle ); corner += startp; - aCornerBuffer.push_back( corner ); + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); } corner = wxPoint( seg_len, -rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; - aCornerBuffer.push_back( corner ); + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); corner = wxPoint( 0, -rayon ); RotatePoint( &corner, -delta_angle ); corner += startp; - aCornerBuffer.push_back( corner ); + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); // add left rounded end: for( int ii = delta; ii < 1800; ii += delta ) @@ -140,12 +291,16 @@ void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, RotatePoint( &corner, ii ); RotatePoint( &corner, -delta_angle ); corner += startp; - aCornerBuffer.push_back( corner ); + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); } + + aCornerBuffer.back().end_contour = true; } -/** function TransformPadWithClearanceToPolygon +/** function D_PAD::TransformShapeWithClearanceToPolygon * Convert the pad shape to a closed polygon * Used in filling zones calculations * Circles and arcs are approximated by segments @@ -156,22 +311,22 @@ void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, * clearance when the circle is approxiamted by segment bigger or equal * to the real clearance value (usually near from 1.0) */ -void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBuffer, - int aClearanceValue, - int aCircleToSegmentsCount, - double aCorrectionFactor ) +void D_PAD:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCornerBuffer, + int aClearanceValue, + int aCircleToSegmentsCount, + double aCorrectionFactor ) { wxPoint corner_position; int ii, angle; int dx = (m_Size.x / 2) + aClearanceValue; int dy = (m_Size.y / 2) + aClearanceValue; - int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree - wxPoint PadShapePos = ReturnShapePos(); /* Note: for pad having a shape offset, - * the pad position is NOT the shape position */ - wxSize psize = m_Size; /* pad size unsed in RECT and TRAPEZOIDAL pads - * trapezoidal pads are considered as rect pad shape having they boudary box size - */ + int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree + wxPoint PadShapePos = ReturnShapePos(); /* Note: for pad having a shape offset, + * the pad position is NOT the shape position */ + wxSize psize = m_Size; /* pad size unsed in RECT and TRAPEZOIDAL pads + * trapezoidal pads are considered as rect pad shape having they boudary box size + */ switch( m_PadShape ) { @@ -180,52 +335,63 @@ void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBu for( ii = 0; ii < aCircleToSegmentsCount; ii++ ) { corner_position = wxPoint( dx, 0 ); - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); // Half increment offset to get more space between + RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); + + // Half increment offset to get more space between angle = ii * delta; RotatePoint( &corner_position, angle ); corner_position += PadShapePos; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } + aCornerBuffer.back().end_contour = true; break; case PAD_OVAL: angle = m_Orient; - if( dy > dx ) // Oval pad X/Y ratio for choosing translation axles + if( dy > dx ) // Oval pad X/Y ratio for choosing translation axles { dy = (int) ( dy * aCorrectionFactor ); int angle_pg; // Polygon angle - wxPoint shape_offset = wxPoint( 0, (dy - dx) ); + wxPoint shape_offset = wxPoint( 0, dy - dx ); RotatePoint( &shape_offset, angle ); // Rotating shape offset vector with component for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) // Half circle end cap... { - corner_position = wxPoint( dx, 0 ); // Coordinate translation +dx + corner_position = wxPoint( dx, 0 ); + + // Coordinate translation +dx RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); RotatePoint( &corner_position, angle ); angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos - shape_offset; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } - for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) // Second half circle end cap... + for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) // Second half circle end cap... { - corner_position = wxPoint( -dx, 0 ); // Coordinate translation -dx + corner_position = wxPoint( -dx, 0 ); + + // Coordinate translation -dx RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); RotatePoint( &corner_position, angle ); angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos + shape_offset; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } + aCornerBuffer.back().end_contour = true; break; } else //if( dy <= dx ) { dx = (int) ( dx * aCorrectionFactor ); - int angle_pg; // Polygon angle + int angle_pg; // Polygon angle wxPoint shape_offset = wxPoint( (dy - dx), 0 ); RotatePoint( &shape_offset, angle ); @@ -237,7 +403,8 @@ void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBu angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos - shape_offset; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) @@ -248,9 +415,11 @@ void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBu angle_pg = ii * delta; RotatePoint( &corner_position, angle_pg ); corner_position += PadShapePos + shape_offset; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } + aCornerBuffer.back().end_contour = true; break; } @@ -260,21 +429,28 @@ void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBu psize.y += ABS( m_DeltaSize.x ); // fall through - case PAD_RECT: // Easy implementation for rectangular cutouts with rounded corners // Easy implementation for rectangular cutouts with rounded corners + case PAD_RECT: // Easy implementation for rectangular cutouts with rounded corners // Easy implementation for rectangular cutouts with rounded corners angle = m_Orient; - int rounding_radius = (int) ( aClearanceValue * aCorrectionFactor ); // Corner rounding radius - int angle_pg; // Polygon increment angle + int rounding_radius = (int) ( aClearanceValue * aCorrectionFactor ); // Corner rounding radius + int angle_pg; // Polygon increment angle for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ ) { corner_position = wxPoint( 0, -rounding_radius ); - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); // Start at half increment offset + RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); + + // Start at half increment offset angle_pg = i * delta; - RotatePoint( &corner_position, angle_pg ); // Rounding vector rotation - corner_position -= psize / 2; // Rounding vector + Pad corner offset - RotatePoint( &corner_position, angle ); // Rotate according to module orientation - corner_position += PadShapePos; // Shift origin to position - aCornerBuffer.push_back( corner_position ); + RotatePoint( &corner_position, angle_pg ); + + // Rounding vector rotation + corner_position -= psize / 2; // Rounding vector + Pad corner offset + RotatePoint( &corner_position, angle ); + + // Rotate according to module orientation + corner_position += PadShapePos; // Shift origin to position + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ ) @@ -286,10 +462,13 @@ void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBu corner_position -= wxPoint( psize.x / 2, -psize.y / 2 ); RotatePoint( &corner_position, angle ); corner_position += PadShapePos; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } - for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ ) + for( int i = 0; + i < aCircleToSegmentsCount / 4 + 1; + i++ ) { corner_position = wxPoint( 0, rounding_radius ); RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); @@ -298,7 +477,8 @@ void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBu corner_position += psize / 2; RotatePoint( &corner_position, angle ); corner_position += PadShapePos; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); } for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ ) @@ -310,9 +490,398 @@ void D_PAD::TransformPadWithClearanceToPolygon( std::vector & aCornerBu corner_position -= wxPoint( -psize.x / 2, psize.y / 2 ); RotatePoint( &corner_position, angle ); corner_position += PadShapePos; - aCornerBuffer.push_back( corner_position ); + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); + } + + aCornerBuffer.back().end_contour = true; + break; + } +} + + +/** function CreateThermalReliefPadPolygon + * Add holes around a pad to create a thermal relief + * copper thickness is min (dx/2, aCopperWitdh) or min (dy/2, aCopperWitdh) + * @param aCornerBuffer = a buffer to store the polygon + * @param aPad = the current pad used to create the thermal shape + * @param aThermalGap = gap in thermal shape + * @param aMinThicknessValue = min copper thickness allowed + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aCorrectionFactor = the correction to apply to circles radius to keep + * @param aThermalRot = for rond pads the rotation of thermal stubs (450 usually for 45 deg.) + */ + +/* 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 substracting 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 (sometime occurs without this condition) + * 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 1: polygons are drawm using outlines witk a thickness = aMinThicknessValue + * so shapes must keep in account this outline thickness + * + * Note 2: + * Trapezoidal pads are not considered here because they are very special case + * and are used in microwave applications and they *DO NOT* have a thermal relief that change the shape + * by creating stubs and destroy their properties. + */ +void CreateThermalReliefPadPolygon( std::vector& aCornerBuffer, + D_PAD& aPad, + int aThermalGap, + int aCopperThickness, + int aMinThicknessValue, + int aCircleToSegmentsCount, + double aCorrectionFactor, + int aThermalRot ) +{ + wxPoint corner, corner_end; + wxPoint PadShapePos = aPad.ReturnShapePos(); /* Note: for pad having a shape offset, + * the pad position is NOT the shape position */ + wxSize copper_thickness; + int dx = aPad.m_Size.x / 2; + int dy = aPad.m_Size.y / 2; + + int delta = 3600 / aCircleToSegmentsCount; // 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_thickness must be decreased by aMinThicknessValue because drawing outlines + * with a thickness of aMinThicknessValue will increase real thickness by aMinThicknessValue + */ + aCopperThickness -= aMinThicknessValue; + if( aCopperThickness < 0 ) + aCopperThickness = 0; + + copper_thickness.x = min( dx, aCopperThickness ); + copper_thickness.y = min( dy, aCopperThickness ); + + 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_thickness.x / 2, copper_thickness.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_thickness.x / 2; + int y = outer_radius - (aThermalGap / 4); + corner.y = (int) sqrt( ( ( (double) y * y ) - (double) corner.x * corner.x ) ); + if( aThermalRot != 0 ) + corners_buffer.push_back( corner ); + + // calculate the starting point of the outter arc + corner.x = copper_thickness.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 2.0 kbool version) + // aThermalRot = 450 (45.0 degrees orientation) seems work fine. + // aThermalRot = 0 with thermal shapes without angle < 90 deg has problems in rare circumstances + // Note: with the 2 step build ( thermal shapes added after areas are built), 0 seems work + int angle_pad = aPad.m_Orient; // Pad orientation + int th_angle = aThermalRot; + for( unsigned ihole = 0; ihole < 4; ihole++ ) + { + for( unsigned ii = 0; ii < corners_buffer.size(); ii++ ) + { + corner = corners_buffer[ii]; + RotatePoint( &corner, th_angle + angle_pad ); // Rotate by segment angle and pad orientation + corner += PadShapePos; + aCornerBuffer.push_back( CPolyPt( corner.x, corner.y ) ); + } + + aCornerBuffer.back().end_contour = true; + th_angle += 900; // Note: th_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_thickness.x, copper_thickness.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_thickness.x / 2, copper_thickness.y / 2 ) ); + + // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge + if( copper_thickness.x > deltasize ) // If copper thickness is more than shape offset, we need to calculate arc intercept point. + { + corner.x = copper_thickness.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_thickness.x / 2; + corner.y = outer_radius; + corners_buffer.push_back( corner ); + corner.x = ( deltasize - copper_thickness.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_thickness.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_thickness.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. + */ + int angle = aPad.m_Orient + supp_angle; + for( int irect = 0; irect < 2; irect++ ) + { + for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) + { + wxPoint cpos = corners_buffer[ic]; + RotatePoint( &cpos, angle ); + cpos += PadShapePos; + aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) ); + } + + aCornerBuffer.back().end_contour = true;; + 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++ ) + { + for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) + { + wxPoint cpos = corners_buffer[ic]; + RotatePoint( &cpos, angle ); + cpos += PadShapePos; + aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) ); + } + + aCornerBuffer.back().end_contour = true; + 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 + + // Modified rectangles with one corner rounded. TODO: merging with oval thermals and possibly round too. + + 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 + + // The first point of polygon buffer is left lower corner, second the crosspoint of thermal spoke sides, + // the third is upper right corner and the rest are rounding vertices going anticlockwise. Note the inveted Y-axis in CG. + corners_buffer.push_back( wxPoint( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) ) ); // Adds small miters to zone + corners_buffer.push_back( wxPoint( -(dx - aThermalGap / 4), -copper_thickness.y / 2 ) ); // fill and spoke corner + corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -copper_thickness.y / 2 ) ); + corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -(dy - aThermalGap / 4) ) ); + corners_buffer.push_back( wxPoint( -(aThermalGap / 4 + copper_thickness.x / 2), -dy ) ); + + int angle = aPad.m_Orient; + int rounding_radius = (int) ( aThermalGap * aCorrectionFactor ); // Corner rounding radius + int angle_pg; // Polygon increment angle + + for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ ) + { + wxPoint corner_position = wxPoint( 0, -rounding_radius ); + RotatePoint( &corner_position, 1800 / aCircleToSegmentsCount ); // Start at half increment offset + angle_pg = i * delta; + RotatePoint( &corner_position, angle_pg ); // Rounding vector rotation + corner_position -= aPad.m_Size / 2; // Rounding vector + Pad corner offset + corners_buffer.push_back( wxPoint( corner_position.x, corner_position.y ) ); + } + + for( int irect = 0; irect < 2; irect++ ) + { + for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) + { + wxPoint cpos = corners_buffer[ic]; + RotatePoint( &cpos, angle ); // Rotate according to module orientation + cpos += PadShapePos; // Shift origin to position + aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) ); + } + + aCornerBuffer.back().end_contour = true; + 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 + for( int irect = 0; irect < 2; irect++ ) + { + for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) + { + wxPoint cpos = corners_buffer[ic]; + RotatePoint( &cpos, angle ); + cpos += PadShapePos; + aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) ); + } + + aCornerBuffer.back().end_contour = true; + angle += 1800; + if( angle >= 3600 ) + angle -= 3600; } break; } + } } diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index c3570e9d45..11e6aaef8c 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -4,6 +4,7 @@ #ifndef CLASS_DRAWSEGMENT_H #define CLASS_DRAWSEGMENT_H +#include "polyline.h" class DRAWSEGMENT : public BOARD_ITEM { @@ -15,8 +16,8 @@ public: int m_Shape; // Shape: line, Circle, Arc int m_Type; // Used in complex associations ( Dimensions.. ) int m_Angle; // Used only for Arcs: Arc angle in 1/10 deg - wxPoint m_BezierC1; // Bezier Control Point 1 - wxPoint m_BezierC2; // Bezier Control Point 1 + wxPoint m_BezierC1; // Bezier Control Point 1 + wxPoint m_BezierC2; // Bezier Control Point 1 std::vector m_BezierPoints; public: @@ -42,13 +43,13 @@ public: * Function GetStart * returns the starting point of the graphic */ - wxPoint GetStart() const; + wxPoint GetStart() const; /** * Function GetEnd * returns the ending point of the graphic */ - wxPoint GetEnd() const; + wxPoint GetEnd() const; /** * Function Save @@ -56,15 +57,15 @@ public: * @param aFile The FILE to write to. * @return bool - true if success writing else false. */ - bool Save( FILE* aFile ) const; + bool Save( FILE* aFile ) const; - bool ReadDrawSegmentDescr( FILE* File, int* LineNum ); + bool ReadDrawSegmentDescr( FILE* File, int* LineNum ); - void Copy( DRAWSEGMENT* source ); + void Copy( DRAWSEGMENT* source ); - void Draw( WinEDA_DrawPanel* panel, wxDC* DC, - int aDrawMode, const wxPoint& offset = ZeroOffset ); + void Draw( WinEDA_DrawPanel* panel, wxDC* DC, + int aDrawMode, const wxPoint& offset = ZeroOffset ); /** * Function DisplayInfo @@ -73,7 +74,7 @@ public: * Is virtual from EDA_BaseStruct. * @param frame A WinEDA_BasePcbFrame in which to print status information. */ - virtual void DisplayInfo( WinEDA_DrawFrame* frame ); + virtual void DisplayInfo( WinEDA_DrawFrame* frame ); /** @@ -82,7 +83,7 @@ public: * @param ref_pos A wxPoint to test * @return bool - true if a hit, else false */ - bool HitTest( const wxPoint& ref_pos ); + bool HitTest( const wxPoint& ref_pos ); /** * Function HitTest (overlayed) @@ -91,7 +92,7 @@ public: * @param refPos the given EDA_Rect to test * @return bool - true if a hit, else false */ - bool HitTest( EDA_Rect& refArea ); + bool HitTest( EDA_Rect& refArea ); /** * Function GetClass @@ -116,36 +117,56 @@ public: return hypot( delta.x, delta.y ); } + /** * Function Move * move this object. * @param const wxPoint& aMoveVector - the move vector for this object. */ - virtual void Move(const wxPoint& aMoveVector) + virtual void Move( const wxPoint& aMoveVector ) { m_Start += aMoveVector; - m_End += aMoveVector; + m_End += aMoveVector; } + /** * Function Rotate * Rotate this object. * @param const wxPoint& aRotCentre - the rotation point. * @param aAngle - the rotation angle in 0.1 degree. */ - virtual void Rotate(const wxPoint& aRotCentre, int aAngle); + virtual void Rotate( const wxPoint& aRotCentre, int aAngle ); /** * Function Flip * Flip this object, i.e. change the board side for this object * @param const wxPoint& aCentre - the rotation point. */ - virtual void Flip(const wxPoint& aCentre ); + virtual void Flip( const wxPoint& aCentre ); + + /** Function TransformShapeWithClearanceToPolygon + * Convert the track shape to a closed polygon + * Used in filling zones calculations + * Circles and arcs are approximated by segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aClearanceValue = the clearance around the pad + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aCorrectionFactor = the correction to apply to circles radius to keep + * clearance when the circle is approxiamted by segment bigger or equal + * to the real clearance value (usually near from 1.0) + */ + void TransformShapeWithClearanceToPolygon( + std::vector & aCornerBuffer, + int aClearanceValue, + int + aCircleToSegmentsCount, + double aCorrectionFactor ); #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ); -#endif +#endif }; diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 7fd44774a1..10afc03fee 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -5,6 +5,7 @@ class Pcb3D_GLCanvas; #include "pad_shapes.h" +#include "polyline.h" /* Default layers used for pads, according to the pad type. * this is default values only, they can be changed for a given pad @@ -141,7 +142,7 @@ public: m_Pos = aPos; } - /** function TransformPadWithClearanceToPolygon + /** function TransformShapeWithClearanceToPolygon * Convert the pad shape to a closed polygon * Used in filling zones calculations * Circles and arcs are approximated by segments @@ -152,7 +153,7 @@ public: * clearance when the circle is approxiamted by segment bigger or equal * to the real clearance value (usually near from 1.0) */ - void TransformPadWithClearanceToPolygon( std::vector & aCornerBuffer, + void TransformShapeWithClearanceToPolygon( std::vector & aCornerBuffer, int aClearanceValue, int aCircleToSegmentsCount, double aCorrectionFactor ); /** diff --git a/pcbnew/class_pcb_text.h b/pcbnew/class_pcb_text.h index e0ab18ebf2..bb188a5f15 100644 --- a/pcbnew/class_pcb_text.h +++ b/pcbnew/class_pcb_text.h @@ -5,6 +5,7 @@ #define CLASS_PCB_TEXT_H #include "base_struct.h" +#include "polyline.h" class TEXTE_PCB : public BOARD_ITEM, public EDA_TextStruct { @@ -110,6 +111,23 @@ public: return wxT("PTEXT"); } + /** Function TransformShapeWithClearanceToPolygon + * Convert the track shape to a closed polygon + * Used in filling zones calculations + * Circles and arcs are approximated by segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aClearanceValue = the clearance around the pad + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aCorrectionFactor = the correction to apply to circles radius to keep + * clearance when the circle is approximated by segment bigger or equal + * to the real clearance value (usually near from 1.0) + */ + void TransformShapeWithClearanceToPolygon( + std::vector & aCornerBuffer, + int aClearanceValue, + int aCircleToSegmentsCount, + double aCorrectionFactor ); + #if defined(DEBUG) /** * Function Show diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index 14100603c1..bb786f9c99 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -6,6 +6,7 @@ #define CLASS_TRACK_H #include "base_struct.h" +#include "polyline.h" // Via attributes (m_Shape parmeter) @@ -148,7 +149,7 @@ public: /* divers */ int Shape() const { return m_Shape & 0xFF; } - /** Function TransformTrackWithClearanceToPolygon + /** Function TransformShapeWithClearanceToPolygon * Convert the track shape to a closed polygon * Used in filling zones calculations * Circles (vias) and arcs (ends of tracks) are approximated by segments @@ -159,7 +160,7 @@ public: * clearance when the circle is approxiamted by segment bigger or equal * to the real clearance value (usually near from 1.0) */ - void TransformTrackWithClearanceToPolygon( std::vector & aCornerBuffer, + void TransformShapeWithClearanceToPolygon( std::vector & aCornerBuffer, int aClearanceValue, int aCircleToSegmentsCount, double aCorrectionFactor ); diff --git a/pcbnew/debug_kbool_key_file_fct.cpp b/pcbnew/debug_kbool_key_file_fct.cpp index 1dcd4d95c0..7f708a71f8 100644 --- a/pcbnew/debug_kbool_key_file_fct.cpp +++ b/pcbnew/debug_kbool_key_file_fct.cpp @@ -14,9 +14,15 @@ #if defined (CREATE_KBOOL_KEY_FILES) || (CREATE_KBOOL_KEY_FILES_FIRST_PASS) +// Helper class to handle a coordinate +struct kfcoord +{ + int x, y; +}; + static FILE* kdebugFile; static char sDate_Time[256]; - +static vector s_EntityCoordinates; void CreateKeyFile() { @@ -48,6 +54,8 @@ void CreateKeyFile() { wxMessageBox( wxT( "CreateKeyFile() cannot create output file" ) ); } + + s_EntityCoordinates.clear(); } @@ -58,11 +66,11 @@ void CloseKeyFile() fprintf( kdebugFile, "\nENDLIB;\n" ); fclose( kdebugFile ); } + s_EntityCoordinates.clear(); } const char* sCurrEntityName = NULL; -static int s_count; void OpenKeyFileEntity( const char* aName ) { @@ -74,7 +82,7 @@ void OpenKeyFileEntity( const char* aName ) fprintf( kdebugFile, "STRNAME %s;\n", aName ); } sCurrEntityName = aName; - s_count = 0; + s_EntityCoordinates.clear(); } @@ -84,20 +92,45 @@ void CloseKeyFileEntity() fprintf( kdebugFile, "\nENDSTR %s;\n", sCurrEntityName ); } - -void StartKeyFilePolygon(int aCornersCount, int aLayer) +/* start a polygon entity in key file +*/ +void StartKeyFilePolygon( int aLayer) { + s_EntityCoordinates.clear(); fprintf( kdebugFile, "\nBOUNDARY; LAYER %d; DATATYPE 0;\n", aLayer ); - fprintf( kdebugFile, " XY %d;\n", aCornersCount ); - s_count = 0; } -void EndKeyFileElement() +/* add a polygon corner to the current polygon entity in key file +*/ +void AddKeyFilePointXY( int aXcoord, int aYcoord) { - if ( s_count == 1 ) - fprintf( kdebugFile, "\n"); - fprintf( kdebugFile, "\nENDEL;\n" ); - s_count = 0; + kfcoord coord; + coord.x = aXcoord; + coord.y = aYcoord; + s_EntityCoordinates.push_back( coord ); +} + + +/* Close a polygon entity in key file + * write the entire polygon data to the file +*/ +void EndKeyFilePolygon() +{ + // Polygon must be closed: test for that and close it if needed + if( s_EntityCoordinates.size() ) + { + if( s_EntityCoordinates.back().x != s_EntityCoordinates[0].x + || s_EntityCoordinates.back().y != s_EntityCoordinates[0].y ) + s_EntityCoordinates.push_back( s_EntityCoordinates[0] ); + } + + fprintf( kdebugFile, " XY %d;\n", s_EntityCoordinates.size() ); + + for( unsigned ii = 0; ii < s_EntityCoordinates.size(); ii ++ ) + fprintf( kdebugFile, " X %d; Y %d;\n", + s_EntityCoordinates[ii].x, s_EntityCoordinates[ii].y ); + fprintf( kdebugFile, "ENDEL;\n" ); + s_EntityCoordinates.clear(); } void CopyPolygonsFromFilledPolysListToKeyFile( ZONE_CONTAINER* aZone, int aLayer ) @@ -106,30 +139,15 @@ void CopyPolygonsFromFilledPolysListToKeyFile( ZONE_CONTAINER* aZone, int aLayer return; unsigned corners_count = aZone->m_FilledPolysList.size(); - int count = 0; unsigned ic = 0; - CPolyPt* corner; - while( ic < corners_count ) { - // Count corners: - count = 0; - for( unsigned ii = ic; ii < corners_count; ii++ ) - { - corner = &aZone->m_FilledPolysList[ii]; - count++; - if( corner->end_contour ) - break; - } - // write corners: - StartKeyFilePolygon( count+1, aLayer ); - corner = &aZone->m_FilledPolysList[ic]; - int startpointX = corner->x; - int startpointY = corner->y; + // write polygon: + StartKeyFilePolygon( aLayer ); for( ; ic < corners_count; ic++ ) { - corner = &aZone->m_FilledPolysList[ic]; + CPolyPt* corner = &aZone->m_FilledPolysList[ic]; AddKeyFilePointXY( corner->x, corner->y ); if( corner->end_contour ) { @@ -137,21 +155,8 @@ void CopyPolygonsFromFilledPolysListToKeyFile( ZONE_CONTAINER* aZone, int aLayer break; } } - // Close polygon: - AddKeyFilePointXY( startpointX, startpointY ); - EndKeyFileElement(); + EndKeyFilePolygon(); } } -void AddKeyFilePointXY( int aXcoord, int aYcoord) -{ - if ( s_count >= 2 ) - { - s_count = 0; - fprintf( kdebugFile, "\n"); - } - fprintf( kdebugFile, " X %d; Y %d;", aXcoord, aYcoord ); - s_count ++; -} - #endif diff --git a/pcbnew/debug_kbool_key_file_fct.h b/pcbnew/debug_kbool_key_file_fct.h index 0fee97983a..c8005b13fe 100644 --- a/pcbnew/debug_kbool_key_file_fct.h +++ b/pcbnew/debug_kbool_key_file_fct.h @@ -38,9 +38,9 @@ void CloseKeyFileEntity(); /* polygon creations: */ void CopyPolygonsFromFilledPolysListToKeyFile( ZONE_CONTAINER* aZone, int aLayer); -void StartKeyFilePolygon(int aCornersCount, int aLayer); +void StartKeyFilePolygon( int aLayer); void AddKeyFilePointXY( int aXcoord, int aYcoord); -void EndKeyFileElement(); +void EndKeyFilePolygon(); #endif // CREATE_KBOOL_KEY_FILES diff --git a/pcbnew/zones_convert_brd_items_to_polygons.cpp b/pcbnew/zones_convert_brd_items_to_polygons.cpp index e283d4d508..c4bb866ea1 100644 --- a/pcbnew/zones_convert_brd_items_to_polygons.cpp +++ b/pcbnew/zones_convert_brd_items_to_polygons.cpp @@ -51,43 +51,40 @@ extern void Test_For_Copper_Island_And_Remove( BOARD* aPcb, ZONE_CONTAINER* aZone_container ); -extern void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, +extern void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aCircleToSegmentsCount, int aWidth ); -#ifdef CREATE_KBOOL_KEY_FILES -bool s_GenDataForKbool = false; -#endif +void CreateThermalReliefPadPolygon( std::vector& aCornerBuffer, + D_PAD& aPad, + int aThermalGap, + int aCopperThickness, + int aMinThicknessValue, + int aCircleToSegmentsCount, + double aCorrectionFactor, + int aThermalRot ); + // Local Functions: #ifdef USE_STUBS_FOR_THERMAL #warning \ USE_STUBS_FOR_THERMAL is defined: for test version only do not use for working pcbnew version -void CreateStubsForThermalShapes( BOARD* aPcb, ZONE_CONTAINER* aZone_container, - int aThermalGap, - int aCopperThickness, int aMinThicknessValue ); +void CreateStubsForThermalShapes( BOARD* aPcb, ZONE_CONTAINER* aZone_container, + int aThermalGap, + int aCopperThickness, int aMinThicknessValue ); #endif -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 ); - -static void AddRingPolygon( Bool_Engine* aBooleng, - wxPoint aCentre, - wxPoint aStart, - int aArcAngle, - int aWidth ); +void AddTrackWithClearancePolygon( Bool_Engine* aBooleng, + TRACK& aTrack, int aClearanceValue ); +void AddPadWithClearancePolygon( Bool_Engine* aBooleng, D_PAD& aPad, int aClearanceValue ); +int AddThermalReliefPadPolygon( Bool_Engine* aBooleng, + D_PAD& aPad, + int aThermalGap, + int aCopperThickness, int aMinThicknessValue ); +void AddRoundedEndsSegmentPolygon( Bool_Engine* aBooleng, + wxPoint aStart, wxPoint aEnd, + int aWidth ); // Local Variables: /* how many segments are used to create a polygon from a circle: */ @@ -300,47 +297,67 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) if( item->GetLayer() != GetLayer() && item->GetLayer() != EDGE_N ) continue; + static std::vector cornerBuffer; + cornerBuffer.clear(); + switch( item->Type() ) { case TYPE_DRAWSEGMENT: - - switch( ( (DRAWSEGMENT*) item )->m_Shape ) - { - case S_CIRCLE: - AddRingPolygon( booleng, ( (DRAWSEGMENT*) item )->m_Start, // Circle centre - ( (DRAWSEGMENT*) item )->m_End, 3600, - ( (DRAWSEGMENT*) item )->m_Width + (2 * m_ZoneClearance) ); - have_poly_to_substract = true; - break; - - case S_ARC: - AddRingPolygon( booleng, ( (DRAWSEGMENT*) item )->m_Start, // Arc centre - ( (DRAWSEGMENT*) item )->m_End, - ( (DRAWSEGMENT*) item )->m_Angle, - ( (DRAWSEGMENT*) item )->m_Width + (2 * m_ZoneClearance) ); - have_poly_to_substract = true; - break; - - default: - AddRoundedEndsSegmentPolygon( booleng, - ( (DRAWSEGMENT*) item )->m_Start, - ( (DRAWSEGMENT*) item )->m_End, - ( (DRAWSEGMENT*) item )->m_Width + - (2 * m_ZoneClearance) ); - have_poly_to_substract = true; - break; - } - + ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( + cornerBuffer, + m_ZoneClearance, + s_CircleToSegmentsCount, + s_Correction ); break; + case TYPE_TEXTE: - AddTextBoxWithClearancePolygon( booleng, (TEXTE_PCB*) item, m_ZoneClearance ); - have_poly_to_substract = true; + ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygon( + cornerBuffer, + m_ZoneClearance, + s_CircleToSegmentsCount, + s_Correction ); break; default: break; } + + if( cornerBuffer.size() == 0 ) + continue; + + // cornerBuffer can contain more than one polygon, + // so read cornerBuffer and verify if there is a end of polygon corner: + for( unsigned icnt = 0; icnt < cornerBuffer.size(); ) + { + booleng->StartPolygonAdd( GROUP_B ); + { + have_poly_to_substract = true; + unsigned ii; + for( ii = icnt; ii < cornerBuffer.size(); ii++ ) + { + booleng->AddPoint( cornerBuffer[ii].x, cornerBuffer[ii].y ); + if( cornerBuffer[ii].end_contour ) + break; + } + + booleng->EndPolygonAdd(); + + #ifdef CREATE_KBOOL_KEY_FILES_FIRST_PASS + StartKeyFilePolygon( 1 ); + for( ii = icnt; ii < cornerBuffer.size(); ii++ ) + { + AddKeyFilePointXY( cornerBuffer[ii].x, cornerBuffer[ii].y ); + if( cornerBuffer[ii].end_contour ) + break; + } + + EndKeyFilePolygon(); + #endif + + icnt = ii + 1; + } + } } #ifdef CREATE_KBOOL_KEY_FILES_FIRST_PASS @@ -417,11 +434,11 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) item_boundingbox.Inflate( m_ThermalReliefGapValue, m_ThermalReliefGapValue ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { - have_poly_to_substract = true; - AddThermalReliefPadPolygon( booleng, *pad, + if( AddThermalReliefPadPolygon( booleng, *pad, m_ThermalReliefGapValue, m_ThermalReliefCopperBridgeValue, - m_ZoneMinThickness ); + m_ZoneMinThickness ) ) + have_poly_to_substract = true; } } } @@ -613,14 +630,14 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) void AddPadWithClearancePolygon( Bool_Engine* aBooleng, D_PAD& aPad, int aClearanceValue ) { - static std::vector cornerBuffer; + static std::vector cornerBuffer; if( aBooleng->StartPolygonAdd( GROUP_B ) == 0 ) return; cornerBuffer.clear(); - aPad.TransformPadWithClearanceToPolygon( cornerBuffer, - aClearanceValue, - s_CircleToSegmentsCount, - s_Correction ); + aPad.TransformShapeWithClearanceToPolygon( cornerBuffer, + aClearanceValue, + s_CircleToSegmentsCount, + s_Correction ); for( unsigned ii = 0; ii < cornerBuffer.size(); ii++ ) aBooleng->AddPoint( cornerBuffer[ii].x, cornerBuffer[ii].y ); @@ -628,13 +645,11 @@ void AddPadWithClearancePolygon( Bool_Engine* aBooleng, aBooleng->EndPolygonAdd(); #ifdef CREATE_KBOOL_KEY_FILES_FIRST_PASS - StartKeyFilePolygon( cornerBuffer.size() + 1, 1 ); + StartKeyFilePolygon( 1 ); for( unsigned ii = 0; ii < cornerBuffer.size(); ii++ ) AddKeyFilePointXY( cornerBuffer[ii].x, cornerBuffer[ii].y ); - // Close polygon - AddKeyFilePointXY( cornerBuffer[0].x, cornerBuffer[0].y ); - EndKeyFileElement(); + EndKeyFilePolygon(); #endif } @@ -642,6 +657,7 @@ void AddPadWithClearancePolygon( Bool_Engine* aBooleng, /** function CreateStubsForThermalShapes() * Only for testing the thermal shapes by stubs purposes * Do not use for working pcbnew versions + * This is just for kbool test only */ void CreateStubsForThermalShapes( BOARD* aPcb, ZONE_CONTAINER* aZone_container, int aThermalGap, @@ -658,9 +674,6 @@ void CreateStubsForThermalShapes( BOARD* aPcb, ZONE_CONTAINER* aZone_cont if( aCopperThickness <= aMinThicknessValue ) return; -#ifdef CREATE_KBOOL_KEY_FILES - s_GenDataForKbool = true; -#endif for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->m_Pads; pad != NULL; pad = pad->Next() ) @@ -737,9 +750,6 @@ void CreateStubsForThermalShapes( BOARD* aPcb, ZONE_CONTAINER* aZone_cont } } -#ifdef CREATE_KBOOL_KEY_FILES - s_GenDataForKbool = false; -#endif if( have_poly_to_add ) { /* Add the main corrected polygon (i.e. the filled area using only one outline) @@ -774,404 +784,60 @@ void CreateStubsForThermalShapes( BOARD* aPcb, ZONE_CONTAINER* aZone_cont * When Kbool calculates the filled areas : * i.e when substracting 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 (sometime occurs without this condition) - * 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 1: polygons are drawm using outlines witk a thickness = aMinThicknessValue - * so shapes must keep in account this outline thickness - * - * Note 2: - * Trapezoidal pads are not considered here because they are very special case - * and are used in microwave applications and they *DO NOT* have a thermal relief that change the shape - * by creating stubs and destroy their properties. + * see CreateThermalReliefPadPolygon(). */ -void AddThermalReliefPadPolygon( Bool_Engine* aBooleng, - D_PAD& aPad, - int aThermalGap, - int aCopperThickness, int aMinThicknessValue ) +int 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_thickness; - int dx = aPad.m_Size.x / 2; - int dy = aPad.m_Size.y / 2; + static std::vector cornerBuffer; + cornerBuffer.clear(); - 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_thickness must be decreased by aMinThicknessValue because drawing outlines - * with a thickness of aMinThicknessValue will increase real thickness by aMinThicknessValue - */ - aCopperThickness -= aMinThicknessValue; - if( aCopperThickness < 0 ) - aCopperThickness = 0; - - copper_thickness.x = min( dx, aCopperThickness ); - copper_thickness.y = min( dy, aCopperThickness ); - - 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_thickness.x / 2, copper_thickness.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_thickness.x / 2; - int y = outer_radius - (aThermalGap / 4); - corner.y = (int) sqrt( ( ( (double) y * y ) - (double) corner.x * corner.x ) ); -#ifndef CREATE_KBOOL_KEY_FILES_WITH_0_DEG - corners_buffer.push_back( corner ); -#endif - - // calculate the starting point of the outter arc - corner.x = copper_thickness.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 2.0 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 - // Note: with the 2 step build ( thermal shapes added after areas are built), 0 seems work + int polycount = 0; + int thermalRot = 450; #ifdef CREATE_KBOOL_KEY_FILES_WITH_0_DEG - angle = 0; -#else - angle = 450; -#endif - int angle_pad = aPad.m_Orient; // Pad orientation - for( unsigned ihole = 0; ihole < 4; ihole++ ) - { - if( aBooleng->StartPolygonAdd( GROUP_B ) ) - { -#ifdef CREATE_KBOOL_KEY_FILES - StartKeyFilePolygon( corners_buffer.size() + 1, 1 ); -#endif - 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 ); -#ifdef CREATE_KBOOL_KEY_FILES - AddKeyFilePointXY( corner.x, corner.y ); -#endif - } - -#ifdef CREATE_KBOOL_KEY_FILES - - // Close polygon - corner = corners_buffer[0]; - RotatePoint( &corner, angle + angle_pad ); // Rotate by segment angle and pad orientation - corner += PadShapePos; - AddKeyFilePointXY( corner.x, corner.y ); + thermalRot = 0; #endif - aBooleng->EndPolygonAdd(); -#ifdef CREATE_KBOOL_KEY_FILES - EndKeyFileElement(); -#endif - angle += 900; // Note: angle in in 0.1 deg. - } - } - } - break; + CreateThermalReliefPadPolygon( cornerBuffer, + aPad, aThermalGap, aCopperThickness, + aMinThicknessValue, + s_CircleToSegmentsCount, + s_Correction, thermalRot ); - case PAD_OVAL: + // cornerBuffer can contain more than one polygon, + // so read cornerBuffer and verify if there is a end of polygon corner: + for( unsigned icnt = 0; icnt < cornerBuffer.size(); ) { - // 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 ) + aBooleng->StartPolygonAdd( GROUP_B ); + polycount++; + unsigned ii; + for( ii = icnt; ii < cornerBuffer.size(); ii++ ) { - EXCHG( dx, dy ); - supp_angle = 900; - EXCHG( copper_thickness.x, copper_thickness.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_thickness.x / 2, copper_thickness.y / 2 ) ); - - // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge - if( copper_thickness.x > deltasize ) // If copper thickness is more than shape offset, we need to calculate arc intercept point. - { - corner.x = copper_thickness.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_thickness.x / 2; - corner.y = outer_radius; - corners_buffer.push_back( corner ); - corner.x = ( deltasize - copper_thickness.x ) / 2; + aBooleng->AddPoint( cornerBuffer[ii].x, cornerBuffer[ii].y ); + if( cornerBuffer[ii].end_contour ) + break; } - // 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_thickness.y / 2; - int px = outer_radius - (aThermalGap / 4); - last_corner.x = - (int) sqrt( ( ( (double) px * px ) - (double) last_corner.y * last_corner.y ) ); + aBooleng->EndPolygonAdd(); - // Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge - corner_end.y = copper_thickness.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) ) +#ifdef CREATE_KBOOL_KEY_FILES + StartKeyFilePolygon( 1 ); + for( ii = icnt; ii < cornerBuffer.size(); ii++ ) { - corners_buffer.push_back( corner + shape_offset ); - RotatePoint( &corner, delta ); + AddKeyFilePointXY( cornerBuffer[ii].x, cornerBuffer[ii].y ); + if( cornerBuffer[ii].end_contour ) + break; } - //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. + EndKeyFilePolygon(); +#endif - /* 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; - } - } + icnt = ii + 1; } - 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 - - // Modified rectangles with one corner rounded. TODO: merging with oval thermals and possibly round too. - - 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 - - // The first point of polygon buffer is left lower corner, second the crosspoint of thermal spoke sides, - // the third is upper right corner and the rest are rounding vertices going anticlockwise. Note the inveted Y-axis in CG. - corners_buffer.push_back( wxPoint( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) ) ); // Adds small miters to zone - corners_buffer.push_back( wxPoint( -(dx - aThermalGap / 4), -copper_thickness.y / 2 ) ); // fill and spoke corner - corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -copper_thickness.y / 2 ) ); - corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -(dy - aThermalGap / 4) ) ); - corners_buffer.push_back( wxPoint( -(aThermalGap / 4 + copper_thickness.x / 2), -dy ) ); - - angle = aPad.m_Orient; - int rounding_radius = (int) ( aThermalGap * s_Correction ); // Corner rounding radius - int angle_pg; // Polygon increment angle - - for( int i = 0; i < s_CircleToSegmentsCount / 4 + 1; i++ ) - { - wxPoint corner_position = wxPoint( 0, -rounding_radius ); - RotatePoint( &corner_position, (1800 / s_CircleToSegmentsCount) ); // Start at half increment offset - angle_pg = i * delta; - RotatePoint( &corner_position, angle_pg ); // Rounding vector rotation - corner_position -= aPad.m_Size / 2; // Rounding vector + Pad corner offset - corners_buffer.push_back( wxPoint( corner_position.x, corner_position.y ) ); - } - - 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 ); // Rotate according to module orientation - cpos += PadShapePos; // Shift origin to position - 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 - 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; - } - } + return polycount; } @@ -1182,9 +848,9 @@ void AddThermalReliefPadPolygon( Bool_Engine* aBooleng, void AddTrackWithClearancePolygon( Bool_Engine* aBooleng, TRACK& aTrack, int aClearanceValue ) { - static std::vector cornerBuffer; + static std::vector cornerBuffer; cornerBuffer.clear(); - aTrack.TransformTrackWithClearanceToPolygon( cornerBuffer, + aTrack.TransformShapeWithClearanceToPolygon( cornerBuffer, aClearanceValue, s_CircleToSegmentsCount, s_Correction ); @@ -1198,13 +864,11 @@ void AddTrackWithClearancePolygon( Bool_Engine* aBooleng, aBooleng->EndPolygonAdd(); #ifdef CREATE_KBOOL_KEY_FILES_FIRST_PASS - StartKeyFilePolygon( cornerBuffer.size() + 1, 1 ); + StartKeyFilePolygon( 1 ); for( unsigned ii = 0; ii < cornerBuffer.size(); ii++ ) AddKeyFilePointXY( cornerBuffer[ii].x, cornerBuffer[ii].y ); - // Close polygon - AddKeyFilePointXY( cornerBuffer[0].x, cornerBuffer[0].y ); - EndKeyFileElement(); + EndKeyFilePolygon(); #endif } @@ -1217,12 +881,12 @@ void AddRoundedEndsSegmentPolygon( Bool_Engine* aBooleng, wxPoint aStart, wxPoint aEnd, int aWidth ) { - static std::vector cornerBuffer; + static std::vector cornerBuffer; cornerBuffer.clear(); TransformRoundedEndsSegmentToPolygon( cornerBuffer, - aStart, aEnd, - s_CircleToSegmentsCount, - aWidth ); + aStart, aEnd, + s_CircleToSegmentsCount, + aWidth ); if( !aBooleng->StartPolygonAdd( GROUP_B ) ) return; @@ -1233,100 +897,15 @@ void AddRoundedEndsSegmentPolygon( Bool_Engine* aBooleng, aBooleng->EndPolygonAdd(); #ifdef CREATE_KBOOL_KEY_FILES - StartKeyFilePolygon( cornerBuffer.size() + 1, 1 ); + StartKeyFilePolygon( 1 ); for( unsigned ii = 0; ii < cornerBuffer.size(); ii++ ) AddKeyFilePointXY( cornerBuffer[ii].x, cornerBuffer[ii].y ); - // Close polygon - AddKeyFilePointXY( cornerBuffer[0].x, cornerBuffer[0].y ); - EndKeyFileElement(); + EndKeyFilePolygon(); #endif } -/** Function AddRingPolygon - * Add a polygon cutout for an Arc in a zone area - * Convert arcs to multiple straight segments - * @param aBooleng = the bool engine to use - * @param aCentre = centre of the arc or circle - * @param aStart = start point of the arc, or apoint of the circle - * @param aArcAngle = arc angle in 0.1 degrees. For a circle, aArcAngle = 3600 - * @param aWidth = width of the line - */ -void AddRingPolygon( Bool_Engine* aBooleng, wxPoint aCentre, - wxPoint aStart, int aArcAngle, - int aWidth ) -{ - wxPoint arc_start, arc_end; - int delta = 3600 / s_CircleToSegmentsCount; // rot angle in 0.1 degree - - arc_end = arc_start = aStart; - if( aArcAngle != 3600 ) - { - RotatePoint( &arc_end, aCentre, -aArcAngle ); - } - - if( aArcAngle < 0 ) - { - EXCHG( arc_start, arc_end ); - NEGATE( aArcAngle ); - } - - // Compute the ends of segments and creates poly - wxPoint curr_end = arc_start, curr_start = arc_start; - for( int ii = delta; ii < aArcAngle; ii += delta ) - { - curr_end = arc_start; - RotatePoint( &curr_end, aCentre, -ii ); - AddRoundedEndsSegmentPolygon( aBooleng, curr_start, curr_end, aWidth ); - curr_start = curr_end; - } - - if( curr_end != arc_end ) - AddRoundedEndsSegmentPolygon( aBooleng, curr_end, arc_end, aWidth ); -} - - -/** function AddTextBoxWithClearancePolygon - * creates a polygon containing the text and add it to bool engine - */ -void AddTextBoxWithClearancePolygon( Bool_Engine* aBooleng, - TEXTE_PCB* aText, int aClearanceValue ) -{ - if( aText->GetLength() == 0 ) - return; - - wxPoint corners[4]; // Buffer of polygon corners - - EDA_Rect rect = aText->GetTextBox( -1 ); - rect.Inflate( aClearanceValue, aClearanceValue ); - corners[0] = rect.GetOrigin(); - corners[1].y = corners[0].y; - corners[1].x = rect.GetRight(); - corners[2].x = corners[1].x; - corners[2].y = rect.GetBottom(); - corners[3].y = corners[2].y; - corners[3].x = corners[0].x; - - - if( aBooleng->StartPolygonAdd( GROUP_B ) ) - { - for( int ii = 0; ii < 4; ii++ ) - { - // Rotate polygon - RotatePoint( &corners[ii].x, - &corners[ii].y, - aText->m_Pos.x, - aText->m_Pos.y, - aText->m_Orient ); - aBooleng->AddPoint( corners[ii].x, corners[ii].y ); - } - - aBooleng->EndPolygonAdd(); - } -} - - /***********************************************************************************************************/ int ZONE_CONTAINER::CopyPolygonsFromFilledPolysListToBoolengine( Bool_Engine* aBoolengine, GroupType aGroup )