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 (C) 2021-2024 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"
29
30#include <board.h>
32#include <pcb_dimension.h>
33#include <pad.h>
34#include <pcb_shape.h>
35#include <pcb_text.h>
36#include <pcb_track.h>
37#include <core/profile.h>
38#include <string_utils.h>
39#include <zone.h>
40
42
43#include <compoundfilereader.h>
45#include <font/outline_font.h>
46#include <project.h>
47#include <reporter.h>
48#include <trigo.h>
49#include <utf.h>
50#include <wx/docview.h>
51#include <wx/log.h>
52#include <wx/mstream.h>
53#include <wx/wfstream.h>
54#include <wx/zstream.h>
55#include <progress_reporter.h>
56#include <magic_enum.hpp>
57
58
59constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700
60
61
63{
64 return ( aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER )
65 || aLayer == ALTIUM_LAYER::MULTI_LAYER; // TODO: add IsAltiumLayerAPlane?
66}
67
68
70{
72}
73
74FOOTPRINT* ALTIUM_PCB::HelperGetFootprint( uint16_t aComponent ) const
75{
76 if( aComponent == ALTIUM_COMPONENT_NONE || m_components.size() <= aComponent )
77 {
78 THROW_IO_ERROR( wxString::Format( wxT( "Component creator tries to access component id %u "
79 "of %u existing components" ),
80 (unsigned)aComponent, (unsigned)m_components.size() ) );
81 }
82
83 return m_components.at( aComponent );
84}
85
86
88 const std::vector<ALTIUM_VERTICE>& aVertices )
89{
90 for( const ALTIUM_VERTICE& vertex : aVertices )
91 {
92 if( vertex.isRound )
93 {
94 EDA_ANGLE angle( vertex.endangle - vertex.startangle, DEGREES_T );
95 angle.Normalize();
96
97 double startradiant = DEG2RAD( vertex.startangle );
98 double endradiant = DEG2RAD( vertex.endangle );
99 VECTOR2I arcStartOffset = VECTOR2I( KiROUND( std::cos( startradiant ) * vertex.radius ),
100 -KiROUND( std::sin( startradiant ) * vertex.radius ) );
101
102 VECTOR2I arcEndOffset = VECTOR2I( KiROUND( std::cos( endradiant ) * vertex.radius ),
103 -KiROUND( std::sin( endradiant ) * vertex.radius ) );
104
105 VECTOR2I arcStart = vertex.center + arcStartOffset;
106 VECTOR2I arcEnd = vertex.center + arcEndOffset;
107
108 if( GetLineLength( arcStart, vertex.position )
109 < GetLineLength( arcEnd, vertex.position ) )
110 {
111 aLine.Append( SHAPE_ARC( vertex.center, arcStart, -angle ) );
112 }
113 else
114 {
115 aLine.Append( SHAPE_ARC( vertex.center, arcEnd, angle ) );
116 }
117 }
118 else
119 {
120 aLine.Append( vertex.position );
121 }
122 }
123
124 aLine.SetClosed( true );
125}
126
127
129{
130 auto override = m_layermap.find( aAltiumLayer );
131
132 if( override != m_layermap.end() )
133 {
134 return override->second;
135 }
136
137 switch( aAltiumLayer )
138 {
139 case ALTIUM_LAYER::UNKNOWN: return UNDEFINED_LAYER;
140
141 case ALTIUM_LAYER::TOP_LAYER: return F_Cu;
142 case ALTIUM_LAYER::MID_LAYER_1: return In1_Cu;
143 case ALTIUM_LAYER::MID_LAYER_2: return In2_Cu;
144 case ALTIUM_LAYER::MID_LAYER_3: return In3_Cu;
145 case ALTIUM_LAYER::MID_LAYER_4: return In4_Cu;
146 case ALTIUM_LAYER::MID_LAYER_5: return In5_Cu;
147 case ALTIUM_LAYER::MID_LAYER_6: return In6_Cu;
148 case ALTIUM_LAYER::MID_LAYER_7: return In7_Cu;
149 case ALTIUM_LAYER::MID_LAYER_8: return In8_Cu;
150 case ALTIUM_LAYER::MID_LAYER_9: return In9_Cu;
151 case ALTIUM_LAYER::MID_LAYER_10: return In10_Cu;
152 case ALTIUM_LAYER::MID_LAYER_11: return In11_Cu;
153 case ALTIUM_LAYER::MID_LAYER_12: return In12_Cu;
154 case ALTIUM_LAYER::MID_LAYER_13: return In13_Cu;
155 case ALTIUM_LAYER::MID_LAYER_14: return In14_Cu;
156 case ALTIUM_LAYER::MID_LAYER_15: return In15_Cu;
157 case ALTIUM_LAYER::MID_LAYER_16: return In16_Cu;
158 case ALTIUM_LAYER::MID_LAYER_17: return In17_Cu;
159 case ALTIUM_LAYER::MID_LAYER_18: return In18_Cu;
160 case ALTIUM_LAYER::MID_LAYER_19: return In19_Cu;
161 case ALTIUM_LAYER::MID_LAYER_20: return In20_Cu;
162 case ALTIUM_LAYER::MID_LAYER_21: return In21_Cu;
163 case ALTIUM_LAYER::MID_LAYER_22: return In22_Cu;
164 case ALTIUM_LAYER::MID_LAYER_23: return In23_Cu;
165 case ALTIUM_LAYER::MID_LAYER_24: return In24_Cu;
166 case ALTIUM_LAYER::MID_LAYER_25: return In25_Cu;
167 case ALTIUM_LAYER::MID_LAYER_26: return In26_Cu;
168 case ALTIUM_LAYER::MID_LAYER_27: return In27_Cu;
169 case ALTIUM_LAYER::MID_LAYER_28: return In28_Cu;
170 case ALTIUM_LAYER::MID_LAYER_29: return In29_Cu;
171 case ALTIUM_LAYER::MID_LAYER_30: return In30_Cu;
172 case ALTIUM_LAYER::BOTTOM_LAYER: return B_Cu;
173
174 case ALTIUM_LAYER::TOP_OVERLAY: return F_SilkS;
175 case ALTIUM_LAYER::BOTTOM_OVERLAY: return B_SilkS;
176 case ALTIUM_LAYER::TOP_PASTE: return F_Paste;
177 case ALTIUM_LAYER::BOTTOM_PASTE: return B_Paste;
178 case ALTIUM_LAYER::TOP_SOLDER: return F_Mask;
179 case ALTIUM_LAYER::BOTTOM_SOLDER: return B_Mask;
180
181 case ALTIUM_LAYER::INTERNAL_PLANE_1: return UNDEFINED_LAYER;
182 case ALTIUM_LAYER::INTERNAL_PLANE_2: return UNDEFINED_LAYER;
183 case ALTIUM_LAYER::INTERNAL_PLANE_3: return UNDEFINED_LAYER;
184 case ALTIUM_LAYER::INTERNAL_PLANE_4: return UNDEFINED_LAYER;
185 case ALTIUM_LAYER::INTERNAL_PLANE_5: return UNDEFINED_LAYER;
186 case ALTIUM_LAYER::INTERNAL_PLANE_6: return UNDEFINED_LAYER;
187 case ALTIUM_LAYER::INTERNAL_PLANE_7: return UNDEFINED_LAYER;
188 case ALTIUM_LAYER::INTERNAL_PLANE_8: return UNDEFINED_LAYER;
189 case ALTIUM_LAYER::INTERNAL_PLANE_9: return UNDEFINED_LAYER;
190 case ALTIUM_LAYER::INTERNAL_PLANE_10: return UNDEFINED_LAYER;
191 case ALTIUM_LAYER::INTERNAL_PLANE_11: return UNDEFINED_LAYER;
192 case ALTIUM_LAYER::INTERNAL_PLANE_12: return UNDEFINED_LAYER;
193 case ALTIUM_LAYER::INTERNAL_PLANE_13: return UNDEFINED_LAYER;
194 case ALTIUM_LAYER::INTERNAL_PLANE_14: return UNDEFINED_LAYER;
195 case ALTIUM_LAYER::INTERNAL_PLANE_15: return UNDEFINED_LAYER;
196 case ALTIUM_LAYER::INTERNAL_PLANE_16: return UNDEFINED_LAYER;
197
198 case ALTIUM_LAYER::DRILL_GUIDE: return Dwgs_User;
199 case ALTIUM_LAYER::KEEP_OUT_LAYER: return Margin;
200
201 case ALTIUM_LAYER::MECHANICAL_1: return User_1; //Edge_Cuts;
202 case ALTIUM_LAYER::MECHANICAL_2: return User_2;
203 case ALTIUM_LAYER::MECHANICAL_3: return User_3;
204 case ALTIUM_LAYER::MECHANICAL_4: return User_4;
205 case ALTIUM_LAYER::MECHANICAL_5: return User_5;
206 case ALTIUM_LAYER::MECHANICAL_6: return User_6;
207 case ALTIUM_LAYER::MECHANICAL_7: return User_7;
208 case ALTIUM_LAYER::MECHANICAL_8: return User_8;
209 case ALTIUM_LAYER::MECHANICAL_9: return User_9;
210 case ALTIUM_LAYER::MECHANICAL_10: return Dwgs_User;
211 case ALTIUM_LAYER::MECHANICAL_11: return Eco2_User; //Eco1 is used for unknown elements
212 case ALTIUM_LAYER::MECHANICAL_12: return F_Fab;
213 case ALTIUM_LAYER::MECHANICAL_13: return B_Fab; // Don't use courtyard layers for other purposes
214 case ALTIUM_LAYER::MECHANICAL_14: return UNDEFINED_LAYER;
215 case ALTIUM_LAYER::MECHANICAL_15: return UNDEFINED_LAYER;
216 case ALTIUM_LAYER::MECHANICAL_16: return UNDEFINED_LAYER;
217
218 case ALTIUM_LAYER::DRILL_DRAWING: return Dwgs_User;
219 case ALTIUM_LAYER::MULTI_LAYER: return UNDEFINED_LAYER;
220 case ALTIUM_LAYER::CONNECTIONS: return UNDEFINED_LAYER;
221 case ALTIUM_LAYER::BACKGROUND: return UNDEFINED_LAYER;
222 case ALTIUM_LAYER::DRC_ERROR_MARKERS: return UNDEFINED_LAYER;
223 case ALTIUM_LAYER::SELECTIONS: return UNDEFINED_LAYER;
224 case ALTIUM_LAYER::VISIBLE_GRID_1: return UNDEFINED_LAYER;
225 case ALTIUM_LAYER::VISIBLE_GRID_2: return UNDEFINED_LAYER;
226 case ALTIUM_LAYER::PAD_HOLES: return UNDEFINED_LAYER;
227 case ALTIUM_LAYER::VIA_HOLES: return UNDEFINED_LAYER;
228
229 default: return UNDEFINED_LAYER;
230 }
231}
232
233
234std::vector<PCB_LAYER_ID> ALTIUM_PCB::GetKicadLayersToIterate( ALTIUM_LAYER aAltiumLayer ) const
235{
236 static std::set<ALTIUM_LAYER> altiumLayersWithWarning;
237
238 if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER || aAltiumLayer == ALTIUM_LAYER::KEEP_OUT_LAYER )
239 {
240 std::vector<PCB_LAYER_ID> layers;
241 layers.reserve( MAX_CU_LAYERS );
242
243 for( PCB_LAYER_ID layer : LSET::AllCuMask().Seq() )
244 {
245 if( !m_board || m_board->IsLayerEnabled( layer ) )
246 layers.emplace_back( layer );
247 }
248
249 return layers;
250 }
251
252 PCB_LAYER_ID klayer = GetKicadLayer( aAltiumLayer );
253
254 if( klayer == UNDEFINED_LAYER )
255 {
256 auto it = m_layerNames.find( aAltiumLayer );
257 wxString layerName = it != m_layerNames.end() ? it->second : wxString::Format( wxT( "(%d)" ),
258 (int) aAltiumLayer );
259
260 if( m_reporter && altiumLayersWithWarning.insert( aAltiumLayer ).second )
261 {
262 m_reporter->Report( wxString::Format(
263 _( "Altium layer %s has no KiCad equivalent. It has been moved to KiCad "
264 "layer Eco1_User." ), layerName ), RPT_SEVERITY_INFO );
265 }
266
267 klayer = Eco1_User;
269 }
270
271 return { klayer };
272}
273
274
275ALTIUM_PCB::ALTIUM_PCB( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter,
276 LAYER_MAPPING_HANDLER& aHandler, REPORTER* aReporter,
277 const wxString& aLibrary, const wxString& aFootprintName )
278{
279 m_board = aBoard;
280 m_progressReporter = aProgressReporter;
281 m_layerMappingHandler = aHandler;
282 m_reporter = aReporter;
283 m_doneCount = 0;
285 m_totalCount = 0;
287 m_library = aLibrary;
288 m_footprintName = aFootprintName;
289}
290
292{
293}
294
296{
297 const unsigned PROGRESS_DELTA = 250;
298
300 {
301 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
302 {
304 / std::max( 1U, m_totalCount ) );
305
307 THROW_IO_ERROR( _( "Open cancelled by user." ) );
308
310 }
311 }
312}
313
314void ALTIUM_PCB::Parse( const ALTIUM_COMPOUND_FILE& altiumPcbFile,
315 const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
316{
317 // this vector simply declares in which order which functions to call.
318 const std::vector<std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>> parserOrder = {
319 { true, ALTIUM_PCB_DIR::FILE_HEADER,
320 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
321 {
322 this->ParseFileHeader( aFile, fileHeader );
323 } },
324 { true, ALTIUM_PCB_DIR::BOARD6,
325 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
326 {
327 this->ParseBoard6Data( aFile, fileHeader );
328 } },
329 { false, ALTIUM_PCB_DIR::EXTENDPRIMITIVEINFORMATION,
330 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
331 {
332 this->ParseExtendedPrimitiveInformationData( aFile, fileHeader );
333 } },
334 { true, ALTIUM_PCB_DIR::COMPONENTS6,
335 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
336 {
337 this->ParseComponents6Data( aFile, fileHeader );
338 } },
339 { false, ALTIUM_PCB_DIR::MODELS,
340 [this, aFileMapping]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
341 {
342 std::vector<std::string> dir{ aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) };
343 this->ParseModelsData( aFile, fileHeader, dir );
344 } },
345 { true, ALTIUM_PCB_DIR::COMPONENTBODIES6,
346 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
347 {
348 this->ParseComponentsBodies6Data( aFile, fileHeader );
349 } },
350 { true, ALTIUM_PCB_DIR::NETS6,
351 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
352 {
353 this->ParseNets6Data( aFile, fileHeader );
354 } },
355 { true, ALTIUM_PCB_DIR::CLASSES6,
356 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
357 {
358 this->ParseClasses6Data( aFile, fileHeader );
359 } },
360 { true, ALTIUM_PCB_DIR::RULES6,
361 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
362 {
363 this->ParseRules6Data( aFile, fileHeader );
364 } },
365 { true, ALTIUM_PCB_DIR::DIMENSIONS6,
366 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
367 {
368 this->ParseDimensions6Data( aFile, fileHeader );
369 } },
370 { true, ALTIUM_PCB_DIR::POLYGONS6,
371 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
372 {
373 this->ParsePolygons6Data( aFile, fileHeader );
374 } },
375 { true, ALTIUM_PCB_DIR::ARCS6,
376 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
377 {
378 this->ParseArcs6Data( aFile, fileHeader );
379 } },
380 { true, ALTIUM_PCB_DIR::PADS6,
381 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
382 {
383 this->ParsePads6Data( aFile, fileHeader );
384 } },
385 { true, ALTIUM_PCB_DIR::VIAS6,
386 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
387 {
388 this->ParseVias6Data( aFile, fileHeader );
389 } },
390 { true, ALTIUM_PCB_DIR::TRACKS6,
391 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
392 {
393 this->ParseTracks6Data( aFile, fileHeader );
394 } },
395 { false, ALTIUM_PCB_DIR::WIDESTRINGS6,
396 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
397 {
398 this->ParseWideStrings6Data( aFile, fileHeader );
399 } },
400 { true, ALTIUM_PCB_DIR::TEXTS6,
401 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
402 {
403 this->ParseTexts6Data( aFile, fileHeader );
404 } },
405 { true, ALTIUM_PCB_DIR::FILLS6,
406 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
407 {
408 this->ParseFills6Data( aFile, fileHeader );
409 } },
410 { false, ALTIUM_PCB_DIR::BOARDREGIONS,
411 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
412 {
413 this->ParseBoardRegionsData( aFile, fileHeader );
414 } },
415 { true, ALTIUM_PCB_DIR::SHAPEBASEDREGIONS6,
416 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
417 {
418 this->ParseShapeBasedRegions6Data( aFile, fileHeader );
419 } },
420 { true, ALTIUM_PCB_DIR::REGIONS6,
421 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
422 {
423 this->ParseRegions6Data( aFile, fileHeader );
424 } }
425 };
426
427 if( m_progressReporter != nullptr )
428 {
429 // Count number of records we will read for the progress reporter
430 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
431 {
432 bool isRequired;
435 std::tie( isRequired, directory, fp ) = cur;
436
437 if( directory == ALTIUM_PCB_DIR::FILE_HEADER )
438 continue;
439
440 const auto& mappedDirectory = aFileMapping.find( directory );
441
442 if( mappedDirectory == aFileMapping.end() )
443 continue;
444
445 const std::vector<std::string> mappedFile{ mappedDirectory->second, "Header" };
446 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
447
448 if( file == nullptr )
449 continue;
450
451 ALTIUM_BINARY_PARSER reader( altiumPcbFile, file );
452 uint32_t numOfRecords = reader.Read<uint32_t>();
453
454 if( reader.HasParsingError() )
455 {
456 if( m_reporter )
457 {
458 m_reporter->Report( wxString::Format( _( "'%s' was not parsed correctly." ),
459 FormatPath( mappedFile ) ),
461 }
462
463 continue;
464 }
465
466 m_totalCount += numOfRecords;
467
468 if( reader.GetRemainingBytes() != 0 )
469 {
470 if( m_reporter )
471 {
472 m_reporter->Report( wxString::Format( _( "'%s' was not fully parsed." ),
473 FormatPath( mappedFile ) ),
475 }
476
477 continue;
478 }
479 }
480 }
481
482 const auto& boardDirectory = aFileMapping.find( ALTIUM_PCB_DIR::BOARD6 );
483
484 if( boardDirectory != aFileMapping.end() )
485 {
486 std::vector<std::string> mappedFile{ boardDirectory->second, "Data" };
487
488 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
489
490 if( !file )
491 {
493 "This file does not appear to be in a valid PCB Binary Version 6.0 format. In "
494 "Altium Designer, "
495 "make sure to save as \"PCB Binary Files (*.PcbDoc)\"." ) );
496 }
497 }
498
499 // Parse data in specified order
500 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
501 {
502 bool isRequired;
505 std::tie( isRequired, directory, fp ) = cur;
506
507 const auto& mappedDirectory = aFileMapping.find( directory );
508
509 if( mappedDirectory == aFileMapping.end() )
510 {
511 wxASSERT_MSG( !isRequired, wxString::Format( wxT( "Altium Directory of kind %d was "
512 "expected, but no mapping is "
513 "present in the code" ),
514 directory ) );
515 continue;
516 }
517
518 std::vector<std::string> mappedFile{ mappedDirectory->second };
519
520 if( directory != ALTIUM_PCB_DIR::FILE_HEADER )
521 mappedFile.emplace_back( "Data" );
522
523 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
524
525 if( file != nullptr )
526 {
527 fp( altiumPcbFile, file );
528 }
529 else if( isRequired )
530 {
531 if( m_reporter )
532 {
533 m_reporter->Report( wxString::Format( _( "File not found: '%s' for directory '%s'." ),
534 FormatPath( mappedFile ),
535 magic_enum::enum_name( directory ) ),
537 }
538 }
539 }
540
541 // fixup zone priorities since Altium stores them in the opposite order
542 for( ZONE* zone : m_polygons )
543 {
544 if( !zone )
545 continue;
546
547 // Altium "fills" - not poured in Altium
548 if( zone->GetAssignedPriority() == 1000 )
549 {
550 // Unlikely, but you never know
551 if( m_highest_pour_index >= 1000 )
552 zone->SetAssignedPriority( m_highest_pour_index + 1 );
553
554 continue;
555 }
556
557 int priority = m_highest_pour_index - zone->GetAssignedPriority();
558
559 zone->SetAssignedPriority( priority >= 0 ? priority : 0 );
560 }
561
562 // change priority of outer zone to zero
563 for( std::pair<const ALTIUM_LAYER, ZONE*>& zone : m_outer_plane )
564 zone.second->SetAssignedPriority( 0 );
565
566 // Simplify and fracture zone fills in case we constructed them from tracks (hatched fill)
567 for( ZONE* zone : m_polygons )
568 {
569 if( !zone )
570 continue;
571
572 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
573 {
574 if( !zone->HasFilledPolysForLayer( layer ) )
575 continue;
576
577 zone->GetFilledPolysList( layer )->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
578 }
579 }
580
581 // Altium doesn't appear to store either the dimension value nor the dimensioned object in
582 // the dimension record. (Yes, there is a REFERENCE0OBJECTID, but it doesn't point to the
583 // dimensioned object.) We attempt to plug this gap by finding a colocated arc or circle
584 // and using its radius. If there are more than one such arcs/circles, well, :shrug:.
586 {
587 int radius = 0;
588
589 for( BOARD_ITEM* item : m_board->Drawings() )
590 {
591 if( item->Type() != PCB_SHAPE_T )
592 continue;
593
594 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
595
596 if( shape->GetShape() != SHAPE_T::ARC && shape->GetShape() != SHAPE_T::CIRCLE )
597 continue;
598
599 if( shape->GetPosition() == dim->GetPosition() )
600 {
601 radius = shape->GetRadius();
602 break;
603 }
604 }
605
606 if( radius == 0 )
607 {
608 for( PCB_TRACK* track : m_board->Tracks() )
609 {
610 if( track->Type() != PCB_ARC_T )
611 continue;
612
613 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
614
615 if( arc->GetCenter() == dim->GetPosition() )
616 {
617 radius = arc->GetRadius();
618 break;
619 }
620 }
621 }
622
623 // Move the radius point onto the circumference
624 VECTOR2I radialLine = dim->GetEnd() - dim->GetStart();
625 int totalLength = radialLine.EuclideanNorm();
626
627 // Enforce a minimum on the radialLine else we won't have enough precision to get the
628 // angle from it.
629 radialLine = radialLine.Resize( std::max( radius, 2 ) );
630 dim->SetEnd( dim->GetStart() + (VECTOR2I) radialLine );
631 dim->SetLeaderLength( totalLength - radius );
632 dim->Update();
633 }
634
635 // center board
637
640
641 int desired_x = ( w - bbbox.GetWidth() ) / 2;
642 int desired_y = ( h - bbbox.GetHeight() ) / 2;
643
644 VECTOR2I movementVector( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() );
645 m_board->Move( movementVector );
646
648 bds.SetAuxOrigin( bds.GetAuxOrigin() + movementVector );
649 bds.SetGridOrigin( bds.GetGridOrigin() + movementVector );
650
652}
653
654
656 const wxString& aFootprintName )
657{
658 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
659
660 // TODO: what should we do with those layers?
661 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_14, Eco2_User );
662 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_15, Eco2_User );
663 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_16, Eco2_User );
664
665 m_unicodeStrings.clear();
667
668 // TODO: WideStrings are stored as parameterMap in the case of footprints, not as binary
669 // std::string unicodeStringsStreamName = aFootprintName.ToStdString() + "\\WideStrings";
670 // const CFB::COMPOUND_FILE_ENTRY* unicodeStringsData = altiumLibFile.FindStream( unicodeStringsStreamName );
671 // if( unicodeStringsData != nullptr )
672 // {
673 // ParseWideStrings6Data( altiumLibFile, unicodeStringsData );
674 // }
675
676 std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*> ret = altiumLibFile.FindLibFootprintDirName(aFootprintName);
677
678 wxString fpDirName = std::get<0>( ret );
679 const CFB::COMPOUND_FILE_ENTRY* footprintStream = std::get<1>( ret );
680
681 if( fpDirName.IsEmpty() )
682 {
684 wxString::Format( _( "Footprint directory not found: '%s'." ), aFootprintName ) );
685 }
686
687 const std::vector<std::string> streamName{ fpDirName.ToStdString(), "Data" };
688 const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( footprintStream, { "Data" } );
689
690 if( footprintData == nullptr )
691 {
692 THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ),
693 FormatPath( streamName ) ) );
694 }
695
696 ALTIUM_BINARY_PARSER parser( altiumLibFile, footprintData );
697
699 //wxString footprintName = parser.ReadWxString(); // Not used (single-byte char set)
700 parser.SkipSubrecord();
701
702 LIB_ID fpID = AltiumToKiCadLibID( "", aFootprintName ); // TODO: library name
703 footprint->SetFPID( fpID );
704
705 const std::vector<std::string> parametersStreamName{ fpDirName.ToStdString(),
706 "Parameters" };
707 const CFB::COMPOUND_FILE_ENTRY* parametersData =
708 altiumLibFile.FindStream( footprintStream, { "Parameters" } );
709
710 if( parametersData != nullptr )
711 {
712 ALTIUM_BINARY_PARSER parametersReader( altiumLibFile, parametersData );
713 std::map<wxString, wxString> parameterProperties = parametersReader.ReadProperties();
714 wxString description = ALTIUM_PROPS_UTILS::ReadString( parameterProperties,
715 wxT( "DESCRIPTION" ), wxT( "" ) );
716 footprint->SetLibDescription( description );
717 }
718 else
719 {
720 if( m_reporter )
721 {
722 m_reporter->Report( wxString::Format( _( "File not found: '%s'." ),
723 FormatPath( parametersStreamName ) ),
725 }
726
727 footprint->SetLibDescription( wxT( "" ) );
728 }
729
730 const std::vector<std::string> extendedPrimitiveInformationStreamName{
731 "ExtendedPrimitiveInformation", "Data"
732 };
733 const CFB::COMPOUND_FILE_ENTRY* extendedPrimitiveInformationData =
734 altiumLibFile.FindStream( footprintStream, extendedPrimitiveInformationStreamName );
735
736 if( extendedPrimitiveInformationData != nullptr )
737 ParseExtendedPrimitiveInformationData( altiumLibFile, extendedPrimitiveInformationData );
738
739 footprint->SetReference( wxT( "REF**" ) );
740 footprint->SetValue( aFootprintName );
741 footprint->Reference().SetVisible( true ); // TODO: extract visibility information
742 footprint->Value().SetVisible( true );
743
744 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
745 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
746
747 for( PCB_FIELD* field : footprint->Fields() )
748 {
749 field->SetTextSize( defaultTextSize );
750 field->SetTextThickness( defaultTextThickness );
751 }
752
753 for( int primitiveIndex = 0; parser.GetRemainingBytes() >= 4; primitiveIndex++ )
754 {
755 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( parser.Peek<uint8_t>() );
756
757 switch( recordtype )
758 {
759 case ALTIUM_RECORD::ARC:
760 {
761 AARC6 arc( parser );
762 ConvertArcs6ToFootprintItem( footprint.get(), arc, primitiveIndex, false );
763 break;
764 }
765 case ALTIUM_RECORD::PAD:
766 {
767 APAD6 pad( parser );
768 ConvertPads6ToFootprintItem( footprint.get(), pad );
769 break;
770 }
771 case ALTIUM_RECORD::VIA:
772 {
773 AVIA6 via( parser );
774 // TODO: implement
775 break;
776 }
777 case ALTIUM_RECORD::TRACK:
778 {
779 ATRACK6 track( parser );
780 ConvertTracks6ToFootprintItem( footprint.get(), track, primitiveIndex, false );
781 break;
782 }
783 case ALTIUM_RECORD::TEXT:
784 {
785 ATEXT6 text( parser, m_unicodeStrings );
786 ConvertTexts6ToFootprintItem( footprint.get(), text );
787 break;
788 }
789 case ALTIUM_RECORD::FILL:
790 {
791 AFILL6 fill( parser );
792 ConvertFills6ToFootprintItem( footprint.get(), fill, false );
793 break;
794 }
795 case ALTIUM_RECORD::REGION:
796 {
797 AREGION6 region( parser, false );
798 ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region, primitiveIndex );
799 break;
800 }
801 case ALTIUM_RECORD::MODEL:
802 {
803 ACOMPONENTBODY6 componentBody( parser );
804 // Won't be supported for now, as we would need to extract the model
805 break;
806 }
807 default:
808 THROW_IO_ERROR( wxString::Format( _( "Record of unknown type: '%d'." ), recordtype ) );
809 }
810 }
811
812 // Auto-position reference and value
813 footprint->AutoPositionFields();
814
815 if( parser.HasParsingError() )
816 {
817 THROW_IO_ERROR( wxString::Format( wxT( "%s stream was not parsed correctly" ),
818 FormatPath( streamName ) ) );
819 }
820
821 if( parser.GetRemainingBytes() != 0 )
822 {
823 THROW_IO_ERROR( wxString::Format( wxT( "%s stream is not fully parsed" ),
824 FormatPath( streamName ) ) );
825 }
826
827 return footprint.release();
828}
829
830int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
831{
832 if( aId == ALTIUM_NET_UNCONNECTED )
833 {
835 }
836 else if( m_altiumToKicadNetcodes.size() < aId )
837 {
838 THROW_IO_ERROR( wxString::Format( wxT( "Netcode with id %d does not exist. Only %d nets "
839 "are known" ),
840 aId, m_altiumToKicadNetcodes.size() ) );
841 }
842 else
843 {
844 return m_altiumToKicadNetcodes[ aId ];
845 }
846}
847
848const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
849{
850 const auto rules = m_rules.find( aKind );
851
852 if( rules == m_rules.end() )
853 return nullptr;
854
855 for( const ARULE6& rule : rules->second )
856 {
857 if( rule.name == aName )
858 return &rule;
859 }
860
861 return nullptr;
862}
863
865{
866 const auto rules = m_rules.find( aKind );
867
868 if( rules == m_rules.end() )
869 return nullptr;
870
871 for( const ARULE6& rule : rules->second )
872 {
873 if( rule.scope1expr == wxT( "All" ) && rule.scope2expr == wxT( "All" ) )
874 return &rule;
875 }
876
877 return nullptr;
878}
879
881 const CFB::COMPOUND_FILE_ENTRY* aEntry )
882{
883 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
884
886 wxString header = reader.ReadWxString();
887
888 //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
889
890 //reader.SkipSubrecord();
891
892 // TODO: does not seem to work all the time at the moment
893 //if( reader.GetRemainingBytes() != 0 )
894 // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
895}
896
897
899 const CFB::COMPOUND_FILE_ENTRY* aEntry )
900{
902 m_progressReporter->Report( _( "Loading extended primitive information data..." ) );
903
904 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
905
906 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
907 {
908 checkpoint();
909 AEXTENDED_PRIMITIVE_INFORMATION elem( reader );
910
912 std::move( elem ) );
913 }
914
915 if( reader.GetRemainingBytes() != 0 )
916 THROW_IO_ERROR( wxT( "ExtendedPrimitiveInformation stream is not fully parsed" ) );
917}
918
919
921 const CFB::COMPOUND_FILE_ENTRY* aEntry )
922{
924 m_progressReporter->Report( _( "Loading board data..." ) );
925
926 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
927
928 checkpoint();
929 ABOARD6 elem( reader );
930
931 if( reader.GetRemainingBytes() != 0 )
932 THROW_IO_ERROR( wxT( "Board6 stream is not fully parsed" ) );
933
936
937 // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
938 size_t layercount = 0;
939 size_t layerid = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
940
941 while( layerid < elem.stackup.size() && layerid != 0 )
942 {
943 layerid = elem.stackup[ layerid - 1 ].nextId;
944 layercount++;
945 }
946
947 size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
948 m_board->SetCopperLayerCount( kicadLayercount );
949
951 BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
952
953 // create board stackup
954 stackup.RemoveAll(); // Just to be sure
955 stackup.BuildDefaultStackupList( &designSettings, layercount );
956
957 auto it = stackup.GetList().begin();
958
959 // find first copper layer
960 for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
961 ;
962
963 auto curLayer = static_cast<int>( F_Cu );
964
965 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
966 altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
967 altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
968 {
969 // array starts with 0, but stackup with 1
970 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
971
972 // handle unused layer in case of odd layercount
973 if( layer.nextId == 0 && layercount != kicadLayercount )
974 {
975 m_board->SetLayerName( ( *it )->GetBrdLayerId(), wxT( "[unused]" ) );
976
977 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
978 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
979
980 ( *it )->SetThickness( 0 );
981
982 ++it;
983
984 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
985 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
986
987 ( *it )->SetThickness( 0, 0 );
988 ( *it )->SetThicknessLocked( true, 0 );
989 ++it;
990 }
991
992 m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ),
993 static_cast<PCB_LAYER_ID>( curLayer++ ) } );
994
995 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
996 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
997
998 ( *it )->SetThickness( layer.copperthick );
999
1000 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1001 PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
1002
1003 m_board->SetLayerName( klayer, layer.name );
1004
1005 if( layer.copperthick == 0 )
1006 m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
1007 else if( IsAltiumLayerAPlane( alayer ) )
1008 m_board->SetLayerType( klayer, LAYER_T::LT_POWER );
1009
1010 if( klayer == B_Cu )
1011 {
1012 if( layer.nextId != 0 )
1013 THROW_IO_ERROR( wxT( "Board6 stream, unexpected id while parsing last stackup layer" ) );
1014
1015 // overwrite entry from internal -> bottom
1016 m_layermap[alayer] = B_Cu;
1017 break;
1018 }
1019
1020 ++it;
1021
1022 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
1023 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
1024
1025 ( *it )->SetThickness( layer.dielectricthick, 0 );
1026 ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
1027 NotSpecifiedPrm() :
1028 wxString( layer.dielectricmaterial ) );
1029 ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
1030
1031 ++it;
1032 }
1033
1034 remapUnsureLayers( elem.stackup );
1035
1036 // Set name of all non-cu layers
1037 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_OVERLAY );
1038 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::BOTTOM_SOLDER ); altiumLayerId++ )
1039 {
1040 // array starts with 0, but stackup with 1
1041 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1042
1043 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1044 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1045
1046 m_board->SetLayerName( klayer, layer.name );
1047 }
1048
1049 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_1 );
1050 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_16 ); altiumLayerId++ )
1051 {
1052 // array starts with 0, but stackup with 1
1053 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1054
1055 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1056 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1057
1058 m_board->SetLayerName( klayer, layer.name );
1059 }
1060
1062}
1063
1064
1065void ALTIUM_PCB::remapUnsureLayers( std::vector<ABOARD6_LAYER_STACKUP>& aStackup )
1066{
1067 LSET enabledLayers = m_board->GetEnabledLayers();
1068 LSET validRemappingLayers = enabledLayers | LSET::AllBoardTechMask() |
1070
1071 std::vector<INPUT_LAYER_DESC> inputLayers;
1072 std::map<wxString, ALTIUM_LAYER> altiumLayerNameMap;
1073
1074 for( size_t ii = 0; ii < aStackup.size(); ii++ )
1075 {
1076 ABOARD6_LAYER_STACKUP& curLayer = aStackup[ii];
1077 ALTIUM_LAYER layer_num = static_cast<ALTIUM_LAYER>( ii + 1 );
1078 INPUT_LAYER_DESC iLdesc;
1079
1080 if( ii > m_board->GetCopperLayerCount() && layer_num != ALTIUM_LAYER::BOTTOM_LAYER
1081 && !( layer_num >= ALTIUM_LAYER::TOP_OVERLAY
1082 && layer_num <= ALTIUM_LAYER::BOTTOM_SOLDER )
1083 && !( layer_num >= ALTIUM_LAYER::MECHANICAL_1
1084 && layer_num <= ALTIUM_LAYER::MECHANICAL_16 ) )
1085 {
1086 if( layer_num < ALTIUM_LAYER::BOTTOM_LAYER )
1087 continue;
1088
1089 iLdesc.AutoMapLayer = PCB_LAYER_ID::UNDEFINED_LAYER;
1090 }
1091 else
1092 {
1093 iLdesc.AutoMapLayer = GetKicadLayer( layer_num );
1094 }
1095
1096 iLdesc.Name = curLayer.name;
1097 iLdesc.PermittedLayers = validRemappingLayers;
1098 iLdesc.Required = ii < m_board->GetCopperLayerCount() || layer_num == ALTIUM_LAYER::BOTTOM_LAYER;
1099
1100 inputLayers.push_back( iLdesc );
1101 altiumLayerNameMap.insert( { curLayer.name, layer_num } );
1102 m_layerNames.insert( { layer_num, curLayer.name } );
1103 }
1104
1105 if( inputLayers.size() == 0 )
1106 return;
1107
1108 // Callback:
1109 std::map<wxString, PCB_LAYER_ID> reMappedLayers = m_layerMappingHandler( inputLayers );
1110 enabledLayers = LSET();
1111 m_layermap.clear();
1112
1113 for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers )
1114 {
1115 if( layerPair.second == PCB_LAYER_ID::UNDEFINED_LAYER )
1116 {
1117 wxFAIL_MSG( wxT( "Unexpected Layer ID" ) );
1118 continue;
1119 }
1120
1121 ALTIUM_LAYER altiumID = altiumLayerNameMap.at( layerPair.first );
1122 m_layermap.insert_or_assign( altiumID, layerPair.second );
1123 enabledLayers |= LSET( layerPair.second );
1124 }
1125
1126 m_board->SetEnabledLayers( enabledLayers );
1127 m_board->SetVisibleLayers( enabledLayers );
1128}
1129
1130
1131void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
1132{
1133 SHAPE_LINE_CHAIN lineChain;
1134 HelperShapeLineChainFromAltiumVertices( lineChain, aVertices );
1135
1137 LINE_STYLE::SOLID );
1138
1139 for( int i = 0; i <= lineChain.PointCount() && i != -1; i = lineChain.NextShape( i ) )
1140 {
1141 if( lineChain.IsArcStart( i ) )
1142 {
1143 const SHAPE_ARC& currentArc = lineChain.Arc( lineChain.ArcIndex( i ) );
1144 int nextShape = lineChain.NextShape( i );
1145 bool isLastShape = nextShape < 0;
1146
1147 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::ARC );
1148
1149 shape->SetStroke( stroke );
1150 shape->SetLayer( Edge_Cuts );
1151 shape->SetArcGeometry( currentArc.GetP0(), currentArc.GetArcMid(), currentArc.GetP1() );
1152
1153 m_board->Add( shape.release(), ADD_MODE::APPEND );
1154 }
1155 else
1156 {
1157 const SEG& seg = lineChain.Segment( i );
1158
1159 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1160
1161 shape->SetStroke( stroke );
1162 shape->SetLayer( Edge_Cuts );
1163 shape->SetStart( seg.A );
1164 shape->SetEnd( seg.B );
1165
1166 m_board->Add( shape.release(), ADD_MODE::APPEND );
1167 }
1168 }
1169}
1170
1171
1173 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1174{
1175 if( m_progressReporter )
1176 m_progressReporter->Report( _( "Loading netclasses..." ) );
1177
1178 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1179
1180 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1181 {
1182 checkpoint();
1183 ACLASS6 elem( reader );
1184
1185 if( elem.kind == ALTIUM_CLASS_KIND::NET_CLASS )
1186 {
1187 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( elem.name );
1188
1189 for( const wxString& name : elem.names )
1190 {
1191 m_board->GetDesignSettings().m_NetSettings->m_NetClassPatternAssignments.push_back(
1192 {
1193 std::make_unique<EDA_COMBINED_MATCHER>( name, CTX_NETCLASS ),
1194 nc->GetName()
1195 } );
1196 }
1197
1198 if( m_board->GetDesignSettings().m_NetSettings->m_NetClasses.count( nc->GetName() ) )
1199 {
1200 // Name conflict, happens in some unknown circumstances
1201 // unique_ptr will delete nc on this code path
1202 if( m_reporter )
1203 {
1204 wxString msg;
1205 msg.Printf( _( "More than one Altium netclass with name '%s' found. "
1206 "Only the first one will be imported." ), elem.name );
1208 }
1209 }
1210 else
1211 {
1212 m_board->GetDesignSettings().m_NetSettings->m_NetClasses[ nc->GetName() ] = nc;
1213 }
1214 }
1215 }
1216
1217 if( reader.GetRemainingBytes() != 0 )
1218 THROW_IO_ERROR( wxT( "Classes6 stream is not fully parsed" ) );
1219
1221}
1222
1223
1225 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1226{
1227 if( m_progressReporter )
1228 m_progressReporter->Report( _( "Loading components..." ) );
1229
1230 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1231
1232 uint16_t componentId = 0;
1233
1234 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1235 {
1236 checkpoint();
1237 ACOMPONENT6 elem( reader );
1238
1239 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
1240
1242
1243 footprint->SetFPID( fpID );
1244
1245 footprint->SetPosition( elem.position );
1246 footprint->SetOrientationDegrees( elem.rotation );
1247
1248 // KiCad netlisting requires parts to have non-digit + digit annotation.
1249 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1250 wxString reference = elem.sourcedesignator;
1251
1252 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1253 reference.Prepend( wxT( "UNK" ) );
1254
1255 footprint->SetReference( reference );
1256
1257 footprint->SetLocked( elem.locked );
1258 footprint->Reference().SetVisible( elem.nameon );
1259 footprint->Value().SetVisible( elem.commenton );
1260 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1261
1262 m_components.emplace_back( footprint.get() );
1263 m_board->Add( footprint.release(), ADD_MODE::APPEND );
1264
1265 componentId++;
1266 }
1267
1268 if( reader.GetRemainingBytes() != 0 )
1269 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1270}
1271
1272
1274double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1275{
1276 while( Angle < aMin )
1277 Angle += 360.0;
1278
1279 while( Angle >= aMax )
1280 Angle -= 360.0;
1281
1282 return Angle;
1283}
1284
1285
1287 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1288{
1289 if( m_progressReporter )
1290 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1291
1292 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1293
1294 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1295 {
1296 checkpoint();
1297 ACOMPONENTBODY6 elem( reader ); // TODO: implement
1298
1299 if( elem.component == ALTIUM_COMPONENT_NONE )
1300 continue; // TODO: we do not support components for the board yet
1301
1302 if( m_components.size() <= elem.component )
1303 {
1304 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1305 "component id %d of %d existing components" ),
1306 elem.component,
1307 m_components.size() ) );
1308 }
1309
1310 if( !elem.modelIsEmbedded )
1311 continue;
1312
1313 auto modelTuple = m_models.find( elem.modelId );
1314
1315 if( modelTuple == m_models.end() )
1316 {
1317 if( m_reporter )
1318 {
1319 wxString msg;
1320 msg.Printf( wxT( "ComponentsBodies6 stream tries to access model id %s which does "
1321 "not exist" ), elem.modelId );
1323 }
1324
1325 continue;
1326 }
1327
1328 FOOTPRINT* footprint = m_components.at( elem.component );
1329 const VECTOR2I& fpPosition = footprint->GetPosition();
1330
1331 FP_3DMODEL modelSettings;
1332
1333 modelSettings.m_Filename = modelTuple->second;
1334
1335 modelSettings.m_Offset.x = pcbIUScale.IUTomm((int) elem.modelPosition.x - fpPosition.x );
1336 modelSettings.m_Offset.y = -pcbIUScale.IUTomm((int) elem.modelPosition.y - fpPosition.y );
1337 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) elem.modelPosition.z );
1338
1339 EDA_ANGLE orientation = footprint->GetOrientation();
1340
1341 if( footprint->IsFlipped() )
1342 {
1343 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1344 orientation = -orientation;
1345 }
1346
1347 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1348
1349 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1350 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1351 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z
1352 + elem.rotation
1353 + orientation.AsDegrees(),
1354 -180, 180 );
1355 modelSettings.m_Opacity = elem.bodyOpacity;
1356
1357 footprint->Models().push_back( modelSettings );
1358 }
1359
1360 if( reader.GetRemainingBytes() != 0 )
1361 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1362}
1363
1364
1366{
1367 if( aElem.referencePoint.size() != 2 )
1368 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1369
1370 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1371
1372 if( klayer == UNDEFINED_LAYER )
1373 {
1374 if( m_reporter )
1375 {
1376 m_reporter->Report( wxString::Format(
1377 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1378 "It has been moved to KiCad layer Eco1_User." ), aElem.layer ),
1380 }
1381
1382 klayer = Eco1_User;
1383 }
1384
1385 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1386 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1387
1388 std::unique_ptr<PCB_DIM_ALIGNED> dimension = std::make_unique<PCB_DIM_ALIGNED>( m_board, PCB_DIM_ALIGNED_T );
1389
1390 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1391 dimension->SetLayer( klayer );
1392 dimension->SetStart( referencePoint0 );
1393
1394 if( referencePoint0 != aElem.xy1 )
1395 {
1405 VECTOR2I direction = aElem.xy1 - referencePoint0;
1406 VECTOR2I directionNormalVector = VECTOR2I( -direction.y, direction.x );
1407 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1408 SEG segm2( referencePoint1, referencePoint1 + direction );
1409 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1410
1411 if( !intersection )
1412 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1413
1414 dimension->SetEnd( *intersection );
1415
1416 int height = static_cast<int>( EuclideanNorm( direction ) );
1417
1418 if( ( direction.x > 0 || direction.y < 0 ) != ( aElem.angle >= 180.0 ) )
1419 height = -height;
1420
1421 dimension->SetHeight( height );
1422 }
1423 else
1424 {
1425 dimension->SetEnd( referencePoint1 );
1426 }
1427
1428 dimension->SetLineThickness( aElem.linewidth );
1429
1430 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
1431 dimension->SetPrefix( aElem.textprefix );
1432
1433 // Suffix normally (but not always) holds the units
1434 wxRegEx units( wxS( "(mm)|(in)|(mils)|(thou)|(')|(\")" ), wxRE_ADVANCED );
1435
1436 if( units.Matches( aElem.textsuffix ) )
1437 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX );
1438 else
1439 dimension->SetSuffix( aElem.textsuffix );
1440
1441 dimension->SetTextThickness( aElem.textlinewidth );
1442 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1443 dimension->SetItalic( aElem.textitalic );
1444
1445#if 0 // we don't currently support bold; map to thicker text
1446 dimension->Text().SetBold( aElem.textbold );
1447#else
1448 if( aElem.textbold )
1449 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1450#endif
1451
1452 switch( aElem.textunit )
1453 {
1454 case ALTIUM_UNIT::INCHES:
1455 dimension->SetUnits( EDA_UNITS::INCHES );
1456 break;
1457 case ALTIUM_UNIT::MILS:
1458 dimension->SetUnits( EDA_UNITS::MILS );
1459 break;
1460 case ALTIUM_UNIT::MILLIMETERS:
1461 case ALTIUM_UNIT::CENTIMETER:
1462 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1463 break;
1464 default:
1465 break;
1466 }
1467
1468 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1469}
1470
1471
1473{
1474 if( aElem.referencePoint.size() < 2 )
1475 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1476
1477 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1478
1479 if( klayer == UNDEFINED_LAYER )
1480 {
1481 if( m_reporter )
1482 {
1483 m_reporter->Report( wxString::Format(
1484 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1485 "It has been moved to KiCad layer Eco1_User." ),
1486 aElem.layer ), RPT_SEVERITY_INFO );
1487 }
1488
1489 klayer = Eco1_User;
1490 }
1491
1492 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1493 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1494
1495 std::unique_ptr<PCB_DIM_RADIAL> dimension = std::make_unique<PCB_DIM_RADIAL>( m_board );
1496
1497 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1498 dimension->SetLayer( klayer );
1499 dimension->SetStart( referencePoint0 );
1500 dimension->SetEnd( aElem.xy1 );
1501 dimension->SetLineThickness( aElem.linewidth );
1502 dimension->SetKeepTextAligned( false );
1503
1504 dimension->SetPrefix( aElem.textprefix );
1505
1506 // Suffix normally holds the units
1507 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1508 : DIM_UNITS_FORMAT::BARE_SUFFIX );
1509
1510 switch( aElem.textunit )
1511 {
1512 case ALTIUM_UNIT::INCHES:
1513 dimension->SetUnits( EDA_UNITS::INCHES );
1514 break;
1515 case ALTIUM_UNIT::MILS:
1516 dimension->SetUnits( EDA_UNITS::MILS );
1517 break;
1518 case ALTIUM_UNIT::MILLIMETERS:
1519 case ALTIUM_UNIT::CENTIMETER:
1520 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1521 break;
1522 default:
1523 break;
1524 }
1525
1526 if( aElem.textPoint.empty() )
1527 {
1528 if( m_reporter )
1529 {
1530 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1532 }
1533
1534 return;
1535 }
1536
1537 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1538 dimension->SetTextThickness( aElem.textlinewidth );
1539 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1540 dimension->SetItalic( aElem.textitalic );
1541
1542#if 0 // we don't currently support bold; map to thicker text
1543 dimension->SetBold( aElem.textbold );
1544#else
1545 if( aElem.textbold )
1546 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1547#endif
1548
1549 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1550 // close.
1551 dimension->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1552 dimension->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1553
1554 int yAdjust = dimension->GetTextBox().GetCenter().y - dimension->GetTextPos().y;
1555 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1556 dimension->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
1557
1558 m_radialDimensions.push_back( dimension.get() );
1559 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1560}
1561
1562
1564{
1565 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1566
1567 if( klayer == UNDEFINED_LAYER )
1568 {
1569 if( m_reporter )
1570 {
1571 wxString msg;
1572 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1573 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1575 }
1576
1577 klayer = Eco1_User;
1578 }
1579
1580 if( !aElem.referencePoint.empty() )
1581 {
1582 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1583
1584 // line
1585 VECTOR2I last = referencePoint0;
1586 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1587 {
1588 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1589
1590 shape->SetLayer( klayer );
1591 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1592 shape->SetStart( last );
1593 shape->SetEnd( aElem.referencePoint.at( i ) );
1594 last = aElem.referencePoint.at( i );
1595
1596 m_board->Add( shape.release(), ADD_MODE::APPEND );
1597 }
1598
1599 // arrow
1600 if( aElem.referencePoint.size() >= 2 )
1601 {
1602 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1603
1604 if( dirVec.x != 0 || dirVec.y != 0 )
1605 {
1606 double scaling = EuclideanNorm( dirVec ) / aElem.arrowsize;
1607 VECTOR2I arrVec =
1608 VECTOR2I( KiROUND( dirVec.x / scaling ), KiROUND( dirVec.y / scaling ) );
1609 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1610
1611 {
1612 std::unique_ptr<PCB_SHAPE> shape1 =
1613 std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1614
1615 shape1->SetLayer( klayer );
1616 shape1->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1617 shape1->SetStart( referencePoint0 );
1618 shape1->SetEnd( referencePoint0 + arrVec );
1619
1620 m_board->Add( shape1.release(), ADD_MODE::APPEND );
1621 }
1622
1623 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1624
1625 {
1626 std::unique_ptr<PCB_SHAPE> shape2 =
1627 std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1628
1629 shape2->SetLayer( klayer );
1630 shape2->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1631 shape2->SetStart( referencePoint0 );
1632 shape2->SetEnd( referencePoint0 + arrVec );
1633
1634 m_board->Add( shape2.release(), ADD_MODE::APPEND );
1635 }
1636 }
1637 }
1638 }
1639
1640 if( aElem.textPoint.empty() )
1641 {
1642 if( m_reporter )
1643 {
1644 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1646 }
1647
1648 return;
1649 }
1650
1651 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
1652
1653 text->SetText( aElem.textformat );
1654 text->SetPosition( aElem.textPoint.at( 0 ) );
1655 text->SetLayer( klayer );
1656 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1657 text->SetTextThickness( aElem.textlinewidth );
1658 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1659 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1660
1661 m_board->Add( text.release(), ADD_MODE::APPEND );
1662}
1663
1664
1666{
1667 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1668
1669 if( klayer == UNDEFINED_LAYER )
1670 {
1671 if( m_reporter )
1672 {
1673 wxString msg;
1674 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1675 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1677 }
1678
1679 klayer = Eco1_User;
1680 }
1681
1682 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1683 {
1684 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1685
1686 shape->SetLayer( klayer );
1687 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1688 shape->SetStart( aElem.referencePoint.at( i ) );
1689 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1690
1691 m_board->Add( shape.release(), ADD_MODE::APPEND );
1692 }
1693}
1694
1695
1697{
1698 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1699
1700 if( klayer == UNDEFINED_LAYER )
1701 {
1702 if( m_reporter )
1703 {
1704 wxString msg;
1705 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1706 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1708 }
1709
1710 klayer = Eco1_User;
1711 }
1712
1713 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1714 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1715
1716 std::unique_ptr<PCB_DIM_CENTER> dimension = std::make_unique<PCB_DIM_CENTER>( m_board );
1717
1718 dimension->SetLayer( klayer );
1719 dimension->SetLineThickness( aElem.linewidth );
1720 dimension->SetStart( aElem.xy1 );
1721 dimension->SetEnd( aElem.xy1 + vec );
1722
1723 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1724}
1725
1726
1728 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1729{
1730 if( m_progressReporter )
1731 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1732
1733 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1734
1735 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1736 {
1737 checkpoint();
1738 ADIMENSION6 elem( reader );
1739
1740 switch( elem.kind )
1741 {
1742 case ALTIUM_DIMENSION_KIND::LINEAR:
1744 break;
1745 case ALTIUM_DIMENSION_KIND::ANGULAR:
1746 if( m_reporter )
1747 {
1749 wxString::Format( _( "Ignored Angular dimension (not yet supported)." ) ),
1751 }
1752 break;
1753 case ALTIUM_DIMENSION_KIND::RADIAL:
1755 break;
1756 case ALTIUM_DIMENSION_KIND::LEADER:
1758 break;
1759 case ALTIUM_DIMENSION_KIND::DATUM:
1760 if( m_reporter )
1761 {
1763 wxString::Format( _( "Ignored Datum dimension (not yet supported)." ) ),
1765 }
1766 // HelperParseDimensions6Datum( elem );
1767 break;
1768 case ALTIUM_DIMENSION_KIND::BASELINE:
1769 if( m_reporter )
1770 {
1772 wxString::Format( _( "Ignored Baseline dimension (not yet supported)." ) ),
1774 }
1775 break;
1776 case ALTIUM_DIMENSION_KIND::CENTER:
1778 break;
1779 case ALTIUM_DIMENSION_KIND::LINEAR_DIAMETER:
1780 if( m_reporter )
1781 {
1783 wxString::Format( _( "Ignored Linear dimension (not yet supported)." ) ),
1785 }
1786 break;
1787 case ALTIUM_DIMENSION_KIND::RADIAL_DIAMETER:
1788 if( m_reporter )
1789 {
1791 wxString::Format( _( "Ignored Radial dimension (not yet supported)." ) ),
1793 }
1794 break;
1795 default:
1796 if( m_reporter )
1797 {
1798 wxString msg;
1799 msg.Printf( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1801 }
1802 break;
1803 }
1804 }
1805
1806 if( reader.GetRemainingBytes() != 0 )
1807 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
1808}
1809
1810
1812 const CFB::COMPOUND_FILE_ENTRY* aEntry,
1813 const std::vector<std::string>& aRootDir )
1814{
1815 if( m_progressReporter )
1816 m_progressReporter->Report( _( "Loading 3D models..." ) );
1817
1818 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1819
1820 if( reader.GetRemainingBytes() == 0 )
1821 return;
1822
1823 wxString projectPath = wxPathOnly( m_board->GetFileName() );
1824 // TODO: set KIPRJMOD always after import (not only when loading project)?
1825 wxSetEnv( PROJECT_VAR_NAME, projectPath );
1826
1827 // TODO: make this path configurable?
1828 const wxString altiumModelDir = wxT( "ALTIUM_EMBEDDED_MODELS" );
1829
1830 wxFileName altiumModelsPath = wxFileName::DirName( projectPath );
1831 wxString kicadModelPrefix = wxT( "${KIPRJMOD}/" ) + altiumModelDir + wxT( "/" );
1832
1833 if( !altiumModelsPath.AppendDir( altiumModelDir ) )
1834 THROW_IO_ERROR( wxT( "Cannot construct directory path for step models" ) );
1835
1836 // Create dir if it does not exist
1837 if( !altiumModelsPath.DirExists() )
1838 {
1839 if( !altiumModelsPath.Mkdir() )
1840 {
1841 if( m_reporter )
1842 {
1843 wxString msg;
1844 msg.Printf( _( "Failed to create folder '%s'." ) + wxS( " " )
1845 + _( "No 3D-models will be imported." ),
1846 altiumModelsPath.GetFullPath() );
1848 }
1849
1850 return;
1851 }
1852 }
1853
1854 int idx = 0;
1855 wxString invalidChars = wxFileName::GetForbiddenChars();
1856
1857 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1858 {
1859 checkpoint();
1860 AMODEL elem( reader );
1861
1862 std::vector<std::string> stepPath = aRootDir;
1863 stepPath.emplace_back( std::to_string( idx ) );
1864
1865 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii() &&
1866 wxString::npos == elem.name.find_first_of( invalidChars );
1867 wxString storageName = !validName ? wxString::Format( wxT( "model_%d" ), idx )
1868 : elem.name;
1869 wxFileName storagePath( altiumModelsPath.GetPath(), storageName );
1870
1871 idx++;
1872
1873 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
1874
1875 if( stepEntry == nullptr )
1876 {
1877 if( m_reporter )
1878 {
1879 wxString msg;
1880 msg.Printf( _( "File not found: '%s'. 3D-model not imported." ),
1881 FormatPath( stepPath ) );
1883 }
1884
1885 continue;
1886 }
1887
1888 size_t stepSize = static_cast<size_t>( stepEntry->size );
1889 std::vector<char> stepContent( stepSize );
1890
1891 // read file into buffer
1892 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
1893 stepSize );
1894
1895 if( !storagePath.IsDirWritable() )
1896 {
1897 if( m_reporter )
1898 {
1899 wxString msg;
1900 msg.Printf( _( "Insufficient permissions to save file '%s'." ),
1901 storagePath.GetFullPath() );
1903 }
1904
1905 continue;
1906 }
1907
1908 wxMemoryInputStream stepStream( stepContent.data(), stepSize );
1909 wxZlibInputStream zlibInputStream( stepStream );
1910
1911 wxFFileOutputStream outputStream( storagePath.GetFullPath() );
1912
1913 if( !outputStream.IsOk() )
1914 {
1915 if( m_reporter )
1916 {
1917 wxString msg;
1918 msg.Printf( _( "Unable to write file '%s'." ), storagePath.GetFullPath() );
1920 }
1921
1922 continue;
1923 }
1924
1925 outputStream.Write( zlibInputStream );
1926 outputStream.Close();
1927
1928 m_models.insert( { elem.id, kicadModelPrefix + storageName } );
1929 }
1930
1931 if( reader.GetRemainingBytes() != 0 )
1932 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
1933}
1934
1935
1937 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1938{
1939 if( m_progressReporter )
1940 m_progressReporter->Report( _( "Loading nets..." ) );
1941
1942 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1943
1944 wxASSERT( m_altiumToKicadNetcodes.empty() );
1945
1946 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1947 {
1948 checkpoint();
1949 ANET6 elem( reader );
1950
1951 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, 0 );
1952 m_board->Add( netInfo, ADD_MODE::APPEND );
1953
1954 // needs to be called after m_board->Add() as assign us the NetCode
1955 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
1956 }
1957
1958 if( reader.GetRemainingBytes() != 0 )
1959 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
1960}
1961
1963 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1964{
1965 if( m_progressReporter )
1966 m_progressReporter->Report( _( "Loading polygons..." ) );
1967
1968 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1969
1970 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1971 {
1972 checkpoint();
1973 APOLYGON6 elem( reader );
1974
1975 SHAPE_LINE_CHAIN linechain;
1977
1978 if( linechain.PointCount() < 3 )
1979 {
1980 // We have found multiple Altium files with polygon records containing nothing but two
1981 // coincident vertices. These polygons do not appear when opening the file in Altium.
1982 // https://gitlab.com/kicad/code/kicad/-/issues/8183
1983 // Also, polygons with less than 3 points are not supported in KiCad.
1984 //
1985 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
1986 // "points are required." ),
1987 // linechain.PointCount(),
1988 // elem.vertices.size() );
1989
1990 m_polygons.emplace_back( nullptr );
1991 continue;
1992 }
1993
1994 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
1995 m_polygons.emplace_back( zone.get() );
1996
1997 zone->SetNetCode( GetNetCode( elem.net ) );
1998 zone->SetPosition( elem.vertices.at( 0 ).position );
1999 zone->SetLocked( elem.locked );
2000 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
2001 zone->Outline()->AddOutline( linechain );
2002
2003 HelperSetZoneLayers( *zone, elem.layer );
2004
2005 if( elem.pourindex > m_highest_pour_index )
2007
2008 const ARULE6* planeClearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
2009 const ARULE6* zoneClearanceRule = GetRule( ALTIUM_RULE_KIND::CLEARANCE,
2010 wxT( "PolygonClearance" ) );
2011 int planeLayers = 0;
2012 int signalLayers = 0;
2013 int clearance = 0;
2014
2015 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
2016 {
2017 LAYER_T layerType = m_board->GetLayerType( layer );
2018
2019 if( layerType == LT_POWER || layerType == LT_MIXED )
2020 planeLayers++;
2021
2022 if( layerType == LT_SIGNAL || layerType == LT_MIXED )
2023 signalLayers++;
2024 }
2025
2026 if( planeLayers > 0 && planeClearanceRule )
2027 clearance = std::max( clearance, planeClearanceRule->planeclearanceClearance );
2028
2029 if( signalLayers > 0 && zoneClearanceRule )
2030 clearance = std::max( clearance, zoneClearanceRule->clearanceGap );
2031
2032 if( clearance > 0 )
2033 zone->SetLocalClearance( clearance );
2034
2035 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
2036
2037 if( polygonConnectRule != nullptr )
2038 {
2039 switch( polygonConnectRule->polygonconnectStyle )
2040 {
2041 case ALTIUM_CONNECT_STYLE::DIRECT:
2042 zone->SetPadConnection( ZONE_CONNECTION::FULL );
2043 break;
2044
2045 case ALTIUM_CONNECT_STYLE::NONE:
2046 zone->SetPadConnection( ZONE_CONNECTION::NONE );
2047 break;
2048
2049 default:
2050 case ALTIUM_CONNECT_STYLE::RELIEF:
2051 zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
2052 break;
2053 }
2054
2055 // TODO: correct variables?
2056 zone->SetThermalReliefSpokeWidth(
2057 polygonConnectRule->polygonconnectReliefconductorwidth );
2058 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
2059
2060 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
2061 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
2062 }
2063
2064 if( IsAltiumLayerAPlane( elem.layer ) )
2065 {
2066 // outer zone will be set to priority 0 later.
2067 zone->SetAssignedPriority( 1 );
2068
2069 // check if this is the outer zone by simply comparing the BBOX
2070 const auto& outer_plane = m_outer_plane.find( elem.layer );
2071 if( outer_plane == m_outer_plane.end()
2072 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
2073 {
2074 m_outer_plane[elem.layer] = zone.get();
2075 }
2076 }
2077
2078 if( elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::SOLID
2079 && elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::UNKNOWN )
2080 {
2081 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
2082 zone->SetHatchThickness( elem.trackwidth );
2083
2084 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::NONE )
2085 {
2086 // use a small hack to get us only an outline (hopefully)
2087 const BOX2I& bbox = zone->GetBoundingBox();
2088 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
2089 }
2090 else
2091 {
2092 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
2093 }
2094
2095 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45 )
2096 zone->SetHatchOrientation( ANGLE_45 );
2097 }
2098
2099 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2101
2102 m_board->Add( zone.release(), ADD_MODE::APPEND );
2103 }
2104
2105 if( reader.GetRemainingBytes() != 0 )
2106 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
2107}
2108
2110 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2111{
2112 if( m_progressReporter )
2113 m_progressReporter->Report( _( "Loading rules..." ) );
2114
2115 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2116
2117 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2118 {
2119 checkpoint();
2120 ARULE6 elem( reader );
2121
2122 m_rules[elem.kind].emplace_back( elem );
2123 }
2124
2125 // sort rules by priority
2126 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
2127 {
2128 std::sort( val.second.begin(), val.second.end(),
2129 []( const ARULE6& lhs, const ARULE6& rhs )
2130 {
2131 return lhs.priority < rhs.priority;
2132 } );
2133 }
2134
2135 const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::CLEARANCE );
2136 const ARULE6* trackWidthRule = GetRuleDefault( ALTIUM_RULE_KIND::WIDTH );
2137 const ARULE6* routingViasRule = GetRuleDefault( ALTIUM_RULE_KIND::ROUTING_VIAS );
2138 const ARULE6* holeSizeRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_SIZE );
2139 const ARULE6* holeToHoleRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE );
2140
2141 if( clearanceRule )
2143
2144 if( trackWidthRule )
2145 {
2147 // TODO: construct a custom rule for preferredWidth and maxLimit values
2148 }
2149
2150 if( routingViasRule )
2151 {
2152 m_board->GetDesignSettings().m_ViasMinSize = routingViasRule->minWidth;
2154 }
2155
2156 if( holeSizeRule )
2157 {
2158 // TODO: construct a custom rule for minLimit / maxLimit values
2159 }
2160
2161 if( holeToHoleRule )
2163
2164 const ARULE6* soldermaskRule = GetRuleDefault( ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION );
2165 const ARULE6* pastemaskRule = GetRuleDefault( ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION );
2166
2167 if( soldermaskRule )
2169
2170 if( pastemaskRule )
2172
2173 if( reader.GetRemainingBytes() != 0 )
2174 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
2175}
2176
2178 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2179{
2180 if( m_progressReporter )
2181 m_progressReporter->Report( _( "Loading board regions..." ) );
2182
2183 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2184
2185 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2186 {
2187 checkpoint();
2188 AREGION6 elem( reader, false );
2189
2190 // TODO: implement?
2191 }
2192
2193 if( reader.GetRemainingBytes() != 0 )
2194 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
2195}
2196
2198 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2199{
2200 if( m_progressReporter )
2201 m_progressReporter->Report( _( "Loading polygons..." ) );
2202
2203 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2204
2205 /* TODO: use Header section of file */
2206 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2207 {
2208 checkpoint();
2209 AREGION6 elem( reader, true );
2210
2212 || elem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2213 {
2214 // TODO: implement all different types for footprints
2216 }
2217 else
2218 {
2219 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2220 ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
2221 }
2222 }
2223
2224 if( reader.GetRemainingBytes() != 0 )
2225 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
2226}
2227
2228
2230{
2231 if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2232 {
2234 }
2235 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2236 {
2237 SHAPE_LINE_CHAIN linechain;
2239
2240 if( linechain.PointCount() < 3 )
2241 {
2242 // We have found multiple Altium files with polygon records containing nothing but
2243 // two coincident vertices. These polygons do not appear when opening the file in
2244 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2245 // Also, polygons with less than 3 points are not supported in KiCad.
2246 return;
2247 }
2248
2249 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2250
2251 zone->SetIsRuleArea( true );
2252
2253 if( aElem.is_keepout )
2254 {
2256 }
2257 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2258 {
2259 zone->SetDoNotAllowCopperPour( true );
2260 zone->SetDoNotAllowVias( false );
2261 zone->SetDoNotAllowTracks( false );
2262 zone->SetDoNotAllowPads( false );
2263 zone->SetDoNotAllowFootprints( false );
2264 }
2265
2266 zone->SetPosition( aElem.outline.at( 0 ).position );
2267 zone->Outline()->AddOutline( linechain );
2268
2269 HelperSetZoneLayers( *zone, aElem.layer );
2270
2271 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2273
2274 m_board->Add( zone.release(), ADD_MODE::APPEND );
2275 }
2276 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2277 {
2278 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2279
2280 if( klayer == UNDEFINED_LAYER )
2281 {
2282 if( m_reporter )
2283 {
2284 wxString msg;
2285 msg.Printf( _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
2286 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
2288 }
2289
2290 klayer = Eco1_User;
2291 }
2292
2293 SHAPE_LINE_CHAIN linechain;
2295
2296 if( linechain.PointCount() < 3 )
2297 {
2298 // We have found multiple Altium files with polygon records containing nothing but
2299 // two coincident vertices. These polygons do not appear when opening the file in
2300 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2301 // Also, polygons with less than 3 points are not supported in KiCad.
2302 return;
2303 }
2304
2305 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2306
2307 shape->SetPolyShape( linechain );
2308 shape->SetFilled( false );
2309 shape->SetLayer( klayer );
2310 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2311
2312 m_board->Add( shape.release(), ADD_MODE::APPEND );
2313 }
2314 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2315 {
2316 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2317 {
2318 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2320 }
2321 }
2322 else if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2323 {
2325 }
2326 else
2327 {
2328 if( m_reporter )
2329 {
2330 wxString msg;
2331 msg.Printf( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2333 }
2334 }
2335}
2336
2337
2339 const AREGION6& aElem,
2340 const int aPrimitiveIndex )
2341{
2342 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2343 {
2344 SHAPE_LINE_CHAIN linechain;
2346
2347 if( linechain.PointCount() < 3 )
2348 {
2349 // We have found multiple Altium files with polygon records containing nothing but
2350 // two coincident vertices. These polygons do not appear when opening the file in
2351 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2352 // Also, polygons with less than 3 points are not supported in KiCad.
2353 return;
2354 }
2355
2356 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
2357
2358 zone->SetIsRuleArea( true );
2359
2360 if( aElem.is_keepout )
2361 {
2363 }
2364 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2365 {
2366 zone->SetDoNotAllowCopperPour( true );
2367 zone->SetDoNotAllowVias( false );
2368 zone->SetDoNotAllowTracks( false );
2369 zone->SetDoNotAllowPads( false );
2370 zone->SetDoNotAllowFootprints( false );
2371 }
2372
2373 zone->SetPosition( aElem.outline.at( 0 ).position );
2374 zone->Outline()->AddOutline( linechain );
2375
2376 HelperSetZoneLayers( *zone, aElem.layer );
2377
2378 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2380
2381 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
2382 }
2383 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2384 {
2385 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2386 {
2387 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2388 {
2389 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
2390 aPrimitiveIndex );
2391 }
2392 }
2393 }
2394 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE
2395 || aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2396 {
2397 PCB_LAYER_ID klayer = aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT
2398 ? Edge_Cuts
2399 : GetKicadLayer( aElem.layer );
2400
2401 if( klayer == UNDEFINED_LAYER )
2402 {
2403 if( !m_footprintName.IsEmpty() )
2404 {
2405 if( m_reporter )
2406 {
2407 wxString msg;
2408 msg.Printf( _( "Loading library '%s':\n"
2409 "Footprint %s contains a dashed outline on Altium layer (%d) with "
2410 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2411 m_library,
2413 aElem.layer );
2415 }
2416 }
2417 else
2418 {
2419 if( m_reporter )
2420 {
2421 wxString msg;
2422 msg.Printf( _( "Footprint %s contains a dashed outline on Altium layer (%d) with "
2423 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2424 aFootprint->GetReference(),
2425 aElem.layer );
2427 }
2428 }
2429
2430 klayer = Eco1_User;
2431 }
2432
2433 SHAPE_LINE_CHAIN linechain;
2435
2436 if( linechain.PointCount() < 3 )
2437 {
2438 // We have found multiple Altium files with polygon records containing nothing but
2439 // two coincident vertices. These polygons do not appear when opening the file in
2440 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2441 // Also, polygons with less than 3 points are not supported in KiCad.
2442 return;
2443 }
2444
2445 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2446
2447 shape->SetPolyShape( linechain );
2448 shape->SetFilled( false );
2449 shape->SetLayer( klayer );
2450
2451 if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2452 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2453 else
2454 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::SOLID ) );
2455
2456 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2457 }
2458 else
2459 {
2460 if( !m_footprintName.IsEmpty() )
2461 {
2462 if( m_reporter )
2463 {
2464 wxString msg;
2465 msg.Printf( _( "Error loading library '%s':\n"
2466 "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2467 m_library,
2469 aElem.kind );
2471 }
2472 }
2473 else
2474 {
2475 if( m_reporter )
2476 {
2477 wxString msg;
2478 msg.Printf( _( "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2479 aFootprint->GetReference(),
2480 aElem.kind );
2482 }
2483 }
2484 }
2485}
2486
2487
2489 PCB_LAYER_ID aLayer )
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
2497 // but two coincident vertices. These polygons do not appear when opening the
2498 // file in 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 SHAPE_POLY_SET polySet;
2504 polySet.AddOutline( linechain );
2505
2506 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2507 {
2508 SHAPE_LINE_CHAIN hole_linechain;
2509 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2510
2511 if( hole_linechain.PointCount() < 3 )
2512 continue;
2513
2514 polySet.AddHole( hole_linechain );
2515 }
2516
2517 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2518
2519 shape->SetPolyShape( polySet );
2520 shape->SetFilled( true );
2521 shape->SetLayer( aLayer );
2522 shape->SetStroke( STROKE_PARAMS( 0 ) );
2523
2524 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2525 {
2526 shape->SetNetCode( GetNetCode( aElem.net ) );
2527 }
2528
2529 m_board->Add( shape.release(), ADD_MODE::APPEND );
2530}
2531
2532
2534 const AREGION6& aElem,
2535 PCB_LAYER_ID aLayer,
2536 const int aPrimitiveIndex )
2537{
2538 SHAPE_LINE_CHAIN linechain;
2540
2541 if( linechain.PointCount() < 3 )
2542 {
2543 // We have found multiple Altium files with polygon records containing nothing
2544 // but two coincident vertices. These polygons do not appear when opening the
2545 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2546 // Also, polygons with less than 3 points are not supported in KiCad.
2547 return;
2548 }
2549
2550 SHAPE_POLY_SET polySet;
2551 polySet.AddOutline( linechain );
2552
2553 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2554 {
2555 SHAPE_LINE_CHAIN hole_linechain;
2556 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2557
2558 if( hole_linechain.PointCount() < 3 )
2559 continue;
2560
2561 polySet.AddHole( hole_linechain );
2562 }
2563
2564 if( aLayer == F_Cu || aLayer == B_Cu )
2565 {
2566 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
2567
2568 LSET padLayers;
2569 padLayers.set( aLayer );
2570
2571 pad->SetAttribute( PAD_ATTRIB::SMD );
2572 pad->SetShape( PAD_SHAPE::CUSTOM );
2573 pad->SetThermalSpokeAngle( ANGLE_90 );
2574
2575 int anchorSize = 1;
2576 VECTOR2I anchorPos = linechain.CPoint( 0 );
2577
2578 pad->SetShape( PAD_SHAPE::CUSTOM );
2579 pad->SetAnchorPadShape( PAD_SHAPE::CIRCLE );
2580 pad->SetSize( { anchorSize, anchorSize } );
2581 pad->SetPosition( anchorPos );
2582
2583 SHAPE_POLY_SET shapePolys = polySet;
2584 shapePolys.Move( -anchorPos );
2585 pad->AddPrimitivePoly( shapePolys, 0, true );
2586
2587 auto& map = m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::REGION];
2588 auto it = map.find( aPrimitiveIndex );
2589
2590 if( it != map.end() )
2591 {
2592 const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
2593
2594 if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2595 {
2596 pad->SetLocalSolderPasteMargin(
2597 info.pastemaskexpansionmanual ? info.pastemaskexpansionmanual : 1 );
2598 }
2599
2600 if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2601 {
2602 pad->SetLocalSolderMaskMargin(
2603 info.soldermaskexpansionmanual ? info.soldermaskexpansionmanual : 1 );
2604 }
2605
2606 if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
2607 padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
2608
2609 if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
2610 padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
2611 }
2612
2613 pad->SetLayerSet( padLayers );
2614
2615 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
2616 }
2617 else
2618 {
2619 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2620
2621 shape->SetPolyShape( polySet );
2622 shape->SetFilled( true );
2623 shape->SetLayer( aLayer );
2624 shape->SetStroke( STROKE_PARAMS( 0 ) );
2625
2626 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2627 }
2628}
2629
2630
2632 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2633{
2634 if( m_progressReporter )
2635 m_progressReporter->Report( _( "Loading zone fills..." ) );
2636
2637 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2638
2639 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2640 {
2641 checkpoint();
2642 AREGION6 elem( reader, false );
2643
2644 if( elem.polygon != ALTIUM_POLYGON_NONE )
2645 {
2646 if( m_polygons.size() <= elem.polygon )
2647 {
2648 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2649 "of %d existing polygons.",
2650 elem.polygon,
2651 m_polygons.size() ) );
2652 }
2653
2654 ZONE* zone = m_polygons.at( elem.polygon );
2655
2656 if( zone == nullptr )
2657 {
2658 continue; // we know the zone id, but because we do not know the layer we did not
2659 // add it!
2660 }
2661
2662 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2663
2664 if( klayer == UNDEFINED_LAYER )
2665 continue; // Just skip it for now. Users can fill it themselves.
2666
2667 SHAPE_LINE_CHAIN linechain;
2668
2669 for( const ALTIUM_VERTICE& vertice : elem.outline )
2670 linechain.Append( vertice.position );
2671
2672 linechain.Append( elem.outline.at( 0 ).position );
2673 linechain.SetClosed( true );
2674
2675 SHAPE_POLY_SET fill;
2676 fill.AddOutline( linechain );
2677
2678 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2679 {
2680 SHAPE_LINE_CHAIN hole_linechain;
2681
2682 for( const ALTIUM_VERTICE& vertice : hole )
2683 hole_linechain.Append( vertice.position );
2684
2685 hole_linechain.Append( hole.at( 0 ).position );
2686 hole_linechain.SetClosed( true );
2687 fill.AddHole( hole_linechain );
2688 }
2689
2690 if( zone->HasFilledPolysForLayer( klayer ) )
2691 fill.BooleanAdd( *zone->GetFill( klayer ), SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
2692
2694
2695 zone->SetFilledPolysList( klayer, fill );
2696 zone->SetIsFilled( true );
2697 zone->SetNeedRefill( false );
2698 }
2699 }
2700
2701 if( reader.GetRemainingBytes() != 0 )
2702 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2703}
2704
2705
2707 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2708{
2709 if( m_progressReporter )
2710 m_progressReporter->Report( _( "Loading arcs..." ) );
2711
2712 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2713
2714 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2715 {
2716 checkpoint();
2717 AARC6 elem( reader );
2718
2719 if( elem.component == ALTIUM_COMPONENT_NONE )
2720 {
2721 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2722 }
2723 else
2724 {
2725 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2726 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2727 }
2728 }
2729
2730 if( reader.GetRemainingBytes() != 0 )
2731 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2732}
2733
2734
2736{
2737 if( aElem.startangle == 0. && aElem.endangle == 360. )
2738 {
2739 aShape->SetShape( SHAPE_T::CIRCLE );
2740
2741 // TODO: other variants to define circle?
2742 aShape->SetStart( aElem.center );
2743 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
2744 }
2745 else
2746 {
2747 aShape->SetShape( SHAPE_T::ARC );
2748
2749 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2750 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2751
2752 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2753 -KiROUND( startAngle.Sin() * aElem.radius ) );
2754
2755 aShape->SetCenter( aElem.center );
2756 aShape->SetStart( aElem.center + startOffset );
2757 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
2758 }
2759}
2760
2761
2762void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
2763{
2764 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
2765 {
2766 if( m_polygons.size() <= aElem.polygon )
2767 {
2768 THROW_IO_ERROR( wxString::Format( "Tracks stream tries to access polygon id %u "
2769 "of %zu existing polygons.",
2770 aElem.polygon, m_polygons.size() ) );
2771 }
2772
2773 ZONE* zone = m_polygons.at( aElem.polygon );
2774
2775 if( zone == nullptr )
2776 {
2777 return; // we know the zone id, but because we do not know the layer we did not
2778 // add it!
2779 }
2780
2781 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2782
2783 if( klayer == UNDEFINED_LAYER )
2784 return; // Just skip it for now. Users can fill it themselves.
2785
2786 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
2787
2788 // This is not the actual board item. We can use it to create the polygon for the region
2789 PCB_SHAPE shape( nullptr );
2790
2791 ConvertArcs6ToPcbShape( aElem, &shape );
2792 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2793
2794 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
2795 // Will be simplified and fractured later
2796
2797 zone->SetIsFilled( true );
2798 zone->SetNeedRefill( false );
2799
2800 return;
2801 }
2802
2803 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2804 || IsAltiumLayerAPlane( aElem.layer ) )
2805 {
2806 // This is not the actual board item. We can use it to create the polygon for the region
2807 PCB_SHAPE shape( nullptr );
2808
2809 ConvertArcs6ToPcbShape( aElem, &shape );
2810 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2811
2813 }
2814 else
2815 {
2816 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2817 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2818 }
2819
2820 for( const auto& layerExpansionMask :
2821 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
2822 {
2823 int width = aElem.width + ( layerExpansionMask.second * 2 );
2824
2825 if( width > 1 )
2826 {
2827 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
2828
2829 ConvertArcs6ToPcbShape( aElem, arc.get() );
2830 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
2831 arc->SetLayer( layerExpansionMask.first );
2832
2833 m_board->Add( arc.release(), ADD_MODE::APPEND );
2834 }
2835 }
2836}
2837
2838
2840 const int aPrimitiveIndex, const bool aIsBoardImport )
2841{
2842 if( aElem.polygon != ALTIUM_POLYGON_NONE )
2843 {
2844 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Arc with polygon id %d",
2845 aElem.polygon ) );
2846 return;
2847 }
2848
2849 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2850 || IsAltiumLayerAPlane( aElem.layer ) )
2851 {
2852 // This is not the actual board item. We can use it to create the polygon for the region
2853 PCB_SHAPE shape( nullptr );
2854
2855 ConvertArcs6ToPcbShape( aElem, &shape );
2856 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2857
2858 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
2859 aElem.keepoutrestrictions );
2860 }
2861 else
2862 {
2863 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2864 {
2865 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2866 {
2867 // Special case: do to not lose net connections in footprints
2868 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2869 }
2870 else
2871 {
2872 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
2873 }
2874 }
2875 }
2876
2877 for( const auto& layerExpansionMask :
2878 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
2879 {
2880 int width = aElem.width + ( layerExpansionMask.second * 2 );
2881
2882 if( width > 1 )
2883 {
2884 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
2885
2886 ConvertArcs6ToPcbShape( aElem, arc.get() );
2887 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
2888 arc->SetLayer( layerExpansionMask.first );
2889
2890 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
2891 }
2892 }
2893}
2894
2895
2897{
2898 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2899 {
2900 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
2901 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
2902
2903 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2904 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2905
2906 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2907 -KiROUND( startAngle.Sin() * aElem.radius ) );
2908
2909 shape.SetCenter( aElem.center );
2910 shape.SetStart( aElem.center + startOffset );
2911 shape.SetArcAngleAndEnd( includedAngle.Normalize(), true );
2912
2913 // Create actual arc
2914 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(), aElem.width );
2915 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board, &shapeArc );
2916
2917 arc->SetWidth( aElem.width );
2918 arc->SetLayer( aLayer );
2919 arc->SetNetCode( GetNetCode( aElem.net ) );
2920
2921 m_board->Add( arc.release(), ADD_MODE::APPEND );
2922 }
2923 else
2924 {
2925 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
2926
2927 ConvertArcs6ToPcbShape( aElem, arc.get() );
2928 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2929 arc->SetLayer( aLayer );
2930
2931 m_board->Add( arc.release(), ADD_MODE::APPEND );
2932 }
2933}
2934
2935
2937 PCB_LAYER_ID aLayer )
2938{
2939 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
2940
2941 ConvertArcs6ToPcbShape( aElem, arc.get() );
2942 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2943 arc->SetLayer( aLayer );
2944
2945 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
2946}
2947
2948
2950 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2951{
2952 if( m_progressReporter )
2953 m_progressReporter->Report( _( "Loading pads..." ) );
2954
2955 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2956
2957 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2958 {
2959 checkpoint();
2960 APAD6 elem( reader );
2961
2962 if( elem.component == ALTIUM_COMPONENT_NONE )
2963 {
2965 }
2966 else
2967 {
2968 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2969 ConvertPads6ToFootprintItem( footprint, elem );
2970 }
2971 }
2972
2973 if( reader.GetRemainingBytes() != 0 )
2974 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
2975}
2976
2977
2979{
2980 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
2981 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
2982 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
2983 {
2985 }
2986 else
2987 {
2988 // We cannot add a pad directly into the PCB
2989 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
2990 footprint->SetPosition( aElem.position );
2991
2992 ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem );
2993
2994 m_board->Add( footprint.release(), ADD_MODE::APPEND );
2995 }
2996}
2997
2998
3000{
3001 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3002 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3003 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3004 {
3005 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
3006 }
3007 else
3008 {
3009 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
3010 }
3011}
3012
3013
3015{
3016 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3017
3018 pad->SetNumber( aElem.name );
3019 pad->SetNetCode( GetNetCode( aElem.net ) );
3020
3021 pad->SetPosition( aElem.position );
3022 pad->SetOrientationDegrees( aElem.direction );
3023 pad->SetSize( aElem.topsize );
3024 pad->SetThermalSpokeAngle( ANGLE_90 );
3025
3026 if( aElem.holesize == 0 )
3027 {
3028 pad->SetAttribute( PAD_ATTRIB::SMD );
3029 }
3030 else
3031 {
3032 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3033 {
3034 // TODO: I assume other values are possible as well?
3035 if( !m_footprintName.IsEmpty() )
3036 {
3037 if( m_reporter )
3038 {
3039 wxString msg;
3040 msg.Printf( _( "Error loading library '%s':\n"
3041 "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3042 m_library,
3044 aElem.name );
3046 }
3047 }
3048 else
3049 {
3050 if( m_reporter )
3051 {
3052 wxString msg;
3053 msg.Printf( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3054 aFootprint->GetReference(),
3055 aElem.name );
3057 }
3058 }
3059 }
3060
3061 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
3062
3063 if( !aElem.sizeAndShape || aElem.sizeAndShape->holeshape == ALTIUM_PAD_HOLE_SHAPE::ROUND )
3064 {
3065 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3066 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3067 }
3068 else
3069 {
3070 switch( aElem.sizeAndShape->holeshape )
3071 {
3072 case ALTIUM_PAD_HOLE_SHAPE::ROUND:
3073 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
3074 break;
3075
3076 case ALTIUM_PAD_HOLE_SHAPE::SQUARE:
3077 if( !m_footprintName.IsEmpty() )
3078 {
3079 if( m_reporter )
3080 {
3081 wxString msg;
3082 msg.Printf( _( "Loading library '%s':\n"
3083 "Footprint %s pad %s has a square hole (not yet supported)." ),
3084 m_library,
3086 aElem.name );
3088 }
3089 }
3090 else
3091 {
3092 if( m_reporter )
3093 {
3094 wxString msg;
3095 msg.Printf( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
3096 aFootprint->GetReference(),
3097 aElem.name );
3099 }
3100 }
3101
3102 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3103 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3104 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
3105 // this case or rect holes have a different id
3106 break;
3107
3108 case ALTIUM_PAD_HOLE_SHAPE::SLOT:
3109 {
3110 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
3111 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
3112
3113 slotRotation.Normalize();
3114
3115 if( slotRotation.IsHorizontal() )
3116 {
3117 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
3118 }
3119 else if( slotRotation.IsVertical() )
3120 {
3121 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
3122 }
3123 else
3124 {
3125 if( !m_footprintName.IsEmpty() )
3126 {
3127 if( m_reporter )
3128 {
3129 wxString msg;
3130 msg.Printf( _( "Loading library '%s':\n"
3131 "Footprint %s pad %s has a hole-rotation of %f degrees. "
3132 "KiCad only supports 90 degree rotations." ),
3133 m_library,
3135 aElem.name,
3136 slotRotation.AsDegrees() );
3138 }
3139 }
3140 else
3141 {
3142 if( m_reporter )
3143 {
3144 wxString msg;
3145 msg.Printf( _( "Footprint %s pad %s has a hole-rotation of %f degrees. "
3146 "KiCad only supports 90 degree rotations." ),
3147 aFootprint->GetReference(),
3148 aElem.name,
3149 slotRotation.AsDegrees() );
3151 }
3152 }
3153 }
3154
3155 break;
3156 }
3157
3158 default:
3159 case ALTIUM_PAD_HOLE_SHAPE::UNKNOWN:
3160 if( !m_footprintName.IsEmpty() )
3161 {
3162 if( m_reporter )
3163 {
3164 wxString msg;
3165 msg.Printf( _( "Error loading library '%s':\n"
3166 "Footprint %s pad %s uses a hole of unknown kind %d." ),
3167 m_library,
3169 aElem.name,
3170 aElem.sizeAndShape->holeshape );
3172 }
3173 }
3174 else
3175 {
3176 if( m_reporter )
3177 {
3178 wxString msg;
3179 msg.Printf( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
3180 aFootprint->GetReference(),
3181 aElem.name,
3182 aElem.sizeAndShape->holeshape );
3184 }
3185 }
3186
3187 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3188 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3189 break;
3190 }
3191 }
3192
3193 if( aElem.sizeAndShape )
3194 pad->SetOffset( aElem.sizeAndShape->holeoffset[0] );
3195 }
3196
3197 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3198 {
3199 if( !m_footprintName.IsEmpty() )
3200 {
3201 if( m_reporter )
3202 {
3203 wxString msg;
3204 msg.Printf( _( "Error loading library '%s':\n"
3205 "Footprint %s pad %s uses a complex pad stack (not yet supported)." ),
3206 m_library,
3208 aElem.name );
3210 }
3211 }
3212 else
3213 {
3214 if( m_reporter )
3215 {
3216 wxString msg;
3217 msg.Printf( _( "Footprint %s pad %s uses a complex pad stack (not yet supported)." ),
3218 aFootprint->GetReference(),
3219 aElem.name );
3221 }
3222 }
3223 }
3224
3225 switch( aElem.topshape )
3226 {
3227 case ALTIUM_PAD_SHAPE::RECT:
3228 pad->SetShape( PAD_SHAPE::RECTANGLE );
3229 break;
3230
3231 case ALTIUM_PAD_SHAPE::CIRCLE:
3232 if( aElem.sizeAndShape
3233 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
3234 {
3235 pad->SetShape( PAD_SHAPE::ROUNDRECT ); // 100 = round, 0 = rectangular
3236 double ratio = aElem.sizeAndShape->cornerradius[0] / 200.;
3237 pad->SetRoundRectRadiusRatio( ratio );
3238 }
3239 else if( aElem.topsize.x == aElem.topsize.y )
3240 {
3241 pad->SetShape( PAD_SHAPE::CIRCLE );
3242 }
3243 else
3244 {
3245 pad->SetShape( PAD_SHAPE::OVAL );
3246 }
3247
3248 break;
3249
3250 case ALTIUM_PAD_SHAPE::OCTAGONAL:
3251 pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
3252 pad->SetChamferPositions( RECT_CHAMFER_ALL );
3253 pad->SetChamferRectRatio( 0.25 );
3254 break;
3255
3256 case ALTIUM_PAD_SHAPE::UNKNOWN:
3257 default:
3258 if( !m_footprintName.IsEmpty() )
3259 {
3260 if( m_reporter )
3261 {
3262 wxString msg;
3263 msg.Printf( _( "Error loading library '%s':\n"
3264 "Footprint %s pad %s uses an unknown pad-shape." ),
3265 m_library,
3267 aElem.name );
3269 }
3270 }
3271 else
3272 {
3273 if( m_reporter )
3274 {
3275 wxString msg;
3276 msg.Printf( _( "Footprint %s pad %s uses an unknown pad-shape." ),
3277 aFootprint->GetReference(),
3278 aElem.name );
3280 }
3281 }
3282 break;
3283 }
3284
3285 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
3286 {
3287 // KiCad likes NPTH pads to be the same size & shape as their holes
3288 pad->SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE ? PAD_SHAPE::CIRCLE
3289 : PAD_SHAPE::OVAL );
3290 pad->SetSize( pad->GetDrillSize() );
3291 }
3292
3293 switch( aElem.layer )
3294 {
3295 case ALTIUM_LAYER::TOP_LAYER:
3296 pad->SetLayer( F_Cu );
3297 pad->SetLayerSet( PAD::SMDMask() );
3298 break;
3299
3300 case ALTIUM_LAYER::BOTTOM_LAYER:
3301 pad->SetLayer( B_Cu );
3302 pad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
3303 break;
3304
3305 case ALTIUM_LAYER::MULTI_LAYER:
3306 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
3307 break;
3308
3309 default:
3310 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3311 pad->SetLayer( klayer );
3312 pad->SetLayerSet( LSET( 1, klayer ) );
3313 break;
3314 }
3315
3316 if( aElem.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
3317 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
3318
3319 if( aElem.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
3320 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
3321
3322 if( aElem.is_tent_top )
3323 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
3324
3325 if( aElem.is_tent_bottom )
3326 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
3327
3328 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3329}
3330
3331
3333{
3334 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3335
3336 if( klayer == UNDEFINED_LAYER )
3337 {
3338 if( m_reporter )
3339 {
3340 wxString msg;
3341 msg.Printf( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
3342 "equivalent. It has been moved to KiCad layer Eco1_User." ),
3343 aElem.name, aElem.layer );
3345 }
3346
3347 klayer = Eco1_User;
3348 }
3349
3350 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( m_board );
3351
3352 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3353
3354 m_board->Add( pad.release(), ADD_MODE::APPEND );
3355}
3356
3357
3359{
3360 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3361
3362 if( klayer == UNDEFINED_LAYER )
3363 {
3364 if( !m_footprintName.IsEmpty() )
3365 {
3366 if( m_reporter )
3367 {
3368 wxString msg;
3369 msg.Printf( _( "Loading library '%s':\n"
3370 "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3371 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3372 m_library,
3374 aElem.name,
3375 aElem.layer );
3377 }
3378 }
3379 else
3380 {
3381 if( m_reporter )
3382 {
3383 wxString msg;
3384 msg.Printf( _( "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3385 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3386 aFootprint->GetReference(),
3387 aElem.name,
3388 aElem.layer );
3390 }
3391 }
3392
3393 klayer = Eco1_User;
3394 }
3395
3396 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( aFootprint );
3397
3398 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3399
3400 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3401}
3402
3403
3405 PCB_SHAPE* aShape )
3406{
3407 if( aElem.net != ALTIUM_NET_UNCONNECTED )
3408 {
3409 if( m_reporter )
3410 {
3411 wxString msg;
3412 msg.Printf( _( "Non-copper pad %s is connected to a net, which is not supported." ),
3413 aElem.name );
3415 }
3416 }
3417
3418 if( aElem.holesize != 0 )
3419 {
3420 if( m_reporter )
3421 {
3422 wxString msg;
3423 msg.Printf( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
3425 }
3426 }
3427
3428 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3429 {
3430 if( m_reporter )
3431 {
3432 wxString msg;
3433 msg.Printf( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
3434 aElem.name );
3436 }
3437 }
3438
3439 switch( aElem.topshape )
3440 {
3441 case ALTIUM_PAD_SHAPE::RECT:
3442 {
3443 // filled rect
3444 aShape->SetShape( SHAPE_T::POLY );
3445 aShape->SetFilled( true );
3446 aShape->SetLayer( aLayer );
3447 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3448
3449 aShape->SetPolyPoints(
3450 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
3451 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3452 aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3453 aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
3454
3455 if( aElem.direction != 0 )
3456 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3457 }
3458 break;
3459
3460 case ALTIUM_PAD_SHAPE::CIRCLE:
3461 if( aElem.sizeAndShape
3462 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
3463 {
3464 // filled roundrect
3465 int cornerradius = aElem.sizeAndShape->cornerradius[0];
3466 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
3467
3468 aShape->SetLayer( aLayer );
3469 aShape->SetStroke( STROKE_PARAMS( offset * 2, LINE_STYLE::SOLID ) );
3470
3471 if( cornerradius < 100 )
3472 {
3473 int offsetX = aElem.topsize.x / 2 - offset;
3474 int offsetY = aElem.topsize.y / 2 - offset;
3475
3476 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
3477 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
3478 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
3479 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
3480
3481 aShape->SetShape( SHAPE_T::POLY );
3482 aShape->SetFilled( true );
3483 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
3484 }
3485 else if( aElem.topsize.x == aElem.topsize.y )
3486 {
3487 // circle
3488 aShape->SetShape( SHAPE_T::CIRCLE );
3489 aShape->SetFilled( true );
3490 aShape->SetStart( aElem.position );
3491 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3492 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3493 }
3494 else if( aElem.topsize.x < aElem.topsize.y )
3495 {
3496 // short vertical line
3497 aShape->SetShape( SHAPE_T::SEGMENT );
3498 VECTOR2I pointOffset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
3499 aShape->SetStart( aElem.position + pointOffset );
3500 aShape->SetEnd( aElem.position - pointOffset );
3501 }
3502 else
3503 {
3504 // short horizontal line
3505 aShape->SetShape( SHAPE_T::SEGMENT );
3506 VECTOR2I pointOffset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
3507 aShape->SetStart( aElem.position + pointOffset );
3508 aShape->SetEnd( aElem.position - pointOffset );
3509 }
3510
3511 if( aElem.direction != 0 )
3512 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3513 }
3514 else if( aElem.topsize.x == aElem.topsize.y )
3515 {
3516 // filled circle
3517 aShape->SetShape( SHAPE_T::CIRCLE );
3518 aShape->SetFilled( true );
3519 aShape->SetLayer( aLayer );
3520 aShape->SetStart( aElem.position );
3521 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3522 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3523 }
3524 else
3525 {
3526 // short line
3527 aShape->SetShape( SHAPE_T::SEGMENT );
3528 aShape->SetLayer( aLayer );
3529 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
3530 LINE_STYLE::SOLID ) );
3531
3532 if( aElem.topsize.x < aElem.topsize.y )
3533 {
3534 VECTOR2I offset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
3535 aShape->SetStart( aElem.position + offset );
3536 aShape->SetEnd( aElem.position - offset );
3537 }
3538 else
3539 {
3540 VECTOR2I offset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
3541 aShape->SetStart( aElem.position + offset );
3542 aShape->SetEnd( aElem.position - offset );
3543 }
3544
3545 if( aElem.direction != 0 )
3546 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3547 }
3548 break;
3549
3550 case ALTIUM_PAD_SHAPE::OCTAGONAL:
3551 {
3552 // filled octagon
3553 aShape->SetShape( SHAPE_T::POLY );
3554 aShape->SetFilled( true );
3555 aShape->SetLayer( aLayer );
3556 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3557
3558 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
3559 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3560 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3561 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
3562
3563 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
3564 VECTOR2I chamferX( chamfer, 0 );
3565 VECTOR2I chamferY( 0, chamfer );
3566
3567 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
3568 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
3569
3570 if( aElem.direction != 0. )
3571 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3572 }
3573 break;
3574
3575 case ALTIUM_PAD_SHAPE::UNKNOWN:
3576 default:
3577 if( m_reporter )
3578 {
3579 wxString msg;
3580 msg.Printf( _( "Non-copper pad %s uses an unknown pad-shape." ), aElem.name );
3582 }
3583
3584 break;
3585 }
3586}
3587
3588
3590 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3591{
3592 if( m_progressReporter )
3593 m_progressReporter->Report( _( "Loading vias..." ) );
3594
3595 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3596
3597 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3598 {
3599 checkpoint();
3600 AVIA6 elem( reader );
3601
3602 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
3603
3604 via->SetPosition( elem.position );
3605 via->SetWidth( elem.diameter );
3606 via->SetDrill( elem.holesize );
3607 via->SetNetCode( GetNetCode( elem.net ) );
3608 via->SetLocked( elem.is_locked );
3609
3610 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
3611 || elem.layer_start == ALTIUM_LAYER::BOTTOM_LAYER;
3612 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
3613 || elem.layer_end == ALTIUM_LAYER::BOTTOM_LAYER;
3614
3615 if( start_layer_outside && end_layer_outside )
3616 {
3617 via->SetViaType( VIATYPE::THROUGH );
3618 }
3619 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
3620 {
3621 via->SetViaType( VIATYPE::BLIND_BURIED );
3622 }
3623 else
3624 {
3625 via->SetViaType( VIATYPE::MICROVIA ); // TODO: always a microvia?
3626 }
3627
3628 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
3629 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
3630
3631 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
3632 {
3633 if( m_reporter )
3634 {
3635 wxString msg;
3636 msg.Printf( _( "Via from layer %d to %d uses a non-copper layer, which is not "
3637 "supported." ),
3638 elem.layer_start,
3639 elem.layer_end );
3641 }
3642
3643 continue; // just assume through-hole instead.
3644 }
3645
3646 // we need VIATYPE set!
3647 via->SetLayerPair( start_klayer, end_klayer );
3648
3649 m_board->Add( via.release(), ADD_MODE::APPEND );
3650 }
3651
3652 if( reader.GetRemainingBytes() != 0 )
3653 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
3654}
3655
3657 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3658{
3659 if( m_progressReporter )
3660 m_progressReporter->Report( _( "Loading tracks..." ) );
3661
3662 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3663
3664 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
3665 {
3666 checkpoint();
3667 ATRACK6 elem( reader );
3668
3669 if( elem.component == ALTIUM_COMPONENT_NONE )
3670 {
3671 ConvertTracks6ToBoardItem( elem, primitiveIndex );
3672 }
3673 else
3674 {
3675 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3676 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
3677 }
3678 }
3679
3680 if( reader.GetRemainingBytes() != 0 )
3681 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
3682}
3683
3684
3685void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
3686{
3687 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
3688 {
3689 if( m_polygons.size() <= aElem.polygon )
3690 {
3691 // Can happen when reading old Altium files: just skip this item
3692 if( m_reporter )
3693 {
3694 wxString msg;
3695 msg.Printf( wxT( "ATRACK6 stream tries to access polygon id %u "
3696 "of %u existing polygons; skipping it" ),
3697 static_cast<unsigned>( aElem.polygon ),
3698 static_cast<unsigned>( m_polygons.size() ) );
3700 }
3701
3702 return;
3703 }
3704
3705 ZONE* zone = m_polygons.at( aElem.polygon );
3706
3707 if( zone == nullptr )
3708 {
3709 return; // we know the zone id, but because we do not know the layer we did not
3710 // add it!
3711 }
3712
3713 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3714
3715 if( klayer == UNDEFINED_LAYER )
3716 return; // Just skip it for now. Users can fill it themselves.
3717
3718 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
3719
3720 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3721 shape.SetStart( aElem.start );
3722 shape.SetEnd( aElem.end );
3723 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3724
3725 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
3726 // Will be simplified and fractured later
3727
3728 zone->SetIsFilled( true );
3729 zone->SetNeedRefill( false );
3730
3731 return;
3732 }
3733
3734 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3735 || IsAltiumLayerAPlane( aElem.layer ) )
3736 {
3737 // This is not the actual board item. We can use it to create the polygon for the region
3738 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3739 shape.SetStart( aElem.start );
3740 shape.SetEnd( aElem.end );
3741 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3742
3744 }
3745 else
3746 {
3747 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3748 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
3749 }
3750
3751 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
3752 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
3753 {
3754 int width = aElem.width + ( layerExpansionMask.second * 2 );
3755 if( width > 1 )
3756 {
3757 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
3758
3759 seg->SetStart( aElem.start );
3760 seg->SetEnd( aElem.end );
3761 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3762 seg->SetLayer( layerExpansionMask.first );
3763
3764 m_board->Add( seg.release(), ADD_MODE::APPEND );
3765 }
3766 }
3767}
3768
3769
3771 const int aPrimitiveIndex,
3772 const bool aIsBoardImport )
3773{
3774 if( aElem.polygon != ALTIUM_POLYGON_NONE )
3775 {
3776 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Track with polygon id %u",
3777 (unsigned)aElem.polygon ) );
3778 return;
3779 }
3780
3781 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3782 || IsAltiumLayerAPlane( aElem.layer ) )
3783 {
3784 // This is not the actual board item. We can use it to create the polygon for the region
3785 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3786 shape.SetStart( aElem.start );
3787 shape.SetEnd( aElem.end );
3788 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3789
3790 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3791 aElem.keepoutrestrictions );
3792 }
3793 else
3794 {
3795 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3796 {
3797 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3798 {
3799 // Special case: do to not lose net connections in footprints
3800 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
3801 }
3802 else
3803 {
3804 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3805 }
3806 }
3807 }
3808
3809 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
3810 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
3811 {
3812 int width = aElem.width + ( layerExpansionMask.second * 2 );
3813 if( width > 1 )
3814 {
3815 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
3816
3817 seg->SetStart( aElem.start );
3818 seg->SetEnd( aElem.end );
3819 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3820 seg->SetLayer( layerExpansionMask.first );
3821
3822 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
3823 }
3824 }
3825}
3826
3827
3829{
3830 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3831 {
3832 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
3833
3834 track->SetStart( aElem.start );
3835 track->SetEnd( aElem.end );
3836 track->SetWidth( aElem.width );
3837 track->SetLayer( aLayer );
3838 track->SetNetCode( GetNetCode( aElem.net ) );
3839
3840 m_board->Add( track.release(), ADD_MODE::APPEND );
3841 }
3842 else
3843 {
3844 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
3845
3846 seg->SetStart( aElem.start );
3847 seg->SetEnd( aElem.end );
3848 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3849 seg->SetLayer( aLayer );
3850
3851 m_board->Add( seg.release(), ADD_MODE::APPEND );
3852 }
3853}
3854
3855
3857 PCB_LAYER_ID aLayer )
3858{
3859 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
3860
3861 seg->SetStart( aElem.start );
3862 seg->SetEnd( aElem.end );
3863 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3864 seg->SetLayer( aLayer );
3865
3866 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
3867}
3868
3869
3871 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3872{
3873 if( m_progressReporter )
3874 m_progressReporter->Report( _( "Loading unicode strings..." ) );
3875
3876 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3877
3879
3880 if( reader.GetRemainingBytes() != 0 )
3881 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
3882}
3883
3885 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3886{
3887 if( m_progressReporter )
3888 m_progressReporter->Report( _( "Loading text..." ) );
3889
3890 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3891
3892 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3893 {
3894 checkpoint();
3895 ATEXT6 elem( reader, m_unicodeStrings );
3896
3897 if( elem.component == ALTIUM_COMPONENT_NONE )
3898 {
3900 }
3901 else
3902 {
3903 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3904 ConvertTexts6ToFootprintItem( footprint, elem );
3905 }
3906 }
3907
3908 if( reader.GetRemainingBytes() != 0 )
3909 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
3910}
3911
3912
3914{
3915 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3916 {
3917 if( m_reporter )
3918 {
3919 wxString msg;
3920 msg.Printf( _( "Ignored barcode on Altium layer %d (not yet supported)." ),
3921 aElem.layer );
3923 }
3924
3925 return;
3926 }
3927
3928 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3929 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
3930}
3931
3932
3934{
3935 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3936 {
3937 if( !m_footprintName.IsEmpty() )
3938 {
3939 if( m_reporter )
3940 {
3941 wxString msg;
3942 msg.Printf( _( "Error loading library '%s':\n"
3943 "Footprint %s contains barcode on Altium layer %d (not yet supported)." ),
3944 m_library,
3946 aElem.layer );
3948 }
3949 }
3950 else
3951 {
3952 if( m_reporter )
3953 {
3954 wxString msg;
3955 msg.Printf( _( "Footprint %s contains barcode on Altium layer %d (not yet supported)." ),
3956 aFootprint->GetReference(),
3957 aElem.layer );
3959 }
3960 }
3961
3962 return;
3963 }
3964
3965 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3966 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3967}
3968
3969
3971{
3972 std::unique_ptr<PCB_TEXT> pcbText = std::make_unique<PCB_TEXT>( m_board );
3973
3974 static const std::map<wxString, wxString> variableMap = {
3975 { "LAYER_NAME", "LAYER" },
3976 { "PRINT_DATE", "CURRENT_DATE"},
3977 };
3978
3979 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
3980
3981 pcbText->SetText(kicadText);
3982 pcbText->SetLayer( aLayer );
3983 pcbText->SetPosition( aElem.position );
3984 pcbText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3985
3986 ConvertTexts6ToEdaTextSettings( aElem, *pcbText );
3987
3988 m_board->Add( pcbText.release(), ADD_MODE::APPEND );
3989}
3990
3991
3993 PCB_LAYER_ID aLayer )
3994{
3995 PCB_TEXT* fpText;
3996
3997 if( aElem.isDesignator )
3998 {
3999 fpText = &aFootprint->Reference(); // TODO: handle multiple layers
4000 }
4001 else if( aElem.isComment )
4002 {
4003 fpText = &aFootprint->Value(); // TODO: handle multiple layers
4004 }
4005 else
4006 {
4007 fpText = new PCB_TEXT( aFootprint );
4008 aFootprint->Add( fpText, ADD_MODE::APPEND );
4009 }
4010
4011 static const std::map<wxString, wxString> variableMap = {
4012 { "DESIGNATOR", "REFERENCE" },
4013 { "COMMENT", "VALUE" },
4014 { "VALUE", "ALTIUM_VALUE" },
4015 { "LAYER_NAME", "LAYER" },
4016 { "PRINT_DATE", "CURRENT_DATE"},
4017 };
4018
4019 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4020
4021 fpText->SetText(kicadText);
4022 fpText->SetKeepUpright( false );
4023 fpText->SetLayer( aLayer );
4024 fpText->SetPosition( aElem.position );
4025 fpText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4026
4027 ConvertTexts6ToEdaTextSettings( aElem, *fpText );
4028}
4029
4030
4032{
4033 aEdaText.SetTextSize( VECTOR2I( aElem.height, aElem.height ) ); // TODO: parse text width
4034
4035 if( aElem.fonttype == ALTIUM_TEXT_TYPE::TRUETYPE )
4036 {
4037 KIFONT::FONT* font = KIFONT::FONT::GetFont( aElem.fontname, aElem.isBold, aElem.isItalic );
4038 aEdaText.SetFont( font );
4039
4040 if( font->IsOutline() )
4041 {
4042 // TODO: why is this required? Somehow, truetype size is calculated differently
4043 if( font->GetName().Contains( wxS( "Arial" ) ) )
4044 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.63, aElem.height * 0.63 ) );
4045 else
4046 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.5, aElem.height * 0.5 ) );
4047 }
4048 }
4049
4050 aEdaText.SetTextThickness( aElem.strokewidth );
4051 aEdaText.SetBoldFlag( aElem.isBold );
4052 aEdaText.SetItalic( aElem.isItalic );
4053 aEdaText.SetMirrored( aElem.isMirrored );
4054
4055 // Altium position always specifies the bottom left corner
4058
4059 // TODO: correct the position and set proper justification
4060}
4061
4062
4064 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4065{
4066 if( m_progressReporter )
4067 m_progressReporter->Report( _( "Loading rectangles..." ) );
4068
4069 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4070
4071 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4072 {
4073 checkpoint();
4074 AFILL6 elem( reader );
4075
4076 if( elem.component == ALTIUM_COMPONENT_NONE )
4077 {
4079 }
4080 else
4081 {
4082 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4083 ConvertFills6ToFootprintItem( footprint, elem, true );
4084 }
4085 }
4086
4087 if( reader.GetRemainingBytes() != 0 )
4088 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
4089}
4090
4091
4093{
4094 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
4095 {
4096 // This is not the actual board item. We can use it to create the polygon for the region
4097 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4098
4099 shape.SetStart( aElem.pos1 );
4100 shape.SetEnd( aElem.pos2 );
4101 shape.SetFilled( true );
4102 shape.SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
4103
4104 if( aElem.rotation != 0. )
4105 {
4106 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2,
4107 ( aElem.pos1.y + aElem.pos2.y ) / 2 );
4108 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4109 }
4110
4112 }
4113 else
4114 {
4115 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4116 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4117 }
4118}
4119
4120
4122 const bool aIsBoardImport )
4123{
4124 if( aElem.is_keepout
4125 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
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::RECTANGLE );
4129
4130 shape.SetStart( aElem.pos1 );
4131 shape.SetEnd( aElem.pos2 );
4132 shape.SetFilled( true );
4133 shape.SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
4134
4135 if( aElem.rotation != 0. )
4136 {
4137 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2,
4138 ( aElem.pos1.y + aElem.pos2.y ) / 2 );
4139 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4140 }
4141
4142 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4143 aElem.keepoutrestrictions );
4144 }
4145 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
4146 && aElem.net != ALTIUM_NET_UNCONNECTED )
4147 {
4148 // Special case: do to not lose net connections in footprints
4149 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4150 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4151 }
4152 else
4153 {
4154 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4155 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4156 }
4157}
4158
4159
4161{
4162 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::RECTANGLE );
4163
4164 fill->SetFilled( true );
4165 fill->SetLayer( aLayer );
4166 fill->SetStroke( STROKE_PARAMS( 0 ) );
4167
4168 fill->SetStart( aElem.pos1 );
4169 fill->SetEnd( aElem.pos2 );
4170
4171 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4172 {
4173 fill->SetNetCode( GetNetCode( aElem.net ) );
4174 }
4175
4176 if( aElem.rotation != 0. )
4177 {
4178 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4179 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2, ( aElem.pos1.y + aElem.pos2.y ) / 2 );
4180 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4181 }
4182
4183 m_board->Add( fill.release(), ADD_MODE::APPEND );
4184}
4185
4186
4188 PCB_LAYER_ID aLayer )
4189{
4190 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::RECTANGLE );
4191
4192 fill->SetFilled( true );
4193 fill->SetLayer( aLayer );
4194 fill->SetStroke( STROKE_PARAMS( 0 ) );
4195
4196 fill->SetStart( aElem.pos1 );
4197 fill->SetEnd( aElem.pos2 );
4198
4199 if( aElem.rotation != 0. )
4200 {
4201 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4202 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2, ( aElem.pos1.y + aElem.pos2.y ) / 2 );
4203 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4204 }
4205
4206 aFootprint->Add( fill.release(), ADD_MODE::APPEND );
4207}
4208
4209
4210void ALTIUM_PCB::HelperSetZoneLayers( ZONE& aZone, const ALTIUM_LAYER aAltiumLayer )
4211{
4212 LSET layerSet;
4213
4214 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
4215 layerSet.set( klayer );
4216
4217 aZone.SetLayerSet( layerSet );
4218}
4219
4220
4221void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE& aZone, const uint8_t aKeepoutRestrictions )
4222{
4223 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
4224 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
4225 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
4226 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
4227 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
4228
4229 aZone.SetDoNotAllowVias( keepoutRestrictionVia );
4230 aZone.SetDoNotAllowTracks( keepoutRestrictionTrack );
4231 aZone.SetDoNotAllowCopperPour( keepoutRestrictionCopper );
4232 aZone.SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
4233 aZone.SetDoNotAllowFootprints( false );
4234}
4235
4236
4238 const ALTIUM_LAYER aAltiumLayer,
4239 const uint8_t aKeepoutRestrictions )
4240{
4241 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
4242
4243 zone->SetIsRuleArea( true );
4244
4245 HelperSetZoneLayers( *zone, aAltiumLayer );
4246 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4247
4248 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4249
4250 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4252
4253 m_board->Add( zone.release(), ADD_MODE::APPEND );
4254}
4255
4256
4258 const PCB_SHAPE& aShape,
4259 const ALTIUM_LAYER aAltiumLayer,
4260 const uint8_t aKeepoutRestrictions )
4261{
4262 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
4263
4264 zone->SetIsRuleArea( true );
4265
4266 HelperSetZoneLayers( *zone, aAltiumLayer );
4267 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4268
4269 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4270
4271 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4273
4274 // TODO: zone->SetLocalCoord(); missing?
4275 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
4276}
4277
4278
4279std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
4280 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
4281{
4282 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
4283 return {}; // there is nothing to parse
4284
4285 auto elems =
4286 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
4287
4288 if( elems.first == elems.second )
4289 return {}; // there is nothing to parse
4290
4291 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
4292
4293 for( auto it = elems.first; it != elems.second; ++it )
4294 {
4295 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
4296
4297 if( pInf.type == AEXTENDED_PRIMITIVE_INFORMATION_TYPE::MASK )
4298 {
4299 if( pInf.soldermaskexpansionmode == ALTIUM_MODE::MANUAL
4300 || pInf.soldermaskexpansionmode == ALTIUM_MODE::RULE )
4301 {
4302 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
4303 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4304 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4305 {
4306 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
4307 }
4308
4309 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4310 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4311 {
4312 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
4313 }
4314 }
4315 if( pInf.pastemaskexpansionmode == ALTIUM_MODE::MANUAL
4316 || pInf.pastemaskexpansionmode == ALTIUM_MODE::RULE )
4317 {
4318 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4319 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4320 {
4321 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
4322 }
4323
4324 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4325 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4326 {
4327 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
4328 }
4329 }
4330 }
4331 }
4332
4333 return layerExpansionPairs;
4334}
const char * name
Definition: DXF_plotter.cpp:57
std::string FormatPath(const std::vector< std::string > &aVectorPath)
Helper for debug logging (vector -> string)
ALTIUM_RULE_KIND
const uint16_t ALTIUM_NET_UNCONNECTED
const uint16_t ALTIUM_POLYGON_NONE
ALTIUM_LAYER
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)
Definition: altium_pcb.cpp:87
double normalizeAngleDegrees(double Angle, double aMin, double aMax)
Normalize angle to be aMin < angle <= aMax angle is in degrees.
constexpr double BOLD_FACTOR
Definition: altium_pcb.cpp:59
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:62
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:69
std::function< void(const ALTIUM_COMPOUND_FILE &, const CFB::COMPOUND_FILE_ENTRY *)> PARSE_FUNCTION_POINTER_fp
Definition: altium_pcb.h:105
ALTIUM_PCB_DIR
Definition: altium_pcb.h:38
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition: board.h:150
@ LT_POWER
Definition: board.h:153
@ LT_MIXED
Definition: board.h:154
@ LT_SIGNAL
Definition: board.h:152
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:44
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:45
size_t GetRemainingBytes() const
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
std::tuple< wxString, const CFB::COMPOUND_FILE_ENTRY * > FindLibFootprintDirName(const wxString &aFpUnicodeName)
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
void ParseFileHeader(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:880
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition: altium_pcb.h:263
void ParseShapeBasedRegions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::vector< PCB_DIM_RADIAL * > m_radialDimensions
Definition: altium_pcb.h:249
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:273
void ConvertTexts6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_LAYER, PCB_LAYER_ID > m_layermap
Definition: altium_pcb.h:253
void ConvertShapeBasedRegions6ToBoardItemOnLayer(const AREGION6 &aElem, PCB_LAYER_ID aLayer)
void HelperParseDimensions6Leader(const ADIMENSION6 &aElem)
wxString m_footprintName
for footprint library loading error reporting
Definition: altium_pcb.h:270
std::vector< FOOTPRINT * > m_components
Definition: altium_pcb.h:247
const ARULE6 * GetRuleDefault(ALTIUM_RULE_KIND aKind) const
Definition: altium_pcb.cpp:864
void HelperParsePad6NonCopper(const APAD6 &aElem, PCB_LAYER_ID aLayer, PCB_SHAPE *aShape)
std::vector< PCB_LAYER_ID > GetKicadLayersToIterate(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:234
void ParseRegions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseBoard6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:920
void ParseComponentsBodies6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
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 ParseFills6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void Parse(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
Definition: altium_pcb.cpp:314
void ParseBoardRegionsData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
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:259
std::vector< int > m_altiumToKicadNetcodes
Definition: altium_pcb.h:252
unsigned m_totalCount
for progress reporting
Definition: altium_pcb.h:267
void HelperPcpShapeAsBoardKeepoutRegion(const PCB_SHAPE &aShape, const ALTIUM_LAYER aAltiumLayer, const uint8_t aKeepoutRestrictions)
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 ConvertFills6ToFootprintItem(FOOTPRINT *aFootprint, const AFILL6 &aElem, const bool aIsBoardImport)
void ConvertShapeBasedRegions6ToBoardItem(const AREGION6 &aElem)
void HelperCreateBoardOutline(const std::vector< ALTIUM_VERTICE > &aVertices)
void ParseVias6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::map< ALTIUM_RULE_KIND, std::vector< ARULE6 > > m_rules
Definition: altium_pcb.h:255
void HelperSetZoneKeepoutRestrictions(ZONE &aZone, const uint8_t aKeepoutRestrictions)
void ParseClasses6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
unsigned m_doneCount
Definition: altium_pcb.h:265
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
std::map< uint32_t, wxString > m_unicodeStrings
Definition: altium_pcb.h:251
void ConvertTexts6ToBoardItem(const ATEXT6 &aElem)
FOOTPRINT * ParseFootprint(ALTIUM_COMPOUND_FILE &altiumLibFile, const wxString &aFootprintName)
Definition: altium_pcb.cpp:655
void ParseArcs6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperParseDimensions6Center(const ADIMENSION6 &aElem)
void HelperParseDimensions6Radial(const ADIMENSION6 &aElem)
void ParsePads6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
BOARD * m_board
Definition: altium_pcb.h:246
void ParseWideStrings6Data(const ALTIUM_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)
REPORTER * m_reporter
optional; may be nullptr
Definition: altium_pcb.h:264
void ParseComponents6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
int GetNetCode(uint16_t aId) const
Definition: altium_pcb.cpp:830
void ConvertTexts6ToEdaTextSettings(const ATEXT6 &aElem, EDA_TEXT &aEdaText)
wxString m_library
for footprint library loading error reporting
Definition: altium_pcb.h:269
void ConvertPads6ToFootprintItemOnNonCopper(FOOTPRINT *aFootprint, const APAD6 &aElem)
void ConvertTexts6ToFootprintItem(FOOTPRINT *aFootprint, const ATEXT6 &aElem)
unsigned m_lastProgressCount
Definition: altium_pcb.h:266
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 remapUnsureLayers(std::vector< ABOARD6_LAYER_STACKUP > &aStackup)
std::vector< ZONE * > m_polygons
Definition: altium_pcb.h:248
std::map< wxString, wxString > m_models
Definition: altium_pcb.h:250
void ConvertArcs6ToPcbShape(const AARC6 &aElem, PCB_SHAPE *aShape)
void ConvertTracks6ToBoardItemOnLayer(const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void ConvertFills6ToBoardItem(const AFILL6 &aElem)
void ParseModelsData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry, const std::vector< std::string > &aRootDir)
void ParseRules6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
FOOTPRINT * HelperGetFootprint(uint16_t aComponent) const
Definition: altium_pcb.cpp:74
LAYER_MAPPING_HANDLER m_layerMappingHandler
Definition: altium_pcb.h:261
PCB_LAYER_ID GetKicadLayer(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:128
void checkpoint()
Definition: altium_pcb.cpp:295
void ConvertTracks6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void ParseTexts6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParsePolygons6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
ALTIUM_PCB(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter, LAYER_MAPPING_HANDLER &aLayerMappingHandler, REPORTER *aReporter=nullptr, const wxString &aLibrary=wxEmptyString, const wxString &aFootprintName=wxEmptyString)
Definition: altium_pcb.cpp:275
void ConvertArcs6ToBoardItemOnLayer(const AARC6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_RECORD, std::multimap< int, const AEXTENDED_PRIMITIVE_INFORMATION > > m_extendedPrimitiveInformationMaps
Definition: altium_pcb.h:257
std::map< ALTIUM_LAYER, wxString > m_layerNames
Definition: altium_pcb.h:254
void ParseTracks6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertPads6ToBoardItemOnNonCopper(const APAD6 &aElem)
void ParseNets6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseExtendedPrimitiveInformationData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:898
void ParseDimensions6Data(const ALTIUM_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
Definition: altium_pcb.cpp:848
void HelperSetZoneLayers(ZONE &aZone, const ALTIUM_LAYER aAltiumLayer)
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
void SetGridOrigin(const VECTOR2I &aOrigin)
const VECTOR2I & GetGridOrigin()
const VECTOR2I & GetAuxOrigin()
void SetAuxOrigin(const VECTOR2I &aOrigin)
BOARD_STACKUP & GetStackupDescriptor()
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:260
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
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:282
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:677
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:882
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:911
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:697
const PAGE_INFO & GetPageSettings() const
Definition: board.h:671
bool IsLayerEnabled(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition: board.cpp:703
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:598
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:580
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: board.cpp:490
int GetCopperLayerCount() const
Definition: board.cpp:653
const TRACKS & Tracks() const
Definition: board.h:321
void SetVisibleLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:709
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:369
void SetCopperLayerCount(int aCount)
Definition: board.cpp:659
const wxString & GetFileName() const
Definition: board.h:319
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:610
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:794
const DRAWINGS & Drawings() const
Definition: board.h:325
size_type GetHeight() const
Definition: box2.h:205
size_type GetWidth() const
Definition: box2.h:204
coord_type GetY() const
Definition: box2.h:198
coord_type GetX() const
Definition: box2.h:197
EDA_ANGLE Normalize()
Definition: eda_angle.h:255
double Sin() const
Definition: eda_angle.h:212
double AsDegrees() const
Definition: eda_angle.h:155
bool IsHorizontal() const
Definition: eda_angle.h:180
bool IsVertical() const
Definition: eda_angle.h:185
double Cos() const
Definition: eda_angle.h:227
void SetModified()
Definition: eda_item.cpp:64
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:663
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:545
void SetFilled(bool aFlag)
Definition: eda_shape.h:96
int GetRadius() const
Definition: eda_shape.cpp:593
SHAPE_T GetShape() const
Definition: eda_shape.h:120
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:129
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:125
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:119
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:166
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:691
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1204
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:83
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:372
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:250
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:274
void SetBoldFlag(bool aBold)
Definition: eda_text.cpp:235
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:195
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:282
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:181
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:203
void SetItalic(bool aItalic)
Definition: eda_text.cpp:211
void SetFont(KIFONT::FONT *aFont)
Definition: eda_text.cpp:356
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:266
EDA_ANGLE GetOrientation() const
Definition: footprint.h:212
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:624
bool IsFlipped() const
Definition: footprint.h:377
PCB_FIELD & Reference()
Definition: footprint.h:625
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:968
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:205
const wxString & GetReference() const
Definition: footprint.h:588
VECTOR2I GetPosition() const override
Definition: footprint.h:209
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:98
double m_Opacity
Definition: footprint.h:99
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:97
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:100
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)
Definition: font.cpp:146
const wxString & GetName() const
Definition: font.h:147
virtual bool IsOutline() const
Definition: font.h:139
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: layer_ids.h:575
static LSET AllBoardTechMask()
Return a mask holding board technical layers (no CU layer) on both side.
Definition: lset.cpp:938
static LSET UserDefinedLayers()
Return a mask with all of the allowable user defined layers.
Definition: lset.cpp:967
static LSET UserMask()
Definition: lset.cpp:945
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:863
Handle the data for a net.
Definition: netinfo.h:56
int GetNetCode() const
Definition: netinfo.h:108
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:375
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:312
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:333
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:319
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition: page_info.h:162
int GetWidthIU(double aIUScale) const
Gets the page width in IU.
Definition: page_info.h:153
double GetRadius() const
Definition: pcb_track.cpp:1536
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:323
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:75
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:524
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:315
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:86
VECTOR2I GetPosition() const override
Definition: pcb_shape.h:73
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:87
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
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:196
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:115
const VECTOR2I & GetP1() const
Definition: shape_arc.h:114
const VECTOR2I & GetP0() const
Definition: shape_arc.h:113
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 Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union For aFastMode meaning, see function booleanOp.
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
void Move(const VECTOR2I &aVector) override
Simple container to manage line stroke parameters.
Definition: stroke_params.h:81
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:354
T y
Definition: vector3.h:63
T z
Definition: vector3.h:64
T x
Definition: vector3.h:62
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:264
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:721
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:718
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition: zone.h:621
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:720
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:636
void SetIsFilled(bool isFilled)
Definition: zone.h:261
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition: zone.h:607
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:719
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:267
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:722
static int GetDefaultHatchPitch()
Definition: zone.cpp:1051
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:437
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_45
Definition: eda_angle.h:436
@ CTX_NETCLASS
@ ERROR_INSIDE
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
#define MAX_CU_LAYERS
Definition: layer_ids.h:142
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:881
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ In22_Cu
Definition: layer_ids.h:86
@ In11_Cu
Definition: layer_ids.h:75
@ In29_Cu
Definition: layer_ids.h:93
@ In30_Cu
Definition: layer_ids.h:94
@ User_8
Definition: layer_ids.h:130
@ In17_Cu
Definition: layer_ids.h:81
@ Edge_Cuts
Definition: layer_ids.h:113
@ Dwgs_User
Definition: layer_ids.h:109
@ F_Paste
Definition: layer_ids.h:101
@ In9_Cu
Definition: layer_ids.h:73
@ User_6
Definition: layer_ids.h:128
@ User_7
Definition: layer_ids.h:129
@ In19_Cu
Definition: layer_ids.h:83
@ In7_Cu
Definition: layer_ids.h:71
@ In28_Cu
Definition: layer_ids.h:92
@ In26_Cu
Definition: layer_ids.h:90
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ User_5
Definition: layer_ids.h:127
@ Eco1_User
Definition: layer_ids.h:111
@ F_Mask
Definition: layer_ids.h:107
@ In21_Cu
Definition: layer_ids.h:85
@ In23_Cu
Definition: layer_ids.h:87
@ B_Paste
Definition: layer_ids.h:100
@ In15_Cu
Definition: layer_ids.h:79
@ In2_Cu
Definition: layer_ids.h:66
@ User_9
Definition: layer_ids.h:131
@ F_Fab
Definition: layer_ids.h:120
@ In10_Cu
Definition: layer_ids.h:74
@ Margin
Definition: layer_ids.h:114
@ F_SilkS
Definition: layer_ids.h:104
@ In4_Cu
Definition: layer_ids.h:68
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ Eco2_User
Definition: layer_ids.h:112
@ In16_Cu
Definition: layer_ids.h:80
@ In24_Cu
Definition: layer_ids.h:88
@ In1_Cu
Definition: layer_ids.h:65
@ User_3
Definition: layer_ids.h:125
@ User_1
Definition: layer_ids.h:123
@ B_SilkS
Definition: layer_ids.h:103
@ In13_Cu
Definition: layer_ids.h:77
@ User_4
Definition: layer_ids.h:126
@ In8_Cu
Definition: layer_ids.h:72
@ In14_Cu
Definition: layer_ids.h:78
@ User_2
Definition: layer_ids.h:124
@ In12_Cu
Definition: layer_ids.h:76
@ In27_Cu
Definition: layer_ids.h:91
@ In6_Cu
Definition: layer_ids.h:70
@ In5_Cu
Definition: layer_ids.h:69
@ In3_Cu
Definition: layer_ids.h:67
@ In20_Cu
Definition: layer_ids.h:84
@ F_Cu
Definition: layer_ids.h:64
@ In18_Cu
Definition: layer_ids.h:82
@ In25_Cu
Definition: layer_ids.h:89
@ B_Fab
Definition: layer_ids.h:119
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint.
Definition: lset.cpp:680
DIM_PRECISION
Definition: pcb_dimension.h:47
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.
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:39
@ 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
bool is_keepout
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 sourcefootprintlibrary
ALTIUM_LAYER layer
wxString sourcedesignator
int32_t textprecision
ALTIUM_UNIT textunit
wxString textsuffix
uint32_t textlinewidth
wxString textformat
ALTIUM_LAYER layer
std::vector< VECTOR2I > textPoint
wxString textprefix
uint32_t linewidth
ALTIUM_DIMENSION_KIND kind
uint32_t textheight
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
const VECTOR2I position
wxString id
wxString name
wxString name
int32_t soldermaskexpansionmanual
uint16_t net
ALTIUM_LAYER layer
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
ALTIUM_PAD_SHAPE topshape
ALTIUM_PAD_MODE padmode
ALTIUM_MODE pastemaskexpansionmode
uint32_t holesize
double direction
ALTIUM_MODE soldermaskexpansionmode
wxString name
bool is_tent_bottom
uint16_t component
int32_t pastemaskexpansionmanual
bool is_tent_top
VECTOR2I position
VECTOR2I topsize
std::vector< ALTIUM_VERTICE > vertices
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
int32_t pourindex
int32_t trackwidth
ALTIUM_LAYER layer
int32_t gridsize
uint8_t keepoutrestrictions
ALTIUM_LAYER layer
std::vector< ALTIUM_VERTICE > outline
std::vector< std::vector< ALTIUM_VERTICE > > holes
uint16_t component
uint16_t net
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
uint16_t component
wxString text
double rotation
uint32_t height
wxString fontname
ALTIUM_LAYER layer
VECTOR2I position
ALTIUM_TEXT_TYPE fonttype
bool isDesignator
uint32_t strokewidth
VECTOR2I end
uint32_t width
uint16_t net
uint16_t polygon
uint8_t keepoutrestrictions
VECTOR2I start
ALTIUM_LAYER layer
uint16_t component
uint32_t diameter
uint16_t net
VECTOR2I position
bool is_locked
ALTIUM_LAYER layer_start
ALTIUM_LAYER layer_end
uint32_t holesize
constexpr double IUTomm(int iu) const
Definition: base_units.h:86
const double IU_PER_MILS
Definition: base_units.h:77
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
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.
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
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:228
double DEG2RAD(double deg)
Definition: trigo.h:200
double GetLineLength(const VECTOR2I &aPointA, const VECTOR2I &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:194
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:128
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:101
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:118
VECTOR2< int > VECTOR2I
Definition: vector2d.h:602