kicad/3d-viewer/3d_draw.cpp

1261 lines
43 KiB
C++
Raw Normal View History

/*
* 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
*/
2007-05-06 16:03:28 +00:00
#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>
2007-05-06 16:03:28 +00:00
#include <3d_viewer.h>
#include <3d_canvas.h>
#include <info3d_visu.h>
#include <trackball.h>
#include <3d_draw_basic_functions.h>
2007-05-06 16:03:28 +00:00
// Imported function:
extern void CheckGLError();
/* Helper function
* 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
{
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(),
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,
2013-05-08 18:20:58 +00:00
corners.GetPos( jj ),
corners.GetPos( ii ),
aCircleToSegmentsCount, aWidth );
}
}
void EDA_3D_CANVAS::Redraw()
2007-05-06 16:03:28 +00:00
{
// 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();
2007-05-06 16:03:28 +00:00
glMatrixMode( GL_MODELVIEW ); // position viewer
// transformations
2007-05-06 16:03:28 +00:00
GLfloat mat[4][4];
// Translate motion first, so rotations don't mess up the orientation...
glTranslatef( m_draw3dOffset.x, m_draw3dOffset.y, 0.0F );
2007-05-06 16:03:28 +00:00
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 );
2007-05-06 16:03:28 +00:00
if( ! m_glLists[GL_ID_BOARD] || ! m_glLists[GL_ID_TECH_LAYERS] )
2008-07-21 13:44:01 +00:00
CreateDrawGL_List();
2007-05-06 16:03:28 +00:00
if( g_Parm_3D_Visu.GetFlag( FL_AXIS ) && m_glLists[GL_ID_AXIS] )
glCallList( m_glLists[GL_ID_AXIS] );
// 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 all objects in lists
// transparent objects should be drawn after opaque objects
glCallList( m_glLists[GL_ID_BOARD] );
glCallList( m_glLists[GL_ID_TECH_LAYERS] );
if( g_Parm_3D_Visu.GetFlag( FL_COMMENTS ) || g_Parm_3D_Visu.GetFlag( FL_COMMENTS ) )
{
if( ! m_glLists[GL_ID_AUX_LAYERS] )
CreateDrawGL_List();
glCallList( m_glLists[GL_ID_AUX_LAYERS] );
}
if( g_Parm_3D_Visu.GetFlag( FL_MODULE ) )
{
if( ! m_glLists[GL_ID_3DSHAPES_SOLID] )
CreateDrawGL_List();
glCallList( m_glLists[GL_ID_3DSHAPES_SOLID] );
}
// Grid uses transparency: draw it after all objects
if( g_Parm_3D_Visu.GetFlag( FL_GRID ) && m_glLists[GL_ID_GRID] )
glCallList( m_glLists[GL_ID_GRID] );
// This list must be drawn last, because it contains the
// transparent gl objects, which should be drawn after all
// non transparent objects
if( g_Parm_3D_Visu.GetFlag( FL_MODULE ) && m_glLists[GL_ID_3DSHAPES_TRANSP] )
glCallList( m_glLists[GL_ID_3DSHAPES_TRANSP] );
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()
2007-05-06 16:03:28 +00:00
{
BOARD* pcb = 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;
2008-07-21 13:44:01 +00:00
// 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 )
{
VIA *via = static_cast<VIA*>( track );
VIATYPE_T viatype = via->GetViaType();
int holediameter = via->GetDrillValue();
int thickness = g_Parm_3D_Visu.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 );
}
}
// 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();
2011-09-17 15:31:21 +00:00
if( zonelayer == layer )
zone->TransformSolidAreasShapesToPolygonSet(
hightQualityMode ? bufferPolys : bufferZonesPolys,
segcountLowQuality, correctionFactorLQ );
}
2009-11-16 13:18:20 +00:00
}
// draw graphic items on copper layers (texts)
for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
{
if( !item->IsOnLayer( layer ) )
continue;
switch( item->Type() )
{
case PCB_LINE_T: // should not exist on copper layers
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
bufferPolys, 0, segcountforcircle, correctionFactor );
break;
2011-09-17 15:31:21 +00:00
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
2013-05-08 18:20:58 +00:00
bufferPolys.ExportTo( currLayerPolyset );
// Add holes in polygon list
2013-05-08 18:20:58 +00:00
currLayerHoles.Append( allLayerHoles );
if( currLayerHoles.GetCornersCount() > 0 )
2013-05-08 18:20:58 +00:00
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();
2013-05-08 18:20:58 +00:00
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( const TRACK* track = pcb->m_Track; track != NULL; track = track->Next() )
{
const VIA *via = dynamic_cast<const VIA*>(track);
if( via )
Draw3DViaHole( via );
}
// Draw pads holes (vertical cylinders)
for( const 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 );
}
}
void EDA_3D_CANVAS::BuildTechLayers3DView()
{
BOARD* pcb = GetBoard();
// 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( 100000 ); // Reserve for large board
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
allLayerHoles.reserve( 20000 );
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
// 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 );
}
int thickness = g_Parm_3D_Visu.GetCopperThicknessBIU();
// Add via holes
for( VIA* via = GetFirstVia( pcb->m_Track ); via != NULL;
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 != NULL; module = module->Next() )
{
// Add pad hole, if any
D_PAD* pad = module->Pads();
for( ; pad != NULL; pad = pad->Next() )
pad->BuildPadDrillShapePolygon( allLayerHoles, 0,
segcountLowQuality );
}
// draw graphic items, on technical layers
KI_POLYGON_SET brdpolysetHoles;
allLayerHoles.ExportTo( brdpolysetHoles );
for( LAYER_NUM layer = FIRST_NON_COPPER_LAYER; layer <= LAST_NON_COPPER_LAYER;
++layer )
{
// Skip user layers, which are not drawn here
if( IsUserLayer( layer) )
continue;
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 );
}
// Draw non copper zones
if( g_Parm_3D_Visu.GetFlag( FL_ZONE ) )
{
for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
{
ZONE_CONTAINER* zone = pcb->GetArea( ii );
if( !zone->IsOnLayer( layer ) )
continue;
zone->TransformSolidAreasShapesToPolygonSet(
bufferPolys, segcountLowQuality, correctionFactorLQ );
}
}
// 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;
}
int thickness = g_Parm_3D_Visu.GetLayerObjectThicknessBIU( layer );
int zpos = g_Parm_3D_Visu.GetLayerZcoordBIU( layer );
2007-05-06 16:03:28 +00:00
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();
2013-05-08 18:20:58 +00:00
bufferPolys.ImportFrom( currLayerPolyset );
SetGLTechLayersColor( layer );
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos,
thickness, g_Parm_3D_Visu.m_BiuTo3Dunits );
}
2007-05-06 16:03:28 +00:00
}
/**
* Function BuildBoard3DAuxLayers
* Called by CreateDrawGL_List()
* Fills the OpenGL GL_ID_BOARD draw list with items
* on aux layers only
*/
void EDA_3D_CANVAS::BuildBoard3DAuxLayers()
{
const int segcountforcircle = 16;
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
BOARD* pcb = GetBoard();
CPOLYGONS_LIST bufferPolys;
bufferPolys.reserve( 5000 ); // Reserve for items not on board
for( LAYER_NUM layer = FIRST_USER_LAYER; layer <= LAST_USER_LAYER;
++layer )
{
if( !Is3DLayerEnabled( layer ) )
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() )
{
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;
bufferPolys.ExportTo( polyset );
currLayerPolyset += polyset;
int thickness = g_Parm_3D_Visu.GetLayerObjectThicknessBIU( layer );
int zpos = g_Parm_3D_Visu.GetLayerZcoordBIU( layer );
// 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 );
SetGLTechLayersColor( layer );
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos,
thickness, g_Parm_3D_Visu.m_BiuTo3Dunits );
}
2007-05-06 16:03:28 +00:00
}
void EDA_3D_CANVAS::CreateDrawGL_List()
{
BOARD* pcb = GetBoard();
wxBusyCursor dummy;
// Build 3D board parameters:
g_Parm_3D_Visu.InitSettings( pcb );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
// Create axis gl list (if it is not shown, the list will be not called
Draw3DAxis();
// Create grid gl list
if( ! m_glLists[GL_ID_GRID] )
{
m_glLists[GL_ID_GRID] = glGenLists( 1 );
glNewList( m_glLists[GL_ID_GRID], GL_COMPILE );
Draw3DGrid( g_Parm_3D_Visu.m_3D_Grid );
glEndList();
}
// Create Board full gl lists:
// For testing purpose only, display calculation time to generate 3D data
// #define PRINT_CALCULATION_TIME
#ifdef PRINT_CALCULATION_TIME
unsigned strtime = GetRunningMicroSecs();
#endif
if( ! m_glLists[GL_ID_BOARD] )
{
m_glLists[GL_ID_BOARD] = glGenLists( 1 );
glNewList( m_glLists[GL_ID_BOARD], GL_COMPILE );
BuildBoard3DView();
glEndList();
}
if( ! m_glLists[GL_ID_TECH_LAYERS] )
{
m_glLists[GL_ID_TECH_LAYERS] = glGenLists( 1 );
glNewList( m_glLists[GL_ID_TECH_LAYERS], GL_COMPILE );
BuildTechLayers3DView();
glEndList();
}
if( ! m_glLists[GL_ID_AUX_LAYERS] )
{
m_glLists[GL_ID_AUX_LAYERS] = glGenLists( 1 );
glNewList( m_glLists[GL_ID_AUX_LAYERS], GL_COMPILE );
BuildBoard3DAuxLayers();
glEndList();
}
// draw modules 3D shapes
if( ! m_glLists[GL_ID_3DSHAPES_SOLID] && g_Parm_3D_Visu.GetFlag( FL_MODULE ) )
{
m_glLists[GL_ID_3DSHAPES_SOLID] = glGenLists( 1 );
// GL_ID_3DSHAPES_TRANSP is an auxiliary list for 3D shapes;
// Ensure it is cleared before rebuilding it
if( m_glLists[GL_ID_3DSHAPES_TRANSP] )
glDeleteLists( m_glLists[GL_ID_3DSHAPES_TRANSP], 1 );
m_glLists[GL_ID_3DSHAPES_TRANSP] = glGenLists( 1 );
BuildFootprintShape3DList( m_glLists[GL_ID_3DSHAPES_SOLID],
m_glLists[GL_ID_3DSHAPES_TRANSP] );
}
// 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
}
void EDA_3D_CANVAS::BuildFootprintShape3DList( GLuint aOpaqueList,
GLuint aTransparentList)
{
// aOpaqueList is the gl list for non transparent items
// aTransparentList is the gl list for non transparent items,
// which need to be drawn after all other items
BOARD* pcb = GetBoard();
glNewList( aOpaqueList, GL_COMPILE );
bool loadTransparentObjects = false;
for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
module->ReadAndInsert3DComponentShape( this, !loadTransparentObjects,
loadTransparentObjects );
glEndList();
glNewList( aTransparentList, GL_COMPILE );
loadTransparentObjects = true;
for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
module->ReadAndInsert3DComponentShape( this, !loadTransparentObjects,
loadTransparentObjects );
glEndList();
}
void EDA_3D_CANVAS::Draw3DAxis()
{
if( ! m_glLists[GL_ID_AXIS] )
{
m_glLists[GL_ID_AXIS] = glGenLists( 1 );
glNewList( m_glLists[GL_ID_AXIS], GL_COMPILE );
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();
glEndList();
}
}
// 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::Draw3DGrid( 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
const double scale = g_Parm_3D_Visu.m_BiuTo3Dunits;
const double transparency = 0.3;
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( const VIA* aVia )
2007-05-06 16:03:28 +00:00
{
LAYER_NUM top_layer, bottom_layer;
int inner_radius = aVia->GetDrillValue() / 2;
int thickness = g_Parm_3D_Visu.GetCopperThicknessBIU();
2009-10-10 01:25:53 +00:00
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
aVia->LayerPair( &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->GetViaType() );
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 );
2007-05-06 16:03:28 +00:00
}
void MODULE::ReadAndInsert3DComponentShape( EDA_3D_CANVAS* glcanvas,
bool aAllowNonTransparentObjects,
bool aAllowTransparentObjects )
{
// Read from disk and draws the footprint 3D shapes if exists
S3D_MASTER* shape3D = m_3D_Drawings;
double zpos = g_Parm_3D_Visu.GetModulesZcoord3DIU( IsFlipped() );
glPushMatrix();
2012-08-27 11:41:22 +00:00
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( ; shape3D != NULL; shape3D = shape3D->Next() )
{
shape3D->SetLoadNonTransparentObjects( aAllowNonTransparentObjects );
shape3D->SetLoadTransparentObjects( aAllowTransparentObjects );
if( shape3D->Is3DType( S3D_MASTER::FILE3D_VRML ) )
shape3D->ReadData();
}
glPopMatrix();
2007-05-06 16:03:28 +00:00
}
// Draw 3D pads.
void EDA_3D_CANVAS::Draw3DPadHole( const D_PAD* aPad )
2007-05-06 16:03:28 +00:00
{
// 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;
}