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