diff --git a/3d-viewer/3d_canvas/board_adapter.cpp b/3d-viewer/3d_canvas/board_adapter.cpp index cfe297215a..a034d73899 100644 --- a/3d-viewer/3d_canvas/board_adapter.cpp +++ b/3d-viewer/3d_canvas/board_adapter.cpp @@ -256,16 +256,7 @@ unsigned int BOARD_ADAPTER::GetNrSegmentsCircle( int aDiameterBIU ) const { wxASSERT( aDiameterBIU > 0 ); - // Require at least 3 segments for a circle - return std::max( GetArcToSegmentCount( aDiameterBIU / 2, ARC_HIGH_DEF, 360.0 ), 3 ); -} - - -double BOARD_ADAPTER::GetCircleCorrectionFactor( int aNrSides ) const -{ - wxASSERT( aNrSides >= 3 ); - - return GetCircletoPolyCorrectionFactor( aNrSides ); + return GetArcToSegmentCount( aDiameterBIU / 2, ARC_HIGH_DEF, 360.0 ); } diff --git a/3d-viewer/3d_canvas/board_adapter.h b/3d-viewer/3d_canvas/board_adapter.h index 49096378dd..d0c2ced41d 100644 --- a/3d-viewer/3d_canvas/board_adapter.h +++ b/3d-viewer/3d_canvas/board_adapter.h @@ -529,14 +529,6 @@ class BOARD_ADAPTER */ unsigned int GetNrSegmentsCircle( int aDiameterBIU ) const; - /** - * @brief GetCircleCorrectionFactor - computes a angle correction - * factor used when creating circles - * @param aNrSides: the number of segments sides of the circle - * @return a factor to apply to contour creation - */ - double GetCircleCorrectionFactor( int aNrSides ) const; - /** * @brief GetPolyMap - Get maps of polygons's layers * @return the map with polygons's layers diff --git a/3d-viewer/3d_canvas/create_layer_poly.cpp b/3d-viewer/3d_canvas/create_layer_poly.cpp index 477683faea..c08a2e7267 100644 --- a/3d-viewer/3d_canvas/create_layer_poly.cpp +++ b/3d-viewer/3d_canvas/create_layer_poly.cpp @@ -54,8 +54,7 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad, const VECTOR2I& a = path.CPoint( ii ); const VECTOR2I& b = path.CPoint( ii + 1 ); - TransformSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ), - ARC_HIGH_DEF, aWidth ); + TransformOvalToPolygon( aCornerBuffer, (wxPoint) a, (wxPoint) b, aWidth, ARC_HIGH_DEF ); } } diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp index 4805c85726..1c143b5e01 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp @@ -744,8 +744,7 @@ void C3D_RENDER_OGL_LEGACY::generate_3D_Vias_and_Pads() const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3Dunits(); const float thickness = m_boardAdapter.GetCopperThickness3DU(); const int nrSegments = m_boardAdapter.GetNrSegmentsCircle( via->GetDrillValue() ); - const double correctionFactor = m_boardAdapter.GetCircleCorrectionFactor( nrSegments ); - const float hole_inner_radius = ( holediameter / 2.0f ) * correctionFactor; + const float hole_inner_radius = holediameter / 2.0f; const SFVEC2F via_center( via->GetStart().x * m_boardAdapter.BiuTo3Dunits(), -via->GetStart().y * m_boardAdapter.BiuTo3Dunits() ); @@ -805,14 +804,11 @@ void C3D_RENDER_OGL_LEGACY::generate_3D_Vias_and_Pads() // for slots, the diameter is the smaller of (drillsize.x, drillsize.y) int copperThickness = m_boardAdapter.GetCopperThicknessBIU(); int radius = std::min( drillsize.x, drillsize.y ) / 2 + copperThickness; - int nrSegments = m_boardAdapter.GetNrSegmentsCircle( radius * 2 ); - double correctionFactor = m_boardAdapter.GetCircleCorrectionFactor( nrSegments ); - int correction = radius * ( correctionFactor - 1 ); pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly, - copperThickness + correction ); + copperThickness + radius ); - pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, correction ); + pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, radius ); } } } diff --git a/common/plotters/DXF_plotter.cpp b/common/plotters/DXF_plotter.cpp index 422a21cd81..22b0112f4e 100644 --- a/common/plotters/DXF_plotter.cpp +++ b/common/plotters/DXF_plotter.cpp @@ -533,8 +533,8 @@ void DXF_PLOTTER::PlotPoly( const std::vector& aCornerList, // enter outline as polygon: for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) { - TransformSegmentToPolygon( bufferOutline, - aCornerList[ ii - 1 ], aCornerList[ ii ], GetPlotterArcHighDef(), aWidth ); + TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ], + aWidth, GetPlotterArcHighDef() ); } // enter the initial polygon: diff --git a/libs/kimath/include/convert_basic_shapes_to_polygon.h b/libs/kimath/include/convert_basic_shapes_to_polygon.h index f83fa60bac..9752d984f8 100644 --- a/libs/kimath/include/convert_basic_shapes_to_polygon.h +++ b/libs/kimath/include/convert_basic_shapes_to_polygon.h @@ -121,22 +121,6 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aError ); -/** - * Function TransformRoundedEndsSegmentToPolygon - * convert a segment with rounded ends to a polygon - * Convert arcs to multiple straight lines - * @param aCornerBuffer = a buffer to store the polygon - * @param aStart = the segment start point coordinate - * @param aEnd = the segment end point coordinate - * @param aError = the IU allowed for error in approximation - * @param aWidth = the segment width - * Note: the polygon is inside the arc ends, so if you want to have the polygon - * outside the circle, you should give aStart and aEnd calculated with a correction factor - */ -void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd, - int aError, int aWidth ); - - /** * Function TransformArcToPolygon * Creates a polygon from an Arc diff --git a/libs/kimath/include/geometry/geometry_utils.h b/libs/kimath/include/geometry/geometry_utils.h index 26905e758e..6189a830a6 100644 --- a/libs/kimath/include/geometry/geometry_utils.h +++ b/libs/kimath/include/geometry/geometry_utils.h @@ -47,36 +47,33 @@ class EDA_RECT; */ int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree ); -/** When creating polygons to create a clearance polygonal area, the polygon must +/** + * When creating polygons to create a clearance polygonal area, the polygon must * be same or bigger than the original shape. - * Polygons are bigger if the original shape has arcs (round rectangles, ovals, circles...) - * In some cases (in fact only one: when building layer solder mask) modifying - * shapes when converting them to polygons is not acceptable (the modification - * can break calculations) - * so one can disable the shape expansion by calling DisableArcRadiusCorrection( true ) - * Important: calling DisableArcRadiusCorrection( false ) after calculations is - * mandatory to break oher calculations - * @param aDisable = false to create polygons same or outside the original shape - * = true to create polygons same or inside the original shape and minimize - * shape geometric changes + * Polygons are bigger if the original shape has arcs (round rectangles, ovals, + * circles...). However, when building the solder mask layer modifying the shapes + * when converting them to polygons is not acceptable (the modification can break + * calculations). + * So one can disable the shape expansion within a particular scope by allocating + * a DISABLE_ARC_CORRECTION. */ -void DisableArcRadiusCorrection( bool aDisable ); +class DISABLE_ARC_RADIUS_CORRECTION +{ +public: + DISABLE_ARC_RADIUS_CORRECTION(); + ~DISABLE_ARC_RADIUS_CORRECTION(); +}; /** - * @return the correction factor to approximate a circle by segments or 1.0 - * depending on the last call to DisableArcRadiusCorrection() - * @param aSegCountforCircle is the number of segments to approximate the circle + * @return the radius correction to approximate a circle. + * @param aMaxError is the same error value used to calculate the number of segments. * * When creating a polygon from a circle, the polygon is inside the circle. * Only corners are on the circle. * This is incorrect when building clearance areas of circles, that need to build - * the equivalent polygon outside the circle - * The correction factor is a scaling factor to apply to the radius to build a - * polygon outside the circle (only the middle of each segment is on the circle - * - * + * the equivalent polygon outside the circle. */ -double GetCircletoPolyCorrectionFactor( int aSegCountforCircle ); +int GetCircleToPolyCorrection( int aMaxError ); /** * Snap a vector onto the nearest 0, 45 or 90 degree line. diff --git a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp index 51f14ca94d..748d8f3338 100644 --- a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp +++ b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp @@ -42,17 +42,16 @@ void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aCornerBuffer, wxPoint aCenter, int aError ) { wxPoint corner_position; - int numSegs = std::max( GetArcToSegmentCount( aRadius, aError, 360.0 ), 6 ); - int delta = 3600 / numSegs; // rotate angle in 0.1 degree - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int radius = aRadius * correction; // make segments outside the circles - double halfstep = delta/2.0; // the starting value for rot angles + int numSegs = GetArcToSegmentCount( aRadius, aError, 360.0 ); + int delta = 3600 / numSegs; // rotate angle in 0.1 degree + int correction = GetCircleToPolyCorrection( aError ); + int radius = aRadius + correction; // make segments outside the circles + double halfstep = delta / 2.0; // the starting value for rot angles - for( int ii = 0; ii < numSegs; ii++ ) + for( int angle = 0; angle < 3600; angle += delta ) { corner_position.x = radius; corner_position.y = 0; - double angle = (ii * delta) + halfstep; RotatePoint( &corner_position, angle ); corner_position += aCenter; aCornerBuffer.Append( corner_position.x, corner_position.y ); @@ -66,23 +65,27 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCenter, i int aError ) { wxPoint corner_position; - int numSegs = std::max( GetArcToSegmentCount( aRadius, aError, 360.0 ), 6 ); - int delta = 3600 / numSegs; // rotate angle in 0.1 degree - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int radius = aRadius * correction; // make segments outside the circles - double halfstep = delta/2.0; // the starting value for rot angles + int numSegs = GetArcToSegmentCount( aRadius, aError, 360.0 ); + int delta = 3600 / numSegs; // rotate angle in 0.1 degree + int correction = GetCircleToPolyCorrection( aError ); + int radius = aRadius + correction; // make segments outside the circles aCornerBuffer.NewOutline(); - for( int ii = 0; ii < numSegs; ii++ ) + for( int angle = 0; angle < 3600; angle += delta ) { - corner_position.x = radius; - corner_position.y = 0; - double angle = (ii * delta) + halfstep; + corner_position.x = radius; + corner_position.y = 0; RotatePoint( &corner_position, angle ); corner_position += aCenter; aCornerBuffer.Append( corner_position.x, corner_position.y ); } + + // Finish circle + corner_position.x = radius; + corner_position.y = 0; + corner_position += aCenter; + aCornerBuffer.Append( corner_position.x, corner_position.y ); } @@ -94,23 +97,17 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo // However, the width of the segment is too big. // so, later, we will clamp the polygonal shape with the bounding box // of the segment. - int radius = aWidth / 2; - int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 ); + int radius = aWidth / 2; + int numSegs = GetArcToSegmentCount( radius, aError, 360.0 ); + int delta = 3600 / numSegs; // rotate angle in 0.1 degree + int correction = GetCircleToPolyCorrection( aError ); - // Because we want to create 2 arcs (one at each segment end) numSegs must be - // a even value (we will used numSegs/2 later) - if( numSegs % 2 != 0 ) - numSegs++; - - int delta = 3600 / numSegs; // rotate angle in 0.1 degree - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - - radius = radius * correction; // make segments outside the circles + radius += correction; // make segments outside the circles // end point is the coordinate relative to aStart - wxPoint endp = aEnd - aStart; - wxPoint startp = aStart; - wxPoint corner; + wxPoint endp = aEnd - aStart; + wxPoint startp = aStart; + wxPoint corner; SHAPE_POLY_SET polyshape; polyshape.NewOutline(); @@ -125,18 +122,19 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo // delta_angle is in radian double delta_angle = atan2( (double)endp.y, (double)endp.x ); - int seg_len = KiROUND( EuclideanNorm( endp ) ); + int seg_len = KiROUND( EuclideanNorm( endp ) ); // Compute the outlines of the segment, and creates a polygon // Note: the polygonal shape is built from the equivalent horizontal - // segment starting ar 0,0, and ending at seg_len,0 + // segment starting at {0,0}, and ending at {seg_len,0} // add right rounded end: - for( int ii = 0; ii < numSegs / 2; ii++ ) + + for( int angle = 0; angle < 1800; angle += delta ) { corner = wxPoint( 0, radius ); - RotatePoint( &corner, delta * ii ); + RotatePoint( &corner, angle ); corner.x += seg_len; polyshape.Append( corner.x, corner.y ); } @@ -146,10 +144,10 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo polyshape.Append( corner.x, corner.y ); // add left rounded end: - for( int ii = 0; ii < numSegs / 2; ii++ ) + for( int angle = 0; angle < 1800; angle += delta ) { corner = wxPoint( 0, -radius ); - RotatePoint( &corner, delta * ii ); + RotatePoint( &corner, angle ); polyshape.Append( corner.x, corner.y ); } @@ -157,31 +155,27 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo corner = wxPoint( 0, radius ); polyshape.Append( corner.x, corner.y ); - // Now, clamp the polygonal shape (too big) with the segment bounding box - // the polygonal shape bbox equivalent to the segment has a too big height, - // and the right width - if( correction > 1.0 ) - { - SHAPE_POLY_SET bbox; - bbox.NewOutline(); - // Build the bbox (a horizontal rectangle). - int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height - corner.x = -radius - 2; // use a bbox width slightly bigger to avoid - // creating useless corner at segment ends - corner.y = halfwidth; - bbox.Append( corner.x, corner.y ); - corner.y = -halfwidth; - bbox.Append( corner.x, corner.y ); - corner.x = radius + seg_len + 2; - bbox.Append( corner.x, corner.y ); - corner.y = halfwidth; - bbox.Append( corner.x, corner.y ); + // Now trim the edges of the polygonal shape which will be slightly outside the + // track width. + SHAPE_POLY_SET bbox; + bbox.NewOutline(); + // Build the bbox (a horizontal rectangle). + int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height + corner.x = -radius - 2; // use a bbox width slightly bigger to avoid + // creating useless corner at segment ends + corner.y = halfwidth; + bbox.Append( corner.x, corner.y ); + corner.y = -halfwidth; + bbox.Append( corner.x, corner.y ); + corner.x = radius + seg_len + 2; + bbox.Append( corner.x, corner.y ); + corner.y = halfwidth; + bbox.Append( corner.x, corner.y ); - // Now, clamp the shape - polyshape.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - // Note the final polygon is a simple, convex polygon with no hole - // due to the shape of initial polygons - } + // Now, clamp the shape + polyshape.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + // Note the final polygon is a simple, convex polygon with no hole + // due to the shape of initial polygons // Rotate and move the polygon to its right location polyshape.Rotate( delta_angle, VECTOR2I( 0, 0 ) ); @@ -240,18 +234,13 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const for( const wxPoint& corner : corners) outline.Append( corner ); - // These are small radius corners (of which there may be many), so peg the segs-per-circle - // to no more than 16. - int numSegs = std::max( GetArcToSegmentCount( aCornerRadius, aError, 360.0 ), 16 ); + int numSegs = GetArcToSegmentCount( aCornerRadius, aError, 360.0 ); // To build the polygonal shape outside the actual shape, we use a bigger // radius to build rounded corners. - // However, the size of the shape is too large. - // so, we also clamp the polygonal shape with the bounding box - // of the initial shape. - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int radius = aCornerRadius * correction; // make segments outside the circles + int correction = GetCircleToPolyCorrection( aError ); + int radius = aCornerRadius + correction; // make segments outside the circles outline.Inflate( radius, numSegs ); if( correction > 1.0 ) @@ -340,82 +329,12 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const } -void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd, - int aError, int aWidth ) -{ - int radius = aWidth / 2; - wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0) - wxPoint startp = aStart; - wxPoint corner; - VECTOR2I polypoint; - int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 ); - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int delta = 3600 / numSegs; // rotate angle in 0.1 degree - - radius = KiROUND( radius * correction ); - aCornerBuffer.NewOutline(); - - // normalize the position in order to have endp.x >= 0; - if( endp.x < 0 ) - { - endp = aStart - aEnd; - startp = aEnd; - } - - double delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees - int seg_len = KiROUND( EuclideanNorm( endp ) ); - - // Compute the outlines of the segment, and creates a polygon - - // add right rounded end: - for( int ii = 0; ii < 1800; ii += delta ) - { - corner = wxPoint( 0, radius ); - RotatePoint( &corner, ii ); - corner.x += seg_len; - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.Append( polypoint.x, polypoint.y ); - } - - // Finish arc: - corner = wxPoint( seg_len, -radius ); - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.Append( polypoint.x, polypoint.y ); - - // add left rounded end: - for( int ii = 0; ii < 1800; ii += delta ) - { - corner = wxPoint( 0, -radius ); - RotatePoint( &corner, ii ); - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.Append( polypoint.x, polypoint.y ); - } - - // Finish arc: - corner = wxPoint( 0, radius ); - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.Append( polypoint.x, polypoint.y ); -} - - void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, wxPoint aStart, double aArcAngle, int aError, int aWidth ) { wxPoint arc_start, arc_end; int dist = EuclideanNorm( aCentre - aStart ); - int numSegs = std::max( GetArcToSegmentCount( dist, aError, 360.0 ), 6 ); + int numSegs = GetArcToSegmentCount( dist, aError, 360.0 ); int delta = 3600 / numSegs; // rotate angle in 0.1 degree arc_end = arc_start = aStart; @@ -437,12 +356,12 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, wxPo { curr_end = arc_start; RotatePoint( &curr_end, aCentre, -ii ); - TransformSegmentToPolygon( aCornerBuffer, curr_start, curr_end, aError, aWidth ); + TransformOvalToPolygon( aCornerBuffer, curr_start, curr_end, aWidth, aError ); curr_start = curr_end; } if( curr_end != arc_end ) - TransformSegmentToPolygon( aCornerBuffer, curr_end, arc_end, aError, aWidth ); + TransformOvalToPolygon( aCornerBuffer, curr_end, arc_end, aWidth, aError ); } diff --git a/libs/kimath/src/geometry/geometry_utils.cpp b/libs/kimath/src/geometry/geometry_utils.cpp index 8ea449924f..70a76b2d4e 100644 --- a/libs/kimath/src/geometry/geometry_utils.cpp +++ b/libs/kimath/src/geometry/geometry_utils.cpp @@ -34,8 +34,11 @@ #include #include // for KiROUND -// To approximate a circle by segments, a minimal seg count is mandatory -#define MIN_SEGCOUNT_FOR_CIRCLE 6 +// To approximate a circle by segments, a minimal seg count is mandatory. +// Note that this is rarely used as the maxError constraint will yield a higher +// segment count on everything but very small circles. (Even a 0.125mm track +// with a 0.01mm maximum deviation yields 11 segments.) +#define MIN_SEGCOUNT_FOR_CIRCLE 8 int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree ) { @@ -56,39 +59,35 @@ int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree ) int segCount = KiROUND( fabs( aArcAngleDegree ) / arc_increment ); - // Ensure at least one segment is used (can happen for small arcs) - return std::max( segCount, 1 ); + // Ensure at least two segments are used for algorithmic safety + return std::max( segCount, 2 ); } // When creating polygons to create a clearance polygonal area, the polygon must // be same or bigger than the original shape. -// Polygons are bigger if the original shape has arcs (round rectangles, ovals, circles...) -// In some cases (in fact only one: when building layer solder mask) modifying -// shapes when converting them to polygons is not acceptable (the modification -// can break calculations) -// so one can disable the shape expansion by calling KeepPolyInsideShape( true ) -// Important: calling KeepPolyInsideShape( false ) after calculations is -// mandatory to break oher calculations +// Polygons are bigger if the original shape has arcs (round rectangles, ovals, +// circles...). However, when building the solder mask layer modifying the shapes +// when converting them to polygons is not acceptable (the modification can break +// calculations). +// So one can disable the shape expansion within a particular scope by allocating +// a DISABLE_ARC_CORRECTION. + static bool s_disable_arc_correction = false; -// Enable (aInside = false) or disable (aInside = true) polygonal shape expansion -// when converting pads shapes and other items shapes to polygons: -void DisableArcRadiusCorrection( bool aDisable ) +DISABLE_ARC_RADIUS_CORRECTION::DISABLE_ARC_RADIUS_CORRECTION() { - s_disable_arc_correction = aDisable; + s_disable_arc_correction = true; } -double GetCircletoPolyCorrectionFactor( int aSegCountforCircle ) +DISABLE_ARC_RADIUS_CORRECTION::~DISABLE_ARC_RADIUS_CORRECTION() { - /* calculates the coeff to compensate radius reduction of circle - * due to the segment approx. - * For a circle the min radius is radius * cos( 2PI / aSegCountforCircle / 2) - * this is the distance between the center and the middle of the segment. - * therefore, to move the middle of the segment to the circle (distance = radius) - * the correctionFactor is 1 /cos( PI/aSegCountforCircle ) - */ - aSegCountforCircle = std::max( MIN_SEGCOUNT_FOR_CIRCLE, aSegCountforCircle ); - return s_disable_arc_correction ? 1.0 : 1.0 / cos( M_PI / aSegCountforCircle ); + s_disable_arc_correction = false; +} + +int GetCircleToPolyCorrection( int aMaxError ) +{ + // Push all the error to the outside by increasing the radius + return s_disable_arc_correction ? 0 : aMaxError; } diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index cc5a84babb..894bc433a1 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -59,9 +59,8 @@ TSEGM_2_POLY_PRMS prms; static void addTextSegmToPoly( int x0, int y0, int xf, int yf, void* aData ) { TSEGM_2_POLY_PRMS* prm = static_cast( aData ); - TransformSegmentToPolygon( *prm->m_cornerBuffer, - wxPoint( x0, y0 ), wxPoint( xf, yf ), - prm->m_error, prm->m_textWidth ); + TransformOvalToPolygon( *prm->m_cornerBuffer, wxPoint( x0, y0 ), wxPoint( xf, yf ), + prm->m_textWidth, prm->m_error ); } @@ -296,7 +295,7 @@ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygon( PCB_LAYER_ID aLayer, if( board ) maxError = board->GetDesignSettings().m_MaxError; - int numSegs = std::max( GetArcToSegmentCount( GetMinThickness(), maxError, 360.0 ), 12 ); + int numSegs = GetArcToSegmentCount( GetMinThickness(), maxError, 360.0 ); polys.InflateWithLinkedHoles( GetMinThickness()/2, numSegs, SHAPE_POLY_SET::PM_FAST ); @@ -420,10 +419,10 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB if( width > 0 ) { // Add in segments - TransformSegmentToPolygon( aCornerBuffer, pts[0], pts[1], aError, width ); - TransformSegmentToPolygon( aCornerBuffer, pts[1], pts[2], aError, width ); - TransformSegmentToPolygon( aCornerBuffer, pts[2], pts[3], aError, width ); - TransformSegmentToPolygon( aCornerBuffer, pts[3], pts[0], aError, width ); + TransformOvalToPolygon( aCornerBuffer, pts[0], pts[1], width, aError ); + TransformOvalToPolygon( aCornerBuffer, pts[1], pts[2], width, aError ); + TransformOvalToPolygon( aCornerBuffer, pts[2], pts[3], width, aError ); + TransformOvalToPolygon( aCornerBuffer, pts[3], pts[0], width, aError ); } } break; @@ -473,7 +472,7 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB for( wxPoint pt2 : poly ) { if( pt2 != pt1 ) - TransformSegmentToPolygon( aCornerBuffer, pt1, pt2, aError, width ); + TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError ); pt1 = pt2; } @@ -491,7 +490,7 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB if( width != 0 ) { for( unsigned ii = 1; ii < poly.size(); ii++ ) - TransformSegmentToPolygon( aCornerBuffer, poly[ii-1], poly[ii], aError, width ); + TransformOvalToPolygon( aCornerBuffer, poly[ii-1], poly[ii], width, aError ); } } break; @@ -511,29 +510,33 @@ void TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, { wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for tracks." ); - int width = m_Width + ( 2 * aClearanceValue ); switch( Type() ) { case PCB_VIA_T: - { + { int radius = ( m_Width / 2 ) + aClearanceValue; TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aError ); - } + } break; case PCB_ARC_T: - { + { const ARC* arc = static_cast( this ); - VECTOR2D center( arc->GetCenter() ); - double arc_angle = arc->GetAngle(); - TransformArcToPolygon( aCornerBuffer, wxPoint( center.x, center.y ), - GetStart(), arc_angle, aError, width ); - } + int width = m_Width + ( 2 * aClearanceValue ); + VECTOR2D center( arc->GetCenter() ); + double angle = arc->GetAngle(); + + TransformArcToPolygon( aCornerBuffer, (wxPoint) center, GetStart(), angle, aError, width ); + } break; default: + { + int width = m_Width + ( 2 * aClearanceValue ); + TransformOvalToPolygon( aCornerBuffer, m_Start, m_End, width, aError ); + } break; } } @@ -602,10 +605,9 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, if( aClearanceValue ) { - int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), - pad_min_seg_per_circle_count ); - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int clearance = KiROUND( aClearanceValue * correction ); + int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), + pad_min_seg_per_circle_count ); + int clearance = aClearanceValue + GetCircleToPolyCorrection( aError ); outline.Inflate( clearance, numSegs ); // TODO: clamp the inflated polygon, because it is slightly too big: // it was inflated by a value slightly too big to keep rounded corners @@ -620,13 +622,10 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, case PAD_SHAPE_ROUNDRECT: { int radius = GetRoundRectCornerRadius() + aClearanceValue; - int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), - pad_min_seg_per_circle_count ); - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int clearance = KiROUND( aClearanceValue * correction ); + int clearance = aClearanceValue + GetCircleToPolyCorrection( aError ); wxSize shapesize( m_size ); - radius = KiROUND( radius * correction ); + radius = radius + GetCircleToPolyCorrection( aError ); shapesize.x += clearance * 2; shapesize.y += clearance * 2; bool doChamfer = GetShape() == PAD_SHAPE_CHAMFERED_RECT; @@ -652,10 +651,9 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, if( aClearanceValue ) { - int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), - pad_min_seg_per_circle_count ); - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int clearance = KiROUND( aClearanceValue * correction ); + int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), + pad_min_seg_per_circle_count ); + int clearance = aClearanceValue + GetCircleToPolyCorrection( aError ); outline.Inflate( clearance, numSegs ); } @@ -684,8 +682,8 @@ bool D_PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, const SHAPE_SEGMENT* seg = GetEffectiveHoleShape(); - TransformSegmentToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B, - aError, seg->GetWidth() + aInflateValue * 2 ); + TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B, + seg->GetWidth() + aInflateValue * 2, aError ); return true; } diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 62b994fe7a..56bd990508 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -1325,7 +1325,7 @@ void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SE if( board ) maxError = board->GetDesignSettings().m_MaxError; - int segCount = std::max( GetArcToSegmentCount( aClearance, maxError, 360.0 ), 3 ); + int segCount = GetArcToSegmentCount( aClearance, maxError, 360.0 ); polybuffer.Inflate( aClearance, segCount ); } diff --git a/pcbnew/convert_drawsegment_list_to_polygon.cpp b/pcbnew/convert_drawsegment_list_to_polygon.cpp index 42c51440e2..d06d98fc2c 100644 --- a/pcbnew/convert_drawsegment_list_to_polygon.cpp +++ b/pcbnew/convert_drawsegment_list_to_polygon.cpp @@ -536,7 +536,7 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SE double angle = 3600.0; wxPoint start = center; int radius = graphic->GetRadius(); - int steps = std::max( 4, GetArcToSegmentCount( radius, aTolerance, 360.0 ) ); + int steps = GetArcToSegmentCount( radius, aTolerance, 360.0 ); wxPoint nextPt; start.x += radius; diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp index ff97db5b92..bad7a4f763 100644 --- a/pcbnew/drc/drc.cpp +++ b/pcbnew/drc/drc.cpp @@ -1252,39 +1252,45 @@ void DRC::setTransitions() const int UI_EPSILON = Mils2iu( 5 ); -wxPoint DRC::GetLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone ) +wxPoint DRC::GetLocation( PCB_LAYER_ID aLayer, TRACK* aTrack, ZONE_CONTAINER* aZone ) { - SHAPE_POLY_SET* conflictOutline = nullptr; + SHAPE_POLY_SET* zonePoly = nullptr; - PCB_LAYER_ID l = aTrack->GetLayer(); + if( aZone->IsFilled() && aZone->HasFilledPolysForLayer( aLayer ) ) + zonePoly = const_cast( &aZone->GetFilledPolysList( aLayer ) ); - if( aConflictZone->IsFilled() && aConflictZone->HasFilledPolysForLayer( l ) ) - conflictOutline = const_cast( &aConflictZone->GetFilledPolysList( l ) ); + if( !zonePoly || zonePoly->IsEmpty() ) + zonePoly = aZone->Outline(); - if( !conflictOutline || conflictOutline->IsEmpty() ) - conflictOutline = aConflictZone->Outline(); + SEG trackSeg( aTrack->GetStart(), aTrack->GetEnd() ); + SEG::ecoord closestDist_sq = VECTOR2I::ECOORD_MAX; + SEG closestSeg; - wxPoint pt1 = aTrack->GetPosition(); - wxPoint pt2 = aTrack->GetEnd(); - - // If the mid-point is in the zone, then that's a fine place for the marker - if( conflictOutline->SquaredDistance( ( pt1 + pt2 ) / 2 ) == 0 ) - return ( pt1 + pt2 ) / 2; - - // Otherwise do a binary search for a "good enough" marker location - else + for( auto it = zonePoly->CIterateSegments( 0, -1, true ); it; it++ ) { - while( GetLineLength( pt1, pt2 ) > UI_EPSILON ) - { - if( conflictOutline->SquaredDistance( pt1 ) < conflictOutline->SquaredDistance( pt2 ) ) - pt2 = ( pt1 + pt2 ) / 2; - else - pt1 = ( pt1 + pt2 ) / 2; - } + SEG::ecoord dist_sq = trackSeg.SquaredDistance( *it ); - // Once we're within UI_EPSILON pt1 and pt2 are "equivalent" - return pt1; + if( dist_sq < closestDist_sq ) + { + closestDist_sq = dist_sq; + closestSeg = *it; + } } + + VECTOR2I pt1 = closestSeg.A; + VECTOR2I pt2 = closestSeg.B; + + // Do a binary search for a "good enough" marker location + while( GetLineLength( (wxPoint) pt1, (wxPoint) pt2 ) > UI_EPSILON ) + { + if( trackSeg.SquaredDistance( pt1 ) < trackSeg.SquaredDistance( pt2 ) ) + pt2 = ( pt1 + pt2 ) / 2; + else + pt1 = ( pt1 + pt2 ) / 2; + } + + // Once we're within UI_EPSILON pt1 and pt2 are "equivalent" + return (wxPoint) pt1; } diff --git a/pcbnew/drc/drc.h b/pcbnew/drc/drc.h index a7f4bf8ee3..3f18068657 100644 --- a/pcbnew/drc/drc.h +++ b/pcbnew/drc/drc.h @@ -246,8 +246,8 @@ public: /** * Fetches a reasonable point for marking a violoation between two non-point objects. */ - static wxPoint GetLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone ); - static wxPoint GetLocation( TRACK* aTrack, const SEG& aConflictSeg ); + static wxPoint GetLocation( PCB_LAYER_ID aLayer, TRACK* aTrack, ZONE_CONTAINER* aZone ); + static wxPoint GetLocation( TRACK* aTrack, const SEG& aSeg ); /** * Open a dialog and prompts the user, then if a test run button is diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp index c3fd7b67bc..b6f30874f6 100644 --- a/pcbnew/drc/drc_clearance_test_functions.cpp +++ b/pcbnew/drc/drc_clearance_test_functions.cpp @@ -443,9 +443,9 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS /* Phase 3: test DRC with copper zones */ /***************************************/ // Can be *very* time consumming. + if( aTestZones ) { - for( ZONE_CONTAINER* zone : m_pcb->Zones() ) { if( !zone->GetLayerSet().test( aLayer ) || zone->GetIsKeepout() ) @@ -474,7 +474,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( aRefSeg, zone ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, zone ) ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aLayer, aRefSeg, zone ) ); addMarkerToPcb( aCommit, marker ); } } diff --git a/pcbnew/drc/drc_keepout_tester.cpp b/pcbnew/drc/drc_keepout_tester.cpp index fa9ff07817..5bdcff4ba7 100644 --- a/pcbnew/drc/drc_keepout_tester.cpp +++ b/pcbnew/drc/drc_keepout_tester.cpp @@ -88,7 +88,9 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias() if( segm->Type() == PCB_TRACE_T && ( m_keepoutFlags & DISALLOW_TRACKS ) != 0 ) { // Ignore if the keepout zone is not on the same layer - if( !m_zone->IsOnLayer( segm->GetLayer() ) ) + PCB_LAYER_ID layer = segm->GetLayer(); + + if( !m_zone->IsOnLayer( layer ) ) continue; SEG trackSeg( segm->GetStart(), segm->GetEnd() ); @@ -103,7 +105,7 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias() drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( segm, m_zone ); - HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( segm, m_zone ) ) ); + HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( layer, segm, m_zone ) ) ); success = false; } } @@ -143,7 +145,7 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias() drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( segm, m_zone ); - HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( segm, m_zone ) ) ); + HandleMarker( new MARKER_PCB( drcItem, via->GetPosition() ) ); success = false; } } diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index 4ecb9ae1fe..8b73bed992 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -1295,11 +1295,8 @@ ZONE_CONTAINER* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode ) double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 ) + pow( center.y - kicad_y( v1.y ), 2 ) ); - // If we are curving, we need at least 2 segments otherwise - // delta_angle == angle - double delta_angle = angle / std::max( - 2, GetArcToSegmentCount( KiROUND( radius ), - ARC_HIGH_DEF, *v1.curve ) - 1 ); + int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve ); + double delta_angle = angle / segCount; for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle ); @@ -1959,12 +1956,7 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const if( KiROUND( radius ) == 0 ) radius = 1.0; - int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve ) - 1; - - // If we are curving, we need at least 2 segments otherwise delta == angle - if( segCount < 2 ) - segCount = 2; - + int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve ); double delta = angle / segCount; for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta ) @@ -2295,12 +2287,8 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals ) radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) + pow( center.y - kicad_y( w.y1 ), 2 ) ); - // If we are curving, we need at least 2 segments otherwise - // delta_angle == angle - int segments = std::max( 2, GetArcToSegmentCount( KiROUND( radius ), - ARC_HIGH_DEF, - *w.curve ) - 1 ); - delta_angle = angle / segments; + int segs = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *w.curve ); + delta_angle = angle / segs; } while( fabs( angle ) > fabs( delta_angle ) ) diff --git a/pcbnew/exporters/export_vrml.cpp b/pcbnew/exporters/export_vrml.cpp index c01e08ea7c..c6dab06a4a 100644 --- a/pcbnew/exporters/export_vrml.cpp +++ b/pcbnew/exporters/export_vrml.cpp @@ -671,8 +671,7 @@ static void export_vrml_polygon( MODEL_VRML& aModel, LAYER_NUM layer, if( aOutline->GetWidth() ) { - int numSegs = std::max( - GetArcToSegmentCount( aOutline->GetWidth() / 2, ARC_HIGH_DEF, 360.0 ), 6 ); + int numSegs = GetArcToSegmentCount( aOutline->GetWidth() / 2, ARC_HIGH_DEF, 360.0 ); shape.Inflate( aOutline->GetWidth() / 2, numSegs ); shape.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); } diff --git a/pcbnew/microwave/microwave_inductor.cpp b/pcbnew/microwave/microwave_inductor.cpp index 754ca76598..eda0b8ad95 100644 --- a/pcbnew/microwave/microwave_inductor.cpp +++ b/pcbnew/microwave/microwave_inductor.cpp @@ -55,7 +55,7 @@ static void gen_arc( std::vector & aBuffer, { auto first_point = aStartPoint - aCenter; auto radius = KiROUND( EuclideanNorm( first_point ) ); - int seg_count = std::max( GetArcToSegmentCount( radius, ARC_HIGH_DEF, a_ArcAngle / 10.0 ), 3 ); + int seg_count = GetArcToSegmentCount( radius, ARC_HIGH_DEF, a_ArcAngle / 10.0 ); double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count; diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index 4e954e07a2..0ed5e8848b 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -344,7 +344,7 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate() // which can create bad shapes if margin.x is < 0 int maxError = aBoard->GetDesignSettings().m_MaxError; - int numSegs = std::max( GetArcToSegmentCount( margin.x, maxError, 360.0 ), 6 ); + int numSegs = GetArcToSegmentCount( margin.x, maxError, 360.0 ); shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST ); dummy.DeletePrimitivesList(); dummy.AddPrimitivePoly( shape, 0 ); @@ -781,9 +781,6 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, // They do not have a solder Mask margin, because they are graphic items // on this layer (like logos), not actually areas around pads. - // Normal mode to generate polygons from shapes with arcs, if any: - DisableArcRadiusCorrection( false ); - itemplotter.PlotBoardGraphicItems(); for( auto module : aBoard->Modules() ) @@ -812,83 +809,81 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, #if NEW_ALGO // Generate polygons with arcs inside the shape or exact shape // to minimize shape changes created by arc to segment size correction. - DisableArcRadiusCorrection( true ); + DISABLE_ARC_RADIUS_CORRECTION disabler; #endif - - // Plot pads - for( auto module : aBoard->Modules() ) { - // add shapes with their exact mask layer size in initialPolys - module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0 ); - // add shapes inflated by aMinThickness/2 in areas - module->TransformPadsShapesWithClearanceToPolygon( layer, areas, inflate ); - } - - // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true, - if( aPlotOpt.GetPlotViaOnMaskLayer() ) - { - // The current layer is a solder mask, use the global mask clearance for vias - int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; - int via_margin = via_clearance + inflate; - - for( auto track : aBoard->Tracks() ) + // Plot pads + for( auto module : aBoard->Modules() ) { - const VIA* via = dyn_cast( track ); - - if( !via ) - continue; - - // vias are plotted only if they are on the corresponding external copper layer - LSET via_set = via->GetLayerSet(); - - if( via_set[B_Cu] ) - via_set.set( B_Mask ); - - if( via_set[F_Cu] ) - via_set.set( F_Mask ); - - if( !( via_set & aLayerMask ).any() ) - continue; - // add shapes with their exact mask layer size in initialPolys - via->TransformShapeWithClearanceToPolygon( initialPolys, layer, via_clearance ); + module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0 ); // add shapes inflated by aMinThickness/2 in areas - via->TransformShapeWithClearanceToPolygon( areas, layer, via_margin ); + module->TransformPadsShapesWithClearanceToPolygon( layer, areas, inflate ); } - } - // Add filled zone areas. + // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true, + if( aPlotOpt.GetPlotViaOnMaskLayer() ) + { + // The current layer is a solder mask, use the global mask clearance for vias + int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; + int via_margin = via_clearance + inflate; + + for( auto track : aBoard->Tracks() ) + { + const VIA* via = dyn_cast( track ); + + if( !via ) + continue; + + // vias are plotted only if they are on the corresponding external copper layer + LSET via_set = via->GetLayerSet(); + + if( via_set[B_Cu] ) + via_set.set( B_Mask ); + + if( via_set[F_Cu] ) + via_set.set( F_Mask ); + + if( !( via_set & aLayerMask ).any() ) + continue; + + // add shapes with their exact mask layer size in initialPolys + via->TransformShapeWithClearanceToPolygon( initialPolys, layer, via_clearance ); + // add shapes inflated by aMinThickness/2 in areas + via->TransformShapeWithClearanceToPolygon( areas, layer, via_margin ); + } + } + + // Add filled zone areas. #if 0 // Set to 1 if a solder mask margin must be applied to zones on solder mask - int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin; + int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin; #else - int zone_margin = 0; + int zone_margin = 0; #endif - for( ZONE_CONTAINER* zone : aBoard->Zones() ) - { - if( zone->GetLayer() != layer ) - continue; + for( ZONE_CONTAINER* zone : aBoard->Zones() ) + { + if( zone->GetLayer() != layer ) + continue; - // add shapes inflated by aMinThickness/2 in areas - zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin ); - // add shapes with their exact mask layer size in initialPolys - zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin ); + // add shapes inflated by aMinThickness/2 in areas + zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin ); + // add shapes with their exact mask layer size in initialPolys + zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin ); + } + + int maxError = aBoard->GetDesignSettings().m_MaxError; + int numSegs = GetArcToSegmentCount( inflate, maxError, 360.0 ); + + // Merge all polygons: After deflating, not merged (not overlapping) polygons + // will have the initial shape (with perhaps small changes due to deflating transform) + areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + areas.Deflate( inflate, numSegs ); + + // Restore initial settings: + aBoard->GetDesignSettings().m_MaxError = currMaxError; } - int maxError = aBoard->GetDesignSettings().m_MaxError; - int numSegs = std::max( GetArcToSegmentCount( inflate, maxError, 360.0 ), 12 ); - - // Merge all polygons: After deflating, not merged (not overlapping) polygons - // will have the initial shape (with perhaps small changes due to deflating transform) - areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - areas.Deflate( inflate, numSegs ); - - // Restore initial settings: - aBoard->GetDesignSettings().m_MaxError = currMaxError; - - // Restore normal option to build polygons from item shapes: - DisableArcRadiusCorrection( false ); - #if !NEW_ALGO // To avoid a lot of code, use a ZONE_CONTAINER to handle and plot polygons, because our // polygons look exactly like filled areas in zones. diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 6a5c46a20f..cd6a8c198b 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -78,12 +78,11 @@ static const bool s_DumpZonesWhenFilling = false; ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) : - m_board( aBoard ), - m_brdOutlinesValid( false ), - m_commit( aCommit ), - m_progressReporter( nullptr ), - m_high_def( 9 ), - m_low_def( 6 ) + m_board( aBoard ), + m_brdOutlinesValid( false ), + m_commit( aCommit ), + m_progressReporter( nullptr ), + m_maxError( ARC_HIGH_DEF ) { } @@ -470,7 +469,7 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE if( aPad->GetShape() == PAD_SHAPE_CUSTOM ) { SHAPE_POLY_SET poly; - aPad->TransformShapeWithClearanceToPolygon( poly, aLayer, aGap, m_high_def ); + aPad->TransformShapeWithClearanceToPolygon( poly, aLayer, aGap, m_maxError ); // the pad shape in zone can be its convex hull or the shape itself if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) @@ -488,14 +487,7 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE } else { - // Optimizing polygon vertex count: the high definition is used for round - // and oval pads (pads with large arcs) but low def for other shapes (with - // small arcs) - if( aPad->GetShape() == PAD_SHAPE_CIRCLE || aPad->GetShape() == PAD_SHAPE_OVAL || - ( aPad->GetShape() == PAD_SHAPE_ROUNDRECT && aPad->GetRoundRectRadiusRatio() > 0.4 ) ) - aPad->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_high_def ); - else - aPad->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_low_def ); + aPad->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError ); } } @@ -512,7 +504,7 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap, case PCB_LINE_T: { DRAWSEGMENT* seg = (DRAWSEGMENT*) aItem; - seg->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_high_def, + seg->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError, aIgnoreLineWidth ); break; } @@ -525,7 +517,7 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap, case PCB_MODULE_EDGE_T: { EDGE_MODULE* edge = (EDGE_MODULE*) aItem; - edge->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_high_def, + edge->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError, aIgnoreLineWidth ); break; } @@ -674,17 +666,17 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA if( !via->IsPadOnLayer( aLayer ) ) { - TransformCircleToPolygon( aHoles, via->GetPosition(), - ( via->GetDrillValue() + 1 ) / 2 + gap, m_low_def ); + int radius = via->GetDrillValue() / 2 + bds.GetHolePlatingThickness() + gap; + TransformCircleToPolygon( aHoles, via->GetPosition(), radius, m_maxError ); } else { - via->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_low_def ); + via->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_maxError ); } } else { - track->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_low_def ); + track->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_maxError ); } } } @@ -780,15 +772,14 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys ) { - m_high_def = m_board->GetDesignSettings().m_MaxError; - m_low_def = std::min( ARC_LOW_DEF, int( m_high_def * 1.5 ) ); // Reasonable value + m_maxError = m_board->GetDesignSettings().m_MaxError; // Features which are min_width should survive pruning; features that are *less* than // min_width should not. Therefore we subtract epsilon from the min_width when // deflating/inflating. int half_min_width = aZone->GetMinThickness() / 2; int epsilon = Millimeter2iu( 0.001 ); - int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 ); + int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 ); SHAPE_POLY_SET::CORNER_STRATEGY cornerStrategy; @@ -974,7 +965,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, // deflating/inflating. int half_min_width = aZone->GetMinThickness() / 2; int epsilon = Millimeter2iu( 0.001 ); - int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 ); + int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 ); if( m_brdOutlinesValid ) smoothedPoly.BooleanIntersection( m_boardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); diff --git a/pcbnew/zone_filler.h b/pcbnew/zone_filler.h index 512324b7c2..4dbba156cd 100644 --- a/pcbnew/zone_filler.h +++ b/pcbnew/zone_filler.h @@ -115,14 +115,7 @@ private: WX_PROGRESS_REPORTER* m_progressReporter; std::unique_ptr m_uniqueReporter; - // m_high_def can be used to define a high definition arc to polygon approximation - int m_high_def; - - // m_low_def can be used to define a low definition arc to polygon approximation - // Used when converting some pad shapes that can accept lower resolution, vias and track ends. - // Rect pads use m_low_def to reduce the number of segments. For these shapes a low def - // gives a good shape, because the arc is small (90 degrees) and a small part of the shape. - int m_low_def; + int m_maxError; }; #endif