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