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