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