KiCad PCB EDA Suite
pad_draw_functions.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
5  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
7  * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
31 #include <fctsys.h>
32 #include <gr_basic.h>
33 #include <common.h>
34 #include <trigo.h>
35 #include <pcb_screen.h>
36 #include <class_drawpanel.h>
37 #include <draw_graphic_text.h>
39 #include <pcb_edit_frame.h>
40 #include <pcbnew_id.h> // ID_TRACK_BUTT
41 #include <pcbnew.h>
42 #include <class_board.h>
44 
45 
46 
47 /* uncomment this line to show this pad with its specfic size and color
48  * when it is not on copper layers, and only one solder mask layer or solder paste layer
49  * is displayed for this pad
50  * After testing this feature,I am not sure this is a good idea
51  * but the code is left here.
52  */
53 
54 //#define SHOW_PADMASK_REAL_SIZE_AND_COLOR
55 
56 
57 // Helper class to store parameters used to draw a pad
59 {
60  m_DrawPanel = NULL;
62  m_Color = BLACK;
63  m_HoleColor = BLACK; // could be DARKGRAY;
66  m_PadClearance = 0;
67  m_Display_padnum = true;
68  m_Display_netname = true;
69  m_ShowPadFilled = true;
70  m_ShowNCMark = true;
71  m_ShowNotPlatedHole = false;
72  m_IsPrinting = false;
73 }
74 
75 
76 void D_PAD::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDraw_mode,
77  const wxPoint& aOffset )
78 {
79  wxSize mask_margin; // margin (clearance) used for some non copper layers
80 
81 #ifdef SHOW_PADMASK_REAL_SIZE_AND_COLOR
82  int showActualMaskSize = 0; /* Layer number if the actual pad size on mask layer can
83  * be displayed i.e. if only one layer is shown for this pad
84  * and this layer is a mask (solder mask or solder paste
85  */
86 #endif
87 
88  if( m_Flags & DO_NOT_DRAW )
89  return;
90 
91  PAD_DRAWINFO drawInfo;
92 
93  drawInfo.m_Offset = aOffset;
94 
95  /* We can show/hide pads from the layer manager.
96  * options are show/hide pads on front and/or back side of the board
97  * For through pads, we hide them only if both sides are hidden.
98  * smd pads on back are hidden for all layers (copper and technical layers)
99  * on back side of the board
100  * smd pads on front are hidden for all layers (copper and technical layers)
101  * on front side of the board
102  * ECO, edge and Draw layers and not considered
103  */
104 
105  BOARD* brd = GetBoard();
106 
107  auto frame = static_cast<PCB_EDIT_FRAME*> ( aPanel->GetParent() );
108  const auto& cds = frame->Settings().Colors();
109 
110  bool frontVisible = brd->IsElementVisible( LAYER_PAD_FR );
111  bool backVisible = brd->IsElementVisible( LAYER_PAD_BK );
112 
113  if( !frontVisible && !backVisible )
114  return;
115 
116  // If pad is only on front side (no layer on back side)
117  // and if hide front side pads is enabled, do not draw
118  if( !frontVisible && !( m_layerMask & LSET::BackMask() ).any() )
119  return;
120 
121  // If pad is only on back side (no layer on front side)
122  // and if hide back side pads is enabled, do not draw
123  if( !backVisible && !( m_layerMask & LSET::FrontMask() ).any() )
124  return;
125 
126 
127  wxCHECK_RET( frame != NULL, wxT( "Panel has no parent frame window." ) );
128 
129  auto displ_opts = (PCB_DISPLAY_OPTIONS*)( frame->GetDisplayOptions() );
130  PCB_SCREEN* screen = frame->GetScreen();
131 
132  if( displ_opts && displ_opts->m_DisplayPadFill == SKETCH )
133  drawInfo.m_ShowPadFilled = false;
134  else
135  drawInfo.m_ShowPadFilled = true;
136 
138 
139  if( m_layerMask[F_Cu] )
140  {
141  color = cds.GetItemColor( LAYER_PAD_FR );
142  }
143 
144  if( m_layerMask[B_Cu] )
145  {
146  color = color.LegacyMix( cds.GetItemColor( LAYER_PAD_BK ) );
147  }
148 
149  if( color == BLACK ) // Not on a visible copper layer (i.e. still nothing to show)
150  {
151  // If the pad is on only one tech layer, use the layer color else use DARKGRAY
152  LSET mask_non_copper_layers = m_layerMask & ~LSET::AllCuMask();
153 
154 #ifdef SHOW_PADMASK_REAL_SIZE_AND_COLOR
155  mask_non_copper_layers &= brd->GetVisibleLayers();
156 #endif
157  PCB_LAYER_ID pad_layer = mask_non_copper_layers.ExtractLayer();
158 
159  switch( (int) pad_layer )
160  {
161  case UNDEFINED_LAYER: // More than one layer
162  color = DARKGRAY;
163  break;
164 
165  case UNSELECTED_LAYER: // Shouldn't really happen...
166  break;
167 
168  default:
169  color = cds.GetLayerColor( pad_layer );
170 #ifdef SHOW_PADMASK_REAL_SIZE_AND_COLOR
171  showActualMaskSize = pad_layer;
172 #endif
173  }
174  }
175 
176  // if SMD or connector pad and high contrast mode
177  if( ( aDraw_mode & GR_ALLOW_HIGHCONTRAST ) &&
178  ( GetAttribute() == PAD_ATTRIB_SMD || GetAttribute() == PAD_ATTRIB_CONN ) &&
179  displ_opts && displ_opts->m_ContrastModeDisplay )
180  {
181  // when routing tracks
182  if( frame->GetToolId() == ID_TRACK_BUTT )
183  {
184  PCB_LAYER_ID routeTop = screen->m_Route_Layer_TOP;
185  PCB_LAYER_ID routeBot = screen->m_Route_Layer_BOTTOM;
186 
187  // if routing between copper and component layers,
188  // or the current layer is one of said 2 external copper layers,
189  // then highlight only the current layer.
190  if( ( screen->m_Active_Layer == F_Cu || screen->m_Active_Layer == B_Cu ) ||
191  ( routeTop==F_Cu && routeBot==B_Cu ) ||
192  ( routeTop==B_Cu && routeBot==F_Cu )
193  )
194  {
195  if( !IsOnLayer( screen->m_Active_Layer ) )
196  color = COLOR4D( DARKDARKGRAY );
197  }
198  // else routing between an internal signal layer and some other
199  // layer. Grey out all PAD_ATTRIB_SMD pads not on current or the single
200  // selected external layer.
201  else if( !IsOnLayer( screen->m_Active_Layer )
202  && !IsOnLayer( routeTop )
203  && !IsOnLayer( routeBot ) )
204  {
205  color = COLOR4D( DARKDARKGRAY );
206  }
207  }
208  // when not edting tracks, show PAD_ATTRIB_SMD components not on active layer
209  // as greyed out
210  else
211  {
212  if( !IsOnLayer( screen->m_Active_Layer ) )
213  color = COLOR4D( DARKDARKGRAY );
214  }
215  }
216 
217 #ifdef SHOW_PADMASK_REAL_SIZE_AND_COLOR
218  if( showActualMaskSize )
219  {
220  switch( showActualMaskSize )
221  {
222  case B_Mask:
223  case F_Mask:
224  mask_margin.x = mask_margin.y = GetSolderMaskMargin();
225  break;
226 
227  case B_Paste:
228  case F_Paste:
229  mask_margin = GetSolderPasteMargin();
230  break;
231 
232  default:
233  // Another layer which has no margin to handle
234  break;
235  }
236  }
237 #endif
238 
239  // if Contrast mode is ON and a technical layer active, show pads on this
240  // layer so we can see pads on paste or solder layer and the size of the
241  // mask
242  if( ( aDraw_mode & GR_ALLOW_HIGHCONTRAST ) &&
243  displ_opts && displ_opts->m_ContrastModeDisplay && !IsCopperLayer( screen->m_Active_Layer ) )
244  {
245  if( IsOnLayer( screen->m_Active_Layer ) )
246  {
247  color = cds.GetLayerColor( screen->m_Active_Layer );
248 
249  // In high contrast mode, and if the active layer is the mask
250  // layer shows the pad size with the mask clearance
251  switch( screen->m_Active_Layer )
252  {
253  case B_Mask:
254  case F_Mask:
255  mask_margin.x = mask_margin.y = GetSolderMaskMargin();
256  break;
257 
258  case B_Paste:
259  case F_Paste:
260  mask_margin = GetSolderPasteMargin();
261  break;
262 
263  default:
264  break;
265  }
266  }
267  else
268  color = DARKDARKGRAY;
269  }
270  // If use asks for masks to be printed, then print them.
271  else if( screen->m_IsPrinting )
272  {
273  if( ( IsOnLayer( B_Paste ) && brd->IsLayerVisible( B_Paste ) ) ||
274  ( IsOnLayer( F_Paste ) && brd->IsLayerVisible( F_Paste ) ) )
275  {
276  mask_margin = GetSolderPasteMargin();
277  }
278 
279  if( ( IsOnLayer( B_Mask ) && brd->IsLayerVisible( B_Mask ) ) ||
280  ( IsOnLayer( F_Mask ) && brd->IsLayerVisible( F_Mask ) ) )
281  {
282  mask_margin.x = std::max( mask_margin.x, GetSolderMaskMargin() );
283  mask_margin.y = std::max( mask_margin.y, GetSolderMaskMargin() );
284  }
285  }
286 
287  if( ( aDraw_mode & GR_HIGHLIGHT ) && !( aDraw_mode & GR_AND ) )
288  color.SetToLegacyHighlightColor();
289 
290  bool DisplayIsol = displ_opts && displ_opts->m_DisplayPadIsol;
291 
292  if( !( m_layerMask & LSET::AllCuMask() ).any() )
293  DisplayIsol = false;
294 
295  if( ( GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) &&
297  {
298  drawInfo.m_ShowNotPlatedHole = true;
299  drawInfo.m_NPHoleColor = cds.GetItemColor( LAYER_NON_PLATEDHOLES );
300  }
301  // Don't let pads that *should* be NPTHs get lost
302  else if ( PadShouldBeNPTH() )
303  {
304  drawInfo.m_ShowNotPlatedHole = true;
305  drawInfo.m_NPHoleColor = cds.GetItemColor( LAYER_MOD_TEXT_INVISIBLE );
306  }
307 
308  drawInfo.m_DrawMode = aDraw_mode;
309  drawInfo.m_Color = color;
310  drawInfo.m_NoNetMarkColor = cds.GetItemColor( LAYER_NO_CONNECTS );
311  drawInfo.m_DrawPanel = aPanel;
312  drawInfo.m_Mask_margin = mask_margin;
314  drawInfo.m_IsPrinting = screen->m_IsPrinting;
315  color.a = 0.666;
316 
317  /* Get the pad clearance. This has a meaning only for Pcbnew.
318  * for CvPcb GetClearance() creates debug errors because
319  * there is no net classes so a call to GetClearance() is made only when
320  * needed (never needed in CvPcb)
321  */
322  drawInfo.m_PadClearance = DisplayIsol ? GetClearance() : 0;
323 
324  // Draw the pad number
325  if( displ_opts && !displ_opts->m_DisplayPadNum )
326  drawInfo.m_Display_padnum = false;
327 
328  if( displ_opts &&
329  (( displ_opts ->m_DisplayNetNamesMode == 0 ) || ( displ_opts->m_DisplayNetNamesMode == 2 )) )
330  drawInfo.m_Display_netname = false;
331 
332  // Display net names is restricted to pads that are on the active layer
333  // in high contrast mode display
334  if( ( aDraw_mode & GR_ALLOW_HIGHCONTRAST ) &&
335  !IsOnLayer( screen->m_Active_Layer ) && displ_opts && displ_opts->m_ContrastModeDisplay )
336  drawInfo.m_Display_netname = false;
337 
338  DrawShape( aPanel->GetClipBox(), aDC, drawInfo );
339 }
340 
341 
342 void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
343 {
344  wxPoint coord[12];
345  double angle = m_Orient;
346  int seg_width;
347 
348  GRSetDrawMode( aDC, aDrawInfo.m_DrawMode );
349 
350  // calculate pad shape position :
351  wxPoint shape_pos = ShapePos() - aDrawInfo.m_Offset;
352 
353  wxSize halfsize = m_Size;
354  halfsize.x >>= 1;
355  halfsize.y >>= 1;
356 
357  switch( GetShape() )
358  {
359  case PAD_SHAPE_CIRCLE:
360  if( aDrawInfo.m_ShowPadFilled )
361  GRFilledCircle( aClipBox, aDC, shape_pos.x, shape_pos.y,
362  halfsize.x + aDrawInfo.m_Mask_margin.x, 0,
363  aDrawInfo.m_Color, aDrawInfo.m_Color );
364  else
365  GRCircle( aClipBox, aDC, shape_pos.x, shape_pos.y,
366  halfsize.x + aDrawInfo.m_Mask_margin.x,
367  m_PadSketchModePenSize, aDrawInfo.m_Color );
368 
369  if( aDrawInfo.m_PadClearance )
370  {
371  GRCircle( aClipBox,
372  aDC, shape_pos.x, shape_pos.y,
373  halfsize.x + aDrawInfo.m_PadClearance,
374  0, aDrawInfo.m_Color );
375  }
376 
377  break;
378 
379  case PAD_SHAPE_OVAL:
380  {
381  wxPoint segStart, segEnd;
382  seg_width = BuildSegmentFromOvalShape(segStart, segEnd, angle,
383  aDrawInfo.m_Mask_margin);
384  segStart += shape_pos;
385  segEnd += shape_pos;
386 
387  if( aDrawInfo.m_ShowPadFilled )
388  {
389  GRFillCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
390  seg_width, aDrawInfo.m_Color );
391  }
392  else
393  {
394  GRCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
395  seg_width, m_PadSketchModePenSize, aDrawInfo.m_Color );
396  }
397 
398  // Draw the clearance line
399  if( aDrawInfo.m_PadClearance )
400  {
401  seg_width += 2 * aDrawInfo.m_PadClearance;
402  GRCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
403  seg_width, aDrawInfo.m_Color );
404  }
405  }
406  break;
407 
408  case PAD_SHAPE_RECT:
409  case PAD_SHAPE_TRAPEZOID:
410  BuildPadPolygon( coord, aDrawInfo.m_Mask_margin, angle );
411 
412  for( int ii = 0; ii < 4; ii++ )
413  coord[ii] += shape_pos;
414 
415  GRClosedPoly( aClipBox, aDC, 4, coord, aDrawInfo.m_ShowPadFilled,
416  aDrawInfo.m_ShowPadFilled ? 0 : m_PadSketchModePenSize,
417  aDrawInfo.m_Color, aDrawInfo.m_Color );
418 
419  if( aDrawInfo.m_PadClearance )
420  {
421  #define SEGCOUNT 32 // number of segments to approximate a circle
422  SHAPE_POLY_SET outline;
423  TransformShapeWithClearanceToPolygon( outline, aDrawInfo.m_PadClearance, SEGCOUNT, 1.0 );
424 
425  // Draw the polygon: Inflate creates only one convex polygon
426  if( outline.OutlineCount() > 0 )
427  {
428  SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
429 
430  if( poly.PointCount() > 0 )
431  {
432  GRClosedPoly( aClipBox, aDC, poly.PointCount(),
433  (wxPoint*)&poly.Point( 0 ), false, 0,
434  aDrawInfo.m_Color, aDrawInfo.m_Color );
435  }
436  }
437  }
438  break;
439 
440  case PAD_SHAPE_ROUNDRECT:
441  {
442  // Use solder[Paste/Mask]size or pad size to build pad shape to draw
443  wxSize size( GetSize() );
444  size += aDrawInfo.m_Mask_margin * 2;
445  int corner_radius = GetRoundRectCornerRadius( size );
446 
447  // Draw the polygon: Inflate creates only one convex polygon
448  SHAPE_POLY_SET outline;
449  bool filled = aDrawInfo.m_ShowPadFilled;
450 
451  if( filled )
452  {
453  wxPoint centers[4];
454  GetRoundRectCornerCenters( centers, corner_radius, shape_pos,
455  size, GetOrientation() );
456  GRClosedPoly( aClipBox, aDC, 4, centers, true, corner_radius*2,
457  aDrawInfo.m_Color, aDrawInfo.m_Color );
458  }
459  else
460  {
461  TransformRoundRectToPolygon( outline, shape_pos, size, GetOrientation(),
462  corner_radius, 64 );
463 
464  if( outline.OutlineCount() > 0 )
465  {
466  SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
467 
468  if( poly.PointCount() > 0 )
469  {
470  GRClosedPoly( aClipBox, aDC, poly.PointCount(),
471  (wxPoint*)&poly.Point( 0 ), aDrawInfo.m_ShowPadFilled, 0,
472  aDrawInfo.m_Color, aDrawInfo.m_Color );
473  }
474  }
475  }
476 
477  if( aDrawInfo.m_PadClearance )
478  {
479  outline.RemoveAllContours();
480  size = GetSize();
481  size.x += aDrawInfo.m_PadClearance * 2;
482  size.y += aDrawInfo.m_PadClearance * 2;
483  corner_radius = GetRoundRectCornerRadius() + aDrawInfo.m_PadClearance;
484 
485  TransformRoundRectToPolygon( outline, shape_pos, size, GetOrientation(),
486  corner_radius, 32 );
487 
488  if( outline.OutlineCount() > 0 )
489  {
490  // Draw the polygon: Inflate creates only one convex polygon
491  SHAPE_LINE_CHAIN& clearance_poly = outline.Outline( 0 );
492 
493  if( clearance_poly.PointCount() > 0 )
494  {
495  GRClosedPoly( aClipBox, aDC, clearance_poly.PointCount(),
496  (wxPoint*)&clearance_poly.Point( 0 ), false, 0,
497  aDrawInfo.m_Color, aDrawInfo.m_Color );
498  }
499  }
500  }
501  }
502  break;
503 
504  case PAD_SHAPE_CUSTOM:
505  {
506  // The full shape has 2 items
507  // 1- The anchor pad: a round or rect pad located at pad position
508  // 2- The custom complex shape
509  // Note: The anchor pad shape is containing by the custom complex shape polygon
510  // The anchor pad is shown to help user to see where is the anchor, only in sketch mode
511  // (In filled mode, it is merged with the basic shapes)
512  wxPoint pad_pos = GetPosition() - aDrawInfo.m_Offset;
513 
514  // In sketch mode only: Draw the anchor pad: a round or rect pad
515  if( !aDrawInfo.m_ShowPadFilled )
516  {
517  if( GetAnchorPadShape() == PAD_SHAPE_RECT )
518  {
519  wxPoint poly[4];
520  poly[0] = wxPoint( - halfsize.x, - halfsize.y );
521  poly[1] = wxPoint( - halfsize.x, + halfsize.y );
522  poly[2] = wxPoint( + halfsize.x, + halfsize.y );
523  poly[3] = wxPoint( + halfsize.x, - halfsize.y );
524 
525  for( int ii = 0; ii < 4; ++ii )
526  {
527  RotatePoint( &poly[ii], m_Orient );
528  poly[ii] += pad_pos;
529  }
530 
531  GRClosedPoly( aClipBox, aDC, 4, poly, false, 0,
532  aDrawInfo.m_Color, aDrawInfo.m_Color );
533  }
534  else
535  {
536  GRCircle( aClipBox, aDC, pad_pos.x, pad_pos.y,
537  halfsize.x,
538  m_PadSketchModePenSize, aDrawInfo.m_Color );
539  }
540  }
541 
542  SHAPE_POLY_SET outline; // Will contain the corners in board coordinates
543  outline.Append( m_customShapeAsPolygon );
544  CustomShapeAsPolygonToBoardPosition( &outline, pad_pos, GetOrientation() );
545  SHAPE_LINE_CHAIN* poly;
546 
547  const int segmentToCircleCount = 32;
548 
549  if( aDrawInfo.m_Mask_margin.x )
550  {
551  SHAPE_POLY_SET clearance_outline;
552  clearance_outline.Append( outline );
553  clearance_outline.Inflate( aDrawInfo.m_Mask_margin.x, segmentToCircleCount );
554  }
555  else
556  {
557  // Draw the polygon: only one polygon is expected
558  // However we provide a multi polygon shape drawing
559  // ( for the future or to show a non expected shape )
560  for( int jj = 0; jj < outline.OutlineCount(); ++jj )
561  {
562  poly = &outline.Outline( jj );
563 
564  GRClosedPoly( aClipBox, aDC, poly->PointCount(),
565  (wxPoint*)&poly->Point( 0 ), aDrawInfo.m_ShowPadFilled, 0,
566  aDrawInfo.m_Color, aDrawInfo.m_Color );
567  }
568  }
569 
570  if( aDrawInfo.m_PadClearance )
571  {
572  SHAPE_POLY_SET clearance_outline;
573  clearance_outline.Append( outline );
574 
575  clearance_outline.Inflate( aDrawInfo.m_PadClearance, segmentToCircleCount );
576 
577  for( int jj = 0; jj < clearance_outline.OutlineCount(); ++jj )
578  {
579  poly = &clearance_outline.Outline( jj );
580 
581  if( poly->PointCount() > 0 )
582  {
583  GRClosedPoly( aClipBox, aDC, poly->PointCount(),
584  (wxPoint*)&poly->Point( 0 ), false, 0,
585  aDrawInfo.m_Color, aDrawInfo.m_Color );
586  }
587  }
588  }
589  break;
590  }
591 
592  default:
593  break;
594  }
595 
596  // Draw the pad hole
597  wxPoint holepos = m_Pos - aDrawInfo.m_Offset;
598  int hole = m_Drill.x >> 1;
599 
600  bool drawhole = hole > 0;
601 
602  if( !aDrawInfo.m_ShowPadFilled && !aDrawInfo.m_ShowNotPlatedHole )
603  drawhole = false;
604 
605  if( drawhole )
606  {
607  bool blackpenstate = false;
608  COLOR4D fillcolor = aDrawInfo.m_ShowNotPlatedHole? aDrawInfo.m_NPHoleColor :
609  aDrawInfo.m_HoleColor;
610  COLOR4D hole_color = fillcolor;
611 
612  if( aDrawInfo.m_IsPrinting )
613  {
614  fillcolor = COLOR4D::WHITE;
615  blackpenstate = GetGRForceBlackPenState();
616  GRForceBlackPen( false );
617  }
618  else
619  {
620  GRSetDrawMode( aDC, ( aDrawInfo.m_DrawMode != GR_XOR ) ? GR_COPY : GR_XOR );
621  }
622 
623  if( blackpenstate )
624  hole_color = COLOR4D::BLACK;
625 
626  switch( GetDrillShape() )
627  {
629  if( aDC->LogicalToDeviceXRel( hole ) > 1 ) // hole is drawn if hole > 1pixel
630  GRFilledCircle( aClipBox, aDC, holepos.x, holepos.y, hole, 0,
631  hole_color, fillcolor );
632  break;
633 
635  {
636  wxPoint drl_start, drl_end;
637  GetOblongDrillGeometry( drl_start, drl_end, seg_width );
638  drl_start += holepos;
639  drl_end += holepos;
640  GRFilledSegment( aClipBox, aDC, drl_start, drl_end, seg_width, fillcolor );
641  GRCSegm( aClipBox, aDC, drl_start, drl_end, seg_width, hole_color );
642  }
643  break;
644 
645  default:
646  break;
647  }
648 
649  if( aDrawInfo.m_IsPrinting )
650  GRForceBlackPen( blackpenstate );
651  }
652 
653  GRSetDrawMode( aDC, aDrawInfo.m_DrawMode );
654 
655  // Draw "No connect" ( / or \ or cross X ) if necessary
656  if( GetNetCode() == 0 && aDrawInfo.m_ShowNCMark )
657  {
658  int dx0 = std::min( halfsize.x, halfsize.y );
659 
660  if( m_layerMask[F_Cu] ) /* Draw \ */
661  GRLine( aClipBox, aDC, holepos.x - dx0, holepos.y - dx0,
662  holepos.x + dx0, holepos.y + dx0, 0, aDrawInfo.m_NoNetMarkColor );
663 
664  if( m_layerMask[B_Cu] ) // Draw /
665  GRLine( aClipBox, aDC, holepos.x + dx0, holepos.y - dx0,
666  holepos.x - dx0, holepos.y + dx0, 0, aDrawInfo.m_NoNetMarkColor );
667  }
668 
669  if( !aDrawInfo.m_IsPrinting )
670  GRSetDrawMode( aDC, ( aDrawInfo.m_DrawMode != GR_XOR ) ? GR_COPY : GR_XOR );
671 
672  // Draw the pad number
673  if( !aDrawInfo.m_Display_padnum && !aDrawInfo.m_Display_netname )
674  return;
675 
676  wxPoint tpos0 = shape_pos; // Position of the centre of text
677  wxPoint tpos = tpos0;
678  wxSize AreaSize; // size of text area, normalized to AreaSize.y < AreaSize.x
679  int shortname_len = 0;
680 
681  if( aDrawInfo.m_Display_netname )
682  shortname_len = GetShortNetname().Len();
683 
684  if( GetShape() == PAD_SHAPE_CIRCLE )
685  angle = 0;
686 
687  AreaSize = m_Size;
688 
689  if( m_Size.y > m_Size.x )
690  {
691  angle += 900;
692  AreaSize.x = m_Size.y;
693  AreaSize.y = m_Size.x;
694  }
695 
696  if( shortname_len > 0 ) // if there is a netname, provides room to display this netname
697  {
698  AreaSize.y /= 2; // Text used only the upper area of the
699  // pad. The lower area displays the net name
700  tpos.y -= AreaSize.y / 2;
701  }
702 
703  // Calculate the position of text, that is the middle point of the upper
704  // area of the pad
705  RotatePoint( &tpos, shape_pos, angle );
706 
707  // Draw text with an angle between -90 deg and + 90 deg
708  double t_angle = angle;
709  NORMALIZE_ANGLE_90( t_angle );
710 
711  /* Note: in next calculations, texte size is calculated for 3 or more
712  * chars. Of course, pads numbers and nets names can have less than 3
713  * chars. but after some tries, i found this is gives the best look
714  */
715  constexpr int MIN_CHAR_COUNT = 3;
716 
717  unsigned int tsize;
718  EDA_RECT* clipBox = aDrawInfo.m_DrawPanel?
719  aDrawInfo.m_DrawPanel->GetClipBox() : NULL;
720 
721  if( aDrawInfo.m_Display_padnum )
722  {
723  int numpad_len = std::max( (int) m_name.Length(), MIN_CHAR_COUNT );
724  tsize = std::min( (int) AreaSize.y, AreaSize.x / numpad_len );
725 
726  if( aDC->LogicalToDeviceXRel( tsize ) >= MIN_TEXT_SIZE ) // Not drawable when size too small.
727  {
728  // tsize reserve room for marges and segments thickness
729  tsize = ( tsize * 7 ) / 10;
730  DrawGraphicHaloText( clipBox, aDC, tpos,
731  aDrawInfo.m_Color, BLACK, WHITE,
732  m_name, t_angle,
733  wxSize( tsize , tsize ), GR_TEXT_HJUSTIFY_CENTER,
734  GR_TEXT_VJUSTIFY_CENTER, tsize / 7, false, false );
735 
736  }
737  }
738 
739  // display the short netname, if exists
740  if( shortname_len == 0 )
741  return;
742 
743  shortname_len = std::max( shortname_len, MIN_CHAR_COUNT );
744  tsize = std::min( AreaSize.y, AreaSize.x / shortname_len );
745 
746  if( aDC->LogicalToDeviceXRel( tsize ) >= MIN_TEXT_SIZE ) // Not drawable in size too small.
747  {
748  tpos = tpos0;
749 
750  if( aDrawInfo.m_Display_padnum )
751  tpos.y += AreaSize.y / 2;
752 
753  RotatePoint( &tpos, shape_pos, angle );
754 
755  // tsize reserve room for marges and segments thickness
756  tsize = ( tsize * 7 ) / 10;
757  DrawGraphicHaloText( clipBox, aDC, tpos,
758  aDrawInfo.m_Color, BLACK, WHITE,
759  GetShortNetname(), t_angle,
760  wxSize( tsize, tsize ), GR_TEXT_HJUSTIFY_CENTER,
761  GR_TEXT_VJUSTIFY_CENTER, tsize / 7, false, false );
762  }
763 }
764 
765 
774  double aRotation, const wxSize& aMargin) const
775 {
776  int width;
777 
778  if( m_Size.y < m_Size.x ) // Build an horizontal equiv segment
779  {
780  int delta = ( m_Size.x - m_Size.y ) / 2;
781  aSegStart.x = -delta - aMargin.x;
782  aSegStart.y = 0;
783  aSegEnd.x = delta + aMargin.x;
784  aSegEnd.y = 0;
785  width = m_Size.y + ( aMargin.y * 2 );
786  }
787  else // Vertical oval: build a vertical equiv segment
788  {
789  int delta = ( m_Size.y -m_Size.x ) / 2;
790  aSegStart.x = 0;
791  aSegStart.y = -delta - aMargin.y;
792  aSegEnd.x = 0;
793  aSegEnd.y = delta + aMargin.y;
794  width = m_Size.x + ( aMargin.x * 2 );
795  }
796 
797  if( aRotation )
798  {
799  RotatePoint( &aSegStart, aRotation);
800  RotatePoint( &aSegEnd, aRotation);
801  }
802 
803  return width;
804 }
805 
806 
807 void D_PAD::BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue,
808  double aRotation ) const
809 {
810  wxSize delta;
811  wxSize halfsize;
812 
813  halfsize.x = m_Size.x >> 1;
814  halfsize.y = m_Size.y >> 1;
815 
816  switch( GetShape() )
817  {
818  case PAD_SHAPE_RECT:
819  // For rectangular shapes, inflate is easy
820  halfsize += aInflateValue;
821 
822  // Verify if do not deflate more than than size
823  // Only possible for inflate negative values.
824  if( halfsize.x < 0 )
825  halfsize.x = 0;
826 
827  if( halfsize.y < 0 )
828  halfsize.y = 0;
829  break;
830 
831  case PAD_SHAPE_TRAPEZOID:
832  // Trapezoidal pad: verify delta values
833  delta.x = ( m_DeltaSize.x >> 1 );
834  delta.y = ( m_DeltaSize.y >> 1 );
835 
836  // be sure delta values are not to large
837  if( (delta.x < 0) && (delta.x <= -halfsize.y) )
838  delta.x = -halfsize.y + 1;
839 
840  if( (delta.x > 0) && (delta.x >= halfsize.y) )
841  delta.x = halfsize.y - 1;
842 
843  if( (delta.y < 0) && (delta.y <= -halfsize.x) )
844  delta.y = -halfsize.x + 1;
845 
846  if( (delta.y > 0) && (delta.y >= halfsize.x) )
847  delta.y = halfsize.x - 1;
848  break;
849 
850  default: // is used only for rect and trap. pads
851  return;
852  }
853 
854  // Build the basic rectangular or trapezoid shape
855  // delta is null for rectangular shapes
856  aCoord[0].x = -halfsize.x - delta.y; // lower left
857  aCoord[0].y = +halfsize.y + delta.x;
858 
859  aCoord[1].x = -halfsize.x + delta.y; // upper left
860  aCoord[1].y = -halfsize.y - delta.x;
861 
862  aCoord[2].x = +halfsize.x - delta.y; // upper right
863  aCoord[2].y = -halfsize.y + delta.x;
864 
865  aCoord[3].x = +halfsize.x + delta.y; // lower right
866  aCoord[3].y = +halfsize.y - delta.x;
867 
868  // Offsetting the trapezoid shape id needed
869  // It is assumed delta.x or/and delta.y == 0
870  if( GetShape() == PAD_SHAPE_TRAPEZOID && (aInflateValue.x != 0 || aInflateValue.y != 0) )
871  {
872  double angle;
873  wxSize corr;
874 
875  if( delta.y ) // lower and upper segment is horizontal
876  {
877  // Calculate angle of left (or right) segment with vertical axis
878  angle = atan2( (double) m_DeltaSize.y, (double) m_Size.y );
879 
880  // left and right sides are moved by aInflateValue.x in their perpendicular direction
881  // We must calculate the corresponding displacement on the horizontal axis
882  // that is delta.x +- corr.x depending on the corner
883  corr.x = KiROUND( tan( angle ) * aInflateValue.x );
884  delta.x = KiROUND( aInflateValue.x / cos( angle ) );
885 
886  // Horizontal sides are moved up and down by aInflateValue.y
887  delta.y = aInflateValue.y;
888 
889  // corr.y = 0 by the constructor
890  }
891  else if( delta.x ) // left and right segment is vertical
892  {
893  // Calculate angle of lower (or upper) segment with horizontal axis
894  angle = atan2( (double) m_DeltaSize.x, (double) m_Size.x );
895 
896  // lower and upper sides are moved by aInflateValue.x in their perpendicular direction
897  // We must calculate the corresponding displacement on the vertical axis
898  // that is delta.y +- corr.y depending on the corner
899  corr.y = KiROUND( tan( angle ) * aInflateValue.y );
900  delta.y = KiROUND( aInflateValue.y / cos( angle ) );
901 
902  // Vertical sides are moved left and right by aInflateValue.x
903  delta.x = aInflateValue.x;
904 
905  // corr.x = 0 by the constructor
906  }
907  else // the trapezoid is a rectangle
908  {
909  delta = aInflateValue; // this pad is rectangular (delta null).
910  }
911 
912  aCoord[0].x += -delta.x - corr.x; // lower left
913  aCoord[0].y += delta.y + corr.y;
914 
915  aCoord[1].x += -delta.x + corr.x; // upper left
916  aCoord[1].y += -delta.y - corr.y;
917 
918  aCoord[2].x += delta.x - corr.x; // upper right
919  aCoord[2].y += -delta.y + corr.y;
920 
921  aCoord[3].x += delta.x + corr.x; // lower right
922  aCoord[3].y += delta.y - corr.y;
923 
924  /* test coordinates and clamp them if the offset correction is too large:
925  * Note: if a coordinate is bad, the other "symmetric" coordinate is bad
926  * So when a bad coordinate is found, the 2 symmetric coordinates
927  * are set to the minimun value (0)
928  */
929 
930  if( aCoord[0].x > 0 ) // lower left x coordinate must be <= 0
931  aCoord[0].x = aCoord[3].x = 0;
932 
933  if( aCoord[1].x > 0 ) // upper left x coordinate must be <= 0
934  aCoord[1].x = aCoord[2].x = 0;
935 
936  if( aCoord[0].y < 0 ) // lower left y coordinate must be >= 0
937  aCoord[0].y = aCoord[1].y = 0;
938 
939  if( aCoord[3].y < 0 ) // lower right y coordinate must be >= 0
940  aCoord[3].y = aCoord[2].y = 0;
941  }
942 
943  if( aRotation )
944  {
945  for( int ii = 0; ii < 4; ii++ )
946  RotatePoint( &aCoord[ii], aRotation );
947  }
948 }
Definition: colors.h:57
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:673
show a marker on pads with no nets
bool m_Display_padnum
Definition: class_pad.h:75
void GRSetDrawMode(wxDC *DC, GR_DRAWMODE draw_mode)
Definition: gr_basic.cpp:223
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:120
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:65
void Draw(EDA_DRAW_PANEL *aPanel, wxDC *aDC, GR_DRAWMODE aDrawMode, const wxPoint &aOffset=ZeroOffset) override
Function Draw BOARD_ITEMs have their own color information.
void BuildPadPolygon(wxPoint aCoord[4], wxSize aInflateValue, double aRotation) const
Function BuildPadPolygon Has meaning only for polygonal pads (trapezoid and rectangular) Build the Co...
int PointCount() const
Function PointCount()
handle color for not plated holes (holes, not pads)
Class BOARD to handle a board.
void TransformRoundRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, int aCircleToSegmentsCount)
Function TransformRoundRectToPolygon convert a rectangle with rounded corners to a polygon Convert ar...
int color
Definition: DXF_plotter.cpp:62
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:61
void GRCSegm(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, int aPenSize, COLOR4D Color)
Definition: gr_basic.cpp:415
COLOR4D m_NoNetMarkColor
Definition: class_pad.h:72
virtual EDA_RECT * GetClipBox()
bool m_ShowNCMark
Definition: class_pad.h:79
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
int OutlineCount() const
Returns the number of outlines in the set
void GRFilledCircle(EDA_RECT *ClipBox, wxDC *DC, int x, int y, int r, int width, COLOR4D Color, COLOR4D BgColor)
Definition: gr_basic.cpp:778
void DrawGraphicHaloText(EDA_RECT *aClipBox, wxDC *aDC, const wxPoint &aPos, const COLOR4D aBgColor, COLOR4D aColor1, COLOR4D aColor2, const wxString &aText, double aOrient, const wxSize &aSize, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, void(*aCallback)(int x0, int y0, int xf, int yf, void *aData), void *aCallbackData, PLOTTER *aPlotter)
Draw graphic text with a border, so that it can be read on different backgrounds. ...
PCB_LAYER_ID m_Active_Layer
Definition: pcb_screen.h:44
smd pads, back layer
static LSET FrontMask()
Function FrontMask returns a mask holding all technical layers and the external CU layer on front sid...
Definition: lset.cpp:772
void NORMALIZE_ANGLE_90(T &Angle)
Definition: trigo.h:320
void GRClosedPoly(EDA_RECT *ClipBox, wxDC *DC, int n, wxPoint Points[], bool Fill, COLOR4D Color, COLOR4D BgColor)
Function GRClosedPoly draws a closed polygon onto the drawing context aDC and optionally fills and/or...
Definition: gr_basic.cpp:711
static const int delta[8][2]
Definition: solve.cpp:112
bool m_IsPrinting
Definition: class_pad.h:82
#define MIN_TEXT_SIZE
PCB_LAYER_ID ExtractLayer() const
Find the first set PCB_LAYER_ID.
Definition: lset.cpp:612
virtual EDA_DRAW_FRAME * GetParent() const =0
Class PCB_DISPLAY_OPTIONS handles display options like enable/disable some optional drawings...
PCB_LAYER_ID
A quick note on layer IDs:
double a
Alpha component.
Definition: color4d.h:290
Class LSET is a set of PCB_LAYER_IDs.
void Inflate(int aFactor, int aCircleSegmentsCount)
Performs outline inflation/deflation, using round corners.
void GRForceBlackPen(bool flagforce)
Function GRForceBlackPen.
Definition: gr_basic.cpp:204
GR_DRAWMODE
Drawmode. Compositing mode plus a flag or two.
Definition: gr_basic.h:37
Class SHAPE_POLY_SET.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
void GRFillCSegm(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color)
Definition: gr_basic.cpp:518
PCB_GENERAL_SETTINGS & Settings()
GR_DRAWMODE m_DrawMode
Definition: class_pad.h:67
EDA_DRAW_PANEL * m_DrawPanel
Definition: class_pad.h:66
COLOR4D m_HoleColor
Definition: class_pad.h:70
PCB_LAYER_ID m_Route_Layer_BOTTOM
Definition: pcb_screen.h:46
bool m_ShowNotPlatedHole
Definition: class_pad.h:80
void DrawShape(EDA_RECT *aClipBox, wxDC *aDC, PAD_DRAWINFO &aDrawInfo)
Function DrawShape basic function to draw a pad.
void GRLine(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color, wxPenStyle aStyle)
Definition: gr_basic.cpp:285
Like smd, does not appear on the solder paste layer (default) note also has a special attribute in Ge...
Definition: pad_shapes.h:62
int m_PadClearance
Definition: class_pad.h:73
bool m_IsPrinting
Definition: base_screen.h:220
COLORS_DESIGN_SETTINGS & Colors()
BOARD * GetBoard()
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Function IsElementVisible tests whether a given element category is visible.
int BuildSegmentFromOvalShape(wxPoint &aSegStart, wxPoint &aSegEnd, double aRotation, const wxSize &aMargin) const
Function BuildSegmentFromOvalShape Has meaning only for OVAL (and ROUND) pads Build an equivalent seg...
wxPoint m_Offset
Definition: class_pad.h:83
smd pads, front layer
Board layer functions and definitions.
#define max(a, b)
Definition: auxiliary.h:86
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:170
Class SHAPE_LINE_CHAIN.
void GRFilledSegment(EDA_RECT *aClipBox, wxDC *aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor)
Definition: gr_basic.cpp:526
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
LSET GetVisibleLayers() const
Function GetVisibleLayers is a proxy function that calls the correspondent function in m_BoardSetting...
void GRCircle(EDA_RECT *ClipBox, wxDC *DC, int xc, int yc, int r, int width, COLOR4D Color)
Definition: gr_basic.cpp:755
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
Class EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
Class PCB_EDIT_FRAME is the main frame for Pcbnew.
void GetRoundRectCornerCenters(wxPoint aCenters[4], int aRadius, const wxPoint &aPosition, const wxSize &aSize, double aRotation)
Helper function GetRoundRectCornerCenters Has meaning only for rounded rect Returns the centers of th...
The common library.
static LSET BackMask()
Function BackMask returns a mask holding all technical layers and the external CU layer on back side...
Definition: lset.cpp:779
Definition: colors.h:49
bool m_ShowPadFilled
Definition: class_pad.h:77
bool IsCopperLayer(LAYER_NUM aLayerId)
Function IsCopperLayer tests whether a layer is a copper layer.
COLOR4D m_NPHoleColor
Definition: class_pad.h:71
COLOR4D m_Color
Definition: class_pad.h:68
VECTOR2I & Point(int aIndex)
Function Point()
Definition: colors.h:45
bool m_Display_netname
Definition: class_pad.h:76
bool GetGRForceBlackPenState(void)
Function GetGRForceBlackPenState.
Definition: gr_basic.cpp:214
wxSize m_Mask_margin
Definition: class_pad.h:74
Definition: colors.h:68
PCB_LAYER_ID m_Route_Layer_TOP
Definition: pcb_screen.h:45
#define DO_NOT_DRAW
Used to disable draw function.
Definition: base_struct.h:126
#define min(a, b)
Definition: auxiliary.h:85
#define SEGCOUNT
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
Function IsLayerVisible is a proxy function that calls the correspondent function in m_BoardSettings ...
Definition: class_board.h:457
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline) ...
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39