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
1348 // The pattern field may also contain a path when Altium stores it with a full library path.
1349 // Extract just the footprint name portion to avoid creating invalid filenames.
1350 wxString fpName = elem.pattern;
1351
1352 if( fpName.Contains( wxT( "\\" ) ) || fpName.Contains( wxT( "/" ) ) )
1353 {
1354 wxFileName fpPath( fpName, wxPATH_WIN );
1355 fpName = fpPath.GetFullName();
1356 }
1357
1358 LIB_ID fpID = AltiumToKiCadLibID( libName.GetName(), fpName );
1359
1360 footprint->SetFPID( fpID );
1361
1362 footprint->SetPosition( elem.position );
1363 footprint->SetOrientationDegrees( elem.rotation );
1364
1365 // KiCad netlisting requires parts to have non-digit + digit annotation.
1366 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1367 wxString reference = elem.sourcedesignator;
1368
1369 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1370 reference.Prepend( wxT( "UNK" ) );
1371
1372 footprint->SetReference( reference );
1373
1374 KIID id( elem.sourceUniqueID );
1375 KIID pathid( elem.sourceHierachicalPath );
1377 path.push_back( pathid );
1378 path.push_back( id );
1379
1380 footprint->SetPath( path );
1381 footprint->SetSheetname( elem.sourceHierachicalPath );
1382 footprint->SetSheetfile( elem.sourceHierachicalPath + wxT( ".kicad_sch" ));
1383
1384 footprint->SetLocked( elem.locked );
1385 footprint->Reference().SetVisible( elem.nameon );
1386 footprint->Value().SetVisible( elem.commenton );
1387 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1388
1389 m_components.emplace_back( footprint.get() );
1390 m_board->Add( footprint.release(), ADD_MODE::APPEND );
1391
1392 componentId++;
1393 }
1394
1395 if( reader.GetRemainingBytes() != 0 )
1396 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1397}
1398
1399
1401double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1402{
1403 while( Angle < aMin )
1404 Angle += 360.0;
1405
1406 while( Angle >= aMax )
1407 Angle -= 360.0;
1408
1409 return Angle;
1410}
1411
1412
1414 FOOTPRINT* aFootprint,
1415 const ACOMPONENTBODY6& aElem )
1416{
1417 if( m_progressReporter )
1418 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1419
1420 if( !aElem.modelIsEmbedded )
1421 return;
1422
1423 auto model = aAltiumPcbFile.GetLibModel( aElem.modelId );
1424
1425 if( !model )
1426 {
1427 if( m_reporter )
1428 {
1429 m_reporter->Report( wxString::Format( wxT( "Model %s not found for footprint %s" ),
1430 aElem.modelId, aFootprint->GetReference() ),
1432 }
1433
1434 return;
1435 }
1436
1437 const VECTOR2I& fpPosition = aFootprint->GetPosition();
1438
1440 file->name = aElem.modelName;
1441
1442 if( file->name.IsEmpty() )
1443 file->name = model->first.name;
1444
1445 // Decompress the model data before assigning
1446 std::vector<char> decompressedData;
1447 wxMemoryInputStream compressedStream( model->second.data(), model->second.size() );
1448 wxZlibInputStream zlibStream( compressedStream );
1449
1450 // Reserve some space, assuming decompressed data is larger -- STEP file
1451 // compression is typically 5:1 using zlib like Altium does
1452 decompressedData.resize( model->second.size() * 6 );
1453 size_t offset = 0;
1454
1455 while( !zlibStream.Eof() )
1456 {
1457 zlibStream.Read( decompressedData.data() + offset, decompressedData.size() - offset );
1458 size_t bytesRead = zlibStream.LastRead();
1459
1460 if( !bytesRead )
1461 break;
1462
1463 offset += bytesRead;
1464
1465 if( offset >= decompressedData.size() )
1466 decompressedData.resize( 2 * decompressedData.size() ); // Resizing is expensive, avoid if we can
1467 }
1468
1469 decompressedData.resize( offset );
1470
1471 file->decompressedData = std::move( decompressedData );
1473
1475 aFootprint->GetEmbeddedFiles()->AddFile( file );
1476
1477 FP_3DMODEL modelSettings;
1478
1479 modelSettings.m_Filename = aFootprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1480
1481 modelSettings.m_Offset.x = pcbIUScale.IUTomm( (int) aElem.modelPosition.x );
1482 modelSettings.m_Offset.y = -pcbIUScale.IUTomm( (int) aElem.modelPosition.y );
1483 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) aElem.modelPosition.z );
1484
1485 EDA_ANGLE orientation = aFootprint->GetOrientation();
1486
1487 if( aFootprint->IsFlipped() )
1488 {
1489 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1490 orientation = -orientation;
1491 }
1492
1493 VECTOR3D modelRotation( aElem.modelRotation );
1494
1495 if( ( aElem.body_projection == 1 ) != aFootprint->IsFlipped() )
1496 {
1497 modelRotation.x += 180;
1498 modelRotation.z = -modelRotation.z;
1499
1500 modelSettings.m_Offset.z = -DEFAULT_BOARD_THICKNESS_MM - modelSettings.m_Offset.z;
1501 }
1502
1503 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1504
1505 modelSettings.m_Rotation.x = normalizeAngleDegrees( -modelRotation.x, -180, 180 );
1506 modelSettings.m_Rotation.y = normalizeAngleDegrees( -modelRotation.y, -180, 180 );
1507 modelSettings.m_Rotation.z = normalizeAngleDegrees( -modelRotation.z + aElem.rotation
1508 + orientation.AsDegrees(),
1509 -180, 180 );
1510 modelSettings.m_Opacity = aElem.body_opacity_3d;
1511
1512 aFootprint->Models().push_back( modelSettings );
1513}
1514
1515
1517 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1518{
1519 if( m_progressReporter )
1520 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1521
1522 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1523
1524 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1525 {
1526 checkpoint();
1527 ACOMPONENTBODY6 elem( reader );
1528
1529 static const bool skipComponentBodies = ADVANCED_CFG::GetCfg().m_ImportSkipComponentBodies;
1530
1531 if( skipComponentBodies )
1532 continue;
1533
1534 if( elem.component == ALTIUM_COMPONENT_NONE )
1535 continue; // TODO: we do not support components for the board yet
1536
1537 if( m_components.size() <= elem.component )
1538 {
1539 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1540 "component id %d of %zu existing components" ),
1541 elem.component,
1542 m_components.size() ) );
1543 }
1544
1545 if( !elem.modelIsEmbedded )
1546 continue;
1547
1548 auto modelTuple = m_EmbeddedModels.find( elem.modelId );
1549
1550 if( modelTuple == m_EmbeddedModels.end() )
1551 {
1552 if( m_reporter )
1553 {
1554 wxString msg;
1555 msg.Printf( wxT( "ComponentsBodies6 stream tries to access model id %s which does "
1556 "not exist" ), elem.modelId );
1557 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1558 }
1559
1560 continue;
1561 }
1562
1563 const ALTIUM_EMBEDDED_MODEL_DATA& modelData = modelTuple->second;
1564 FOOTPRINT* footprint = m_components.at( elem.component );
1565
1567 file->name = modelData.m_modelname;
1568
1569 wxMemoryInputStream compressedStream( modelData.m_data.data(), modelData.m_data.size() );
1570 wxZlibInputStream zlibStream( compressedStream );
1571 wxMemoryOutputStream decompressedStream;
1572
1573 zlibStream.Read( decompressedStream );
1574 file->decompressedData.resize( decompressedStream.GetSize() );
1575 decompressedStream.CopyTo( file->decompressedData.data(), file->decompressedData.size() );
1576
1578 footprint->GetEmbeddedFiles()->AddFile( file );
1579
1580 FP_3DMODEL modelSettings;
1581
1582 modelSettings.m_Filename = footprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1583 VECTOR2I fpPosition = footprint->GetPosition();
1584
1585 modelSettings.m_Offset.x =
1586 pcbIUScale.IUTomm( KiROUND( elem.modelPosition.x - fpPosition.x ) );
1587 modelSettings.m_Offset.y =
1588 -pcbIUScale.IUTomm( KiROUND( elem.modelPosition.y - fpPosition.y ) );
1589 modelSettings.m_Offset.z = pcbIUScale.IUTomm( KiROUND( elem.modelPosition.z ) );
1590
1591 EDA_ANGLE orientation = footprint->GetOrientation();
1592
1593 if( footprint->IsFlipped() )
1594 {
1595 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1596 orientation = -orientation;
1597 }
1598
1599 if( ( elem.body_projection == 1 ) != footprint->IsFlipped() )
1600 {
1601 elem.modelRotation.x += 180;
1602 elem.modelRotation.z = -elem.modelRotation.z;
1603
1604 modelSettings.m_Offset.z =
1605 -pcbIUScale.IUTomm( m_board->GetDesignSettings().GetBoardThickness() )
1606 - modelSettings.m_Offset.z;
1607 }
1608
1609 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1610
1611 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1612 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1613 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z + elem.rotation
1614 + orientation.AsDegrees(),
1615 -180, 180 );
1616
1617 modelSettings.m_Opacity = elem.body_opacity_3d;
1618
1619 footprint->Models().push_back( modelSettings );
1620 }
1621
1622 if( reader.GetRemainingBytes() != 0 )
1623 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1624}
1625
1626
1628{
1629 if( aElem.referencePoint.size() != 2 )
1630 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1631
1632 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1633
1634 if( klayer == UNDEFINED_LAYER )
1635 {
1636 if( m_reporter )
1637 {
1638 m_reporter->Report( wxString::Format(
1639 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1640 "It has been moved to KiCad layer Eco1_User." ), aElem.layer ),
1642 }
1643
1644 klayer = Eco1_User;
1645 }
1646
1647 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1648 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1649
1650 std::unique_ptr<PCB_DIM_ALIGNED> dimension = std::make_unique<PCB_DIM_ALIGNED>( m_board, PCB_DIM_ALIGNED_T );
1651
1652 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1653 dimension->SetLayer( klayer );
1654 dimension->SetStart( referencePoint0 );
1655
1656 if( referencePoint0 != aElem.xy1 )
1657 {
1667 VECTOR2I direction = aElem.xy1 - referencePoint0;
1668 VECTOR2I referenceDiff = referencePoint1 - referencePoint0;
1669 VECTOR2I directionNormalVector = direction.Perpendicular();
1670 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1671 SEG segm2( referencePoint1, referencePoint1 + direction );
1672 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1673
1674 if( !intersection )
1675 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1676
1677 dimension->SetEnd( *intersection );
1678
1679 int height = direction.EuclideanNorm();
1680
1681 if( direction.Cross( referenceDiff ) > 0 )
1682 height = -height;
1683
1684 dimension->SetHeight( height );
1685 }
1686 else
1687 {
1688 dimension->SetEnd( referencePoint1 );
1689 }
1690
1691 dimension->SetLineThickness( aElem.linewidth );
1692
1693 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
1694 dimension->SetPrefix( aElem.textprefix );
1695
1696
1697 int dist = ( dimension->GetEnd() - dimension->GetStart() ).EuclideanNorm();
1698
1699 if( dist < 3 * dimension->GetArrowLength() )
1700 dimension->SetArrowDirection( DIM_ARROW_DIRECTION::INWARD );
1701
1702 // Suffix normally (but not always) holds the units
1703 wxRegEx units( wxS( "(mm)|(in)|(mils)|(thou)|(')|(\")" ), wxRE_ADVANCED );
1704
1705 if( units.Matches( aElem.textsuffix ) )
1706 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX );
1707 else
1708 dimension->SetSuffix( aElem.textsuffix );
1709
1710 dimension->SetTextThickness( aElem.textlinewidth );
1711 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1712 dimension->SetItalic( aElem.textitalic );
1713
1714#if 0 // we don't currently support bold; map to thicker text
1715 dimension->Text().SetBold( aElem.textbold );
1716#else
1717 if( aElem.textbold )
1718 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1719#endif
1720
1721 switch( aElem.textunit )
1722 {
1723 case ALTIUM_UNIT::INCH: dimension->SetUnits( EDA_UNITS::INCH ); break;
1724 case ALTIUM_UNIT::MILS: dimension->SetUnits( EDA_UNITS::MILS ); break;
1725 case ALTIUM_UNIT::MM: dimension->SetUnits( EDA_UNITS::MM ); break;
1726 case ALTIUM_UNIT::CM: dimension->SetUnits( EDA_UNITS::MM ); break;
1727 default: break;
1728 }
1729
1730 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1731}
1732
1733
1735{
1736 if( aElem.referencePoint.size() < 2 )
1737 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1738
1739 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1740
1741 if( klayer == UNDEFINED_LAYER )
1742 {
1743 if( m_reporter )
1744 {
1745 m_reporter->Report( wxString::Format(
1746 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1747 "It has been moved to KiCad layer Eco1_User." ),
1748 aElem.layer ), RPT_SEVERITY_INFO );
1749 }
1750
1751 klayer = Eco1_User;
1752 }
1753
1754 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1755 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1756
1757 std::unique_ptr<PCB_DIM_RADIAL> dimension = std::make_unique<PCB_DIM_RADIAL>( m_board );
1758
1759 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1760 dimension->SetLayer( klayer );
1761 dimension->SetStart( referencePoint0 );
1762 dimension->SetEnd( aElem.xy1 );
1763 dimension->SetLineThickness( aElem.linewidth );
1764 dimension->SetKeepTextAligned( false );
1765
1766 dimension->SetPrefix( aElem.textprefix );
1767
1768 // Suffix normally holds the units
1769 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1771
1772 switch( aElem.textunit )
1773 {
1774 case ALTIUM_UNIT::INCH: dimension->SetUnits( EDA_UNITS::INCH ); break;
1775 case ALTIUM_UNIT::MILS: dimension->SetUnits( EDA_UNITS::MILS ); break;
1776 case ALTIUM_UNIT::MM: dimension->SetUnits( EDA_UNITS::MM ); break;
1777 case ALTIUM_UNIT::CM: dimension->SetUnits( EDA_UNITS::MM ); break;
1778 default: break;
1779 }
1780
1781 if( aElem.textPoint.empty() )
1782 {
1783 if( m_reporter )
1784 {
1785 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1787 }
1788
1789 return;
1790 }
1791
1792 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1793 dimension->SetTextThickness( aElem.textlinewidth );
1794 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1795 dimension->SetItalic( aElem.textitalic );
1796
1797#if 0 // we don't currently support bold; map to thicker text
1798 dimension->SetBold( aElem.textbold );
1799#else
1800 if( aElem.textbold )
1801 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1802#endif
1803
1804 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1805 // close.
1806 dimension->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1807 dimension->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1808
1809 int yAdjust = dimension->GetTextBox( nullptr ).GetCenter().y - dimension->GetTextPos().y;
1810 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1811 dimension->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
1812
1813 m_radialDimensions.push_back( dimension.get() );
1814 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1815}
1816
1817
1819{
1820 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1821
1822 if( klayer == UNDEFINED_LAYER )
1823 {
1824 if( m_reporter )
1825 {
1826 wxString msg;
1827 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1828 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1829 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1830 }
1831
1832 klayer = Eco1_User;
1833 }
1834
1835 if( !aElem.referencePoint.empty() )
1836 {
1837 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1838
1839 // line
1840 VECTOR2I last = referencePoint0;
1841 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1842 {
1843 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1844
1845 shape->SetLayer( klayer );
1846 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1847 shape->SetStart( last );
1848 shape->SetEnd( aElem.referencePoint.at( i ) );
1849 last = aElem.referencePoint.at( i );
1850
1851 m_board->Add( shape.release(), ADD_MODE::APPEND );
1852 }
1853
1854 // arrow
1855 if( aElem.referencePoint.size() >= 2 )
1856 {
1857 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1858
1859 if( dirVec.x != 0 || dirVec.y != 0 )
1860 {
1861 double scaling = (double) dirVec.EuclideanNorm() / aElem.arrowsize;
1862 VECTOR2I arrVec = KiROUND( dirVec.x / scaling, dirVec.y / scaling );
1863 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1864
1865 {
1866 std::unique_ptr<PCB_SHAPE> shape1 = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1867
1868 shape1->SetLayer( klayer );
1869 shape1->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1870 shape1->SetStart( referencePoint0 );
1871 shape1->SetEnd( referencePoint0 + arrVec );
1872
1873 m_board->Add( shape1.release(), ADD_MODE::APPEND );
1874 }
1875
1876 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1877
1878 {
1879 std::unique_ptr<PCB_SHAPE> shape2 = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1880
1881 shape2->SetLayer( klayer );
1882 shape2->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1883 shape2->SetStart( referencePoint0 );
1884 shape2->SetEnd( referencePoint0 + arrVec );
1885
1886 m_board->Add( shape2.release(), ADD_MODE::APPEND );
1887 }
1888 }
1889 }
1890 }
1891
1892 if( aElem.textPoint.empty() )
1893 {
1894 if( m_reporter )
1895 {
1896 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1898 }
1899
1900 return;
1901 }
1902
1903 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
1904
1905 text->SetText( aElem.textformat );
1906 text->SetPosition( aElem.textPoint.at( 0 ) );
1907 text->SetLayer( klayer );
1908 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1909 text->SetTextThickness( aElem.textlinewidth );
1910 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1911 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1912
1913 m_board->Add( text.release(), ADD_MODE::APPEND );
1914}
1915
1916
1918{
1919 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1920
1921 if( klayer == UNDEFINED_LAYER )
1922 {
1923 if( m_reporter )
1924 {
1925 wxString msg;
1926 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1927 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1928 m_reporter->Report( msg, RPT_SEVERITY_INFO );
1929 }
1930
1931 klayer = Eco1_User;
1932 }
1933
1934 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1935 {
1936 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1937
1938 shape->SetLayer( klayer );
1939 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1940 shape->SetStart( aElem.referencePoint.at( i ) );
1941 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1942
1943 m_board->Add( shape.release(), ADD_MODE::APPEND );
1944 }
1945}
1946
1947
1949{
1950 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1951
1952 if( klayer == UNDEFINED_LAYER )
1953 {
1954 if( m_reporter )
1955 {
1956 wxString msg;
1957 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1958 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1959 m_reporter->Report( msg, RPT_SEVERITY_INFO );
1960 }
1961
1962 klayer = Eco1_User;
1963 }
1964
1965 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1966 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1967
1968 std::unique_ptr<PCB_DIM_CENTER> dimension = std::make_unique<PCB_DIM_CENTER>( m_board );
1969
1970 dimension->SetLayer( klayer );
1971 dimension->SetLineThickness( aElem.linewidth );
1972 dimension->SetStart( aElem.xy1 );
1973 dimension->SetEnd( aElem.xy1 + vec );
1974
1975 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1976}
1977
1978
1980 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1981{
1982 if( m_progressReporter )
1983 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1984
1985 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1986
1987 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1988 {
1989 checkpoint();
1990 ADIMENSION6 elem( reader );
1991
1992 switch( elem.kind )
1993 {
1996 break;
1998 if( m_reporter )
1999 {
2000 m_reporter->Report( wxString::Format( _( "Ignored Angular dimension (not yet supported)." ) ),
2002 }
2003 break;
2006 break;
2009 break;
2011 if( m_reporter )
2012 {
2013 m_reporter->Report( wxString::Format( _( "Ignored Datum dimension (not yet supported)." ) ),
2015 }
2016 // HelperParseDimensions6Datum( elem );
2017 break;
2019 if( m_reporter )
2020 {
2021 m_reporter->Report( wxString::Format( _( "Ignored Baseline dimension (not yet supported)." ) ),
2023 }
2024 break;
2027 break;
2029 if( m_reporter )
2030 {
2031 m_reporter->Report( wxString::Format( _( "Ignored Linear dimension (not yet supported)." ) ),
2033 }
2034 break;
2036 if( m_reporter )
2037 {
2038 m_reporter->Report( wxString::Format( _( "Ignored Radial dimension (not yet supported)." ) ),
2040 }
2041 break;
2042 default:
2043 if( m_reporter )
2044 {
2045 wxString msg;
2046 msg.Printf( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
2047 m_reporter->Report( msg, RPT_SEVERITY_INFO );
2048 }
2049 break;
2050 }
2051 }
2052
2053 if( reader.GetRemainingBytes() != 0 )
2054 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
2055}
2056
2057
2059 const CFB::COMPOUND_FILE_ENTRY* aEntry,
2060 const std::vector<std::string>& aRootDir )
2061{
2062 if( m_progressReporter )
2063 m_progressReporter->Report( _( "Loading 3D models..." ) );
2064
2065 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2066
2067 if( reader.GetRemainingBytes() == 0 )
2068 return;
2069
2070 int idx = 0;
2071 wxString invalidChars = wxFileName::GetForbiddenChars();
2072
2073 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2074 {
2075 checkpoint();
2076 AMODEL elem( reader );
2077
2078 std::vector<std::string> stepPath = aRootDir;
2079 stepPath.emplace_back( std::to_string( idx ) );
2080
2081 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii()
2082 && wxString::npos == elem.name.find_first_of( invalidChars );
2083 wxString storageName = validName ? elem.name : wxString::Format( wxT( "model_%d" ), idx );
2084
2085 idx++;
2086
2087 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
2088
2089 if( stepEntry == nullptr )
2090 {
2091 if( m_reporter )
2092 {
2093 wxString msg;
2094 msg.Printf( _( "File not found: '%s'. 3D-model not imported." ), FormatPath( stepPath ) );
2095 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2096 }
2097
2098 continue;
2099 }
2100
2101 size_t stepSize = static_cast<size_t>( stepEntry->size );
2102 std::vector<char> stepContent( stepSize );
2103
2104 // read file into buffer
2105 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
2106 stepSize );
2107
2108 m_EmbeddedModels.insert( std::make_pair(
2109 elem.id, ALTIUM_EMBEDDED_MODEL_DATA( storageName, elem.rotation, elem.z_offset,
2110 std::move( stepContent ) ) ) );
2111 }
2112
2113 // Append _<index> to duplicate filenames
2114 std::map<wxString, std::vector<wxString>> nameIdMap;
2115
2116 for( auto& [id, data] : m_EmbeddedModels )
2117 nameIdMap[data.m_modelname].push_back( id );
2118
2119 for( auto& [name, ids] : nameIdMap )
2120 {
2121 for( size_t i = 1; i < ids.size(); i++ )
2122 {
2123 const wxString& id = ids[i];
2124
2125 auto modelTuple = m_EmbeddedModels.find( id );
2126
2127 if( modelTuple == m_EmbeddedModels.end() )
2128 continue;
2129
2130 wxString modelName = modelTuple->second.m_modelname;
2131
2132 if( modelName.Contains( "." ) )
2133 {
2134 wxString ext;
2135 wxString baseName = modelName.BeforeLast( '.', &ext );
2136
2137 modelTuple->second.m_modelname = baseName + '_' + std::to_string( i ) + '.' + ext;
2138 }
2139 else
2140 {
2141 modelTuple->second.m_modelname = modelName + '_' + std::to_string( i );
2142 }
2143 }
2144 }
2145
2146 if( reader.GetRemainingBytes() != 0 )
2147 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
2148}
2149
2150
2152 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2153{
2154 if( m_progressReporter )
2155 m_progressReporter->Report( _( "Loading nets..." ) );
2156
2157 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2158
2159 wxASSERT( m_altiumToKicadNetcodes.empty() );
2160
2161 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2162 {
2163 checkpoint();
2164 ANET6 elem( reader );
2165
2166 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, -1 );
2167 m_board->Add( netInfo, ADD_MODE::APPEND );
2168
2169 // needs to be called after m_board->Add() as assign us the NetCode
2170 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
2171 }
2172
2173 if( reader.GetRemainingBytes() != 0 )
2174 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
2175}
2176
2178 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2179{
2180 if( m_progressReporter )
2181 m_progressReporter->Report( _( "Loading polygons..." ) );
2182
2183 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2184
2185 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2186 {
2187 checkpoint();
2188 APOLYGON6 elem( reader );
2189
2190 SHAPE_LINE_CHAIN linechain;
2192
2193 if( linechain.PointCount() < 3 )
2194 {
2195 // We have found multiple Altium files with polygon records containing nothing but two
2196 // coincident vertices. These polygons do not appear when opening the file in Altium.
2197 // https://gitlab.com/kicad/code/kicad/-/issues/8183
2198 // Also, polygons with less than 3 points are not supported in KiCad.
2199 //
2200 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
2201 // "points are required." ),
2202 // linechain.PointCount(),
2203 // elem.vertices.size() );
2204
2205 m_polygons.emplace_back( nullptr );
2206 continue;
2207 }
2208
2209 SHAPE_POLY_SET outline( linechain );
2210
2212 {
2213 // Altium "Hatched" or "None" polygon outlines have thickness, convert it to KiCad's representation.
2215 ARC_HIGH_DEF, true );
2216 }
2217
2218 if( outline.OutlineCount() != 1 && m_reporter )
2219 {
2220 wxString msg;
2221 msg.Printf( _( "Polygon outline count is %d, expected 1." ), outline.OutlineCount() );
2222
2223 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2224 }
2225
2226 if( outline.OutlineCount() == 0 )
2227 continue;
2228
2229 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>(m_board);
2230
2231 // Be sure to set the zone layer before setting the net code
2232 // so that we know that this is a copper zone and so needs a valid net code.
2233 HelperSetZoneLayers( *zone, elem.layer );
2234 zone->SetNetCode( GetNetCode( elem.net ) );
2235 zone->SetPosition( elem.vertices.at( 0 ).position );
2236 zone->SetLocked( elem.locked );
2237 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
2238 zone->Outline()->AddOutline( outline.Outline( 0 ) );
2239
2240 if( elem.pourindex > m_highest_pour_index )
2242
2243 const ARULE6* planeClearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
2244 const ARULE6* zoneClearanceRule = GetRule( ALTIUM_RULE_KIND::CLEARANCE, wxT( "PolygonClearance" ) );
2245 int planeLayers = 0;
2246 int signalLayers = 0;
2247 int clearance = 0;
2248
2249 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
2250 {
2251 LAYER_T layerType = m_board->GetLayerType( layer );
2252
2253 if( layerType == LT_POWER || layerType == LT_MIXED )
2254 planeLayers++;
2255
2256 if( layerType == LT_SIGNAL || layerType == LT_MIXED )
2257 signalLayers++;
2258 }
2259
2260 if( planeLayers > 0 && planeClearanceRule )
2261 clearance = std::max( clearance, planeClearanceRule->planeclearanceClearance );
2262
2263 if( signalLayers > 0 && zoneClearanceRule )
2264 clearance = std::max( clearance, zoneClearanceRule->clearanceGap );
2265
2266 if( clearance > 0 )
2267 zone->SetLocalClearance( clearance );
2268
2269 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
2270
2271 if( polygonConnectRule != nullptr )
2272 {
2273 switch( polygonConnectRule->polygonconnectStyle )
2274 {
2276 zone->SetPadConnection( ZONE_CONNECTION::FULL );
2277 break;
2278
2280 zone->SetPadConnection( ZONE_CONNECTION::NONE );
2281 break;
2282
2283 default:
2285 zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
2286 break;
2287 }
2288
2289 // TODO: correct variables?
2290 zone->SetThermalReliefSpokeWidth(
2291 polygonConnectRule->polygonconnectReliefconductorwidth );
2292 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
2293
2294 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
2295 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
2296 }
2297
2298 if( IsAltiumLayerAPlane( elem.layer ) )
2299 {
2300 // outer zone will be set to priority 0 later.
2301 zone->SetAssignedPriority( 1 );
2302
2303 // check if this is the outer zone by simply comparing the BBOX
2304 const auto& outer_plane = m_outer_plane.find( elem.layer );
2305 if( outer_plane == m_outer_plane.end()
2306 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
2307 {
2308 m_outer_plane[elem.layer] = zone.get();
2309 }
2310 }
2311
2314 {
2315 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
2316 zone->SetHatchThickness( elem.trackwidth );
2317
2319 {
2320 // use a small hack to get us only an outline (hopefully)
2321 const BOX2I& bbox = zone->GetBoundingBox();
2322 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
2323 }
2324 else
2325 {
2326 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
2327 }
2328
2330 zone->SetHatchOrientation( ANGLE_45 );
2331 }
2332
2333 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2335
2336 m_polygons.emplace_back( zone.get() );
2337 m_board->Add( zone.release(), ADD_MODE::APPEND );
2338 }
2339
2340 if( reader.GetRemainingBytes() != 0 )
2341 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
2342}
2343
2345 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2346{
2347 if( m_progressReporter )
2348 m_progressReporter->Report( _( "Loading rules..." ) );
2349
2350 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2351
2352 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2353 {
2354 checkpoint();
2355 ARULE6 elem( reader );
2356
2357 m_rules[elem.kind].emplace_back( elem );
2358 }
2359
2360 // sort rules by priority
2361 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
2362 {
2363 std::sort( val.second.begin(), val.second.end(),
2364 []( const ARULE6& lhs, const ARULE6& rhs )
2365 {
2366 return lhs.priority < rhs.priority;
2367 } );
2368 }
2369
2370 const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::CLEARANCE );
2371 const ARULE6* trackWidthRule = GetRuleDefault( ALTIUM_RULE_KIND::WIDTH );
2372 const ARULE6* routingViasRule = GetRuleDefault( ALTIUM_RULE_KIND::ROUTING_VIAS );
2373 const ARULE6* holeSizeRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_SIZE );
2375
2376 if( clearanceRule )
2377 m_board->GetDesignSettings().m_MinClearance = clearanceRule->clearanceGap;
2378
2379 if( trackWidthRule )
2380 {
2381 m_board->GetDesignSettings().m_TrackMinWidth = trackWidthRule->minLimit;
2382 // TODO: construct a custom rule for preferredWidth and maxLimit values
2383 }
2384
2385 if( routingViasRule )
2386 {
2387 m_board->GetDesignSettings().m_ViasMinSize = routingViasRule->minWidth;
2388 m_board->GetDesignSettings().m_MinThroughDrill = routingViasRule->minHoleWidth;
2389 }
2390
2391 if( holeSizeRule )
2392 {
2393 // TODO: construct a custom rule for minLimit / maxLimit values
2394 }
2395
2396 if( holeToHoleRule )
2397 m_board->GetDesignSettings().m_HoleToHoleMin = holeToHoleRule->clearanceGap;
2398
2401
2402 if( soldermaskRule )
2403 m_board->GetDesignSettings().m_SolderMaskExpansion = soldermaskRule->soldermaskExpansion;
2404
2405 if( pastemaskRule )
2406 m_board->GetDesignSettings().m_SolderPasteMargin = pastemaskRule->pastemaskExpansion;
2407
2408 if( reader.GetRemainingBytes() != 0 )
2409 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
2410}
2411
2413 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2414{
2415 if( m_progressReporter )
2416 m_progressReporter->Report( _( "Loading board regions..." ) );
2417
2418 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2419
2420 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2421 {
2422 checkpoint();
2423 AREGION6 elem( reader, false );
2424
2425 // TODO: implement?
2426 }
2427
2428 if( reader.GetRemainingBytes() != 0 )
2429 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
2430}
2431
2433 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2434{
2435 if( m_progressReporter )
2436 m_progressReporter->Report( _( "Loading polygons..." ) );
2437
2438 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2439
2440 /* TODO: use Header section of file */
2441 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2442 {
2443 checkpoint();
2444 AREGION6 elem( reader, true );
2445
2448 {
2449 // TODO: implement all different types for footprints
2451 }
2452 else
2453 {
2454 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2455 ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
2456 }
2457 }
2458
2459 if( reader.GetRemainingBytes() != 0 )
2460 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
2461}
2462
2463
2465{
2467 {
2469 }
2470 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2471 {
2472 SHAPE_LINE_CHAIN linechain;
2474
2475 if( linechain.PointCount() < 3 )
2476 {
2477 // We have found multiple Altium files with polygon records containing nothing but
2478 // two coincident vertices. These polygons do not appear when opening the file in
2479 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2480 // Also, polygons with less than 3 points are not supported in KiCad.
2481 return;
2482 }
2483
2484 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2485
2486 zone->SetIsRuleArea( true );
2487
2488 if( aElem.is_keepout )
2489 {
2491 }
2492 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2493 {
2494 zone->SetDoNotAllowZoneFills( true );
2495 zone->SetDoNotAllowVias( false );
2496 zone->SetDoNotAllowTracks( false );
2497 zone->SetDoNotAllowPads( false );
2498 zone->SetDoNotAllowFootprints( false );
2499 }
2500
2501 zone->SetPosition( aElem.outline.at( 0 ).position );
2502 zone->Outline()->AddOutline( linechain );
2503
2504 HelperSetZoneLayers( *zone, aElem.layer );
2505
2506 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2508
2509 m_board->Add( zone.release(), ADD_MODE::APPEND );
2510 }
2511 else if( aElem.is_teardrop )
2512 {
2513 SHAPE_LINE_CHAIN linechain;
2515
2516 if( linechain.PointCount() < 3 )
2517 {
2518 // Polygons with less than 3 points are not supported in KiCad.
2519 return;
2520 }
2521
2522 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2523
2524 zone->SetPosition( aElem.outline.at( 0 ).position );
2525 zone->Outline()->AddOutline( linechain );
2526
2527 HelperSetZoneLayers( *zone, aElem.layer );
2528 zone->SetNetCode( GetNetCode( aElem.net ) );
2529 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_UNSPECIFIED );
2530 zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER );
2531
2532 SHAPE_POLY_SET fill;
2533 fill.Append( linechain );
2534 fill.Fracture();
2535
2536 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2537 zone->SetFilledPolysList( klayer, fill );
2538
2539 zone->SetIsFilled( true );
2540 zone->SetNeedRefill( false );
2541
2542 m_board->Add( zone.release(), ADD_MODE::APPEND );
2543 }
2544 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2545 {
2546 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2547
2548 if( klayer == UNDEFINED_LAYER )
2549 {
2550 if( m_reporter )
2551 {
2552 wxString msg;
2553 msg.Printf( _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
2554 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
2555 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2556 }
2557
2558 klayer = Eco1_User;
2559 }
2560
2561 SHAPE_LINE_CHAIN linechain;
2563
2564 if( linechain.PointCount() < 3 )
2565 {
2566 // We have found multiple Altium files with polygon records containing nothing but
2567 // two coincident vertices. These polygons do not appear when opening the file in
2568 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2569 // Also, polygons with less than 3 points are not supported in KiCad.
2570 return;
2571 }
2572
2573 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2574
2575 shape->SetPolyShape( linechain );
2576 shape->SetFilled( false );
2577 shape->SetLayer( klayer );
2578 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2579
2580 m_board->Add( shape.release(), ADD_MODE::APPEND );
2581 }
2582 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2583 {
2584 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2585 {
2586 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2588 }
2589 }
2590 else if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2591 {
2593 }
2594 else
2595 {
2596 if( m_reporter )
2597 {
2598 wxString msg;
2599 msg.Printf( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2600 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2601 }
2602 }
2603}
2604
2605
2607 const AREGION6& aElem,
2608 const int aPrimitiveIndex )
2609{
2610 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2611 {
2612 SHAPE_LINE_CHAIN linechain;
2614
2615 if( linechain.PointCount() < 3 )
2616 {
2617 // We have found multiple Altium files with polygon records containing nothing but
2618 // two coincident vertices. These polygons do not appear when opening the file in
2619 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2620 // Also, polygons with less than 3 points are not supported in KiCad.
2621 return;
2622 }
2623
2624 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
2625
2626 zone->SetIsRuleArea( true );
2627
2628 if( aElem.is_keepout )
2629 {
2631 }
2632 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2633 {
2634 zone->SetDoNotAllowZoneFills( true );
2635 zone->SetDoNotAllowVias( false );
2636 zone->SetDoNotAllowTracks( false );
2637 zone->SetDoNotAllowPads( false );
2638 zone->SetDoNotAllowFootprints( false );
2639 }
2640
2641 zone->SetPosition( aElem.outline.at( 0 ).position );
2642 zone->Outline()->AddOutline( linechain );
2643
2644 HelperSetZoneLayers( *zone, aElem.layer );
2645
2646 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2648
2649 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
2650 }
2651 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2652 {
2653 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2654 {
2655 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2656 {
2657 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
2658 aPrimitiveIndex );
2659 }
2660 }
2661 }
2664 {
2666 ? Edge_Cuts
2667 : GetKicadLayer( aElem.layer );
2668
2669 if( klayer == UNDEFINED_LAYER )
2670 {
2671 if( !m_footprintName.IsEmpty() )
2672 {
2673 if( m_reporter )
2674 {
2675 wxString msg;
2676 msg.Printf( _( "Loading library '%s':\n"
2677 "Footprint %s contains a dashed outline on Altium layer (%d) with "
2678 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2679 m_library,
2681 aElem.layer );
2682 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2683 }
2684 }
2685 else
2686 {
2687 if( m_reporter )
2688 {
2689 wxString msg;
2690 msg.Printf( _( "Footprint %s contains a dashed outline on Altium layer (%d) with "
2691 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2692 aFootprint->GetReference(),
2693 aElem.layer );
2694 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2695 }
2696 }
2697
2698 klayer = Eco1_User;
2699 }
2700
2701 SHAPE_LINE_CHAIN linechain;
2703
2704 if( linechain.PointCount() < 3 )
2705 {
2706 // We have found multiple Altium files with polygon records containing nothing but
2707 // two coincident vertices. These polygons do not appear when opening the file in
2708 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2709 // Also, polygons with less than 3 points are not supported in KiCad.
2710 return;
2711 }
2712
2713 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2714
2715 shape->SetPolyShape( linechain );
2716 shape->SetFilled( false );
2717 shape->SetLayer( klayer );
2718
2720 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2721 else
2722 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::SOLID ) );
2723
2724 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2725 }
2726 else
2727 {
2728 if( !m_footprintName.IsEmpty() )
2729 {
2730 if( m_reporter )
2731 {
2732 wxString msg;
2733 msg.Printf( _( "Error loading library '%s':\n"
2734 "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2735 m_library,
2737 aElem.kind );
2738 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2739 }
2740 }
2741 else
2742 {
2743 if( m_reporter )
2744 {
2745 wxString msg;
2746 msg.Printf( _( "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2747 aFootprint->GetReference(),
2748 aElem.kind );
2749 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2750 }
2751 }
2752 }
2753}
2754
2755
2757 PCB_LAYER_ID aLayer )
2758{
2759 SHAPE_LINE_CHAIN linechain;
2761
2762 if( linechain.PointCount() < 3 )
2763 {
2764 // We have found multiple Altium files with polygon records containing nothing
2765 // but two coincident vertices. These polygons do not appear when opening the
2766 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2767 // Also, polygons with less than 3 points are not supported in KiCad.
2768 return;
2769 }
2770
2771 SHAPE_POLY_SET polySet;
2772 polySet.AddOutline( linechain );
2773
2774 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2775 {
2776 SHAPE_LINE_CHAIN hole_linechain;
2777 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2778
2779 if( hole_linechain.PointCount() < 3 )
2780 continue;
2781
2782 polySet.AddHole( hole_linechain );
2783 }
2784
2785 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2786
2787 shape->SetPolyShape( polySet );
2788 shape->SetFilled( true );
2789 shape->SetLayer( aLayer );
2790 shape->SetStroke( STROKE_PARAMS( 0 ) );
2791
2792 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2793 {
2794 shape->SetNetCode( GetNetCode( aElem.net ) );
2795 }
2796
2797 m_board->Add( shape.release(), ADD_MODE::APPEND );
2798}
2799
2800
2802 const AREGION6& aElem,
2803 PCB_LAYER_ID aLayer,
2804 const int aPrimitiveIndex )
2805{
2806 SHAPE_LINE_CHAIN linechain;
2808
2809 if( linechain.PointCount() < 3 )
2810 {
2811 // We have found multiple Altium files with polygon records containing nothing
2812 // but two coincident vertices. These polygons do not appear when opening the
2813 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2814 // Also, polygons with less than 3 points are not supported in KiCad.
2815 return;
2816 }
2817
2818 SHAPE_POLY_SET polySet;
2819 polySet.AddOutline( linechain );
2820
2821 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2822 {
2823 SHAPE_LINE_CHAIN hole_linechain;
2824 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2825
2826 if( hole_linechain.PointCount() < 3 )
2827 continue;
2828
2829 polySet.AddHole( hole_linechain );
2830 }
2831
2832 if( aLayer == F_Cu || aLayer == B_Cu )
2833 {
2834 // TODO(JE) padstacks -- not sure what should happen here yet
2835 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
2836
2837 LSET padLayers;
2838 padLayers.set( aLayer );
2839
2840 pad->SetAttribute( PAD_ATTRIB::SMD );
2842 pad->SetThermalSpokeAngle( ANGLE_90 );
2843
2844 int anchorSize = 1;
2845 VECTOR2I anchorPos = linechain.CPoint( 0 );
2846
2847 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
2848 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
2849 pad->SetPosition( anchorPos );
2850
2851 SHAPE_POLY_SET shapePolys = polySet;
2852 shapePolys.Move( -anchorPos );
2853 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, shapePolys, 0, true );
2854
2856 auto it = map.find( aPrimitiveIndex );
2857
2858 if( it != map.end() )
2859 {
2860 const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
2861
2862 if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2863 {
2864 pad->SetLocalSolderPasteMargin( info.pastemaskexpansionmanual );
2865 }
2866
2867 if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2868 {
2869 pad->SetLocalSolderMaskMargin( info.soldermaskexpansionmanual );
2870 }
2871
2872 if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
2873 padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
2874
2875 if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
2876 padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
2877 }
2878
2879 pad->SetLayerSet( padLayers );
2880
2881 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
2882 }
2883 else
2884 {
2885 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2886
2887 shape->SetPolyShape( polySet );
2888 shape->SetFilled( true );
2889 shape->SetLayer( aLayer );
2890 shape->SetStroke( STROKE_PARAMS( 0 ) );
2891
2892 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2893 }
2894}
2895
2896
2898 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2899{
2900 if( m_progressReporter )
2901 m_progressReporter->Report( _( "Loading zone fills..." ) );
2902
2903 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2904
2905 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2906 {
2907 checkpoint();
2908 AREGION6 elem( reader, false );
2909
2910 if( elem.polygon != ALTIUM_POLYGON_NONE )
2911 {
2912 if( m_polygons.size() <= elem.polygon )
2913 {
2914 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2915 "of %d existing polygons.",
2916 elem.polygon,
2917 m_polygons.size() ) );
2918 }
2919
2920 ZONE* zone = m_polygons.at( elem.polygon );
2921
2922 if( zone == nullptr )
2923 {
2924 continue; // we know the zone id, but because we do not know the layer we did not
2925 // add it!
2926 }
2927
2928 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2929
2930 if( klayer == UNDEFINED_LAYER )
2931 continue; // Just skip it for now. Users can fill it themselves.
2932
2933 SHAPE_LINE_CHAIN linechain;
2934
2935 for( const ALTIUM_VERTICE& vertice : elem.outline )
2936 linechain.Append( vertice.position );
2937
2938 linechain.Append( elem.outline.at( 0 ).position );
2939 linechain.SetClosed( true );
2940
2941 SHAPE_POLY_SET fill;
2942 fill.AddOutline( linechain );
2943
2944 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2945 {
2946 SHAPE_LINE_CHAIN hole_linechain;
2947
2948 for( const ALTIUM_VERTICE& vertice : hole )
2949 hole_linechain.Append( vertice.position );
2950
2951 hole_linechain.Append( hole.at( 0 ).position );
2952 hole_linechain.SetClosed( true );
2953 fill.AddHole( hole_linechain );
2954 }
2955
2956 if( zone->HasFilledPolysForLayer( klayer ) )
2957 fill.BooleanAdd( *zone->GetFill( klayer ) );
2958
2959 fill.Fracture();
2960
2961 zone->SetFilledPolysList( klayer, fill );
2962 zone->SetIsFilled( true );
2963 zone->SetNeedRefill( false );
2964 }
2965 }
2966
2967 if( reader.GetRemainingBytes() != 0 )
2968 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2969}
2970
2971
2973 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2974{
2975 if( m_progressReporter )
2976 m_progressReporter->Report( _( "Loading arcs..." ) );
2977
2978 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2979
2980 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2981 {
2982 checkpoint();
2983 AARC6 elem( reader );
2984
2985 if( elem.component == ALTIUM_COMPONENT_NONE )
2986 {
2987 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2988 }
2989 else
2990 {
2991 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2992 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2993 }
2994 }
2995
2996 if( reader.GetRemainingBytes() != 0 )
2997 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2998}
2999
3000
3002{
3003 if( aElem.startangle == 0. && aElem.endangle == 360. )
3004 {
3005 aShape->SetShape( SHAPE_T::CIRCLE );
3006
3007 // TODO: other variants to define circle?
3008 aShape->SetStart( aElem.center );
3009 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
3010 }
3011 else
3012 {
3013 aShape->SetShape( SHAPE_T::ARC );
3014
3015 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
3016 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
3017
3018 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
3019 -KiROUND( startAngle.Sin() * aElem.radius ) );
3020
3021 aShape->SetCenter( aElem.center );
3022 aShape->SetStart( aElem.center + startOffset );
3023 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
3024 }
3025}
3026
3027
3028void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
3029{
3030 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
3031 {
3032 if( m_polygons.size() <= aElem.polygon )
3033 {
3034 THROW_IO_ERROR( wxString::Format( "Tracks stream tries to access polygon id %u "
3035 "of %zu existing polygons.",
3036 aElem.polygon, m_polygons.size() ) );
3037 }
3038
3039 ZONE* zone = m_polygons.at( aElem.polygon );
3040
3041 if( zone == nullptr )
3042 {
3043 return; // we know the zone id, but because we do not know the layer we did not
3044 // add it!
3045 }
3046
3047 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3048
3049 if( klayer == UNDEFINED_LAYER )
3050 return; // Just skip it for now. Users can fill it themselves.
3051
3052 if( !zone->HasFilledPolysForLayer( klayer ) )
3053 return;
3054
3055 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
3056
3057 // This is not the actual board item. We can use it to create the polygon for the region
3058 PCB_SHAPE shape( nullptr );
3059
3060 ConvertArcs6ToPcbShape( aElem, &shape );
3062
3063 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
3064 // Will be simplified and fractured later
3065
3066 zone->SetIsFilled( true );
3067 zone->SetNeedRefill( false );
3068
3069 return;
3070 }
3071
3072 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3073 || IsAltiumLayerAPlane( aElem.layer ) )
3074 {
3075 // This is not the actual board item. We can use it to create the polygon for the region
3076 PCB_SHAPE shape( nullptr );
3077
3078 ConvertArcs6ToPcbShape( aElem, &shape );
3080
3082 }
3083 else
3084 {
3085 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3086 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
3087 }
3088
3089 for( const auto& layerExpansionMask :
3091 {
3092 int width = aElem.width + ( layerExpansionMask.second * 2 );
3093
3094 if( width > 1 )
3095 {
3096 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
3097
3098 ConvertArcs6ToPcbShape( aElem, arc.get() );
3099 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3100 arc->SetLayer( layerExpansionMask.first );
3101
3102 m_board->Add( arc.release(), ADD_MODE::APPEND );
3103 }
3104 }
3105}
3106
3107
3109 const int aPrimitiveIndex, const bool aIsBoardImport )
3110{
3111 if( aElem.polygon != ALTIUM_POLYGON_NONE )
3112 {
3113 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Arc with polygon id %d",
3114 aElem.polygon ) );
3115 return;
3116 }
3117
3118 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3119 || IsAltiumLayerAPlane( aElem.layer ) )
3120 {
3121 // This is not the actual board item. We can use it to create the polygon for the region
3122 PCB_SHAPE shape( nullptr );
3123
3124 ConvertArcs6ToPcbShape( aElem, &shape );
3126
3127 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3128 aElem.keepoutrestrictions );
3129 }
3130 else
3131 {
3132 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3133 {
3134 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3135 {
3136 // Special case: do to not lose net connections in footprints
3137 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
3138 }
3139 else
3140 {
3141 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3142 }
3143 }
3144 }
3145
3146 for( const auto& layerExpansionMask :
3148 {
3149 int width = aElem.width + ( layerExpansionMask.second * 2 );
3150
3151 if( width > 1 )
3152 {
3153 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3154
3155 ConvertArcs6ToPcbShape( aElem, arc.get() );
3156 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3157 arc->SetLayer( layerExpansionMask.first );
3158
3159 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3160 }
3161 }
3162}
3163
3164
3166{
3167 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3168 {
3169 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
3170 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
3171
3172 includedAngle.Normalize();
3173
3174 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
3175 -KiROUND( startAngle.Sin() * aElem.radius ) );
3176
3177 if( includedAngle.AsDegrees() >= 0.1 )
3178 {
3179 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
3180 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
3181
3182 shape.SetCenter( aElem.center );
3183 shape.SetStart( aElem.center + startOffset );
3184 shape.SetArcAngleAndEnd( includedAngle, true );
3185
3186 // Create actual arc
3187 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(),
3188 aElem.width );
3189 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board, &shapeArc );
3190
3191 arc->SetWidth( aElem.width );
3192 arc->SetLayer( aLayer );
3193 arc->SetNetCode( GetNetCode( aElem.net ) );
3194
3195 m_board->Add( arc.release(), ADD_MODE::APPEND );
3196 }
3197 }
3198 else
3199 {
3200 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>(m_board);
3201
3202 ConvertArcs6ToPcbShape( aElem, arc.get() );
3203 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3204 arc->SetLayer( aLayer );
3205
3206 m_board->Add( arc.release(), ADD_MODE::APPEND );
3207 }
3208}
3209
3210
3212 PCB_LAYER_ID aLayer )
3213{
3214 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3215
3216 ConvertArcs6ToPcbShape( aElem, arc.get() );
3217 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3218 arc->SetLayer( aLayer );
3219
3220 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3221}
3222
3223
3225 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3226{
3227 if( m_progressReporter )
3228 m_progressReporter->Report( _( "Loading pads..." ) );
3229
3230 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3231
3232 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3233 {
3234 checkpoint();
3235 APAD6 elem( reader );
3236
3237 if( elem.component == ALTIUM_COMPONENT_NONE )
3238 {
3240 }
3241 else
3242 {
3243 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3244 ConvertPads6ToFootprintItem( footprint, elem );
3245 }
3246 }
3247
3248 if( reader.GetRemainingBytes() != 0 )
3249 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
3250}
3251
3252
3254{
3255 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3256 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3257 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3258 {
3260 }
3261 else
3262 {
3263 // We cannot add a pad directly into the PCB
3264 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
3265 footprint->SetPosition( aElem.position );
3266
3267 ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem );
3268
3269 m_board->Add( footprint.release(), ADD_MODE::APPEND );
3270 }
3271}
3272
3273
3275{
3276 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3277
3278 pad->SetNumber( "" );
3279 pad->SetNetCode( GetNetCode( aElem.net ) );
3280
3281 pad->SetPosition( aElem.position );
3282 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( aElem.diameter, aElem.diameter ) );
3283 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3284 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3286 pad->SetAttribute( PAD_ATTRIB::PTH );
3287
3288 // Pads are always through holes in KiCad
3289 pad->SetLayerSet( LSET().AllCuMask() );
3290
3291 if( aElem.viamode == ALTIUM_PAD_MODE::SIMPLE )
3292 {
3293 pad->Padstack().SetMode( PADSTACK::MODE::NORMAL );
3294 }
3296 {
3297 pad->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
3298 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[1], aElem.diameter_by_layer[1] ),
3300 }
3301 else
3302 {
3303 pad->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
3304 int altiumIdx = 0;
3305
3306 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, 32 ) )
3307 {
3308 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[altiumIdx],
3309 aElem.diameter_by_layer[altiumIdx] ), layer );
3310 altiumIdx++;
3311 }
3312 }
3313
3314 if( aElem.is_tent_top )
3315 {
3316 pad->Padstack().FrontOuterLayers().has_solder_mask = true;
3317 }
3318 else
3319 {
3320 pad->Padstack().FrontOuterLayers().has_solder_mask = false;
3321 pad->SetLayerSet( pad->GetLayerSet().set( F_Mask ) );
3322 }
3323
3324 if( aElem.is_tent_bottom )
3325 {
3326 pad->Padstack().BackOuterLayers().has_solder_mask = true;
3327 }
3328 else
3329 {
3330 pad->Padstack().BackOuterLayers().has_solder_mask = false;
3331 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ) );
3332 }
3333
3334 if( aElem.is_locked )
3335 pad->SetLocked( true );
3336
3337 if( aElem.soldermask_expansion_manual )
3338 {
3339 pad->Padstack().FrontOuterLayers().solder_mask_margin = aElem.soldermask_expansion_front;
3340 pad->Padstack().BackOuterLayers().solder_mask_margin = aElem.soldermask_expansion_back;
3341 }
3342
3343
3344 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3345}
3346
3347
3349{
3350 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3351 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3352 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3353 {
3354 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
3355 }
3356 else
3357 {
3358 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
3359 }
3360}
3361
3362
3364{
3365 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3366
3367 pad->SetNumber( aElem.name );
3368 pad->SetNetCode( GetNetCode( aElem.net ) );
3369
3370 pad->SetPosition( aElem.position );
3371 pad->SetOrientationDegrees( aElem.direction );
3372 pad->SetThermalSpokeAngle( ANGLE_90 );
3373
3374 if( aElem.holesize == 0 )
3375 {
3376 pad->SetAttribute( PAD_ATTRIB::SMD );
3377 }
3378 else
3379 {
3380 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3381 {
3382 // TODO: I assume other values are possible as well?
3383 if( !m_footprintName.IsEmpty() )
3384 {
3385 if( m_reporter )
3386 {
3387 wxString msg;
3388 msg.Printf( _( "Error loading library '%s':\n"
3389 "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3390 m_library,
3392 aElem.name );
3393 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
3394 }
3395 }
3396 else
3397 {
3398 if( m_reporter )
3399 {
3400 wxString msg;
3401 msg.Printf( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3402 aFootprint->GetReference(),
3403 aElem.name );
3404 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
3405 }
3406 }
3407 }
3408
3409 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
3410
3412 {
3413 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3414 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3415 }
3416 else
3417 {
3418 switch( aElem.sizeAndShape->holeshape )
3419 {
3421 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
3422 break;
3423
3425 if( !m_footprintName.IsEmpty() )
3426 {
3427 if( m_reporter )
3428 {
3429 wxString msg;
3430 msg.Printf( _( "Loading library '%s':\n"
3431 "Footprint %s pad %s has a square hole (not yet supported)." ),
3432 m_library,
3434 aElem.name );
3435 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3436 }
3437 }
3438 else
3439 {
3440 if( m_reporter )
3441 {
3442 wxString msg;
3443 msg.Printf( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
3444 aFootprint->GetReference(),
3445 aElem.name );
3446 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3447 }
3448 }
3449
3450 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3451 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3452 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
3453 // this case or rect holes have a different id
3454 break;
3455
3457 {
3458 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
3459 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
3460
3461 slotRotation.Normalize();
3462
3463 if( slotRotation.IsHorizontal() )
3464 {
3465 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
3466 }
3467 else if( slotRotation.IsVertical() )
3468 {
3469 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
3470 }
3471 else
3472 {
3473 if( !m_footprintName.IsEmpty() )
3474 {
3475 if( m_reporter )
3476 {
3477 wxString msg;
3478 msg.Printf( _( "Loading library '%s':\n"
3479 "Footprint %s pad %s has a hole-rotation of %d degrees. "
3480 "KiCad only supports 90 degree rotations." ),
3481 m_library,
3483 aElem.name,
3484 KiROUND( slotRotation.AsDegrees() ) );
3485 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3486 }
3487 }
3488 else
3489 {
3490 if( m_reporter )
3491 {
3492 wxString msg;
3493 msg.Printf( _( "Footprint %s pad %s has a hole-rotation of %d degrees. "
3494 "KiCad only supports 90 degree rotations." ),
3495 aFootprint->GetReference(),
3496 aElem.name,
3497 KiROUND( slotRotation.AsDegrees() ) );
3498 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3499 }
3500 }
3501 }
3502
3503 break;
3504 }
3505
3506 default:
3508 if( !m_footprintName.IsEmpty() )
3509 {
3510 if( m_reporter )
3511 {
3512 wxString msg;
3513 msg.Printf( _( "Error loading library '%s':\n"
3514 "Footprint %s pad %s uses a hole of unknown kind %d." ),
3515 m_library,
3517 aElem.name,
3518 aElem.sizeAndShape->holeshape );
3519 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3520 }
3521 }
3522 else
3523 {
3524 if( m_reporter )
3525 {
3526 wxString msg;
3527 msg.Printf( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
3528 aFootprint->GetReference(),
3529 aElem.name,
3530 aElem.sizeAndShape->holeshape );
3531 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3532 }
3533 }
3534
3535 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3536 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3537 break;
3538 }
3539 }
3540
3541 if( aElem.sizeAndShape )
3542 pad->SetOffset( PADSTACK::ALL_LAYERS, aElem.sizeAndShape->holeoffset[0] );
3543 }
3544
3545 PADSTACK& ps = pad->Padstack();
3546
3547 auto setCopperGeometry =
3548 [&]( PCB_LAYER_ID aLayer, ALTIUM_PAD_SHAPE aShape, const VECTOR2I& aSize )
3549 {
3550 int altLayer = CopperLayerToOrdinal( aLayer );
3551
3552 ps.SetSize( aSize, aLayer );
3553
3554 switch( aShape )
3555 {
3557 ps.SetShape( PAD_SHAPE::RECTANGLE, aLayer );
3558 break;
3559
3561 if( aElem.sizeAndShape
3563 {
3564 ps.SetShape( PAD_SHAPE::ROUNDRECT, aLayer ); // 100 = round, 0 = rectangular
3565 double ratio = aElem.sizeAndShape->cornerradius[altLayer] / 200.;
3566 ps.SetRoundRectRadiusRatio( ratio, aLayer );
3567 }
3568 else if( aElem.topsize.x == aElem.topsize.y )
3569 {
3570 ps.SetShape( PAD_SHAPE::CIRCLE, aLayer );
3571 }
3572 else
3573 {
3574 ps.SetShape( PAD_SHAPE::OVAL, aLayer );
3575 }
3576
3577 break;
3578
3580 ps.SetShape( PAD_SHAPE::CHAMFERED_RECT, aLayer );
3582 ps.SetChamferRatio( 0.25, aLayer );
3583 break;
3584
3586 default:
3587 if( !m_footprintName.IsEmpty() )
3588 {
3589 if( m_reporter )
3590 {
3591 wxString msg;
3592 msg.Printf( _( "Error loading library '%s':\n"
3593 "Footprint %s pad %s uses an unknown pad shape." ),
3594 m_library,
3596 aElem.name );
3597 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3598 }
3599 }
3600 else
3601 {
3602 if( m_reporter )
3603 {
3604 wxString msg;
3605 msg.Printf( _( "Footprint %s pad %s uses an unknown pad shape." ),
3606 aFootprint->GetReference(),
3607 aElem.name );
3608 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3609 }
3610 }
3611 break;
3612 }
3613 };
3614
3615 switch( aElem.padmode )
3616 {
3619 setCopperGeometry( PADSTACK::ALL_LAYERS, aElem.topshape, aElem.topsize );
3620 break;
3621
3624 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3625 setCopperGeometry( PADSTACK::INNER_LAYERS, aElem.midshape, aElem.midsize );
3626 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3627 break;
3628
3631
3632 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3633 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3634 setCopperGeometry( In1_Cu, aElem.midshape, aElem.midsize );
3635
3636 if( aElem.sizeAndShape )
3637 {
3638 size_t i = 0;
3639
3640 LSET intLayers = aFootprint->BoardLayerSet();
3641 intLayers &= LSET::InternalCuMask();
3642 intLayers.set( In1_Cu, false ); // Already handled above
3643
3644 for( PCB_LAYER_ID layer : intLayers )
3645 {
3646 setCopperGeometry( layer, aElem.sizeAndShape->inner_shape[i],
3647 VECTOR2I( aElem.sizeAndShape->inner_size[i].x,
3648 aElem.sizeAndShape->inner_size[i].y ) );
3649 i++;
3650 }
3651 }
3652
3653 break;
3654 }
3655
3656 switch( aElem.layer )
3657 {
3659 pad->SetLayer( F_Cu );
3660 pad->SetLayerSet( PAD::SMDMask() );
3661 break;
3662
3664 pad->SetLayer( B_Cu );
3665 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
3666 break;
3667
3669 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
3670 break;
3671
3672 default:
3673 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3674 pad->SetLayer( klayer );
3675 pad->SetLayerSet( LSET( { klayer } ) );
3676 break;
3677 }
3678
3680 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
3681
3683 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
3684
3685 if( aElem.is_tent_top )
3686 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
3687
3688 if( aElem.is_tent_bottom )
3689 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
3690
3691 pad->SetPadToDieLength( aElem.pad_to_die_length );
3692 pad->SetPadToDieDelay( aElem.pad_to_die_delay );
3693
3694 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3695}
3696
3697
3699{
3700 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3701
3702 if( klayer == UNDEFINED_LAYER )
3703 {
3704 if( m_reporter )
3705 {
3706 wxString msg;
3707 msg.Printf( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
3708 "equivalent. It has been moved to KiCad layer Eco1_User." ),
3709 aElem.name, aElem.layer );
3710 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3711 }
3712
3713 klayer = Eco1_User;
3714 }
3715
3716 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( m_board );
3717
3718 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3719
3720 m_board->Add( pad.release(), ADD_MODE::APPEND );
3721}
3722
3723
3725{
3726 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3727
3728 if( klayer == UNDEFINED_LAYER )
3729 {
3730 if( !m_footprintName.IsEmpty() )
3731 {
3732 if( m_reporter )
3733 {
3734 wxString msg;
3735 msg.Printf( _( "Loading library '%s':\n"
3736 "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3737 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3738 m_library,
3740 aElem.name,
3741 aElem.layer );
3742 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3743 }
3744 }
3745 else
3746 {
3747 if( m_reporter )
3748 {
3749 wxString msg;
3750 msg.Printf( _( "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3751 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3752 aFootprint->GetReference(),
3753 aElem.name,
3754 aElem.layer );
3755 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3756 }
3757 }
3758
3759 klayer = Eco1_User;
3760 }
3761
3762 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( aFootprint );
3763
3764 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3765
3766 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3767}
3768
3769
3771 PCB_SHAPE* aShape )
3772{
3773 if( aElem.net != ALTIUM_NET_UNCONNECTED )
3774 {
3775 if( m_reporter )
3776 {
3777 wxString msg;
3778 msg.Printf( _( "Non-copper pad %s is connected to a net, which is not supported." ),
3779 aElem.name );
3780 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3781 }
3782 }
3783
3784 if( aElem.holesize != 0 )
3785 {
3786 if( m_reporter )
3787 {
3788 wxString msg;
3789 msg.Printf( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
3790 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3791 }
3792 }
3793
3794 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3795 {
3796 if( m_reporter )
3797 {
3798 wxString msg;
3799 msg.Printf( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
3800 aElem.name );
3801 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3802 }
3803 }
3804
3805 switch( aElem.topshape )
3806 {
3808 {
3809 // filled rect
3810 aShape->SetShape( SHAPE_T::POLY );
3811 aShape->SetFilled( true );
3812 aShape->SetLayer( aLayer );
3813 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3814
3815 aShape->SetPolyPoints(
3816 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
3817 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3818 aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3819 aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
3820
3821 if( aElem.direction != 0 )
3822 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3823 }
3824 break;
3825
3827 if( aElem.sizeAndShape
3829 {
3830 // filled roundrect
3831 int cornerradius = aElem.sizeAndShape->cornerradius[0];
3832 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
3833
3834 aShape->SetLayer( aLayer );
3835 aShape->SetStroke( STROKE_PARAMS( offset * 2, LINE_STYLE::SOLID ) );
3836
3837 if( cornerradius < 100 )
3838 {
3839 int offsetX = aElem.topsize.x / 2 - offset;
3840 int offsetY = aElem.topsize.y / 2 - offset;
3841
3842 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
3843 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
3844 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
3845 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
3846
3847 aShape->SetShape( SHAPE_T::POLY );
3848 aShape->SetFilled( true );
3849 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
3850 }
3851 else if( aElem.topsize.x == aElem.topsize.y )
3852 {
3853 // circle
3854 aShape->SetShape( SHAPE_T::CIRCLE );
3855 aShape->SetFilled( true );
3856 aShape->SetStart( aElem.position );
3857 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3858 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3859 }
3860 else if( aElem.topsize.x < aElem.topsize.y )
3861 {
3862 // short vertical line
3863 aShape->SetShape( SHAPE_T::SEGMENT );
3864 VECTOR2I pointOffset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3865 aShape->SetStart( aElem.position + pointOffset );
3866 aShape->SetEnd( aElem.position - pointOffset );
3867 }
3868 else
3869 {
3870 // short horizontal line
3871 aShape->SetShape( SHAPE_T::SEGMENT );
3872 VECTOR2I pointOffset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3873 aShape->SetStart( aElem.position + pointOffset );
3874 aShape->SetEnd( aElem.position - pointOffset );
3875 }
3876
3877 if( aElem.direction != 0 )
3878 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3879 }
3880 else if( aElem.topsize.x == aElem.topsize.y )
3881 {
3882 // filled circle
3883 aShape->SetShape( SHAPE_T::CIRCLE );
3884 aShape->SetFilled( true );
3885 aShape->SetLayer( aLayer );
3886 aShape->SetStart( aElem.position );
3887 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3888 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3889 }
3890 else
3891 {
3892 // short line
3893 aShape->SetShape( SHAPE_T::SEGMENT );
3894 aShape->SetLayer( aLayer );
3895 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
3897
3898 if( aElem.topsize.x < aElem.topsize.y )
3899 {
3900 VECTOR2I offset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3901 aShape->SetStart( aElem.position + offset );
3902 aShape->SetEnd( aElem.position - offset );
3903 }
3904 else
3905 {
3906 VECTOR2I offset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3907 aShape->SetStart( aElem.position + offset );
3908 aShape->SetEnd( aElem.position - offset );
3909 }
3910
3911 if( aElem.direction != 0 )
3912 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3913 }
3914 break;
3915
3917 {
3918 // filled octagon
3919 aShape->SetShape( SHAPE_T::POLY );
3920 aShape->SetFilled( true );
3921 aShape->SetLayer( aLayer );
3922 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3923
3924 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
3925 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3926 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3927 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
3928
3929 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
3930 VECTOR2I chamferX( chamfer, 0 );
3931 VECTOR2I chamferY( 0, chamfer );
3932
3933 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
3934 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
3935
3936 if( aElem.direction != 0. )
3937 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3938 }
3939 break;
3940
3942 default:
3943 if( m_reporter )
3944 {
3945 wxString msg;
3946 msg.Printf( _( "Non-copper pad %s uses an unknown pad shape." ), aElem.name );
3947 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3948 }
3949
3950 break;
3951 }
3952}
3953
3954
3956 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3957{
3958 if( m_progressReporter )
3959 m_progressReporter->Report( _( "Loading vias..." ) );
3960
3961 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3962
3963 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3964 {
3965 checkpoint();
3966 AVIA6 elem( reader );
3967
3968 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
3969
3970 via->SetPosition( elem.position );
3971 via->SetDrill( elem.holesize );
3972 via->SetNetCode( GetNetCode( elem.net ) );
3973 via->SetLocked( elem.is_locked );
3974
3975 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
3977 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
3979
3980 if( start_layer_outside && end_layer_outside )
3981 {
3982 via->SetViaType( VIATYPE::THROUGH );
3983 }
3984 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
3985 {
3986 via->SetViaType( VIATYPE::BURIED );
3987 }
3988 else
3989 {
3990 via->SetViaType( VIATYPE::BLIND );
3991 }
3992
3993 // TODO: Altium has a specific flag for microvias, independent of start/end layer
3994#if 0
3995 if( something )
3996 via->SetViaType( VIATYPE::MICROVIA );
3997#endif
3998
3999 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
4000 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
4001
4002 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
4003 {
4004 if( m_reporter )
4005 {
4006 wxString msg;
4007 msg.Printf( _( "Via from layer %d to %d uses a non-copper layer, which is not "
4008 "supported." ),
4009 elem.layer_start,
4010 elem.layer_end );
4011 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4012 }
4013
4014 continue; // just assume through-hole instead.
4015 }
4016
4017 // we need VIATYPE set!
4018 via->SetLayerPair( start_klayer, end_klayer );
4019
4020 switch( elem.viamode )
4021 {
4022 default:
4024 via->SetWidth( PADSTACK::ALL_LAYERS, elem.diameter );
4025 break;
4026
4028 via->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
4029 via->SetWidth( F_Cu, elem.diameter_by_layer[0] );
4030 via->SetWidth( PADSTACK::INNER_LAYERS, elem.diameter_by_layer[1] );
4031 via->SetWidth( B_Cu, elem.diameter_by_layer[31] );
4032 break;
4033
4035 {
4036 via->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
4037
4038 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, MAX_CU_LAYERS ) )
4039 {
4040 int altiumLayer = CopperLayerToOrdinal( layer );
4041 wxCHECK2_MSG( altiumLayer < 32, break,
4042 "Altium importer expects 32 or fewer copper layers" );
4043
4044 via->SetWidth( layer, elem.diameter_by_layer[altiumLayer] );
4045 }
4046
4047 break;
4048 }
4049 }
4050
4052 {
4053 via->SetFrontTentingMode( elem.is_tent_top ? TENTING_MODE::TENTED
4055 via->SetBackTentingMode( elem.is_tent_bottom ? TENTING_MODE::TENTED
4057 }
4058
4059 m_board->Add( via.release(), ADD_MODE::APPEND );
4060 }
4061
4062 if( reader.GetRemainingBytes() != 0 )
4063 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
4064}
4065
4067 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4068{
4069 if( m_progressReporter )
4070 m_progressReporter->Report( _( "Loading tracks..." ) );
4071
4072 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4073
4074 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
4075 {
4076 checkpoint();
4077 ATRACK6 elem( reader );
4078
4079 if( elem.component == ALTIUM_COMPONENT_NONE )
4080 {
4081 ConvertTracks6ToBoardItem( elem, primitiveIndex );
4082 }
4083 else
4084 {
4085 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4086 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
4087 }
4088 }
4089
4090 if( reader.GetRemainingBytes() != 0 )
4091 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
4092}
4093
4094
4095void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
4096{
4097 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
4098 {
4099 if( m_polygons.size() <= aElem.polygon )
4100 {
4101 // Can happen when reading old Altium files: just skip this item
4102 if( m_reporter )
4103 {
4104 wxString msg;
4105 msg.Printf( wxT( "ATRACK6 stream tries to access polygon id %u "
4106 "of %u existing polygons; skipping it" ),
4107 static_cast<unsigned>( aElem.polygon ),
4108 static_cast<unsigned>( m_polygons.size() ) );
4109 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4110 }
4111
4112 return;
4113 }
4114
4115 ZONE* zone = m_polygons.at( aElem.polygon );
4116
4117 if( zone == nullptr )
4118 {
4119 return; // we know the zone id, but because we do not know the layer we did not
4120 // add it!
4121 }
4122
4123 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
4124
4125 if( klayer == UNDEFINED_LAYER )
4126 return; // Just skip it for now. Users can fill it themselves.
4127
4128 if( !zone->HasFilledPolysForLayer( klayer ) )
4129 return;
4130
4131 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
4132
4133 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4134 shape.SetStart( aElem.start );
4135 shape.SetEnd( aElem.end );
4137
4138 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
4139 // Will be simplified and fractured later
4140
4141 zone->SetIsFilled( true );
4142 zone->SetNeedRefill( false );
4143
4144 return;
4145 }
4146
4147 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4148 || IsAltiumLayerAPlane( aElem.layer ) )
4149 {
4150 // This is not the actual board item. We can use it to create the polygon for the region
4151 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4152 shape.SetStart( aElem.start );
4153 shape.SetEnd( aElem.end );
4155
4157 }
4158 else
4159 {
4160 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4161 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4162 }
4163
4164 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4165 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4166 {
4167 int width = aElem.width + ( layerExpansionMask.second * 2 );
4168 if( width > 1 )
4169 {
4170 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4171
4172 seg->SetStart( aElem.start );
4173 seg->SetEnd( aElem.end );
4174 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4175 seg->SetLayer( layerExpansionMask.first );
4176
4177 m_board->Add( seg.release(), ADD_MODE::APPEND );
4178 }
4179 }
4180}
4181
4182
4184 const int aPrimitiveIndex,
4185 const bool aIsBoardImport )
4186{
4187 if( aElem.polygon != ALTIUM_POLYGON_NONE )
4188 {
4189 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Track with polygon id %u",
4190 (unsigned)aElem.polygon ) );
4191 return;
4192 }
4193
4194 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4195 || IsAltiumLayerAPlane( aElem.layer ) )
4196 {
4197 // This is not the actual board item. We can use it to create the polygon for the region
4198 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4199 shape.SetStart( aElem.start );
4200 shape.SetEnd( aElem.end );
4202
4203 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4204 aElem.keepoutrestrictions );
4205 }
4206 else
4207 {
4208 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4209 {
4210 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4211 {
4212 // Special case: do to not lose net connections in footprints
4213 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4214 }
4215 else
4216 {
4217 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4218 }
4219 }
4220 }
4221
4222 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4223 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4224 {
4225 int width = aElem.width + ( layerExpansionMask.second * 2 );
4226 if( width > 1 )
4227 {
4228 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4229
4230 seg->SetStart( aElem.start );
4231 seg->SetEnd( aElem.end );
4232 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4233 seg->SetLayer( layerExpansionMask.first );
4234
4235 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4236 }
4237 }
4238}
4239
4240
4242{
4243 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4244 {
4245 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
4246
4247 track->SetStart( aElem.start );
4248 track->SetEnd( aElem.end );
4249 track->SetWidth( aElem.width );
4250 track->SetLayer( aLayer );
4251 track->SetNetCode( GetNetCode( aElem.net ) );
4252
4253 m_board->Add( track.release(), ADD_MODE::APPEND );
4254 }
4255 else
4256 {
4257 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4258
4259 seg->SetStart( aElem.start );
4260 seg->SetEnd( aElem.end );
4261 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4262 seg->SetLayer( aLayer );
4263
4264 m_board->Add( seg.release(), ADD_MODE::APPEND );
4265 }
4266}
4267
4268
4270 PCB_LAYER_ID aLayer )
4271{
4272 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4273
4274 seg->SetStart( aElem.start );
4275 seg->SetEnd( aElem.end );
4276 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4277 seg->SetLayer( aLayer );
4278
4279 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4280}
4281
4282
4284 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4285{
4286 if( m_progressReporter )
4287 m_progressReporter->Report( _( "Loading unicode strings..." ) );
4288
4289 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4290
4292
4293 if( reader.GetRemainingBytes() != 0 )
4294 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
4295}
4296
4298 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4299{
4300 if( m_progressReporter )
4301 m_progressReporter->Report( _( "Loading text..." ) );
4302
4303 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4304
4305 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4306 {
4307 checkpoint();
4308 ATEXT6 elem( reader, m_unicodeStrings );
4309
4310 if( elem.component == ALTIUM_COMPONENT_NONE )
4311 {
4313 }
4314 else
4315 {
4316 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4317 ConvertTexts6ToFootprintItem( footprint, elem );
4318 }
4319 }
4320
4321 if( reader.GetRemainingBytes() != 0 )
4322 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
4323}
4324
4325
4327{
4328 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4329 {
4330 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4331 ConvertBarcodes6ToBoardItemOnLayer( aElem, klayer );
4332 return;
4333 }
4334
4335 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4336 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
4337}
4338
4339
4341{
4342 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4343 {
4344 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4345 ConvertBarcodes6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4346 return;
4347 }
4348
4349 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4350 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4351}
4352
4353
4355{
4356 std::unique_ptr<PCB_TEXTBOX> pcbTextbox = std::make_unique<PCB_TEXTBOX>( m_board );
4357 std::unique_ptr<PCB_TEXT> pcbText = std::make_unique<PCB_TEXT>( m_board );
4358
4359 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4360
4361 static const std::map<wxString, wxString> variableMap = {
4362 { "LAYER_NAME", "LAYER" },
4363 { "PRINT_DATE", "CURRENT_DATE"},
4364 };
4365
4366 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4367 BOARD_ITEM* item = pcbText.get();
4368 EDA_TEXT* text = pcbText.get();
4369 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4370
4371 if( isTextbox )
4372 {
4373 item = pcbTextbox.get();
4374 text = pcbTextbox.get();
4375
4377 HelperSetTextboxAlignmentAndPos( aElem, pcbTextbox.get() );
4378 }
4379 else
4380 {
4383 }
4384
4385 text->SetText( kicadText );
4386 item->SetLayer( aLayer );
4387 item->SetIsKnockout( aElem.isInverted );
4388
4389 if( isTextbox )
4390 m_board->Add( pcbTextbox.release(), ADD_MODE::APPEND );
4391 else
4392 m_board->Add( pcbText.release(), ADD_MODE::APPEND );
4393}
4394
4395
4397 PCB_LAYER_ID aLayer )
4398{
4399 std::unique_ptr<PCB_TEXTBOX> fpTextbox = std::make_unique<PCB_TEXTBOX>( aFootprint );
4400 std::unique_ptr<PCB_TEXT> fpText = std::make_unique<PCB_TEXT>( aFootprint );
4401
4402 BOARD_ITEM* item = fpText.get();
4403 EDA_TEXT* text = fpText.get();
4404 PCB_FIELD* field = nullptr;
4405
4406 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4407 bool toAdd = false;
4408
4409 if( aElem.isDesignator )
4410 {
4411 item = &aFootprint->Reference(); // TODO: handle multiple layers
4412 text = &aFootprint->Reference();
4413 field = &aFootprint->Reference();
4414 }
4415 else if( aElem.isComment )
4416 {
4417 item = &aFootprint->Value(); // TODO: handle multiple layers
4418 text = &aFootprint->Value();
4419 field = &aFootprint->Value();
4420 }
4421 else
4422 {
4423 item = fpText.get();
4424 text = fpText.get();
4425 toAdd = true;
4426 }
4427
4428 static const std::map<wxString, wxString> variableMap = {
4429 { "DESIGNATOR", "REFERENCE" },
4430 { "COMMENT", "VALUE" },
4431 { "VALUE", "ALTIUM_VALUE" },
4432 { "LAYER_NAME", "LAYER" },
4433 { "PRINT_DATE", "CURRENT_DATE"},
4434 };
4435
4436 if( isTextbox )
4437 {
4438 item = fpTextbox.get();
4439 text = fpTextbox.get();
4440
4442 HelperSetTextboxAlignmentAndPos( aElem, fpTextbox.get() );
4443 }
4444 else
4445 {
4448 }
4449
4450 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4451
4452 text->SetText( kicadText );
4453 text->SetKeepUpright( false );
4454 item->SetLayer( aLayer );
4455 item->SetIsKnockout( aElem.isInverted );
4456
4457 if( toAdd )
4458 {
4459 if( isTextbox )
4460 aFootprint->Add( fpTextbox.release(), ADD_MODE::APPEND );
4461 else
4462 aFootprint->Add( fpText.release(), ADD_MODE::APPEND );
4463 }
4464}
4465
4466
4468{
4469 std::unique_ptr<PCB_BARCODE> pcbBarcode = std::make_unique<PCB_BARCODE>( m_board );
4470
4471 pcbBarcode->SetLayer( aLayer );
4472 pcbBarcode->SetPosition( aElem.position );
4473 pcbBarcode->SetWidth( aElem.textbox_rect_width );
4474 pcbBarcode->SetHeight( aElem.textbox_rect_height );
4475 pcbBarcode->SetMargin( aElem.barcode_margin );
4476 pcbBarcode->SetText( aElem.text );
4477
4478 switch( aElem.barcode_type )
4479 {
4480 case ALTIUM_BARCODE_TYPE::CODE39: pcbBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4481 case ALTIUM_BARCODE_TYPE::CODE128: pcbBarcode->SetKind( BARCODE_T::CODE_128 ); break;
4482 default: pcbBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4483 }
4484
4485 pcbBarcode->SetIsKnockout( aElem.barcode_inverted );
4486 pcbBarcode->AssembleBarcode();
4487
4488 m_board->Add( pcbBarcode.release(), ADD_MODE::APPEND );
4489}
4490
4491
4493 PCB_LAYER_ID aLayer )
4494{
4495 std::unique_ptr<PCB_BARCODE> fpBarcode = std::make_unique<PCB_BARCODE>( aFootprint );
4496
4497 fpBarcode->SetLayer( aLayer );
4498 fpBarcode->SetPosition( aElem.position );
4499 fpBarcode->SetWidth( aElem.textbox_rect_width );
4500 fpBarcode->SetHeight( aElem.textbox_rect_height );
4501 fpBarcode->SetMargin( aElem.barcode_margin );
4502 fpBarcode->SetText( aElem.text );
4503
4504 switch( aElem.barcode_type )
4505 {
4506 case ALTIUM_BARCODE_TYPE::CODE39: fpBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4507 case ALTIUM_BARCODE_TYPE::CODE128: fpBarcode->SetKind( BARCODE_T::CODE_128 ); break;
4508 default: fpBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4509 }
4510
4511 fpBarcode->SetIsKnockout( aElem.barcode_inverted );
4512 fpBarcode->AssembleBarcode();
4513
4514 aFootprint->Add( fpBarcode.release(), ADD_MODE::APPEND );
4515}
4516
4517
4519{
4520 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4521
4522 // Altium textboxes do not have borders
4523 aTextbox->SetBorderEnabled( false );
4524
4525 // Calculate position
4526 VECTOR2I kposition = aElem.position;
4527
4528 if( aElem.isMirrored )
4529 kposition.x -= aElem.textbox_rect_width;
4530
4531 kposition.y -= aElem.textbox_rect_height;
4532
4533#if 0
4534 // Compensate for KiCad's textbox margin
4535 int charWidth = aTextbox->GetTextWidth();
4536 int charHeight = aTextbox->GetTextHeight();
4537
4538 VECTOR2I kicadMargin;
4539
4540 if( !aTextbox->GetFont() || aTextbox->GetFont()->IsStroke() )
4541 kicadMargin = VECTOR2I( charWidth * 0.933, charHeight * 0.67 );
4542 else
4543 kicadMargin = VECTOR2I( charWidth * 0.808, charHeight * 0.844 );
4544
4545 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height )
4546 + kicadMargin * 2 - margin * 2 );
4547
4548 kposition = kposition - kicadMargin + margin;
4549#else
4550 aTextbox->SetMarginBottom( margin );
4551 aTextbox->SetMarginLeft( margin );
4552 aTextbox->SetMarginRight( margin );
4553 aTextbox->SetMarginTop( margin );
4554
4555 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height ) );
4556#endif
4557
4558 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4559
4560 aTextbox->SetPosition( kposition );
4561
4562 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4565
4566 switch( justification )
4567 {
4573 break;
4579 break;
4585 break;
4586 default:
4587 if( m_reporter )
4588 {
4589 wxString msg;
4590 msg.Printf( _( "Unknown textbox justification %d, aText %s" ), justification,
4591 aElem.text );
4592 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4593 }
4594
4597 break;
4598 }
4599
4600 aTextbox->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4601}
4602
4603
4605{
4606 VECTOR2I kposition = aElem.position;
4607
4608 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4609 int rectWidth = aElem.textbox_rect_width - margin * 2;
4610 int rectHeight = aElem.height;
4611
4612 if( aElem.isMirrored )
4613 rectWidth = -rectWidth;
4614
4615 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4618
4619 switch( justification )
4620 {
4624
4625 kposition.y -= rectHeight;
4626 break;
4630
4631 kposition.y -= rectHeight / 2;
4632 break;
4636 break;
4640
4641 kposition.x += rectWidth / 2;
4642 kposition.y -= rectHeight;
4643 break;
4647
4648 kposition.x += rectWidth / 2;
4649 kposition.y -= rectHeight / 2;
4650 break;
4654
4655 kposition.x += rectWidth / 2;
4656 break;
4660
4661 kposition.x += rectWidth;
4662 kposition.y -= rectHeight;
4663 break;
4667
4668 kposition.x += rectWidth;
4669 kposition.y -= rectHeight / 2;
4670 break;
4674
4675 kposition.x += rectWidth;
4676 break;
4677 default:
4680 break;
4681 }
4682
4683 int charWidth = aText->GetTextWidth();
4684 int charHeight = aText->GetTextHeight();
4685
4686 // Correct for KiCad's baseline offset.
4687 // Text height and font must be set correctly before calling.
4688 if( !aText->GetFont() || aText->GetFont()->IsStroke() )
4689 {
4690 switch( aText->GetVertJustify() )
4691 {
4692 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charHeight * 0.0407; break;
4693 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charHeight * 0.0355; break;
4694 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charHeight * 0.1225; break;
4695 default: break;
4696 }
4697 }
4698 else
4699 {
4700 switch( aText->GetVertJustify() )
4701 {
4702 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charWidth * 0.016; break;
4703 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charWidth * 0.085; break;
4704 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charWidth * 0.17; break;
4705 default: break;
4706 }
4707 }
4708
4709 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4710
4711 aText->SetTextPos( kposition );
4712 aText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4713}
4714
4715
4717{
4718 aEdaText.SetTextSize( VECTOR2I( aElem.height, aElem.height ) );
4719
4721 {
4722 KIFONT::FONT* font = KIFONT::FONT::GetFont( aElem.fontname, aElem.isBold, aElem.isItalic );
4723 aEdaText.SetFont( font );
4724
4725 if( font->IsOutline() )
4726 {
4727 // TODO: why is this required? Somehow, truetype size is calculated differently
4728 if( font->GetName().Contains( wxS( "Arial" ) ) )
4729 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.63, aElem.height * 0.63 ) );
4730 else
4731 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.5, aElem.height * 0.5 ) );
4732 }
4733 }
4734
4735 aEdaText.SetTextThickness( aElem.strokewidth );
4736 aEdaText.SetBoldFlag( aElem.isBold );
4737 aEdaText.SetItalic( aElem.isItalic );
4738 aEdaText.SetMirrored( aElem.isMirrored );
4739}
4740
4741
4743 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4744{
4745 if( m_progressReporter )
4746 m_progressReporter->Report( _( "Loading rectangles..." ) );
4747
4748 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4749
4750 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4751 {
4752 checkpoint();
4753 AFILL6 elem( reader );
4754
4755 if( elem.component == ALTIUM_COMPONENT_NONE )
4756 {
4758 }
4759 else
4760 {
4761 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4762 ConvertFills6ToFootprintItem( footprint, elem, true );
4763 }
4764 }
4765
4766 if( reader.GetRemainingBytes() != 0 )
4767 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
4768}
4769
4770
4772{
4773 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
4774 {
4775 // This is not the actual board item. We can use it to create the polygon for the region
4776 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4777
4778 shape.SetStart( aElem.pos1 );
4779 shape.SetEnd( aElem.pos2 );
4780 shape.SetFilled( true );
4782
4783 if( aElem.rotation != 0. )
4784 {
4785 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4786 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4787 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4788 }
4789
4791 }
4792 else
4793 {
4794 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4795 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4796 }
4797}
4798
4799
4801 const bool aIsBoardImport )
4802{
4803 if( aElem.is_keepout
4804 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
4805 {
4806 // This is not the actual board item. We can use it to create the polygon for the region
4807 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4808
4809 shape.SetStart( aElem.pos1 );
4810 shape.SetEnd( aElem.pos2 );
4811 shape.SetFilled( true );
4813
4814 if( aElem.rotation != 0. )
4815 {
4816 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4817 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4818 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4819 }
4820
4821 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4822 aElem.keepoutrestrictions );
4823 }
4824 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
4825 && aElem.net != ALTIUM_NET_UNCONNECTED )
4826 {
4827 // Special case: do to not lose net connections in footprints
4828 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4829 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4830 }
4831 else
4832 {
4833 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4834 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4835 }
4836}
4837
4838
4840{
4841 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::RECTANGLE );
4842
4843 fill->SetFilled( true );
4844 fill->SetLayer( aLayer );
4845 fill->SetStroke( STROKE_PARAMS( 0 ) );
4846
4847 fill->SetStart( aElem.pos1 );
4848 fill->SetEnd( aElem.pos2 );
4849
4850 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4851 {
4852 fill->SetNetCode( GetNetCode( aElem.net ) );
4853 }
4854
4855 if( aElem.rotation != 0. )
4856 {
4857 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4858 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4859 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4860 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4861 }
4862
4863 m_board->Add( fill.release(), ADD_MODE::APPEND );
4864}
4865
4866
4868 PCB_LAYER_ID aLayer )
4869{
4870 if( aLayer == F_Cu || aLayer == B_Cu )
4871 {
4872 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
4873
4874 LSET padLayers;
4875 padLayers.set( aLayer );
4876
4877 pad->SetAttribute( PAD_ATTRIB::SMD );
4878 EDA_ANGLE rotation( aElem.rotation, DEGREES_T );
4879
4880 // Handle rotation multiples of 90 degrees
4881 if( rotation.IsCardinal() )
4882 {
4884
4885 int width = std::abs( aElem.pos2.x - aElem.pos1.x );
4886 int height = std::abs( aElem.pos2.y - aElem.pos1.y );
4887
4888 // Swap width and height for 90 or 270 degree rotations
4889 if( rotation.IsCardinal90() )
4890 std::swap( width, height );
4891
4892 pad->SetSize( PADSTACK::ALL_LAYERS, { width, height } );
4893 pad->SetPosition( aElem.pos1 / 2 + aElem.pos2 / 2 );
4894 }
4895 else
4896 {
4898
4899 int anchorSize = std::min( std::abs( aElem.pos2.x - aElem.pos1.x ),
4900 std::abs( aElem.pos2.y - aElem.pos1.y ) );
4901 VECTOR2I anchorPos = aElem.pos1;
4902
4903 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
4904 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
4905 pad->SetPosition( anchorPos );
4906
4907 SHAPE_POLY_SET shapePolys;
4908 shapePolys.NewOutline();
4909 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4910 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4911 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4912 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4913 shapePolys.Outline( 0 ).SetClosed( true );
4914
4915 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2 - anchorPos.x,
4916 aElem.pos1.y / 2 + aElem.pos2.y / 2 - anchorPos.y );
4917 shapePolys.Rotate( EDA_ANGLE( aElem.rotation, DEGREES_T ), center );
4918 pad->AddPrimitivePoly( F_Cu, shapePolys, 0, true );
4919 }
4920
4921 pad->SetThermalSpokeAngle( ANGLE_90 );
4922 pad->SetLayerSet( padLayers );
4923
4924 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
4925 }
4926 else
4927 {
4928 std::unique_ptr<PCB_SHAPE> fill =
4929 std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::RECTANGLE );
4930
4931 fill->SetFilled( true );
4932 fill->SetLayer( aLayer );
4933 fill->SetStroke( STROKE_PARAMS( 0 ) );
4934
4935 fill->SetStart( aElem.pos1 );
4936 fill->SetEnd( aElem.pos2 );
4937
4938 if( aElem.rotation != 0. )
4939 {
4940 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4941 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4942 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4943 }
4944
4945 aFootprint->Add( fill.release(), ADD_MODE::APPEND );
4946 }
4947}
4948
4949
4950void ALTIUM_PCB::HelperSetZoneLayers( ZONE& aZone, const ALTIUM_LAYER aAltiumLayer )
4951{
4952 LSET layerSet;
4953
4954 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
4955 layerSet.set( klayer );
4956
4957 aZone.SetLayerSet( layerSet );
4958}
4959
4960
4961void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE& aZone, const uint8_t aKeepoutRestrictions )
4962{
4963 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
4964 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
4965 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
4966 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
4967 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
4968
4969 aZone.SetDoNotAllowVias( keepoutRestrictionVia );
4970 aZone.SetDoNotAllowTracks( keepoutRestrictionTrack );
4971 aZone.SetDoNotAllowZoneFills( keepoutRestrictionCopper );
4972 aZone.SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
4973 aZone.SetDoNotAllowFootprints( false );
4974}
4975
4976
4978 const ALTIUM_LAYER aAltiumLayer,
4979 const uint8_t aKeepoutRestrictions )
4980{
4981 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
4982
4983 zone->SetIsRuleArea( true );
4984
4985 HelperSetZoneLayers( *zone, aAltiumLayer );
4986 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4987
4988 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4989
4990 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4992
4993 m_board->Add( zone.release(), ADD_MODE::APPEND );
4994}
4995
4996
4998 const PCB_SHAPE& aShape,
4999 const ALTIUM_LAYER aAltiumLayer,
5000 const uint8_t aKeepoutRestrictions )
5001{
5002 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
5003
5004 zone->SetIsRuleArea( true );
5005
5006 HelperSetZoneLayers( *zone, aAltiumLayer );
5007 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
5008
5009 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
5010
5011 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
5013
5014 // TODO: zone->SetLocalCoord(); missing?
5015 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
5016}
5017
5018
5019std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
5020 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
5021{
5022 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
5023 return {}; // there is nothing to parse
5024
5025 auto elems =
5026 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
5027
5028 if( elems.first == elems.second )
5029 return {}; // there is nothing to parse
5030
5031 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
5032
5033 for( auto it = elems.first; it != elems.second; ++it )
5034 {
5035 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
5036
5038 {
5041 {
5042 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
5043 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
5044 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5045 {
5046 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
5047 }
5048
5049 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
5050 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5051 {
5052 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
5053 }
5054 }
5057 {
5058 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
5059 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5060 {
5061 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
5062 }
5063
5064 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
5065 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
5066 {
5067 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
5068 }
5069 }
5070 }
5071 }
5072
5073 return layerExpansionPairs;
5074}
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:544
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:588
KIFONT::FONT * GetFont() const
Definition eda_text.h:247
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:405
int GetTextWidth() const
Definition eda_text.h:264
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:429
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:390
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:296
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:311
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:319
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:511
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:421
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:330
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:787
bool IsFlipped() const
Definition footprint.h:524
PCB_FIELD & Reference()
Definition footprint.h:788
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:323
const wxString & GetReference() const
Definition footprint.h:751
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition footprint.h:1203
VECTOR2I GetPosition() const override
Definition footprint.h:327
VECTOR3D m_Offset
3D model offset (mm)
Definition footprint.h:122
double m_Opacity
Definition footprint.h:123
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition footprint.h:121
wxString m_Filename
The 3D shape filename in 3D library.
Definition footprint.h:124
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:683
static const LSET & UserMask()
Definition lset.cpp:690
static LSET UserDefinedLayersMask(int aUserDefinedLayerCount=MAX_USER_DEFINED_LAYERS)
Return a mask with the requested number of user defined layers.
Definition lset.cpp:704
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:577
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:945
void SetMode(MODE aMode)
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition padstack.cpp:974
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition padstack.cpp:885
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition padstack.cpp:992
@ 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:859
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:302
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:734
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:614
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:733
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:630
void SetIsFilled(bool isFilled)
Definition zone.h:299
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition zone.h:598
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:551
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:732
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:735
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:731
static int GetDefaultHatchPitch()
Definition zone.cpp:1328
@ 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