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