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 ); }
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 ); } }
/* * 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 ); } }
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 ) ); }
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() ); }
std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE::syncPad( D_PAD* aPad ) { LAYER_RANGE layers( 0, MAX_CU_LAYERS - 1 ); // ignore non-copper pads if( ( aPad->GetLayerSet() & LSET::AllCuMask()).none() ) return NULL; switch( aPad->GetAttribute() ) { case PAD_ATTRIB_STANDARD: break; case PAD_ATTRIB_SMD: case PAD_ATTRIB_HOLE_NOT_PLATED: case PAD_ATTRIB_CONN: { LSET lmsk = aPad->GetLayerSet(); bool is_copper = false; for( int i = 0; i < MAX_CU_LAYERS; i++ ) { if( lmsk[i] ) { is_copper = true; if( aPad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED ) layers = LAYER_RANGE( i ); break; } } if( !is_copper ) return NULL; } break; default: wxLogTrace( "PNS", "unsupported pad type 0x%x", aPad->GetAttribute() ); return NULL; } std::unique_ptr< PNS::SOLID > solid( new PNS::SOLID ); solid->SetLayers( layers ); solid->SetNet( aPad->GetNetCode() ); solid->SetParent( aPad ); wxPoint wx_c = aPad->ShapePos(); wxSize wx_sz = aPad->GetSize(); 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 ) { solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) ); } else if( aPad->GetShape() == PAD_SHAPE_CUSTOM ) { SHAPE_POLY_SET outline; outline.Append( aPad->GetCustomShapeAsPolygon() ); aPad->CustomShapeAsPolygonToBoardPosition( &outline, wx_c, aPad->GetOrientation() ); SHAPE_CONVEX* shape = new SHAPE_CONVEX(); // We use the convex hull of the pad shape, because PnS knows // only convex shapes. std::vector<wxPoint> convex_hull; BuildConvexHull( convex_hull, outline ); for( unsigned ii = 0; ii < convex_hull.size(); ii++ ) shape->Append( convex_hull[ii] ); 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_CONVEX* shape = new SHAPE_CONVEX(); for( int ii = 0; ii < 4; ii++ ) { shape->Append( wx_c + coords[ii] ); } solid->SetShape( shape ); break; } case PAD_SHAPE_ROUNDRECT: { SHAPE_POLY_SET outline; const int segmentToCircleCount = 64; aPad->BuildPadShapePolygon( outline, wxSize( 0, 0 ), segmentToCircleCount, 1.0 ); // TransformRoundRectToPolygon creates only one convex polygon SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); SHAPE_CONVEX* shape = new SHAPE_CONVEX(); for( int ii = 0; ii < poly.PointCount(); ++ii ) { shape->Append( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); } 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; wxPoint corner; SHAPE_CONVEX* shape = new SHAPE_CONVEX(); int w = aPad->BuildSegmentFromOvalShape( start, end, 0.0, wxSize( 0, 0 ) ); if( start.y == 0 ) corner = wxPoint( start.x, -( w / 2 ) ); else corner = wxPoint( w / 2, start.y ); RotatePoint( &start, aPad->GetOrientation() ); RotatePoint( &corner, aPad->GetOrientation() ); shape->Append( wx_c + corner ); for( int rot = 100; rot <= 1800; rot += 100 ) { wxPoint p( corner ); RotatePoint( &p, start, rot ); shape->Append( wx_c + p ); } if( end.y == 0 ) corner = wxPoint( end.x, w / 2 ); else corner = wxPoint( -( w / 2 ), end.y ); RotatePoint( &end, aPad->GetOrientation() ); RotatePoint( &corner, aPad->GetOrientation() ); shape->Append( wx_c + corner ); for( int rot = 100; rot <= 1800; rot += 100 ) { wxPoint p( corner ); RotatePoint( &p, end, rot ); shape->Append( wx_c + p ); } solid->SetShape( shape ); } break; case PAD_SHAPE_RECT: case PAD_SHAPE_TRAPEZOID: { wxPoint coords[4]; aPad->BuildPadPolygon( coords, wxSize( 0, 0 ), aPad->GetOrientation() ); SHAPE_CONVEX* shape = new SHAPE_CONVEX(); for( int ii = 0; ii < 4; ii++ ) { shape->Append( wx_c + coords[ii] ); } solid->SetShape( shape ); break; } case PAD_SHAPE_ROUNDRECT: { SHAPE_POLY_SET outline; const int segmentToCircleCount = 32; aPad->BuildPadShapePolygon( outline, wxSize( 0, 0 ), segmentToCircleCount, 1.0 ); // TransformRoundRectToPolygon creates only one convex polygon SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); SHAPE_CONVEX* shape = new SHAPE_CONVEX(); for( int ii = 0; ii < poly.PointCount(); ++ii ) { shape->Append( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); } solid->SetShape( shape ); break; } default: wxLogTrace( "PNS", "unsupported pad shape" ); return nullptr; } } } return solid; }
void Convert_path_polygon_to_polygon_blocks_and_dummy_blocks( const SHAPE_POLY_SET &aMainPath, CGENERICCONTAINER2D &aDstContainer, float aBiuTo3DunitsScale, float aDivFactor, const BOARD_ITEM &aBoardItem ) { BOX2I pathBounds = aMainPath.BBox(); // Get the path wxASSERT( aMainPath.OutlineCount() == 1 ); const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aMainPath.CPolygon( 0 ); wxASSERT( curr_polywithholes.size() == 1 ); const SHAPE_LINE_CHAIN& path = curr_polywithholes[0]; // a simple polygon // Convert the points to segments class CBBOX2D bbox; bbox.Reset(); // Contains the main list of segments and each segment normal interpolated SEGMENTS_WIDTH_NORMALS segments_and_normals; // Contains a closed polygon used to calc if points are inside SEGMENTS segments; segments_and_normals.resize( path.PointCount() ); segments.resize( path.PointCount() ); for( int i = 0; i < path.PointCount(); i++ ) { const VECTOR2I& a = path.CPoint( i ); const SFVEC2F point ( (float)( a.x) * aBiuTo3DunitsScale, (float)(-a.y) * aBiuTo3DunitsScale ); bbox.Union( point ); segments_and_normals[i].m_Start = point; segments[i].m_Start = point; } bbox.ScaleNextUp(); // Calc the slopes, normals and some statistics about this polygon unsigned int i; unsigned int j = segments_and_normals.size() - 1; // Temporary normal to the segment, it will later be used for interpolation std::vector< SFVEC2F > tmpSegmentNormals; tmpSegmentNormals.resize( segments_and_normals.size() ); float medOfTheSquaresSegmentLength = 0.0f; #ifdef PRINT_STATISTICS_3D_VIEWER float minLength = FLT_MAX; #endif for( i = 0; i < segments_and_normals.size(); j = i++ ) { const SFVEC2F slope = segments_and_normals[j].m_Start - segments_and_normals[i].m_Start; segments_and_normals[i].m_Precalc_slope = slope; // Calculate constants for each segment segments[i].m_inv_JY_minus_IY = 1.0f / ( segments_and_normals[j].m_Start.y - segments_and_normals[i].m_Start.y ); segments[i].m_JX_minus_IX = ( segments_and_normals[j].m_Start.x - segments_and_normals[i].m_Start.x ); // The normal orientation expect a fixed polygon orientation (!TODO: which one?) //tmpSegmentNormals[i] = glm::normalize( SFVEC2F( -slope.y, +slope.x ) ); tmpSegmentNormals[i] = glm::normalize( SFVEC2F( slope.y, -slope.x ) ); const float length = slope.x * slope.x + slope.y * slope.y; #ifdef PRINT_STATISTICS_3D_VIEWER if( length < minLength ) minLength = length; #endif medOfTheSquaresSegmentLength += length; } #ifdef PRINT_STATISTICS_3D_VIEWER float minSegmentLength = sqrt( minLength ); #endif // This calc an approximation of medium lengths, that will be used to calc // the size of the division. medOfTheSquaresSegmentLength /= segments_and_normals.size(); medOfTheSquaresSegmentLength = sqrt( medOfTheSquaresSegmentLength ); // Compute the normal interpolation // If calculate the dot between the segments, if they are above/below some // threshould it will not interpolated it (ex: if you are in a edge corner // or in a smooth transaction) j = segments_and_normals.size() - 1; for( i = 0; i < segments_and_normals.size(); j = i++ ) { const SFVEC2F normalBeforeSeg = tmpSegmentNormals[j]; const SFVEC2F normalSeg = tmpSegmentNormals[i]; const SFVEC2F normalAfterSeg = tmpSegmentNormals[ (i + 1) % segments_and_normals.size() ]; const float dotBefore = glm::dot( normalBeforeSeg, normalSeg ); const float dotAfter = glm::dot( normalAfterSeg, normalSeg ); if( dotBefore < 0.7f ) segments_and_normals[i].m_Normals.m_Start = normalSeg; else segments_and_normals[i].m_Normals.m_Start = glm::normalize( (((normalBeforeSeg * dotBefore ) + normalSeg) * 0.5f) ); if( dotAfter < 0.7f ) segments_and_normals[i].m_Normals.m_End = normalSeg; else segments_and_normals[i].m_Normals.m_End = glm::normalize( (((normalAfterSeg * dotAfter ) + normalSeg) * 0.5f) ); } if( aDivFactor == 0.0f ) aDivFactor = medOfTheSquaresSegmentLength; SFVEC2UI grid_divisions; grid_divisions.x = (unsigned int)( (bbox.GetExtent().x / aDivFactor) ); grid_divisions.y = (unsigned int)( (bbox.GetExtent().y / aDivFactor) ); grid_divisions = glm::clamp( grid_divisions , SFVEC2UI( 1, 1 ), SFVEC2UI( MAX_NR_DIVISIONS, MAX_NR_DIVISIONS ) ); // Calculate the steps advance of the grid SFVEC2F blockAdvance; blockAdvance.x = bbox.GetExtent().x / (float)grid_divisions.x; blockAdvance.y = bbox.GetExtent().y / (float)grid_divisions.y; wxASSERT( blockAdvance.x > 0.0f ); wxASSERT( blockAdvance.y > 0.0f ); const int leftToRight_inc = (pathBounds.GetRight() - pathBounds.GetLeft()) / grid_divisions.x; const int topToBottom_inc = (pathBounds.GetBottom() - pathBounds.GetTop()) / grid_divisions.y; // Statistics unsigned int stats_n_empty_blocks = 0; unsigned int stats_n_dummy_blocks = 0; unsigned int stats_n_poly_blocks = 0; unsigned int stats_sum_size_of_polygons = 0; // Step by each block of a grid trying to extract segments and create // polygon blocks int topToBottom = pathBounds.GetTop(); float blockY = bbox.Max().y; for( unsigned int iy = 0; iy < grid_divisions.y; iy++ ) { int leftToRight = pathBounds.GetLeft(); float blockX = bbox.Min().x; for( unsigned int ix = 0; ix < grid_divisions.x; ix++ ) { CBBOX2D blockBox( SFVEC2F( blockX, blockY - blockAdvance.y ), SFVEC2F( blockX + blockAdvance.x, blockY ) ); // Make the box large to it will catch (intersect) the edges blockBox.ScaleNextUp(); blockBox.ScaleNextUp(); blockBox.ScaleNextUp(); SEGMENTS_WIDTH_NORMALS extractedSegments; extractPathsFrom( segments_and_normals, blockBox, extractedSegments ); if( extractedSegments.empty() ) { SFVEC2F p1( blockBox.Min().x, blockBox.Min().y ); SFVEC2F p2( blockBox.Max().x, blockBox.Min().y ); SFVEC2F p3( blockBox.Max().x, blockBox.Max().y ); SFVEC2F p4( blockBox.Min().x, blockBox.Max().y ); if( polygon_IsPointInside( segments, p1 ) || polygon_IsPointInside( segments, p2 ) || polygon_IsPointInside( segments, p3 ) || polygon_IsPointInside( segments, p4 ) ) { // In this case, the segments are not intersecting the // polygon, so it means that if any point is inside it, // then all other are inside the polygon. // This is a full bbox inside, so add a dummy box aDstContainer.Add( new CDUMMYBLOCK2D( blockBox, aBoardItem ) ); stats_n_dummy_blocks++; } else { // Points are outside, so this block complety missed the polygon // In this case, no objects need to be added stats_n_empty_blocks++; } } else { // At this point, the borders of polygon were intersected by the // bounding box, so we must calculate a new polygon that will // close that small block. // This block will be used to calculate if points are inside // the (sub block) polygon. SHAPE_POLY_SET subBlockPoly; SHAPE_LINE_CHAIN sb = SHAPE_LINE_CHAIN( VECTOR2I( leftToRight, topToBottom ), VECTOR2I( leftToRight + leftToRight_inc, topToBottom ), VECTOR2I( leftToRight + leftToRight_inc, topToBottom + topToBottom_inc ), VECTOR2I( leftToRight, topToBottom + topToBottom_inc ) ); //sb.Append( leftToRight, topToBottom ); sb.SetClosed( true ); subBlockPoly.AddOutline( sb ); // We need here a strictly simple polygon with outlines and holes SHAPE_POLY_SET solution; solution.BooleanIntersection( aMainPath, subBlockPoly, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); OUTERS_AND_HOLES outersAndHoles; outersAndHoles.m_Holes.clear(); outersAndHoles.m_Outers.clear(); for( int idx = 0; idx < solution.OutlineCount(); idx++ ) { const SHAPE_LINE_CHAIN & outline = solution.Outline( idx ); SEGMENTS solutionSegment; polygon_Convert( outline, solutionSegment, aBiuTo3DunitsScale ); outersAndHoles.m_Outers.push_back( solutionSegment ); stats_sum_size_of_polygons += solutionSegment.size(); for( int holeIdx = 0; holeIdx < solution.HoleCount( idx ); holeIdx++ ) { const SHAPE_LINE_CHAIN & hole = solution.Hole( idx, holeIdx ); polygon_Convert( hole, solutionSegment, aBiuTo3DunitsScale ); outersAndHoles.m_Holes.push_back( solutionSegment ); stats_sum_size_of_polygons += solutionSegment.size(); } } if( !outersAndHoles.m_Outers.empty() ) { aDstContainer.Add( new CPOLYGONBLOCK2D( extractedSegments, outersAndHoles, aBoardItem ) ); stats_n_poly_blocks++; } } blockX += blockAdvance.x; leftToRight += leftToRight_inc; } blockY -= blockAdvance.y; topToBottom += topToBottom_inc; } #ifdef PRINT_STATISTICS_3D_VIEWER printf( "////////////////////////////////////////////////////////////////////////////////\n" ); printf( "Convert_path_polygon_to_polygon_blocks_and_dummy_blocks\n" ); printf( " grid_divisions (%u, %u)\n", grid_divisions.x, grid_divisions.y ); printf( " N Total Blocks %u\n", grid_divisions.x * grid_divisions.y ); printf( " N Empty Blocks %u\n", stats_n_empty_blocks ); printf( " N Dummy Blocks %u\n", stats_n_dummy_blocks ); printf( " N Polyg Blocks %u\n", stats_n_poly_blocks ); printf( " Med N Seg Poly %u\n", stats_sum_size_of_polygons / stats_n_poly_blocks ); printf( " medOfTheSquaresSegmentLength %f\n", medOfTheSquaresSegmentLength ); printf( " minSegmentLength %f\n", minSegmentLength ); printf( " aDivFactor %f\n", aDivFactor ); printf( "////////////////////////////////////////////////////////////////////////////////\n" ); #endif }