3D viewer: a few optimizations to speed up scene building, and better look of circles in footprint outlines.

This commit is contained in:
jean-pierre charras 2015-08-11 13:56:02 +02:00
parent 65ebfe12c1
commit 5ba6cb975b
4 changed files with 144 additions and 108 deletions

View File

@ -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()

View File

@ -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<VIA*>( 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<VIA*>( 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

View File

@ -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<TEXTE_MODULE *> 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++ )
{

View File

@ -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