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