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