1009 lines
34 KiB
C++
1009 lines
34 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 1992-2012 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
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you may find one here:
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
/**
|
|
* @file 3d_draw.cpp
|
|
*/
|
|
|
|
#include <fctsys.h>
|
|
#include <common.h>
|
|
#include <trigo.h>
|
|
#include <pcbstruct.h>
|
|
#include <drawtxt.h>
|
|
#include <layers_id_colors_and_visibility.h>
|
|
|
|
#include <wxBasePcbFrame.h>
|
|
#include <class_board.h>
|
|
#include <class_module.h>
|
|
#include <class_track.h>
|
|
#include <class_edge_mod.h>
|
|
#include <class_zone.h>
|
|
#include <class_drawsegment.h>
|
|
#include <class_pcb_text.h>
|
|
#include <colors_selection.h>
|
|
#include <convert_basic_shapes_to_polygon.h>
|
|
|
|
#include <3d_viewer.h>
|
|
#include <3d_canvas.h>
|
|
#include <info3d_visu.h>
|
|
#include <trackball.h>
|
|
#include <3d_draw_basic_functions.h>
|
|
|
|
// Imported function:
|
|
extern void Set_Object_Data( std::vector<S3D_VERTEX>& aVertices, double aBiuTo3DUnits );
|
|
extern void CheckGLError();
|
|
|
|
/* returns true if aLayer should be displayed, false otherwise
|
|
*/
|
|
static bool Is3DLayerEnabled( LAYER_NUM aLayer );
|
|
|
|
/* returns the Z orientation parameter 1.0 or -1.0 for aLayer
|
|
* Z orientation is 1.0 for all layers but "back" layers:
|
|
* LAYER_N_BACK , ADHESIVE_N_BACK, SOLDERPASTE_N_BACK ), SILKSCREEN_N_BACK
|
|
* used to calculate the Z orientation parameter for glNormal3f
|
|
*/
|
|
static GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer );
|
|
|
|
/* Helper function BuildPadShapeThickOutlineAsPolygon:
|
|
* Build a pad shape outline as polygon, to draw pads on silkscreen layer
|
|
* with a line thickness = aWidth
|
|
* Used only to draw pads outlines on silkscreen layers.
|
|
*/
|
|
static void BuildPadShapeThickOutlineAsPolygon( D_PAD* aPad,
|
|
CPOLYGONS_LIST& aCornerBuffer,
|
|
int aWidth,
|
|
int aCircleToSegmentsCount,
|
|
double aCorrectionFactor )
|
|
{
|
|
if( aPad->GetShape() == PAD_CIRCLE ) // Draw a ring
|
|
{
|
|
TransformRingToPolygon( aCornerBuffer, aPad->ReturnShapePos(),
|
|
aPad->GetSize().x / 2, aCircleToSegmentsCount, aWidth );
|
|
return;
|
|
}
|
|
|
|
// For other shapes, draw polygon outlines
|
|
CPOLYGONS_LIST corners;
|
|
aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ),
|
|
aCircleToSegmentsCount, aCorrectionFactor );
|
|
|
|
// Add outlines as thick segments in polygon buffer
|
|
for( unsigned ii = 0, jj = corners.GetCornersCount() - 1;
|
|
ii < corners.GetCornersCount(); jj = ii, ii++ )
|
|
{
|
|
TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
|
|
corners.GetPos( jj ),
|
|
corners.GetPos( ii ),
|
|
aCircleToSegmentsCount, aWidth );
|
|
}
|
|
}
|
|
|
|
|
|
void EDA_3D_CANVAS::Redraw( bool finish )
|
|
{
|
|
// SwapBuffer requires the window to be shown before calling
|
|
if( !IsShown() )
|
|
return;
|
|
|
|
SetCurrent( *m_glRC );
|
|
|
|
// Set the OpenGL viewport according to the client size of this canvas.
|
|
// This is done here rather than in a wxSizeEvent handler because our
|
|
// OpenGL rendering context (and thus viewport setting) is used with
|
|
// multiple canvases: If we updated the viewport in the wxSizeEvent
|
|
// handler, changing the size of one canvas causes a viewport setting that
|
|
// is wrong when next another canvas is repainted.
|
|
const wxSize ClientSize = GetClientSize();
|
|
|
|
// *MUST* be called *after* SetCurrent( ):
|
|
glViewport( 0, 0, ClientSize.x, ClientSize.y );
|
|
|
|
InitGL();
|
|
|
|
glMatrixMode( GL_MODELVIEW ); // position viewer
|
|
// transformations
|
|
GLfloat mat[4][4];
|
|
|
|
// Translate motion first, so rotations don't mess up the orientation...
|
|
glTranslatef( m_draw3dOffset.x, m_draw3dOffset.y, 0.0F );
|
|
|
|
build_rotmatrix( mat, g_Parm_3D_Visu.m_Quat );
|
|
glMultMatrixf( &mat[0][0] );
|
|
|
|
glRotatef( g_Parm_3D_Visu.m_Rot[0], 1.0, 0.0, 0.0 );
|
|
glRotatef( g_Parm_3D_Visu.m_Rot[1], 0.0, 1.0, 0.0 );
|
|
glRotatef( g_Parm_3D_Visu.m_Rot[2], 0.0, 0.0, 1.0 );
|
|
|
|
if( m_gllist )
|
|
glCallList( m_gllist );
|
|
else
|
|
CreateDrawGL_List();
|
|
|
|
glFlush();
|
|
|
|
if( finish )
|
|
glFinish();
|
|
|
|
SwapBuffers();
|
|
}
|
|
|
|
// Helper function: initialize the copper color to draw the board
|
|
// in realistic mode.
|
|
static inline void SetGLCopperColor()
|
|
{
|
|
// Generates a golden yellow color, near board "copper" color
|
|
const double lum = 0.7/255.0;
|
|
glColor4f( 255.0*lum, 223.0*lum, 0.0*lum, 1.0 );
|
|
}
|
|
|
|
// Helper function: initialize the color to draw the epoxy layers
|
|
// ( body board and solder mask layers) in realistic mode.
|
|
static inline void SetGLEpoxyColor( double aTransparency = 1.0 )
|
|
{
|
|
// Generates an epoxy color, near board color
|
|
const double lum = 0.2/255.0;
|
|
glColor4f( 100.0*lum, 255.0*lum, 180.0*lum, aTransparency );
|
|
}
|
|
|
|
// Helper function: initialize the color to draw the non copper layers
|
|
// in realistic mode and normal mode.
|
|
static inline void SetGLTechLayersColor( LAYER_NUM aLayer )
|
|
{
|
|
if( g_Parm_3D_Visu.IsRealisticMode() )
|
|
{
|
|
switch( aLayer )
|
|
{
|
|
case SOLDERPASTE_N_BACK:
|
|
case SOLDERPASTE_N_FRONT:
|
|
SetGLColor( DARKGRAY, 0.7 );
|
|
break;
|
|
|
|
case SILKSCREEN_N_BACK:
|
|
case SILKSCREEN_N_FRONT:
|
|
SetGLColor( LIGHTGRAY, 0.9 );
|
|
break;
|
|
|
|
case SOLDERMASK_N_BACK:
|
|
case SOLDERMASK_N_FRONT:
|
|
SetGLEpoxyColor( 0.7 );
|
|
break;
|
|
|
|
default:
|
|
EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( aLayer );
|
|
SetGLColor( color, 0.7 );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( aLayer );
|
|
SetGLColor( color, 0.7 );
|
|
}
|
|
}
|
|
|
|
|
|
void EDA_3D_CANVAS::BuildBoard3DView()
|
|
{
|
|
PCB_BASE_FRAME* pcbframe = Parent()->Parent();
|
|
BOARD* pcb = pcbframe->GetBoard();
|
|
bool realistic_mode = g_Parm_3D_Visu.IsRealisticMode();
|
|
|
|
// Number of segments to draw a circle using segments
|
|
const int segcountforcircle = 16;
|
|
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
|
|
const int segcountLowQuality = 12; // segments to draw a circle with low quality
|
|
// to reduce time calculations
|
|
// for holes and items which do not need
|
|
// a fine representation
|
|
double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2) );
|
|
|
|
CPOLYGONS_LIST bufferPolys;
|
|
bufferPolys.reserve( 200000 ); // Reserve for large board (tracks mainly)
|
|
|
|
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
|
|
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
|
|
allLayerHoles.reserve( 20000 );
|
|
|
|
// Build a polygon from edge cut items
|
|
wxString msg;
|
|
if( ! pcb->GetBoardPolygonOutlines( bufferPcbOutlines,
|
|
allLayerHoles, &msg ) )
|
|
{
|
|
msg << wxT("\n\n") <<
|
|
_("Unable to calculate the board outlines.\n"
|
|
"Therefore use the board boundary box.");
|
|
wxMessageBox( msg );
|
|
}
|
|
|
|
CPOLYGONS_LIST bufferZonesPolys;
|
|
bufferZonesPolys.reserve( 500000 ); // Reserve for large board ( copper zones mainly )
|
|
|
|
CPOLYGONS_LIST currLayerHoles; // Contains holes for the current layer
|
|
bool throughHolesListBuilt = false; // flag to build the through hole polygon list only once
|
|
bool hightQualityMode = false;
|
|
|
|
for( LAYER_NUM layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER;
|
|
layer++ )
|
|
{
|
|
if( layer != LAST_COPPER_LAYER
|
|
&& layer >= g_Parm_3D_Visu.m_CopperLayersCount )
|
|
continue;
|
|
|
|
// Skip non enabled layers in normal mode,
|
|
// and internal layers in realistic mode
|
|
if( !Is3DLayerEnabled( layer ) )
|
|
continue;
|
|
|
|
bufferPolys.RemoveAllContours();
|
|
bufferZonesPolys.RemoveAllContours();
|
|
currLayerHoles.RemoveAllContours();
|
|
|
|
// Draw tracks:
|
|
for( TRACK* track = pcb->m_Track; track != NULL; track = track->Next() )
|
|
{
|
|
if( !track->IsOnLayer( layer ) )
|
|
continue;
|
|
|
|
track->TransformShapeWithClearanceToPolygon( bufferPolys,
|
|
0, segcountforcircle,
|
|
correctionFactor );
|
|
|
|
// Add via hole
|
|
if( track->Type() == PCB_VIA_T )
|
|
{
|
|
int shape = track->GetShape();
|
|
int holediameter = track->GetDrillValue();
|
|
int thickness = g_Parm_3D_Visu.GetCopperThicknessBIU();
|
|
int hole_outer_radius = (holediameter + thickness) / 2;
|
|
|
|
if( shape != VIA_THROUGH )
|
|
TransformCircleToPolygon( currLayerHoles,
|
|
track->GetStart(), hole_outer_radius,
|
|
segcountLowQuality );
|
|
else if( !throughHolesListBuilt )
|
|
TransformCircleToPolygon( allLayerHoles,
|
|
track->GetStart(), hole_outer_radius,
|
|
segcountLowQuality );
|
|
}
|
|
}
|
|
|
|
// draw pads
|
|
for( MODULE* module = pcb->m_Modules; module != NULL; module = module->Next() )
|
|
{
|
|
module->TransformPadsShapesWithClearanceToPolygon( layer,
|
|
bufferPolys,
|
|
0,
|
|
segcountforcircle,
|
|
correctionFactor );
|
|
|
|
// Micro-wave modules may have items on copper layers
|
|
module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
|
|
bufferPolys,
|
|
0,
|
|
segcountforcircle,
|
|
correctionFactor );
|
|
|
|
// Add pad hole, if any
|
|
if( !throughHolesListBuilt )
|
|
{
|
|
D_PAD* pad = module->Pads();
|
|
|
|
for( ; pad != NULL; pad = pad->Next() )
|
|
pad->BuildPadDrillShapePolygon( allLayerHoles, 0,
|
|
segcountLowQuality );
|
|
}
|
|
}
|
|
|
|
// Draw copper zones
|
|
if( g_Parm_3D_Visu.GetFlag( FL_ZONE ) )
|
|
{
|
|
for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
|
|
{
|
|
ZONE_CONTAINER* zone = pcb->GetArea( ii );
|
|
LAYER_NUM zonelayer = zone->GetLayer();
|
|
|
|
if( zonelayer == layer )
|
|
zone->TransformSolidAreasShapesToPolygonSet(
|
|
hightQualityMode ? bufferPolys : bufferZonesPolys,
|
|
segcountLowQuality, correctionFactorLQ );
|
|
}
|
|
}
|
|
|
|
// draw graphic items
|
|
for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
|
|
{
|
|
if( !item->IsOnLayer( layer ) )
|
|
continue;
|
|
|
|
switch( item->Type() )
|
|
{
|
|
case PCB_LINE_T:
|
|
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
|
|
bufferPolys, 0,
|
|
segcountforcircle,
|
|
correctionFactor );
|
|
break;
|
|
|
|
case PCB_TEXT_T:
|
|
( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
|
|
bufferPolys, 0, segcountforcircle, correctionFactor );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// bufferPolys contains polygons to merge. Many overlaps .
|
|
// Calculate merged polygons
|
|
if( bufferPolys.GetCornersCount() == 0 )
|
|
continue;
|
|
|
|
KI_POLYGON_SET currLayerPolyset;
|
|
KI_POLYGON_SET polysetHoles;
|
|
|
|
// Add polygons, without holes
|
|
bufferPolys.ExportTo( currLayerPolyset );
|
|
|
|
// Add holes in polygon list
|
|
currLayerHoles.Append( allLayerHoles );
|
|
|
|
if( currLayerHoles.GetCornersCount() > 0 )
|
|
currLayerHoles.ExportTo( polysetHoles );
|
|
|
|
// Merge polygons, remove holes
|
|
currLayerPolyset -= polysetHoles;
|
|
|
|
int thickness = g_Parm_3D_Visu.GetLayerObjectThicknessBIU( layer );
|
|
int zpos = g_Parm_3D_Visu.GetLayerZcoordBIU( layer );
|
|
|
|
if( realistic_mode )
|
|
SetGLCopperColor();
|
|
else
|
|
{
|
|
EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer );
|
|
SetGLColor( color );
|
|
}
|
|
|
|
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
|
|
|
|
bufferPolys.RemoveAllContours();
|
|
bufferPolys.ImportFrom( currLayerPolyset );
|
|
Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos,
|
|
thickness,
|
|
g_Parm_3D_Visu.m_BiuTo3Dunits );
|
|
|
|
if( bufferZonesPolys.GetCornersCount() )
|
|
Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos,
|
|
thickness,
|
|
g_Parm_3D_Visu.m_BiuTo3Dunits );
|
|
|
|
throughHolesListBuilt = true;
|
|
}
|
|
|
|
// Draw vias holes (vertical cylinders)
|
|
for( TRACK* track = pcb->m_Track; track != NULL; track = track->Next() )
|
|
{
|
|
if( track->Type() == PCB_VIA_T )
|
|
Draw3DViaHole( (SEGVIA*) track );
|
|
}
|
|
|
|
// Draw pads holes (vertical cylinders)
|
|
for( MODULE* module = pcb->m_Modules; module != NULL; module = module->Next() )
|
|
{
|
|
for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() )
|
|
Draw3DPadHole( pad );
|
|
}
|
|
|
|
// Draw board substrate:
|
|
if( bufferPcbOutlines.GetCornersCount() &&
|
|
( realistic_mode || g_Parm_3D_Visu.GetFlag( FL_SHOW_BOARD_BODY ) ) )
|
|
{
|
|
int copper_thickness = g_Parm_3D_Visu.GetCopperThicknessBIU();
|
|
// a small offset between substrate and external copper layer to avoid artifacts
|
|
// when drawing copper items on board
|
|
int epsilon = Millimeter2iu( 0.01 );
|
|
int zpos = g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_BACK );
|
|
int board_thickness = g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_FRONT )
|
|
- g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_BACK );
|
|
// items on copper layers and having a thickness = copper_thickness
|
|
// are drawn from zpos - copper_thickness/2 to zpos + copper_thickness
|
|
// therefore substrate position is copper_thickness/2 to
|
|
// substrate_height - copper_thickness/2
|
|
zpos += (copper_thickness + epsilon) / 2;
|
|
board_thickness -= copper_thickness + epsilon;
|
|
|
|
if( realistic_mode )
|
|
SetGLEpoxyColor();
|
|
else
|
|
{
|
|
EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( EDGE_N );
|
|
SetGLColor( color, 0.7 );
|
|
}
|
|
|
|
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( LAYER_N_FRONT ) );
|
|
KI_POLYGON_SET currLayerPolyset;
|
|
KI_POLYGON_SET polysetHoles;
|
|
|
|
// Add polygons, without holes
|
|
bufferPcbOutlines.ExportTo( currLayerPolyset );
|
|
|
|
// Build holes list
|
|
allLayerHoles.ExportTo( polysetHoles );
|
|
|
|
// remove holes
|
|
currLayerPolyset -= polysetHoles;
|
|
|
|
bufferPcbOutlines.RemoveAllContours();
|
|
bufferPcbOutlines.ImportFrom( currLayerPolyset );
|
|
|
|
// for Draw3D_SolidHorizontalPolyPolygons, zpos it the middle between bottom and top
|
|
// sides
|
|
Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness/2,
|
|
board_thickness, g_Parm_3D_Visu.m_BiuTo3Dunits );
|
|
}
|
|
|
|
// draw graphic items, not on copper layers
|
|
KI_POLYGON_SET brdpolysetHoles;
|
|
allLayerHoles.ExportTo( brdpolysetHoles );
|
|
|
|
for( LAYER_NUM layer = FIRST_NON_COPPER_LAYER; layer <= LAST_NON_COPPER_LAYER;
|
|
layer++ )
|
|
{
|
|
if( !Is3DLayerEnabled( layer ) )
|
|
continue;
|
|
|
|
if( layer == EDGE_N && g_Parm_3D_Visu.GetFlag( FL_SHOW_BOARD_BODY ) )
|
|
continue;
|
|
|
|
bufferPolys.RemoveAllContours();
|
|
|
|
for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
|
|
{
|
|
if( !item->IsOnLayer( layer ) )
|
|
continue;
|
|
|
|
switch( item->Type() )
|
|
{
|
|
case PCB_LINE_T:
|
|
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
|
|
bufferPolys, 0,
|
|
segcountforcircle,
|
|
correctionFactor );
|
|
break;
|
|
|
|
case PCB_TEXT_T:
|
|
( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
|
|
bufferPolys, 0, segcountforcircle, correctionFactor );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for( MODULE* module = pcb->m_Modules; module != NULL; module = module->Next() )
|
|
{
|
|
if( layer == SILKSCREEN_N_FRONT || layer == SILKSCREEN_N_BACK )
|
|
{
|
|
D_PAD* pad = module->Pads();
|
|
int linewidth = g_DrawDefaultLineThickness;
|
|
|
|
for( ; pad != NULL; pad = pad->Next() )
|
|
{
|
|
if( !pad->IsOnLayer( layer ) )
|
|
continue;
|
|
|
|
BuildPadShapeThickOutlineAsPolygon( pad, bufferPolys,
|
|
linewidth,
|
|
segcountforcircle, correctionFactor );
|
|
}
|
|
}
|
|
else
|
|
module->TransformPadsShapesWithClearanceToPolygon( layer,
|
|
bufferPolys,
|
|
0,
|
|
segcountforcircle,
|
|
correctionFactor );
|
|
|
|
module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
|
|
bufferPolys,
|
|
0,
|
|
segcountforcircle,
|
|
correctionFactor );
|
|
}
|
|
|
|
// bufferPolys contains polygons to merge. Many overlaps .
|
|
// Calculate merged polygons and remove pads and vias holes
|
|
if( bufferPolys.GetCornersCount() == 0 )
|
|
continue;
|
|
KI_POLYGON_SET currLayerPolyset;
|
|
KI_POLYGON_SET polyset;
|
|
|
|
// Solder mask layers are "negative" layers.
|
|
// Shapes should be removed from the full board area.
|
|
if( layer == SOLDERMASK_N_BACK || layer == SOLDERMASK_N_FRONT )
|
|
{
|
|
bufferPcbOutlines.ExportTo( currLayerPolyset );
|
|
bufferPolys.Append( allLayerHoles );
|
|
bufferPolys.ExportTo( polyset );
|
|
currLayerPolyset -= polyset;
|
|
}
|
|
// Remove holes from Solder paste layers and siklscreen
|
|
else if( layer == SOLDERPASTE_N_BACK || layer == SOLDERPASTE_N_FRONT
|
|
|| layer == SILKSCREEN_N_BACK || layer == SILKSCREEN_N_FRONT )
|
|
{
|
|
bufferPolys.ExportTo( currLayerPolyset );
|
|
currLayerPolyset -= brdpolysetHoles;
|
|
}
|
|
else // usuall layers, merge polys built from each item shape:
|
|
{
|
|
bufferPolys.ExportTo( polyset );
|
|
currLayerPolyset += polyset;
|
|
}
|
|
|
|
SetGLTechLayersColor( layer );
|
|
int thickness = g_Parm_3D_Visu.GetLayerObjectThicknessBIU( layer );
|
|
int zpos = g_Parm_3D_Visu.GetLayerZcoordBIU( layer );
|
|
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
|
|
|
|
if( layer == EDGE_N )
|
|
{
|
|
thickness = g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_FRONT )
|
|
- g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_BACK );
|
|
zpos = g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_BACK )
|
|
+ (thickness / 2);
|
|
}
|
|
else
|
|
{
|
|
// for Draw3D_SolidHorizontalPolyPolygons, zpos it the middle between bottom and top
|
|
// sides.
|
|
// However for top layers, zpos should be the bottom layer pos,
|
|
// and for bottom layers, zpos should be the top layer pos.
|
|
if( Get3DLayer_Z_Orientation( layer ) > 0 )
|
|
zpos += thickness/2;
|
|
else
|
|
zpos -= thickness/2 ;
|
|
}
|
|
|
|
bufferPolys.RemoveAllContours();
|
|
bufferPolys.ImportFrom( currLayerPolyset );
|
|
Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos,
|
|
thickness, g_Parm_3D_Visu.m_BiuTo3Dunits );
|
|
}
|
|
|
|
// draw modules 3D shapes
|
|
for( MODULE* module = pcb->m_Modules; module != NULL; module = module->Next() )
|
|
module->ReadAndInsert3DComponentShape( this );
|
|
}
|
|
|
|
|
|
GLuint EDA_3D_CANVAS::CreateDrawGL_List()
|
|
{
|
|
PCB_BASE_FRAME* pcbframe = Parent()->Parent();
|
|
BOARD* pcb = pcbframe->GetBoard();
|
|
|
|
wxBusyCursor dummy;
|
|
|
|
m_gllist = glGenLists( 1 );
|
|
|
|
// Build 3D board parameters:
|
|
g_Parm_3D_Visu.InitSettings( pcb );
|
|
|
|
glNewList( m_gllist, GL_COMPILE_AND_EXECUTE );
|
|
|
|
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
|
|
|
// draw axis
|
|
if( g_Parm_3D_Visu.GetFlag( FL_AXIS ) )
|
|
{
|
|
glEnable( GL_COLOR_MATERIAL );
|
|
SetGLColor( WHITE );
|
|
glBegin( GL_LINES );
|
|
glNormal3f( 0.0f, 0.0f, 1.0f ); // Normal is Z axis
|
|
glVertex3f( 0.0f, 0.0f, 0.0f );
|
|
glVertex3f( 1.0f, 0.0f, 0.0f ); // X axis
|
|
glVertex3f( 0.0f, 0.0f, 0.0f );
|
|
glVertex3f( 0.0f, -1.0f, 0.0f ); // Y axis
|
|
glNormal3f( 1.0f, 0.0f, 0.0f ); // Normal is Y axis
|
|
glVertex3f( 0.0f, 0.0f, 0.0f );
|
|
glVertex3f( 0.0f, 0.0f, 0.3f ); // Z axis
|
|
glEnd();
|
|
}
|
|
|
|
// move the board in order to draw it with its center at 0,0 3D coordinates
|
|
glTranslatef( -g_Parm_3D_Visu.m_BoardPos.x * g_Parm_3D_Visu.m_BiuTo3Dunits,
|
|
-g_Parm_3D_Visu.m_BoardPos.y * g_Parm_3D_Visu.m_BiuTo3Dunits,
|
|
0.0F );
|
|
|
|
// Draw Board:
|
|
// For testing purpose only, display calculation time to generate 3D data
|
|
// #define PRINT_CALCULATION_TIME
|
|
|
|
#ifdef PRINT_CALCULATION_TIME
|
|
unsigned strtime = GetRunningMicroSecs();
|
|
#endif
|
|
|
|
BuildBoard3DView();
|
|
|
|
// Draw grid
|
|
if( g_Parm_3D_Visu.GetFlag( FL_GRID ) )
|
|
DrawGrid( g_Parm_3D_Visu.m_3D_Grid );
|
|
|
|
glEndList();
|
|
|
|
// Test for errors
|
|
CheckGLError();
|
|
|
|
#ifdef PRINT_CALCULATION_TIME
|
|
unsigned endtime = GetRunningMicroSecs();
|
|
wxString msg;
|
|
msg.Printf( "Built data %.1f ms", (double) (endtime - strtime) / 1000 );
|
|
Parent()->SetStatusText( msg, 0 );
|
|
#endif
|
|
|
|
return m_gllist;
|
|
}
|
|
|
|
|
|
// draw a 3D grid: an horizontal grid (XY plane and Z = 0,
|
|
// and a vertical grid (XZ plane and Y = 0)
|
|
void EDA_3D_CANVAS::DrawGrid( double aGriSizeMM )
|
|
{
|
|
double zpos = 0.0;
|
|
EDA_COLOR_T gridcolor = DARKGRAY; // Color of grid lines
|
|
EDA_COLOR_T gridcolor_marker = LIGHTGRAY; // Color of grid lines every 5 lines
|
|
double scale = g_Parm_3D_Visu.m_BiuTo3Dunits;
|
|
double transparency = 0.4;
|
|
|
|
glNormal3f( 0.0, 0.0, 1.0 );
|
|
|
|
wxSize brd_size = g_Parm_3D_Visu.m_BoardSize;
|
|
wxPoint brd_center_pos = g_Parm_3D_Visu.m_BoardPos;
|
|
NEGATE( brd_center_pos.y );
|
|
|
|
int xsize = std::max( brd_size.x, Millimeter2iu( 100 ) );
|
|
int ysize = std::max( brd_size.y, Millimeter2iu( 100 ) );
|
|
|
|
// Grid limits, in 3D units
|
|
double xmin = (brd_center_pos.x - xsize / 2) * scale;
|
|
double xmax = (brd_center_pos.x + xsize / 2) * scale;
|
|
double ymin = (brd_center_pos.y - ysize / 2) * scale;
|
|
double ymax = (brd_center_pos.y + ysize / 2) * scale;
|
|
double zmin = Millimeter2iu( -50 ) * scale;
|
|
double zmax = Millimeter2iu( 100 ) * scale;
|
|
|
|
// Draw horizontal grid centered on 3D origin (center of the board)
|
|
for( int ii = 0; ; ii++ )
|
|
{
|
|
if( (ii % 5) )
|
|
SetGLColor( gridcolor, transparency );
|
|
else
|
|
SetGLColor( gridcolor_marker, transparency );
|
|
|
|
int delta = KiROUND( ii * aGriSizeMM * IU_PER_MM );
|
|
|
|
if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
|
|
{
|
|
glBegin( GL_LINES );
|
|
glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
|
|
glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
|
|
glEnd();
|
|
|
|
if( ii != 0 )
|
|
{
|
|
glBegin( GL_LINES );
|
|
glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
|
|
glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
|
|
{
|
|
glBegin( GL_LINES );
|
|
glVertex3f( xmin, -(brd_center_pos.y + delta) * scale, zpos );
|
|
glVertex3f( xmax, -(brd_center_pos.y + delta) * scale, zpos );
|
|
glEnd();
|
|
|
|
if( ii != 0 )
|
|
{
|
|
glBegin( GL_LINES );
|
|
glVertex3f( xmin, -(brd_center_pos.y - delta) * scale, zpos );
|
|
glVertex3f( xmax, -(brd_center_pos.y - delta) * scale, zpos );
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
|
|
break;
|
|
}
|
|
|
|
// Draw vertical grid n Z axis
|
|
glNormal3f( 0.0, -1.0, 0.0 );
|
|
|
|
// Draw vertical grid lines (parallel to Z axis)
|
|
for( int ii = 0; ; ii++ )
|
|
{
|
|
if( (ii % 5) )
|
|
SetGLColor( gridcolor, transparency );
|
|
else
|
|
SetGLColor( gridcolor_marker, transparency );
|
|
|
|
double delta = ii * aGriSizeMM * IU_PER_MM;
|
|
|
|
glBegin( GL_LINES );
|
|
glVertex3f( (brd_center_pos.x + delta) * scale, -brd_center_pos.y * scale, zmin );
|
|
glVertex3f( (brd_center_pos.x + delta) * scale, -brd_center_pos.y * scale, zmax );
|
|
glEnd();
|
|
|
|
if( ii != 0 )
|
|
{
|
|
glBegin( GL_LINES );
|
|
glVertex3f( (brd_center_pos.x - delta) * scale, -brd_center_pos.y * scale, zmin );
|
|
glVertex3f( (brd_center_pos.x - delta) * scale, -brd_center_pos.y * scale, zmax );
|
|
glEnd();
|
|
}
|
|
|
|
if( delta > xsize / 2 )
|
|
break;
|
|
}
|
|
|
|
// Draw horizontal grid lines on Z axis
|
|
for( int ii = 0; ; ii++ )
|
|
{
|
|
if( (ii % 5) )
|
|
SetGLColor( gridcolor, transparency);
|
|
else
|
|
SetGLColor( gridcolor_marker, transparency );
|
|
|
|
double delta = ii * aGriSizeMM * IU_PER_MM * scale;
|
|
|
|
if( delta <= zmax )
|
|
{
|
|
// Draw grid lines on Z axis (positive Z axis coordinates)
|
|
glBegin( GL_LINES );
|
|
glVertex3f( xmin, -brd_center_pos.y * scale, delta );
|
|
glVertex3f( xmax, -brd_center_pos.y * scale, delta );
|
|
glEnd();
|
|
}
|
|
|
|
if( delta <= -zmin && ( ii != 0 ) )
|
|
{
|
|
// Draw grid lines on Z axis (negative Z axis coordinates)
|
|
glBegin( GL_LINES );
|
|
glVertex3f( xmin, -brd_center_pos.y * scale, -delta );
|
|
glVertex3f( xmax, -brd_center_pos.y * scale, -delta );
|
|
glEnd();
|
|
}
|
|
|
|
if( ( delta > zmax ) && ( delta > -zmin ) )
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void EDA_3D_CANVAS::Draw3DViaHole( SEGVIA* aVia )
|
|
{
|
|
LAYER_NUM top_layer, bottom_layer;
|
|
int inner_radius = aVia->GetDrillValue() / 2;
|
|
int thickness = g_Parm_3D_Visu.GetCopperThicknessBIU();
|
|
|
|
aVia->ReturnLayerPair( &top_layer, &bottom_layer );
|
|
|
|
// Drawing via hole:
|
|
if( g_Parm_3D_Visu.IsRealisticMode() )
|
|
SetGLCopperColor();
|
|
else
|
|
{
|
|
EDA_COLOR_T color = g_ColorsSettings.GetItemColor( VIAS_VISIBLE + aVia->GetShape() );
|
|
SetGLColor( color );
|
|
}
|
|
|
|
int height = g_Parm_3D_Visu.GetLayerZcoordBIU( top_layer ) -
|
|
g_Parm_3D_Visu.GetLayerZcoordBIU( bottom_layer ) - thickness;
|
|
int zpos = g_Parm_3D_Visu.GetLayerZcoordBIU( bottom_layer ) + thickness / 2;
|
|
|
|
Draw3D_ZaxisCylinder( aVia->GetStart(), inner_radius + thickness / 2, height,
|
|
thickness, zpos, g_Parm_3D_Visu.m_BiuTo3Dunits );
|
|
}
|
|
|
|
|
|
void MODULE::ReadAndInsert3DComponentShape( EDA_3D_CANVAS* glcanvas )
|
|
{
|
|
// Draw module shape: 3D shape if exists (or module outlines if not exists)
|
|
S3D_MASTER* struct3D = m_3D_Drawings;
|
|
|
|
if( g_Parm_3D_Visu.GetFlag( FL_MODULE ) )
|
|
{
|
|
double zpos;
|
|
|
|
if( IsFlipped() )
|
|
zpos = g_Parm_3D_Visu.GetModulesZcoord3DIU( true );
|
|
else
|
|
zpos = g_Parm_3D_Visu.GetModulesZcoord3DIU( false );
|
|
|
|
glPushMatrix();
|
|
|
|
glTranslatef( m_Pos.x * g_Parm_3D_Visu.m_BiuTo3Dunits,
|
|
-m_Pos.y * g_Parm_3D_Visu.m_BiuTo3Dunits,
|
|
zpos );
|
|
|
|
if( m_Orient )
|
|
glRotatef( (double) m_Orient / 10, 0.0, 0.0, 1.0 );
|
|
|
|
if( IsFlipped() )
|
|
{
|
|
glRotatef( 180.0, 0.0, 1.0, 0.0 );
|
|
glRotatef( 180.0, 0.0, 0.0, 1.0 );
|
|
}
|
|
|
|
for( ; struct3D != NULL; struct3D = struct3D->Next() )
|
|
{
|
|
if( struct3D->Is3DType( S3D_MASTER::FILE3D_VRML ) )
|
|
struct3D->ReadData();
|
|
}
|
|
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
|
|
|
|
// Draw 3D pads.
|
|
void EDA_3D_CANVAS::Draw3DPadHole( D_PAD* aPad )
|
|
{
|
|
// Draw the pad hole
|
|
wxSize drillsize = aPad->GetDrillSize();
|
|
bool hasHole = drillsize.x && drillsize.y;
|
|
|
|
if( !hasHole )
|
|
return;
|
|
|
|
// Store here the points to approximate hole by segments
|
|
CPOLYGONS_LIST holecornersBuffer;
|
|
int thickness = g_Parm_3D_Visu.GetCopperThicknessBIU();
|
|
int height = g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_FRONT ) -
|
|
g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_BACK );
|
|
|
|
if( g_Parm_3D_Visu.IsRealisticMode() )
|
|
SetGLCopperColor();
|
|
else
|
|
SetGLColor( DARKGRAY );
|
|
|
|
int holeZpoz = g_Parm_3D_Visu.GetLayerZcoordBIU( LAYER_N_BACK ) + thickness / 2;
|
|
int holeHeight = height - thickness;
|
|
|
|
if( drillsize.x == drillsize.y ) // usual round hole
|
|
{
|
|
Draw3D_ZaxisCylinder( aPad->GetPosition(),
|
|
(drillsize.x + thickness) / 2, holeHeight,
|
|
thickness, holeZpoz, g_Parm_3D_Visu.m_BiuTo3Dunits );
|
|
}
|
|
else // Oblong hole
|
|
{
|
|
wxPoint ends_offset;
|
|
int width;
|
|
|
|
if( drillsize.x > drillsize.y ) // Horizontal oval
|
|
{
|
|
ends_offset.x = ( drillsize.x - drillsize.y ) / 2;
|
|
width = drillsize.y;
|
|
}
|
|
else // Vertical oval
|
|
{
|
|
ends_offset.y = ( drillsize.y - drillsize.x ) / 2;
|
|
width = drillsize.x;
|
|
}
|
|
|
|
RotatePoint( &ends_offset, aPad->GetOrientation() );
|
|
|
|
wxPoint start = aPad->GetPosition() + ends_offset;
|
|
wxPoint end = aPad->GetPosition() - ends_offset;
|
|
int hole_radius = ( width + thickness ) / 2;
|
|
|
|
// Draw the hole
|
|
Draw3D_ZaxisOblongCylinder( start, end, hole_radius, holeHeight,
|
|
thickness, holeZpoz, g_Parm_3D_Visu.m_BiuTo3Dunits );
|
|
}
|
|
}
|
|
|
|
|
|
bool Is3DLayerEnabled( LAYER_NUM aLayer )
|
|
{
|
|
DISPLAY3D_FLG flg;
|
|
bool realistic_mode = g_Parm_3D_Visu.IsRealisticMode();
|
|
|
|
// see if layer needs to be shown
|
|
// check the flags
|
|
switch( aLayer )
|
|
{
|
|
case ADHESIVE_N_BACK:
|
|
case ADHESIVE_N_FRONT:
|
|
flg = FL_ADHESIVE;
|
|
break;
|
|
|
|
case SOLDERPASTE_N_BACK:
|
|
case SOLDERPASTE_N_FRONT:
|
|
flg = FL_SOLDERPASTE;
|
|
break;
|
|
|
|
case SILKSCREEN_N_BACK:
|
|
case SILKSCREEN_N_FRONT:
|
|
flg = FL_SILKSCREEN;
|
|
break;
|
|
|
|
case SOLDERMASK_N_BACK:
|
|
case SOLDERMASK_N_FRONT:
|
|
flg = FL_SOLDERMASK;
|
|
break;
|
|
|
|
case DRAW_N:
|
|
case COMMENT_N:
|
|
if( realistic_mode )
|
|
return false;
|
|
|
|
flg = FL_COMMENTS;
|
|
break;
|
|
|
|
case ECO1_N:
|
|
case ECO2_N:
|
|
if( realistic_mode )
|
|
return false;
|
|
|
|
flg = FL_ECO;
|
|
break;
|
|
|
|
case LAYER_N_BACK:
|
|
case LAYER_N_FRONT:
|
|
return g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( aLayer )
|
|
|| realistic_mode;
|
|
break;
|
|
|
|
default:
|
|
// the layer is an internal copper layer
|
|
if( realistic_mode )
|
|
return false;
|
|
|
|
return g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( aLayer );
|
|
}
|
|
|
|
// if the layer has a flag, return the flag
|
|
return g_Parm_3D_Visu.GetFlag( flg ) &&
|
|
g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( aLayer );
|
|
}
|
|
|
|
|
|
GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer )
|
|
{
|
|
double nZ = 1.0;
|
|
|
|
if( ( aLayer == LAYER_N_BACK )
|
|
|| ( aLayer == ADHESIVE_N_BACK )
|
|
|| ( aLayer == SOLDERPASTE_N_BACK )
|
|
|| ( aLayer == SILKSCREEN_N_BACK )
|
|
|| ( aLayer == SOLDERMASK_N_BACK ) )
|
|
nZ = -1.0;
|
|
|
|
return nZ;
|
|
}
|