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"
29
30#include <board.h>
32#include <pcb_dimension.h>
33#include <pad.h>
34#include <pcb_shape.h>
35#include <pcb_text.h>
36#include <pcb_track.h>
37#include <core/profile.h>
38#include <string_utils.h>
39#include <zone.h>
40
42
43#include <compoundfilereader.h>
45#include <font/outline_font.h>
46#include <project.h>
47#include <reporter.h>
48#include <trigo.h>
49#include <utf.h>
50#include <wx/docview.h>
51#include <wx/log.h>
52#include <wx/mstream.h>
53#include <wx/wfstream.h>
54#include <wx/zstream.h>
55#include <progress_reporter.h>
56#include <magic_enum.hpp>
57
58
59constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700
60
61
63{
64 return ( aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER )
65 || aLayer == ALTIUM_LAYER::MULTI_LAYER; // TODO: add IsAltiumLayerAPlane?
66}
67
68
70{
72}
73
74FOOTPRINT* ALTIUM_PCB::HelperGetFootprint( uint16_t aComponent ) const
75{
76 if( aComponent == ALTIUM_COMPONENT_NONE || m_components.size() <= aComponent )
77 {
78 THROW_IO_ERROR( wxString::Format( wxT( "Component creator tries to access component id %u "
79 "of %u existing components" ),
80 (unsigned)aComponent, (unsigned)m_components.size() ) );
81 }
82
83 return m_components.at( aComponent );
84}
85
86
88 const std::vector<ALTIUM_VERTICE>& aVertices )
89{
90 for( const ALTIUM_VERTICE& vertex : aVertices )
91 {
92 if( vertex.isRound )
93 {
94 EDA_ANGLE angle( vertex.endangle - vertex.startangle, DEGREES_T );
95 angle.Normalize();
96
97 double startradiant = DEG2RAD( vertex.startangle );
98 double endradiant = DEG2RAD( vertex.endangle );
99 VECTOR2I arcStartOffset = VECTOR2I( KiROUND( std::cos( startradiant ) * vertex.radius ),
100 -KiROUND( std::sin( startradiant ) * vertex.radius ) );
101
102 VECTOR2I arcEndOffset = VECTOR2I( KiROUND( std::cos( endradiant ) * vertex.radius ),
103 -KiROUND( std::sin( endradiant ) * vertex.radius ) );
104
105 VECTOR2I arcStart = vertex.center + arcStartOffset;
106 VECTOR2I arcEnd = vertex.center + arcEndOffset;
107
108 if( arcStart.Distance( vertex.position )
109 < arcEnd.Distance( vertex.position ) )
110 {
111 aLine.Append( SHAPE_ARC( vertex.center, arcStart, -angle ) );
112 }
113 else
114 {
115 aLine.Append( SHAPE_ARC( vertex.center, arcEnd, angle ) );
116 }
117 }
118 else
119 {
120 aLine.Append( vertex.position );
121 }
122 }
123
124 aLine.SetClosed( true );
125}
126
127
129{
130 auto override = m_layermap.find( aAltiumLayer );
131
132 if( override != m_layermap.end() )
133 {
134 return override->second;
135 }
136
137 switch( aAltiumLayer )
138 {
139 case ALTIUM_LAYER::UNKNOWN: return UNDEFINED_LAYER;
140
141 case ALTIUM_LAYER::TOP_LAYER: return F_Cu;
142 case ALTIUM_LAYER::MID_LAYER_1: return In1_Cu;
143 case ALTIUM_LAYER::MID_LAYER_2: return In2_Cu;
144 case ALTIUM_LAYER::MID_LAYER_3: return In3_Cu;
145 case ALTIUM_LAYER::MID_LAYER_4: return In4_Cu;
146 case ALTIUM_LAYER::MID_LAYER_5: return In5_Cu;
147 case ALTIUM_LAYER::MID_LAYER_6: return In6_Cu;
148 case ALTIUM_LAYER::MID_LAYER_7: return In7_Cu;
149 case ALTIUM_LAYER::MID_LAYER_8: return In8_Cu;
150 case ALTIUM_LAYER::MID_LAYER_9: return In9_Cu;
151 case ALTIUM_LAYER::MID_LAYER_10: return In10_Cu;
152 case ALTIUM_LAYER::MID_LAYER_11: return In11_Cu;
153 case ALTIUM_LAYER::MID_LAYER_12: return In12_Cu;
154 case ALTIUM_LAYER::MID_LAYER_13: return In13_Cu;
155 case ALTIUM_LAYER::MID_LAYER_14: return In14_Cu;
156 case ALTIUM_LAYER::MID_LAYER_15: return In15_Cu;
157 case ALTIUM_LAYER::MID_LAYER_16: return In16_Cu;
158 case ALTIUM_LAYER::MID_LAYER_17: return In17_Cu;
159 case ALTIUM_LAYER::MID_LAYER_18: return In18_Cu;
160 case ALTIUM_LAYER::MID_LAYER_19: return In19_Cu;
161 case ALTIUM_LAYER::MID_LAYER_20: return In20_Cu;
162 case ALTIUM_LAYER::MID_LAYER_21: return In21_Cu;
163 case ALTIUM_LAYER::MID_LAYER_22: return In22_Cu;
164 case ALTIUM_LAYER::MID_LAYER_23: return In23_Cu;
165 case ALTIUM_LAYER::MID_LAYER_24: return In24_Cu;
166 case ALTIUM_LAYER::MID_LAYER_25: return In25_Cu;
167 case ALTIUM_LAYER::MID_LAYER_26: return In26_Cu;
168 case ALTIUM_LAYER::MID_LAYER_27: return In27_Cu;
169 case ALTIUM_LAYER::MID_LAYER_28: return In28_Cu;
170 case ALTIUM_LAYER::MID_LAYER_29: return In29_Cu;
171 case ALTIUM_LAYER::MID_LAYER_30: return In30_Cu;
172 case ALTIUM_LAYER::BOTTOM_LAYER: return B_Cu;
173
174 case ALTIUM_LAYER::TOP_OVERLAY: return F_SilkS;
175 case ALTIUM_LAYER::BOTTOM_OVERLAY: return B_SilkS;
176 case ALTIUM_LAYER::TOP_PASTE: return F_Paste;
177 case ALTIUM_LAYER::BOTTOM_PASTE: return B_Paste;
178 case ALTIUM_LAYER::TOP_SOLDER: return F_Mask;
179 case ALTIUM_LAYER::BOTTOM_SOLDER: return B_Mask;
180
181 case ALTIUM_LAYER::INTERNAL_PLANE_1: return UNDEFINED_LAYER;
182 case ALTIUM_LAYER::INTERNAL_PLANE_2: return UNDEFINED_LAYER;
183 case ALTIUM_LAYER::INTERNAL_PLANE_3: return UNDEFINED_LAYER;
184 case ALTIUM_LAYER::INTERNAL_PLANE_4: return UNDEFINED_LAYER;
185 case ALTIUM_LAYER::INTERNAL_PLANE_5: return UNDEFINED_LAYER;
186 case ALTIUM_LAYER::INTERNAL_PLANE_6: return UNDEFINED_LAYER;
187 case ALTIUM_LAYER::INTERNAL_PLANE_7: return UNDEFINED_LAYER;
188 case ALTIUM_LAYER::INTERNAL_PLANE_8: return UNDEFINED_LAYER;
189 case ALTIUM_LAYER::INTERNAL_PLANE_9: return UNDEFINED_LAYER;
190 case ALTIUM_LAYER::INTERNAL_PLANE_10: return UNDEFINED_LAYER;
191 case ALTIUM_LAYER::INTERNAL_PLANE_11: return UNDEFINED_LAYER;
192 case ALTIUM_LAYER::INTERNAL_PLANE_12: return UNDEFINED_LAYER;
193 case ALTIUM_LAYER::INTERNAL_PLANE_13: return UNDEFINED_LAYER;
194 case ALTIUM_LAYER::INTERNAL_PLANE_14: return UNDEFINED_LAYER;
195 case ALTIUM_LAYER::INTERNAL_PLANE_15: return UNDEFINED_LAYER;
196 case ALTIUM_LAYER::INTERNAL_PLANE_16: return UNDEFINED_LAYER;
197
198 case ALTIUM_LAYER::DRILL_GUIDE: return Dwgs_User;
199 case ALTIUM_LAYER::KEEP_OUT_LAYER: return Margin;
200
201 case ALTIUM_LAYER::MECHANICAL_1: return User_1; //Edge_Cuts;
202 case ALTIUM_LAYER::MECHANICAL_2: return User_2;
203 case ALTIUM_LAYER::MECHANICAL_3: return User_3;
204 case ALTIUM_LAYER::MECHANICAL_4: return User_4;
205 case ALTIUM_LAYER::MECHANICAL_5: return User_5;
206 case ALTIUM_LAYER::MECHANICAL_6: return User_6;
207 case ALTIUM_LAYER::MECHANICAL_7: return User_7;
208 case ALTIUM_LAYER::MECHANICAL_8: return User_8;
209 case ALTIUM_LAYER::MECHANICAL_9: return User_9;
210 case ALTIUM_LAYER::MECHANICAL_10: return Dwgs_User;
211 case ALTIUM_LAYER::MECHANICAL_11: return Eco2_User; //Eco1 is used for unknown elements
212 case ALTIUM_LAYER::MECHANICAL_12: return F_Fab;
213 case ALTIUM_LAYER::MECHANICAL_13: return B_Fab; // Don't use courtyard layers for other purposes
214 case ALTIUM_LAYER::MECHANICAL_14: return UNDEFINED_LAYER;
215 case ALTIUM_LAYER::MECHANICAL_15: return UNDEFINED_LAYER;
216 case ALTIUM_LAYER::MECHANICAL_16: return UNDEFINED_LAYER;
217
218 case ALTIUM_LAYER::DRILL_DRAWING: return Dwgs_User;
219 case ALTIUM_LAYER::MULTI_LAYER: return UNDEFINED_LAYER;
220 case ALTIUM_LAYER::CONNECTIONS: return UNDEFINED_LAYER;
221 case ALTIUM_LAYER::BACKGROUND: return UNDEFINED_LAYER;
222 case ALTIUM_LAYER::DRC_ERROR_MARKERS: return UNDEFINED_LAYER;
223 case ALTIUM_LAYER::SELECTIONS: return UNDEFINED_LAYER;
224 case ALTIUM_LAYER::VISIBLE_GRID_1: return UNDEFINED_LAYER;
225 case ALTIUM_LAYER::VISIBLE_GRID_2: return UNDEFINED_LAYER;
226 case ALTIUM_LAYER::PAD_HOLES: return UNDEFINED_LAYER;
227 case ALTIUM_LAYER::VIA_HOLES: return UNDEFINED_LAYER;
228
229 default: return UNDEFINED_LAYER;
230 }
231}
232
233
234std::vector<PCB_LAYER_ID> ALTIUM_PCB::GetKicadLayersToIterate( ALTIUM_LAYER aAltiumLayer ) const
235{
236 static std::set<ALTIUM_LAYER> altiumLayersWithWarning;
237
238 if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER || aAltiumLayer == ALTIUM_LAYER::KEEP_OUT_LAYER )
239 {
240 std::vector<PCB_LAYER_ID> layers;
241 layers.reserve( MAX_CU_LAYERS );
242
243 for( PCB_LAYER_ID layer : LSET::AllCuMask().Seq() )
244 {
245 if( !m_board || m_board->IsLayerEnabled( layer ) )
246 layers.emplace_back( layer );
247 }
248
249 return layers;
250 }
251
252 PCB_LAYER_ID klayer = GetKicadLayer( aAltiumLayer );
253
254 if( klayer == UNDEFINED_LAYER )
255 {
256 auto it = m_layerNames.find( aAltiumLayer );
257 wxString layerName = it != m_layerNames.end() ? it->second : wxString::Format( wxT( "(%d)" ),
258 (int) aAltiumLayer );
259
260 if( m_reporter && altiumLayersWithWarning.insert( aAltiumLayer ).second )
261 {
262 m_reporter->Report( wxString::Format(
263 _( "Altium layer %s has no KiCad equivalent. It has been moved to KiCad "
264 "layer Eco1_User." ), layerName ), RPT_SEVERITY_INFO );
265 }
266
267 klayer = Eco1_User;
269 }
270
271 return { klayer };
272}
273
274
275ALTIUM_PCB::ALTIUM_PCB( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter,
276 LAYER_MAPPING_HANDLER& aHandler, REPORTER* aReporter,
277 const wxString& aLibrary, const wxString& aFootprintName )
278{
279 m_board = aBoard;
280 m_progressReporter = aProgressReporter;
281 m_layerMappingHandler = aHandler;
282 m_reporter = aReporter;
283 m_doneCount = 0;
285 m_totalCount = 0;
287 m_library = aLibrary;
288 m_footprintName = aFootprintName;
289}
290
292{
293}
294
296{
297 const unsigned PROGRESS_DELTA = 250;
298
300 {
301 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
302 {
304 / std::max( 1U, m_totalCount ) );
305
307 THROW_IO_ERROR( _( "Open cancelled by user." ) );
308
310 }
311 }
312}
313
314void ALTIUM_PCB::Parse( const ALTIUM_COMPOUND_FILE& altiumPcbFile,
315 const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
316{
317 // this vector simply declares in which order which functions to call.
318 const std::vector<std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>> parserOrder = {
319 { true, ALTIUM_PCB_DIR::FILE_HEADER,
320 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
321 {
322 this->ParseFileHeader( aFile, fileHeader );
323 } },
324 { true, ALTIUM_PCB_DIR::BOARD6,
325 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
326 {
327 this->ParseBoard6Data( aFile, fileHeader );
328 } },
329 { false, ALTIUM_PCB_DIR::EXTENDPRIMITIVEINFORMATION,
330 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
331 {
332 this->ParseExtendedPrimitiveInformationData( aFile, fileHeader );
333 } },
334 { true, ALTIUM_PCB_DIR::COMPONENTS6,
335 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
336 {
337 this->ParseComponents6Data( aFile, fileHeader );
338 } },
339 { false, ALTIUM_PCB_DIR::MODELS,
340 [this, aFileMapping]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
341 {
342 std::vector<std::string> dir{ aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) };
343 this->ParseModelsData( aFile, fileHeader, dir );
344 } },
345 { true, ALTIUM_PCB_DIR::COMPONENTBODIES6,
346 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
347 {
348 this->ParseComponentsBodies6Data( aFile, fileHeader );
349 } },
350 { true, ALTIUM_PCB_DIR::NETS6,
351 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
352 {
353 this->ParseNets6Data( aFile, fileHeader );
354 } },
355 { true, ALTIUM_PCB_DIR::CLASSES6,
356 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
357 {
358 this->ParseClasses6Data( aFile, fileHeader );
359 } },
360 { true, ALTIUM_PCB_DIR::RULES6,
361 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
362 {
363 this->ParseRules6Data( aFile, fileHeader );
364 } },
365 { true, ALTIUM_PCB_DIR::DIMENSIONS6,
366 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
367 {
368 this->ParseDimensions6Data( aFile, fileHeader );
369 } },
370 { true, ALTIUM_PCB_DIR::POLYGONS6,
371 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
372 {
373 this->ParsePolygons6Data( aFile, fileHeader );
374 } },
375 { true, ALTIUM_PCB_DIR::ARCS6,
376 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
377 {
378 this->ParseArcs6Data( aFile, fileHeader );
379 } },
380 { true, ALTIUM_PCB_DIR::PADS6,
381 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
382 {
383 this->ParsePads6Data( aFile, fileHeader );
384 } },
385 { true, ALTIUM_PCB_DIR::VIAS6,
386 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
387 {
388 this->ParseVias6Data( aFile, fileHeader );
389 } },
390 { true, ALTIUM_PCB_DIR::TRACKS6,
391 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
392 {
393 this->ParseTracks6Data( aFile, fileHeader );
394 } },
395 { false, ALTIUM_PCB_DIR::WIDESTRINGS6,
396 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
397 {
398 this->ParseWideStrings6Data( aFile, fileHeader );
399 } },
400 { true, ALTIUM_PCB_DIR::TEXTS6,
401 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
402 {
403 this->ParseTexts6Data( aFile, fileHeader );
404 } },
405 { true, ALTIUM_PCB_DIR::FILLS6,
406 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
407 {
408 this->ParseFills6Data( aFile, fileHeader );
409 } },
410 { false, ALTIUM_PCB_DIR::BOARDREGIONS,
411 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
412 {
413 this->ParseBoardRegionsData( aFile, fileHeader );
414 } },
415 { true, ALTIUM_PCB_DIR::SHAPEBASEDREGIONS6,
416 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
417 {
418 this->ParseShapeBasedRegions6Data( aFile, fileHeader );
419 } },
420 { true, ALTIUM_PCB_DIR::REGIONS6,
421 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
422 {
423 this->ParseRegions6Data( aFile, fileHeader );
424 } }
425 };
426
427 if( m_progressReporter != nullptr )
428 {
429 // Count number of records we will read for the progress reporter
430 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
431 {
432 bool isRequired;
435 std::tie( isRequired, directory, fp ) = cur;
436
437 if( directory == ALTIUM_PCB_DIR::FILE_HEADER )
438 continue;
439
440 const auto& mappedDirectory = aFileMapping.find( directory );
441
442 if( mappedDirectory == aFileMapping.end() )
443 continue;
444
445 const std::vector<std::string> mappedFile{ mappedDirectory->second, "Header" };
446 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
447
448 if( file == nullptr )
449 continue;
450
451 ALTIUM_BINARY_PARSER reader( altiumPcbFile, file );
452 uint32_t numOfRecords = reader.Read<uint32_t>();
453
454 if( reader.HasParsingError() )
455 {
456 if( m_reporter )
457 {
458 m_reporter->Report( wxString::Format( _( "'%s' was not parsed correctly." ),
459 FormatPath( mappedFile ) ),
461 }
462
463 continue;
464 }
465
466 m_totalCount += numOfRecords;
467
468 if( reader.GetRemainingBytes() != 0 )
469 {
470 if( m_reporter )
471 {
472 m_reporter->Report( wxString::Format( _( "'%s' was not fully parsed." ),
473 FormatPath( mappedFile ) ),
475 }
476
477 continue;
478 }
479 }
480 }
481
482 const auto& boardDirectory = aFileMapping.find( ALTIUM_PCB_DIR::BOARD6 );
483
484 if( boardDirectory != aFileMapping.end() )
485 {
486 std::vector<std::string> mappedFile{ boardDirectory->second, "Data" };
487
488 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
489
490 if( !file )
491 {
493 "This file does not appear to be in a valid PCB Binary Version 6.0 format. In "
494 "Altium Designer, "
495 "make sure to save as \"PCB Binary Files (*.PcbDoc)\"." ) );
496 }
497 }
498
499 // Parse data in specified order
500 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
501 {
502 bool isRequired;
505 std::tie( isRequired, directory, fp ) = cur;
506
507 const auto& mappedDirectory = aFileMapping.find( directory );
508
509 if( mappedDirectory == aFileMapping.end() )
510 {
511 wxASSERT_MSG( !isRequired, wxString::Format( wxT( "Altium Directory of kind %d was "
512 "expected, but no mapping is "
513 "present in the code" ),
514 directory ) );
515 continue;
516 }
517
518 std::vector<std::string> mappedFile{ mappedDirectory->second };
519
520 if( directory != ALTIUM_PCB_DIR::FILE_HEADER )
521 mappedFile.emplace_back( "Data" );
522
523 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
524
525 if( file != nullptr )
526 {
527 fp( altiumPcbFile, file );
528 }
529 else if( isRequired )
530 {
531 if( m_reporter )
532 {
533 m_reporter->Report( wxString::Format( _( "File not found: '%s' for directory '%s'." ),
534 FormatPath( mappedFile ),
535 magic_enum::enum_name( directory ) ),
537 }
538 }
539 }
540
541 // fixup zone priorities since Altium stores them in the opposite order
542 for( ZONE* zone : m_polygons )
543 {
544 if( !zone )
545 continue;
546
547 // Altium "fills" - not poured in Altium
548 if( zone->GetAssignedPriority() == 1000 )
549 {
550 // Unlikely, but you never know
551 if( m_highest_pour_index >= 1000 )
552 zone->SetAssignedPriority( m_highest_pour_index + 1 );
553
554 continue;
555 }
556
557 int priority = m_highest_pour_index - zone->GetAssignedPriority();
558
559 zone->SetAssignedPriority( priority >= 0 ? priority : 0 );
560 }
561
562 // change priority of outer zone to zero
563 for( std::pair<const ALTIUM_LAYER, ZONE*>& zone : m_outer_plane )
564 zone.second->SetAssignedPriority( 0 );
565
566 // Simplify and fracture zone fills in case we constructed them from tracks (hatched fill)
567 for( ZONE* zone : m_polygons )
568 {
569 if( !zone )
570 continue;
571
572 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
573 {
574 if( !zone->HasFilledPolysForLayer( layer ) )
575 continue;
576
577 zone->GetFilledPolysList( layer )->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
578 }
579 }
580
581 // Altium doesn't appear to store either the dimension value nor the dimensioned object in
582 // the dimension record. (Yes, there is a REFERENCE0OBJECTID, but it doesn't point to the
583 // dimensioned object.) We attempt to plug this gap by finding a colocated arc or circle
584 // and using its radius. If there are more than one such arcs/circles, well, :shrug:.
586 {
587 int radius = 0;
588
589 for( BOARD_ITEM* item : m_board->Drawings() )
590 {
591 if( item->Type() != PCB_SHAPE_T )
592 continue;
593
594 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
595
596 if( shape->GetShape() != SHAPE_T::ARC && shape->GetShape() != SHAPE_T::CIRCLE )
597 continue;
598
599 if( shape->GetPosition() == dim->GetPosition() )
600 {
601 radius = shape->GetRadius();
602 break;
603 }
604 }
605
606 if( radius == 0 )
607 {
608 for( PCB_TRACK* track : m_board->Tracks() )
609 {
610 if( track->Type() != PCB_ARC_T )
611 continue;
612
613 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
614
615 if( arc->GetCenter() == dim->GetPosition() )
616 {
617 radius = arc->GetRadius();
618 break;
619 }
620 }
621 }
622
623 // Move the radius point onto the circumference
624 VECTOR2I radialLine = dim->GetEnd() - dim->GetStart();
625 int totalLength = radialLine.EuclideanNorm();
626
627 // Enforce a minimum on the radialLine else we won't have enough precision to get the
628 // angle from it.
629 radialLine = radialLine.Resize( std::max( radius, 2 ) );
630 dim->SetEnd( dim->GetStart() + (VECTOR2I) radialLine );
631 dim->SetLeaderLength( totalLength - radius );
632 dim->Update();
633 }
634
635 // center board
637
640
641 int desired_x = ( w - bbbox.GetWidth() ) / 2;
642 int desired_y = ( h - bbbox.GetHeight() ) / 2;
643
644 VECTOR2I movementVector( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() );
645 m_board->Move( movementVector );
646
648 bds.SetAuxOrigin( bds.GetAuxOrigin() + movementVector );
649 bds.SetGridOrigin( bds.GetGridOrigin() + movementVector );
650
652}
653
654
656 const wxString& aFootprintName )
657{
658 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
659
660 // TODO: what should we do with those layers?
661 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_14, Eco2_User );
662 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_15, Eco2_User );
663 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_16, Eco2_User );
664
665 m_unicodeStrings.clear();
667
668 // TODO: WideStrings are stored as parameterMap in the case of footprints, not as binary
669 // std::string unicodeStringsStreamName = aFootprintName.ToStdString() + "\\WideStrings";
670 // const CFB::COMPOUND_FILE_ENTRY* unicodeStringsData = altiumLibFile.FindStream( unicodeStringsStreamName );
671 // if( unicodeStringsData != nullptr )
672 // {
673 // ParseWideStrings6Data( altiumLibFile, unicodeStringsData );
674 // }
675
676 std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*> ret = altiumLibFile.FindLibFootprintDirName(aFootprintName);
677
678 wxString fpDirName = std::get<0>( ret );
679 const CFB::COMPOUND_FILE_ENTRY* footprintStream = std::get<1>( ret );
680
681 if( fpDirName.IsEmpty() )
682 {
684 wxString::Format( _( "Footprint directory not found: '%s'." ), aFootprintName ) );
685 }
686
687 const std::vector<std::string> streamName{ fpDirName.ToStdString(), "Data" };
688 const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( footprintStream, { "Data" } );
689
690 if( footprintData == nullptr )
691 {
692 THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ),
693 FormatPath( streamName ) ) );
694 }
695
696 ALTIUM_BINARY_PARSER parser( altiumLibFile, footprintData );
697
699 //wxString footprintName = parser.ReadWxString(); // Not used (single-byte char set)
700 parser.SkipSubrecord();
701
702 LIB_ID fpID = AltiumToKiCadLibID( "", aFootprintName ); // TODO: library name
703 footprint->SetFPID( fpID );
704
705 const std::vector<std::string> parametersStreamName{ fpDirName.ToStdString(),
706 "Parameters" };
707 const CFB::COMPOUND_FILE_ENTRY* parametersData =
708 altiumLibFile.FindStream( footprintStream, { "Parameters" } );
709
710 if( parametersData != nullptr )
711 {
712 ALTIUM_BINARY_PARSER parametersReader( altiumLibFile, parametersData );
713 std::map<wxString, wxString> parameterProperties = parametersReader.ReadProperties();
714 wxString description = ALTIUM_PROPS_UTILS::ReadString( parameterProperties,
715 wxT( "DESCRIPTION" ), wxT( "" ) );
716 footprint->SetLibDescription( description );
717 }
718 else
719 {
720 if( m_reporter )
721 {
722 m_reporter->Report( wxString::Format( _( "File not found: '%s'." ),
723 FormatPath( parametersStreamName ) ),
725 }
726
727 footprint->SetLibDescription( wxT( "" ) );
728 }
729
730 const std::vector<std::string> extendedPrimitiveInformationStreamName{
731 "ExtendedPrimitiveInformation", "Data"
732 };
733 const CFB::COMPOUND_FILE_ENTRY* extendedPrimitiveInformationData =
734 altiumLibFile.FindStream( footprintStream, extendedPrimitiveInformationStreamName );
735
736 if( extendedPrimitiveInformationData != nullptr )
737 ParseExtendedPrimitiveInformationData( altiumLibFile, extendedPrimitiveInformationData );
738
739 footprint->SetReference( wxT( "REF**" ) );
740 footprint->SetValue( aFootprintName );
741 footprint->Reference().SetVisible( true ); // TODO: extract visibility information
742 footprint->Value().SetVisible( true );
743
744 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
745 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
746
747 for( PCB_FIELD* field : footprint->Fields() )
748 {
749 field->SetTextSize( defaultTextSize );
750 field->SetTextThickness( defaultTextThickness );
751 }
752
753 for( int primitiveIndex = 0; parser.GetRemainingBytes() >= 4; primitiveIndex++ )
754 {
755 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( parser.Peek<uint8_t>() );
756
757 switch( recordtype )
758 {
759 case ALTIUM_RECORD::ARC:
760 {
761 AARC6 arc( parser );
762 ConvertArcs6ToFootprintItem( footprint.get(), arc, primitiveIndex, false );
763 break;
764 }
765 case ALTIUM_RECORD::PAD:
766 {
767 APAD6 pad( parser );
768 ConvertPads6ToFootprintItem( footprint.get(), pad );
769 break;
770 }
771 case ALTIUM_RECORD::VIA:
772 {
773 AVIA6 via( parser );
774 // TODO: implement
775 break;
776 }
777 case ALTIUM_RECORD::TRACK:
778 {
779 ATRACK6 track( parser );
780 ConvertTracks6ToFootprintItem( footprint.get(), track, primitiveIndex, false );
781 break;
782 }
783 case ALTIUM_RECORD::TEXT:
784 {
785 ATEXT6 text( parser, m_unicodeStrings );
786 ConvertTexts6ToFootprintItem( footprint.get(), text );
787 break;
788 }
789 case ALTIUM_RECORD::FILL:
790 {
791 AFILL6 fill( parser );
792 ConvertFills6ToFootprintItem( footprint.get(), fill, false );
793 break;
794 }
795 case ALTIUM_RECORD::REGION:
796 {
797 AREGION6 region( parser, false );
798 ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region, primitiveIndex );
799 break;
800 }
801 case ALTIUM_RECORD::MODEL:
802 {
803 ACOMPONENTBODY6 componentBody( parser );
804 // Won't be supported for now, as we would need to extract the model
805 break;
806 }
807 default:
808 THROW_IO_ERROR( wxString::Format( _( "Record of unknown type: '%d'." ), recordtype ) );
809 }
810 }
811
812 // Auto-position reference and value
813 footprint->AutoPositionFields();
814
815 if( parser.HasParsingError() )
816 {
817 THROW_IO_ERROR( wxString::Format( wxT( "%s stream was not parsed correctly" ),
818 FormatPath( streamName ) ) );
819 }
820
821 if( parser.GetRemainingBytes() != 0 )
822 {
823 THROW_IO_ERROR( wxString::Format( wxT( "%s stream is not fully parsed" ),
824 FormatPath( streamName ) ) );
825 }
826
827 return footprint.release();
828}
829
830int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
831{
832 if( aId == ALTIUM_NET_UNCONNECTED )
833 {
835 }
836 else if( m_altiumToKicadNetcodes.size() < aId )
837 {
838 THROW_IO_ERROR( wxString::Format( wxT( "Netcode with id %d does not exist. Only %d nets "
839 "are known" ),
840 aId, m_altiumToKicadNetcodes.size() ) );
841 }
842 else
843 {
844 return m_altiumToKicadNetcodes[ aId ];
845 }
846}
847
848const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
849{
850 const auto rules = m_rules.find( aKind );
851
852 if( rules == m_rules.end() )
853 return nullptr;
854
855 for( const ARULE6& rule : rules->second )
856 {
857 if( rule.name == aName )
858 return &rule;
859 }
860
861 return nullptr;
862}
863
865{
866 const auto rules = m_rules.find( aKind );
867
868 if( rules == m_rules.end() )
869 return nullptr;
870
871 for( const ARULE6& rule : rules->second )
872 {
873 if( rule.scope1expr == wxT( "All" ) && rule.scope2expr == wxT( "All" ) )
874 return &rule;
875 }
876
877 return nullptr;
878}
879
881 const CFB::COMPOUND_FILE_ENTRY* aEntry )
882{
883 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
884
886 wxString header = reader.ReadWxString();
887
888 //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
889
890 //reader.SkipSubrecord();
891
892 // TODO: does not seem to work all the time at the moment
893 //if( reader.GetRemainingBytes() != 0 )
894 // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
895}
896
897
899 const CFB::COMPOUND_FILE_ENTRY* aEntry )
900{
902 m_progressReporter->Report( _( "Loading extended primitive information data..." ) );
903
904 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
905
906 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
907 {
908 checkpoint();
909 AEXTENDED_PRIMITIVE_INFORMATION elem( reader );
910
912 std::move( elem ) );
913 }
914
915 if( reader.GetRemainingBytes() != 0 )
916 THROW_IO_ERROR( wxT( "ExtendedPrimitiveInformation stream is not fully parsed" ) );
917}
918
919
921 const CFB::COMPOUND_FILE_ENTRY* aEntry )
922{
924 m_progressReporter->Report( _( "Loading board data..." ) );
925
926 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
927
928 checkpoint();
929 ABOARD6 elem( reader );
930
931 if( reader.GetRemainingBytes() != 0 )
932 THROW_IO_ERROR( wxT( "Board6 stream is not fully parsed" ) );
933
936
937 // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
938 size_t layercount = 0;
939 size_t layerid = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
940
941 while( layerid < elem.stackup.size() && layerid != 0 )
942 {
943 layerid = elem.stackup[ layerid - 1 ].nextId;
944 layercount++;
945 }
946
947 size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
948 m_board->SetCopperLayerCount( kicadLayercount );
949
951 BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
952
953 // create board stackup
954 stackup.RemoveAll(); // Just to be sure
955 stackup.BuildDefaultStackupList( &designSettings, layercount );
956
957 auto it = stackup.GetList().begin();
958
959 // find first copper layer
960 for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
961 ;
962
963 auto curLayer = static_cast<int>( F_Cu );
964
965 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
966 altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
967 altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
968 {
969 // array starts with 0, but stackup with 1
970 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
971
972 // handle unused layer in case of odd layercount
973 if( layer.nextId == 0 && layercount != kicadLayercount )
974 {
975 m_board->SetLayerName( ( *it )->GetBrdLayerId(), wxT( "[unused]" ) );
976
977 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
978 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
979
980 ( *it )->SetThickness( 0 );
981
982 ++it;
983
984 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
985 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
986
987 ( *it )->SetThickness( 0, 0 );
988 ( *it )->SetThicknessLocked( true, 0 );
989 ++it;
990 }
991
992 m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ),
993 static_cast<PCB_LAYER_ID>( curLayer++ ) } );
994
995 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
996 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
997
998 ( *it )->SetThickness( layer.copperthick );
999
1000 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1001 PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
1002
1003 m_board->SetLayerName( klayer, layer.name );
1004
1005 if( layer.copperthick == 0 )
1006 m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
1007 else if( IsAltiumLayerAPlane( alayer ) )
1008 m_board->SetLayerType( klayer, LAYER_T::LT_POWER );
1009
1010 if( klayer == B_Cu )
1011 {
1012 if( layer.nextId != 0 )
1013 THROW_IO_ERROR( wxT( "Board6 stream, unexpected id while parsing last stackup layer" ) );
1014
1015 // overwrite entry from internal -> bottom
1016 m_layermap[alayer] = B_Cu;
1017 break;
1018 }
1019
1020 ++it;
1021
1022 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
1023 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
1024
1025 ( *it )->SetThickness( layer.dielectricthick, 0 );
1026 ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
1027 NotSpecifiedPrm() :
1028 wxString( layer.dielectricmaterial ) );
1029 ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
1030
1031 ++it;
1032 }
1033
1034 remapUnsureLayers( elem.stackup );
1035
1036 // Set name of all non-cu layers
1037 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_OVERLAY );
1038 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::BOTTOM_SOLDER ); altiumLayerId++ )
1039 {
1040 // array starts with 0, but stackup with 1
1041 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1042
1043 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1044 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1045
1046 m_board->SetLayerName( klayer, layer.name );
1047 }
1048
1049 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_1 );
1050 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_16 ); altiumLayerId++ )
1051 {
1052 // array starts with 0, but stackup with 1
1053 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
1054
1055 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
1056 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1057
1058 m_board->SetLayerName( klayer, layer.name );
1059 }
1060
1062}
1063
1064
1065void ALTIUM_PCB::remapUnsureLayers( std::vector<ABOARD6_LAYER_STACKUP>& aStackup )
1066{
1067 LSET enabledLayers = m_board->GetEnabledLayers();
1068 LSET validRemappingLayers = enabledLayers | LSET::AllBoardTechMask() |
1070
1071 std::vector<INPUT_LAYER_DESC> inputLayers;
1072 std::map<wxString, ALTIUM_LAYER> altiumLayerNameMap;
1073
1074 for( size_t ii = 0; ii < aStackup.size(); ii++ )
1075 {
1076 ABOARD6_LAYER_STACKUP& curLayer = aStackup[ii];
1077 ALTIUM_LAYER layer_num = static_cast<ALTIUM_LAYER>( ii + 1 );
1078 INPUT_LAYER_DESC iLdesc;
1079
1080 if( ii > m_board->GetCopperLayerCount() && layer_num != ALTIUM_LAYER::BOTTOM_LAYER
1081 && !( layer_num >= ALTIUM_LAYER::TOP_OVERLAY
1082 && layer_num <= ALTIUM_LAYER::BOTTOM_SOLDER )
1083 && !( layer_num >= ALTIUM_LAYER::MECHANICAL_1
1084 && layer_num <= ALTIUM_LAYER::MECHANICAL_16 ) )
1085 {
1086 if( layer_num < ALTIUM_LAYER::BOTTOM_LAYER )
1087 continue;
1088
1089 iLdesc.AutoMapLayer = PCB_LAYER_ID::UNDEFINED_LAYER;
1090 }
1091 else
1092 {
1093 iLdesc.AutoMapLayer = GetKicadLayer( layer_num );
1094 }
1095
1096 iLdesc.Name = curLayer.name;
1097 iLdesc.PermittedLayers = validRemappingLayers;
1098 iLdesc.Required = ii < m_board->GetCopperLayerCount() || layer_num == ALTIUM_LAYER::BOTTOM_LAYER;
1099
1100 inputLayers.push_back( iLdesc );
1101 altiumLayerNameMap.insert( { curLayer.name, layer_num } );
1102 m_layerNames.insert( { layer_num, curLayer.name } );
1103 }
1104
1105 if( inputLayers.size() == 0 )
1106 return;
1107
1108 // Callback:
1109 std::map<wxString, PCB_LAYER_ID> reMappedLayers = m_layerMappingHandler( inputLayers );
1110 enabledLayers = LSET();
1111 m_layermap.clear();
1112
1113 for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers )
1114 {
1115 if( layerPair.second == PCB_LAYER_ID::UNDEFINED_LAYER )
1116 {
1117 wxFAIL_MSG( wxT( "Unexpected Layer ID" ) );
1118 continue;
1119 }
1120
1121 ALTIUM_LAYER altiumID = altiumLayerNameMap.at( layerPair.first );
1122 m_layermap.insert_or_assign( altiumID, layerPair.second );
1123 enabledLayers |= LSET( layerPair.second );
1124 }
1125
1126 m_board->SetEnabledLayers( enabledLayers );
1127 m_board->SetVisibleLayers( enabledLayers );
1128}
1129
1130
1131void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
1132{
1133 SHAPE_LINE_CHAIN lineChain;
1134 HelperShapeLineChainFromAltiumVertices( lineChain, aVertices );
1135
1137 LINE_STYLE::SOLID );
1138
1139 for( int i = 0; i <= lineChain.PointCount() && i != -1; i = lineChain.NextShape( i ) )
1140 {
1141 if( lineChain.IsArcStart( i ) )
1142 {
1143 const SHAPE_ARC& currentArc = lineChain.Arc( lineChain.ArcIndex( i ) );
1144 int nextShape = lineChain.NextShape( i );
1145 bool isLastShape = nextShape < 0;
1146
1147 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::ARC );
1148
1149 shape->SetStroke( stroke );
1150 shape->SetLayer( Edge_Cuts );
1151 shape->SetArcGeometry( currentArc.GetP0(), currentArc.GetArcMid(), currentArc.GetP1() );
1152
1153 m_board->Add( shape.release(), ADD_MODE::APPEND );
1154 }
1155 else
1156 {
1157 const SEG& seg = lineChain.Segment( i );
1158
1159 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1160
1161 shape->SetStroke( stroke );
1162 shape->SetLayer( Edge_Cuts );
1163 shape->SetStart( seg.A );
1164 shape->SetEnd( seg.B );
1165
1166 m_board->Add( shape.release(), ADD_MODE::APPEND );
1167 }
1168 }
1169}
1170
1171
1173 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1174{
1175 if( m_progressReporter )
1176 m_progressReporter->Report( _( "Loading netclasses..." ) );
1177
1178 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1179
1180 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1181 {
1182 checkpoint();
1183 ACLASS6 elem( reader );
1184
1185 if( elem.kind == ALTIUM_CLASS_KIND::NET_CLASS )
1186 {
1187 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( elem.name );
1188
1189 for( const wxString& name : elem.names )
1190 {
1191 m_board->GetDesignSettings().m_NetSettings->m_NetClassPatternAssignments.push_back(
1192 {
1193 std::make_unique<EDA_COMBINED_MATCHER>( name, CTX_NETCLASS ),
1194 nc->GetName()
1195 } );
1196 }
1197
1198 if( m_board->GetDesignSettings().m_NetSettings->m_NetClasses.count( nc->GetName() ) )
1199 {
1200 // Name conflict, happens in some unknown circumstances
1201 // unique_ptr will delete nc on this code path
1202 if( m_reporter )
1203 {
1204 wxString msg;
1205 msg.Printf( _( "More than one Altium netclass with name '%s' found. "
1206 "Only the first one will be imported." ), elem.name );
1208 }
1209 }
1210 else
1211 {
1212 m_board->GetDesignSettings().m_NetSettings->m_NetClasses[ nc->GetName() ] = nc;
1213 }
1214 }
1215 }
1216
1217 if( reader.GetRemainingBytes() != 0 )
1218 THROW_IO_ERROR( wxT( "Classes6 stream is not fully parsed" ) );
1219
1221}
1222
1223
1225 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1226{
1227 if( m_progressReporter )
1228 m_progressReporter->Report( _( "Loading components..." ) );
1229
1230 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1231
1232 uint16_t componentId = 0;
1233
1234 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1235 {
1236 checkpoint();
1237 ACOMPONENT6 elem( reader );
1238
1239 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
1240
1242
1243 footprint->SetFPID( fpID );
1244
1245 footprint->SetPosition( elem.position );
1246 footprint->SetOrientationDegrees( elem.rotation );
1247
1248 // KiCad netlisting requires parts to have non-digit + digit annotation.
1249 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1250 wxString reference = elem.sourcedesignator;
1251
1252 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1253 reference.Prepend( wxT( "UNK" ) );
1254
1255 footprint->SetReference( reference );
1256
1257 footprint->SetLocked( elem.locked );
1258 footprint->Reference().SetVisible( elem.nameon );
1259 footprint->Value().SetVisible( elem.commenton );
1260 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1261
1262 m_components.emplace_back( footprint.get() );
1263 m_board->Add( footprint.release(), ADD_MODE::APPEND );
1264
1265 componentId++;
1266 }
1267
1268 if( reader.GetRemainingBytes() != 0 )
1269 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1270}
1271
1272
1274double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1275{
1276 while( Angle < aMin )
1277 Angle += 360.0;
1278
1279 while( Angle >= aMax )
1280 Angle -= 360.0;
1281
1282 return Angle;
1283}
1284
1285
1287 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1288{
1289 if( m_progressReporter )
1290 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1291
1292 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1293
1294 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1295 {
1296 checkpoint();
1297 ACOMPONENTBODY6 elem( reader ); // TODO: implement
1298
1299 if( elem.component == ALTIUM_COMPONENT_NONE )
1300 continue; // TODO: we do not support components for the board yet
1301
1302 if( m_components.size() <= elem.component )
1303 {
1304 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1305 "component id %d of %d existing components" ),
1306 elem.component,
1307 m_components.size() ) );
1308 }
1309
1310 if( !elem.modelIsEmbedded )
1311 continue;
1312
1313 auto modelTuple = m_models.find( elem.modelId );
1314
1315 if( modelTuple == m_models.end() )
1316 {
1317 if( m_reporter )
1318 {
1319 wxString msg;
1320 msg.Printf( wxT( "ComponentsBodies6 stream tries to access model id %s which does "
1321 "not exist" ), elem.modelId );
1323 }
1324
1325 continue;
1326 }
1327
1328 FOOTPRINT* footprint = m_components.at( elem.component );
1329 const VECTOR2I& fpPosition = footprint->GetPosition();
1330
1331 FP_3DMODEL modelSettings;
1332
1333 modelSettings.m_Filename = modelTuple->second;
1334
1335 modelSettings.m_Offset.x = pcbIUScale.IUTomm((int) elem.modelPosition.x - fpPosition.x );
1336 modelSettings.m_Offset.y = -pcbIUScale.IUTomm((int) elem.modelPosition.y - fpPosition.y );
1337 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) elem.modelPosition.z );
1338
1339 EDA_ANGLE orientation = footprint->GetOrientation();
1340
1341 if( footprint->IsFlipped() )
1342 {
1343 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1344 orientation = -orientation;
1345 }
1346
1347 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1348
1349 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1350 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1351 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z
1352 + elem.rotation
1353 + orientation.AsDegrees(),
1354 -180, 180 );
1355 modelSettings.m_Opacity = elem.bodyOpacity;
1356
1357 footprint->Models().push_back( modelSettings );
1358 }
1359
1360 if( reader.GetRemainingBytes() != 0 )
1361 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1362}
1363
1364
1366{
1367 if( aElem.referencePoint.size() != 2 )
1368 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1369
1370 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1371
1372 if( klayer == UNDEFINED_LAYER )
1373 {
1374 if( m_reporter )
1375 {
1376 m_reporter->Report( wxString::Format(
1377 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1378 "It has been moved to KiCad layer Eco1_User." ), aElem.layer ),
1380 }
1381
1382 klayer = Eco1_User;
1383 }
1384
1385 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1386 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1387
1388 std::unique_ptr<PCB_DIM_ALIGNED> dimension = std::make_unique<PCB_DIM_ALIGNED>( m_board, PCB_DIM_ALIGNED_T );
1389
1390 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1391 dimension->SetLayer( klayer );
1392 dimension->SetStart( referencePoint0 );
1393
1394 if( referencePoint0 != aElem.xy1 )
1395 {
1405 VECTOR2I direction = aElem.xy1 - referencePoint0;
1406 VECTOR2I directionNormalVector = VECTOR2I( -direction.y, direction.x );
1407 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1408 SEG segm2( referencePoint1, referencePoint1 + direction );
1409 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1410
1411 if( !intersection )
1412 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1413
1414 dimension->SetEnd( *intersection );
1415
1416 int height = direction.EuclideanNorm();
1417
1418 if( ( direction.x > 0 || direction.y < 0 ) != ( aElem.angle >= 180.0 ) )
1419 height = -height;
1420
1421 dimension->SetHeight( height );
1422 }
1423 else
1424 {
1425 dimension->SetEnd( referencePoint1 );
1426 }
1427
1428 dimension->SetLineThickness( aElem.linewidth );
1429
1430 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
1431 dimension->SetPrefix( aElem.textprefix );
1432
1433 // Suffix normally (but not always) holds the units
1434 wxRegEx units( wxS( "(mm)|(in)|(mils)|(thou)|(')|(\")" ), wxRE_ADVANCED );
1435
1436 if( units.Matches( aElem.textsuffix ) )
1437 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX );
1438 else
1439 dimension->SetSuffix( aElem.textsuffix );
1440
1441 dimension->SetTextThickness( aElem.textlinewidth );
1442 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1443 dimension->SetItalic( aElem.textitalic );
1444
1445#if 0 // we don't currently support bold; map to thicker text
1446 dimension->Text().SetBold( aElem.textbold );
1447#else
1448 if( aElem.textbold )
1449 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1450#endif
1451
1452 switch( aElem.textunit )
1453 {
1454 case ALTIUM_UNIT::INCHES:
1455 dimension->SetUnits( EDA_UNITS::INCHES );
1456 break;
1457 case ALTIUM_UNIT::MILS:
1458 dimension->SetUnits( EDA_UNITS::MILS );
1459 break;
1460 case ALTIUM_UNIT::MILLIMETERS:
1461 case ALTIUM_UNIT::CENTIMETER:
1462 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1463 break;
1464 default:
1465 break;
1466 }
1467
1468 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1469}
1470
1471
1473{
1474 if( aElem.referencePoint.size() < 2 )
1475 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1476
1477 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1478
1479 if( klayer == UNDEFINED_LAYER )
1480 {
1481 if( m_reporter )
1482 {
1483 m_reporter->Report( wxString::Format(
1484 _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1485 "It has been moved to KiCad layer Eco1_User." ),
1486 aElem.layer ), RPT_SEVERITY_INFO );
1487 }
1488
1489 klayer = Eco1_User;
1490 }
1491
1492 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1493 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1494
1495 std::unique_ptr<PCB_DIM_RADIAL> dimension = std::make_unique<PCB_DIM_RADIAL>( m_board );
1496
1497 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1498 dimension->SetLayer( klayer );
1499 dimension->SetStart( referencePoint0 );
1500 dimension->SetEnd( aElem.xy1 );
1501 dimension->SetLineThickness( aElem.linewidth );
1502 dimension->SetKeepTextAligned( false );
1503
1504 dimension->SetPrefix( aElem.textprefix );
1505
1506 // Suffix normally holds the units
1507 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1508 : DIM_UNITS_FORMAT::BARE_SUFFIX );
1509
1510 switch( aElem.textunit )
1511 {
1512 case ALTIUM_UNIT::INCHES:
1513 dimension->SetUnits( EDA_UNITS::INCHES );
1514 break;
1515 case ALTIUM_UNIT::MILS:
1516 dimension->SetUnits( EDA_UNITS::MILS );
1517 break;
1518 case ALTIUM_UNIT::MILLIMETERS:
1519 case ALTIUM_UNIT::CENTIMETER:
1520 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1521 break;
1522 default:
1523 break;
1524 }
1525
1526 if( aElem.textPoint.empty() )
1527 {
1528 if( m_reporter )
1529 {
1530 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1532 }
1533
1534 return;
1535 }
1536
1537 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1538 dimension->SetTextThickness( aElem.textlinewidth );
1539 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1540 dimension->SetItalic( aElem.textitalic );
1541
1542#if 0 // we don't currently support bold; map to thicker text
1543 dimension->SetBold( aElem.textbold );
1544#else
1545 if( aElem.textbold )
1546 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1547#endif
1548
1549 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1550 // close.
1551 dimension->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1552 dimension->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1553
1554 int yAdjust = dimension->GetTextBox().GetCenter().y - dimension->GetTextPos().y;
1555 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1556 dimension->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
1557
1558 m_radialDimensions.push_back( dimension.get() );
1559 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1560}
1561
1562
1564{
1565 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1566
1567 if( klayer == UNDEFINED_LAYER )
1568 {
1569 if( m_reporter )
1570 {
1571 wxString msg;
1572 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1573 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1575 }
1576
1577 klayer = Eco1_User;
1578 }
1579
1580 if( !aElem.referencePoint.empty() )
1581 {
1582 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1583
1584 // line
1585 VECTOR2I last = referencePoint0;
1586 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1587 {
1588 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1589
1590 shape->SetLayer( klayer );
1591 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1592 shape->SetStart( last );
1593 shape->SetEnd( aElem.referencePoint.at( i ) );
1594 last = aElem.referencePoint.at( i );
1595
1596 m_board->Add( shape.release(), ADD_MODE::APPEND );
1597 }
1598
1599 // arrow
1600 if( aElem.referencePoint.size() >= 2 )
1601 {
1602 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1603
1604 if( dirVec.x != 0 || dirVec.y != 0 )
1605 {
1606 double scaling = dirVec.EuclideanNorm() / aElem.arrowsize;
1607 VECTOR2I arrVec =
1608 VECTOR2I( KiROUND( dirVec.x / scaling ), KiROUND( dirVec.y / scaling ) );
1609 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1610
1611 {
1612 std::unique_ptr<PCB_SHAPE> shape1 =
1613 std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1614
1615 shape1->SetLayer( klayer );
1616 shape1->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1617 shape1->SetStart( referencePoint0 );
1618 shape1->SetEnd( referencePoint0 + arrVec );
1619
1620 m_board->Add( shape1.release(), ADD_MODE::APPEND );
1621 }
1622
1623 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1624
1625 {
1626 std::unique_ptr<PCB_SHAPE> shape2 =
1627 std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1628
1629 shape2->SetLayer( klayer );
1630 shape2->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1631 shape2->SetStart( referencePoint0 );
1632 shape2->SetEnd( referencePoint0 + arrVec );
1633
1634 m_board->Add( shape2.release(), ADD_MODE::APPEND );
1635 }
1636 }
1637 }
1638 }
1639
1640 if( aElem.textPoint.empty() )
1641 {
1642 if( m_reporter )
1643 {
1644 m_reporter->Report( wxT( "No text position present for leader dimension object" ),
1646 }
1647
1648 return;
1649 }
1650
1651 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
1652
1653 text->SetText( aElem.textformat );
1654 text->SetPosition( aElem.textPoint.at( 0 ) );
1655 text->SetLayer( klayer );
1656 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1657 text->SetTextThickness( aElem.textlinewidth );
1658 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1659 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1660
1661 m_board->Add( text.release(), ADD_MODE::APPEND );
1662}
1663
1664
1666{
1667 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1668
1669 if( klayer == UNDEFINED_LAYER )
1670 {
1671 if( m_reporter )
1672 {
1673 wxString msg;
1674 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1675 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1677 }
1678
1679 klayer = Eco1_User;
1680 }
1681
1682 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1683 {
1684 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
1685
1686 shape->SetLayer( klayer );
1687 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, LINE_STYLE::SOLID ) );
1688 shape->SetStart( aElem.referencePoint.at( i ) );
1689 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1690
1691 m_board->Add( shape.release(), ADD_MODE::APPEND );
1692 }
1693}
1694
1695
1697{
1698 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1699
1700 if( klayer == UNDEFINED_LAYER )
1701 {
1702 if( m_reporter )
1703 {
1704 wxString msg;
1705 msg.Printf( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1706 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
1708 }
1709
1710 klayer = Eco1_User;
1711 }
1712
1713 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1714 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1715
1716 std::unique_ptr<PCB_DIM_CENTER> dimension = std::make_unique<PCB_DIM_CENTER>( m_board );
1717
1718 dimension->SetLayer( klayer );
1719 dimension->SetLineThickness( aElem.linewidth );
1720 dimension->SetStart( aElem.xy1 );
1721 dimension->SetEnd( aElem.xy1 + vec );
1722
1723 m_board->Add( dimension.release(), ADD_MODE::APPEND );
1724}
1725
1726
1728 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1729{
1730 if( m_progressReporter )
1731 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1732
1733 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1734
1735 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1736 {
1737 checkpoint();
1738 ADIMENSION6 elem( reader );
1739
1740 switch( elem.kind )
1741 {
1742 case ALTIUM_DIMENSION_KIND::LINEAR:
1744 break;
1745 case ALTIUM_DIMENSION_KIND::ANGULAR:
1746 if( m_reporter )
1747 {
1749 wxString::Format( _( "Ignored Angular dimension (not yet supported)." ) ),
1751 }
1752 break;
1753 case ALTIUM_DIMENSION_KIND::RADIAL:
1755 break;
1756 case ALTIUM_DIMENSION_KIND::LEADER:
1758 break;
1759 case ALTIUM_DIMENSION_KIND::DATUM:
1760 if( m_reporter )
1761 {
1763 wxString::Format( _( "Ignored Datum dimension (not yet supported)." ) ),
1765 }
1766 // HelperParseDimensions6Datum( elem );
1767 break;
1768 case ALTIUM_DIMENSION_KIND::BASELINE:
1769 if( m_reporter )
1770 {
1772 wxString::Format( _( "Ignored Baseline dimension (not yet supported)." ) ),
1774 }
1775 break;
1776 case ALTIUM_DIMENSION_KIND::CENTER:
1778 break;
1779 case ALTIUM_DIMENSION_KIND::LINEAR_DIAMETER:
1780 if( m_reporter )
1781 {
1783 wxString::Format( _( "Ignored Linear dimension (not yet supported)." ) ),
1785 }
1786 break;
1787 case ALTIUM_DIMENSION_KIND::RADIAL_DIAMETER:
1788 if( m_reporter )
1789 {
1791 wxString::Format( _( "Ignored Radial dimension (not yet supported)." ) ),
1793 }
1794 break;
1795 default:
1796 if( m_reporter )
1797 {
1798 wxString msg;
1799 msg.Printf( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1801 }
1802 break;
1803 }
1804 }
1805
1806 if( reader.GetRemainingBytes() != 0 )
1807 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
1808}
1809
1810
1812 const CFB::COMPOUND_FILE_ENTRY* aEntry,
1813 const std::vector<std::string>& aRootDir )
1814{
1815 if( m_progressReporter )
1816 m_progressReporter->Report( _( "Loading 3D models..." ) );
1817
1818 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1819
1820 if( reader.GetRemainingBytes() == 0 )
1821 return;
1822
1823 wxString projectPath = wxPathOnly( m_board->GetFileName() );
1824 // TODO: set KIPRJMOD always after import (not only when loading project)?
1825 wxSetEnv( PROJECT_VAR_NAME, projectPath );
1826
1827 // TODO: make this path configurable?
1828 const wxString altiumModelDir = wxT( "ALTIUM_EMBEDDED_MODELS" );
1829
1830 wxFileName altiumModelsPath = wxFileName::DirName( projectPath );
1831 wxString kicadModelPrefix = wxT( "${KIPRJMOD}/" ) + altiumModelDir + wxT( "/" );
1832
1833 if( !altiumModelsPath.AppendDir( altiumModelDir ) )
1834 THROW_IO_ERROR( wxT( "Cannot construct directory path for step models" ) );
1835
1836 // Create dir if it does not exist
1837 if( !altiumModelsPath.DirExists() )
1838 {
1839 if( !altiumModelsPath.Mkdir() )
1840 {
1841 if( m_reporter )
1842 {
1843 wxString msg;
1844 msg.Printf( _( "Failed to create folder '%s'." ) + wxS( " " )
1845 + _( "No 3D-models will be imported." ),
1846 altiumModelsPath.GetFullPath() );
1848 }
1849
1850 return;
1851 }
1852 }
1853
1854 int idx = 0;
1855 wxString invalidChars = wxFileName::GetForbiddenChars();
1856
1857 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1858 {
1859 checkpoint();
1860 AMODEL elem( reader );
1861
1862 std::vector<std::string> stepPath = aRootDir;
1863 stepPath.emplace_back( std::to_string( idx ) );
1864
1865 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii() &&
1866 wxString::npos == elem.name.find_first_of( invalidChars );
1867 wxString storageName = !validName ? wxString::Format( wxT( "model_%d" ), idx )
1868 : elem.name;
1869 wxFileName storagePath( altiumModelsPath.GetPath(), storageName );
1870
1871 idx++;
1872
1873 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
1874
1875 if( stepEntry == nullptr )
1876 {
1877 if( m_reporter )
1878 {
1879 wxString msg;
1880 msg.Printf( _( "File not found: '%s'. 3D-model not imported." ),
1881 FormatPath( stepPath ) );
1883 }
1884
1885 continue;
1886 }
1887
1888 size_t stepSize = static_cast<size_t>( stepEntry->size );
1889 std::vector<char> stepContent( stepSize );
1890
1891 // read file into buffer
1892 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
1893 stepSize );
1894
1895 if( !storagePath.IsDirWritable() )
1896 {
1897 if( m_reporter )
1898 {
1899 wxString msg;
1900 msg.Printf( _( "Insufficient permissions to save file '%s'." ),
1901 storagePath.GetFullPath() );
1903 }
1904
1905 continue;
1906 }
1907
1908 wxMemoryInputStream stepStream( stepContent.data(), stepSize );
1909 wxZlibInputStream zlibInputStream( stepStream );
1910
1911 wxFFileOutputStream outputStream( storagePath.GetFullPath() );
1912
1913 if( !outputStream.IsOk() )
1914 {
1915 if( m_reporter )
1916 {
1917 wxString msg;
1918 msg.Printf( _( "Unable to write file '%s'." ), storagePath.GetFullPath() );
1920 }
1921
1922 continue;
1923 }
1924
1925 outputStream.Write( zlibInputStream );
1926 outputStream.Close();
1927
1928 m_models.insert( { elem.id, kicadModelPrefix + storageName } );
1929 }
1930
1931 if( reader.GetRemainingBytes() != 0 )
1932 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
1933}
1934
1935
1937 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1938{
1939 if( m_progressReporter )
1940 m_progressReporter->Report( _( "Loading nets..." ) );
1941
1942 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1943
1944 wxASSERT( m_altiumToKicadNetcodes.empty() );
1945
1946 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1947 {
1948 checkpoint();
1949 ANET6 elem( reader );
1950
1951 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, 0 );
1952 m_board->Add( netInfo, ADD_MODE::APPEND );
1953
1954 // needs to be called after m_board->Add() as assign us the NetCode
1955 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
1956 }
1957
1958 if( reader.GetRemainingBytes() != 0 )
1959 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
1960}
1961
1963 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1964{
1965 if( m_progressReporter )
1966 m_progressReporter->Report( _( "Loading polygons..." ) );
1967
1968 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
1969
1970 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1971 {
1972 checkpoint();
1973 APOLYGON6 elem( reader );
1974
1975 SHAPE_LINE_CHAIN linechain;
1977
1978 if( linechain.PointCount() < 3 )
1979 {
1980 // We have found multiple Altium files with polygon records containing nothing but two
1981 // coincident vertices. These polygons do not appear when opening the file in Altium.
1982 // https://gitlab.com/kicad/code/kicad/-/issues/8183
1983 // Also, polygons with less than 3 points are not supported in KiCad.
1984 //
1985 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
1986 // "points are required." ),
1987 // linechain.PointCount(),
1988 // elem.vertices.size() );
1989
1990 m_polygons.emplace_back( nullptr );
1991 continue;
1992 }
1993
1994 SHAPE_POLY_SET outline( linechain );
1995
1996 if( elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::SOLID )
1997 {
1998 // Altium "Hatched" or "None" polygon outlines have thickness, convert it to KiCad's representation.
1999 outline.Inflate( elem.trackwidth / 2, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS,
2000 ARC_HIGH_DEF, true );
2001 }
2002
2003 if( outline.OutlineCount() != 1 && m_reporter )
2004 {
2005 wxString msg;
2006 msg.Printf( _( "Polygon outline count is %d, expected 1." ), outline.OutlineCount() );
2007
2009 }
2010
2011 if( outline.OutlineCount() == 0 )
2012 continue;
2013
2014 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>(m_board);
2015 m_polygons.emplace_back(zone.get());
2016
2017 zone->SetNetCode( GetNetCode( elem.net ) );
2018 zone->SetPosition( elem.vertices.at( 0 ).position );
2019 zone->SetLocked( elem.locked );
2020 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
2021 zone->Outline()->AddOutline( outline.Outline( 0 ) );
2022
2023 HelperSetZoneLayers( *zone, elem.layer );
2024
2025 if( elem.pourindex > m_highest_pour_index )
2027
2028 const ARULE6* planeClearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
2029 const ARULE6* zoneClearanceRule = GetRule( ALTIUM_RULE_KIND::CLEARANCE,
2030 wxT( "PolygonClearance" ) );
2031 int planeLayers = 0;
2032 int signalLayers = 0;
2033 int clearance = 0;
2034
2035 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
2036 {
2037 LAYER_T layerType = m_board->GetLayerType( layer );
2038
2039 if( layerType == LT_POWER || layerType == LT_MIXED )
2040 planeLayers++;
2041
2042 if( layerType == LT_SIGNAL || layerType == LT_MIXED )
2043 signalLayers++;
2044 }
2045
2046 if( planeLayers > 0 && planeClearanceRule )
2047 clearance = std::max( clearance, planeClearanceRule->planeclearanceClearance );
2048
2049 if( signalLayers > 0 && zoneClearanceRule )
2050 clearance = std::max( clearance, zoneClearanceRule->clearanceGap );
2051
2052 if( clearance > 0 )
2053 zone->SetLocalClearance( clearance );
2054
2055 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
2056
2057 if( polygonConnectRule != nullptr )
2058 {
2059 switch( polygonConnectRule->polygonconnectStyle )
2060 {
2061 case ALTIUM_CONNECT_STYLE::DIRECT:
2062 zone->SetPadConnection( ZONE_CONNECTION::FULL );
2063 break;
2064
2065 case ALTIUM_CONNECT_STYLE::NONE:
2066 zone->SetPadConnection( ZONE_CONNECTION::NONE );
2067 break;
2068
2069 default:
2070 case ALTIUM_CONNECT_STYLE::RELIEF:
2071 zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
2072 break;
2073 }
2074
2075 // TODO: correct variables?
2076 zone->SetThermalReliefSpokeWidth(
2077 polygonConnectRule->polygonconnectReliefconductorwidth );
2078 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
2079
2080 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
2081 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
2082 }
2083
2084 if( IsAltiumLayerAPlane( elem.layer ) )
2085 {
2086 // outer zone will be set to priority 0 later.
2087 zone->SetAssignedPriority( 1 );
2088
2089 // check if this is the outer zone by simply comparing the BBOX
2090 const auto& outer_plane = m_outer_plane.find( elem.layer );
2091 if( outer_plane == m_outer_plane.end()
2092 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
2093 {
2094 m_outer_plane[elem.layer] = zone.get();
2095 }
2096 }
2097
2098 if( elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::SOLID
2099 && elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::UNKNOWN )
2100 {
2101 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
2102 zone->SetHatchThickness( elem.trackwidth );
2103
2104 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::NONE )
2105 {
2106 // use a small hack to get us only an outline (hopefully)
2107 const BOX2I& bbox = zone->GetBoundingBox();
2108 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
2109 }
2110 else
2111 {
2112 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
2113 }
2114
2115 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45 )
2116 zone->SetHatchOrientation( ANGLE_45 );
2117 }
2118
2119 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2121
2122 m_board->Add( zone.release(), ADD_MODE::APPEND );
2123 }
2124
2125 if( reader.GetRemainingBytes() != 0 )
2126 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
2127}
2128
2130 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2131{
2132 if( m_progressReporter )
2133 m_progressReporter->Report( _( "Loading rules..." ) );
2134
2135 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2136
2137 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2138 {
2139 checkpoint();
2140 ARULE6 elem( reader );
2141
2142 m_rules[elem.kind].emplace_back( elem );
2143 }
2144
2145 // sort rules by priority
2146 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
2147 {
2148 std::sort( val.second.begin(), val.second.end(),
2149 []( const ARULE6& lhs, const ARULE6& rhs )
2150 {
2151 return lhs.priority < rhs.priority;
2152 } );
2153 }
2154
2155 const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::CLEARANCE );
2156 const ARULE6* trackWidthRule = GetRuleDefault( ALTIUM_RULE_KIND::WIDTH );
2157 const ARULE6* routingViasRule = GetRuleDefault( ALTIUM_RULE_KIND::ROUTING_VIAS );
2158 const ARULE6* holeSizeRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_SIZE );
2159 const ARULE6* holeToHoleRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE );
2160
2161 if( clearanceRule )
2163
2164 if( trackWidthRule )
2165 {
2167 // TODO: construct a custom rule for preferredWidth and maxLimit values
2168 }
2169
2170 if( routingViasRule )
2171 {
2172 m_board->GetDesignSettings().m_ViasMinSize = routingViasRule->minWidth;
2174 }
2175
2176 if( holeSizeRule )
2177 {
2178 // TODO: construct a custom rule for minLimit / maxLimit values
2179 }
2180
2181 if( holeToHoleRule )
2183
2184 const ARULE6* soldermaskRule = GetRuleDefault( ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION );
2185 const ARULE6* pastemaskRule = GetRuleDefault( ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION );
2186
2187 if( soldermaskRule )
2189
2190 if( pastemaskRule )
2192
2193 if( reader.GetRemainingBytes() != 0 )
2194 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
2195}
2196
2198 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2199{
2200 if( m_progressReporter )
2201 m_progressReporter->Report( _( "Loading board regions..." ) );
2202
2203 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2204
2205 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2206 {
2207 checkpoint();
2208 AREGION6 elem( reader, false );
2209
2210 // TODO: implement?
2211 }
2212
2213 if( reader.GetRemainingBytes() != 0 )
2214 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
2215}
2216
2218 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2219{
2220 if( m_progressReporter )
2221 m_progressReporter->Report( _( "Loading polygons..." ) );
2222
2223 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2224
2225 /* TODO: use Header section of file */
2226 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2227 {
2228 checkpoint();
2229 AREGION6 elem( reader, true );
2230
2232 || elem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2233 {
2234 // TODO: implement all different types for footprints
2236 }
2237 else
2238 {
2239 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2240 ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
2241 }
2242 }
2243
2244 if( reader.GetRemainingBytes() != 0 )
2245 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
2246}
2247
2248
2250{
2251 if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2252 {
2254 }
2255 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2256 {
2257 SHAPE_LINE_CHAIN linechain;
2259
2260 if( linechain.PointCount() < 3 )
2261 {
2262 // We have found multiple Altium files with polygon records containing nothing but
2263 // two coincident vertices. These polygons do not appear when opening the file in
2264 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2265 // Also, polygons with less than 3 points are not supported in KiCad.
2266 return;
2267 }
2268
2269 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
2270
2271 zone->SetIsRuleArea( true );
2272
2273 if( aElem.is_keepout )
2274 {
2276 }
2277 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2278 {
2279 zone->SetDoNotAllowCopperPour( true );
2280 zone->SetDoNotAllowVias( false );
2281 zone->SetDoNotAllowTracks( false );
2282 zone->SetDoNotAllowPads( false );
2283 zone->SetDoNotAllowFootprints( false );
2284 }
2285
2286 zone->SetPosition( aElem.outline.at( 0 ).position );
2287 zone->Outline()->AddOutline( linechain );
2288
2289 HelperSetZoneLayers( *zone, aElem.layer );
2290
2291 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2293
2294 m_board->Add( zone.release(), ADD_MODE::APPEND );
2295 }
2296 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2297 {
2298 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2299
2300 if( klayer == UNDEFINED_LAYER )
2301 {
2302 if( m_reporter )
2303 {
2304 wxString msg;
2305 msg.Printf( _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
2306 "It has been moved to KiCad layer Eco1_User." ), aElem.layer );
2308 }
2309
2310 klayer = Eco1_User;
2311 }
2312
2313 SHAPE_LINE_CHAIN linechain;
2315
2316 if( linechain.PointCount() < 3 )
2317 {
2318 // We have found multiple Altium files with polygon records containing nothing but
2319 // two coincident vertices. These polygons do not appear when opening the file in
2320 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2321 // Also, polygons with less than 3 points are not supported in KiCad.
2322 return;
2323 }
2324
2325 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2326
2327 shape->SetPolyShape( linechain );
2328 shape->SetFilled( false );
2329 shape->SetLayer( klayer );
2330 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2331
2332 m_board->Add( shape.release(), ADD_MODE::APPEND );
2333 }
2334 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2335 {
2336 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2337 {
2338 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2340 }
2341 }
2342 else if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2343 {
2345 }
2346 else
2347 {
2348 if( m_reporter )
2349 {
2350 wxString msg;
2351 msg.Printf( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2353 }
2354 }
2355}
2356
2357
2359 const AREGION6& aElem,
2360 const int aPrimitiveIndex )
2361{
2362 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2363 {
2364 SHAPE_LINE_CHAIN linechain;
2366
2367 if( linechain.PointCount() < 3 )
2368 {
2369 // We have found multiple Altium files with polygon records containing nothing but
2370 // two coincident vertices. These polygons do not appear when opening the file in
2371 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2372 // Also, polygons with less than 3 points are not supported in KiCad.
2373 return;
2374 }
2375
2376 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
2377
2378 zone->SetIsRuleArea( true );
2379
2380 if( aElem.is_keepout )
2381 {
2383 }
2384 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2385 {
2386 zone->SetDoNotAllowCopperPour( true );
2387 zone->SetDoNotAllowVias( false );
2388 zone->SetDoNotAllowTracks( false );
2389 zone->SetDoNotAllowPads( false );
2390 zone->SetDoNotAllowFootprints( false );
2391 }
2392
2393 zone->SetPosition( aElem.outline.at( 0 ).position );
2394 zone->Outline()->AddOutline( linechain );
2395
2396 HelperSetZoneLayers( *zone, aElem.layer );
2397
2398 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2400
2401 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
2402 }
2403 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2404 {
2405 if( aElem.polygon == ALTIUM_POLYGON_NONE )
2406 {
2407 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2408 {
2409 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
2410 aPrimitiveIndex );
2411 }
2412 }
2413 }
2414 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE
2415 || aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2416 {
2417 PCB_LAYER_ID klayer = aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT
2418 ? Edge_Cuts
2419 : GetKicadLayer( aElem.layer );
2420
2421 if( klayer == UNDEFINED_LAYER )
2422 {
2423 if( !m_footprintName.IsEmpty() )
2424 {
2425 if( m_reporter )
2426 {
2427 wxString msg;
2428 msg.Printf( _( "Loading library '%s':\n"
2429 "Footprint %s contains a dashed outline on Altium layer (%d) with "
2430 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2431 m_library,
2433 aElem.layer );
2435 }
2436 }
2437 else
2438 {
2439 if( m_reporter )
2440 {
2441 wxString msg;
2442 msg.Printf( _( "Footprint %s contains a dashed outline on Altium layer (%d) with "
2443 "no KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
2444 aFootprint->GetReference(),
2445 aElem.layer );
2447 }
2448 }
2449
2450 klayer = Eco1_User;
2451 }
2452
2453 SHAPE_LINE_CHAIN linechain;
2455
2456 if( linechain.PointCount() < 3 )
2457 {
2458 // We have found multiple Altium files with polygon records containing nothing but
2459 // two coincident vertices. These polygons do not appear when opening the file in
2460 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2461 // Also, polygons with less than 3 points are not supported in KiCad.
2462 return;
2463 }
2464
2465 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2466
2467 shape->SetPolyShape( linechain );
2468 shape->SetFilled( false );
2469 shape->SetLayer( klayer );
2470
2471 if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2472 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::DASH ) );
2473 else
2474 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), LINE_STYLE::SOLID ) );
2475
2476 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2477 }
2478 else
2479 {
2480 if( !m_footprintName.IsEmpty() )
2481 {
2482 if( m_reporter )
2483 {
2484 wxString msg;
2485 msg.Printf( _( "Error loading library '%s':\n"
2486 "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2487 m_library,
2489 aElem.kind );
2491 }
2492 }
2493 else
2494 {
2495 if( m_reporter )
2496 {
2497 wxString msg;
2498 msg.Printf( _( "Footprint %s contains polygon shape of kind %d (not yet supported)." ),
2499 aFootprint->GetReference(),
2500 aElem.kind );
2502 }
2503 }
2504 }
2505}
2506
2507
2509 PCB_LAYER_ID aLayer )
2510{
2511 SHAPE_LINE_CHAIN linechain;
2513
2514 if( linechain.PointCount() < 3 )
2515 {
2516 // We have found multiple Altium files with polygon records containing nothing
2517 // but two coincident vertices. These polygons do not appear when opening the
2518 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2519 // Also, polygons with less than 3 points are not supported in KiCad.
2520 return;
2521 }
2522
2523 SHAPE_POLY_SET polySet;
2524 polySet.AddOutline( linechain );
2525
2526 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2527 {
2528 SHAPE_LINE_CHAIN hole_linechain;
2529 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2530
2531 if( hole_linechain.PointCount() < 3 )
2532 continue;
2533
2534 polySet.AddHole( hole_linechain );
2535 }
2536
2537 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::POLY );
2538
2539 shape->SetPolyShape( polySet );
2540 shape->SetFilled( true );
2541 shape->SetLayer( aLayer );
2542 shape->SetStroke( STROKE_PARAMS( 0 ) );
2543
2544 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2545 {
2546 shape->SetNetCode( GetNetCode( aElem.net ) );
2547 }
2548
2549 m_board->Add( shape.release(), ADD_MODE::APPEND );
2550}
2551
2552
2554 const AREGION6& aElem,
2555 PCB_LAYER_ID aLayer,
2556 const int aPrimitiveIndex )
2557{
2558 SHAPE_LINE_CHAIN linechain;
2560
2561 if( linechain.PointCount() < 3 )
2562 {
2563 // We have found multiple Altium files with polygon records containing nothing
2564 // but two coincident vertices. These polygons do not appear when opening the
2565 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2566 // Also, polygons with less than 3 points are not supported in KiCad.
2567 return;
2568 }
2569
2570 SHAPE_POLY_SET polySet;
2571 polySet.AddOutline( linechain );
2572
2573 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2574 {
2575 SHAPE_LINE_CHAIN hole_linechain;
2576 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2577
2578 if( hole_linechain.PointCount() < 3 )
2579 continue;
2580
2581 polySet.AddHole( hole_linechain );
2582 }
2583
2584 if( aLayer == F_Cu || aLayer == B_Cu )
2585 {
2586 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
2587
2588 LSET padLayers;
2589 padLayers.set( aLayer );
2590
2591 pad->SetAttribute( PAD_ATTRIB::SMD );
2592 pad->SetShape( PAD_SHAPE::CUSTOM );
2593 pad->SetThermalSpokeAngle( ANGLE_90 );
2594
2595 int anchorSize = 1;
2596 VECTOR2I anchorPos = linechain.CPoint( 0 );
2597
2598 pad->SetShape( PAD_SHAPE::CUSTOM );
2599 pad->SetAnchorPadShape( PAD_SHAPE::CIRCLE );
2600 pad->SetSize( { anchorSize, anchorSize } );
2601 pad->SetPosition( anchorPos );
2602
2603 SHAPE_POLY_SET shapePolys = polySet;
2604 shapePolys.Move( -anchorPos );
2605 pad->AddPrimitivePoly( shapePolys, 0, true );
2606
2607 auto& map = m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::REGION];
2608 auto it = map.find( aPrimitiveIndex );
2609
2610 if( it != map.end() )
2611 {
2612 const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
2613
2614 if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2615 {
2616 pad->SetLocalSolderPasteMargin(
2617 info.pastemaskexpansionmanual ? info.pastemaskexpansionmanual : 1 );
2618 }
2619
2620 if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2621 {
2622 pad->SetLocalSolderMaskMargin(
2623 info.soldermaskexpansionmanual ? info.soldermaskexpansionmanual : 1 );
2624 }
2625
2626 if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
2627 padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
2628
2629 if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
2630 padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
2631 }
2632
2633 pad->SetLayerSet( padLayers );
2634
2635 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
2636 }
2637 else
2638 {
2639 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
2640
2641 shape->SetPolyShape( polySet );
2642 shape->SetFilled( true );
2643 shape->SetLayer( aLayer );
2644 shape->SetStroke( STROKE_PARAMS( 0 ) );
2645
2646 aFootprint->Add( shape.release(), ADD_MODE::APPEND );
2647 }
2648}
2649
2650
2652 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2653{
2654 if( m_progressReporter )
2655 m_progressReporter->Report( _( "Loading zone fills..." ) );
2656
2657 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2658
2659 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2660 {
2661 checkpoint();
2662 AREGION6 elem( reader, false );
2663
2664 if( elem.polygon != ALTIUM_POLYGON_NONE )
2665 {
2666 if( m_polygons.size() <= elem.polygon )
2667 {
2668 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2669 "of %d existing polygons.",
2670 elem.polygon,
2671 m_polygons.size() ) );
2672 }
2673
2674 ZONE* zone = m_polygons.at( elem.polygon );
2675
2676 if( zone == nullptr )
2677 {
2678 continue; // we know the zone id, but because we do not know the layer we did not
2679 // add it!
2680 }
2681
2682 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2683
2684 if( klayer == UNDEFINED_LAYER )
2685 continue; // Just skip it for now. Users can fill it themselves.
2686
2687 SHAPE_LINE_CHAIN linechain;
2688
2689 for( const ALTIUM_VERTICE& vertice : elem.outline )
2690 linechain.Append( vertice.position );
2691
2692 linechain.Append( elem.outline.at( 0 ).position );
2693 linechain.SetClosed( true );
2694
2695 SHAPE_POLY_SET fill;
2696 fill.AddOutline( linechain );
2697
2698 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2699 {
2700 SHAPE_LINE_CHAIN hole_linechain;
2701
2702 for( const ALTIUM_VERTICE& vertice : hole )
2703 hole_linechain.Append( vertice.position );
2704
2705 hole_linechain.Append( hole.at( 0 ).position );
2706 hole_linechain.SetClosed( true );
2707 fill.AddHole( hole_linechain );
2708 }
2709
2710 if( zone->HasFilledPolysForLayer( klayer ) )
2711 fill.BooleanAdd( *zone->GetFill( klayer ), SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
2712
2714
2715 zone->SetFilledPolysList( klayer, fill );
2716 zone->SetIsFilled( true );
2717 zone->SetNeedRefill( false );
2718 }
2719 }
2720
2721 if( reader.GetRemainingBytes() != 0 )
2722 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2723}
2724
2725
2727 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2728{
2729 if( m_progressReporter )
2730 m_progressReporter->Report( _( "Loading arcs..." ) );
2731
2732 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2733
2734 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2735 {
2736 checkpoint();
2737 AARC6 elem( reader );
2738
2739 if( elem.component == ALTIUM_COMPONENT_NONE )
2740 {
2741 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2742 }
2743 else
2744 {
2745 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2746 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2747 }
2748 }
2749
2750 if( reader.GetRemainingBytes() != 0 )
2751 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2752}
2753
2754
2756{
2757 if( aElem.startangle == 0. && aElem.endangle == 360. )
2758 {
2759 aShape->SetShape( SHAPE_T::CIRCLE );
2760
2761 // TODO: other variants to define circle?
2762 aShape->SetStart( aElem.center );
2763 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
2764 }
2765 else
2766 {
2767 aShape->SetShape( SHAPE_T::ARC );
2768
2769 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2770 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2771
2772 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2773 -KiROUND( startAngle.Sin() * aElem.radius ) );
2774
2775 aShape->SetCenter( aElem.center );
2776 aShape->SetStart( aElem.center + startOffset );
2777 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
2778 }
2779}
2780
2781
2782void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
2783{
2784 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
2785 {
2786 if( m_polygons.size() <= aElem.polygon )
2787 {
2788 THROW_IO_ERROR( wxString::Format( "Tracks stream tries to access polygon id %u "
2789 "of %zu existing polygons.",
2790 aElem.polygon, m_polygons.size() ) );
2791 }
2792
2793 ZONE* zone = m_polygons.at( aElem.polygon );
2794
2795 if( zone == nullptr )
2796 {
2797 return; // we know the zone id, but because we do not know the layer we did not
2798 // add it!
2799 }
2800
2801 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2802
2803 if( klayer == UNDEFINED_LAYER )
2804 return; // Just skip it for now. Users can fill it themselves.
2805
2806 if( !zone->HasFilledPolysForLayer( klayer ) )
2807 return;
2808
2809 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
2810
2811 // This is not the actual board item. We can use it to create the polygon for the region
2812 PCB_SHAPE shape( nullptr );
2813
2814 ConvertArcs6ToPcbShape( aElem, &shape );
2815 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2816
2817 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
2818 // Will be simplified and fractured later
2819
2820 zone->SetIsFilled( true );
2821 zone->SetNeedRefill( false );
2822
2823 return;
2824 }
2825
2826 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2827 || IsAltiumLayerAPlane( aElem.layer ) )
2828 {
2829 // This is not the actual board item. We can use it to create the polygon for the region
2830 PCB_SHAPE shape( nullptr );
2831
2832 ConvertArcs6ToPcbShape( aElem, &shape );
2833 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2834
2836 }
2837 else
2838 {
2839 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2840 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2841 }
2842
2843 for( const auto& layerExpansionMask :
2844 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
2845 {
2846 int width = aElem.width + ( layerExpansionMask.second * 2 );
2847
2848 if( width > 1 )
2849 {
2850 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
2851
2852 ConvertArcs6ToPcbShape( aElem, arc.get() );
2853 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
2854 arc->SetLayer( layerExpansionMask.first );
2855
2856 m_board->Add( arc.release(), ADD_MODE::APPEND );
2857 }
2858 }
2859}
2860
2861
2863 const int aPrimitiveIndex, const bool aIsBoardImport )
2864{
2865 if( aElem.polygon != ALTIUM_POLYGON_NONE )
2866 {
2867 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Arc with polygon id %d",
2868 aElem.polygon ) );
2869 return;
2870 }
2871
2872 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2873 || IsAltiumLayerAPlane( aElem.layer ) )
2874 {
2875 // This is not the actual board item. We can use it to create the polygon for the region
2876 PCB_SHAPE shape( nullptr );
2877
2878 ConvertArcs6ToPcbShape( aElem, &shape );
2879 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2880
2881 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
2882 aElem.keepoutrestrictions );
2883 }
2884 else
2885 {
2886 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2887 {
2888 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2889 {
2890 // Special case: do to not lose net connections in footprints
2891 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2892 }
2893 else
2894 {
2895 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
2896 }
2897 }
2898 }
2899
2900 for( const auto& layerExpansionMask :
2901 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
2902 {
2903 int width = aElem.width + ( layerExpansionMask.second * 2 );
2904
2905 if( width > 1 )
2906 {
2907 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
2908
2909 ConvertArcs6ToPcbShape( aElem, arc.get() );
2910 arc->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
2911 arc->SetLayer( layerExpansionMask.first );
2912
2913 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
2914 }
2915 }
2916}
2917
2918
2920{
2921 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2922 {
2923 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
2924 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
2925
2926 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2927 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2928
2929 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2930 -KiROUND( startAngle.Sin() * aElem.radius ) );
2931
2932 shape.SetCenter( aElem.center );
2933 shape.SetStart( aElem.center + startOffset );
2934 shape.SetArcAngleAndEnd( includedAngle.Normalize(), true );
2935
2936 // Create actual arc
2937 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(), aElem.width );
2938 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board, &shapeArc );
2939
2940 arc->SetWidth( aElem.width );
2941 arc->SetLayer( aLayer );
2942 arc->SetNetCode( GetNetCode( aElem.net ) );
2943
2944 m_board->Add( arc.release(), ADD_MODE::APPEND );
2945 }
2946 else
2947 {
2948 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( m_board );
2949
2950 ConvertArcs6ToPcbShape( aElem, arc.get() );
2951 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2952 arc->SetLayer( aLayer );
2953
2954 m_board->Add( arc.release(), ADD_MODE::APPEND );
2955 }
2956}
2957
2958
2960 PCB_LAYER_ID aLayer )
2961{
2962 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( aFootprint );
2963
2964 ConvertArcs6ToPcbShape( aElem, arc.get() );
2965 arc->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
2966 arc->SetLayer( aLayer );
2967
2968 aFootprint->Add( arc.release(), ADD_MODE::APPEND );
2969}
2970
2971
2973 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2974{
2975 if( m_progressReporter )
2976 m_progressReporter->Report( _( "Loading pads..." ) );
2977
2978 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
2979
2980 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2981 {
2982 checkpoint();
2983 APAD6 elem( reader );
2984
2985 if( elem.component == ALTIUM_COMPONENT_NONE )
2986 {
2988 }
2989 else
2990 {
2991 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2992 ConvertPads6ToFootprintItem( footprint, elem );
2993 }
2994 }
2995
2996 if( reader.GetRemainingBytes() != 0 )
2997 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
2998}
2999
3000
3002{
3003 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3004 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3005 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3006 {
3008 }
3009 else
3010 {
3011 // We cannot add a pad directly into the PCB
3012 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
3013 footprint->SetPosition( aElem.position );
3014
3015 ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem );
3016
3017 m_board->Add( footprint.release(), ADD_MODE::APPEND );
3018 }
3019}
3020
3021
3023{
3024 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
3025 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
3026 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3027 {
3028 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
3029 }
3030 else
3031 {
3032 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
3033 }
3034}
3035
3036
3038{
3039 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
3040
3041 pad->SetNumber( aElem.name );
3042 pad->SetNetCode( GetNetCode( aElem.net ) );
3043
3044 pad->SetPosition( aElem.position );
3045 pad->SetOrientationDegrees( aElem.direction );
3046 pad->SetSize( aElem.topsize );
3047 pad->SetThermalSpokeAngle( ANGLE_90 );
3048
3049 if( aElem.holesize == 0 )
3050 {
3051 pad->SetAttribute( PAD_ATTRIB::SMD );
3052 }
3053 else
3054 {
3055 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
3056 {
3057 // TODO: I assume other values are possible as well?
3058 if( !m_footprintName.IsEmpty() )
3059 {
3060 if( m_reporter )
3061 {
3062 wxString msg;
3063 msg.Printf( _( "Error loading library '%s':\n"
3064 "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3065 m_library,
3067 aElem.name );
3069 }
3070 }
3071 else
3072 {
3073 if( m_reporter )
3074 {
3075 wxString msg;
3076 msg.Printf( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
3077 aFootprint->GetReference(),
3078 aElem.name );
3080 }
3081 }
3082 }
3083
3084 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
3085
3086 if( !aElem.sizeAndShape || aElem.sizeAndShape->holeshape == ALTIUM_PAD_HOLE_SHAPE::ROUND )
3087 {
3088 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3089 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
3090 }
3091 else
3092 {
3093 switch( aElem.sizeAndShape->holeshape )
3094 {
3095 case ALTIUM_PAD_HOLE_SHAPE::ROUND:
3096 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
3097 break;
3098
3099 case ALTIUM_PAD_HOLE_SHAPE::SQUARE:
3100 if( !m_footprintName.IsEmpty() )
3101 {
3102 if( m_reporter )
3103 {
3104 wxString msg;
3105 msg.Printf( _( "Loading library '%s':\n"
3106 "Footprint %s pad %s has a square hole (not yet supported)." ),
3107 m_library,
3109 aElem.name );
3111 }
3112 }
3113 else
3114 {
3115 if( m_reporter )
3116 {
3117 wxString msg;
3118 msg.Printf( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
3119 aFootprint->GetReference(),
3120 aElem.name );
3122 }
3123 }
3124
3125 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3126 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3127 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
3128 // this case or rect holes have a different id
3129 break;
3130
3131 case ALTIUM_PAD_HOLE_SHAPE::SLOT:
3132 {
3133 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
3134 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
3135
3136 slotRotation.Normalize();
3137
3138 if( slotRotation.IsHorizontal() )
3139 {
3140 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
3141 }
3142 else if( slotRotation.IsVertical() )
3143 {
3144 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
3145 }
3146 else
3147 {
3148 if( !m_footprintName.IsEmpty() )
3149 {
3150 if( m_reporter )
3151 {
3152 wxString msg;
3153 msg.Printf( _( "Loading library '%s':\n"
3154 "Footprint %s pad %s has a hole-rotation of %f degrees. "
3155 "KiCad only supports 90 degree rotations." ),
3156 m_library,
3158 aElem.name,
3159 slotRotation.AsDegrees() );
3161 }
3162 }
3163 else
3164 {
3165 if( m_reporter )
3166 {
3167 wxString msg;
3168 msg.Printf( _( "Footprint %s pad %s has a hole-rotation of %f degrees. "
3169 "KiCad only supports 90 degree rotations." ),
3170 aFootprint->GetReference(),
3171 aElem.name,
3172 slotRotation.AsDegrees() );
3174 }
3175 }
3176 }
3177
3178 break;
3179 }
3180
3181 default:
3182 case ALTIUM_PAD_HOLE_SHAPE::UNKNOWN:
3183 if( !m_footprintName.IsEmpty() )
3184 {
3185 if( m_reporter )
3186 {
3187 wxString msg;
3188 msg.Printf( _( "Error loading library '%s':\n"
3189 "Footprint %s pad %s uses a hole of unknown kind %d." ),
3190 m_library,
3192 aElem.name,
3193 aElem.sizeAndShape->holeshape );
3195 }
3196 }
3197 else
3198 {
3199 if( m_reporter )
3200 {
3201 wxString msg;
3202 msg.Printf( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
3203 aFootprint->GetReference(),
3204 aElem.name,
3205 aElem.sizeAndShape->holeshape );
3207 }
3208 }
3209
3210 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
3211 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
3212 break;
3213 }
3214 }
3215
3216 if( aElem.sizeAndShape )
3217 pad->SetOffset( aElem.sizeAndShape->holeoffset[0] );
3218 }
3219
3220 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3221 {
3222 if( !m_footprintName.IsEmpty() )
3223 {
3224 if( m_reporter )
3225 {
3226 wxString msg;
3227 msg.Printf( _( "Error loading library '%s':\n"
3228 "Footprint %s pad %s uses a complex pad stack (not yet supported)." ),
3229 m_library,
3231 aElem.name );
3233 }
3234 }
3235 else
3236 {
3237 if( m_reporter )
3238 {
3239 wxString msg;
3240 msg.Printf( _( "Footprint %s pad %s uses a complex pad stack (not yet supported)." ),
3241 aFootprint->GetReference(),
3242 aElem.name );
3244 }
3245 }
3246 }
3247
3248 switch( aElem.topshape )
3249 {
3250 case ALTIUM_PAD_SHAPE::RECT:
3251 pad->SetShape( PAD_SHAPE::RECTANGLE );
3252 break;
3253
3254 case ALTIUM_PAD_SHAPE::CIRCLE:
3255 if( aElem.sizeAndShape
3256 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
3257 {
3258 pad->SetShape( PAD_SHAPE::ROUNDRECT ); // 100 = round, 0 = rectangular
3259 double ratio = aElem.sizeAndShape->cornerradius[0] / 200.;
3260 pad->SetRoundRectRadiusRatio( ratio );
3261 }
3262 else if( aElem.topsize.x == aElem.topsize.y )
3263 {
3264 pad->SetShape( PAD_SHAPE::CIRCLE );
3265 }
3266 else
3267 {
3268 pad->SetShape( PAD_SHAPE::OVAL );
3269 }
3270
3271 break;
3272
3273 case ALTIUM_PAD_SHAPE::OCTAGONAL:
3274 pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
3275 pad->SetChamferPositions( RECT_CHAMFER_ALL );
3276 pad->SetChamferRectRatio( 0.25 );
3277 break;
3278
3279 case ALTIUM_PAD_SHAPE::UNKNOWN:
3280 default:
3281 if( !m_footprintName.IsEmpty() )
3282 {
3283 if( m_reporter )
3284 {
3285 wxString msg;
3286 msg.Printf( _( "Error loading library '%s':\n"
3287 "Footprint %s pad %s uses an unknown pad-shape." ),
3288 m_library,
3290 aElem.name );
3292 }
3293 }
3294 else
3295 {
3296 if( m_reporter )
3297 {
3298 wxString msg;
3299 msg.Printf( _( "Footprint %s pad %s uses an unknown pad-shape." ),
3300 aFootprint->GetReference(),
3301 aElem.name );
3303 }
3304 }
3305 break;
3306 }
3307
3308 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
3309 {
3310 // KiCad likes NPTH pads to be the same size & shape as their holes
3311 pad->SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE ? PAD_SHAPE::CIRCLE
3312 : PAD_SHAPE::OVAL );
3313 pad->SetSize( pad->GetDrillSize() );
3314 }
3315
3316 switch( aElem.layer )
3317 {
3318 case ALTIUM_LAYER::TOP_LAYER:
3319 pad->SetLayer( F_Cu );
3320 pad->SetLayerSet( PAD::SMDMask() );
3321 break;
3322
3323 case ALTIUM_LAYER::BOTTOM_LAYER:
3324 pad->SetLayer( B_Cu );
3325 pad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
3326 break;
3327
3328 case ALTIUM_LAYER::MULTI_LAYER:
3329 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
3330 break;
3331
3332 default:
3333 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3334 pad->SetLayer( klayer );
3335 pad->SetLayerSet( LSET( 1, klayer ) );
3336 break;
3337 }
3338
3339 if( aElem.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
3340 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
3341
3342 if( aElem.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
3343 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
3344
3345 if( aElem.is_tent_top )
3346 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
3347
3348 if( aElem.is_tent_bottom )
3349 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
3350
3351 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3352}
3353
3354
3356{
3357 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3358
3359 if( klayer == UNDEFINED_LAYER )
3360 {
3361 if( m_reporter )
3362 {
3363 wxString msg;
3364 msg.Printf( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
3365 "equivalent. It has been moved to KiCad layer Eco1_User." ),
3366 aElem.name, aElem.layer );
3368 }
3369
3370 klayer = Eco1_User;
3371 }
3372
3373 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( m_board );
3374
3375 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3376
3377 m_board->Add( pad.release(), ADD_MODE::APPEND );
3378}
3379
3380
3382{
3383 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3384
3385 if( klayer == UNDEFINED_LAYER )
3386 {
3387 if( !m_footprintName.IsEmpty() )
3388 {
3389 if( m_reporter )
3390 {
3391 wxString msg;
3392 msg.Printf( _( "Loading library '%s':\n"
3393 "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3394 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3395 m_library,
3397 aElem.name,
3398 aElem.layer );
3400 }
3401 }
3402 else
3403 {
3404 if( m_reporter )
3405 {
3406 wxString msg;
3407 msg.Printf( _( "Footprint %s non-copper pad %s found on an Altium layer (%d) with no "
3408 "KiCad equivalent. It has been moved to KiCad layer Eco1_User." ),
3409 aFootprint->GetReference(),
3410 aElem.name,
3411 aElem.layer );
3413 }
3414 }
3415
3416 klayer = Eco1_User;
3417 }
3418
3419 std::unique_ptr<PCB_SHAPE> pad = std::make_unique<PCB_SHAPE>( aFootprint );
3420
3421 HelperParsePad6NonCopper( aElem, klayer, pad.get() );
3422
3423 aFootprint->Add( pad.release(), ADD_MODE::APPEND );
3424}
3425
3426
3428 PCB_SHAPE* aShape )
3429{
3430 if( aElem.net != ALTIUM_NET_UNCONNECTED )
3431 {
3432 if( m_reporter )
3433 {
3434 wxString msg;
3435 msg.Printf( _( "Non-copper pad %s is connected to a net, which is not supported." ),
3436 aElem.name );
3438 }
3439 }
3440
3441 if( aElem.holesize != 0 )
3442 {
3443 if( m_reporter )
3444 {
3445 wxString msg;
3446 msg.Printf( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
3448 }
3449 }
3450
3451 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
3452 {
3453 if( m_reporter )
3454 {
3455 wxString msg;
3456 msg.Printf( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
3457 aElem.name );
3459 }
3460 }
3461
3462 switch( aElem.topshape )
3463 {
3464 case ALTIUM_PAD_SHAPE::RECT:
3465 {
3466 // filled rect
3467 aShape->SetShape( SHAPE_T::POLY );
3468 aShape->SetFilled( true );
3469 aShape->SetLayer( aLayer );
3470 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3471
3472 aShape->SetPolyPoints(
3473 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
3474 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3475 aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
3476 aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
3477
3478 if( aElem.direction != 0 )
3479 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3480 }
3481 break;
3482
3483 case ALTIUM_PAD_SHAPE::CIRCLE:
3484 if( aElem.sizeAndShape
3485 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
3486 {
3487 // filled roundrect
3488 int cornerradius = aElem.sizeAndShape->cornerradius[0];
3489 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
3490
3491 aShape->SetLayer( aLayer );
3492 aShape->SetStroke( STROKE_PARAMS( offset * 2, LINE_STYLE::SOLID ) );
3493
3494 if( cornerradius < 100 )
3495 {
3496 int offsetX = aElem.topsize.x / 2 - offset;
3497 int offsetY = aElem.topsize.y / 2 - offset;
3498
3499 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
3500 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
3501 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
3502 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
3503
3504 aShape->SetShape( SHAPE_T::POLY );
3505 aShape->SetFilled( true );
3506 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
3507 }
3508 else if( aElem.topsize.x == aElem.topsize.y )
3509 {
3510 // circle
3511 aShape->SetShape( SHAPE_T::CIRCLE );
3512 aShape->SetFilled( true );
3513 aShape->SetStart( aElem.position );
3514 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3515 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3516 }
3517 else if( aElem.topsize.x < aElem.topsize.y )
3518 {
3519 // short vertical line
3520 aShape->SetShape( SHAPE_T::SEGMENT );
3521 VECTOR2I pointOffset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
3522 aShape->SetStart( aElem.position + pointOffset );
3523 aShape->SetEnd( aElem.position - pointOffset );
3524 }
3525 else
3526 {
3527 // short horizontal line
3528 aShape->SetShape( SHAPE_T::SEGMENT );
3529 VECTOR2I pointOffset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
3530 aShape->SetStart( aElem.position + pointOffset );
3531 aShape->SetEnd( aElem.position - pointOffset );
3532 }
3533
3534 if( aElem.direction != 0 )
3535 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3536 }
3537 else if( aElem.topsize.x == aElem.topsize.y )
3538 {
3539 // filled circle
3540 aShape->SetShape( SHAPE_T::CIRCLE );
3541 aShape->SetFilled( true );
3542 aShape->SetLayer( aLayer );
3543 aShape->SetStart( aElem.position );
3544 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
3545 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, LINE_STYLE::SOLID ) );
3546 }
3547 else
3548 {
3549 // short line
3550 aShape->SetShape( SHAPE_T::SEGMENT );
3551 aShape->SetLayer( aLayer );
3552 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
3553 LINE_STYLE::SOLID ) );
3554
3555 if( aElem.topsize.x < aElem.topsize.y )
3556 {
3557 VECTOR2I offset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
3558 aShape->SetStart( aElem.position + offset );
3559 aShape->SetEnd( aElem.position - offset );
3560 }
3561 else
3562 {
3563 VECTOR2I offset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
3564 aShape->SetStart( aElem.position + offset );
3565 aShape->SetEnd( aElem.position - offset );
3566 }
3567
3568 if( aElem.direction != 0 )
3569 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3570 }
3571 break;
3572
3573 case ALTIUM_PAD_SHAPE::OCTAGONAL:
3574 {
3575 // filled octagon
3576 aShape->SetShape( SHAPE_T::POLY );
3577 aShape->SetFilled( true );
3578 aShape->SetLayer( aLayer );
3579 aShape->SetStroke( STROKE_PARAMS( 0 ) );
3580
3581 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
3582 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3583 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
3584 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
3585
3586 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
3587 VECTOR2I chamferX( chamfer, 0 );
3588 VECTOR2I chamferY( 0, chamfer );
3589
3590 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
3591 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
3592
3593 if( aElem.direction != 0. )
3594 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
3595 }
3596 break;
3597
3598 case ALTIUM_PAD_SHAPE::UNKNOWN:
3599 default:
3600 if( m_reporter )
3601 {
3602 wxString msg;
3603 msg.Printf( _( "Non-copper pad %s uses an unknown pad-shape." ), aElem.name );
3605 }
3606
3607 break;
3608 }
3609}
3610
3611
3613 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3614{
3615 if( m_progressReporter )
3616 m_progressReporter->Report( _( "Loading vias..." ) );
3617
3618 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3619
3620 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3621 {
3622 checkpoint();
3623 AVIA6 elem( reader );
3624
3625 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
3626
3627 via->SetPosition( elem.position );
3628 via->SetWidth( elem.diameter );
3629 via->SetDrill( elem.holesize );
3630 via->SetNetCode( GetNetCode( elem.net ) );
3631 via->SetLocked( elem.is_locked );
3632
3633 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
3634 || elem.layer_start == ALTIUM_LAYER::BOTTOM_LAYER;
3635 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
3636 || elem.layer_end == ALTIUM_LAYER::BOTTOM_LAYER;
3637
3638 if( start_layer_outside && end_layer_outside )
3639 {
3640 via->SetViaType( VIATYPE::THROUGH );
3641 }
3642 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
3643 {
3644 via->SetViaType( VIATYPE::BLIND_BURIED );
3645 }
3646 else
3647 {
3648 via->SetViaType( VIATYPE::MICROVIA ); // TODO: always a microvia?
3649 }
3650
3651 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
3652 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
3653
3654 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
3655 {
3656 if( m_reporter )
3657 {
3658 wxString msg;
3659 msg.Printf( _( "Via from layer %d to %d uses a non-copper layer, which is not "
3660 "supported." ),
3661 elem.layer_start,
3662 elem.layer_end );
3664 }
3665
3666 continue; // just assume through-hole instead.
3667 }
3668
3669 // we need VIATYPE set!
3670 via->SetLayerPair( start_klayer, end_klayer );
3671
3672 m_board->Add( via.release(), ADD_MODE::APPEND );
3673 }
3674
3675 if( reader.GetRemainingBytes() != 0 )
3676 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
3677}
3678
3680 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3681{
3682 if( m_progressReporter )
3683 m_progressReporter->Report( _( "Loading tracks..." ) );
3684
3685 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3686
3687 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
3688 {
3689 checkpoint();
3690 ATRACK6 elem( reader );
3691
3692 if( elem.component == ALTIUM_COMPONENT_NONE )
3693 {
3694 ConvertTracks6ToBoardItem( elem, primitiveIndex );
3695 }
3696 else
3697 {
3698 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3699 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
3700 }
3701 }
3702
3703 if( reader.GetRemainingBytes() != 0 )
3704 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
3705}
3706
3707
3708void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
3709{
3710 if( aElem.polygon != ALTIUM_POLYGON_NONE && aElem.polygon != ALTIUM_POLYGON_BOARD )
3711 {
3712 if( m_polygons.size() <= aElem.polygon )
3713 {
3714 // Can happen when reading old Altium files: just skip this item
3715 if( m_reporter )
3716 {
3717 wxString msg;
3718 msg.Printf( wxT( "ATRACK6 stream tries to access polygon id %u "
3719 "of %u existing polygons; skipping it" ),
3720 static_cast<unsigned>( aElem.polygon ),
3721 static_cast<unsigned>( m_polygons.size() ) );
3723 }
3724
3725 return;
3726 }
3727
3728 ZONE* zone = m_polygons.at( aElem.polygon );
3729
3730 if( zone == nullptr )
3731 {
3732 return; // we know the zone id, but because we do not know the layer we did not
3733 // add it!
3734 }
3735
3736 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
3737
3738 if( klayer == UNDEFINED_LAYER )
3739 return; // Just skip it for now. Users can fill it themselves.
3740
3741 if( !zone->HasFilledPolysForLayer( klayer ) )
3742 return;
3743
3744 SHAPE_POLY_SET* fill = zone->GetFill( klayer );
3745
3746 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3747 shape.SetStart( aElem.start );
3748 shape.SetEnd( aElem.end );
3749 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3750
3751 shape.EDA_SHAPE::TransformShapeToPolygon( *fill, 0, ARC_HIGH_DEF, ERROR_INSIDE );
3752 // Will be simplified and fractured later
3753
3754 zone->SetIsFilled( true );
3755 zone->SetNeedRefill( false );
3756
3757 return;
3758 }
3759
3760 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3761 || IsAltiumLayerAPlane( aElem.layer ) )
3762 {
3763 // This is not the actual board item. We can use it to create the polygon for the region
3764 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3765 shape.SetStart( aElem.start );
3766 shape.SetEnd( aElem.end );
3767 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3768
3770 }
3771 else
3772 {
3773 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3774 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
3775 }
3776
3777 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
3778 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
3779 {
3780 int width = aElem.width + ( layerExpansionMask.second * 2 );
3781 if( width > 1 )
3782 {
3783 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
3784
3785 seg->SetStart( aElem.start );
3786 seg->SetEnd( aElem.end );
3787 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3788 seg->SetLayer( layerExpansionMask.first );
3789
3790 m_board->Add( seg.release(), ADD_MODE::APPEND );
3791 }
3792 }
3793}
3794
3795
3797 const int aPrimitiveIndex,
3798 const bool aIsBoardImport )
3799{
3800 if( aElem.polygon != ALTIUM_POLYGON_NONE )
3801 {
3802 wxFAIL_MSG( wxString::Format( "Altium: Unexpected footprint Track with polygon id %u",
3803 (unsigned)aElem.polygon ) );
3804 return;
3805 }
3806
3807 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3808 || IsAltiumLayerAPlane( aElem.layer ) )
3809 {
3810 // This is not the actual board item. We can use it to create the polygon for the region
3811 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3812 shape.SetStart( aElem.start );
3813 shape.SetEnd( aElem.end );
3814 shape.SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3815
3816 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3817 aElem.keepoutrestrictions );
3818 }
3819 else
3820 {
3821 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3822 {
3823 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3824 {
3825 // Special case: do to not lose net connections in footprints
3826 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
3827 }
3828 else
3829 {
3830 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3831 }
3832 }
3833 }
3834
3835 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
3836 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
3837 {
3838 int width = aElem.width + ( layerExpansionMask.second * 2 );
3839 if( width > 1 )
3840 {
3841 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
3842
3843 seg->SetStart( aElem.start );
3844 seg->SetEnd( aElem.end );
3845 seg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
3846 seg->SetLayer( layerExpansionMask.first );
3847
3848 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
3849 }
3850 }
3851}
3852
3853
3855{
3856 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3857 {
3858 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
3859
3860 track->SetStart( aElem.start );
3861 track->SetEnd( aElem.end );
3862 track->SetWidth( aElem.width );
3863 track->SetLayer( aLayer );
3864 track->SetNetCode( GetNetCode( aElem.net ) );
3865
3866 m_board->Add( track.release(), ADD_MODE::APPEND );
3867 }
3868 else
3869 {
3870 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::SEGMENT );
3871
3872 seg->SetStart( aElem.start );
3873 seg->SetEnd( aElem.end );
3874 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3875 seg->SetLayer( aLayer );
3876
3877 m_board->Add( seg.release(), ADD_MODE::APPEND );
3878 }
3879}
3880
3881
3883 PCB_LAYER_ID aLayer )
3884{
3885 std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::SEGMENT );
3886
3887 seg->SetStart( aElem.start );
3888 seg->SetEnd( aElem.end );
3889 seg->SetStroke( STROKE_PARAMS( aElem.width, LINE_STYLE::SOLID ) );
3890 seg->SetLayer( aLayer );
3891
3892 aFootprint->Add( seg.release(), ADD_MODE::APPEND );
3893}
3894
3895
3897 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3898{
3899 if( m_progressReporter )
3900 m_progressReporter->Report( _( "Loading unicode strings..." ) );
3901
3902 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3903
3905
3906 if( reader.GetRemainingBytes() != 0 )
3907 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
3908}
3909
3911 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3912{
3913 if( m_progressReporter )
3914 m_progressReporter->Report( _( "Loading text..." ) );
3915
3916 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
3917
3918 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3919 {
3920 checkpoint();
3921 ATEXT6 elem( reader, m_unicodeStrings );
3922
3923 if( elem.component == ALTIUM_COMPONENT_NONE )
3924 {
3926 }
3927 else
3928 {
3929 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3930 ConvertTexts6ToFootprintItem( footprint, elem );
3931 }
3932 }
3933
3934 if( reader.GetRemainingBytes() != 0 )
3935 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
3936}
3937
3938
3940{
3941 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3942 {
3943 if( m_reporter )
3944 {
3945 wxString msg;
3946 msg.Printf( _( "Ignored barcode on Altium layer %d (not yet supported)." ),
3947 aElem.layer );
3949 }
3950
3951 return;
3952 }
3953
3954 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3955 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
3956}
3957
3958
3960{
3961 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3962 {
3963 if( !m_footprintName.IsEmpty() )
3964 {
3965 if( m_reporter )
3966 {
3967 wxString msg;
3968 msg.Printf( _( "Error loading library '%s':\n"
3969 "Footprint %s contains barcode on Altium layer %d (not yet supported)." ),
3970 m_library,
3972 aElem.layer );
3974 }
3975 }
3976 else
3977 {
3978 if( m_reporter )
3979 {
3980 wxString msg;
3981 msg.Printf( _( "Footprint %s contains barcode on Altium layer %d (not yet supported)." ),
3982 aFootprint->GetReference(),
3983 aElem.layer );
3985 }
3986 }
3987
3988 return;
3989 }
3990
3991 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3992 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3993}
3994
3995
3997{
3998 std::unique_ptr<PCB_TEXT> pcbText = std::make_unique<PCB_TEXT>( m_board );
3999
4000 static const std::map<wxString, wxString> variableMap = {
4001 { "LAYER_NAME", "LAYER" },
4002 { "PRINT_DATE", "CURRENT_DATE"},
4003 };
4004
4005 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4006
4007 pcbText->SetText(kicadText);
4008 pcbText->SetLayer( aLayer );
4009 pcbText->SetPosition( aElem.position );
4010 pcbText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4011
4012 ConvertTexts6ToEdaTextSettings( aElem, *pcbText );
4013
4014 m_board->Add( pcbText.release(), ADD_MODE::APPEND );
4015}
4016
4017
4019 PCB_LAYER_ID aLayer )
4020{
4021 PCB_TEXT* fpText;
4022
4023 if( aElem.isDesignator )
4024 {
4025 fpText = &aFootprint->Reference(); // TODO: handle multiple layers
4026 }
4027 else if( aElem.isComment )
4028 {
4029 fpText = &aFootprint->Value(); // TODO: handle multiple layers
4030 }
4031 else
4032 {
4033 fpText = new PCB_TEXT( aFootprint );
4034 aFootprint->Add( fpText, ADD_MODE::APPEND );
4035 }
4036
4037 static const std::map<wxString, wxString> variableMap = {
4038 { "DESIGNATOR", "REFERENCE" },
4039 { "COMMENT", "VALUE" },
4040 { "VALUE", "ALTIUM_VALUE" },
4041 { "LAYER_NAME", "LAYER" },
4042 { "PRINT_DATE", "CURRENT_DATE"},
4043 };
4044
4045 wxString kicadText = AltiumPcbSpecialStringsToKiCadStrings( aElem.text, variableMap );
4046
4047 fpText->SetText(kicadText);
4048 fpText->SetKeepUpright( false );
4049 fpText->SetLayer( aLayer );
4050 fpText->SetPosition( aElem.position );
4051 fpText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4052
4053 ConvertTexts6ToEdaTextSettings( aElem, *fpText );
4054}
4055
4056
4058{
4059 aEdaText.SetTextSize( VECTOR2I( aElem.height, aElem.height ) ); // TODO: parse text width
4060
4061 if( aElem.fonttype == ALTIUM_TEXT_TYPE::TRUETYPE )
4062 {
4063 KIFONT::FONT* font = KIFONT::FONT::GetFont( aElem.fontname, aElem.isBold, aElem.isItalic );
4064 aEdaText.SetFont( font );
4065
4066 if( font->IsOutline() )
4067 {
4068 // TODO: why is this required? Somehow, truetype size is calculated differently
4069 if( font->GetName().Contains( wxS( "Arial" ) ) )
4070 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.63, aElem.height * 0.63 ) );
4071 else
4072 aEdaText.SetTextSize( VECTOR2I( aElem.height * 0.5, aElem.height * 0.5 ) );
4073 }
4074 }
4075
4076 aEdaText.SetTextThickness( aElem.strokewidth );
4077 aEdaText.SetBoldFlag( aElem.isBold );
4078 aEdaText.SetItalic( aElem.isItalic );
4079 aEdaText.SetMirrored( aElem.isMirrored );
4080
4081 // Altium position always specifies the bottom left corner
4084
4085 // TODO: correct the position and set proper justification
4086}
4087
4088
4090 const CFB::COMPOUND_FILE_ENTRY* aEntry )
4091{
4092 if( m_progressReporter )
4093 m_progressReporter->Report( _( "Loading rectangles..." ) );
4094
4095 ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
4096
4097 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
4098 {
4099 checkpoint();
4100 AFILL6 elem( reader );
4101
4102 if( elem.component == ALTIUM_COMPONENT_NONE )
4103 {
4105 }
4106 else
4107 {
4108 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
4109 ConvertFills6ToFootprintItem( footprint, elem, true );
4110 }
4111 }
4112
4113 if( reader.GetRemainingBytes() != 0 )
4114 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
4115}
4116
4117
4119{
4120 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
4121 {
4122 // This is not the actual board item. We can use it to create the polygon for the region
4123 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4124
4125 shape.SetStart( aElem.pos1 );
4126 shape.SetEnd( aElem.pos2 );
4127 shape.SetFilled( true );
4128 shape.SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
4129
4130 if( aElem.rotation != 0. )
4131 {
4132 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4133 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4134 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4135 }
4136
4138 }
4139 else
4140 {
4141 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4142 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4143 }
4144}
4145
4146
4148 const bool aIsBoardImport )
4149{
4150 if( aElem.is_keepout
4151 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
4152 {
4153 // This is not the actual board item. We can use it to create the polygon for the region
4154 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
4155
4156 shape.SetStart( aElem.pos1 );
4157 shape.SetEnd( aElem.pos2 );
4158 shape.SetFilled( true );
4159 shape.SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
4160
4161 if( aElem.rotation != 0. )
4162 {
4163 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4164 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4165 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4166 }
4167
4168 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
4169 aElem.keepoutrestrictions );
4170 }
4171 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
4172 && aElem.net != ALTIUM_NET_UNCONNECTED )
4173 {
4174 // Special case: do to not lose net connections in footprints
4175 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4176 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
4177 }
4178 else
4179 {
4180 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
4181 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
4182 }
4183}
4184
4185
4187{
4188 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( m_board, SHAPE_T::RECTANGLE );
4189
4190 fill->SetFilled( true );
4191 fill->SetLayer( aLayer );
4192 fill->SetStroke( STROKE_PARAMS( 0 ) );
4193
4194 fill->SetStart( aElem.pos1 );
4195 fill->SetEnd( aElem.pos2 );
4196
4197 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
4198 {
4199 fill->SetNetCode( GetNetCode( aElem.net ) );
4200 }
4201
4202 if( aElem.rotation != 0. )
4203 {
4204 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4205 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4206 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4207 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4208 }
4209
4210 m_board->Add( fill.release(), ADD_MODE::APPEND );
4211}
4212
4213
4215 PCB_LAYER_ID aLayer )
4216{
4217 std::unique_ptr<PCB_SHAPE> fill = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::RECTANGLE );
4218
4219 fill->SetFilled( true );
4220 fill->SetLayer( aLayer );
4221 fill->SetStroke( STROKE_PARAMS( 0 ) );
4222
4223 fill->SetStart( aElem.pos1 );
4224 fill->SetEnd( aElem.pos2 );
4225
4226 if( aElem.rotation != 0. )
4227 {
4228 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
4229 VECTOR2I center( aElem.pos1.x / 2 + aElem.pos2.x / 2,
4230 aElem.pos1.y / 2 + aElem.pos2.y / 2 );
4231 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
4232 }
4233
4234 aFootprint->Add( fill.release(), ADD_MODE::APPEND );
4235}
4236
4237
4238void ALTIUM_PCB::HelperSetZoneLayers( ZONE& aZone, const ALTIUM_LAYER aAltiumLayer )
4239{
4240 LSET layerSet;
4241
4242 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
4243 layerSet.set( klayer );
4244
4245 aZone.SetLayerSet( layerSet );
4246}
4247
4248
4249void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE& aZone, const uint8_t aKeepoutRestrictions )
4250{
4251 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
4252 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
4253 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
4254 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
4255 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
4256
4257 aZone.SetDoNotAllowVias( keepoutRestrictionVia );
4258 aZone.SetDoNotAllowTracks( keepoutRestrictionTrack );
4259 aZone.SetDoNotAllowCopperPour( keepoutRestrictionCopper );
4260 aZone.SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
4261 aZone.SetDoNotAllowFootprints( false );
4262}
4263
4264
4266 const ALTIUM_LAYER aAltiumLayer,
4267 const uint8_t aKeepoutRestrictions )
4268{
4269 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
4270
4271 zone->SetIsRuleArea( true );
4272
4273 HelperSetZoneLayers( *zone, aAltiumLayer );
4274 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4275
4276 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4277
4278 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4280
4281 m_board->Add( zone.release(), ADD_MODE::APPEND );
4282}
4283
4284
4286 const PCB_SHAPE& aShape,
4287 const ALTIUM_LAYER aAltiumLayer,
4288 const uint8_t aKeepoutRestrictions )
4289{
4290 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aFootprint );
4291
4292 zone->SetIsRuleArea( true );
4293
4294 HelperSetZoneLayers( *zone, aAltiumLayer );
4295 HelperSetZoneKeepoutRestrictions( *zone, aKeepoutRestrictions );
4296
4297 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
4298
4299 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
4301
4302 // TODO: zone->SetLocalCoord(); missing?
4303 aFootprint->Add( zone.release(), ADD_MODE::APPEND );
4304}
4305
4306
4307std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
4308 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
4309{
4310 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
4311 return {}; // there is nothing to parse
4312
4313 auto elems =
4314 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
4315
4316 if( elems.first == elems.second )
4317 return {}; // there is nothing to parse
4318
4319 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
4320
4321 for( auto it = elems.first; it != elems.second; ++it )
4322 {
4323 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
4324
4325 if( pInf.type == AEXTENDED_PRIMITIVE_INFORMATION_TYPE::MASK )
4326 {
4327 if( pInf.soldermaskexpansionmode == ALTIUM_MODE::MANUAL
4328 || pInf.soldermaskexpansionmode == ALTIUM_MODE::RULE )
4329 {
4330 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
4331 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4332 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4333 {
4334 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
4335 }
4336
4337 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4338 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4339 {
4340 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
4341 }
4342 }
4343 if( pInf.pastemaskexpansionmode == ALTIUM_MODE::MANUAL
4344 || pInf.pastemaskexpansionmode == ALTIUM_MODE::RULE )
4345 {
4346 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
4347 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4348 {
4349 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
4350 }
4351
4352 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
4353 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
4354 {
4355 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
4356 }
4357 }
4358 }
4359 }
4360
4361 return layerExpansionPairs;
4362}
const char * name
Definition: DXF_plotter.cpp:57
std::string FormatPath(const std::vector< std::string > &aVectorPath)
Helper for debug logging (vector -> string)
ALTIUM_RULE_KIND
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:87
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:59
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:62
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:69
std::function< void(const ALTIUM_COMPOUND_FILE &, const CFB::COMPOUND_FILE_ENTRY *)> PARSE_FUNCTION_POINTER_fp
Definition: altium_pcb.h:105
ALTIUM_PCB_DIR
Definition: altium_pcb.h:38
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:150
@ LT_POWER
Definition: board.h:153
@ LT_MIXED
Definition: board.h:154
@ LT_SIGNAL
Definition: board.h:152
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:44
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:45
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
std::tuple< wxString, const CFB::COMPOUND_FILE_ENTRY * > FindLibFootprintDirName(const wxString &aFpUnicodeName)
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
void ParseFileHeader(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:880
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition: altium_pcb.h:263
void ParseShapeBasedRegions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::vector< PCB_DIM_RADIAL * > m_radialDimensions
Definition: altium_pcb.h:249
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:273
void ConvertTexts6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_LAYER, PCB_LAYER_ID > m_layermap
Definition: altium_pcb.h:253
void ConvertShapeBasedRegions6ToBoardItemOnLayer(const AREGION6 &aElem, PCB_LAYER_ID aLayer)
void HelperParseDimensions6Leader(const ADIMENSION6 &aElem)
wxString m_footprintName
for footprint library loading error reporting
Definition: altium_pcb.h:270
std::vector< FOOTPRINT * > m_components
Definition: altium_pcb.h:247
const ARULE6 * GetRuleDefault(ALTIUM_RULE_KIND aKind) const
Definition: altium_pcb.cpp:864
void HelperParsePad6NonCopper(const APAD6 &aElem, PCB_LAYER_ID aLayer, PCB_SHAPE *aShape)
std::vector< PCB_LAYER_ID > GetKicadLayersToIterate(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:234
void ParseRegions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseBoard6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:920
void ParseComponentsBodies6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
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 ParseFills6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void Parse(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
Definition: altium_pcb.cpp:314
void ParseBoardRegionsData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
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:259
std::vector< int > m_altiumToKicadNetcodes
Definition: altium_pcb.h:252
unsigned m_totalCount
for progress reporting
Definition: altium_pcb.h:267
void HelperPcpShapeAsBoardKeepoutRegion(const PCB_SHAPE &aShape, const ALTIUM_LAYER aAltiumLayer, const uint8_t aKeepoutRestrictions)
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 ConvertFills6ToFootprintItem(FOOTPRINT *aFootprint, const AFILL6 &aElem, const bool aIsBoardImport)
void ConvertShapeBasedRegions6ToBoardItem(const AREGION6 &aElem)
void HelperCreateBoardOutline(const std::vector< ALTIUM_VERTICE > &aVertices)
void ParseVias6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::map< ALTIUM_RULE_KIND, std::vector< ARULE6 > > m_rules
Definition: altium_pcb.h:255
void HelperSetZoneKeepoutRestrictions(ZONE &aZone, const uint8_t aKeepoutRestrictions)
void ParseClasses6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
unsigned m_doneCount
Definition: altium_pcb.h:265
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
std::map< uint32_t, wxString > m_unicodeStrings
Definition: altium_pcb.h:251
void ConvertTexts6ToBoardItem(const ATEXT6 &aElem)
FOOTPRINT * ParseFootprint(ALTIUM_COMPOUND_FILE &altiumLibFile, const wxString &aFootprintName)
Definition: altium_pcb.cpp:655
void ParseArcs6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperParseDimensions6Center(const ADIMENSION6 &aElem)
void HelperParseDimensions6Radial(const ADIMENSION6 &aElem)
void ParsePads6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
BOARD * m_board
Definition: altium_pcb.h:246
void ParseWideStrings6Data(const ALTIUM_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)
REPORTER * m_reporter
optional; may be nullptr
Definition: altium_pcb.h:264
void ParseComponents6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
int GetNetCode(uint16_t aId) const
Definition: altium_pcb.cpp:830
void ConvertTexts6ToEdaTextSettings(const ATEXT6 &aElem, EDA_TEXT &aEdaText)
wxString m_library
for footprint library loading error reporting
Definition: altium_pcb.h:269
void ConvertPads6ToFootprintItemOnNonCopper(FOOTPRINT *aFootprint, const APAD6 &aElem)
void ConvertTexts6ToFootprintItem(FOOTPRINT *aFootprint, const ATEXT6 &aElem)
unsigned m_lastProgressCount
Definition: altium_pcb.h:266
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 remapUnsureLayers(std::vector< ABOARD6_LAYER_STACKUP > &aStackup)
std::vector< ZONE * > m_polygons
Definition: altium_pcb.h:248
std::map< wxString, wxString > m_models
Definition: altium_pcb.h:250
void ConvertArcs6ToPcbShape(const AARC6 &aElem, PCB_SHAPE *aShape)
void ConvertTracks6ToBoardItemOnLayer(const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void ConvertFills6ToBoardItem(const AFILL6 &aElem)
void ParseModelsData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry, const std::vector< std::string > &aRootDir)
void ParseRules6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
FOOTPRINT * HelperGetFootprint(uint16_t aComponent) const
Definition: altium_pcb.cpp:74
LAYER_MAPPING_HANDLER m_layerMappingHandler
Definition: altium_pcb.h:261
PCB_LAYER_ID GetKicadLayer(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:128
void checkpoint()
Definition: altium_pcb.cpp:295
void ConvertTracks6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATRACK6 &aElem, PCB_LAYER_ID aLayer)
void ParseTexts6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParsePolygons6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
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:275
void ConvertArcs6ToBoardItemOnLayer(const AARC6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_RECORD, std::multimap< int, const AEXTENDED_PRIMITIVE_INFORMATION > > m_extendedPrimitiveInformationMaps
Definition: altium_pcb.h:257
std::map< ALTIUM_LAYER, wxString > m_layerNames
Definition: altium_pcb.h:254
void ParseTracks6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ConvertPads6ToBoardItemOnNonCopper(const APAD6 &aElem)
void ParseNets6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void ParseExtendedPrimitiveInformationData(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:898
void ParseDimensions6Data(const ALTIUM_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:848
void HelperSetZoneLayers(ZONE &aZone, const ALTIUM_LAYER aAltiumLayer)
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
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:77
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:265
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:282
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:677
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:882
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:911
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:697
const PAGE_INFO & GetPageSettings() const
Definition: board.h:671
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:703
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:598
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:580
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: board.cpp:490
int GetCopperLayerCount() const
Definition: board.cpp:653
const TRACKS & Tracks() const
Definition: board.h:321
void SetVisibleLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:709
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:369
void SetCopperLayerCount(int aCount)
Definition: board.cpp:659
const wxString & GetFileName() const
Definition: board.h:319
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:610
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:794
const DRAWINGS & Drawings() const
Definition: board.h:325
size_type GetHeight() const
Definition: box2.h:205
size_type GetWidth() const
Definition: box2.h:204
coord_type GetY() const
Definition: box2.h:198
coord_type GetX() const
Definition: box2.h:197
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 IsVertical() const
Definition: eda_angle.h:143
double Cos() const
Definition: eda_angle.h:189
void SetModified()
Definition: eda_item.cpp:64
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:689
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:571
void SetFilled(bool aFlag)
Definition: eda_shape.h:101
int GetRadius() const
Definition: eda_shape.cpp:619
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:717
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1226
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:83
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:372
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:250
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:274
void SetBoldFlag(bool aBold)
Definition: eda_text.cpp:235
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:195
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:282
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:181
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:203
void SetItalic(bool aItalic)
Definition: eda_text.cpp:211
void SetFont(KIFONT::FONT *aFont)
Definition: eda_text.cpp:356
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:266
EDA_ANGLE GetOrientation() const
Definition: footprint.h:214
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:625
bool IsFlipped() const
Definition: footprint.h:378
PCB_FIELD & Reference()
Definition: footprint.h:626
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:968
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:207
const wxString & GetReference() const
Definition: footprint.h:589
VECTOR2I GetPosition() const override
Definition: footprint.h:211
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:98
double m_Opacity
Definition: footprint.h:99
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:97
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:100
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)
Definition: font.cpp:146
const wxString & GetName() const
Definition: font.h:147
virtual bool IsOutline() const
Definition: font.h:139
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: layer_ids.h:576
static LSET AllBoardTechMask()
Return a mask holding board technical layers (no CU layer) on both side.
Definition: lset.cpp:938
static LSET UserDefinedLayers()
Return a mask with all of the allowable user defined layers.
Definition: lset.cpp:967
static LSET UserMask()
Definition: lset.cpp:945
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:863
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
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:285
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:306
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:292
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:1505
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:300
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:75
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:524
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:315
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:86
VECTOR2I GetPosition() const override
Definition: pcb_shape.h:73
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:87
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:71
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:196
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:115
const VECTOR2I & GetP1() const
Definition: shape_arc.h:114
const VECTOR2I & GetP0() const
Definition: shape_arc.h:113
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 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 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 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:81
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:515
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:278
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:380
T y
Definition: vector3.h:63
T z
Definition: vector3.h:64
T x
Definition: vector3.h:62
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:264
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:721
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:718
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition: zone.h:621
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:720
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:636
void SetIsFilled(bool isFilled)
Definition: zone.h:261
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition: zone.h:607
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:719
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:267
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:722
static int GetDefaultHatchPitch()
Definition: zone.cpp:1051
#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
@ CTX_NETCLASS
@ ERROR_INSIDE
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
#define MAX_CU_LAYERS
Definition: layer_ids.h:142
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:882
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ In22_Cu
Definition: layer_ids.h:86
@ In11_Cu
Definition: layer_ids.h:75
@ In29_Cu
Definition: layer_ids.h:93
@ In30_Cu
Definition: layer_ids.h:94
@ User_8
Definition: layer_ids.h:130
@ In17_Cu
Definition: layer_ids.h:81
@ Edge_Cuts
Definition: layer_ids.h:113
@ Dwgs_User
Definition: layer_ids.h:109
@ F_Paste
Definition: layer_ids.h:101
@ In9_Cu
Definition: layer_ids.h:73
@ User_6
Definition: layer_ids.h:128
@ User_7
Definition: layer_ids.h:129
@ In19_Cu
Definition: layer_ids.h:83
@ In7_Cu
Definition: layer_ids.h:71
@ In28_Cu
Definition: layer_ids.h:92
@ In26_Cu
Definition: layer_ids.h:90
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ User_5
Definition: layer_ids.h:127
@ Eco1_User
Definition: layer_ids.h:111
@ F_Mask
Definition: layer_ids.h:107
@ In21_Cu
Definition: layer_ids.h:85
@ In23_Cu
Definition: layer_ids.h:87
@ B_Paste
Definition: layer_ids.h:100
@ In15_Cu
Definition: layer_ids.h:79
@ In2_Cu
Definition: layer_ids.h:66
@ User_9
Definition: layer_ids.h:131
@ F_Fab
Definition: layer_ids.h:120
@ In10_Cu
Definition: layer_ids.h:74
@ Margin
Definition: layer_ids.h:114
@ F_SilkS
Definition: layer_ids.h:104
@ In4_Cu
Definition: layer_ids.h:68
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ Eco2_User
Definition: layer_ids.h:112
@ In16_Cu
Definition: layer_ids.h:80
@ In24_Cu
Definition: layer_ids.h:88
@ In1_Cu
Definition: layer_ids.h:65
@ User_3
Definition: layer_ids.h:125
@ User_1
Definition: layer_ids.h:123
@ B_SilkS
Definition: layer_ids.h:103
@ In13_Cu
Definition: layer_ids.h:77
@ User_4
Definition: layer_ids.h:126
@ In8_Cu
Definition: layer_ids.h:72
@ In14_Cu
Definition: layer_ids.h:78
@ User_2
Definition: layer_ids.h:124
@ In12_Cu
Definition: layer_ids.h:76
@ In27_Cu
Definition: layer_ids.h:91
@ In6_Cu
Definition: layer_ids.h:70
@ In5_Cu
Definition: layer_ids.h:69
@ In3_Cu
Definition: layer_ids.h:67
@ In20_Cu
Definition: layer_ids.h:84
@ F_Cu
Definition: layer_ids.h:64
@ In18_Cu
Definition: layer_ids.h:82
@ In25_Cu
Definition: layer_ids.h:89
@ B_Fab
Definition: layer_ids.h:119
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint.
Definition: lset.cpp:680
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.
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:39
@ 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