/* Build the filled solid areas data from real outlines (stored in m_Poly) * The solid areas can be more than one on copper layers, and do not have holes * ( holes are linked by overlapping segments to the main outline) */ bool ZONE_FILLER::fillSingleZone( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys ) const { SHAPE_POLY_SET smoothedPoly; /* convert outlines + holes to outlines without holes (adding extra segments if necessary) * m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building * this zone */ if ( !aZone->BuildSmoothedPoly( smoothedPoly ) ) return false; if( aZone->IsOnCopperLayer() ) { computeRawFilledAreas( aZone, smoothedPoly, aRawPolys, aFinalPolys ); } else { aRawPolys = smoothedPoly; aFinalPolys = smoothedPoly; aFinalPolys.Inflate( -aZone->GetMinThickness() / 2, 16 ); aFinalPolys.Fracture( SHAPE_POLY_SET::PM_FAST ); } return true; }
/* Build a pad outline as non filled polygon, to draw pads on silkscreen layer * Used only to draw pads outlines on silkscreen layers. */ void EDA_3D_CANVAS::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad, SHAPE_POLY_SET& aCornerBuffer, int aWidth, int aCircleToSegmentsCount, double aCorrectionFactor ) { if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring { TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), aPad->GetSize().x / 2, aCircleToSegmentsCount, aWidth ); return; } // For other shapes, draw polygon outlines SHAPE_POLY_SET corners; aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ), aCircleToSegmentsCount, aCorrectionFactor ); // Add outlines as thick segments in polygon buffer const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); for( int ii = 0; ii < path.PointCount(); ii++ ) { const VECTOR2I& a = path.CPoint( ii ); const VECTOR2I& b = path.CPoint( ii + 1 ); TransformRoundedEndsSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ), aCircleToSegmentsCount, aWidth ); } }
void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int aWidth, EDA_DRAW_MODE_T aPlotMode, void* aData ) { if( aPlotMode == SKETCH ) { std::vector<wxPoint> cornerList; SHAPE_POLY_SET outlineBuffer; TransformOvalClearanceToPolygon( outlineBuffer, aStart, aEnd, aWidth, 32 , 1.0 ); const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline(0 ); for( int jj = 0; jj < path.PointCount(); jj++ ) cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) ); // Ensure the polygon is closed if( cornerList[0] != cornerList[cornerList.size() - 1] ) cornerList.push_back( cornerList[0] ); PlotPoly( cornerList, NO_FILL ); } else { MoveTo( aStart ); FinishTo( aEnd ); } }
void Polygon_Calc_BBox_3DU( const SHAPE_POLY_SET &aPolysList, CBBOX2D &aOutBBox , float aBiuTo3DunitsScale ) { aOutBBox.Reset(); for( int idx = 0; idx < aPolysList.OutlineCount(); ++idx ) { // Each polygon in aPolysList is a polygon with holes const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aPolysList.CPolygon( idx ); for( unsigned ipoly = 0; ipoly < curr_polywithholes.size(); ++ipoly ) { const SHAPE_LINE_CHAIN& path = curr_polywithholes[ipoly]; // a simple polygon for( int jj = 0; jj < path.PointCount(); jj++ ) { const VECTOR2I& a = path.CPoint( jj ); aOutBBox.Union( SFVEC2F( (float) a.x * aBiuTo3DunitsScale, (float)-a.y * aBiuTo3DunitsScale ) ); } } } aOutBBox.ScaleNextUp(); }
void Convert_shape_line_polygon_to_triangles( SHAPE_POLY_SET &aPolyList, CGENERICCONTAINER2D &aDstContainer, float aBiuTo3DunitsScale , const BOARD_ITEM &aBoardItem ) { aPolyList.CacheTriangulation(); const double conver_d = (double)aBiuTo3DunitsScale; for( unsigned int j = 0; j < aPolyList.TriangulatedPolyCount(); j++ ) { auto triPoly = aPolyList.TriangulatedPolygon( j ); for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ ) { VECTOR2I a; VECTOR2I b; VECTOR2I c; triPoly->GetTriangle( i, a, b, c ); aDstContainer.Add( new CTRIANGLE2D( SFVEC2F( a.x * conver_d, -a.y * conver_d ), SFVEC2F( b.x * conver_d, -b.y * conver_d ), SFVEC2F( c.x * conver_d, -c.y * conver_d ), aBoardItem ) ); } } }
void HPGL_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize, int aCornerRadius, double aOrient, EDA_DRAW_MODE_T aTraceMode ) { SHAPE_POLY_SET outline; const int segmentToCircleCount = 32; wxSize size = aSize; if( aTraceMode == FILLED ) { // in filled mode, the pen diameter is removed from size // to keep the pad size size.x -= KiROUND( penDiameter ) / 2; size.x = std::max( size.x, 0); size.y -= KiROUND( penDiameter ) / 2; size.y = std::max( size.y, 0); // keep aCornerRadius to a value < min size x,y < 2: aCornerRadius = std::min( aCornerRadius, std::min( size.x, size.y ) /2 ); } TransformRoundRectToPolygon( outline, aPadPos, size, aOrient, aCornerRadius, segmentToCircleCount ); // TransformRoundRectToPolygon creates only one convex polygon std::vector< wxPoint > cornerList; cornerList.reserve( segmentToCircleCount + 4 ); SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); for( int ii = 0; ii < poly.PointCount(); ++ii ) cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL ); }
/* * Function BuildPadShapePolygon * Build the Corner list of the polygonal shape, * depending on shape, extra size (clearance ...) pad and orientation * Note: for Round and oval pads this function is equivalent to * TransformShapeWithClearanceToPolygon, but not for other shapes */ void D_PAD::BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue, int aSegmentsPerCircle, double aCorrectionFactor ) const { wxPoint corners[4]; wxPoint PadShapePos = ShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ switch( GetShape() ) { case PAD_SHAPE_CIRCLE: case PAD_SHAPE_OVAL: TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x, aSegmentsPerCircle, aCorrectionFactor ); break; case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT: aCornerBuffer.NewOutline(); BuildPadPolygon( corners, aInflateValue, m_Orient ); for( int ii = 0; ii < 4; ii++ ) { corners[ii] += PadShapePos; // Shift origin to position aCornerBuffer.Append( corners[ii].x, corners[ii].y ); } break; } }
/** * Function TransformBoundingBoxWithClearanceToPolygon * Convert the text bounding box to a rectangular polygon * Used in filling zones calculations * Circles and arcs are approximated by segments * @param aCornerBuffer = a buffer to store the polygon * @param aClearanceValue = the clearance around the text bounding box */ void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue ) const { if( GetText().Length() == 0 ) return; wxPoint corners[4]; // Buffer of polygon corners EDA_RECT rect = GetTextBox( -1 ); rect.Inflate( aClearanceValue ); corners[0].x = rect.GetOrigin().x; corners[0].y = rect.GetOrigin().y; corners[1].y = corners[0].y; corners[1].x = rect.GetRight(); corners[2].x = corners[1].x; corners[2].y = rect.GetBottom(); corners[3].y = corners[2].y; corners[3].x = corners[0].x; aCornerBuffer.NewOutline(); for( int ii = 0; ii < 4; ii++ ) { // Rotate polygon RotatePoint( &corners[ii].x, &corners[ii].y, m_Pos.x, m_Pos.y, m_Orient ); aCornerBuffer.Append( corners[ii].x, corners[ii].y ); } }
void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize, SHAPE_POLY_SET* aPolygons, EDA_DRAW_MODE_T aTraceMode, void* aData ) { // A Pad custom is plotted as polygon. // A flashed circle @aPadPos is added (anchor pad) // However, because the anchor pad can be circle or rect, we use only // a circle not bigger than the rect. // the main purpose is to print a flashed DCode as pad anchor if( aTraceMode == FILLED ) FlashPadCircle( aPadPos, std::min( aSize.x, aSize.y ), aTraceMode, aData ); GBR_METADATA gbr_metadata; if( aData ) { gbr_metadata = *static_cast<GBR_METADATA*>( aData ); // If the pad is drawn on a copper layer, // set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR if( gbr_metadata.IsCopper() ) gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); wxString attrname( ".P" ); gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers } SHAPE_POLY_SET polyshape = *aPolygons; if( aTraceMode != FILLED ) { SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata ); polyshape.Inflate( -GetCurrentLineWidth()/2, 16 ); } std::vector< wxPoint > cornerList; for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt ) { SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt ); cornerList.clear(); for( int ii = 0; ii < poly.PointCount(); ++ii ) cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); // Close polygon cornerList.push_back( cornerList[0] ); PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL, aTraceMode == FILLED ? 0 : GetCurrentLineWidth(), &gbr_metadata ); } }
void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize, int aCornerRadius, double aOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) { GBR_METADATA gbr_metadata; if( aData ) { gbr_metadata = *static_cast<GBR_METADATA*>( aData ); // If the pad is drawn on a copper layer, // set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR if( gbr_metadata.IsCopper() ) gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); wxString attrname( ".P" ); gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers } if( aTraceMode != FILLED ) SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata ); // Currently, a Pad RoundRect is plotted as polygon. // TODO: use Aperture macro and flash it SHAPE_POLY_SET outline; const int segmentToCircleCount = 64; TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, segmentToCircleCount ); if( aTraceMode != FILLED ) outline.Inflate( -GetCurrentLineWidth()/2, 16 ); std::vector< wxPoint > cornerList; // TransformRoundRectToPolygon creates only one convex polygon SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); cornerList.reserve( poly.PointCount() + 1 ); for( int ii = 0; ii < poly.PointCount(); ++ii ) cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); // Close polygon cornerList.push_back( cornerList[0] ); PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL, aTraceMode == FILLED ? 0 : GetCurrentLineWidth(), &gbr_metadata ); // Now, flash a pad anchor, if a netlist attribute is set // (remove me when a Aperture macro will be used) if( aData && aTraceMode == FILLED ) { int diameter = std::min( aSize.x, aSize.y ); FlashPadCircle( aPadPos, diameter, aTraceMode , aData ); } }
void CLAYER_TRIANGLES::AddToMiddleContourns( const SHAPE_POLY_SET &aPolySet, float zBot, float zTop, double aBiuTo3Du, bool aInvertFaceDirection ) { wxASSERT( aPolySet.OutlineCount() > 0 ); if( aPolySet.OutlineCount() == 0 ) return; // Calculate an estimation of points to reserve unsigned int nrContournPointsToReserve = 0; for( int i = 0; i < aPolySet.OutlineCount(); ++i ) { const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i ); nrContournPointsToReserve += pathOutline.PointCount(); for( int h = 0; h < aPolySet.HoleCount( i ); ++h ) { const SHAPE_LINE_CHAIN &hole = aPolySet.CHole( i, h ); nrContournPointsToReserve += hole.PointCount(); } } // Request to reserve more space m_layer_middle_contourns_quads->Reserve_More( nrContournPointsToReserve * 2, true ); #pragma omp parallel for for( signed int i = 0; i < aPolySet.OutlineCount(); ++i ) { // Add outline const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i ); AddToMiddleContourns( pathOutline, zBot, zTop, aBiuTo3Du, aInvertFaceDirection ); // Add holes for this outline for( int h = 0; h < aPolySet.HoleCount( i ); ++h ) { const SHAPE_LINE_CHAIN &hole = aPolySet.CHole( i, h ); AddToMiddleContourns( hole, zBot, zTop, aBiuTo3Du, aInvertFaceDirection ); } } }
const CPOLYGONS_LIST ConvertPolySetToPolyList(const SHAPE_POLY_SET& aPolyset) { CPOLYGONS_LIST list; CPolyPt corner, firstCorner; const SHAPE_POLY_SET::POLYGON& poly = aPolyset.CPolygon( 0 ); for( unsigned int jj = 0; jj < poly.size() ; jj++ ) { const SHAPE_LINE_CHAIN& path = poly[jj]; for( int i = 0; i < path.PointCount(); i++ ) { const VECTOR2I &v = path.CPoint( i ); corner.x = v.x; corner.y = v.y; corner.end_contour = false; if( i == 0 ) firstCorner = corner; list.AddCorner( corner ); } firstCorner.end_contour = true; list.AddCorner( firstCorner ); } return list; }
void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( SHAPE_POLY_SET& aCornerBuffer, int aError ) const { if( GetFilledPolysList().IsEmpty() ) return; // add filled areas polygons aCornerBuffer.Append( m_FilledPolysList ); auto board = GetBoard(); int maxError = ARC_HIGH_DEF; if( board ) maxError = board->GetDesignSettings().m_MaxError; // add filled areas outlines, which are drawn with thick lines for( int i = 0; i < m_FilledPolysList.OutlineCount(); i++ ) { const SHAPE_LINE_CHAIN& path = m_FilledPolysList.COutline( i ); for( int j = 0; j < path.PointCount(); j++ ) { const VECTOR2I& a = path.CPoint( j ); const VECTOR2I& b = path.CPoint( j + 1 ); TransformRoundedEndsSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ), maxError, GetMinThickness() ); } } }
/* Function TransformSolidAreasShapesToPolygonSet * Convert solid areas full shapes to polygon set * (the full shape is the polygon area with a thick outline) * Used in 3D view * Arcs (ends of segments) are approximated by segments * aCornerBuffer = a buffer to store the polygons * aCircleToSegmentsCount = the number of segments to approximate a circle * aCorrectionFactor = the correction to apply to arcs radius to roughly * keep arc radius when approximated by segments */ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( SHAPE_POLY_SET& aCornerBuffer, int aCircleToSegmentsCount, double aCorrectionFactor ) { if( GetFilledPolysList().IsEmpty() ) return; // add filled areas polygons aCornerBuffer.Append( m_FilledPolysList ); // add filled areas outlines, which are drawn with thick lines for( int i = 0; i < m_FilledPolysList.OutlineCount(); i++ ) { const SHAPE_LINE_CHAIN& path = m_FilledPolysList.COutline( i ); for( int j = 0; j < path.PointCount(); j++ ) { const VECTOR2I& a = path.CPoint( j ); const VECTOR2I& b = path.CPoint( j + 1 ); TransformRoundedEndsSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ), aCircleToSegmentsCount, GetMinThickness() ); } } }
/* * Function BuildPadShapePolygon * Build the Corner list of the polygonal shape, * depending on shape, extra size (clearance ...) pad and orientation * Note: for Round and 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 { wxPoint corners[4]; wxPoint padShapePos = ShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ 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: aCornerBuffer.NewOutline(); BuildPadPolygon( corners, aInflateValue, m_Orient ); for( int ii = 0; ii < 4; ii++ ) { corners[ii] += padShapePos; // Shift origin to position aCornerBuffer.Append( corners[ii].x, corners[ii].y ); } break; case PAD_SHAPE_CUSTOM: // for a custom shape, that is in fact a polygon (with holes), we can use only a inflate value. // so use ( aInflateValue.x + aInflateValue.y ) / 2 as polygon inflate value. // (different values for aInflateValue.x and aInflateValue.y has no sense for a custom pad) TransformShapeWithClearanceToPolygon( aCornerBuffer, ( aInflateValue.x + aInflateValue.y ) / 2 ); break; } }
void ZONE_CONTAINER::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, int aError, bool ignoreLineWidth ) const { wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for zones." ); aCornerBuffer = m_FilledPolysList; aCornerBuffer.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); }
// The helper function for D_CODE::ConvertShapeToPolygon(). // Add a hole to a polygon static void addHoleToPolygon( SHAPE_POLY_SET* aPolygon, APERTURE_DEF_HOLETYPE aHoleShape, wxSize aSize, wxPoint aAnchorPos ) { wxPoint currpos; SHAPE_POLY_SET holeBuffer; if( aHoleShape == APT_DEF_ROUND_HOLE ) { TransformCircleToPolygon( holeBuffer, wxPoint( 0, 0 ), aSize.x / 2, SEGS_CNT ); } else if( aHoleShape == APT_DEF_RECT_HOLE ) { holeBuffer.NewOutline(); currpos.x = aSize.x / 2; currpos.y = aSize.y / 2; holeBuffer.Append( VECTOR2I( currpos ) ); // link to hole and begin hole currpos.x -= aSize.x; holeBuffer.Append( VECTOR2I( currpos ) ); currpos.y -= aSize.y; holeBuffer.Append( VECTOR2I( currpos ) ); currpos.x += aSize.x; holeBuffer.Append( VECTOR2I( currpos ) ); currpos.y += aSize.y; holeBuffer.Append( VECTOR2I( currpos ) ); // close hole } aPolygon->BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST ); // Needed for legacy canvas only aPolygon->Fracture( SHAPE_POLY_SET::PM_FAST ); }
/* * Function DrawApertureMacroShape * Draw the primitive shape for flashed items. * When an item is flashed, this is the shape of the item */ void APERTURE_MACRO::DrawApertureMacroShape( GERBER_DRAW_ITEM* aParent, EDA_RECT* aClipBox, wxDC* aDC, COLOR4D aColor, wxPoint aShapePos, bool aFilledShape ) { SHAPE_POLY_SET* shapeBuffer = GetApertureMacroShape( aParent, aShapePos ); if( shapeBuffer->OutlineCount() == 0 ) return; for( int ii = 0; ii < shapeBuffer->OutlineCount(); ii++ ) { SHAPE_LINE_CHAIN& poly = shapeBuffer->Outline( ii ); GRClosedPoly( aClipBox, aDC, poly.PointCount(), (wxPoint*)&poly.Point( 0 ), aFilledShape, aColor, aColor ); } }
const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList ) { SHAPE_POLY_SET rv; unsigned corners_count = aList.GetCornersCount(); // Enter main outline: this is the first contour unsigned ic = 0; if( !corners_count ) return rv; int index = 0; while( ic < corners_count ) { int hole = -1; if( index == 0 ) { rv.NewOutline(); hole = -1; } else { hole = rv.NewHole(); } while( ic < corners_count ) { rv.Append( aList.GetX( ic ), aList.GetY( ic ), 0, hole ); if( aList.IsEndContour( ic ) ) break; ic++; } ic++; index++; } return rv; }
/** * Function TransformRoundRectToPolygon * convert a rectangle with rounded corners to a polygon * Convert arcs to multiple straight lines * @param aCornerBuffer = a buffer to store the polygon * @param aPosition = the coordinate of the center of the rectangle * @param aSize = the size of the rectangle * @param aRadius = radius of rounded corners * @param aRotation = rotation in 0.1 degrees of the rectangle * @param aCircleToSegmentsCount = the number of segments to approximate a circle */ void TransformRoundRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, const wxSize& aSize, double aRotation, int aCornerRadius, int aCircleToSegmentsCount ) { wxPoint corners[4]; GetRoundRectCornerCenters( corners, aCornerRadius, aPosition, aSize, aRotation ); SHAPE_POLY_SET outline; outline.NewOutline(); for( int ii = 0; ii < 4; ++ii ) outline.Append( corners[ii].x, corners[ii].y ); outline.Inflate( aCornerRadius, aCircleToSegmentsCount ); // Add the outline: aCornerBuffer.Append( outline ); }
void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize, int aCornerRadius, double aOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) { SHAPE_POLY_SET outline; const int segmentToCircleCount = 64; TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, segmentToCircleCount ); // TransformRoundRectToPolygon creates only one convex polygon SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); MoveTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) ); for( int ii = 1; ii < poly.PointCount(); ++ii ) LineTo( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); FinishTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) ); }
/* This function is used to extract a board outlines (3D view, automatic zones build ...) * Any closed outline inside the main outline is a hole * All contours should be closed, i.e. valid closed polygon vertices */ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation ) { PCB_TYPE_COLLECTOR items; // Get all the DRAWSEGMENTS and module graphics into 'items', // then keep only those on layer == Edge_Cuts. static const KICAD_T scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT }; items.Collect( aBoard, scan_graphics ); // Make a working copy of aSegList, because the list is modified during calculations std::vector< DRAWSEGMENT* > segList; for( int ii = 0; ii < items.GetCount(); ii++ ) { if( items[ii]->GetLayer() == Edge_Cuts ) segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) ); } bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance, aErrorLocation ); if( !success || !aOutlines.OutlineCount() ) { // Creates a valid polygon outline is not possible. // So uses the board edge cuts bounding box to create a // rectangular outline // When no edge cuts items, build a contour // from global bounding box EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox(); // If null area, uses the global bounding box. if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) ) bbbox = aBoard->ComputeBoundingBox(); // Ensure non null area. If happen, gives a minimal size. if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) ) bbbox.Inflate( Millimeter2iu( 1.0 ) ); aOutlines.RemoveAllContours(); aOutlines.NewOutline(); wxPoint corner; aOutlines.Append( bbbox.GetOrigin() ); corner.x = bbbox.GetOrigin().x; corner.y = bbbox.GetEnd().y; aOutlines.Append( corner ); aOutlines.Append( bbbox.GetEnd() ); corner.x = bbbox.GetEnd().x; corner.y = bbbox.GetOrigin().y; aOutlines.Append( corner ); } return success; }
SHAPE_POLY_SET* APERTURE_MACRO::GetApertureMacroShape( const GERBER_DRAW_ITEM* aParent, wxPoint aShapePos ) { SHAPE_POLY_SET holeBuffer; bool hasHole = false; m_shape.RemoveAllContours(); for( AM_PRIMITIVES::iterator prim_macro = primitives.begin(); prim_macro != primitives.end(); ++prim_macro ) { if( prim_macro->primitive_id == AMP_COMMENT ) continue; if( prim_macro->IsAMPrimitiveExposureOn( aParent ) ) prim_macro->DrawBasicShape( aParent, m_shape, aShapePos ); else { prim_macro->DrawBasicShape( aParent, holeBuffer, aShapePos ); if( holeBuffer.OutlineCount() ) // we have a new hole in shape: remove the hole { m_shape.BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST ); holeBuffer.RemoveAllContours(); hasHole = true; } } } // If a hole is defined inside a polygon, we must fracture the polygon // to be able to drawn it (i.e link holes by overlapping edges) if( hasHole ) m_shape.Fracture( SHAPE_POLY_SET::PM_FAST ); m_boundingBox = EDA_RECT( wxPoint( 0, 0 ), wxSize( 1, 1 ) ); auto bb = m_shape.BBox(); wxPoint center( bb.Centre().x, bb.Centre().y ); m_boundingBox.Move( aParent->GetABPosition( center ) ); m_boundingBox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 ); return &m_shape; }
/** * Function TransformCircleToPolygon * convert a circle to a polygon, using multiple straight lines * @param aCornerBuffer = a buffer to store the polygon * @param aCenter = the center of the circle * @param aRadius = the radius of the circle * @param aCircleToSegmentsCount = the number of segments to approximate a circle * Note: the polygon is inside the circle, so if you want to have the polygon * outside the circle, you should give aRadius calculated with a corrrection factor */ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCenter, int aRadius, int aCircleToSegmentsCount ) { wxPoint corner_position; int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree int halfstep = 1800 / aCircleToSegmentsCount; // the starting value for rot angles aCornerBuffer.NewOutline(); for( int ii = 0; ii < aCircleToSegmentsCount; ii++ ) { corner_position.x = aRadius; corner_position.y = 0; int angle = (ii * delta) + halfstep; RotatePoint( &corner_position.x, &corner_position.y, angle ); corner_position += aCenter; aCornerBuffer.Append( corner_position.x, corner_position.y ); } }
/** * Function TransformRingToPolygon * Creates a polygon from a ring * Convert arcs to multiple straight segments * @param aCornerBuffer = a buffer to store the polygon * @param aCentre = centre of the arc or circle * @param aRadius = radius of the circle * @param aCircleToSegmentsCount = the number of segments to approximate a circle * @param aWidth = width (thickness) of the ring */ void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, int aRadius, int aCircleToSegmentsCount, int aWidth ) { int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree // Compute the corners posituions and creates poly wxPoint curr_point; int inner_radius = aRadius - ( aWidth / 2 ); int outer_radius = inner_radius + aWidth; aCornerBuffer.NewOutline(); // Draw the inner circle of the ring for( int ii = 0; ii < 3600; ii += delta ) { curr_point.x = inner_radius; curr_point.y = 0; RotatePoint( &curr_point, ii ); curr_point += aCentre; aCornerBuffer.Append( curr_point.x, curr_point.y ); } // Draw the last point of inner circle aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); // Draw the outer circle of the ring for( int ii = 0; ii < 3600; ii += delta ) { curr_point.x = outer_radius; curr_point.y = 0; RotatePoint( &curr_point, -ii ); curr_point += aCentre; aCornerBuffer.Append( curr_point.x, curr_point.y ); } // Draw the last point of outer circle aCornerBuffer.Append( aCentre.x + outer_radius, aCentre.y ); aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); }
bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area_to_combine ) { if( area_ref == area_to_combine ) { wxASSERT( 0 ); return false; } SHAPE_POLY_SET mergedOutlines = *area_ref->Outline(); SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline(); mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST ); mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST ); // We should have one polygon with hole // We can have 2 polygons with hole, if the 2 initial polygons have only one common corner // and therefore cannot be merged (they are dectected as intersecting) // but we should never have more than 2 polys if( mergedOutlines.OutlineCount() > 2 ) { wxLogMessage(wxT("BOARD::CombineAreas error: more than 2 polys after merging") ); return false; } if( mergedOutlines.OutlineCount() > 1 ) return false; // Update the area with the new merged outline delete area_ref->Outline(); area_ref->SetOutline( new SHAPE_POLY_SET( mergedOutlines ) ); RemoveArea( aDeletedList, area_to_combine ); area_ref->SetLocalFlags( 1 ); area_ref->Hatch(); return true; }
void CINFO3D_VISU::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad, SHAPE_POLY_SET& aCornerBuffer, int aWidth ) const { if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring { unsigned int nr_sides_per_circle = GetNrSegmentsCircle( ( aPad->GetSize().x / 2 + aWidth / 2 ) * 2 ); TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), aPad->GetSize().x / 2, nr_sides_per_circle, aWidth ); return; } // For other shapes, draw polygon outlines SHAPE_POLY_SET corners; unsigned int nr_sides_per_circle = GetNrSegmentsCircle( glm::min( aPad->GetSize().x, aPad->GetSize().y) ); buildPadShapePolygon( aPad, corners, wxSize( 0, 0 ), nr_sides_per_circle, GetCircleCorrectionFactor( nr_sides_per_circle ) ); // Add outlines as thick segments in polygon buffer const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); for( int ii = 0; ii < path.PointCount(); ++ii ) { const VECTOR2I& a = path.CPoint( ii ); const VECTOR2I& b = path.CPoint( ii + 1 ); TransformRoundedEndsSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ), nr_sides_per_circle, aWidth ); } }
void PSLIKE_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize, int aCornerRadius, double aOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) { wxSize size( aSize ); if( aTraceMode == FILLED ) SetCurrentLineWidth( 0 ); else { SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); size.x -= GetCurrentLineWidth(); size.y -= GetCurrentLineWidth(); aCornerRadius -= GetCurrentLineWidth()/2; } SHAPE_POLY_SET outline; const int segmentToCircleCount = 64; TransformRoundRectToPolygon( outline, aPadPos, size, aOrient, aCornerRadius, segmentToCircleCount ); std::vector< wxPoint > cornerList; cornerList.reserve( segmentToCircleCount + 5 ); // TransformRoundRectToPolygon creates only one convex polygon SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); for( int ii = 0; ii < poly.PointCount(); ++ii ) cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); // Close polygon cornerList.push_back( cornerList[0] ); PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, GetCurrentLineWidth() ); }
/** * Function TransformRingToPolygon * Creates a polygon from a ring * Convert arcs to multiple straight segments * @param aCornerBuffer = a buffer to store the polygon * @param aCentre = centre of the arc or circle * @param aRadius = radius of the circle * @param aCircleToSegmentsCount = the number of segments to approximate a circle * @param aWidth = width (thickness) of the ring */ void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, int aRadius, int aCircleToSegmentsCount, 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; if( inner_radius <= 0 ) { //In this case, the ring is just a circle (no hole inside) TransformCircleToPolygon( aCornerBuffer, aCentre, aRadius + ( aWidth / 2 ), aCircleToSegmentsCount ); return; } aCornerBuffer.NewOutline(); // Draw the inner circle of the ring int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree for( int ii = 0; ii < 3600; ii += delta ) { curr_point.x = inner_radius; curr_point.y = 0; RotatePoint( &curr_point, ii ); curr_point += aCentre; aCornerBuffer.Append( curr_point.x, curr_point.y ); } // Draw the last point of inner circle aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); // Draw the outer circle of the ring // the first point creates also a segment from the inner to the outer polygon for( int ii = 0; ii < 3600; ii += delta ) { curr_point.x = outer_radius; curr_point.y = 0; RotatePoint( &curr_point, -ii ); curr_point += aCentre; aCornerBuffer.Append( curr_point.x, curr_point.y ); } // Draw the last point of outer circle aCornerBuffer.Append( aCentre.x + outer_radius, aCentre.y ); // And connect the outer polygon to the inner polygon,. // because a segment from inner to the outer polygon was already created, // the final polygon is the inner and the outer outlines connected by // 2 overlapping segments aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); }
// 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 CINFO3D_VISU::buildPadShapePolygon( const D_PAD* aPad, SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue, int aSegmentsPerCircle, double aCorrectionFactor ) const { wxPoint corners[4]; 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: aPad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x, aSegmentsPerCircle, aCorrectionFactor ); break; case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT: { 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; default: wxFAIL_MSG( wxT( "CINFO3D_VISU::buildPadShapePolygon: found a not implemented pad shape (new shape?)" ) ); break; } }