KiCad PCB EDA Suite
Loading...
Searching...
No Matches
altium_pcb.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019-2020 Thomas Pointhuber <[email protected]>
5 * Copyright (C) 2021-2024 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include "altium_pcb.h"
26#include "altium_parser_pcb.h"
30
31#include <board.h>
33#include <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( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
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->Fields() )
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
1311
1312 footprint->SetFPID( fpID );
1313
1314 footprint->SetPosition( elem.position );
1315 footprint->SetOrientationDegrees( elem.rotation );
1316
1317 // KiCad netlisting requires parts to have non-digit + digit annotation.
1318 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1319 wxString reference = elem.sourcedesignator;
1320
1321 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1322 reference.Prepend( wxT( "UNK" ) );
1323
1324 footprint->SetReference( reference );
1325
1326 footprint->SetLocked( elem.locked );
1327 footprint->Reference().SetVisible( elem.nameon );
1328 footprint->Value().SetVisible( elem.commenton );
1329 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1330
1331 m_components.emplace_back( footprint.get() );
1332 m_board->Add( footprint.release(), ADD_MODE::APPEND );
1333
1334 componentId++;
1335 }
1336
1337 if( reader.GetRemainingBytes() != 0 )
1338 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1339}
1340
1341
1343double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1344{
1345 while( Angle < aMin )
1346 Angle += 360.0;
1347
1348 while( Angle >= aMax )
1349 Angle -= 360.0;
1350
1351 return Angle;
1352}
1353
1354
1356 FOOTPRINT* aFootprint,
1357 const ACOMPONENTBODY6& aElem )
1358{
1359 if( m_progressReporter )
1360 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1361
1362 if( !aElem.modelIsEmbedded )
1363 return;
1364
1365 auto model = aAltiumPcbFile.GetLibModel( aElem.modelId );
1366
1367 if( !model )
1368 {
1369 if( m_reporter )
1370 {
1371 m_reporter->Report( wxString::Format( wxT( "Model %s not found for footprint %s" ),
1372 aElem.modelId, aFootprint->GetReference() ),
1374 }
1375
1376 return;
1377 }
1378
1379 const VECTOR2I& fpPosition = aFootprint->GetPosition();
1380
1382 file->name = aElem.modelName;
1383
1384 if( file->name.IsEmpty() )
1385 file->name = model->first.name;
1386
1387 // Decompress the model data before assigning
1388 std::vector<char> decompressedData;
1389 wxMemoryInputStream compressedStream( model->second.data(), model->second.size() );
1390 wxZlibInputStream zlibStream( compressedStream );
1391
1392 // Reserve some space, assuming decompressed data is larger -- STEP file
1393 // compression is typically 5:1 using zlib like Altium does
1394 decompressedData.resize( model->second.size() * 6 );
1395 size_t offset = 0;
1396
1397 while( !zlibStream.Eof() )
1398 {
1399 zlibStream.Read( decompressedData.data() + offset, decompressedData.size() - offset );
1400 size_t bytesRead = zlibStream.LastRead();
1401
1402 if( !bytesRead )
1403 break;
1404
1405 offset += bytesRead;
1406
1407 if( offset >= decompressedData.size() )
1408 decompressedData.resize( 2 * decompressedData.size() ); // Resizing is expensive, avoid if we can
1409 }
1410
1411 decompressedData.resize( offset );
1412
1413 file->decompressedData = std::move( decompressedData );
1415
1417 aFootprint->GetEmbeddedFiles()->AddFile( file );
1418
1419 FP_3DMODEL modelSettings;
1420
1421 modelSettings.m_Filename = aFootprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1422
1423 modelSettings.m_Offset.x = pcbIUScale.IUTomm( (int) aElem.modelPosition.x );
1424 modelSettings.m_Offset.y = -pcbIUScale.IUTomm( (int) aElem.modelPosition.y );
1425 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) aElem.modelPosition.z );
1426
1427 EDA_ANGLE orientation = aFootprint->GetOrientation();
1428
1429 if( aFootprint->IsFlipped() )
1430 {
1431 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1432 orientation = -orientation;
1433 }
1434
1435 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1436
1437 modelSettings.m_Rotation.x = normalizeAngleDegrees( -aElem.modelRotation.x, -180, 180 );
1438 modelSettings.m_Rotation.y = normalizeAngleDegrees( -aElem.modelRotation.y, -180, 180 );
1439 modelSettings.m_Rotation.z = normalizeAngleDegrees( -aElem.modelRotation.z + aElem.rotation
1440 + orientation.AsDegrees(),
1441 -180, 180 );
1442 modelSettings.m_Opacity = aElem.body_opacity_3d;
1443
1444 aFootprint->Models().push_back( modelSettings );
1445}
1446
1447
1449 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1450{
1451 if( m_progressReporter )
1452 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1453
1454 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1455
1456 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1457 {
1458 checkpoint();
1459 ACOMPONENTBODY6 elem( reader );
1460
1461 if( elem.component == ALTIUM_COMPONENT_NONE )
1462 continue; // TODO: we do not support components for the board yet
1463
1464 if( m_components.size() <= elem.component )
1465 {
1466 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1467 "component id %d of %zu existing components" ),
1468 elem.component,
1469 m_components.size() ) );
1470 }
1471
1472 if( !elem.modelIsEmbedded )
1473 continue;
1474
1475 auto modelTuple = m_EmbeddedModels.find( elem.modelId );
1476
1477 if( modelTuple == m_EmbeddedModels.end() )
1478 {
1479 if( m_reporter )
1480 {
1481 wxString msg;
1482 msg.Printf( wxT( "ComponentsBodies6 stream tries to access model id %s which does "
1483 "not exist" ), elem.modelId );
1485 }
1486
1487 continue;
1488 }
1489
1490 const ALTIUM_EMBEDDED_MODEL_DATA& modelData = modelTuple->second;
1491 FOOTPRINT* footprint = m_components.at( elem.component );
1492
1494 file->name = modelData.m_modelname;
1495
1496 wxMemoryInputStream compressedStream( modelData.m_data.data(), modelData.m_data.size() );
1497 wxZlibInputStream zlibStream( compressedStream );
1498 wxMemoryOutputStream decompressedStream;
1499
1500 zlibStream.Read( decompressedStream );
1501 file->decompressedData.resize( decompressedStream.GetSize() );
1502 decompressedStream.CopyTo( file->decompressedData.data(), file->decompressedData.size() );
1503
1505 footprint->GetEmbeddedFiles()->AddFile( file );
1506
1507 FP_3DMODEL modelSettings;
1508
1509 modelSettings.m_Filename = footprint->GetEmbeddedFiles()->GetEmbeddedFileLink( *file );
1510 VECTOR2I fpPosition = footprint->GetPosition();
1511
1512 modelSettings.m_Offset.x =
1513 pcbIUScale.IUTomm( KiROUND( elem.modelPosition.x - fpPosition.x ) );
1514 modelSettings.m_Offset.y =
1515 -pcbIUScale.IUTomm( KiROUND( elem.modelPosition.y - fpPosition.y ) );
1516 modelSettings.m_Offset.z = pcbIUScale.IUTomm( KiROUND( elem.modelPosition.z ) );
1517
1518 EDA_ANGLE orientation = footprint->GetOrientation();
1519
1520 if( footprint->IsFlipped() )
1521 {
1522 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1523 orientation = -orientation;
1524 }
1525
1526 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1527
1528 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1529 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1530 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z + elem.rotation
1531 + orientation.AsDegrees(),
1532 -180, 180 );
1533
1534 modelSettings.m_Opacity = elem.body_opacity_3d;
1535
1536 footprint->Models().push_back( modelSettings );
1537 }
1538
1539 if( reader.GetRemainingBytes() != 0 )
1540 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1541}
1542
1543
1545{
1546 if( aElem.referencePoint.size() != 2 )
1547 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1548
1549 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1550
1551 if( klayer == UNDEFINED_LAYER )
1552 {
1553 if( m_reporter )
1554 {
1555 m_reporter->Report( wxString::Format(
1556 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1557 "It has been moved to KiCad layer Eco1_User." ), aElem.layer ),
1559 }
1560
1561 klayer = Eco1_User;
1562 }
1563
1564 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1565 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1566
1567 std::unique_ptr<PCB_DIM_ALIGNED> dimension = std::make_unique<PCB_DIM_ALIGNED>( m_board, PCB_DIM_ALIGNED_T );
1568
1569 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1570 dimension->SetLayer( klayer );
1571 dimension->SetStart( referencePoint0 );
1572
1573 if( referencePoint0 != aElem.xy1 )
1574 {
1584 VECTOR2I direction = aElem.xy1 - referencePoint0;
1585 VECTOR2I directionNormalVector = VECTOR2I( -direction.y, direction.x );
1586 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1587 SEG segm2( referencePoint1, referencePoint1 + direction );
1588 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1589
1590 if( !intersection )
1591 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1592
1593 dimension->SetEnd( *intersection );
1594
1595 int height = direction.EuclideanNorm();
1596
1597 if( ( direction.x > 0 || direction.y < 0 ) != ( aElem.angle >= 180.0 ) )
1598 height = -height;
1599
1600 dimension->SetHeight( height );
1601 }
1602 else
1603 {
1604 dimension->SetEnd( referencePoint1 );
1605 }
1606
1607 dimension->SetLineThickness( aElem.linewidth );
1608
1609 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
1610 dimension->SetPrefix( aElem.textprefix );
1611
1612 // Suffix normally (but not always) holds the units
1613 wxRegEx units( wxS( "(mm)|(in)|(mils)|(thou)|(')|(\")" ), wxRE_ADVANCED );
1614
1615 if( units.Matches( aElem.textsuffix ) )
1616 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX );
1617 else
1618 dimension->SetSuffix( aElem.textsuffix );
1619
1620 dimension->SetTextThickness( aElem.textlinewidth );
1621 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1622 dimension->SetItalic( aElem.textitalic );
1623
1624#if 0 // we don't currently support bold; map to thicker text
1625 dimension->Text().SetBold( aElem.textbold );
1626#else
1627 if( aElem.textbold )
1628 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1629#endif
1630
1631 switch( aElem.textunit )
1632 {
1633 case ALTIUM_UNIT::INCHES:
1634 dimension->SetUnits( EDA_UNITS::INCHES );
1635 break;
1636 case ALTIUM_UNIT::MILS:
1637 dimension->SetUnits( EDA_UNITS::MILS );
1638 break;
1639 case ALTIUM_UNIT::MILLIMETERS:
1640 case ALTIUM_UNIT::CENTIMETER:
1641 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1642 break;
1643 default:
1644 break;
1645 }
1646
1647 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1648}
1649
1650
1652{
1653 if( aElem.referencePoint.size() < 2 )
1654 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1655
1656 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1657
1658 if( klayer == UNDEFINED_LAYER )
1659 {
1660 if( m_reporter )
1661 {
1662 m_reporter->Report( wxString::Format(
1663 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1664 "It has been moved to KiCad layer Eco1_User." ),
1665 aElem.layer ), RPT_SEVERITY_INFO );
1666 }
1667
1668 klayer = Eco1_User;
1669 }
1670
1671 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1672 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1673
1674 std::unique_ptr<PCB_DIM_RADIAL> dimension = std::make_unique<PCB_DIM_RADIAL>( m_board );
1675
1676 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1677 dimension->SetLayer( klayer );
1678 dimension->SetStart( referencePoint0 );
1679 dimension->SetEnd( aElem.xy1 );
1680 dimension->SetLineThickness( aElem.linewidth );
1681 dimension->SetKeepTextAligned( false );
1682
1683 dimension->SetPrefix( aElem.textprefix );
1684
1685 // Suffix normally holds the units
1686 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1687 : DIM_UNITS_FORMAT::BARE_SUFFIX );
1688
1689 switch( aElem.textunit )
1690 {
1691 case ALTIUM_UNIT::INCHES:
1692 dimension->SetUnits( EDA_UNITS::INCHES );
1693 break;
1694 case ALTIUM_UNIT::MILS:
1695 dimension->SetUnits( EDA_UNITS::MILS );
1696 break;
1697 case ALTIUM_UNIT::MILLIMETERS:
1698 case ALTIUM_UNIT::CENTIMETER:
1699 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1700 break;
1701 default:
1702 break;
1703 }
1704
1705 if( aElem.textPoint.empty() )
1706 {
1707 if( m_reporter )
1708 {
1709 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1711 }
1712
1713 return;
1714 }
1715
1716 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1717 dimension->SetTextThickness( aElem.textlinewidth );
1718 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1719 dimension->SetItalic( aElem.textitalic );
1720
1721#if 0 // we don't currently support bold; map to thicker text
1722 dimension->SetBold( aElem.textbold );
1723#else
1724 if( aElem.textbold )
1725 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1726#endif
1727
1728 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1729 // close.
1730 dimension->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1731 dimension->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1732
1733 int yAdjust = dimension->GetTextBox().GetCenter().y - dimension->GetTextPos().y;
1734 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1735 dimension->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
1736
1737 m_radialDimensions.push_back( dimension.get() );
1738 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1739}
1740
1741
1743{
1744 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1745
1746 if( klayer == UNDEFINED_LAYER )
1747 {
1748 if( m_reporter )
1749 {
1750 wxString msg;
1751 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1752 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1754 }
1755
1756 klayer = Eco1_User;
1757 }
1758
1759 if( !aElem.referencePoint.empty() )
1760 {
1761 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1762
1763 // line
1764 VECTOR2I last = referencePoint0;
1765 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1766 {
1767 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1768
1769 shape->SetLayer( klayer );
1770 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1771 shape->SetStart( last );
1772 shape->SetEnd( aElem.referencePoint.at( i ) );
1773 last = aElem.referencePoint.at( i );
1774
1775 m_board->Add( shape.release(), ADD_MODE::APPEND );
1776 }
1777
1778 // arrow
1779 if( aElem.referencePoint.size() >= 2 )
1780 {
1781 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1782
1783 if( dirVec.x != 0 || dirVec.y != 0 )
1784 {
1785 double scaling = dirVec.EuclideanNorm() / aElem.arrowsize;
1786 VECTOR2I arrVec =
1787 VECTOR2I( KiROUND( dirVec.x / scaling ), KiROUND( dirVec.y / scaling ) );
1788 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1789
1790 {
1791 std::unique_ptr<PCB_SHAPE> shape1 =
1792 std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1793
1794 shape1->SetLayer( klayer );
1795 shape1->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1796 shape1->SetStart( referencePoint0 );
1797 shape1->SetEnd( referencePoint0 + arrVec );
1798
1799 m_board->Add( shape1.release(), ADD_MODE::APPEND );
1800 }
1801
1802 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1803
1804 {
1805 std::unique_ptr<PCB_SHAPE> shape2 =
1806 std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1807
1808 shape2->SetLayer( klayer );
1809 shape2->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1810 shape2->SetStart( referencePoint0 );
1811 shape2->SetEnd( referencePoint0 + arrVec );
1812
1813 m_board->Add( shape2.release(), ADD_MODE::APPEND );
1814 }
1815 }
1816 }
1817 }
1818
1819 if( aElem.textPoint.empty() )
1820 {
1821 if( m_reporter )
1822 {
1823 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1825 }
1826
1827 return;
1828 }
1829
1830 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
1831
1832 text->SetText( aElem.textformat );
1833 text->SetPosition( aElem.textPoint.at( 0 ) );
1834 text->SetLayer( klayer );
1835 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1836 text->SetTextThickness( aElem.textlinewidth );
1837 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1838 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1839
1840 m_board->Add( text.release(), ADD_MODE::APPEND );
1841}
1842
1843
1845{
1846 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1847
1848 if( klayer == UNDEFINED_LAYER )
1849 {
1850 if( m_reporter )
1851 {
1852 wxString msg;
1853 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1854 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1856 }
1857
1858 klayer = Eco1_User;
1859 }
1860
1861 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1862 {
1863 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1864
1865 shape->SetLayer( klayer );
1866 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1867 shape->SetStart( aElem.referencePoint.at( i ) );
1868 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1869
1870 m_board->Add( shape.release(), ADD_MODE::APPEND );
1871 }
1872}
1873
1874
1876{
1877 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1878
1879 if( klayer == UNDEFINED_LAYER )
1880 {
1881 if( m_reporter )
1882 {
1883 wxString msg;
1884 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1885 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1887 }
1888
1889 klayer = Eco1_User;
1890 }
1891
1892 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1893 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1894
1895 std::unique_ptr<PCB_DIM_CENTER> dimension = std::make_unique<PCB_DIM_CENTER>( m_board );
1896
1897 dimension->SetLayer( klayer );
1898 dimension->SetLineThickness( aElem.linewidth );
1899 dimension->SetStart( aElem.xy1 );
1900 dimension->SetEnd( aElem.xy1 + vec );
1901
1902 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1903}
1904
1905
1907 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1908{
1909 if( m_progressReporter )
1910 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1911
1912 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1913
1914 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1915 {
1916 checkpoint();
1917 ADIMENSION6 elem( reader );
1918
1919 switch( elem.kind )
1920 {
1921 case ALTIUM_DIMENSION_KIND::LINEAR:
1923 break;
1924 case ALTIUM_DIMENSION_KIND::ANGULAR:
1925 if( m_reporter )
1926 {
1928 wxString::Format( _( "Ignored Angular dimension (not yet supported)." ) ),
1930 }
1931 break;
1932 case ALTIUM_DIMENSION_KIND::RADIAL:
1934 break;
1935 case ALTIUM_DIMENSION_KIND::LEADER:
1937 break;
1938 case ALTIUM_DIMENSION_KIND::DATUM:
1939 if( m_reporter )
1940 {
1942 wxString::Format( _( "Ignored Datum dimension (not yet supported)." ) ),
1944 }
1945 // HelperParseDimensions6Datum( elem );
1946 break;
1947 case ALTIUM_DIMENSION_KIND::BASELINE:
1948 if( m_reporter )
1949 {
1951 wxString::Format( _( "Ignored Baseline dimension (not yet supported)." ) ),
1953 }
1954 break;
1955 case ALTIUM_DIMENSION_KIND::CENTER:
1957 break;
1958 case ALTIUM_DIMENSION_KIND::LINEAR_DIAMETER:
1959 if( m_reporter )
1960 {
1962 wxString::Format( _( "Ignored Linear dimension (not yet supported)." ) ),
1964 }
1965 break;
1966 case ALTIUM_DIMENSION_KIND::RADIAL_DIAMETER:
1967 if( m_reporter )
1968 {
1970 wxString::Format( _( "Ignored Radial dimension (not yet supported)." ) ),
1972 }
1973 break;
1974 default:
1975 if( m_reporter )
1976 {
1977 wxString msg;
1978 msg.Printf( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1980 }
1981 break;
1982 }
1983 }
1984
1985 if( reader.GetRemainingBytes() != 0 )
1986 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
1987}
1988
1989
1991 const CFB::COMPOUND_FILE_ENTRY* aEntry,
1992 const std::vector<std::string>& aRootDir )
1993{
1994 if( m_progressReporter )
1995 m_progressReporter->Report( _( "Loading 3D models..." ) );
1996
1997 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1998
1999 if( reader.GetRemainingBytes() == 0 )
2000 return;
2001
2002 int idx = 0;
2003 wxString invalidChars = wxFileName::GetForbiddenChars();
2004
2005 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2006 {
2007 checkpoint();
2008 AMODEL elem( reader );
2009
2010 std::vector<std::string> stepPath = aRootDir;
2011 stepPath.emplace_back( std::to_string( idx ) );
2012
2013 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii() &&
2014 wxString::npos == elem.name.find_first_of( invalidChars );
2015 wxString storageName = !validName ? wxString::Format( wxT( "model_%d" ), idx )
2016 : elem.name;
2017
2018 idx++;
2019
2020 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
2021
2022 if( stepEntry == nullptr )
2023 {
2024 if( m_reporter )
2025 {
2026 wxString msg;
2027 msg.Printf( _( "File not found: '%s'. 3D-model not imported." ),
2028 FormatPath( stepPath ) );
2030 }
2031
2032 continue;
2033 }
2034
2035 size_t stepSize = static_cast<size_t>( stepEntry->size );
2036 std::vector<char> stepContent( stepSize );
2037
2038 // read file into buffer
2039 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
2040 stepSize );
2041
2042 m_EmbeddedModels.insert( std::make_pair(
2043 elem.id, ALTIUM_EMBEDDED_MODEL_DATA( storageName, elem.rotation, elem.z_offset,
2044 std::move( stepContent ) ) ) );
2045 }
2046
2047 if( reader.GetRemainingBytes() != 0 )
2048 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
2049}
2050
2051
2053 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2054{
2055 if( m_progressReporter )
2056 m_progressReporter->Report( _( "Loading nets..." ) );
2057
2058 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2059
2060 wxASSERT( m_altiumToKicadNetcodes.empty() );
2061
2062 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2063 {
2064 checkpoint();
2065 ANET6 elem( reader );
2066
2067 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, 0 );
2068 m_board->Add( netInfo, ADD_MODE::APPEND );
2069
2070 // needs to be called after m_board->Add() as assign us the NetCode
2071 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
2072 }
2073
2074 if( reader.GetRemainingBytes() != 0 )
2075 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
2076}
2077
2079 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2080{
2081 if( m_progressReporter )
2082 m_progressReporter->Report( _( "Loading polygons..." ) );
2083
2084 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2085
2086 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2087 {
2088 checkpoint();
2089 APOLYGON6 elem( reader );
2090
2091 SHAPE_LINE_CHAIN linechain;
2093
2094 if( linechain.PointCount() < 3 )
2095 {
2096 // We have found multiple Altium files with polygon records containing nothing but two
2097 // coincident vertices. These polygons do not appear when opening the file in Altium.
2098 // https://gitlab.com/kicad/code/kicad/-/issues/8183
2099 // Also, polygons with less than 3 points are not supported in KiCad.
2100 //
2101 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
2102 // "points are required." ),
2103 // linechain.PointCount(),
2104 // elem.vertices.size() );
2105
2106 m_polygons.emplace_back( nullptr );
2107 continue;
2108 }
2109
2110 SHAPE_POLY_SET outline( linechain );
2111
2112 if( elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::SOLID )
2113 {
2114 // Altium "Hatched" or "None" polygon outlines have thickness, convert it to KiCad's representation.
2115 outline.Inflate( elem.trackwidth / 2, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS,
2116 ARC_HIGH_DEF, true );
2117 }
2118
2119 if( outline.OutlineCount() != 1 && m_reporter )
2120 {
2121 wxString msg;
2122 msg.Printf( _( "Polygon outline count is %d, expected 1." ), outline.OutlineCount() );
2123
2125 }
2126
2127 if( outline.OutlineCount() == 0 )
2128 continue;
2129
2130 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>(m_board);
2131 m_polygons.emplace_back(zone.get());
2132
2133 zone->SetNetCode( GetNetCode( elem.net ) );
2134 zone->SetPosition( elem.vertices.at( 0 ).position );
2135 zone->SetLocked( elem.locked );
2136 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
2137 zone->Outline()->AddOutline( outline.Outline( 0 ) );
2138
2139 HelperSetZoneLayers( *zone, elem.layer );
2140
2141 if( elem.pourindex > m_highest_pour_index )
2143
2144 const ARULE6* planeClearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
2145 const ARULE6* zoneClearanceRule = GetRule( ALTIUM_RULE_KIND::CLEARANCE,
2146 wxT( "PolygonClearance" ) );
2147 int planeLayers = 0;
2148 int signalLayers = 0;
2149 int clearance = 0;
2150
2151 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
2152 {
2153 LAYER_T layerType = m_board->GetLayerType( layer );
2154
2155 if( layerType == LT_POWER || layerType == LT_MIXED )
2156 planeLayers++;
2157
2158 if( layerType == LT_SIGNAL || layerType == LT_MIXED )
2159 signalLayers++;
2160 }
2161
2162 if( planeLayers > 0 && planeClearanceRule )
2163 clearance = std::max( clearance, planeClearanceRule->planeclearanceClearance );
2164
2165 if( signalLayers > 0 && zoneClearanceRule )
2166 clearance = std::max( clearance, zoneClearanceRule->clearanceGap );
2167
2168 if( clearance > 0 )
2169 zone->SetLocalClearance( clearance );
2170
2171 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
2172
2173 if( polygonConnectRule != nullptr )
2174 {
2175 switch( polygonConnectRule->polygonconnectStyle )
2176 {
2177 case ALTIUM_CONNECT_STYLE::DIRECT:
2178 zone->SetPadConnection( ZONE_CONNECTION::FULL );
2179 break;
2180
2181 case ALTIUM_CONNECT_STYLE::NONE:
2182 zone->SetPadConnection( ZONE_CONNECTION::NONE );
2183 break;
2184
2185 default:
2186 case ALTIUM_CONNECT_STYLE::RELIEF:
2187 zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
2188 break;
2189 }
2190
2191 // TODO: correct variables?
2192 zone->SetThermalReliefSpokeWidth(
2193 polygonConnectRule->polygonconnectReliefconductorwidth );
2194 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
2195
2196 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
2197 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
2198 }
2199
2200 if( IsAltiumLayerAPlane( elem.layer ) )
2201 {
2202 // outer zone will be set to priority 0 later.
2203 zone->SetAssignedPriority( 1 );
2204
2205 // check if this is the outer zone by simply comparing the BBOX
2206 const auto& outer_plane = m_outer_plane.find( elem.layer );
2207 if( outer_plane == m_outer_plane.end()
2208 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
2209 {
2210 m_outer_plane[elem.layer] = zone.get();
2211 }
2212 }
2213
2214 if( elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::SOLID
2215 && elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::UNKNOWN )
2216 {
2217 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
2218 zone->SetHatchThickness( elem.trackwidth );
2219
2220 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::NONE )
2221 {
2222 // use a small hack to get us only an outline (hopefully)
2223 const BOX2I& bbox = zone->GetBoundingBox();
2224 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
2225 }
2226 else
2227 {
2228 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
2229 }
2230
2231 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45 )
2232 zone->SetHatchOrientation( ANGLE_45 );
2233 }
2234
2235 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2237
2238 m_board->Add( zone.release(), ADD_MODE::APPEND );
2239 }
2240
2241 if( reader.GetRemainingBytes() != 0 )
2242 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
2243}
2244
2246 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2247{
2248 if( m_progressReporter )
2249 m_progressReporter->Report( _( "Loading rules..." ) );
2250
2251 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2252
2253 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2254 {
2255 checkpoint();
2256 ARULE6 elem( reader );
2257
2258 m_rules[elem.kind].emplace_back( elem );
2259 }
2260
2261 // sort rules by priority
2262 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
2263 {
2264 std::sort( val.second.begin(), val.second.end(),
2265 []( const ARULE6& lhs, const ARULE6& rhs )
2266 {
2267 return lhs.priority < rhs.priority;
2268 } );
2269 }
2270
2271 const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::CLEARANCE );
2272 const ARULE6* trackWidthRule = GetRuleDefault( ALTIUM_RULE_KIND::WIDTH );
2273 const ARULE6* routingViasRule = GetRuleDefault( ALTIUM_RULE_KIND::ROUTING_VIAS );
2274 const ARULE6* holeSizeRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_SIZE );
2275 const ARULE6* holeToHoleRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE );
2276
2277 if( clearanceRule )
2279
2280 if( trackWidthRule )
2281 {
2283 // TODO: construct a custom rule for preferredWidth and maxLimit values
2284 }
2285
2286 if( routingViasRule )
2287 {
2288 m_board->GetDesignSettings().m_ViasMinSize = routingViasRule->minWidth;
2290 }
2291
2292 if( holeSizeRule )
2293 {
2294 // TODO: construct a custom rule for minLimit / maxLimit values
2295 }
2296
2297 if( holeToHoleRule )
2299
2300 const ARULE6* soldermaskRule = GetRuleDefault( ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION );
2301 const ARULE6* pastemaskRule = GetRuleDefault( ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION );
2302
2303 if( soldermaskRule )
2305
2306 if( pastemaskRule )
2308
2309 if( reader.GetRemainingBytes() != 0 )
2310 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
2311}
2312
2314 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2315{
2316 if( m_progressReporter )
2317 m_progressReporter->Report( _( "Loading board regions..." ) );
2318
2319 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2320
2321 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2322 {
2323 checkpoint();
2324 AREGION6 elem( reader, false );
2325
2326 // TODO: implement?
2327 }
2328
2329 if( reader.GetRemainingBytes() != 0 )
2330 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
2331}
2332
2334 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2335{
2336 if( m_progressReporter )
2337 m_progressReporter->Report( _( "Loading polygons..." ) );
2338
2339 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2340
2341 /* TODO: use Header section of file */
2342 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2343 {
2344 checkpoint();
2345 AREGION6 elem( reader, true );
2346
2348 || elem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2349 {
2350 // TODO: implement all different types for footprints
2352 }
2353 else
2354 {
2355 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2356 ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
2357 }
2358 }
2359
2360 if( reader.GetRemainingBytes() != 0 )
2361 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
2362}
2363
2364
2366{
2367 if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2368 {
2370 }
2371 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2372 {
2373 SHAPE_LINE_CHAIN linechain;
2375
2376 if( linechain.PointCount() < 3 )
2377 {
2378 // We have found multiple Altium files with polygon records containing nothing but
2379 // two coincident vertices. These polygons do not appear when opening the file in
2380 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2381 // Also, polygons with less than 3 points are not supported in KiCad.
2382 return;
2383 }
2384
2385 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2386
2387 zone->SetIsRuleArea( true );
2388
2389 if( aElem.is_keepout )
2390 {
2392 }
2393 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2394 {
2395 zone->SetDoNotAllowCopperPour( true );
2396 zone->SetDoNotAllowVias( false );
2397 zone->SetDoNotAllowTracks( false );
2398 zone->SetDoNotAllowPads( false );
2399 zone->SetDoNotAllowFootprints( false );
2400 }
2401
2402 zone->SetPosition( aElem.outline.at( 0 ).position );
2403 zone->Outline()->AddOutline( linechain );
2404
2405 HelperSetZoneLayers( *zone, aElem.layer );
2406
2407 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2409
2410 m_board->Add( zone.release(), ADD_MODE::APPEND );
2411 }
2412 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2413 {
2414 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2415
2416 if( klayer == UNDEFINED_LAYER )
2417 {
2418 if( m_reporter )
2419 {
2420 wxString msg;
2421 msg.Printf( _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
2422 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
2424 }
2425
2426 klayer = Eco1_User;
2427 }
2428
2429 SHAPE_LINE_CHAIN linechain;
2431
2432 if( linechain.PointCount() < 3 )
2433 {
2434 // We have found multiple Altium files with polygon records containing nothing but
2435 // two coincident vertices. These polygons do not appear when opening the file in
2436 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2437 // Also, polygons with less than 3 points are not supported in KiCad.
2438 return;
2439 }
2440
2441 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2442
2443 shape->SetPolyShape( linechain );
2444 shape->SetFilled( false );
2445 shape->SetLayer( klayer );
2446 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2447
2448 m_board->Add( shape.release(), ADD_MODE::APPEND );
2449 }
2450 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2451 {
2452 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2453 {
2454 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2456 }
2457 }
2458 else if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2459 {
2461 }
2462 else
2463 {
2464 if( m_reporter )
2465 {
2466 wxString msg;
2467 msg.Printf( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2469 }
2470 }
2471}
2472
2473
2475 const AREGION6& aElem,
2476 const int aPrimitiveIndex )
2477{
2478 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2479 {
2480 SHAPE_LINE_CHAIN linechain;
2482
2483 if( linechain.PointCount() < 3 )
2484 {
2485 // We have found multiple Altium files with polygon records containing nothing but
2486 // two coincident vertices. These polygons do not appear when opening the file in
2487 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2488 // Also, polygons with less than 3 points are not supported in KiCad.
2489 return;
2490 }
2491
2492 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
2493
2494 zone->SetIsRuleArea( true );
2495
2496 if( aElem.is_keepout )
2497 {
2499 }
2500 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2501 {
2502 zone->SetDoNotAllowCopperPour( true );
2503 zone->SetDoNotAllowVias( false );
2504 zone->SetDoNotAllowTracks( false );
2505 zone->SetDoNotAllowPads( false );
2506 zone->SetDoNotAllowFootprints( false );
2507 }
2508
2509 zone->SetPosition( aElem.outline.at( 0 ).position );
2510 zone->Outline()->AddOutline( linechain );
2511
2512 HelperSetZoneLayers( *zone, aElem.layer );
2513
2514 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2516
2517 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
2518 }
2519 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2520 {
2521 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2522 {
2523 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2524 {
2525 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
2526 aPrimitiveIndex );
2527 }
2528 }
2529 }
2530 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE
2531 || aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2532 {
2533 PCB_LAYER_ID klayer = aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT
2534 ? Edge_Cuts
2535 : GetKicadLayer( aElem.layer );
2536
2537 if( klayer == UNDEFINED_LAYER )
2538 {
2539 if( !m_footprintName.IsEmpty() )
2540 {
2541 if( m_reporter )
2542 {
2543 wxString msg;
2544 msg.Printf( _( "Loading library '%s':\n"
2545 "Footprint %s contains a dashed outline on Altium layer (%d) with "
2546 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2547 m_library,
2549 aElem.layer );
2551 }
2552 }
2553 else
2554 {
2555 if( m_reporter )
2556 {
2557 wxString msg;
2558 msg.Printf( _( "Footprint %s contains a dashed outline on Altium layer (%d) with "
2559 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2560 aFootprint->GetReference(),
2561 aElem.layer );
2563 }
2564 }
2565
2566 klayer = Eco1_User;
2567 }
2568
2569 SHAPE_LINE_CHAIN linechain;
2571
2572 if( linechain.PointCount() < 3 )
2573 {
2574 // We have found multiple Altium files with polygon records containing nothing but
2575 // two coincident vertices. These polygons do not appear when opening the file in
2576 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2577 // Also, polygons with less than 3 points are not supported in KiCad.
2578 return;
2579 }
2580
2581 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2582
2583 shape->SetPolyShape( linechain );
2584 shape->SetFilled( false );
2585 shape->SetLayer( klayer );
2586
2587 if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2588 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2589 else
2590 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::SOLID ) );
2591
2592 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2593 }
2594 else
2595 {
2596 if( !m_footprintName.IsEmpty() )
2597 {
2598 if( m_reporter )
2599 {
2600 wxString msg;
2601 msg.Printf( _( "Error loading library '%s':\n"
2602 "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2603 m_library,
2605 aElem.kind );
2607 }
2608 }
2609 else
2610 {
2611 if( m_reporter )
2612 {
2613 wxString msg;
2614 msg.Printf( _( "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2615 aFootprint->GetReference(),
2616 aElem.kind );
2618 }
2619 }
2620 }
2621}
2622
2623
2625 PCB_LAYER_ID aLayer )
2626{
2627 SHAPE_LINE_CHAIN linechain;
2629
2630 if( linechain.PointCount() < 3 )
2631 {
2632 // We have found multiple Altium files with polygon records containing nothing
2633 // but two coincident vertices. These polygons do not appear when opening the
2634 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2635 // Also, polygons with less than 3 points are not supported in KiCad.
2636 return;
2637 }
2638
2639 SHAPE_POLY_SET polySet;
2640 polySet.AddOutline( linechain );
2641
2642 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2643 {
2644 SHAPE_LINE_CHAIN hole_linechain;
2645 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2646
2647 if( hole_linechain.PointCount() < 3 )
2648 continue;
2649
2650 polySet.AddHole( hole_linechain );
2651 }
2652
2653 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2654
2655 shape->SetPolyShape( polySet );
2656 shape->SetFilled( true );
2657 shape->SetLayer( aLayer );
2658 shape->SetStroke( STROKE_PARAMS( 0 ) );
2659
2660 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2661 {
2662 shape->SetNetCode( GetNetCode( aElem.net ) );
2663 }
2664
2665 m_board->Add( shape.release(), ADD_MODE::APPEND );
2666}
2667
2668
2670 const AREGION6& aElem,
2671 PCB_LAYER_ID aLayer,
2672 const int aPrimitiveIndex )
2673{
2674 SHAPE_LINE_CHAIN linechain;
2676
2677 if( linechain.PointCount() < 3 )
2678 {
2679 // We have found multiple Altium files with polygon records containing nothing
2680 // but two coincident vertices. These polygons do not appear when opening the
2681 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2682 // Also, polygons with less than 3 points are not supported in KiCad.
2683 return;
2684 }
2685
2686 SHAPE_POLY_SET polySet;
2687 polySet.AddOutline( linechain );
2688
2689 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2690 {
2691 SHAPE_LINE_CHAIN hole_linechain;
2692 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2693
2694 if( hole_linechain.PointCount() < 3 )
2695 continue;
2696
2697 polySet.AddHole( hole_linechain );
2698 }
2699
2700 if( aLayer == F_Cu || aLayer == B_Cu )
2701 {
2702 // TODO(JE) padstacks -- not sure what should happen here yet
2703 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
2704
2705 LSET padLayers;
2706 padLayers.set( aLayer );
2707
2708 pad->SetAttribute( PAD_ATTRIB::SMD );
2709 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CUSTOM );
2710 pad->SetThermalSpokeAngle( ANGLE_90 );
2711
2712 int anchorSize = 1;
2713 VECTOR2I anchorPos = linechain.CPoint( 0 );
2714
2715 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
2716 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
2717 pad->SetPosition( anchorPos );
2718
2719 SHAPE_POLY_SET shapePolys = polySet;
2720 shapePolys.Move( -anchorPos );
2721 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, shapePolys, 0, true );
2722
2723 auto& map = m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::REGION];
2724 auto it = map.find( aPrimitiveIndex );
2725
2726 if( it != map.end() )
2727 {
2728 const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
2729
2730 if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2731 {
2732 pad->SetLocalSolderPasteMargin(
2733 info.pastemaskexpansionmanual ? info.pastemaskexpansionmanual : 1 );
2734 }
2735
2736 if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2737 {
2738 pad->SetLocalSolderMaskMargin(
2739 info.soldermaskexpansionmanual ? info.soldermaskexpansionmanual : 1 );
2740 }
2741
2742 if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
2743 padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
2744
2745 if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
2746 padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
2747 }
2748
2749 pad->SetLayerSet( padLayers );
2750
2751 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
2752 }
2753 else
2754 {
2755 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2756
2757 shape->SetPolyShape( polySet );
2758 shape->SetFilled( true );
2759 shape->SetLayer( aLayer );
2760 shape->SetStroke( STROKE_PARAMS( 0 ) );
2761
2762 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2763 }
2764}
2765
2766
2768 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2769{
2770 if( m_progressReporter )
2771 m_progressReporter->Report( _( "Loading zone fills..." ) );
2772
2773 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2774
2775 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2776 {
2777 checkpoint();
2778 AREGION6 elem( reader, false );
2779
2780 if( elem.polygon != ALTIUM_POLYGON_NONE )
2781 {
2782 if( m_polygons.size() <= elem.polygon )
2783 {
2784 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2785 "of %d existing polygons.",
2786 elem.polygon,
2787 m_polygons.size() ) );
2788 }
2789
2790 ZONE* zone = m_polygons.at( elem.polygon );
2791
2792 if( zone == nullptr )
2793 {
2794 continue; // we know the zone id, but because we do not know the layer we did not
2795 // add it!
2796 }
2797
2798 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2799
2800 if( klayer == UNDEFINED_LAYER )
2801 continue; // Just skip it for now. Users can fill it themselves.
2802
2803 SHAPE_LINE_CHAIN linechain;
2804
2805 for( const ALTIUM_VERTICE& vertice : elem.outline )
2806 linechain.Append( vertice.position );
2807
2808 linechain.Append( elem.outline.at( 0 ).position );
2809 linechain.SetClosed( true );
2810
2811 SHAPE_POLY_SET fill;
2812 fill.AddOutline( linechain );
2813
2814 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2815 {
2816 SHAPE_LINE_CHAIN hole_linechain;
2817
2818 for( const ALTIUM_VERTICE& vertice : hole )
2819 hole_linechain.Append( vertice.position );
2820
2821 hole_linechain.Append( hole.at( 0 ).position );
2822 hole_linechain.SetClosed( true );
2823 fill.AddHole( hole_linechain );
2824 }
2825
2826 if( zone->HasFilledPolysForLayer( klayer ) )
2827 fill.BooleanAdd( *zone->GetFill( klayer ), SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
2828
2830
2831 zone->SetFilledPolysList( klayer, fill );
2832 zone->SetIsFilled( true );
2833 zone->SetNeedRefill( false );
2834 }
2835 }
2836
2837 if( reader.GetRemainingBytes() != 0 )
2838 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2839}
2840
2841
2843 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2844{
2845 if( m_progressReporter )
2846 m_progressReporter->Report( _( "Loading arcs..." ) );
2847
2848 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2849
2850 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2851 {
2852 checkpoint();
2853 AARC6 elem( reader );
2854
2855 if( elem.component == ALTIUM_COMPONENT_NONE )
2856 {
2857 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2858 }
2859 else
2860 {
2861 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2862 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2863 }
2864 }
2865
2866 if( reader.GetRemainingBytes() != 0 )
2867 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2868}
2869
2870
2872{
2873 if( aElem.startangle == 0. && aElem.endangle == 360. )
2874 {
2875 aShape->SetShape( SHAPE_T::CIRCLE );
2876
2877 // TODO: other variants to define circle?
2878 aShape->SetStart( aElem.center );
2879 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
2880 }
2881 else
2882 {
2883 aShape->SetShape( SHAPE_T::ARC );
2884
2885 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2886 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2887
2888 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2889 -KiROUND( startAngle.Sin() * aElem.radius ) );
2890
2891 aShape->SetCenter( aElem.center );
2892 aShape->SetStart( aElem.center + startOffset );
2893 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
2894 }
2895}
2896
2897
2898void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
2899{
2900 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
2901 {
2902 if( m_polygons.size() <= aElem.polygon )
2903 {
2904 THROW_IO_ERROR( wxString::Format( "Tracks stream tries to access polygon id %u "
2905 "of %zu existing polygons.",
2906 aElem.polygon, m_polygons.size() ) );
2907 }
2908
2909 ZONE* zone = m_polygons.at( aElem.polygon );
2910
2911 if( zone == nullptr )
2912 {
2913 return; // we know the zone id, but because we do not know the layer we did not
2914 // add it!
2915 }
2916
2917 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2918
2919 if( klayer == UNDEFINED_LAYER )
2920 return; // Just skip it for now. Users can fill it themselves.
2921
2922 if( !zone->HasFilledPolysForLayer( klayer ) )
2923 return;
2924
2925 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
2926
2927 // This is not the actual board item. We can use it to create the polygon for the region
2928 PCB_SHAPE shape( nullptr );
2929
2930 ConvertArcs6ToPcbShape( aElem, &shape );
2931 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2932
2933 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
2934 // Will be simplified and fractured later
2935
2936 zone->SetIsFilled( true );
2937 zone->SetNeedRefill( false );
2938
2939 return;
2940 }
2941
2942 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2943 || IsAltiumLayerAPlane( aElem.layer ) )
2944 {
2945 // This is not the actual board item. We can use it to create the polygon for the region
2946 PCB_SHAPE shape( nullptr );
2947
2948 ConvertArcs6ToPcbShape( aElem, &shape );
2949 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2950
2952 }
2953 else
2954 {
2955 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2956 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2957 }
2958
2959 for( const auto& layerExpansionMask :
2960 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
2961 {
2962 int width = aElem.width + ( layerExpansionMask.second * 2 );
2963
2964 if( width > 1 )
2965 {
2966 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
2967
2968 ConvertArcs6ToPcbShape( aElem, arc.get() );
2969 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
2970 arc->SetLayer( layerExpansionMask.first );
2971
2972 m_board->Add( arc.release(), ADD_MODE::APPEND );
2973 }
2974 }
2975}
2976
2977
2979 const int aPrimitiveIndex, const bool aIsBoardImport )
2980{
2981 if( aElem.polygon != ALTIUM_POLYGON_NONE )
2982 {
2983 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Arc with polygon id %d",
2984 aElem.polygon ) );
2985 return;
2986 }
2987
2988 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2989 || IsAltiumLayerAPlane( aElem.layer ) )
2990 {
2991 // This is not the actual board item. We can use it to create the polygon for the region
2992 PCB_SHAPE shape( nullptr );
2993
2994 ConvertArcs6ToPcbShape( aElem, &shape );
2995 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2996
2997 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
2998 aElem.keepoutrestrictions );
2999 }
3000 else
3001 {
3002 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3003 {
3004 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3005 {
3006 // Special case: do to not lose net connections in footprints
3007 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
3008 }
3009 else
3010 {
3011 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3012 }
3013 }
3014 }
3015
3016 for( const auto& layerExpansionMask :
3017 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
3018 {
3019 int width = aElem.width + ( layerExpansionMask.second * 2 );
3020
3021 if( width > 1 )
3022 {
3023 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3024
3025 ConvertArcs6ToPcbShape( aElem, arc.get() );
3026 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3027 arc->SetLayer( layerExpansionMask.first );
3028
3029 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3030 }
3031 }
3032}
3033
3034
3036{
3037 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3038 {
3039 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
3040 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
3041
3042 includedAngle.Normalize();
3043
3044 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
3045 -KiROUND( startAngle.Sin() * aElem.radius ) );
3046
3047 if( includedAngle.AsDegrees() >= 0.1 )
3048 {
3049 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
3050 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
3051
3052 shape.SetCenter( aElem.center );
3053 shape.SetStart( aElem.center + startOffset );
3054 shape.SetArcAngleAndEnd( includedAngle, true );
3055
3056 // Create actual arc
3057 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(),
3058 aElem.width );
3059 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board, &shapeArc );
3060
3061 arc->SetWidth( aElem.width );
3062 arc->SetLayer( aLayer );
3063 arc->SetNetCode( GetNetCode( aElem.net ) );
3064
3065 m_board->Add( arc.release(), ADD_MODE::APPEND );
3066 }
3067 }
3068 else
3069 {
3070 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>(m_board);
3071
3072 ConvertArcs6ToPcbShape( aElem, arc.get() );
3073 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3074 arc->SetLayer( aLayer );
3075
3076 m_board->Add( arc.release(), ADD_MODE::APPEND );
3077 }
3078}
3079
3080
3082 PCB_LAYER_ID aLayer )
3083{
3084 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
3085
3086 ConvertArcs6ToPcbShape( aElem, arc.get() );
3087 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3088 arc->SetLayer( aLayer );
3089
3090 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
3091}
3092
3093
3095 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3096{
3097 if( m_progressReporter )
3098 m_progressReporter->Report( _( "Loading pads..." ) );
3099
3100 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3101
3102 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3103 {
3104 checkpoint();
3105 APAD6 elem( reader );
3106
3107 if( elem.component == ALTIUM_COMPONENT_NONE )
3108 {
3110 }
3111 else
3112 {
3113 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3114 ConvertPads6ToFootprintItem( footprint, elem );
3115 }
3116 }
3117
3118 if( reader.GetRemainingBytes() != 0 )
3119 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
3120}
3121
3122
3124{
3125 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3126 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3127 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3128 {
3130 }
3131 else
3132 {
3133 // We cannot add a pad directly into the PCB
3134 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
3135 footprint->SetPosition( aElem.position );
3136
3137 ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem );
3138
3139 m_board->Add( footprint.release(), ADD_MODE::APPEND );
3140 }
3141}
3142
3143
3145{
3146 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3147
3148 pad->SetNumber( "" );
3149 pad->SetNetCode( GetNetCode( aElem.net ) );
3150
3151 pad->SetPosition( aElem.position );
3152 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( aElem.diameter, aElem.diameter ) );
3153 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3154 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3155 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
3156 pad->SetAttribute( PAD_ATTRIB::PTH );
3157
3158 // Pads are always through holes in KiCad
3159 pad->SetLayerSet( LSET().AllCuMask() );
3160
3161 if( aElem.viamode == ALTIUM_PAD_MODE::SIMPLE )
3162 {
3163 pad->Padstack().SetMode( PADSTACK::MODE::NORMAL );
3164 }
3165 else if( aElem.viamode == ALTIUM_PAD_MODE::TOP_MIDDLE_BOTTOM )
3166 {
3167 pad->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
3168 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[1], aElem.diameter_by_layer[1] ),
3170 }
3171 else
3172 {
3173 pad->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
3174 int altiumIdx = 0;
3175
3176 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, 32 ) )
3177 {
3178 pad->Padstack().SetSize( VECTOR2I( aElem.diameter_by_layer[altiumIdx],
3179 aElem.diameter_by_layer[altiumIdx] ), layer );
3180 altiumIdx++;
3181 }
3182 }
3183
3184 if( aElem.is_tent_top )
3185 {
3186 pad->Padstack().FrontOuterLayers().has_solder_mask = true;
3187 }
3188 else
3189 {
3190 pad->Padstack().FrontOuterLayers().has_solder_mask = false;
3191 pad->SetLayerSet( pad->GetLayerSet().set( F_Mask ) );
3192 }
3193
3194 if( aElem.is_tent_bottom )
3195 {
3196 pad->Padstack().BackOuterLayers().has_solder_mask = true;
3197 }
3198 else
3199 {
3200 pad->Padstack().BackOuterLayers().has_solder_mask = false;
3201 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ) );
3202 }
3203
3204 if( aElem.is_locked )
3205 pad->SetLocked( true );
3206
3207 if( aElem.soldermask_expansion_manual )
3208 {
3209 pad->Padstack().FrontOuterLayers().solder_mask_margin = aElem.soldermask_expansion_front;
3210 pad->Padstack().BackOuterLayers().solder_mask_margin = aElem.soldermask_expansion_back;
3211 }
3212
3213
3214 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3215}
3216
3217
3219{
3220 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3221 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3222 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3223 {
3224 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
3225 }
3226 else
3227 {
3228 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
3229 }
3230}
3231
3232
3234{
3235 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3236
3237 pad->SetNumber( aElem.name );
3238 pad->SetNetCode( GetNetCode( aElem.net ) );
3239
3240 pad->SetPosition( aElem.position );
3241 pad->SetOrientationDegrees( aElem.direction );
3242 pad->SetThermalSpokeAngle( ANGLE_90 );
3243
3244 if( aElem.holesize == 0 )
3245 {
3246 pad->SetAttribute( PAD_ATTRIB::SMD );
3247 }
3248 else
3249 {
3250 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3251 {
3252 // TODO: I assume other values are possible as well?
3253 if( !m_footprintName.IsEmpty() )
3254 {
3255 if( m_reporter )
3256 {
3257 wxString msg;
3258 msg.Printf( _( "Error loading library '%s':\n"
3259 "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3260 m_library,
3262 aElem.name );
3264 }
3265 }
3266 else
3267 {
3268 if( m_reporter )
3269 {
3270 wxString msg;
3271 msg.Printf( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3272 aFootprint->GetReference(),
3273 aElem.name );
3275 }
3276 }
3277 }
3278
3279 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
3280
3281 if( !aElem.sizeAndShape || aElem.sizeAndShape->holeshape == ALTIUM_PAD_HOLE_SHAPE::ROUND )
3282 {
3283 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3284 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3285 }
3286 else
3287 {
3288 switch( aElem.sizeAndShape->holeshape )
3289 {
3290 case ALTIUM_PAD_HOLE_SHAPE::ROUND:
3291 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
3292 break;
3293
3294 case ALTIUM_PAD_HOLE_SHAPE::SQUARE:
3295 if( !m_footprintName.IsEmpty() )
3296 {
3297 if( m_reporter )
3298 {
3299 wxString msg;
3300 msg.Printf( _( "Loading library '%s':\n"
3301 "Footprint %s pad %s has a square hole (not yet supported)." ),
3302 m_library,
3304 aElem.name );
3306 }
3307 }
3308 else
3309 {
3310 if( m_reporter )
3311 {
3312 wxString msg;
3313 msg.Printf( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
3314 aFootprint->GetReference(),
3315 aElem.name );
3317 }
3318 }
3319
3320 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3321 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3322 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
3323 // this case or rect holes have a different id
3324 break;
3325
3326 case ALTIUM_PAD_HOLE_SHAPE::SLOT:
3327 {
3328 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
3329 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
3330
3331 slotRotation.Normalize();
3332
3333 if( slotRotation.IsHorizontal() )
3334 {
3335 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
3336 }
3337 else if( slotRotation.IsVertical() )
3338 {
3339 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
3340 }
3341 else
3342 {
3343 if( !m_footprintName.IsEmpty() )
3344 {
3345 if( m_reporter )
3346 {
3347 wxString msg;
3348 msg.Printf( _( "Loading library '%s':\n"
3349 "Footprint %s pad %s has a hole-rotation of %f degrees. "
3350 "KiCad only supports 90 degree rotations." ),
3351 m_library,
3353 aElem.name,
3354 slotRotation.AsDegrees() );
3356 }
3357 }
3358 else
3359 {
3360 if( m_reporter )
3361 {
3362 wxString msg;
3363 msg.Printf( _( "Footprint %s pad %s has a hole-rotation of %f degrees. "
3364 "KiCad only supports 90 degree rotations." ),
3365 aFootprint->GetReference(),
3366 aElem.name,
3367 slotRotation.AsDegrees() );
3369 }
3370 }
3371 }
3372
3373 break;
3374 }
3375
3376 default:
3377 case ALTIUM_PAD_HOLE_SHAPE::UNKNOWN:
3378 if( !m_footprintName.IsEmpty() )
3379 {
3380 if( m_reporter )
3381 {
3382 wxString msg;
3383 msg.Printf( _( "Error loading library '%s':\n"
3384 "Footprint %s pad %s uses a hole of unknown kind %d." ),
3385 m_library,
3387 aElem.name,
3388 aElem.sizeAndShape->holeshape );
3390 }
3391 }
3392 else
3393 {
3394 if( m_reporter )
3395 {
3396 wxString msg;
3397 msg.Printf( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
3398 aFootprint->GetReference(),
3399 aElem.name,
3400 aElem.sizeAndShape->holeshape );
3402 }
3403 }
3404
3405 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3406 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3407 break;
3408 }
3409 }
3410
3411 if( aElem.sizeAndShape )
3412 pad->SetOffset( PADSTACK::ALL_LAYERS, aElem.sizeAndShape->holeoffset[0] );
3413 }
3414
3415 PADSTACK& ps = pad->Padstack();
3416
3417 auto setCopperGeometry =
3418 [&]( PCB_LAYER_ID aLayer, ALTIUM_PAD_SHAPE aShape, const VECTOR2I& aSize )
3419 {
3420 int altLayer = CopperLayerToOrdinal( aLayer );
3421
3422 ps.SetSize( aSize, aLayer );
3423
3424 switch( aShape )
3425 {
3426 case ALTIUM_PAD_SHAPE::RECT:
3427 ps.SetShape( PAD_SHAPE::RECTANGLE, aLayer );
3428 break;
3429
3430 case ALTIUM_PAD_SHAPE::CIRCLE:
3431 if( aElem.sizeAndShape
3432 && aElem.sizeAndShape->alt_shape[altLayer] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
3433 {
3434 ps.SetShape( PAD_SHAPE::ROUNDRECT, aLayer ); // 100 = round, 0 = rectangular
3435 double ratio = aElem.sizeAndShape->cornerradius[altLayer] / 200.;
3436 ps.SetRoundRectRadiusRatio( ratio, aLayer );
3437 }
3438 else if( aElem.topsize.x == aElem.topsize.y )
3439 {
3440 ps.SetShape( PAD_SHAPE::CIRCLE, aLayer );
3441 }
3442 else
3443 {
3444 ps.SetShape( PAD_SHAPE::OVAL, aLayer );
3445 }
3446
3447 break;
3448
3449 case ALTIUM_PAD_SHAPE::OCTAGONAL:
3450 ps.SetShape( PAD_SHAPE::CHAMFERED_RECT, aLayer );
3452 ps.SetChamferRatio( 0.25, aLayer );
3453 break;
3454
3455 case ALTIUM_PAD_SHAPE::UNKNOWN:
3456 default:
3457 if( !m_footprintName.IsEmpty() )
3458 {
3459 if( m_reporter )
3460 {
3461 wxString msg;
3462 msg.Printf( _( "Error loading library '%s':\n"
3463 "Footprint %s pad %s uses an unknown pad-shape." ),
3464 m_library,
3466 aElem.name );
3468 }
3469 }
3470 else
3471 {
3472 if( m_reporter )
3473 {
3474 wxString msg;
3475 msg.Printf( _( "Footprint %s pad %s uses an unknown pad-shape." ),
3476 aFootprint->GetReference(),
3477 aElem.name );
3479 }
3480 }
3481 break;
3482 }
3483 };
3484
3485 switch( aElem.padmode )
3486 {
3487 case ALTIUM_PAD_MODE::SIMPLE:
3489 setCopperGeometry( PADSTACK::ALL_LAYERS, aElem.topshape, aElem.topsize );
3490 break;
3491
3492 case ALTIUM_PAD_MODE::TOP_MIDDLE_BOTTOM:
3494 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3495 setCopperGeometry( PADSTACK::INNER_LAYERS, aElem.midshape, aElem.midsize );
3496 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3497 break;
3498
3499 case ALTIUM_PAD_MODE::FULL_STACK:
3501
3502 setCopperGeometry( F_Cu, aElem.topshape, aElem.topsize );
3503 setCopperGeometry( B_Cu, aElem.botshape, aElem.botsize );
3504 setCopperGeometry( In1_Cu, aElem.midshape, aElem.midsize );
3505
3506 if( aElem.sizeAndShape )
3507 {
3508 size_t i = 0;
3509
3511 {
3512 setCopperGeometry( layer, aElem.sizeAndShape->inner_shape[i],
3513 VECTOR2I( aElem.sizeAndShape->inner_size[i].x,
3514 aElem.sizeAndShape->inner_size[i].y ) );
3515 i++;
3516 }
3517 }
3518
3519 break;
3520 }
3521
3522 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
3523 {
3524 // KiCad likes NPTH pads to be the same size & shape as their holes
3525 pad->SetShape( PADSTACK::ALL_LAYERS, pad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE ? PAD_SHAPE::CIRCLE
3526 : PAD_SHAPE::OVAL );
3527 pad->SetSize( PADSTACK::ALL_LAYERS, pad->GetDrillSize() );
3528 }
3529
3530 switch( aElem.layer )
3531 {
3532 case ALTIUM_LAYER::TOP_LAYER:
3533 pad->SetLayer( F_Cu );
3534 pad->SetLayerSet( PAD::SMDMask() );
3535 break;
3536
3537 case ALTIUM_LAYER::BOTTOM_LAYER:
3538 pad->SetLayer( B_Cu );
3539 pad->SetLayerSet( PAD::SMDMask().Flip() );
3540 break;
3541
3542 case ALTIUM_LAYER::MULTI_LAYER:
3543 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
3544 break;
3545
3546 default:
3547 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3548 pad->SetLayer( klayer );
3549 pad->SetLayerSet( LSET( { klayer } ) );
3550 break;
3551 }
3552
3553 if( aElem.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
3554 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
3555
3556 if( aElem.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
3557 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
3558
3559 if( aElem.is_tent_top )
3560 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
3561
3562 if( aElem.is_tent_bottom )
3563 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
3564
3565 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3566}
3567
3568
3570{
3571 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3572
3573 if( klayer == UNDEFINED_LAYER )
3574 {
3575 if( m_reporter )
3576 {
3577 wxString msg;
3578 msg.Printf( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
3579 "equivalent. It has been moved to KiCad layer Eco1_User." ),
3580 aElem.name, aElem.layer );
3582 }
3583
3584 klayer = Eco1_User;
3585 }
3586
3587 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( m_board );
3588
3589 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3590
3591 m_board->Add( pad.release(), ADD_MODE::APPEND );
3592}
3593
3594
3596{
3597 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3598
3599 if( klayer == UNDEFINED_LAYER )
3600 {
3601 if( !m_footprintName.IsEmpty() )
3602 {
3603 if( m_reporter )
3604 {
3605 wxString msg;
3606 msg.Printf( _( "Loading library '%s':\n"
3607 "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3608 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3609 m_library,
3611 aElem.name,
3612 aElem.layer );
3614 }
3615 }
3616 else
3617 {
3618 if( m_reporter )
3619 {
3620 wxString msg;
3621 msg.Printf( _( "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3622 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3623 aFootprint->GetReference(),
3624 aElem.name,
3625 aElem.layer );
3627 }
3628 }
3629
3630 klayer = Eco1_User;
3631 }
3632
3633 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( aFootprint );
3634
3635 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3636
3637 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3638}
3639
3640
3642 PCB_SHAPE* aShape )
3643{
3644 if( aElem.net != ALTIUM_NET_UNCONNECTED )
3645 {
3646 if( m_reporter )
3647 {
3648 wxString msg;
3649 msg.Printf( _( "Non-copper pad %s is connected to a net, which is not supported." ),
3650 aElem.name );
3652 }
3653 }
3654
3655 if( aElem.holesize != 0 )
3656 {
3657 if( m_reporter )
3658 {
3659 wxString msg;
3660 msg.Printf( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
3662 }
3663 }
3664
3665 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3666 {
3667 if( m_reporter )
3668 {
3669 wxString msg;
3670 msg.Printf( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
3671 aElem.name );
3673 }
3674 }
3675
3676 switch( aElem.topshape )
3677 {
3678 case ALTIUM_PAD_SHAPE::RECT:
3679 {
3680 // filled rect
3681 aShape->SetShape( SHAPE_T::POLY );
3682 aShape->SetFilled( true );
3683 aShape->SetLayer( aLayer );
3684 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3685
3686 aShape->SetPolyPoints(
3687 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
3688 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3689 aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3690 aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
3691
3692 if( aElem.direction != 0 )
3693 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3694 }
3695 break;
3696
3697 case ALTIUM_PAD_SHAPE::CIRCLE:
3698 if( aElem.sizeAndShape
3699 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
3700 {
3701 // filled roundrect
3702 int cornerradius = aElem.sizeAndShape->cornerradius[0];
3703 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
3704
3705 aShape->SetLayer( aLayer );
3706 aShape->SetStroke( STROKE_PARAMS( offset * 2, LINE_STYLE::SOLID ) );
3707
3708 if( cornerradius < 100 )
3709 {
3710 int offsetX = aElem.topsize.x / 2 - offset;
3711 int offsetY = aElem.topsize.y / 2 - offset;
3712
3713 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
3714 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
3715 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
3716 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
3717
3718 aShape->SetShape( SHAPE_T::POLY );
3719 aShape->SetFilled( true );
3720 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
3721 }
3722 else if( aElem.topsize.x == aElem.topsize.y )
3723 {
3724 // circle
3725 aShape->SetShape( SHAPE_T::CIRCLE );
3726 aShape->SetFilled( true );
3727 aShape->SetStart( aElem.position );
3728 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3729 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3730 }
3731 else if( aElem.topsize.x < aElem.topsize.y )
3732 {
3733 // short vertical line
3734 aShape->SetShape( SHAPE_T::SEGMENT );
3735 VECTOR2I pointOffset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3736 aShape->SetStart( aElem.position + pointOffset );
3737 aShape->SetEnd( aElem.position - pointOffset );
3738 }
3739 else
3740 {
3741 // short horizontal line
3742 aShape->SetShape( SHAPE_T::SEGMENT );
3743 VECTOR2I pointOffset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3744 aShape->SetStart( aElem.position + pointOffset );
3745 aShape->SetEnd( aElem.position - pointOffset );
3746 }
3747
3748 if( aElem.direction != 0 )
3749 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3750 }
3751 else if( aElem.topsize.x == aElem.topsize.y )
3752 {
3753 // filled circle
3754 aShape->SetShape( SHAPE_T::CIRCLE );
3755 aShape->SetFilled( true );
3756 aShape->SetLayer( aLayer );
3757 aShape->SetStart( aElem.position );
3758 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3759 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3760 }
3761 else
3762 {
3763 // short line
3764 aShape->SetShape( SHAPE_T::SEGMENT );
3765 aShape->SetLayer( aLayer );
3766 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
3767 LINE_STYLE::SOLID ) );
3768
3769 if( aElem.topsize.x < aElem.topsize.y )
3770 {
3771 VECTOR2I offset( 0, ( aElem.topsize.y / 2 - aElem.topsize.x / 2 ) );
3772 aShape->SetStart( aElem.position + offset );
3773 aShape->SetEnd( aElem.position - offset );
3774 }
3775 else
3776 {
3777 VECTOR2I offset( ( aElem.topsize.x / 2 - aElem.topsize.y / 2 ), 0 );
3778 aShape->SetStart( aElem.position + offset );
3779 aShape->SetEnd( aElem.position - offset );
3780 }
3781
3782 if( aElem.direction != 0 )
3783 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3784 }
3785 break;
3786
3787 case ALTIUM_PAD_SHAPE::OCTAGONAL:
3788 {
3789 // filled octagon
3790 aShape->SetShape( SHAPE_T::POLY );
3791 aShape->SetFilled( true );
3792 aShape->SetLayer( aLayer );
3793 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3794
3795 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
3796 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3797 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3798 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
3799
3800 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
3801 VECTOR2I chamferX( chamfer, 0 );
3802 VECTOR2I chamferY( 0, chamfer );
3803
3804 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
3805 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
3806
3807 if( aElem.direction != 0. )
3808 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3809 }
3810 break;
3811
3812 case ALTIUM_PAD_SHAPE::UNKNOWN:
3813 default:
3814 if( m_reporter )
3815 {
3816 wxString msg;
3817 msg.Printf( _( "Non-copper pad %s uses an unknown pad-shape." ), aElem.name );
3819 }
3820
3821 break;
3822 }
3823}
3824
3825
3827 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3828{
3829 if( m_progressReporter )
3830 m_progressReporter->Report( _( "Loading vias..." ) );
3831
3832 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3833
3834 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3835 {
3836 checkpoint();
3837 AVIA6 elem( reader );
3838
3839 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
3840
3841 via->SetPosition( elem.position );
3842 via->SetDrill( elem.holesize );
3843 via->SetNetCode( GetNetCode( elem.net ) );
3844 via->SetLocked( elem.is_locked );
3845
3846 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
3847 || elem.layer_start == ALTIUM_LAYER::BOTTOM_LAYER;
3848 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
3849 || elem.layer_end == ALTIUM_LAYER::BOTTOM_LAYER;
3850
3851 if( start_layer_outside && end_layer_outside )
3852 {
3853 via->SetViaType( VIATYPE::THROUGH );
3854 }
3855 else if( ( !start_layer_outside ) || ( !end_layer_outside ) )
3856 {
3857 via->SetViaType( VIATYPE::BLIND_BURIED );
3858 }
3859
3860 // TODO: Altium has a specific flag for microvias, independent of start/end layer
3861#if 0
3862 if( something )
3863 via->SetViaType( VIATYPE::MICROVIA );
3864#endif
3865
3866 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
3867 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
3868
3869 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
3870 {
3871 if( m_reporter )
3872 {
3873 wxString msg;
3874 msg.Printf( _( "Via from layer %d to %d uses a non-copper layer, which is not "
3875 "supported." ),
3876 elem.layer_start,
3877 elem.layer_end );
3879 }
3880
3881 continue; // just assume through-hole instead.
3882 }
3883
3884 // we need VIATYPE set!
3885 via->SetLayerPair( start_klayer, end_klayer );
3886
3887 switch( elem.viamode )
3888 {
3889 default:
3890 case ALTIUM_PAD_MODE::SIMPLE:
3891 via->SetWidth( PADSTACK::ALL_LAYERS, elem.diameter );
3892 break;
3893
3894 case ALTIUM_PAD_MODE::TOP_MIDDLE_BOTTOM:
3895 via->Padstack().SetMode( PADSTACK::MODE::FRONT_INNER_BACK );
3896 via->SetWidth( F_Cu, elem.diameter_by_layer[0] );
3897 via->SetWidth( PADSTACK::INNER_LAYERS, elem.diameter_by_layer[1] );
3898 via->SetWidth( B_Cu, elem.diameter_by_layer[31] );
3899 break;
3900
3901 case ALTIUM_PAD_MODE::FULL_STACK:
3902 {
3903 via->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
3904
3905 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, MAX_CU_LAYERS ) )
3906 {
3907 int altiumLayer = CopperLayerToOrdinal( layer );
3908 wxCHECK2_MSG( altiumLayer < 32, break,
3909 "Altium importer expects 32 or fewer copper layers" );
3910
3911 via->SetWidth( layer, elem.diameter_by_layer[altiumLayer] );
3912 }
3913
3914 break;
3915 }
3916 }
3917
3919 {
3920 via->SetFrontTentingMode( elem.is_tent_top ? TENTING_MODE::TENTED
3921 : TENTING_MODE::NOT_TENTED );
3922 via->SetBackTentingMode( elem.is_tent_bottom ? TENTING_MODE::TENTED
3923 : TENTING_MODE::NOT_TENTED );
3924 }
3925
3926 m_board->Add( via.release(), ADD_MODE::APPEND );
3927 }
3928
3929 if( reader.GetRemainingBytes() != 0 )
3930 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
3931}
3932
3934 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3935{
3936 if( m_progressReporter )
3937 m_progressReporter->Report( _( "Loading tracks..." ) );
3938
3939 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3940
3941 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
3942 {
3943 checkpoint();
3944 ATRACK6 elem( reader );
3945
3946 if( elem.component == ALTIUM_COMPONENT_NONE )
3947 {
3948 ConvertTracks6ToBoardItem( elem, primitiveIndex );
3949 }
3950 else
3951 {
3952 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3953 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
3954 }
3955 }
3956
3957 if( reader.GetRemainingBytes() != 0 )
3958 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
3959}
3960
3961
3962void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
3963{
3964 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
3965 {
3966 if( m_polygons.size() <= aElem.polygon )
3967 {
3968 // Can happen when reading old Altium files: just skip this item
3969 if( m_reporter )
3970 {
3971 wxString msg;
3972 msg.Printf( wxT( "ATRACK6 stream tries to access polygon id %u "
3973 "of %u existing polygons; skipping it" ),
3974 static_cast<unsigned>( aElem.polygon ),
3975 static_cast<unsigned>( m_polygons.size() ) );
3977 }
3978
3979 return;
3980 }
3981
3982 ZONE* zone = m_polygons.at( aElem.polygon );
3983
3984 if( zone == nullptr )
3985 {
3986 return; // we know the zone id, but because we do not know the layer we did not
3987 // add it!
3988 }
3989
3990 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3991
3992 if( klayer == UNDEFINED_LAYER )
3993 return; // Just skip it for now. Users can fill it themselves.
3994
3995 if( !zone->HasFilledPolysForLayer( klayer ) )
3996 return;
3997
3998 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
3999
4000 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4001 shape.SetStart( aElem.start );
4002 shape.SetEnd( aElem.end );
4003 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4004
4005 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
4006 // Will be simplified and fractured later
4007
4008 zone->SetIsFilled( true );
4009 zone->SetNeedRefill( false );
4010
4011 return;
4012 }
4013
4014 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4015 || IsAltiumLayerAPlane( aElem.layer ) )
4016 {
4017 // This is not the actual board item. We can use it to create the polygon for the region
4018 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4019 shape.SetStart( aElem.start );
4020 shape.SetEnd( aElem.end );
4021 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4022
4024 }
4025 else
4026 {
4027 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4028 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4029 }
4030
4031 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4032 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4033 {
4034 int width = aElem.width + ( layerExpansionMask.second * 2 );
4035 if( width > 1 )
4036 {
4037 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4038
4039 seg->SetStart( aElem.start );
4040 seg->SetEnd( aElem.end );
4041 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4042 seg->SetLayer( layerExpansionMask.first );
4043
4044 m_board->Add( seg.release(), ADD_MODE::APPEND );
4045 }
4046 }
4047}
4048
4049
4051 const int aPrimitiveIndex,
4052 const bool aIsBoardImport )
4053{
4054 if( aElem.polygon != ALTIUM_POLYGON_NONE )
4055 {
4056 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Track with polygon id %u",
4057 (unsigned)aElem.polygon ) );
4058 return;
4059 }
4060
4061 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
4062 || IsAltiumLayerAPlane( aElem.layer ) )
4063 {
4064 // This is not the actual board item. We can use it to create the polygon for the region
4065 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
4066 shape.SetStart( aElem.start );
4067 shape.SetEnd( aElem.end );
4068 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4069
4070 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4071 aElem.keepoutrestrictions );
4072 }
4073 else
4074 {
4075 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4076 {
4077 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4078 {
4079 // Special case: do to not lose net connections in footprints
4080 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
4081 }
4082 else
4083 {
4084 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4085 }
4086 }
4087 }
4088
4089 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
4090 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
4091 {
4092 int width = aElem.width + ( layerExpansionMask.second * 2 );
4093 if( width > 1 )
4094 {
4095 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4096
4097 seg->SetStart( aElem.start );
4098 seg->SetEnd( aElem.end );
4099 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
4100 seg->SetLayer( layerExpansionMask.first );
4101
4102 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4103 }
4104 }
4105}
4106
4107
4109{
4110 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4111 {
4112 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
4113
4114 track->SetStart( aElem.start );
4115 track->SetEnd( aElem.end );
4116 track->SetWidth( aElem.width );
4117 track->SetLayer( aLayer );
4118 track->SetNetCode( GetNetCode( aElem.net ) );
4119
4120 m_board->Add( track.release(), ADD_MODE::APPEND );
4121 }
4122 else
4123 {
4124 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
4125
4126 seg->SetStart( aElem.start );
4127 seg->SetEnd( aElem.end );
4128 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4129 seg->SetLayer( aLayer );
4130
4131 m_board->Add( seg.release(), ADD_MODE::APPEND );
4132 }
4133}
4134
4135
4137 PCB_LAYER_ID aLayer )
4138{
4139 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
4140
4141 seg->SetStart( aElem.start );
4142 seg->SetEnd( aElem.end );
4143 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
4144 seg->SetLayer( aLayer );
4145
4146 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
4147}
4148
4149
4151 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4152{
4153 if( m_progressReporter )
4154 m_progressReporter->Report( _( "Loading unicode strings..." ) );
4155
4156 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4157
4159
4160 if( reader.GetRemainingBytes() != 0 )
4161 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
4162}
4163
4165 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4166{
4167 if( m_progressReporter )
4168 m_progressReporter->Report( _( "Loading text..." ) );
4169
4170 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4171
4172 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4173 {
4174 checkpoint();
4175 ATEXT6 elem( reader, m_unicodeStrings );
4176
4177 if( elem.component == ALTIUM_COMPONENT_NONE )
4178 {
4180 }
4181 else
4182 {
4183 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4184 ConvertTexts6ToFootprintItem( footprint, elem );
4185 }
4186 }
4187
4188 if( reader.GetRemainingBytes() != 0 )
4189 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
4190}
4191
4192
4194{
4195 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4196 {
4197 if( m_reporter )
4198 {
4199 wxString msg;
4200 msg.Printf( _( "Ignored barcode on Altium layer %d (not yet supported)." ),
4201 aElem.layer );
4203 }
4204
4205 return;
4206 }
4207
4208 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4209 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
4210}
4211
4212
4214{
4215 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
4216 {
4217 if( !m_footprintName.IsEmpty() )
4218 {
4219 if( m_reporter )
4220 {
4221 wxString msg;
4222 msg.Printf( _( "Error loading library '%s':\n"
4223 "Footprint %s contains barcode on Altium layer %d (not yet supported)." ),
4224 m_library,
4226 aElem.layer );
4228 }
4229 }
4230 else
4231 {
4232 if( m_reporter )
4233 {
4234 wxString msg;
4235 msg.Printf( _( "Footprint %s contains barcode on Altium layer %d (not yet supported)." ),
4236 aFootprint->GetReference(),
4237 aElem.layer );
4239 }
4240 }
4241
4242 return;
4243 }
4244
4245 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4246 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4247}
4248
4249
4251{
4252 std::unique_ptr<PCB_TEXTBOX> pcbTextbox = std::make_unique<PCB_TEXTBOX>( m_board );
4253 std::unique_ptr<PCB_TEXT> pcbText = std::make_unique<PCB_TEXT>( m_board );
4254
4255 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4256
4257 static const std::map<wxString, wxString> variableMap = {
4258 { "LAYER_NAME", "LAYER" },
4259 { "PRINT_DATE", "CURRENT_DATE"},
4260 };
4261
4262 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4263 BOARD_ITEM* item = pcbText.get();
4264 EDA_TEXT* text = pcbText.get();
4265 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4266
4267 if( isTextbox )
4268 {
4269 item = pcbTextbox.get();
4270 text = pcbTextbox.get();
4271
4273 HelperSetTextboxAlignmentAndPos( aElem, pcbTextbox.get() );
4274 }
4275 else
4276 {
4279 }
4280
4281 text->SetText( kicadText );
4282 item->SetLayer( aLayer );
4283 item->SetIsKnockout( aElem.isInverted );
4284
4285 if( isTextbox )
4286 m_board->Add( pcbTextbox.release(), ADD_MODE::APPEND );
4287 else
4288 m_board->Add( pcbText.release(), ADD_MODE::APPEND );
4289}
4290
4291
4293 PCB_LAYER_ID aLayer )
4294{
4295 std::unique_ptr<PCB_TEXTBOX> fpTextbox = std::make_unique<PCB_TEXTBOX>( aFootprint );
4296 std::unique_ptr<PCB_TEXT> fpText = std::make_unique<PCB_TEXT>( aFootprint );
4297
4298 BOARD_ITEM* item = fpText.get();
4299 EDA_TEXT* text = fpText.get();
4300 PCB_FIELD* field = nullptr;
4301
4302 bool isTextbox = aElem.isFrame && !aElem.isInverted; // Textbox knockout is not supported
4303 bool toAdd = false;
4304
4305 if( aElem.isDesignator )
4306 {
4307 item = &aFootprint->Reference(); // TODO: handle multiple layers
4308 text = &aFootprint->Reference();
4309 field = &aFootprint->Reference();
4310 }
4311 else if( aElem.isComment )
4312 {
4313 item = &aFootprint->Value(); // TODO: handle multiple layers
4314 text = &aFootprint->Value();
4315 field = &aFootprint->Value();
4316 }
4317 else
4318 {
4319 item = fpText.get();
4320 text = fpText.get();
4321 toAdd = true;
4322 }
4323
4324 static const std::map<wxString, wxString> variableMap = {
4325 { "DESIGNATOR", "REFERENCE" },
4326 { "COMMENT", "VALUE" },
4327 { "VALUE", "ALTIUM_VALUE" },
4328 { "LAYER_NAME", "LAYER" },
4329 { "PRINT_DATE", "CURRENT_DATE"},
4330 };
4331
4332 if( isTextbox )
4333 {
4334 item = fpTextbox.get();
4335 text = fpTextbox.get();
4336
4338 HelperSetTextboxAlignmentAndPos( aElem, fpTextbox.get() );
4339 }
4340 else
4341 {
4344 }
4345
4346 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4347
4348 text->SetText( kicadText );
4349 text->SetKeepUpright( false );
4350 item->SetLayer( aLayer );
4351 item->SetIsKnockout( aElem.isInverted );
4352
4353 if( toAdd )
4354 {
4355 if( isTextbox )
4356 aFootprint->Add( fpTextbox.release(), ADD_MODE::APPEND );
4357 else
4358 aFootprint->Add( fpText.release(), ADD_MODE::APPEND );
4359 }
4360}
4361
4362
4364{
4365 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4366
4367 // Altium textboxes do not have borders
4368 aTextbox->SetBorderEnabled( false );
4369
4370 // Calculate position
4371 VECTOR2I kposition = aElem.position;
4372
4373 if( aElem.isMirrored )
4374 kposition.x -= aElem.textbox_rect_width;
4375
4376 kposition.y -= aElem.textbox_rect_height;
4377
4378#if 0
4379 // Compensate for KiCad's textbox margin
4380 int charWidth = aTextbox->GetTextWidth();
4381 int charHeight = aTextbox->GetTextHeight();
4382
4383 VECTOR2I kicadMargin;
4384
4385 if( !aTextbox->GetFont() || aTextbox->GetFont()->IsStroke() )
4386 kicadMargin = VECTOR2I( charWidth * 0.933, charHeight * 0.67 );
4387 else
4388 kicadMargin = VECTOR2I( charWidth * 0.808, charHeight * 0.844 );
4389
4390 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height )
4391 + kicadMargin * 2 - margin * 2 );
4392
4393 kposition = kposition - kicadMargin + margin;
4394#else
4395 aTextbox->SetMarginBottom( margin );
4396 aTextbox->SetMarginLeft( margin );
4397 aTextbox->SetMarginRight( margin );
4398 aTextbox->SetMarginTop( margin );
4399
4400 aTextbox->SetEnd( VECTOR2I( aElem.textbox_rect_width, aElem.textbox_rect_height ) );
4401#endif
4402
4403 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4404
4405 aTextbox->SetPosition( kposition );
4406
4407 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4409 : ALTIUM_TEXT_POSITION::LEFT_BOTTOM;
4410
4411 switch( justification )
4412 {
4413 case ALTIUM_TEXT_POSITION::LEFT_TOP:
4414 case ALTIUM_TEXT_POSITION::LEFT_CENTER:
4415 case ALTIUM_TEXT_POSITION::LEFT_BOTTOM:
4418 break;
4419 case ALTIUM_TEXT_POSITION::CENTER_TOP:
4420 case ALTIUM_TEXT_POSITION::CENTER_CENTER:
4421 case ALTIUM_TEXT_POSITION::CENTER_BOTTOM:
4424 break;
4425 case ALTIUM_TEXT_POSITION::RIGHT_TOP:
4426 case ALTIUM_TEXT_POSITION::RIGHT_CENTER:
4427 case ALTIUM_TEXT_POSITION::RIGHT_BOTTOM:
4430 break;
4431 default:
4432 if( m_reporter )
4433 {
4434 wxString msg;
4435 msg.Printf( _( "Unknown textbox justification %d, aText %s" ), justification,
4436 aElem.text );
4438 }
4439
4442 break;
4443 }
4444
4445 aTextbox->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4446}
4447
4448
4450{
4451 VECTOR2I kposition = aElem.position;
4452
4453 int margin = aElem.isOffsetBorder ? aElem.text_offset_width : aElem.margin_border_width;
4454 int rectWidth = aElem.textbox_rect_width - margin * 2;
4455 int rectHeight = aElem.height;
4456
4457 if( aElem.isMirrored )
4458 rectWidth = -rectWidth;
4459
4460 ALTIUM_TEXT_POSITION justification = aElem.isJustificationValid
4462 : ALTIUM_TEXT_POSITION::LEFT_BOTTOM;
4463
4464 switch( justification )
4465 {
4466 case ALTIUM_TEXT_POSITION::LEFT_TOP:
4469
4470 kposition.y -= rectHeight;
4471 break;
4472 case ALTIUM_TEXT_POSITION::LEFT_CENTER:
4475
4476 kposition.y -= rectHeight / 2;
4477 break;
4478 case ALTIUM_TEXT_POSITION::LEFT_BOTTOM:
4481 break;
4482 case ALTIUM_TEXT_POSITION::CENTER_TOP:
4485
4486 kposition.x += rectWidth / 2;
4487 kposition.y -= rectHeight;
4488 break;
4489 case ALTIUM_TEXT_POSITION::CENTER_CENTER:
4492
4493 kposition.x += rectWidth / 2;
4494 kposition.y -= rectHeight / 2;
4495 break;
4496 case ALTIUM_TEXT_POSITION::CENTER_BOTTOM:
4499
4500 kposition.x += rectWidth / 2;
4501 break;
4502 case ALTIUM_TEXT_POSITION::RIGHT_TOP:
4505
4506 kposition.x += rectWidth;
4507 kposition.y -= rectHeight;
4508 break;
4509 case ALTIUM_TEXT_POSITION::RIGHT_CENTER:
4512
4513 kposition.x += rectWidth;
4514 kposition.y -= rectHeight / 2;
4515 break;
4516 case ALTIUM_TEXT_POSITION::RIGHT_BOTTOM:
4519
4520 kposition.x += rectWidth;
4521 break;
4522 default:
4525 break;
4526 }
4527
4528 int charWidth = aText->GetTextWidth();
4529 int charHeight = aText->GetTextHeight();
4530
4531 // Correct for KiCad's baseline offset.
4532 // Text height and font must be set correctly before calling.
4533 if( !aText->GetFont() || aText->GetFont()->IsStroke() )
4534 {
4535 switch( aText->GetVertJustify() )
4536 {
4537 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charHeight * 0.0407; break;
4538 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charHeight * 0.0355; break;
4539 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charHeight * 0.1225; break;
4540 default: break;
4541 }
4542 }
4543 else
4544 {
4545 switch( aText->GetVertJustify() )
4546 {
4547 case GR_TEXT_V_ALIGN_TOP: kposition.y -= charWidth * 0.016; break;
4548 case GR_TEXT_V_ALIGN_CENTER: kposition.y += charWidth * 0.085; break;
4549 case GR_TEXT_V_ALIGN_BOTTOM: kposition.y += charWidth * 0.17; break;
4550 default: break;
4551 }
4552 }
4553
4554 RotatePoint( kposition, aElem.position, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4555
4556 aText->SetTextPos( kposition );
4557 aText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4558}
4559
4560
4562{
4563 aEdaText.SetTextSize( VECTOR2I( aElem.height, aElem.height ) );
4564
4565 if( aElem.fonttype == ALTIUM_TEXT_TYPE::TRUETYPE )
4566 {
4567 KIFONT::FONT* font = KIFONT::FONT::GetFont( aElem.fontname, aElem.isBold, aElem.isItalic );
4568 aEdaText.SetFont( font );
4569
4570 if( font->IsOutline() )
4571 {
4572 // TODO: why is this required? Somehow, truetype size is calculated differently
4573 if( font->GetName().Contains( wxS( "Arial" ) ) )
4574 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.63, aElem.height * 0.63 ) );
4575 else
4576 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.5, aElem.height * 0.5 ) );
4577 }
4578 }
4579
4580 aEdaText.SetTextThickness( aElem.strokewidth );
4581 aEdaText.SetBoldFlag( aElem.isBold );
4582 aEdaText.SetItalic( aElem.isItalic );
4583 aEdaText.SetMirrored( aElem.isMirrored );
4584}
4585
4586
4588 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4589{
4590 if( m_progressReporter )
4591 m_progressReporter->Report( _( "Loading rectangles..." ) );
4592
4593 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4594
4595 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4596 {
4597 checkpoint();
4598 AFILL6 elem( reader );
4599
4600 if( elem.component == ALTIUM_COMPONENT_NONE )
4601 {
4603 }
4604 else
4605 {
4606 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4607 ConvertFills6ToFootprintItem( footprint, elem, true );
4608 }
4609 }
4610
4611 if( reader.GetRemainingBytes() != 0 )
4612 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
4613}
4614
4615
4617{
4618 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
4619 {
4620 // This is not the actual board item. We can use it to create the polygon for the region
4621 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4622
4623 shape.SetStart( aElem.pos1 );
4624 shape.SetEnd( aElem.pos2 );
4625 shape.SetFilled( true );
4626 shape.SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
4627
4628 if( aElem.rotation != 0. )
4629 {
4630 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4631 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4632 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4633 }
4634
4636 }
4637 else
4638 {
4639 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4640 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4641 }
4642}
4643
4644
4646 const bool aIsBoardImport )
4647{
4648 if( aElem.is_keepout
4649 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
4650 {
4651 // This is not the actual board item. We can use it to create the polygon for the region
4652 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4653
4654 shape.SetStart( aElem.pos1 );
4655 shape.SetEnd( aElem.pos2 );
4656 shape.SetFilled( true );
4657 shape.SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
4658
4659 if( aElem.rotation != 0. )
4660 {
4661 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4662 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4663 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4664 }
4665
4666 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4667 aElem.keepoutrestrictions );
4668 }
4669 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
4670 && aElem.net != ALTIUM_NET_UNCONNECTED )
4671 {
4672 // Special case: do to not lose net connections in footprints
4673 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4674 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4675 }
4676 else
4677 {
4678 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4679 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4680 }
4681}
4682
4683
4685{
4686 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::RECTANGLE );
4687
4688 fill->SetFilled( true );
4689 fill->SetLayer( aLayer );
4690 fill->SetStroke( STROKE_PARAMS( 0 ) );
4691
4692 fill->SetStart( aElem.pos1 );
4693 fill->SetEnd( aElem.pos2 );
4694
4695 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4696 {
4697 fill->SetNetCode( GetNetCode( aElem.net ) );
4698 }
4699
4700 if( aElem.rotation != 0. )
4701 {
4702 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4703 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4704 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4705 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4706 }
4707
4708 m_board->Add( fill.release(), ADD_MODE::APPEND );
4709}
4710
4711
4713 PCB_LAYER_ID aLayer )
4714{
4715 if( aLayer == F_Cu || aLayer == B_Cu )
4716 {
4717 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
4718
4719 LSET padLayers;
4720 padLayers.set( aLayer );
4721
4722 pad->SetAttribute( PAD_ATTRIB::SMD );
4723 EDA_ANGLE rotation( aElem.rotation, DEGREES_T );
4724
4725 // Handle rotation multiples of 90 degrees
4726 if( rotation.IsCardinal() )
4727 {
4728 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::RECTANGLE );
4729
4730 int width = std::abs( aElem.pos2.x - aElem.pos1.x );
4731 int height = std::abs( aElem.pos2.y - aElem.pos1.y );
4732
4733 // Swap width and height for 90 or 270 degree rotations
4734 if( rotation.IsCardinal90() )
4735 std::swap( width, height );
4736
4737 pad->SetSize( PADSTACK::ALL_LAYERS, { width, height } );
4738 pad->SetPosition( aElem.pos1 / 2 + aElem.pos2 / 2 );
4739 }
4740 else
4741 {
4742 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CUSTOM );
4743
4744 int anchorSize = std::min( std::abs( aElem.pos2.x - aElem.pos1.x ),
4745 std::abs( aElem.pos2.y - aElem.pos1.y ) );
4746 VECTOR2I anchorPos = aElem.pos1;
4747
4748 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
4749 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
4750 pad->SetPosition( anchorPos );
4751
4752 SHAPE_POLY_SET shapePolys;
4753 shapePolys.NewOutline();
4754 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4755 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos1.y - anchorPos.y );
4756 shapePolys.Append( aElem.pos2.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4757 shapePolys.Append( aElem.pos1.x - anchorPos.x, aElem.pos2.y - anchorPos.y );
4758 shapePolys.Outline( 0 ).SetClosed( true );
4759
4760 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2 - anchorPos.x,
4761 aElem.pos1.y / 2 + aElem.pos2.y / 2 - anchorPos.y );
4762 shapePolys.Rotate( EDA_ANGLE( aElem.rotation, DEGREES_T ), center );
4763 pad->AddPrimitivePoly( F_Cu, shapePolys, 0, true );
4764 }
4765
4766 pad->SetThermalSpokeAngle( ANGLE_90 );
4767 pad->SetLayerSet( padLayers );
4768
4769 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
4770 }
4771 else
4772 {
4773 std::unique_ptr<PCB_SHAPE> fill =
4774 std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::RECTANGLE );
4775
4776 fill->SetFilled( true );
4777 fill->SetLayer( aLayer );
4778 fill->SetStroke( STROKE_PARAMS( 0 ) );
4779
4780 fill->SetStart( aElem.pos1 );
4781 fill->SetEnd( aElem.pos2 );
4782
4783 if( aElem.rotation != 0. )
4784 {
4785 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4786 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4787 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4788 }
4789
4790 aFootprint->Add( fill.release(), ADD_MODE::APPEND );
4791 }
4792}
4793
4794
4795void ALTIUM_PCB::HelperSetZoneLayers( ZONE& aZone, const ALTIUM_LAYER aAltiumLayer )
4796{
4797 LSET layerSet;
4798
4799 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
4800 layerSet.set( klayer );
4801
4802 aZone.SetLayerSet( layerSet );
4803}
4804
4805
4806void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE& aZone, const uint8_t aKeepoutRestrictions )
4807{
4808 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
4809 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
4810 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
4811 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
4812 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
4813
4814 aZone.SetDoNotAllowVias( keepoutRestrictionVia );
4815 aZone.SetDoNotAllowTracks( keepoutRestrictionTrack );
4816 aZone.SetDoNotAllowCopperPour( keepoutRestrictionCopper );
4817 aZone.SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
4818 aZone.SetDoNotAllowFootprints( false );
4819}
4820
4821
4823 const ALTIUM_LAYER aAltiumLayer,
4824 const uint8_t aKeepoutRestrictions )
4825{
4826 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
4827
4828 zone->SetIsRuleArea( true );
4829
4830 HelperSetZoneLayers( *zone, aAltiumLayer );
4831 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4832
4833 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4834
4835 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4837
4838 m_board->Add( zone.release(), ADD_MODE::APPEND );
4839}
4840
4841
4843 const PCB_SHAPE& aShape,
4844 const ALTIUM_LAYER aAltiumLayer,
4845 const uint8_t aKeepoutRestrictions )
4846{
4847 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
4848
4849 zone->SetIsRuleArea( true );
4850
4851 HelperSetZoneLayers( *zone, aAltiumLayer );
4852 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4853
4854 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4855
4856 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4858
4859 // TODO: zone->SetLocalCoord(); missing?
4860 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
4861}
4862
4863
4864std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
4865 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
4866{
4867 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
4868 return {}; // there is nothing to parse
4869
4870 auto elems =
4871 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
4872
4873 if( elems.first == elems.second )
4874 return {}; // there is nothing to parse
4875
4876 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
4877
4878 for( auto it = elems.first; it != elems.second; ++it )
4879 {
4880 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
4881
4882 if( pInf.type == AEXTENDED_PRIMITIVE_INFORMATION_TYPE::MASK )
4883 {
4884 if( pInf.soldermaskexpansionmode == ALTIUM_MODE::MANUAL
4885 || pInf.soldermaskexpansionmode == ALTIUM_MODE::RULE )
4886 {
4887 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
4888 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4889 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4890 {
4891 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
4892 }
4893
4894 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4895 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4896 {
4897 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
4898 }
4899 }
4900 if( pInf.pastemaskexpansionmode == ALTIUM_MODE::MANUAL
4901 || pInf.pastemaskexpansionmode == ALTIUM_MODE::RULE )
4902 {
4903 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4904 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4905 {
4906 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
4907 }
4908
4909 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4910 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4911 {
4912 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
4913 }
4914 }
4915 }
4916 }
4917
4918 return layerExpansionPairs;
4919}
const char * name
Definition: DXF_plotter.cpp:57
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:153
@ LT_POWER
Definition: board.h:156
@ LT_MIXED
Definition: board.h:157
@ LT_SIGNAL
Definition: board.h:155
@ 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:115
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:325
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:288
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:290
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:778
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:1003
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:929
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:798
const PAGE_INFO & GetPageSettings() const
Definition: board.h:689
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:804
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:612
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:592
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: board.cpp:502
int GetCopperLayerCount() const
Definition: board.cpp:741
const TRACKS & Tracks() const
Definition: board.h:329
void SetVisibleLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:810
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:377
void SetCopperLayerCount(int aCount)
Definition: board.cpp:747
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:631
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
const DRAWINGS & Drawings() const
Definition: board.h:333
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:896
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:778
void SetFilled(bool aFlag)
Definition: eda_shape.h:108
int GetRadius() const
Definition: eda_shape.cpp:826
SHAPE_T GetShape() const
Definition: eda_shape.h:132
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:924
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1451
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:251
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:506
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:551
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:248
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:283
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition: eda_text.h:190
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:291
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition: eda_text.cpp:299
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
Returns the link for an embedded file.
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Loads a file from disk and adds it to the collection.
static RETURN_CODE CompressAndEncode(EMBEDDED_FILE &aFile)
Takes data from the #decompressedData buffer and compresses it using ZSTD into the #compressedEncoded...
EDA_ANGLE GetOrientation() const
Definition: footprint.h:227
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:658
bool IsFlipped() const
Definition: footprint.h:391
PCB_FIELD & Reference()
Definition: footprint.h:659
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1025
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:220
const wxString & GetReference() const
Definition: footprint.h:622
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: footprint.h:1001
VECTOR2I GetPosition() const override
Definition: footprint.h:224
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:101
double m_Opacity
Definition: footprint.h:102
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:100
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:103
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:123
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:36
static LSET AllBoardTechMask()
Return a mask holding board technical layers (no CU layer) on both side.
Definition: lset.cpp:751
static LSET UserDefinedLayers()
Return a mask with all of the allowable user defined layers.
Definition: lset.cpp:773
static LSET UserMask()
Definition: lset.cpp:758
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:1109
void SetMode(MODE aMode)
Definition: padstack.cpp:821
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1138
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1034
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1156
@ 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:1040
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:269
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:290
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:276
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:1855
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:116
const VECTOR2I & GetP1() const
Definition: shape_arc.h:115
const VECTOR2I & GetP0() const
Definition: shape_arc.h:114
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
int NextShape(int aPointIndex) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
bool IsArcStart(size_t aIndex) const
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union For aFastMode meaning, see function booleanOp.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
Simple container to manage line stroke parameters.
Definition: stroke_params.h:79
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:557
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:487
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:748
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:751
static int GetDefaultHatchPitch()
Definition: zone.cpp:1281
#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)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:531
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:741
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:126
@ 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:691