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 = VECTOR2I( KiROUND( std::cos( startradiant ) * vertex.radius ),
106 -KiROUND( std::sin( startradiant ) * vertex.radius ) );
107
108 VECTOR2I arcEndOffset = VECTOR2I( KiROUND( std::cos( endradiant ) * vertex.radius ),
109 -KiROUND( 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( ii >= m_board->GetCopperLayerCount() && layer_num != ALTIUM_LAYER::BOTTOM_LAYER
1159 && !( layer_num >= ALTIUM_LAYER::TOP_OVERLAY
1160 && layer_num <= ALTIUM_LAYER::BOTTOM_SOLDER )
1161 && !( layer_num >= ALTIUM_LAYER::MECHANICAL_1
1162 && layer_num <= ALTIUM_LAYER::MECHANICAL_16 ) )
1163 {
1164 if( layer_num < ALTIUM_LAYER::BOTTOM_LAYER )
1165 continue;
1166
1168 }
1169 else
1170 {
1171 iLdesc.AutoMapLayer = GetKicadLayer( layer_num );
1172 }
1173
1174 iLdesc.Name = curLayer.name;
1175 iLdesc.PermittedLayers = validRemappingLayers;
1176 iLdesc.Required = ii < m_board->GetCopperLayerCount() || layer_num == ALTIUM_LAYER::BOTTOM_LAYER;
1177
1178 inputLayers.push_back( iLdesc );
1179 altiumLayerNameMap.insert( { curLayer.name, layer_num } );
1180 m_layerNames.insert( { layer_num, curLayer.name } );
1181 }
1182
1183 if( inputLayers.size() == 0 )
1184 return;
1185
1186 // Callback:
1187 std::map<wxString, PCB_LAYER_ID> reMappedLayers = m_layerMappingHandler( inputLayers );
1188 m_layermap.clear();
1189
1190 for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers )
1191 {
1192 if( layerPair.second == PCB_LAYER_ID::UNDEFINED_LAYER )
1193 {
1194 wxFAIL_MSG( wxT( "Unexpected Layer ID" ) );
1195 continue;
1196 }
1197
1198 ALTIUM_LAYER altiumID = altiumLayerNameMap.at( layerPair.first );
1199 m_layermap.insert_or_assign( altiumID, layerPair.second );
1200 enabledLayers |= LSET( { layerPair.second } );
1201 }
1202
1203 m_board->SetEnabledLayers( enabledLayers );
1204 m_board->SetVisibleLayers( enabledLayers );
1205}
1206
1207
1208void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
1209{
1210 SHAPE_LINE_CHAIN lineChain;
1211 HelperShapeLineChainFromAltiumVertices( lineChain, aVertices );
1212
1213 STROKE_PARAMS stroke( m_board->GetDesignSettings().GetLineThickness( Edge_Cuts ),
1215
1216 for( int i = 0; i <= lineChain.PointCount() && i != -1; i = lineChain.NextShape( i ) )
1217 {
1218 if( lineChain.IsArcStart( i ) )
1219 {
1220 const SHAPE_ARC& currentArc = lineChain.Arc( lineChain.ArcIndex( i ) );
1221 int nextShape = lineChain.NextShape( i );
1222 bool isLastShape = nextShape < 0;
1223
1224 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::ARC );
1225
1226 shape->SetStroke( stroke );
1227 shape->SetLayer( Edge_Cuts );
1228 shape->SetArcGeometry( currentArc.GetP0(), currentArc.GetArcMid(), currentArc.GetP1() );
1229
1230 m_board->Add( shape.release(), ADD_MODE::APPEND );
1231 }
1232 else
1233 {
1234 const SEG& seg = lineChain.Segment( i );
1235
1236 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1237
1238 shape->SetStroke( stroke );
1239 shape->SetLayer( Edge_Cuts );
1240 shape->SetStart( seg.A );
1241 shape->SetEnd( seg.B );
1242
1243 m_board->Add( shape.release(), ADD_MODE::APPEND );
1244 }
1245 }
1246}
1247
1248
1250 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1251{
1252 if( m_progressReporter )
1253 m_progressReporter->Report( _( "Loading netclasses..." ) );
1254
1255 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1256
1257 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1258 {
1259 checkpoint();
1260 ACLASS6 elem( reader );
1261
1263 {
1264 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( elem.name );
1265
1266 for( const wxString& name : elem.names )
1267 {
1268 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment(
1269 name, nc->GetName() );
1270 }
1271
1272 if( m_board->GetDesignSettings().m_NetSettings->HasNetclass( nc->GetName() ) )
1273 {
1274 // Name conflict, happens in some unknown circumstances
1275 // unique_ptr will delete nc on this code path
1276 if( m_reporter )
1277 {
1278 wxString msg;
1279 msg.Printf( _( "More than one Altium netclass with name '%s' found. "
1280 "Only the first one will be imported." ), elem.name );
1281 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1282 }
1283 }
1284 else
1285 {
1286 m_board->GetDesignSettings().m_NetSettings->SetNetclass( nc->GetName(), nc );
1287 }
1288 }
1289 }
1290
1291 if( reader.GetRemainingBytes() != 0 )
1292 THROW_IO_ERROR( wxT( "Classes6 stream is not fully parsed" ) );
1293
1294 m_board->m_LegacyNetclassesLoaded = true;
1295}
1296
1297
1299 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1300{
1301 if( m_progressReporter )
1302 m_progressReporter->Report( _( "Loading components..." ) );
1303
1304 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1305
1306 uint16_t componentId = 0;
1307
1308 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1309 {
1310 checkpoint();
1311 ACOMPONENT6 elem( reader );
1312
1313 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
1314
1315 // Altium stores the footprint library information needed to find the footprint in the
1316 // source library in the sourcefootprintlibrary field. Since Altium is a Windows-only
1317 // program, the path separator is always a backslash. We need strip the extra path information
1318 // here to prevent overly-long LIB_IDs because KiCad doesn't store the full path to the
1319 // footprint library in the design file, only in a library table.
1320 wxFileName libName( elem.sourcefootprintlibrary, wxPATH_WIN );
1321 LIB_ID fpID = AltiumToKiCadLibID( libName.GetName(), elem.pattern );
1322
1323 footprint->SetFPID( fpID );
1324
1325 footprint->SetPosition( elem.position );
1326 footprint->SetOrientationDegrees( elem.rotation );
1327
1328 // KiCad netlisting requires parts to have non-digit + digit annotation.
1329 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1330 wxString reference = elem.sourcedesignator;
1331
1332 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1333 reference.Prepend( wxT( "UNK" ) );
1334
1335 footprint->SetReference( reference );
1336
1337 KIID id( elem.sourceUniqueID );
1338 KIID pathid( elem.sourceHierachicalPath );
1340 path.push_back( pathid );
1341 path.push_back( id );
1342
1343 footprint->SetPath( path );
1344 footprint->SetSheetname( elem.sourceHierachicalPath );
1345 footprint->SetSheetfile( elem.sourceHierachicalPath + wxT( ".kicad_sch" ));
1346
1347 footprint->SetLocked( elem.locked );
1348 footprint->Reference().SetVisible( elem.nameon );
1349 footprint->Value().SetVisible( elem.commenton );
1350 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1351
1352 m_components.emplace_back( footprint.get() );
1353 m_board->Add( footprint.release(), ADD_MODE::APPEND );
1354
1355 componentId++;
1356 }
1357
1358 if( reader.GetRemainingBytes() != 0 )
1359 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1360}
1361
1362
1364double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1365{
1366 while( Angle < aMin )
1367 Angle += 360.0;
1368
1369 while( Angle >= aMax )
1370 Angle -= 360.0;
1371
1372 return Angle;
1373}
1374
1375
1377 FOOTPRINT* aFootprint,
1378 const ACOMPONENTBODY6& aElem )
1379{
1380 if( m_progressReporter )
1381 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1382
1383 if( !aElem.modelIsEmbedded )
1384 return;
1385
1386 auto model = aAltiumPcbFile.GetLibModel( aElem.modelId );
1387
1388 if( !model )
1389 {
1390 if( m_reporter )
1391 {
1392 m_reporter->Report( wxString::Format( wxT( "Model %s not found for footprint %s" ),
1393 aElem.modelId, aFootprint->GetReference() ),
1395 }
1396
1397 return;
1398 }
1399
1400 const VECTOR2I& fpPosition = aFootprint->GetPosition();
1401
1403 file->name = aElem.modelName;
1404
1405 if( file->name.IsEmpty() )
1406 file->name = model->first.name;
1407
1408 // Decompress the model data before assigning
1409 std::vector<char> decompressedData;
1410 wxMemoryInputStream compressedStream( model->second.data(), model->second.size() );
1411 wxZlibInputStream zlibStream( compressedStream );
1412
1413 // Reserve some space, assuming decompressed data is larger -- STEP file
1414 // compression is typically 5:1 using zlib like Altium does
1415 decompressedData.resize( model->second.size() * 6 );
1416 size_t offset = 0;
1417
1418 while( !zlibStream.Eof() )
1419 {
1420 zlibStream.Read( decompressedData.data() + offset, decompressedData.size() - offset );
1421 size_t bytesRead = zlibStream.LastRead();
1422
1423 if( !bytesRead )
1424 break;
1425
1426 offset += bytesRead;
1427
1428 if( offset >= decompressedData.size() )
1429 decompressedData.resize( 2 * decompressedData.size() ); // Resizing is expensive, avoid if we can
1430 }
1431
1432 decompressedData.resize( offset );
1433
1434 file->decompressedData = std::move( decompressedData );
1436
1438 aFootprint->GetEmbeddedFiles()->AddFile( file );
1439
1440 FP_3DMODEL modelSettings;
1441
1442 modelSettings.m_Filename = aFootprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1443
1444 modelSettings.m_Offset.x = pcbIUScale.IUTomm( (int) aElem.modelPosition.x );
1445 modelSettings.m_Offset.y = -pcbIUScale.IUTomm( (int) aElem.modelPosition.y );
1446 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) aElem.modelPosition.z );
1447
1448 EDA_ANGLE orientation = aFootprint->GetOrientation();
1449
1450 if( aFootprint->IsFlipped() )
1451 {
1452 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1453 orientation = -orientation;
1454 }
1455
1456 VECTOR3D modelRotation( aElem.modelRotation );
1457
1458 if( ( aElem.body_projection == 1 ) != aFootprint->IsFlipped() )
1459 {
1460 modelRotation.x += 180;
1461 modelRotation.z = -modelRotation.z;
1462
1463 modelSettings.m_Offset.z = -DEFAULT_BOARD_THICKNESS_MM - modelSettings.m_Offset.z;
1464 }
1465
1466 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1467
1468 modelSettings.m_Rotation.x = normalizeAngleDegrees( -modelRotation.x, -180, 180 );
1469 modelSettings.m_Rotation.y = normalizeAngleDegrees( -modelRotation.y, -180, 180 );
1470 modelSettings.m_Rotation.z = normalizeAngleDegrees( -modelRotation.z + aElem.rotation
1471 + orientation.AsDegrees(),
1472 -180, 180 );
1473 modelSettings.m_Opacity = aElem.body_opacity_3d;
1474
1475 aFootprint->Models().push_back( modelSettings );
1476}
1477
1478
1480 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1481{
1482 if( m_progressReporter )
1483 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1484
1485 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1486
1487 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1488 {
1489 checkpoint();
1490 ACOMPONENTBODY6 elem( reader );
1491
1492 static const bool skipComponentBodies = ADVANCED_CFG::GetCfg().m_ImportSkipComponentBodies;
1493
1494 if( skipComponentBodies )
1495 continue;
1496
1497 if( elem.component == ALTIUM_COMPONENT_NONE )
1498 continue; // TODO: we do not support components for the board yet
1499
1500 if( m_components.size() <= elem.component )
1501 {
1502 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1503 "component id %d of %zu existing components" ),
1504 elem.component,
1505 m_components.size() ) );
1506 }
1507
1508 if( !elem.modelIsEmbedded )
1509 continue;
1510
1511 auto modelTuple = m_EmbeddedModels.find( elem.modelId );
1512
1513 if( modelTuple == m_EmbeddedModels.end() )
1514 {
1515 if( m_reporter )
1516 {
1517 wxString msg;
1518 msg.Printf( wxT( "ComponentsBodies6 stream tries to access model id %s which does "
1519 "not exist" ), elem.modelId );
1520 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1521 }
1522
1523 continue;
1524 }
1525
1526 const ALTIUM_EMBEDDED_MODEL_DATA& modelData = modelTuple->second;
1527 FOOTPRINT* footprint = m_components.at( elem.component );
1528
1530 file->name = modelData.m_modelname;
1531
1532 wxMemoryInputStream compressedStream( modelData.m_data.data(), modelData.m_data.size() );
1533 wxZlibInputStream zlibStream( compressedStream );
1534 wxMemoryOutputStream decompressedStream;
1535
1536 zlibStream.Read( decompressedStream );
1537 file->decompressedData.resize( decompressedStream.GetSize() );
1538 decompressedStream.CopyTo( file->decompressedData.data(), file->decompressedData.size() );
1539
1541 footprint->GetEmbeddedFiles()->AddFile( file );
1542
1543 FP_3DMODEL modelSettings;
1544
1545 modelSettings.m_Filename = footprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1546 VECTOR2I fpPosition = footprint->GetPosition();
1547
1548 modelSettings.m_Offset.x =
1549 pcbIUScale.IUTomm( KiROUND( elem.modelPosition.x - fpPosition.x ) );
1550 modelSettings.m_Offset.y =
1551 -pcbIUScale.IUTomm( KiROUND( elem.modelPosition.y - fpPosition.y ) );
1552 modelSettings.m_Offset.z = pcbIUScale.IUTomm( KiROUND( elem.modelPosition.z ) );
1553
1554 EDA_ANGLE orientation = footprint->GetOrientation();
1555
1556 if( footprint->IsFlipped() )
1557 {
1558 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1559 orientation = -orientation;
1560 }
1561
1562 if( ( elem.body_projection == 1 ) != footprint->IsFlipped() )
1563 {
1564 elem.modelRotation.x += 180;
1565 elem.modelRotation.z = -elem.modelRotation.z;
1566
1567 modelSettings.m_Offset.z =
1568 -pcbIUScale.IUTomm( m_board->GetDesignSettings().GetBoardThickness() )
1569 - modelSettings.m_Offset.z;
1570 }
1571
1572 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1573
1574 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1575 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1576 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z + elem.rotation
1577 + orientation.AsDegrees(),
1578 -180, 180 );
1579
1580 modelSettings.m_Opacity = elem.body_opacity_3d;
1581
1582 footprint->Models().push_back( modelSettings );
1583 }
1584
1585 if( reader.GetRemainingBytes() != 0 )
1586 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1587}
1588
1589
1591{
1592 if( aElem.referencePoint.size() != 2 )
1593 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1594
1595 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1596
1597 if( klayer == UNDEFINED_LAYER )
1598 {
1599 if( m_reporter )
1600 {
1601 m_reporter->Report( wxString::Format(
1602 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1603 "It has been moved to KiCad layer Eco1_User." ), aElem.layer ),
1605 }
1606
1607 klayer = Eco1_User;
1608 }
1609
1610 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1611 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1612
1613 std::unique_ptr<PCB_DIM_ALIGNED> dimension = std::make_unique<PCB_DIM_ALIGNED>( m_board, PCB_DIM_ALIGNED_T );
1614
1615 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1616 dimension->SetLayer( klayer );
1617 dimension->SetStart( referencePoint0 );
1618
1619 if( referencePoint0 != aElem.xy1 )
1620 {
1630 VECTOR2I direction = aElem.xy1 - referencePoint0;
1631 VECTOR2I referenceDiff = referencePoint1 - referencePoint0;
1632 VECTOR2I directionNormalVector = direction.Perpendicular();
1633 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1634 SEG segm2( referencePoint1, referencePoint1 + direction );
1635 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1636
1637 if( !intersection )
1638 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1639
1640 dimension->SetEnd( *intersection );
1641
1642 int height = direction.EuclideanNorm();
1643
1644 if( direction.Cross( referenceDiff ) > 0 )
1645 height = -height;
1646
1647 dimension->SetHeight( height );
1648 }
1649 else
1650 {
1651 dimension->SetEnd( referencePoint1 );
1652 }
1653
1654 dimension->SetLineThickness( aElem.linewidth );
1655
1656 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
1657 dimension->SetPrefix( aElem.textprefix );
1658
1659
1660 int dist = ( dimension->GetEnd() - dimension->GetStart() ).EuclideanNorm();
1661
1662 if( dist < 3 * dimension->GetArrowLength() )
1663 dimension->SetArrowDirection( DIM_ARROW_DIRECTION::INWARD );
1664
1665 // Suffix normally (but not always) holds the units
1666 wxRegEx units( wxS( "(mm)|(in)|(mils)|(thou)|(')|(\")" ), wxRE_ADVANCED );
1667
1668 if( units.Matches( aElem.textsuffix ) )
1669 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX );
1670 else
1671 dimension->SetSuffix( aElem.textsuffix );
1672
1673 dimension->SetTextThickness( aElem.textlinewidth );
1674 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1675 dimension->SetItalic( aElem.textitalic );
1676
1677#if 0 // we don't currently support bold; map to thicker text
1678 dimension->Text().SetBold( aElem.textbold );
1679#else
1680 if( aElem.textbold )
1681 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1682#endif
1683
1684 switch( aElem.textunit )
1685 {
1686 case ALTIUM_UNIT::INCH: dimension->SetUnits( EDA_UNITS::INCH ); break;
1687 case ALTIUM_UNIT::MILS: dimension->SetUnits( EDA_UNITS::MILS ); break;
1688 case ALTIUM_UNIT::MM: dimension->SetUnits( EDA_UNITS::MM ); break;
1689 case ALTIUM_UNIT::CM: dimension->SetUnits( EDA_UNITS::MM ); break;
1690 default: break;
1691 }
1692
1693 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1694}
1695
1696
1698{
1699 if( aElem.referencePoint.size() < 2 )
1700 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1701
1702 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1703
1704 if( klayer == UNDEFINED_LAYER )
1705 {
1706 if( m_reporter )
1707 {
1708 m_reporter->Report( wxString::Format(
1709 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1710 "It has been moved to KiCad layer Eco1_User." ),
1711 aElem.layer ), RPT_SEVERITY_INFO );
1712 }
1713
1714 klayer = Eco1_User;
1715 }
1716
1717 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1718 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1719
1720 std::unique_ptr<PCB_DIM_RADIAL> dimension = std::make_unique<PCB_DIM_RADIAL>( m_board );
1721
1722 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1723 dimension->SetLayer( klayer );
1724 dimension->SetStart( referencePoint0 );
1725 dimension->SetEnd( aElem.xy1 );
1726 dimension->SetLineThickness( aElem.linewidth );
1727 dimension->SetKeepTextAligned( false );
1728
1729 dimension->SetPrefix( aElem.textprefix );
1730
1731 // Suffix normally holds the units
1732 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1734
1735 switch( aElem.textunit )
1736 {
1737 case ALTIUM_UNIT::INCH: dimension->SetUnits( EDA_UNITS::INCH ); break;
1738 case ALTIUM_UNIT::MILS: dimension->SetUnits( EDA_UNITS::MILS ); break;
1739 case ALTIUM_UNIT::MM: dimension->SetUnits( EDA_UNITS::MM ); break;
1740 case ALTIUM_UNIT::CM: dimension->SetUnits( EDA_UNITS::MM ); break;
1741 default: break;
1742 }
1743
1744 if( aElem.textPoint.empty() )
1745 {
1746 if( m_reporter )
1747 {
1748 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1750 }
1751
1752 return;
1753 }
1754
1755 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1756 dimension->SetTextThickness( aElem.textlinewidth );
1757 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1758 dimension->SetItalic( aElem.textitalic );
1759
1760#if 0 // we don't currently support bold; map to thicker text
1761 dimension->SetBold( aElem.textbold );
1762#else
1763 if( aElem.textbold )
1764 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1765#endif
1766
1767 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1768 // close.
1769 dimension->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1770 dimension->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1771
1772 int yAdjust = dimension->GetTextBox( nullptr ).GetCenter().y - dimension->GetTextPos().y;
1773 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1774 dimension->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
1775
1776 m_radialDimensions.push_back( dimension.get() );
1777 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1778}
1779
1780
1782{
1783 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1784
1785 if( klayer == UNDEFINED_LAYER )
1786 {
1787 if( m_reporter )
1788 {
1789 wxString msg;
1790 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1791 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1792 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1793 }
1794
1795 klayer = Eco1_User;
1796 }
1797
1798 if( !aElem.referencePoint.empty() )
1799 {
1800 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1801
1802 // line
1803 VECTOR2I last = referencePoint0;
1804 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1805 {
1806 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1807
1808 shape->SetLayer( klayer );
1809 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1810 shape->SetStart( last );
1811 shape->SetEnd( aElem.referencePoint.at( i ) );
1812 last = aElem.referencePoint.at( i );
1813
1814 m_board->Add( shape.release(), ADD_MODE::APPEND );
1815 }
1816
1817 // arrow
1818 if( aElem.referencePoint.size() >= 2 )
1819 {
1820 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1821
1822 if( dirVec.x != 0 || dirVec.y != 0 )
1823 {
1824 double scaling = (double) dirVec.EuclideanNorm() / aElem.arrowsize;
1825 VECTOR2I arrVec = VECTOR2I( KiROUND( dirVec.x / scaling ), KiROUND( dirVec.y / scaling ) );
1826 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1827
1828 {
1829 std::unique_ptr<PCB_SHAPE> shape1 = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1830
1831 shape1->SetLayer( klayer );
1832 shape1->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1833 shape1->SetStart( referencePoint0 );
1834 shape1->SetEnd( referencePoint0 + arrVec );
1835
1836 m_board->Add( shape1.release(), ADD_MODE::APPEND );
1837 }
1838
1839 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1840
1841 {
1842 std::unique_ptr<PCB_SHAPE> shape2 = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1843
1844 shape2->SetLayer( klayer );
1845 shape2->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1846 shape2->SetStart( referencePoint0 );
1847 shape2->SetEnd( referencePoint0 + arrVec );
1848
1849 m_board->Add( shape2.release(), ADD_MODE::APPEND );
1850 }
1851 }
1852 }
1853 }
1854
1855 if( aElem.textPoint.empty() )
1856 {
1857 if( m_reporter )
1858 {
1859 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1861 }
1862
1863 return;
1864 }
1865
1866 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
1867
1868 text->SetText( aElem.textformat );
1869 text->SetPosition( aElem.textPoint.at( 0 ) );
1870 text->SetLayer( klayer );
1871 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1872 text->SetTextThickness( aElem.textlinewidth );
1873 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1874 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1875
1876 m_board->Add( text.release(), ADD_MODE::APPEND );
1877}
1878
1879
1881{
1882 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1883
1884 if( klayer == UNDEFINED_LAYER )
1885 {
1886 if( m_reporter )
1887 {
1888 wxString msg;
1889 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1890 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1891 m_reporter->Report( msg, RPT_SEVERITY_INFO );
1892 }
1893
1894 klayer = Eco1_User;
1895 }
1896
1897 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1898 {
1899 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1900
1901 shape->SetLayer( klayer );
1902 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1903 shape->SetStart( aElem.referencePoint.at( i ) );
1904 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1905
1906 m_board->Add( shape.release(), ADD_MODE::APPEND );
1907 }
1908}
1909
1910
1912{
1913 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1914
1915 if( klayer == UNDEFINED_LAYER )
1916 {
1917 if( m_reporter )
1918 {
1919 wxString msg;
1920 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1921 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1922 m_reporter->Report( msg, RPT_SEVERITY_INFO );
1923 }
1924
1925 klayer = Eco1_User;
1926 }
1927
1928 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1929 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1930
1931 std::unique_ptr<PCB_DIM_CENTER> dimension = std::make_unique<PCB_DIM_CENTER>( m_board );
1932
1933 dimension->SetLayer( klayer );
1934 dimension->SetLineThickness( aElem.linewidth );
1935 dimension->SetStart( aElem.xy1 );
1936 dimension->SetEnd( aElem.xy1 + vec );
1937
1938 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1939}
1940
1941
1943 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1944{
1945 if( m_progressReporter )
1946 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1947
1948 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1949
1950 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1951 {
1952 checkpoint();
1953 ADIMENSION6 elem( reader );
1954
1955 switch( elem.kind )
1956 {
1959 break;
1961 if( m_reporter )
1962 {
1963 m_reporter->Report( wxString::Format( _( "Ignored Angular dimension (not yet supported)." ) ),
1965 }
1966 break;
1969 break;
1972 break;
1974 if( m_reporter )
1975 {
1976 m_reporter->Report( wxString::Format( _( "Ignored Datum dimension (not yet supported)." ) ),
1978 }
1979 // HelperParseDimensions6Datum( elem );
1980 break;
1982 if( m_reporter )
1983 {
1984 m_reporter->Report( wxString::Format( _( "Ignored Baseline dimension (not yet supported)." ) ),
1986 }
1987 break;
1990 break;
1992 if( m_reporter )
1993 {
1994 m_reporter->Report( wxString::Format( _( "Ignored Linear dimension (not yet supported)." ) ),
1996 }
1997 break;
1999 if( m_reporter )
2000 {
2001 m_reporter->Report( wxString::Format( _( "Ignored Radial dimension (not yet supported)." ) ),
2003 }
2004 break;
2005 default:
2006 if( m_reporter )
2007 {
2008 wxString msg;
2009 msg.Printf( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
2010 m_reporter->Report( msg, RPT_SEVERITY_INFO );
2011 }
2012 break;
2013 }
2014 }
2015
2016 if( reader.GetRemainingBytes() != 0 )
2017 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
2018}
2019
2020
2022 const CFB::COMPOUND_FILE_ENTRY* aEntry,
2023 const std::vector<std::string>& aRootDir )
2024{
2025 if( m_progressReporter )
2026 m_progressReporter->Report( _( "Loading 3D models..." ) );
2027
2028 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2029
2030 if( reader.GetRemainingBytes() == 0 )
2031 return;
2032
2033 int idx = 0;
2034 wxString invalidChars = wxFileName::GetForbiddenChars();
2035
2036 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2037 {
2038 checkpoint();
2039 AMODEL elem( reader );
2040
2041 std::vector<std::string> stepPath = aRootDir;
2042 stepPath.emplace_back( std::to_string( idx ) );
2043
2044 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii()
2045 && wxString::npos == elem.name.find_first_of( invalidChars );
2046 wxString storageName = validName ? elem.name : wxString::Format( wxT( "model_%d" ), idx );
2047
2048 idx++;
2049
2050 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
2051
2052 if( stepEntry == nullptr )
2053 {
2054 if( m_reporter )
2055 {
2056 wxString msg;
2057 msg.Printf( _( "File not found: '%s'. 3D-model not imported." ), FormatPath( stepPath ) );
2058 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2059 }
2060
2061 continue;
2062 }
2063
2064 size_t stepSize = static_cast<size_t>( stepEntry->size );
2065 std::vector<char> stepContent( stepSize );
2066
2067 // read file into buffer
2068 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
2069 stepSize );
2070
2071 m_EmbeddedModels.insert( std::make_pair(
2072 elem.id, ALTIUM_EMBEDDED_MODEL_DATA( storageName, elem.rotation, elem.z_offset,
2073 std::move( stepContent ) ) ) );
2074 }
2075
2076 // Append _<index> to duplicate filenames
2077 std::map<wxString, std::vector<wxString>> nameIdMap;
2078
2079 for( auto& [id, data] : m_EmbeddedModels )
2080 nameIdMap[data.m_modelname].push_back( id );
2081
2082 for( auto& [name, ids] : nameIdMap )
2083 {
2084 for( size_t i = 1; i < ids.size(); i++ )
2085 {
2086 const wxString& id = ids[i];
2087
2088 auto modelTuple = m_EmbeddedModels.find( id );
2089
2090 if( modelTuple == m_EmbeddedModels.end() )
2091 continue;
2092
2093 wxString modelName = modelTuple->second.m_modelname;
2094
2095 if( modelName.Contains( "." ) )
2096 {
2097 wxString ext;
2098 wxString baseName = modelName.BeforeLast( '.', &ext );
2099
2100 modelTuple->second.m_modelname = baseName + '_' + std::to_string( i ) + '.' + ext;
2101 }
2102 else
2103 {
2104 modelTuple->second.m_modelname = modelName + '_' + std::to_string( i );
2105 }
2106 }
2107 }
2108
2109 if( reader.GetRemainingBytes() != 0 )
2110 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
2111}
2112
2113
2115 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2116{
2117 if( m_progressReporter )
2118 m_progressReporter->Report( _( "Loading nets..." ) );
2119
2120 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2121
2122 wxASSERT( m_altiumToKicadNetcodes.empty() );
2123
2124 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2125 {
2126 checkpoint();
2127 ANET6 elem( reader );
2128
2129 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, -1 );
2130 m_board->Add( netInfo, ADD_MODE::APPEND );
2131
2132 // needs to be called after m_board->Add() as assign us the NetCode
2133 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
2134 }
2135
2136 if( reader.GetRemainingBytes() != 0 )
2137 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
2138}
2139
2141 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2142{
2143 if( m_progressReporter )
2144 m_progressReporter->Report( _( "Loading polygons..." ) );
2145
2146 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2147
2148 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2149 {
2150 checkpoint();
2151 APOLYGON6 elem( reader );
2152
2153 SHAPE_LINE_CHAIN linechain;
2155
2156 if( linechain.PointCount() < 3 )
2157 {
2158 // We have found multiple Altium files with polygon records containing nothing but two
2159 // coincident vertices. These polygons do not appear when opening the file in Altium.
2160 // https://gitlab.com/kicad/code/kicad/-/issues/8183
2161 // Also, polygons with less than 3 points are not supported in KiCad.
2162 //
2163 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
2164 // "points are required." ),
2165 // linechain.PointCount(),
2166 // elem.vertices.size() );
2167
2168 m_polygons.emplace_back( nullptr );
2169 continue;
2170 }
2171
2172 SHAPE_POLY_SET outline( linechain );
2173
2175 {
2176 // Altium "Hatched" or "None" polygon outlines have thickness, convert it to KiCad's representation.
2178 ARC_HIGH_DEF, true );
2179 }
2180
2181 if( outline.OutlineCount() != 1 && m_reporter )
2182 {
2183 wxString msg;
2184 msg.Printf( _( "Polygon outline count is %d, expected 1." ), outline.OutlineCount() );
2185
2186 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2187 }
2188
2189 if( outline.OutlineCount() == 0 )
2190 continue;
2191
2192 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>(m_board);
2193
2194 // Be sure to set the zone layer before setting the net code
2195 // so that we know that this is a copper zone and so needs a valid net code.
2196 HelperSetZoneLayers( *zone, elem.layer );
2197 zone->SetNetCode( GetNetCode( elem.net ) );
2198 zone->SetPosition( elem.vertices.at( 0 ).position );
2199 zone->SetLocked( elem.locked );
2200 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
2201 zone->Outline()->AddOutline( outline.Outline( 0 ) );
2202
2203 if( elem.pourindex > m_highest_pour_index )
2205
2206 const ARULE6* planeClearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
2207 const ARULE6* zoneClearanceRule = GetRule( ALTIUM_RULE_KIND::CLEARANCE, wxT( "PolygonClearance" ) );
2208 int planeLayers = 0;
2209 int signalLayers = 0;
2210 int clearance = 0;
2211
2212 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
2213 {
2214 LAYER_T layerType = m_board->GetLayerType( layer );
2215
2216 if( layerType == LT_POWER || layerType == LT_MIXED )
2217 planeLayers++;
2218
2219 if( layerType == LT_SIGNAL || layerType == LT_MIXED )
2220 signalLayers++;
2221 }
2222
2223 if( planeLayers > 0 && planeClearanceRule )
2224 clearance = std::max( clearance, planeClearanceRule->planeclearanceClearance );
2225
2226 if( signalLayers > 0 && zoneClearanceRule )
2227 clearance = std::max( clearance, zoneClearanceRule->clearanceGap );
2228
2229 if( clearance > 0 )
2230 zone->SetLocalClearance( clearance );
2231
2232 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
2233
2234 if( polygonConnectRule != nullptr )
2235 {
2236 switch( polygonConnectRule->polygonconnectStyle )
2237 {
2239 zone->SetPadConnection( ZONE_CONNECTION::FULL );
2240 break;
2241
2243 zone->SetPadConnection( ZONE_CONNECTION::NONE );
2244 break;
2245
2246 default:
2248 zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
2249 break;
2250 }
2251
2252 // TODO: correct variables?
2253 zone->SetThermalReliefSpokeWidth(
2254 polygonConnectRule->polygonconnectReliefconductorwidth );
2255 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
2256
2257 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
2258 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
2259 }
2260
2261 if( IsAltiumLayerAPlane( elem.layer ) )
2262 {
2263 // outer zone will be set to priority 0 later.
2264 zone->SetAssignedPriority( 1 );
2265
2266 // check if this is the outer zone by simply comparing the BBOX
2267 const auto& outer_plane = m_outer_plane.find( elem.layer );
2268 if( outer_plane == m_outer_plane.end()
2269 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
2270 {
2271 m_outer_plane[elem.layer] = zone.get();
2272 }
2273 }
2274
2277 {
2278 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
2279 zone->SetHatchThickness( elem.trackwidth );
2280
2282 {
2283 // use a small hack to get us only an outline (hopefully)
2284 const BOX2I& bbox = zone->GetBoundingBox();
2285 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
2286 }
2287 else
2288 {
2289 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
2290 }
2291
2293 zone->SetHatchOrientation( ANGLE_45 );
2294 }
2295
2296 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2298
2299 m_polygons.emplace_back( zone.get() );
2300 m_board->Add( zone.release(), ADD_MODE::APPEND );
2301 }
2302
2303 if( reader.GetRemainingBytes() != 0 )
2304 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
2305}
2306
2308 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2309{
2310 if( m_progressReporter )
2311 m_progressReporter->Report( _( "Loading rules..." ) );
2312
2313 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2314
2315 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2316 {
2317 checkpoint();
2318 ARULE6 elem( reader );
2319
2320 m_rules[elem.kind].emplace_back( elem );
2321 }
2322
2323 // sort rules by priority
2324 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
2325 {
2326 std::sort( val.second.begin(), val.second.end(),
2327 []( const ARULE6& lhs, const ARULE6& rhs )
2328 {
2329 return lhs.priority < rhs.priority;
2330 } );
2331 }
2332
2333 const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::CLEARANCE );
2334 const ARULE6* trackWidthRule = GetRuleDefault( ALTIUM_RULE_KIND::WIDTH );
2335 const ARULE6* routingViasRule = GetRuleDefault( ALTIUM_RULE_KIND::ROUTING_VIAS );
2336 const ARULE6* holeSizeRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_SIZE );
2338
2339 if( clearanceRule )
2340 m_board->GetDesignSettings().m_MinClearance = clearanceRule->clearanceGap;
2341
2342 if( trackWidthRule )
2343 {
2344 m_board->GetDesignSettings().m_TrackMinWidth = trackWidthRule->minLimit;
2345 // TODO: construct a custom rule for preferredWidth and maxLimit values
2346 }
2347
2348 if( routingViasRule )
2349 {
2350 m_board->GetDesignSettings().m_ViasMinSize = routingViasRule->minWidth;
2351 m_board->GetDesignSettings().m_MinThroughDrill = routingViasRule->minHoleWidth;
2352 }
2353
2354 if( holeSizeRule )
2355 {
2356 // TODO: construct a custom rule for minLimit / maxLimit values
2357 }
2358
2359 if( holeToHoleRule )
2360 m_board->GetDesignSettings().m_HoleToHoleMin = holeToHoleRule->clearanceGap;
2361
2364
2365 if( soldermaskRule )
2366 m_board->GetDesignSettings().m_SolderMaskExpansion = soldermaskRule->soldermaskExpansion;
2367
2368 if( pastemaskRule )
2369 m_board->GetDesignSettings().m_SolderPasteMargin = pastemaskRule->pastemaskExpansion;
2370
2371 if( reader.GetRemainingBytes() != 0 )
2372 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
2373}
2374
2376 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2377{
2378 if( m_progressReporter )
2379 m_progressReporter->Report( _( "Loading board regions..." ) );
2380
2381 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2382
2383 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2384 {
2385 checkpoint();
2386 AREGION6 elem( reader, false );
2387
2388 // TODO: implement?
2389 }
2390
2391 if( reader.GetRemainingBytes() != 0 )
2392 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
2393}
2394
2396 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2397{
2398 if( m_progressReporter )
2399 m_progressReporter->Report( _( "Loading polygons..." ) );
2400
2401 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2402
2403 /* TODO: use Header section of file */
2404 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2405 {
2406 checkpoint();
2407 AREGION6 elem( reader, true );
2408
2411 {
2412 // TODO: implement all different types for footprints
2414 }
2415 else
2416 {
2417 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2418 ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
2419 }
2420 }
2421
2422 if( reader.GetRemainingBytes() != 0 )
2423 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
2424}
2425
2426
2428{
2430 {
2432 }
2433 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2434 {
2435 SHAPE_LINE_CHAIN linechain;
2437
2438 if( linechain.PointCount() < 3 )
2439 {
2440 // We have found multiple Altium files with polygon records containing nothing but
2441 // two coincident vertices. These polygons do not appear when opening the file in
2442 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2443 // Also, polygons with less than 3 points are not supported in KiCad.
2444 return;
2445 }
2446
2447 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2448
2449 zone->SetIsRuleArea( true );
2450
2451 if( aElem.is_keepout )
2452 {
2454 }
2455 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2456 {
2457 zone->SetDoNotAllowZoneFills( true );
2458 zone->SetDoNotAllowVias( false );
2459 zone->SetDoNotAllowTracks( false );
2460 zone->SetDoNotAllowPads( false );
2461 zone->SetDoNotAllowFootprints( false );
2462 }
2463
2464 zone->SetPosition( aElem.outline.at( 0 ).position );
2465 zone->Outline()->AddOutline( linechain );
2466
2467 HelperSetZoneLayers( *zone, aElem.layer );
2468
2469 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2471
2472 m_board->Add( zone.release(), ADD_MODE::APPEND );
2473 }
2474 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2475 {
2476 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2477
2478 if( klayer == UNDEFINED_LAYER )
2479 {
2480 if( m_reporter )
2481 {
2482 wxString msg;
2483 msg.Printf( _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
2484 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
2485 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2486 }
2487
2488 klayer = Eco1_User;
2489 }
2490
2491 SHAPE_LINE_CHAIN linechain;
2493
2494 if( linechain.PointCount() < 3 )
2495 {
2496 // We have found multiple Altium files with polygon records containing nothing but
2497 // two coincident vertices. These polygons do not appear when opening the file in
2498 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2499 // Also, polygons with less than 3 points are not supported in KiCad.
2500 return;
2501 }
2502
2503 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2504
2505 shape->SetPolyShape( linechain );
2506 shape->SetFilled( false );
2507 shape->SetLayer( klayer );
2508 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2509
2510 m_board->Add( shape.release(), ADD_MODE::APPEND );
2511 }
2512 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2513 {
2514 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2515 {
2516 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2518 }
2519 }
2520 else if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2521 {
2523 }
2524 else
2525 {
2526 if( m_reporter )
2527 {
2528 wxString msg;
2529 msg.Printf( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2530 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2531 }
2532 }
2533}
2534
2535
2537 const AREGION6& aElem,
2538 const int aPrimitiveIndex )
2539{
2540 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2541 {
2542 SHAPE_LINE_CHAIN linechain;
2544
2545 if( linechain.PointCount() < 3 )
2546 {
2547 // We have found multiple Altium files with polygon records containing nothing but
2548 // two coincident vertices. These polygons do not appear when opening the file in
2549 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2550 // Also, polygons with less than 3 points are not supported in KiCad.
2551 return;
2552 }
2553
2554 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
2555
2556 zone->SetIsRuleArea( true );
2557
2558 if( aElem.is_keepout )
2559 {
2561 }
2562 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2563 {
2564 zone->SetDoNotAllowZoneFills( true );
2565 zone->SetDoNotAllowVias( false );
2566 zone->SetDoNotAllowTracks( false );
2567 zone->SetDoNotAllowPads( false );
2568 zone->SetDoNotAllowFootprints( false );
2569 }
2570
2571 zone->SetPosition( aElem.outline.at( 0 ).position );
2572 zone->Outline()->AddOutline( linechain );
2573
2574 HelperSetZoneLayers( *zone, aElem.layer );
2575
2576 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2578
2579 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
2580 }
2581 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2582 {
2583 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2584 {
2585 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2586 {
2587 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
2588 aPrimitiveIndex );
2589 }
2590 }
2591 }
2594 {
2596 ? Edge_Cuts
2597 : GetKicadLayer( aElem.layer );
2598
2599 if( klayer == UNDEFINED_LAYER )
2600 {
2601 if( !m_footprintName.IsEmpty() )
2602 {
2603 if( m_reporter )
2604 {
2605 wxString msg;
2606 msg.Printf( _( "Loading library '%s':\n"
2607 "Footprint %s contains a dashed outline on Altium layer (%d) with "
2608 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2609 m_library,
2611 aElem.layer );
2612 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2613 }
2614 }
2615 else
2616 {
2617 if( m_reporter )
2618 {
2619 wxString msg;
2620 msg.Printf( _( "Footprint %s contains a dashed outline on Altium layer (%d) with "
2621 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2622 aFootprint->GetReference(),
2623 aElem.layer );
2624 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2625 }
2626 }
2627
2628 klayer = Eco1_User;
2629 }
2630
2631 SHAPE_LINE_CHAIN linechain;
2633
2634 if( linechain.PointCount() < 3 )
2635 {
2636 // We have found multiple Altium files with polygon records containing nothing but
2637 // two coincident vertices. These polygons do not appear when opening the file in
2638 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2639 // Also, polygons with less than 3 points are not supported in KiCad.
2640 return;
2641 }
2642
2643 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2644
2645 shape->SetPolyShape( linechain );
2646 shape->SetFilled( false );
2647 shape->SetLayer( klayer );
2648
2650 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2651 else
2652 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::SOLID ) );
2653
2654 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2655 }
2656 else
2657 {
2658 if( !m_footprintName.IsEmpty() )
2659 {
2660 if( m_reporter )
2661 {
2662 wxString msg;
2663 msg.Printf( _( "Error loading library '%s':\n"
2664 "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2665 m_library,
2667 aElem.kind );
2668 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2669 }
2670 }
2671 else
2672 {
2673 if( m_reporter )
2674 {
2675 wxString msg;
2676 msg.Printf( _( "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2677 aFootprint->GetReference(),
2678 aElem.kind );
2679 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2680 }
2681 }
2682 }
2683}
2684
2685
2687 PCB_LAYER_ID aLayer )
2688{
2689 SHAPE_LINE_CHAIN linechain;
2691
2692 if( linechain.PointCount() < 3 )
2693 {
2694 // We have found multiple Altium files with polygon records containing nothing
2695 // but two coincident vertices. These polygons do not appear when opening the
2696 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2697 // Also, polygons with less than 3 points are not supported in KiCad.
2698 return;
2699 }
2700
2701 SHAPE_POLY_SET polySet;
2702 polySet.AddOutline( linechain );
2703
2704 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2705 {
2706 SHAPE_LINE_CHAIN hole_linechain;
2707 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2708
2709 if( hole_linechain.PointCount() < 3 )
2710 continue;
2711
2712 polySet.AddHole( hole_linechain );
2713 }
2714
2715 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2716
2717 shape->SetPolyShape( polySet );
2718 shape->SetFilled( true );
2719 shape->SetLayer( aLayer );
2720 shape->SetStroke( STROKE_PARAMS( 0 ) );
2721
2722 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2723 {
2724 shape->SetNetCode( GetNetCode( aElem.net ) );
2725 }
2726
2727 m_board->Add( shape.release(), ADD_MODE::APPEND );
2728}
2729
2730
2732 const AREGION6& aElem,
2733 PCB_LAYER_ID aLayer,
2734 const int aPrimitiveIndex )
2735{
2736 SHAPE_LINE_CHAIN linechain;
2738
2739 if( linechain.PointCount() < 3 )
2740 {
2741 // We have found multiple Altium files with polygon records containing nothing
2742 // but two coincident vertices. These polygons do not appear when opening the
2743 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2744 // Also, polygons with less than 3 points are not supported in KiCad.
2745 return;
2746 }
2747
2748 SHAPE_POLY_SET polySet;
2749 polySet.AddOutline( linechain );
2750
2751 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2752 {
2753 SHAPE_LINE_CHAIN hole_linechain;
2754 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2755
2756 if( hole_linechain.PointCount() < 3 )
2757 continue;
2758
2759 polySet.AddHole( hole_linechain );
2760 }
2761
2762 if( aLayer == F_Cu || aLayer == B_Cu )
2763 {
2764 // TODO(JE) padstacks -- not sure what should happen here yet
2765 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
2766
2767 LSET padLayers;
2768 padLayers.set( aLayer );
2769
2770 pad->SetAttribute( PAD_ATTRIB::SMD );
2772 pad->SetThermalSpokeAngle( ANGLE_90 );
2773
2774 int anchorSize = 1;
2775 VECTOR2I anchorPos = linechain.CPoint( 0 );
2776
2777 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
2778 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
2779 pad->SetPosition( anchorPos );
2780
2781 SHAPE_POLY_SET shapePolys = polySet;
2782 shapePolys.Move( -anchorPos );
2783 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, shapePolys, 0, true );
2784
2786 auto it = map.find( aPrimitiveIndex );
2787
2788 if( it != map.end() )
2789 {
2790 const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
2791
2792 if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2793 {
2794 pad->SetLocalSolderPasteMargin( info.pastemaskexpansionmanual );
2795 }
2796
2797 if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2798 {
2799 pad->SetLocalSolderMaskMargin( info.soldermaskexpansionmanual );
2800 }
2801
2802 if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
2803 padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
2804
2805 if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
2806 padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
2807 }
2808
2809 pad->SetLayerSet( padLayers );
2810
2811 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
2812 }
2813 else
2814 {
2815 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2816
2817 shape->SetPolyShape( polySet );
2818 shape->SetFilled( true );
2819 shape->SetLayer( aLayer );
2820 shape->SetStroke( STROKE_PARAMS( 0 ) );
2821
2822 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2823 }
2824}
2825
2826
2828 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2829{
2830 if( m_progressReporter )
2831 m_progressReporter->Report( _( "Loading zone fills..." ) );
2832
2833 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2834
2835 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2836 {
2837 checkpoint();
2838 AREGION6 elem( reader, false );
2839
2840 if( elem.polygon != ALTIUM_POLYGON_NONE )
2841 {
2842 if( m_polygons.size() <= elem.polygon )
2843 {
2844 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2845 "of %d existing polygons.",
2846 elem.polygon,
2847 m_polygons.size() ) );
2848 }
2849
2850 ZONE* zone = m_polygons.at( elem.polygon );
2851
2852 if( zone == nullptr )
2853 {
2854 continue; // we know the zone id, but because we do not know the layer we did not
2855 // add it!
2856 }
2857
2858 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2859
2860 if( klayer == UNDEFINED_LAYER )
2861 continue; // Just skip it for now. Users can fill it themselves.
2862
2863 SHAPE_LINE_CHAIN linechain;
2864
2865 for( const ALTIUM_VERTICE& vertice : elem.outline )
2866 linechain.Append( vertice.position );
2867
2868 linechain.Append( elem.outline.at( 0 ).position );
2869 linechain.SetClosed( true );
2870
2871 SHAPE_POLY_SET fill;
2872 fill.AddOutline( linechain );
2873
2874 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2875 {
2876 SHAPE_LINE_CHAIN hole_linechain;
2877
2878 for( const ALTIUM_VERTICE& vertice : hole )
2879 hole_linechain.Append( vertice.position );
2880
2881 hole_linechain.Append( hole.at( 0 ).position );
2882 hole_linechain.SetClosed( true );
2883 fill.AddHole( hole_linechain );
2884 }
2885
2886 if( zone->HasFilledPolysForLayer( klayer ) )
2887 fill.BooleanAdd( *zone->GetFill( klayer ) );
2888
2889 fill.Fracture();
2890
2891 zone->SetFilledPolysList( klayer, fill );
2892 zone->SetIsFilled( true );
2893 zone->SetNeedRefill( false );
2894 }
2895 }
2896
2897 if( reader.GetRemainingBytes() != 0 )
2898 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2899}
2900
2901
2903 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2904{
2905 if( m_progressReporter )
2906 m_progressReporter->Report( _( "Loading arcs..." ) );
2907
2908 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2909
2910 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2911 {
2912 checkpoint();
2913 AARC6 elem( reader );
2914
2915 if( elem.component == ALTIUM_COMPONENT_NONE )
2916 {
2917 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2918 }
2919 else
2920 {
2921 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2922 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2923 }
2924 }
2925
2926 if( reader.GetRemainingBytes() != 0 )
2927 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2928}
2929
2930
2932{
2933 if( aElem.startangle == 0. && aElem.endangle == 360. )
2934 {
2935 aShape->SetShape( SHAPE_T::CIRCLE );
2936
2937 // TODO: other variants to define circle?
2938 aShape->SetStart( aElem.center );
2939 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
2940 }
2941 else
2942 {
2943 aShape->SetShape( SHAPE_T::ARC );
2944
2945 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2946 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2947
2948 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2949 -KiROUND( startAngle.Sin() * aElem.radius ) );
2950
2951 aShape->SetCenter( aElem.center );
2952 aShape->SetStart( aElem.center + startOffset );
2953 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
2954 }
2955}
2956
2957
2958void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
2959{
2960 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
2961 {
2962 if( m_polygons.size() <= aElem.polygon )
2963 {
2964 THROW_IO_ERROR( wxString::Format( "Tracks stream tries to access polygon id %u "
2965 "of %zu existing polygons.",
2966 aElem.polygon, m_polygons.size() ) );
2967 }
2968
2969 ZONE* zone = m_polygons.at( aElem.polygon );
2970
2971 if( zone == nullptr )
2972 {
2973 return; // we know the zone id, but because we do not know the layer we did not
2974 // add it!
2975 }
2976
2977 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2978
2979 if( klayer == UNDEFINED_LAYER )
2980 return; // Just skip it for now. Users can fill it themselves.
2981
2982 if( !zone->HasFilledPolysForLayer( klayer ) )
2983 return;
2984
2985 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
2986
2987 // This is not the actual board item. We can use it to create the polygon for the region
2988 PCB_SHAPE shape( nullptr );
2989
2990 ConvertArcs6ToPcbShape( aElem, &shape );
2992
2993 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
2994 // Will be simplified and fractured later
2995
2996 zone->SetIsFilled( true );
2997 zone->SetNeedRefill( false );
2998
2999 return;
3000 }
3001
3002 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3003 || IsAltiumLayerAPlane( aElem.layer ) )
3004 {
3005 // This is not the actual board item. We can use it to create the polygon for the region
3006 PCB_SHAPE shape( nullptr );
3007
3008 ConvertArcs6ToPcbShape( aElem, &shape );
3010
3012 }
3013 else
3014 {
3015 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3016 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
3017 }
3018
3019 for( const auto& layerExpansionMask :
3021 {
3022 int width = aElem.width + ( layerExpansionMask.second * 2 );
3023
3024 if( width > 1 )
3025 {
3026 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
3027
3028 ConvertArcs6ToPcbShape( aElem, arc.get() );
3029 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3030 arc->SetLayer( layerExpansionMask.first );
3031
3032 m_board->Add( arc.release(), ADD_MODE::APPEND );
3033 }
3034 }
3035}
3036
3037
3039 const int aPrimitiveIndex, const bool aIsBoardImport )
3040{
3041 if( aElem.polygon != ALTIUM_POLYGON_NONE )
3042 {
3043 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Arc with polygon id %d",
3044 aElem.polygon ) );
3045 return;
3046 }
3047
3048 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3049 || IsAltiumLayerAPlane( aElem.layer ) )
3050 {
3051 // This is not the actual board item. We can use it to create the polygon for the region
3052 PCB_SHAPE shape( nullptr );
3053
3054 ConvertArcs6ToPcbShape( aElem, &shape );
3056
3057 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3058 aElem.keepoutrestrictions );
3059 }
3060 else
3061 {
3062 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3063 {
3064 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3065 {
3066 // Special case: do to not lose net connections in footprints
3067 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
3068 }
3069 else
3070 {
3071 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3072 }
3073 }
3074 }
3075
3076 for( const auto& layerExpansionMask :
3078 {
3079 int width = aElem.width + ( layerExpansionMask.second * 2 );
3080
3081 if( width > 1 )
3082 {
3083 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3084
3085 ConvertArcs6ToPcbShape( aElem, arc.get() );
3086 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3087 arc->SetLayer( layerExpansionMask.first );
3088
3089 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3090 }
3091 }
3092}
3093
3094
3096{
3097 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3098 {
3099 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
3100 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
3101
3102 includedAngle.Normalize();
3103
3104 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
3105 -KiROUND( startAngle.Sin() * aElem.radius ) );
3106
3107 if( includedAngle.AsDegrees() >= 0.1 )
3108 {
3109 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
3110 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
3111
3112 shape.SetCenter( aElem.center );
3113 shape.SetStart( aElem.center + startOffset );
3114 shape.SetArcAngleAndEnd( includedAngle, true );
3115
3116 // Create actual arc
3117 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(),
3118 aElem.width );
3119 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board, &shapeArc );
3120
3121 arc->SetWidth( aElem.width );
3122 arc->SetLayer( aLayer );
3123 arc->SetNetCode( GetNetCode( aElem.net ) );
3124
3125 m_board->Add( arc.release(), ADD_MODE::APPEND );
3126 }
3127 }
3128 else
3129 {
3130 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>(m_board);
3131
3132 ConvertArcs6ToPcbShape( aElem, arc.get() );
3133 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3134 arc->SetLayer( aLayer );
3135
3136 m_board->Add( arc.release(), ADD_MODE::APPEND );
3137 }
3138}
3139
3140
3142 PCB_LAYER_ID aLayer )
3143{
3144 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3145
3146 ConvertArcs6ToPcbShape( aElem, arc.get() );
3147 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3148 arc->SetLayer( aLayer );
3149
3150 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3151}
3152
3153
3155 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3156{
3157 if( m_progressReporter )
3158 m_progressReporter->Report( _( "Loading pads..." ) );
3159
3160 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3161
3162 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3163 {
3164 checkpoint();
3165 APAD6 elem( reader );
3166
3167 if( elem.component == ALTIUM_COMPONENT_NONE )
3168 {
3170 }
3171 else
3172 {
3173 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3174 ConvertPads6ToFootprintItem( footprint, elem );
3175 }
3176 }
3177
3178 if( reader.GetRemainingBytes() != 0 )
3179 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
3180}
3181
3182
3184{
3185 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3186 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3187 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3188 {
3190 }
3191 else
3192 {
3193 // We cannot add a pad directly into the PCB
3194 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
3195 footprint->SetPosition( aElem.position );
3196
3197 ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem );
3198
3199 m_board->Add( footprint.release(), ADD_MODE::APPEND );
3200 }
3201}
3202
3203
3205{
3206 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3207
3208 pad->SetNumber( "" );
3209 pad->SetNetCode( GetNetCode( aElem.net ) );
3210
3211 pad->SetPosition( aElem.position );
3212 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( aElem.diameter, aElem.diameter ) );
3213 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3214 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3216 pad->SetAttribute( PAD_ATTRIB::PTH );
3217
3218 // Pads are always through holes in KiCad
3219 pad->SetLayerSet( LSET().AllCuMask() );
3220
3221 if( aElem.viamode == ALTIUM_PAD_MODE::SIMPLE )
3222 {
3223 pad->Padstack().SetMode( PADSTACK::MODE::NORMAL );
3224 }
3226 {
3227 pad->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
3228 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[1], aElem.diameter_by_layer[1] ),
3230 }
3231 else
3232 {
3233 pad->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
3234 int altiumIdx = 0;
3235
3236 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, 32 ) )
3237 {
3238 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[altiumIdx],
3239 aElem.diameter_by_layer[altiumIdx] ), layer );
3240 altiumIdx++;
3241 }
3242 }
3243
3244 if( aElem.is_tent_top )
3245 {
3246 pad->Padstack().FrontOuterLayers().has_solder_mask = true;
3247 }
3248 else
3249 {
3250 pad->Padstack().FrontOuterLayers().has_solder_mask = false;
3251 pad->SetLayerSet( pad->GetLayerSet().set( F_Mask ) );
3252 }
3253
3254 if( aElem.is_tent_bottom )
3255 {
3256 pad->Padstack().BackOuterLayers().has_solder_mask = true;
3257 }
3258 else
3259 {
3260 pad->Padstack().BackOuterLayers().has_solder_mask = false;
3261 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ) );
3262 }
3263
3264 if( aElem.is_locked )
3265 pad->SetLocked( true );
3266
3267 if( aElem.soldermask_expansion_manual )
3268 {
3269 pad->Padstack().FrontOuterLayers().solder_mask_margin = aElem.soldermask_expansion_front;
3270 pad->Padstack().BackOuterLayers().solder_mask_margin = aElem.soldermask_expansion_back;
3271 }
3272
3273
3274 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3275}
3276
3277
3279{
3280 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3281 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3282 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3283 {
3284 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
3285 }
3286 else
3287 {
3288 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
3289 }
3290}
3291
3292
3294{
3295 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3296
3297 pad->SetNumber( aElem.name );
3298 pad->SetNetCode( GetNetCode( aElem.net ) );
3299
3300 pad->SetPosition( aElem.position );
3301 pad->SetOrientationDegrees( aElem.direction );
3302 pad->SetThermalSpokeAngle( ANGLE_90 );
3303
3304 if( aElem.holesize == 0 )
3305 {
3306 pad->SetAttribute( PAD_ATTRIB::SMD );
3307 }
3308 else
3309 {
3310 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3311 {
3312 // TODO: I assume other values are possible as well?
3313 if( !m_footprintName.IsEmpty() )
3314 {
3315 if( m_reporter )
3316 {
3317 wxString msg;
3318 msg.Printf( _( "Error loading library '%s':\n"
3319 "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3320 m_library,
3322 aElem.name );
3323 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
3324 }
3325 }
3326 else
3327 {
3328 if( m_reporter )
3329 {
3330 wxString msg;
3331 msg.Printf( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3332 aFootprint->GetReference(),
3333 aElem.name );
3334 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
3335 }
3336 }
3337 }
3338
3339 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
3340
3342 {
3343 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3344 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3345 }
3346 else
3347 {
3348 switch( aElem.sizeAndShape->holeshape )
3349 {
3351 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
3352 break;
3353
3355 if( !m_footprintName.IsEmpty() )
3356 {
3357 if( m_reporter )
3358 {
3359 wxString msg;
3360 msg.Printf( _( "Loading library '%s':\n"
3361 "Footprint %s pad %s has a square hole (not yet supported)." ),
3362 m_library,
3364 aElem.name );
3365 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3366 }
3367 }
3368 else
3369 {
3370 if( m_reporter )
3371 {
3372 wxString msg;
3373 msg.Printf( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
3374 aFootprint->GetReference(),
3375 aElem.name );
3376 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3377 }
3378 }
3379
3380 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3381 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3382 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
3383 // this case or rect holes have a different id
3384 break;
3385
3387 {
3388 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
3389 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
3390
3391 slotRotation.Normalize();
3392
3393 if( slotRotation.IsHorizontal() )
3394 {
3395 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
3396 }
3397 else if( slotRotation.IsVertical() )
3398 {
3399 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
3400 }
3401 else
3402 {
3403 if( !m_footprintName.IsEmpty() )
3404 {
3405 if( m_reporter )
3406 {
3407 wxString msg;
3408 msg.Printf( _( "Loading library '%s':\n"
3409 "Footprint %s pad %s has a hole-rotation of %d degrees. "
3410 "KiCad only supports 90 degree rotations." ),
3411 m_library,
3413 aElem.name,
3414 KiROUND( slotRotation.AsDegrees() ) );
3415 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3416 }
3417 }
3418 else
3419 {
3420 if( m_reporter )
3421 {
3422 wxString msg;
3423 msg.Printf( _( "Footprint %s pad %s has a hole-rotation of %d degrees. "
3424 "KiCad only supports 90 degree rotations." ),
3425 aFootprint->GetReference(),
3426 aElem.name,
3427 KiROUND( slotRotation.AsDegrees() ) );
3428 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3429 }
3430 }
3431 }
3432
3433 break;
3434 }
3435
3436 default:
3438 if( !m_footprintName.IsEmpty() )
3439 {
3440 if( m_reporter )
3441 {
3442 wxString msg;
3443 msg.Printf( _( "Error loading library '%s':\n"
3444 "Footprint %s pad %s uses a hole of unknown kind %d." ),
3445 m_library,
3447 aElem.name,
3448 aElem.sizeAndShape->holeshape );
3449 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3450 }
3451 }
3452 else
3453 {
3454 if( m_reporter )
3455 {
3456 wxString msg;
3457 msg.Printf( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
3458 aFootprint->GetReference(),
3459 aElem.name,
3460 aElem.sizeAndShape->holeshape );
3461 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3462 }
3463 }
3464
3465 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3466 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3467 break;
3468 }
3469 }
3470
3471 if( aElem.sizeAndShape )
3472 pad->SetOffset( PADSTACK::ALL_LAYERS, aElem.sizeAndShape->holeoffset[0] );
3473 }
3474
3475 PADSTACK& ps = pad->Padstack();
3476
3477 auto setCopperGeometry =
3478 [&]( PCB_LAYER_ID aLayer, ALTIUM_PAD_SHAPE aShape, const VECTOR2I& aSize )
3479 {
3480 int altLayer = CopperLayerToOrdinal( aLayer );
3481
3482 ps.SetSize( aSize, aLayer );
3483
3484 switch( aShape )
3485 {
3487 ps.SetShape( PAD_SHAPE::RECTANGLE, aLayer );
3488 break;
3489
3491 if( aElem.sizeAndShape
3493 {
3494 ps.SetShape( PAD_SHAPE::ROUNDRECT, aLayer ); // 100 = round, 0 = rectangular
3495 double ratio = aElem.sizeAndShape->cornerradius[altLayer] / 200.;
3496 ps.SetRoundRectRadiusRatio( ratio, aLayer );
3497 }
3498 else if( aElem.topsize.x == aElem.topsize.y )
3499 {
3500 ps.SetShape( PAD_SHAPE::CIRCLE, aLayer );
3501 }
3502 else
3503 {
3504 ps.SetShape( PAD_SHAPE::OVAL, aLayer );
3505 }
3506
3507 break;
3508
3510 ps.SetShape( PAD_SHAPE::CHAMFERED_RECT, aLayer );
3512 ps.SetChamferRatio( 0.25, aLayer );
3513 break;
3514
3516 default:
3517 if( !m_footprintName.IsEmpty() )
3518 {
3519 if( m_reporter )
3520 {
3521 wxString msg;
3522 msg.Printf( _( "Error loading library '%s':\n"
3523 "Footprint %s pad %s uses an unknown pad shape." ),
3524 m_library,
3526 aElem.name );
3527 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3528 }
3529 }
3530 else
3531 {
3532 if( m_reporter )
3533 {
3534 wxString msg;
3535 msg.Printf( _( "Footprint %s pad %s uses an unknown pad shape." ),
3536 aFootprint->GetReference(),
3537 aElem.name );
3538 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3539 }
3540 }
3541 break;
3542 }
3543 };
3544
3545 switch( aElem.padmode )
3546 {
3549 setCopperGeometry( PADSTACK::ALL_LAYERS, aElem.topshape, aElem.topsize );
3550 break;
3551
3554 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3555 setCopperGeometry( PADSTACK::INNER_LAYERS, aElem.midshape, aElem.midsize );
3556 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3557 break;
3558
3561
3562 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3563 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3564 setCopperGeometry( In1_Cu, aElem.midshape, aElem.midsize );
3565
3566 if( aElem.sizeAndShape )
3567 {
3568 size_t i = 0;
3569
3570 LSET intLayers = aFootprint->BoardLayerSet();
3571 intLayers &= LSET::InternalCuMask();
3572 intLayers.set( In1_Cu, false ); // Already handled above
3573
3574 for( PCB_LAYER_ID layer : intLayers )
3575 {
3576 setCopperGeometry( layer, aElem.sizeAndShape->inner_shape[i],
3577 VECTOR2I( aElem.sizeAndShape->inner_size[i].x,
3578 aElem.sizeAndShape->inner_size[i].y ) );
3579 i++;
3580 }
3581 }
3582
3583 break;
3584 }
3585
3586 switch( aElem.layer )
3587 {
3589 pad->SetLayer( F_Cu );
3590 pad->SetLayerSet( PAD::SMDMask() );
3591 break;
3592
3594 pad->SetLayer( B_Cu );
3595 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
3596 break;
3597
3599 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
3600 break;
3601
3602 default:
3603 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3604 pad->SetLayer( klayer );
3605 pad->SetLayerSet( LSET( { klayer } ) );
3606 break;
3607 }
3608
3610 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
3611
3613 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
3614
3615 if( aElem.is_tent_top )
3616 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
3617
3618 if( aElem.is_tent_bottom )
3619 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
3620
3621 pad->SetPadToDieLength( aElem.pad_to_die_length );
3622 pad->SetPadToDieDelay( aElem.pad_to_die_delay );
3623
3624 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3625}
3626
3627
3629{
3630 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3631
3632 if( klayer == UNDEFINED_LAYER )
3633 {
3634 if( m_reporter )
3635 {
3636 wxString msg;
3637 msg.Printf( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
3638 "equivalent. It has been moved to KiCad layer Eco1_User." ),
3639 aElem.name, aElem.layer );
3640 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3641 }
3642
3643 klayer = Eco1_User;
3644 }
3645
3646 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( m_board );
3647
3648 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3649
3650 m_board->Add( pad.release(), ADD_MODE::APPEND );
3651}
3652
3653
3655{
3656 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3657
3658 if( klayer == UNDEFINED_LAYER )
3659 {
3660 if( !m_footprintName.IsEmpty() )
3661 {
3662 if( m_reporter )
3663 {
3664 wxString msg;
3665 msg.Printf( _( "Loading library '%s':\n"
3666 "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3667 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3668 m_library,
3670 aElem.name,
3671 aElem.layer );
3672 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3673 }
3674 }
3675 else
3676 {
3677 if( m_reporter )
3678 {
3679 wxString msg;
3680 msg.Printf( _( "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3681 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3682 aFootprint->GetReference(),
3683 aElem.name,
3684 aElem.layer );
3685 m_reporter->Report( msg, RPT_SEVERITY_INFO );
3686 }
3687 }
3688
3689 klayer = Eco1_User;
3690 }
3691
3692 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( aFootprint );
3693
3694 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3695
3696 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3697}
3698
3699
3701 PCB_SHAPE* aShape )
3702{
3703 if( aElem.net != ALTIUM_NET_UNCONNECTED )
3704 {
3705 if( m_reporter )
3706 {
3707 wxString msg;
3708 msg.Printf( _( "Non-copper pad %s is connected to a net, which is not supported." ),
3709 aElem.name );
3710 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3711 }
3712 }
3713
3714 if( aElem.holesize != 0 )
3715 {
3716 if( m_reporter )
3717 {
3718 wxString msg;
3719 msg.Printf( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
3720 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3721 }
3722 }
3723
3724 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3725 {
3726 if( m_reporter )
3727 {
3728 wxString msg;
3729 msg.Printf( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
3730 aElem.name );
3731 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3732 }
3733 }
3734
3735 switch( aElem.topshape )
3736 {
3738 {
3739 // filled rect
3740 aShape->SetShape( SHAPE_T::POLY );
3741 aShape->SetFilled( true );
3742 aShape->SetLayer( aLayer );
3743 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3744
3745 aShape->SetPolyPoints(
3746 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
3747 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
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
3751 if( aElem.direction != 0 )
3752 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3753 }
3754 break;
3755
3757 if( aElem.sizeAndShape
3759 {
3760 // filled roundrect
3761 int cornerradius = aElem.sizeAndShape->cornerradius[0];
3762 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
3763
3764 aShape->SetLayer( aLayer );
3765 aShape->SetStroke( STROKE_PARAMS( offset * 2, LINE_STYLE::SOLID ) );
3766
3767 if( cornerradius < 100 )
3768 {
3769 int offsetX = aElem.topsize.x / 2 - offset;
3770 int offsetY = aElem.topsize.y / 2 - offset;
3771
3772 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
3773 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
3774 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
3775 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
3776
3777 aShape->SetShape( SHAPE_T::POLY );
3778 aShape->SetFilled( true );
3779 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
3780 }
3781 else if( aElem.topsize.x == aElem.topsize.y )
3782 {
3783 // circle
3784 aShape->SetShape( SHAPE_T::CIRCLE );
3785 aShape->SetFilled( true );
3786 aShape->SetStart( aElem.position );
3787 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3788 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3789 }
3790 else if( aElem.topsize.x < aElem.topsize.y )
3791 {
3792 // short vertical line
3793 aShape->SetShape( SHAPE_T::SEGMENT );
3794 VECTOR2I pointOffset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3795 aShape->SetStart( aElem.position + pointOffset );
3796 aShape->SetEnd( aElem.position - pointOffset );
3797 }
3798 else
3799 {
3800 // short horizontal line
3801 aShape->SetShape( SHAPE_T::SEGMENT );
3802 VECTOR2I pointOffset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3803 aShape->SetStart( aElem.position + pointOffset );
3804 aShape->SetEnd( aElem.position - pointOffset );
3805 }
3806
3807 if( aElem.direction != 0 )
3808 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3809 }
3810 else if( aElem.topsize.x == aElem.topsize.y )
3811 {
3812 // filled circle
3813 aShape->SetShape( SHAPE_T::CIRCLE );
3814 aShape->SetFilled( true );
3815 aShape->SetLayer( aLayer );
3816 aShape->SetStart( aElem.position );
3817 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3818 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3819 }
3820 else
3821 {
3822 // short line
3823 aShape->SetShape( SHAPE_T::SEGMENT );
3824 aShape->SetLayer( aLayer );
3825 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
3827
3828 if( aElem.topsize.x < aElem.topsize.y )
3829 {
3830 VECTOR2I offset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3831 aShape->SetStart( aElem.position + offset );
3832 aShape->SetEnd( aElem.position - offset );
3833 }
3834 else
3835 {
3836 VECTOR2I offset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3837 aShape->SetStart( aElem.position + offset );
3838 aShape->SetEnd( aElem.position - offset );
3839 }
3840
3841 if( aElem.direction != 0 )
3842 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3843 }
3844 break;
3845
3847 {
3848 // filled octagon
3849 aShape->SetShape( SHAPE_T::POLY );
3850 aShape->SetFilled( true );
3851 aShape->SetLayer( aLayer );
3852 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3853
3854 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
3855 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3856 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3857 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
3858
3859 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
3860 VECTOR2I chamferX( chamfer, 0 );
3861 VECTOR2I chamferY( 0, chamfer );
3862
3863 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
3864 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
3865
3866 if( aElem.direction != 0. )
3867 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3868 }
3869 break;
3870
3872 default:
3873 if( m_reporter )
3874 {
3875 wxString msg;
3876 msg.Printf( _( "Non-copper pad %s uses an unknown pad shape." ), aElem.name );
3877 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3878 }
3879
3880 break;
3881 }
3882}
3883
3884
3886 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3887{
3888 if( m_progressReporter )
3889 m_progressReporter->Report( _( "Loading vias..." ) );
3890
3891 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3892
3893 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3894 {
3895 checkpoint();
3896 AVIA6 elem( reader );
3897
3898 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
3899
3900 via->SetPosition( elem.position );
3901 via->SetDrill( elem.holesize );
3902 via->SetNetCode( GetNetCode( elem.net ) );
3903 via->SetLocked( elem.is_locked );
3904
3905 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
3907 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
3909
3910 if( start_layer_outside && end_layer_outside )
3911 {
3912 via->SetViaType( VIATYPE::THROUGH );
3913 }
3914 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
3915 {
3916 via->SetViaType( VIATYPE::BURIED );
3917 }
3918 else
3919 {
3920 via->SetViaType( VIATYPE::BLIND );
3921 }
3922
3923 // TODO: Altium has a specific flag for microvias, independent of start/end layer
3924#if 0
3925 if( something )
3926 via->SetViaType( VIATYPE::MICROVIA );
3927#endif
3928
3929 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
3930 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
3931
3932 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
3933 {
3934 if( m_reporter )
3935 {
3936 wxString msg;
3937 msg.Printf( _( "Via from layer %d to %d uses a non-copper layer, which is not "
3938 "supported." ),
3939 elem.layer_start,
3940 elem.layer_end );
3941 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
3942 }
3943
3944 continue; // just assume through-hole instead.
3945 }
3946
3947 // we need VIATYPE set!
3948 via->SetLayerPair( start_klayer, end_klayer );
3949
3950 switch( elem.viamode )
3951 {
3952 default:
3954 via->SetWidth( PADSTACK::ALL_LAYERS, elem.diameter );
3955 break;
3956
3958 via->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
3959 via->SetWidth( F_Cu, elem.diameter_by_layer[0] );
3960 via->SetWidth( PADSTACK::INNER_LAYERS, elem.diameter_by_layer[1] );
3961 via->SetWidth( B_Cu, elem.diameter_by_layer[31] );
3962 break;
3963
3965 {
3966 via->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
3967
3968 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, MAX_CU_LAYERS ) )
3969 {
3970 int altiumLayer = CopperLayerToOrdinal( layer );
3971 wxCHECK2_MSG( altiumLayer < 32, break,
3972 "Altium importer expects 32 or fewer copper layers" );
3973
3974 via->SetWidth( layer, elem.diameter_by_layer[altiumLayer] );
3975 }
3976
3977 break;
3978 }
3979 }
3980
3982 {
3983 via->SetFrontTentingMode( elem.is_tent_top ? TENTING_MODE::TENTED
3985 via->SetBackTentingMode( elem.is_tent_bottom ? TENTING_MODE::TENTED
3987 }
3988
3989 m_board->Add( via.release(), ADD_MODE::APPEND );
3990 }
3991
3992 if( reader.GetRemainingBytes() != 0 )
3993 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
3994}
3995
3997 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3998{
3999 if( m_progressReporter )
4000 m_progressReporter->Report( _( "Loading tracks..." ) );
4001
4002 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4003
4004 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
4005 {
4006 checkpoint();
4007 ATRACK6 elem( reader );
4008
4009 if( elem.component == ALTIUM_COMPONENT_NONE )
4010 {
4011 ConvertTracks6ToBoardItem( elem, primitiveIndex );
4012 }
4013 else
4014 {
4015 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4016 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
4017 }
4018 }
4019
4020 if( reader.GetRemainingBytes() != 0 )
4021 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
4022}
4023
4024
4025void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
4026{
4027 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
4028 {
4029 if( m_polygons.size() <= aElem.polygon )
4030 {
4031 // Can happen when reading old Altium files: just skip this item
4032 if( m_reporter )
4033 {
4034 wxString msg;
4035 msg.Printf( wxT( "ATRACK6 stream tries to access polygon id %u "
4036 "of %u existing polygons; skipping it" ),
4037 static_cast<unsigned>( aElem.polygon ),
4038 static_cast<unsigned>( m_polygons.size() ) );
4039 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4040 }
4041
4042 return;
4043 }
4044
4045 ZONE* zone = m_polygons.at( aElem.polygon );
4046
4047 if( zone == nullptr )
4048 {
4049 return; // we know the zone id, but because we do not know the layer we did not
4050 // add it!
4051 }
4052
4053 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
4054
4055 if( klayer == UNDEFINED_LAYER )
4056 return; // Just skip it for now. Users can fill it themselves.
4057
4058 if( !zone->HasFilledPolysForLayer( klayer ) )
4059 return;
4060
4061 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
4062
4063 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4064 shape.SetStart( aElem.start );
4065 shape.SetEnd( aElem.end );
4067
4068 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
4069 // Will be simplified and fractured later
4070
4071 zone->SetIsFilled( true );
4072 zone->SetNeedRefill( false );
4073
4074 return;
4075 }
4076
4077 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4078 || IsAltiumLayerAPlane( aElem.layer ) )
4079 {
4080 // This is not the actual board item. We can use it to create the polygon for the region
4081 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4082 shape.SetStart( aElem.start );
4083 shape.SetEnd( aElem.end );
4085
4087 }
4088 else
4089 {
4090 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4091 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4092 }
4093
4094 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4095 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4096 {
4097 int width = aElem.width + ( layerExpansionMask.second * 2 );
4098 if( width > 1 )
4099 {
4100 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4101
4102 seg->SetStart( aElem.start );
4103 seg->SetEnd( aElem.end );
4104 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4105 seg->SetLayer( layerExpansionMask.first );
4106
4107 m_board->Add( seg.release(), ADD_MODE::APPEND );
4108 }
4109 }
4110}
4111
4112
4114 const int aPrimitiveIndex,
4115 const bool aIsBoardImport )
4116{
4117 if( aElem.polygon != ALTIUM_POLYGON_NONE )
4118 {
4119 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Track with polygon id %u",
4120 (unsigned)aElem.polygon ) );
4121 return;
4122 }
4123
4124 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4125 || IsAltiumLayerAPlane( aElem.layer ) )
4126 {
4127 // This is not the actual board item. We can use it to create the polygon for the region
4128 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4129 shape.SetStart( aElem.start );
4130 shape.SetEnd( aElem.end );
4132
4133 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4134 aElem.keepoutrestrictions );
4135 }
4136 else
4137 {
4138 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4139 {
4140 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4141 {
4142 // Special case: do to not lose net connections in footprints
4143 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4144 }
4145 else
4146 {
4147 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4148 }
4149 }
4150 }
4151
4152 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4153 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4154 {
4155 int width = aElem.width + ( layerExpansionMask.second * 2 );
4156 if( width > 1 )
4157 {
4158 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4159
4160 seg->SetStart( aElem.start );
4161 seg->SetEnd( aElem.end );
4162 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4163 seg->SetLayer( layerExpansionMask.first );
4164
4165 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4166 }
4167 }
4168}
4169
4170
4172{
4173 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4174 {
4175 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
4176
4177 track->SetStart( aElem.start );
4178 track->SetEnd( aElem.end );
4179 track->SetWidth( aElem.width );
4180 track->SetLayer( aLayer );
4181 track->SetNetCode( GetNetCode( aElem.net ) );
4182
4183 m_board->Add( track.release(), ADD_MODE::APPEND );
4184 }
4185 else
4186 {
4187 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4188
4189 seg->SetStart( aElem.start );
4190 seg->SetEnd( aElem.end );
4191 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4192 seg->SetLayer( aLayer );
4193
4194 m_board->Add( seg.release(), ADD_MODE::APPEND );
4195 }
4196}
4197
4198
4200 PCB_LAYER_ID aLayer )
4201{
4202 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4203
4204 seg->SetStart( aElem.start );
4205 seg->SetEnd( aElem.end );
4206 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4207 seg->SetLayer( aLayer );
4208
4209 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4210}
4211
4212
4214 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4215{
4216 if( m_progressReporter )
4217 m_progressReporter->Report( _( "Loading unicode strings..." ) );
4218
4219 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4220
4222
4223 if( reader.GetRemainingBytes() != 0 )
4224 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
4225}
4226
4228 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4229{
4230 if( m_progressReporter )
4231 m_progressReporter->Report( _( "Loading text..." ) );
4232
4233 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4234
4235 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4236 {
4237 checkpoint();
4238 ATEXT6 elem( reader, m_unicodeStrings );
4239
4240 if( elem.component == ALTIUM_COMPONENT_NONE )
4241 {
4243 }
4244 else
4245 {
4246 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4247 ConvertTexts6ToFootprintItem( footprint, elem );
4248 }
4249 }
4250
4251 if( reader.GetRemainingBytes() != 0 )
4252 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
4253}
4254
4255
4257{
4258 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4259 {
4260 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4261 ConvertBarcodes6ToBoardItemOnLayer( aElem, klayer );
4262 return;
4263 }
4264
4265 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4266 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
4267}
4268
4269
4271{
4272 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4273 {
4274 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4275 ConvertBarcodes6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4276 return;
4277 }
4278
4279 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4280 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4281}
4282
4283
4285{
4286 std::unique_ptr<PCB_TEXTBOX> pcbTextbox = std::make_unique<PCB_TEXTBOX>( m_board );
4287 std::unique_ptr<PCB_TEXT> pcbText = std::make_unique<PCB_TEXT>( m_board );
4288
4289 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4290
4291 static const std::map<wxString, wxString> variableMap = {
4292 { "LAYER_NAME", "LAYER" },
4293 { "PRINT_DATE", "CURRENT_DATE"},
4294 };
4295
4296 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4297 BOARD_ITEM* item = pcbText.get();
4298 EDA_TEXT* text = pcbText.get();
4299 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4300
4301 if( isTextbox )
4302 {
4303 item = pcbTextbox.get();
4304 text = pcbTextbox.get();
4305
4307 HelperSetTextboxAlignmentAndPos( aElem, pcbTextbox.get() );
4308 }
4309 else
4310 {
4313 }
4314
4315 text->SetText( kicadText );
4316 item->SetLayer( aLayer );
4317 item->SetIsKnockout( aElem.isInverted );
4318
4319 if( isTextbox )
4320 m_board->Add( pcbTextbox.release(), ADD_MODE::APPEND );
4321 else
4322 m_board->Add( pcbText.release(), ADD_MODE::APPEND );
4323}
4324
4325
4327 PCB_LAYER_ID aLayer )
4328{
4329 std::unique_ptr<PCB_TEXTBOX> fpTextbox = std::make_unique<PCB_TEXTBOX>( aFootprint );
4330 std::unique_ptr<PCB_TEXT> fpText = std::make_unique<PCB_TEXT>( aFootprint );
4331
4332 BOARD_ITEM* item = fpText.get();
4333 EDA_TEXT* text = fpText.get();
4334 PCB_FIELD* field = nullptr;
4335
4336 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4337 bool toAdd = false;
4338
4339 if( aElem.isDesignator )
4340 {
4341 item = &aFootprint->Reference(); // TODO: handle multiple layers
4342 text = &aFootprint->Reference();
4343 field = &aFootprint->Reference();
4344 }
4345 else if( aElem.isComment )
4346 {
4347 item = &aFootprint->Value(); // TODO: handle multiple layers
4348 text = &aFootprint->Value();
4349 field = &aFootprint->Value();
4350 }
4351 else
4352 {
4353 item = fpText.get();
4354 text = fpText.get();
4355 toAdd = true;
4356 }
4357
4358 static const std::map<wxString, wxString> variableMap = {
4359 { "DESIGNATOR", "REFERENCE" },
4360 { "COMMENT", "VALUE" },
4361 { "VALUE", "ALTIUM_VALUE" },
4362 { "LAYER_NAME", "LAYER" },
4363 { "PRINT_DATE", "CURRENT_DATE"},
4364 };
4365
4366 if( isTextbox )
4367 {
4368 item = fpTextbox.get();
4369 text = fpTextbox.get();
4370
4372 HelperSetTextboxAlignmentAndPos( aElem, fpTextbox.get() );
4373 }
4374 else
4375 {
4378 }
4379
4380 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4381
4382 text->SetText( kicadText );
4383 text->SetKeepUpright( false );
4384 item->SetLayer( aLayer );
4385 item->SetIsKnockout( aElem.isInverted );
4386
4387 if( toAdd )
4388 {
4389 if( isTextbox )
4390 aFootprint->Add( fpTextbox.release(), ADD_MODE::APPEND );
4391 else
4392 aFootprint->Add( fpText.release(), ADD_MODE::APPEND );
4393 }
4394}
4395
4396
4398{
4399 std::unique_ptr<PCB_BARCODE> pcbBarcode = std::make_unique<PCB_BARCODE>( m_board );
4400
4401 pcbBarcode->SetLayer( aLayer );
4402 pcbBarcode->SetPosition( aElem.position );
4403 pcbBarcode->SetWidth( aElem.textbox_rect_width );
4404 pcbBarcode->SetHeight( aElem.textbox_rect_height );
4405 pcbBarcode->SetMargin( aElem.barcode_margin );
4406 pcbBarcode->SetText( aElem.text );
4407
4408 switch( aElem.barcode_type )
4409 {
4410 case ALTIUM_BARCODE_TYPE::CODE39: pcbBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4411 case ALTIUM_BARCODE_TYPE::CODE128: pcbBarcode->SetKind( BARCODE_T::CODE_128 ); break;
4412 default: pcbBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4413 }
4414
4415 pcbBarcode->SetIsKnockout( aElem.barcode_inverted );
4416 pcbBarcode->AssembleBarcode();
4417
4418 m_board->Add( pcbBarcode.release(), ADD_MODE::APPEND );
4419}
4420
4421
4423 PCB_LAYER_ID aLayer )
4424{
4425 std::unique_ptr<PCB_BARCODE> fpBarcode = std::make_unique<PCB_BARCODE>( aFootprint );
4426
4427 fpBarcode->SetLayer( aLayer );
4428 fpBarcode->SetPosition( aElem.position );
4429 fpBarcode->SetWidth( aElem.textbox_rect_width );
4430 fpBarcode->SetHeight( aElem.textbox_rect_height );
4431 fpBarcode->SetMargin( aElem.barcode_margin );
4432 fpBarcode->SetText( aElem.text );
4433
4434 switch( aElem.barcode_type )
4435 {
4436 case ALTIUM_BARCODE_TYPE::CODE39: fpBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4437 case ALTIUM_BARCODE_TYPE::CODE128: fpBarcode->SetKind( BARCODE_T::CODE_128 ); break;
4438 default: fpBarcode->SetKind( BARCODE_T::CODE_39 ); break;
4439 }
4440
4441 fpBarcode->SetIsKnockout( aElem.barcode_inverted );
4442 fpBarcode->AssembleBarcode();
4443
4444 aFootprint->Add( fpBarcode.release(), ADD_MODE::APPEND );
4445}
4446
4447
4449{
4450 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4451
4452 // Altium textboxes do not have borders
4453 aTextbox->SetBorderEnabled( false );
4454
4455 // Calculate position
4456 VECTOR2I kposition = aElem.position;
4457
4458 if( aElem.isMirrored )
4459 kposition.x -= aElem.textbox_rect_width;
4460
4461 kposition.y -= aElem.textbox_rect_height;
4462
4463#if 0
4464 // Compensate for KiCad's textbox margin
4465 int charWidth = aTextbox->GetTextWidth();
4466 int charHeight = aTextbox->GetTextHeight();
4467
4468 VECTOR2I kicadMargin;
4469
4470 if( !aTextbox->GetFont() || aTextbox->GetFont()->IsStroke() )
4471 kicadMargin = VECTOR2I( charWidth * 0.933, charHeight * 0.67 );
4472 else
4473 kicadMargin = VECTOR2I( charWidth * 0.808, charHeight * 0.844 );
4474
4475 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height )
4476 + kicadMargin * 2 - margin * 2 );
4477
4478 kposition = kposition - kicadMargin + margin;
4479#else
4480 aTextbox->SetMarginBottom( margin );
4481 aTextbox->SetMarginLeft( margin );
4482 aTextbox->SetMarginRight( margin );
4483 aTextbox->SetMarginTop( margin );
4484
4485 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height ) );
4486#endif
4487
4488 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4489
4490 aTextbox->SetPosition( kposition );
4491
4492 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4495
4496 switch( justification )
4497 {
4503 break;
4509 break;
4515 break;
4516 default:
4517 if( m_reporter )
4518 {
4519 wxString msg;
4520 msg.Printf( _( "Unknown textbox justification %d, aText %s" ), justification,
4521 aElem.text );
4522 m_reporter->Report( msg, RPT_SEVERITY_DEBUG );
4523 }
4524
4527 break;
4528 }
4529
4530 aTextbox->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4531}
4532
4533
4535{
4536 VECTOR2I kposition = aElem.position;
4537
4538 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4539 int rectWidth = aElem.textbox_rect_width - margin * 2;
4540 int rectHeight = aElem.height;
4541
4542 if( aElem.isMirrored )
4543 rectWidth = -rectWidth;
4544
4545 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4548
4549 switch( justification )
4550 {
4554
4555 kposition.y -= rectHeight;
4556 break;
4560
4561 kposition.y -= rectHeight / 2;
4562 break;
4566 break;
4570
4571 kposition.x += rectWidth / 2;
4572 kposition.y -= rectHeight;
4573 break;
4577
4578 kposition.x += rectWidth / 2;
4579 kposition.y -= rectHeight / 2;
4580 break;
4584
4585 kposition.x += rectWidth / 2;
4586 break;
4590
4591 kposition.x += rectWidth;
4592 kposition.y -= rectHeight;
4593 break;
4597
4598 kposition.x += rectWidth;
4599 kposition.y -= rectHeight / 2;
4600 break;
4604
4605 kposition.x += rectWidth;
4606 break;
4607 default:
4610 break;
4611 }
4612
4613 int charWidth = aText->GetTextWidth();
4614 int charHeight = aText->GetTextHeight();
4615
4616 // Correct for KiCad's baseline offset.
4617 // Text height and font must be set correctly before calling.
4618 if( !aText->GetFont() || aText->GetFont()->IsStroke() )
4619 {
4620 switch( aText->GetVertJustify() )
4621 {
4622 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charHeight * 0.0407; break;
4623 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charHeight * 0.0355; break;
4624 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charHeight * 0.1225; break;
4625 default: break;
4626 }
4627 }
4628 else
4629 {
4630 switch( aText->GetVertJustify() )
4631 {
4632 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charWidth * 0.016; break;
4633 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charWidth * 0.085; break;
4634 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charWidth * 0.17; break;
4635 default: break;
4636 }
4637 }
4638
4639 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4640
4641 aText->SetTextPos( kposition );
4642 aText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4643}
4644
4645
4647{
4648 aEdaText.SetTextSize( VECTOR2I( aElem.height, aElem.height ) );
4649
4651 {
4652 KIFONT::FONT* font = KIFONT::FONT::GetFont( aElem.fontname, aElem.isBold, aElem.isItalic );
4653 aEdaText.SetFont( font );
4654
4655 if( font->IsOutline() )
4656 {
4657 // TODO: why is this required? Somehow, truetype size is calculated differently
4658 if( font->GetName().Contains( wxS( "Arial" ) ) )
4659 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.63, aElem.height * 0.63 ) );
4660 else
4661 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.5, aElem.height * 0.5 ) );
4662 }
4663 }
4664
4665 aEdaText.SetTextThickness( aElem.strokewidth );
4666 aEdaText.SetBoldFlag( aElem.isBold );
4667 aEdaText.SetItalic( aElem.isItalic );
4668 aEdaText.SetMirrored( aElem.isMirrored );
4669}
4670
4671
4673 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4674{
4675 if( m_progressReporter )
4676 m_progressReporter->Report( _( "Loading rectangles..." ) );
4677
4678 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4679
4680 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4681 {
4682 checkpoint();
4683 AFILL6 elem( reader );
4684
4685 if( elem.component == ALTIUM_COMPONENT_NONE )
4686 {
4688 }
4689 else
4690 {
4691 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4692 ConvertFills6ToFootprintItem( footprint, elem, true );
4693 }
4694 }
4695
4696 if( reader.GetRemainingBytes() != 0 )
4697 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
4698}
4699
4700
4702{
4703 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
4704 {
4705 // This is not the actual board item. We can use it to create the polygon for the region
4706 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4707
4708 shape.SetStart( aElem.pos1 );
4709 shape.SetEnd( aElem.pos2 );
4710 shape.SetFilled( true );
4712
4713 if( aElem.rotation != 0. )
4714 {
4715 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4716 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4717 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4718 }
4719
4721 }
4722 else
4723 {
4724 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4725 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4726 }
4727}
4728
4729
4731 const bool aIsBoardImport )
4732{
4733 if( aElem.is_keepout
4734 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
4735 {
4736 // This is not the actual board item. We can use it to create the polygon for the region
4737 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4738
4739 shape.SetStart( aElem.pos1 );
4740 shape.SetEnd( aElem.pos2 );
4741 shape.SetFilled( true );
4743
4744 if( aElem.rotation != 0. )
4745 {
4746 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4747 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4748 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4749 }
4750
4751 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4752 aElem.keepoutrestrictions );
4753 }
4754 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
4755 && aElem.net != ALTIUM_NET_UNCONNECTED )
4756 {
4757 // Special case: do to not lose net connections in footprints
4758 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4759 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4760 }
4761 else
4762 {
4763 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4764 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4765 }
4766}
4767
4768
4770{
4771 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::RECTANGLE );
4772
4773 fill->SetFilled( true );
4774 fill->SetLayer( aLayer );
4775 fill->SetStroke( STROKE_PARAMS( 0 ) );
4776
4777 fill->SetStart( aElem.pos1 );
4778 fill->SetEnd( aElem.pos2 );
4779
4780 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4781 {
4782 fill->SetNetCode( GetNetCode( aElem.net ) );
4783 }
4784
4785 if( aElem.rotation != 0. )
4786 {
4787 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4788 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4789 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4790 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4791 }
4792
4793 m_board->Add( fill.release(), ADD_MODE::APPEND );
4794}
4795
4796
4798 PCB_LAYER_ID aLayer )
4799{
4800 if( aLayer == F_Cu || aLayer == B_Cu )
4801 {
4802 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
4803
4804 LSET padLayers;
4805 padLayers.set( aLayer );
4806
4807 pad->SetAttribute( PAD_ATTRIB::SMD );
4808 EDA_ANGLE rotation( aElem.rotation, DEGREES_T );
4809
4810 // Handle rotation multiples of 90 degrees
4811 if( rotation.IsCardinal() )
4812 {
4814
4815 int width = std::abs( aElem.pos2.x - aElem.pos1.x );
4816 int height = std::abs( aElem.pos2.y - aElem.pos1.y );
4817
4818 // Swap width and height for 90 or 270 degree rotations
4819 if( rotation.IsCardinal90() )
4820 std::swap( width, height );
4821
4822 pad->SetSize( PADSTACK::ALL_LAYERS, { width, height } );
4823 pad->SetPosition( aElem.pos1 / 2 + aElem.pos2 / 2 );
4824 }
4825 else
4826 {
4828
4829 int anchorSize = std::min( std::abs( aElem.pos2.x - aElem.pos1.x ),
4830 std::abs( aElem.pos2.y - aElem.pos1.y ) );
4831 VECTOR2I anchorPos = aElem.pos1;
4832
4833 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
4834 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
4835 pad->SetPosition( anchorPos );
4836
4837 SHAPE_POLY_SET shapePolys;
4838 shapePolys.NewOutline();
4839 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4840 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4841 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4842 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4843 shapePolys.Outline( 0 ).SetClosed( true );
4844
4845 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2 - anchorPos.x,
4846 aElem.pos1.y / 2 + aElem.pos2.y / 2 - anchorPos.y );
4847 shapePolys.Rotate( EDA_ANGLE( aElem.rotation, DEGREES_T ), center );
4848 pad->AddPrimitivePoly( F_Cu, shapePolys, 0, true );
4849 }
4850
4851 pad->SetThermalSpokeAngle( ANGLE_90 );
4852 pad->SetLayerSet( padLayers );
4853
4854 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
4855 }
4856 else
4857 {
4858 std::unique_ptr<PCB_SHAPE> fill =
4859 std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::RECTANGLE );
4860
4861 fill->SetFilled( true );
4862 fill->SetLayer( aLayer );
4863 fill->SetStroke( STROKE_PARAMS( 0 ) );
4864
4865 fill->SetStart( aElem.pos1 );
4866 fill->SetEnd( aElem.pos2 );
4867
4868 if( aElem.rotation != 0. )
4869 {
4870 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4871 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4872 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4873 }
4874
4875 aFootprint->Add( fill.release(), ADD_MODE::APPEND );
4876 }
4877}
4878
4879
4880void ALTIUM_PCB::HelperSetZoneLayers( ZONE& aZone, const ALTIUM_LAYER aAltiumLayer )
4881{
4882 LSET layerSet;
4883
4884 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
4885 layerSet.set( klayer );
4886
4887 aZone.SetLayerSet( layerSet );
4888}
4889
4890
4891void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE& aZone, const uint8_t aKeepoutRestrictions )
4892{
4893 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
4894 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
4895 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
4896 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
4897 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
4898
4899 aZone.SetDoNotAllowVias( keepoutRestrictionVia );
4900 aZone.SetDoNotAllowTracks( keepoutRestrictionTrack );
4901 aZone.SetDoNotAllowZoneFills( keepoutRestrictionCopper );
4902 aZone.SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
4903 aZone.SetDoNotAllowFootprints( false );
4904}
4905
4906
4908 const ALTIUM_LAYER aAltiumLayer,
4909 const uint8_t aKeepoutRestrictions )
4910{
4911 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
4912
4913 zone->SetIsRuleArea( true );
4914
4915 HelperSetZoneLayers( *zone, aAltiumLayer );
4916 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4917
4918 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4919
4920 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4922
4923 m_board->Add( zone.release(), ADD_MODE::APPEND );
4924}
4925
4926
4928 const PCB_SHAPE& aShape,
4929 const ALTIUM_LAYER aAltiumLayer,
4930 const uint8_t aKeepoutRestrictions )
4931{
4932 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
4933
4934 zone->SetIsRuleArea( true );
4935
4936 HelperSetZoneLayers( *zone, aAltiumLayer );
4937 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4938
4939 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4940
4941 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4943
4944 // TODO: zone->SetLocalCoord(); missing?
4945 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
4946}
4947
4948
4949std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
4950 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
4951{
4952 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
4953 return {}; // there is nothing to parse
4954
4955 auto elems =
4956 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
4957
4958 if( elems.first == elems.second )
4959 return {}; // there is nothing to parse
4960
4961 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
4962
4963 for( auto it = elems.first; it != elems.second; ++it )
4964 {
4965 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
4966
4968 {
4971 {
4972 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
4973 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4974 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4975 {
4976 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
4977 }
4978
4979 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4980 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4981 {
4982 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
4983 }
4984 }
4987 {
4988 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4989 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4990 {
4991 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
4992 }
4993
4994 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4995 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4996 {
4997 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
4998 }
4999 }
5000 }
5001 }
5002
5003 return layerExpansionPairs;
5004}
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:79
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:320
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:280
virtual LSET BoardLayerSet() const
Return the LSET for the board that this item resides on.
Manage layers needed to make a physical board.
void RemoveAll()
Delete all items in list and clear the list.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
int BuildBoardThicknessFromStackup() const
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h: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:544
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:589
KIFONT::FONT * GetFont() const
Definition eda_text.h:247
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:404
int GetTextWidth() const
Definition eda_text.h:264
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:428
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:389
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:295
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:310
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:318
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:510
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:420
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:365
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:125
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
void SetMode(MODE aMode)
Definition padstack.cpp:833
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:139
@ CUSTOM
Shapes can be defined on arbitrary layers.
Definition padstack.h:141
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:140
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:145
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:148
Definition pad.h:54
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:291
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition pad.cpp:312
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:298
double GetRadius() const
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h: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:118
const VECTOR2I & GetP1() const
Definition shape_arc.h:117
const VECTOR2I & GetP0() const
Definition shape_arc.h:116
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
int NextShape(int aPointIndex) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
bool IsArcStart(size_t aIndex) const
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
Simple container to manage line stroke parameters.
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition vector2d.h:546
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:561
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition vector2d.h:314
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetNeedRefill(bool aNeedRefill)
Definition zone.h:296
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:728
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:606
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:727
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:621
void SetIsFilled(bool isFilled)
Definition zone.h:293
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition zone.h:592
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:511
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:726
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:729
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:725
static int GetDefaultHatchPitch()
Definition zone.cpp:1281
@ CHAMFER_ACUTE_CORNERS
Acute angles are chamfered.
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
bool m_ImportSkipComponentBodies
Skip importing component bodies when importing some format files, such as Altium.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define MAX_CU_LAYERS
Definition layer_ids.h:176
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:676
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:912
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ In22_Cu
Definition layer_ids.h:87
@ In11_Cu
Definition layer_ids.h:76
@ In29_Cu
Definition layer_ids.h:94
@ In30_Cu
Definition layer_ids.h:95
@ User_8
Definition layer_ids.h:131
@ User_11
Definition layer_ids.h:134
@ In17_Cu
Definition layer_ids.h:82
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ In9_Cu
Definition layer_ids.h:74
@ User_6
Definition layer_ids.h:129
@ User_7
Definition layer_ids.h:130
@ In19_Cu
Definition layer_ids.h:84
@ In7_Cu
Definition layer_ids.h:72
@ In28_Cu
Definition layer_ids.h:93
@ In26_Cu
Definition layer_ids.h:91
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ User_14
Definition layer_ids.h:137
@ User_5
Definition layer_ids.h:128
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ In21_Cu
Definition layer_ids.h:86
@ In23_Cu
Definition layer_ids.h:88
@ B_Paste
Definition layer_ids.h:105
@ In15_Cu
Definition layer_ids.h:80
@ In2_Cu
Definition layer_ids.h:67
@ User_10
Definition layer_ids.h:133
@ User_9
Definition layer_ids.h:132
@ F_Fab
Definition layer_ids.h:119
@ In10_Cu
Definition layer_ids.h:75
@ Margin
Definition layer_ids.h:113
@ F_SilkS
Definition layer_ids.h:100
@ In4_Cu
Definition layer_ids.h:69
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Eco2_User
Definition layer_ids.h:110
@ In16_Cu
Definition layer_ids.h:81
@ In24_Cu
Definition layer_ids.h:89
@ In1_Cu
Definition layer_ids.h:66
@ User_3
Definition layer_ids.h:126
@ User_1
Definition layer_ids.h:124
@ User_12
Definition layer_ids.h:135
@ B_SilkS
Definition layer_ids.h:101
@ In13_Cu
Definition layer_ids.h:78
@ User_4
Definition layer_ids.h:127
@ In8_Cu
Definition layer_ids.h:73
@ In14_Cu
Definition layer_ids.h:79
@ User_13
Definition layer_ids.h:136
@ User_2
Definition layer_ids.h:125
@ In12_Cu
Definition layer_ids.h:77
@ In27_Cu
Definition layer_ids.h:92
@ In6_Cu
Definition layer_ids.h:71
@ In5_Cu
Definition layer_ids.h:70
@ In3_Cu
Definition layer_ids.h:68
@ In20_Cu
Definition layer_ids.h:85
@ F_Cu
Definition layer_ids.h:64
@ In18_Cu
Definition layer_ids.h:83
@ In25_Cu
Definition layer_ids.h:90
@ B_Fab
Definition layer_ids.h:118
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition kicad_algo.h:84
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:87
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:83
@ PTH
Plated through hole pad.
Definition padstack.h:82
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ RECTANGLE
Definition padstack.h:54
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.
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