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