KiCad PCB EDA Suite
altium_pcb.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019-2020 Thomas Pointhuber <[email protected]>
5 * Copyright (C) 2021-2023 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#include "altium_pcb.h"
26#include "altium_parser_pcb.h"
29
30#include <board.h>
32#include <pcb_dimension.h>
33#include <pad.h>
34#include <pcb_shape.h>
35#include <pcb_text.h>
36#include <pcb_track.h>
37#include <string_utils.h>
38
39#include <fp_shape.h>
40#include <fp_text.h>
41#include <zone.h>
42
44
45#include <compoundfilereader.h>
47#include <project.h>
48#include <trigo.h>
49#include <utf.h>
50#include <wx/docview.h>
51#include <wx/log.h>
52#include <wx/mstream.h>
53#include <wx/wfstream.h>
54#include <wx/zstream.h>
55#include <progress_reporter.h>
56
57
58constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700
59
60
62{
63 return ( aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER )
64 || aLayer == ALTIUM_LAYER::MULTI_LAYER; // TODO: add IsAltiumLayerAPlane?
65}
66
67
69{
71}
72
73FOOTPRINT* ALTIUM_PCB::HelperGetFootprint( uint16_t aComponent ) const
74{
75 if( aComponent == ALTIUM_COMPONENT_NONE || m_components.size() <= aComponent )
76 {
77 THROW_IO_ERROR( wxString::Format( wxT( "Component creator tries to access component id %d "
78 "of %d existing components" ),
79 aComponent, m_components.size() ) );
80 }
81
82 return m_components.at( aComponent );
83}
84
86{
87 if( aComponent == ALTIUM_COMPONENT_NONE )
88 {
89 PCB_SHAPE* shape = new PCB_SHAPE( m_board );
90 m_board->Add( shape, ADD_MODE::APPEND );
91 return shape;
92 }
93 else
94 {
95 if( m_components.size() <= aComponent )
96 {
97 THROW_IO_ERROR( wxString::Format( wxT( "Component creator tries to access component "
98 "id %d of %d existing components" ),
99 aComponent,
100 m_components.size() ) );
101 }
102
103 FOOTPRINT* footprint = m_components.at( aComponent );
104 PCB_SHAPE* fpShape = new FP_SHAPE( footprint );
105
106 footprint->Add( fpShape, ADD_MODE::APPEND );
107 return fpShape;
108 }
109}
110
111
112void ALTIUM_PCB::HelperShapeSetLocalCoord( PCB_SHAPE* aShape, uint16_t aComponent )
113{
114 if( aComponent != ALTIUM_COMPONENT_NONE )
115 {
116 FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( aShape );
117
118 if( fpShape )
119 {
120 fpShape->SetLocalCoord();
121
122 // TODO: SetLocalCoord() does not update the polygon shape!
123 // This workaround converts the poly shape into the local coordinates
124 SHAPE_POLY_SET& polyShape = fpShape->GetPolyShape();
125
126 if( !polyShape.IsEmpty() )
127 {
128 FOOTPRINT* fp = m_components.at( aComponent );
129
130 polyShape.Move( -fp->GetPosition() );
131 polyShape.Rotate( fp->GetOrientation() );
132 }
133 }
134 }
135}
136
137
139{
140 aShape->SetLocalCoord();
141
142 // TODO: SetLocalCoord() does not update the polygon shape!
143 // This workaround converts the poly shape into the local coordinates
144 SHAPE_POLY_SET& polyShape = aShape->GetPolyShape();
145
146 if( !polyShape.IsEmpty() )
147 {
148 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aShape->GetParent() );
149
150 if( fp )
151 {
152 polyShape.Move( -fp->GetPosition() );
153 polyShape.Rotate( fp->GetOrientation() );
154 }
155 }
156}
157
158
160 const std::vector<ALTIUM_VERTICE>& aVertices )
161{
162 for( const ALTIUM_VERTICE& vertex : aVertices )
163 {
164 if( vertex.isRound )
165 {
166 EDA_ANGLE angle( vertex.endangle - vertex.startangle, DEGREES_T );
167 angle.Normalize();
168
169 double startradiant = DEG2RAD( vertex.startangle );
170 double endradiant = DEG2RAD( vertex.endangle );
171 VECTOR2I arcStartOffset = VECTOR2I( KiROUND( std::cos( startradiant ) * vertex.radius ),
172 -KiROUND( std::sin( startradiant ) * vertex.radius ) );
173
174 VECTOR2I arcEndOffset = VECTOR2I( KiROUND( std::cos( endradiant ) * vertex.radius ),
175 -KiROUND( std::sin( endradiant ) * vertex.radius ) );
176
177 VECTOR2I arcStart = vertex.center + arcStartOffset;
178 VECTOR2I arcEnd = vertex.center + arcEndOffset;
179
180 if( GetLineLength( arcStart, vertex.position )
181 < GetLineLength( arcEnd, vertex.position ) )
182 {
183 aLine.Append( SHAPE_ARC( vertex.center, arcStart, -angle ) );
184 }
185 else
186 {
187 aLine.Append( SHAPE_ARC( vertex.center, arcEnd, angle ) );
188 }
189 }
190 else
191 {
192 aLine.Append( vertex.position );
193 }
194 }
195
196 aLine.SetClosed( true );
197}
198
199
201{
202 auto override = m_layermap.find( aAltiumLayer );
203
204 if( override != m_layermap.end() )
205 {
206 return override->second;
207 }
208
209 switch( aAltiumLayer )
210 {
212
213 case ALTIUM_LAYER::TOP_LAYER: return F_Cu;
244 case ALTIUM_LAYER::BOTTOM_LAYER: return B_Cu;
245
248 case ALTIUM_LAYER::TOP_PASTE: return F_Paste;
250 case ALTIUM_LAYER::TOP_SOLDER: return F_Mask;
252
269
272
273 case ALTIUM_LAYER::MECHANICAL_1: return User_1; //Edge_Cuts;
283 case ALTIUM_LAYER::MECHANICAL_11: return Eco2_User; //Eco1 is used for unknown elements
285 case ALTIUM_LAYER::MECHANICAL_13: return B_Fab; // Don't use courtyard layers for other purposes
289
300
301 default: return UNDEFINED_LAYER;
302 }
303}
304
305
306std::vector<PCB_LAYER_ID> ALTIUM_PCB::GetKicadLayersToIterate( ALTIUM_LAYER aAltiumLayer ) const
307{
308 static std::set<ALTIUM_LAYER> altiumLayersWithWarning;
309
310 if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
311 {
312 std::vector<PCB_LAYER_ID> layers;
313 layers.reserve( MAX_CU_LAYERS ); // TODO: only use Cu layers which are on the board
315 layer = static_cast<PCB_LAYER_ID>( static_cast<int>( layer ) + 1 ) )
316 {
317 layers.emplace_back( layer );
318 }
319
320 return layers;
321 }
322
323 PCB_LAYER_ID klayer = GetKicadLayer( aAltiumLayer );
324
325 if( klayer == UNDEFINED_LAYER )
326 {
327 wxLogWarning( _( "Altium layer (%d) has no KiCad equivalent. It has been moved to KiCad "
328 "layer Eco1_User." ),
329 aAltiumLayer );
330 klayer = Eco1_User;
331 }
332
333 return { klayer };
334}
335
336
337ALTIUM_PCB::ALTIUM_PCB( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
338{
339 m_board = aBoard;
340 m_progressReporter = aProgressReporter;
341 m_doneCount = 0;
343 m_totalCount = 0;
345}
346
348{
349}
350
352{
353 const unsigned PROGRESS_DELTA = 250;
354
356 {
357 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
358 {
360 / std::max( 1U, m_totalCount ) );
361
363 THROW_IO_ERROR( _( "Open cancelled by user." ) );
364
366 }
367 }
368}
369
370void ALTIUM_PCB::Parse( const ALTIUM_COMPOUND_FILE& altiumPcbFile,
371 const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
372{
373 // this vector simply declares in which order which functions to call.
374 const std::vector<std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>> parserOrder = {
376 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
377 {
378 this->ParseFileHeader( aFile, fileHeader );
379 } },
381 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
382 {
383 this->ParseBoard6Data( aFile, fileHeader );
384 } },
386 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
387 {
388 this->ParseExtendedPrimitiveInformationData( aFile, fileHeader );
389 } },
391 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
392 {
393 this->ParseComponents6Data( aFile, fileHeader );
394 } },
396 [this, aFileMapping]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
397 {
398 std::vector<std::string> dir{ aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) };
399 this->ParseModelsData( aFile, fileHeader, dir );
400 } },
402 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
403 {
404 this->ParseComponentsBodies6Data( aFile, fileHeader );
405 } },
406 { true, ALTIUM_PCB_DIR::NETS6,
407 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
408 {
409 this->ParseNets6Data( aFile, fileHeader );
410 } },
412 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
413 {
414 this->ParseClasses6Data( aFile, fileHeader );
415 } },
417 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
418 {
419 this->ParseRules6Data( aFile, fileHeader );
420 } },
422 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
423 {
424 this->ParseDimensions6Data( aFile, fileHeader );
425 } },
427 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
428 {
429 this->ParsePolygons6Data( aFile, fileHeader );
430 } },
431 { true, ALTIUM_PCB_DIR::ARCS6,
432 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
433 {
434 this->ParseArcs6Data( aFile, fileHeader );
435 } },
436 { true, ALTIUM_PCB_DIR::PADS6,
437 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
438 {
439 this->ParsePads6Data( aFile, fileHeader );
440 } },
441 { true, ALTIUM_PCB_DIR::VIAS6,
442 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
443 {
444 this->ParseVias6Data( aFile, fileHeader );
445 } },
447 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
448 {
449 this->ParseTracks6Data( aFile, fileHeader );
450 } },
452 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
453 {
454 this->ParseWideStrings6Data( aFile, fileHeader );
455 } },
457 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
458 {
459 this->ParseTexts6Data( aFile, fileHeader );
460 } },
462 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
463 {
464 this->ParseFills6Data( aFile, fileHeader );
465 } },
467 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
468 {
469 this->ParseBoardRegionsData( aFile, fileHeader );
470 } },
472 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
473 {
474 this->ParseShapeBasedRegions6Data( aFile, fileHeader );
475 } },
477 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
478 {
479 this->ParseRegions6Data( aFile, fileHeader );
480 } }
481 };
482
483 if( m_progressReporter != nullptr )
484 {
485 // Count number of records we will read for the progress reporter
486 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
487 {
488 bool isRequired;
491 std::tie( isRequired, directory, fp ) = cur;
492
494 continue;
495
496 const auto& mappedDirectory = aFileMapping.find( directory );
497
498 if( mappedDirectory == aFileMapping.end() )
499 continue;
500
501 const std::vector<std::string> mappedFile{ mappedDirectory->second, "Header" };
502 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
503
504 if( file == nullptr )
505 continue;
506
507 ALTIUM_PARSER reader( altiumPcbFile, file );
508 uint32_t numOfRecords = reader.Read<uint32_t>();
509
510 if( reader.HasParsingError() )
511 {
512 wxLogError( _( "'%s' was not parsed correctly." ), FormatPath( mappedFile ) );
513 continue;
514 }
515
516 m_totalCount += numOfRecords;
517
518 if( reader.GetRemainingBytes() != 0 )
519 {
520 wxLogError( _( "'%s' was not fully parsed." ), FormatPath( mappedFile ) );
521 continue;
522 }
523 }
524 }
525
526 // Parse data in specified order
527 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
528 {
529 bool isRequired;
532 std::tie( isRequired, directory, fp ) = cur;
533
534 const auto& mappedDirectory = aFileMapping.find( directory );
535
536 if( mappedDirectory == aFileMapping.end() )
537 {
538 wxASSERT_MSG( !isRequired, wxString::Format( wxT( "Altium Directory of kind %d was "
539 "expected, but no mapping is "
540 "present in the code" ),
541 directory ) );
542 continue;
543 }
544
545 std::vector<std::string> mappedFile{ mappedDirectory->second };
546
548 mappedFile.emplace_back( "Data" );
549
550 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
551
552 if( file != nullptr )
553 fp( altiumPcbFile, file );
554 else if( isRequired )
555 wxLogError( _( "File not found: '%s'." ), FormatPath( mappedFile ) );
556 }
557
558 // fixup zone priorities since Altium stores them in the opposite order
559 for( ZONE* zone : m_polygons )
560 {
561 if( !zone )
562 continue;
563
564 // Altium "fills" - not poured in Altium
565 if( zone->GetAssignedPriority() == 1000 )
566 {
567 // Unlikely, but you never know
568 if( m_highest_pour_index >= 1000 )
569 zone->SetAssignedPriority( m_highest_pour_index + 1 );
570
571 continue;
572 }
573
574 int priority = m_highest_pour_index - zone->GetAssignedPriority();
575
576 zone->SetAssignedPriority( priority >= 0 ? priority : 0 );
577 }
578
579 // change priority of outer zone to zero
580 for( std::pair<const ALTIUM_LAYER, ZONE*>& zone : m_outer_plane )
581 zone.second->SetAssignedPriority( 0 );
582
583 // Altium doesn't appear to store either the dimension value nor the dimensioned object in
584 // the dimension record. (Yes, there is a REFERENCE0OBJECTID, but it doesn't point to the
585 // dimensioned object.) We attempt to plug this gap by finding a colocated arc or circle
586 // and using its radius. If there are more than one such arcs/circles, well, :shrug:.
588 {
589 int radius = 0;
590
591 for( BOARD_ITEM* item : m_board->Drawings() )
592 {
593 if( item->Type() != PCB_SHAPE_T )
594 continue;
595
596 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
597
598 if( shape->GetShape() != SHAPE_T::ARC && shape->GetShape() != SHAPE_T::CIRCLE )
599 continue;
600
601 if( shape->GetPosition() == dim->GetPosition() )
602 {
603 radius = shape->GetRadius();
604 break;
605 }
606 }
607
608 if( radius == 0 )
609 {
610 for( PCB_TRACK* track : m_board->Tracks() )
611 {
612 if( track->Type() != PCB_ARC_T )
613 continue;
614
615 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
616
617 if( arc->GetCenter() == dim->GetPosition() )
618 {
619 radius = arc->GetRadius();
620 break;
621 }
622 }
623 }
624
625 // Move the radius point onto the circumference
626 VECTOR2I radialLine = dim->GetEnd() - dim->GetStart();
627 int totalLength = radialLine.EuclideanNorm();
628
629 // Enforce a minimum on the radialLine else we won't have enough precision to get the
630 // angle from it.
631 radialLine = radialLine.Resize( std::max( radius, 2 ) );
632 dim->SetEnd( dim->GetStart() + (VECTOR2I) radialLine );
633 dim->SetLeaderLength( totalLength - radius );
634 dim->Update();
635 }
636
637 // center board
639
642
643 int desired_x = ( w - bbbox.GetWidth() ) / 2;
644 int desired_y = ( h - bbbox.GetHeight() ) / 2;
645
646 VECTOR2I movementVector( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() );
647 m_board->Move( movementVector );
648
650 bds.SetAuxOrigin( bds.GetAuxOrigin() + movementVector );
651 bds.SetGridOrigin( bds.GetGridOrigin() + movementVector );
652
654}
655
656
658 const wxString& aFootprintName )
659{
660 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
661
662 // TODO: what should we do with those layers?
666
667 m_unicodeStrings.clear();
669 // TODO: WideStrings are stored as parameterMap in the case of footprints, not as binary
670 // std::string unicodeStringsStreamName = aFootprintName.ToStdString() + "\\WideStrings";
671 // const CFB::COMPOUND_FILE_ENTRY* unicodeStringsData = altiumLibFile.FindStream( unicodeStringsStreamName );
672 // if( unicodeStringsData != nullptr )
673 // {
674 // ParseWideStrings6Data( altiumLibFile, unicodeStringsData );
675 // }
676
677 const std::vector<std::string> streamName{ aFootprintName.ToStdString(), "Data" };
678 const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( streamName );
679
680 if( footprintData == nullptr )
681 {
682 THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ),
683 FormatPath( streamName ) ) );
684 }
685
686 ALTIUM_PARSER parser( altiumLibFile, footprintData );
687
689 wxString footprintName = parser.ReadWxString();
690 parser.SkipSubrecord();
691
692 LIB_ID fpID = AltiumToKiCadLibID( "", footprintName ); // TODO: library name
693 footprint->SetFPID( fpID );
694
695 const std::vector<std::string> parametersStreamName{ aFootprintName.ToStdString(),
696 "Parameters" };
697 const CFB::COMPOUND_FILE_ENTRY* parametersData =
698 altiumLibFile.FindStream( parametersStreamName );
699
700 if( parametersData != nullptr )
701 {
702 ALTIUM_PARSER parametersReader( altiumLibFile, parametersData );
703 std::map<wxString, wxString> parameterProperties = parametersReader.ReadProperties();
704 wxString description = ALTIUM_PARSER::ReadString( parameterProperties,
705 wxT( "DESCRIPTION" ),
706 wxT( "" ) );
707 footprint->SetDescription( description );
708 }
709 else
710 {
711 wxLogError( _( "File not found: '%s'." ), FormatPath( parametersStreamName ) );
712 footprint->SetDescription( wxT( "" ) );
713 }
714
715 const std::vector<std::string> extendedPrimitiveInformationStreamName{
716 aFootprintName.ToStdString(), "ExtendedPrimitiveInformation", "Data"
717 };
718 const CFB::COMPOUND_FILE_ENTRY* extendedPrimitiveInformationData =
719 altiumLibFile.FindStream( extendedPrimitiveInformationStreamName );
720
721 if( extendedPrimitiveInformationData != nullptr )
722 ParseExtendedPrimitiveInformationData( altiumLibFile, extendedPrimitiveInformationData );
723
724 footprint->SetReference( wxT( "REF**" ) );
725 footprint->SetValue( footprintName );
726 footprint->Reference().SetVisible( true ); // TODO: extract visibility information
727 footprint->Value().SetVisible( true );
728
729 for( int primitiveIndex = 0; parser.GetRemainingBytes() >= 4; primitiveIndex++ )
730 {
731 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( parser.Peek<uint8_t>() );
732
733 switch( recordtype )
734 {
736 {
737 AARC6 arc( parser );
738 ConvertArcs6ToFootprintItem( footprint.get(), arc, primitiveIndex, false );
739 break;
740 }
742 {
743 APAD6 pad( parser );
744 ConvertPads6ToFootprintItem( footprint.get(), pad );
745 break;
746 }
748 {
749 AVIA6 via( parser );
750 // TODO: implement
751 break;
752 }
754 {
755 ATRACK6 track( parser );
756 ConvertTracks6ToFootprintItem( footprint.get(), track, primitiveIndex, false );
757 break;
758 }
760 {
761 ATEXT6 text( parser, m_unicodeStrings );
762 ConvertTexts6ToFootprintItem( footprint.get(), text );
763 break;
764 }
766 {
767 AFILL6 fill( parser );
768 ConvertFills6ToFootprintItem( footprint.get(), fill, false );
769 break;
770 }
772 {
773 AREGION6 region( parser, false );
774 ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region );
775 break;
776 }
778 {
779 ACOMPONENTBODY6 componentBody( parser );
780 // Won't be supported for now, as we would need to extract the model
781 break;
782 }
783 default:
784 THROW_IO_ERROR( wxString::Format( _( "Record of unknown type: '%d'." ), recordtype ) );
785 }
786 }
787
788 if( parser.HasParsingError() )
789 {
790 THROW_IO_ERROR( wxString::Format( wxT( "%s stream was not parsed correctly" ),
791 FormatPath( streamName ) ) );
792 }
793
794 if( parser.GetRemainingBytes() != 0 )
795 {
796 THROW_IO_ERROR( wxString::Format( wxT( "%s stream is not fully parsed" ),
797 FormatPath( streamName ) ) );
798 }
799
800 return footprint.release();
801}
802
803int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
804{
805 if( aId == ALTIUM_NET_UNCONNECTED )
806 {
808 }
809 else if( m_altiumToKicadNetcodes.size() < aId )
810 {
811 THROW_IO_ERROR( wxString::Format( wxT( "Netcode with id %d does not exist. Only %d nets "
812 "are known" ),
813 aId, m_altiumToKicadNetcodes.size() ) );
814 }
815 else
816 {
817 return m_altiumToKicadNetcodes[ aId ];
818 }
819}
820
821const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
822{
823 const auto rules = m_rules.find( aKind );
824
825 if( rules == m_rules.end() )
826 return nullptr;
827
828 for( const ARULE6& rule : rules->second )
829 {
830 if( rule.name == aName )
831 return &rule;
832 }
833
834 return nullptr;
835}
836
838{
839 const auto rules = m_rules.find( aKind );
840
841 if( rules == m_rules.end() )
842 return nullptr;
843
844 for( const ARULE6& rule : rules->second )
845 {
846 if( rule.scope1expr == wxT( "All" ) && rule.scope2expr == wxT( "All" ) )
847 return &rule;
848 }
849
850 return nullptr;
851}
852
854 const CFB::COMPOUND_FILE_ENTRY* aEntry )
855{
856 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
857
859 wxString header = reader.ReadWxString();
860
861 //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
862
863 //reader.SkipSubrecord();
864
865 // TODO: does not seem to work all the time at the moment
866 //if( reader.GetRemainingBytes() != 0 )
867 // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
868}
869
871 const CFB::COMPOUND_FILE_ENTRY* aEntry )
872{
874 m_progressReporter->Report( _( "Loading extended primitive information data..." ) );
875
876 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
877
878 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
879 {
880 checkpoint();
881 AEXTENDED_PRIMITIVE_INFORMATION elem( reader );
882
884 std::move( elem ) );
885 }
886
887 if( reader.GetRemainingBytes() != 0 )
888 THROW_IO_ERROR( wxT( "ExtendedPrimitiveInformation stream is not fully parsed" ) );
889}
890
892 const CFB::COMPOUND_FILE_ENTRY* aEntry )
893{
895 m_progressReporter->Report( _( "Loading board data..." ) );
896
897 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
898
899 checkpoint();
900 ABOARD6 elem( reader );
901
902 if( reader.GetRemainingBytes() != 0 )
903 THROW_IO_ERROR( wxT( "Board6 stream is not fully parsed" ) );
904
907
908 // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
909 size_t layercount = 0;
910 size_t layerid = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
911
912 while( layerid < elem.stackup.size() && layerid != 0 )
913 {
914 layerid = elem.stackup[ layerid - 1 ].nextId;
915 layercount++;
916 }
917
918 size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
919 m_board->SetCopperLayerCount( kicadLayercount );
920
922 BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
923
924 // create board stackup
925 stackup.RemoveAll(); // Just to be sure
926 stackup.BuildDefaultStackupList( &designSettings, layercount );
927
928 auto it = stackup.GetList().begin();
929
930 // find first copper layer
931 for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
932 ;
933
934 auto curLayer = static_cast<int>( F_Cu );
935
936 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
937 altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
938 altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
939 {
940 // array starts with 0, but stackup with 1
941 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
942
943 // handle unused layer in case of odd layercount
944 if( layer.nextId == 0 && layercount != kicadLayercount )
945 {
946 m_board->SetLayerName( ( *it )->GetBrdLayerId(), wxT( "[unused]" ) );
947
948 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
949 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
950
951 ( *it )->SetThickness( 0 );
952
953 ++it;
954
955 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
956 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
957
958 ( *it )->SetThickness( 0, 0 );
959 ( *it )->SetThicknessLocked( true, 0 );
960 ++it;
961 }
962
963 m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ),
964 static_cast<PCB_LAYER_ID>( curLayer++ ) } );
965
966 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
967 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
968
969 ( *it )->SetThickness( layer.copperthick );
970
971 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
972 PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
973
974 m_board->SetLayerName( klayer, layer.name );
975
976 if( layer.copperthick == 0 )
977 m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
978 else if( IsAltiumLayerAPlane( alayer ) )
980
981 if( klayer == B_Cu )
982 {
983 if( layer.nextId != 0 )
984 THROW_IO_ERROR( wxT( "Board6 stream, unexpected id while parsing last stackup layer" ) );
985
986 // overwrite entry from internal -> bottom
987 m_layermap[alayer] = B_Cu;
988 break;
989 }
990
991 ++it;
992
993 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
994 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
995
996 ( *it )->SetThickness( layer.dielectricthick, 0 );
997 ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
999 wxString( layer.dielectricmaterial ) );
1000 ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
1001
1002 ++it;
1003 }
1004
1005 // Set name of all non-cu layers
1006 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_OVERLAY );
1007 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::BOTTOM_SOLDER ); altiumLayerId++ )
1008 {
1009 // array starts with 0, but stackup with 1
1010 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1011
1012 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1013 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1014
1015 m_board->SetLayerName( klayer, layer.name );
1016 }
1017
1018 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_1 );
1019 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_16 ); altiumLayerId++ )
1020 {
1021 // array starts with 0, but stackup with 1
1022 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1023
1024 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1025 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1026
1027 m_board->SetLayerName( klayer, layer.name );
1028 }
1029
1031}
1032
1033void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
1034{
1035 SHAPE_LINE_CHAIN lineChain;
1036 HelperShapeLineChainFromAltiumVertices( lineChain, aVertices );
1037
1040
1041 for( int i = 0; i <= lineChain.PointCount() && i != -1; i = lineChain.NextShape( i ) )
1042 {
1043 if( lineChain.IsArcStart( i ) )
1044 {
1045 const SHAPE_ARC& currentArc = lineChain.Arc( lineChain.ArcIndex( i ) );
1046 int nextShape = lineChain.NextShape( i );
1047 bool isLastShape = nextShape < 0;
1048
1049 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::ARC );
1050 m_board->Add( shape, ADD_MODE::APPEND );
1051
1052 shape->SetStroke( stroke );
1053 shape->SetLayer( Edge_Cuts );
1054 shape->SetArcGeometry( currentArc.GetP0(), currentArc.GetArcMid(), currentArc.GetP1() );
1055 }
1056 else
1057 {
1058 const SEG& seg = lineChain.Segment( i );
1059
1061 m_board->Add( shape, ADD_MODE::APPEND );
1062
1063 shape->SetStroke( stroke );
1064 shape->SetLayer( Edge_Cuts );
1065 shape->SetStart( seg.A );
1066 shape->SetEnd( seg.B );
1067 }
1068 }
1069}
1070
1072 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1073{
1074 if( m_progressReporter )
1075 m_progressReporter->Report( _( "Loading netclasses..." ) );
1076
1077 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1078
1079 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1080 {
1081 checkpoint();
1082 ACLASS6 elem( reader );
1083
1085 {
1086 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( elem.name );
1087
1088 for( const wxString& name : elem.names )
1089 {
1090 m_board->GetDesignSettings().m_NetSettings->m_NetClassPatternAssignments.push_back(
1091 {
1092 std::make_unique<EDA_COMBINED_MATCHER>( name, CTX_NETCLASS ),
1093 nc->GetName()
1094 } );
1095 }
1096
1097 if( m_board->GetDesignSettings().m_NetSettings->m_NetClasses.count( nc->GetName() ) )
1098 {
1099 // Name conflict, this is likely a bad board file.
1100 // unique_ptr will delete nc on this code path
1101 THROW_IO_ERROR( wxString::Format( _( "Duplicate netclass name '%s'." ), elem.name ) );
1102 }
1103 else
1104 {
1105 m_board->GetDesignSettings().m_NetSettings->m_NetClasses[ nc->GetName() ] = nc;
1106 }
1107 }
1108 }
1109
1110 if( reader.GetRemainingBytes() != 0 )
1111 THROW_IO_ERROR( wxT( "Classes6 stream is not fully parsed" ) );
1112
1114}
1115
1117 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1118{
1119 if( m_progressReporter )
1120 m_progressReporter->Report( _( "Loading components..." ) );
1121
1122 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1123
1124 uint16_t componentId = 0;
1125
1126 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1127 {
1128 checkpoint();
1129 ACOMPONENT6 elem( reader );
1130
1131 FOOTPRINT* footprint = new FOOTPRINT( m_board );
1132 m_board->Add( footprint, ADD_MODE::APPEND );
1133 m_components.emplace_back( footprint );
1134
1136
1137 footprint->SetFPID( fpID );
1138
1139 footprint->SetPosition( elem.position );
1140 footprint->SetOrientationDegrees( elem.rotation );
1141
1142 // KiCad netlisting requires parts to have non-digit + digit annotation.
1143 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1144 wxString reference = elem.sourcedesignator;
1145
1146 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1147 reference.Prepend( wxT( "UNK" ) );
1148
1149 footprint->SetReference( reference );
1150
1151 footprint->SetLocked( elem.locked );
1152 footprint->Reference().SetVisible( elem.nameon );
1153 footprint->Value().SetVisible( elem.commenton );
1154 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1155
1156 componentId++;
1157 }
1158
1159 if( reader.GetRemainingBytes() != 0 )
1160 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1161}
1162
1163
1165double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1166{
1167 while( Angle < aMin )
1168 Angle += 360.0;
1169
1170 while( Angle >= aMax )
1171 Angle -= 360.0;
1172
1173 return Angle;
1174}
1175
1176
1178 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1179{
1180 if( m_progressReporter )
1181 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1182
1183 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1184
1185 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1186 {
1187 checkpoint();
1188 ACOMPONENTBODY6 elem( reader ); // TODO: implement
1189
1190 if( elem.component == ALTIUM_COMPONENT_NONE )
1191 continue; // TODO: we do not support components for the board yet
1192
1193 if( m_components.size() <= elem.component )
1194 {
1195 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1196 "component id %d of %d existing components" ),
1197 elem.component,
1198 m_components.size() ) );
1199 }
1200
1201 if( !elem.modelIsEmbedded )
1202 continue;
1203
1204 auto modelTuple = m_models.find( elem.modelId );
1205
1206 if( modelTuple == m_models.end() )
1207 {
1208 wxLogError( wxT( "ComponentsBodies6 stream tries to access model id %s which does not "
1209 "exist" ),
1210 elem.modelId );
1211 continue;
1212 }
1213
1214 FOOTPRINT* footprint = m_components.at( elem.component );
1215 const VECTOR2I& fpPosition = footprint->GetPosition();
1216
1217 FP_3DMODEL modelSettings;
1218
1219 modelSettings.m_Filename = modelTuple->second;
1220
1221 modelSettings.m_Offset.x = pcbIUScale.IUTomm((int) elem.modelPosition.x - fpPosition.x );
1222 modelSettings.m_Offset.y = -pcbIUScale.IUTomm((int) elem.modelPosition.y - fpPosition.y );
1223 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) elem.modelPosition.z );
1224
1225 EDA_ANGLE orientation = footprint->GetOrientation();
1226
1227 if( footprint->IsFlipped() )
1228 {
1229 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1230 orientation = -orientation;
1231 }
1232
1233 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1234
1235 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1236 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1237 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z
1238 + elem.rotation
1239 + orientation.AsDegrees(),
1240 -180, 180 );
1241 modelSettings.m_Opacity = elem.bodyOpacity;
1242
1243 footprint->Models().push_back( modelSettings );
1244 }
1245
1246 if( reader.GetRemainingBytes() != 0 )
1247 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1248}
1249
1250
1252{
1253 if( aElem.referencePoint.size() != 2 )
1254 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1255
1256 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1257
1258 if( klayer == UNDEFINED_LAYER )
1259 {
1260 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1261 "It has been moved to KiCad layer Eco1_User." ),
1262 aElem.layer );
1263 klayer = Eco1_User;
1264 }
1265
1266 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1267 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1268
1270 m_board->Add( dimension, ADD_MODE::APPEND );
1271
1272 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1273 dimension->SetLayer( klayer );
1274 dimension->SetStart( referencePoint0 );
1275
1276 if( referencePoint0 != aElem.xy1 )
1277 {
1287 VECTOR2I direction = aElem.xy1 - referencePoint0;
1288 VECTOR2I directionNormalVector = VECTOR2I( -direction.y, direction.x );
1289 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1290 SEG segm2( referencePoint1, referencePoint1 + direction );
1291 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1292
1293 if( !intersection )
1294 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1295
1296 dimension->SetEnd( *intersection );
1297
1298 int height = static_cast<int>( EuclideanNorm( direction ) );
1299
1300 if( direction.x <= 0 && direction.y <= 0 ) // TODO: I suspect this is not always correct
1301 height = -height;
1302
1303 dimension->SetHeight( height );
1304 }
1305 else
1306 {
1307 dimension->SetEnd( referencePoint1 );
1308 }
1309
1310 dimension->SetLineThickness( aElem.linewidth );
1311
1312 dimension->SetPrefix( aElem.textprefix );
1313
1314 // Suffix normally holds the units
1315 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1317
1318 dimension->SetTextThickness( aElem.textlinewidth );
1319 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1320 dimension->SetItalic( aElem.textitalic );
1321
1322#if 0 // we don't currently support bold; map to thicker text
1323 dimension->Text().SetBold( aElem.textbold );
1324#else
1325 if( aElem.textbold )
1326 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1327#endif
1328
1329 switch( aElem.textunit )
1330 {
1332 dimension->SetUnits( EDA_UNITS::INCHES );
1333 break;
1334 case ALTIUM_UNIT::MILS:
1335 dimension->SetUnits( EDA_UNITS::MILS );
1336 break;
1339 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1340 break;
1341 default:
1342 break;
1343 }
1344}
1345
1346
1348{
1349 if( aElem.referencePoint.size() < 2 )
1350 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1351
1352 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1353
1354 if( klayer == UNDEFINED_LAYER )
1355 {
1356 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1357 "It has been moved to KiCad layer Eco1_User." ),
1358 aElem.layer );
1359 klayer = Eco1_User;
1360 }
1361
1362 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1363 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1364
1365 PCB_DIM_RADIAL* dimension = new PCB_DIM_RADIAL( m_board );
1366 m_board->Add( dimension, ADD_MODE::APPEND );
1367 m_radialDimensions.push_back( dimension );
1368
1369 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1370 dimension->SetLayer( klayer );
1371 dimension->SetStart( referencePoint0 );
1372 dimension->SetEnd( aElem.xy1 );
1373 dimension->SetLineThickness( aElem.linewidth );
1374 dimension->SetKeepTextAligned( false );
1375
1376 dimension->SetPrefix( aElem.textprefix );
1377
1378 // Suffix normally holds the units
1379 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1381
1382 switch( aElem.textunit )
1383 {
1385 dimension->SetUnits( EDA_UNITS::INCHES );
1386 break;
1387 case ALTIUM_UNIT::MILS:
1388 dimension->SetUnits( EDA_UNITS::MILS );
1389 break;
1392 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1393 break;
1394 default:
1395 break;
1396 }
1397
1398 if( aElem.textPoint.empty() )
1399 {
1400 wxLogError( wxT( "No text position present for leader dimension object" ) );
1401 return;
1402 }
1403
1404 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1405 dimension->SetTextThickness( aElem.textlinewidth );
1406 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1407 dimension->SetItalic( aElem.textitalic );
1408
1409#if 0 // we don't currently support bold; map to thicker text
1410 dimension->SetBold( aElem.textbold );
1411#else
1412 if( aElem.textbold )
1413 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1414#endif
1415
1416 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1417 // close.
1420
1421 int yAdjust = dimension->GetTextBox().GetCenter().y - dimension->GetTextPos().y;
1422 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1424}
1425
1426
1428{
1429 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1430
1431 if( klayer == UNDEFINED_LAYER )
1432 {
1433 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1434 "It has been moved to KiCad layer Eco1_User." ),
1435 aElem.layer );
1436 klayer = Eco1_User;
1437 }
1438
1439 if( !aElem.referencePoint.empty() )
1440 {
1441 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1442
1443 // line
1444 VECTOR2I last = referencePoint0;
1445 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1446 {
1448 m_board->Add( shape, ADD_MODE::APPEND );
1449 shape->SetLayer( klayer );
1451 shape->SetStart( last );
1452 shape->SetEnd( aElem.referencePoint.at( i ) );
1453 last = aElem.referencePoint.at( i );
1454 }
1455
1456 // arrow
1457 if( aElem.referencePoint.size() >= 2 )
1458 {
1459 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1460
1461 if( dirVec.x != 0 || dirVec.y != 0 )
1462 {
1463 double scaling = EuclideanNorm( dirVec ) / aElem.arrowsize;
1464 VECTOR2I arrVec = VECTOR2I( KiROUND( dirVec.x / scaling ),
1465 KiROUND( dirVec.y / scaling ) );
1466 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1467
1468 PCB_SHAPE* shape1 = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1469 m_board->Add( shape1, ADD_MODE::APPEND );
1470 shape1->SetLayer( klayer );
1472 shape1->SetStart( referencePoint0 );
1473 shape1->SetEnd( referencePoint0 + arrVec );
1474
1475 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1476
1477 PCB_SHAPE* shape2 = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1478 m_board->Add( shape2, ADD_MODE::APPEND );
1479 shape2->SetLayer( klayer );
1481 shape2->SetStart( referencePoint0 );
1482 shape2->SetEnd( referencePoint0 + arrVec );
1483 }
1484 }
1485 }
1486
1487 if( aElem.textPoint.empty() )
1488 {
1489 wxLogError( wxT( "No text position present for leader dimension object" ) );
1490 return;
1491 }
1492
1493 PCB_TEXT* text = new PCB_TEXT( m_board );
1495 text->SetText( aElem.textformat );
1496 text->SetPosition( aElem.textPoint.at( 0 ) );
1497 text->SetLayer( klayer );
1498 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1499 text->SetTextThickness( aElem.textlinewidth );
1500 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1501 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1502}
1503
1504
1506{
1507 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1508
1509 if( klayer == UNDEFINED_LAYER )
1510 {
1511 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1512 "It has been moved to KiCad layer Eco1_User." ),
1513 aElem.layer );
1514 klayer = Eco1_User;
1515 }
1516
1517 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1518 {
1520 m_board->Add( shape, ADD_MODE::APPEND );
1521 shape->SetLayer( klayer );
1523 shape->SetStart( aElem.referencePoint.at( i ) );
1524 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1525 }
1526}
1527
1528
1530{
1531 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1532
1533 if( klayer == UNDEFINED_LAYER )
1534 {
1535 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1536 "It has been moved to KiCad layer Eco1_User." ),
1537 aElem.layer );
1538 klayer = Eco1_User;
1539 }
1540
1541 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1542 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1543
1544 PCB_DIM_CENTER* dimension = new PCB_DIM_CENTER( m_board );
1545 m_board->Add( dimension, ADD_MODE::APPEND );
1546 dimension->SetLayer( klayer );
1547 dimension->SetLineThickness( aElem.linewidth );
1548 dimension->SetStart( aElem.xy1 );
1549 dimension->SetEnd( aElem.xy1 + vec );
1550}
1551
1552
1554 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1555{
1556 if( m_progressReporter )
1557 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1558
1559 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1560
1561 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1562 {
1563 checkpoint();
1564 ADIMENSION6 elem( reader );
1565
1566 switch( elem.kind )
1567 {
1570 break;
1573 break;
1576 break;
1578 wxLogError( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1579 // HelperParseDimensions6Datum( elem );
1580 break;
1583 break;
1584 default:
1585 wxLogError( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1586 break;
1587 }
1588 }
1589
1590 if( reader.GetRemainingBytes() != 0 )
1591 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
1592}
1593
1594
1596 const CFB::COMPOUND_FILE_ENTRY* aEntry,
1597 const std::vector<std::string>& aRootDir )
1598{
1599 if( m_progressReporter )
1600 m_progressReporter->Report( _( "Loading 3D models..." ) );
1601
1602 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1603
1604 if( reader.GetRemainingBytes() == 0 )
1605 return;
1606
1607 wxString projectPath = wxPathOnly( m_board->GetFileName() );
1608 // TODO: set KIPRJMOD always after import (not only when loading project)?
1609 wxSetEnv( PROJECT_VAR_NAME, projectPath );
1610
1611 // TODO: make this path configurable?
1612 const wxString altiumModelDir = wxT( "ALTIUM_EMBEDDED_MODELS" );
1613
1614 wxFileName altiumModelsPath = wxFileName::DirName( projectPath );
1615 wxString kicadModelPrefix = wxT( "${KIPRJMOD}/" ) + altiumModelDir + wxT( "/" );
1616
1617 if( !altiumModelsPath.AppendDir( altiumModelDir ) )
1618 THROW_IO_ERROR( wxT( "Cannot construct directory path for step models" ) );
1619
1620 // Create dir if it does not exist
1621 if( !altiumModelsPath.DirExists() )
1622 {
1623 if( !altiumModelsPath.Mkdir() )
1624 {
1625 wxLogError( _( "Failed to create folder '%s'." ) + wxS( " " )
1626 + _( "No 3D-models will be imported." ),
1627 altiumModelsPath.GetFullPath() );
1628 return;
1629 }
1630 }
1631
1632 int idx = 0;
1633 wxString invalidChars = wxFileName::GetForbiddenChars();
1634
1635 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1636 {
1637 checkpoint();
1638 AMODEL elem( reader );
1639
1640 std::vector<std::string> stepPath = aRootDir;
1641 stepPath.emplace_back( std::to_string( idx ) );
1642
1643 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii() &&
1644 wxString::npos == elem.name.find_first_of( invalidChars );
1645 wxString storageName = !validName ? wxString::Format( wxT( "model_%d" ), idx )
1646 : elem.name;
1647 wxFileName storagePath( altiumModelsPath.GetPath(), storageName );
1648
1649 idx++;
1650
1651 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
1652
1653 if( stepEntry == nullptr )
1654 {
1655 wxLogError( _( "File not found: '%s'. 3D-model not imported." ),
1656 FormatPath( stepPath ) );
1657 continue;
1658 }
1659
1660 size_t stepSize = static_cast<size_t>( stepEntry->size );
1661 std::vector<char> stepContent( stepSize );
1662
1663 // read file into buffer
1664 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
1665 stepSize );
1666
1667 if( !storagePath.IsDirWritable() )
1668 {
1669 wxLogError( _( "Insufficient permissions to save file '%s'." ),
1670 storagePath.GetFullPath() );
1671 continue;
1672 }
1673
1674 wxMemoryInputStream stepStream( stepContent.data(), stepSize );
1675 wxZlibInputStream zlibInputStream( stepStream );
1676
1677 wxFFileOutputStream outputStream( storagePath.GetFullPath() );
1678 outputStream.Write( zlibInputStream );
1679 outputStream.Close();
1680
1681 m_models.insert( { elem.id, kicadModelPrefix + storageName } );
1682 }
1683
1684 if( reader.GetRemainingBytes() != 0 )
1685 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
1686}
1687
1688
1690 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1691{
1692 if( m_progressReporter )
1693 m_progressReporter->Report( _( "Loading nets..." ) );
1694
1695 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1696
1697 wxASSERT( m_altiumToKicadNetcodes.empty() );
1698 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1699 {
1700 checkpoint();
1701 ANET6 elem( reader );
1702
1703 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, 0 );
1704 m_board->Add( netInfo, ADD_MODE::APPEND );
1705
1706 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
1707 }
1708
1709 if( reader.GetRemainingBytes() != 0 )
1710 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
1711}
1712
1714 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1715{
1716 if( m_progressReporter )
1717 m_progressReporter->Report( _( "Loading polygons..." ) );
1718
1719 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1720
1721 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1722 {
1723 checkpoint();
1724 APOLYGON6 elem( reader );
1725
1726 SHAPE_LINE_CHAIN linechain;
1728
1729 if( linechain.PointCount() < 2 )
1730 {
1731 // We have found multiple Altium files with polygon records containing nothing but two
1732 // coincident vertices. These polygons do not appear when opening the file in Altium.
1733 // https://gitlab.com/kicad/code/kicad/-/issues/8183
1734 //
1735 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
1736 // "points are required." ),
1737 // linechain.PointCount(),
1738 // elem.vertices.size() );
1739
1740 m_polygons.emplace_back( nullptr );
1741 continue;
1742 }
1743
1744 ZONE* zone = new ZONE( m_board );
1745 m_board->Add( zone, ADD_MODE::APPEND );
1746 m_polygons.emplace_back( zone );
1747
1748 zone->SetNetCode( GetNetCode( elem.net ) );
1749 zone->SetPosition( elem.vertices.at( 0 ).position );
1750 zone->SetLocked( elem.locked );
1751 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
1752 zone->Outline()->AddOutline( linechain );
1753
1754 HelperSetZoneLayers( zone, elem.layer );
1755
1756 if( elem.pourindex > m_highest_pour_index )
1758
1759 // TODO: more flexible rule parsing
1761
1762 if( clearanceRule != nullptr )
1763 zone->SetLocalClearance( clearanceRule->planeclearanceClearance );
1764
1765 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
1766
1767 if( polygonConnectRule != nullptr )
1768 {
1769 switch( polygonConnectRule->polygonconnectStyle )
1770 {
1773 break;
1774
1777 break;
1778
1779 default:
1782 break;
1783 }
1784
1785 // TODO: correct variables?
1787 polygonConnectRule->polygonconnectReliefconductorwidth );
1788 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
1789
1790 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
1791 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
1792 }
1793
1794 if( IsAltiumLayerAPlane( elem.layer ) )
1795 {
1796 // outer zone will be set to priority 0 later.
1797 zone->SetAssignedPriority( 1 );
1798
1799 // check if this is the outer zone by simply comparing the BBOX
1800 const auto& outer_plane = m_outer_plane.find( elem.layer );
1801 if( outer_plane == m_outer_plane.end()
1802 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
1803 {
1804 m_outer_plane[elem.layer] = zone;
1805 }
1806 }
1807
1810 {
1812 zone->SetHatchThickness( elem.trackwidth );
1813
1815 {
1816 // use a small hack to get us only an outline (hopefully)
1817 const BOX2I& bbox = zone->GetBoundingBox();
1818 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
1819 }
1820 else
1821 {
1822 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
1823 }
1824
1827 }
1828
1831 }
1832
1833 if( reader.GetRemainingBytes() != 0 )
1834 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
1835}
1836
1838 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1839{
1840 if( m_progressReporter )
1841 m_progressReporter->Report( _( "Loading rules..." ) );
1842
1843 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1844
1845 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1846 {
1847 checkpoint();
1848 ARULE6 elem( reader );
1849
1850 m_rules[elem.kind].emplace_back( elem );
1851 }
1852
1853 // sort rules by priority
1854 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
1855 {
1856 std::sort( val.second.begin(), val.second.end(),
1857 []( const ARULE6& lhs, const ARULE6& rhs )
1858 {
1859 return lhs.priority < rhs.priority;
1860 } );
1861 }
1862
1863 if( reader.GetRemainingBytes() != 0 )
1864 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
1865}
1866
1868 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1869{
1870 if( m_progressReporter )
1871 m_progressReporter->Report( _( "Loading board regions..." ) );
1872
1873 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1874
1875 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1876 {
1877 checkpoint();
1878 AREGION6 elem( reader, false );
1879
1880 // TODO: implement?
1881 }
1882
1883 if( reader.GetRemainingBytes() != 0 )
1884 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
1885}
1886
1888 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1889{
1890 if( m_progressReporter )
1891 m_progressReporter->Report( _( "Loading zones..." ) );
1892
1893 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1894
1895 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1896 {
1897 checkpoint();
1898 AREGION6 elem( reader, true );
1899
1902 {
1903 // TODO: implement all different types for footprints
1905 }
1906 else
1907 {
1908 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
1910 }
1911 }
1912
1913 if( reader.GetRemainingBytes() != 0 )
1914 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
1915}
1916
1917
1919{
1921 {
1923 }
1924 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
1925 {
1926 SHAPE_LINE_CHAIN linechain;
1928
1929 if( linechain.PointCount() < 2 )
1930 {
1931 // We have found multiple Altium files with polygon records containing nothing but
1932 // two coincident vertices. These polygons do not appear when opening the file in
1933 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
1934 return;
1935 }
1936
1937 ZONE* zone = new ZONE( m_board );
1938 m_board->Add( zone, ADD_MODE::APPEND );
1939
1940 zone->SetIsRuleArea( true );
1941
1943
1944 zone->SetPosition( aElem.outline.at( 0 ).position );
1945 zone->Outline()->AddOutline( linechain );
1946
1947 HelperSetZoneLayers( zone, aElem.layer );
1948
1951 }
1952 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
1953 {
1954 if( aElem.subpolyindex == ALTIUM_POLYGON_NONE )
1955 {
1956 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
1957 {
1959 }
1960 }
1961 }
1962 else
1963 {
1964 wxLogError( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
1965 }
1966}
1967
1968
1970 const AREGION6& aElem )
1971{
1972 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
1973 {
1974 SHAPE_LINE_CHAIN linechain;
1976
1977 if( linechain.PointCount() < 2 )
1978 {
1979 // We have found multiple Altium files with polygon records containing nothing but
1980 // two coincident vertices. These polygons do not appear when opening the file in
1981 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
1982 return;
1983 }
1984
1985 FP_ZONE* zone = new FP_ZONE( aFootprint );
1986 aFootprint->Add( zone, ADD_MODE::APPEND );
1987
1988 zone->SetIsRuleArea( true );
1989
1991
1992 zone->SetPosition( aElem.outline.at( 0 ).position );
1993 zone->Outline()->AddOutline( linechain );
1994
1995 HelperSetZoneLayers( zone, aElem.layer );
1996
1999 }
2000 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2001 {
2002 if( aElem.subpolyindex == ALTIUM_POLYGON_NONE )
2003 {
2004 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2005 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
2006 }
2007 }
2008 else
2009 {
2010 wxLogError( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2011 }
2012}
2013
2014
2016 PCB_LAYER_ID aLayer )
2017{
2018 SHAPE_LINE_CHAIN linechain;
2020
2021 if( linechain.PointCount() < 2 )
2022 {
2023 // We have found multiple Altium files with polygon records containing nothing
2024 // but two coincident vertices. These polygons do not appear when opening the
2025 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2026 return;
2027 }
2028
2029 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::POLY );
2030
2031 shape->SetPolyShape( linechain );
2032 shape->SetFilled( true );
2033 shape->SetLayer( aLayer );
2034 shape->SetStroke( STROKE_PARAMS( 0 ) );
2035
2036 m_board->Add( shape, ADD_MODE::APPEND );
2037}
2038
2039
2041 const AREGION6& aElem,
2042 PCB_LAYER_ID aLayer )
2043{
2044 SHAPE_LINE_CHAIN linechain;
2046
2047 if( linechain.PointCount() < 2 )
2048 {
2049 // We have found multiple Altium files with polygon records containing nothing
2050 // but two coincident vertices. These polygons do not appear when opening the
2051 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2052 return;
2053 }
2054
2055 FP_SHAPE* shape = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2056
2057 shape->SetPolyShape( linechain );
2058 shape->SetFilled( true );
2059 shape->SetLayer( aLayer );
2060 shape->SetStroke( STROKE_PARAMS( 0 ) );
2061
2062 HelperShapeSetLocalCoord( shape );
2063 aFootprint->Add( shape, ADD_MODE::APPEND );
2064}
2065
2066
2068 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2069{
2070 if( m_progressReporter )
2071 m_progressReporter->Report( _( "Loading zone fills..." ) );
2072
2073 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2074
2075 for( ZONE* zone : m_polygons )
2076 {
2077 if( zone )
2078 zone->UnFill(); // just to be sure
2079 }
2080
2081 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2082 {
2083 checkpoint();
2084 AREGION6 elem( reader, false );
2085
2086 if( elem.subpolyindex != ALTIUM_POLYGON_NONE )
2087 {
2088 if( m_polygons.size() <= elem.subpolyindex )
2089 {
2090 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2091 "of %d existing polygons.",
2092 elem.subpolyindex,
2093 m_polygons.size() ) );
2094 }
2095
2096 ZONE *zone = m_polygons.at( elem.subpolyindex );
2097
2098 if( zone == nullptr )
2099 {
2100 continue; // we know the zone id, but because we do not know the layer we did not
2101 // add it!
2102 }
2103
2104 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2105
2106 if( klayer == UNDEFINED_LAYER )
2107 continue; // Just skip it for now. Users can fill it themselves.
2108
2109 SHAPE_LINE_CHAIN linechain;
2110
2111 for( const ALTIUM_VERTICE& vertice : elem.outline )
2112 linechain.Append( vertice.position );
2113
2114 linechain.Append( elem.outline.at( 0 ).position );
2115 linechain.SetClosed( true );
2116
2117 SHAPE_POLY_SET fill;
2118 fill.AddOutline( linechain );
2119
2120 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2121 {
2122 SHAPE_LINE_CHAIN hole_linechain;
2123
2124 for( const ALTIUM_VERTICE& vertice : hole )
2125 hole_linechain.Append( vertice.position );
2126
2127 hole_linechain.Append( hole.at( 0 ).position );
2128 hole_linechain.SetClosed( true );
2129 fill.AddHole( hole_linechain );
2130 }
2131
2132 if( zone->HasFilledPolysForLayer( klayer ) )
2133 fill.BooleanAdd( *zone->GetFill( klayer ), SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
2134
2136
2137 zone->SetFilledPolysList( klayer, fill );
2138 zone->SetIsFilled( true );
2139 zone->SetNeedRefill( false );
2140 }
2141 }
2142
2143 if( reader.GetRemainingBytes() != 0 )
2144 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2145}
2146
2147
2149 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2150{
2151 if( m_progressReporter )
2152 m_progressReporter->Report( _( "Loading arcs..." ) );
2153
2154 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2155
2156 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2157 {
2158 checkpoint();
2159 AARC6 elem( reader );
2160
2161 if( elem.component == ALTIUM_COMPONENT_NONE )
2162 {
2163 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2164 }
2165 else
2166 {
2167 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2168 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2169 }
2170 }
2171
2172 if( reader.GetRemainingBytes() != 0 )
2173 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2174}
2175
2176
2178{
2179 if( aElem.startangle == 0. && aElem.endangle == 360. )
2180 {
2181 aShape->SetShape( SHAPE_T::CIRCLE );
2182
2183 // TODO: other variants to define circle?
2184 aShape->SetStart( aElem.center );
2185 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
2186 }
2187 else
2188 {
2189 aShape->SetShape( SHAPE_T::ARC );
2190
2191 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2192 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2193
2194 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2195 -KiROUND( startAngle.Sin() * aElem.radius ) );
2196
2197 aShape->SetCenter( aElem.center );
2198 aShape->SetStart( aElem.center + startOffset );
2199 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
2200 }
2201}
2202
2203
2204void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
2205{
2206 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
2207 return;
2208
2209 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2210 || IsAltiumLayerAPlane( aElem.layer ) )
2211 {
2212 // This is not the actual board item. We can use it to create the polygon for the region
2213 PCB_SHAPE shape( nullptr );
2214
2215 ConvertArcs6ToPcbShape( aElem, &shape );
2217
2219 }
2220 else
2221 {
2222 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2223 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2224 }
2225
2226 for( const auto layerExpansionMask :
2228 {
2229 int width = aElem.width + ( layerExpansionMask.second * 2 );
2230
2231 if( width > 1 )
2232 {
2233 PCB_SHAPE* arc = new PCB_SHAPE( m_board );
2234
2235 ConvertArcs6ToPcbShape( aElem, arc );
2237 arc->SetLayer( layerExpansionMask.first );
2238
2239 m_board->Add( arc, ADD_MODE::APPEND );
2240 }
2241 }
2242}
2243
2244
2246 const int aPrimitiveIndex, const bool aIsBoardImport )
2247{
2248 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
2249 return;
2250
2251 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2252 || IsAltiumLayerAPlane( aElem.layer ) )
2253 {
2254 // This is not the actual board item. We can use it to create the polygon for the region
2255 PCB_SHAPE shape( nullptr );
2256
2257 ConvertArcs6ToPcbShape( aElem, &shape );
2259
2260 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
2261 aElem.keepoutrestrictions );
2262 }
2263 else
2264 {
2265 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2266 {
2267 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2268 {
2269 // Special case: do to not lose net connections in footprints
2270 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2271 }
2272 else
2273 {
2274 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
2275 }
2276 }
2277 }
2278
2279 for( const auto layerExpansionMask :
2281 {
2282 int width = aElem.width + ( layerExpansionMask.second * 2 );
2283
2284 if( width > 1 )
2285 {
2286 FP_SHAPE* arc = new FP_SHAPE( aFootprint );
2287
2288 ConvertArcs6ToPcbShape( aElem, arc );
2290 arc->SetLayer( layerExpansionMask.first );
2291
2292 arc->SetLocalCoord();
2293 aFootprint->Add( arc, ADD_MODE::APPEND );
2294 }
2295 }
2296}
2297
2298
2300{
2301 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2302 {
2303 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
2304 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
2305
2306 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2307 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2308
2309 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2310 -KiROUND( startAngle.Sin() * aElem.radius ) );
2311
2312 shape.SetCenter( aElem.center );
2313 shape.SetStart( aElem.center + startOffset );
2314 shape.SetArcAngleAndEnd( includedAngle.Normalize(), true );
2315
2316 // Create actual arc
2317 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(), aElem.width );
2318 PCB_ARC* arc = new PCB_ARC( m_board, &shapeArc );
2319
2320 arc->SetWidth( aElem.width );
2321 arc->SetLayer( aLayer );
2322 arc->SetNetCode( GetNetCode( aElem.net ) );
2323
2324 m_board->Add( arc, ADD_MODE::APPEND );
2325 }
2326 else
2327 {
2328 PCB_SHAPE* arc = new PCB_SHAPE( m_board );
2329
2330 ConvertArcs6ToPcbShape( aElem, arc );
2332 arc->SetLayer( aLayer );
2333
2334 m_board->Add( arc, ADD_MODE::APPEND );
2335 }
2336}
2337
2338
2340 PCB_LAYER_ID aLayer )
2341{
2342 FP_SHAPE* arc = new FP_SHAPE( aFootprint );
2343
2344 ConvertArcs6ToPcbShape( aElem, arc );
2346 arc->SetLayer( aLayer );
2347
2348 arc->SetLocalCoord();
2349 aFootprint->Add( arc, ADD_MODE::APPEND );
2350}
2351
2352
2354 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2355{
2356 if( m_progressReporter )
2357 m_progressReporter->Report( _( "Loading pads..." ) );
2358
2359 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2360
2361 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2362 {
2363 checkpoint();
2364 APAD6 elem( reader );
2365
2366 if( elem.component == ALTIUM_COMPONENT_NONE )
2367 {
2369 }
2370 else
2371 {
2372 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2373 ConvertPads6ToFootprintItem( footprint, elem );
2374 }
2375 }
2376
2377 if( reader.GetRemainingBytes() != 0 )
2378 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
2379}
2380
2381
2383{
2384 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
2385 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
2386 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
2387 {
2389 }
2390 else
2391 {
2392 // We cannot add a pad directly into the PCB
2393 FOOTPRINT* footprint = new FOOTPRINT( m_board );
2394 footprint->SetPosition( aElem.position );
2395
2396 ConvertPads6ToFootprintItemOnCopper( footprint, aElem );
2397
2398 m_board->Add( footprint, ADD_MODE::APPEND );
2399 }
2400}
2401
2402
2404{
2405 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
2406 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
2407 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
2408 {
2409 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
2410 }
2411 else
2412 {
2413 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
2414 }
2415}
2416
2417
2419{
2420 PAD* pad = new PAD( aFootprint );
2421
2422 pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
2423
2424 pad->SetNumber( aElem.name );
2425 pad->SetNetCode( GetNetCode( aElem.net ) );
2426
2427 pad->SetPosition( aElem.position );
2428 pad->SetOrientationDegrees( aElem.direction );
2429 pad->SetLocalCoord();
2430
2431 pad->SetSize( aElem.topsize );
2432
2433 if( aElem.holesize == 0 )
2434 {
2435 pad->SetAttribute( PAD_ATTRIB::SMD );
2436 }
2437 else
2438 {
2439 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
2440 {
2441 // TODO: I assume other values are possible as well?
2442 wxLogError( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
2443 aFootprint->GetReference(),
2444 aElem.name );
2445 }
2446
2447 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
2448
2449 if( !aElem.sizeAndShape || aElem.sizeAndShape->holeshape == ALTIUM_PAD_HOLE_SHAPE::ROUND )
2450 {
2452 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
2453 }
2454 else
2455 {
2456 switch( aElem.sizeAndShape->holeshape )
2457 {
2459 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
2460 break;
2461
2463 wxLogWarning( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
2464 aFootprint->GetReference(),
2465 aElem.name );
2466
2468 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
2469 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
2470 // this case or rect holes have a different id
2471 break;
2472
2474 {
2476 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
2477
2478 slotRotation.Normalize();
2479
2480 if( slotRotation.IsHorizontal() )
2481 {
2482 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
2483 }
2484 else if( slotRotation.IsVertical() )
2485 {
2486 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
2487 }
2488 else
2489 {
2490 wxLogWarning( _( "Footprint %s pad %s has a hole-rotation of %f degrees. "
2491 "KiCad only supports 90 degree rotations." ),
2492 aFootprint->GetReference(),
2493 aElem.name,
2494 slotRotation.AsDegrees() );
2495 }
2496
2497 break;
2498 }
2499
2500 default:
2502 wxLogError( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
2503 aFootprint->GetReference(),
2504 aElem.name,
2505 aElem.sizeAndShape->holeshape );
2506
2508 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
2509 break;
2510 }
2511 }
2512
2513 if( aElem.sizeAndShape )
2514 pad->SetOffset( aElem.sizeAndShape->holeoffset[0] );
2515 }
2516
2517 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
2518 {
2519 wxLogError( _( "Footprint %s pad %s uses a complex pad stack (not yet supported.)" ),
2520 aFootprint->GetReference(), aElem.name );
2521 }
2522
2523 switch( aElem.topshape )
2524 {
2526 pad->SetShape( PAD_SHAPE::RECT );
2527 break;
2528
2530 if( aElem.sizeAndShape
2531 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
2532 {
2533 pad->SetShape( PAD_SHAPE::ROUNDRECT ); // 100 = round, 0 = rectangular
2534 double ratio = aElem.sizeAndShape->cornerradius[0] / 200.;
2535 pad->SetRoundRectRadiusRatio( ratio );
2536 }
2537 else if( aElem.topsize.x == aElem.topsize.y )
2538 {
2539 pad->SetShape( PAD_SHAPE::CIRCLE );
2540 }
2541 else
2542 {
2543 pad->SetShape( PAD_SHAPE::OVAL );
2544 }
2545
2546 break;
2547
2549 pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
2550 pad->SetChamferPositions( RECT_CHAMFER_ALL );
2551 pad->SetChamferRectRatio( 0.25 );
2552 break;
2553
2555 default:
2556 wxLogError( _( "Footprint %s pad %s uses an unknown pad-shape." ),
2557 aFootprint->GetReference(), aElem.name );
2558 break;
2559 }
2560
2561 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
2562 {
2563 // KiCad likes NPTH pads to be the same size & shape as their holes
2564 pad->SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ? PAD_SHAPE::CIRCLE
2565 : PAD_SHAPE::OVAL );
2566 pad->SetSize( pad->GetDrillSize() );
2567 }
2568
2569 switch( aElem.layer )
2570 {
2572 pad->SetLayer( F_Cu );
2573 pad->SetLayerSet( PAD::SMDMask() );
2574 break;
2575
2577 pad->SetLayer( B_Cu );
2578 pad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
2579 break;
2580
2582 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
2583 break;
2584
2585 default:
2586 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2587 pad->SetLayer( klayer );
2588 pad->SetLayerSet( LSET( 1, klayer ) );
2589 break;
2590 }
2591
2593 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
2594
2596 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
2597
2598 if( aElem.is_tent_top )
2599 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
2600
2601 if( aElem.is_tent_bottom )
2602 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
2603
2604 aFootprint->Add( pad, ADD_MODE::APPEND );
2605}
2606
2607
2609{
2610 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2611
2612 if( klayer == UNDEFINED_LAYER )
2613 {
2614 wxLogWarning( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
2615 "equivalent. It has been moved to KiCad layer Eco1_User." ),
2616 aElem.name, aElem.layer );
2617 klayer = Eco1_User;
2618 }
2619
2620 PCB_SHAPE* pad = new PCB_SHAPE( m_board );
2621
2622 HelperParsePad6NonCopper( aElem, klayer, pad );
2623
2625}
2626
2627
2629{
2630 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2631
2632 if( klayer == UNDEFINED_LAYER )
2633 {
2634 wxLogWarning(
2635 _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad equivalent. "
2636 "It has been moved to KiCad layer Eco1_User." ),
2637 aElem.name, aElem.layer );
2638 klayer = Eco1_User;
2639 }
2640
2641 FP_SHAPE* pad = new FP_SHAPE( aFootprint );
2642
2643 HelperParsePad6NonCopper( aElem, klayer, pad );
2644
2646 aFootprint->Add( pad, ADD_MODE::APPEND );
2647}
2648
2649
2651 PCB_SHAPE* aShape )
2652{
2653 if( aElem.net != ALTIUM_NET_UNCONNECTED )
2654 {
2655 wxLogError( _( "Non-copper pad %s is connected to a net, which is not supported." ),
2656 aElem.name );
2657 }
2658
2659 if( aElem.holesize != 0 )
2660 {
2661 wxLogError( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
2662 }
2663
2664 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
2665 {
2666 wxLogWarning( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
2667 aElem.name );
2668 }
2669
2670 switch( aElem.topshape )
2671 {
2673 {
2674 // filled rect
2675 aShape->SetShape( SHAPE_T::POLY );
2676 aShape->SetFilled( true );
2677 aShape->SetLayer( aLayer );
2678 aShape->SetStroke( STROKE_PARAMS( 0 ) );
2679
2680 aShape->SetPolyPoints(
2681 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
2682 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
2683 aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
2684 aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
2685
2686 if( aElem.direction != 0 )
2687 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2688 }
2689 break;
2690
2692 if( aElem.sizeAndShape
2693 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
2694 {
2695 // filled roundrect
2696 int cornerradius = aElem.sizeAndShape->cornerradius[0];
2697 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
2698
2699 aShape->SetLayer( aLayer );
2700 aShape->SetStroke( STROKE_PARAMS( offset * 2, PLOT_DASH_TYPE::SOLID ) );
2701
2702 if( cornerradius < 100 )
2703 {
2704 int offsetX = aElem.topsize.x / 2 - offset;
2705 int offsetY = aElem.topsize.y / 2 - offset;
2706
2707 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
2708 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
2709 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
2710 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
2711
2712 aShape->SetShape( SHAPE_T::POLY );
2713 aShape->SetFilled( true );
2714 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
2715 }
2716 else if( aElem.topsize.x == aElem.topsize.y )
2717 {
2718 // circle
2719 aShape->SetShape( SHAPE_T::CIRCLE );
2720 aShape->SetFilled( true );
2721 aShape->SetStart( aElem.position );
2722 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
2723 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, PLOT_DASH_TYPE::SOLID ) );
2724 }
2725 else if( aElem.topsize.x < aElem.topsize.y )
2726 {
2727 // short vertical line
2728 aShape->SetShape( SHAPE_T::SEGMENT );
2729 VECTOR2I pointOffset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
2730 aShape->SetStart( aElem.position + pointOffset );
2731 aShape->SetEnd( aElem.position - pointOffset );
2732 }
2733 else
2734 {
2735 // short horizontal line
2736 aShape->SetShape( SHAPE_T::SEGMENT );
2737 VECTOR2I pointOffset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
2738 aShape->SetStart( aElem.position + pointOffset );
2739 aShape->SetEnd( aElem.position - pointOffset );
2740 }
2741
2742 if( aElem.direction != 0 )
2743 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2744 }
2745 else if( aElem.topsize.x == aElem.topsize.y )
2746 {
2747 // filled circle
2748 aShape->SetShape( SHAPE_T::CIRCLE );
2749 aShape->SetFilled( true );
2750 aShape->SetLayer( aLayer );
2751 aShape->SetStart( aElem.position );
2752 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
2753 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, PLOT_DASH_TYPE::SOLID ) );
2754 }
2755 else
2756 {
2757 // short line
2758 aShape->SetShape( SHAPE_T::SEGMENT );
2759 aShape->SetLayer( aLayer );
2760 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
2762
2763 if( aElem.topsize.x < aElem.topsize.y )
2764 {
2765 VECTOR2I offset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
2766 aShape->SetStart( aElem.position + offset );
2767 aShape->SetEnd( aElem.position - offset );
2768 }
2769 else
2770 {
2771 VECTOR2I offset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
2772 aShape->SetStart( aElem.position + offset );
2773 aShape->SetEnd( aElem.position - offset );
2774 }
2775
2776 if( aElem.direction != 0 )
2777 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2778 }
2779 break;
2780
2782 {
2783 // filled octagon
2784 aShape->SetShape( SHAPE_T::POLY );
2785 aShape->SetFilled( true );
2786 aShape->SetLayer( aLayer );
2787 aShape->SetStroke( STROKE_PARAMS( 0 ) );
2788
2789 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
2790 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2791 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2792 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
2793
2794 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
2795 VECTOR2I chamferX( chamfer, 0 );
2796 VECTOR2I chamferY( 0, chamfer );
2797
2798 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
2799 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
2800
2801 if( aElem.direction != 0. )
2802 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2803 }
2804 break;
2805
2807 default:
2808 wxLogError( _( "Non-copper pad %s uses an unknown pad-shape." ), aElem.name );
2809 break;
2810 }
2811}
2812
2813
2815 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2816{
2817 if( m_progressReporter )
2818 m_progressReporter->Report( _( "Loading vias..." ) );
2819
2820 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2821
2822 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2823 {
2824 checkpoint();
2825 AVIA6 elem( reader );
2826
2827 PCB_VIA* via = new PCB_VIA( m_board );
2829
2830 via->SetPosition( elem.position );
2831 via->SetWidth( elem.diameter );
2832 via->SetDrill( elem.holesize );
2833 via->SetNetCode( GetNetCode( elem.net ) );
2834 via->SetLocked( elem.is_locked );
2835
2836 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
2838 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
2840
2841 if( start_layer_outside && end_layer_outside )
2842 {
2843 via->SetViaType( VIATYPE::THROUGH );
2844 }
2845 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
2846 {
2847 via->SetViaType( VIATYPE::BLIND_BURIED );
2848 }
2849 else
2850 {
2851 via->SetViaType( VIATYPE::MICROVIA ); // TODO: always a microvia?
2852 }
2853
2854 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
2855 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
2856
2857 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
2858 {
2859 wxLogError( _( "Via from layer %d to %d uses a non-copper layer, which is not "
2860 "supported." ),
2861 elem.layer_start,
2862 elem.layer_end );
2863 continue; // just assume through-hole instead.
2864 }
2865
2866 // we need VIATYPE set!
2867 via->SetLayerPair( start_klayer, end_klayer );
2868 }
2869
2870 if( reader.GetRemainingBytes() != 0 )
2871 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
2872}
2873
2875 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2876{
2877 if( m_progressReporter )
2878 m_progressReporter->Report( _( "Loading tracks..." ) );
2879
2880 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2881
2882 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2883 {
2884 checkpoint();
2885 ATRACK6 elem( reader );
2886
2887 if( elem.component == ALTIUM_COMPONENT_NONE )
2888 {
2889 ConvertTracks6ToBoardItem( elem, primitiveIndex );
2890 }
2891 else
2892 {
2893 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2894 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
2895 }
2896 }
2897
2898 if( reader.GetRemainingBytes() != 0 )
2899 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
2900}
2901
2902
2903void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
2904{
2905 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
2906 return;
2907
2908 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2909 || IsAltiumLayerAPlane( aElem.layer ) )
2910 {
2911 // This is not the actual board item. We can use it to create the polygon for the region
2912 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
2913 shape.SetStart( aElem.start );
2914 shape.SetEnd( aElem.end );
2916
2918 }
2919 else
2920 {
2921 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2922 {
2923 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
2924 }
2925 }
2926
2927 for( const auto layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
2928 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
2929 {
2930 int width = aElem.width + ( layerExpansionMask.second * 2 );
2931 if( width > 1 )
2932 {
2934
2935 seg->SetStart( aElem.start );
2936 seg->SetEnd( aElem.end );
2938 seg->SetLayer( layerExpansionMask.first );
2939
2940 m_board->Add( seg, ADD_MODE::APPEND );
2941 }
2942 }
2943}
2944
2945
2947 const int aPrimitiveIndex,
2948 const bool aIsBoardImport )
2949{
2950 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
2951 return;
2952
2953 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2954 || IsAltiumLayerAPlane( aElem.layer ) )
2955 {
2956 // This is not the actual board item. We can use it to create the polygon for the region
2957 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
2958 shape.SetStart( aElem.start );
2959 shape.SetEnd( aElem.end );
2961
2962 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
2963 aElem.keepoutrestrictions );
2964 }
2965 else
2966 {
2967 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2968 {
2969 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2970 {
2971 // Special case: do to not lose net connections in footprints
2972 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
2973 }
2974 else
2975 {
2976 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
2977 }
2978 }
2979 }
2980
2981 for( const auto layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
2982 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
2983 {
2984 int width = aElem.width + ( layerExpansionMask.second * 2 );
2985 if( width > 1 )
2986 {
2987 FP_SHAPE* seg = new FP_SHAPE( aFootprint, SHAPE_T::SEGMENT );
2988
2989 seg->SetStart( aElem.start );
2990 seg->SetEnd( aElem.end );
2992 seg->SetLayer( layerExpansionMask.first );
2993
2994 seg->SetLocalCoord();
2995 aFootprint->Add( seg, ADD_MODE::APPEND );
2996 }
2997 }
2998}
2999
3000
3002{
3003 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3004 {
3005 PCB_TRACK* track = new PCB_TRACK( m_board );
3006
3007 track->SetStart( aElem.start );
3008 track->SetEnd( aElem.end );
3009 track->SetWidth( aElem.width );
3010 track->SetLayer( aLayer );
3011 track->SetNetCode( GetNetCode( aElem.net ) );
3012
3013 m_board->Add( track, ADD_MODE::APPEND );
3014 }
3015 else
3016 {
3018
3019 seg->SetStart( aElem.start );
3020 seg->SetEnd( aElem.end );
3022 seg->SetLayer( aLayer );
3023
3024 m_board->Add( seg, ADD_MODE::APPEND );
3025 }
3026}
3027
3028
3030 PCB_LAYER_ID aLayer )
3031{
3032 FP_SHAPE* seg = new FP_SHAPE( aFootprint, SHAPE_T::SEGMENT );
3033
3034 seg->SetStart( aElem.start );
3035 seg->SetEnd( aElem.end );
3037 seg->SetLayer( aLayer );
3038
3039 seg->SetLocalCoord();
3040 aFootprint->Add( seg, ADD_MODE::APPEND );
3041}
3042
3043
3045 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3046{
3047 if( m_progressReporter )
3048 m_progressReporter->Report( _( "Loading unicode strings..." ) );
3049
3050 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3051
3053
3054 if( reader.GetRemainingBytes() != 0 )
3055 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
3056}
3057
3059 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3060{
3061 if( m_progressReporter )
3062 m_progressReporter->Report( _( "Loading text..." ) );
3063
3064 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3065
3066 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3067 {
3068 checkpoint();
3069 ATEXT6 elem( reader, m_unicodeStrings );
3070
3071 if( elem.component == ALTIUM_COMPONENT_NONE )
3072 {
3074 }
3075 else
3076 {
3077 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3078 ConvertTexts6ToFootprintItem( footprint, elem );
3079 }
3080 }
3081
3082 if( reader.GetRemainingBytes() != 0 )
3083 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
3084}
3085
3086
3088{
3089 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3090 {
3091 wxLogError( _( "Ignored barcode on Altium layer %d (not yet supported)." ), aElem.layer );
3092 return;
3093 }
3094
3095 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3096 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
3097}
3098
3099
3101{
3102 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3103 {
3104 wxLogError( _( "Ignored barcode on Altium layer %d (not yet supported)." ), aElem.layer );
3105 return;
3106 }
3107
3108 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3109 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3110}
3111
3112
3114{
3115 PCB_TEXT* pcbText = new PCB_TEXT( m_board );
3116
3117 // TODO: improve parsing of variables
3118 wxString trimmedText = aElem.text;
3119 trimmedText.Trim();
3120
3121 if( trimmedText.CmpNoCase( wxT( ".Layer_Name" ) ) == 0 )
3122 pcbText->SetText( wxT( "${LAYER}" ) );
3123 else
3124 pcbText->SetText( aElem.text );
3125
3126 pcbText->SetLayer( aLayer );
3127 pcbText->SetPosition( aElem.position );
3128 pcbText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3129
3130 ConvertTexts6ToEdaTextSettings( aElem, pcbText );
3131
3132 m_board->Add( pcbText, ADD_MODE::APPEND );
3133}
3134
3135
3137 PCB_LAYER_ID aLayer )
3138{
3139 FP_TEXT* fpText;
3140 if( aElem.isDesignator )
3141 {
3142 fpText = &aFootprint->Reference(); // TODO: handle multiple layers
3143 }
3144 else if( aElem.isComment )
3145 {
3146 fpText = &aFootprint->Value(); // TODO: handle multiple layers
3147 }
3148 else
3149 {
3150 fpText = new FP_TEXT( aFootprint );
3151 aFootprint->Add( fpText, ADD_MODE::APPEND );
3152 }
3153
3154 // TODO: improve parsing of variables
3155 wxString trimmedText = aElem.text;
3156 trimmedText.Trim();
3157
3158 if( !aElem.isDesignator && trimmedText.CmpNoCase( wxT( ".Designator" ) ) == 0 )
3159 fpText->SetText( wxT( "${REFERENCE}" ) );
3160 else if( !aElem.isComment && trimmedText.CmpNoCase( wxT( ".Comment" ) ) == 0 )
3161 fpText->SetText( wxT( "${VALUE}" ) );
3162 else if( trimmedText.CmpNoCase( wxT( ".Layer_Name" ) ) == 0 )
3163 fpText->SetText( wxT( "${LAYER}" ) );
3164 else
3165 fpText->SetText( aElem.text );
3166
3167 fpText->SetKeepUpright( false );
3168 fpText->SetLayer( aLayer );
3169 fpText->SetPosition( aElem.position );
3170 fpText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) - aFootprint->GetOrientation() );
3171
3172 ConvertTexts6ToEdaTextSettings( aElem, fpText );
3173
3174 fpText->SetLocalCoord();
3175}
3176
3177
3179{
3181 {
3182 // TODO: why is this required? Somehow, truetype size is calculated differently
3183 aEdaText->SetTextSize( VECTOR2I( aElem.height / 2, aElem.height / 2 ) );
3184 }
3185 else
3186 {
3187 aEdaText->SetTextSize( VECTOR2I( aElem.height, aElem.height ) ); // TODO: parse text width
3188 }
3189
3190 aEdaText->SetTextThickness( aElem.strokewidth );
3191 aEdaText->SetBold( aElem.isBold );
3192 aEdaText->SetItalic( aElem.isItalic );
3193 aEdaText->SetMirrored( aElem.isMirrored );
3194
3195 if( aElem.isDesignator || aElem.isComment ) // That's just a bold assumption
3196 {
3199 }
3200 else
3201 {
3202 switch( aElem.textposition )
3203 {
3208 break;
3213 break;
3218 break;
3219 default:
3220 wxLogError( wxT( "Unexpected horizontal Text Position. This should never happen." ) );
3221 break;
3222 }
3223
3224 switch( aElem.textposition )
3225 {
3230 break;
3235 break;
3240 break;
3241 default:
3242 wxLogError( wxT( "Unexpected vertical text position. This should never happen." ) );
3243 break;
3244 }
3245 }
3246}
3247
3248
3250 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3251{
3252 if( m_progressReporter )
3253 m_progressReporter->Report( _( "Loading rectangles..." ) );
3254
3255 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3256
3257 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3258 {
3259 checkpoint();
3260 AFILL6 elem( reader );
3261
3262 if( elem.component == ALTIUM_COMPONENT_NONE )
3263 {
3265 }
3266 else
3267 {
3268 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3269 ConvertFills6ToFootprintItem( footprint, elem, true );
3270 }
3271 }
3272
3273 if( reader.GetRemainingBytes() != 0 )
3274 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
3275}
3276
3277
3279{
3280 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3281 || aElem.net != ALTIUM_NET_UNCONNECTED )
3282 {
3284 }
3285 else
3286 {
3287 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3288 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
3289 }
3290}
3291
3292
3294 const bool aIsBoardImport )
3295{
3296 if( aElem.is_keepout
3297 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
3298 {
3299 // This is not the actual board item. We can use it to create the polygon for the region
3300 PCB_SHAPE shape( nullptr, SHAPE_T::RECT );
3301 shape.SetStart( aElem.pos1 );
3302 shape.SetEnd( aElem.pos2 );
3304
3305 if( aElem.rotation != 0. )
3306 {
3307 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2,
3308 ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3309 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3310 }
3311
3312 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3313 aElem.keepoutrestrictions );
3314 }
3315 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
3316 && aElem.net != ALTIUM_NET_UNCONNECTED )
3317 {
3318 // Special case: do to not lose net connections in footprints
3320 }
3321 else
3322 {
3323 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3324 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3325 }
3326}
3327
3328
3330{
3331 ZONE* zone = new ZONE( m_board );
3332 m_board->Add( zone, ADD_MODE::APPEND );
3333
3334 zone->SetNetCode( GetNetCode( aElem.net ) );
3335
3336 zone->SetPosition( aElem.pos1 );
3337 zone->SetAssignedPriority( 1000 );
3338
3339 HelperSetZoneLayers( zone, aElem.layer );
3340
3341 VECTOR2I p11( aElem.pos1.x, aElem.pos1.y );
3342 VECTOR2I p12( aElem.pos1.x, aElem.pos2.y );
3343 VECTOR2I p22( aElem.pos2.x, aElem.pos2.y );
3344 VECTOR2I p21( aElem.pos2.x, aElem.pos1.y );
3345
3346 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2, ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3347
3348 const int outlineIdx = -1; // this is the id of the copper zone main outline
3349 zone->AppendCorner( p11, outlineIdx );
3350 zone->AppendCorner( p12, outlineIdx );
3351 zone->AppendCorner( p22, outlineIdx );
3352 zone->AppendCorner( p21, outlineIdx );
3353
3354 // should be correct?
3355 zone->SetLocalClearance( 0 );
3357
3358 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
3359 {
3360 zone->SetIsRuleArea( true );
3361
3363 }
3364
3365 if( aElem.rotation != 0. )
3366 zone->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3367
3370}
3371
3372
3374{
3375 PCB_SHAPE* fill = new PCB_SHAPE( m_board, SHAPE_T::RECT );
3376
3377 fill->SetFilled( true );
3378 fill->SetLayer( aLayer );
3379 fill->SetStroke( STROKE_PARAMS( 0 ) );
3380
3381 fill->SetStart( aElem.pos1 );
3382 fill->SetEnd( aElem.pos2 );
3383
3384 if( aElem.rotation != 0. )
3385 {
3386 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
3387 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2, ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3388 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3389 }
3390
3391 m_board->Add( fill, ADD_MODE::APPEND );
3392}
3393
3394
3396 PCB_LAYER_ID aLayer )
3397{
3398 FP_SHAPE* fill = new FP_SHAPE( aFootprint, SHAPE_T::RECT );
3399
3400 fill->SetFilled( true );
3401 fill->SetLayer( aLayer );
3402 fill->SetStroke( STROKE_PARAMS( 0 ) );
3403
3404 fill->SetStart( aElem.pos1 );
3405 fill->SetEnd( aElem.pos2 );
3406
3407 if( aElem.rotation != 0. )
3408 {
3409 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
3410 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2, ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3411 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3412 }
3413
3414 fill->SetLocalCoord();
3415 aFootprint->Add( fill, ADD_MODE::APPEND );
3416}
3417
3418
3419void ALTIUM_PCB::HelperSetZoneLayers( ZONE* aZone, const ALTIUM_LAYER aAltiumLayer )
3420{
3421 if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER || aAltiumLayer == ALTIUM_LAYER::KEEP_OUT_LAYER )
3422 {
3423 aZone->SetLayerSet( LSET::AllCuMask() );
3424 }
3425 else
3426 {
3427 LSET layerSet;
3428
3429 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
3430 layerSet.set( klayer );
3431
3432 aZone->SetLayerSet( layerSet );
3433 }
3434}
3435
3436
3437void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE* aZone, const uint8_t aKeepoutRestrictions )
3438{
3439 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
3440 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
3441 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
3442 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
3443 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
3444
3445 aZone->SetDoNotAllowVias( keepoutRestrictionVia );
3446 aZone->SetDoNotAllowTracks( keepoutRestrictionTrack );
3447 aZone->SetDoNotAllowCopperPour( keepoutRestrictionCopper );
3448 aZone->SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
3449 aZone->SetDoNotAllowFootprints( false );
3450}
3451
3452
3454 const ALTIUM_LAYER aAltiumLayer,
3455 const uint8_t aKeepoutRestrictions )
3456{
3457 ZONE* zone = new ZONE( m_board );
3458
3459 zone->SetIsRuleArea( true );
3460
3461 HelperSetZoneLayers( zone, aAltiumLayer );
3462 HelperSetZoneKeepoutRestrictions( zone, aKeepoutRestrictions );
3463
3464 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
3465
3468
3469 m_board->Add( zone, ADD_MODE::APPEND );
3470}
3471
3472
3474 const PCB_SHAPE& aShape,
3475 const ALTIUM_LAYER aAltiumLayer,
3476 const uint8_t aKeepoutRestrictions )
3477{
3478 FP_ZONE* zone = new FP_ZONE( aFootprint );
3479
3480 zone->SetIsRuleArea( true );
3481
3482 HelperSetZoneLayers( zone, aAltiumLayer );
3483 HelperSetZoneKeepoutRestrictions( zone, aKeepoutRestrictions );
3484
3485 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
3486
3489
3490 // TODO: zone->SetLocalCoord(); missing?
3491 aFootprint->Add( zone, ADD_MODE::APPEND );
3492}
3493
3494
3495std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
3496 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
3497{
3498 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
3499 return {}; // there is nothing to parse
3500
3501 auto elems =
3502 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
3503
3504 if( elems.first == elems.second )
3505 return {}; // there is nothing to parse
3506
3507 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
3508
3509 for( auto it = elems.first; it != elems.second; ++it )
3510 {
3511 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
3512
3514 {
3517 {
3518 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
3519 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
3520 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3521 {
3522 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
3523 }
3524
3525 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
3526 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3527 {
3528 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
3529 }
3530 }
3533 {
3534 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
3535 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3536 {
3537 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
3538 }
3539
3540 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
3541 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3542 {
3543 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
3544 }
3545 }
3546 }
3547 }
3548
3549 return layerExpansionPairs;
3550}
const char * name
Definition: DXF_plotter.cpp:56
std::string FormatPath(const std::vector< std::string > &aVectorPath)
Helper for debug logging (vector -> string)
ALTIUM_RULE_KIND
const uint16_t ALTIUM_NET_UNCONNECTED
const uint16_t ALTIUM_POLYGON_NONE
ALTIUM_LAYER
ALTIUM_RECORD
const int ALTIUM_COMPONENT_NONE
LIB_ID AltiumToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
void HelperShapeLineChainFromAltiumVertices(SHAPE_LINE_CHAIN &aLine, const std::vector< ALTIUM_VERTICE > &aVertices)
Definition: altium_pcb.cpp:159
double normalizeAngleDegrees(double Angle, double aMin, double aMax)
Normalize angle to be aMin < angle <= aMax angle is in degrees.
constexpr double BOLD_FACTOR
Definition: altium_pcb.cpp:58
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:61
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:68
std::function< void(const ALTIUM_COMPOUND_FILE &, const CFB::COMPOUND_FILE_ENTRY *)> PARSE_FUNCTION_POINTER_fp
Definition: altium_pcb.h:103
ALTIUM_PCB_DIR
Definition: altium_pcb.h:35
@ EXTENDPRIMITIVEINFORMATION
constexpr int ARC_HIGH_DEF
Definition: base_units.h:121
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
@ LT_POWER
Definition: board.h:150
@ LT_JUMPER
Definition: board.h:152
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:43
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:44
const CFB::CompoundFileReader & GetCompoundFileReader() const
Definition: altium_parser.h:64
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
wxString ReadWxString()
size_t GetRemainingBytes() const
bool HasParsingError()
std::map< wxString, wxString > ReadProperties()
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
std::map< uint32_t, wxString > ReadWideStringTable()
size_t ReadAndSetSubrecordLength()
void SkipSubrecord()
void ParseFileHeader(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:853
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition: altium_pcb.h:254
void HelperSetZoneLayers(ZONE *aZone, const ALTIUM_LAYER aAltiumLayer)
void ParseShapeBasedRegions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::vector< PCB_DIM_RADIAL * > m_radialDimensions
Definition: altium_pcb.h:243
void ConvertArcs6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const AARC6 &aElem, PCB_LAYER_ID aLayer)
void ConvertTracks6ToBoardItem(const ATRACK6 &aElem, const int aPrimitiveIndex)
void ConvertTracks6ToFootprintItem(FOOTPRINT *aFootprint, const ATRACK6 &aElem, const int aPrimitiveIndex, const bool aIsBoardImport)
int m_highest_pour_index
Altium stores pour order across all layers.
Definition: altium_pcb.h:260
void ConvertTexts6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_LAYER, PCB_LAYER_ID > m_layermap
Definition: altium_pcb.h:247
void ConvertShapeBasedRegions6ToBoardItemOnLayer(const AREGION6 &aElem, PCB_LAYER_ID aLayer)
void HelperParseDimensions6Leader(const ADIMENSION6 &aElem)
std::vector< FOOTPRINT * > m_components
Definition: altium_pcb.h:241
const ARULE6 * GetRuleDefault(ALTIUM_RULE_KIND aKind) const
Definition: altium_pcb.cpp:837
void HelperParsePad6NonCopper(const APAD6 &aElem, PCB_LAYER_ID aLayer, PCB_SHAPE *aShape)
std::vector< PCB_LAYER_ID > GetKicadLayersToIterate(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:306
void ParseRegions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseBoard6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:891
void ParseComponentsBodies6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperPcpShapeAsFootprintKeepoutRegion(FOOTPRINT *aFootprint, const PCB_SHAPE &aShape, const ALTIUM_LAYER aAltiumLayer, const uint8_t aKeepoutRestrictions)
void ParseFills6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void Parse(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
Definition: altium_pcb.cpp:370
void ParseBoardRegionsData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertArcs6ToBoardItem(const AARC6 &aElem, const int aPrimitiveIndex)
std::map< ALTIUM_LAYER, ZONE * > m_outer_plane
Definition: altium_pcb.h:252
std::vector< int > m_altiumToKicadNetcodes
Definition: altium_pcb.h:246
ALTIUM_PCB(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter)
Definition: altium_pcb.cpp:337
unsigned m_totalCount
for progress reporting
Definition: altium_pcb.h:257
void HelperPcpShapeAsBoardKeepoutRegion(const PCB_SHAPE &aShape, const ALTIUM_LAYER aAltiumLayer, const uint8_t aKeepoutRestrictions)
void ConvertFills6ToBoardItemOnLayer(const AFILL6 &aElem, PCB_LAYER_ID aLayer)
std::vector< std::pair< PCB_LAYER_ID, int > > HelperGetSolderAndPasteMaskExpansions(const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer)
void ConvertFills6ToFootprintItem(FOOTPRINT *aFootprint, const AFILL6 &aElem, const bool aIsBoardImport)
void ConvertShapeBasedRegions6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const AREGION6 &aElem, PCB_LAYER_ID aLayer)
void ConvertShapeBasedRegions6ToBoardItem(const AREGION6 &aElem)
void HelperCreateBoardOutline(const std::vector< ALTIUM_VERTICE > &aVertices)
FOOTPRINT * ParseFootprint(const ALTIUM_COMPOUND_FILE &altiumLibFile, const wxString &aFootprintName)
Definition: altium_pcb.cpp:657
void ParseVias6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::map< ALTIUM_RULE_KIND, std::vector< ARULE6 > > m_rules
Definition: altium_pcb.h:248
void ParseClasses6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
unsigned m_doneCount
Definition: altium_pcb.h:255
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
std::map< uint32_t, wxString > m_unicodeStrings
Definition: altium_pcb.h:245
void ConvertTexts6ToBoardItem(const ATEXT6 &aElem)
void ParseArcs6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperParseDimensions6Center(const ADIMENSION6 &aElem)
void HelperParseDimensions6Radial(const ADIMENSION6 &aElem)
void HelperSetZoneKeepoutRestrictions(ZONE *aZone, const uint8_t aKeepoutRestrictions)
PCB_SHAPE * HelperCreateAndAddShape(uint16_t aComponent)
Definition: altium_pcb.cpp:85
void ParsePads6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
BOARD * m_board
Definition: altium_pcb.h:240
void ParseWideStrings6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertTexts6ToBoardItemOnLayer(const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
void ConvertPads6ToFootprintItemOnCopper(FOOTPRINT *aFootprint, const APAD6 &aElem)
void ParseComponents6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
int GetNetCode(uint16_t aId) const
Definition: altium_pcb.cpp:803
void ConvertPads6ToFootprintItemOnNonCopper(FOOTPRINT *aFootprint, const APAD6 &aElem)
void ConvertTexts6ToFootprintItem(FOOTPRINT *aFootprint, const ATEXT6 &aElem)
unsigned m_lastProgressCount
Definition: altium_pcb.h:256
void ConvertArcs6ToFootprintItem(FOOTPRINT *aFootprint, const AARC6 &aElem, const int aPrimitiveIndex, const bool aIsBoardImport)
void HelperParseDimensions6Datum(const ADIMENSION6 &aElem)
void ConvertPads6ToBoardItem(const APAD6 &aElem)
void ConvertFills6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const AFILL6 &aElem, PCB_LAYER_ID aLayer)
void HelperShapeSetLocalCoord(PCB_SHAPE *aShape, uint16_t aComponent)
Definition: altium_pcb.cpp:112
std::vector< ZONE * > m_polygons
Definition: altium_pcb.h:242
std::map< wxString, wxString > m_models
Definition: altium_pcb.h:244
void ConvertArcs6ToPcbShape(const AARC6 &aElem, PCB_SHAPE *aShape)
void ConvertTracks6ToBoardItemOnLayer(const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void ConvertFills6ToBoardItem(const AFILL6 &aElem)
void ParseModelsData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry, const std::vector< std::string > &aRootDir)
void ParseRules6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
FOOTPRINT * HelperGetFootprint(uint16_t aComponent) const
Definition: altium_pcb.cpp:73
PCB_LAYER_ID GetKicadLayer(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:200
void ConvertFills6ToBoardItemWithNet(const AFILL6 &aElem)
void checkpoint()
Definition: altium_pcb.cpp:351
void ConvertTracks6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void ParseTexts6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParsePolygons6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertArcs6ToBoardItemOnLayer(const AARC6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_RECORD, std::multimap< int, const AEXTENDED_PRIMITIVE_INFORMATION > > m_extendedPrimitiveInformationMaps
Definition: altium_pcb.h:250
void ConvertTexts6ToEdaTextSettings(const ATEXT6 &aElem, EDA_TEXT *aEdaText)
void ParseTracks6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertPads6ToBoardItemOnNonCopper(const APAD6 &aElem)
void ConvertShapeBasedRegions6ToFootprintItem(FOOTPRINT *aFootprint, const AREGION6 &aElem)
void ParseNets6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseExtendedPrimitiveInformationData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:870
void ParseDimensions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertPads6ToFootprintItem(FOOTPRINT *aFootprint, const APAD6 &aElem)
const ARULE6 * GetRule(ALTIUM_RULE_KIND aKind, const wxString &aName) const
Definition: altium_pcb.cpp:821
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
void SetGridOrigin(const VECTOR2I &aOrigin)
const VECTOR2I & GetGridOrigin()
const VECTOR2I & GetAuxOrigin()
void SetAuxOrigin(const VECTOR2I &aOrigin)
BOARD_STACKUP & GetStackupDescriptor()
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:70
virtual void SetLocked(bool aLocked)
Definition: board_item.h:266
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:226
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:175
Manage layers needed to make a physical board.
void RemoveAll()
Delete all items in list and clear the list.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:269
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:772
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:842
const PAGE_INFO & GetPageSettings() const
Definition: board.h:632
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:490
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: board.cpp:401
TRACKS & Tracks()
Definition: board.h:308
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:352
void SetCopperLayerCount(int aCount)
Definition: board.cpp:569
const wxString & GetFileName() const
Definition: board.h:306
DRAWINGS & Drawings()
Definition: board.h:314
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:520
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:704
const Vec GetCenter() const
Definition: box2.h:195
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetY() const
Definition: box2.h:181
coord_type GetWidth() const
Definition: box2.h:187
bool Contains(const Vec &aPoint) const
Definition: box2.h:141
coord_type GetX() const
Definition: box2.h:180
EDA_ANGLE Normalize()
Definition: eda_angle.h:249
double Sin() const
Definition: eda_angle.h:206
double AsDegrees() const
Definition: eda_angle.h:149
bool IsHorizontal() const
Definition: eda_angle.h:174
bool IsVertical() const
Definition: eda_angle.h:179
double Cos() const
Definition: eda_angle.h:221
void SetModified()
Definition: eda_item.cpp:64
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:585
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:470
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:247
void SetFilled(bool aFlag)
Definition: eda_shape.h:95
int GetRadius() const
Definition: eda_shape.cpp:523
SHAPE_T GetShape() const
Definition: eda_shape.h:113
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:255
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:124
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:120
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:112
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:149
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:555
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:596
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1121
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:72
BOX2I GetTextBox(int aLine=-1, bool aInvertY=false) const
Useful in multiline texts to calculate the full text or a line area (for zones filling,...
Definition: eda_text.cpp:505
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:208
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:373
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:226
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:250
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:219
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:187
void SetBold(bool aBold)
Definition: eda_text.cpp:211
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:258
void SetTextSize(const VECTOR2I &aNewSize)
Definition: eda_text.cpp:349
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:165
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:195
int GetTextThickness() const
Definition: eda_text.h:112
void SetItalic(bool aItalic)
Definition: eda_text.cpp:203
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:242
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:1688
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:213
void SetLocked(bool isLocked) override
Set the #MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:350
EDA_ANGLE GetOrientation() const
Definition: footprint.h:191
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:203
bool IsFlipped() const
Definition: footprint.h:324
void SetReference(const wxString &aReference)
Definition: footprint.h:528
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:567
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:568
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:184
const wxString & GetReference() const
Definition: footprint.h:519
VECTOR2I GetPosition() const override
Definition: footprint.h:188
FP_TEXT & Reference()
Definition: footprint.h:568
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:93
double m_Opacity
Definition: footprint.h:94
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:92
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:95
virtual void SetLocalCoord()
Set relative coordinates from draw coordinates.
Definition: fp_shape.cpp:52
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: fp_shape.cpp:343
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: fp_text.h:92
void SetLocalCoord()
Definition: fp_text.cpp:220
A specialization of ZONE for use in footprints.
Definition: zone.h:916
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:532
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
Handle the data for a net.
Definition: netinfo.h:67
int GetNetCode() const
Definition: netinfo.h:119
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:387
Definition: pad.h:60
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:196
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:217
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:203
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition: page_info.h:153
int GetWidthIU(double aIUScale) const
Gets the page width in IU.
Definition: page_info.h:144
double GetRadius() const
Definition: pcb_track.cpp:1140
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:322
void SetUnitsFormat(const DIM_UNITS_FORMAT aFormat)
void SetUnits(EDA_UNITS aUnits)
void SetPrefix(const wxString &aPrefix)
void SetLineThickness(int aWidth)
virtual void SetEnd(const VECTOR2I &aPoint)
void SetPrecision(DIM_PRECISION aPrecision)
virtual void SetStart(const VECTOR2I &aPoint)
void SetKeepTextAligned(bool aKeepAligned)
For better understanding of the points that make a dimension:
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
Mark the center of a circle or arc with a cross shape.
A radial dimension indicates either the radius or diameter of an arc or circle.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:67
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:191
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:72
VECTOR2I GetPosition() const override
Definition: pcb_shape.h:65
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:81
void SetWidth(int aWidth)
Definition: pcb_track.h:107
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:110
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:113
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition: seg.cpp:196
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:114
const VECTOR2I & GetP1() const
Definition: shape_arc.h:113
const VECTOR2I & GetP0() const
Definition: shape_arc.h:112
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
int NextShape(int aPointIndex, bool aForwards=true) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
bool IsArcStart(size_t aIndex) const
SEG Segment(int aIndex)
Return a copy of the aIndex-th 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 Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
bool IsEmpty() const
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Return the area of this poly set.
void Move(const VECTOR2I &aVector) override
Simple container to manage line stroke parameters.
Definition: stroke_params.h:88
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:350
T y
Definition: vector3.h:62
T z
Definition: vector3.h:63
T x
Definition: vector3.h:61
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
void SetHatchThickness(int aThickness)
Definition: zone.h:267
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:246
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:714
void SetPosition(const VECTOR2I &aPos) override
Definition: zone.h:102
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
Definition: zone.cpp:826
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:329
void SetMinThickness(int aMinThickness)
Definition: zone.h:252
void SetHatchOrientation(const EDA_ANGLE &aStep)
Definition: zone.h:273
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:711
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition: zone.h:187
SHAPE_POLY_SET * Outline()
Definition: zone.h:318
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition: zone.h:614
void SetIsRuleArea(bool aEnable)
Definition: zone.h:710
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:713
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:629
int GetMinThickness() const
Definition: zone.h:251
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
Definition: zone.cpp:690
void SetIsFilled(bool isFilled)
Definition: zone.h:243
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: zone.h:173
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition: zone.h:600
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:712
void SetLocalClearance(int aClearance)
Definition: zone.h:153
void SetThermalReliefGap(int aThermalReliefGap)
Definition: zone.h:176
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:272
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:715
bool AppendCorner(VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition: zone.cpp:766
void SetAssignedPriority(unsigned aPriority)
Definition: zone.h:107
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:249
void SetHatchGap(int aStep)
Definition: zone.h:270
static int GetDefaultHatchPitch()
Definition: zone.cpp:991
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE & ANGLE_45
Definition: eda_angle.h:430
@ CTX_NETCLASS
@ ERROR_INSIDE
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
#define MAX_CU_LAYERS
Definition: layer_ids.h:140
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:827
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ In22_Cu
Definition: layer_ids.h:86
@ In11_Cu
Definition: layer_ids.h:75
@ In29_Cu
Definition: layer_ids.h:93
@ In30_Cu
Definition: layer_ids.h:94
@ User_8
Definition: layer_ids.h:130
@ In17_Cu
Definition: layer_ids.h:81
@ Edge_Cuts
Definition: layer_ids.h:113
@ Dwgs_User
Definition: layer_ids.h:109
@ F_Paste
Definition: layer_ids.h:101
@ In9_Cu
Definition: layer_ids.h:73
@ User_6
Definition: layer_ids.h:128
@ User_7
Definition: layer_ids.h:129
@ In19_Cu
Definition: layer_ids.h:83
@ In7_Cu
Definition: layer_ids.h:71
@ In28_Cu
Definition: layer_ids.h:92
@ In26_Cu
Definition: layer_ids.h:90
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ User_5
Definition: layer_ids.h:127
@ Eco1_User
Definition: layer_ids.h:111
@ F_Mask
Definition: layer_ids.h:107
@ In21_Cu
Definition: layer_ids.h:85
@ In23_Cu
Definition: layer_ids.h:87
@ B_Paste
Definition: layer_ids.h:100
@ In15_Cu
Definition: layer_ids.h:79
@ In2_Cu
Definition: layer_ids.h:66
@ User_9
Definition: layer_ids.h:131
@ F_Fab
Definition: layer_ids.h:120
@ In10_Cu
Definition: layer_ids.h:74
@ Margin
Definition: layer_ids.h:114
@ F_SilkS
Definition: layer_ids.h:104
@ In4_Cu
Definition: layer_ids.h:68
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ Eco2_User
Definition: layer_ids.h:112
@ In16_Cu
Definition: layer_ids.h:80
@ In24_Cu
Definition: layer_ids.h:88
@ In1_Cu
Definition: layer_ids.h:65
@ User_3
Definition: layer_ids.h:125
@ User_1
Definition: layer_ids.h:123
@ B_SilkS
Definition: layer_ids.h:103
@ In13_Cu
Definition: layer_ids.h:77
@ User_4
Definition: layer_ids.h:126
@ In8_Cu
Definition: layer_ids.h:72
@ In14_Cu
Definition: layer_ids.h:78
@ User_2
Definition: layer_ids.h:124
@ In12_Cu
Definition: layer_ids.h:76
@ In27_Cu
Definition: layer_ids.h:91
@ In6_Cu
Definition: layer_ids.h:70
@ In5_Cu
Definition: layer_ids.h:69
@ In3_Cu
Definition: layer_ids.h:67
@ In20_Cu
Definition: layer_ids.h:84
@ F_Cu
Definition: layer_ids.h:64
@ In18_Cu
Definition: layer_ids.h:82
@ In25_Cu
Definition: layer_ids.h:89
@ B_Fab
Definition: layer_ids.h:119
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint.
Definition: lset.cpp:590
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
@ NPTH
like PAD_PTH, but not plated
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
@ PAD_DRILL_SHAPE_CIRCLE
Definition: pad_shapes.h:70
@ PAD_DRILL_SHAPE_OBLONG
Definition: pad_shapes.h:71
DIM_PRECISION
Definition: pcb_dimension.h:47
@ BLIND_BURIED
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:39
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
wxString NotSpecifiedPrm()
double startangle
uint16_t component
bool is_keepout
uint32_t width
ALTIUM_LAYER layer
uint8_t keepoutrestrictions
VECTOR2I center
uint32_t radius
uint16_t net
bool is_polygonoutline
double endangle
uint16_t subpolyindex
VECTOR2I sheetpos
std::vector< ABOARD6_LAYER_STACKUP > stackup
std::vector< ALTIUM_VERTICE > board_vertices
std::vector< wxString > names
ALTIUM_CLASS_KIND kind
wxString name
wxString sourcefootprintlibrary
ALTIUM_LAYER layer
wxString sourcedesignator
int32_t textprecision
ALTIUM_UNIT textunit
wxString textsuffix
uint32_t textlinewidth
wxString textformat
ALTIUM_LAYER layer
std::vector< VECTOR2I > textPoint
wxString textprefix
uint32_t linewidth
ALTIUM_DIMENSION_KIND kind
uint32_t textheight
std::vector< VECTOR2I > referencePoint
AEXTENDED_PRIMITIVE_INFORMATION_TYPE type
VECTOR2I pos2
ALTIUM_LAYER layer
uint16_t net
VECTOR2I pos1
double rotation
uint8_t keepoutrestrictions
uint16_t component
const VECTOR2I position
wxString id
wxString name
wxString name
int32_t soldermaskexpansionmanual
uint16_t net
ALTIUM_LAYER layer
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
ALTIUM_PAD_SHAPE topshape
ALTIUM_PAD_MODE padmode
ALTIUM_MODE pastemaskexpansionmode
uint32_t holesize
double direction
ALTIUM_MODE soldermaskexpansionmode
wxString name
bool is_tent_bottom
uint16_t component
int32_t pastemaskexpansionmanual
bool is_tent_top
VECTOR2I position
VECTOR2I topsize
std::vector< ALTIUM_VERTICE > vertices
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
int32_t pourindex
int32_t trackwidth
ALTIUM_LAYER layer