diff --git a/3d-viewer/3d_canvas/board_adapter.h b/3d-viewer/3d_canvas/board_adapter.h index 7f65d59e7e..f91ffa7cb6 100644 --- a/3d-viewer/3d_canvas/board_adapter.h +++ b/3d-viewer/3d_canvas/board_adapter.h @@ -603,21 +603,10 @@ class BOARD_ADAPTER SHAPE_POLY_SET &aCornerBuffer, int aWidth) const; - void transformPadsShapesWithClearanceToPolygon( const PADS &aPads, - PCB_LAYER_ID aLayer, - SHAPE_POLY_SET &aCornerBuffer, - int aInflateValue, - bool aSkipNPTHPadsWihNoCopper) const; - void transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer ) const; - void buildPadShapePolygon( const D_PAD *aPad, - SHAPE_POLY_SET &aCornerBuffer, - wxSize aInflateValue ) const; - - public: SFVEC3D m_BgColorBot; ///< background bottom color SFVEC3D m_BgColorTop; ///< background top color diff --git a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp index 4bccbf6685..0c1ce03771 100644 --- a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp +++ b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp @@ -33,7 +33,6 @@ #include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.h" #include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h" #include "../3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h" -#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h" #include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h" #include #include @@ -45,13 +44,15 @@ #include #include #include +#include #include +#include +#include #include #include #include - // These variables are parameters used in addTextSegmToContainer. // But addTextSegmToContainer is a call-back function, // so we cannot send them as arguments. @@ -310,261 +311,89 @@ void BOARD_ADAPTER::createNewTrack( const TRACK* aTrack, CGENERICCONTAINER2D *aD } -// Based on: -// void D_PAD:: TransformShapeWithClearanceToPolygon( -// board_items_to_polygon_shape_transform.cpp void BOARD_ADAPTER::createNewPadWithClearance( const D_PAD* aPad, CGENERICCONTAINER2D *aDstContainer, wxSize aClearanceValue ) const { - // note: for most of shapes, aClearanceValue.x = aClearanceValue.y - // only rectangular and oval shapes can have different values - // when drawn on the solder paste layer, because we can have a margin that is a - // percent of pad size - const int dx = (aPad->GetSize().x / 2) + aClearanceValue.x; - const int dy = (aPad->GetSize().y / 2) + aClearanceValue.y; + SHAPE_POLY_SET poly; - if( !dx || !dy ) + if( aClearanceValue.x != aClearanceValue.y ) { - wxLogTrace( m_logTrace, - wxT( "BOARD_ADAPTER::createNewPadWithClearance - found an invalid pad" ) ); - - return; + // Our shape-based builder can't handle differing x:y clearance values (which + // get generated when relative paste margin is used with an oblong pad). So + // we fake a larger pad and run the general-purpose polygon builder on it. + D_PAD dummy( *aPad ); + dummy.SetSize( aPad->GetSize() + aClearanceValue + aClearanceValue ); + dummy.TransformShapeWithClearanceToPolygon( poly, 0 ); } - - wxPoint PadShapePos = aPad->ShapePos(); // Note: for pad having a shape offset, - // the pad position is NOT the shape position - - switch( aPad->GetShape() ) + else { - case PAD_SHAPE_CIRCLE: - { - const float radius = dx * m_biuTo3Dunits; - - const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, - -PadShapePos.y * m_biuTo3Dunits ); - - aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); - } - break; - - case PAD_SHAPE_OVAL: - { - if( dx == dy ) + for( const std::shared_ptr& shape : aPad->GetEffectiveShapes() ) { - // The segment object cannot store start and end the same position, - // so add a circle instead - const float radius = dx * m_biuTo3Dunits; - - const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, - -PadShapePos.y * m_biuTo3Dunits ); - - aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); - } - else - { - // An oval pad has the same shape as a segment with rounded ends - - int iwidth; - wxPoint shape_offset = wxPoint( 0, 0 ); - - if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis + switch( shape->Type() ) { - shape_offset.y = dy - dx; - iwidth = dx * 2; - } - else //if( dy < dx ) + case SH_SEGMENT: { - shape_offset.x = dy - dx; - iwidth = dy * 2; - } + const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape.get(); + const SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits, + -seg->GetSeg().A.y * m_biuTo3Dunits ); + const SFVEC2F end3DU ( seg->GetSeg().B.x * m_biuTo3Dunits, + -seg->GetSeg().B.y * m_biuTo3Dunits ); + const int width = seg->GetWidth() + aClearanceValue.x * 2; - RotatePoint( &shape_offset, aPad->GetOrientation() ); - - const wxPoint start = PadShapePos - shape_offset; - const wxPoint end = PadShapePos + shape_offset; - - const SFVEC2F start3DU( start.x * m_biuTo3Dunits, -start.y * m_biuTo3Dunits ); - const SFVEC2F end3DU ( end.x * m_biuTo3Dunits, -end.y * m_biuTo3Dunits ); - - // Cannot add segments that have the same start and end point - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, - (iwidth / 2) * m_biuTo3Dunits, - *aPad ) ); - } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, - iwidth * m_biuTo3Dunits, - *aPad ) ); - } - } - } - break; - - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_RECT: - { - // see pcbnew/board_items_to_polygon_shape_transform.cpp - wxPoint corners[4]; - bool drawOutline; - - // For aClearanceValue.x == aClearanceValue.y and > 0 we use the pad shape - // and draw outlines with thicknes = aClearanceValue. - // Otherwise we draw only the inflated/deflated shape - if( aClearanceValue.x > 0 && aClearanceValue.x == aClearanceValue.y ) - { - drawOutline = true; - aPad->BuildPadPolygon( corners, wxSize( 0, 0 ), aPad->GetOrientation() ); - } - else - { - drawOutline = false; - aPad->BuildPadPolygon( corners, aClearanceValue, aPad->GetOrientation() ); - } - - SFVEC2F corners3DU[4]; - - // Note: for pad having a shape offset, - // the pad position is NOT the shape position - for( unsigned int ii = 0; ii < 4; ++ii ) - { - corners[ii] += aPad->ShapePos(); // Shift origin to position - - corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, - -corners[ii].y * m_biuTo3Dunits ); - } - - - // Learn more at: - // https://lists.launchpad.net/kicad-developers/msg18729.html - - // Add the PAD polygon - aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], - corners3DU[1], - corners3DU[2], - corners3DU[3], - *aPad ) ); - - // Add the PAD contours - // Round segments cannot have 0-length elements, so we approximate them - // as a small circle - if( drawOutline ) - { - for( int i = 1; i <= 4; i++ ) - { - if( Is_segment_a_circle( corners3DU[i - 1], corners3DU[i & 3] ) ) + // Cannot add segments that have the same start and end point + if( Is_segment_a_circle( start3DU, end3DU ) ) { - aDstContainer->Add( new CFILLEDCIRCLE2D( corners3DU[i - 1], - aClearanceValue.x * m_biuTo3Dunits, - *aPad ) ); + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + ( width / 2) * m_biuTo3Dunits, + *aPad ) ); } else { - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[i - 1], - corners3DU[i & 3], - aClearanceValue.x * 2.0f * m_biuTo3Dunits, + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, + width * m_biuTo3Dunits, *aPad ) ); } } - } - } - break; + break; - case PAD_SHAPE_ROUNDRECT: - { - wxSize shapesize( aPad->GetSize() ); - shapesize.x += aClearanceValue.x * 2; - shapesize.y += aClearanceValue.y * 2; - - int rounding_radius = aPad->GetRoundRectCornerRadius( shapesize ); - - wxPoint corners[4]; - - GetRoundRectCornerCenters( corners, - rounding_radius, - PadShapePos, - shapesize, - aPad->GetOrientation() ); - - SFVEC2F corners3DU[4]; - - for( unsigned int ii = 0; ii < 4; ++ii ) - corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, - -corners[ii].y * m_biuTo3Dunits ); - - // Add the PAD polygon (For some reason the corners need - // to be inverted to display with the correctly orientation) - aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], - corners3DU[3], - corners3DU[2], - corners3DU[1], - *aPad ) ); - - // Add the PAD contours - // Round segments cannot have 0-length elements, so we approximate them - // as a small circle - for( int i = 1; i <= 4; i++ ) - { - if( Is_segment_a_circle( corners3DU[i - 1], corners3DU[i & 3] ) ) + case SH_CIRCLE: { - aDstContainer->Add( new CFILLEDCIRCLE2D( corners3DU[i - 1], - rounding_radius * m_biuTo3Dunits, - *aPad ) ); + const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape.get(); + const int radius = circle->GetRadius() + aClearanceValue.x; + const SFVEC2F center( circle->GetCenter().x * m_biuTo3Dunits, + -circle->GetCenter().y * m_biuTo3Dunits ); + + aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius * m_biuTo3Dunits, *aPad ) ); } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[i - 1], - corners3DU[i & 3], - rounding_radius * 2.0f * m_biuTo3Dunits, - *aPad ) ); + break; + + case SH_SIMPLE: + poly.AddOutline( static_cast( shape.get() )->Vertices() ); + break; + + case SH_POLY_SET: + poly = *(SHAPE_POLY_SET*) shape.get(); + break; + + default: + wxFAIL_MSG( "BOARD_ADAPTER::createNewPadWithClearance unimplemented shape" ); + break; } } } - break; - case PAD_SHAPE_CHAMFERED_RECT: + if( !poly.IsEmpty() ) { - wxSize shapesize( aPad->GetSize() ); - shapesize.x += aClearanceValue.x * 2; - shapesize.y += aClearanceValue.y * 2; - - SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates - - int corner_radius = aPad->GetRoundRectCornerRadius( shapesize ); - TransformRoundChamferedRectToPolygon( polyList, PadShapePos, shapesize, aPad->GetOrientation(), - corner_radius, aPad->GetChamferRectRatio(), - aPad->GetChamferPositions(), ARC_HIGH_DEF ); - - // Add the PAD polygon - Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad ); - - } - break; - - case PAD_SHAPE_CUSTOM: - { - SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates - polyList.Append( aPad->GetCustomShapeAsPolygon() ); - aPad->CustomShapeAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() ); - if( aClearanceValue.x ) - polyList.Inflate( aClearanceValue.x, 32 ); + poly.Inflate( aClearanceValue.x, 32 ); // Add the PAD polygon - Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad ); - - } - break; + Convert_shape_line_polygon_to_triangles( poly, *aDstContainer, m_biuTo3Dunits, *aPad ); } } -// Based on: -// BuildPadDrillShapePolygon -// board_items_to_polygon_shape_transform.cpp COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValue ) { wxSize drillSize = aPad->GetDrillSize(); @@ -587,29 +416,16 @@ COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValu } else // Oblong hole { - wxPoint start, end; - int width; + const std::shared_ptr& seg = aPad->GetEffectiveHoleShape(); + float width = seg->GetWidth() + aInflateValue * 2; - aPad->GetOblongGeometry( aPad->GetDrillSize(), &start, &end, &width ); + SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits, + -seg->GetSeg().A.y * m_biuTo3Dunits ); - width += aInflateValue * 2; - start += aPad->GetPosition(); - end += aPad->GetPosition(); + SFVEC2F end3DU ( seg->GetSeg().B.x * m_biuTo3Dunits, + -seg->GetSeg().B.y * m_biuTo3Dunits ); - SFVEC2F start3DU( start.x * m_biuTo3Dunits, - -start.y * m_biuTo3Dunits ); - - SFVEC2F end3DU ( end.x * m_biuTo3Dunits, - -end.y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - return new CFILLEDCIRCLE2D( start3DU, (width / 2) * m_biuTo3Dunits, *aPad ); - } - else - { - return new CROUNDSEGMENT2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad ); - } + return new CROUNDSEGMENT2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad ); } return NULL; @@ -951,15 +767,13 @@ void BOARD_ADAPTER::AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneC float radius = line_thickness/2; if( radius > 0.0 ) // degenerated circles crash 3D viewer - aDstContainer->Add( - new CFILLEDCIRCLE2D( start3DU, radius, - *aZoneContainer ) ); + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, radius, + *aZoneContainer ) ); } else { - aDstContainer->Add( - new CROUNDSEGMENT2D( start3DU, end3DU, line_thickness, - *aZoneContainer ) ); + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, line_thickness, + *aZoneContainer ) ); } } } @@ -986,12 +800,9 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad, return; } - // For other shapes, draw polygon outlines + // For other shapes, add outlines as thick segments in polygon buffer SHAPE_POLY_SET corners; - aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ) ); - - - // Add outlines as thick segments in polygon buffer + aPad->TransformShapeWithClearanceToPolygon( corners, 0 ); const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); @@ -1005,13 +816,13 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad, if( Is_segment_a_circle( start3DU, end3DU ) ) { - aDstContainer->Add( - new CFILLEDCIRCLE2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits, *aPad ) ); + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits, + *aPad ) ); } else { - aDstContainer->Add( - new CROUNDSEGMENT2D( start3DU, end3DU, aWidth * m_biuTo3Dunits, *aPad ) ); + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, aWidth * m_biuTo3Dunits, + *aPad ) ); } } } diff --git a/3d-viewer/3d_canvas/create_layer_items.cpp b/3d-viewer/3d_canvas/create_layer_items.cpp index 148a80889a..d3ab39af6d 100644 --- a/3d-viewer/3d_canvas/create_layer_items.cpp +++ b/3d-viewer/3d_canvas/create_layer_items.cpp @@ -33,13 +33,7 @@ #include "board_adapter.h" #include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.h" #include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h" -#include "../3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h" -#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h" -#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h" -#include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h" -#include "../3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h" #include "../3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h" -#include "../3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h" #include #include @@ -47,16 +41,17 @@ #include #include #include -#include #include #include -#include #include #include #include #include +#ifdef PRINT_STATISTICS_3D_VIEWER #include +#endif + void BOARD_ADAPTER::destroyLayers() { @@ -501,13 +496,13 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED ) { - pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly, inflate ); - pad->BuildPadDrillShapePolygon( m_through_inner_holes_poly, 0 ); + pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly, inflate ); + pad->TransformHoleWithClearanceToPolygon( m_through_inner_holes_poly, 0 ); } else { // If not plated, no copper. - pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly_NPTH, inflate ); + pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly_NPTH, inflate ); } } } @@ -568,11 +563,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) // Note: NPTH pads are not drawn on copper layers when the pad // has same shape as its hole - transformPadsShapesWithClearanceToPolygon( module->Pads(), - curr_layer_id, - *layerPoly, - 0, - true ); + module->TransformPadsShapesWithClearanceToPolygon( curr_layer_id, + *layerPoly, + 0, + true ); // Micro-wave modules may have items on copper layers module->TransformGraphicTextWithClearanceToPolygonSet( curr_layer_id, @@ -983,8 +977,8 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) } else { - transformPadsShapesWithClearanceToPolygon( - module->Pads(), curr_layer_id, *layerPoly, 0, false ); + module->TransformPadsShapesWithClearanceToPolygon( curr_layer_id, *layerPoly, 0, + false ); } // On tech layers, use a poor circle approximation, only for texts (stroke font) diff --git a/3d-viewer/3d_canvas/create_layer_poly.cpp b/3d-viewer/3d_canvas/create_layer_poly.cpp index e3e6c6f5ef..b0e9cd7f8e 100644 --- a/3d-viewer/3d_canvas/create_layer_poly.cpp +++ b/3d-viewer/3d_canvas/create_layer_poly.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015-2016 Mario Luzeiro - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,9 +25,7 @@ /** * @file create_layer_poly.cpp * @brief This file implements the creation of the pcb board items in the poly - * contours format. It is based on the function found in the files: - * board_items_to_polygon_shape_transform.cpp - * board_items_to_polygon_shape_transform.cpp + * contours format. */ #include "board_adapter.h" @@ -36,93 +34,20 @@ #include -// This is the same function as in board_items_to_polygon_shape_transform.cpp -// but it adds the rect/trapezoid shapes with a different winding -void BOARD_ADAPTER::buildPadShapePolygon( const D_PAD* aPad, - SHAPE_POLY_SET& aCornerBuffer, - wxSize aInflateValue ) const -{ - wxPoint PadShapePos = aPad->ShapePos(); /* Note: for pad having a shape offset, - * the pad position is NOT the shape position */ - switch( aPad->GetShape() ) - { - case PAD_SHAPE_CIRCLE: - case PAD_SHAPE_OVAL: - case PAD_SHAPE_ROUNDRECT: - case PAD_SHAPE_CHAMFERED_RECT: - { - // We are using TransformShapeWithClearanceToPolygon to build the shape. - // Currently, this method uses only the same inflate value for X and Y dirs. - // so because here this is not the case, we use a inflated dummy pad to build - // the polygonal shape - // TODO: remove this dummy pad when TransformShapeWithClearanceToPolygon will use - // a wxSize to inflate the pad size - D_PAD dummy( *aPad ); - wxSize new_size = aPad->GetSize() + aInflateValue + aInflateValue; - dummy.SetSize( new_size ); - dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 ); - } - break; - - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_RECT: - { - wxPoint corners[4]; - SHAPE_LINE_CHAIN aLineChain; - - aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() ); - - for( int ii = 0; ii < 4; ++ii ) - { - corners[3-ii] += PadShapePos; // Shift origin to position - aLineChain.Append( corners[3-ii].x, corners[3-ii].y ); - } - - aLineChain.SetClosed( true ); - - aCornerBuffer.AddOutline( aLineChain ); - } - break; - - case PAD_SHAPE_CUSTOM: - { - SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates - auto inflate_val = std::max( aInflateValue.x, aInflateValue.y ); - - polyList.Append( aPad->GetCustomShapeAsPolygon() ); - aPad->CustomShapeAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() ); - - if( inflate_val > 0 ) - { - int numSegs = GetNrSegmentsCircle( inflate_val ); - polyList.Inflate( inflate_val, numSegs ); - } - - aCornerBuffer.Append( polyList ); - } - break; - } -} - - void BOARD_ADAPTER::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad, SHAPE_POLY_SET& aCornerBuffer, - int aWidth ) const + int aWidth ) const { if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring { - TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), - aPad->GetSize().x / 2, ARC_HIGH_DEF, aWidth ); + TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), aPad->GetSize().x / 2, + ARC_HIGH_DEF, aWidth ); return; } - - // For other shapes, draw polygon outlines + // For other shapes, add outlines as thick segments in polygon buffer SHAPE_POLY_SET corners; - - buildPadShapePolygon( aPad, corners, wxSize( 0, 0 ) ); - - // Add outlines as thick segments in polygon buffer + aPad->TransformShapeWithClearanceToPolygon( corners, 0 ); const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); @@ -131,94 +56,24 @@ 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 ); + TransformSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ), + ARC_HIGH_DEF, aWidth ); } } -// Based on the same function name in board_items_to_polyshape_transform.cpp -// It was implemented here to allow dynamic segments count per pad shape -void BOARD_ADAPTER::transformPadsShapesWithClearanceToPolygon( const PADS& aPads, PCB_LAYER_ID aLayer, - SHAPE_POLY_SET& aCornerBuffer, - int aInflateValue, - bool aSkipNPTHPadsWihNoCopper ) const -{ - wxSize margin; - for( auto pad : aPads ) - { - if( !pad->IsOnLayer(aLayer) ) - continue; - - // NPTH pads are not drawn on layers if the shape size and pos is the same - // as their hole: - if( aSkipNPTHPadsWihNoCopper && (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED) ) - { - if( (pad->GetDrillSize() == pad->GetSize()) && - (pad->GetOffset() == wxPoint( 0, 0 )) ) - { - switch( pad->GetShape() ) - { - case PAD_SHAPE_CIRCLE: - if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) - continue; - break; - - case PAD_SHAPE_OVAL: - if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) - continue; - break; - - default: - break; - } - } - } - - switch( aLayer ) - { - case F_Mask: - case B_Mask: - margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; - break; - - case F_Paste: - case B_Paste: - margin = pad->GetSolderPasteMargin(); - margin.x += aInflateValue; - margin.y += aInflateValue; - break; - - default: - margin.x = margin.y = aInflateValue; - break; - } - - buildPadShapePolygon( pad, aCornerBuffer, margin ); - } -} - void BOARD_ADAPTER::transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer ) const { - for( auto item : aModule->GraphicalItems() ) + for( BOARD_ITEM* item : aModule->GraphicalItems() ) { - switch( item->Type() ) + if( item->Type() == PCB_MODULE_EDGE_T ) { - case PCB_MODULE_EDGE_T: - { - EDGE_MODULE*outline = (EDGE_MODULE*) item; + EDGE_MODULE* outline = (EDGE_MODULE*) item; - if( outline->GetLayer() != aLayer ) - break; - - outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 ); - } - break; - - default: - break; + if( outline->GetLayer() == aLayer ) + outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 ); } } } 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 20d774c53b..24db3c57d7 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 @@ -817,10 +817,10 @@ void C3D_RENDER_OGL_LEGACY::generate_3D_Vias_and_Pads() double correctionFactor = m_boardAdapter.GetCircleCorrectionFactor( nrSegments ); int correction = radius * ( correctionFactor - 1 ); - pad->BuildPadDrillShapePolygon( - tht_outer_holes_poly, copperThickness + correction ); + pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly, + copperThickness + correction ); - pad->BuildPadDrillShapePolygon( tht_inner_holes_poly, correction ); + pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, correction ); } } } diff --git a/libs/kimath/include/convert_basic_shapes_to_polygon.h b/libs/kimath/include/convert_basic_shapes_to_polygon.h index bba344bf3b..f83fa60bac 100644 --- a/libs/kimath/include/convert_basic_shapes_to_polygon.h +++ b/libs/kimath/include/convert_basic_shapes_to_polygon.h @@ -110,8 +110,8 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint& * 4 = BOTTOM_LEFT * 8 = BOTTOM_RIGHT * One can have more than one chamfered corner by ORing the corner identifers - * @param aApproxErrorMax = the IU allowed for error in approximation - * @param aMinSegPerCircleCount = the minimal segments per circle count in approximation + * @param aError = the IU allowed for error in approximation + * @param aSegsPerCircle = the minimal segments per circle count in approximation * (aApproxErrorMax can generate must more seg count than aMinSegPerCircleCount) * To allow a reasonable good shape even for very small shapes, the min count is 16 * (must be a multiple of 4 becauseusually arcs are 90 deg. @@ -119,8 +119,7 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint& void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, const wxSize& aSize, double aRotation, int aCornerRadius, - double aChamferRatio, int aChamferCorners, - int aApproxErrorMax, int aMinSegPerCircleCount = 16 ); + double aChamferRatio, int aChamferCorners, int aError ); /** * Function TransformRoundedEndsSegmentToPolygon diff --git a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp index 7acb635d4e..6811212608 100644 --- a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp +++ b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp @@ -5,7 +5,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,8 +38,7 @@ #include -void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aBuffer, - wxPoint aCenter, int aRadius, +void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aCornerBuffer, wxPoint aCenter, int aRadius, int aError ) { wxPoint corner_position; @@ -56,10 +55,10 @@ void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aBuffer, double angle = (ii * delta) + halfstep; RotatePoint( &corner_position, angle ); corner_position += aCenter; - aBuffer.Append( corner_position.x, corner_position.y ); + aCornerBuffer.Append( corner_position.x, corner_position.y ); } - aBuffer.SetClosed( true ); + aCornerBuffer.SetClosed( true ); } @@ -192,8 +191,8 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo } -void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, - const wxPoint& aPosition, const wxSize& aSize, double aRotation ) +void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint& aPosition, + const wxSize& aSize, double aRotation ) { wxSize size( aSize/2 ); @@ -202,25 +201,16 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, // Ensure size is > 0, to avoid generating unusable shapes // which can crash kicad. - if( size.x <= 1 ) - size.x = 1; - if( size.y <= 1 ) - size.y = 1; + size.x = std::max( 1, size.x ); + size.y = std::max( 1, size.y ); - aCenters[0].x = -size.x; - aCenters[0].y = size.y; - - aCenters[1].x = size.x; - aCenters[1].y = size.y; - - aCenters[2].x = size.x; - aCenters[2].y = -size.y; - - aCenters[3].x = -size.x; - aCenters[3].y = -size.y; + aCenters[0] = wxPoint( -size.x, size.y ); + aCenters[1] = wxPoint( size.x, size.y ); + aCenters[2] = wxPoint( size.x, -size.y ); + aCenters[3] = wxPoint( -size.x, -size.y ); // Rotate the polygon - if( aRotation ) + if( aRotation != 0.0 ) { for( int ii = 0; ii < 4; ii++ ) RotatePoint( &aCenters[ii], aRotation ); @@ -232,11 +222,10 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, } -void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, - const wxPoint& aPosition, const wxSize& aSize, - double aRotation, int aCornerRadius, - double aChamferRatio, int aChamferCorners, - int aApproxErrorMax, int aMinSegPerCircleCount ) +void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, + const wxSize& aSize, double aRotation, + int aCornerRadius, double aChamferRatio, + int aChamferCorners, int aError ) { // Build the basic shape in orientation 0.0, position 0,0 for chamfered corners // or in actual position/orientation for round rect only @@ -248,11 +237,12 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, SHAPE_POLY_SET outline; outline.NewOutline(); - for( int ii = 0; ii < 4; ++ii ) - outline.Append( corners[ii].x, corners[ii].y ); + for( const wxPoint& corner : corners) + outline.Append( corner ); - int numSegs = std::max( GetArcToSegmentCount( aCornerRadius, aApproxErrorMax, 360.0 ), - aMinSegPerCircleCount ); + // 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 ); outline.Inflate( aCornerRadius, numSegs ); if( aChamferCorners == RECT_NO_CHAMFER ) // no chamfer @@ -322,8 +312,7 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, } -void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, - wxPoint aStart, wxPoint aEnd, +void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aError, int aWidth ) { int radius = aWidth / 2; @@ -392,9 +381,8 @@ void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, } -void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, - wxPoint aCentre, wxPoint aStart, double aArcAngle, - int aError, int aWidth ) +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 ); @@ -404,9 +392,7 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, arc_end = arc_start = aStart; if( aArcAngle != 3600 ) - { RotatePoint( &arc_end, aCentre, -aArcAngle ); - } if( aArcAngle < 0 ) { @@ -422,8 +408,7 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, { curr_end = arc_start; RotatePoint( &curr_end, aCentre, -ii ); - TransformSegmentToPolygon( aCornerBuffer, curr_start, curr_end, aError, - aWidth ); + TransformSegmentToPolygon( aCornerBuffer, curr_start, curr_end, aError, aWidth ); curr_start = curr_end; } @@ -432,14 +417,12 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, } -void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer, - wxPoint aCentre, int aRadius, +void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, int aRadius, int aError, int aWidth ) { // Compute the corners positions and creates the poly - wxPoint curr_point; - int inner_radius = aRadius - ( aWidth / 2 ); - int outer_radius = inner_radius + aWidth; + int inner_radius = aRadius - ( aWidth / 2 ); + int outer_radius = inner_radius + aWidth; if( inner_radius <= 0 ) { //In this case, the ring is just a circle (no hole inside) diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 0252114329..ab37e9d749 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -40,8 +40,10 @@ #include #include #include +#include #include // for KiROUND + // A helper struct for the callback function // These variables are parameters used in addTextSegmToPoly. // But addTextSegmToPoly is a call-back function, @@ -519,8 +521,8 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, // Most of time pads are using the segment count given by aError value. const int pad_min_seg_per_circle_count = 16; double angle = m_Orient; - int dx = (m_Size.x / 2) + aClearanceValue; - int dy = (m_Size.y / 2) + aClearanceValue; + int dx = m_Size.x / 2; + int dy = m_Size.y / 2; wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset, // the pad position is NOT the shape position @@ -528,35 +530,20 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, switch( GetShape() ) { case PAD_SHAPE_CIRCLE: - TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError ); - break; - case PAD_SHAPE_OVAL: - - // If the oval is actually a circle (same x/y size), treat it the same if( dx == dy ) { - TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError ); + TransformCircleToPolygon( aCornerBuffer, padShapePos, dx + aClearanceValue, aError ); } else { - int width; - wxPoint shape_offset; - if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis - { - shape_offset.y = dy - dx; - width = dx * 2; - } - else //if( dy <= dx ) - { - shape_offset.x = dy - dx; - width = dy * 2; - } + int half_width = std::min( dx, dy ); + wxPoint delta( dx - half_width, dy - half_width ); - RotatePoint( &shape_offset, angle ); - wxPoint start = padShapePos - shape_offset; - wxPoint end = padShapePos + shape_offset; - TransformOvalToPolygon( aCornerBuffer, start, end, width, aError ); + RotatePoint( &delta, angle ); + + TransformOvalToPolygon( aCornerBuffer, padShapePos - delta, padShapePos + delta, + half_width * 2 + aClearanceValue, aError ); } break; @@ -564,14 +551,21 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT: { + int ddx = GetShape() == PAD_SHAPE_TRAPEZOID ? m_DeltaSize.x / 2 : 0; + int ddy = GetShape() == PAD_SHAPE_TRAPEZOID ? m_DeltaSize.y / 2 : 0; + wxPoint corners[4]; - BuildPadPolygon( corners, wxSize( 0, 0 ), angle ); + corners[0] = wxPoint( -dx + ddy, dy + ddx ); + corners[1] = wxPoint( dx - ddy, dy - ddx ); + corners[2] = wxPoint( dx + ddy, -dy + ddx ); + corners[3] = wxPoint( -dx - ddy, -dy - ddx ); SHAPE_POLY_SET outline; outline.NewOutline(); for( wxPoint& corner : corners ) { + RotatePoint( &corner, angle ); corner += padShapePos; outline.Append( corner.x, corner.y ); } @@ -615,9 +609,11 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, case PAD_SHAPE_CUSTOM: { - SHAPE_POLY_SET outline; // Will contain the corners in board coordinates - outline.Append( m_customShapeAsPolygon ); - CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() ); + SHAPE_POLY_SET outline; + MergePrimitivesAsPolygon( &outline ); + outline.Rotate( -DECIDEG2RAD( m_Orient ) ); + outline.Move( VECTOR2I( m_Pos ) ); + // TODO: do we need the Simplify() & Fracture() if we're not inflating? outline.Simplify( SHAPE_POLY_SET::PM_FAST ); @@ -640,91 +636,18 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, -/* - * Function BuildPadShapePolygon - * Build the corner list of the polygonal shape, depending on shape, clearance and orientation - * Note: for round & oval pads this function is equivalent to TransformShapeWithClearanceToPolygon, - * but not for other shapes - */ -void D_PAD::BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue, - int aError ) const -{ - switch( GetShape() ) - { - case PAD_SHAPE_CIRCLE: - case PAD_SHAPE_OVAL: - case PAD_SHAPE_ROUNDRECT: - case PAD_SHAPE_CHAMFERED_RECT: - { - // We are using TransformShapeWithClearanceToPolygon to build the shape. - // Currently, this method uses only the same inflate value for X and Y dirs. - // so because here this is not the case, we use a inflated dummy pad to build - // the polygonal shape - // TODO: remove this dummy pad when TransformShapeWithClearanceToPolygon will use - // a wxSize to inflate the pad size - D_PAD dummy( *this ); - dummy.SetSize( GetSize() + aInflateValue + aInflateValue ); - dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 ); - } - break; - - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_RECT: - { - wxPoint corners[4]; - wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset, - // the pad position is NOT the shape position - - aCornerBuffer.NewOutline(); - BuildPadPolygon( corners, aInflateValue, m_Orient ); - - for( wxPoint& corner : corners ) - { - corner += padShapePos; // Shift origin to position - aCornerBuffer.Append( corner.x, corner.y ); - } - } - break; - - case PAD_SHAPE_CUSTOM: - { - // For a custom shape, that is in fact a polygon (with holes), we use only a single - // inflate value (different values for X and Y have no definition for a custom pad). - int inflate = ( aInflateValue.x + aInflateValue.y ) / 2; - - TransformShapeWithClearanceToPolygon( aCornerBuffer, inflate ); - } - break; - } -} - - -bool D_PAD::BuildPadDrillShapePolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, - int aError ) const +bool D_PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, + int aError ) const { wxSize drillsize = GetDrillSize(); if( !drillsize.x || !drillsize.y ) return false; - if( drillsize.x == drillsize.y ) // usual round hole - { - int radius = ( drillsize.x / 2 ) + aInflateValue; - TransformCircleToPolygon( aCornerBuffer, GetPosition(), radius, aError ); - } - else // Oblong hole - { - wxPoint start, end; - int width; + const std::shared_ptr& seg = GetEffectiveHoleShape(); - GetOblongGeometry( GetDrillSize(), &start, &end, &width ); - - start += GetPosition(); - end += GetPosition(); - width += aInflateValue * 2; - - TransformSegmentToPolygon( aCornerBuffer, start, end, aError, width ); - } + TransformSegmentToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B, + aError, seg->GetWidth() + aInflateValue * 2 ); return true; } diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index 0178df2fe4..3b257dd3c9 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -243,15 +243,8 @@ public: double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; } void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; } - void SetZoneConnection( ZONE_CONNECTION aType ) - { - m_ZoneConnection = aType; - } - - ZONE_CONNECTION GetZoneConnection() const - { - return m_ZoneConnection; - } + void SetZoneConnection( ZONE_CONNECTION aType ) { m_ZoneConnection = aType; } + ZONE_CONNECTION GetZoneConnection() const { return m_ZoneConnection; } void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; } int GetThermalWidth() const { return m_ThermalWidth; } diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 6f4a359d54..39bd317725 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -36,16 +36,18 @@ #include #include // for KiROUND #include -#include +#include +#include +#include #include #include - #include #include #include #include #include +#include D_PAD::D_PAD( MODULE* parent ) : BOARD_CONNECTED_ITEM( parent, PCB_PAD_T ) @@ -87,7 +89,8 @@ D_PAD::D_PAD( MODULE* parent ) : SetSubRatsnest( 0 ); // used in ratsnest calculations - m_boundingRadius = -1; + m_shapesDirty = true; + m_effectiveBoundingRadius = 0; } @@ -133,77 +136,31 @@ bool D_PAD::IsFlipped() const return false; } -int D_PAD::boundingRadius() const + +int D_PAD::calcBoundingRadius() const { - int x, y; - int radius; + int radius = 0; + SHAPE_POLY_SET polygons; + TransformShapeWithClearanceToPolygon( polygons, 0 ); - switch( GetShape() ) + for( int cnt = 0; cnt < polygons.OutlineCount(); ++cnt ) { - case PAD_SHAPE_CIRCLE: - radius = m_Size.x / 2; - break; + const SHAPE_LINE_CHAIN& poly = polygons.COutline( cnt ); - case PAD_SHAPE_OVAL: - radius = std::max( m_Size.x, m_Size.y ) / 2; - break; - - case PAD_SHAPE_RECT: - radius = 1 + KiROUND( EuclideanNorm( m_Size ) / 2 ); - break; - - case PAD_SHAPE_TRAPEZOID: - x = m_Size.x + std::abs( m_DeltaSize.y ); // Remember: m_DeltaSize.y is the m_Size.x change - y = m_Size.y + std::abs( m_DeltaSize.x ); // Remember: m_DeltaSize.x is the m_Size.y change - radius = 1 + KiROUND( hypot( x, y ) / 2 ); - break; - - case PAD_SHAPE_ROUNDRECT: - radius = GetRoundRectCornerRadius(); - x = m_Size.x >> 1; - y = m_Size.y >> 1; - radius += 1 + KiROUND( EuclideanNorm( wxSize( x - radius, y - radius ))); - break; - - case PAD_SHAPE_CHAMFERED_RECT: - radius = GetRoundRectCornerRadius(); - x = m_Size.x >> 1; - y = m_Size.y >> 1; - radius += 1 + KiROUND( EuclideanNorm( wxSize( x - radius, y - radius ))); - // TODO: modify radius if the chamfer is smaller than corner radius - break; - - case PAD_SHAPE_CUSTOM: - radius = 0; - - for( int cnt = 0; cnt < m_customShapeAsPolygon.OutlineCount(); ++cnt ) + for( int ii = 0; ii < poly.PointCount(); ++ii ) { - const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( cnt ); - for( int ii = 0; ii < poly.PointCount(); ++ii ) - { - int dist = KiROUND( poly.CPoint( ii ).EuclideanNorm() ); - radius = std::max( radius, dist ); - } + int dist = KiROUND( ( poly.CPoint( ii ) - m_Pos ).EuclideanNorm() ); + radius = std::max( radius, dist ); } - - radius += 1; - break; - - default: - radius = 0; } - return radius; + return radius + 1; } -int D_PAD::GetRoundRectCornerRadius( const wxSize& aSize ) const +int D_PAD::GetRoundRectCornerRadius() const { - // radius of rounded corners, usually 25% of shorter pad edge for now - int r = aSize.x > aSize.y ? aSize.y : aSize.x; - r = int( r * m_padRoundRectRadiusScale ); - - return r; + return KiROUND( std::min( m_Size.x, m_Size.y ) * m_padRoundRectRadiusScale ); } @@ -216,207 +173,194 @@ void D_PAD::SetRoundRectCornerRadius( double aRadius ) } -/** - * Function BuildSegmentFromOvalShape - * Has meaning only for OVAL (and ROUND) pads. - * Build an equivalent segment having the same shape as the OVAL shape, - * aSegStart and aSegEnd are the ending points of the equivalent segment of the shape - * aRotation is the asked rotation of the segment (usually m_Orient) - */ -int D_PAD::BuildSegmentFromOvalShape( wxPoint& aSegStart, wxPoint& aSegEnd, double aRotation, - const wxSize& aMargin ) const +void D_PAD::SetRoundRectRadiusRatio( double aRadiusScale ) { - int width; + m_padRoundRectRadiusScale = std::max( 0.0, std::min( aRadiusScale, 0.5 ) ); - if( m_Size.y < m_Size.x ) // Build an horizontal equiv segment + m_shapesDirty = true; +} + + +void D_PAD::SetChamferRectRatio( double aChamferScale ) +{ + m_padChamferRectScale = std::max( 0.0, std::min( aChamferScale, 0.5 ) ); + + m_shapesDirty = true; +} + + +const std::vector>& D_PAD::GetEffectiveShapes() const +{ + if( m_shapesDirty ) + buildEffectiveShapes(); + + return m_effectiveShapes; +} + + +const std::shared_ptr& D_PAD::GetEffectiveHoleShape() const +{ + if( m_shapesDirty ) + buildEffectiveShapes(); + + return m_effectiveHoleShape; +} + + +int D_PAD::GetBoundingRadius() const +{ + if( m_shapesDirty ) + buildEffectiveShapes(); + + return m_effectiveBoundingRadius; +} + + +void D_PAD::buildEffectiveShapes() const +{ + m_effectiveShapes.clear(); + m_effectiveHoleShape = nullptr; + + auto add = [this]( SHAPE* aShape ) + { + m_effectiveShapes.emplace_back( aShape ); + }; + + wxPoint shapePos = ShapePos(); // Fetch only once; rotation involves trig + PAD_SHAPE_T effectiveShape = GetShape(); + + if( GetShape() == PAD_SHAPE_CUSTOM ) + effectiveShape = GetAnchorPadShape(); + + switch( effectiveShape ) { - int delta = ( m_Size.x - m_Size.y ) / 2; - aSegStart.x = -delta - aMargin.x; - aSegStart.y = 0; - aSegEnd.x = delta + aMargin.x; - aSegEnd.y = 0; - width = m_Size.y + ( aMargin.y * 2 ); + case PAD_SHAPE_CIRCLE: + add( new SHAPE_CIRCLE( shapePos, m_Size.x / 2 ) ); + break; + + case PAD_SHAPE_OVAL: + { + wxSize half_size = m_Size / 2; + int half_width = std::min( half_size.x, half_size.y ); + wxPoint half_len( half_size.x - half_width, half_size.y - half_width ); + + RotatePoint( &half_len, m_Orient ); + + add( new SHAPE_SEGMENT( shapePos - half_len, shapePos + half_len, half_width * 2 ) ); } - else // Vertical oval: build a vertical equiv segment + break; + + case PAD_SHAPE_RECT: + case PAD_SHAPE_TRAPEZOID: + case PAD_SHAPE_ROUNDRECT: { - int delta = ( m_Size.y -m_Size.x ) / 2; - aSegStart.x = 0; - aSegStart.y = -delta - aMargin.y; - aSegEnd.x = 0; - aSegEnd.y = delta + aMargin.y; - width = m_Size.x + ( aMargin.x * 2 ); + int r = GetRoundRectCornerRadius(); + wxPoint half_size( m_Size.x / 2, m_Size.y / 2 ); + wxSize trap_delta( 0, 0 ); + + if( effectiveShape == PAD_SHAPE_ROUNDRECT ) + half_size -= wxPoint( r, r ); + else if( effectiveShape == PAD_SHAPE_TRAPEZOID ) + trap_delta = m_DeltaSize / 2; + + SHAPE_LINE_CHAIN corners; + + corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x ); + corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x ); + corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x ); + corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x ); + + corners.Rotate( -DECIDEG2RAD( m_Orient ) ); + corners.Move( shapePos ); + + add( new SHAPE_SIMPLE( corners ) ); + + if( effectiveShape == PAD_SHAPE_ROUNDRECT ) + { + add( new SHAPE_SEGMENT( corners.CPoint( 0 ), corners.CPoint( 1 ), r * 2 ) ); + add( new SHAPE_SEGMENT( corners.CPoint( 1 ), corners.CPoint( 2 ), r * 2 ) ); + add( new SHAPE_SEGMENT( corners.CPoint( 2 ), corners.CPoint( 3 ), r * 2 ) ); + add( new SHAPE_SEGMENT( corners.CPoint( 3 ), corners.CPoint( 0 ), r * 2 ) ); + } + } + break; + + case PAD_SHAPE_CHAMFERED_RECT: + { + SHAPE_POLY_SET outline; + auto board = GetBoard(); + int maxError = ARC_HIGH_DEF; + + if( board ) + maxError = board->GetDesignSettings().m_MaxError; + + TransformRoundChamferedRectToPolygon( outline, wxPoint(0,0), GetSize(), m_Orient, + GetRoundRectCornerRadius(), GetChamferRectRatio(), + GetChamferPositions(), maxError ); + + add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) ); + } + break; + + default: + wxFAIL_MSG( "D_PAD::buildEffectiveShapes: Unsupported pad shape" ); + break; } - if( aRotation ) + if( GetShape() == PAD_SHAPE_CUSTOM ) { - RotatePoint( &aSegStart, aRotation); - RotatePoint( &aSegEnd, aRotation); +#if 1 + // For now we add these in as a single SHAPE_POLY_SET, but once we've moved them + // over to shapes themselves then we can just copy them across (with the requisite + // rotating and offsetting). + SHAPE_POLY_SET* poly = new SHAPE_POLY_SET(); + MergePrimitivesAsPolygon( poly ); + poly->Rotate( -DECIDEG2RAD( m_Orient ) ); + poly->Move( shapePos ); + add( poly ); +#else + for( const std::shared_ptr& primitive : m_basicShapes ) + { + SHAPE* copy = primitive->Clone(); + copy->Rotate( -DECIDEG2RAD( m_Orient ) ); + copy->Move( shapePos ); + add( copy ); + } +#endif } - return width; + // Bounding radius + // + m_effectiveBoundingRadius = calcBoundingRadius(); + + // Hole shape + // + wxSize half_size = m_Drill / 2; + int half_width = std::min( half_size.x, half_size.y ); + wxPoint half_len( half_size.x - half_width, half_size.y - half_width ); + + RotatePoint( &half_len, m_Orient ); + + m_effectiveHoleShape = std::make_shared( m_Pos - half_len, m_Pos + half_len, + half_width * 2 ); + + // All done + // + m_shapesDirty = false; } const EDA_RECT D_PAD::GetBoundingBox() const { - EDA_RECT area; - wxPoint quadrant1, quadrant2, quadrant3, quadrant4; - int x, y, r, dx, dy; + EDA_RECT bbox; - wxPoint center = ShapePos(); - wxPoint endPoint; - - EDA_RECT endRect; - - switch( GetShape() ) + for( const std::shared_ptr& shape : GetEffectiveShapes() ) { - case PAD_SHAPE_CIRCLE: - area.SetOrigin( center ); - area.Inflate( m_Size.x / 2 ); - break; - - case PAD_SHAPE_OVAL: - /* To get the BoundingBox of an oval pad: - * a) If the pad is ROUND, see method for PAD_SHAPE_CIRCLE above - * OTHERWISE: - * b) Construct EDA_RECT for portion between circular ends - * c) Rotate that EDA_RECT - * d) Add the circular ends to the EDA_RECT - */ - - // Test if the shape is circular - if( m_Size.x == m_Size.y ) - { - area.SetOrigin( center ); - area.Inflate( m_Size.x / 2 ); - break; - } - - if( m_Size.x > m_Size.y ) - { - // Pad is horizontal - dx = ( m_Size.x - m_Size.y ) / 2; - dy = m_Size.y / 2; - - // Location of end-points - x = dx; - y = 0; - r = dy; - } - else - { - // Pad is vertical - dx = m_Size.x / 2; - dy = ( m_Size.y - m_Size.x ) / 2; - - x = 0; - y = dy; - r = dx; - } - - // Construct the center rectangle and rotate - area.SetOrigin( center ); - area.Inflate( dx, dy ); - area = area.GetBoundingBoxRotated( center, m_Orient ); - - endPoint = wxPoint( x, y ); - RotatePoint( &endPoint, m_Orient ); - - // Add points at each quadrant of circular regions - endRect.SetOrigin( center + endPoint ); - endRect.Inflate( r ); - - area.Merge( endRect ); - - endRect.SetSize( 0, 0 ); - endRect.SetOrigin( center - endPoint ); - endRect.Inflate( r ); - - area.Merge( endRect ); - - break; - - case PAD_SHAPE_RECT: - case PAD_SHAPE_ROUNDRECT: - case PAD_SHAPE_CHAMFERED_RECT: - // Use two opposite corners and track their rotation - // (use symmetry for other points) - quadrant1.x = m_Size.x/2; - quadrant1.y = m_Size.y/2; - quadrant2.x = -m_Size.x/2; - quadrant2.y = m_Size.y/2; - - RotatePoint( &quadrant1, m_Orient ); - RotatePoint( &quadrant2, m_Orient ); - dx = std::max( std::abs( quadrant1.x ) , std::abs( quadrant2.x ) ); - dy = std::max( std::abs( quadrant1.y ) , std::abs( quadrant2.y ) ); - - // Set the bbox - area.SetOrigin( ShapePos() ); - area.Inflate( dx, dy ); - break; - - case PAD_SHAPE_TRAPEZOID: - // Use the four corners and track their rotation - // (Trapezoids will not be symmetric) - - quadrant1.x = (m_Size.x + m_DeltaSize.y)/2; - quadrant1.y = (m_Size.y - m_DeltaSize.x)/2; - - quadrant2.x = -(m_Size.x + m_DeltaSize.y)/2; - quadrant2.y = (m_Size.y + m_DeltaSize.x)/2; - - quadrant3.x = -(m_Size.x - m_DeltaSize.y)/2; - quadrant3.y = -(m_Size.y + m_DeltaSize.x)/2; - - quadrant4.x = (m_Size.x - m_DeltaSize.y)/2; - quadrant4.y = -(m_Size.y - m_DeltaSize.x)/2; - - RotatePoint( &quadrant1, m_Orient ); - RotatePoint( &quadrant2, m_Orient ); - RotatePoint( &quadrant3, m_Orient ); - RotatePoint( &quadrant4, m_Orient ); - - x = std::min( quadrant1.x, std::min( quadrant2.x, std::min( quadrant3.x, quadrant4.x) ) ); - y = std::min( quadrant1.y, std::min( quadrant2.y, std::min( quadrant3.y, quadrant4.y) ) ); - dx = std::max( quadrant1.x, std::max( quadrant2.x, std::max( quadrant3.x, quadrant4.x) ) ); - dy = std::max( quadrant1.y, std::max( quadrant2.y, std::max( quadrant3.y, quadrant4.y) ) ); - - area.SetOrigin( ShapePos().x + x, ShapePos().y + y ); - area.SetSize( dx-x, dy-y ); - break; - - case PAD_SHAPE_CUSTOM: - { - SHAPE_POLY_SET polySet( m_customShapeAsPolygon ); - // Move shape to actual position - CustomShapeAsPolygonToBoardPosition( &polySet, GetPosition(), GetOrientation() ); - quadrant1 = m_Pos; - quadrant2 = m_Pos; - - for( int cnt = 0; cnt < polySet.OutlineCount(); ++cnt ) - { - const SHAPE_LINE_CHAIN& poly = polySet.COutline( cnt ); - - for( int ii = 0; ii < poly.PointCount(); ++ii ) - { - quadrant1.x = std::min( quadrant1.x, poly.CPoint( ii ).x ); - quadrant1.y = std::min( quadrant1.y, poly.CPoint( ii ).y ); - quadrant2.x = std::max( quadrant2.x, poly.CPoint( ii ).x ); - quadrant2.y = std::max( quadrant2.y, poly.CPoint( ii ).y ); - } - } - - area.SetOrigin( quadrant1 ); - area.SetEnd( quadrant2 ); - } - break; - - default: - break; + BOX2I r = shape->BBox(); + bbox.Merge( EDA_RECT( (wxPoint) r.GetOrigin(), wxSize( r.GetWidth(), r.GetHeight() ) ) ); } - return area; + return bbox; } @@ -457,12 +401,16 @@ void D_PAD::SetAttribute( PAD_ATTR_T aAttribute ) if( aAttribute == PAD_ATTRIB_SMD ) m_Drill = wxSize( 0, 0 ); + + m_shapesDirty = true; } void D_PAD::SetProperty( PAD_PROP_T aProperty ) { m_Property = aProperty; + + m_shapesDirty = true; } @@ -470,6 +418,8 @@ void D_PAD::SetOrientation( double aAngle ) { NORMALIZE_ANGLE_POS( aAngle ); m_Orient = aAngle; + + m_shapesDirty = true; } @@ -501,7 +451,7 @@ void D_PAD::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) // Flip the basic shapes, in custom pads FlipPrimitives(); - // m_boundingRadius = -1; the shape has not been changed + m_shapesDirty = true; } @@ -509,19 +459,17 @@ void D_PAD::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) void D_PAD::FlipPrimitives() { // Flip custom shapes - for( unsigned ii = 0; ii < m_basicShapes.size(); ++ii ) + for( PAD_CS_PRIMITIVE& primitive : m_basicShapes ) { - PAD_CS_PRIMITIVE& primitive = m_basicShapes[ii]; - MIRROR( primitive.m_Start.y, 0 ); MIRROR( primitive.m_End.y, 0 ); primitive.m_ArcAngle = -primitive.m_ArcAngle; switch( primitive.m_Shape ) { - case S_POLYGON: // polygon - for( unsigned jj = 0; jj < primitive.m_Poly.size(); jj++ ) - MIRROR( primitive.m_Poly[jj].y, 0 ); + case S_POLYGON: + for( wxPoint& pt : primitive.m_Poly ) + MIRROR( pt.y, 0 ); break; default: @@ -529,18 +477,15 @@ void D_PAD::FlipPrimitives() } } - // Flip local coordinates in merged Polygon - m_customShapeAsPolygon.Mirror( false, true ); + m_shapesDirty = true; } void D_PAD::MirrorXPrimitives( int aX ) { // Mirror custom shapes - for( unsigned ii = 0; ii < m_basicShapes.size(); ++ii ) + for( PAD_CS_PRIMITIVE& primitive : m_basicShapes ) { - PAD_CS_PRIMITIVE& primitive = m_basicShapes[ii]; - MIRROR( primitive.m_Start.x, aX ); MIRROR( primitive.m_End.x, aX ); primitive.m_ArcAngle = -primitive.m_ArcAngle; @@ -548,8 +493,8 @@ void D_PAD::MirrorXPrimitives( int aX ) switch( primitive.m_Shape ) { case S_POLYGON: // polygon - for( unsigned jj = 0; jj < primitive.m_Poly.size(); jj++ ) - MIRROR( primitive.m_Poly[jj].x, 0 ); + for( wxPoint& pt : primitive.m_Poly ) + MIRROR( pt.x, 0 ); break; default: @@ -557,12 +502,7 @@ void D_PAD::MirrorXPrimitives( int aX ) } } - // Mirror the local coordinates in merged Polygon - for( int cnt = 0; cnt < m_customShapeAsPolygon.OutlineCount(); ++cnt ) - { - SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.Outline( cnt ); - poly.Mirror( true, false ); - } + m_shapesDirty = true; } @@ -735,7 +675,7 @@ wxSize D_PAD::GetSolderPasteMargin() const } -ZONE_CONNECTION D_PAD::GetZoneConnection() const +ZONE_CONNECTION D_PAD::GetEffectiveZoneConnection() const { MODULE* module = GetParent(); @@ -768,150 +708,6 @@ int D_PAD::GetThermalGap() const } -void D_PAD::BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, - double aRotation ) const -{ - wxSize delta; - wxSize halfsize; - - halfsize.x = m_Size.x >> 1; - halfsize.y = m_Size.y >> 1; - - switch( GetShape() ) - { - case PAD_SHAPE_RECT: - // For rectangular shapes, inflate is easy - halfsize += aInflateValue; - - // Verify if do not deflate more than than size - // Only possible for inflate negative values. - if( halfsize.x < 0 ) - halfsize.x = 0; - - if( halfsize.y < 0 ) - halfsize.y = 0; - break; - - case PAD_SHAPE_TRAPEZOID: - // Trapezoidal pad: verify delta values - delta.x = ( m_DeltaSize.x >> 1 ); - delta.y = ( m_DeltaSize.y >> 1 ); - - // be sure delta values are not to large - if( (delta.x < 0) && (delta.x <= -halfsize.y) ) - delta.x = -halfsize.y + 1; - - if( (delta.x > 0) && (delta.x >= halfsize.y) ) - delta.x = halfsize.y - 1; - - if( (delta.y < 0) && (delta.y <= -halfsize.x) ) - delta.y = -halfsize.x + 1; - - if( (delta.y > 0) && (delta.y >= halfsize.x) ) - delta.y = halfsize.x - 1; - break; - - default: // is used only for rect and trap. pads - return; - } - - // Build the basic rectangular or trapezoid shape - // delta is null for rectangular shapes - aCoord[0].x = -halfsize.x - delta.y; // lower left - aCoord[0].y = +halfsize.y + delta.x; - - aCoord[1].x = -halfsize.x + delta.y; // upper left - aCoord[1].y = -halfsize.y - delta.x; - - aCoord[2].x = +halfsize.x - delta.y; // upper right - aCoord[2].y = -halfsize.y + delta.x; - - aCoord[3].x = +halfsize.x + delta.y; // lower right - aCoord[3].y = +halfsize.y - delta.x; - - // Offsetting the trapezoid shape id needed - // It is assumed delta.x or/and delta.y == 0 - if( GetShape() == PAD_SHAPE_TRAPEZOID && (aInflateValue.x != 0 || aInflateValue.y != 0) ) - { - double angle; - wxSize corr; - - if( delta.y ) // lower and upper segment is horizontal - { - // Calculate angle of left (or right) segment with vertical axis - angle = atan2( (double) m_DeltaSize.y, (double) m_Size.y ); - - // left and right sides are moved by aInflateValue.x in their perpendicular direction - // We must calculate the corresponding displacement on the horizontal axis - // that is delta.x +- corr.x depending on the corner - corr.x = KiROUND( tan( angle ) * aInflateValue.x ); - delta.x = KiROUND( aInflateValue.x / cos( angle ) ); - - // Horizontal sides are moved up and down by aInflateValue.y - delta.y = aInflateValue.y; - - // corr.y = 0 by the constructor - } - else if( delta.x ) // left and right segment is vertical - { - // Calculate angle of lower (or upper) segment with horizontal axis - angle = atan2( (double) m_DeltaSize.x, (double) m_Size.x ); - - // lower and upper sides are moved by aInflateValue.x in their perpendicular direction - // We must calculate the corresponding displacement on the vertical axis - // that is delta.y +- corr.y depending on the corner - corr.y = KiROUND( tan( angle ) * aInflateValue.y ); - delta.y = KiROUND( aInflateValue.y / cos( angle ) ); - - // Vertical sides are moved left and right by aInflateValue.x - delta.x = aInflateValue.x; - - // corr.x = 0 by the constructor - } - else // the trapezoid is a rectangle - { - delta = aInflateValue; // this pad is rectangular (delta null). - } - - aCoord[0].x += -delta.x - corr.x; // lower left - aCoord[0].y += delta.y + corr.y; - - aCoord[1].x += -delta.x + corr.x; // upper left - aCoord[1].y += -delta.y - corr.y; - - aCoord[2].x += delta.x - corr.x; // upper right - aCoord[2].y += -delta.y + corr.y; - - aCoord[3].x += delta.x + corr.x; // lower right - aCoord[3].y += delta.y - corr.y; - - /* test coordinates and clamp them if the offset correction is too large: - * Note: if a coordinate is bad, the other "symmetric" coordinate is bad - * So when a bad coordinate is found, the 2 symmetric coordinates - * are set to the minimun value (0) - */ - - if( aCoord[0].x > 0 ) // lower left x coordinate must be <= 0 - aCoord[0].x = aCoord[3].x = 0; - - if( aCoord[1].x > 0 ) // upper left x coordinate must be <= 0 - aCoord[1].x = aCoord[2].x = 0; - - if( aCoord[0].y < 0 ) // lower left y coordinate must be >= 0 - aCoord[0].y = aCoord[1].y = 0; - - if( aCoord[3].y < 0 ) // lower right y coordinate must be >= 0 - aCoord[3].y = aCoord[2].y = 0; - } - - if( aRotation ) - { - for( int ii = 0; ii < 4; ii++ ) - RotatePoint( &aCoord[ii], aRotation ); - } -} - - void D_PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) { EDA_UNITS units = aFrame->GetUserUnits(); @@ -1039,103 +835,16 @@ void D_PAD::GetOblongGeometry( const wxSize& aDrillOrPadSize, bool D_PAD::HitTest( const wxPoint& aPosition, int aAccuracy ) const { - int dx, dy; + VECTOR2I delta = aPosition - GetPosition(); + int boundingRadius = GetBoundingRadius() + aAccuracy; - wxPoint shape_pos = ShapePos(); - - wxPoint delta = aPosition - shape_pos; - - // first test: a test point must be inside a minimum sized bounding circle. - int radius = GetBoundingRadius(); - - if( ( abs( delta.x ) > radius ) || ( abs( delta.y ) > radius ) ) + if( delta.SquaredEuclideanNorm() > SEG::Square( boundingRadius ) ) return false; - dx = m_Size.x >> 1; // dx also is the radius for rounded pads - dy = m_Size.y >> 1; + SHAPE_POLY_SET polySet; + TransformShapeWithClearanceToPolygon( polySet, aAccuracy ); - switch( GetShape() ) - { - case PAD_SHAPE_CIRCLE: - if( KiROUND( EuclideanNorm( delta ) ) <= dx ) - return true; - - break; - - case PAD_SHAPE_TRAPEZOID: - { - wxPoint poly[4]; - BuildPadPolygon( poly, wxSize(0,0), 0 ); - RotatePoint( &delta, -m_Orient ); - - return TestPointInsidePolygon( poly, 4, delta ); - } - - case PAD_SHAPE_OVAL: - { - RotatePoint( &delta, -m_Orient ); - // An oval pad has the same shape as a segment with rounded ends - // After rotation, the test point is relative to an horizontal pad - int dist; - wxPoint offset; - if( dy > dx ) // shape is a vertical oval - { - offset.y = dy - dx; - dist = dx; - } - else //if( dy <= dx ) shape is an horizontal oval - { - offset.x = dy - dx; - dist = dy; - } - return TestSegmentHit( delta, - offset, offset, dist ); - } - break; - - case PAD_SHAPE_RECT: - RotatePoint( &delta, -m_Orient ); - - if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) ) - return true; - - break; - - case PAD_SHAPE_CHAMFERED_RECT: - case PAD_SHAPE_ROUNDRECT: - { - // Check for hit in polygon - SHAPE_POLY_SET outline; - bool doChamfer = GetShape() == PAD_SHAPE_CHAMFERED_RECT; - auto board = GetBoard(); - int maxError = ARC_HIGH_DEF; - - if( board ) - maxError = board->GetDesignSettings().m_MaxError; - - TransformRoundChamferedRectToPolygon( outline, wxPoint(0,0), GetSize(), m_Orient, - GetRoundRectCornerRadius(), - doChamfer ? GetChamferRectRatio() : 0.0, - doChamfer ? GetChamferPositions() : 0, - maxError ); - - const SHAPE_LINE_CHAIN &poly = outline.COutline( 0 ); - return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta ); - } - break; - - case PAD_SHAPE_CUSTOM: - // Check for hit in polygon - RotatePoint( &delta, -m_Orient ); - - if( m_customShapeAsPolygon.OutlineCount() ) - { - const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( 0 ); - return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta ); - } - break; - } - - return false; + return polySet.Contains( aPosition ); } @@ -1145,182 +854,37 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con arect.Normalize(); arect.Inflate( aAccuracy ); - wxPoint shapePos = ShapePos(); + EDA_RECT bbox = GetBoundingBox(); - EDA_RECT shapeRect; - - int r; - - EDA_RECT bb = GetBoundingBox(); - - wxPoint endCenter; - int radius; - - if( !arect.Intersects( bb ) ) + if( !arect.Intersects( bbox ) ) return false; // This covers total containment for all test cases - if( arect.Contains( bb ) ) + if( arect.Contains( bbox ) ) return true; - switch( GetShape() ) - { - case PAD_SHAPE_CIRCLE: - return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() ); + SHAPE_POLY_SET selRect; + selRect.NewOutline(); + selRect.Append( arect.GetOrigin() ); + selRect.Append( VECTOR2I( arect.GetRight(), arect.GetTop() ) ); + selRect.Append( VECTOR2I( arect.GetRight(), arect.GetBottom() ) ); + selRect.Append( VECTOR2I( arect.GetLeft(), arect.GetBottom() ) ); - case PAD_SHAPE_RECT: - case PAD_SHAPE_CHAMFERED_RECT: // TODO use a finer shape analysis - shapeRect.SetOrigin( shapePos ); - shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 ); - return arect.Intersects( shapeRect, m_Orient ); + SHAPE_POLY_SET padPoly; + TransformShapeWithClearanceToPolygon( padPoly, aAccuracy ); - case PAD_SHAPE_OVAL: - // Circlular test if dimensions are equal - if( m_Size.x == m_Size.y ) - return arect.IntersectsCircle( shapePos, GetBoundingRadius() ); + selRect.BooleanIntersection( padPoly, SHAPE_POLY_SET::PM_FAST ); - shapeRect.SetOrigin( shapePos ); + double padArea = padPoly.Outline( 0 ).Area(); + double intersection = selRect.Outline( 0 ).Area(); - // Horizontal dimension is greater - if( m_Size.x > m_Size.y ) - { - radius = m_Size.y / 2; - - shapeRect.Inflate( m_Size.x / 2 - radius, radius ); - - endCenter = wxPoint( m_Size.x / 2 - radius, 0 ); - RotatePoint( &endCenter, m_Orient ); - - // Test circular ends - if( arect.IntersectsCircle( shapePos + endCenter, radius ) || - arect.IntersectsCircle( shapePos - endCenter, radius ) ) - { - return true; - } - } - else - { - radius = m_Size.x / 2; - - shapeRect.Inflate( radius, m_Size.y / 2 - radius ); - - endCenter = wxPoint( 0, m_Size.y / 2 - radius ); - RotatePoint( &endCenter, m_Orient ); - - // Test circular ends - if( arect.IntersectsCircle( shapePos + endCenter, radius ) || - arect.IntersectsCircle( shapePos - endCenter, radius ) ) - { - return true; - } - } - - // Test rectangular portion between rounded ends - if( arect.Intersects( shapeRect, m_Orient ) ) - return true; - - break; - - case PAD_SHAPE_TRAPEZOID: - /* Trapezoid intersection tests: - * A) Any points of rect inside trapezoid - * B) Any points of trapezoid inside rect - * C) Any sides of trapezoid cross rect - */ - { - - wxPoint poly[4]; - BuildPadPolygon( poly, wxSize( 0, 0 ), 0 ); - - wxPoint corners[4]; - - corners[0] = wxPoint( arect.GetLeft(), arect.GetTop() ); - corners[1] = wxPoint( arect.GetRight(), arect.GetTop() ); - corners[2] = wxPoint( arect.GetRight(), arect.GetBottom() ); - corners[3] = wxPoint( arect.GetLeft(), arect.GetBottom() ); - - for( int i=0; i<4; i++ ) - { - RotatePoint( &poly[i], m_Orient ); - poly[i] += shapePos; - } - - for( int ii=0; ii<4; ii++ ) - { - if( TestPointInsidePolygon( poly, 4, corners[ii] ) ) - return true; - - if( arect.Contains( poly[ii] ) ) - return true; - - if( arect.Intersects( poly[ii], poly[(ii+1) % 4] ) ) - return true; - } - - return false; - } - - case PAD_SHAPE_ROUNDRECT: - /* RoundRect intersection can be broken up into simple tests: - * a) Test intersection of horizontal rect - * b) Test intersection of vertical rect - * c) Test intersection of each corner - */ - r = GetRoundRectCornerRadius(); - - /* Test A - intersection of horizontal rect */ - shapeRect.SetSize( 0, 0 ); - shapeRect.SetOrigin( shapePos ); - shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 - r ); - - // Short-circuit test for zero width or height - if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 && - arect.Intersects( shapeRect, m_Orient ) ) - { - return true; - } - - /* Test B - intersection of vertical rect */ - shapeRect.SetSize( 0, 0 ); - shapeRect.SetOrigin( shapePos ); - shapeRect.Inflate( m_Size.x / 2 - r, m_Size.y / 2 ); - - // Short-circuit test for zero width or height - if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 && - arect.Intersects( shapeRect, m_Orient ) ) - { - return true; - } - - /* Test C - intersection of each corner */ - - endCenter = wxPoint( m_Size.x / 2 - r, m_Size.y / 2 - r ); - RotatePoint( &endCenter, m_Orient ); - - if( arect.IntersectsCircle( shapePos + endCenter, r ) || - arect.IntersectsCircle( shapePos - endCenter, r ) ) - { - return true; - } - - endCenter = wxPoint( m_Size.x / 2 - r, -m_Size.y / 2 + r ); - RotatePoint( &endCenter, m_Orient ); - - if( arect.IntersectsCircle( shapePos + endCenter, r ) || - arect.IntersectsCircle( shapePos - endCenter, r ) ) - { - return true; - } - - break; - - default: - break; - } - - return false; + if( intersection > ( padArea * 0.99 ) ) + return true; + else + return !aContained && intersection > 0; } + int D_PAD::Compare( const D_PAD* padref, const D_PAD* padcmp ) { int diff; @@ -1384,6 +948,8 @@ void D_PAD::Rotate( const wxPoint& aRotCentre, double aAngle ) m_Orient = NormalizeAngle360Min( m_Orient + aAngle ); SetLocalCoord(); + + m_shapesDirty = true; } @@ -1653,16 +1219,18 @@ void D_PAD::ImportSettingsFrom( const D_PAD& aMasterPad ) SetLocalSolderPasteMargin( aMasterPad.GetLocalSolderPasteMargin() ); SetLocalSolderPasteMarginRatio( aMasterPad.GetLocalSolderPasteMarginRatio() ); - SetZoneConnection( aMasterPad.GetZoneConnection() ); + SetZoneConnection( aMasterPad.GetEffectiveZoneConnection() ); SetThermalWidth( aMasterPad.GetThermalWidth() ); SetThermalGap( aMasterPad.GetThermalGap() ); // Add or remove custom pad shapes: SetPrimitives( aMasterPad.GetPrimitives() ); SetAnchorPadShape( aMasterPad.GetAnchorPadShape() ); - MergePrimitivesAsPolygon(); + + m_shapesDirty = true; } + void D_PAD::SwapData( BOARD_ITEM* aImage ) { assert( aImage->Type() == PCB_PAD_T ); diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 8d2babe825..be4bbbad37 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -40,6 +40,8 @@ class DRAWSEGMENT; class PARAM_CFG; +class SHAPE; +class SHAPE_SEGMENT; enum CUST_PAD_SHAPE_IN_ZONE { @@ -52,7 +54,6 @@ class EDA_3D_CANVAS; class MODULE; class EDGE_MODULE; class TRACK; -class MSG_PANEL_INFO; namespace KIGFX { @@ -115,10 +116,6 @@ public: class D_PAD : public BOARD_CONNECTED_ITEM { -public: - static int m_PadSketchModePenSize; ///< Pen size used to draw pads in sketch mode - ///< (mode used to print pads on silkscreen layer) - public: D_PAD( MODULE* parent ); @@ -181,34 +178,14 @@ public: * Set the pad name (sometimes called pad number, although * it can be an array reference like AA12). */ - void SetName( const wxString& aName ) - { - m_name = aName; - } + void SetName( const wxString& aName ) { m_name = aName; } + const wxString& GetName() const { return m_name; } /** * Set the pad function (pin name in schematic) */ - void SetPinFunction( const wxString& aName ) - { - m_pinFunction = aName; - } - - /** - * @return the pad name - */ - const wxString& GetName() const - { - return m_name; - } - - /** - * @return the pad function (pin name in schematic) - */ - const wxString& GetPinFunction() const - { - return m_pinFunction; - } + void SetPinFunction( const wxString& aName ) { m_pinFunction = aName; } + const wxString& GetPinFunction() const { return m_pinFunction; } bool PadNameEqual( const D_PAD* other ) const { @@ -219,8 +196,8 @@ public: * Function GetShape * @return the shape of this pad. */ - PAD_SHAPE_T GetShape() const { return m_padShape; } - void SetShape( PAD_SHAPE_T aShape ) { m_padShape = aShape; m_boundingRadius = -1; } + void SetShape( PAD_SHAPE_T aShape ) { m_padShape = aShape; m_shapesDirty = true; } + PAD_SHAPE_T GetShape() const { return m_padShape; } void SetPosition( const wxPoint& aPos ) override { m_Pos = aPos; } const wxPoint GetPosition() const override { return m_Pos; } @@ -259,7 +236,7 @@ public: void SetAnchorPadShape( PAD_SHAPE_T aShape ) { m_anchorPadShape = ( aShape == PAD_SHAPE_RECT ) ? PAD_SHAPE_RECT : PAD_SHAPE_CIRCLE; - m_boundingRadius = -1; + m_shapesDirty = true; } /** @@ -272,8 +249,8 @@ public: return ( GetLayerSet() & LSET::AllCuMask() ) != 0; } - void SetY( int y ) { m_Pos.y = y; } - void SetX( int x ) { m_Pos.x = x; } + void SetY( int y ) { m_Pos.y = y; m_shapesDirty = true; } + void SetX( int x ) { m_Pos.x = x; m_shapesDirty = true; } void SetPos0( const wxPoint& aPos ) { m_Pos0 = aPos; } const wxPoint& GetPos0() const { return m_Pos0; } @@ -281,16 +258,16 @@ public: void SetY0( int y ) { m_Pos0.y = y; } void SetX0( int x ) { m_Pos0.x = x; } - void SetSize( const wxSize& aSize ) { m_Size = aSize; m_boundingRadius = -1; } + void SetSize( const wxSize& aSize ) { m_Size = aSize; m_shapesDirty = true; } const wxSize& GetSize() const { return m_Size; } - void SetDelta( const wxSize& aSize ) { m_DeltaSize = aSize; m_boundingRadius = -1; } + void SetDelta( const wxSize& aSize ) { m_DeltaSize = aSize; m_shapesDirty = true; } const wxSize& GetDelta() const { return m_DeltaSize; } - void SetDrillSize( const wxSize& aSize ) { m_Drill = aSize; } + void SetDrillSize( const wxSize& aSize ) { m_Drill = aSize; m_shapesDirty = true; } const wxSize& GetDrillSize() const { return m_Drill; } - void SetOffset( const wxPoint& aOffset ) { m_Offset = aOffset; } + void SetOffset( const wxPoint& aOffset ) { m_Offset = aOffset; m_shapesDirty = true; } const wxPoint& GetOffset() const { return m_Offset; } /** @@ -303,67 +280,35 @@ public: * a arc * a curve */ - void AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, - bool aMergePrimitives = true ); - void AddPrimitivePoly( const std::vector& aPoly, int aThickness, - bool aMergePrimitives = true ); - void AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness, - bool aMergePrimitives = true ); - void AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness, - bool aMergePrimitives = true ); ///< ring or circle basic shape - void AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness, - bool aMergePrimitives = true ); + void AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness ); + void AddPrimitivePoly( const std::vector& aPoly, int aThickness ); + void AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness ); + void AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness ); + void AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness ); void AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle, - int aThickness, bool aMergePrimitives = true ); + int aThickness ); void AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1, - const wxPoint& aCtrl2, int aThickness, bool aMergePrimitives = true ); + const wxPoint& aCtrl2, int aThickness ); bool GetBestAnchorPosition( VECTOR2I& aPos ); /** - * Merge all basic shapes, converted to a polygon in one polygon, - * in m_customShapeAsPolygon - * @return true if OK, false in there is more than one polygon - * in m_customShapeAsPolygon - * @param aMergedPolygon = the SHAPE_POLY_SET to fill. - * if NULL, m_customShapeAsPolygon is the target - * @param aCircleToSegmentsCount = number of segment to approximate a circle - * (default = 32) + * Merge all basic shapes to a SHAPE_POLY_SET * Note: The corners coordinates are relative to the pad position, orientation 0, */ - bool MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon = NULL ); + void MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon ) const; /** * clear the basic shapes list */ void DeletePrimitivesList(); - /** - * When created, the corners coordinates are relative to the pad position, orientation 0, - * in m_customShapeAsPolygon - * CustomShapeAsPolygonToBoardPosition transform these coordinates to actual - * (board) coordinates - * @param aMergedPolygon = the corners coordinates, relative to aPosition and - * rotated by aRotation - * @param aPosition = the position of the shape (usually the pad shape, but - * not always, when moving the pad) - * @param aRotation = the rotation of the shape (usually the pad rotation, but - * not always, in DRC) - */ - void CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon, - wxPoint aPosition, double aRotation ) const; - /** * Accessor to the basic shape list */ const std::vector& GetPrimitives() const { return m_basicShapes; } - /** - * Accessor to the custom shape as one polygon - */ - const SHAPE_POLY_SET& GetCustomShapeAsPolygon() const { return m_customShapeAsPolygon; } - void Flip( const wxPoint& aCentre, bool aFlipLeftRight ) override; /** @@ -373,24 +318,18 @@ public: /** * Mirror the primitives about a coordinate - * - * @param aX the x coordinate about which to mirror */ void MirrorXPrimitives( int aX ); /** * Import to the basic shape list - * @return true if OK, false if issues - * (more than one polygon to build the polygon shape list) */ - bool SetPrimitives( const std::vector& aPrimitivesList ); + void SetPrimitives( const std::vector& aPrimitivesList ); /** * Add to the basic shape list - * @return true if OK, false if issues - * (more than one polygon to build the polygon shape list) */ - bool AddPrimitives( const std::vector& aPrimitivesList ); + void AddPrimitives( const std::vector& aPrimitivesList ); /** @@ -414,21 +353,11 @@ public: double GetOrientationDegrees() const { return m_Orient/10.0; } double GetOrientationRadians() const { return m_Orient*M_PI/1800; } - void SetDrillShape( PAD_DRILL_SHAPE_T aDrillShape ) - { m_drillShape = aDrillShape; } + void SetDrillShape( PAD_DRILL_SHAPE_T aShape ) { m_drillShape = aShape; m_shapesDirty = true; } PAD_DRILL_SHAPE_T GetDrillShape() const { return m_drillShape; } /** - * Function GetOblongGeometry calculates the start point, end point and width of an - * equivalent segment which have the same position and width as the pad (for circular - * of oval pads) or hole - * - * NB: points returned are RELATIVE to the PAD POSITION. For board coordinates holes - * will need to be offset by GetPosition() and pads by ShapePos(). - * - * @param aStartPoint = first point of the equivalent segment, relative to the pad position. - * @param aEndPoint = second point of the equivalent segment, relative to the pad position. - * @param aWidth = width equivalent segment. + * JEY TODO: temporary until Tom is done with DRC stuff.... */ void GetOblongGeometry( const wxSize& aDrillOrPadSize, wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const; @@ -461,20 +390,47 @@ public: double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; } void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; } - /** * Function TransformShapeWithClearanceToPolygon - * Convert the pad shape to a closed polygon - * Used in filling zones calculations - * Circles and arcs are approximated by segments + * Convert the pad shape to a closed polygon. Circles and arcs are approximated by segments. * @param aCornerBuffer = a buffer to store the polygon * @param aClearanceValue = the clearance around the pad - * @param aMaxError = Maximum error from true when converting arcs - * @param ignoreLineWidth = used for edge cut items where the line width is only - * for visualization + * @param aMaxError = maximum error from true when converting arcs + * @param ignoreLineWidth = used for edge cuts where the line width is only for visualization */ void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, - int aMaxError = ARC_HIGH_DEF, bool ignoreLineWidth = false ) const override; + int aMaxError = ARC_HIGH_DEF, + bool ignoreLineWidth = false ) const override; + + /** + * Function TransformHoleWithClearanceToPolygon + * Build the Corner list of the polygonal drill shape in the board coordinate system. + * @param aCornerBuffer = a buffer to fill. + * @param aInflateValue = the clearance or margin value. + * @param aError = maximum deviation of an arc from the polygon approximation + * @return false if the pad has no hole, true otherwise + */ + bool TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, + int aError = ARC_HIGH_DEF ) const; + + /** + * Function GetEffectiveShapes + * Returns a list of SHAPE objects representing the pad's copper. + */ + const std::vector>& GetEffectiveShapes() const; + + /** + * Function GetEffectiveHoleShape + * Returns a list of SHAPE objects representing the pad's hole. + */ + const std::shared_ptr& GetEffectiveHoleShape() const; + + /** + * Function GetBoundingRadius + * returns the radius of a minimum sized circle which fully encloses this pad. + * The center is the pad position NOT THE SHAPE POS! + */ + int GetBoundingRadius() const; /** * Function GetLocalClearanceOverrides @@ -515,17 +471,14 @@ public: */ wxSize GetSolderPasteMargin() const; - void SetZoneConnection( ZONE_CONNECTION aType ) - { - m_ZoneConnection = aType; - } + void SetZoneConnection( ZONE_CONNECTION aType ) { m_ZoneConnection = aType; } + ZONE_CONNECTION GetZoneConnection() const { return m_ZoneConnection; } - ZONE_CONNECTION GetZoneConnection() const; - - ZONE_CONNECTION GetLocalZoneConnection() const - { - return m_ZoneConnection; - } + /** + * Return the zone connection in effect (either locally overridden or overridden in the + * parent module). + */ + ZONE_CONNECTION GetEffectiveZoneConnection() const; void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; } int GetThermalWidth() const; @@ -534,110 +487,12 @@ public: int GetThermalGap() const; /** - * Function BuildPadPolygon - * Has meaning only for polygonal pads (trapezoid and rectangular) - * Build the Corner list of the polygonal shape, - * depending on shape, extra size (clearance ...) and orientation - * @param aCoord = a buffer to fill (4 corners). - * @param aInflateValue = wxSize: the clearance or margin value. value > 0: - * inflate, < 0 deflate - * @param aRotation = full rotation of the polygon - */ - void BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, double aRotation ) const; - - /** - * Function GetRoundRectCornerRadius + * Function SetRoundRectCornerRadius * Has meaning only for rounded rect pads * @return The radius of the rounded corners for this pad. */ - int GetRoundRectCornerRadius() const - { - return GetRoundRectCornerRadius( m_Size ); - } - - /** - * Helper function GetRoundRectCornerRadius - * Has meaning only for rounded rect pads - * Returns the radius of the rounded corners of a rectangle - * size aSize, using others setting of the pad - * @param aSize = size of the of the round rect. Usually the pad size - * but can be the size of the pad on solder mask or solder paste - * @return The radius of the rounded corners for this pad size. - */ - int GetRoundRectCornerRadius( const wxSize& aSize ) const; - - /** - * Set the rounded rectangle radius ratio based on a given radius - * @param aRadius = desired radius of curvature - */ void SetRoundRectCornerRadius( double aRadius ); - - /** - * Function BuildPadShapePolygon - * Build the Corner list of the polygonal shape, - * depending on shape, extra size (clearance ...) pad and orientation - * This function is similar to TransformShapeWithClearanceToPolygon, - * but the difference is BuildPadShapePolygon creates a polygon shape exactly - * similar to pad shape, which a size inflated by aInflateValue - * and TransformShapeWithClearanceToPolygon creates a more complex shape (for instance - * a rectangular pad is converted in a rectangulr shape with ronded corners) - * @param aCornerBuffer = a buffer to fill. - * @param aInflateValue = the clearance or margin value. - * value > 0: inflate, < 0 deflate, = 0 : no change - * the clearance can have different values for x and y directions - * (relative to the pad) - * @param aError = Maximum deviation of an arc from the polygon segment - */ - void BuildPadShapePolygon( - SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue, int aError = ARC_HIGH_DEF ) const; - - /** - * Function BuildPadDrillShapePolygon - * Build the Corner list of the polygonal drill shape, - * depending on shape pad hole and orientation - * @param aCornerBuffer = a buffer to fill. - * @param aInflateValue = the clearance or margin value. - * value > 0: inflate, < 0 deflate, = 0 : no change - * @param aError = Maximum deviation of an arc from the polygon approximation - * @return false if the pad has no hole, true otherwise - */ - bool BuildPadDrillShapePolygon( - SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, int aError = ARC_HIGH_DEF ) const; - - /** - * Function BuildSegmentFromOvalShape - * Has meaning only for OVAL (and ROUND) pads - * Build an equivalent segment having the same shape as the OVAL shape, - * Useful in draw function and in DRC and HitTest functions, - * because segments are already well handled by track tests - * @param aSegStart = the starting point of the equivalent segment relative to the shape - * position. - * @param aSegEnd = the ending point of the equivalent segment, relative to the shape position - * @param aRotation = full rotation of the segment - * @param aRotation = full rotation of the segment - * @param aMargin = a margin around the shape (for instance mask margin) - * @return the width of the segment - */ - int BuildSegmentFromOvalShape( wxPoint& aSegStart, wxPoint& aSegEnd, - double aRotation, const wxSize& aMargin ) const; - - /** - * Function GetBoundingRadius - * returns the radius of a minimum sized circle which fully encloses this pad. - * The center is the pad position - */ - int GetBoundingRadius() const - { - // Any member function which would affect this calculation should set - // m_boundingRadius to -1 to re-trigger the calculation from here. - // Currently that is only m_Size, m_DeltaSize, and m_padShape accessors. - if( m_boundingRadius == -1 ) - { - m_boundingRadius = boundingRadius(); - } - - return m_boundingRadius; - } + int GetRoundRectCornerRadius() const; wxPoint ShapePos() const; @@ -648,36 +503,8 @@ public: * Cannot be > 0.5 * the normalized IPC-7351C value is 0.25 */ - double GetRoundRectRadiusRatio() const - { - return m_padRoundRectRadiusScale; - } - - /** - * has meaning only for rounded rect pads - * Set the scaling factor between the smaller Y or Y size and the radius - * of the rounded corners. - * Cannot be < 0.5 and obviously must be > 0 - * the normalized IPC-7351C value is 0.25 - */ - void SetRoundRectRadiusRatio( double aRadiusScale ) - { - if( aRadiusScale < 0.0 ) - aRadiusScale = 0.0; - - m_padRoundRectRadiusScale = std::min( aRadiusScale, 0.5 ); - } - - /** - * has meaning only for chamfered rect pads - * @return the ratio between the smaller Y or Y size and the radius - * of the rounded corners. - * Cannot be > 0.5 - */ - double GetChamferRectRatio() const - { - return m_padChamferRectScale; - } + void SetRoundRectRadiusRatio( double aRadiusScale ); + double GetRoundRectRadiusRatio() const { return m_padRoundRectRadiusScale; } /** * has meaning only for chamfered rect pads @@ -685,19 +512,8 @@ public: * of the rounded corners. * Cannot be < 0.5 and obviously must be > 0 */ - void SetChamferRectRatio( double aChamferScale ) - { - if( aChamferScale < 0.0 ) - aChamferScale = 0.0; - - m_padChamferRectScale = std::min( aChamferScale, 0.5 ); - } - - /** - * has meaning only for chamfered rect pads - * @return the position of the chamfer for a 0 orientation - */ - int GetChamferPositions() const { return m_chamferPositions; } + void SetChamferRectRatio( double aChamferScale ); + double GetChamferRectRatio() const { return m_padChamferRectScale; } /** * has meaning only for chamfered rect pads @@ -705,10 +521,8 @@ public: * RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT, * RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT */ - void SetChamferPositions( int aChamferPositions ) - { - m_chamferPositions = aChamferPositions; - } + void SetChamferPositions( int aPositions ) { m_chamferPositions = aPositions; } + int GetChamferPositions() const { return m_chamferPositions; } /** * Function GetSubRatsnest @@ -814,40 +628,36 @@ public: private: /** - * Function boundingRadius + * Function calcBoundingRadius * returns a calculated radius of a bounding circle for this pad. */ - int boundingRadius() const; + int calcBoundingRadius() const; - bool buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError ); + void addCustomPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError ) const; -private: // Private variable members: - - // Actually computed and cached on demand by the accessor - mutable int m_boundingRadius; ///< radius of the circle containing the pad shape + void buildEffectiveShapes() const; +private: wxString m_name; ///< pad name (pin number in schematic) - wxString m_pinFunction; ///< pin function in schematic - // TODO: Remove m_Pos from Pad or make private. View positions calculated from m_Pos0 wxPoint m_Pos; ///< pad Position on board PAD_SHAPE_T m_padShape; ///< Shape: PAD_SHAPE_CIRCLE, PAD_SHAPE_RECT, ///< PAD_SHAPE_OVAL, PAD_SHAPE_TRAPEZOID, ///< PAD_SHAPE_ROUNDRECT, PAD_SHAPE_POLYGON + mutable bool m_shapesDirty; + mutable int m_effectiveBoundingRadius; + mutable std::vector> m_effectiveShapes; + mutable std::shared_ptr m_effectiveHoleShape; + /** for free shape pads: a list of basic shapes, * in local coordinates, orient 0, coordinates relative to m_Pos * They are expected to define only one copper area. */ std::vector m_basicShapes; - /** for free shape pads: the set of basic shapes, merged as one polygon, - * in local coordinates, orient 0, coordinates relative to m_Pos - */ - SHAPE_POLY_SET m_customShapeAsPolygon; - /** * How to build the custom shape in zone, to create the clearance area: * CUST_PAD_SHAPE_IN_ZONE_OUTLINE = use pad shape @@ -871,10 +681,10 @@ private: // Private variable members: ///< to corner radius, default 0.25 double m_padChamferRectScale; ///< scaling factor from smallest m_Size coord ///< to chamfer value, default 0.25 - int m_chamferPositions; ///< the positions of the chamfered position for a 0 orientation + int m_chamferPositions; ///< the positions of the chamfers for a 0 orientation - PAD_SHAPE_T m_anchorPadShape; ///< for custom shaped pads: shape of pad anchor, - ///< PAD_SHAPE_RECT, PAD_SHAPE_CIRCLE + PAD_SHAPE_T m_anchorPadShape; ///< for custom shaped pads: shape of pad anchor, + ///< PAD_SHAPE_RECT, PAD_SHAPE_CIRCLE /** * m_Offset is useful only for oblong and rect pads (it can be used for other diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index a1221775f1..117d957fe0 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -784,10 +784,10 @@ void ZONE_CONTAINER::Mirror( const wxPoint& aMirrorRef, bool aMirrorLeftRight ) ZONE_CONNECTION ZONE_CONTAINER::GetPadConnection( D_PAD* aPad ) const { - if( aPad == NULL || aPad->GetZoneConnection() == ZONE_CONNECTION::INHERITED ) + if( aPad == NULL || aPad->GetEffectiveZoneConnection() == ZONE_CONNECTION::INHERITED ) return m_PadConnection; else - return aPad->GetZoneConnection(); + return aPad->GetEffectiveZoneConnection(); } diff --git a/pcbnew/connectivity/connectivity_items.cpp b/pcbnew/connectivity/connectivity_items.cpp index b358acf252..fc8b6dee9c 100644 --- a/pcbnew/connectivity/connectivity_items.cpp +++ b/pcbnew/connectivity/connectivity_items.cpp @@ -105,7 +105,7 @@ const VECTOR2I CN_ITEM::GetAnchor( int n ) const RotatePoint( pt1, pad->ShapePos(), pad->GetOrientation() ); SHAPE_POLY_SET padPolySet; - pad->BuildPadShapePolygon( padPolySet, wxSize( 0, 0 ), ARC_LOW_DEF ); + pad->TransformShapeWithClearanceToPolygon( padPolySet, 0, ARC_LOW_DEF ); const SHAPE_LINE_CHAIN& padOutline = padPolySet.COutline( 0 ); SHAPE_LINE_CHAIN::INTERSECTIONS intersections; diff --git a/pcbnew/dialogs/dialog_pad_properties.cpp b/pcbnew/dialogs/dialog_pad_properties.cpp index 153dc9b89d..e79c422b22 100644 --- a/pcbnew/dialogs/dialog_pad_properties.cpp +++ b/pcbnew/dialogs/dialog_pad_properties.cpp @@ -551,7 +551,7 @@ void DIALOG_PAD_PROPERTIES::initValues() else m_SolderPasteMarginRatioCtrl->SetValue( msg ); - switch( m_dummyPad->GetLocalZoneConnection() ) + switch( m_dummyPad->GetZoneConnection() ) { default: case ZONE_CONNECTION::INHERITED: m_ZoneConnectionChoice->SetSelection( 0 ); break; @@ -1199,7 +1199,10 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK() if( m_dummyPad->GetShape() == PAD_SHAPE_CUSTOM ) { - if( !m_dummyPad->MergePrimitivesAsPolygon( ) ) + SHAPE_POLY_SET mergedPolygon; + m_dummyPad->MergePrimitivesAsPolygon( &mergedPolygon ); + + if( mergedPolygon.OutlineCount() > 1 ) error_msgs.Add( _( "Incorrect pad shape: the shape must be equivalent to only one polygon" ) ); } @@ -1446,7 +1449,7 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow() m_currentPad->SetRoundRectRadiusRatio( m_padMaster->GetRoundRectRadiusRatio() ); m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() ); m_currentPad->SetChamferPositions( m_padMaster->GetChamferPositions() ); - m_currentPad->SetZoneConnection( m_padMaster->GetZoneConnection() ); + m_currentPad->SetZoneConnection( m_padMaster->GetEffectiveZoneConnection() ); // rounded rect pads with radius ratio = 0 are in fact rect pads. // So set the right shape (and perhaps issues with a radius = 0) diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp index 874ef0c80a..57cb54ffcb 100644 --- a/pcbnew/drc/drc.cpp +++ b/pcbnew/drc/drc.cpp @@ -1084,10 +1084,9 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem ) // Fast test to detect a pad candidate inside the text bounding box // Finer test (time consumming) is made only for pads near the text. - int bb_radius = pad->GetBoundingRadius() + minClearance; - VECTOR2I shape_pos( pad->ShapePos() ); + int bb_radius = pad->GetBoundingRadius() + minClearance; - if( !rect_area.Collide( SEG( shape_pos, shape_pos ), bb_radius ) ) + if( !rect_area.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) ) continue; SHAPE_POLY_SET padOutline; diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp index 736d9a3e39..bb8becb780 100644 --- a/pcbnew/drc/drc_clearance_test_functions.cpp +++ b/pcbnew/drc/drc_clearance_test_functions.cpp @@ -685,230 +685,37 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual ) { - // relativePadPos is the aPad shape position relative to the aRefPad shape position - wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos(); - - int center2center = KiROUND( EuclideanNorm( relativePadPos ) ); + int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - aRefPad->ShapePos() ) ); // Quick test: Clearance is OK if the bounding circles are further away than aMinClearance if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance ) return true; - /* Here, pads are near and DRC depends on the pad shapes. We must compare distance using - * a fine shape analysis. - * Because a circle or oval shape is the easier shape to test, swap pads to have aRefPad be - * a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. If aRefPad = TRAPEZOID and aPad = RECT, also swap. - */ - bool swap_pads; - swap_pads = false; + // JEY TODO: + // TOM TODO: MTV only works as a proxy for actual-distance for convex shapes - // swap pads to make comparisons easier - // Note also a ROUNDRECT pad with a corner radius = r can be considered as - // a smaller RECT (size - 2*r) with a clearance increased by r - // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other - if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE ) + VECTOR2I mtv; + VECTOR2I maxMtv( 0, 0 ); + + for( const std::shared_ptr& aShape : aRefPad->GetEffectiveShapes() ) { - // pad ref shape is here oval, rect, roundrect, chamfered rect, trapezoid or custom - switch( aPad->GetShape() ) + for( const std::shared_ptr& bShape : aPad->GetEffectiveShapes() ) { - case PAD_SHAPE_CIRCLE: - swap_pads = true; - break; - - case PAD_SHAPE_OVAL: - swap_pads = true; - break; - - case PAD_SHAPE_RECT: - case PAD_SHAPE_ROUNDRECT: - if( aRefPad->GetShape() != PAD_SHAPE_OVAL ) - swap_pads = true; - break; - - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_CHAMFERED_RECT: - case PAD_SHAPE_CUSTOM: - break; + if( aShape->Collide( bShape.get(), aMinClearance, mtv ) ) + { + if( mtv.SquaredEuclideanNorm() > maxMtv.SquaredEuclideanNorm() ) + maxMtv = mtv; + } } } - if( swap_pads ) + if( maxMtv.x > 0 || maxMtv.y > 0 ) { - std::swap( aRefPad, aPad ); - relativePadPos = -relativePadPos; + *aActual = std::max( 0, aMinClearance - maxMtv.EuclideanNorm() ); + return false; } - bool diag = true; - - if( ( aRefPad->GetShape() == PAD_SHAPE_CIRCLE || aRefPad->GetShape() == PAD_SHAPE_OVAL ) ) - { - /* Treat an oval pad as a line segment along the hole's major axis, - * shortened by half its minor axis. - * A circular pad is just a degenerate case of an oval hole. - */ - wxPoint refPadStart, refPadEnd; - int refPadWidth; - - aRefPad->GetOblongGeometry( aRefPad->GetSize(), &refPadStart, &refPadEnd, &refPadWidth ); - refPadStart += aRefPad->ShapePos(); - refPadEnd += aRefPad->ShapePos(); - - SEG refPadSeg( refPadStart, refPadEnd ); - diag = checkClearanceSegmToPad( refPadSeg, refPadWidth, aPad, aMinClearance, aActual ); - } - else - { - int dist_extra = 0; - - // corners of aRefPad (used only for rect/roundrect/trap pad) - wxPoint polyref[4]; - // corners of aRefPad (used only for custom pad) - SHAPE_POLY_SET polysetref; - - if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT ) - { - int padRadius = aRefPad->GetRoundRectCornerRadius(); - dist_extra = padRadius; - GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(), - aRefPad->GetOrientation() ); - } - else if( aRefPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT ) - { - BOARD* board = aRefPad->GetBoard(); - int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF; - - // The reference pad can be rotated. Calculate the rotated coordinates. - // (note, the ref pad position is the origin of coordinates for this drc test) - int padRadius = aRefPad->GetRoundRectCornerRadius(); - - TransformRoundChamferedRectToPolygon( polysetref, wxPoint( 0, 0 ), aRefPad->GetSize(), - aRefPad->GetOrientation(), - padRadius, aRefPad->GetChamferRectRatio(), - aRefPad->GetChamferPositions(), maxError ); - } - else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM ) - { - polysetref.Append( aRefPad->GetCustomShapeAsPolygon() ); - - // The reference pad can be rotated. Calculate the rotated coordinates. - // (note, the ref pad position is the origin of coordinates for this drc test) - aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref, wxPoint( 0, 0 ), - aRefPad->GetOrientation() ); - } - else - { - // BuildPadPolygon has meaning for rect a trapeziod shapes and returns the 4 corners. - aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); - } - - // corners of aPad (used only for rect/roundrect/trap pad) - wxPoint polycompare[4]; - // corners of aPad (used only custom pad) - SHAPE_POLY_SET polysetcompare; - - switch( aPad->GetShape() ) - { - case PAD_SHAPE_ROUNDRECT: - case PAD_SHAPE_RECT: - case PAD_SHAPE_CHAMFERED_RECT: - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_CUSTOM: - if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT ) - { - int padRadius = aPad->GetRoundRectCornerRadius(); - dist_extra = padRadius; - GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(), - aPad->GetOrientation() ); - } - else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT ) - { - BOARD* board = aRefPad->GetBoard(); - int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF; - - // The pad to compare can be rotated. Calculate the rotated coordinates. - // ( note, the pad to compare position is the relativePadPos for this drc test) - int padRadius = aPad->GetRoundRectCornerRadius(); - - TransformRoundChamferedRectToPolygon( polysetcompare, relativePadPos, - aPad->GetSize(), aPad->GetOrientation(), - padRadius, aPad->GetChamferRectRatio(), - aPad->GetChamferPositions(), maxError ); - } - else if( aPad->GetShape() == PAD_SHAPE_CUSTOM ) - { - polysetcompare.Append( aPad->GetCustomShapeAsPolygon() ); - - // The pad to compare can be rotated. Calculate the rotated coordinates. - // ( note, the pad to compare position is the relativePadPos for this drc test) - aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare, relativePadPos, - aPad->GetOrientation() ); - } - else - { - aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); - - // Move aPad shape to relativePadPos - for( int ii = 0; ii < 4; ii++ ) - polycompare[ii] += relativePadPos; - } - - // And now test polygons: We have 3 cases: - // one poly is complex and the other is basic (has only 4 corners) - // both polys are complex - // both polys are basic (have only 4 corners) the most usual case - if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0) - { - const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 ); - // And now test polygons: - if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), - polycompare, 4, aMinClearance + dist_extra, aActual ) ) - { - *aActual = std::max( 0, *aActual - dist_extra ); - diag = false; - } - } - else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount()) - { - const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 ); - // And now test polygons: - if( !poly2polyDRC((wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), - polyref, 4, aMinClearance + dist_extra, aActual ) ) - { - *aActual = std::max( 0, *aActual - dist_extra ); - diag = false; - } - } - else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() ) - { - const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 ); - const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 ); - - // And now test polygons: - if( !poly2polyDRC((wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), - (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), - aMinClearance + dist_extra, aActual ) ) - { - *aActual = std::max( 0, *aActual - dist_extra ); - diag = false; - } - } - else - { - if( !poly2polyDRC( polyref, 4, polycompare, 4, aMinClearance + dist_extra, aActual ) ) - { - *aActual = std::max( 0, *aActual - dist_extra ); - diag = false; - } - } - break; - - default: - wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() ); - break; - } - } - - return diag; + return true; } diff --git a/pcbnew/exporters/export_gencad.cpp b/pcbnew/exporters/export_gencad.cpp index 65683cda47..41f6a0e238 100644 --- a/pcbnew/exporters/export_gencad.cpp +++ b/pcbnew/exporters/export_gencad.cpp @@ -594,8 +594,14 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb ) { fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR ); + int ddx = pad->GetDelta().x / 2; + int ddy = pad->GetDelta().y / 2; + wxPoint poly[4]; - pad->BuildPadPolygon( poly, wxSize( 0, 0 ), 0 ); + poly[0] = wxPoint( -dx + ddy, dy + ddx ); + poly[1] = wxPoint( dx - ddy, dy - ddx ); + poly[2] = wxPoint( dx + ddy, -dy + ddx ); + poly[3] = wxPoint( -dx - ddy, -dy - ddx ); for( int cur = 0; cur < 4; ++cur ) { @@ -613,7 +619,8 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb ) { fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR ); - const SHAPE_POLY_SET& outline = pad->GetCustomShapeAsPolygon(); + SHAPE_POLY_SET outline; + pad->MergePrimitivesAsPolygon( &outline ); for( int jj = 0; jj < outline.OutlineCount(); ++jj ) { diff --git a/pcbnew/exporters/export_vrml.cpp b/pcbnew/exporters/export_vrml.cpp index cd038461fb..a63e8206da 100644 --- a/pcbnew/exporters/export_vrml.cpp +++ b/pcbnew/exporters/export_vrml.cpp @@ -1151,7 +1151,7 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aTinLayer, D_P case PAD_SHAPE_CHAMFERED_RECT: { SHAPE_POLY_SET polySet; - const int corner_radius = aPad->GetRoundRectCornerRadius( aPad->GetSize() ); + const int corner_radius = aPad->GetRoundRectCornerRadius(); TransformRoundChamferedRectToPolygon( polySet, wxPoint( 0, 0 ), aPad->GetSize(), 0.0, corner_radius, 0.0, 0, ARC_HIGH_DEF ); std::vector< wxRealPoint > cornerList; diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 78b5e55e60..0d857ddec8 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -1479,8 +1479,8 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const if( aPad->GetLocalClearance() != 0 ) StrPrintf( &output, " (clearance %s)", FormatInternalUnits( aPad->GetLocalClearance() ).c_str() ); - if( aPad->GetZoneConnection() != ZONE_CONNECTION::INHERITED ) - StrPrintf( &output, " (zone_connect %d)", static_cast( aPad->GetZoneConnection() ) ); + if( aPad->GetEffectiveZoneConnection() != ZONE_CONNECTION::INHERITED ) + StrPrintf( &output, " (zone_connect %d)", static_cast( aPad->GetEffectiveZoneConnection() ) ); if( aPad->GetThermalWidth() != 0 ) StrPrintf( &output, " (thermal_width %s)", FormatInternalUnits( aPad->GetThermalWidth() ).c_str() ); diff --git a/pcbnew/pad_custom_shape_functions.cpp b/pcbnew/pad_custom_shape_functions.cpp index fc8946ae58..90b4170c39 100644 --- a/pcbnew/pad_custom_shape_functions.cpp +++ b/pcbnew/pad_custom_shape_functions.cpp @@ -146,7 +146,7 @@ void PAD_CS_PRIMITIVE::Rotate( const wxPoint& aRotCentre, double aAngle ) * the shape is a polygon (can be with thick outline), segment, circle or arc */ -void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool aMergePrimitives ) +void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness ) { std::vector points; @@ -158,39 +158,33 @@ void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool for( auto iter = poly_no_hole.CIterate(); iter; iter++ ) points.emplace_back( iter->x, iter->y ); - AddPrimitivePoly( points, aThickness, aMergePrimitives ); + AddPrimitivePoly( points, aThickness ); } -void D_PAD::AddPrimitivePoly( const std::vector& aPoly, int aThickness, - bool aMergePrimitives ) +void D_PAD::AddPrimitivePoly( const std::vector& aPoly, int aThickness ) { PAD_CS_PRIMITIVE shape( S_POLYGON ); shape.m_Poly = aPoly; shape.m_Thickness = aThickness; m_basicShapes.push_back( shape ); - - if( aMergePrimitives ) - MergePrimitivesAsPolygon(); + m_shapesDirty = true; } -void D_PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness, - bool aMergePrimitives ) +void D_PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness ) { PAD_CS_PRIMITIVE shape( S_SEGMENT ); shape.m_Start = aStart; shape.m_End = aEnd; shape.m_Thickness = aThickness; m_basicShapes.push_back( shape ); - - if( aMergePrimitives ) - MergePrimitivesAsPolygon(); + m_shapesDirty = true; } void D_PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle, - int aThickness, bool aMergePrimitives ) + int aThickness ) { PAD_CS_PRIMITIVE shape( S_ARC ); shape.m_Start = aCenter; @@ -198,14 +192,12 @@ void D_PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int shape.m_ArcAngle = aArcAngle; shape.m_Thickness = aThickness; m_basicShapes.push_back( shape ); - - if( aMergePrimitives ) - MergePrimitivesAsPolygon(); + m_shapesDirty = true; } void D_PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1, - const wxPoint& aCtrl2, int aThickness, bool aMergePrimitives ) + const wxPoint& aCtrl2, int aThickness ) { PAD_CS_PRIMITIVE shape( S_CURVE ); shape.m_Start = aStart; @@ -214,41 +206,33 @@ void D_PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const shape.m_Ctrl2 = aCtrl2; shape.m_Thickness = aThickness; m_basicShapes.push_back( shape ); - - if( aMergePrimitives ) - MergePrimitivesAsPolygon(); + m_shapesDirty = true; } -void D_PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness, - bool aMergePrimitives ) +void D_PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness ) { PAD_CS_PRIMITIVE shape( S_CIRCLE ); shape.m_Start = aCenter; shape.m_Radius = aRadius; shape.m_Thickness = aThickness; m_basicShapes.push_back( shape ); - - if( aMergePrimitives ) - MergePrimitivesAsPolygon(); + m_shapesDirty = true; } -void D_PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness, - bool aMergePrimitives ) +void D_PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness ) { PAD_CS_PRIMITIVE shape( S_RECT ); shape.m_Start = aStart; shape.m_End = aEnd; shape.m_Thickness = aThickness; m_basicShapes.push_back( shape ); - - if( aMergePrimitives ) - MergePrimitivesAsPolygon(); + m_shapesDirty = true; } -bool D_PAD::SetPrimitives( const std::vector& aPrimitivesList ) +void D_PAD::SetPrimitives( const std::vector& aPrimitivesList ) { // clear old list m_basicShapes.clear(); @@ -257,17 +241,16 @@ bool D_PAD::SetPrimitives( const std::vector& aPrimitivesList if( aPrimitivesList.size() ) m_basicShapes = aPrimitivesList; - // Only one polygon is expected (pad area = only one copper area) - return MergePrimitivesAsPolygon(); + m_shapesDirty = true; } -bool D_PAD::AddPrimitives( const std::vector& aPrimitivesList ) +void D_PAD::AddPrimitives( const std::vector& aPrimitivesList ) { for( const auto& prim : aPrimitivesList ) m_basicShapes.push_back( prim ); - return MergePrimitivesAsPolygon(); + m_shapesDirty = true; } @@ -275,44 +258,44 @@ bool D_PAD::AddPrimitives( const std::vector& aPrimitivesList void D_PAD::DeletePrimitivesList() { m_basicShapes.clear(); - m_customShapeAsPolygon.RemoveAllContours(); + m_shapesDirty = true; } -bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError ) - +void D_PAD::addCustomPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError ) const { - SHAPE_POLY_SET aux_polyset; + SHAPE_POLY_SET polyset; - for( PAD_CS_PRIMITIVE& bshape : m_basicShapes ) + for( const PAD_CS_PRIMITIVE& bshape : m_basicShapes ) { switch( bshape.m_Shape ) { case S_CURVE: { - std::vector ctrlPoints = { bshape.m_Start, bshape.m_Ctrl1, bshape.m_Ctrl2, bshape.m_End }; + std::vector ctrlPoints = { bshape.m_Start, bshape.m_Ctrl1, bshape.m_Ctrl2, + bshape.m_End }; BEZIER_POLY converter( ctrlPoints ); std::vector< wxPoint> poly; converter.GetPoly( poly, bshape.m_Thickness ); for( unsigned ii = 1; ii < poly.size(); ii++ ) { - TransformSegmentToPolygon( - aux_polyset, poly[ ii - 1 ], poly[ ii ], aError, bshape.m_Thickness ); + TransformSegmentToPolygon( polyset, poly[ ii - 1 ], poly[ ii ], aError, + bshape.m_Thickness ); } break; } case S_SEGMENT: // usual segment : line with rounded ends { - TransformSegmentToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, aError, + TransformSegmentToPolygon( polyset, bshape.m_Start, bshape.m_End, aError, bshape.m_Thickness ); break; } case S_ARC: // Arc with rounded ends { - TransformArcToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, bshape.m_ArcAngle, + TransformArcToPolygon( polyset, bshape.m_Start, bshape.m_End, bshape.m_ArcAngle, aError, bshape.m_Thickness ); break; } @@ -320,80 +303,63 @@ bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError ) case S_CIRCLE: // ring or circle { if( bshape.m_Thickness ) // ring - TransformRingToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError, + TransformRingToPolygon( polyset, bshape.m_Start, bshape.m_Radius, aError, bshape.m_Thickness ); else // Filled circle - TransformCircleToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError ); + TransformCircleToPolygon( polyset, bshape.m_Start, bshape.m_Radius, aError ); break; } case S_RECT: - bshape.m_Poly.clear(); - bshape.m_Poly.emplace_back( bshape.m_Start ); - bshape.m_Poly.emplace_back( bshape.m_End.x, bshape.m_Start.y ); - bshape.m_Poly.emplace_back( bshape.m_End ); - bshape.m_Poly.emplace_back( bshape.m_Start.x, bshape.m_End.y ); - - KI_FALLTHROUGH; - case S_POLYGON: // polygon { - if( bshape.m_Poly.size() < 2 ) - break; // Malformed polygon. + SHAPE_POLY_SET poly; + poly.NewOutline(); - // Insert the polygon: - const std::vector< wxPoint>& poly = bshape.m_Poly; - aux_polyset.NewOutline(); - - if( bshape.m_Thickness ) + if( bshape.m_Shape == S_RECT ) { - SHAPE_POLY_SET polyset; - polyset.NewOutline(); - - for( const wxPoint& pt : poly ) - polyset.Append( pt.x, pt.y ); - - int numSegs = std::max( GetArcToSegmentCount( bshape.m_Thickness / 2, aError, 360.0 ), 6 ); - polyset.Inflate( bshape.m_Thickness / 2, numSegs ); - - aux_polyset.Append( polyset ); + poly.Append( bshape.m_Start ); + poly.Append( bshape.m_End.x, bshape.m_Start.y ); + poly.Append( bshape.m_End ); + poly.Append( bshape.m_Start.x, bshape.m_End.y ); } else { - for( const wxPoint& pt : poly ) - aux_polyset.Append( pt.x, pt.y ); + for( const wxPoint& pt : bshape.m_Poly ) + poly.Append( pt ); } - if( bshape.m_Shape == S_RECT ) - bshape.m_Poly.clear(); + if( bshape.m_Thickness ) + { + int numSegs = std::max( GetArcToSegmentCount( bshape.m_Thickness / 2, aError, 360.0 ), 6 ); + poly.Inflate( bshape.m_Thickness / 2, numSegs ); + } + + // Insert the polygon: + polyset.NewOutline(); + polyset.Append( poly ); } break; default: // un-handled primitive - wxASSERT_MSG( false, wxT( "D_PAD::buildCustomPadPolygon not implemented for " - + BOARD_ITEM::ShowShape( bshape.m_Shape ) ) ); + wxASSERT_MSG( false, "D_PAD::addCustomPadPrimitivesToPolygon not implemented for " + + BOARD_ITEM::ShowShape( bshape.m_Shape ) ); break; } } - aux_polyset.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyset.Simplify( SHAPE_POLY_SET::PM_FAST ); // Merge all polygons with the initial pad anchor shape - if( aux_polyset.OutlineCount() ) + if( polyset.OutlineCount() ) { - aMergedPolygon->BooleanAdd( aux_polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); } - - return aMergedPolygon->OutlineCount() <= 1; } -/* Merge all basic shapes, converted to a polygon in one polygon, - * return true if OK, false in there is more than one polygon - * in aMergedPolygon - */ -bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon ) +void D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon ) const { auto board = GetBoard(); int maxError = ARC_HIGH_DEF; @@ -401,58 +367,35 @@ bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon ) if( board ) maxError = board->GetDesignSettings().m_MaxError; - // if aMergedPolygon == NULL, use m_customShapeAsPolygon as target - - if( !aMergedPolygon ) - aMergedPolygon = &m_customShapeAsPolygon; - aMergedPolygon->RemoveAllContours(); // Add the anchor pad shape in aMergedPolygon, others in aux_polyset: // The anchor pad is always at 0,0 switch( GetAnchorPadShape() ) { - default: - case PAD_SHAPE_CIRCLE: - TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError ); - break; - case PAD_SHAPE_RECT: { SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y ); aMergedPolygon->AddOutline( rect.Outline() ); + } + break; + default: + case PAD_SHAPE_CIRCLE: + TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError ); break; } - } - if( !buildCustomPadPolygon( aMergedPolygon, maxError ) ) - return false; - - m_boundingRadius = -1; // The current bounding radius is no longer valid. - - return aMergedPolygon->OutlineCount() <= 1; -} - - -void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon, - wxPoint aPosition, double aRotation ) const -{ - if( aMergedPolygon->OutlineCount() == 0 ) - return; - - // Move, rotate, ... coordinates in aMergedPolygon according to the - // pad position and orientation - aMergedPolygon->Rotate( -DECIDEG2RAD( aRotation ) ); - aMergedPolygon->Move( VECTOR2I( aPosition ) ); + addCustomPadPrimitivesToPolygon( aMergedPolygon, maxError ); } bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos ) { SHAPE_POLY_SET poly; + addCustomPadPrimitivesToPolygon( &poly, ARC_LOW_DEF ); - if( !buildCustomPadPolygon( &poly, ARC_LOW_DEF ) ) + if( poly.OutlineCount() > 1 ) return false; const int minSteps = 10; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index daeaee01db..4c910f986d 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -44,7 +44,9 @@ #include #include #include - +#include +#include +#include using namespace KIGFX; @@ -790,161 +792,59 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer ) // Choose drawing settings depending on if we are drawing a pad itself or a hole if( aLayer == LAYER_PADS_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES ) { - m_gal->Save(); - m_gal->Translate( VECTOR2D( aPad->GetPosition() ) ); - m_gal->Rotate( -aPad->GetOrientationRadians() ); + const std::shared_ptr& seg = aPad->GetEffectiveHoleShape(); - // Drawing hole: has same shape as PAD_CIRCLE or PAD_OVAL - size = getDrillSize( aPad ) / 2.0; - - if( getDrillShape( aPad ) == PAD_DRILL_SHAPE_OBLONG ) - { - if( size.y >= size.x ) - { - m = ( size.y - size.x ); - n = size.x; - - m_gal->DrawArc( VECTOR2D( 0, -m ), n, -M_PI, 0 ); - m_gal->DrawArc( VECTOR2D( 0, m ), n, M_PI, 0 ); - - if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] ) - { - m_gal->DrawLine( VECTOR2D( -n, -m ), VECTOR2D( -n, m ) ); - m_gal->DrawLine( VECTOR2D( n, -m ), VECTOR2D( n, m ) ); - } - else - { - m_gal->DrawRectangle( VECTOR2D( -n, -m ), VECTOR2D( n, m ) ); - } - } - else - { - m = ( size.x - size.y ); - n = size.y; - m_gal->DrawArc( VECTOR2D( -m, 0 ), n, M_PI / 2, 3 * M_PI / 2 ); - m_gal->DrawArc( VECTOR2D( m, 0 ), n, M_PI / 2, -M_PI / 2 ); - - if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] ) - { - m_gal->DrawLine( VECTOR2D( -m, -n ), VECTOR2D( m, -n ) ); - m_gal->DrawLine( VECTOR2D( -m, n ), VECTOR2D( m, n ) ); - } - else - { - m_gal->DrawRectangle( VECTOR2D( -m, -n ), VECTOR2D( m, n ) ); - } - } - } - else - { - m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), size.x ); - } - - m_gal->Restore(); + m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() ); } else { - SHAPE_POLY_SET polySet; + wxSize pad_size = aPad->GetSize(); + wxSize margin; switch( aLayer ) { case F_Mask: case B_Mask: - { - int clearance = aPad->GetSolderMaskMargin(); - - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) - { - int radius = (aPad->GetSize().x/2) + clearance; - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius ); - } - else if( aPad->GetShape() == PAD_SHAPE_OVAL ) - { - if( aPad->GetSize().x == aPad->GetSize().y ) - { - int radius = (aPad->GetSize().x/2) + clearance; - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius ); - } - else - { - wxPoint seg_start, seg_end; - aPad->BuildSegmentFromOvalShape( seg_start, seg_end, - aPad->GetOrientation(), - wxSize( 0, 0 ) ); - int seg_width = std::min( aPad->GetSize().x, aPad->GetSize().y ) - + 2*clearance; - m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ), - VECTOR2D( aPad->ShapePos()+seg_end ), - seg_width ); - } - } - else - { - aPad->TransformShapeWithClearanceToPolygon( polySet, clearance ); - m_gal->DrawPolygon( polySet ); - } - } + margin.x = margin.y = aPad->GetSolderMaskMargin(); break; case F_Paste: case B_Paste: - { - wxSize pad_size = aPad->GetSize(); - wxSize margin = aPad->GetSolderPasteMargin(); - const_cast(aPad)->SetSize( pad_size + margin + margin ); - - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 ); - else if( aPad->GetShape() == PAD_SHAPE_OVAL ) - { - if( aPad->GetSize().x == aPad->GetSize().y ) - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 ); - else - { - wxPoint seg_start, seg_end; - aPad->BuildSegmentFromOvalShape( seg_start, seg_end, - aPad->GetOrientation(), - wxSize( 0, 0 ) ); - int seg_width = std::min( aPad->GetSize().x, aPad->GetSize().y ); - m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ), - VECTOR2D( aPad->ShapePos()+seg_end ), - seg_width ); - } - } - else - { - aPad->TransformShapeWithClearanceToPolygon( polySet, 0 ); - m_gal->DrawPolygon( polySet ); - } - const_cast(aPad)->SetSize( pad_size ); - } + margin = aPad->GetSolderPasteMargin(); break; default: - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 ); - else if( aPad->GetShape() == PAD_SHAPE_OVAL ) - { - if( aPad->GetSize().x == aPad->GetSize().y ) - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 ); - else - { - wxPoint seg_start, seg_end; - aPad->BuildSegmentFromOvalShape( seg_start, seg_end, - aPad->GetOrientation(), - wxSize( 0, 0 ) ); - m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ), - VECTOR2D( aPad->ShapePos()+seg_end ), - std::min( aPad->GetSize().x, aPad->GetSize().y ) ); - } - } - else - { - aPad->TransformShapeWithClearanceToPolygon( polySet, 0 ); - m_gal->DrawPolygon( polySet ); - } + margin.x = margin.y = 0; break; } + + if( margin.x != margin.y ) + { + const_cast( aPad )->SetSize( pad_size + margin + margin ); + margin.x = margin.y = 0; + } + + const std::vector>& shapes = aPad->GetEffectiveShapes(); + + if( shapes.size() == 1 && shapes[0]->Type() == SH_SEGMENT ) + { + const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shapes[0].get(); + m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() + 2 * margin.x ); + } + else if( shapes.size() == 1 && shapes[0]->Type() == SH_CIRCLE ) + { + const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shapes[0].get(); + m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + margin.x ); + } + else + { + SHAPE_POLY_SET polySet; + aPad->TransformShapeWithClearanceToPolygon( polySet, margin.x ); + m_gal->DrawPolygon( polySet ); + } + + if( aPad->GetSize() != pad_size ) + const_cast( aPad )->SetSize( pad_size ); } // Clearance outlines @@ -961,31 +861,17 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer ) m_gal->SetStrokeColor( color ); int clearance = aPad->GetClearance(); - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) - { - int radius = (aPad->GetSize().x/2) + clearance; - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius ); - } - else if( aPad->GetShape() == PAD_SHAPE_OVAL ) - { - if( aPad->GetSize().x == aPad->GetSize().y ) - { - int radius = (aPad->GetSize().x/2) + clearance; - m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius ); - } - else - { - wxPoint seg_start, seg_end; + const std::vector>& shapes = aPad->GetEffectiveShapes(); - aPad->BuildSegmentFromOvalShape( seg_start, seg_end, - aPad->GetOrientation(), - wxSize( 0, 0 ) ); - int seg_width = std::min( aPad->GetSize().x, aPad->GetSize().y ) - + 2*clearance; - m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ), - VECTOR2D( aPad->ShapePos()+seg_end ), - seg_width ); - } + if( shapes.size() == 1 && shapes[0]->Type() == SH_SEGMENT ) + { + const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shapes[0].get(); + m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() + 2 * clearance ); + } + else if( shapes.size() == 1 && shapes[0]->Type() == SH_CIRCLE ) + { + const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shapes[0].get(); + m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + clearance ); } else { diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 2874bfb062..472d0739ed 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -3352,33 +3352,33 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent ) case T_gr_arc: dummysegm = parseDRAWSEGMENT(); pad->AddPrimitiveArc( dummysegm->GetCenter(), dummysegm->GetArcStart(), - dummysegm->GetAngle(), dummysegm->GetWidth(), false ); + dummysegm->GetAngle(), dummysegm->GetWidth() ); break; case T_gr_line: dummysegm = parseDRAWSEGMENT(); pad->AddPrimitiveSegment( dummysegm->GetStart(), dummysegm->GetEnd(), - dummysegm->GetWidth(), false ); + dummysegm->GetWidth() ); break; case T_gr_circle: dummysegm = parseDRAWSEGMENT( true ); // Circles with 0 thickness are allowed // ( filled circles ) pad->AddPrimitiveCircle( dummysegm->GetCenter(), dummysegm->GetRadius(), - dummysegm->GetWidth(), false ); + dummysegm->GetWidth() ); break; case T_gr_rect: dummysegm = parseDRAWSEGMENT( true ); pad->AddPrimitiveRect( dummysegm->GetStart(), dummysegm->GetEnd(), - dummysegm->GetWidth(), false ); + dummysegm->GetWidth() ); break; case T_gr_poly: dummysegm = parseDRAWSEGMENT(); pad->AddPrimitivePoly( dummysegm->BuildPolyPointsList(), - dummysegm->GetWidth(), false ); + dummysegm->GetWidth() ); break; case T_gr_curve: @@ -3386,7 +3386,7 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent ) pad->AddPrimitiveCurve( dummysegm->GetStart(), dummysegm->GetEnd(), dummysegm->GetBezControl1(), dummysegm->GetBezControl2(), - dummysegm->GetWidth(), false ); + dummysegm->GetWidth() ); break; default: @@ -3411,10 +3411,6 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent ) } } - // Be sure the custom shape polygon is built: - if( pad->GetShape() == PAD_SHAPE_CUSTOM ) - pad->MergePrimitivesAsPolygon(); - return pad.release(); } diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index 6bbd426a2c..47c6641e40 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -249,70 +250,6 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, continue; } - wxSize margin; - double width_adj = 0; - - if( onCopperLayer ) - width_adj = itemplotter.getFineWidthAdj(); - - if( onSolderMaskLayer ) - margin.x = margin.y = pad->GetSolderMaskMargin(); - - if( onSolderPasteLayer ) - margin = pad->GetSolderPasteMargin(); - - // Now offset the pad size by margin + width_adj - // this is easy for most shapes, but not for a trapezoid or a custom shape - wxSize padPlotsSize; - wxSize extraSize = margin * 2; - extraSize.x += width_adj; - extraSize.y += width_adj; - - // Store these parameters that can be modified to plot inflated/deflated pads shape - wxSize deltaSize = pad->GetDelta(); // has meaning only for trapezoidal pads - PAD_SHAPE_T padShape = pad->GetShape(); - double padCornerRadius = pad->GetRoundRectCornerRadius(); - - if( pad->GetShape() == PAD_SHAPE_TRAPEZOID ) - { // The easy way is to use BuildPadPolygon to calculate - // size and delta of the trapezoidal pad after offseting: - wxPoint coord[4]; - pad->BuildPadPolygon( coord, extraSize/2, 0.0 ); - // Calculate the size and delta from polygon corners coordinates: - // coord[0] is the lower left - // coord[1] is the upper left - // coord[2] is the upper right - // coord[3] is the lower right - - // the size is the distance between middle of segments - // (left/right or top/bottom) - // size X is the dist between left and right middle points: - padPlotsSize.x = ( ( -coord[0].x + coord[3].x ) // the lower segment X length - + ( -coord[1].x + coord[2].x ) ) // the upper segment X length - / 2; // the Y size is the half sum - // size Y is the dist between top and bottom middle points: - padPlotsSize.y = ( ( coord[0].y - coord[1].y ) // the left segment Y lenght - + ( coord[3].y - coord[2].y ) ) // the right segment Y lenght - / 2; // the Y size is the half sum - - // calculate the delta ( difference of lenght between 2 opposite edges ) - // The delta.x is the delta along the X axis, therefore the delta of Y lenghts - wxSize delta; - - if( coord[0].y != coord[3].y ) - delta.x = coord[0].y - coord[3].y; - else - delta.y = coord[1].x - coord[0].x; - - pad->SetDelta( delta ); - } - else - padPlotsSize = pad->GetSize() + extraSize; - - // Don't draw a null size item : - if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) - continue; - COLOR4D color = COLOR4D::BLACK; if( pad->GetLayerSet()[B_Cu] ) @@ -326,8 +263,30 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, else if( sketchPads && aLayerMask[B_Fab] ) color = aPlotOpt.ColorSettings()->GetColor( B_Fab ); - // Temporary set the pad size to the required plot size: - wxSize tmppadsize = pad->GetSize(); + wxSize margin; + int width_adj = 0; + + if( onCopperLayer ) + width_adj = itemplotter.getFineWidthAdj(); + + if( onSolderMaskLayer ) + margin.x = margin.y = pad->GetSolderMaskMargin(); + + if( onSolderPasteLayer ) + margin = pad->GetSolderPasteMargin(); + + // Now offset the pad size by margin + width_adj + wxSize padPlotsSize = pad->GetSize() + margin * 2 + wxSize( width_adj, width_adj ); + + // Store these parameters that can be modified to plot inflated/deflated pads shape + PAD_SHAPE_T padShape = pad->GetShape(); + wxSize padSize = pad->GetSize(); + wxSize padDelta = pad->GetDelta(); // has meaning only for trapezoidal pads + double padCornerRadius = pad->GetRoundRectCornerRadius(); + + // Don't draw a null size item : + if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) + continue; switch( pad->GetShape() ) { @@ -348,14 +307,26 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, if( margin.x > 0 ) { pad->SetShape( PAD_SHAPE_ROUNDRECT ); - pad->SetSize( padPlotsSize ); pad->SetRoundRectCornerRadius( margin.x ); } - KI_FALLTHROUGH; + + pad->SetSize( padPlotsSize ); + itemplotter.PlotPad( pad, color, padPlotMode ); + break; case PAD_SHAPE_TRAPEZOID: + { + wxSize scale( padPlotsSize.x / padSize.x, padPlotsSize.y / padSize.y ); + pad->SetDelta( wxSize( padDelta.x * scale.x, padDelta.y * scale.y ) ); + + pad->SetSize( padPlotsSize ); + itemplotter.PlotPad( pad, color, padPlotMode ); + } + break; + case PAD_SHAPE_ROUNDRECT: case PAD_SHAPE_CHAMFERED_RECT: + // Chamfer and rounding are stored as a percent and so don't need scaling pad->SetSize( padPlotsSize ); itemplotter.PlotPad( pad, color, padPlotMode ); break; @@ -373,8 +344,7 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, int numSegs = std::max( GetArcToSegmentCount( margin.x, maxError, 360.0 ), 6 ); shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST ); dummy.DeletePrimitivesList(); - dummy.AddPrimitivePoly( shape, 0, false ); - dummy.MergePrimitivesAsPolygon(); + dummy.AddPrimitivePoly( shape, 0 ); // Be sure the anchor pad is not bigger than the deflated shape because this // anchor will be added to the pad shape when plotting the pad. So now the @@ -388,8 +358,8 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, } // Restore the pad parameters modified by the plot code - pad->SetSize( tmppadsize ); - pad->SetDelta( deltaSize ); + pad->SetSize( padSize ); + pad->SetDelta( padDelta ); pad->SetShape( padShape ); pad->SetRoundRectCornerRadius( padCornerRadius ); } @@ -630,7 +600,6 @@ static const PCB_LAYER_ID plot_seq[] = { void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { - BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerSet( aLayerMask ); @@ -646,7 +615,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, outlines.Simplify( SHAPE_POLY_SET::PM_FAST ); // Plot outlines - std::vector< wxPoint > cornerList; + std::vector cornerList; // Now we have one or more basic polygons: plot each polygon for( int ii = 0; ii < outlines.OutlineCount(); ii++ ) @@ -673,9 +642,9 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, int smallDrill = (aPlotOpt.GetDrillMarksType() == PCB_PLOT_PARAMS::SMALL_DRILL_SHAPE) ? SMALL_DRILL : INT_MAX; - for( auto module : aBoard->Modules() ) + for( MODULE* module : aBoard->Modules() ) { - for( auto pad : module->Pads() ) + for( D_PAD* pad : module->Pads() ) { wxSize hole = pad->GetDrillSize(); @@ -690,19 +659,17 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, else { // Note: small drill marks have no significance when applied to slots - wxPoint drl_start, drl_end; - int width; - - pad->GetOblongGeometry( pad->GetDrillSize(), &drl_start, &drl_end, &width ); - aPlotter->ThickSegment( pad->GetPosition() + drl_start, - pad->GetPosition() + drl_end, width, SKETCH, NULL ); + const std::shared_ptr& seg = pad->GetEffectiveHoleShape(); + aPlotter->ThickSegment( (wxPoint) seg->GetSeg().A, + (wxPoint) seg->GetSeg().B, + seg->GetWidth(), SKETCH, NULL ); } } } } // Plot vias holes - for( auto track : aBoard->Tracks() ) + for( TRACK* track : aBoard->Tracks() ) { const VIA* via = dyn_cast( track ); diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index d5c3ff36cb..34dac509ab 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -224,13 +224,9 @@ void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, COLOR4D aColor, EDA_DRAW_MODE_T aPl aPad->GetOrientation(), aPlotMode, &gbr_metadata ); break; - case PAD_SHAPE_TRAPEZOID: - { - wxPoint coord[4]; - aPad->BuildPadPolygon( coord, wxSize(0,0), 0 ); - m_plotter->FlashPadTrapez( shape_pos, coord, - aPad->GetOrientation(), aPlotMode, &gbr_metadata ); - } + case PAD_SHAPE_RECT: + m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), aPad->GetOrientation(), aPlotMode, + &gbr_metadata ); break; case PAD_SHAPE_ROUNDRECT: @@ -238,39 +234,20 @@ void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, COLOR4D aColor, EDA_DRAW_MODE_T aPl aPad->GetOrientation(), aPlotMode, &gbr_metadata ); break; - case PAD_SHAPE_CHAMFERED_RECT: - { - SHAPE_POLY_SET polygons; - const int corner_radius = aPad->GetRoundRectCornerRadius( aPad->GetSize() ); - TransformRoundChamferedRectToPolygon( polygons, shape_pos, aPad->GetSize(), - aPad->GetOrientation(), corner_radius, aPad->GetChamferRectRatio(), - aPad->GetChamferPositions(), m_board->GetDesignSettings().m_MaxError ); - - if( polygons.OutlineCount() == 0 ) - break; - - int min_dim = std::min( aPad->GetSize().x, aPad->GetSize().y ) /2; - m_plotter->FlashPadCustom( shape_pos,wxSize( min_dim, min_dim ), &polygons, aPlotMode, &gbr_metadata ); - } - break; - - case PAD_SHAPE_CUSTOM: - { - SHAPE_POLY_SET polygons; - aPad->MergePrimitivesAsPolygon( &polygons ); - - if( polygons.OutlineCount() == 0 ) - break; - - aPad->CustomShapeAsPolygonToBoardPosition( &polygons, shape_pos, aPad->GetOrientation() ); - m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), &polygons, aPlotMode, &gbr_metadata ); - } - break; - - case PAD_SHAPE_RECT: default: - m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), - aPad->GetOrientation(), aPlotMode, &gbr_metadata ); + case PAD_SHAPE_TRAPEZOID: + case PAD_SHAPE_CHAMFERED_RECT: + case PAD_SHAPE_CUSTOM: + { + SHAPE_POLY_SET polygons; + aPad->TransformShapeWithClearanceToPolygon( polygons, 0 ); + + if( polygons.OutlineCount() == 0 ) + break; + + m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), &polygons, aPlotMode, + &gbr_metadata ); + } break; } } diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 76c10df02f..5703bf06a0 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -617,24 +617,23 @@ std::unique_ptr PNS_KICAD_IFACE_BASE::syncPad( D_PAD* aPad ) wxPoint offset = aPad->GetOffset(); VECTOR2I c( wx_c.x, wx_c.y ); - VECTOR2I sz( wx_sz.x, wx_sz.y ); RotatePoint( &offset, aPad->GetOrientation() ); solid->SetPos( VECTOR2I( c.x - offset.x, c.y - offset.y ) ); solid->SetOffset( VECTOR2I( offset.x, offset.y ) ); - double orient = aPad->GetOrientation() / 10.0; - - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) + if( aPad->GetEffectiveShapes().size() == 1 ) { - solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) ); + solid->SetShape( aPad->GetEffectiveShapes()[0]->Clone() ); } - else if( aPad->GetShape() == PAD_SHAPE_CUSTOM ) + else { + // JEY TODO: + // TOM TODO: move to SHAPE_COMPOUND... + SHAPE_POLY_SET outline; - outline.Append( aPad->GetCustomShapeAsPolygon() ); - aPad->CustomShapeAsPolygonToBoardPosition( &outline, wx_c, aPad->GetOrientation() ); + aPad->TransformShapeWithClearanceToPolygon( outline, 0 ); SHAPE_SIMPLE* shape = new SHAPE_SIMPLE(); @@ -643,138 +642,7 @@ std::unique_ptr PNS_KICAD_IFACE_BASE::syncPad( D_PAD* aPad ) solid->SetShape( shape ); } - else - { - if( orient == 0.0 || orient == 90.0 || orient == 180.0 || orient == 270.0 ) - { - if( orient == 90.0 || orient == 270.0 ) - sz = VECTOR2I( sz.y, sz.x ); - switch( aPad->GetShape() ) - { - case PAD_SHAPE_OVAL: - if( sz.x == sz.y ) - solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) ); - else - { - VECTOR2I delta; - - if( sz.x > sz.y ) - delta = VECTOR2I( ( sz.x - sz.y ) / 2, 0 ); - else - delta = VECTOR2I( 0, ( sz.y - sz.x ) / 2 ); - - SHAPE_SEGMENT* shape = new SHAPE_SEGMENT( c - delta, c + delta, - std::min( sz.x, sz.y ) ); - solid->SetShape( shape ); - } - break; - - case PAD_SHAPE_RECT: - solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) ); - break; - - case PAD_SHAPE_TRAPEZOID: - { - wxPoint coords[4]; - aPad->BuildPadPolygon( coords, wxSize( 0, 0 ), aPad->GetOrientation() ); - SHAPE_SIMPLE* shape = new SHAPE_SIMPLE(); - - for( int ii = 0; ii < 4; ii++ ) - { - shape->Append( wx_c + coords[ii] ); - } - - solid->SetShape( shape ); - break; - } - - case PAD_SHAPE_CHAMFERED_RECT: - case PAD_SHAPE_ROUNDRECT: - { - SHAPE_POLY_SET outline; - aPad->BuildPadShapePolygon( outline, wxSize( 0, 0 ) ); - - // TransformRoundRectToPolygon creates only one convex polygon - SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); - SHAPE_SIMPLE* shape = new SHAPE_SIMPLE(); - - for( int ii = 0; ii < poly.PointCount(); ++ii ) - shape->Append( poly.CPoint( ii ) ); - - solid->SetShape( shape ); - } - break; - - default: - wxLogTrace( "PNS", "unsupported pad shape" ); - return nullptr; - } - } - else - { - switch( aPad->GetShape() ) - { - // PAD_SHAPE_CIRCLE and PAD_SHAPE_CUSTOM already handled above - - case PAD_SHAPE_OVAL: - if( sz.x == sz.y ) - solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) ); - else - { - wxPoint start; - wxPoint end; - - int w = aPad->BuildSegmentFromOvalShape( start, end, aPad->GetOrientation(), - wxSize( 0, 0 ) ); - - SHAPE_SEGMENT* shape = new SHAPE_SEGMENT( start, end, w ); - shape->Move( aPad->ShapePos() ); - solid->SetShape( shape ); - } - break; - - case PAD_SHAPE_RECT: - case PAD_SHAPE_TRAPEZOID: - { - wxPoint coords[4]; - aPad->BuildPadPolygon( coords, wxSize( 0, 0 ), aPad->GetOrientation() ); - - SHAPE_SIMPLE* shape = new SHAPE_SIMPLE(); - for( int ii = 0; ii < 4; ii++ ) - { - shape->Append( wx_c + coords[ii] ); - } - - solid->SetShape( shape ); - break; - } - - case PAD_SHAPE_CHAMFERED_RECT: - case PAD_SHAPE_ROUNDRECT: - { - SHAPE_POLY_SET outline; - aPad->BuildPadShapePolygon( outline, wxSize( 0, 0 ) ); - - // TransformRoundRectToPolygon creates only one convex polygon - SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); - SHAPE_SIMPLE* shape = new SHAPE_SIMPLE(); - - for( int ii = 0; ii < poly.PointCount(); ++ii ) - { - shape->Append( wxPoint( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) ); - } - - solid->SetShape( shape ); - break; - } - - default: - wxLogTrace( "PNS", "unsupported pad shape" ); - return nullptr; - } - } - } return solid; } diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index f69dc17c17..9ff39dfd42 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -534,15 +534,17 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad ) case PAD_SHAPE_CUSTOM: { std::vector polygonal_shape; - const SHAPE_POLY_SET& pad_shape = aPad->GetCustomShapeAsPolygon(); + SHAPE_POLY_SET pad_shape; + aPad->MergePrimitivesAsPolygon( &pad_shape ); - #ifdef EXPORT_CUSTOM_PADS_CONVEX_HULL +#ifdef EXPORT_CUSTOM_PADS_CONVEX_HULL BuildConvexHull( polygonal_shape, pad_shape ); - #else +#else const SHAPE_LINE_CHAIN& p_outline = pad_shape.COutline( 0 ); + for( int ii = 0; ii < p_outline.PointCount(); ++ii ) polygonal_shape.push_back( wxPoint( p_outline.CPoint( ii ) ) ); - #endif +#endif // The polygon must be closed if( polygonal_shape.front() != polygonal_shape.back() ) diff --git a/pcbnew/tools/footprint_editor_tools.cpp b/pcbnew/tools/footprint_editor_tools.cpp index bd8c3567b5..32d9524ab6 100644 --- a/pcbnew/tools/footprint_editor_tools.cpp +++ b/pcbnew/tools/footprint_editor_tools.cpp @@ -596,13 +596,14 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent ) } pad->SetPosition( wxPoint( anchor->x, anchor->y ) ); + pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle ); pad->AddPrimitives( shapes ); pad->ClearFlags(); - bool result = pad->MergePrimitivesAsPolygon(); - pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle ); + SHAPE_POLY_SET mergedPolygon; + pad->MergePrimitivesAsPolygon( &mergedPolygon ); - if( !result ) + if( mergedPolygon.OutlineCount() > 1 ) { DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n" "selected items do not form a single solid shape.") ); diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index ca62bb8c7f..e5fe951bee 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -391,18 +391,14 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles ) { if( aPad->GetShape() == PAD_SHAPE_CUSTOM ) { - // the pad shape in zone can be its convex hull or the shape itself - SHAPE_POLY_SET outline( aPad->GetCustomShapeAsPolygon() ); - int numSegs = std::max( GetArcToSegmentCount( aGap, m_high_def, 360.0 ), 6 ); - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - outline.Inflate( KiROUND( aGap * correction ), numSegs ); - aPad->CustomShapeAsPolygonToBoardPosition( &outline, aPad->GetPosition(), - aPad->GetOrientation() ); + SHAPE_POLY_SET poly; + aPad->TransformShapeWithClearanceToPolygon( poly, aGap, m_high_def ); + // the pad shape in zone can be its convex hull or the shape itself if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) { std::vector convex_hull; - BuildConvexHull( convex_hull, outline ); + BuildConvexHull( convex_hull, poly ); aHoles.NewOutline(); @@ -410,7 +406,7 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles ) aHoles.Append( pt ); } else - aHoles.Append( outline ); + aHoles.Append( poly ); } else {