KiCad PCB EDA Suite
create_3Dgraphic_brd_items.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) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
33 #include "../3d_rendering/3d_render_raytracing/shapes2D/ring_2d.h"
34 #include "../3d_rendering/3d_render_raytracing/shapes2D/filled_circle_2d.h"
35 #include "../3d_rendering/3d_render_raytracing/shapes2D/round_segment_2d.h"
36 #include "../3d_rendering/3d_render_raytracing/shapes2D/triangle_2d.h"
37 #include <board_adapter.h>
38 #include <board.h>
39 #include <footprint.h>
40 #include <pad.h>
41 #include <pcb_text.h>
42 #include <fp_shape.h>
43 #include <zone.h>
44 #include <kicad_string.h>
45 #include <fp_text.h>
47 #include <trigo.h>
48 #include <geometry/shape_segment.h>
50 #include <geometry/shape_circle.h>
51 #include <geometry/shape_rect.h>
52 #include <geometry/shape_simple.h>
53 #include <gr_text.h>
54 #include <utility>
55 #include <vector>
56 #include <wx/log.h>
57 
58 
59 // These variables are parameters used in addTextSegmToContainer.
60 // But addTextSegmToContainer is a call-back function,
61 // so we cannot send them as arguments.
62 static int s_textWidth;
64 static float s_biuTo3Dunits;
65 static const BOARD_ITEM* s_boardItem = nullptr;
66 
67 
68 // This is a call back function, used by GRText to draw the 3D text shape:
69 void addTextSegmToContainer( int x0, int y0, int xf, int yf, void* aData )
70 {
71  const SFVEC2F start3DU( x0 * s_biuTo3Dunits, -y0 * s_biuTo3Dunits );
72  const SFVEC2F end3DU ( xf * s_biuTo3Dunits, -yf * s_biuTo3Dunits );
73 
74  if( Is_segment_a_circle( start3DU, end3DU ) )
76  *s_boardItem) );
77  else
79  *s_boardItem ) );
80 }
81 
82 
83 // Based on
84 // void PCB_TEXT::TransformTextShapeWithClearanceToPolygon
85 // board_items_to_polygon_shape_transform.cpp
87  PCB_LAYER_ID aLayerId, int aClearanceValue )
88 {
89  wxSize size = aText->GetTextSize();
90 
91  if( aText->IsMirrored() )
92  size.x = -size.x;
93 
94  s_boardItem = (const BOARD_ITEM *) &aText;
95  s_dstcontainer = aDstContainer;
96  s_textWidth = aText->GetEffectiveTextPenWidth() + ( 2 * aClearanceValue );
98 
99  // not actually used, but needed by GRText
100  const COLOR4D dummy_color;
101 
102  // Use the actual text width to generate segments. The segment position depend on
103  // text thickness and justification
104  bool isBold = aText->IsBold();
105  int penWidth = aText->GetEffectiveTextPenWidth();
106 
107  GRText( nullptr, aText->GetTextPos(), dummy_color, aText->GetShownText(),
108  aText->GetTextAngle(), size, aText->GetHorizJustify(), aText->GetVertJustify(),
109  penWidth, aText->IsItalic(), isBold, addTextSegmToContainer );
110 }
111 
112 
114  CONTAINER_2D_BASE* aDstContainer,
115  PCB_LAYER_ID aLayerId, int aClearanceValue )
116 {
117  addShapeWithClearance( &aDimension->Text(), aDstContainer, aLayerId, aClearanceValue );
118 
119  const int linewidth = aDimension->GetLineThickness() + ( 2 * aClearanceValue );
120 
121  for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
122  {
123  switch( shape->Type() )
124  {
125  case SH_SEGMENT:
126  {
127  const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
128 
129  const SFVEC2F start3DU( seg.A.x * m_biuTo3Dunits, -seg.A.y * m_biuTo3Dunits );
130 
131  const SFVEC2F end3DU( seg.B.x * m_biuTo3Dunits, -seg.B.y * m_biuTo3Dunits );
132 
133  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth * m_biuTo3Dunits,
134  *aDimension ) );
135  break;
136  }
137 
138  case SH_CIRCLE:
139  {
140  int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
141  int deltar = aDimension->GetLineThickness();
142 
143  SFVEC2F center( shape->Centre().x * m_biuTo3Dunits,
144  shape->Centre().y * m_biuTo3Dunits );
145 
146  aDstContainer->Add( new RING_2D( center, ( radius - deltar ) * m_biuTo3Dunits,
147  ( radius + deltar ) * m_biuTo3Dunits, *aDimension ) );
148 
149  break;
150  }
151 
152  default:
153  break;
154  }
155 
156  }
157 
158 }
159 
160 
161 // Based on
162 // void FOOTPRINT::TransformFPShapesWithClearanceToPolygonSet
163 // board_items_to_polygon_shape_transform.cpp#L204
165  CONTAINER_2D_BASE* aDstContainer,
166  PCB_LAYER_ID aLayerId, int aInflateValue )
167 {
168  std::vector<FP_TEXT*> texts; // List of FP_TEXT to convert
169  FP_SHAPE* outline;
170 
171  for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
172  {
173  switch( item->Type() )
174  {
175  case PCB_FP_TEXT_T:
176  {
177  FP_TEXT* text = static_cast<FP_TEXT*>( item );
178 
179  if( text->GetLayer() == aLayerId && text->IsVisible() )
180  texts.push_back( text );
181  }
182  break;
183 
184 
185  case PCB_FP_SHAPE_T:
186  {
187  outline = (FP_SHAPE*) item;
188 
189  if( outline->GetLayer() != aLayerId )
190  break;
191 
192  addShapeWithClearance( (const PCB_SHAPE*) outline, aDstContainer, aLayerId, 0 );
193  }
194  break;
195 
196  default:
197  break;
198  }
199  }
200 
201  // Convert texts for footprints
202  if( aFootprint->Reference().GetLayer() == aLayerId && aFootprint->Reference().IsVisible() )
203  texts.push_back( &aFootprint->Reference() );
204 
205  if( aFootprint->Value().GetLayer() == aLayerId && aFootprint->Value().IsVisible() )
206  texts.push_back( &aFootprint->Value() );
207 
208  s_boardItem = (const BOARD_ITEM *)&aFootprint->Value();
209  s_dstcontainer = aDstContainer;
211 
212  for( FP_TEXT* text : texts )
213  {
214  s_textWidth = text->GetEffectiveTextPenWidth() + ( 2 * aInflateValue );
215  wxSize size = text->GetTextSize();
216  bool isBold = text->IsBold();
217  int penWidth = text->GetEffectiveTextPenWidth();
218 
219  if( text->IsMirrored() )
220  size.x = -size.x;
221 
222  GRText( nullptr, text->GetTextPos(), BLACK, text->GetShownText(), text->GetDrawRotation(),
223  size, text->GetHorizJustify(), text->GetVertJustify(), penWidth, text->IsItalic(),
224  isBold, addTextSegmToContainer );
225  }
226 }
227 
228 
229 void BOARD_ADAPTER::createTrack( const PCB_TRACK* aTrack, CONTAINER_2D_BASE* aDstContainer,
230  int aClearanceValue )
231 {
232  SFVEC2F start3DU( aTrack->GetStart().x * m_biuTo3Dunits,
233  -aTrack->GetStart().y * m_biuTo3Dunits ); // y coord is inverted
234 
235  switch( aTrack->Type() )
236  {
237  case PCB_VIA_T:
238  {
239  const float radius = ( ( aTrack->GetWidth() / 2 ) + aClearanceValue ) * m_biuTo3Dunits;
240  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius, *aTrack ) );
241  break;
242  }
243 
244  case PCB_ARC_T:
245  {
246  const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
247  VECTOR2D center( arc->GetCenter() );
248  double arc_angle = arc->GetAngle();
249  double radius = arc->GetRadius();
250  int arcsegcount = GetArcToSegmentCount( radius, Millimeter2iu( 0.005), arc_angle/10 );
251  int circlesegcount;
252 
253  // We need a circle to segment count. However, the arc angle can be small, and the
254  // radius very big. so we calculate a reasonable value for circlesegcount.
255  if( arcsegcount <= 1 ) // The arc will be approximated by a segment
256  circlesegcount = 1;
257  else
258  {
259  double cnt = arcsegcount * 3600/std::abs( arc_angle );
260 
261 #define SEG_CNT_MAX 128
262  if( cnt < SEG_CNT_MAX )
263  {
264  circlesegcount = (int)cnt;
265 
266  if( circlesegcount == 0 )
267  circlesegcount = 1;
268  }
269  else
270  {
271  circlesegcount = SEG_CNT_MAX;
272  }
273  }
274 
275  transformArcToSegments( wxPoint( center.x, center.y ), arc->GetStart(),
276  arc_angle, circlesegcount,
277  arc->GetWidth() + 2 * aClearanceValue, aDstContainer, *arc );
278  break;
279  }
280 
281  case PCB_TRACE_T: // Track is a usual straight segment
282  {
283  SFVEC2F end3DU( aTrack->GetEnd().x * m_biuTo3Dunits, -aTrack->GetEnd().y * m_biuTo3Dunits );
284 
285  // Cannot add segments that have the same start and end point
286  if( Is_segment_a_circle( start3DU, end3DU ) )
287  {
288  const float radius = ((aTrack->GetWidth() / 2) + aClearanceValue) * m_biuTo3Dunits;
289 
290  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius, *aTrack ) );
291  }
292  else
293  {
294  const float width = (aTrack->GetWidth() + 2 * aClearanceValue ) * m_biuTo3Dunits;
295 
296  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, width, *aTrack ) );
297  }
298 
299  break;
300  }
301 
302  default:
303  break;
304  }
305 }
306 
307 
309  PCB_LAYER_ID aLayer, wxSize aClearanceValue ) const
310 {
311  SHAPE_POLY_SET poly;
312 
313  // Our shape-based builder can't handle negative or differing x:y clearance values (the
314  // former are common for solder paste while the later get generated when a relative paste
315  // margin is used with an oblong pad). So we apply this huge hack and fake a larger pad to
316  // run the general-purpose polygon builder on.
317  // Of course being a hack it falls down when dealing with custom shape pads (where the size
318  // is only the size of the anchor), so for those we punt and just use aClearanceValue.x.
319 
320  if( ( aClearanceValue.x < 0 || aClearanceValue.x != aClearanceValue.y )
321  && aPad->GetShape() != PAD_SHAPE::CUSTOM )
322  {
323  PAD dummy( *aPad );
324  dummy.SetSize( aPad->GetSize() + aClearanceValue + aClearanceValue );
325  dummy.TransformShapeWithClearanceToPolygon( poly, aLayer, 0, ARC_HIGH_DEF, ERROR_INSIDE );
326  aClearanceValue = { 0, 0 };
327  }
328  else
329  {
330  auto padShapes = std::static_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );
331 
332  for( const SHAPE* shape : padShapes->Shapes() )
333  {
334  switch( shape->Type() )
335  {
336  case SH_SEGMENT:
337  {
338  const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape;
339  const SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits,
340  -seg->GetSeg().A.y * m_biuTo3Dunits );
341  const SFVEC2F end3DU ( seg->GetSeg().B.x * m_biuTo3Dunits,
342  -seg->GetSeg().B.y * m_biuTo3Dunits );
343  const int width = seg->GetWidth() + aClearanceValue.x * 2;
344 
345  // Cannot add segments that have the same start and end point
346  if( Is_segment_a_circle( start3DU, end3DU ) )
347  {
348  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU,
349  ( width / 2) * m_biuTo3Dunits,
350  *aPad ) );
351  }
352  else
353  {
354  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU,
355  width * m_biuTo3Dunits,
356  *aPad ) );
357  }
358  }
359  break;
360 
361  case SH_CIRCLE:
362  {
363  const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape;
364  const int radius = circle->GetRadius() + aClearanceValue.x;
365  const SFVEC2F center( circle->GetCenter().x * m_biuTo3Dunits,
366  -circle->GetCenter().y * m_biuTo3Dunits );
367 
368  aDstContainer->Add( new FILLED_CIRCLE_2D( center, radius * m_biuTo3Dunits,
369  *aPad ) );
370  }
371  break;
372 
373  case SH_RECT:
374  {
375  SHAPE_RECT* rect = (SHAPE_RECT*) shape;
376 
377  poly.NewOutline();
378  poly.Append( rect->GetPosition() );
379  poly.Append( rect->GetPosition().x + rect->GetSize().x, rect->GetPosition().y );
380  poly.Append( rect->GetPosition() + rect->GetSize() );
381  poly.Append( rect->GetPosition().x, rect->GetPosition().y + rect->GetSize().y );
382  }
383  break;
384 
385  case SH_SIMPLE:
386  poly.AddOutline( static_cast<const SHAPE_SIMPLE*>( shape )->Vertices() );
387  break;
388 
389  case SH_POLY_SET:
390  poly = *(SHAPE_POLY_SET*) shape;
391  break;
392 
393  case SH_ARC:
394  {
395  SHAPE_ARC* arc = (SHAPE_ARC*) shape;
397 
398  for( int i = 0; i < l.SegmentCount(); i++ )
399  {
400  SHAPE_SEGMENT seg( l.Segment( i ).A, l.Segment( i ).B, arc->GetWidth() );
401  const SFVEC2F start3DU( seg.GetSeg().A.x * m_biuTo3Dunits,
402  -seg.GetSeg().A.y * m_biuTo3Dunits );
403  const SFVEC2F end3DU( seg.GetSeg().B.x * m_biuTo3Dunits,
404  -seg.GetSeg().B.y * m_biuTo3Dunits );
405  const int width = arc->GetWidth() + aClearanceValue.x * 2;
406 
407  // Cannot add segments that have the same start and end point
408  if( Is_segment_a_circle( start3DU, end3DU ) )
409  {
410  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU,
411  ( width / 2) * m_biuTo3Dunits,
412  *aPad ) );
413  }
414  else
415  {
416  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU,
417  width * m_biuTo3Dunits,
418  *aPad ) );
419  }
420  }
421  }
422  break;
423 
424  default:
425  wxFAIL_MSG( "BOARD_ADAPTER::createPadWithClearance no implementation for "
426  + SHAPE_TYPE_asString( shape->Type() ) );
427  break;
428  }
429  }
430  }
431 
432  if( !poly.IsEmpty() )
433  {
434  if( aClearanceValue.x )
435  poly.Inflate( aClearanceValue.x, 32 );
436 
437  // Add the PAD polygon
438  ConvertPolygonToTriangles( poly, *aDstContainer, m_biuTo3Dunits, *aPad );
439  }
440 }
441 
442 
443 OBJECT_2D* BOARD_ADAPTER::createPadWithDrill( const PAD* aPad, int aInflateValue )
444 {
445  wxSize drillSize = aPad->GetDrillSize();
446 
447  if( !drillSize.x || !drillSize.y )
448  {
449  wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::createPadWithDrill - found an invalid pad" ) );
450  return nullptr;
451  }
452 
453  if( drillSize.x == drillSize.y ) // usual round hole
454  {
455  const int radius = ( drillSize.x / 2 ) + aInflateValue;
456 
457  const SFVEC2F center( aPad->GetPosition().x * m_biuTo3Dunits,
458  -aPad->GetPosition().y * m_biuTo3Dunits );
459 
460  return new FILLED_CIRCLE_2D( center, radius * m_biuTo3Dunits, *aPad );
461 
462  }
463  else // Oblong hole
464  {
465  const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
466  float width = seg->GetWidth() + aInflateValue * 2;
467 
468  SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits,
469  -seg->GetSeg().A.y * m_biuTo3Dunits );
470 
471  SFVEC2F end3DU ( seg->GetSeg().B.x * m_biuTo3Dunits,
472  -seg->GetSeg().B.y * m_biuTo3Dunits );
473 
474  return new ROUND_SEGMENT_2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad );
475  }
476 
477  return nullptr;
478 }
479 
480 
482  CONTAINER_2D_BASE* aDstContainer,
483  PCB_LAYER_ID aLayerId, int aInflateValue,
484  bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads,
485  bool aSkipNonPlatedPads )
486 {
487  for( PAD* pad : aFootprint->Pads() )
488  {
489  if( !pad->IsOnLayer( aLayerId ) )
490  continue;
491 
492  // Skip pad annulus when not connected on this layer (if removing is enabled)
493  if( !pad->FlashLayer( aLayerId ) && IsCopperLayer( aLayerId ) )
494  continue;
495 
496  // NPTH pads are not drawn on layers if the
497  // shape size and pos is the same as their hole:
498  if( aSkipNPTHPadsWihNoCopper && ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
499  {
500  if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
501  {
502  switch( pad->GetShape() )
503  {
504  case PAD_SHAPE::CIRCLE:
505  if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
506  continue;
507  break;
508 
509  case PAD_SHAPE::OVAL:
510  if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
511  continue;
512  break;
513 
514  default:
515  break;
516  }
517  }
518  }
519 
520  const bool isPlated = ( ( aLayerId == F_Cu ) && pad->FlashLayer( F_Mask ) ) ||
521  ( ( aLayerId == B_Cu ) && pad->FlashLayer( B_Mask ) );
522 
523  if( aSkipPlatedPads && isPlated )
524  continue;
525 
526  if( aSkipNonPlatedPads && !isPlated )
527  continue;
528 
529  wxSize margin( aInflateValue, aInflateValue );
530 
531  switch( aLayerId )
532  {
533  case F_Mask:
534  case B_Mask:
535  margin.x += pad->GetSolderMaskMargin();
536  margin.y += pad->GetSolderMaskMargin();
537  break;
538 
539  case F_Paste:
540  case B_Paste:
541  margin += pad->GetSolderPasteMargin();
542  break;
543 
544  default:
545  break;
546  }
547 
548  createPadWithClearance( pad, aDstContainer, aLayerId, margin );
549  }
550 }
551 
552 
553 // based on TransformArcToPolygon function from
554 // common/convert_basic_shapes_to_polygon.cpp
555 void BOARD_ADAPTER::transformArcToSegments( const wxPoint& aCentre, const wxPoint& aStart,
556  double aArcAngle, int aCircleToSegmentsCount,
557  int aWidth, CONTAINER_2D_BASE* aDstContainer,
558  const BOARD_ITEM& aBoardItem )
559 {
560  wxPoint arc_start, arc_end;
561  int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree
562 
563  arc_end = arc_start = aStart;
564 
565  if( aArcAngle != 3600 )
566  {
567  RotatePoint( &arc_end, aCentre, -aArcAngle );
568  }
569 
570  if( aArcAngle < 0 )
571  {
572  std::swap( arc_start, arc_end );
573  aArcAngle = -aArcAngle;
574  }
575 
576  // Compute the ends of segments and creates poly
577  wxPoint curr_end = arc_start;
578  wxPoint curr_start = arc_start;
579 
580  for( int ii = delta; ii < aArcAngle; ii += delta )
581  {
582  curr_end = arc_start;
583  RotatePoint( &curr_end, aCentre, -ii );
584 
585  const SFVEC2F start3DU( curr_start.x * m_biuTo3Dunits, -curr_start.y * m_biuTo3Dunits );
586  const SFVEC2F end3DU ( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits );
587 
588  if( Is_segment_a_circle( start3DU, end3DU ) )
589  {
590  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
591  aBoardItem ) );
592  }
593  else
594  {
595  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
596  aBoardItem ) );
597  }
598 
599  curr_start = curr_end;
600  }
601 
602  if( curr_end != arc_end )
603  {
604  const SFVEC2F start3DU( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits );
605  const SFVEC2F end3DU ( arc_end.x * m_biuTo3Dunits, -arc_end.y * m_biuTo3Dunits );
606 
607  if( Is_segment_a_circle( start3DU, end3DU ) )
608  {
609  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
610  aBoardItem ) );
611  }
612  else
613  {
614  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
615  aBoardItem ) );
616  }
617  }
618 }
619 
620 
621 // Based on
622 // TransformShapeWithClearanceToPolygon
623 // board_items_to_polygon_shape_transform.cpp#L431
625  CONTAINER_2D_BASE* aDstContainer, PCB_LAYER_ID aLayerId,
626  int aClearanceValue )
627 {
628  // The full width of the lines to create
629  // The extra 1 protects the inner/outer radius values from degeneracy
630  const int linewidth = aShape->GetWidth() + ( 2 * aClearanceValue ) + 1;
631 
632  switch( aShape->GetShape() )
633  {
635  {
636  const SFVEC2F center3DU( aShape->GetCenter().x * m_biuTo3Dunits,
637  -aShape->GetCenter().y * m_biuTo3Dunits );
638 
639  float inner_radius = ( aShape->GetRadius() - linewidth / 2 ) * m_biuTo3Dunits;
640  float outer_radius = ( aShape->GetRadius() + linewidth / 2 ) * m_biuTo3Dunits;
641 
642  if( inner_radius < 0 )
643  inner_radius = 0;
644 
645  if( aShape->IsFilled() )
646  aDstContainer->Add( new FILLED_CIRCLE_2D( center3DU, outer_radius, *aShape ) );
647  else
648  aDstContainer->Add( new RING_2D( center3DU, inner_radius, outer_radius, *aShape ) );
649  }
650  break;
651 
653  if( aShape->IsFilled() )
654  {
655  SHAPE_POLY_SET polyList;
656 
657  aShape->TransformShapeWithClearanceToPolygon( polyList, aLayerId, 0,
658  ARC_HIGH_DEF, ERROR_INSIDE );
659 
660  polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
661 
662  ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aShape );
663  }
664  else
665  {
666  std::vector<wxPoint> pts = aShape->GetRectCorners();
667 
668  const SFVEC2F topLeft3DU( pts[0].x * m_biuTo3Dunits, -pts[0].y * m_biuTo3Dunits );
669  const SFVEC2F topRight3DU( pts[1].x * m_biuTo3Dunits, -pts[1].y * m_biuTo3Dunits );
670  const SFVEC2F botRight3DU( pts[2].x * m_biuTo3Dunits, -pts[2].y * m_biuTo3Dunits );
671  const SFVEC2F botLeft3DU( pts[3].x * m_biuTo3Dunits, -pts[3].y * m_biuTo3Dunits );
672 
673  aDstContainer->Add( new ROUND_SEGMENT_2D( topLeft3DU, topRight3DU,
674  linewidth * m_biuTo3Dunits, *aShape ) );
675  aDstContainer->Add( new ROUND_SEGMENT_2D( topRight3DU, botRight3DU,
676  linewidth * m_biuTo3Dunits, *aShape ) );
677  aDstContainer->Add( new ROUND_SEGMENT_2D( botRight3DU, botLeft3DU,
678  linewidth * m_biuTo3Dunits, *aShape ) );
679  aDstContainer->Add( new ROUND_SEGMENT_2D( botLeft3DU, topLeft3DU,
680  linewidth * m_biuTo3Dunits, *aShape ) );
681  }
682  break;
683 
684  case PCB_SHAPE_TYPE::ARC:
685  {
686  unsigned int segCount = GetCircleSegmentCount( aShape->GetBoundingBox().GetSizeMax() );
687 
688  transformArcToSegments( aShape->GetCenter(), aShape->GetArcStart(), aShape->GetAngle(),
689  segCount, linewidth, aDstContainer, *aShape );
690  }
691  break;
692 
694  {
695  const SFVEC2F start3DU( aShape->GetStart().x * m_biuTo3Dunits,
696  -aShape->GetStart().y * m_biuTo3Dunits );
697 
698  const SFVEC2F end3DU ( aShape->GetEnd().x * m_biuTo3Dunits,
699  -aShape->GetEnd().y * m_biuTo3Dunits );
700 
701  if( Is_segment_a_circle( start3DU, end3DU ) )
702  {
703  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( linewidth / 2 ) * m_biuTo3Dunits,
704  *aShape ) );
705  }
706  else
707  {
708  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth * m_biuTo3Dunits,
709  *aShape ) );
710  }
711  }
712  break;
713 
716  {
717  SHAPE_POLY_SET polyList;
718 
719  aShape->TransformShapeWithClearanceToPolygon( polyList, aLayerId, 0,
720  ARC_HIGH_DEF, ERROR_INSIDE );
721 
722  polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
723 
724  if( polyList.IsEmpty() ) // Just for caution
725  break;
726 
727  ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aShape );
728  }
729  break;
730 
731  default:
732  wxFAIL_MSG( "BOARD_ADAPTER::addShapeWithClearance no implementation for "
733  + PCB_SHAPE_TYPE_T_asString( aShape->GetShape() ) );
734  break;
735  }
736 }
737 
738 
739 // Based on
740 // TransformSolidAreasShapesToPolygonSet
741 // board_items_to_polygon_shape_transform.cpp
742 void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZoneContainer,
743  CONTAINER_2D_BASE* aDstContainer, PCB_LAYER_ID aLayerId )
744 {
745  // Copy the polys list because we have to simplify it
746  SHAPE_POLY_SET polyList = SHAPE_POLY_SET( aZoneContainer->GetFilledPolysList( aLayerId ) );
747 
748  // This convert the poly in outline and holes
749  ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aZoneContainer );
750 
751  // add filled areas outlines, which are drawn with thick lines segments
752  // but only if filled polygons outlines have thickness
753  if( !aZoneContainer->GetFilledPolysUseThickness() )
754  return;
755 
756  float line_thickness = aZoneContainer->GetMinThickness() * m_biuTo3Dunits;
757 
758  for( int i = 0; i < polyList.OutlineCount(); ++i )
759  {
760  // Add outline
761  const SHAPE_LINE_CHAIN& pathOutline = polyList.COutline( i );
762 
763  for( int j = 0; j < pathOutline.PointCount(); ++j )
764  {
765  const VECTOR2I& a = pathOutline.CPoint( j );
766  const VECTOR2I& b = pathOutline.CPoint( j + 1 );
767 
768  SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
769  SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
770 
771  if( Is_segment_a_circle( start3DU, end3DU ) )
772  {
773  float radius = line_thickness/2;
774 
775  if( radius > 0.0 ) // degenerated circles crash 3D viewer
776  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius, *aZoneContainer ) );
777  }
778  else
779  {
780  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, line_thickness,
781  *aZoneContainer ) );
782  }
783  }
784 
785  // Add holes (of the poly, ie: the open parts) for this outline
786  for( int h = 0; h < polyList.HoleCount( i ); ++h )
787  {
788  const SHAPE_LINE_CHAIN& pathHole = polyList.CHole( i, h );
789 
790  for( int j = 0; j < pathHole.PointCount(); j++ )
791  {
792  const VECTOR2I& a = pathHole.CPoint( j );
793  const VECTOR2I& b = pathHole.CPoint( j + 1 );
794 
795  SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
796  SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
797 
798  if( Is_segment_a_circle( start3DU, end3DU ) )
799  {
800  float radius = line_thickness/2;
801 
802  if( radius > 0.0 ) // degenerated circles crash 3D viewer
803  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius,
804  *aZoneContainer ) );
805  }
806  else
807  {
808  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, line_thickness,
809  *aZoneContainer ) );
810  }
811  }
812  }
813  }
814 }
815 
816 
818  int aWidth )
819 {
820  if( aPad->GetShape() == PAD_SHAPE::CIRCLE ) // Draw a ring
821  {
822  const SFVEC2F center3DU( aPad->ShapePos().x * m_biuTo3Dunits,
823  -aPad->ShapePos().y * m_biuTo3Dunits );
824 
825  const int radius = aPad->GetSize().x / 2;
826  const float inner_radius = ( radius - aWidth / 2 ) * m_biuTo3Dunits;
827  const float outer_radius = ( radius + aWidth / 2 ) * m_biuTo3Dunits;
828 
829  aDstContainer->Add( new RING_2D( center3DU, inner_radius, outer_radius, *aPad ) );
830 
831  return;
832  }
833 
834  // For other shapes, add outlines as thick segments in polygon buffer
835  const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon();
836  const SHAPE_LINE_CHAIN& path = corners->COutline( 0 );
837 
838  for( int j = 0; j < path.PointCount(); j++ )
839  {
840  const VECTOR2I& a = path.CPoint( j );
841  const VECTOR2I& b = path.CPoint( j + 1 );
842 
843  SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
844  SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
845 
846  if( Is_segment_a_circle( start3DU, end3DU ) )
847  {
848  aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
849  *aPad ) );
850  }
851  else
852  {
853  aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
854  *aPad ) );
855  }
856  }
857 }
bool IsBold() const
Definition: eda_text.h:190
virtual wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:282
bool IsFilled() const
Definition: pcb_shape.h:75
void ConvertPolygonToTriangles(SHAPE_POLY_SET &aPolyList, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, const BOARD_ITEM &aBoardItem)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
EDA_TEXT_VJUSTIFY_T GetVertJustify() const
Definition: eda_text.h:206
bool IsMirrored() const
Definition: eda_text.h:196
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
void createPadWithClearance(const PAD *aPad, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayer, wxSize aClearanceValue) const
int GetRadius() const
Definition: shape_circle.h:107
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: pcb_shape.h:134
void buildPadOutlineAsSegments(const PAD *aPad, CONTAINER_2D_BASE *aDstContainer, int aWidth)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
#define SEG_CNT_MAX
OBJECT_2D * createPadWithDrill(const PAD *aPad, int aInflateValue)
PCB_TEXT & Text()
PCB_SHAPE_TYPE GetShape() const
Definition: pcb_shape.h:110
bool IsVisible() const
Definition: eda_text.h:193
int GetRadius() const
Return the radius of this item.
Definition: pcb_shape.cpp:445
bool IsEmpty() const
std::vector< wxPoint > GetRectCorners() const
Definition: pcb_shape.cpp:998
int GetWidth() const
Definition: pcb_shape.h:97
void transformArcToSegments(const wxPoint &aCentre, const wxPoint &aStart, double aArcAngle, int aCircleToSegmentsCount, int aWidth, CONTAINER_2D_BASE *aDstContainer, const BOARD_ITEM &aBoardItem)
static void isPlated(LIBEVAL::CONTEXT *aCtx, void *self)
double GetTextAngle() const
Definition: eda_text.h:181
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
const std::vector< std::shared_ptr< SHAPE > > & GetShapes() const
const SHAPE_SEGMENT * GetEffectiveHoleShape() const
Return a SHAPE object representing the pad's hole.
Definition: pad.cpp:289
static CONTAINER_2D_BASE * s_dstcontainer
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
Abstract dimension API.
Definition: pcb_dimension.h:95
const SHAPE_POLY_SET & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:632
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
const VECTOR2I GetCenter() const
Definition: shape_circle.h:112
int GetWidth() const
Definition: pcb_track.h:102
Definition: color4d.h:44
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
void addFootprintShapesWithClearance(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, int aInflateValue)
int PointCount() const
Function PointCount()
const SEG & GetSeg() const
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
int GetEffectiveTextPenWidth(int aDefaultWidth=0) const
The EffectiveTextPenWidth uses the text thickness if > 1 or aDefaultWidth.
Definition: eda_text.cpp:149
PADS & Pads()
Definition: footprint.h:159
wxPoint GetArcStart() const
Definition: pcb_shape.h:157
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:457
static int s_textWidth
void Add(OBJECT_2D *aObject)
Definition: container_2d.h:49
const VECTOR2I GetSize() const
Definition: shape_rect.h:124
polygon (not yet used for tracks, but could be in microwave apps)
bool IsItalic() const
Definition: eda_text.h:187
FP_TEXT & Reference()
Definition: footprint.h:458
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:70
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: pcb_text.cpp:56
const VECTOR2I & CPoint(int aIndex) const
Function Point()
const wxSize & GetDrillSize() const
Definition: pad.h:239
wxPoint ShapePos() const
Definition: pad.cpp:660
like PAD_PTH, but not plated
PCB_LAYER_ID
A quick note on layer IDs:
void addShapeWithClearance(const PCB_TEXT *aText, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, int aClearanceValue)
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:205
int GetMinThickness() const
Definition: zone.h:241
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
const SHAPE_LINE_CHAIN ConvertToPolyline(double aAccuracy=0.005 *PCB_IU_PER_MM) const
Constructs a SHAPE_LINE_CHAIN of segments from a given arc.
Definition: shape_arc.cpp:417
static float s_biuTo3Dunits
Represent a set of closed polygons.
const VECTOR2I & GetPosition() const
Definition: shape_rect.h:116
const wxSize & GetTextSize() const
Definition: eda_text.h:245
double GetRadius() const
Definition: pcb_track.cpp:970
segment with non rounded ends
const wxSize & GetSize() const
Definition: pad.h:229
wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.cpp:344
void addSolidAreasShapes(const ZONE *aZoneContainer, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId)
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: pcb_shape.h:124
void addPadsWithClearance(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, int aInflateValue, bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads, bool aSkipNonPlatedPads)
double m_biuTo3Dunits
Top (End) Z position of each layer in 3D units.
PAD_SHAPE GetShape() const
Definition: pad.h:166
static wxString SHAPE_TYPE_asString(SHAPE_TYPE a)
Definition: shape.h:55
circular arc
Definition: shape.h:50
void createTrack(const PCB_TRACK *aTrack, CONTAINER_2D_BASE *aDstContainer, int aClearanceValue)
DRAWINGS & GraphicalItems()
Definition: footprint.h:162
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: pad.cpp:280
a few functions useful in geometry calculations.
void Simplify(POLYGON_MODE aFastMode)
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
An abstract shape on 2D plane.
Definition: shape.h:116
bool Is_segment_a_circle(const SFVEC2F &aStart, const SFVEC2F &aEnd)
Check if segment start and end is very close to each other.
int NewOutline()
Creates a new hole in a given outline.
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
int SegmentCount() const
Function SegmentCount()
circle
Definition: shape.h:46
void Inflate(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
void GRText(wxDC *aDC, const wxPoint &aPos, COLOR4D aColor, 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 a graphic text (like footprint texts).
Definition: gr_text.cpp:130
Definition: seg.h:40
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
static wxString PCB_SHAPE_TYPE_T_asString(PCB_SHAPE_TYPE a)
Definition: board_item.h:57
SEG Segment(int aIndex)
Function Segment()
int GetWidth() const
Definition: shape_arc.h:112
set of polygons (with holes, etc.)
Definition: shape.h:48
double GetAngle() const
Definition: pcb_track.cpp:976
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
wxPoint GetPosition() const override
Definition: pad.h:174
SHAPE_LINE_CHAIN.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_shape.cpp:580
VECTOR2I A
Definition: seg.h:48
double GetAngle() const
Definition: pcb_shape.h:107
usual segment : line with rounded ends
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon() const
Definition: pad.cpp:271
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
void addTextSegmToContainer(int x0, int y0, int xf, int yf, void *aData)
const wxPoint & GetTextPos() const
Definition: eda_text.h:254
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
axis-aligned rectangle
Definition: shape.h:43
static const BOARD_ITEM * s_boardItem
Definition: pad.h:57
Arcs (with rounded ends)
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the draw segment to a closed polygon.
int GetWidth() const
static constexpr int Millimeter2iu(double mm)
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:171
simple polygon
Definition: shape.h:47
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
unsigned int GetCircleSegmentCount(float aDiameter3DU) const
int GetSizeMax() const
Definition: eda_rect.h:96
int GetLineThickness() const
const wxPoint & GetStart() const
Definition: pcb_track.h:108
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
line segment
Definition: shape.h:44
bool GetFilledPolysUseThickness() const
Definition: zone.h:686
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
VECTOR2I B
Definition: seg.h:49