kicad/pcbnew/class_pad_draw_functions.cpp

603 lines
18 KiB
C++

/*******************************/
/* class_pad_draw_function.cpp */
/*******************************/
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "trigo.h"
#include "pcbnew_id.h" // ID_TRACK_BUTT
#include "class_drawpanel.h"
#include "drawtxt.h"
#include "pcbnew.h"
#include "class_board_design_settings.h"
#include "colors_selection.h"
/** Draw a pad:
* @param DC = device context
* @param offset = draw offset
* @param draw_mode = mode: GR_OR, GR_XOR, GR_AND...
*/
void D_PAD::Draw( WinEDA_DrawPanel* panel, wxDC* DC, int draw_mode,
const wxPoint& offset )
{
int ii;
int color = 0;
int ux0, uy0,
dx, dx0, dy, dy0,
rotdx,
delta_cx, delta_cy,
xc, yc;
int angle;
wxPoint coord[4];
int fillpad = 0;
wxPoint shape_pos;
wxSize mask_margin; // margin (clearance) used for some non copper layers
if( m_Flags & DO_NOT_DRAW )
return;
/* We can show/hide pads from the layer manager.
* options are show/hide pads on front and/or back side of the board
* For through pads, we hide them only if both sides are hidden.
* smd pads on back are hidden for all layers (copper and technical layers)
* on back side of the board
* smd pads on front are hidden for all layers (copper and technical layers)
* on front side of the board
* ECO, edge and Draw layers and not considered
*/
// Mask layers for Back side of board
#define BACK_SIDE_LAYERS \
(LAYER_BACK | ADHESIVE_LAYER_BACK | SOLDERPASTE_LAYER_BACK\
| SILKSCREEN_LAYER_BACK | SOLDERMASK_LAYER_BACK)
// Mask layers for Front side of board
#define FRONT_SIDE_LAYERS \
(LAYER_FRONT | ADHESIVE_LAYER_FRONT | SOLDERPASTE_LAYER_FRONT\
| SILKSCREEN_LAYER_FRONT | SOLDERMASK_LAYER_FRONT)
BOARD * brd = GetBoard( );
bool frontVisible = brd->IsElementVisible( PCB_VISIBLE(PAD_FR_VISIBLE) );
bool backVisible = brd->IsElementVisible( PCB_VISIBLE(PAD_BK_VISIBLE) );
if( !frontVisible && !backVisible )
return;
/* If pad are only on front side (no layer on back side)
* and if hide front side pads is enabled, do not draw
*/
if( !frontVisible && ( (m_Masque_Layer & BACK_SIDE_LAYERS) == 0 ) )
return;
/* If pad are only on back side (no layer on front side)
* and if hide back side pads is enabled, do not draw
*/
if( !backVisible && ( (m_Masque_Layer & FRONT_SIDE_LAYERS) == 0 ) )
return;
WinEDA_BasePcbFrame* frame = (WinEDA_BasePcbFrame*) panel->GetParent();
PCB_SCREEN* screen = frame->GetScreen();
if( frame->m_DisplayPadFill == FILLED )
fillpad = 1;
#if defined(PCBNEW) || defined(__WXMAC__)
if( m_Flags & IS_MOVED || !DisplayOpt.DisplayPadFill )
fillpad = 0;
#endif
if( m_Masque_Layer & LAYER_FRONT )
{
color = brd->GetVisibleElementColor(PAD_FR_VISIBLE);
}
if( m_Masque_Layer & LAYER_BACK )
{
color |= brd->GetVisibleElementColor(PAD_BK_VISIBLE);
}
if( color == 0 ) /* Not on copper layer */
{
// If the pad in on only one tech layer, use the layer color
// else use DARKGRAY
switch( m_Masque_Layer & ~ALL_CU_LAYERS )
{
case ADHESIVE_LAYER_BACK:
color = brd->GetLayerColor(ADHESIVE_N_BACK);
break;
case ADHESIVE_LAYER_FRONT:
color = brd->GetLayerColor(ADHESIVE_N_FRONT);
break;
case SOLDERPASTE_LAYER_BACK:
color = brd->GetLayerColor(SOLDERPASTE_N_BACK);
break;
case SOLDERPASTE_LAYER_FRONT:
color = brd->GetLayerColor(SOLDERPASTE_N_FRONT);
break;
case SILKSCREEN_LAYER_BACK:
color = brd->GetLayerColor(SILKSCREEN_N_BACK);
break;
case SILKSCREEN_LAYER_FRONT:
color = brd->GetLayerColor(SILKSCREEN_N_FRONT);
break;
case SOLDERMASK_LAYER_BACK:
color = brd->GetLayerColor(SOLDERMASK_N_BACK);
break;
case SOLDERMASK_LAYER_FRONT:
color = brd->GetLayerColor(SOLDERMASK_N_FRONT);
break;
case DRAW_LAYER:
color = brd->GetLayerColor(DRAW_N);
break;
case COMMENT_LAYER:
color = brd->GetLayerColor(COMMENT_N);
break;
case ECO1_LAYER:
color = brd->GetLayerColor(ECO1_N);
break;
case ECO2_LAYER:
color = brd->GetLayerColor(ECO2_N);
break;
case EDGE_LAYER:
color = brd->GetLayerColor(EDGE_N);
break;
default:
color = DARKGRAY;
break;
}
}
// if PAD_SMD pad and high contrast mode
if( ( m_Attribut == PAD_SMD || m_Attribut == PAD_CONN )
&& DisplayOpt.ContrastModeDisplay )
{
// when routing tracks
if( frame && frame->m_ID_current_state == ID_TRACK_BUTT )
{
int routeTop = screen->m_Route_Layer_TOP;
int routeBot = screen->m_Route_Layer_BOTTOM;
// if routing between copper and component layers,
// or the current layer is one of said 2 external copper layers,
// then highlight only the current layer.
if( ( ( 1 << routeTop ) | ( 1 << routeBot ) )
== ( LAYER_BACK | LAYER_FRONT )
|| ( ( 1 << screen->m_Active_Layer )
& ( LAYER_BACK | LAYER_FRONT ) ) )
{
if( !IsOnLayer( screen->m_Active_Layer ) )
{
color &= ~MASKCOLOR;
color |= DARKDARKGRAY;
}
}
// else routing between an internal signal layer and some other
// layer. Grey out all PAD_SMD pads not on current or the single
// selected external layer.
else if( !IsOnLayer( screen->m_Active_Layer )
&& !IsOnLayer( routeTop )
&& !IsOnLayer( routeBot ) )
{
color &= ~MASKCOLOR;
color |= DARKDARKGRAY;
}
}
// when not edting tracks, show PAD_SMD components not on active layer
// as greyed out
else
{
if( !IsOnLayer( screen->m_Active_Layer ) )
{
color &= ~MASKCOLOR;
color |= DARKDARKGRAY;
}
}
}
// if Contrast mode is ON and a technical layer active, show pads on this
// layer so we can see pads on paste or solder layer and the size of the
// mask
if( DisplayOpt.ContrastModeDisplay
&& screen->m_Active_Layer > LAST_COPPER_LAYER )
{
if( IsOnLayer( screen->m_Active_Layer ) )
{
color = brd->GetLayerColor(screen->m_Active_Layer);
// In hight contrast mode, and if the active layer is the mask
// layer shows the pad size with the mask clearance
switch( screen->m_Active_Layer )
{
case SOLDERMASK_N_BACK:
case SOLDERMASK_N_FRONT:
mask_margin.x = mask_margin.y = GetSolderMaskMargin();
break;
case SOLDERPASTE_N_BACK:
case SOLDERPASTE_N_FRONT:
mask_margin = GetSolderPasteMargin();
break;
default:
break;
}
}
else
color = DARKDARKGRAY;
}
if( draw_mode & GR_SURBRILL )
{
if( draw_mode & GR_AND )
color &= ~HIGHT_LIGHT_FLAG;
else
color |= HIGHT_LIGHT_FLAG;
}
if( color & HIGHT_LIGHT_FLAG )
color = ColorRefs[color & MASKCOLOR].m_LightColor;
GRSetDrawMode( DC, draw_mode ); /* mode de trace */
/* calcul du centre des pads en coordonnees Ecran : */
shape_pos = ReturnShapePos();
ux0 = shape_pos.x - offset.x;
uy0 = shape_pos.y - offset.y;
xc = ux0;
yc = uy0;
dx = dx0 = m_Size.x >> 1;
dy = dy0 = m_Size.y >> 1; /* demi dim dx et dy */
angle = m_Orient;
bool DisplayIsol = DisplayOpt.DisplayPadIsol;
if( ( m_Masque_Layer & ALL_CU_LAYERS ) == 0 )
DisplayIsol = FALSE;
SetAlpha( &color, 170 );
/* Get the pad clearance. This has a meaning only for Pcbnew.
* for Cvpcb (and Gerbview) GetClearance() creates debug errors because
* there is no net classes so a call to GetClearance() is made only when
* needed (never needed in Cvpcb nor in Gerbview)
*/
int padClearance = DisplayIsol ? GetClearance() : 0;
switch( GetShape() )
{
case PAD_CIRCLE:
if( fillpad )
GRFilledCircle( &panel->m_ClipBox, DC, xc, yc,
dx + mask_margin.x, 0, color, color );
else
GRCircle( &panel->m_ClipBox, DC, xc, yc, dx + mask_margin.x,
m_PadSketchModePenSize, color );
if( DisplayIsol )
{
GRCircle( &panel->m_ClipBox,
DC,
xc,
yc,
dx + padClearance,
0,
color );
}
break;
case PAD_OVAL:
if( dx > dy ) /* horizontal */
{
delta_cx = dx - dy;
delta_cy = 0;
rotdx = m_Size.y + ( mask_margin.y * 2 );
}
else /* vertical */
{
delta_cx = 0;
delta_cy = dy - dx;
rotdx = m_Size.x + ( mask_margin.x * 2 );
}
RotatePoint( &delta_cx, &delta_cy, angle );
if( fillpad )
{
GRFillCSegm( &panel->m_ClipBox, DC,
ux0 + delta_cx, uy0 + delta_cy,
ux0 - delta_cx, uy0 - delta_cy,
rotdx, color );
}
else
{
GRCSegm( &panel->m_ClipBox, DC,
ux0 + delta_cx, uy0 + delta_cy,
ux0 - delta_cx, uy0 - delta_cy,
rotdx, m_PadSketchModePenSize, color );
}
/* Draw the isolation line. */
if( DisplayIsol )
{
rotdx = rotdx + 2 * padClearance;
GRCSegm( &panel->m_ClipBox, DC, ux0 + delta_cx, uy0 + delta_cy,
ux0 - delta_cx, uy0 - delta_cy,
rotdx, color );
}
break;
case PAD_RECT:
case PAD_TRAPEZOID:
{
int ddx, ddy;
ddx = ( m_DeltaSize.x >> 1 );
ddy = ( m_DeltaSize.y >> 1 );
coord[0].x = -dx - ddy - mask_margin.x;
coord[0].y = +dy + ddx + mask_margin.y;
coord[1].x = -dx + ddy - mask_margin.x;
coord[1].y = -dy - ddx - mask_margin.y;
coord[2].x = +dx - ddy + mask_margin.x;
coord[2].y = -dy + ddx - mask_margin.y;
coord[3].x = +dx + ddy + mask_margin.x;
coord[3].y = +dy - ddx + mask_margin.y;
for( ii = 0; ii < 4; ii++ )
{
RotatePoint( &coord[ii].x, &coord[ii].y, angle );
coord[ii].x = coord[ii].x + ux0;
coord[ii].y = coord[ii].y + uy0;
}
GRClosedPoly( &panel->m_ClipBox, DC, 4, coord, fillpad,
fillpad ? 0 : m_PadSketchModePenSize, color, color );
if( DisplayIsol )
{
dx += padClearance;
dy += padClearance;
coord[0].x = -dx - ddy;
coord[0].y = dy + ddx;
coord[1].x = -dx + ddy;
coord[1].y = -dy - ddx;
coord[2].x = dx - ddy;
coord[2].y = -dy + ddx;
coord[3].x = dx + ddy;
coord[3].y = dy - ddx;
for( ii = 0; ii < 4; ii++ )
{
RotatePoint( &coord[ii].x, &coord[ii].y, angle );
coord[ii].x = coord[ii].x + ux0;
coord[ii].y = coord[ii].y + uy0;
}
GRClosedPoly( &panel->m_ClipBox, DC, 4, coord, 0, color, color );
}
}
break;
default:
break;
}
/* Draw the pad hole */
int cx0 = m_Pos.x - offset.x;
int cy0 = m_Pos.y - offset.y;
int hole = m_Drill.x >> 1;
if( fillpad && hole )
{
bool blackpenstate = false;
if( screen->m_IsPrinting )
{
blackpenstate = GetGRForceBlackPenState();
GRForceBlackPen( false );
color = g_DrawBgColor;
}
else
color = BLACK; // or DARKGRAY;
if( draw_mode != GR_XOR )
GRSetDrawMode( DC, GR_COPY );
else
GRSetDrawMode( DC, GR_XOR );
switch( m_DrillShape )
{
case PAD_CIRCLE:
#ifdef USE_WX_ZOOM
if( DC->LogicalToDeviceXRel( hole ) > 1 )
#else
if( screen->Scale( hole ) > 1 ) /* draw hole if its size is enough */
#endif
GRFilledCircle( &panel->m_ClipBox, DC, cx0, cy0, hole, 0,
color, color );
break;
case PAD_OVAL:
dx = m_Drill.x >> 1;
dy = m_Drill.y >> 1;
if( m_Drill.x > m_Drill.y ) /* horizontal */
{
delta_cx = dx - dy;
delta_cy = 0;
rotdx = m_Drill.y;
}
else /* vertical */
{
delta_cx = 0;
delta_cy = dy - dx;
rotdx = m_Drill.x;
}
RotatePoint( &delta_cx, &delta_cy, angle );
GRFillCSegm( &panel->m_ClipBox, DC, cx0 + delta_cx, cy0 + delta_cy,
cx0 - delta_cx, cy0 - delta_cy, rotdx, color );
break;
default:
break;
}
if( screen->m_IsPrinting )
GRForceBlackPen( blackpenstate );
}
GRSetDrawMode( DC, draw_mode );
/* Draw "No connect" ( / or \ or cross X ) if necessary. : */
if( m_Netname.IsEmpty() && brd->IsElementVisible( PCB_VISIBLE(NO_CONNECTS_VISIBLE) ) )
{
dx0 = MIN( dx0, dy0 );
int nc_color = BLUE;
if( m_Masque_Layer & LAYER_FRONT ) /* Draw \ */
GRLine( &panel->m_ClipBox, DC, cx0 - dx0, cy0 - dx0,
cx0 + dx0, cy0 + dx0, 0, nc_color );
if( m_Masque_Layer & LAYER_BACK ) /* Draw / */
GRLine( &panel->m_ClipBox, DC, cx0 + dx0, cy0 - dx0,
cx0 - dx0, cy0 + dx0, 0, nc_color );
}
/* Draw the pad number */
bool display_padnum = true;
if( frame && !frame->m_DisplayPadNum )
display_padnum = false;
bool display_netname = true;
if( ( DisplayOpt.DisplayNetNamesMode == 0 )
|| ( DisplayOpt.DisplayNetNamesMode == 2 ) )
display_netname = false;
if( !display_padnum && !display_netname )
return;
wxPoint tpos0 = wxPoint( ux0, uy0 ); // Position of the centre of text
wxPoint tpos = tpos0;
wxSize AreaSize; // size of text area, normalized to
// AreaSize.y < AreaSize.x
int shortname_len = m_ShortNetname.Len();
if( !display_netname )
shortname_len = 0;
if( GetShape() == PAD_CIRCLE )
angle = 0;
AreaSize = m_Size;
if( m_Size.y > m_Size.x )
{
angle += 900;
AreaSize.x = m_Size.y;
AreaSize.y = m_Size.x;
}
if( shortname_len > 0 ) // if there is a netname, provides room
// to display this netname
{
AreaSize.y /= 2; // Text used only the upper area of the
// pad. The lower area displays the net
// name
tpos.y -= AreaSize.y / 2;
}
// Calculate the position of text, that is the middle point of the upper
// area of the pad
RotatePoint( &tpos, wxPoint( ux0, uy0 ), angle );
/* Draw text with an angle between -90 deg and + 90 deg */
int t_angle = angle;
NORMALIZE_ANGLE_90( t_angle );
/* Note: in next calculations, texte size is calculated for 3 or more
* chars. Of course, pads numbers and nets names can have less than 3
* chars. but after some tries, i found this is gives the best look
*/
#define MIN_CHAR_COUNT 3
wxString buffer;
int tsize;
if( display_padnum )
{
ReturnStringPadName( buffer );
int numpad_len = buffer.Len();
numpad_len = MAX( numpad_len, MIN_CHAR_COUNT );
tsize = min( AreaSize.y, AreaSize.x / numpad_len );
#define CHAR_SIZE_MIN 5
#ifdef USE_WX_ZOOM
if( DC->LogicalToDeviceXRel( tsize ) >= CHAR_SIZE_MIN ) // Not drawable when size too small.
#else
if( screen->Scale( tsize ) >= CHAR_SIZE_MIN ) // Not drawable when size too small.
#endif
{
tsize = (int) ( tsize * 0.8 ); // reserve room for
// marges and segments
// thickness
DrawGraphicText( panel, DC, tpos, WHITE, buffer, t_angle,
wxSize( tsize, tsize ), GR_TEXT_HJUSTIFY_CENTER,
GR_TEXT_VJUSTIFY_CENTER, tsize / 7, false, false,
false );
}
}
// display the short netname, if exists
if( shortname_len == 0 )
return;
shortname_len = MAX( shortname_len, MIN_CHAR_COUNT );
tsize = min( AreaSize.y, AreaSize.x / shortname_len );
#ifdef USE_WX_ZOOM
if( DC->LogicalToDeviceXRel( tsize ) >= CHAR_SIZE_MIN ) // Not drawable in size too small.
#else
if( screen->Scale( tsize ) >= CHAR_SIZE_MIN ) // Not drawable in size too small.
#endif
{
if( !( !IsOnLayer( screen->m_Active_Layer )
&& DisplayOpt.ContrastModeDisplay ) )
{
tpos = tpos0;
if( display_padnum )
tpos.y += AreaSize.y / 2;
RotatePoint( &tpos, wxPoint( ux0, uy0 ), angle );
tsize = (int) ( tsize * 0.8 ); // reserve room for marges and
// segments thickness
DrawGraphicText( panel, DC, tpos, WHITE, m_ShortNetname, t_angle,
wxSize( tsize, tsize ), GR_TEXT_HJUSTIFY_CENTER,
GR_TEXT_VJUSTIFY_CENTER, tsize / 7, false, false );
}
}
}