KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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"
30
31#include <board.h>
33#include <layer_range.h>
34#include <pcb_dimension.h>
35#include <pad.h>
36#include <pcb_shape.h>
37#include <pcb_text.h>
38#include <pcb_textbox.h>
39#include <pcb_track.h>
40#include <pcb_barcode.h>
41#include <core/profile.h>
42#include <string_utils.h>
43#include <tools/pad_tool.h>
44#include <zone.h>
45
47
48#include <advanced_config.h>
49#include <compoundfilereader.h>
51#include <font/outline_font.h>
52#include <project.h>
53#include <reporter.h>
54#include <trigo.h>
55#include <utf.h>
56#include <wx/docview.h>
57#include <wx/log.h>
58#include <wx/mstream.h>
59#include <wx/wfstream.h>
60#include <wx/zstream.h>
61#include <progress_reporter.h>
62#include <magic_enum.hpp>
63
64
65constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700
66
67
69{
70 return ( aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER )
71 || aLayer == ALTIUM_LAYER::MULTI_LAYER; // TODO: add IsAltiumLayerAPlane?
72}
73
74
79
80FOOTPRINT* ALTIUM_PCB::HelperGetFootprint( uint16_t aComponent ) const
81{
82 if( aComponent == ALTIUM_COMPONENT_NONE || m_components.size() <= aComponent )
83 {
84 THROW_IO_ERROR( wxString::Format( wxT( "Component creator tries to access component id %u "
85 "of %u existing components" ),
86 (unsigned)aComponent, (unsigned)m_components.size() ) );
87 }
88
89 return m_components.at( aComponent );
90}
91
92
94 const std::vector<ALTIUM_VERTICE>& aVertices )
95{
96 for( const ALTIUM_VERTICE& vertex : aVertices )
97 {
98 if( vertex.isRound )
99 {
100 EDA_ANGLE angle( vertex.endangle - vertex.startangle, DEGREES_T );
101 angle.Normalize();
102
103 double startradiant = DEG2RAD( vertex.startangle );
104 double endradiant = DEG2RAD( vertex.endangle );
105 VECTOR2I arcStartOffset = KiROUND( std::cos( startradiant ) * vertex.radius,
106 -std::sin( startradiant ) * vertex.radius );
107
108 VECTOR2I arcEndOffset = KiROUND( std::cos( endradiant ) * vertex.radius,
109 -std::sin( endradiant ) * vertex.radius );
110
111 VECTOR2I arcStart = vertex.center + arcStartOffset;
112 VECTOR2I arcEnd = vertex.center + arcEndOffset;
113
114 bool isShort = arcStart.Distance( arcEnd ) < pcbIUScale.mmToIU( 0.001 )
115 || angle.AsDegrees() < 0.2;
116
117 if( arcStart.Distance( vertex.position )
118 < arcEnd.Distance( vertex.position ) )
119 {
120 if( !isShort )
121 {
122 aLine.Append( SHAPE_ARC( vertex.center, arcStart, -angle ) );
123 }
124 else
125 {
126 aLine.Append( arcStart );
127 aLine.Append( arcEnd );
128 }
129 }
130 else
131 {
132 if( !isShort )
133 {
134 aLine.Append( SHAPE_ARC( vertex.center, arcEnd, angle ) );
135 }
136 else
137 {
138 aLine.Append( arcEnd );
139 aLine.Append( arcStart );
140 }
141 }
142 }
143 else
144 {
145 aLine.Append( vertex.position );
146 }
147 }
148
149 aLine.SetClosed( true );
150}
151
152
154{
155 auto override = m_layermap.find( aAltiumLayer );
156
157 if( override != m_layermap.end() )
158 {
159 return override->second;
160 }
161
162 switch( aAltiumLayer )
163 {
165
166 case ALTIUM_LAYER::TOP_LAYER: return F_Cu;
197 case ALTIUM_LAYER::BOTTOM_LAYER: return B_Cu;
198
201 case ALTIUM_LAYER::TOP_PASTE: return F_Paste;
203 case ALTIUM_LAYER::TOP_SOLDER: return F_Mask;
205
222
225
226 case ALTIUM_LAYER::MECHANICAL_1: return User_1; //Edge_Cuts;
236 case ALTIUM_LAYER::MECHANICAL_11: return User_11; //Eco1 is used for unknown elements
238 case ALTIUM_LAYER::MECHANICAL_13: return B_Fab; // Don't use courtyard layers for other purposes
242
253
254 default: return UNDEFINED_LAYER;
255 }
256}
257
258
259std::vector<PCB_LAYER_ID> ALTIUM_PCB::GetKicadLayersToIterate( ALTIUM_LAYER aAltiumLayer ) const
260{
261 static std::set<ALTIUM_LAYER> altiumLayersWithWarning;
262
263 if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER || aAltiumLayer == ALTIUM_LAYER::KEEP_OUT_LAYER )
264 {
265 int layerCount = m_board ? m_board->GetCopperLayerCount() : 32;
266 std::vector<PCB_LAYER_ID> layers;
267 layers.reserve( layerCount );
268
269 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
270 {
271 if( !m_board || m_board->IsLayerEnabled( layer ) )
272 layers.emplace_back( layer );
273 }
274
275 return layers;
276 }
277
278 PCB_LAYER_ID klayer = GetKicadLayer( aAltiumLayer );
279
280 if( klayer == UNDEFINED_LAYER )
281 {
282 auto it = m_layerNames.find( aAltiumLayer );
283 wxString layerName = it != m_layerNames.end() ? it->second : wxString::Format( wxT( "(%d)" ),
284 (int) aAltiumLayer );
285
286 if( m_reporter && altiumLayersWithWarning.insert( aAltiumLayer ).second )
287 {
288 m_reporter->Report( wxString::Format(
289 _( "Altium layer %s has no KiCad equivalent. It has been moved to KiCad "
290 "layer Eco1_User." ), layerName ), RPT_SEVERITY_INFO );
291 }
292
293 klayer = Eco1_User;
294
295 if( m_board )
296 m_board->SetEnabledLayers( m_board->GetEnabledLayers() | LSET( { klayer } ) );
297 }
298
299 return { klayer };
300}
301
302
303ALTIUM_PCB::ALTIUM_PCB( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter,
304 LAYER_MAPPING_HANDLER& aHandler, REPORTER* aReporter,
305 const wxString& aLibrary, const wxString& aFootprintName )
306{
307 m_board = aBoard;
308 m_progressReporter = aProgressReporter;
309 m_layerMappingHandler = aHandler;
310 m_reporter = aReporter;
311 m_doneCount = 0;
313 m_totalCount = 0;
315 m_library = aLibrary;
316 m_footprintName = aFootprintName;
317}
318
322
324{
325 const unsigned PROGRESS_DELTA = 250;
326
328 {
329 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
330 {
331 m_progressReporter->SetCurrentProgress( ( (double) m_doneCount )
332 / std::max( 1U, m_totalCount ) );
333
334 if( !m_progressReporter->KeepRefreshing() )
335 THROW_IO_ERROR( _( "File import canceled by user." ) );
336
338 }
339 }
340}
341
343 const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
344{
345 // this vector simply declares in which order which functions to call.
346 const std::vector<std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>> parserOrder = {
348 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
349 {
350 this->ParseFileHeader( aFile, fileHeader );
351 } },
353 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
354 {
355 this->ParseBoard6Data( aFile, fileHeader );
356 } },
358 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
359 {
360 this->ParseExtendedPrimitiveInformationData( aFile, fileHeader );
361 } },
363 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
364 {
365 this->ParseComponents6Data( aFile, fileHeader );
366 } },
367 { false, ALTIUM_PCB_DIR::MODELS,
368 [this, aFileMapping]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
369 {
370 std::vector<std::string> dir{ aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) };
371 this->ParseModelsData( aFile, fileHeader, dir );
372 } },
374 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
375 {
376 this->ParseComponentsBodies6Data( aFile, fileHeader );
377 } },
378 { true, ALTIUM_PCB_DIR::NETS6,
379 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
380 {
381 this->ParseNets6Data( aFile, fileHeader );
382 } },
384 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
385 {
386 this->ParseClasses6Data( aFile, fileHeader );
387 } },
389 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
390 {
391 this->ParseRules6Data( aFile, fileHeader );
392 } },
394 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
395 {
396 this->ParseDimensions6Data( aFile, fileHeader );
397 } },
399 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
400 {
401 this->ParsePolygons6Data( aFile, fileHeader );
402 } },
403 { true, ALTIUM_PCB_DIR::ARCS6,
404 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
405 {
406 this->ParseArcs6Data( aFile, fileHeader );
407 } },
408 { true, ALTIUM_PCB_DIR::PADS6,
409 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
410 {
411 this->ParsePads6Data( aFile, fileHeader );
412 } },
413 { true, ALTIUM_PCB_DIR::VIAS6,
414 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
415 {
416 this->ParseVias6Data( aFile, fileHeader );
417 } },
419 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
420 {
421 this->ParseTracks6Data( aFile, fileHeader );
422 } },
424 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
425 {
426 this->ParseWideStrings6Data( aFile, fileHeader );
427 } },
429 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
430 {
431 this->ParseTexts6Data( aFile, fileHeader );
432 } },
434 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
435 {
436 this->ParseFills6Data( aFile, fileHeader );
437 } },
439 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
440 {
441 this->ParseBoardRegionsData( aFile, fileHeader );
442 } },
444 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
445 {
446 this->ParseShapeBasedRegions6Data( aFile, fileHeader );
447 } },
449 [this]( const ALTIUM_PCB_COMPOUND_FILE& aFile, auto fileHeader )
450 {
451 this->ParseRegions6Data( aFile, fileHeader );
452 } }
453 };
454
455 if( m_progressReporter != nullptr )
456 {
457 // Count number of records we will read for the progress reporter
458 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
459 {
460 bool isRequired;
463 std::tie( isRequired, directory, fp ) = cur;
464
466 continue;
467
468 const auto& mappedDirectory = aFileMapping.find( directory );
469
470 if( mappedDirectory == aFileMapping.end() )
471 continue;
472
473 const std::vector<std::string> mappedFile{ mappedDirectory->second, "Header" };
474 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
475
476 if( file == nullptr )
477 continue;
478
479 ALTIUM_BINARY_PARSER reader( altiumPcbFile, file );
480 uint32_t numOfRecords = reader.Read<uint32_t>();
481
482 if( reader.HasParsingError() )
483 {
484 if( m_reporter )
485 {
486 m_reporter->Report( wxString::Format( _( "'%s' was not parsed correctly." ),
487 FormatPath( mappedFile ) ),
489 }
490
491 continue;
492 }
493
494 m_totalCount += numOfRecords;
495
496 if( reader.GetRemainingBytes() != 0 )
497 {
498 if( m_reporter )
499 {
500 m_reporter->Report( wxString::Format( _( "'%s' was not fully parsed." ),
501 FormatPath( mappedFile ) ),
503 }
504
505 continue;
506 }
507 }
508 }
509
510 const auto& boardDirectory = aFileMapping.find( ALTIUM_PCB_DIR::BOARD6 );
511
512 if( boardDirectory != aFileMapping.end() )
513 {
514 std::vector<std::string> mappedFile{ boardDirectory->second, "Data" };
515
516 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
517
518 if( !file )
519 {
521 "This file does not appear to be in a valid PCB Binary Version 6.0 format. In "
522 "Altium Designer, "
523 "make sure to save as \"PCB Binary Files (*.PcbDoc)\"." ) );
524 }
525 }
526
527 // Parse data in specified order
528 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
529 {
530 bool isRequired;
533 std::tie( isRequired, directory, fp ) = cur;
534
535 const auto& mappedDirectory = aFileMapping.find( directory );
536
537 if( mappedDirectory == aFileMapping.end() )
538 {
539 wxASSERT_MSG( !isRequired, wxString::Format( wxT( "Altium Directory of kind %d was "
540 "expected, but no mapping is "
541 "present in the code" ),
542 directory ) );
543 continue;
544 }
545
546 std::vector<std::string> mappedFile{ mappedDirectory->second };
547
549 mappedFile.emplace_back( "Data" );
550
551 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
552
553 if( file != nullptr )
554 {
555 fp( altiumPcbFile, file );
556 }
557 else if( isRequired )
558 {
559 if( m_reporter )
560 {
561 m_reporter->Report( wxString::Format( _( "File not found: '%s' for directory '%s'." ),
562 FormatPath( mappedFile ),
563 magic_enum::enum_name( directory ) ),
565 }
566 }
567 }
568
569 // fixup zone priorities since Altium stores them in the opposite order
570 for( ZONE* zone : m_polygons )
571 {
572 if( !zone )
573 continue;
574
575 // Altium "fills" - not poured in Altium
576 if( zone->GetAssignedPriority() == 1000 )
577 {
578 // Unlikely, but you never know
579 if( m_highest_pour_index >= 1000 )
580 zone->SetAssignedPriority( m_highest_pour_index + 1 );
581
582 continue;
583 }
584
585 int priority = m_highest_pour_index - zone->GetAssignedPriority();
586
587 zone->SetAssignedPriority( priority >= 0 ? priority : 0 );
588 }
589
590 // change priority of outer zone to zero
591 for( std::pair<const ALTIUM_LAYER, ZONE*>& zone : m_outer_plane )
592 zone.second->SetAssignedPriority( 0 );
593
594 // Simplify and fracture zone fills in case we constructed them from tracks (hatched fill)
595 for( ZONE* zone : m_polygons )
596 {
597 if( !zone )
598 continue;
599
600 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
601 {
602 if( !zone->HasFilledPolysForLayer( layer ) )
603 continue;
604
605 zone->GetFilledPolysList( layer )->Fracture();
606 }
607 }
608
609 // Altium doesn't appear to store either the dimension value nor the dimensioned object in
610 // the dimension record. (Yes, there is a REFERENCE0OBJECTID, but it doesn't point to the
611 // dimensioned object.) We attempt to plug this gap by finding a colocated arc or circle
612 // and using its radius. If there are more than one such arcs/circles, well, :shrug:.
614 {
615 int radius = 0;
616
617 for( BOARD_ITEM* item : m_board->Drawings() )
618 {
619 if( item->Type() != PCB_SHAPE_T )
620 continue;
621
622 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
623
624 if( shape->GetShape() != SHAPE_T::ARC && shape->GetShape() != SHAPE_T::CIRCLE )
625 continue;
626
627 if( shape->GetPosition() == dim->GetPosition() )
628 {
629 radius = shape->GetRadius();
630 break;
631 }
632 }
633
634 if( radius == 0 )
635 {
636 for( PCB_TRACK* track : m_board->Tracks() )
637 {
638 if( track->Type() != PCB_ARC_T )
639 continue;
640
641 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
642
643 if( arc->GetCenter() == dim->GetPosition() )
644 {
645 radius = arc->GetRadius();
646 break;
647 }
648 }
649 }
650
651 // Move the radius point onto the circumference
652 VECTOR2I radialLine = dim->GetEnd() - dim->GetStart();
653 int totalLength = radialLine.EuclideanNorm();
654
655 // Enforce a minimum on the radialLine else we won't have enough precision to get the
656 // angle from it.
657 radialLine = radialLine.Resize( std::max( radius, 2 ) );
658 dim->SetEnd( dim->GetStart() + (VECTOR2I) radialLine );
659 dim->SetLeaderLength( totalLength - radius );
660 dim->Update();
661 }
662
663 // center board
664 BOX2I bbbox = m_board->GetBoardEdgesBoundingBox();
665
666 int w = m_board->GetPageSettings().GetWidthIU( pcbIUScale.IU_PER_MILS );
667 int h = m_board->GetPageSettings().GetHeightIU( pcbIUScale.IU_PER_MILS );
668
669 int desired_x = ( w - bbbox.GetWidth() ) / 2;
670 int desired_y = ( h - bbbox.GetHeight() ) / 2;
671
672 VECTOR2I movementVector( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() );
673 m_board->Move( movementVector );
674
675 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
676 bds.SetAuxOrigin( bds.GetAuxOrigin() + movementVector );
677 bds.SetGridOrigin( bds.GetGridOrigin() + movementVector );
678
679 m_board->SetModified();
680}
681
682
684 const wxString& aFootprintName )
685{
686 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
687
688 // TODO: what should we do with those layers?
692
693 m_unicodeStrings.clear();
695
696 // TODO: WideStrings are stored as parameterMap in the case of footprints, not as binary
697 // std::string unicodeStringsStreamName = aFootprintName.ToStdString() + "\\WideStrings";
698 // const CFB::COMPOUND_FILE_ENTRY* unicodeStringsData = altiumLibFile.FindStream( unicodeStringsStreamName );
699 // if( unicodeStringsData != nullptr )
700 // {
701 // ParseWideStrings6Data( altiumLibFile, unicodeStringsData );
702 // }
703
704 std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*> ret =
705 altiumLibFile.FindLibFootprintDirName( aFootprintName );
706
707 wxString fpDirName = std::get<0>( ret );
708 const CFB::COMPOUND_FILE_ENTRY* footprintStream = std::get<1>( ret );
709
710 if( fpDirName.IsEmpty() )
711 {
713 wxString::Format( _( "Footprint directory not found: '%s'." ), aFootprintName ) );
714 }
715
716 const std::vector<std::string> streamName{ fpDirName.ToStdString(), "Data" };
717 const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( footprintStream, { "Data" } );
718
719 if( footprintData == nullptr )
720 {
721 THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ),
722 FormatPath( streamName ) ) );
723 }
724
725 ALTIUM_BINARY_PARSER parser( altiumLibFile, footprintData );
726
728 //wxString footprintName = parser.ReadWxString(); // Not used (single-byte char set)
729 parser.SkipSubrecord();
730
731 LIB_ID fpID = AltiumToKiCadLibID( "", aFootprintName ); // TODO: library name
732 footprint->SetFPID( fpID );
733
734 const std::vector<std::string> parametersStreamName{ fpDirName.ToStdString(),
735 "Parameters" };
736 const CFB::COMPOUND_FILE_ENTRY* parametersData =
737 altiumLibFile.FindStream( footprintStream, { "Parameters" } );
738
739 if( parametersData != nullptr )
740 {
741 ALTIUM_BINARY_PARSER parametersReader( altiumLibFile, parametersData );
742 std::map<wxString, wxString> parameterProperties = parametersReader.ReadProperties();
743 wxString description = ALTIUM_PROPS_UTILS::ReadString( parameterProperties,
744 wxT( "DESCRIPTION" ), wxT( "" ) );
745 footprint->SetLibDescription( description );
746 }
747 else
748 {
749 if( m_reporter )
750 {
751 m_reporter->Report( wxString::Format( _( "File not found: '%s'." ),
752 FormatPath( parametersStreamName ) ),
754 }
755
756 footprint->SetLibDescription( wxT( "" ) );
757 }
758
759 const std::vector<std::string> extendedPrimitiveInformationStreamName{
760 "ExtendedPrimitiveInformation", "Data"
761 };
762 const CFB::COMPOUND_FILE_ENTRY* extendedPrimitiveInformationData =
763 altiumLibFile.FindStream( footprintStream, extendedPrimitiveInformationStreamName );
764
765 if( extendedPrimitiveInformationData != nullptr )
766 ParseExtendedPrimitiveInformationData( altiumLibFile, extendedPrimitiveInformationData );
767
768 footprint->SetReference( wxT( "REF**" ) );
769 footprint->SetValue( aFootprintName );
770 footprint->Reference().SetVisible( true ); // TODO: extract visibility information
771 footprint->Value().SetVisible( true );
772
773 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
774 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
775
776 for( PCB_FIELD* field : footprint->GetFields() )
777 {
778 field->SetTextSize( defaultTextSize );
779 field->SetTextThickness( defaultTextThickness );
780 }
781
782 for( int primitiveIndex = 0; parser.GetRemainingBytes() >= 4; primitiveIndex++ )
783 {
784 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( parser.Peek<uint8_t>() );
785
786 switch( recordtype )
787 {
789 {
790 AARC6 arc( parser );
791 ConvertArcs6ToFootprintItem( footprint.get(), arc, primitiveIndex, false );
792 break;
793 }
795 {
796 APAD6 pad( parser );
797 ConvertPads6ToFootprintItem( footprint.get(), pad );
798 break;
799 }
801 {
802 AVIA6 via( parser );
803 ConvertVias6ToFootprintItem( footprint.get(), via );
804 break;
805 }
807 {
808 ATRACK6 track( parser );
809 ConvertTracks6ToFootprintItem( footprint.get(), track, primitiveIndex, false );
810 break;
811 }
813 {
814 ATEXT6 text( parser, m_unicodeStrings );
815 ConvertTexts6ToFootprintItem( footprint.get(), text );
816 break;
817 }
819 {
820 AFILL6 fill( parser );
821 ConvertFills6ToFootprintItem( footprint.get(), fill, false );
822 break;
823 }
825 {
826 AREGION6 region( parser, false );
827 ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region, primitiveIndex );
828 break;
829 }
831 {
832 ACOMPONENTBODY6 componentBody( parser );
833 ConvertComponentBody6ToFootprintItem( altiumLibFile, footprint.get(), componentBody );
834 break;
835 }
836 default:
837 THROW_IO_ERROR( wxString::Format( _( "Record of unknown type: '%d'." ), recordtype ) );
838 }
839 }
840
841
842 // Loop over this multiple times to catch pads that are jumpered to each other by multiple shapes
843 for( bool changes = true; changes; )
844 {
845 changes = false;
846
847 alg::for_all_pairs( footprint->Pads().begin(), footprint->Pads().end(),
848 [&changes]( PAD* aPad1, PAD* aPad2 )
849 {
850 if( !( aPad1->GetNumber().IsEmpty() ^ aPad2->GetNumber().IsEmpty() ) )
851 return;
852
853 for( PCB_LAYER_ID layer : aPad1->GetLayerSet() )
854 {
855 std::shared_ptr<SHAPE> shape1 = aPad1->GetEffectiveShape( layer );
856 std::shared_ptr<SHAPE> shape2 = aPad2->GetEffectiveShape( layer );
857
858 if( shape1->Collide( shape2.get() ) )
859 {
860 if( aPad1->GetNumber().IsEmpty() )
861 aPad1->SetNumber( aPad2->GetNumber() );
862 else
863 aPad2->SetNumber( aPad1->GetNumber() );
864
865 changes = true;
866 }
867 }
868 } );
869 }
870
871 // Auto-position reference and value
872 footprint->AutoPositionFields();
873
874 if( parser.HasParsingError() )
875 {
876 THROW_IO_ERROR( wxString::Format( wxT( "%s stream was not parsed correctly" ),
877 FormatPath( streamName ) ) );
878 }
879
880 if( parser.GetRemainingBytes() != 0 )
881 {
882 THROW_IO_ERROR( wxString::Format( wxT( "%s stream is not fully parsed" ),
883 FormatPath( streamName ) ) );
884 }
885
886 return footprint.release();
887}
888
889int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
890{
891 if( aId == ALTIUM_NET_UNCONNECTED )
892 {
894 }
895 else if( m_altiumToKicadNetcodes.size() < aId )
896 {
897 THROW_IO_ERROR( wxString::Format( wxT( "Netcode with id %d does not exist. Only %d nets "
898 "are known" ),
899 aId, m_altiumToKicadNetcodes.size() ) );
900 }
901 else
902 {
903 return m_altiumToKicadNetcodes[ aId ];
904 }
905}
906
907const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
908{
909 const auto rules = m_rules.find( aKind );
910
911 if( rules == m_rules.end() )
912 return nullptr;
913
914 for( const ARULE6& rule : rules->second )
915 {
916 if( rule.name == aName )
917 return &rule;
918 }
919
920 return nullptr;
921}
922
924{
925 const auto rules = m_rules.find( aKind );
926
927 if( rules == m_rules.end() )
928 return nullptr;
929
930 for( const ARULE6& rule : rules->second )
931 {
932 if( rule.scope1expr == wxT( "All" ) && rule.scope2expr == wxT( "All" ) )
933 return &rule;
934 }
935
936 return nullptr;
937}
938
940 const CFB::COMPOUND_FILE_ENTRY* aEntry )
941{
942 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
943
945 wxString header = reader.ReadWxString();
946
947 //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
948
949 //reader.SkipSubrecord();
950
951 // TODO: does not seem to work all the time at the moment
952 //if( reader.GetRemainingBytes() != 0 )
953 // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
954}
955
956
958 const CFB::COMPOUND_FILE_ENTRY* aEntry )
959{
961 m_progressReporter->Report( _( "Loading extended primitive information data..." ) );
962
963 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
964
965 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
966 {
967 checkpoint();
968 AEXTENDED_PRIMITIVE_INFORMATION elem( reader );
969
971 std::move( elem ) );
972 }
973
974 if( reader.GetRemainingBytes() != 0 )
975 THROW_IO_ERROR( wxT( "ExtendedPrimitiveInformation stream is not fully parsed" ) );
976}
977
978
980 const CFB::COMPOUND_FILE_ENTRY* aEntry )
981{
983 m_progressReporter->Report( _( "Loading board data..." ) );
984
985 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
986
987 checkpoint();
988 ABOARD6 elem( reader );
989
990 if( reader.GetRemainingBytes() != 0 )
991 THROW_IO_ERROR( wxT( "Board6 stream is not fully parsed" ) );
992
993 m_board->GetDesignSettings().SetAuxOrigin( elem.sheetpos );
994 m_board->GetDesignSettings().SetGridOrigin( elem.sheetpos );
995
996 // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
997 size_t layercount = 0;
998 size_t layerid = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
999
1000 while( layerid < elem.stackup.size() && layerid != 0 )
1001 {
1002 layerid = elem.stackup[ layerid - 1 ].nextId;
1003 layercount++;
1004 }
1005
1006 size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
1007 m_board->SetCopperLayerCount( kicadLayercount );
1008
1009 BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
1010 BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
1011
1012 // create board stackup
1013 stackup.RemoveAll(); // Just to be sure
1014 stackup.BuildDefaultStackupList( &designSettings, layercount );
1015
1016 auto it = stackup.GetList().begin();
1017
1018 // find first copper layer
1019 for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
1020 ;
1021
1022 auto cuLayer = LAYER_RANGE( F_Cu, B_Cu, 32 ).begin();
1023
1024 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
1025 altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
1026 altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
1027 {
1028 // array starts with 0, but stackup with 1
1029 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1030
1031 // handle unused layer in case of odd layercount
1032 if( layer.nextId == 0 && layercount != kicadLayercount )
1033 {
1034 m_board->SetLayerName( ( *it )->GetBrdLayerId(), wxT( "[unused]" ) );
1035
1036 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
1037 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
1038
1039 ( *it )->SetThickness( 0 );
1040
1041 ++it;
1042
1043 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
1044 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
1045
1046 ( *it )->SetThickness( 0, 0 );
1047 ( *it )->SetThicknessLocked( true, 0 );
1048 ++it;
1049 }
1050
1051 m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ), *cuLayer } );
1052 ++cuLayer;
1053
1054 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
1055 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
1056
1057 ( *it )->SetThickness( layer.copperthick );
1058
1059 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1060 PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
1061
1062 m_board->SetLayerName( klayer, layer.name );
1063
1064 if( layer.copperthick == 0 )
1065 m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
1066 else if( IsAltiumLayerAPlane( alayer ) )
1067 m_board->SetLayerType( klayer, LAYER_T::LT_POWER );
1068
1069 if( klayer == B_Cu )
1070 {
1071 if( layer.nextId != 0 )
1072 THROW_IO_ERROR( wxT( "Board6 stream, unexpected id while parsing last stackup layer" ) );
1073
1074 // overwrite entry from internal -> bottom
1075 m_layermap[alayer] = B_Cu;
1076 break;
1077 }
1078
1079 ++it;
1080
1081 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
1082 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
1083
1084 ( *it )->SetThickness( layer.dielectricthick, 0 );
1085 ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
1086 NotSpecifiedPrm() :
1087 wxString( layer.dielectricmaterial ) );
1088 ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
1089
1090 ++it;
1091 }
1092
1093 remapUnsureLayers( elem.stackup );
1094
1095 // Set name of all non-cu layers
1096 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_OVERLAY );
1097 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::BOTTOM_SOLDER ); altiumLayerId++ )
1098 {
1099 // array starts with 0, but stackup with 1
1100 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1101
1102 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1103 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1104
1105 m_board->SetLayerName( klayer, layer.name );
1106 }
1107
1108 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_1 );
1109 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_16 ); altiumLayerId++ )
1110 {
1111 // array starts with 0, but stackup with 1
1112 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1113
1114 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1115 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1116
1117 m_board->SetLayerName( klayer, layer.name );
1118 }
1119
1121 m_board->GetDesignSettings().SetBoardThickness( stackup.BuildBoardThicknessFromStackup() );
1122}
1123
1124
1125void ALTIUM_PCB::remapUnsureLayers( std::vector<ABOARD6_LAYER_STACKUP>& aStackup )
1126{
1127 LSET enabledLayers = m_board->GetEnabledLayers();
1128 LSET validRemappingLayers = enabledLayers | LSET::AllBoardTechMask() |
1130
1131 if( aStackup.size() == 0 )
1132 return;
1133
1134 std::vector<INPUT_LAYER_DESC> inputLayers;
1135 std::map<wxString, ALTIUM_LAYER> altiumLayerNameMap;
1136
1137 ABOARD6_LAYER_STACKUP& curLayer = aStackup[0];
1138 ALTIUM_LAYER layer_num;
1139 INPUT_LAYER_DESC iLdesc;
1140
1141 auto next =
1142 [&]( size_t ii ) -> size_t
1143 {
1144 // Within the copper stack, the nextId can be used to hop over unused layers in
1145 // a particular Altium board. The IDs start with ALTIUM_LAYER::UNKNOWN but the
1146 // first copper layer in the array will be ALTIUM_LAYER::TOP_LAYER.
1147 if( layer_num < ALTIUM_LAYER::BOTTOM_LAYER )
1148 return curLayer.nextId - 1;
1149 else
1150 return ii + 1;
1151 };
1152
1153 for( size_t ii = 0; ii < aStackup.size(); ii = next( ii ) )
1154 {
1155 curLayer = aStackup[ii];
1156 layer_num = static_cast<ALTIUM_LAYER>( ii + 1 );
1157
1158 if( m_layermap.find( layer_num ) != m_layermap.end() )
1159 continue;
1160
1161 if( ii >= m_board->GetCopperLayerCount() && layer_num != ALTIUM_LAYER::BOTTOM_LAYER
1162 && !( layer_num >= ALTIUM_LAYER::TOP_OVERLAY
1163 && layer_num <= ALTIUM_LAYER::BOTTOM_SOLDER )
1164 && !( layer_num >= ALTIUM_LAYER::MECHANICAL_1
1165 && layer_num <= ALTIUM_LAYER::MECHANICAL_16 ) )
1166 {
1167 if( layer_num < ALTIUM_LAYER::BOTTOM_LAYER )
1168 continue;
1169
1171 }
1172 else
1173 {
1174 iLdesc.AutoMapLayer = GetKicadLayer( layer_num );
1175 }
1176
1177 iLdesc.Name = curLayer.name;
1178 iLdesc.PermittedLayers = validRemappingLayers;
1179 iLdesc.Required = ii < m_board->GetCopperLayerCount() || layer_num == ALTIUM_LAYER::BOTTOM_LAYER;
1180
1181 inputLayers.push_back( iLdesc );
1182 altiumLayerNameMap.insert( { curLayer.name, layer_num } );
1183 m_layerNames.insert( { layer_num, curLayer.name } );
1184 }
1185
1186 if( inputLayers.size() == 0 )
1187 return;
1188
1189 // Callback:
1190 std::map<wxString, PCB_LAYER_ID> reMappedLayers = m_layerMappingHandler( inputLayers );
1191
1192 for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers )
1193 {
1194 if( layerPair.second == PCB_LAYER_ID::UNDEFINED_LAYER )
1195 {
1196 // Layer mapping handler returned UNDEFINED_LAYER - skip this layer
1197 // This can happen for layers that don't have a KiCad equivalent
1198 if( m_reporter )
1199 {
1200 m_reporter->Report( wxString::Format( _( "Layer '%s' could not be mapped and "
1201 "will be skipped." ),
1202 layerPair.first ),
1204 }
1205
1206 continue;
1207 }
1208
1209 ALTIUM_LAYER altiumID = altiumLayerNameMap.at( layerPair.first );
1210 m_layermap.insert_or_assign( altiumID, layerPair.second );
1211 enabledLayers |= LSET( { layerPair.second } );
1212 }
1213
1214 m_board->SetEnabledLayers( enabledLayers );
1215 m_board->SetVisibleLayers( enabledLayers );
1216}
1217
1218
1219void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
1220{
1221 SHAPE_LINE_CHAIN lineChain;
1222 HelperShapeLineChainFromAltiumVertices( lineChain, aVertices );
1223
1224 STROKE_PARAMS stroke( m_board->GetDesignSettings().GetLineThickness( Edge_Cuts ),
1226
1227 for( int i = 0; i <= lineChain.PointCount() && i != -1; i = lineChain.NextShape( i ) )
1228 {
1229 if( lineChain.IsArcStart( i ) )
1230 {
1231 const SHAPE_ARC& currentArc = lineChain.Arc( lineChain.ArcIndex( i ) );
1232 int nextShape = lineChain.NextShape( i );
1233 bool isLastShape = nextShape < 0;
1234
1235 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::ARC );
1236
1237 shape->SetStroke( stroke );
1238 shape->SetLayer( Edge_Cuts );
1239 shape->SetArcGeometry( currentArc.GetP0(), currentArc.GetArcMid(), currentArc.GetP1() );
1240
1241 m_board->Add( shape.release(), ADD_MODE::APPEND );
1242 }
1243 else
1244 {
1245 const SEG& seg = lineChain.Segment( i );
1246
1247 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1248
1249 shape->SetStroke( stroke );
1250 shape->SetLayer( Edge_Cuts );
1251 shape->SetStart( seg.A );
1252 shape->SetEnd( seg.B );
1253
1254 m_board->Add( shape.release(), ADD_MODE::APPEND );
1255 }
1256 }
1257}
1258
1259
1261 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1262{
1263 if( m_progressReporter )
1264 m_progressReporter->Report( _( "Loading netclasses..." ) );
1265
1266 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1267
1268 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1269 {
1270 checkpoint();
1271 ACLASS6 elem( reader );
1272
1274 {
1275 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( elem.name );
1276
1277 for( const wxString& name : elem.names )
1278 {
1279 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment(
1280 name, nc->GetName() );
1281 }
1282
1283 if( m_board->GetDesignSettings().m_NetSettings->HasNetclass( nc->GetName() ) )
1284 {
1285 // Name conflict, happens in some unknown circumstances
1286 // unique_ptr will delete nc on this code path
1287 if( m_reporter )
1288 {
1289 wxString msg;
1290 msg.Printf( _( "More than one Altium netclass with name '%s' found. "
1291 "Only the first one will be imported." ), elem.name );
1292 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1293 }
1294 }
1295 else
1296 {
1297 m_board->GetDesignSettings().m_NetSettings->SetNetclass( nc->GetName(), nc );
1298 }
1299 }
1300 }
1301
1302 if( reader.GetRemainingBytes() != 0 )
1303 THROW_IO_ERROR( wxT( "Classes6 stream is not fully parsed" ) );
1304
1305 // Now that all netclasses and pattern assignments are set up, resolve the pattern
1306 // assignments to direct netclass assignments on each net.
1307 std::shared_ptr<NET_SETTINGS> netSettings = m_board->GetDesignSettings().m_NetSettings;
1308
1309 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
1310 {
1311 if( net->GetNetCode() > 0 )
1312 {
1313 std::shared_ptr<NETCLASS> netclass = netSettings->GetEffectiveNetClass( net->GetNetname() );
1314
1315 if( netclass )
1316 net->SetNetClass( netclass );
1317 }
1318 }
1319
1320 m_board->m_LegacyNetclassesLoaded = true;
1321}
1322
1323
1325 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1326{
1327 if( m_progressReporter )
1328 m_progressReporter->Report( _( "Loading components..." ) );
1329
1330 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1331
1332 uint16_t componentId = 0;
1333
1334 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1335 {
1336 checkpoint();
1337 ACOMPONENT6 elem( reader );
1338
1339 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
1340
1341 // Altium stores the footprint library information needed to find the footprint in the
1342 // source library in the sourcefootprintlibrary field. Since Altium is a Windows-only
1343 // program, the path separator is always a backslash. We need strip the extra path information
1344 // here to prevent overly-long LIB_IDs because KiCad doesn't store the full path to the
1345 // footprint library in the design file, only in a library table.
1346 wxFileName libName( elem.sourcefootprintlibrary, wxPATH_WIN );
1347 LIB_ID fpID = AltiumToKiCadLibID( libName.GetName(), elem.pattern );
1348
1349 footprint->SetFPID( fpID );
1350
1351 footprint->SetPosition( elem.position );
1352 footprint->SetOrientationDegrees( elem.rotation );
1353
1354 // KiCad netlisting requires parts to have non-digit + digit annotation.
1355 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1356 wxString reference = elem.sourcedesignator;
1357
1358 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1359 reference.Prepend( wxT( "UNK" ) );
1360
1361 footprint->SetReference( reference );
1362
1363 KIID id( elem.sourceUniqueID );
1364 KIID pathid( elem.sourceHierachicalPath );
1366 path.push_back( pathid );
1367 path.push_back( id );
1368
1369 footprint->SetPath( path );
1370 footprint->SetSheetname( elem.sourceHierachicalPath );
1371 footprint->SetSheetfile( elem.sourceHierachicalPath + wxT( ".kicad_sch" ));
1372
1373 footprint->SetLocked( elem.locked );
1374 footprint->Reference().SetVisible( elem.nameon );
1375 footprint->Value().SetVisible( elem.commenton );
1376 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1377
1378 m_components.emplace_back( footprint.get() );
1379 m_board->Add( footprint.release(), ADD_MODE::APPEND );
1380
1381 componentId++;
1382 }
1383
1384 if( reader.GetRemainingBytes() != 0 )
1385 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1386}
1387
1388
1390double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1391{
1392 while( Angle < aMin )
1393 Angle += 360.0;
1394
1395 while( Angle >= aMax )
1396 Angle -= 360.0;
1397
1398 return Angle;
1399}
1400
1401
1403 FOOTPRINT* aFootprint,
1404 const ACOMPONENTBODY6& aElem )
1405{
1406 if( m_progressReporter )
1407 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1408
1409 if( !aElem.modelIsEmbedded )
1410 return;
1411
1412 auto model = aAltiumPcbFile.GetLibModel( aElem.modelId );
1413
1414 if( !model )
1415 {
1416 if( m_reporter )
1417 {
1418 m_reporter->Report( wxString::Format( wxT( "Model %s not found for footprint %s" ),
1419 aElem.modelId, aFootprint->GetReference() ),
1421 }
1422
1423 return;
1424 }
1425
1426 const VECTOR2I& fpPosition = aFootprint->GetPosition();
1427
1429 file->name = aElem.modelName;
1430
1431 if( file->name.IsEmpty() )
1432 file->name = model->first.name;
1433
1434 // Decompress the model data before assigning
1435 std::vector<char> decompressedData;
1436 wxMemoryInputStream compressedStream( model->second.data(), model->second.size() );
1437 wxZlibInputStream zlibStream( compressedStream );
1438
1439 // Reserve some space, assuming decompressed data is larger -- STEP file
1440 // compression is typically 5:1 using zlib like Altium does
1441 decompressedData.resize( model->second.size() * 6 );
1442 size_t offset = 0;
1443
1444 while( !zlibStream.Eof() )
1445 {
1446 zlibStream.Read( decompressedData.data() + offset, decompressedData.size() - offset );
1447 size_t bytesRead = zlibStream.LastRead();
1448
1449 if( !bytesRead )
1450 break;
1451
1452 offset += bytesRead;
1453
1454 if( offset >= decompressedData.size() )
1455 decompressedData.resize( 2 * decompressedData.size() ); // Resizing is expensive, avoid if we can
1456 }
1457
1458 decompressedData.resize( offset );
1459
1460 file->decompressedData = std::move( decompressedData );
1462
1464 aFootprint->GetEmbeddedFiles()->AddFile( file );
1465
1466 FP_3DMODEL modelSettings;
1467
1468 modelSettings.m_Filename = aFootprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1469
1470 modelSettings.m_Offset.x = pcbIUScale.IUTomm( (int) aElem.modelPosition.x );
1471 modelSettings.m_Offset.y = -pcbIUScale.IUTomm( (int) aElem.modelPosition.y );
1472 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) aElem.modelPosition.z );
1473
1474 EDA_ANGLE orientation = aFootprint->GetOrientation();
1475
1476 if( aFootprint->IsFlipped() )
1477 {
1478 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1479 orientation = -orientation;
1480 }
1481
1482 VECTOR3D modelRotation( aElem.modelRotation );
1483
1484 if( ( aElem.body_projection == 1 ) != aFootprint->IsFlipped() )
1485 {
1486 modelRotation.x += 180;
1487 modelRotation.z = -modelRotation.z;
1488
1489 modelSettings.m_Offset.z = -DEFAULT_BOARD_THICKNESS_MM - modelSettings.m_Offset.z;
1490 }
1491
1492 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1493
1494 modelSettings.m_Rotation.x = normalizeAngleDegrees( -modelRotation.x, -180, 180 );
1495 modelSettings.m_Rotation.y = normalizeAngleDegrees( -modelRotation.y, -180, 180 );
1496 modelSettings.m_Rotation.z = normalizeAngleDegrees( -modelRotation.z + aElem.rotation
1497 + orientation.AsDegrees(),
1498 -180, 180 );
1499 modelSettings.m_Opacity = aElem.body_opacity_3d;
1500
1501 aFootprint->Models().push_back( modelSettings );
1502}
1503
1504
1506 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1507{
1508 if( m_progressReporter )
1509 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1510
1511 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1512
1513 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1514 {
1515 checkpoint();
1516 ACOMPONENTBODY6 elem( reader );
1517
1518 static const bool skipComponentBodies = ADVANCED_CFG::GetCfg().m_ImportSkipComponentBodies;
1519
1520 if( skipComponentBodies )
1521 continue;
1522
1523 if( elem.component == ALTIUM_COMPONENT_NONE )
1524 continue; // TODO: we do not support components for the board yet
1525
1526 if( m_components.size() <= elem.component )
1527 {
1528 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1529 "component id %d of %zu existing components" ),
1530 elem.component,
1531 m_components.size() ) );
1532 }
1533
1534 if( !elem.modelIsEmbedded )
1535 continue;
1536
1537 auto modelTuple = m_EmbeddedModels.find( elem.modelId );
1538
1539 if( modelTuple == m_EmbeddedModels.end() )
1540 {
1541 if( m_reporter )
1542 {
1543 wxString msg;
1544 msg.Printf( wxT( "ComponentsBodies6 stream tries to access model id %s which does "
1545 "not exist" ), elem.modelId );
1546 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1547 }
1548
1549 continue;
1550 }
1551
1552 const ALTIUM_EMBEDDED_MODEL_DATA& modelData = modelTuple->second;
1553 FOOTPRINT* footprint = m_components.at( elem.component );
1554
1556 file->name = modelData.m_modelname;
1557
1558 wxMemoryInputStream compressedStream( modelData.m_data.data(), modelData.m_data.size() );
1559 wxZlibInputStream zlibStream( compressedStream );
1560 wxMemoryOutputStream decompressedStream;
1561
1562 zlibStream.Read( decompressedStream );
1563 file->decompressedData.resize( decompressedStream.GetSize() );
1564 decompressedStream.CopyTo( file->decompressedData.data(), file->decompressedData.size() );
1565
1567 footprint->GetEmbeddedFiles()->AddFile( file );
1568
1569 FP_3DMODEL modelSettings;
1570
1571 modelSettings.m_Filename = footprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1572 VECTOR2I fpPosition = footprint->GetPosition();
1573
1574 modelSettings.m_Offset.x =
1575 pcbIUScale.IUTomm( KiROUND( elem.modelPosition.x - fpPosition.x ) );
1576 modelSettings.m_Offset.y =
1577 -pcbIUScale.IUTomm( KiROUND( elem.modelPosition.y - fpPosition.y ) );
1578 modelSettings.m_Offset.z = pcbIUScale.IUTomm( KiROUND( elem.modelPosition.z ) );
1579
1580 EDA_ANGLE orientation = footprint->GetOrientation();
1581
1582 if( footprint->IsFlipped() )
1583 {
1584 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1585 orientation = -orientation;
1586 }
1587
1588 if( ( elem.body_projection == 1 ) != footprint->IsFlipped() )
1589 {
1590 elem.modelRotation.x += 180;
1591 elem.modelRotation.z = -elem.modelRotation.z;
1592
1593 modelSettings.m_Offset.z =
1594 -pcbIUScale.IUTomm( m_board->GetDesignSettings().GetBoardThickness() )
1595 - modelSettings.m_Offset.z;
1596 }
1597
1598 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1599
1600 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1601 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1602 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z + elem.rotation
1603 + orientation.AsDegrees(),
1604 -180, 180 );
1605
1606 modelSettings.m_Opacity = elem.body_opacity_3d;
1607
1608 footprint->Models().push_back( modelSettings );
1609 }
1610
1611 if( reader.GetRemainingBytes() != 0 )
1612 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1613}
1614
1615
1617{
1618 if( aElem.referencePoint.size() != 2 )
1619 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1620
1621 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1622
1623 if( klayer == UNDEFINED_LAYER )
1624 {
1625 if( m_reporter )
1626 {
1627 m_reporter->Report( wxString::Format(
1628 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1629 "It has been moved to KiCad layer Eco1_User." ), aElem.layer ),
1631 }
1632
1633 klayer = Eco1_User;
1634 }
1635
1636 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1637 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1638
1639 std::unique_ptr<PCB_DIM_ALIGNED> dimension = std::make_unique<PCB_DIM_ALIGNED>( m_board, PCB_DIM_ALIGNED_T );
1640
1641 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1642 dimension->SetLayer( klayer );
1643 dimension->SetStart( referencePoint0 );
1644
1645 if( referencePoint0 != aElem.xy1 )
1646 {
1656 VECTOR2I direction = aElem.xy1 - referencePoint0;
1657 VECTOR2I referenceDiff = referencePoint1 - referencePoint0;
1658 VECTOR2I directionNormalVector = direction.Perpendicular();
1659 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1660 SEG segm2( referencePoint1, referencePoint1 + direction );
1661 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1662
1663 if( !intersection )
1664 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1665
1666 dimension->SetEnd( *intersection );
1667
1668 int height = direction.EuclideanNorm();
1669
1670 if( direction.Cross( referenceDiff ) > 0 )
1671 height = -height;
1672
1673 dimension->SetHeight( height );
1674 }
1675 else
1676 {
1677 dimension->SetEnd( referencePoint1 );
1678 }
1679
1680 dimension->SetLineThickness( aElem.linewidth );
1681
1682 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
1683 dimension->SetPrefix( aElem.textprefix );
1684
1685
1686 int dist = ( dimension->GetEnd() - dimension->GetStart() ).EuclideanNorm();
1687
1688 if( dist < 3 * dimension->GetArrowLength() )
1689 dimension->SetArrowDirection( DIM_ARROW_DIRECTION::INWARD );
1690
1691 // Suffix normally (but not always) holds the units
1692 wxRegEx units( wxS( "(mm)|(in)|(mils)|(thou)|(')|(\")" ), wxRE_ADVANCED );
1693
1694 if( units.Matches( aElem.textsuffix ) )
1695 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX );
1696 else
1697 dimension->SetSuffix( aElem.textsuffix );
1698
1699 dimension->SetTextThickness( aElem.textlinewidth );
1700 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1701 dimension->SetItalic( aElem.textitalic );
1702
1703#if 0 // we don't currently support bold; map to thicker text
1704 dimension->Text().SetBold( aElem.textbold );
1705#else
1706 if( aElem.textbold )
1707 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1708#endif
1709
1710 switch( aElem.textunit )
1711 {
1712 case ALTIUM_UNIT::INCH: dimension->SetUnits( EDA_UNITS::INCH ); break;
1713 case ALTIUM_UNIT::MILS: dimension->SetUnits( EDA_UNITS::MILS ); break;
1714 case ALTIUM_UNIT::MM: dimension->SetUnits( EDA_UNITS::MM ); break;
1715 case ALTIUM_UNIT::CM: dimension->SetUnits( EDA_UNITS::MM ); break;
1716 default: break;
1717 }
1718
1719 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1720}
1721
1722
1724{
1725 if( aElem.referencePoint.size() < 2 )
1726 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1727
1728 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1729
1730 if( klayer == UNDEFINED_LAYER )
1731 {
1732 if( m_reporter )
1733 {
1734 m_reporter->Report( wxString::Format(
1735 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1736 "It has been moved to KiCad layer Eco1_User." ),
1737 aElem.layer ), RPT_SEVERITY_INFO );
1738 }
1739
1740 klayer = Eco1_User;
1741 }
1742
1743 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1744 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1745
1746 std::unique_ptr<PCB_DIM_RADIAL> dimension = std::make_unique<PCB_DIM_RADIAL>( m_board );
1747
1748 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1749 dimension->SetLayer( klayer );
1750 dimension->SetStart( referencePoint0 );
1751 dimension->SetEnd( aElem.xy1 );
1752 dimension->SetLineThickness( aElem.linewidth );
1753 dimension->SetKeepTextAligned( false );
1754
1755 dimension->SetPrefix( aElem.textprefix );
1756
1757 // Suffix normally holds the units
1758 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1760
1761 switch( aElem.textunit )
1762 {
1763 case ALTIUM_UNIT::INCH: dimension->SetUnits( EDA_UNITS::INCH ); break;
1764 case ALTIUM_UNIT::MILS: dimension->SetUnits( EDA_UNITS::MILS ); break;
1765 case ALTIUM_UNIT::MM: dimension->SetUnits( EDA_UNITS::MM ); break;
1766 case ALTIUM_UNIT::CM: dimension->SetUnits( EDA_UNITS::MM ); break;
1767 default: break;
1768 }
1769
1770 if( aElem.textPoint.empty() )
1771 {
1772 if( m_reporter )
1773 {
1774 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1776 }
1777
1778 return;
1779 }
1780
1781 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1782 dimension->SetTextThickness( aElem.textlinewidth );
1783 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1784 dimension->SetItalic( aElem.textitalic );
1785
1786#if 0 // we don't currently support bold; map to thicker text
1787 dimension->SetBold( aElem.textbold );
1788#else
1789 if( aElem.textbold )
1790 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1791#endif
1792
1793 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1794 // close.
1795 dimension->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1796 dimension->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1797
1798 int yAdjust = dimension->GetTextBox( nullptr ).GetCenter().y - dimension->GetTextPos().y;
1799 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1800 dimension->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
1801
1802 m_radialDimensions.push_back( dimension.get() );
1803 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1804}
1805
1806
1808{
1809 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1810
1811 if( klayer == UNDEFINED_LAYER )
1812 {
1813 if( m_reporter )
1814 {
1815 wxString msg;
1816 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1817 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1818 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1819 }
1820
1821 klayer = Eco1_User;
1822 }
1823
1824 if( !aElem.referencePoint.empty() )
1825 {
1826 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1827
1828 // line
1829 VECTOR2I last = referencePoint0;
1830 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1831 {
1832 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1833
1834 shape->SetLayer( klayer );
1835 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1836 shape->SetStart( last );
1837 shape->SetEnd( aElem.referencePoint.at( i ) );
1838 last = aElem.referencePoint.at( i );
1839
1840 m_board->Add( shape.release(), ADD_MODE::APPEND );
1841 }
1842
1843 // arrow
1844 if( aElem.referencePoint.size() >= 2 )
1845 {
1846 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1847
1848 if( dirVec.x != 0 || dirVec.y != 0 )
1849 {
1850 double scaling = (double) dirVec.EuclideanNorm() / aElem.arrowsize;
1851 VECTOR2I arrVec = KiROUND( dirVec.x / scaling, dirVec.y / scaling );
1852 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1853
1854 {
1855 std::unique_ptr<PCB_SHAPE> shape1 = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1856
1857 shape1->SetLayer( klayer );
1858 shape1->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1859 shape1->SetStart( referencePoint0 );
1860 shape1->SetEnd( referencePoint0 + arrVec );
1861
1862 m_board->Add( shape1.release(), ADD_MODE::APPEND );
1863 }
1864
1865 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1866
1867 {
1868 std::unique_ptr<PCB_SHAPE> shape2 = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1869
1870 shape2->SetLayer( klayer );
1871 shape2->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1872 shape2->SetStart( referencePoint0 );
1873 shape2->SetEnd( referencePoint0 + arrVec );
1874
1875 m_board->Add( shape2.release(), ADD_MODE::APPEND );
1876 }
1877 }
1878 }
1879 }
1880
1881 if( aElem.textPoint.empty() )
1882 {
1883 if( m_reporter )
1884 {
1885 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1887 }
1888
1889 return;
1890 }
1891
1892 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
1893
1894 text->SetText( aElem.textformat );
1895 text->SetPosition( aElem.textPoint.at( 0 ) );
1896 text->SetLayer( klayer );
1897 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1898 text->SetTextThickness( aElem.textlinewidth );
1899 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1900 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1901
1902 m_board->Add( text.release(), ADD_MODE::APPEND );
1903}
1904
1905
1907{
1908 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1909
1910 if( klayer == UNDEFINED_LAYER )
1911 {
1912 if( m_reporter )
1913 {
1914 wxString msg;
1915 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1916 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1917 m_reporter->Report( msg, RPT_SEVERITY_INFO );
1918 }
1919
1920 klayer = Eco1_User;
1921 }
1922
1923 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1924 {
1925 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1926
1927 shape->SetLayer( klayer );
1928 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1929 shape->SetStart( aElem.referencePoint.at( i ) );
1930 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1931
1932 m_board->Add( shape.release(), ADD_MODE::APPEND );
1933 }
1934}
1935
1936
1938{
1939 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1940
1941 if( klayer == UNDEFINED_LAYER )
1942 {
1943 if( m_reporter )
1944 {
1945 wxString msg;
1946 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1947 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1948 m_reporter->Report( msg, RPT_SEVERITY_INFO );
1949 }
1950
1951 klayer = Eco1_User;
1952 }
1953
1954 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1955 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1956
1957 std::unique_ptr<PCB_DIM_CENTER> dimension = std::make_unique<PCB_DIM_CENTER>( m_board );
1958
1959 dimension->SetLayer( klayer );
1960 dimension->SetLineThickness( aElem.linewidth );
1961 dimension->SetStart( aElem.xy1 );
1962 dimension->SetEnd( aElem.xy1 + vec );
1963
1964 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1965}
1966
1967
1969 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1970{
1971 if( m_progressReporter )
1972 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1973
1974 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1975
1976 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1977 {
1978 checkpoint();
1979 ADIMENSION6 elem( reader );
1980
1981 switch( elem.kind )
1982 {
1985 break;
1987 if( m_reporter )
1988 {
1989 m_reporter->Report( wxString::Format( _( "Ignored Angular dimension (not yet supported)." ) ),
1991 }
1992 break;
1995 break;
1998 break;
2000 if( m_reporter )
2001 {
2002 m_reporter->Report( wxString::Format( _( "Ignored Datum dimension (not yet supported)." ) ),
2004 }
2005 // HelperParseDimensions6Datum( elem );
2006 break;
2008 if( m_reporter )
2009 {
2010 m_reporter->Report( wxString::Format( _( "Ignored Baseline dimension (not yet supported)." ) ),
2012 }
2013 break;
2016 break;
2018 if( m_reporter )
2019 {
2020 m_reporter->Report( wxString::Format( _( "Ignored Linear dimension (not yet supported)." ) ),
2022 }
2023 break;
2025 if( m_reporter )
2026 {
2027 m_reporter->Report( wxString::Format( _( "Ignored Radial dimension (not yet supported)." ) ),
2029 }
2030 break;
2031 default:
2032 if( m_reporter )
2033 {
2034 wxString msg;
2035 msg.Printf( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
2036 m_reporter->Report( msg, RPT_SEVERITY_INFO );
2037 }
2038 break;
2039 }
2040 }
2041
2042 if( reader.GetRemainingBytes() != 0 )
2043 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
2044}
2045
2046
2048 const CFB::COMPOUND_FILE_ENTRY* aEntry,
2049 const std::vector<std::string>& aRootDir )
2050{
2051 if( m_progressReporter )
2052 m_progressReporter->Report( _( "Loading 3D models..." ) );
2053
2054 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2055
2056 if( reader.GetRemainingBytes() == 0 )
2057 return;
2058
2059 int idx = 0;
2060 wxString invalidChars = wxFileName::GetForbiddenChars();
2061
2062 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2063 {
2064 checkpoint();
2065 AMODEL elem( reader );
2066
2067 std::vector<std::string> stepPath = aRootDir;
2068 stepPath.emplace_back( std::to_string( idx ) );
2069
2070 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii()
2071 && wxString::npos == elem.name.find_first_of( invalidChars );
2072 wxString storageName = validName ? elem.name : wxString::Format( wxT( "model_%d" ), idx );
2073
2074 idx++;
2075
2076 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
2077
2078 if( stepEntry == nullptr )
2079 {
2080 if( m_reporter )
2081 {
2082 wxString msg;
2083 msg.Printf( _( "File not found: '%s'. 3D-model not imported." ), FormatPath( stepPath ) );
2084 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2085 }
2086
2087 continue;
2088 }
2089
2090 size_t stepSize = static_cast<size_t>( stepEntry->size );
2091 std::vector<char> stepContent( stepSize );
2092
2093 // read file into buffer
2094 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
2095 stepSize );
2096
2097 m_EmbeddedModels.insert( std::make_pair(
2098 elem.id, ALTIUM_EMBEDDED_MODEL_DATA( storageName, elem.rotation, elem.z_offset,
2099 std::move( stepContent ) ) ) );
2100 }
2101
2102 // Append _<index> to duplicate filenames
2103 std::map<wxString, std::vector<wxString>> nameIdMap;
2104
2105 for( auto& [id, data] : m_EmbeddedModels )
2106 nameIdMap[data.m_modelname].push_back( id );
2107
2108 for( auto& [name, ids] : nameIdMap )
2109 {
2110 for( size_t i = 1; i < ids.size(); i++ )
2111 {
2112 const wxString& id = ids[i];
2113
2114 auto modelTuple = m_EmbeddedModels.find( id );
2115
2116 if( modelTuple == m_EmbeddedModels.end() )
2117 continue;
2118
2119 wxString modelName = modelTuple->second.m_modelname;
2120
2121 if( modelName.Contains( "." ) )
2122 {
2123 wxString ext;
2124 wxString baseName = modelName.BeforeLast( '.', &ext );
2125
2126 modelTuple->second.m_modelname = baseName + '_' + std::to_string( i ) + '.' + ext;
2127 }
2128 else
2129 {
2130 modelTuple->second.m_modelname = modelName + '_' + std::to_string( i );
2131 }
2132 }
2133 }
2134
2135 if( reader.GetRemainingBytes() != 0 )
2136 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
2137}
2138
2139
2141 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2142{
2143 if( m_progressReporter )
2144 m_progressReporter->Report( _( "Loading nets..." ) );
2145
2146 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2147
2148 wxASSERT( m_altiumToKicadNetcodes.empty() );
2149
2150 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2151 {
2152 checkpoint();
2153 ANET6 elem( reader );
2154
2155 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, -1 );
2156 m_board->Add( netInfo, ADD_MODE::APPEND );
2157
2158 // needs to be called after m_board->Add() as assign us the NetCode
2159 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
2160 }
2161
2162 if( reader.GetRemainingBytes() != 0 )
2163 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
2164}
2165
2167 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2168{
2169 if( m_progressReporter )
2170 m_progressReporter->Report( _( "Loading polygons..." ) );
2171
2172 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2173
2174 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2175 {
2176 checkpoint();
2177 APOLYGON6 elem( reader );
2178
2179 SHAPE_LINE_CHAIN linechain;
2181
2182 if( linechain.PointCount() < 3 )
2183 {
2184 // We have found multiple Altium files with polygon records containing nothing but two
2185 // coincident vertices. These polygons do not appear when opening the file in Altium.
2186 // https://gitlab.com/kicad/code/kicad/-/issues/8183
2187 // Also, polygons with less than 3 points are not supported in KiCad.
2188 //
2189 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
2190 // "points are required." ),
2191 // linechain.PointCount(),
2192 // elem.vertices.size() );
2193
2194 m_polygons.emplace_back( nullptr );
2195 continue;
2196 }
2197
2198 SHAPE_POLY_SET outline( linechain );
2199
2201 {
2202 // Altium "Hatched" or "None" polygon outlines have thickness, convert it to KiCad's representation.
2204 ARC_HIGH_DEF, true );
2205 }
2206
2207 if( outline.OutlineCount() != 1 && m_reporter )
2208 {
2209 wxString msg;
2210 msg.Printf( _( "Polygon outline count is %d, expected 1." ), outline.OutlineCount() );
2211
2212 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2213 }
2214
2215 if( outline.OutlineCount() == 0 )
2216 continue;
2217
2218 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>(m_board);
2219
2220 // Be sure to set the zone layer before setting the net code
2221 // so that we know that this is a copper zone and so needs a valid net code.
2222 HelperSetZoneLayers( *zone, elem.layer );
2223 zone->SetNetCode( GetNetCode( elem.net ) );
2224 zone->SetPosition( elem.vertices.at( 0 ).position );
2225 zone->SetLocked( elem.locked );
2226 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
2227 zone->Outline()->AddOutline( outline.Outline( 0 ) );
2228
2229 if( elem.pourindex > m_highest_pour_index )
2231
2232 const ARULE6* planeClearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
2233 const ARULE6* zoneClearanceRule = GetRule( ALTIUM_RULE_KIND::CLEARANCE, wxT( "PolygonClearance" ) );
2234 int planeLayers = 0;
2235 int signalLayers = 0;
2236 int clearance = 0;
2237
2238 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
2239 {
2240 LAYER_T layerType = m_board->GetLayerType( layer );
2241
2242 if( layerType == LT_POWER || layerType == LT_MIXED )
2243 planeLayers++;
2244
2245 if( layerType == LT_SIGNAL || layerType == LT_MIXED )
2246 signalLayers++;
2247 }
2248
2249 if( planeLayers > 0 && planeClearanceRule )
2250 clearance = std::max( clearance, planeClearanceRule->planeclearanceClearance );
2251
2252 if( signalLayers > 0 && zoneClearanceRule )
2253 clearance = std::max( clearance, zoneClearanceRule->clearanceGap );
2254
2255 if( clearance > 0 )
2256 zone->SetLocalClearance( clearance );
2257
2258 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
2259
2260 if( polygonConnectRule != nullptr )
2261 {
2262 switch( polygonConnectRule->polygonconnectStyle )
2263 {
2265 zone->SetPadConnection( ZONE_CONNECTION::FULL );
2266 break;
2267
2269 zone->SetPadConnection( ZONE_CONNECTION::NONE );
2270 break;
2271
2272 default:
2274 zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
2275 break;
2276 }
2277
2278 // TODO: correct variables?
2279 zone->SetThermalReliefSpokeWidth(
2280 polygonConnectRule->polygonconnectReliefconductorwidth );
2281 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
2282
2283 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
2284 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
2285 }
2286
2287 if( IsAltiumLayerAPlane( elem.layer ) )
2288 {
2289 // outer zone will be set to priority 0 later.
2290 zone->SetAssignedPriority( 1 );
2291
2292 // check if this is the outer zone by simply comparing the BBOX
2293 const auto& outer_plane = m_outer_plane.find( elem.layer );
2294 if( outer_plane == m_outer_plane.end()
2295 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
2296 {
2297 m_outer_plane[elem.layer] = zone.get();
2298 }
2299 }
2300
2303 {
2304 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
2305 zone->SetHatchThickness( elem.trackwidth );
2306
2308 {
2309 // use a small hack to get us only an outline (hopefully)
2310 const BOX2I& bbox = zone->GetBoundingBox();
2311 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
2312 }
2313 else
2314 {
2315 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
2316 }
2317
2319 zone->SetHatchOrientation( ANGLE_45 );
2320 }
2321
2322 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2324
2325 m_polygons.emplace_back( zone.get() );
2326 m_board->Add( zone.release(), ADD_MODE::APPEND );
2327 }
2328
2329 if( reader.GetRemainingBytes() != 0 )
2330 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
2331}
2332
2334 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2335{
2336 if( m_progressReporter )
2337 m_progressReporter->Report( _( "Loading rules..." ) );
2338
2339 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2340
2341 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2342 {
2343 checkpoint();
2344 ARULE6 elem( reader );
2345
2346 m_rules[elem.kind].emplace_back( elem );
2347 }
2348
2349 // sort rules by priority
2350 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
2351 {
2352 std::sort( val.second.begin(), val.second.end(),
2353 []( const ARULE6& lhs, const ARULE6& rhs )
2354 {
2355 return lhs.priority < rhs.priority;
2356 } );
2357 }
2358
2359 const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::CLEARANCE );
2360 const ARULE6* trackWidthRule = GetRuleDefault( ALTIUM_RULE_KIND::WIDTH );
2361 const ARULE6* routingViasRule = GetRuleDefault( ALTIUM_RULE_KIND::ROUTING_VIAS );
2362 const ARULE6* holeSizeRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_SIZE );
2364
2365 if( clearanceRule )
2366 m_board->GetDesignSettings().m_MinClearance = clearanceRule->clearanceGap;
2367
2368 if( trackWidthRule )
2369 {
2370 m_board->GetDesignSettings().m_TrackMinWidth = trackWidthRule->minLimit;
2371 // TODO: construct a custom rule for preferredWidth and maxLimit values
2372 }
2373
2374 if( routingViasRule )
2375 {
2376 m_board->GetDesignSettings().m_ViasMinSize = routingViasRule->minWidth;
2377 m_board->GetDesignSettings().m_MinThroughDrill = routingViasRule->minHoleWidth;
2378 }
2379
2380 if( holeSizeRule )
2381 {
2382 // TODO: construct a custom rule for minLimit / maxLimit values
2383 }
2384
2385 if( holeToHoleRule )
2386 m_board->GetDesignSettings().m_HoleToHoleMin = holeToHoleRule->clearanceGap;
2387
2390
2391 if( soldermaskRule )
2392 m_board->GetDesignSettings().m_SolderMaskExpansion = soldermaskRule->soldermaskExpansion;
2393
2394 if( pastemaskRule )
2395 m_board->GetDesignSettings().m_SolderPasteMargin = pastemaskRule->pastemaskExpansion;
2396
2397 if( reader.GetRemainingBytes() != 0 )
2398 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
2399}
2400
2402 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2403{
2404 if( m_progressReporter )
2405 m_progressReporter->Report( _( "Loading board regions..." ) );
2406
2407 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2408
2409 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2410 {
2411 checkpoint();
2412 AREGION6 elem( reader, false );
2413
2414 // TODO: implement?
2415 }
2416
2417 if( reader.GetRemainingBytes() != 0 )
2418 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
2419}
2420
2422 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2423{
2424 if( m_progressReporter )
2425 m_progressReporter->Report( _( "Loading polygons..." ) );
2426
2427 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2428
2429 /* TODO: use Header section of file */
2430 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2431 {
2432 checkpoint();
2433 AREGION6 elem( reader, true );
2434
2437 {
2438 // TODO: implement all different types for footprints
2440 }
2441 else
2442 {
2443 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2444 ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
2445 }
2446 }
2447
2448 if( reader.GetRemainingBytes() != 0 )
2449 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
2450}
2451
2452
2454{
2456 {
2458 }
2459 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2460 {
2461 SHAPE_LINE_CHAIN linechain;
2463
2464 if( linechain.PointCount() < 3 )
2465 {
2466 // We have found multiple Altium files with polygon records containing nothing but
2467 // two coincident vertices. These polygons do not appear when opening the file in
2468 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2469 // Also, polygons with less than 3 points are not supported in KiCad.
2470 return;
2471 }
2472
2473 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2474
2475 zone->SetIsRuleArea( true );
2476
2477 if( aElem.is_keepout )
2478 {
2480 }
2481 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2482 {
2483 zone->SetDoNotAllowZoneFills( true );
2484 zone->SetDoNotAllowVias( false );
2485 zone->SetDoNotAllowTracks( false );
2486 zone->SetDoNotAllowPads( false );
2487 zone->SetDoNotAllowFootprints( false );
2488 }
2489
2490 zone->SetPosition( aElem.outline.at( 0 ).position );
2491 zone->Outline()->AddOutline( linechain );
2492
2493 HelperSetZoneLayers( *zone, aElem.layer );
2494
2495 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2497
2498 m_board->Add( zone.release(), ADD_MODE::APPEND );
2499 }
2500 else if( aElem.is_teardrop )
2501 {
2502 SHAPE_LINE_CHAIN linechain;
2504
2505 if( linechain.PointCount() < 3 )
2506 {
2507 // Polygons with less than 3 points are not supported in KiCad.
2508 return;
2509 }
2510
2511 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2512
2513 zone->SetPosition( aElem.outline.at( 0 ).position );
2514 zone->Outline()->AddOutline( linechain );
2515
2516 HelperSetZoneLayers( *zone, aElem.layer );
2517 zone->SetNetCode( GetNetCode( aElem.net ) );
2518 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_UNSPECIFIED );
2519 zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER );
2520
2521 SHAPE_POLY_SET fill;
2522 fill.Append( linechain );
2523 fill.Fracture();
2524
2525 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2526 zone->SetFilledPolysList( klayer, fill );
2527
2528 zone->SetIsFilled( true );
2529 zone->SetNeedRefill( false );
2530
2531 m_board->Add( zone.release(), ADD_MODE::APPEND );
2532 }
2533 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2534 {
2535 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2536
2537 if( klayer == UNDEFINED_LAYER )
2538 {
2539 if( m_reporter )
2540 {
2541 wxString msg;
2542 msg.Printf( _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
2543 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
2544 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2545 }
2546
2547 klayer = Eco1_User;
2548 }
2549
2550 SHAPE_LINE_CHAIN linechain;
2552
2553 if( linechain.PointCount() < 3 )
2554 {
2555 // We have found multiple Altium files with polygon records containing nothing but
2556 // two coincident vertices. These polygons do not appear when opening the file in
2557 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2558 // Also, polygons with less than 3 points are not supported in KiCad.
2559 return;
2560 }
2561
2562 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2563
2564 shape->SetPolyShape( linechain );
2565 shape->SetFilled( false );
2566 shape->SetLayer( klayer );
2567 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2568
2569 m_board->Add( shape.release(), ADD_MODE::APPEND );
2570 }
2571 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2572 {
2573 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2574 {
2575 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2577 }
2578 }
2579 else if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2580 {
2582 }
2583 else
2584 {
2585 if( m_reporter )
2586 {
2587 wxString msg;
2588 msg.Printf( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2589 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2590 }
2591 }
2592}
2593
2594
2596 const AREGION6& aElem,
2597 const int aPrimitiveIndex )
2598{
2599 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2600 {
2601 SHAPE_LINE_CHAIN linechain;
2603
2604 if( linechain.PointCount() < 3 )
2605 {
2606 // We have found multiple Altium files with polygon records containing nothing but
2607 // two coincident vertices. These polygons do not appear when opening the file in
2608 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2609 // Also, polygons with less than 3 points are not supported in KiCad.
2610 return;
2611 }
2612
2613 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
2614
2615 zone->SetIsRuleArea( true );
2616
2617 if( aElem.is_keepout )
2618 {
2620 }
2621 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2622 {
2623 zone->SetDoNotAllowZoneFills( true );
2624 zone->SetDoNotAllowVias( false );
2625 zone->SetDoNotAllowTracks( false );
2626 zone->SetDoNotAllowPads( false );
2627 zone->SetDoNotAllowFootprints( false );
2628 }
2629
2630 zone->SetPosition( aElem.outline.at( 0 ).position );
2631 zone->Outline()->AddOutline( linechain );
2632
2633 HelperSetZoneLayers( *zone, aElem.layer );
2634
2635 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2637
2638 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
2639 }
2640 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2641 {
2642 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2643 {
2644 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2645 {
2646 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
2647 aPrimitiveIndex );
2648 }
2649 }
2650 }
2653 {
2655 ? Edge_Cuts
2656 : GetKicadLayer( aElem.layer );
2657
2658 if( klayer == UNDEFINED_LAYER )
2659 {
2660 if( !m_footprintName.IsEmpty() )
2661 {
2662 if( m_reporter )
2663 {
2664 wxString msg;
2665 msg.Printf( _( "Loading library '%s':\n"
2666 "Footprint %s contains a dashed outline on Altium layer (%d) with "
2667 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2668 m_library,
2670 aElem.layer );
2671 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2672 }
2673 }
2674 else
2675 {
2676 if( m_reporter )
2677 {
2678 wxString msg;
2679 msg.Printf( _( "Footprint %s contains a dashed outline on Altium layer (%d) with "
2680 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2681 aFootprint->GetReference(),
2682 aElem.layer );
2683 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2684 }
2685 }
2686
2687 klayer = Eco1_User;
2688 }
2689
2690 SHAPE_LINE_CHAIN linechain;
2692
2693 if( linechain.PointCount() < 3 )
2694 {
2695 // We have found multiple Altium files with polygon records containing nothing but
2696 // two coincident vertices. These polygons do not appear when opening the file in
2697 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2698 // Also, polygons with less than 3 points are not supported in KiCad.
2699 return;
2700 }
2701
2702 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2703
2704 shape->SetPolyShape( linechain );
2705 shape->SetFilled( false );
2706 shape->SetLayer( klayer );
2707
2709 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2710 else
2711 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::SOLID ) );
2712
2713 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2714 }
2715 else
2716 {
2717 if( !m_footprintName.IsEmpty() )
2718 {
2719 if( m_reporter )
2720 {
2721 wxString msg;
2722 msg.Printf( _( "Error loading library '%s':\n"
2723 "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2724 m_library,
2726 aElem.kind );
2727 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2728 }
2729 }
2730 else
2731 {
2732 if( m_reporter )
2733 {
2734 wxString msg;
2735 msg.Printf( _( "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2736 aFootprint->GetReference(),
2737 aElem.kind );
2738 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2739 }
2740 }
2741 }
2742}
2743
2744
2746 PCB_LAYER_ID aLayer )
2747{
2748 SHAPE_LINE_CHAIN linechain;
2750
2751 if( linechain.PointCount() < 3 )
2752 {
2753 // We have found multiple Altium files with polygon records containing nothing
2754 // but two coincident vertices. These polygons do not appear when opening the
2755 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2756 // Also, polygons with less than 3 points are not supported in KiCad.
2757 return;
2758 }
2759
2760 SHAPE_POLY_SET polySet;
2761 polySet.AddOutline( linechain );
2762
2763 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2764 {
2765 SHAPE_LINE_CHAIN hole_linechain;
2766 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2767
2768 if( hole_linechain.PointCount() < 3 )
2769 continue;
2770
2771 polySet.AddHole( hole_linechain );
2772 }
2773
2774 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2775
2776 shape->SetPolyShape( polySet );
2777 shape->SetFilled( true );
2778 shape->SetLayer( aLayer );
2779 shape->SetStroke( STROKE_PARAMS( 0 ) );
2780
2781 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2782 {
2783 shape->SetNetCode( GetNetCode( aElem.net ) );
2784 }
2785
2786 m_board->Add( shape.release(), ADD_MODE::APPEND );
2787}
2788
2789
2791 const AREGION6& aElem,
2792 PCB_LAYER_ID aLayer,
2793 const int aPrimitiveIndex )
2794{
2795 SHAPE_LINE_CHAIN linechain;
2797
2798 if( linechain.PointCount() < 3 )
2799 {
2800 // We have found multiple Altium files with polygon records containing nothing
2801 // but two coincident vertices. These polygons do not appear when opening the
2802 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2803 // Also, polygons with less than 3 points are not supported in KiCad.
2804 return;
2805 }
2806
2807 SHAPE_POLY_SET polySet;
2808 polySet.AddOutline( linechain );
2809
2810 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2811 {
2812 SHAPE_LINE_CHAIN hole_linechain;
2813 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2814
2815 if( hole_linechain.PointCount() < 3 )
2816 continue;
2817
2818 polySet.AddHole( hole_linechain );
2819 }
2820
2821 if( aLayer == F_Cu || aLayer == B_Cu )
2822 {
2823 // TODO(JE) padstacks -- not sure what should happen here yet
2824 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
2825
2826 LSET padLayers;
2827 padLayers.set( aLayer );
2828
2829 pad->SetAttribute( PAD_ATTRIB::SMD );
2831 pad->SetThermalSpokeAngle( ANGLE_90 );
2832
2833 int anchorSize = 1;
2834 VECTOR2I anchorPos = linechain.CPoint( 0 );
2835
2836 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
2837 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
2838 pad->SetPosition( anchorPos );
2839
2840 SHAPE_POLY_SET shapePolys = polySet;
2841 shapePolys.Move( -anchorPos );
2842 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, shapePolys, 0, true );
2843
2845 auto it = map.find( aPrimitiveIndex );
2846
2847 if( it != map.end() )
2848 {
2849 const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
2850
2851 if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2852 {
2853 pad->SetLocalSolderPasteMargin( info.pastemaskexpansionmanual );
2854 }
2855
2856 if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2857 {
2858 pad->SetLocalSolderMaskMargin( info.soldermaskexpansionmanual );
2859 }
2860
2861 if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
2862 padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
2863
2864 if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
2865 padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
2866 }
2867
2868 pad->SetLayerSet( padLayers );
2869
2870 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
2871 }
2872 else
2873 {
2874 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2875
2876 shape->SetPolyShape( polySet );
2877 shape->SetFilled( true );
2878 shape->SetLayer( aLayer );
2879 shape->SetStroke( STROKE_PARAMS( 0 ) );
2880
2881 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2882 }
2883}
2884
2885
2887 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2888{
2889 if( m_progressReporter )
2890 m_progressReporter->Report( _( "Loading zone fills..." ) );
2891
2892 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2893
2894 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2895 {
2896 checkpoint();
2897 AREGION6 elem( reader, false );
2898
2899 if( elem.polygon != ALTIUM_POLYGON_NONE )
2900 {
2901 if( m_polygons.size() <= elem.polygon )
2902 {
2903 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2904 "of %d existing polygons.",
2905 elem.polygon,
2906 m_polygons.size() ) );
2907 }
2908
2909 ZONE* zone = m_polygons.at( elem.polygon );
2910
2911 if( zone == nullptr )
2912 {
2913 continue; // we know the zone id, but because we do not know the layer we did not
2914 // add it!
2915 }
2916
2917 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2918
2919 if( klayer == UNDEFINED_LAYER )
2920 continue; // Just skip it for now. Users can fill it themselves.
2921
2922 SHAPE_LINE_CHAIN linechain;
2923
2924 for( const ALTIUM_VERTICE& vertice : elem.outline )
2925 linechain.Append( vertice.position );
2926
2927 linechain.Append( elem.outline.at( 0 ).position );
2928 linechain.SetClosed( true );
2929
2930 SHAPE_POLY_SET fill;
2931 fill.AddOutline( linechain );
2932
2933 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2934 {
2935 SHAPE_LINE_CHAIN hole_linechain;
2936
2937 for( const ALTIUM_VERTICE& vertice : hole )
2938 hole_linechain.Append( vertice.position );
2939
2940 hole_linechain.Append( hole.at( 0 ).position );
2941 hole_linechain.SetClosed( true );
2942 fill.AddHole( hole_linechain );
2943 }
2944
2945 if( zone->HasFilledPolysForLayer( klayer ) )
2946 fill.BooleanAdd( *zone->GetFill( klayer ) );
2947
2948 fill.Fracture();
2949
2950 zone->SetFilledPolysList( klayer, fill );
2951 zone->SetIsFilled( true );
2952 zone->SetNeedRefill( false );
2953 }
2954 }
2955
2956 if( reader.GetRemainingBytes() != 0 )
2957 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2958}
2959
2960
2962 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2963{
2964 if( m_progressReporter )
2965 m_progressReporter->Report( _( "Loading arcs..." ) );
2966
2967 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2968
2969 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2970 {
2971 checkpoint();
2972 AARC6 elem( reader );
2973
2974 if( elem.component == ALTIUM_COMPONENT_NONE )
2975 {
2976 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2977 }
2978 else
2979 {
2980 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2981 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2982 }
2983 }
2984
2985 if( reader.GetRemainingBytes() != 0 )
2986 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2987}
2988
2989
2991{
2992 if( aElem.startangle == 0. && aElem.endangle == 360. )
2993 {
2994 aShape->SetShape( SHAPE_T::CIRCLE );
2995
2996 // TODO: other variants to define circle?
2997 aShape->SetStart( aElem.center );
2998 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
2999 }
3000 else
3001 {
3002 aShape->SetShape( SHAPE_T::ARC );
3003
3004 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
3005 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
3006
3007 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
3008 -KiROUND( startAngle.Sin() * aElem.radius ) );
3009
3010 aShape->SetCenter( aElem.center );
3011 aShape->SetStart( aElem.center + startOffset );
3012 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
3013 }
3014}
3015
3016
3017void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
3018{
3019 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
3020 {
3021 if( m_polygons.size() <= aElem.polygon )
3022 {
3023 THROW_IO_ERROR( wxString::Format( "Tracks stream tries to access polygon id %u "
3024 "of %zu existing polygons.",
3025 aElem.polygon, m_polygons.size() ) );
3026 }
3027
3028 ZONE* zone = m_polygons.at( aElem.polygon );
3029
3030 if( zone == nullptr )
3031 {
3032 return; // we know the zone id, but because we do not know the layer we did not
3033 // add it!
3034 }
3035
3036 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3037
3038 if( klayer == UNDEFINED_LAYER )
3039 return; // Just skip it for now. Users can fill it themselves.
3040
3041 if( !zone->HasFilledPolysForLayer( klayer ) )
3042 return;
3043
3044 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
3045
3046 // This is not the actual board item. We can use it to create the polygon for the region
3047 PCB_SHAPE shape( nullptr );
3048
3049 ConvertArcs6ToPcbShape( aElem, &shape );
3051
3052 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
3053 // Will be simplified and fractured later
3054
3055 zone->SetIsFilled( true );
3056 zone->SetNeedRefill( false );
3057
3058 return;
3059 }
3060
3061 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3062 || IsAltiumLayerAPlane( aElem.layer ) )
3063 {
3064 // This is not the actual board item. We can use it to create the polygon for the region
3065 PCB_SHAPE shape( nullptr );
3066
3067 ConvertArcs6ToPcbShape( aElem, &shape );
3069
3071 }
3072 else
3073 {
3074 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3075 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
3076 }
3077
3078 for( const auto& layerExpansionMask :
3080 {
3081 int width = aElem.width + ( layerExpansionMask.second * 2 );
3082
3083 if( width > 1 )
3084 {
3085 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
3086
3087 ConvertArcs6ToPcbShape( aElem, arc.get() );
3088 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3089 arc->SetLayer( layerExpansionMask.first );
3090
3091 m_board->Add( arc.release(), ADD_MODE::APPEND );
3092 }
3093 }
3094}
3095
3096
3098 const int aPrimitiveIndex, const bool aIsBoardImport )
3099{
3100 if( aElem.polygon != ALTIUM_POLYGON_NONE )
3101 {
3102 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Arc with polygon id %d",
3103 aElem.polygon ) );
3104 return;
3105 }
3106
3107 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3108 || IsAltiumLayerAPlane( aElem.layer ) )
3109 {
3110 // This is not the actual board item. We can use it to create the polygon for the region
3111 PCB_SHAPE shape( nullptr );
3112
3113 ConvertArcs6ToPcbShape( aElem, &shape );
3115
3116 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3117 aElem.keepoutrestrictions );
3118 }
3119 else
3120 {
3121 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3122 {
3123 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3124 {
3125 // Special case: do to not lose net connections in footprints
3126 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
3127 }
3128 else
3129 {
3130 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3131 }
3132 }
3133 }
3134
3135 for( const auto& layerExpansionMask :
3137 {
3138 int width = aElem.width + ( layerExpansionMask.second * 2 );
3139
3140 if( width > 1 )
3141 {
3142 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3143
3144 ConvertArcs6ToPcbShape( aElem, arc.get() );
3145 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3146 arc->SetLayer( layerExpansionMask.first );
3147
3148 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3149 }
3150 }
3151}
3152
3153
3155{
3156 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3157 {
3158 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
3159 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
3160
3161 includedAngle.Normalize();
3162
3163 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
3164 -KiROUND( startAngle.Sin() * aElem.radius ) );
3165
3166 if( includedAngle.AsDegrees() >= 0.1 )
3167 {
3168 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
3169 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
3170
3171 shape.SetCenter( aElem.center );
3172 shape.SetStart( aElem.center + startOffset );
3173 shape.SetArcAngleAndEnd( includedAngle, true );
3174
3175 // Create actual arc
3176 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(),
3177 aElem.width );
3178 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board, &shapeArc );
3179
3180 arc->SetWidth( aElem.width );
3181 arc->SetLayer( aLayer );
3182 arc->SetNetCode( GetNetCode( aElem.net ) );
3183
3184 m_board->Add( arc.release(), ADD_MODE::APPEND );
3185 }
3186 }
3187 else
3188 {
3189 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>(m_board);
3190
3191 ConvertArcs6ToPcbShape( aElem, arc.get() );
3192 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3193 arc->SetLayer( aLayer );
3194
3195 m_board->Add( arc.release(), ADD_MODE::APPEND );
3196 }
3197}
3198
3199
3201 PCB_LAYER_ID aLayer )
3202{
3203 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3204
3205 ConvertArcs6ToPcbShape( aElem, arc.get() );
3206 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3207 arc->SetLayer( aLayer );
3208
3209 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3210}
3211
3212
3214 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3215{
3216 if( m_progressReporter )
3217 m_progressReporter->Report( _( "Loading pads..." ) );
3218
3219 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3220
3221 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3222 {
3223 checkpoint();
3224 APAD6 elem( reader );
3225
3226 if( elem.component == ALTIUM_COMPONENT_NONE )
3227 {
3229 }
3230 else
3231 {
3232 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3233 ConvertPads6ToFootprintItem( footprint, elem );
3234 }
3235 }
3236
3237 if( reader.GetRemainingBytes() != 0 )
3238 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
3239}
3240
3241
3243{
3244 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3245 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3246 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3247 {
3249 }
3250 else
3251 {
3252 // We cannot add a pad directly into the PCB
3253 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
3254 footprint->SetPosition( aElem.position );
3255
3256 ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem );
3257
3258 m_board->Add( footprint.release(), ADD_MODE::APPEND );
3259 }
3260}
3261
3262
3264{
3265 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3266
3267 pad->SetNumber( "" );
3268 pad->SetNetCode( GetNetCode( aElem.net ) );
3269
3270 pad->SetPosition( aElem.position );
3271 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( aElem.diameter, aElem.diameter ) );
3272 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3273 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3275 pad->SetAttribute( PAD_ATTRIB::PTH );
3276
3277 // Pads are always through holes in KiCad
3278 pad->SetLayerSet( LSET().AllCuMask() );
3279
3280 if( aElem.viamode == ALTIUM_PAD_MODE::SIMPLE )
3281 {
3282 pad->Padstack().SetMode( PADSTACK::MODE::NORMAL );
3283 }
3285 {
3286 pad->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
3287 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[1], aElem.diameter_by_layer[1] ),
3289 }
3290 else
3291 {
3292 pad->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
3293 int altiumIdx = 0;
3294
3295 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, 32 ) )
3296 {
3297 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[altiumIdx],
3298 aElem.diameter_by_layer[altiumIdx] ), layer );
3299 altiumIdx++;
3300 }
3301 }
3302
3303 if( aElem.is_tent_top )
3304 {
3305 pad->Padstack().FrontOuterLayers().has_solder_mask = true;
3306 }
3307 else
3308 {
3309 pad->Padstack().FrontOuterLayers().has_solder_mask = false;
3310 pad->SetLayerSet( pad->GetLayerSet().set( F_Mask ) );
3311 }
3312
3313 if( aElem.is_tent_bottom )
3314 {
3315 pad->Padstack().BackOuterLayers().has_solder_mask = true;
3316 }
3317 else
3318 {
3319 pad->Padstack().BackOuterLayers().has_solder_mask = false;
3320 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ) );
3321 }
3322
3323 if( aElem.is_locked )
3324 pad->SetLocked( true );
3325
3326 if( aElem.soldermask_expansion_manual )
3327 {
3328 pad->Padstack().FrontOuterLayers().solder_mask_margin = aElem.soldermask_expansion_front;
3329 pad->Padstack().BackOuterLayers().solder_mask_margin = aElem.soldermask_expansion_back;
3330 }
3331
3332
3333 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3334}
3335
3336
3338{
3339 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3340 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3341 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3342 {
3343 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
3344 }
3345 else
3346 {
3347 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
3348 }
3349}
3350
3351
3353{
3354 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3355
3356 pad->SetNumber( aElem.name );
3357 pad->SetNetCode( GetNetCode( aElem.net ) );
3358
3359 pad->SetPosition( aElem.position );
3360 pad->SetOrientationDegrees( aElem.direction );
3361 pad->SetThermalSpokeAngle( ANGLE_90 );
3362
3363 if( aElem.holesize == 0 )
3364 {
3365 pad->SetAttribute( PAD_ATTRIB::SMD );
3366 }
3367 else
3368 {
3369 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3370 {
3371 // TODO: I assume other values are possible as well?
3372 if( !m_footprintName.IsEmpty() )
3373 {
3374 if( m_reporter )
3375 {
3376 wxString msg;
3377 msg.Printf( _( "Error loading library '%s':\n"
3378 "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3379 m_library,
3381 aElem.name );
3382 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
3383 }
3384 }
3385 else
3386 {
3387 if( m_reporter )
3388 {
3389 wxString msg;
3390 msg.Printf( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3391 aFootprint->GetReference(),
3392 aElem.name );
3393 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
3394 }
3395 }
3396 }
3397
3398 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
3399
3401 {
3402 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3403 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3404 }
3405 else
3406 {
3407 switch( aElem.sizeAndShape->holeshape )
3408 {
3410 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
3411 break;
3412
3414 if( !m_footprintName.IsEmpty() )
3415 {
3416 if( m_reporter )
3417 {
3418 wxString msg;
3419 msg.Printf( _( "Loading library '%s':\n"
3420 "Footprint %s pad %s has a square hole (not yet supported)." ),
3421 m_library,
3423 aElem.name );
3424 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3425 }
3426 }
3427 else
3428 {
3429 if( m_reporter )
3430 {
3431 wxString msg;
3432 msg.Printf( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
3433 aFootprint->GetReference(),
3434 aElem.name );
3435 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3436 }
3437 }
3438
3439 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3440 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3441 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
3442 // this case or rect holes have a different id
3443 break;
3444
3446 {
3447 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
3448 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
3449
3450 slotRotation.Normalize();
3451
3452 if( slotRotation.IsHorizontal() )
3453 {
3454 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
3455 }
3456 else if( slotRotation.IsVertical() )
3457 {
3458 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
3459 }
3460 else
3461 {
3462 if( !m_footprintName.IsEmpty() )
3463 {
3464 if( m_reporter )
3465 {
3466 wxString msg;
3467 msg.Printf( _( "Loading library '%s':\n"
3468 "Footprint %s pad %s has a hole-rotation of %d degrees. "
3469 "KiCad only supports 90 degree rotations." ),
3470 m_library,
3472 aElem.name,
3473 KiROUND( slotRotation.AsDegrees() ) );
3474 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3475 }
3476 }
3477 else
3478 {
3479 if( m_reporter )
3480 {
3481 wxString msg;
3482 msg.Printf( _( "Footprint %s pad %s has a hole-rotation of %d degrees. "
3483 "KiCad only supports 90 degree rotations." ),
3484 aFootprint->GetReference(),
3485 aElem.name,
3486 KiROUND( slotRotation.AsDegrees() ) );
3487 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3488 }
3489 }
3490 }
3491
3492 break;
3493 }
3494
3495 default:
3497 if( !m_footprintName.IsEmpty() )
3498 {
3499 if( m_reporter )
3500 {
3501 wxString msg;
3502 msg.Printf( _( "Error loading library '%s':\n"
3503 "Footprint %s pad %s uses a hole of unknown kind %d." ),
3504 m_library,
3506 aElem.name,
3507 aElem.sizeAndShape->holeshape );
3508 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3509 }
3510 }
3511 else
3512 {
3513 if( m_reporter )
3514 {
3515 wxString msg;
3516 msg.Printf( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
3517 aFootprint->GetReference(),
3518 aElem.name,
3519 aElem.sizeAndShape->holeshape );
3520 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3521 }
3522 }
3523
3524 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3525 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3526 break;
3527 }
3528 }
3529
3530 if( aElem.sizeAndShape )
3531 pad->SetOffset( PADSTACK::ALL_LAYERS, aElem.sizeAndShape->holeoffset[0] );
3532 }
3533
3534 PADSTACK& ps = pad->Padstack();
3535
3536 auto setCopperGeometry =
3537 [&]( PCB_LAYER_ID aLayer, ALTIUM_PAD_SHAPE aShape, const VECTOR2I& aSize )
3538 {
3539 int altLayer = CopperLayerToOrdinal( aLayer );
3540
3541 ps.SetSize( aSize, aLayer );
3542
3543 switch( aShape )
3544 {
3546 ps.SetShape( PAD_SHAPE::RECTANGLE, aLayer );
3547 break;
3548
3550 if( aElem.sizeAndShape
3552 {
3553 ps.SetShape( PAD_SHAPE::ROUNDRECT, aLayer ); // 100 = round, 0 = rectangular
3554 double ratio = aElem.sizeAndShape->cornerradius[altLayer] / 200.;
3555 ps.SetRoundRectRadiusRatio( ratio, aLayer );
3556 }
3557 else if( aElem.topsize.x == aElem.topsize.y )
3558 {
3559 ps.SetShape( PAD_SHAPE::CIRCLE, aLayer );
3560 }
3561 else
3562 {
3563 ps.SetShape( PAD_SHAPE::OVAL, aLayer );
3564 }
3565
3566 break;
3567
3569 ps.SetShape( PAD_SHAPE::CHAMFERED_RECT, aLayer );
3571 ps.SetChamferRatio( 0.25, aLayer );
3572 break;
3573
3575 default:
3576 if( !m_footprintName.IsEmpty() )
3577 {
3578 if( m_reporter )
3579 {
3580 wxString msg;
3581 msg.Printf( _( "Error loading library '%s':\n"
3582 "Footprint %s pad %s uses an unknown pad shape." ),
3583 m_library,
3585 aElem.name );
3586 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3587 }
3588 }
3589 else
3590 {
3591 if( m_reporter )
3592 {
3593 wxString msg;
3594 msg.Printf( _( "Footprint %s pad %s uses an unknown pad shape." ),
3595 aFootprint->GetReference(),
3596 aElem.name );
3597 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3598 }
3599 }
3600 break;
3601 }
3602 };
3603
3604 switch( aElem.padmode )
3605 {
3608 setCopperGeometry( PADSTACK::ALL_LAYERS, aElem.topshape, aElem.topsize );
3609 break;
3610
3613 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3614 setCopperGeometry( PADSTACK::INNER_LAYERS, aElem.midshape, aElem.midsize );
3615 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3616 break;
3617
3620
3621 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3622 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3623 setCopperGeometry( In1_Cu, aElem.midshape, aElem.midsize );
3624
3625 if( aElem.sizeAndShape )
3626 {
3627 size_t i = 0;
3628
3629 LSET intLayers = aFootprint->BoardLayerSet();
3630 intLayers &= LSET::InternalCuMask();
3631 intLayers.set( In1_Cu, false ); // Already handled above
3632
3633 for( PCB_LAYER_ID layer : intLayers )
3634 {
3635 setCopperGeometry( layer, aElem.sizeAndShape->inner_shape[i],
3636 VECTOR2I( aElem.sizeAndShape->inner_size[i].x,
3637 aElem.sizeAndShape->inner_size[i].y ) );
3638 i++;
3639 }
3640 }
3641
3642 break;
3643 }
3644
3645 switch( aElem.layer )
3646 {
3648 pad->SetLayer( F_Cu );
3649 pad->SetLayerSet( PAD::SMDMask() );
3650 break;
3651
3653 pad->SetLayer( B_Cu );
3654 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
3655 break;
3656
3658 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
3659 break;
3660
3661 default:
3662 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3663 pad->SetLayer( klayer );
3664 pad->SetLayerSet( LSET( { klayer } ) );
3665 break;
3666 }
3667
3669 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
3670
3672 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
3673
3674 if( aElem.is_tent_top )
3675 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
3676
3677 if( aElem.is_tent_bottom )
3678 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
3679
3680 pad->SetPadToDieLength( aElem.pad_to_die_length );
3681 pad->SetPadToDieDelay( aElem.pad_to_die_delay );
3682
3683 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3684}
3685
3686
3688{
3689 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3690
3691 if( klayer == UNDEFINED_LAYER )
3692 {
3693 if( m_reporter )
3694 {
3695 wxString msg;
3696 msg.Printf( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
3697 "equivalent. It has been moved to KiCad layer Eco1_User." ),
3698 aElem.name, aElem.layer );
3699 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3700 }
3701
3702 klayer = Eco1_User;
3703 }
3704
3705 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( m_board );
3706
3707 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3708
3709 m_board->Add( pad.release(), ADD_MODE::APPEND );
3710}
3711
3712
3714{
3715 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3716
3717 if( klayer == UNDEFINED_LAYER )
3718 {
3719 if( !m_footprintName.IsEmpty() )
3720 {
3721 if( m_reporter )
3722 {
3723 wxString msg;
3724 msg.Printf( _( "Loading library '%s':\n"
3725 "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3726 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3727 m_library,
3729 aElem.name,
3730 aElem.layer );
3731 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3732 }
3733 }
3734 else
3735 {
3736 if( m_reporter )
3737 {
3738 wxString msg;
3739 msg.Printf( _( "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3740 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3741 aFootprint->GetReference(),
3742 aElem.name,
3743 aElem.layer );
3744 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3745 }
3746 }
3747
3748 klayer = Eco1_User;
3749 }
3750
3751 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( aFootprint );
3752
3753 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3754
3755 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3756}
3757
3758
3760 PCB_SHAPE* aShape )
3761{
3762 if( aElem.net != ALTIUM_NET_UNCONNECTED )
3763 {
3764 if( m_reporter )
3765 {
3766 wxString msg;
3767 msg.Printf( _( "Non-copper pad %s is connected to a net, which is not supported." ),
3768 aElem.name );
3769 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3770 }
3771 }
3772
3773 if( aElem.holesize != 0 )
3774 {
3775 if( m_reporter )
3776 {
3777 wxString msg;
3778 msg.Printf( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
3779 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3780 }
3781 }
3782
3783 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3784 {
3785 if( m_reporter )
3786 {
3787 wxString msg;
3788 msg.Printf( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
3789 aElem.name );
3790 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3791 }
3792 }
3793
3794 switch( aElem.topshape )
3795 {
3797 {
3798 // filled rect
3799 aShape->SetShape( SHAPE_T::POLY );
3800 aShape->SetFilled( true );
3801 aShape->SetLayer( aLayer );
3802 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3803
3804 aShape->SetPolyPoints(
3805 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
3806 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3807 aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3808 aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
3809
3810 if( aElem.direction != 0 )
3811 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3812 }
3813 break;
3814
3816 if( aElem.sizeAndShape
3818 {
3819 // filled roundrect
3820 int cornerradius = aElem.sizeAndShape->cornerradius[0];
3821 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
3822
3823 aShape->SetLayer( aLayer );
3824 aShape->SetStroke( STROKE_PARAMS( offset * 2, LINE_STYLE::SOLID ) );
3825
3826 if( cornerradius < 100 )
3827 {
3828 int offsetX = aElem.topsize.x / 2 - offset;
3829 int offsetY = aElem.topsize.y / 2 - offset;
3830
3831 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
3832 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
3833 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
3834 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
3835
3836 aShape->SetShape( SHAPE_T::POLY );
3837 aShape->SetFilled( true );
3838 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
3839 }
3840 else if( aElem.topsize.x == aElem.topsize.y )
3841 {
3842 // circle
3843 aShape->SetShape( SHAPE_T::CIRCLE );
3844 aShape->SetFilled( true );
3845 aShape->SetStart( aElem.position );
3846 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3847 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3848 }
3849 else if( aElem.topsize.x < aElem.topsize.y )
3850 {
3851 // short vertical line
3852 aShape->SetShape( SHAPE_T::SEGMENT );
3853 VECTOR2I pointOffset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3854 aShape->SetStart( aElem.position + pointOffset );
3855 aShape->SetEnd( aElem.position - pointOffset );
3856 }
3857 else
3858 {
3859 // short horizontal line
3860 aShape->SetShape( SHAPE_T::SEGMENT );
3861 VECTOR2I pointOffset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3862 aShape->SetStart( aElem.position + pointOffset );
3863 aShape->SetEnd( aElem.position - pointOffset );
3864 }
3865
3866 if( aElem.direction != 0 )
3867 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3868 }
3869 else if( aElem.topsize.x == aElem.topsize.y )
3870 {
3871 // filled circle
3872 aShape->SetShape( SHAPE_T::CIRCLE );
3873 aShape->SetFilled( true );
3874 aShape->SetLayer( aLayer );
3875 aShape->SetStart( aElem.position );
3876 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3877 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3878 }
3879 else
3880 {
3881 // short line
3882 aShape->SetShape( SHAPE_T::SEGMENT );
3883 aShape->SetLayer( aLayer );
3884 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
3886
3887 if( aElem.topsize.x < aElem.topsize.y )
3888 {
3889 VECTOR2I offset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3890 aShape->SetStart( aElem.position + offset );
3891 aShape->SetEnd( aElem.position - offset );
3892 }
3893 else
3894 {
3895 VECTOR2I offset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3896 aShape->SetStart( aElem.position + offset );
3897 aShape->SetEnd( aElem.position - offset );
3898 }
3899
3900 if( aElem.direction != 0 )
3901 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3902 }
3903 break;
3904
3906 {
3907 // filled octagon
3908 aShape->SetShape( SHAPE_T::POLY );
3909 aShape->SetFilled( true );
3910 aShape->SetLayer( aLayer );
3911 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3912
3913 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
3914 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3915 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3916 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
3917
3918 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
3919 VECTOR2I chamferX( chamfer, 0 );
3920 VECTOR2I chamferY( 0, chamfer );
3921
3922 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
3923 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
3924
3925 if( aElem.direction != 0. )
3926 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3927 }
3928 break;
3929
3931 default:
3932 if( m_reporter )
3933 {
3934 wxString msg;
3935 msg.Printf( _( "Non-copper pad %s uses an unknown pad shape." ), aElem.name );
3936 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3937 }
3938
3939 break;
3940 }
3941}
3942
3943
3945 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3946{
3947 if( m_progressReporter )
3948 m_progressReporter->Report( _( "Loading vias..." ) );
3949
3950 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3951
3952 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3953 {
3954 checkpoint();
3955 AVIA6 elem( reader );
3956
3957 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
3958
3959 via->SetPosition( elem.position );
3960 via->SetDrill( elem.holesize );
3961 via->SetNetCode( GetNetCode( elem.net ) );
3962 via->SetLocked( elem.is_locked );
3963
3964 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
3966 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
3968
3969 if( start_layer_outside && end_layer_outside )
3970 {
3971 via->SetViaType( VIATYPE::THROUGH );
3972 }
3973 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
3974 {
3975 via->SetViaType( VIATYPE::BURIED );
3976 }
3977 else
3978 {
3979 via->SetViaType( VIATYPE::BLIND );
3980 }
3981
3982 // TODO: Altium has a specific flag for microvias, independent of start/end layer
3983#if 0
3984 if( something )
3985 via->SetViaType( VIATYPE::MICROVIA );
3986#endif
3987
3988 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
3989 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
3990
3991 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
3992 {
3993 if( m_reporter )
3994 {
3995 wxString msg;
3996 msg.Printf( _( "Via from layer %d to %d uses a non-copper layer, which is not "
3997 "supported." ),
3998 elem.layer_start,
3999 elem.layer_end );
4000 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4001 }
4002
4003 continue; // just assume through-hole instead.
4004 }
4005
4006 // we need VIATYPE set!
4007 via->SetLayerPair( start_klayer, end_klayer );
4008
4009 switch( elem.viamode )
4010 {
4011 default:
4013 via->SetWidth( PADSTACK::ALL_LAYERS, elem.diameter );
4014 break;
4015
4017 via->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
4018 via->SetWidth( F_Cu, elem.diameter_by_layer[0] );
4019 via->SetWidth( PADSTACK::INNER_LAYERS, elem.diameter_by_layer[1] );
4020 via->SetWidth( B_Cu, elem.diameter_by_layer[31] );
4021 break;
4022
4024 {
4025 via->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
4026
4027 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, MAX_CU_LAYERS ) )
4028 {
4029 int altiumLayer = CopperLayerToOrdinal( layer );
4030 wxCHECK2_MSG( altiumLayer < 32, break,
4031 "Altium importer expects 32 or fewer copper layers" );
4032
4033 via->SetWidth( layer, elem.diameter_by_layer[altiumLayer] );
4034 }
4035
4036 break;
4037 }
4038 }
4039
4041 {
4042 via->SetFrontTentingMode( elem.is_tent_top ? TENTING_MODE::TENTED
4044 via->SetBackTentingMode( elem.is_tent_bottom ? TENTING_MODE::TENTED
4046 }
4047
4048 m_board->Add( via.release(), ADD_MODE::APPEND );
4049 }
4050
4051 if( reader.GetRemainingBytes() != 0 )
4052 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
4053}
4054
4056 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4057{
4058 if( m_progressReporter )
4059 m_progressReporter->Report( _( "Loading tracks..." ) );
4060
4061 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4062
4063 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
4064 {
4065 checkpoint();
4066 ATRACK6 elem( reader );
4067
4068 if( elem.component == ALTIUM_COMPONENT_NONE )
4069 {
4070 ConvertTracks6ToBoardItem( elem, primitiveIndex );
4071 }
4072 else
4073 {
4074 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4075 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
4076 }
4077 }
4078
4079 if( reader.GetRemainingBytes() != 0 )
4080 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
4081}
4082
4083
4084void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
4085{
4086 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
4087 {
4088 if( m_polygons.size() <= aElem.polygon )
4089 {
4090 // Can happen when reading old Altium files: just skip this item
4091 if( m_reporter )
4092 {
4093 wxString msg;
4094 msg.Printf( wxT( "ATRACK6 stream tries to access polygon id %u "
4095 "of %u existing polygons; skipping it" ),
4096 static_cast<unsigned>( aElem.polygon ),
4097 static_cast<unsigned>( m_polygons.size() ) );
4098 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4099 }
4100
4101 return;
4102 }
4103
4104 ZONE* zone = m_polygons.at( aElem.polygon );
4105
4106 if( zone == nullptr )
4107 {
4108 return; // we know the zone id, but because we do not know the layer we did not
4109 // add it!
4110 }
4111
4112 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
4113
4114 if( klayer == UNDEFINED_LAYER )
4115 return; // Just skip it for now. Users can fill it themselves.
4116
4117 if( !zone->HasFilledPolysForLayer( klayer ) )
4118 return;
4119
4120 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
4121
4122 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4123 shape.SetStart( aElem.start );
4124 shape.SetEnd( aElem.end );
4126
4127 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
4128 // Will be simplified and fractured later
4129
4130 zone->SetIsFilled( true );
4131 zone->SetNeedRefill( false );
4132
4133 return;
4134 }
4135
4136 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4137 || IsAltiumLayerAPlane( aElem.layer ) )
4138 {
4139 // This is not the actual board item. We can use it to create the polygon for the region
4140 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4141 shape.SetStart( aElem.start );
4142 shape.SetEnd( aElem.end );
4144
4146 }
4147 else
4148 {
4149 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4150 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4151 }
4152
4153 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4154 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4155 {
4156 int width = aElem.width + ( layerExpansionMask.second * 2 );
4157 if( width > 1 )
4158 {
4159 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4160
4161 seg->SetStart( aElem.start );
4162 seg->SetEnd( aElem.end );
4163 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4164 seg->SetLayer( layerExpansionMask.first );
4165
4166 m_board->Add( seg.release(), ADD_MODE::APPEND );
4167 }
4168 }
4169}
4170
4171
4173 const int aPrimitiveIndex,
4174 const bool aIsBoardImport )
4175{
4176 if( aElem.polygon != ALTIUM_POLYGON_NONE )
4177 {
4178 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Track with polygon id %u",
4179 (unsigned)aElem.polygon ) );
4180 return;
4181 }
4182
4183 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4184 || IsAltiumLayerAPlane( aElem.layer ) )
4185 {
4186 // This is not the actual board item. We can use it to create the polygon for the region
4187 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4188 shape.SetStart( aElem.start );
4189 shape.SetEnd( aElem.end );
4191
4192 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4193 aElem.keepoutrestrictions );
4194 }
4195 else
4196 {
4197 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4198 {
4199 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4200 {
4201 // Special case: do to not lose net connections in footprints
4202 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4203 }
4204 else
4205 {
4206 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4207 }
4208 }
4209 }
4210
4211 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4212 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4213 {
4214 int width = aElem.width + ( layerExpansionMask.second * 2 );
4215 if( width > 1 )
4216 {
4217 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4218
4219 seg->SetStart( aElem.start );
4220 seg->SetEnd( aElem.end );
4221 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4222 seg->SetLayer( layerExpansionMask.first );
4223
4224 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4225 }
4226 }
4227}
4228
4229
4231{
4232 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4233 {
4234 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
4235
4236 track->SetStart( aElem.start );
4237 track->SetEnd( aElem.end );
4238 track->SetWidth( aElem.width );
4239 track->SetLayer( aLayer );
4240 track->SetNetCode( GetNetCode( aElem.net ) );
4241
4242 m_board->Add( track.release(), ADD_MODE::APPEND );
4243 }
4244 else
4245 {
4246 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4247
4248 seg->SetStart( aElem.start );
4249 seg->SetEnd( aElem.end );
4250 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4251 seg->SetLayer( aLayer );
4252
4253 m_board->Add( seg.release(), ADD_MODE::APPEND );
4254 }
4255}
4256
4257
4259 PCB_LAYER_ID aLayer )
4260{
4261 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4262
4263 seg->SetStart( aElem.start );
4264 seg->SetEnd( aElem.end );
4265 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4266 seg->SetLayer( aLayer );
4267
4268 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4269}
4270
4271
4273 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4274{
4275 if( m_progressReporter )
4276 m_progressReporter->Report( _( "Loading unicode strings..." ) );
4277
4278 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4279
4281
4282 if( reader.GetRemainingBytes() != 0 )
4283 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
4284}
4285
4287 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4288{
4289 if( m_progressReporter )
4290 m_progressReporter->Report( _( "Loading text..." ) );
4291
4292 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4293
4294 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4295 {
4296 checkpoint();
4297 ATEXT6 elem( reader, m_unicodeStrings );
4298
4299 if( elem.component == ALTIUM_COMPONENT_NONE )
4300 {
4302 }
4303 else
4304 {
4305 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4306 ConvertTexts6ToFootprintItem( footprint, elem );
4307 }
4308 }
4309
4310 if( reader.GetRemainingBytes() != 0 )
4311 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
4312}
4313
4314
4316{
4317 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4318 {
4319 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4320 ConvertBarcodes6ToBoardItemOnLayer( aElem, klayer );
4321 return;
4322 }
4323
4324 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4325 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
4326}
4327
4328
4330{
4331 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4332 {
4333 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4334 ConvertBarcodes6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4335 return;
4336 }
4337
4338 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4339 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4340}
4341
4342
4344{
4345 std::unique_ptr<PCB_TEXTBOX> pcbTextbox = std::make_unique<PCB_TEXTBOX>( m_board );
4346 std::unique_ptr<PCB_TEXT> pcbText = std::make_unique<PCB_TEXT>( m_board );
4347
4348 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4349
4350 static const std::map<wxString, wxString> variableMap = {
4351 { "LAYER_NAME", "LAYER" },
4352 { "PRINT_DATE", "CURRENT_DATE"},
4353 };
4354
4355 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4356 BOARD_ITEM* item = pcbText.get();
4357 EDA_TEXT* text = pcbText.get();
4358 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4359
4360 if( isTextbox )
4361 {
4362 item = pcbTextbox.get();
4363 text = pcbTextbox.get();
4364
4366 HelperSetTextboxAlignmentAndPos( aElem, pcbTextbox.get() );
4367 }
4368 else
4369 {
4372 }
4373
4374 text->SetText( kicadText );
4375 item->SetLayer( aLayer );
4376 item->SetIsKnockout( aElem.isInverted );
4377
4378 if( isTextbox )
4379 m_board->Add( pcbTextbox.release(), ADD_MODE::APPEND );
4380 else
4381 m_board->Add( pcbText.release(), ADD_MODE::APPEND );
4382}
4383
4384
4386 PCB_LAYER_ID aLayer )
4387{
4388 std::unique_ptr<PCB_TEXTBOX> fpTextbox = std::make_unique<PCB_TEXTBOX>( aFootprint );
4389 std::unique_ptr<PCB_TEXT> fpText = std::make_unique<PCB_TEXT>( aFootprint );
4390
4391 BOARD_ITEM* item = fpText.get();
4392 EDA_TEXT* text = fpText.get();
4393 PCB_FIELD* field = nullptr;
4394
4395 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4396 bool toAdd = false;
4397
4398 if( aElem.isDesignator )
4399 {
4400 item = &aFootprint->Reference(); // TODO: handle multiple layers
4401 text = &aFootprint->Reference();
4402 field = &aFootprint->Reference();
4403 }
4404 else if( aElem.isComment )
4405 {
4406 item = &aFootprint->Value(); // TODO: handle multiple layers
4407 text = &aFootprint->Value();
4408 field = &aFootprint->Value();
4409 }
4410 else
4411 {
4412 item = fpText.get();
4413 text = fpText.get();
4414 toAdd = true;
4415 }
4416
4417 static const std::map<wxString, wxString> variableMap = {
4418 { "DESIGNATOR", "REFERENCE" },
4419 { "COMMENT", "VALUE" },
4420 { "VALUE", "ALTIUM_VALUE" },
4421 { "LAYER_NAME", "LAYER" },
4422 { "PRINT_DATE", "CURRENT_DATE"},
4423 };
4424
4425 if( isTextbox )
4426 {
4427 item = fpTextbox.get();
4428 text = fpTextbox.get();
4429
4431 HelperSetTextboxAlignmentAndPos( aElem, fpTextbox.get() );
4432 }
4433 else
4434 {
4437 }
4438
4439 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4440
4441 text->SetText( kicadText );
4442 text->SetKeepUpright( false );
4443 item->SetLayer( aLayer );
4444 item->SetIsKnockout( aElem.isInverted );
4445
4446 if( toAdd )
4447 {
4448 if( isTextbox )
4449 aFootprint->Add( fpTextbox.release(), ADD_MODE::APPEND );
4450 else
4451 aFootprint->Add( fpText.release(), ADD_MODE::APPEND );
4452 }
4453}
4454
4455
4457{
4458 std::unique_ptr<PCB_BARCODE> pcbBarcode = std::make_unique<PCB_BARCODE>( m_board );
4459
4460 pcbBarcode->SetLayer( aLayer );
4461 pcbBarcode->SetPosition( aElem.position );
4462 pcbBarcode->SetWidth( aElem.textbox_rect_width );
4463 pcbBarcode->SetHeight( aElem.textbox_rect_height );
4464 pcbBarcode->SetMargin( aElem.barcode_margin );
4465 pcbBarcode->SetText( aElem.text );
4466
4467 switch( aElem.barcode_type )
4468 {
4469 case ALTIUM_BARCODE_TYPE::CODE39: pcbBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4470 case ALTIUM_BARCODE_TYPE::CODE128: pcbBarcode->SetKind( BARCODE_T::CODE_128 ); break;
4471 default: pcbBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4472 }
4473
4474 pcbBarcode->SetIsKnockout( aElem.barcode_inverted );
4475 pcbBarcode->AssembleBarcode();
4476
4477 m_board->Add( pcbBarcode.release(), ADD_MODE::APPEND );
4478}
4479
4480
4482 PCB_LAYER_ID aLayer )
4483{
4484 std::unique_ptr<PCB_BARCODE> fpBarcode = std::make_unique<PCB_BARCODE>( aFootprint );
4485
4486 fpBarcode->SetLayer( aLayer );
4487 fpBarcode->SetPosition( aElem.position );
4488 fpBarcode->SetWidth( aElem.textbox_rect_width );
4489 fpBarcode->SetHeight( aElem.textbox_rect_height );
4490 fpBarcode->SetMargin( aElem.barcode_margin );
4491 fpBarcode->SetText( aElem.text );
4492
4493 switch( aElem.barcode_type )
4494 {
4495 case ALTIUM_BARCODE_TYPE::CODE39: fpBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4496 case ALTIUM_BARCODE_TYPE::CODE128: fpBarcode->SetKind( BARCODE_T::CODE_128 ); break;
4497 default: fpBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4498 }
4499
4500 fpBarcode->SetIsKnockout( aElem.barcode_inverted );
4501 fpBarcode->AssembleBarcode();
4502
4503 aFootprint->Add( fpBarcode.release(), ADD_MODE::APPEND );
4504}
4505
4506
4508{
4509 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4510
4511 // Altium textboxes do not have borders
4512 aTextbox->SetBorderEnabled( false );
4513
4514 // Calculate position
4515 VECTOR2I kposition = aElem.position;
4516
4517 if( aElem.isMirrored )
4518 kposition.x -= aElem.textbox_rect_width;
4519
4520 kposition.y -= aElem.textbox_rect_height;
4521
4522#if 0
4523 // Compensate for KiCad's textbox margin
4524 int charWidth = aTextbox->GetTextWidth();
4525 int charHeight = aTextbox->GetTextHeight();
4526
4527 VECTOR2I kicadMargin;
4528
4529 if( !aTextbox->GetFont() || aTextbox->GetFont()->IsStroke() )
4530 kicadMargin = VECTOR2I( charWidth * 0.933, charHeight * 0.67 );
4531 else
4532 kicadMargin = VECTOR2I( charWidth * 0.808, charHeight * 0.844 );
4533
4534 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height )
4535 + kicadMargin * 2 - margin * 2 );
4536
4537 kposition = kposition - kicadMargin + margin;
4538#else
4539 aTextbox->SetMarginBottom( margin );
4540 aTextbox->SetMarginLeft( margin );
4541 aTextbox->SetMarginRight( margin );
4542 aTextbox->SetMarginTop( margin );
4543
4544 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height ) );
4545#endif
4546
4547 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4548
4549 aTextbox->SetPosition( kposition );
4550
4551 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4554
4555 switch( justification )
4556 {
4562 break;
4568 break;
4574 break;
4575 default:
4576 if( m_reporter )
4577 {
4578 wxString msg;
4579 msg.Printf( _( "Unknown textbox justification %d, aText %s" ), justification,
4580 aElem.text );
4581 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4582 }
4583
4586 break;
4587 }
4588
4589 aTextbox->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4590}
4591
4592
4594{
4595 VECTOR2I kposition = aElem.position;
4596
4597 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4598 int rectWidth = aElem.textbox_rect_width - margin * 2;
4599 int rectHeight = aElem.height;
4600
4601 if( aElem.isMirrored )
4602 rectWidth = -rectWidth;
4603
4604 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4607
4608 switch( justification )
4609 {
4613
4614 kposition.y -= rectHeight;
4615 break;
4619
4620 kposition.y -= rectHeight / 2;
4621 break;
4625 break;
4629
4630 kposition.x += rectWidth / 2;
4631 kposition.y -= rectHeight;
4632 break;
4636
4637 kposition.x += rectWidth / 2;
4638 kposition.y -= rectHeight / 2;
4639 break;
4643
4644 kposition.x += rectWidth / 2;
4645 break;
4649
4650 kposition.x += rectWidth;
4651 kposition.y -= rectHeight;
4652 break;
4656
4657 kposition.x += rectWidth;
4658 kposition.y -= rectHeight / 2;
4659 break;
4663
4664 kposition.x += rectWidth;
4665 break;
4666 default:
4669 break;
4670 }
4671
4672 int charWidth = aText->GetTextWidth();
4673 int charHeight = aText->GetTextHeight();
4674
4675 // Correct for KiCad's baseline offset.
4676 // Text height and font must be set correctly before calling.
4677 if( !aText->GetFont() || aText->GetFont()->IsStroke() )
4678 {
4679 switch( aText->GetVertJustify() )
4680 {
4681 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charHeight * 0.0407; break;
4682 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charHeight * 0.0355; break;
4683 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charHeight * 0.1225; break;
4684 default: break;
4685 }
4686 }
4687 else
4688 {
4689 switch( aText->GetVertJustify() )
4690 {
4691 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charWidth * 0.016; break;
4692 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charWidth * 0.085; break;
4693 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charWidth * 0.17; break;
4694 default: break;
4695 }
4696 }
4697
4698 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4699
4700 aText->SetTextPos( kposition );
4701 aText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4702}
4703
4704
4706{
4707 aEdaText.SetTextSize( VECTOR2I( aElem.height, aElem.height ) );
4708
4710 {
4711 KIFONT::FONT* font = KIFONT::FONT::GetFont( aElem.fontname, aElem.isBold, aElem.isItalic );
4712 aEdaText.SetFont( font );
4713
4714 if( font->IsOutline() )
4715 {
4716 // TODO: why is this required? Somehow, truetype size is calculated differently
4717 if( font->GetName().Contains( wxS( "Arial" ) ) )
4718 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.63, aElem.height * 0.63 ) );
4719 else
4720 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.5, aElem.height * 0.5 ) );
4721 }
4722 }
4723
4724 aEdaText.SetTextThickness( aElem.strokewidth );
4725 aEdaText.SetBoldFlag( aElem.isBold );
4726 aEdaText.SetItalic( aElem.isItalic );
4727 aEdaText.SetMirrored( aElem.isMirrored );
4728}
4729
4730
4732 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4733{
4734 if( m_progressReporter )
4735 m_progressReporter->Report( _( "Loading rectangles..." ) );
4736
4737 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4738
4739 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4740 {
4741 checkpoint();
4742 AFILL6 elem( reader );
4743
4744 if( elem.component == ALTIUM_COMPONENT_NONE )
4745 {
4747 }
4748 else
4749 {
4750 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4751 ConvertFills6ToFootprintItem( footprint, elem, true );
4752 }
4753 }
4754
4755 if( reader.GetRemainingBytes() != 0 )
4756 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
4757}
4758
4759
4761{
4762 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
4763 {
4764 // This is not the actual board item. We can use it to create the polygon for the region
4765 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4766
4767 shape.SetStart( aElem.pos1 );
4768 shape.SetEnd( aElem.pos2 );
4769 shape.SetFilled( true );
4771
4772 if( aElem.rotation != 0. )
4773 {
4774 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4775 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4776 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4777 }
4778
4780 }
4781 else
4782 {
4783 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4784 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4785 }
4786}
4787
4788
4790 const bool aIsBoardImport )
4791{
4792 if( aElem.is_keepout
4793 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
4794 {
4795 // This is not the actual board item. We can use it to create the polygon for the region
4796 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4797
4798 shape.SetStart( aElem.pos1 );
4799 shape.SetEnd( aElem.pos2 );
4800 shape.SetFilled( true );
4802
4803 if( aElem.rotation != 0. )
4804 {
4805 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4806 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4807 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4808 }
4809
4810 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4811 aElem.keepoutrestrictions );
4812 }
4813 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
4814 && aElem.net != ALTIUM_NET_UNCONNECTED )
4815 {
4816 // Special case: do to not lose net connections in footprints
4817 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4818 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4819 }
4820 else
4821 {
4822 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4823 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4824 }
4825}
4826
4827
4829{
4830 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::RECTANGLE );
4831
4832 fill->SetFilled( true );
4833 fill->SetLayer( aLayer );
4834 fill->SetStroke( STROKE_PARAMS( 0 ) );
4835
4836 fill->SetStart( aElem.pos1 );
4837 fill->SetEnd( aElem.pos2 );
4838
4839 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4840 {
4841 fill->SetNetCode( GetNetCode( aElem.net ) );
4842 }
4843
4844 if( aElem.rotation != 0. )
4845 {
4846 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4847 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4848 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4849 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4850 }
4851
4852 m_board->Add( fill.release(), ADD_MODE::APPEND );
4853}
4854
4855
4857 PCB_LAYER_ID aLayer )
4858{
4859 if( aLayer == F_Cu || aLayer == B_Cu )
4860 {
4861 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
4862
4863 LSET padLayers;
4864 padLayers.set( aLayer );
4865
4866 pad->SetAttribute( PAD_ATTRIB::SMD );
4867 EDA_ANGLE rotation( aElem.rotation, DEGREES_T );
4868
4869 // Handle rotation multiples of 90 degrees
4870 if( rotation.IsCardinal() )
4871 {
4873
4874 int width = std::abs( aElem.pos2.x - aElem.pos1.x );
4875 int height = std::abs( aElem.pos2.y - aElem.pos1.y );
4876
4877 // Swap width and height for 90 or 270 degree rotations
4878 if( rotation.IsCardinal90() )
4879 std::swap( width, height );
4880
4881 pad->SetSize( PADSTACK::ALL_LAYERS, { width, height } );
4882 pad->SetPosition( aElem.pos1 / 2 + aElem.pos2 / 2 );
4883 }
4884 else
4885 {
4887
4888 int anchorSize = std::min( std::abs( aElem.pos2.x - aElem.pos1.x ),
4889 std::abs( aElem.pos2.y - aElem.pos1.y ) );
4890 VECTOR2I anchorPos = aElem.pos1;
4891
4892 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
4893 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
4894 pad->SetPosition( anchorPos );
4895
4896 SHAPE_POLY_SET shapePolys;
4897 shapePolys.NewOutline();
4898 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4899 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4900 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4901 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4902 shapePolys.Outline( 0 ).SetClosed( true );
4903
4904 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2 - anchorPos.x,
4905 aElem.pos1.y / 2 + aElem.pos2.y / 2 - anchorPos.y );
4906 shapePolys.Rotate( EDA_ANGLE( aElem.rotation, DEGREES_T ), center );
4907 pad->AddPrimitivePoly( F_Cu, shapePolys, 0, true );
4908 }
4909
4910 pad->SetThermalSpokeAngle( ANGLE_90 );
4911 pad->SetLayerSet( padLayers );
4912
4913 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
4914 }
4915 else
4916 {
4917 std::unique_ptr<PCB_SHAPE> fill =
4918 std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::RECTANGLE );
4919
4920 fill->SetFilled( true );
4921 fill->SetLayer( aLayer );
4922 fill->SetStroke( STROKE_PARAMS( 0 ) );
4923
4924 fill->SetStart( aElem.pos1 );
4925 fill->SetEnd( aElem.pos2 );
4926
4927 if( aElem.rotation != 0. )
4928 {
4929 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4930 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4931 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4932 }
4933
4934 aFootprint->Add( fill.release(), ADD_MODE::APPEND );
4935 }
4936}
4937
4938
4939void ALTIUM_PCB::HelperSetZoneLayers( ZONE& aZone, const ALTIUM_LAYER aAltiumLayer )
4940{
4941 LSET layerSet;
4942
4943 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
4944 layerSet.set( klayer );
4945
4946 aZone.SetLayerSet( layerSet );
4947}
4948
4949
4950void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE& aZone, const uint8_t aKeepoutRestrictions )
4951{
4952 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
4953 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
4954 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
4955 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
4956 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
4957
4958 aZone.SetDoNotAllowVias( keepoutRestrictionVia );
4959 aZone.SetDoNotAllowTracks( keepoutRestrictionTrack );
4960 aZone.SetDoNotAllowZoneFills( keepoutRestrictionCopper );
4961 aZone.SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
4962 aZone.SetDoNotAllowFootprints( false );
4963}
4964
4965
4967 const ALTIUM_LAYER aAltiumLayer,
4968 const uint8_t aKeepoutRestrictions )
4969{
4970 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
4971
4972 zone->SetIsRuleArea( true );
4973
4974 HelperSetZoneLayers( *zone, aAltiumLayer );
4975 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4976
4977 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4978
4979 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4981
4982 m_board->Add( zone.release(), ADD_MODE::APPEND );
4983}
4984
4985
4987 const PCB_SHAPE& aShape,
4988 const ALTIUM_LAYER aAltiumLayer,
4989 const uint8_t aKeepoutRestrictions )
4990{
4991 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
4992
4993 zone->SetIsRuleArea( true );
4994
4995 HelperSetZoneLayers( *zone, aAltiumLayer );
4996 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4997
4998 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4999
5000 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
5002
5003 // TODO: zone->SetLocalCoord(); missing?
5004 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
5005}
5006
5007
5008std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
5009 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
5010{
5011 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
5012 return {}; // there is nothing to parse
5013
5014 auto elems =
5015 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
5016
5017 if( elems.first == elems.second )
5018 return {}; // there is nothing to parse
5019
5020 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
5021
5022 for( auto it = elems.first; it != elems.second; ++it )
5023 {
5024 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
5025
5027 {
5030 {
5031 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
5032 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
5033 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5034 {
5035 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
5036 }
5037
5038 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
5039 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5040 {
5041 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
5042 }
5043 }
5046 {
5047 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
5048 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5049 {
5050 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
5051 }
5052
5053 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
5054 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5055 {
5056 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
5057 }
5058 }
5059 }
5060 }
5061
5062 return layerExpansionPairs;
5063}
const char * name
std::string FormatPath(const std::vector< std::string > &aVectorPath)
Helper for debug logging (vector -> string)
ALTIUM_TEXT_POSITION
ALTIUM_RULE_KIND
ALTIUM_PAD_SHAPE
const uint16_t ALTIUM_NET_UNCONNECTED
const uint16_t ALTIUM_POLYGON_NONE
const uint16_t ALTIUM_POLYGON_BOARD
ALTIUM_RECORD
const int ALTIUM_COMPONENT_NONE
LIB_ID AltiumToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
wxString AltiumPcbSpecialStringsToKiCadStrings(const wxString &aString, const std::map< wxString, wxString > &aOverrides)
void HelperShapeLineChainFromAltiumVertices(SHAPE_LINE_CHAIN &aLine, const std::vector< ALTIUM_VERTICE > &aVertices)
double normalizeAngleDegrees(double Angle, double aMin, double aMax)
Normalize angle to be aMin < angle <= aMax angle is in degrees.
constexpr double BOLD_FACTOR
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
ALTIUM_PCB_DIR
Definition altium_pcb.h:38
@ EXTENDPRIMITIVEINFORMATION
Definition altium_pcb.h:56
std::function< void(const ALTIUM_PCB_COMPOUND_FILE &, const CFB::COMPOUND_FILE_ENTRY *)> PARSE_FUNCTION_POINTER_fp
Definition altium_pcb.h:119
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:129
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition board.h:185
@ LT_POWER
Definition board.h:188
@ LT_MIXED
Definition board.h:189
@ LT_JUMPER
Definition board.h:190
@ LT_SIGNAL
Definition board.h:187
#define DEFAULT_BOARD_THICKNESS_MM
@ BS_ITEM_TYPE_COPPER
@ BS_ITEM_TYPE_DIELECTRIC
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
std::map< uint32_t, wxString > ReadWideStringTable()
std::map< wxString, wxString > ReadProperties(std::function< std::map< wxString, wxString >(const std::string &)> handleBinaryData=[](const std::string &) { return std::map< wxString, wxString >();})
const CFB::CompoundFileReader & GetCompoundFileReader() const
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
const std::pair< AMODEL, std::vector< char > > * GetLibModel(const wxString &aModelID) const
std::tuple< wxString, const CFB::COMPOUND_FILE_ENTRY * > FindLibFootprintDirName(const wxString &aFpUnicodeName)
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition altium_pcb.h:288
void ParseClasses6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::vector< PCB_DIM_RADIAL * > m_radialDimensions
Definition altium_pcb.h:273
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:298
void ConvertTexts6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_LAYER, PCB_LAYER_ID > m_layermap
Definition altium_pcb.h:276
void ConvertShapeBasedRegions6ToBoardItemOnLayer(const AREGION6 &aElem, PCB_LAYER_ID aLayer)
void ParseVias6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperParseDimensions6Leader(const ADIMENSION6 &aElem)
wxString m_footprintName
for footprint library loading error reporting
Definition altium_pcb.h:295
std::vector< FOOTPRINT * > m_components
Definition altium_pcb.h:271
void ParseShapeBasedRegions6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
const ARULE6 * GetRuleDefault(ALTIUM_RULE_KIND aKind) const
void HelperParsePad6NonCopper(const APAD6 &aElem, PCB_LAYER_ID aLayer, PCB_SHAPE *aShape)
void ParseRegions6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::vector< PCB_LAYER_ID > GetKicadLayersToIterate(ALTIUM_LAYER aAltiumLayer) const
void ConvertShapeBasedRegions6ToFootprintItem(FOOTPRINT *aFootprint, const AREGION6 &aElem, const int aPrimitiveIndex)
void HelperPcpShapeAsFootprintKeepoutRegion(FOOTPRINT *aFootprint, const PCB_SHAPE &aShape, const ALTIUM_LAYER aAltiumLayer, const uint8_t aKeepoutRestrictions)
void ConvertArcs6ToBoardItem(const AARC6 &aElem, const int aPrimitiveIndex)
void ConvertShapeBasedRegions6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const AREGION6 &aElem, PCB_LAYER_ID aLayer, const int aPrimitiveIndex)
std::map< ALTIUM_LAYER, ZONE * > m_outer_plane
Definition altium_pcb.h:284
std::vector< int > m_altiumToKicadNetcodes
Definition altium_pcb.h:275
unsigned m_totalCount
for progress reporting
Definition altium_pcb.h:292
void ParseComponents6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperPcpShapeAsBoardKeepoutRegion(const PCB_SHAPE &aShape, const ALTIUM_LAYER aAltiumLayer, const uint8_t aKeepoutRestrictions)
std::map< wxString, ALTIUM_EMBEDDED_MODEL_DATA > m_EmbeddedModels
Definition altium_pcb.h:279
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 ConvertBarcodes6ToBoardItemOnLayer(const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
void ConvertComponentBody6ToFootprintItem(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, FOOTPRINT *aFootprint, const ACOMPONENTBODY6 &aElem)
void HelperSetTextAlignmentAndPos(const ATEXT6 &aElem, EDA_TEXT *aEdaText)
void ParseBoard6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertFills6ToFootprintItem(FOOTPRINT *aFootprint, const AFILL6 &aElem, const bool aIsBoardImport)
void ParseExtendedPrimitiveInformationData(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseFileHeader(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertShapeBasedRegions6ToBoardItem(const AREGION6 &aElem)
void HelperCreateBoardOutline(const std::vector< ALTIUM_VERTICE > &aVertices)
std::map< ALTIUM_RULE_KIND, std::vector< ARULE6 > > m_rules
Definition altium_pcb.h:280
void ConvertVias6ToFootprintItem(FOOTPRINT *aFootprint, const AVIA6 &aElem)
void ParseRules6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperSetZoneKeepoutRestrictions(ZONE &aZone, const uint8_t aKeepoutRestrictions)
unsigned m_doneCount
Definition altium_pcb.h:290
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
void ParseTracks6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::map< uint32_t, wxString > m_unicodeStrings
Definition altium_pcb.h:274
void ConvertTexts6ToBoardItem(const ATEXT6 &aElem)
void HelperParseDimensions6Center(const ADIMENSION6 &aElem)
void HelperParseDimensions6Radial(const ADIMENSION6 &aElem)
BOARD * m_board
Definition altium_pcb.h:270
void ParseBoardRegionsData(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseArcs6Data(const ALTIUM_PCB_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)
FOOTPRINT * ParseFootprint(ALTIUM_PCB_COMPOUND_FILE &altiumLibFile, const wxString &aFootprintName)
REPORTER * m_reporter
optional; may be nullptr
Definition altium_pcb.h:289
int GetNetCode(uint16_t aId) const
void ConvertTexts6ToEdaTextSettings(const ATEXT6 &aElem, EDA_TEXT &aEdaText)
wxString m_library
for footprint library loading error reporting
Definition altium_pcb.h:294
void ConvertPads6ToFootprintItemOnNonCopper(FOOTPRINT *aFootprint, const APAD6 &aElem)
void ConvertTexts6ToFootprintItem(FOOTPRINT *aFootprint, const ATEXT6 &aElem)
unsigned m_lastProgressCount
Definition altium_pcb.h:291
void ParseFills6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
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 ParseWideStrings6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void remapUnsureLayers(std::vector< ABOARD6_LAYER_STACKUP > &aStackup)
std::vector< ZONE * > m_polygons
Definition altium_pcb.h:272
void ParsePads6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertArcs6ToPcbShape(const AARC6 &aElem, PCB_SHAPE *aShape)
void ConvertTracks6ToBoardItemOnLayer(const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void ConvertFills6ToBoardItem(const AFILL6 &aElem)
FOOTPRINT * HelperGetFootprint(uint16_t aComponent) const
LAYER_MAPPING_HANDLER m_layerMappingHandler
Definition altium_pcb.h:286
void ParsePolygons6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
PCB_LAYER_ID GetKicadLayer(ALTIUM_LAYER aAltiumLayer) const
void checkpoint()
void ParseComponentsBodies6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertTracks6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void Parse(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
ALTIUM_PCB(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter, LAYER_MAPPING_HANDLER &aLayerMappingHandler, REPORTER *aReporter=nullptr, const wxString &aLibrary=wxEmptyString, const wxString &aFootprintName=wxEmptyString)
void ConvertArcs6ToBoardItemOnLayer(const AARC6 &aElem, PCB_LAYER_ID aLayer)
void ParseTexts6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::map< ALTIUM_RECORD, std::multimap< int, const AEXTENDED_PRIMITIVE_INFORMATION > > m_extendedPrimitiveInformationMaps
Definition altium_pcb.h:282
void ParseModelsData(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry, const std::vector< std::string > &aRootDir)
std::map< ALTIUM_LAYER, wxString > m_layerNames
Definition altium_pcb.h:277
void ConvertPads6ToBoardItemOnNonCopper(const APAD6 &aElem)
void HelperSetTextboxAlignmentAndPos(const ATEXT6 &aElem, PCB_TEXTBOX *aPcbTextbox)
void ConvertBarcodes6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
void ParseNets6Data(const ALTIUM_PCB_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
void ParseDimensions6Data(const ALTIUM_PCB_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperSetZoneLayers(ZONE &aZone, const ALTIUM_LAYER aAltiumLayer)
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
BASE_SET & set(size_t pos)
Definition base_set.h:116
Container for design settings for a BOARD object.
void SetGridOrigin(const VECTOR2I &aOrigin)
const VECTOR2I & GetGridOrigin() const
void SetAuxOrigin(const VECTOR2I &aOrigin)
const VECTOR2I & GetAuxOrigin() const
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:324
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:284
virtual LSET BoardLayerSet() const
Return the LSET for the board that this item resides on.
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
int BuildBoardThicknessFromStackup() 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:322
constexpr coord_type GetY() const
Definition box2.h:208
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr coord_type GetX() const
Definition box2.h:207
constexpr size_type GetHeight() const
Definition box2.h:215
EDA_ANGLE Normalize()
Definition eda_angle.h:229
double Sin() const
Definition eda_angle.h:178
double AsDegrees() const
Definition eda_angle.h:116
bool IsHorizontal() const
Definition eda_angle.h:142
bool IsCardinal() const
Definition eda_angle.cpp:40
bool IsVertical() const
Definition eda_angle.h:148
bool IsCardinal90() const
Definition eda_angle.cpp:54
double Cos() const
Definition eda_angle.h:197
EDA_ANGLE GetArcAngle() const
void SetCenter(const VECTOR2I &aCenter)
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:169
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:136
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:178
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:174
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:168
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:80
int GetTextHeight() const
Definition eda_text.h:267
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:541
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:585
KIFONT::FONT * GetFont() const
Definition eda_text.h:247
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:402
int GetTextWidth() const
Definition eda_text.h:264
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:426
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:387
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:293
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:308
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:316
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:508
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:418
wxString GetEmbeddedFileLink(const EMBEDDED_FILE &aFile) const
Return the link for an embedded file.
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
static RETURN_CODE CompressAndEncode(EMBEDDED_FILE &aFile)
Take data from the #decompressedData buffer and compresses it using ZSTD into the #compressedEncodedD...
EDA_ANGLE GetOrientation() const
Definition footprint.h:328
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:777
bool IsFlipped() const
Definition footprint.h:514
PCB_FIELD & Reference()
Definition footprint.h:778
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:321
const wxString & GetReference() const
Definition footprint.h:741
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition footprint.h:1193
VECTOR2I GetPosition() const override
Definition footprint.h:325
VECTOR3D m_Offset
3D model offset (mm)
Definition footprint.h:120
double m_Opacity
Definition footprint.h:121
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition footprint.h:119
wxString m_Filename
The 3D shape filename in 3D library.
Definition footprint.h:122
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:131
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:147
virtual bool IsStroke() const
Definition font.h:138
const wxString & GetName() const
Definition font.h:149
virtual bool IsOutline() const
Definition font.h:139
Definition kiid.h:49
LAYER_RANGE_ITERATOR begin() const
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 lset.h:37
static const LSET & AllBoardTechMask()
Return a mask holding board technical layers (no CU layer) on both side.
Definition lset.cpp:666
static const LSET & UserMask()
Definition lset.cpp:673
static LSET UserDefinedLayersMask(int aUserDefinedLayerCount=MAX_USER_DEFINED_LAYERS)
Return a mask with the requested number of user defined layers.
Definition lset.cpp:687
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition lset.cpp:560
Handle the data for a net.
Definition netinfo.h:54
int GetNetCode() const
Definition netinfo.h:106
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:247
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:157
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition padstack.cpp:944
void SetMode(MODE aMode)
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition padstack.cpp:973
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition padstack.cpp:884
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition padstack.cpp:991
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
@ CUSTOM
Shapes can be defined on arbitrary layers.
Definition padstack.h:173
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:172
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition padstack.cpp:858
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:180
Definition pad.h:55
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:337
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition pad.cpp:358
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:344
double GetRadius() const
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h:354
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:81
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_shape.h:78
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition pcb_shape.h:92
VECTOR2I GetPosition() const override
Definition pcb_shape.h:79
void SetBorderEnabled(bool enabled)
void SetMarginTop(int aTop)
Definition pcb_textbox.h:85
void SetMarginLeft(int aLeft)
Definition pcb_textbox.h:84
void SetMarginBottom(int aBottom)
Definition pcb_textbox.h:87
void SetTextAngle(const EDA_ANGLE &aAngle) override
void SetMarginRight(int aRight)
Definition pcb_textbox.h:86
A progress reporter interface for use in multi-threaded environments.
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
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:446
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:120
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
const VECTOR2I & GetP0() const
Definition shape_arc.h:118
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
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.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
int NextShape(int aPointIndex) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
bool IsArcStart(size_t aIndex) const
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
Simple container to manage line stroke parameters.
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition vector2d.h:546
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:561
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition vector2d.h:314
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetNeedRefill(bool aNeedRefill)
Definition zone.h:292
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:720
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:602
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:719
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:617
void SetIsFilled(bool isFilled)
Definition zone.h:289
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition zone.h:588
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:523
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:718
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:721
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:717
static int GetDefaultHatchPitch()
Definition zone.cpp:1285
@ CHAMFER_ACUTE_CORNERS
Acute angles are chamfered.
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
bool m_ImportSkipComponentBodies
Skip importing component bodies when importing some format files, such as Altium.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define MAX_CU_LAYERS
Definition layer_ids.h:176
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
size_t CopperLayerToOrdinal(PCB_LAYER_ID aLayer)
Converts KiCad copper layer enum to an ordinal between the front and back layers.
Definition layer_ids.h:913
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ In22_Cu
Definition layer_ids.h:87
@ In11_Cu
Definition layer_ids.h:76
@ In29_Cu
Definition layer_ids.h:94
@ In30_Cu
Definition layer_ids.h:95
@ User_8
Definition layer_ids.h:131
@ User_11
Definition layer_ids.h:134
@ In17_Cu
Definition layer_ids.h:82
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ In9_Cu
Definition layer_ids.h:74
@ User_6
Definition layer_ids.h:129
@ User_7
Definition layer_ids.h:130
@ In19_Cu
Definition layer_ids.h:84
@ In7_Cu
Definition layer_ids.h:72
@ In28_Cu
Definition layer_ids.h:93
@ In26_Cu
Definition layer_ids.h:91
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ User_14
Definition layer_ids.h:137
@ User_5
Definition layer_ids.h:128
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ In21_Cu
Definition layer_ids.h:86
@ In23_Cu
Definition layer_ids.h:88
@ B_Paste
Definition layer_ids.h:105
@ In15_Cu
Definition layer_ids.h:80
@ In2_Cu
Definition layer_ids.h:67
@ User_10
Definition layer_ids.h:133
@ User_9
Definition layer_ids.h:132
@ F_Fab
Definition layer_ids.h:119
@ In10_Cu
Definition layer_ids.h:75
@ Margin
Definition layer_ids.h:113
@ F_SilkS
Definition layer_ids.h:100
@ In4_Cu
Definition layer_ids.h:69
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Eco2_User
Definition layer_ids.h:110
@ In16_Cu
Definition layer_ids.h:81
@ In24_Cu
Definition layer_ids.h:89
@ In1_Cu
Definition layer_ids.h:66
@ User_3
Definition layer_ids.h:126
@ User_1
Definition layer_ids.h:124
@ User_12
Definition layer_ids.h:135
@ B_SilkS
Definition layer_ids.h:101
@ In13_Cu
Definition layer_ids.h:78
@ User_4
Definition layer_ids.h:127
@ In8_Cu
Definition layer_ids.h:73
@ In14_Cu
Definition layer_ids.h:79
@ User_13
Definition layer_ids.h:136
@ User_2
Definition layer_ids.h:125
@ In12_Cu
Definition layer_ids.h:77
@ In27_Cu
Definition layer_ids.h:92
@ In6_Cu
Definition layer_ids.h:71
@ In5_Cu
Definition layer_ids.h:70
@ In3_Cu
Definition layer_ids.h:68
@ In20_Cu
Definition layer_ids.h:85
@ F_Cu
Definition layer_ids.h:64
@ In18_Cu
Definition layer_ids.h:83
@ In25_Cu
Definition layer_ids.h:90
@ B_Fab
Definition layer_ids.h:118
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition kicad_algo.h:84
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ RECTANGLE
Definition padstack.h:54
BARCODE class definition.
@ INWARD
>--—<
DIM_PRECISION
@ THROUGH
Definition pcb_track.h:68
@ MICROVIA
Definition pcb_track.h:71
std::function< std::map< wxString, PCB_LAYER_ID >(const std::vector< INPUT_LAYER_DESC > &)> LAYER_MAPPING_HANDLER
Pointer to a function that takes a map of source and KiCad layers and returns a re-mapped version.
CITER next(CITER it)
Definition ptree.cpp:124
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_DEBUG
@ RPT_SEVERITY_INFO
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
wxString NotSpecifiedPrm()
double startangle
uint16_t component
uint32_t width
ALTIUM_LAYER layer
uint8_t keepoutrestrictions
uint16_t polygon
VECTOR2I center
uint32_t radius
uint16_t net
double endangle
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 sourceHierachicalPath
wxString sourcefootprintlibrary
ALTIUM_LAYER layer
wxString sourcedesignator
wxString sourceUniqueID
ALTIUM_UNIT textunit
uint32_t textlinewidth
ALTIUM_LAYER layer
std::vector< VECTOR2I > textPoint
ALTIUM_DIMENSION_KIND kind
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
std::vector< char > m_data
Definition altium_pcb.h:109
const VECTOR2I position
double z_offset
wxString id
wxString name
VECTOR3D rotation
wxString name
ALTIUM_PAD_SHAPE inner_shape[29]
ALTIUM_PAD_HOLE_SHAPE holeshape
ALTIUM_PAD_SHAPE_ALT alt_shape[32]
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
int32_t pad_to_die_delay
bool is_tent_bottom
VECTOR2I botsize
ALTIUM_PAD_SHAPE botshape
uint16_t component
VECTOR2I midsize
ALTIUM_PAD_SHAPE midshape
int32_t pastemaskexpansionmanual
VECTOR2I position
VECTOR2I topsize
int32_t pad_to_die_length
std::vector< ALTIUM_VERTICE > vertices
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
ALTIUM_LAYER layer
uint8_t keepoutrestrictions
ALTIUM_LAYER layer
std::vector< ALTIUM_VERTICE > outline
std::vector< std::vector< ALTIUM_VERTICE > > holes
uint16_t component
uint16_t polygon
ALTIUM_REGION_KIND kind
ALTIUM_RULE_KIND kind
ALTIUM_CONNECT_STYLE polygonconnectStyle
wxString scope1expr
wxString name
int planeclearanceClearance
int32_t polygonconnectReliefconductorwidth
int pastemaskExpansion
wxString scope2expr
int soldermaskExpansion
int32_t polygonconnectAirgapwidth
uint32_t text_offset_width
VECTOR2I barcode_margin
uint32_t textbox_rect_height
uint16_t component
ALTIUM_TEXT_POSITION textbox_rect_justification
wxString text
uint32_t textbox_rect_width
double rotation
uint32_t margin_border_width
uint32_t height
wxString fontname
bool isJustificationValid
ALTIUM_BARCODE_TYPE barcode_type
ALTIUM_LAYER layer
VECTOR2I position
ALTIUM_TEXT_TYPE fonttype
bool isOffsetBorder
bool barcode_inverted
uint32_t strokewidth
uint32_t width
uint16_t polygon
uint8_t keepoutrestrictions
VECTOR2I start
ALTIUM_LAYER layer
uint16_t component
uint32_t diameter
uint16_t net
VECTOR2I position
int32_t soldermask_expansion_front
bool is_tent_bottom
bool soldermask_expansion_manual
ALTIUM_PAD_MODE viamode
int32_t soldermask_expansion_back
uint32_t diameter_by_layer[32]
ALTIUM_LAYER layer_start
ALTIUM_LAYER layer_end
uint32_t holesize
std::vector< char > decompressedData
Describes an imported layer and how it could be mapped to KiCad Layers.
PCB_LAYER_ID AutoMapLayer
Best guess as to what the equivalent KiCad layer might be.
bool Required
Should we require the layer to be assigned?
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
wxString Name
Imported layer name as displayed in original application.
std::string path
KIBIS_MODEL * model
VECTOR2I center
int radius
VECTOR2I end
int clearance
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
double DEG2RAD(double deg)
Definition trigo.h:166
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:102
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR3< double > VECTOR3D
Definition vector3.h:230
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ NONE
Pads are not covered.
Definition zones.h:49
@ FULL
pads are covered by copper
Definition zones.h:51