diff --git a/3d-viewer/3d_canvas.h b/3d-viewer/3d_canvas.h index c82f58a092..f3cd5d4d24 100644 --- a/3d-viewer/3d_canvas.h +++ b/3d-viewer/3d_canvas.h @@ -263,6 +263,17 @@ private: */ void buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* aActivity ); + /** + * Function buildBoardThroughHolesPolygonList + * Helper funtion to build the list of the board through holes polygons + * @param allBoardHoles = the SHAPE_POLY_SET to populate + * @param aSegCountPerCircle = the number of segments to approximate a circle + * @param aOptimizeLargeCircles = true to use more than aSegCountPerCircle + * for large circles (a large circle dimatere is > 1mm ) + */ + void buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardHoles, + int aSegCountPerCircle, bool aOptimizeLargeCircles ); + /** * Function buildShadowList * Called by CreateDrawGL_List() diff --git a/3d-viewer/3d_draw_board_body.cpp b/3d-viewer/3d_draw_board_body.cpp index 95ed31f047..e9a30bc213 100644 --- a/3d-viewer/3d_draw_board_body.cpp +++ b/3d-viewer/3d_draw_board_body.cpp @@ -70,6 +70,78 @@ */ GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer ); +void EDA_3D_CANVAS::buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardHoles, + int aSegCountPerCircle, bool aOptimizeLargeCircles ) +{ + // hole diameter value to change seg count by circle: + int small_hole_limit = Millimeter2iu( 1.0 ); + int copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); + + BOARD* pcb = GetBoard(); + + // Build holes of through vias: + for( TRACK* track = pcb->m_Track; track; track = track->Next() ) + { + if( track->Type() != PCB_VIA_T ) + continue; + + VIA *via = static_cast( track ); + + if( via->GetViaType() != VIA_THROUGH ) + continue; + + int holediameter = via->GetDrillValue(); + int hole_outer_radius = (holediameter + copper_thickness) / 2; + + TransformCircleToPolygon( allBoardHoles, + via->GetStart(), hole_outer_radius, + aSegCountPerCircle ); + } + + // Build holes of through pads: + for( MODULE* footprint = pcb->m_Modules; footprint; footprint = footprint->Next() ) + { + for( D_PAD* pad = footprint->Pads(); pad; pad = pad->Next() ) + { + // Calculate a factor to apply to segcount for large holes ( > 1 mm) + // (bigger pad drill size -> more segments) because holes in pads can have + // very different sizes and optimizing this segcount gives a better look + // Mainly mounting holes have a size bigger than small_hole_limit + wxSize padHole = pad->GetDrillSize(); + + if( ! padHole.x ) // Not drilled pad like SMD pad + continue; + + // we use the hole diameter to calculate the seg count. + // for round holes, padHole.x == padHole.y + // for oblong holes, the diameter is the smaller of (padHole.x, padHole.y) + int diam = std::min( padHole.x, padHole.y ); + int segcount = aSegCountPerCircle; + + if( diam > small_hole_limit ) + { + double segFactor = (double)diam / small_hole_limit; + segcount = (int)(aSegCountPerCircle * segFactor); + + // limit segcount to 48. For a circle this is a very good approx. + if( segcount > 48 ) + segcount = 48; + } + + // The hole in the body is inflated by copper thickness. + int inflate = copper_thickness; + + // If not plated, no copper. + if( pad->GetAttribute () == PAD_HOLE_NOT_PLATED ) + inflate = 0; + + pad->BuildPadDrillShapePolygon( allBoardHoles, inflate, segcount ); + } + } + + allBoardHoles.Simplify(); +} + void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, REPORTER* aErrorMessages, REPORTER* aActivity ) @@ -85,7 +157,7 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); // Number of segments to convert a circle to polygon - // We use 2 values: the first gives a good shape + // We use 2 values: the first gives a good shape (for instanes rond pads) // the second is used to speed up calculations, when a poor approximation is acceptable (holes) const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2.0) ); @@ -95,11 +167,13 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, // a fine representation double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) ); - SHAPE_POLY_SET bufferPolys; - SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines - SHAPE_POLY_SET bufferZonesPolys; - SHAPE_POLY_SET currLayerHoles, allLayerHoles; // Contains holes for the current layer - // + zones when holes are removed from zones + SHAPE_POLY_SET bufferPolys; // copper areas: tracks, pads and filled zones areas + // when holes are removed from zones + SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines + SHAPE_POLY_SET bufferZonesPolys; // copper filled zones areas + // when holes are not removed from zones + SHAPE_POLY_SET currLayerHoles; // Contains holes for the current layer + SHAPE_POLY_SET allLayerHoles; // Contains holes for all layers // Build a polygon from edge cut items wxString msg; @@ -115,7 +189,9 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, } } - bool throughHolesListBuilt = false; // flag to build the through hole polygon list only once + // Build board holes, with optimization of large holes shape. + buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true ); + LSET cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount ); glNewList( aBoardList, GL_COMPILE ); @@ -136,7 +212,7 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, bufferZonesPolys.RemoveAllContours(); currLayerHoles.RemoveAllContours(); - // Draw tracks: + // Draw track shapes: for( TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( layer ) ) @@ -146,30 +222,27 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, 0, segcountforcircle, correctionFactor ); - // Add via hole + // Add blind/buried via holes if( track->Type() == PCB_VIA_T ) { VIA *via = static_cast( track ); - VIATYPE_T viatype = via->GetViaType(); + + if( via->GetViaType() == VIA_THROUGH ) + continue; // already done + int holediameter = via->GetDrillValue(); int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); int hole_outer_radius = (holediameter + thickness) / 2; - if( viatype != VIA_THROUGH ) - TransformCircleToPolygon( currLayerHoles, - via->GetStart(), hole_outer_radius, - segcountLowQuality ); - else if( !throughHolesListBuilt ) - TransformCircleToPolygon( allLayerHoles, - via->GetStart(), hole_outer_radius, - segcountLowQuality ); + TransformCircleToPolygon( currLayerHoles, + via->GetStart(), hole_outer_radius, + segcountLowQuality ); } } - // draw pads + // draw pad shapes for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { - int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); // Note: NPTH pads are not drawn on copper layers when the pad // has same shape as its hole module->TransformPadsShapesWithClearanceToPolygon( layer, @@ -185,44 +258,7 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, segcountforcircle, correctionFactor ); - // Add pad hole, if any - if( !throughHolesListBuilt ) - { - D_PAD* pad = module->Pads(); - - for( ; pad; pad = pad->Next() ) - { - // Calculate a factor to apply to segcount for large holes ( > 1 mm) - // (bigger pad drill size -> more segments) because holes in pads can have - // very different sizes and optimizing this segcount gives a better look - // Mainly mounting holes have a size bigger thon 1 mm - wxSize padHole = pad->GetDrillSize(); - - if( ! padHole.x ) // Not drilled pad like SMD pad - continue; - - // we use the hole diameter to calculate the seg count. - // for round holes, padHole.x == padHole.y - // for oblong holes, the diameter is the smaller of (padHole.x, padHole.y) - int diam = std::min( padHole.x, padHole.y ); - double segFactor = (double)diam / Millimeter2iu( 1.0 ); - - int segcount = (int)(segcountLowQuality * segFactor); - - // Clamp segcount between segcountLowQuality and 48. - // 48 segm for a circle is a very good approx. - segcount = Clamp( segcountLowQuality, segcount, 48 ); - - // The hole in the body is inflated by copper thickness. - int inflate = thickness; - - // If not plated, no copper. - if( pad->GetAttribute () == PAD_HOLE_NOT_PLATED ) - inflate = 0; - - pad->BuildPadDrillShapePolygon( allLayerHoles, inflate, segcount ); - } - } + // pad holes are already in list. } // Draw copper zones. Note: @@ -269,32 +305,31 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, } } - if( !throughHolesListBuilt ) - allLayerHoles.Simplify(); - // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons if( bufferPolys.IsEmpty() ) continue; // Use Clipper lib to subtract holes to copper areas - - currLayerHoles.Append(allLayerHoles); - currLayerHoles.Simplify(); - - bufferPolys.BooleanSubtract( currLayerHoles ); + if( currLayerHoles.OutlineCount() ) + { + currLayerHoles.Append(allLayerHoles); + currLayerHoles.Simplify(); + bufferPolys.BooleanSubtract( currLayerHoles ); + } + else + bufferPolys.BooleanSubtract( allLayerHoles ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) - // If we are not using thickness, then the znormal must face the layer direction - // because it will draw just one plane + // If we are not using thickness, then the z-normal has to match the layer direction + // because just one plane will be drawn if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); - if( realistic_mode ) { setGLCopperColor(); @@ -319,8 +354,6 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); } - - throughHolesListBuilt = true; } if( aActivity ) @@ -409,6 +442,11 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2) ); + // segments to draw a circle to build texts. Is is used only to build + // the shape of each segment of the stroke font, therefore no need to have + // many segments per circle. + const int segcountInStrokeFont = 8; + SHAPE_POLY_SET bufferPolys; SHAPE_POLY_SET allLayerHoles; // Contains through holes, calculated only once SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines @@ -427,34 +465,8 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a } } - int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); - - // Add via holes - for( VIA* via = GetFirstVia( pcb->m_Track ); via; - via = GetFirstVia( via->Next() ) ) - { - VIATYPE_T viatype = via->GetViaType(); - int holediameter = via->GetDrillValue(); - int hole_outer_radius = (holediameter + thickness) / 2; - - if( viatype == VIA_THROUGH ) - TransformCircleToPolygon( allLayerHoles, - via->GetStart(), hole_outer_radius, - segcountLowQuality ); - } - - // draw pads holes - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - { - // Add pad hole, if any - D_PAD* pad = module->Pads(); - - for( ; pad; pad = pad->Next() ) - pad->BuildPadDrillShapePolygon( allLayerHoles, 0, - segcountLowQuality ); - } - - allLayerHoles.Simplify(); + // Build board holes, with no optimization of large holes shape. + buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, false ); // draw graphic items, on technical layers @@ -494,12 +506,12 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( - bufferPolys, 0, segcountforcircle, correctionFactor ); + bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( - bufferPolys, 0, segcountLowQuality, correctionFactorLQ ); + bufferPolys, 0, segcountLowQuality, 1.0 ); break; default: @@ -511,6 +523,8 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a { if( layer == F_SilkS || layer == B_SilkS ) { + // On silk screen layers, the pad shape is only the pad outline + // never a filled shape D_PAD* pad = module->Pads(); int linewidth = g_DrawDefaultLineThickness; @@ -527,8 +541,9 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); + // On tech layers, use a poor circle approximation, only for texts (stroke font) module->TransformGraphicShapesWithClearanceToPolygonSet( layer, - bufferPolys, 0, segcountLowQuality, correctionFactorLQ ); + bufferPolys, 0, segcountforcircle, correctionFactor, segcountInStrokeFont ); } // Draw non copper zones diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index e38ac4b0ec..b91c6974ee 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -201,11 +201,12 @@ void MODULE::TransformPadsShapesWithClearanceToPolygon( LAYER_ID aLayer, * initial radius * aCorrectionFactor */ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet( - LAYER_ID aLayer, + LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer, - int aInflateValue, - int aCircleToSegmentsCount, - double aCorrectionFactor ) + int aInflateValue, + int aCircleToSegmentsCount, + double aCorrectionFactor, + int aCircleToSegmentsCountForTexts ) { std::vector texts; // List of TEXTE_MODULE to convert EDGE_MODULE* outline; @@ -248,7 +249,12 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet( texts.push_back( &Value() ); s_cornerBuffer = &aCornerBuffer; - s_textCircle2SegmentCount = aCircleToSegmentsCount; + + // To allow optimization of circles approximated by segments, + // aCircleToSegmentsCountForTexts, when not 0, is used. + // if 0 (default value) the aCircleToSegmentsCount is used + s_textCircle2SegmentCount = aCircleToSegmentsCountForTexts ? + aCircleToSegmentsCountForTexts : aCircleToSegmentsCount; for( unsigned ii = 0; ii < texts.size(); ii++ ) { diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index a7aba66618..f97c20be13 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -358,13 +358,17 @@ public: * if aCorrectionFactor = 1.0, the polygon is inside the circle * the radius of circle approximated by segments is * initial radius * aCorrectionFactor + * @param aCircleToSegmentsCountForTexts = number of segments to generate + * a circle when building the texts polygonal shapes of the stroke font + * if 0, use the aCircleToSegmentsCount value */ void TransformGraphicShapesWithClearanceToPolygonSet( LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, int aCircleToSegmentsCount, - double aCorrectionFactor ); + double aCorrectionFactor, + int aCircleToSegmentsCountForTexts = 0 ); /** * Function DrawEdgesOnly