kicad/3d-viewer/3d_draw.cpp

1858 lines
57 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 <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 <info3d_visu.h>
#include <trackball.h>
// angle increment to draw a circle, approximated by segments
#define ANGLE_INC(x) (3600/(x))
// Thickness of copper
// TODO: use the actual copper thickness to draw copper items with thickness
#define COPPER_THICKNESS (int)(0.035 * IU_PER_MM)
extern void CheckGLError();
/* draw a thick segment using 3D primitives, in a XY plane
* wxPoint aStart, wxPoint aEnd = YX position of ends in board units
* aWidth = width of segment in board units
* aThickness = thickness of segment in board units
* aZpos = z position of segment in board units
*/
static void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
int aWidth, int aThickness, int aZpos,
double aBiuTo3DUnits );
static void Draw3D_SegmentWithHole( double startx, double starty,
double endx, double endy,
double width, double holex,
double holey, double holeradius,
double zpos );
/* draw an arc using 3D primitives, in a plane parallel to the XY plane
* aCenterPos = 3D position of the center
* aStartPointX, aStartPointY = start point coordinate of arc (3D units)
* aWidth = width of the circle (3D units)
* aArcAngle = arc angle in 1/10 degrees
* aWidth = thickness of arc
*/
static void Draw3D_ArcSegment( const S3D_VERTEX& aCenterPos,
double aStartPointX, double aStartPointY,
double aArcAngle, double aWidth );
/* draw a circle with hole using 3D primitives, in a XY plane
* aCenterPos = 3D position of the center
* aRadius = radius of the circle (3D units)
* aHoleRadius = radius of the hole (3D units)
* Used to draw vias and round pads with round hole
*/
static void Draw3D_FilledCircleWithHole( const S3D_VERTEX& aPos, double aRadius, double aHoleRadius );
/* draw a circle using 3D primitives, in a plane parallel to the XY plane
* aCenterPos = 3D position of the center
* aRadius = radius of the circle (3D units)
* aWidth = width of the circle (3D units)
* Does the same think as Draw3D_FilledCircleWithHole, just does not use the same parmeters
* Used to draw circular segments
*/
static void Draw3D_ThickCircle( const S3D_VERTEX& aCenterPos, double aRadius, double aWidth );
/* returns true if aLayer should be displayed, false otherwise
*/
static bool Is3DLayerEnabled( int aLayer );
/* returns the Z orientation parmeter 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 parmeter for glNormal3f
*/
static GLfloat Get3DLayer_Z_Orientation( int aLayer );
/* draw a cylinder using 3D primitives.
* the cylinder axis is parallel to the Z axis
* aCenterPos = 3D position of the axis cylinder
* aRadius = radius of the cylinder (3D units)
* aHeight = height of the cylinder (3D units)
*/
static void Draw3D_ZaxisCylinder( const S3D_VERTEX& aPos, double aRadius, double aHeight );
/* draw an oblong cylinder (oblong hole) using 3D primitives.
* the cylinder axis are parallel to the Z axis
* aStartPos = 3D position of the first axis cylinder
* aEndx, aEndy = 3D position of the second axis cylinder
* aRadius = radius of the cylinder (3D units)
* aHeight = height of the cylinder (3D units)
*/
static void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aStartPos,
double aEndx, double aEndy,
double aRadius, double aHeight );
#ifndef CALLBACK
#define CALLBACK
#endif
// CALLBACK functions for GLU_TESS
static void CALLBACK tessBeginCB( GLenum which );
static void CALLBACK tessEndCB();
static void CALLBACK tessErrorCB( GLenum errorCode );
static void CALLBACK tessCPolyPt2Vertex( const GLvoid* data );
static void CALLBACK tesswxPoint2Vertex( const GLvoid* data );
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();
}
GLuint EDA_3D_CANVAS::CreateDrawGL_List()
{
PCB_BASE_FRAME* pcbframe = Parent()->Parent();
BOARD* pcb = pcbframe->GetBoard();
wxBusyCursor dummy;
m_gllist = glGenLists( 1 );
EDA_RECT bbbox = pcbframe->GetBoardBoundingBox();
g_Parm_3D_Visu.m_BoardSettings = &pcb->GetDesignSettings();
g_Parm_3D_Visu.m_BoardSize = bbbox.GetSize();
g_Parm_3D_Visu.m_BoardPos = bbbox.Centre();
g_Parm_3D_Visu.m_BoardPos.y = -g_Parm_3D_Visu.m_BoardPos.y;
g_Parm_3D_Visu.m_CopperLayersCount = pcb->GetCopperLayerCount();
// Ensure the board has 2 sides for 3D views, because it is hard to find
// a *really* single side board in the true life...
if( g_Parm_3D_Visu.m_CopperLayersCount < 2 )
g_Parm_3D_Visu.m_CopperLayersCount = 2;
g_Parm_3D_Visu.m_BoardScale = 2.0 / std::max( g_Parm_3D_Visu.m_BoardSize.x,
g_Parm_3D_Visu.m_BoardSize.y );
g_Parm_3D_Visu.m_EpoxyThickness = pcb->GetDesignSettings().GetBoardThickness()
* g_Parm_3D_Visu.m_BoardScale;
// Arbitrary choose a thickness for non copper layers:
g_Parm_3D_Visu.m_NonCopperLayerThickness = g_Parm_3D_Visu.m_EpoxyThickness / 20;
// Init Z position of each layer
// calculate z position for each copper layer
int layer;
int copper_layers_cnt = g_Parm_3D_Visu.m_CopperLayersCount;
for( layer = 0; layer < copper_layers_cnt; layer++ )
{
g_Parm_3D_Visu.m_LayerZcoord[layer] =
g_Parm_3D_Visu.m_EpoxyThickness * layer / (copper_layers_cnt - 1);
}
double zpos_copper_back = g_Parm_3D_Visu.m_LayerZcoord[0];
double zpos_copper_front = g_Parm_3D_Visu.m_EpoxyThickness;
// Fill remaining unused copper layers and front layer zpos
// with g_Parm_3D_Visu.m_EpoxyThickness
for( ; layer <= LAST_COPPER_LAYER; layer++ )
{
g_Parm_3D_Visu.m_LayerZcoord[layer] = g_Parm_3D_Visu.m_EpoxyThickness;
}
// calculate z position for each non copper layer
for( int layer_id = FIRST_NO_COPPER_LAYER; layer_id < NB_LAYERS; layer_id++ )
{
double zpos;
switch( layer_id )
{
case ADHESIVE_N_BACK:
zpos = zpos_copper_back -
4 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
case ADHESIVE_N_FRONT:
zpos = zpos_copper_front +
4 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
case SOLDERPASTE_N_BACK:
zpos = zpos_copper_back -
3 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
case SOLDERPASTE_N_FRONT:
zpos = zpos_copper_front +
3 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
case SOLDERMASK_N_BACK:
zpos = zpos_copper_back -
1 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
case SOLDERMASK_N_FRONT:
zpos = zpos_copper_front +
1 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
case SILKSCREEN_N_BACK:
zpos = zpos_copper_back -
2 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
case SILKSCREEN_N_FRONT:
zpos = zpos_copper_front +
2 * g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
default:
zpos = zpos_copper_front +
(layer_id - FIRST_NO_COPPER_LAYER + 5) *
g_Parm_3D_Visu.m_NonCopperLayerThickness;
break;
}
g_Parm_3D_Visu.m_LayerZcoord[layer_id] = zpos;
}
glNewList( m_gllist, GL_COMPILE_AND_EXECUTE );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
// draw axis
if (g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.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();
}
// Draw epoxy limits (do not use, works and test in progress)
// TODO
// 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_BoardScale,
-g_Parm_3D_Visu.m_BoardPos.y * g_Parm_3D_Visu.m_BoardScale,
0.0F );
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
// draw tracks and vias :
for( TRACK* track = pcb->m_Track; track != NULL; track = track->Next() )
{
if( track->Type() == PCB_VIA_T )
Draw3D_Via( (SEGVIA*) track );
else
{
int layer = track->GetLayer();
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) )
Draw3D_Track( track );
}
}
if (g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ZONE])
{
for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
{
int layer = pcb->GetArea( ii )->GetLayer();
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) )
Draw3D_Zone( pcb->GetArea( ii ) );
}
}
// draw graphic items
EDA_ITEM* PtStruct;
for( PtStruct = pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
{
switch( PtStruct->Type() )
{
case PCB_LINE_T:
{
DRAWSEGMENT* segment = (DRAWSEGMENT*) PtStruct;
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( segment->GetLayer() ) )
Draw3D_DrawSegment( segment );
}
break;
case PCB_TEXT_T:
{
TEXTE_PCB* text = (TEXTE_PCB*) PtStruct;
if( Is3DLayerEnabled( text->GetLayer() ) )
Draw3D_DrawText( text );
}
break;
default:
break;
}
}
// draw footprints
MODULE* Module = pcb->m_Modules;
for( ; Module != NULL; Module = Module->Next() )
Module->Draw3D( this );
// Draw grid
if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_GRID] )
DrawGrid( g_Parm_3D_Visu.m_3D_Grid );
glEndList();
// Test for errors
CheckGLError();
return m_gllist;
}
/* Draw a zone (solid copper areas in aZone)
*/
void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone )
{
int layer = aZone->GetLayer();
int color = g_ColorsSettings.GetLayerColor( layer );
if( layer == LAST_COPPER_LAYER )
layer = g_Parm_3D_Visu.m_CopperLayersCount - 1;
int zpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale );
SetGLColor( color );
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
if( aZone->m_FillMode == 0 )
{
// solid polygons only are used to fill areas
if( aZone->GetFilledPolysList().size() > 3 )
{
Draw3D_SolidPolygonsInZones( aZone );
}
}
else
{
// segments are used to fill areas
for( unsigned iseg = 0; iseg < aZone->m_FillSegmList.size(); iseg++ )
Draw3D_SolidSegment( aZone->m_FillSegmList[iseg].m_Start,
aZone->m_FillSegmList[iseg].m_End,
aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos,
g_Parm_3D_Visu.m_BoardScale );
}
// Draw copper area outlines
std::vector<CPolyPt> polysList = aZone->GetFilledPolysList();
if( polysList.size() == 0 )
return;
if( aZone->m_ZoneMinThickness <= 1 )
return;
int imax = polysList.size() - 1;
CPolyPt* firstcorner = &polysList[0];
CPolyPt* begincorner = firstcorner;
for( int ic = 1; ic <= imax; ic++ )
{
CPolyPt* endcorner = &polysList[ic];
if( begincorner->m_utility == 0 )
{
// Draw only basic outlines, not extra segments
wxPoint start( begincorner->x, begincorner->y );
wxPoint end( endcorner->x, endcorner->y );
Draw3D_SolidSegment( start, end,
aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos,
g_Parm_3D_Visu.m_BoardScale );
}
if( (endcorner->end_contour) || (ic == imax) )
{
// the last corner of a filled area is found: draw it
if( endcorner->m_utility == 0 )
{
// Draw only basic outlines, not extra segments
wxPoint start( endcorner->x, endcorner->y );
wxPoint end( firstcorner->x, firstcorner->y );
Draw3D_SolidSegment( start, end,
aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos,
g_Parm_3D_Visu.m_BoardScale );
}
ic++;
if( ic < imax - 1 )
begincorner = firstcorner = &polysList[ic];
}
else
{
begincorner = endcorner;
}
}
}
// draw mm grid..
void EDA_3D_CANVAS::DrawGrid( double aGriSizeMM )
{
double zpos = -g_Parm_3D_Visu.m_NonCopperLayerThickness/2;
int gridcolor = DARKGRAY; // Color of grid lines
int gridcolor_marker = LIGHTGRAY; // Color of grid lines every 5 lines
double scale = g_Parm_3D_Visu.m_BoardScale;
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 );
else
SetGLColor( gridcolor_marker );
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 );
else
SetGLColor( gridcolor_marker );
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 );
else
SetGLColor( gridcolor_marker );
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::Draw3D_Track( TRACK* aTrack )
{
int layer = aTrack->GetLayer();
int color = g_ColorsSettings.GetLayerColor( layer );
if( layer == LAST_COPPER_LAYER )
layer = g_Parm_3D_Visu.m_CopperLayersCount - 1;
int zpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale );
SetGLColor( color );
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
Draw3D_SolidSegment( aTrack->m_Start, aTrack->m_End,
aTrack->m_Width, COPPER_THICKNESS, zpos,
g_Parm_3D_Visu.m_BoardScale );
}
void EDA_3D_CANVAS::Draw3D_SolidPolygonsInZones( ZONE_CONTAINER* aZone )
{
double zpos;
int layer = aZone->GetLayer();
int color = g_ColorsSettings.GetLayerColor( layer );
if( layer == LAST_COPPER_LAYER )
layer = g_Parm_3D_Visu.m_CopperLayersCount - 1;
zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
g_Parm_3D_Visu.m_ActZpos = zpos;
SetGLColor( color );
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
GLUtesselator* tess = gluNewTess();
gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*)() )tessBeginCB );
gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*)() )tessEndCB );
gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*)() )tessErrorCB );
gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*)() )tessCPolyPt2Vertex );
GLdouble v_data[3];
v_data[2] = zpos;
//gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
// Draw solid areas contained in this zone
int StartContour = 1;
std::vector<CPolyPt> polysList = aZone->GetFilledPolysList();
for( unsigned ii = 0; ii < polysList.size(); ii++ )
{
if( StartContour == 1 )
{
gluTessBeginPolygon( tess, NULL );
gluTessBeginContour( tess );
StartContour = 0;
}
v_data[0] = polysList[ii].x * g_Parm_3D_Visu.m_BoardScale;
v_data[1] = -polysList[ii].y * g_Parm_3D_Visu.m_BoardScale;
gluTessVertex( tess, v_data, &polysList[ii] );
if( polysList[ii].end_contour == 1 )
{
gluTessEndContour( tess );
gluTessEndPolygon( tess );
StartContour = 1;
}
}
gluDeleteTess( tess );
}
void EDA_3D_CANVAS::Draw3D_Via( SEGVIA* via )
{
double x, y, r, hole;
int layer, top_layer, bottom_layer;
double zpos, height;
int color;
r = via->m_Width * g_Parm_3D_Visu.m_BoardScale / 2;
hole = via->GetDrillValue();
hole *= g_Parm_3D_Visu.m_BoardScale / 2;
x = via->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
y = via->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
via->ReturnLayerPair( &top_layer, &bottom_layer );
// Drawing filled circles:
for( layer = bottom_layer; layer < g_Parm_3D_Visu.m_CopperLayersCount; layer++ )
{
zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
if( layer < g_Parm_3D_Visu.m_CopperLayersCount - 1 )
{
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
continue;
color = g_ColorsSettings.GetLayerColor( layer );
}
else
{
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( LAYER_N_FRONT ) == false )
continue;
color = g_ColorsSettings.GetLayerColor( LAYER_N_FRONT );
}
SetGLColor( color );
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
if( layer == LAYER_N_BACK )
zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
else
zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
S3D_VERTEX pos( x, -y, zpos);
Draw3D_FilledCircleWithHole( pos, r, hole );
if( layer >= top_layer )
break;
}
// Drawing hole:
color = g_ColorsSettings.GetItemColor( VIAS_VISIBLE + via->m_Shape );
SetGLColor( color );
height = g_Parm_3D_Visu.m_LayerZcoord[top_layer] - g_Parm_3D_Visu.m_LayerZcoord[bottom_layer];
S3D_VERTEX position( x, -y, g_Parm_3D_Visu.m_LayerZcoord[bottom_layer] );
Draw3D_ZaxisCylinder( position, hole, height );
}
void EDA_3D_CANVAS::Draw3D_DrawSegment( DRAWSEGMENT* segment )
{
int layer = segment->GetLayer();
int color = g_ColorsSettings.GetLayerColor( layer );
SetGLColor( color );
double w = segment->GetWidth() * g_Parm_3D_Visu.m_BoardScale;
double x = segment->GetStart().x * g_Parm_3D_Visu.m_BoardScale;
double y = segment->GetStart().y * g_Parm_3D_Visu.m_BoardScale;
double xf = segment->GetEnd().x * g_Parm_3D_Visu.m_BoardScale;
double yf = segment->GetEnd().y * g_Parm_3D_Visu.m_BoardScale;
if( layer == EDGE_N )
{
for( layer = 0; layer < g_Parm_3D_Visu.m_CopperLayersCount; layer++ )
{
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
double zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
switch( segment->GetShape() )
{
case S_ARC:
{
S3D_VERTEX pos( xf, -yf, zpos );
Draw3D_ArcSegment( pos, x, -y, segment->GetAngle(), w );
}
break;
case S_CIRCLE:
{
S3D_VERTEX pos( x, -y, zpos );
Draw3D_ThickCircle( pos, hypot( x - xf, y - yf ), w );
}
break;
default:
Draw3D_SolidSegment( segment->GetStart(), segment->GetEnd(),
segment->GetWidth(), COPPER_THICKNESS,
KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] /
g_Parm_3D_Visu.m_BoardScale ),
g_Parm_3D_Visu.m_BoardScale );
break;
}
}
}
else
{
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
double zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
if( Is3DLayerEnabled( layer ) )
{
switch( segment->GetShape() )
{
case S_ARC:
{
S3D_VERTEX pos( xf, -yf, zpos );
Draw3D_ArcSegment( pos, x, -y, segment->GetAngle(), w );
}
break;
case S_CIRCLE:
{
S3D_VERTEX pos( x, -y, zpos );
Draw3D_ThickCircle( pos, hypot( x - xf, y - yf ), w );
}
break;
default:
Draw3D_SolidSegment( segment->GetStart(), segment->GetEnd(),
segment->GetWidth(), COPPER_THICKNESS,
KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] /
g_Parm_3D_Visu.m_BoardScale ),
g_Parm_3D_Visu.m_BoardScale );
break;
}
}
}
}
// These variables are used in Draw3dTextSegm.
// But Draw3dTextSegm is a call back function, so we cannot send them as arguments,
// so they are static.
int s_Text3DWidth, s_Text3DZPos;
// This is a call back function, used by DrawGraphicText to draw the 3D text shape:
static void Draw3dTextSegm( int x0, int y0, int xf, int yf )
{
Draw3D_SolidSegment( wxPoint( x0, y0), wxPoint( xf, yf ),
s_Text3DWidth, COPPER_THICKNESS, s_Text3DZPos,
g_Parm_3D_Visu.m_BoardScale );
}
void EDA_3D_CANVAS::Draw3D_DrawText( TEXTE_PCB* text )
{
int layer = text->GetLayer();
int color = g_ColorsSettings.GetLayerColor( layer );
SetGLColor( color );
s_Text3DZPos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale );
s_Text3DWidth = text->GetThickness();
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
wxSize size = text->m_Size;
if( text->m_Mirror )
NEGATE( size.x );
if( text->m_MultilineAllowed )
{
wxPoint pos = text->m_Pos;
wxArrayString* list = wxStringSplit( text->m_Text, '\n' );
wxPoint offset;
offset.y = text->GetInterline();
RotatePoint( &offset, text->GetOrientation() );
for( unsigned i = 0; i<list->Count(); i++ )
{
wxString txt = list->Item( i );
DrawGraphicText( NULL, NULL, pos, (EDA_COLOR_T) color,
txt, text->GetOrientation(), size,
text->m_HJustify, text->m_VJustify,
text->GetThickness(), text->m_Italic,
true, Draw3dTextSegm );
pos += offset;
}
delete list;
}
else
{
DrawGraphicText( NULL, NULL, text->m_Pos, (EDA_COLOR_T) color,
text->m_Text, text->GetOrientation(), size,
text->m_HJustify, text->m_VJustify,
text->GetThickness(), text->m_Italic,
true,
Draw3dTextSegm );
}
}
void MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
{
D_PAD* pad = m_Pads;
// Draw pads
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
for( ; pad != NULL; pad = pad->Next() )
{
pad->Draw3D( glcanvas );
}
// Draw module shape: 3D shape if exists (or module outlines if not exists)
S3D_MASTER* Struct3D = m_3D_Drawings;
bool As3dShape = false;
if (g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_MODULE])
{
glPushMatrix();
glTranslatef( m_Pos.x * g_Parm_3D_Visu.m_BoardScale,
-m_Pos.y * g_Parm_3D_Visu.m_BoardScale,
g_Parm_3D_Visu.m_LayerZcoord[m_Layer] );
if( m_Orient )
{
glRotatef( (double) m_Orient / 10, 0.0, 0.0, 1.0 );
}
if( m_Layer == LAYER_N_BACK )
{
glRotatef( 180.0, 0.0, 1.0, 0.0 );
glRotatef( 180.0, 0.0, 0.0, 1.0 );
}
DataScale3D = g_Parm_3D_Visu.m_BoardScale * UNITS3D_TO_UNITSPCB;
for( ; Struct3D != NULL; Struct3D = Struct3D->Next() )
{
if( !Struct3D->m_Shape3DName.IsEmpty() )
{
As3dShape = true;
Struct3D->ReadData();
}
}
glPopMatrix();
}
EDA_ITEM* Struct = m_Drawings;
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
for( ; Struct != NULL; Struct = Struct->Next() )
{
switch( Struct->Type() )
{
case PCB_MODULE_TEXT_T:
break;
case PCB_MODULE_EDGE_T:
{
EDGE_MODULE* edge = (EDGE_MODULE*) Struct;
// Draw module edges when no 3d shape exists.
// Always draw pcb edges.
if( !As3dShape || edge->GetLayer() == EDGE_N )
edge->Draw3D( glcanvas );
}
break;
default:
break;
}
}
}
void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
{
wxString s;
int dx, dy;
double x, y, fx, fy, w;
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( m_Layer ) == false )
return;
int color = g_ColorsSettings.GetLayerColor( m_Layer );
SetGLColor( color );
dx = m_End.x;
dy = m_End.y;
w = m_Width * g_Parm_3D_Visu.m_BoardScale;
x = m_Start.x * g_Parm_3D_Visu.m_BoardScale;
y = m_Start.y * g_Parm_3D_Visu.m_BoardScale;
fx = dx * g_Parm_3D_Visu.m_BoardScale;
fy = dy * g_Parm_3D_Visu.m_BoardScale;
if( m_Layer == EDGE_N )
{
for( int layer = 0; layer < g_Parm_3D_Visu.m_CopperLayersCount; layer++ )
{
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
int izpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale );
switch( m_Shape )
{
case S_SEGMENT:
Draw3D_SolidSegment( m_Start, m_End, m_Width,
COPPER_THICKNESS, izpos,
g_Parm_3D_Visu.m_BoardScale );
break;
case S_CIRCLE:
{
S3D_VERTEX pos( x, -y, g_Parm_3D_Visu.m_LayerZcoord[layer] );
Draw3D_ThickCircle( pos, hypot( x - fx, y - fy ), w);
}
break;
case S_ARC:
{
S3D_VERTEX pos( fx, -fy, g_Parm_3D_Visu.m_LayerZcoord[layer] );
Draw3D_ArcSegment( pos, x, -y, (double) m_Angle, w );
}
break;
case S_POLYGON:
{
// We must compute true coordinates from m_PolyPoints
// which are relative to module position and module orientation = 0
std::vector<wxPoint> points = m_PolyPoints;
MODULE* module = (MODULE*) m_Parent;
if( module == NULL )
break;
for( unsigned ii = 0; ii < points.size(); ii++ )
{
wxPoint& pt = points[ii];
RotatePoint( &pt.x, &pt.y, module->GetOrientation() );
pt += module->m_Pos;
}
glcanvas->Draw3D_Polygon( points, g_Parm_3D_Visu.m_LayerZcoord[layer] );
}
break;
default:
s.Printf( wxT( "Error: Shape nr %d not implemented!\n" ), m_Shape );
D( printf( "%s", TO_UTF8( s ) ); )
break;
}
}
}
else
{
glNormal3f( 0.0, 0.0, (m_Layer == LAYER_N_BACK) ? -1.0 : 1.0 );
int izpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[m_Layer] / g_Parm_3D_Visu.m_BoardScale );
switch( m_Shape )
{
case S_SEGMENT:
Draw3D_SolidSegment( m_Start, m_End, m_Width,
COPPER_THICKNESS, izpos,
g_Parm_3D_Visu.m_BoardScale );
break;
case S_CIRCLE:
{
S3D_VERTEX pos( x, -y, g_Parm_3D_Visu.m_LayerZcoord[m_Layer] );
Draw3D_ThickCircle( pos, hypot( x - fx, y - fy ), w );
}
break;
case S_ARC:
{
S3D_VERTEX pos( fx, -fy, g_Parm_3D_Visu.m_LayerZcoord[m_Layer] );
Draw3D_ArcSegment( pos, x, -y, (double) m_Angle, w );
}
break;
case S_POLYGON:
{
// We must compute true coordinates from m_PolyPoints
// which are relative to module position and module orientation = 0
std::vector<wxPoint> points = m_PolyPoints;
MODULE* module = (MODULE*) m_Parent;
if( module == NULL )
break;
for( unsigned ii = 0; ii < points.size(); ii++ )
{
wxPoint& pt = points[ii];
RotatePoint( &pt.x, &pt.y, module->GetOrientation() );
pt += module->m_Pos;
}
glcanvas->Draw3D_Polygon( points, g_Parm_3D_Visu.m_LayerZcoord[m_Layer] );
}
break;
default:
s.Printf( wxT( "Error: Shape nr %d not implemented!\n" ), m_Shape );
D( printf( "%s", TO_UTF8( s ) ); )
break;
}
}
}
// Draw 3D pads.
void D_PAD::Draw3D( EDA_3D_CANVAS* glcanvas )
{
int ii, ll, layer, nlmax;
int ux0, uy0,
dx, dx0, dy, dy0,
delta_cx, delta_cy,
xc, yc;
int angle;
double zpos;
double x, y, r, w;
bool Oncu, Oncmp, Both;
int color;
double scale = g_Parm_3D_Visu.m_BoardScale;
double holeX = (double) m_Drill.x * scale / 2;
double holeY = (double) m_Drill.y * scale / 2;
double hole = fmin( holeX, holeY );
// Calculate the center of the pad.
wxPoint shape_pos = ReturnShapePos();
ux0 = shape_pos.x;
uy0 = shape_pos.y;
xc = ux0;
yc = uy0;
dx = dx0 = m_Size.x >> 1;
dy = dy0 = m_Size.y >> 1;
angle = m_Orient;
double drillx = m_Pos.x * scale;
double drilly = m_Pos.y * scale;
double height = g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_FRONT] -
g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_BACK];
// Draw the pad hole
if( holeX && holeY )
{
SetGLColor( DARKGRAY );
S3D_VERTEX position( drillx, -drilly, g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_BACK] );
if( holeX == holeY ) // usual round hole
Draw3D_ZaxisCylinder( position, hole, height );
else // Oval hole
{
double ldx, ldy;
if( holeX > holeY ) // Horizontal oval
{
ldx = holeX - holeY;
ldy = 0;
w = m_Size.y * scale;
}
else // Vertical oval
{
ldx = 0;
ldy = holeY - holeX;
w = m_Size.x * scale;
}
RotatePoint( &ldx, &ldy, angle );
{
double ox = ux0 * scale + ldx;
double oy = uy0 * scale + ldy;
double fx = ux0 * scale - ldx;
double fy = uy0 * scale - ldy;
S3D_VERTEX position( ox, -oy, g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_BACK] );
Draw3D_ZaxisOblongCylinder( position, fx, -fy, hole, height );
}
}
}
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
nlmax = g_Parm_3D_Visu.m_CopperLayersCount - 1;
Oncu = (m_layerMask & LAYER_BACK) ? true : false;
Oncmp = (m_layerMask & LAYER_FRONT) ? true : false;
Both = Oncu && Oncmp;
switch( m_PadShape & 0x7F )
{
case PAD_CIRCLE:
x = xc * scale;
y = yc * scale;
r = (double) dx * scale;
for( layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; layer++ )
{
if( layer && (layer == nlmax) )
layer = LAYER_N_FRONT;
if( (layer == LAYER_N_FRONT) && !Oncmp )
continue;
if( (layer == LAYER_N_BACK) && !Oncu )
continue;
if( (layer > FIRST_COPPER_LAYER) && (layer < LAST_COPPER_LAYER) && !Both )
continue;
color = g_ColorsSettings.GetLayerColor( layer );
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
continue;
SetGLColor( color );
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
if( layer == LAYER_N_BACK )
zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
else
zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
S3D_VERTEX position( x, -y, zpos );
Draw3D_FilledCircleWithHole( position, r, hole );
}
break;
case PAD_OVAL:
if( dx > dy ) // Horizontal ellipse
{
delta_cx = dx - dy;
delta_cy = 0;
w = m_Size.y * scale;
}
else // Vertical ellipse
{
delta_cx = 0;
delta_cy = dy - dx;
w = m_Size.x * scale;
}
RotatePoint( &delta_cx, &delta_cy, angle );
{
double ox, oy, fx, fy;
ox = (double) ( ux0 + delta_cx ) * scale;
oy = (double) ( uy0 + delta_cy ) * scale;
fx = (double) ( ux0 - delta_cx ) * scale;
fy = (double) ( uy0 - delta_cy ) * scale;
for( layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; layer++ )
{
if( layer && (layer == nlmax) )
layer = LAYER_N_FRONT;
if( (layer == LAYER_N_FRONT) && !Oncmp )
continue;
if( (layer == LAYER_N_BACK) && !Oncu )
continue;
if( (layer > FIRST_COPPER_LAYER) && (layer < LAST_COPPER_LAYER) && !Both )
continue;
color = g_ColorsSettings.GetLayerColor( layer );
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
continue;
SetGLColor( color );
zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
if( layer == LAYER_N_BACK )
zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
else
zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
Draw3D_SegmentWithHole( ox, -oy, fx, -fy, w, drillx, -drilly, hole, zpos );
}
}
break;
case PAD_RECT:
case PAD_TRAPEZOID:
{
wxPoint coord[5];
wxRealPoint fcoord[8], f_hole_coord[8];
BuildPadPolygon( coord, wxSize(0,0), angle );
for( ii = 0; ii < 4; ii++ )
{
coord[ii].x += ux0;
coord[ii].y += uy0;
ll = ii * 2;
fcoord[ll].x = coord[ii].x *scale;
fcoord[ll].y = coord[ii].y *scale;
}
for( ii = 0; ii < 7; ii += 2 )
{
ll = ii + 2;
if( ll > 7 )
ll -= 8;
fcoord[ii + 1].x = (fcoord[ii].x + fcoord[ll].x) / 2;
fcoord[ii + 1].y = (fcoord[ii].y + fcoord[ll].y) / 2;
}
for( ii = 0; ii < 8; ii++ )
{
f_hole_coord[ii].x = -hole * 0.707;
f_hole_coord[ii].y = hole * 0.707;
RotatePoint( &f_hole_coord[ii].x, &f_hole_coord[ii].y, angle - (ii * 450) );
f_hole_coord[ii].x += drillx;
f_hole_coord[ii].y += drilly;
}
for( layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; layer++ )
{
if( layer && (layer == nlmax) )
layer = LAYER_N_FRONT;
if( (layer == LAYER_N_FRONT) && !Oncmp )
continue;
if( (layer == LAYER_N_BACK) && !Oncu )
continue;
if( (layer > FIRST_COPPER_LAYER) && (layer < LAST_COPPER_LAYER) && !Both )
continue;
color = g_ColorsSettings.GetLayerColor( layer );
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
continue;
SetGLColor( color );
zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
if( layer == LAYER_N_BACK )
zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
else
zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
glBegin( GL_QUAD_STRIP );
for( ii = 0; ii < 8; ii++ )
{
glVertex3f( f_hole_coord[ii].x, -f_hole_coord[ii].y, zpos );
glVertex3f( fcoord[ii].x, -fcoord[ii].y, zpos );
}
glVertex3f( f_hole_coord[0].x, -f_hole_coord[0].y, zpos );
glVertex3f( fcoord[0].x, -fcoord[0].y, zpos );
glEnd();
}
}
break;
default:
break;
}
}
void SetGLColor( int color )
{
double red, green, blue;
StructColors colordata = ColorRefs[color & MASKCOLOR];
red = colordata.m_Red / 255.0;
blue = colordata.m_Blue / 255.0;
green = colordata.m_Green / 255.0;
glColor3f( red, green, blue );
}
void Draw3D_FilledCircleWithHole( const S3D_VERTEX& aPos, double radius, double hole_radius )
{
const int slice = 16;
const int rot_angle = ANGLE_INC(slice);
glBegin( GL_QUAD_STRIP );
for( int ii = 0; ii <= slice; ii++ )
{
double x = hole_radius;
double y = 0.0;
RotatePoint( &x, &y, ii * rot_angle );
glVertex3f( x + aPos.x, y + aPos.y, aPos.z );
x = radius;
y = 0.0;
RotatePoint( &x, &y, ii * rot_angle );
glVertex3f( x + aPos.x, y + aPos.y, aPos.z );
}
glEnd();
}
static void Draw3D_ZaxisCylinder( const S3D_VERTEX& aPos, double aRadius, double aHeight )
{
const int slice = 12;
std::vector< S3D_VERTEX > coords;
coords.resize( 4 );
double tmp = DataScale3D;
DataScale3D = 1.0; // Coordinate is already in range for Set_Object_Data();
coords[0].x = coords[1].x = aPos.x + aRadius;
coords[0].y = coords[1].y = aPos.y;
coords[0].z = coords[3].z = aPos.z;
coords[1].z = coords[2].z = aPos.z + aHeight;
for( int ii = 0; ii <= slice; ii++ )
{
double x = aRadius;
double y = 0.0;
RotatePoint( &x, &y, ii * ANGLE_INC(slice) );
coords[2].x = coords[3].x = aPos.x + x;
coords[2].y = coords[3].y = aPos.y + y;
Set_Object_Data( coords );
coords[0].x = coords[2].x;
coords[0].y = coords[2].y;
coords[1].x = coords[3].x;
coords[1].y = coords[3].y;
}
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
DataScale3D = tmp;
}
/* draw a thick segment using 3D primitives, in a XY plane
* wxPoint aStart, wxPoint aEnd = YX position of end in board units
* aWidth = width of segment in board units
* aThickness = thickness of segment in board units
* aZpos = z position of segment in board units
*/
void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
int aWidth, int aThickness, int aZpos, double aBiuTo3DUnits )
{
std::vector <CPolyPt> cornerBuffer;
const int slice = 16;
TransformRoundedEndsSegmentToPolygon(cornerBuffer, aStart, aEnd, slice, aWidth );
//Build the 3D data : upper side then lower side
double zupperpos = ( aZpos + (aThickness/2) ) * aBiuTo3DUnits;
double zlowerpos = ( aZpos - (aThickness/2) ) * aBiuTo3DUnits;
if( aThickness )
glNormal3f( 0.0, 0.0, 1.0 ); // Normale is Z axis
double zpos = zupperpos; // start with upper side
for( int face = 0; face < 2; face ++ )
{
glBegin( GL_POLYGON );
for( unsigned ii = 0; ii < cornerBuffer.size(); ii++ )
glVertex3f( cornerBuffer[ii].x * aBiuTo3DUnits,
- cornerBuffer[ii].y * aBiuTo3DUnits, zpos );
// Close the polygon shape
glVertex3f( cornerBuffer[0].x * aBiuTo3DUnits,
- cornerBuffer[0].y * aBiuTo3DUnits, zpos );
glEnd();
if( aThickness == 0 )
return;
// Prepare the creation of lower side
glNormal3f( 0.0, 0.0, -1.0 ); // Normale now is -Z axis
zpos = zlowerpos;
}
//Build the 3D data : vertical sides
std::vector< S3D_VERTEX > vertices;
vertices.resize(4);
for( int ii = 0; ii < slice; ii++ )
{
int jj = ii+1;
if( jj >=slice )
jj = 0;
vertices[0].x = cornerBuffer[ii].x;
vertices[0].y = -cornerBuffer[ii].y;
vertices[0].z = aZpos + (aThickness/2);
vertices[1].x = cornerBuffer[ii].x;
vertices[1].y = -cornerBuffer[ii].y;
vertices[1].z = aZpos - (aThickness/2);
vertices[2].x = cornerBuffer[jj].x;
vertices[2].y = -cornerBuffer[jj].y;
vertices[2].z = aZpos - (aThickness/2);
vertices[3].x = cornerBuffer[jj].x;
vertices[3].y = -cornerBuffer[jj].y;
vertices[3].z = aZpos + (aThickness/2);
Set_Object_Data( vertices );
}
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
}
/* Draw a polygon similar to a segment ends with round hole
*/
static void Draw3D_SegmentWithHole( double startx, double starty,
double endx, double endy,
double width, double holex,
double holey, double holeradius,
double zpos )
{
double x, y, xin, yin;
double firstx = 0, firsty = 0, firstxin = 0, firstyin = 0;
int ii, angle, theta;
// Calculate the coordinates of the segment assumed horizontal
// Then turn the strips of the desired angle
// All calculations are done with startx, starty as local origin
endx -= startx;
endy -= starty;
holex -= startx;
holey -= starty;
angle = (int) ( ( atan2( endy, endx ) * 1800 / M_PI ) + 0.5 );
RotatePoint( &endx, &endy, angle );
RotatePoint( &holex, &holey, angle );
width /= 2;
glBegin( GL_QUAD_STRIP );
// Path of the flare to right (1st half polygon at the end of the segment)
// around the half-hole drilling
for( ii = 0; ii <= 8; ii++ )
{
x = 0.0;
y = -width;
xin = 0.0;
yin = -holeradius;
theta = -ii * 225;
RotatePoint( &x, &y, theta );
RotatePoint( &xin, &yin, theta );
x += endx;
RotatePoint( &x, &y, -angle );
xin += holex;
RotatePoint( &xin, &yin, -angle );
glVertex3f( startx + xin, starty + yin, zpos );
glVertex3f( startx + x, starty + y, zpos );
if( ii == 0 )
{
firstx = startx + x;
firsty = starty + y;
firstxin = startx + xin;
firstyin = starty + yin;
}
}
// Layout of the rounded left (2nd half polygon is the origin of the
// segment)
for( ii = 0; ii <= 8; ii++ )
{
theta = -ii * 225;
x = 0.0;
y = width;
RotatePoint( &x, &y, -angle + theta );
xin = 0.0;
yin = holeradius;
RotatePoint( &xin, &yin, theta );
xin += holex;
RotatePoint( &xin, &yin, -angle );
glVertex3f( startx + xin, starty + yin, zpos );
glVertex3f( startx + x, starty + y, zpos );
}
glVertex3f( firstxin, firstyin, zpos );
glVertex3f( firstx, firsty, zpos );
glEnd();
}
static void Draw3D_ArcSegment( const S3D_VERTEX& aCenterPos,
double aStartPointX, double aStartPointY,
double aArcAngle, double aWidth )
{
const int slice = 16; // Number of segments to approximate a circle by segments
double arcStart_Angle;
arcStart_Angle = (atan2( aStartPointX - aCenterPos.x, aStartPointY - aCenterPos.y ) * 1800 / M_PI );
double radius = hypot( aStartPointX - aCenterPos.x, aStartPointY - aCenterPos.y )
+ ( aWidth / 2);
double hole = radius - aWidth;
// Calculate the number of segments to approximate this arc
int imax = (int) ( (double) aArcAngle / ANGLE_INC(slice) );
if( imax < 0 )
imax = -imax;
if( imax == 0 )
imax = 1;
// Adjust delta_angle to have exactly imax segments in arc_angle
// i.e. arc_angle = imax delta_agnle.
double delta_angle = (double) aArcAngle / imax;
glBegin( GL_QUAD_STRIP );
for( int ii = 0; ii <= imax; ii++ )
{
double angle = (double) ii * delta_angle;
angle += arcStart_Angle + 900;
double dx = hole;
double dy = 0.0;
RotatePoint( &dx, &dy, (int) angle );
glVertex3f( dx + aStartPointX, dy + aStartPointY, aCenterPos.z );
dx = radius;
dy = 0.0;
RotatePoint( &dx, &dy, (int) angle );
glVertex3f( dx + aStartPointX, dy + aStartPointY, aCenterPos.z );
}
glEnd();
}
static void Draw3D_ThickCircle( const S3D_VERTEX& aCenterPos, double aRadius, double aWidth )
{
double outer_radius = aRadius + ( aWidth / 2);
double inner_radius = outer_radius - aWidth;
Draw3D_FilledCircleWithHole( aCenterPos, outer_radius, inner_radius );
}
/*
* Function Draw3D_ZaxisOblongCylinder:
* draw a segment with an oblong hole.
* Used to draw oblong holes
*/
void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aStartPos,
double aEndx, double aEndy,
double aRadius, double aHeight )
{
const int slice = 16;
std::vector<S3D_VERTEX> coords;
coords.resize( 4 );
double tmp = DataScale3D;
DataScale3D = 1.0; // Coordinate is already in range for Set_Object_Data();
double dx = aEndx - aStartPos.x;
double dy = aEndy - aStartPos.y;
double angle = atan2( dy, dx ) * 1800 / M_PI;
dx = 0;
dy = aRadius;
// draws the first rectangle between half cylinders
RotatePoint( &dx, &dy, angle );
coords[0].x = coords[1].x = aStartPos.x + dx;
coords[0].y = coords[1].y = aStartPos.y - dy;
coords[2].x = coords[3].x = aEndx + dx;
coords[2].y = coords[3].y = aEndy - dy;
coords[0].z = coords[3].z = aStartPos.z;
coords[1].z = coords[2].z = aStartPos.z + aHeight;
Set_Object_Data( coords );
// Draw the first half cylinder
for( int ii = 1; ii <= slice/2; ii++ )
{
double ddx = dx;
double ddy = -dy;
RotatePoint( &ddx, &ddy, -ii * ANGLE_INC(slice) );
coords[0].x = coords[2].x;
coords[0].y = coords[2].y;
coords[1].x = coords[3].x;
coords[1].y = coords[3].y;
coords[2].x = coords[3].x = aEndx + ddx;
coords[2].y = coords[3].y = aEndy + ddy;
Set_Object_Data( coords );
}
// draws the second rectangle between half cylinders
coords[0].x = coords[1].x = aEndx - dx;
coords[0].y = coords[1].y = aEndy + dy;
coords[2].x = coords[3].x = aStartPos.x - dx;
coords[2].y = coords[3].y = aStartPos.y + dy;
coords[0].z = coords[3].z = aStartPos.z;
coords[1].z = coords[2].z = aStartPos.z + aHeight;
Set_Object_Data( coords );
// Draw the second half cylinder
for( int ii = 1; ii <= slice/2; ii++ )
{
double ddx = -dx;
double ddy = dy;
RotatePoint( &ddx, &ddy, -ii * ANGLE_INC(slice) );
coords[0].x = coords[2].x;
coords[0].y = coords[2].y;
coords[1].x = coords[3].x;
coords[1].y = coords[3].y;
coords[2].x = coords[3].x = aStartPos.x + ddx;
coords[2].y = coords[3].y = aStartPos.y + ddy;
Set_Object_Data( coords );
}
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
DataScale3D = tmp;
}
void EDA_3D_CANVAS::Draw3D_Polygon( std::vector<wxPoint>& aCornersList, double aZpos )
{
g_Parm_3D_Visu.m_ActZpos = aZpos;
GLUtesselator* tess = gluNewTess();
gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*)() )tessBeginCB );
gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*)() )tessEndCB );
gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*)() )tessErrorCB );
gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*)() )tesswxPoint2Vertex );
GLdouble v_data[3];
v_data[2] = aZpos;
//gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
// Draw solid polygon
gluTessBeginPolygon( tess, NULL );
gluTessBeginContour( tess );
for( unsigned ii = 0; ii < aCornersList.size(); ii++ )
{
v_data[0] = aCornersList[ii].x * g_Parm_3D_Visu.m_BoardScale;
v_data[1] = -aCornersList[ii].y * g_Parm_3D_Visu.m_BoardScale;
// gluTessVertex store pointers on data, not data, so do not store
// different corners values in a temporary variable
// but send pointer on each corner value in aCornersList
gluTessVertex( tess, v_data, &aCornersList[ii] );
}
gluTessEndContour( tess );
gluTessEndPolygon( tess );
gluDeleteTess( tess );
}
bool Is3DLayerEnabled( int aLayer )
{
int flg = -1;
// see if layer needs to be shown
// check the flags
switch (aLayer)
{
case DRAW_N:
flg=g_Parm_3D_Visu.FL_DRAWINGS;
break;
case COMMENT_N:
flg=g_Parm_3D_Visu.FL_COMMENTS;
break;
case ECO1_N:
flg=g_Parm_3D_Visu.FL_ECO1;
break;
case ECO2_N:
flg=g_Parm_3D_Visu.FL_ECO2;
break;
}
// the layer was not a layer with a flag, so show it
if( flg < 0 )
return true;
// if the layer has a flag, return the flag
return g_Parm_3D_Visu.m_DrawFlags[flg];
}
GLfloat Get3DLayer_Z_Orientation( int aLayer )
{
double nZ;
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;
}
///////////////////////////////////////////////////////////////////////////////
// GLU_TESS CALLBACKS
///////////////////////////////////////////////////////////////////////////////
void CALLBACK tessBeginCB( GLenum which )
{
glBegin( which );
}
void CALLBACK tessEndCB()
{
glEnd();
}
void CALLBACK tessCPolyPt2Vertex( const GLvoid* data )
{
// cast back to double type
const CPolyPt* ptr = (const CPolyPt*) data;
glVertex3f( ptr->x * g_Parm_3D_Visu.m_BoardScale,
-ptr->y * g_Parm_3D_Visu.m_BoardScale,
g_Parm_3D_Visu.m_ActZpos );
}
void CALLBACK tesswxPoint2Vertex( const GLvoid* data )
{
const wxPoint* ptr = (const wxPoint*) data;
glVertex3f( ptr->x * g_Parm_3D_Visu.m_BoardScale,
-ptr->y * g_Parm_3D_Visu.m_BoardScale,
g_Parm_3D_Visu.m_ActZpos );
}
void CALLBACK tessErrorCB( GLenum errorCode )
{
#if defined(DEBUG)
const GLubyte* errorStr;
errorStr = gluErrorString( errorCode );
// DEBUG //
D( printf( "Tess ERROR: %s\n", errorStr ); )
#endif
}