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 
25 /**
26  * @file  create_graphic_brd_items.cpp
27  * @brief This file implements the creation of 2D graphic primitives of pcb items:
28  *  pads, tracks, drawsegments, texts....
29  * It is based on the function found in the files:
30  *  board_items_to_polygon_shape_transform.cpp
31  */
32 
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 <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 <string_utils.h>
45 #include <fp_text.h>
46 #include <convert_basic_shapes_to_polygon.h>
47 #include <trigo.h>
48 #include <geometry/shape_segment.h>
49 #include <geometry/geometry_utils.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;
63 static CONTAINER_2D_BASE* s_dstcontainer = nullptr;
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:
addTextSegmToContainer(int x0,int y0,int xf,int yf,void * aData)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 ) )
75         s_dstcontainer->Add( new FILLED_CIRCLE_2D( start3DU, ( s_textWidth / 2 ) * s_biuTo3Dunits,
76                                                    *s_boardItem) );
77     else
78         s_dstcontainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, s_textWidth * s_biuTo3Dunits,
79                                                    *s_boardItem ) );
80 }
81 
82 
83 // Based on
84 // void PCB_TEXT::TransformTextShapeWithClearanceToPolygon
85 // board_items_to_polygon_shape_transform.cpp
addShapeWithClearance(const PCB_TEXT * aText,CONTAINER_2D_BASE * aDstContainer,PCB_LAYER_ID aLayerId,int aClearanceValue)86 void BOARD_ADAPTER::addShapeWithClearance( const PCB_TEXT* aText, CONTAINER_2D_BASE* aDstContainer,
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 );
97     s_biuTo3Dunits = m_biuTo3Dunits;
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 
addShapeWithClearance(const PCB_DIMENSION_BASE * aDimension,CONTAINER_2D_BASE * aDstContainer,PCB_LAYER_ID aLayerId,int aClearanceValue)113 void BOARD_ADAPTER::addShapeWithClearance( const PCB_DIMENSION_BASE* aDimension,
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
addFootprintShapesWithClearance(const FOOTPRINT * aFootprint,CONTAINER_2D_BASE * aDstContainer,PCB_LAYER_ID aLayerId,int aInflateValue)164 void BOARD_ADAPTER::addFootprintShapesWithClearance( const FOOTPRINT* aFootprint,
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;
210     s_biuTo3Dunits = m_biuTo3Dunits;
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 
createTrack(const PCB_TRACK * aTrack,CONTAINER_2D_BASE * aDstContainer,int aClearanceValue)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 
createPadWithClearance(const PAD * aPad,CONTAINER_2D_BASE * aDstContainer,PCB_LAYER_ID aLayer,const wxSize & aClearanceValue) const308 void BOARD_ADAPTER::createPadWithClearance( const PAD* aPad, CONTAINER_2D_BASE* aDstContainer,
309                                             PCB_LAYER_ID aLayer,
310                                             const wxSize& aClearanceValue ) const
311 {
312     SHAPE_POLY_SET poly;
313     wxSize clearance = aClearanceValue;
314 
315     // Our shape-based builder can't handle negative or differing x:y clearance values (the
316     // former are common for solder paste while the later get generated when a relative paste
317     // margin is used with an oblong pad).  So we apply this huge hack and fake a larger pad to
318     // run the general-purpose polygon builder on.
319     // Of course being a hack it falls down when dealing with custom shape pads (where the size
320     // is only the size of the anchor), so for those we punt and just use aClearanceValue.x.
321 
322     if( ( clearance.x < 0 || clearance.x != clearance.y )
323             && aPad->GetShape() != PAD_SHAPE::CUSTOM )
324     {
325         wxSize dummySize = aPad->GetSize() + clearance + clearance;
326 
327         if( dummySize.x <= 0 || dummySize.y <= 0 )
328             return;
329 
330         PAD dummy( *aPad );
331         dummy.SetSize( dummySize );
332         dummy.TransformShapeWithClearanceToPolygon( poly, aLayer, 0, ARC_HIGH_DEF, ERROR_INSIDE );
333         clearance = { 0, 0 };
334     }
335     else
336     {
337         auto padShapes = std::static_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );
338 
339         for( const SHAPE* shape : padShapes->Shapes() )
340         {
341             switch( shape->Type() )
342             {
343             case SH_SEGMENT:
344             {
345                 const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape;
346                 const SFVEC2F        start3DU(  seg->GetSeg().A.x * m_biuTo3Dunits,
347                                                -seg->GetSeg().A.y * m_biuTo3Dunits );
348                 const SFVEC2F        end3DU  (  seg->GetSeg().B.x * m_biuTo3Dunits,
349                                                -seg->GetSeg().B.y * m_biuTo3Dunits );
350                 const int            width = seg->GetWidth() + clearance.x * 2;
351 
352                  // Cannot add segments that have the same start and end point
353                 if( Is_segment_a_circle( start3DU, end3DU ) )
354                 {
355                     aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU,
356                                                               ( width / 2 ) * m_biuTo3Dunits,
357                                                               *aPad ) );
358                 }
359                 else
360                 {
361                     aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU,
362                                                               width * m_biuTo3Dunits,
363                                                               *aPad ) );
364                 }
365             }
366                 break;
367 
368             case SH_CIRCLE:
369             {
370                 const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape;
371                 const int           radius = circle->GetRadius() + clearance.x;
372                 const SFVEC2F       center(  circle->GetCenter().x * m_biuTo3Dunits,
373                                              -circle->GetCenter().y * m_biuTo3Dunits );
374 
375                 aDstContainer->Add( new FILLED_CIRCLE_2D( center, radius * m_biuTo3Dunits,
376                                                           *aPad ) );
377             }
378                 break;
379 
380             case SH_RECT:
381             {
382                 SHAPE_RECT* rect = (SHAPE_RECT*) shape;
383 
384                 poly.NewOutline();
385                 poly.Append( rect->GetPosition() );
386                 poly.Append( rect->GetPosition().x + rect->GetSize().x, rect->GetPosition().y );
387                 poly.Append( rect->GetPosition() + rect->GetSize() );
388                 poly.Append( rect->GetPosition().x, rect->GetPosition().y + rect->GetSize().y );
389             }
390                 break;
391 
392             case SH_SIMPLE:
393                 poly.AddOutline( static_cast<const SHAPE_SIMPLE*>( shape )->Vertices() );
394                 break;
395 
396             case SH_POLY_SET:
397                 poly = *(SHAPE_POLY_SET*) shape;
398                 break;
399 
400             case SH_ARC:
401             {
402                 SHAPE_ARC* arc = (SHAPE_ARC*) shape;
403                 SHAPE_LINE_CHAIN l = arc->ConvertToPolyline();
404 
405                 for( int i = 0; i < l.SegmentCount(); i++ )
406                 {
407                     SHAPE_SEGMENT seg( l.Segment( i ).A, l.Segment( i ).B, arc->GetWidth() );
408                     const SFVEC2F start3DU(  seg.GetSeg().A.x * m_biuTo3Dunits,
409                                              -seg.GetSeg().A.y * m_biuTo3Dunits );
410                     const SFVEC2F end3DU( seg.GetSeg().B.x * m_biuTo3Dunits,
411                                           -seg.GetSeg().B.y * m_biuTo3Dunits );
412                     const int width = arc->GetWidth() + clearance.x * 2;
413 
414                      // Cannot add segments that have the same start and end point
415                     if( Is_segment_a_circle( start3DU, end3DU ) )
416                     {
417                         aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU,
418                                                                   ( width / 2 ) * m_biuTo3Dunits,
419                                                                   *aPad ) );
420                     }
421                     else
422                     {
423                         aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU,
424                                                                   width * m_biuTo3Dunits,
425                                                                   *aPad ) );
426                     }
427                 }
428             }
429                 break;
430 
431             default:
432                 wxFAIL_MSG( "BOARD_ADAPTER::createPadWithClearance no implementation for "
433                             + SHAPE_TYPE_asString( shape->Type() ) );
434                 break;
435             }
436         }
437     }
438 
439     if( !poly.IsEmpty() )
440     {
441         if( clearance.x )
442             poly.Inflate( clearance.x, 32 );
443 
444         // Add the PAD polygon
445         ConvertPolygonToTriangles( poly, *aDstContainer, m_biuTo3Dunits, *aPad );
446     }
447 }
448 
449 
createPadWithDrill(const PAD * aPad,int aInflateValue)450 OBJECT_2D* BOARD_ADAPTER::createPadWithDrill( const PAD* aPad, int aInflateValue )
451 {
452     wxSize drillSize = aPad->GetDrillSize();
453 
454     if( !drillSize.x || !drillSize.y )
455     {
456         wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::createPadWithDrill - found an invalid pad" ) );
457         return nullptr;
458     }
459 
460     if( drillSize.x == drillSize.y )    // usual round hole
461     {
462         const int radius = ( drillSize.x / 2 ) + aInflateValue;
463 
464         const SFVEC2F center(  aPad->GetPosition().x * m_biuTo3Dunits,
465                               -aPad->GetPosition().y * m_biuTo3Dunits );
466 
467         return new FILLED_CIRCLE_2D( center, radius * m_biuTo3Dunits, *aPad );
468 
469     }
470     else                                // Oblong hole
471     {
472         const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
473         float width = seg->GetWidth() + aInflateValue * 2;
474 
475         SFVEC2F start3DU(  seg->GetSeg().A.x * m_biuTo3Dunits,
476                           -seg->GetSeg().A.y * m_biuTo3Dunits );
477 
478         SFVEC2F end3DU (  seg->GetSeg().B.x * m_biuTo3Dunits,
479                          -seg->GetSeg().B.y * m_biuTo3Dunits );
480 
481         return new ROUND_SEGMENT_2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad );
482     }
483 
484     return nullptr;
485 }
486 
487 
addPadsWithClearance(const FOOTPRINT * aFootprint,CONTAINER_2D_BASE * aDstContainer,PCB_LAYER_ID aLayerId,int aInflateValue,bool aSkipNPTHPadsWihNoCopper,bool aSkipPlatedPads,bool aSkipNonPlatedPads)488 void BOARD_ADAPTER::addPadsWithClearance( const FOOTPRINT* aFootprint,
489                                           CONTAINER_2D_BASE* aDstContainer,
490                                           PCB_LAYER_ID aLayerId, int aInflateValue,
491                                           bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads,
492                                           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
504         // shape size and pos is the same as their hole:
505         if( aSkipNPTHPadsWihNoCopper && ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
506         {
507             if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
508             {
509                 switch( pad->GetShape() )
510                 {
511                 case PAD_SHAPE::CIRCLE:
512                     if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
513                         continue;
514 
515                     break;
516 
517                 case PAD_SHAPE::OVAL:
518                     if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
519                         continue;
520 
521                     break;
522 
523                 default:
524                     break;
525                 }
526             }
527         }
528 
529         const bool isPlated = ( ( aLayerId == F_Cu ) && pad->FlashLayer( F_Mask ) ) ||
530                               ( ( aLayerId == B_Cu ) && pad->FlashLayer( B_Mask ) );
531 
532         if( aSkipPlatedPads && isPlated )
533             continue;
534 
535         if( aSkipNonPlatedPads && !isPlated )
536             continue;
537 
538         wxSize margin( aInflateValue, aInflateValue );
539 
540         switch( aLayerId )
541         {
542         case F_Mask:
543         case B_Mask:
544             margin.x += pad->GetSolderMaskMargin();
545             margin.y += pad->GetSolderMaskMargin();
546             break;
547 
548         case F_Paste:
549         case B_Paste:
550             margin += pad->GetSolderPasteMargin();
551             break;
552 
553         default:
554             break;
555         }
556 
557         createPadWithClearance( pad, aDstContainer, aLayerId, margin );
558     }
559 }
560 
561 
562 // based on TransformArcToPolygon function from
563 // common/convert_basic_shapes_to_polygon.cpp
transformArcToSegments(const wxPoint & aCentre,const wxPoint & aStart,double aArcAngle,int aCircleToSegmentsCount,int aWidth,CONTAINER_2D_BASE * aDstContainer,const BOARD_ITEM & aBoardItem)564 void BOARD_ADAPTER::transformArcToSegments( const wxPoint& aCentre, const wxPoint& aStart,
565                                             double aArcAngle, int aCircleToSegmentsCount,
566                                             int aWidth, CONTAINER_2D_BASE* aDstContainer,
567                                             const BOARD_ITEM& aBoardItem )
568 {
569     wxPoint arc_start, arc_end;
570     int     delta = 3600 / aCircleToSegmentsCount;   // rotate angle in 0.1 degree
571 
572     arc_end = arc_start = aStart;
573 
574     if( aArcAngle != 3600 )
575     {
576         RotatePoint( &arc_end, aCentre, -aArcAngle );
577     }
578 
579     if( aArcAngle < 0 )
580     {
581         std::swap( arc_start, arc_end );
582         aArcAngle = -aArcAngle;
583     }
584 
585     // Compute the ends of segments and creates poly
586     wxPoint curr_end    = arc_start;
587     wxPoint curr_start  = arc_start;
588 
589     for( int ii = delta; ii < aArcAngle; ii += delta )
590     {
591         curr_end = arc_start;
592         RotatePoint( &curr_end, aCentre, -ii );
593 
594         const SFVEC2F start3DU( curr_start.x * m_biuTo3Dunits, -curr_start.y * m_biuTo3Dunits );
595         const SFVEC2F end3DU  ( curr_end.x   * m_biuTo3Dunits, -curr_end.y   * m_biuTo3Dunits );
596 
597         if( Is_segment_a_circle( start3DU, end3DU ) )
598         {
599             aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
600                                                       aBoardItem ) );
601         }
602         else
603         {
604             aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
605                                                       aBoardItem ) );
606         }
607 
608         curr_start = curr_end;
609     }
610 
611     if( curr_end != arc_end )
612     {
613         const SFVEC2F start3DU( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits );
614         const SFVEC2F end3DU  ( arc_end.x  * m_biuTo3Dunits, -arc_end.y  * m_biuTo3Dunits );
615 
616         if( Is_segment_a_circle( start3DU, end3DU ) )
617         {
618             aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
619                                                       aBoardItem ) );
620         }
621         else
622         {
623             aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
624                                                       aBoardItem ) );
625         }
626     }
627 }
628 
629 
630 // Based on
631 // TransformShapeWithClearanceToPolygon
632 // board_items_to_polygon_shape_transform.cpp#L431
addShapeWithClearance(const PCB_SHAPE * aShape,CONTAINER_2D_BASE * aDstContainer,PCB_LAYER_ID aLayerId,int aClearanceValue)633 void BOARD_ADAPTER::addShapeWithClearance( const PCB_SHAPE* aShape,
634                                            CONTAINER_2D_BASE* aDstContainer, PCB_LAYER_ID aLayerId,
635                                            int aClearanceValue )
636 {
637     // The full width of the lines to create
638     // The extra 1 protects the inner/outer radius values from degeneracy
639     const int linewidth = aShape->GetWidth() + ( 2 * aClearanceValue ) + 1;
640 
641     switch( aShape->GetShape() )
642     {
643     case SHAPE_T::CIRCLE:
644     {
645         const SFVEC2F center3DU( aShape->GetCenter().x * m_biuTo3Dunits,
646                                  -aShape->GetCenter().y * m_biuTo3Dunits );
647 
648         float inner_radius = ( aShape->GetRadius() - linewidth / 2 ) * m_biuTo3Dunits;
649         float outer_radius = ( aShape->GetRadius() + linewidth / 2 ) * m_biuTo3Dunits;
650 
651         if( inner_radius < 0 )
652             inner_radius = 0;
653 
654         if( aShape->IsFilled() )
655             aDstContainer->Add( new FILLED_CIRCLE_2D( center3DU, outer_radius, *aShape ) );
656         else
657             aDstContainer->Add( new RING_2D( center3DU, inner_radius, outer_radius, *aShape ) );
658     }
659         break;
660 
661     case SHAPE_T::RECT:
662         if( aShape->IsFilled() )
663         {
664             SHAPE_POLY_SET polyList;
665 
666             aShape->TransformShapeWithClearanceToPolygon( polyList, aLayerId, 0,
667                                                           ARC_HIGH_DEF, ERROR_INSIDE );
668 
669             polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
670 
671             ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aShape );
672         }
673         else
674         {
675             std::vector<wxPoint> pts = aShape->GetRectCorners();
676 
677             const SFVEC2F topLeft3DU(  pts[0].x * m_biuTo3Dunits, -pts[0].y * m_biuTo3Dunits );
678             const SFVEC2F topRight3DU( pts[1].x * m_biuTo3Dunits, -pts[1].y * m_biuTo3Dunits );
679             const SFVEC2F botRight3DU( pts[2].x * m_biuTo3Dunits, -pts[2].y * m_biuTo3Dunits );
680             const SFVEC2F botLeft3DU(  pts[3].x * m_biuTo3Dunits, -pts[3].y * m_biuTo3Dunits );
681 
682             aDstContainer->Add( new ROUND_SEGMENT_2D( topLeft3DU, topRight3DU,
683                                                       linewidth * m_biuTo3Dunits, *aShape ) );
684             aDstContainer->Add( new ROUND_SEGMENT_2D( topRight3DU, botRight3DU,
685                                                       linewidth * m_biuTo3Dunits, *aShape ) );
686             aDstContainer->Add( new ROUND_SEGMENT_2D( botRight3DU, botLeft3DU,
687                                                       linewidth * m_biuTo3Dunits, *aShape ) );
688             aDstContainer->Add( new ROUND_SEGMENT_2D( botLeft3DU, topLeft3DU,
689                                                       linewidth * m_biuTo3Dunits, *aShape ) );
690         }
691         break;
692 
693     case SHAPE_T::ARC:
694     {
695         unsigned int segCount = GetCircleSegmentCount( aShape->GetBoundingBox().GetSizeMax() );
696 
697         transformArcToSegments( aShape->GetCenter(), aShape->GetStart(), aShape->GetArcAngle(),
698                                 segCount, linewidth, aDstContainer, *aShape );
699     }
700     break;
701 
702     case SHAPE_T::SEGMENT:
703     {
704         const SFVEC2F start3DU( aShape->GetStart().x * m_biuTo3Dunits,
705                                 -aShape->GetStart().y * m_biuTo3Dunits );
706 
707         const SFVEC2F end3DU  ( aShape->GetEnd().x * m_biuTo3Dunits,
708                                 -aShape->GetEnd().y * m_biuTo3Dunits );
709 
710         if( Is_segment_a_circle( start3DU, end3DU ) )
711         {
712             aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( linewidth / 2 ) * m_biuTo3Dunits,
713                                                      *aShape ) );
714         }
715         else
716         {
717             aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth * m_biuTo3Dunits,
718                                                       *aShape ) );
719         }
720     }
721     break;
722 
723     case SHAPE_T::BEZIER:
724     case SHAPE_T::POLY:
725     {
726         SHAPE_POLY_SET polyList;
727 
728         aShape->TransformShapeWithClearanceToPolygon( polyList, aLayerId, 0,
729                                                       ARC_HIGH_DEF, ERROR_INSIDE );
730 
731         polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
732 
733         if( polyList.IsEmpty() ) // Just for caution
734             break;
735 
736         ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aShape );
737     }
738     break;
739 
740     default:
741         wxFAIL_MSG( "BOARD_ADAPTER::addShapeWithClearance no implementation for "
742                     + aShape->SHAPE_T_asString() );
743         break;
744     }
745 }
746 
747 
748 // Based on
749 // TransformSolidAreasShapesToPolygonSet
750 // board_items_to_polygon_shape_transform.cpp
addSolidAreasShapes(const ZONE * aZoneContainer,CONTAINER_2D_BASE * aDstContainer,PCB_LAYER_ID aLayerId)751 void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZoneContainer,
752                                          CONTAINER_2D_BASE* aDstContainer, PCB_LAYER_ID aLayerId )
753 {
754     // Copy the polys list because we have to simplify it
755     SHAPE_POLY_SET polyList = SHAPE_POLY_SET( aZoneContainer->GetFilledPolysList( aLayerId ) );
756 
757     // This convert the poly in outline and holes
758     ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aZoneContainer );
759 
760     // add filled areas outlines, which are drawn with thick lines segments
761     // but only if filled polygons outlines have thickness
762     if( !aZoneContainer->GetFilledPolysUseThickness() )
763         return;
764 
765     float line_thickness = aZoneContainer->GetMinThickness() * m_biuTo3Dunits;
766 
767     for( int i = 0; i < polyList.OutlineCount(); ++i )
768     {
769         // Add outline
770         const SHAPE_LINE_CHAIN& pathOutline = polyList.COutline( i );
771 
772         for( int j = 0; j < pathOutline.PointCount(); ++j )
773         {
774             const VECTOR2I& a = pathOutline.CPoint( j );
775             const VECTOR2I& b = pathOutline.CPoint( j + 1 );
776 
777             SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
778             SFVEC2F end3DU  ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
779 
780             if( Is_segment_a_circle( start3DU, end3DU ) )
781             {
782                 float radius = line_thickness/2;
783 
784                 if( radius > 0.0 )  // degenerated circles crash 3D viewer
785                     aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius, *aZoneContainer ) );
786             }
787             else
788             {
789                 aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, line_thickness,
790                                                           *aZoneContainer ) );
791             }
792         }
793 
794         // Add holes (of the poly, ie: the open parts) for this outline
795         for( int h = 0; h < polyList.HoleCount( i ); ++h )
796         {
797             const SHAPE_LINE_CHAIN& pathHole = polyList.CHole( i, h );
798 
799             for( int j = 0; j < pathHole.PointCount(); j++ )
800             {
801                 const VECTOR2I& a = pathHole.CPoint( j );
802                 const VECTOR2I& b = pathHole.CPoint( j + 1 );
803 
804                 SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
805                 SFVEC2F end3DU  ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
806 
807                 if( Is_segment_a_circle( start3DU, end3DU ) )
808                 {
809                     float radius = line_thickness/2;
810 
811                     if( radius > 0.0 )  // degenerated circles crash 3D viewer
812                         aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius,
813                                                                   *aZoneContainer ) );
814                 }
815                 else
816                 {
817                     aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, line_thickness,
818                                                               *aZoneContainer ) );
819                 }
820             }
821         }
822     }
823 }
824 
825 
buildPadOutlineAsSegments(const PAD * aPad,CONTAINER_2D_BASE * aDstContainer,int aWidth)826 void BOARD_ADAPTER::buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BASE* aDstContainer,
827                                                int aWidth )
828 {
829     if( aPad->GetShape() == PAD_SHAPE::CIRCLE )    // Draw a ring
830     {
831         const SFVEC2F center3DU( aPad->ShapePos().x * m_biuTo3Dunits,
832                                  -aPad->ShapePos().y * m_biuTo3Dunits );
833 
834         const int radius = aPad->GetSize().x / 2;
835         const float inner_radius = ( radius - aWidth / 2 ) * m_biuTo3Dunits;
836         const float outer_radius = ( radius + aWidth / 2 ) * m_biuTo3Dunits;
837 
838         aDstContainer->Add( new RING_2D( center3DU, inner_radius, outer_radius, *aPad ) );
839 
840         return;
841     }
842 
843     // For other shapes, add outlines as thick segments in polygon buffer
844     const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon();
845     const SHAPE_LINE_CHAIN&                path = corners->COutline( 0 );
846 
847     for( int j = 0; j < path.PointCount(); j++ )
848     {
849         const VECTOR2I& a = path.CPoint( j );
850         const VECTOR2I& b = path.CPoint( j + 1 );
851 
852         SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
853         SFVEC2F end3DU  ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
854 
855         if( Is_segment_a_circle( start3DU, end3DU ) )
856         {
857             aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
858                                                       *aPad ) );
859         }
860         else
861         {
862             aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
863                                                       *aPad ) );
864         }
865     }
866 }
867