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 <[email protected]>
5 * Copyright (C) 1992-2022 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/raytracing/shapes2D/ring_2d.h"
34#include "../3d_rendering/raytracing/shapes2D/filled_circle_2d.h"
35#include "../3d_rendering/raytracing/shapes2D/round_segment_2d.h"
36#include "../3d_rendering/raytracing/shapes2D/triangle_2d.h"
37#include "fp_textbox.h"
38#include <board_adapter.h>
39#include <footprint.h>
40#include <pad.h>
41#include <pcb_text.h>
42#include <fp_shape.h>
44#include <pcb_painter.h> // for PCB_RENDER_SETTINGS
45#include <zone.h>
46#include <fp_text.h>
48#include <trigo.h>
52#include <geometry/shape_rect.h>
54#include <utility>
55#include <vector>
56#include <wx/log.h>
57#include <macros.h>
58#include <callback_gal.h>
59
60
61#define TO_3DU( x ) ( ( x ) * m_biuTo3Dunits )
62
63#define TO_SFVEC2F( vec ) SFVEC2F( TO_3DU( vec.x ), TO_3DU( -vec.y ) )
64
65
66void BOARD_ADAPTER::addText( const EDA_TEXT* aText, CONTAINER_2D_BASE* aContainer,
67 const BOARD_ITEM* aOwner )
68{
70 TEXT_ATTRIBUTES attrs = aText->GetAttributes();
71 float penWidth_3DU = TO_3DU( aText->GetEffectiveTextPenWidth() );
72 KIFONT::FONT* font = aText->GetFont();
73
74 if( !font )
75 font = KIFONT::FONT::GetFont( wxEmptyString, aText->IsBold(), aText->IsItalic() );
76
77 if( aOwner && aOwner->IsKnockout() )
78 {
79 SHAPE_POLY_SET knockouts;
80
81 CALLBACK_GAL callback_gal( empty_opts,
82 // Polygon callback
83 [&]( const SHAPE_LINE_CHAIN& aPoly )
84 {
85 knockouts.AddOutline( aPoly );
86 } );
87
89 attrs.m_Angle = aText->GetDrawRotation();
90
91 callback_gal.SetIsFill( font->IsOutline() );
92 callback_gal.SetIsStroke( font->IsStroke() );
93 callback_gal.SetLineWidth( attrs.m_StrokeWidth );
94 font->Draw( &callback_gal, aText->GetShownText(), aText->GetDrawPos(), attrs );
95
96 SHAPE_POLY_SET finalPoly;
97 int margin = attrs.m_StrokeWidth * 1.5 +
99
100 aText->TransformBoundingBoxToPolygon( &finalPoly, margin );
101 finalPoly.BooleanSubtract( knockouts, SHAPE_POLY_SET::PM_FAST );
103
104 ConvertPolygonToTriangles( finalPoly, *aContainer, m_biuTo3Dunits, *aOwner );
105 }
106 else
107 {
108 CALLBACK_GAL callback_gal( empty_opts,
109 // Stroke callback
110 [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
111 {
112 const SFVEC2F pt1_3DU = TO_SFVEC2F( aPt1 );
113 const SFVEC2F pt2_3DU = TO_SFVEC2F( aPt2 );
114
115 if( penWidth_3DU == 0.0 )
116 {
117 // Don't attempt to render degenerate shapes
118 }
119 else if( Is_segment_a_circle( pt1_3DU, pt2_3DU ) )
120 {
121 // Cannot add segments that have the same start and end point
122 aContainer->Add( new FILLED_CIRCLE_2D( pt1_3DU, penWidth_3DU / 2,
123 *aOwner ) );
124 }
125 else
126 {
127 aContainer->Add( new ROUND_SEGMENT_2D( pt1_3DU, pt2_3DU, penWidth_3DU,
128 *aOwner ) );
129 }
130 },
131 // Triangulation callback
132 [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2, const VECTOR2I& aPt3 )
133 {
134 aContainer->Add( new TRIANGLE_2D( TO_SFVEC2F( aPt1 ), TO_SFVEC2F( aPt2 ),
135 TO_SFVEC2F( aPt3 ), *aOwner ) );
136 } );
137
138 attrs.m_Angle = aText->GetDrawRotation();
139
140 font->Draw( &callback_gal, aText->GetShownText(), aText->GetDrawPos(), attrs );
141 }
142}
143
144
146 const BOARD_ITEM* aOwner )
147{
148 addText( aDimension, aContainer, aDimension );
149
150 const int linewidth = aDimension->GetLineThickness();
151
152 for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
153 {
154 switch( shape->Type() )
155 {
156 case SH_SEGMENT:
157 {
158 const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
159
160 aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( seg.A ), TO_SFVEC2F( seg.B ),
161 TO_3DU( linewidth ), *aOwner ) );
162 break;
163 }
164
165 case SH_CIRCLE:
166 {
167 int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
168 int delta = aDimension->GetLineThickness() / 2;
169
170 aContainer->Add( new RING_2D( TO_SFVEC2F( shape->Centre() ), TO_3DU( radius - delta ),
171 TO_3DU( radius + delta ), *aOwner ) );
172 break;
173 }
174
175 default:
176 break;
177 }
178 }
179}
180
181
183 PCB_LAYER_ID aLayerId )
184{
186
187 if( aFootprint->Reference().GetLayer() == aLayerId && aFootprint->Reference().IsVisible() )
188 addText( &aFootprint->Reference(), aContainer, &aFootprint->Reference() );
189
190 if( aFootprint->Value().GetLayer() == aLayerId && aFootprint->Value().IsVisible() )
191 addText( &aFootprint->Value(), aContainer, &aFootprint->Value() );
192
193 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
194 {
195 switch( item->Type() )
196 {
197 case PCB_FP_TEXT_T:
198 {
199 FP_TEXT* text = static_cast<FP_TEXT*>( item );
200
201 if( text->GetLayer() == aLayerId && text->IsVisible() )
202 addText( text, aContainer, text );
203
204 break;
205 }
206
207 case PCB_FP_TEXTBOX_T:
208 {
209 FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( item );
210
211 if( textbox->GetLayer() == aLayerId )
212 {
213 addShape( textbox, aContainer, aFootprint );
214 addText( textbox, aContainer, aFootprint );
215 }
216
217 break;
218 }
219
225 {
226 PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( item );
227
228 if( dimension->GetLayer() == aLayerId )
229 addShape( dimension, aContainer, aFootprint );
230
231 break;
232 }
233
234 case PCB_FP_SHAPE_T:
235 {
236 FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
237
238 if( shape->GetLayer() == aLayerId )
239 addShape( shape, aContainer, aFootprint );
240
241 break;
242 }
243
244 default:
245 break;
246 }
247 }
248}
249
250
251void BOARD_ADAPTER::createTrack( const PCB_TRACK* aTrack, CONTAINER_2D_BASE* aDstContainer )
252{
253 SFVEC2F start3DU = TO_SFVEC2F( aTrack->GetStart() );
254 SFVEC2F end3DU = TO_SFVEC2F( aTrack->GetEnd() );
255
256 switch( aTrack->Type() )
257 {
258 case PCB_VIA_T:
259 {
260 const float radius3DU = TO_3DU( aTrack->GetWidth() / 2 );
261
262 if( radius3DU > 0.0 )
263 aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius3DU, *aTrack ) );
264
265 break;
266 }
267
268 case PCB_ARC_T:
269 {
270 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
271
272 VECTOR2D center( arc->GetCenter() );
273 EDA_ANGLE arc_angle = arc->GetAngle();
274 double radius = arc->GetRadius();
275 int arcsegcount = GetArcToSegmentCount( radius, ARC_HIGH_DEF, arc_angle );
276 int circlesegcount;
277
278 // We need a circle to segment count. However, the arc angle can be small, and the
279 // radius very big. so we calculate a reasonable value for circlesegcount.
280 if( arcsegcount <= 1 ) // The arc will be approximated by a segment
281 {
282 circlesegcount = 1;
283 }
284 else
285 {
286 circlesegcount = KiROUND( arcsegcount * 360.0 / std::abs( arc_angle.AsDegrees() ) );
287 circlesegcount = std::max( 1, std::min( circlesegcount, 128 ) );
288 }
289
290 transformArcToSegments( VECTOR2I( center.x, center.y ), arc->GetStart(), arc_angle,
291 circlesegcount, arc->GetWidth(), aDstContainer, *arc );
292 break;
293 }
294
295 case PCB_TRACE_T: // Track is a usual straight segment
296 {
297 if( aTrack->GetWidth() == 0 )
298 {
299 // Don't attempt to render degenerate shapes
300 }
301 else if( Is_segment_a_circle( start3DU, end3DU ) )
302 {
303 // Cannot add segments that have the same start and end point
304 aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aTrack->GetWidth() / 2 ),
305 *aTrack ) );
306 }
307 else
308 {
309 aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aTrack->GetWidth() ),
310 *aTrack ) );
311 }
312
313 break;
314 }
315
316 default:
317 break;
318 }
319}
320
321
323 PCB_LAYER_ID aLayer, const VECTOR2I& aMargin ) const
324{
325 SHAPE_POLY_SET poly;
326 int maxError = GetBoard()->GetDesignSettings().m_MaxError;
327 VECTOR2I clearance = aMargin;
328
329 // Our shape-based builder can't handle negative or differing x:y clearance values (the
330 // former are common for solder paste while the later get generated when a relative paste
331 // margin is used with an oblong pad). So we apply this huge hack and fake a larger pad to
332 // run the general-purpose polygon builder on.
333 // Of course being a hack it falls down when dealing with custom shape pads (where the size
334 // is only the size of the anchor), so for those we punt and just use aMargin.x.
335
336 if( ( clearance.x < 0 || clearance.x != clearance.y )
337 && aPad->GetShape() != PAD_SHAPE::CUSTOM )
338 {
339 VECTOR2I dummySize = VECTOR2I( aPad->GetSize() ) + clearance + clearance;
340
341 if( dummySize.x <= 0 || dummySize.y <= 0 )
342 return;
343
344 PAD dummy( *aPad );
345 dummy.SetSize( VECTOR2I( dummySize.x, dummySize.y ) );
346 dummy.TransformShapeToPolygon( poly, aLayer, 0, maxError, ERROR_INSIDE );
347 clearance = { 0, 0 };
348 }
349 else
350 {
351 auto padShapes = std::static_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );
352
353 for( const SHAPE* shape : padShapes->Shapes() )
354 {
355 switch( shape->Type() )
356 {
357 case SH_SEGMENT:
358 {
359 const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*>( shape );
360
361 const SFVEC2F a3DU = TO_SFVEC2F( seg->GetSeg().A );
362 const SFVEC2F b3DU = TO_SFVEC2F( seg->GetSeg().B );
363 const double width3DU = TO_3DU( seg->GetWidth() + clearance.x * 2 );
364
365 if( width3DU == 0.0 )
366 {
367 // Don't attempt to render degenerate shapes
368 }
369 else if( Is_segment_a_circle( a3DU, b3DU ) )
370 {
371 // Cannot add segments that have the same start and end point
372 aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
373 }
374 else
375 {
376 aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
377 }
378
379 break;
380 }
381
382 case SH_CIRCLE:
383 {
384 const SHAPE_CIRCLE* circle = static_cast<const SHAPE_CIRCLE*>( shape );
385
386 const double radius3DU = TO_3DU( circle->GetRadius() + clearance.x );
387 const SFVEC2F center3DU = TO_SFVEC2F( circle->GetCenter() );
388
389 // Don't render zero radius circles
390 if( radius3DU > 0.0 )
391 aContainer->Add( new FILLED_CIRCLE_2D( center3DU, radius3DU, *aPad ) );
392
393 break;
394 }
395
396 case SH_RECT:
397 {
398 const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>( shape );
399
400 poly.NewOutline();
401 poly.Append( rect->GetPosition() );
402 poly.Append( rect->GetPosition().x + rect->GetSize().x, rect->GetPosition().y );
403 poly.Append( rect->GetPosition() + rect->GetSize() );
404 poly.Append( rect->GetPosition().x, rect->GetPosition().y + rect->GetSize().y );
405 break;
406 }
407
408 case SH_SIMPLE:
409 poly.AddOutline( static_cast<const SHAPE_SIMPLE*>( shape )->Vertices() );
410 break;
411
412 case SH_POLY_SET:
413 poly = *(SHAPE_POLY_SET*) shape;
414 break;
415
416 case SH_ARC:
417 {
418 const SHAPE_ARC* arc = static_cast<const SHAPE_ARC*>( shape );
419 SHAPE_LINE_CHAIN l = arc->ConvertToPolyline( maxError );
420
421 for( int i = 0; i < l.SegmentCount(); i++ )
422 {
423 SHAPE_SEGMENT seg( l.Segment( i ).A, l.Segment( i ).B, arc->GetWidth() );
424 const SFVEC2F a3DU = TO_SFVEC2F( seg.GetSeg().A );
425 const SFVEC2F b3DU = TO_SFVEC2F( seg.GetSeg().B );
426 const double width3DU = TO_3DU( arc->GetWidth() + clearance.x * 2 );
427
428 if( width3DU == 0.0 )
429 {
430 // Don't attempt to render degenerate shapes
431 }
432 else if( Is_segment_a_circle( a3DU, b3DU ) )
433 {
434 // Cannot add segments that have the same start and end point
435 aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
436 }
437 else
438 {
439 aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
440 }
441 }
442
443 break;
444 }
445
446 default:
447 UNIMPLEMENTED_FOR( SHAPE_TYPE_asString( shape->Type() ) );
448 break;
449 }
450 }
451 }
452
453 if( !poly.IsEmpty() )
454 {
455 if( clearance.x )
456 poly.Inflate( clearance.x, 32 );
457
458 // Add the PAD polygon
459 ConvertPolygonToTriangles( poly, *aContainer, m_biuTo3Dunits, *aPad );
460 }
461}
462
463
464OBJECT_2D* BOARD_ADAPTER::createPadWithDrill( const PAD* aPad, int aInflateValue )
465{
466 if( !aPad->HasHole() )
467 {
468 wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::createPadWithDrill - found an invalid pad" ) );
469 return nullptr;
470 }
471
472 std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
473
474 if( slot->GetSeg().A == slot->GetSeg().B )
475 {
476 return new FILLED_CIRCLE_2D( TO_SFVEC2F( slot->GetSeg().A ),
477 TO_3DU( slot->GetWidth() / 2 + aInflateValue ),
478 *aPad );
479 }
480 else
481 {
482 return new ROUND_SEGMENT_2D( TO_SFVEC2F( slot->GetSeg().A ),
483 TO_SFVEC2F( slot->GetSeg().B ),
484 TO_3DU( slot->GetWidth() + aInflateValue * 2 ),
485 *aPad );
486 }
487}
488
489
490void BOARD_ADAPTER::addPads( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aContainer,
491 PCB_LAYER_ID aLayerId, bool aSkipNPTHPadsWihNoCopper,
492 bool aSkipPlatedPads, bool aSkipNonPlatedPads )
493{
494 for( PAD* pad : aFootprint->Pads() )
495 {
496 if( !pad->IsOnLayer( aLayerId ) )
497 continue;
498
499 // Skip pad annulus when not connected on this layer (if removing is enabled)
500 if( !pad->FlashLayer( aLayerId ) && IsCopperLayer( aLayerId ) )
501 continue;
502
503 // NPTH pads are not drawn on layers if the shape size and pos is the same as their hole:
504 if( aSkipNPTHPadsWihNoCopper && ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
505 {
506 if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == VECTOR2I( 0, 0 ) )
507 {
508 switch( pad->GetShape() )
509 {
511 if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
512 continue;
513
514 break;
515
516 case PAD_SHAPE::OVAL:
517 if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
518 continue;
519
520 break;
521
522 default:
523 break;
524 }
525 }
526 }
527
528 VECTOR2I margin( 0, 0 );
529
530 switch( aLayerId )
531 {
532 case F_Cu:
533 if( aSkipPlatedPads && pad->FlashLayer( F_Mask ) )
534 continue;
535
536 if( aSkipNonPlatedPads && !pad->FlashLayer( F_Mask ) )
537 continue;
538
539 break;
540
541 case B_Cu:
542 if( aSkipPlatedPads && pad->FlashLayer( B_Mask ) )
543 continue;
544
545 if( aSkipNonPlatedPads && !pad->FlashLayer( B_Mask ) )
546 continue;
547
548 break;
549
550 case F_Mask:
551 case B_Mask:
552 margin.x += pad->GetSolderMaskExpansion();
553 margin.y += pad->GetSolderMaskExpansion();
554 break;
555
556 case F_Paste:
557 case B_Paste:
558 margin += pad->GetSolderPasteMargin();
559 break;
560
561 default:
562 break;
563 }
564
565 createPadWithMargin( pad, aContainer, aLayerId, margin );
566 }
567}
568
569
570// based on TransformArcToPolygon function from
571// common/convert_basic_shapes_to_polygon.cpp
572void BOARD_ADAPTER::transformArcToSegments( const VECTOR2I& aCentre, const VECTOR2I& aStart,
573 const EDA_ANGLE& aArcAngle, int aCircleToSegmentsCount,
574 int aWidth, CONTAINER_2D_BASE* aContainer,
575 const BOARD_ITEM& aOwner )
576{
577 // Don't attempt to render degenerate shapes
578 if( aWidth == 0 )
579 return;
580
581 VECTOR2I arc_start, arc_end;
582 EDA_ANGLE arcAngle( aArcAngle );
583 EDA_ANGLE delta = ANGLE_360 / aCircleToSegmentsCount; // rotate angle
584
585 arc_end = arc_start = aStart;
586
587 if( arcAngle != ANGLE_360 )
588 RotatePoint( arc_end, aCentre, -arcAngle );
589
590 if( arcAngle < ANGLE_0 )
591 {
592 std::swap( arc_start, arc_end );
593 arcAngle = -arcAngle;
594 }
595
596 // Compute the ends of segments and creates poly
597 VECTOR2I curr_end = arc_start;
598 VECTOR2I curr_start = arc_start;
599
600 for( EDA_ANGLE ii = delta; ii < arcAngle; ii += delta )
601 {
602 curr_end = arc_start;
603 RotatePoint( curr_end, aCentre, -ii );
604
605 const SFVEC2F start3DU = TO_SFVEC2F( curr_start );
606 const SFVEC2F end3DU = TO_SFVEC2F( curr_end );
607
608 if( Is_segment_a_circle( start3DU, end3DU ) )
609 aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), aOwner ) );
610 else
611 aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), aOwner ) );
612
613 curr_start = curr_end;
614 }
615
616 if( curr_end != arc_end )
617 {
618 const SFVEC2F start3DU = TO_SFVEC2F( curr_end );
619 const SFVEC2F end3DU = TO_SFVEC2F( arc_end );
620
621 if( Is_segment_a_circle( start3DU, end3DU ) )
622 aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), aOwner ) );
623 else
624 aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), aOwner ) );
625 }
626}
627
628
629void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aContainer,
630 const BOARD_ITEM* aOwner )
631{
632 // The full width of the lines to create
633 // The extra 1 protects the inner/outer radius values from degeneracy
634 const int linewidth = aShape->GetWidth() + 1;
635 PLOT_DASH_TYPE lineStyle = aShape->GetStroke().GetPlotStyle();
636
637 if( lineStyle <= PLOT_DASH_TYPE::FIRST_TYPE )
638 {
639 switch( aShape->GetShape() )
640 {
641 case SHAPE_T::CIRCLE:
642 {
643 const SFVEC2F center3DU = TO_SFVEC2F( aShape->GetCenter() );
644 float inner_radius3DU = TO_3DU( aShape->GetRadius() - linewidth / 2 );
645 float outer_radius3DU = TO_3DU( aShape->GetRadius() + linewidth / 2 );
646
647 if( inner_radius3DU < 0 )
648 inner_radius3DU = 0.0;
649
650 if( outer_radius3DU == 0.0 )
651 {
652 // Don't attempt to render degenerate shapes
653 }
654 else if( aShape->IsFilled() )
655 {
656 aContainer->Add( new FILLED_CIRCLE_2D( center3DU, outer_radius3DU,
657 *aOwner ) );
658 }
659 else
660 {
661 aContainer->Add( new RING_2D( center3DU, inner_radius3DU, outer_radius3DU,
662 *aOwner ) );
663 }
664
665 break;
666 }
667
668 case SHAPE_T::RECT:
669 if( aShape->IsFilled() )
670 {
671 SHAPE_POLY_SET polyList;
672
674 ERROR_INSIDE );
675
677
678 ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aOwner );
679 }
680 else
681 {
682 std::vector<VECTOR2I> pts = aShape->GetRectCorners();
683
684 aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[0] ), TO_SFVEC2F( pts[1] ),
685 TO_3DU( linewidth ), *aOwner ) );
686 aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[1] ), TO_SFVEC2F( pts[2] ),
687 TO_3DU( linewidth ), *aOwner ) );
688 aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[2] ), TO_SFVEC2F( pts[3] ),
689 TO_3DU( linewidth ), *aOwner ) );
690 aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[3] ), TO_SFVEC2F( pts[0] ),
691 TO_3DU( linewidth ), *aOwner ) );
692 }
693 break;
694
695 case SHAPE_T::ARC:
696 {
697 unsigned int segCount = GetCircleSegmentCount( aShape->GetBoundingBox().GetSizeMax() );
698
699 transformArcToSegments( aShape->GetCenter(), aShape->GetStart(), aShape->GetArcAngle(),
700 segCount, linewidth, aContainer, *aOwner );
701 break;
702 }
703
704 case SHAPE_T::SEGMENT:
705 {
706 const SFVEC2F start3DU = TO_SFVEC2F( aShape->GetStart() );
707 const SFVEC2F end3DU = TO_SFVEC2F( aShape->GetEnd() );
708 const double linewidth3DU = TO_3DU( linewidth );
709
710 if( linewidth3DU == 0.0 )
711 {
712 // Don't attempt to render degenerate shapes
713 }
714 else if( Is_segment_a_circle( start3DU, end3DU ) )
715 {
716 // Cannot add segments that have the same start and end point
717 aContainer->Add( new FILLED_CIRCLE_2D( start3DU, linewidth3DU / 2, *aOwner ) );
718 }
719 else
720 {
721 aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth3DU, *aOwner ) );
722 }
723
724 break;
725 }
726
727 case SHAPE_T::BEZIER:
728 case SHAPE_T::POLY:
729 {
730 SHAPE_POLY_SET polyList;
731
733 ERROR_INSIDE );
734
735 if( polyList.IsEmpty() ) // Just for caution
736 break;
737
738 ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aOwner );
739 break;
740 }
741
742 default:
743 wxFAIL_MSG( wxT( "BOARD_ADAPTER::addShape no implementation for " )
744 + aShape->SHAPE_T_asString() );
745 break;
746 }
747 }
748 else if( linewidth > 0 )
749 {
750 std::vector<SHAPE*> shapes = aShape->MakeEffectiveShapes( true );
751 SFVEC2F a3DU;
752 SFVEC2F b3DU;
753 double width3DU = TO_3DU( linewidth );
754
755 const PCB_PLOT_PARAMS& plotParams = aShape->GetBoard()->GetPlotOptions();
756 KIGFX::PCB_RENDER_SETTINGS renderSettings;
757
758 renderSettings.SetDashLengthRatio( plotParams.GetDashedLineDashRatio() );
759 renderSettings.SetGapLengthRatio( plotParams.GetDashedLineGapRatio() );
760
761 for( SHAPE* shape : shapes )
762 {
763 STROKE_PARAMS::Stroke( shape, lineStyle, linewidth, &renderSettings,
764 [&]( const VECTOR2I& a, const VECTOR2I& b )
765 {
766 a3DU = TO_SFVEC2F( a );
767 b3DU = TO_SFVEC2F( b );
768
769 if( Is_segment_a_circle( a3DU, b3DU ) )
770 aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aOwner ) );
771 else
772 aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aOwner ) );
773 } );
774 }
775
776 for( SHAPE* shape : shapes )
777 delete shape;
778 }
779}
780
781
783 PCB_LAYER_ID aLayerId )
784{
785 // This convert the poly in outline and holes
786 ConvertPolygonToTriangles( *aZone->GetFilledPolysList( aLayerId ), *aContainer,
787 m_biuTo3Dunits, *aZone );
788}
789
790
792 int aWidth )
793{
794 if( aPad->GetShape() == PAD_SHAPE::CIRCLE ) // Draw a ring
795 {
796 const SFVEC2F center3DU = TO_SFVEC2F( aPad->ShapePos() );
797 const int radius = aPad->GetSize().x / 2;
798 const float inner_radius3DU = TO_3DU( radius - aWidth / 2 );
799 const float outer_radius3DU = TO_3DU( radius + aWidth / 2 );
800
801 aContainer->Add( new RING_2D( center3DU, inner_radius3DU, outer_radius3DU, *aPad ) );
802
803 return;
804 }
805
806 // For other shapes, add outlines as thick segments in polygon buffer
807 const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon();
808 const SHAPE_LINE_CHAIN& path = corners->COutline( 0 );
809
810 for( int j = 0; j < path.PointCount(); j++ )
811 {
812 SFVEC2F start3DU = TO_SFVEC2F( path.CPoint( j ) );
813 SFVEC2F end3DU = TO_SFVEC2F( path.CPoint( j + 1 ) );
814
815 if( aWidth == 0 )
816 {
817 // Don't attempt to render degenerate shapes
818 }
819 else if( Is_segment_a_circle( start3DU, end3DU ) )
820 {
821 // Cannot add segments that have the same start and end point
822 aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), *aPad ) );
823 }
824 else
825 {
826 aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), *aPad ) );
827 }
828 }
829}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:121
void addPads(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads, bool aSkipNonPlatedPads)
void addFootprintShapes(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId)
void addSolidAreasShapes(const ZONE *aZone, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId)
void createPadWithMargin(const PAD *aPad, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayer, const VECTOR2I &aMargin) const
const BOARD * GetBoard() const noexcept
void transformArcToSegments(const VECTOR2I &aCentre, const VECTOR2I &aStart, const EDA_ANGLE &aArcAngle, int aCircleToSegmentsCount, int aWidth, CONTAINER_2D_BASE *aDstContainer, const BOARD_ITEM &aOwner)
void buildPadOutlineAsSegments(const PAD *aPad, CONTAINER_2D_BASE *aDstContainer, int aWidth)
void addShape(const PCB_SHAPE *aShape, CONTAINER_2D_BASE *aContainer, const BOARD_ITEM *aOwner)
void createTrack(const PCB_TRACK *aTrack, CONTAINER_2D_BASE *aDstContainer)
unsigned int GetCircleSegmentCount(float aDiameter3DU) const
OBJECT_2D * createPadWithDrill(const PAD *aPad, int aInflateValue)
double m_biuTo3Dunits
Scale factor to convert board internal units to 3D units normalized between -1.0 and 1....
void addText(const EDA_TEXT *aText, CONTAINER_2D_BASE *aDstContainer, const BOARD_ITEM *aOwner)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:70
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:192
virtual bool IsKnockout() const
Definition: board_item.h:262
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:43
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:635
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:704
int GetSizeMax() const
Definition: box2.h:200
void Add(OBJECT_2D *aObject)
Definition: container_2d.h:49
double AsDegrees() const
Definition: eda_angle.h:149
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:585
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.h:289
bool IsFilled() const
Definition: eda_shape.h:90
int GetRadius() const
Definition: eda_shape.cpp:523
SHAPE_T GetShape() const
Definition: eda_shape.h:113
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:145
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:120
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1035
int GetWidth() const
Definition: eda_shape.h:109
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:75
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:72
void TransformBoundingBoxToPolygon(SHAPE_POLY_SET *aBuffer, int aClearance) const
Convert the text bounding box to a rectangular polygon depending on the text orientation,...
Definition: eda_text.cpp:947
bool IsItalic() const
Definition: eda_text.h:130
virtual bool IsVisible() const
Definition: eda_text.h:136
KIFONT::FONT * GetFont() const
Definition: eda_text.h:188
virtual EDA_ANGLE GetDrawRotation() const
Definition: eda_text.h:317
virtual VECTOR2I GetDrawPos() const
Definition: eda_text.h:318
const TEXT_ATTRIBUTES & GetAttributes() const
Definition: eda_text.h:172
int GetEffectiveTextPenWidth(int aDefaultPenWidth=0) const
The EffectiveTextPenWidth uses the text thickness if > 1 or aDefaultPenWidth.
Definition: eda_text.cpp:299
bool IsBold() const
Definition: eda_text.h:133
virtual wxString GetShownText(int aDepth=0, bool aAllowExtraText=true) const
Return the string actually shown after processing of the base text.
Definition: eda_text.h:98
PADS & Pads()
Definition: footprint.h:170
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:567
DRAWINGS & GraphicalItems()
Definition: footprint.h:173
FP_TEXT & Reference()
Definition: footprint.h:568
FONT is an abstract base class for both outline and stroke fonts.
Definition: font.h:105
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false)
Definition: font.cpp:138
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttrs) const
Draw a string.
Definition: font.cpp:232
virtual bool IsStroke() const
Definition: font.h:112
virtual bool IsOutline() const
Definition: font.h:113
PCB specific render settings.
Definition: pcb_painter.h:72
void SetGapLengthRatio(double aRatio)
void SetDashLengthRatio(double aRatio)
Definition: pad.h:60
VECTOR2I ShapePos() const
Definition: pad.cpp:770
PAD_SHAPE GetShape() const
Definition: pad.h:195
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon() const
Definition: pad.cpp:361
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING flashPTHPads=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: pad.cpp:370
bool HasHole() const override
Definition: pad.h:107
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition: pad.cpp:407
const VECTOR2I & GetSize() const
Definition: pad.h:258
double GetRadius() const
Definition: pcb_track.cpp:1140
EDA_ANGLE GetAngle() const
Definition: pcb_track.cpp:1146
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:322
Abstract dimension API.
int GetLineThickness() const
const std::vector< std::shared_ptr< SHAPE > > & GetShapes() const
Parameters and options when plotting/printing a board.
double GetDashedLineGapRatio() const
double GetDashedLineDashRatio() const
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_shape.h:101
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:67
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
Definition: pcb_shape.cpp:390
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:71
int GetWidth() const
Definition: pcb_track.h:108
const VECTOR2I & GetStart() const
Definition: pcb_track.h:114
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:111
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
int GetWidth() const
Definition: shape_arc.h:157
const SHAPE_LINE_CHAIN ConvertToPolyline(double aAccuracy=DefaultAccuracyForPCB(), double *aEffectiveAccuracy=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
Definition: shape_arc.cpp:470
int GetRadius() const
Definition: shape_circle.h:108
const VECTOR2I GetCenter() const
Definition: shape_circle.h:113
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int SegmentCount() const
Return the number of segments in this line chain.
SEG Segment(int aIndex)
Return a copy of the aIndex-th segment in the line chain.
Represent a set of closed polygons.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
bool IsEmpty() const
void Inflate(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
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...
void Simplify(POLYGON_MODE aFastMode)
int NewOutline()
Creates a new hole in a given outline.
const VECTOR2I & GetPosition() const
Definition: shape_rect.h:127
const VECTOR2I GetSize() const
Definition: shape_rect.h:135
const SEG & GetSeg() const
int GetWidth() const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:42
const SHAPE_LINE_CHAIN & Vertices() const
Return the list of vertices defining this simple polygon.
Definition: shape_simple.h:124
An abstract shape on 2D plane.
Definition: shape.h:124
static void Stroke(const SHAPE *aShape, PLOT_DASH_TYPE aLineStyle, int aWidth, const KIGFX::RENDER_SETTINGS *aRenderSettings, std::function< void(const VECTOR2I &a, const VECTOR2I &b)> aStroker)
PLOT_DASH_TYPE GetPlotStyle() const
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:608
#define TO_3DU(x)
#define TO_SFVEC2F(vec)
static constexpr EDA_ANGLE & ANGLE_360
Definition: eda_angle.h:435
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:429
a few functions useful in geometry calculations.
@ ERROR_INSIDE
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
int GetKnockoutTextMargin(const VECTOR2I &aSize, int aThickness)
Returns the margin for knocking out text.
Definition: gr_text.h:104
static const wxChar * m_logTrace
Trace mask used to enable or disable debug output for this class.
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:827
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ F_Paste
Definition: layer_ids.h:101
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ F_Mask
Definition: layer_ids.h:107
@ B_Paste
Definition: layer_ids.h:100
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
@ NPTH
like PAD_PTH, but not plated
@ PAD_DRILL_SHAPE_CIRCLE
Definition: pad_shapes.h:70
bool Is_segment_a_circle(const SFVEC2F &aStart, const SFVEC2F &aEnd)
Check if segment start and end is very close to each other.
@ SH_POLY_SET
set of polygons (with holes, etc.)
Definition: shape.h:50
@ SH_RECT
axis-aligned rectangle
Definition: shape.h:45
@ SH_CIRCLE
circle
Definition: shape.h:48
@ SH_SIMPLE
simple polygon
Definition: shape.h:49
@ SH_SEGMENT
line segment
Definition: shape.h:46
@ SH_ARC
circular arc
Definition: shape.h:52
static wxString SHAPE_TYPE_asString(SHAPE_TYPE a)
Definition: shape.h:57
std::vector< FAB_LAYER_COLOR > dummy
PLOT_DASH_TYPE
Dashed line types.
Definition: stroke_params.h:48
constexpr int delta
void ConvertPolygonToTriangles(const SHAPE_POLY_SET &aPolyList, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, const BOARD_ITEM &aBoardItem)
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
@ PCB_FP_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:95
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:102
@ PCB_FP_TEXTBOX_T
class FP_TEXTBOX, wrapped text in a footprint
Definition: typeinfo.h:93
@ PCB_FP_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:97
@ PCB_FP_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:99
@ PCB_FP_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:96
@ PCB_FP_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:98
@ PCB_FP_TEXT_T
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:103
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42