KiCad PCB EDA Suite
Loading...
Searching...
No Matches
odb_feature.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 * Author: SYSUEric <[email protected]>.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "odb_feature.h"
22
23#include <sstream>
24#include <map>
25
26#include <wx/log.h>
27
28#include "pcb_shape.h"
29#include "odb_defines.h"
30#include "pcb_track.h"
31#include "pcb_textbox.h"
32#include "zone.h"
33#include "board.h"
35#include "geometry/eda_angle.h"
36#include "odb_eda_data.h"
37#include "pcb_io_odbpp.h"
38#include <callback_gal.h>
39#include <string_utils.h>
40
41
42void FEATURES_MANAGER::AddFeatureLine( const VECTOR2I& aStart, const VECTOR2I& aEnd,
43 uint64_t aWidth )
44{
45 AddFeature<ODB_LINE>( ODB::AddXY( aStart ), ODB::AddXY( aEnd ),
47}
48
49
50void FEATURES_MANAGER::AddFeatureArc( const VECTOR2I& aStart, const VECTOR2I& aEnd,
51 const VECTOR2I& aCenter, uint64_t aWidth,
52 ODB_DIRECTION aDirection )
53{
54 AddFeature<ODB_ARC>( ODB::AddXY( aStart ), ODB::AddXY( aEnd ), ODB::AddXY( aCenter ),
55 AddCircleSymbol( ODB::SymDouble2String( aWidth ) ), aDirection );
56}
57
58
59void FEATURES_MANAGER::AddPadCircle( const VECTOR2I& aCenter, uint64_t aDiameter,
60 const EDA_ANGLE& aAngle, bool aMirror,
61 double aResize /*= 1.0 */ )
62{
64 AddCircleSymbol( ODB::SymDouble2String( aDiameter ) ), aAngle, aMirror,
65 aResize );
66}
67
68
69bool FEATURES_MANAGER::AddContour( const SHAPE_POLY_SET& aPolySet, int aOutline /*= 0*/,
70 FILL_T aFillType /*= FILL_T::FILLED_SHAPE*/ )
71{
72 // todo: args modify aPolySet.Polygon( aOutline ) instead of aPolySet
73
74 if( aPolySet.OutlineCount() < ( aOutline + 1 ) )
75 return false;
76
77 AddFeatureSurface( aPolySet.Polygon( aOutline ), aFillType );
78
79 return true;
80}
81
82
84{
85 int stroke_width = aShape.GetWidth();
86
87 switch( aShape.GetShape() )
88 {
89 case SHAPE_T::CIRCLE:
90 {
91 int diameter = aShape.GetRadius() * 2;
93 wxString innerDim = ODB::SymDouble2String( ( diameter - stroke_width / 2 ) );
94 wxString outerDim = ODB::SymDouble2String( ( stroke_width + diameter ) );
95
96 if( aShape.IsSolidFill() )
98 else
99 AddFeature<ODB_PAD>( ODB::AddXY( center ), AddRoundDonutSymbol( outerDim, innerDim ) );
100
101 break;
102 }
103
105 {
106 int width = std::abs( aShape.GetRectangleWidth() ) + stroke_width;
107 int height = std::abs( aShape.GetRectangleHeight() ) + stroke_width;
108 wxString rad = ODB::SymDouble2String( ( stroke_width / 2.0 ) );
110
111 if( aShape.IsSolidFill() )
112 {
115 ODB::SymDouble2String( height ), rad ) );
116 }
117 else
118 {
121 ODB::SymDouble2String( height ),
122 ODB::SymDouble2String( stroke_width ),
123 rad ) );
124 }
125
126 break;
127 }
128
129 case SHAPE_T::POLY:
130 {
131 int soldermask_min_thickness = 0;
132
133 // TODO: check if soldermask_min_thickness should be Stroke width
134
135 if( aLayer != UNDEFINED_LAYER && LSET( { F_Mask, B_Mask } ).Contains( aLayer ) )
136 soldermask_min_thickness = stroke_width;
137
138 int maxError = m_board->GetDesignSettings().m_MaxError;
139 SHAPE_POLY_SET poly_set;
140
141 if( soldermask_min_thickness == 0 )
142 {
143 poly_set = aShape.GetPolyShape().CloneDropTriangulation();
144 poly_set.Fracture();
145 }
146 else
147 {
148 SHAPE_POLY_SET initialPolys;
149
150 // add shapes inflated by aMinThickness/2 in areas
151 aShape.TransformShapeToPolygon( initialPolys, aLayer, 0, maxError, ERROR_OUTSIDE );
152 aShape.TransformShapeToPolygon( poly_set, aLayer, soldermask_min_thickness / 2 - 1,
153 maxError, ERROR_OUTSIDE );
154
155 poly_set.Simplify();
156 poly_set.Deflate( soldermask_min_thickness / 2 - 1,
158 poly_set.BooleanAdd( initialPolys );
159 poly_set.Fracture();
160 }
161
162 // ODB++ surface features can only represent closed polygons. We add a surface for
163 // the fill of the shape, if present, and add line segments for the outline, if present.
164 if( aShape.IsSolidFill() )
165 {
166 for( int ii = 0; ii < poly_set.OutlineCount(); ++ii )
167 {
168 AddContour( poly_set, ii, FILL_T::FILLED_SHAPE );
169
170 if( stroke_width != 0 )
171 {
172 for( int jj = 0; jj < poly_set.COutline( ii ).SegmentCount(); ++jj )
173 {
174 const SEG& seg = poly_set.COutline( ii ).CSegment( jj );
175 AddFeatureLine( seg.A, seg.B, stroke_width );
176 }
177 }
178 }
179 }
180 else
181 {
182 for( int ii = 0; ii < poly_set.OutlineCount(); ++ii )
183 {
184 for( int jj = 0; jj < poly_set.COutline( ii ).SegmentCount(); ++jj )
185 {
186 const SEG& seg = poly_set.COutline( ii ).CSegment( jj );
187 AddFeatureLine( seg.A, seg.B, stroke_width );
188 }
189 }
190 }
191
192 break;
193 }
194
195 case SHAPE_T::ARC:
196 {
198
199 AddFeatureArc( aShape.GetStart(), aShape.GetEnd(), aShape.GetCenter(), stroke_width, dir );
200 break;
201 }
202
203 case SHAPE_T::BEZIER:
204 {
205 const std::vector<VECTOR2I>& points = aShape.GetBezierPoints();
206
207 for( size_t i = 0; i < points.size() - 1; i++ )
208 AddFeatureLine( points[i], points[i + 1], stroke_width );
209
210 break;
211 }
212
213 case SHAPE_T::SEGMENT:
214 AddFeatureLine( aShape.GetStart(), aShape.GetEnd(), stroke_width );
215 break;
216
217 default:
218 wxLogError( wxT( "Unknown shape when adding ODB++ layer feature" ) );
219 break;
220 }
221
222 if( aShape.IsHatchedFill() )
223 {
224 for( int ii = 0; ii < aShape.GetHatching().OutlineCount(); ++ii )
226 }
227}
228
229
231 FILL_T aFillType /*= FILL_T::FILLED_SHAPE */ )
232{
233 AddFeature<ODB_SURFACE>( aPolygon, aFillType );
234}
235
236
238{
239 FOOTPRINT* fp = aPad.GetParentFootprint();
240 bool mirror = false;
241
242 if( aPad.GetOrientation() != ANGLE_0 )
243 {
244 if( fp && fp->IsFlipped() )
245 mirror = true;
246 }
247
248 int maxError = m_board->GetDesignSettings().m_MaxError;
249
250 VECTOR2I expansion{ 0, 0 };
251
252 if( aLayer != UNDEFINED_LAYER && LSET( { F_Mask, B_Mask } ).Contains( aLayer ) )
253 expansion.x = expansion.y = aPad.GetSolderMaskExpansion( aLayer );
254
255 if( aLayer != UNDEFINED_LAYER && LSET( { F_Paste, B_Paste } ).Contains( aLayer ) )
256 expansion = aPad.GetSolderPasteMargin( aLayer );
257
258 int mask_clearance = expansion.x;
259
260 VECTOR2I plotSize = aPad.GetSize( aLayer ) + 2 * expansion;
261
262 VECTOR2I center = aPad.ShapePos( aLayer );
263
264 wxString width = ODB::SymDouble2String( std::abs( plotSize.x ) );
265 wxString height = ODB::SymDouble2String( std::abs( plotSize.y ) );
266
267 switch( aPad.GetShape( aLayer ) )
268 {
270 {
271 wxString diam = ODB::SymDouble2String( plotSize.x );
272
274 mirror );
275
276 break;
277 }
279 {
280 if( mask_clearance > 0 )
281 {
282 wxString rad = ODB::SymDouble2String( mask_clearance );
283
284 AddFeature<ODB_PAD>( ODB::AddXY( center ), AddRoundRectSymbol( width, height, rad ),
285 aPad.GetOrientation(), mirror );
286 }
287 else
288 {
290 aPad.GetOrientation(), mirror );
291 }
292
293 break;
294 }
295 case PAD_SHAPE::OVAL:
296 {
298 aPad.GetOrientation(), mirror );
299 break;
300 }
302 {
303 wxString rad = ODB::SymDouble2String( aPad.GetRoundRectCornerRadius( aLayer ) );
304
305 AddFeature<ODB_PAD>( ODB::AddXY( center ), AddRoundRectSymbol( width, height, rad ),
306 aPad.GetOrientation(), mirror );
307
308 break;
309 }
311 {
312 int shorterSide = std::min( plotSize.x, plotSize.y );
313 int chamfer = std::max(
314 0, KiROUND( aPad.GetChamferRectRatio( aLayer ) * shorterSide ) );
315 wxString rad = ODB::SymDouble2String( chamfer );
316 int positions = aPad.GetChamferPositions( aLayer );
317
319 AddChamferRectSymbol( width, height, rad, positions ),
320 aPad.GetOrientation(), mirror );
321
322 break;
323 }
325 {
326 SHAPE_POLY_SET outline;
327
328 aPad.TransformShapeToPolygon( outline, aLayer, 0, maxError, ERROR_INSIDE );
329
330 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
331 // which can create bad shapes if margin.x is < 0
332
333 if( mask_clearance )
334 {
336 maxError );
337 }
338
339 for( int ii = 0; ii < outline.OutlineCount(); ++ii )
340 AddContour( outline, ii );
341
342 break;
343 }
345 {
346 SHAPE_POLY_SET shape;
347 aPad.MergePrimitivesAsPolygon( aLayer, &shape );
348
349 // as for custome shape, odb++ don't rotate the polygon,
350 // so we rotate the polygon in kicad anticlockwise
351
352 shape.Rotate( aPad.GetOrientation() );
353 shape.Move( center );
354
355 if( expansion != VECTOR2I( 0, 0 ) )
356 {
357 shape.InflateWithLinkedHoles( std::max( expansion.x, expansion.y ),
359 }
360
361 for( int ii = 0; ii < shape.OutlineCount(); ++ii )
362 AddContour( shape, ii );
363
364 break;
365 }
366 default: wxLogError( wxT( "Unknown pad type" ) ); break;
367 }
368}
369
370
371void FEATURES_MANAGER::InitFeatureList( PCB_LAYER_ID aLayer, std::vector<BOARD_ITEM*>& aItems )
372{
373 auto add_track = [&]( PCB_TRACK* track )
374 {
375 auto iter = GetODBPlugin()->GetViaTraceSubnetMap().find( track );
376
377 if( iter == GetODBPlugin()->GetViaTraceSubnetMap().end() )
378 {
379 wxLogError( wxT( "Failed to get subnet track data" ) );
380 return;
381 }
382
383 auto subnet = iter->second;
384
385 if( track->Type() == PCB_TRACE_T )
386 {
387 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
388 shape.SetStart( track->GetStart() );
389 shape.SetEnd( track->GetEnd() );
390 shape.SetWidth( track->GetWidth() );
391
392 AddShape( shape );
393 subnet->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::COPPER, m_layerName,
394 m_featuresList.size() - 1 );
395 }
396 else if( track->Type() == PCB_ARC_T )
397 {
398 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
399 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
400 shape.SetArcGeometry( arc->GetStart(), arc->GetMid(), arc->GetEnd() );
401 shape.SetWidth( arc->GetWidth() );
402
403 AddShape( shape );
404
405 subnet->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::COPPER, m_layerName,
406 m_featuresList.size() - 1 );
407 }
408 else
409 {
410 // add via
411 PCB_VIA* via = static_cast<PCB_VIA*>( track );
412
413 bool hole = false;
414
415 if( aLayer != PCB_LAYER_ID::UNDEFINED_LAYER )
416 {
417 hole = m_layerName.Contains( "plugging" );
418 }
419 else
420 {
421 hole = m_layerName.Contains( "drill" ) || m_layerName.Contains( "filling" )
422 || m_layerName.Contains( "capping" );
423 }
424
425 if( hole )
426 {
427 AddViaDrillHole( via, aLayer );
428 subnet->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::HOLE, m_layerName,
429 m_featuresList.size() - 1 );
430
431 // TODO: confirm TOOLING_HOLE
432 // AddSystemAttribute( *m_featuresList.back(), ODB_ATTR::PAD_USAGE::TOOLING_HOLE );
433
434 if( !m_featuresList.empty() )
435 {
438 *m_featuresList.back(),
439 ODB_ATTR::GEOMETRY{ "VIA_RoundD" + std::to_string( via->GetWidth( aLayer ) ) } );
440 }
441 }
442 else
443 {
444 // to draw via copper shape on copper layer
445 AddVia( via, aLayer );
446 subnet->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::COPPER, m_layerName,
447 m_featuresList.size() - 1 );
448
449 if( !m_featuresList.empty() )
450 {
453 *m_featuresList.back(),
454 ODB_ATTR::GEOMETRY{ "VIA_RoundD" + std::to_string( via->GetWidth( aLayer ) ) } );
455 }
456 }
457 }
458 };
459
460 auto add_zone = [&]( ZONE* zone )
461 {
462 SHAPE_POLY_SET zone_shape = zone->GetFilledPolysList( aLayer )->CloneDropTriangulation();
463
464 for( int ii = 0; ii < zone_shape.OutlineCount(); ++ii )
465 {
466 AddContour( zone_shape, ii );
467
468 auto iter = GetODBPlugin()->GetPlaneSubnetMap().find( std::make_pair( aLayer, zone ) );
469
470 if( iter == GetODBPlugin()->GetPlaneSubnetMap().end() )
471 {
472 wxLogError( wxT( "Failed to get subnet plane data" ) );
473 return;
474 }
475
476 iter->second->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::COPPER, m_layerName,
477 m_featuresList.size() - 1 );
478
479 if( zone->IsTeardropArea() && !m_featuresList.empty() )
480 AddSystemAttribute( *m_featuresList.back(), ODB_ATTR::TEAR_DROP{ true } );
481 }
482 };
483
484 auto add_text = [&]( BOARD_ITEM* item )
485 {
486 EDA_TEXT* text_item = nullptr;
487
488 if( PCB_TEXT* tmp_text = dynamic_cast<PCB_TEXT*>( item ) )
489 text_item = static_cast<EDA_TEXT*>( tmp_text );
490 else if( PCB_TEXTBOX* tmp_text = dynamic_cast<PCB_TEXTBOX*>( item ) )
491 text_item = static_cast<EDA_TEXT*>( tmp_text );
492
493 if( !text_item || !text_item->IsVisible() || text_item->GetShownText( false ).empty() )
494 return;
495
496 auto plot_text = [&]( const VECTOR2I& aPos, const wxString& aTextString,
497 const TEXT_ATTRIBUTES& aAttributes, KIFONT::FONT* aFont,
498 const KIFONT::METRICS& aFontMetrics )
499 {
501
502 TEXT_ATTRIBUTES attributes = aAttributes;
503 int penWidth = attributes.m_StrokeWidth;
504
505 if( penWidth == 0 && attributes.m_Bold ) // Use default values if aPenWidth == 0
506 penWidth =
507 GetPenSizeForBold( std::min( attributes.m_Size.x, attributes.m_Size.y ) );
508
509 if( penWidth < 0 )
510 penWidth = -penWidth;
511
512 attributes.m_StrokeWidth = penWidth;
513
514 std::list<VECTOR2I> pts;
515
516 auto push_pts = [&]()
517 {
518 if( pts.size() < 2 )
519 return;
520
521 // Polylines are only allowed for more than 3 points.
522 // Otherwise, we have to use a line
523
524 if( pts.size() < 3 )
525 {
526 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
527 shape.SetStart( pts.front() );
528 shape.SetEnd( pts.back() );
529 shape.SetWidth( attributes.m_StrokeWidth );
530
531 AddShape( shape );
533 ODB_ATTR::STRING{ aTextString.ToStdString() } );
534 }
535 else
536 {
537 for( auto it = pts.begin(); std::next( it ) != pts.end(); ++it )
538 {
539 auto it2 = std::next( it );
540 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
541 shape.SetStart( *it );
542 shape.SetEnd( *it2 );
543 shape.SetWidth( attributes.m_StrokeWidth );
544 AddShape( shape );
545
546 if( !m_featuresList.empty() )
547 {
549 ODB_ATTR::STRING{ aTextString.ToStdString() } );
550 }
551 }
552 }
553
554 pts.clear();
555 };
556
557 CALLBACK_GAL callback_gal(
558 empty_opts,
559 // Stroke callback
560 [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
561 {
562 if( !pts.empty() )
563 {
564 if( aPt1 == pts.back() )
565 pts.push_back( aPt2 );
566 else if( aPt2 == pts.front() )
567 pts.push_front( aPt1 );
568 else if( aPt1 == pts.front() )
569 pts.push_front( aPt2 );
570 else if( aPt2 == pts.back() )
571 pts.push_back( aPt1 );
572 else
573 {
574 push_pts();
575 pts.push_back( aPt1 );
576 pts.push_back( aPt2 );
577 }
578 }
579 else
580 {
581 pts.push_back( aPt1 );
582 pts.push_back( aPt2 );
583 }
584 },
585 // Polygon callback
586 [&]( const SHAPE_LINE_CHAIN& aPoly )
587 {
588 if( aPoly.PointCount() < 3 )
589 return;
590
591 SHAPE_POLY_SET poly_set;
592 poly_set.AddOutline( aPoly );
593
594 for( int ii = 0; ii < poly_set.OutlineCount(); ++ii )
595 {
596 AddContour( poly_set, ii, FILL_T::FILLED_SHAPE );
597
598 if( !m_featuresList.empty() )
599 {
601 ODB_ATTR::STRING{ aTextString.ToStdString() } );
602 }
603 }
604 } );
605
606 aFont->Draw( &callback_gal, aTextString, aPos, aAttributes, aFontMetrics );
607
608 if( !pts.empty() )
609 push_pts();
610 };
611
612 bool isKnockout = false;
613
614 if( item->Type() == PCB_TEXT_T || item->Type() == PCB_FIELD_T )
615 isKnockout = static_cast<PCB_TEXT*>( item )->IsKnockout();
616 else if( item->Type() == PCB_TEXTBOX_T )
617 isKnockout = static_cast<PCB_TEXTBOX*>( item )->IsKnockout();
618
619 const KIFONT::METRICS& fontMetrics = item->GetFontMetrics();
620 KIFONT::FONT* font = text_item->GetDrawFont( nullptr );
621 wxString shownText( text_item->GetShownText( true ) );
622
623 if( shownText.IsEmpty() )
624 return;
625
626 VECTOR2I pos = text_item->GetTextPos();
627
628 TEXT_ATTRIBUTES attrs = text_item->GetAttributes();
629 attrs.m_StrokeWidth = text_item->GetEffectiveTextPenWidth();
630 attrs.m_Angle = text_item->GetDrawRotation();
631 attrs.m_Multiline = false;
632
633 if( isKnockout )
634 {
635 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
636 SHAPE_POLY_SET finalpolyset;
637
638 text->TransformTextToPolySet( finalpolyset, 0, m_board->GetDesignSettings().m_MaxError,
639 ERROR_INSIDE );
640 finalpolyset.Fracture();
641
642 for( int ii = 0; ii < finalpolyset.OutlineCount(); ++ii )
643 {
644 AddContour( finalpolyset, ii, FILL_T::FILLED_SHAPE );
645
646 if( !m_featuresList.empty() )
647 {
649 ODB_ATTR::STRING{ shownText.ToStdString() } );
650 }
651 }
652 }
653 else if( text_item->IsMultilineAllowed() )
654 {
655 std::vector<VECTOR2I> positions;
656 wxArrayString strings_list;
657 wxStringSplit( shownText, strings_list, '\n' );
658 positions.reserve( strings_list.Count() );
659
660 text_item->GetLinePositions( nullptr, positions, strings_list.Count() );
661
662 for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
663 {
664 wxString& txt = strings_list.Item( ii );
665 plot_text( positions[ii], txt, attrs, font, fontMetrics );
666 }
667 }
668 else
669 {
670 plot_text( pos, shownText, attrs, font, fontMetrics );
671 }
672 };
673
674
675 auto add_shape = [&]( PCB_SHAPE* shape )
676 {
677 // FOOTPRINT* fp = shape->GetParentFootprint();
678 AddShape( *shape, aLayer );
679 };
680
681 auto add_pad = [&]( PAD* pad )
682 {
683 auto iter = GetODBPlugin()->GetPadSubnetMap().find( pad );
684
685 if( iter == GetODBPlugin()->GetPadSubnetMap().end() )
686 {
687 wxLogError( wxT( "Failed to get subnet top data" ) );
688 return;
689 }
690
691 if( aLayer != PCB_LAYER_ID::UNDEFINED_LAYER )
692 {
693 // FOOTPRINT* fp = pad->GetParentFootprint();
694
695 AddPadShape( *pad, aLayer );
696
697 iter->second->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::COPPER, m_layerName,
698 m_featuresList.size() - 1 );
699 if( !m_featuresList.empty() )
701
702 if( !pad->HasHole() && !m_featuresList.empty() )
703 AddSystemAttribute( *m_featuresList.back(), ODB_ATTR::SMD{ true } );
704 }
705 else
706 {
707 // drill layer round hole or slot hole
708 if( m_layerName.Contains( "drill" ) )
709 {
710 // here we exchange round hole or slot hole into pad to draw in drill layer
711 PAD dummy( *pad );
712 dummy.Padstack().SetMode( PADSTACK::MODE::NORMAL );
713
714 if( pad->GetDrillSizeX() == pad->GetDrillSizeY() )
715 dummy.SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE ); // round hole shape
716 else
717 dummy.SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::OVAL ); // slot hole shape
718
719 dummy.SetOffset( PADSTACK::ALL_LAYERS,
720 VECTOR2I( 0, 0 ) ); // use hole position not pad position
721 dummy.SetSize( PADSTACK::ALL_LAYERS, pad->GetDrillSize() );
722
723 AddPadShape( dummy, aLayer );
724
725 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
726 {
727 // only plated holes link to subnet
728 iter->second->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::HOLE, m_layerName,
729 m_featuresList.size() - 1 );
730
731 if( !m_featuresList.empty() )
733 }
734 else
735 {
736 if( !m_featuresList.empty() )
738 }
739 }
740 }
741 // AddSystemAttribute( *m_featuresList.back(),
742 // ODB_ATTR::GEOMETRY{ "PAD_xxxx" } );
743 };
744
745 for( BOARD_ITEM* item : aItems )
746 {
747 switch( item->Type() )
748 {
749 case PCB_TRACE_T:
750 case PCB_ARC_T:
751 case PCB_VIA_T:
752 add_track( static_cast<PCB_TRACK*>( item ) );
753 break;
754
755 case PCB_ZONE_T:
756 add_zone( static_cast<ZONE*>( item ) );
757 break;
758
759 case PCB_PAD_T:
760 add_pad( static_cast<PAD*>( item ) );
761 break;
762
763 case PCB_SHAPE_T:
764 add_shape( static_cast<PCB_SHAPE*>( item ) );
765 break;
766
767 case PCB_TEXT_T:
768 case PCB_FIELD_T:
769 add_text( item );
770 break;
771
772 case PCB_TEXTBOX_T:
773 add_text( item );
774
775 if( static_cast<PCB_TEXTBOX*>( item )->IsBorderEnabled() )
776 add_shape( static_cast<PCB_TEXTBOX*>( item ) );
777
778 break;
779
780 case PCB_DIMENSION_T:
781 case PCB_TARGET_T:
783 case PCB_DIM_LEADER_T:
784 case PCB_DIM_CENTER_T:
785 case PCB_DIM_RADIAL_T:
787 //TODO: Add support for dimensions
788 break;
789
790 default: break;
791 }
792 }
793}
794
795
797{
798 if( !aVia->FlashLayer( aLayer ) )
799 return;
800
801 PAD dummy( nullptr ); // default pad shape is circle
802 dummy.SetPadstack( aVia->Padstack() );
803 dummy.SetPosition( aVia->GetStart() );
804
805 AddPadShape( dummy, aLayer );
806}
807
808
810{
811 PAD dummy( nullptr ); // default pad shape is circle
812 int hole = aVia->GetDrillValue();
813 dummy.SetPosition( aVia->GetStart() );
814 dummy.SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( hole, hole ) );
815
816 AddPadShape( dummy, aLayer );
817}
818
819
820void FEATURES_MANAGER::GenerateProfileFeatures( std::ostream& ost ) const
821{
822 ost << "UNITS=" << PCB_IO_ODBPP::m_unitsStr << std::endl;
823 ost << "#\n#Num Features\n#" << std::endl;
824 ost << "F " << m_featuresList.size() << std::endl;
825
826 if( m_featuresList.empty() )
827 return;
828
829 ost << "#\n#Layer features\n#" << std::endl;
830
831 for( const auto& feat : m_featuresList )
832 {
833 feat->WriteFeatures( ost );
834 }
835}
836
837
838void FEATURES_MANAGER::GenerateFeatureFile( std::ostream& ost ) const
839{
840 ost << "UNITS=" << PCB_IO_ODBPP::m_unitsStr << std::endl;
841 ost << "#\n#Num Features\n#" << std::endl;
842 ost << "F " << m_featuresList.size() << std::endl << std::endl;
843
844 if( m_featuresList.empty() )
845 return;
846
847 ost << "#\n#Feature symbol names\n#" << std::endl;
848
849 for( const auto& [n, name] : m_allSymMap )
850 {
851 ost << "$" << n << " " << name << std::endl;
852 }
853
854 WriteAttributes( ost );
855
856 ost << "#\n#Layer features\n#" << std::endl;
857
858 for( const auto& feat : m_featuresList )
859 {
860 feat->WriteFeatures( ost );
861 }
862}
863
864
865void ODB_FEATURE::WriteFeatures( std::ostream& ost )
866{
867 switch( GetFeatureType() )
868 {
869 case FEATURE_TYPE::LINE: ost << "L "; break;
870
871 case FEATURE_TYPE::ARC: ost << "A "; break;
872
873 case FEATURE_TYPE::PAD: ost << "P "; break;
874
875 case FEATURE_TYPE::SURFACE: ost << "S "; break;
876 default: return;
877 }
878
879 WriteRecordContent( ost );
880 ost << std::endl;
881}
882
883
884void ODB_LINE::WriteRecordContent( std::ostream& ost )
885{
886 ost << m_start.first << " " << m_start.second << " " << m_end.first << " " << m_end.second
887 << " " << m_symIndex << " P 0";
888
889 WriteAttributes( ost );
890}
891
892
893void ODB_ARC::WriteRecordContent( std::ostream& ost )
894{
895 ost << m_start.first << " " << m_start.second << " " << m_end.first << " " << m_end.second
896 << " " << m_center.first << " " << m_center.second << " " << m_symIndex << " P 0 "
897 << ( m_direction == ODB_DIRECTION::CW ? "Y" : "N" );
898
899 WriteAttributes( ost );
900}
901
902
903void ODB_PAD::WriteRecordContent( std::ostream& ost )
904{
905 ost << m_center.first << " " << m_center.second << " ";
906
907 // TODO: support resize symbol
908 // ost << "-1" << " " << m_symIndex << " "
909 // << m_resize << " P 0 ";
910
911 ost << m_symIndex << " P 0 ";
912
913 if( m_mirror )
914 ost << "9 " << ODB::Double2String( m_angle.Normalize().AsDegrees() );
915 else
916 ost << "8 " << ODB::Double2String( ( ANGLE_360 - m_angle ).Normalize().AsDegrees() );
917
918 WriteAttributes( ost );
919}
920
921
922ODB_SURFACE::ODB_SURFACE( uint32_t aIndex, const SHAPE_POLY_SET::POLYGON& aPolygon,
923 FILL_T aFillType /*= FILL_T::FILLED_SHAPE*/ ) : ODB_FEATURE( aIndex )
924{
925 if( !aPolygon.empty() && aPolygon[0].PointCount() >= 3 )
926 {
927 m_surfaces = std::make_unique<ODB_SURFACE_DATA>( aPolygon );
928 if( aFillType != FILL_T::NO_FILL )
929 {
930 m_surfaces->AddPolygonHoles( aPolygon );
931 }
932 }
933 else
934 {
935 delete this;
936 }
937}
938
939
940void ODB_SURFACE::WriteRecordContent( std::ostream& ost )
941{
942 ost << "P 0";
943 WriteAttributes( ost );
944 ost << std::endl;
945 m_surfaces->WriteData( ost );
946 ost << "SE";
947}
948
949
951{
952 const std::vector<VECTOR2I>& pts = aPolygon[0].CPoints();
953 if( !pts.empty() )
954 {
955 if( m_polygons.empty() )
956 {
957 m_polygons.resize( 1 );
958 }
959
960 m_polygons.at( 0 ).reserve( pts.size() );
961 m_polygons.at( 0 ).emplace_back( pts.back() );
962
963 for( size_t jj = 0; jj < pts.size(); ++jj )
964 {
965 m_polygons.at( 0 ).emplace_back( pts.at( jj ) );
966 }
967 }
968}
969
970
972{
973 for( size_t ii = 1; ii < aPolygon.size(); ++ii )
974 {
975 wxCHECK2( aPolygon[ii].PointCount() >= 3, continue );
976
977 const std::vector<VECTOR2I>& hole = aPolygon[ii].CPoints();
978
979 if( hole.empty() )
980 continue;
981
982 if( m_polygons.size() <= ii )
983 {
984 m_polygons.resize( ii + 1 );
985
986 m_polygons[ii].reserve( hole.size() );
987 }
988
989 m_polygons.at( ii ).emplace_back( hole.back() );
990
991 for( size_t jj = 0; jj < hole.size(); ++jj )
992 {
993 m_polygons.at( ii ).emplace_back( hole[jj] );
994 }
995 }
996}
997
998
999void ODB_SURFACE_DATA::WriteData( std::ostream& ost ) const
1000{
1001 ODB::CHECK_ONCE is_island;
1002
1003 for( const auto& contour : m_polygons )
1004 {
1005 if( contour.empty() )
1006 continue;
1007
1008 ost << "OB " << ODB::AddXY( contour.back().m_end ).first << " "
1009 << ODB::AddXY( contour.back().m_end ).second << " ";
1010
1011 if( is_island() )
1012 ost << "I";
1013 else
1014 ost << "H";
1015 ost << std::endl;
1016
1017 for( const auto& line : contour )
1018 {
1019 if( SURFACE_LINE::LINE_TYPE::SEGMENT == line.m_type )
1020 ost << "OS " << ODB::AddXY( line.m_end ).first << " "
1021 << ODB::AddXY( line.m_end ).second << std::endl;
1022 else
1023 ost << "OC " << ODB::AddXY( line.m_end ).first << " "
1024 << ODB::AddXY( line.m_end ).second << " " << ODB::AddXY( line.m_center ).first
1025 << " " << ODB::AddXY( line.m_center ).second << " "
1026 << ( line.m_direction == ODB_DIRECTION::CW ? "Y" : "N" ) << std::endl;
1027 }
1028 ost << "OE" << std::endl;
1029 }
1030}
const char * name
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
void WriteAttributes(std::ostream &ost, const std::string &prefix="") const
void AddSystemAttribute(Tr &r, Ta v)
void WriteAttributes(std::ostream &ost) const
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
FOOTPRINT * GetParentFootprint() const
const SHAPE_POLY_SET & GetHatching() const
Definition eda_shape.h:148
int GetRectangleWidth() const
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:337
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:168
bool IsHatchedFill() const
Definition eda_shape.h:124
bool IsSolidFill() const
Definition eda_shape.h:117
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:177
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:173
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:320
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:219
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
int GetRectangleHeight() const
bool IsClockwiseArc() const
void SetWidth(int aWidth)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:79
const VECTOR2I & GetTextPos() const
Definition eda_text.h:272
bool IsMultilineAllowed() const
Definition eda_text.h:196
virtual bool IsVisible() const
Definition eda_text.h:186
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:376
virtual KIFONT::FONT * GetDrawFont(const RENDER_SETTINGS *aSettings) const
Definition eda_text.cpp:650
const TEXT_ATTRIBUTES & GetAttributes() const
Definition eda_text.h:230
int GetEffectiveTextPenWidth(int aDefaultPenWidth=0) const
The EffectiveTextPenWidth uses the text thickness if > 1 or aDefaultPenWidth.
Definition eda_text.cpp:466
void GetLinePositions(const RENDER_SETTINGS *aSettings, std::vector< VECTOR2I > &aPositions, int aLineCount) const
Populate aPositions with the position of each line of a multiline text, according to the vertical jus...
Definition eda_text.cpp:912
virtual wxString GetShownText(bool aAllowExtraText, int aDepth=0) const
Return the string actually shown after processing of the base text.
Definition eda_text.h:108
void AddPadShape(const PAD &aPad, PCB_LAYER_ID aLayer)
uint32_t AddRoundRectDonutSymbol(const wxString &aOuterWidth, const wxString &aOuterHeight, const wxString &aLineWidth, const wxString &aRadius)
void AddShape(const PCB_SHAPE &aShape, PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
uint32_t AddRectSymbol(const wxString &aWidth, const wxString &aHeight)
void AddVia(const PCB_VIA *aVia, PCB_LAYER_ID aLayer)
PCB_IO_ODBPP * GetODBPlugin()
void InitFeatureList(PCB_LAYER_ID aLayer, std::vector< BOARD_ITEM * > &aItems)
void AddFeatureLine(const VECTOR2I &aStart, const VECTOR2I &aEnd, uint64_t aWidth)
void AddFeatureArc(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, uint64_t aWidth, ODB_DIRECTION aDirection)
std::map< uint32_t, wxString > m_allSymMap
void GenerateFeatureFile(std::ostream &ost) const
void AddViaDrillHole(const PCB_VIA *aVia, PCB_LAYER_ID aLayer)
void AddPadCircle(const VECTOR2I &aCenter, uint64_t aDiameter, const EDA_ANGLE &aAngle, bool aMirror, double aResize=1.0)
void AddFeatureSurface(const SHAPE_POLY_SET::POLYGON &aPolygon, FILL_T aFillType=FILL_T::FILLED_SHAPE)
uint32_t AddRoundRectSymbol(const wxString &aWidth, const wxString &aHeight, const wxString &aRadius)
wxString m_layerName
uint32_t AddOvalSymbol(const wxString &aWidth, const wxString &aHeight)
void GenerateProfileFeatures(std::ostream &ost) const
std::list< std::unique_ptr< ODB_FEATURE > > m_featuresList
void AddFeature(Args &&... args)
uint32_t AddCircleSymbol(const wxString &aDiameter)
Definition odb_feature.h:95
uint32_t AddChamferRectSymbol(const wxString &aWidth, const wxString &aHeight, const wxString &aRadius, int aPositions)
uint32_t AddRoundDonutSymbol(const wxString &aOuterDim, const wxString &aInnerDim)
bool AddContour(const SHAPE_POLY_SET &aPolySet, int aOutline=0, FILL_T aFillType=FILL_T::FILLED_SHAPE)
bool IsFlipped() const
Definition footprint.h:434
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:131
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
std::pair< wxString, wxString > m_end
std::pair< wxString, wxString > m_start
uint32_t m_symIndex
virtual void WriteRecordContent(std::ostream &ost) override
ODB_DIRECTION m_direction
std::pair< wxString, wxString > m_center
virtual void WriteRecordContent(std::ostream &ost)=0
virtual void WriteFeatures(std::ostream &ost)
virtual FEATURE_TYPE GetFeatureType()=0
ODB_FEATURE(uint32_t aIndex)
virtual void WriteRecordContent(std::ostream &ost) override
std::pair< wxString, wxString > m_start
std::pair< wxString, wxString > m_end
uint32_t m_symIndex
uint32_t m_symIndex
virtual void WriteRecordContent(std::ostream &ost) override
bool m_mirror
EDA_ANGLE m_angle
std::pair< wxString, wxString > m_center
void WriteData(std::ostream &ost) const
std::vector< std::vector< SURFACE_LINE > > m_polygons
ODB_SURFACE_DATA(const SHAPE_POLY_SET::POLYGON &aPolygon)
void AddPolygonHoles(const SHAPE_POLY_SET::POLYGON &aPolygon)
std::unique_ptr< ODB_SURFACE_DATA > m_surfaces
virtual void WriteRecordContent(std::ostream &ost) override
ODB_SURFACE(uint32_t aIndex, const SHAPE_POLY_SET::POLYGON &aPolygon, FILL_T aFillType=FILL_T::FILLED_SHAPE)
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:139
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:145
Definition pad.h:54
void MergePrimitivesAsPolygon(PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aMergedPolygon, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
Definition pad.cpp:2689
int GetRoundRectCornerRadius(PCB_LAYER_ID aLayer) const
Definition pad.cpp:467
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:195
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
Definition pad.cpp:2024
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1177
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:408
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:711
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:694
VECTOR2I GetSolderPasteMargin(PCB_LAYER_ID aLayer) const
Usually < 0 (mask shape smaller than pad)because the margin can be dependent on the pad size,...
Definition pad.cpp:1232
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1068
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
const VECTOR2I & GetMid() const
Definition pcb_track.h:345
std::map< std::pair< PCB_LAYER_ID, ZONE * >, EDA_DATA::SUB_NET_PLANE * > & GetPlaneSubnetMap()
std::map< PCB_TRACK *, EDA_DATA::SUB_NET * > & GetViaTraceSubnetMap()
std::map< const PAD *, EDA_DATA::SUB_NET_TOEPRINT * > & GetPadSubnetMap()
static std::string m_unitsStr
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:81
int GetWidth() const override
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.
const VECTOR2I & GetStart() const
Definition pcb_track.h:152
const VECTOR2I & GetEnd() const
Definition pcb_track.h:149
virtual int GetWidth() const
Definition pcb_track.h:146
bool FlashLayer(int aLayer) const
Check to see whether the via should have a pad on the specific layer.
const PADSTACK & Padstack() const
Definition pcb_track.h:459
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
int OutlineCount() const
Return the number of outlines in the set.
void InflateWithLinkedHoles(int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError)
Perform outline inflation/deflation, using round corners.
void Move(const VECTOR2I &aVector) override
SHAPE_POLY_SET CloneDropTriangulation() const
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Handle a list of polygons defining a copper zone.
Definition zone.h:74
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ ROUND_ALL_CORNERS
All angles are rounded.
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
FILL_T
Definition eda_shape.h:56
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
int GetPenSizeForBold(int aTextSize)
Definition gr_text.cpp:37
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_Paste
Definition layer_ids.h:104
@ B_Mask
Definition layer_ids.h:98
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ UNDEFINED_LAYER
Definition layer_ids.h:61
std::pair< wxString, wxString > AddXY(const VECTOR2I &aVec)
Definition odb_util.cpp:167
wxString Double2String(double aVal)
Definition odb_util.cpp:127
wxString SymDouble2String(double aVal)
Definition odb_util.cpp:155
VECTOR2I GetShapePosition(const PCB_SHAPE &aShape)
Definition odb_util.cpp:178
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
ODB_DIRECTION
Definition odb_feature.h:35
@ PTH
Plated through hole pad.
Definition padstack.h:82
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
std::vector< FAB_LAYER_COLOR > dummy
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
VECTOR2I center
VECTOR2I end
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:102
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:90
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:106
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:101
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:100
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:104
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695