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-2023 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 <trigo.h>
48#include <utf.h>
49#include <wx/docview.h>
50#include <wx/log.h>
51#include <wx/mstream.h>
52#include <wx/wfstream.h>
53#include <wx/zstream.h>
54#include <progress_reporter.h>
55#include <magic_enum.hpp>
56
57
58constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700
59
60
62{
63 return ( aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER )
64 || aLayer == ALTIUM_LAYER::MULTI_LAYER; // TODO: add IsAltiumLayerAPlane?
65}
66
67
69{
71}
72
73FOOTPRINT* ALTIUM_PCB::HelperGetFootprint( uint16_t aComponent ) const
74{
75 if( aComponent == ALTIUM_COMPONENT_NONE || m_components.size() <= aComponent )
76 {
77 THROW_IO_ERROR( wxString::Format( wxT( "Component creator tries to access component id %d "
78 "of %d existing components" ),
79 aComponent, m_components.size() ) );
80 }
81
82 return m_components.at( aComponent );
83}
84
85
87 const std::vector<ALTIUM_VERTICE>& aVertices )
88{
89 for( const ALTIUM_VERTICE& vertex : aVertices )
90 {
91 if( vertex.isRound )
92 {
93 EDA_ANGLE angle( vertex.endangle - vertex.startangle, DEGREES_T );
94 angle.Normalize();
95
96 double startradiant = DEG2RAD( vertex.startangle );
97 double endradiant = DEG2RAD( vertex.endangle );
98 VECTOR2I arcStartOffset = VECTOR2I( KiROUND( std::cos( startradiant ) * vertex.radius ),
99 -KiROUND( std::sin( startradiant ) * vertex.radius ) );
100
101 VECTOR2I arcEndOffset = VECTOR2I( KiROUND( std::cos( endradiant ) * vertex.radius ),
102 -KiROUND( std::sin( endradiant ) * vertex.radius ) );
103
104 VECTOR2I arcStart = vertex.center + arcStartOffset;
105 VECTOR2I arcEnd = vertex.center + arcEndOffset;
106
107 if( GetLineLength( arcStart, vertex.position )
108 < GetLineLength( arcEnd, vertex.position ) )
109 {
110 aLine.Append( SHAPE_ARC( vertex.center, arcStart, -angle ) );
111 }
112 else
113 {
114 aLine.Append( SHAPE_ARC( vertex.center, arcEnd, angle ) );
115 }
116 }
117 else
118 {
119 aLine.Append( vertex.position );
120 }
121 }
122
123 aLine.SetClosed( true );
124}
125
126
128{
129 auto override = m_layermap.find( aAltiumLayer );
130
131 if( override != m_layermap.end() )
132 {
133 return override->second;
134 }
135
136 switch( aAltiumLayer )
137 {
138 case ALTIUM_LAYER::UNKNOWN: return UNDEFINED_LAYER;
139
140 case ALTIUM_LAYER::TOP_LAYER: return F_Cu;
141 case ALTIUM_LAYER::MID_LAYER_1: return In1_Cu;
142 case ALTIUM_LAYER::MID_LAYER_2: return In2_Cu;
143 case ALTIUM_LAYER::MID_LAYER_3: return In3_Cu;
144 case ALTIUM_LAYER::MID_LAYER_4: return In4_Cu;
145 case ALTIUM_LAYER::MID_LAYER_5: return In5_Cu;
146 case ALTIUM_LAYER::MID_LAYER_6: return In6_Cu;
147 case ALTIUM_LAYER::MID_LAYER_7: return In7_Cu;
148 case ALTIUM_LAYER::MID_LAYER_8: return In8_Cu;
149 case ALTIUM_LAYER::MID_LAYER_9: return In9_Cu;
150 case ALTIUM_LAYER::MID_LAYER_10: return In10_Cu;
151 case ALTIUM_LAYER::MID_LAYER_11: return In11_Cu;
152 case ALTIUM_LAYER::MID_LAYER_12: return In12_Cu;
153 case ALTIUM_LAYER::MID_LAYER_13: return In13_Cu;
154 case ALTIUM_LAYER::MID_LAYER_14: return In14_Cu;
155 case ALTIUM_LAYER::MID_LAYER_15: return In15_Cu;
156 case ALTIUM_LAYER::MID_LAYER_16: return In16_Cu;
157 case ALTIUM_LAYER::MID_LAYER_17: return In17_Cu;
158 case ALTIUM_LAYER::MID_LAYER_18: return In18_Cu;
159 case ALTIUM_LAYER::MID_LAYER_19: return In19_Cu;
160 case ALTIUM_LAYER::MID_LAYER_20: return In20_Cu;
161 case ALTIUM_LAYER::MID_LAYER_21: return In21_Cu;
162 case ALTIUM_LAYER::MID_LAYER_22: return In22_Cu;
163 case ALTIUM_LAYER::MID_LAYER_23: return In23_Cu;
164 case ALTIUM_LAYER::MID_LAYER_24: return In24_Cu;
165 case ALTIUM_LAYER::MID_LAYER_25: return In25_Cu;
166 case ALTIUM_LAYER::MID_LAYER_26: return In26_Cu;
167 case ALTIUM_LAYER::MID_LAYER_27: return In27_Cu;
168 case ALTIUM_LAYER::MID_LAYER_28: return In28_Cu;
169 case ALTIUM_LAYER::MID_LAYER_29: return In29_Cu;
170 case ALTIUM_LAYER::MID_LAYER_30: return In30_Cu;
171 case ALTIUM_LAYER::BOTTOM_LAYER: return B_Cu;
172
173 case ALTIUM_LAYER::TOP_OVERLAY: return F_SilkS;
174 case ALTIUM_LAYER::BOTTOM_OVERLAY: return B_SilkS;
175 case ALTIUM_LAYER::TOP_PASTE: return F_Paste;
176 case ALTIUM_LAYER::BOTTOM_PASTE: return B_Paste;
177 case ALTIUM_LAYER::TOP_SOLDER: return F_Mask;
178 case ALTIUM_LAYER::BOTTOM_SOLDER: return B_Mask;
179
180 case ALTIUM_LAYER::INTERNAL_PLANE_1: return UNDEFINED_LAYER;
181 case ALTIUM_LAYER::INTERNAL_PLANE_2: return UNDEFINED_LAYER;
182 case ALTIUM_LAYER::INTERNAL_PLANE_3: return UNDEFINED_LAYER;
183 case ALTIUM_LAYER::INTERNAL_PLANE_4: return UNDEFINED_LAYER;
184 case ALTIUM_LAYER::INTERNAL_PLANE_5: return UNDEFINED_LAYER;
185 case ALTIUM_LAYER::INTERNAL_PLANE_6: return UNDEFINED_LAYER;
186 case ALTIUM_LAYER::INTERNAL_PLANE_7: return UNDEFINED_LAYER;
187 case ALTIUM_LAYER::INTERNAL_PLANE_8: return UNDEFINED_LAYER;
188 case ALTIUM_LAYER::INTERNAL_PLANE_9: return UNDEFINED_LAYER;
189 case ALTIUM_LAYER::INTERNAL_PLANE_10: return UNDEFINED_LAYER;
190 case ALTIUM_LAYER::INTERNAL_PLANE_11: return UNDEFINED_LAYER;
191 case ALTIUM_LAYER::INTERNAL_PLANE_12: return UNDEFINED_LAYER;
192 case ALTIUM_LAYER::INTERNAL_PLANE_13: return UNDEFINED_LAYER;
193 case ALTIUM_LAYER::INTERNAL_PLANE_14: return UNDEFINED_LAYER;
194 case ALTIUM_LAYER::INTERNAL_PLANE_15: return UNDEFINED_LAYER;
195 case ALTIUM_LAYER::INTERNAL_PLANE_16: return UNDEFINED_LAYER;
196
197 case ALTIUM_LAYER::DRILL_GUIDE: return Dwgs_User;
198 case ALTIUM_LAYER::KEEP_OUT_LAYER: return Margin;
199
200 case ALTIUM_LAYER::MECHANICAL_1: return User_1; //Edge_Cuts;
201 case ALTIUM_LAYER::MECHANICAL_2: return User_2;
202 case ALTIUM_LAYER::MECHANICAL_3: return User_3;
203 case ALTIUM_LAYER::MECHANICAL_4: return User_4;
204 case ALTIUM_LAYER::MECHANICAL_5: return User_5;
205 case ALTIUM_LAYER::MECHANICAL_6: return User_6;
206 case ALTIUM_LAYER::MECHANICAL_7: return User_7;
207 case ALTIUM_LAYER::MECHANICAL_8: return User_8;
208 case ALTIUM_LAYER::MECHANICAL_9: return User_9;
209 case ALTIUM_LAYER::MECHANICAL_10: return Dwgs_User;
210 case ALTIUM_LAYER::MECHANICAL_11: return Eco2_User; //Eco1 is used for unknown elements
211 case ALTIUM_LAYER::MECHANICAL_12: return F_Fab;
212 case ALTIUM_LAYER::MECHANICAL_13: return B_Fab; // Don't use courtyard layers for other purposes
213 case ALTIUM_LAYER::MECHANICAL_14: return UNDEFINED_LAYER;
214 case ALTIUM_LAYER::MECHANICAL_15: return UNDEFINED_LAYER;
215 case ALTIUM_LAYER::MECHANICAL_16: return UNDEFINED_LAYER;
216
217 case ALTIUM_LAYER::DRILL_DRAWING: return Dwgs_User;
218 case ALTIUM_LAYER::MULTI_LAYER: return UNDEFINED_LAYER;
219 case ALTIUM_LAYER::CONNECTIONS: return UNDEFINED_LAYER;
220 case ALTIUM_LAYER::BACKGROUND: return UNDEFINED_LAYER;
221 case ALTIUM_LAYER::DRC_ERROR_MARKERS: return UNDEFINED_LAYER;
222 case ALTIUM_LAYER::SELECTIONS: return UNDEFINED_LAYER;
223 case ALTIUM_LAYER::VISIBLE_GRID_1: return UNDEFINED_LAYER;
224 case ALTIUM_LAYER::VISIBLE_GRID_2: return UNDEFINED_LAYER;
225 case ALTIUM_LAYER::PAD_HOLES: return UNDEFINED_LAYER;
226 case ALTIUM_LAYER::VIA_HOLES: return UNDEFINED_LAYER;
227
228 default: return UNDEFINED_LAYER;
229 }
230}
231
232
233std::vector<PCB_LAYER_ID> ALTIUM_PCB::GetKicadLayersToIterate( ALTIUM_LAYER aAltiumLayer ) const
234{
235 static std::set<ALTIUM_LAYER> altiumLayersWithWarning;
236
237 if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER || aAltiumLayer == ALTIUM_LAYER::KEEP_OUT_LAYER )
238 {
239 std::vector<PCB_LAYER_ID> layers;
240 layers.reserve( MAX_CU_LAYERS );
241
242 for( PCB_LAYER_ID layer : LSET::AllCuMask().Seq() )
243 {
244 if( !m_board || m_board->IsLayerEnabled( layer ) )
245 layers.emplace_back( layer );
246 }
247
248 return layers;
249 }
250
251 PCB_LAYER_ID klayer = GetKicadLayer( aAltiumLayer );
252
253 if( klayer == UNDEFINED_LAYER )
254 {
255 wxLogWarning( _( "Altium layer (%d) has no KiCad equivalent. It has been moved to KiCad "
256 "layer Eco1_User." ),
257 aAltiumLayer );
258 klayer = Eco1_User;
259 }
260
261 return { klayer };
262}
263
264
265ALTIUM_PCB::ALTIUM_PCB( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
266{
267 m_board = aBoard;
268 m_progressReporter = aProgressReporter;
269 m_doneCount = 0;
271 m_totalCount = 0;
273}
274
276{
277}
278
280{
281 const unsigned PROGRESS_DELTA = 250;
282
284 {
285 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
286 {
288 / std::max( 1U, m_totalCount ) );
289
291 THROW_IO_ERROR( _( "Open cancelled by user." ) );
292
294 }
295 }
296}
297
298void ALTIUM_PCB::Parse( const ALTIUM_COMPOUND_FILE& altiumPcbFile,
299 const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
300{
301 // this vector simply declares in which order which functions to call.
302 const std::vector<std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>> parserOrder = {
303 { true, ALTIUM_PCB_DIR::FILE_HEADER,
304 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
305 {
306 this->ParseFileHeader( aFile, fileHeader );
307 } },
308 { true, ALTIUM_PCB_DIR::BOARD6,
309 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
310 {
311 this->ParseBoard6Data( aFile, fileHeader );
312 } },
313 { false, ALTIUM_PCB_DIR::EXTENDPRIMITIVEINFORMATION,
314 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
315 {
316 this->ParseExtendedPrimitiveInformationData( aFile, fileHeader );
317 } },
318 { true, ALTIUM_PCB_DIR::COMPONENTS6,
319 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
320 {
321 this->ParseComponents6Data( aFile, fileHeader );
322 } },
323 { true, ALTIUM_PCB_DIR::MODELS,
324 [this, aFileMapping]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
325 {
326 std::vector<std::string> dir{ aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) };
327 this->ParseModelsData( aFile, fileHeader, dir );
328 } },
329 { true, ALTIUM_PCB_DIR::COMPONENTBODIES6,
330 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
331 {
332 this->ParseComponentsBodies6Data( aFile, fileHeader );
333 } },
334 { true, ALTIUM_PCB_DIR::NETS6,
335 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
336 {
337 this->ParseNets6Data( aFile, fileHeader );
338 } },
339 { true, ALTIUM_PCB_DIR::CLASSES6,
340 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
341 {
342 this->ParseClasses6Data( aFile, fileHeader );
343 } },
344 { true, ALTIUM_PCB_DIR::RULES6,
345 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
346 {
347 this->ParseRules6Data( aFile, fileHeader );
348 } },
349 { true, ALTIUM_PCB_DIR::DIMENSIONS6,
350 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
351 {
352 this->ParseDimensions6Data( aFile, fileHeader );
353 } },
354 { true, ALTIUM_PCB_DIR::POLYGONS6,
355 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
356 {
357 this->ParsePolygons6Data( aFile, fileHeader );
358 } },
359 { true, ALTIUM_PCB_DIR::ARCS6,
360 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
361 {
362 this->ParseArcs6Data( aFile, fileHeader );
363 } },
364 { true, ALTIUM_PCB_DIR::PADS6,
365 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
366 {
367 this->ParsePads6Data( aFile, fileHeader );
368 } },
369 { true, ALTIUM_PCB_DIR::VIAS6,
370 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
371 {
372 this->ParseVias6Data( aFile, fileHeader );
373 } },
374 { true, ALTIUM_PCB_DIR::TRACKS6,
375 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
376 {
377 this->ParseTracks6Data( aFile, fileHeader );
378 } },
379 { false, ALTIUM_PCB_DIR::WIDESTRINGS6,
380 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
381 {
382 this->ParseWideStrings6Data( aFile, fileHeader );
383 } },
384 { true, ALTIUM_PCB_DIR::TEXTS6,
385 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
386 {
387 this->ParseTexts6Data( aFile, fileHeader );
388 } },
389 { true, ALTIUM_PCB_DIR::FILLS6,
390 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
391 {
392 this->ParseFills6Data( aFile, fileHeader );
393 } },
394 { false, ALTIUM_PCB_DIR::BOARDREGIONS,
395 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
396 {
397 this->ParseBoardRegionsData( aFile, fileHeader );
398 } },
399 { true, ALTIUM_PCB_DIR::SHAPEBASEDREGIONS6,
400 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
401 {
402 this->ParseShapeBasedRegions6Data( aFile, fileHeader );
403 } },
404 { true, ALTIUM_PCB_DIR::REGIONS6,
405 [this]( const ALTIUM_COMPOUND_FILE& aFile, auto fileHeader )
406 {
407 this->ParseRegions6Data( aFile, fileHeader );
408 } }
409 };
410
411 if( m_progressReporter != nullptr )
412 {
413 // Count number of records we will read for the progress reporter
414 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
415 {
416 bool isRequired;
419 std::tie( isRequired, directory, fp ) = cur;
420
421 if( directory == ALTIUM_PCB_DIR::FILE_HEADER )
422 continue;
423
424 const auto& mappedDirectory = aFileMapping.find( directory );
425
426 if( mappedDirectory == aFileMapping.end() )
427 continue;
428
429 const std::vector<std::string> mappedFile{ mappedDirectory->second, "Header" };
430 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
431
432 if( file == nullptr )
433 continue;
434
435 ALTIUM_PARSER reader( altiumPcbFile, file );
436 uint32_t numOfRecords = reader.Read<uint32_t>();
437
438 if( reader.HasParsingError() )
439 {
440 wxLogError( _( "'%s' was not parsed correctly." ), FormatPath( mappedFile ) );
441 continue;
442 }
443
444 m_totalCount += numOfRecords;
445
446 if( reader.GetRemainingBytes() != 0 )
447 {
448 wxLogError( _( "'%s' was not fully parsed." ), FormatPath( mappedFile ) );
449 continue;
450 }
451 }
452 }
453
454 const auto& boardDirectory = aFileMapping.find( ALTIUM_PCB_DIR::BOARD6 );
455
456 if( boardDirectory != aFileMapping.end() )
457 {
458 std::vector<std::string> mappedFile{ boardDirectory->second, "Data" };
459
460 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
461
462 if( !file )
463 {
465 "This file does not appear to be in a valid PCB Binary Version 6.0 format. In "
466 "Altium Designer, "
467 "make sure to save as \"PCB Binary Files (*.PcbDoc)\"." ) );
468 }
469 }
470
471 // Parse data in specified order
472 for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
473 {
474 bool isRequired;
477 std::tie( isRequired, directory, fp ) = cur;
478
479 const auto& mappedDirectory = aFileMapping.find( directory );
480
481 if( mappedDirectory == aFileMapping.end() )
482 {
483 wxASSERT_MSG( !isRequired, wxString::Format( wxT( "Altium Directory of kind %d was "
484 "expected, but no mapping is "
485 "present in the code" ),
486 directory ) );
487 continue;
488 }
489
490 std::vector<std::string> mappedFile{ mappedDirectory->second };
491
492 if( directory != ALTIUM_PCB_DIR::FILE_HEADER )
493 mappedFile.emplace_back( "Data" );
494
495 const CFB::COMPOUND_FILE_ENTRY* file = altiumPcbFile.FindStream( mappedFile );
496
497 if( file != nullptr )
498 {
499 fp( altiumPcbFile, file );
500 }
501 else if( isRequired )
502 {
503 wxLogError( _( "File not found: '%s' for directory '%s'." ), FormatPath( mappedFile ),
504 magic_enum::enum_name( directory ) );
505 }
506 }
507
508 // fixup zone priorities since Altium stores them in the opposite order
509 for( ZONE* zone : m_polygons )
510 {
511 if( !zone )
512 continue;
513
514 // Altium "fills" - not poured in Altium
515 if( zone->GetAssignedPriority() == 1000 )
516 {
517 // Unlikely, but you never know
518 if( m_highest_pour_index >= 1000 )
519 zone->SetAssignedPriority( m_highest_pour_index + 1 );
520
521 continue;
522 }
523
524 int priority = m_highest_pour_index - zone->GetAssignedPriority();
525
526 zone->SetAssignedPriority( priority >= 0 ? priority : 0 );
527 }
528
529 // change priority of outer zone to zero
530 for( std::pair<const ALTIUM_LAYER, ZONE*>& zone : m_outer_plane )
531 zone.second->SetAssignedPriority( 0 );
532
533 // Altium doesn't appear to store either the dimension value nor the dimensioned object in
534 // the dimension record. (Yes, there is a REFERENCE0OBJECTID, but it doesn't point to the
535 // dimensioned object.) We attempt to plug this gap by finding a colocated arc or circle
536 // and using its radius. If there are more than one such arcs/circles, well, :shrug:.
538 {
539 int radius = 0;
540
541 for( BOARD_ITEM* item : m_board->Drawings() )
542 {
543 if( item->Type() != PCB_SHAPE_T )
544 continue;
545
546 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
547
548 if( shape->GetShape() != SHAPE_T::ARC && shape->GetShape() != SHAPE_T::CIRCLE )
549 continue;
550
551 if( shape->GetPosition() == dim->GetPosition() )
552 {
553 radius = shape->GetRadius();
554 break;
555 }
556 }
557
558 if( radius == 0 )
559 {
560 for( PCB_TRACK* track : m_board->Tracks() )
561 {
562 if( track->Type() != PCB_ARC_T )
563 continue;
564
565 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
566
567 if( arc->GetCenter() == dim->GetPosition() )
568 {
569 radius = arc->GetRadius();
570 break;
571 }
572 }
573 }
574
575 // Move the radius point onto the circumference
576 VECTOR2I radialLine = dim->GetEnd() - dim->GetStart();
577 int totalLength = radialLine.EuclideanNorm();
578
579 // Enforce a minimum on the radialLine else we won't have enough precision to get the
580 // angle from it.
581 radialLine = radialLine.Resize( std::max( radius, 2 ) );
582 dim->SetEnd( dim->GetStart() + (VECTOR2I) radialLine );
583 dim->SetLeaderLength( totalLength - radius );
584 dim->Update();
585 }
586
587 // center board
589
592
593 int desired_x = ( w - bbbox.GetWidth() ) / 2;
594 int desired_y = ( h - bbbox.GetHeight() ) / 2;
595
596 VECTOR2I movementVector( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() );
597 m_board->Move( movementVector );
598
600 bds.SetAuxOrigin( bds.GetAuxOrigin() + movementVector );
601 bds.SetGridOrigin( bds.GetGridOrigin() + movementVector );
602
604}
605
606
608 const wxString& aFootprintName )
609{
610 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
611
612 // TODO: what should we do with those layers?
613 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_14, Eco2_User );
614 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_15, Eco2_User );
615 m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_16, Eco2_User );
616
617 m_unicodeStrings.clear();
619
620 // TODO: WideStrings are stored as parameterMap in the case of footprints, not as binary
621 // std::string unicodeStringsStreamName = aFootprintName.ToStdString() + "\\WideStrings";
622 // const CFB::COMPOUND_FILE_ENTRY* unicodeStringsData = altiumLibFile.FindStream( unicodeStringsStreamName );
623 // if( unicodeStringsData != nullptr )
624 // {
625 // ParseWideStrings6Data( altiumLibFile, unicodeStringsData );
626 // }
627
628 std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*> ret = altiumLibFile.FindLibFootprintDirName(aFootprintName);
629
630 wxString fpDirName = std::get<0>( ret );
631 const CFB::COMPOUND_FILE_ENTRY* footprintStream = std::get<1>( ret );
632
633 if( fpDirName.IsEmpty() )
634 {
636 wxString::Format( _( "Footprint directory not found: '%s'." ), aFootprintName ) );
637 }
638
639 const std::vector<std::string> streamName{ fpDirName.ToStdString(), "Data" };
640 const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( footprintStream, { "Data" } );
641
642 if( footprintData == nullptr )
643 {
644 THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ),
645 FormatPath( streamName ) ) );
646 }
647
648 ALTIUM_PARSER parser( altiumLibFile, footprintData );
649
651 //wxString footprintName = parser.ReadWxString(); // Not used (single-byte char set)
652 parser.SkipSubrecord();
653
654 LIB_ID fpID = AltiumToKiCadLibID( "", aFootprintName ); // TODO: library name
655 footprint->SetFPID( fpID );
656
657 const std::vector<std::string> parametersStreamName{ fpDirName.ToStdString(),
658 "Parameters" };
659 const CFB::COMPOUND_FILE_ENTRY* parametersData =
660 altiumLibFile.FindStream( footprintStream, { "Parameters" } );
661
662 if( parametersData != nullptr )
663 {
664 ALTIUM_PARSER parametersReader( altiumLibFile, parametersData );
665 std::map<wxString, wxString> parameterProperties = parametersReader.ReadProperties();
666 wxString description = ALTIUM_PARSER::ReadString( parameterProperties,
667 wxT( "DESCRIPTION" ),
668 wxT( "" ) );
669 footprint->SetLibDescription( description );
670 }
671 else
672 {
673 wxLogError( _( "File not found: '%s'." ), FormatPath( parametersStreamName ) );
674 footprint->SetLibDescription( wxT( "" ) );
675 }
676
677 const std::vector<std::string> extendedPrimitiveInformationStreamName{
678 "ExtendedPrimitiveInformation", "Data"
679 };
680 const CFB::COMPOUND_FILE_ENTRY* extendedPrimitiveInformationData =
681 altiumLibFile.FindStream( footprintStream, extendedPrimitiveInformationStreamName );
682
683 if( extendedPrimitiveInformationData != nullptr )
684 ParseExtendedPrimitiveInformationData( altiumLibFile, extendedPrimitiveInformationData );
685
686 footprint->SetReference( wxT( "REF**" ) );
687 footprint->SetValue( aFootprintName );
688 footprint->Reference().SetVisible( true ); // TODO: extract visibility information
689 footprint->Value().SetVisible( true );
690
691 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
692 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
693
694 for( PCB_FIELD* field : footprint->Fields() )
695 {
696 field->SetTextSize( defaultTextSize );
697 field->SetTextThickness( defaultTextThickness );
698 }
699
700 for( int primitiveIndex = 0; parser.GetRemainingBytes() >= 4; primitiveIndex++ )
701 {
702 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( parser.Peek<uint8_t>() );
703
704 switch( recordtype )
705 {
706 case ALTIUM_RECORD::ARC:
707 {
708 AARC6 arc( parser );
709 ConvertArcs6ToFootprintItem( footprint.get(), arc, primitiveIndex, false );
710 break;
711 }
712 case ALTIUM_RECORD::PAD:
713 {
714 APAD6 pad( parser );
715 ConvertPads6ToFootprintItem( footprint.get(), pad );
716 break;
717 }
718 case ALTIUM_RECORD::VIA:
719 {
720 AVIA6 via( parser );
721 // TODO: implement
722 break;
723 }
724 case ALTIUM_RECORD::TRACK:
725 {
726 ATRACK6 track( parser );
727 ConvertTracks6ToFootprintItem( footprint.get(), track, primitiveIndex, false );
728 break;
729 }
730 case ALTIUM_RECORD::TEXT:
731 {
732 ATEXT6 text( parser, m_unicodeStrings );
733 ConvertTexts6ToFootprintItem( footprint.get(), text );
734 break;
735 }
736 case ALTIUM_RECORD::FILL:
737 {
738 AFILL6 fill( parser );
739 ConvertFills6ToFootprintItem( footprint.get(), fill, false );
740 break;
741 }
742 case ALTIUM_RECORD::REGION:
743 {
744 AREGION6 region( parser, false );
745 ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region, primitiveIndex );
746 break;
747 }
748 case ALTIUM_RECORD::MODEL:
749 {
750 ACOMPONENTBODY6 componentBody( parser );
751 // Won't be supported for now, as we would need to extract the model
752 break;
753 }
754 default:
755 THROW_IO_ERROR( wxString::Format( _( "Record of unknown type: '%d'." ), recordtype ) );
756 }
757 }
758
759 // Auto-position reference and value
760 footprint->AutoPositionFields();
761
762 if( parser.HasParsingError() )
763 {
764 THROW_IO_ERROR( wxString::Format( wxT( "%s stream was not parsed correctly" ),
765 FormatPath( streamName ) ) );
766 }
767
768 if( parser.GetRemainingBytes() != 0 )
769 {
770 THROW_IO_ERROR( wxString::Format( wxT( "%s stream is not fully parsed" ),
771 FormatPath( streamName ) ) );
772 }
773
774 return footprint.release();
775}
776
777int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
778{
779 if( aId == ALTIUM_NET_UNCONNECTED )
780 {
782 }
783 else if( m_altiumToKicadNetcodes.size() < aId )
784 {
785 THROW_IO_ERROR( wxString::Format( wxT( "Netcode with id %d does not exist. Only %d nets "
786 "are known" ),
787 aId, m_altiumToKicadNetcodes.size() ) );
788 }
789 else
790 {
791 return m_altiumToKicadNetcodes[ aId ];
792 }
793}
794
795const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
796{
797 const auto rules = m_rules.find( aKind );
798
799 if( rules == m_rules.end() )
800 return nullptr;
801
802 for( const ARULE6& rule : rules->second )
803 {
804 if( rule.name == aName )
805 return &rule;
806 }
807
808 return nullptr;
809}
810
812{
813 const auto rules = m_rules.find( aKind );
814
815 if( rules == m_rules.end() )
816 return nullptr;
817
818 for( const ARULE6& rule : rules->second )
819 {
820 if( rule.scope1expr == wxT( "All" ) && rule.scope2expr == wxT( "All" ) )
821 return &rule;
822 }
823
824 return nullptr;
825}
826
828 const CFB::COMPOUND_FILE_ENTRY* aEntry )
829{
830 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
831
833 wxString header = reader.ReadWxString();
834
835 //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
836
837 //reader.SkipSubrecord();
838
839 // TODO: does not seem to work all the time at the moment
840 //if( reader.GetRemainingBytes() != 0 )
841 // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
842}
843
845 const CFB::COMPOUND_FILE_ENTRY* aEntry )
846{
848 m_progressReporter->Report( _( "Loading extended primitive information data..." ) );
849
850 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
851
852 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
853 {
854 checkpoint();
855 AEXTENDED_PRIMITIVE_INFORMATION elem( reader );
856
858 std::move( elem ) );
859 }
860
861 if( reader.GetRemainingBytes() != 0 )
862 THROW_IO_ERROR( wxT( "ExtendedPrimitiveInformation stream is not fully parsed" ) );
863}
864
866 const CFB::COMPOUND_FILE_ENTRY* aEntry )
867{
869 m_progressReporter->Report( _( "Loading board data..." ) );
870
871 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
872
873 checkpoint();
874 ABOARD6 elem( reader );
875
876 if( reader.GetRemainingBytes() != 0 )
877 THROW_IO_ERROR( wxT( "Board6 stream is not fully parsed" ) );
878
881
882 // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
883 size_t layercount = 0;
884 size_t layerid = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
885
886 while( layerid < elem.stackup.size() && layerid != 0 )
887 {
888 layerid = elem.stackup[ layerid - 1 ].nextId;
889 layercount++;
890 }
891
892 size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
893 m_board->SetCopperLayerCount( kicadLayercount );
894
896 BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
897
898 // create board stackup
899 stackup.RemoveAll(); // Just to be sure
900 stackup.BuildDefaultStackupList( &designSettings, layercount );
901
902 auto it = stackup.GetList().begin();
903
904 // find first copper layer
905 for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
906 ;
907
908 auto curLayer = static_cast<int>( F_Cu );
909
910 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
911 altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
912 altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
913 {
914 // array starts with 0, but stackup with 1
915 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
916
917 // handle unused layer in case of odd layercount
918 if( layer.nextId == 0 && layercount != kicadLayercount )
919 {
920 m_board->SetLayerName( ( *it )->GetBrdLayerId(), wxT( "[unused]" ) );
921
922 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
923 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
924
925 ( *it )->SetThickness( 0 );
926
927 ++it;
928
929 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
930 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
931
932 ( *it )->SetThickness( 0, 0 );
933 ( *it )->SetThicknessLocked( true, 0 );
934 ++it;
935 }
936
937 m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ),
938 static_cast<PCB_LAYER_ID>( curLayer++ ) } );
939
940 if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
941 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
942
943 ( *it )->SetThickness( layer.copperthick );
944
945 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
946 PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
947
948 m_board->SetLayerName( klayer, layer.name );
949
950 if( layer.copperthick == 0 )
951 m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
952 else if( IsAltiumLayerAPlane( alayer ) )
953 m_board->SetLayerType( klayer, LAYER_T::LT_POWER );
954
955 if( klayer == B_Cu )
956 {
957 if( layer.nextId != 0 )
958 THROW_IO_ERROR( wxT( "Board6 stream, unexpected id while parsing last stackup layer" ) );
959
960 // overwrite entry from internal -> bottom
961 m_layermap[alayer] = B_Cu;
962 break;
963 }
964
965 ++it;
966
967 if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
968 THROW_IO_ERROR( wxT( "Board6 stream, unexpected item while parsing stackup" ) );
969
970 ( *it )->SetThickness( layer.dielectricthick, 0 );
971 ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
973 wxString( layer.dielectricmaterial ) );
974 ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
975
976 ++it;
977 }
978
979 // Set name of all non-cu layers
980 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_OVERLAY );
981 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::BOTTOM_SOLDER ); altiumLayerId++ )
982 {
983 // array starts with 0, but stackup with 1
984 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
985
986 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
987 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
988
989 m_board->SetLayerName( klayer, layer.name );
990 }
991
992 for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_1 );
993 altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_16 ); altiumLayerId++ )
994 {
995 // array starts with 0, but stackup with 1
996 ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
997
998 ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
999 PCB_LAYER_ID klayer = GetKicadLayer( alayer );
1000
1001 m_board->SetLayerName( klayer, layer.name );
1002 }
1003
1005}
1006
1007void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
1008{
1009 SHAPE_LINE_CHAIN lineChain;
1010 HelperShapeLineChainFromAltiumVertices( lineChain, aVertices );
1011
1013 PLOT_DASH_TYPE::SOLID );
1014
1015 for( int i = 0; i <= lineChain.PointCount() && i != -1; i = lineChain.NextShape( i ) )
1016 {
1017 if( lineChain.IsArcStart( i ) )
1018 {
1019 const SHAPE_ARC& currentArc = lineChain.Arc( lineChain.ArcIndex( i ) );
1020 int nextShape = lineChain.NextShape( i );
1021 bool isLastShape = nextShape < 0;
1022
1023 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::ARC );
1024 m_board->Add( shape, ADD_MODE::APPEND );
1025
1026 shape->SetStroke( stroke );
1027 shape->SetLayer( Edge_Cuts );
1028 shape->SetArcGeometry( currentArc.GetP0(), currentArc.GetArcMid(), currentArc.GetP1() );
1029 }
1030 else
1031 {
1032 const SEG& seg = lineChain.Segment( i );
1033
1034 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1035 m_board->Add( shape, ADD_MODE::APPEND );
1036
1037 shape->SetStroke( stroke );
1038 shape->SetLayer( Edge_Cuts );
1039 shape->SetStart( seg.A );
1040 shape->SetEnd( seg.B );
1041 }
1042 }
1043}
1044
1046 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1047{
1048 if( m_progressReporter )
1049 m_progressReporter->Report( _( "Loading netclasses..." ) );
1050
1051 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1052
1053 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1054 {
1055 checkpoint();
1056 ACLASS6 elem( reader );
1057
1058 if( elem.kind == ALTIUM_CLASS_KIND::NET_CLASS )
1059 {
1060 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( elem.name );
1061
1062 for( const wxString& name : elem.names )
1063 {
1064 m_board->GetDesignSettings().m_NetSettings->m_NetClassPatternAssignments.push_back(
1065 {
1066 std::make_unique<EDA_COMBINED_MATCHER>( name, CTX_NETCLASS ),
1067 nc->GetName()
1068 } );
1069 }
1070
1071 if( m_board->GetDesignSettings().m_NetSettings->m_NetClasses.count( nc->GetName() ) )
1072 {
1073 // Name conflict, happens in some unknown circumstances
1074 // unique_ptr will delete nc on this code path
1075 wxLogWarning( _( "More than one Altium netclass with name '%s' found. "
1076 "Only the first one will be imported." ), elem.name );
1077 }
1078 else
1079 {
1080 m_board->GetDesignSettings().m_NetSettings->m_NetClasses[ nc->GetName() ] = nc;
1081 }
1082 }
1083 }
1084
1085 if( reader.GetRemainingBytes() != 0 )
1086 THROW_IO_ERROR( wxT( "Classes6 stream is not fully parsed" ) );
1087
1089}
1090
1092 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1093{
1094 if( m_progressReporter )
1095 m_progressReporter->Report( _( "Loading components..." ) );
1096
1097 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1098
1099 uint16_t componentId = 0;
1100
1101 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1102 {
1103 checkpoint();
1104 ACOMPONENT6 elem( reader );
1105
1106 FOOTPRINT* footprint = new FOOTPRINT( m_board );
1107 m_board->Add( footprint, ADD_MODE::APPEND );
1108 m_components.emplace_back( footprint );
1109
1111
1112 footprint->SetFPID( fpID );
1113
1114 footprint->SetPosition( elem.position );
1115 footprint->SetOrientationDegrees( elem.rotation );
1116
1117 // KiCad netlisting requires parts to have non-digit + digit annotation.
1118 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1119 wxString reference = elem.sourcedesignator;
1120
1121 if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1122 reference.Prepend( wxT( "UNK" ) );
1123
1124 footprint->SetReference( reference );
1125
1126 footprint->SetLocked( elem.locked );
1127 footprint->Reference().SetVisible( elem.nameon );
1128 footprint->Value().SetVisible( elem.commenton );
1129 footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
1130
1131 componentId++;
1132 }
1133
1134 if( reader.GetRemainingBytes() != 0 )
1135 THROW_IO_ERROR( wxT( "Components6 stream is not fully parsed" ) );
1136}
1137
1138
1140double normalizeAngleDegrees( double Angle, double aMin, double aMax )
1141{
1142 while( Angle < aMin )
1143 Angle += 360.0;
1144
1145 while( Angle >= aMax )
1146 Angle -= 360.0;
1147
1148 return Angle;
1149}
1150
1151
1153 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1154{
1155 if( m_progressReporter )
1156 m_progressReporter->Report( _( "Loading component 3D models..." ) );
1157
1158 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1159
1160 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1161 {
1162 checkpoint();
1163 ACOMPONENTBODY6 elem( reader ); // TODO: implement
1164
1165 if( elem.component == ALTIUM_COMPONENT_NONE )
1166 continue; // TODO: we do not support components for the board yet
1167
1168 if( m_components.size() <= elem.component )
1169 {
1170 THROW_IO_ERROR( wxString::Format( wxT( "ComponentsBodies6 stream tries to access "
1171 "component id %d of %d existing components" ),
1172 elem.component,
1173 m_components.size() ) );
1174 }
1175
1176 if( !elem.modelIsEmbedded )
1177 continue;
1178
1179 auto modelTuple = m_models.find( elem.modelId );
1180
1181 if( modelTuple == m_models.end() )
1182 {
1183 wxLogError( wxT( "ComponentsBodies6 stream tries to access model id %s which does not "
1184 "exist" ),
1185 elem.modelId );
1186 continue;
1187 }
1188
1189 FOOTPRINT* footprint = m_components.at( elem.component );
1190 const VECTOR2I& fpPosition = footprint->GetPosition();
1191
1192 FP_3DMODEL modelSettings;
1193
1194 modelSettings.m_Filename = modelTuple->second;
1195
1196 modelSettings.m_Offset.x = pcbIUScale.IUTomm((int) elem.modelPosition.x - fpPosition.x );
1197 modelSettings.m_Offset.y = -pcbIUScale.IUTomm((int) elem.modelPosition.y - fpPosition.y );
1198 modelSettings.m_Offset.z = pcbIUScale.IUTomm( (int) elem.modelPosition.z );
1199
1200 EDA_ANGLE orientation = footprint->GetOrientation();
1201
1202 if( footprint->IsFlipped() )
1203 {
1204 modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1205 orientation = -orientation;
1206 }
1207
1208 RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1209
1210 modelSettings.m_Rotation.x = normalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1211 modelSettings.m_Rotation.y = normalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1212 modelSettings.m_Rotation.z = normalizeAngleDegrees( -elem.modelRotation.z
1213 + elem.rotation
1214 + orientation.AsDegrees(),
1215 -180, 180 );
1216 modelSettings.m_Opacity = elem.bodyOpacity;
1217
1218 footprint->Models().push_back( modelSettings );
1219 }
1220
1221 if( reader.GetRemainingBytes() != 0 )
1222 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream is not fully parsed" ) );
1223}
1224
1225
1227{
1228 if( aElem.referencePoint.size() != 2 )
1229 THROW_IO_ERROR( wxT( "Incorrect number of reference points for linear dimension object" ) );
1230
1231 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1232
1233 if( klayer == UNDEFINED_LAYER )
1234 {
1235 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1236 "It has been moved to KiCad layer Eco1_User." ),
1237 aElem.layer );
1238 klayer = Eco1_User;
1239 }
1240
1241 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1242 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1243
1245 m_board->Add( dimension, ADD_MODE::APPEND );
1246
1247 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1248 dimension->SetLayer( klayer );
1249 dimension->SetStart( referencePoint0 );
1250
1251 if( referencePoint0 != aElem.xy1 )
1252 {
1262 VECTOR2I direction = aElem.xy1 - referencePoint0;
1263 VECTOR2I directionNormalVector = VECTOR2I( -direction.y, direction.x );
1264 SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1265 SEG segm2( referencePoint1, referencePoint1 + direction );
1266 OPT_VECTOR2I intersection( segm1.Intersect( segm2, true, true ) );
1267
1268 if( !intersection )
1269 THROW_IO_ERROR( wxT( "Invalid dimension. This should never happen." ) );
1270
1271 dimension->SetEnd( *intersection );
1272
1273 int height = static_cast<int>( EuclideanNorm( direction ) );
1274
1275 if( ( direction.x > 0 || direction.y < 0 ) != ( aElem.angle >= 180.0 ) )
1276 height = -height;
1277
1278 dimension->SetHeight( height );
1279 }
1280 else
1281 {
1282 dimension->SetEnd( referencePoint1 );
1283 }
1284
1285 dimension->SetLineThickness( aElem.linewidth );
1286
1287 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
1288 dimension->SetPrefix( aElem.textprefix );
1289
1290 // Suffix normally (but not always) holds the units
1291 wxRegEx units( wxS( "(mm)|(in)|(mils)|(thou)|(')|(\")" ), wxRE_ADVANCED );
1292
1293 if( units.Matches( aElem.textsuffix ) )
1294 dimension->SetUnitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX );
1295 else
1296 dimension->SetSuffix( aElem.textsuffix );
1297
1298 dimension->SetTextThickness( aElem.textlinewidth );
1299 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1300 dimension->SetItalic( aElem.textitalic );
1301
1302#if 0 // we don't currently support bold; map to thicker text
1303 dimension->Text().SetBold( aElem.textbold );
1304#else
1305 if( aElem.textbold )
1306 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1307#endif
1308
1309 switch( aElem.textunit )
1310 {
1311 case ALTIUM_UNIT::INCHES:
1312 dimension->SetUnits( EDA_UNITS::INCHES );
1313 break;
1314 case ALTIUM_UNIT::MILS:
1315 dimension->SetUnits( EDA_UNITS::MILS );
1316 break;
1317 case ALTIUM_UNIT::MILLIMETERS:
1318 case ALTIUM_UNIT::CENTIMETER:
1319 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1320 break;
1321 default:
1322 break;
1323 }
1324}
1325
1326
1328{
1329 if( aElem.referencePoint.size() < 2 )
1330 THROW_IO_ERROR( wxT( "Not enough reference points for radial dimension object" ) );
1331
1332 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1333
1334 if( klayer == UNDEFINED_LAYER )
1335 {
1336 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1337 "It has been moved to KiCad layer Eco1_User." ),
1338 aElem.layer );
1339 klayer = Eco1_User;
1340 }
1341
1342 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1343 VECTOR2I referencePoint1 = aElem.referencePoint.at( 1 );
1344
1345 PCB_DIM_RADIAL* dimension = new PCB_DIM_RADIAL( m_board );
1346 m_board->Add( dimension, ADD_MODE::APPEND );
1347 m_radialDimensions.push_back( dimension );
1348
1349 dimension->SetPrecision( static_cast<DIM_PRECISION>( aElem.textprecision ) );
1350 dimension->SetLayer( klayer );
1351 dimension->SetStart( referencePoint0 );
1352 dimension->SetEnd( aElem.xy1 );
1353 dimension->SetLineThickness( aElem.linewidth );
1354 dimension->SetKeepTextAligned( false );
1355
1356 dimension->SetPrefix( aElem.textprefix );
1357
1358 // Suffix normally holds the units
1359 dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1360 : DIM_UNITS_FORMAT::BARE_SUFFIX );
1361
1362 switch( aElem.textunit )
1363 {
1364 case ALTIUM_UNIT::INCHES:
1365 dimension->SetUnits( EDA_UNITS::INCHES );
1366 break;
1367 case ALTIUM_UNIT::MILS:
1368 dimension->SetUnits( EDA_UNITS::MILS );
1369 break;
1370 case ALTIUM_UNIT::MILLIMETERS:
1371 case ALTIUM_UNIT::CENTIMETER:
1372 dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1373 break;
1374 default:
1375 break;
1376 }
1377
1378 if( aElem.textPoint.empty() )
1379 {
1380 wxLogError( wxT( "No text position present for leader dimension object" ) );
1381 return;
1382 }
1383
1384 dimension->SetTextPos( aElem.textPoint.at( 0 ) );
1385 dimension->SetTextThickness( aElem.textlinewidth );
1386 dimension->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) );
1387 dimension->SetItalic( aElem.textitalic );
1388
1389#if 0 // we don't currently support bold; map to thicker text
1390 dimension->SetBold( aElem.textbold );
1391#else
1392 if( aElem.textbold )
1393 dimension->SetTextThickness( dimension->GetTextThickness() * BOLD_FACTOR );
1394#endif
1395
1396 // It's unclear exactly how Altium figures it's text positioning, but this gets us reasonably
1397 // close.
1400
1401 int yAdjust = dimension->GetTextBox().GetCenter().y - dimension->GetTextPos().y;
1402 dimension->SetTextPos( dimension->GetTextPos() + VECTOR2I( 0, yAdjust + aElem.textgap ) );
1404}
1405
1406
1408{
1409 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1410
1411 if( klayer == UNDEFINED_LAYER )
1412 {
1413 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1414 "It has been moved to KiCad layer Eco1_User." ),
1415 aElem.layer );
1416 klayer = Eco1_User;
1417 }
1418
1419 if( !aElem.referencePoint.empty() )
1420 {
1421 VECTOR2I referencePoint0 = aElem.referencePoint.at( 0 );
1422
1423 // line
1424 VECTOR2I last = referencePoint0;
1425 for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1426 {
1427 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1428 m_board->Add( shape, ADD_MODE::APPEND );
1429 shape->SetLayer( klayer );
1430 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, PLOT_DASH_TYPE::SOLID ) );
1431 shape->SetStart( last );
1432 shape->SetEnd( aElem.referencePoint.at( i ) );
1433 last = aElem.referencePoint.at( i );
1434 }
1435
1436 // arrow
1437 if( aElem.referencePoint.size() >= 2 )
1438 {
1439 VECTOR2I dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1440
1441 if( dirVec.x != 0 || dirVec.y != 0 )
1442 {
1443 double scaling = EuclideanNorm( dirVec ) / aElem.arrowsize;
1444 VECTOR2I arrVec = VECTOR2I( KiROUND( dirVec.x / scaling ),
1445 KiROUND( dirVec.y / scaling ) );
1446 RotatePoint( arrVec, EDA_ANGLE( 20.0, DEGREES_T ) );
1447
1448 PCB_SHAPE* shape1 = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1449 m_board->Add( shape1, ADD_MODE::APPEND );
1450 shape1->SetLayer( klayer );
1451 shape1->SetStroke( STROKE_PARAMS( aElem.linewidth, PLOT_DASH_TYPE::SOLID ) );
1452 shape1->SetStart( referencePoint0 );
1453 shape1->SetEnd( referencePoint0 + arrVec );
1454
1455 RotatePoint( arrVec, EDA_ANGLE( -40.0, DEGREES_T ) );
1456
1457 PCB_SHAPE* shape2 = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1458 m_board->Add( shape2, ADD_MODE::APPEND );
1459 shape2->SetLayer( klayer );
1460 shape2->SetStroke( STROKE_PARAMS( aElem.linewidth, PLOT_DASH_TYPE::SOLID ) );
1461 shape2->SetStart( referencePoint0 );
1462 shape2->SetEnd( referencePoint0 + arrVec );
1463 }
1464 }
1465 }
1466
1467 if( aElem.textPoint.empty() )
1468 {
1469 wxLogError( wxT( "No text position present for leader dimension object" ) );
1470 return;
1471 }
1472
1473 PCB_TEXT* text = new PCB_TEXT( m_board );
1474 m_board->Add( text, ADD_MODE::APPEND );
1475 text->SetText( aElem.textformat );
1476 text->SetPosition( aElem.textPoint.at( 0 ) );
1477 text->SetLayer( klayer );
1478 text->SetTextSize( VECTOR2I( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1479 text->SetTextThickness( aElem.textlinewidth );
1480 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1481 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1482}
1483
1484
1486{
1487 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1488
1489 if( klayer == UNDEFINED_LAYER )
1490 {
1491 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1492 "It has been moved to KiCad layer Eco1_User." ),
1493 aElem.layer );
1494 klayer = Eco1_User;
1495 }
1496
1497 for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1498 {
1499 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1500 m_board->Add( shape, ADD_MODE::APPEND );
1501 shape->SetLayer( klayer );
1502 shape->SetStroke( STROKE_PARAMS( aElem.linewidth, PLOT_DASH_TYPE::SOLID ) );
1503 shape->SetStart( aElem.referencePoint.at( i ) );
1504 // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1505 }
1506}
1507
1508
1510{
1511 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1512
1513 if( klayer == UNDEFINED_LAYER )
1514 {
1515 wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1516 "It has been moved to KiCad layer Eco1_User." ),
1517 aElem.layer );
1518 klayer = Eco1_User;
1519 }
1520
1521 VECTOR2I vec = VECTOR2I( 0, aElem.height / 2 );
1522 RotatePoint( vec, EDA_ANGLE( aElem.angle, DEGREES_T ) );
1523
1524 PCB_DIM_CENTER* dimension = new PCB_DIM_CENTER( m_board );
1525 m_board->Add( dimension, ADD_MODE::APPEND );
1526 dimension->SetLayer( klayer );
1527 dimension->SetLineThickness( aElem.linewidth );
1528 dimension->SetStart( aElem.xy1 );
1529 dimension->SetEnd( aElem.xy1 + vec );
1530}
1531
1532
1534 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1535{
1536 if( m_progressReporter )
1537 m_progressReporter->Report( _( "Loading dimension drawings..." ) );
1538
1539 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1540
1541 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1542 {
1543 checkpoint();
1544 ADIMENSION6 elem( reader );
1545
1546 switch( elem.kind )
1547 {
1548 case ALTIUM_DIMENSION_KIND::LINEAR:
1550 break;
1551 case ALTIUM_DIMENSION_KIND::RADIAL:
1553 break;
1554 case ALTIUM_DIMENSION_KIND::LEADER:
1556 break;
1557 case ALTIUM_DIMENSION_KIND::DATUM:
1558 wxLogError( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1559 // HelperParseDimensions6Datum( elem );
1560 break;
1561 case ALTIUM_DIMENSION_KIND::CENTER:
1563 break;
1564 default:
1565 wxLogError( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1566 break;
1567 }
1568 }
1569
1570 if( reader.GetRemainingBytes() != 0 )
1571 THROW_IO_ERROR( wxT( "Dimensions6 stream is not fully parsed" ) );
1572}
1573
1574
1576 const CFB::COMPOUND_FILE_ENTRY* aEntry,
1577 const std::vector<std::string>& aRootDir )
1578{
1579 if( m_progressReporter )
1580 m_progressReporter->Report( _( "Loading 3D models..." ) );
1581
1582 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1583
1584 if( reader.GetRemainingBytes() == 0 )
1585 return;
1586
1587 wxString projectPath = wxPathOnly( m_board->GetFileName() );
1588 // TODO: set KIPRJMOD always after import (not only when loading project)?
1589 wxSetEnv( PROJECT_VAR_NAME, projectPath );
1590
1591 // TODO: make this path configurable?
1592 const wxString altiumModelDir = wxT( "ALTIUM_EMBEDDED_MODELS" );
1593
1594 wxFileName altiumModelsPath = wxFileName::DirName( projectPath );
1595 wxString kicadModelPrefix = wxT( "${KIPRJMOD}/" ) + altiumModelDir + wxT( "/" );
1596
1597 if( !altiumModelsPath.AppendDir( altiumModelDir ) )
1598 THROW_IO_ERROR( wxT( "Cannot construct directory path for step models" ) );
1599
1600 // Create dir if it does not exist
1601 if( !altiumModelsPath.DirExists() )
1602 {
1603 if( !altiumModelsPath.Mkdir() )
1604 {
1605 wxLogError( _( "Failed to create folder '%s'." ) + wxS( " " )
1606 + _( "No 3D-models will be imported." ),
1607 altiumModelsPath.GetFullPath() );
1608 return;
1609 }
1610 }
1611
1612 int idx = 0;
1613 wxString invalidChars = wxFileName::GetForbiddenChars();
1614
1615 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1616 {
1617 checkpoint();
1618 AMODEL elem( reader );
1619
1620 std::vector<std::string> stepPath = aRootDir;
1621 stepPath.emplace_back( std::to_string( idx ) );
1622
1623 bool validName = !elem.name.IsEmpty() && elem.name.IsAscii() &&
1624 wxString::npos == elem.name.find_first_of( invalidChars );
1625 wxString storageName = !validName ? wxString::Format( wxT( "model_%d" ), idx )
1626 : elem.name;
1627 wxFileName storagePath( altiumModelsPath.GetPath(), storageName );
1628
1629 idx++;
1630
1631 const CFB::COMPOUND_FILE_ENTRY* stepEntry = aAltiumPcbFile.FindStream( stepPath );
1632
1633 if( stepEntry == nullptr )
1634 {
1635 wxLogError( _( "File not found: '%s'. 3D-model not imported." ),
1636 FormatPath( stepPath ) );
1637 continue;
1638 }
1639
1640 size_t stepSize = static_cast<size_t>( stepEntry->size );
1641 std::vector<char> stepContent( stepSize );
1642
1643 // read file into buffer
1644 aAltiumPcbFile.GetCompoundFileReader().ReadFile( stepEntry, 0, stepContent.data(),
1645 stepSize );
1646
1647 if( !storagePath.IsDirWritable() )
1648 {
1649 wxLogError( _( "Insufficient permissions to save file '%s'." ),
1650 storagePath.GetFullPath() );
1651 continue;
1652 }
1653
1654 wxMemoryInputStream stepStream( stepContent.data(), stepSize );
1655 wxZlibInputStream zlibInputStream( stepStream );
1656
1657 wxFFileOutputStream outputStream( storagePath.GetFullPath() );
1658 outputStream.Write( zlibInputStream );
1659 outputStream.Close();
1660
1661 m_models.insert( { elem.id, kicadModelPrefix + storageName } );
1662 }
1663
1664 if( reader.GetRemainingBytes() != 0 )
1665 THROW_IO_ERROR( wxT( "Models stream is not fully parsed" ) );
1666}
1667
1668
1670 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1671{
1672 if( m_progressReporter )
1673 m_progressReporter->Report( _( "Loading nets..." ) );
1674
1675 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1676
1677 wxASSERT( m_altiumToKicadNetcodes.empty() );
1678 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1679 {
1680 checkpoint();
1681 ANET6 elem( reader );
1682
1683 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, elem.name, 0 );
1684 m_board->Add( netInfo, ADD_MODE::APPEND );
1685
1686 m_altiumToKicadNetcodes.push_back( netInfo->GetNetCode() );
1687 }
1688
1689 if( reader.GetRemainingBytes() != 0 )
1690 THROW_IO_ERROR( wxT( "Nets6 stream is not fully parsed" ) );
1691}
1692
1694 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1695{
1696 if( m_progressReporter )
1697 m_progressReporter->Report( _( "Loading polygons..." ) );
1698
1699 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1700
1701 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1702 {
1703 checkpoint();
1704 APOLYGON6 elem( reader );
1705
1706 SHAPE_LINE_CHAIN linechain;
1708
1709 if( linechain.PointCount() < 3 )
1710 {
1711 // We have found multiple Altium files with polygon records containing nothing but two
1712 // coincident vertices. These polygons do not appear when opening the file in Altium.
1713 // https://gitlab.com/kicad/code/kicad/-/issues/8183
1714 // Also, polygons with less than 3 points are not supported in KiCad.
1715 //
1716 // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
1717 // "points are required." ),
1718 // linechain.PointCount(),
1719 // elem.vertices.size() );
1720
1721 m_polygons.emplace_back( nullptr );
1722 continue;
1723 }
1724
1725 ZONE* zone = new ZONE( m_board );
1726 m_board->Add( zone, ADD_MODE::APPEND );
1727 m_polygons.emplace_back( zone );
1728
1729 zone->SetNetCode( GetNetCode( elem.net ) );
1730 zone->SetPosition( elem.vertices.at( 0 ).position );
1731 zone->SetLocked( elem.locked );
1732 zone->SetAssignedPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
1733 zone->Outline()->AddOutline( linechain );
1734
1735 HelperSetZoneLayers( zone, elem.layer );
1736
1737 if( elem.pourindex > m_highest_pour_index )
1739
1740 // TODO: more flexible rule parsing
1741 const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
1742
1743 if( clearanceRule != nullptr )
1744 zone->SetLocalClearance( clearanceRule->planeclearanceClearance );
1745
1746 const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
1747
1748 if( polygonConnectRule != nullptr )
1749 {
1750 switch( polygonConnectRule->polygonconnectStyle )
1751 {
1752 case ALTIUM_CONNECT_STYLE::DIRECT:
1753 zone->SetPadConnection( ZONE_CONNECTION::FULL );
1754 break;
1755
1756 case ALTIUM_CONNECT_STYLE::NONE:
1757 zone->SetPadConnection( ZONE_CONNECTION::NONE );
1758 break;
1759
1760 default:
1761 case ALTIUM_CONNECT_STYLE::RELIEF:
1762 zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
1763 break;
1764 }
1765
1766 // TODO: correct variables?
1768 polygonConnectRule->polygonconnectReliefconductorwidth );
1769 zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
1770
1771 if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
1772 zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
1773 }
1774
1775 if( IsAltiumLayerAPlane( elem.layer ) )
1776 {
1777 // outer zone will be set to priority 0 later.
1778 zone->SetAssignedPriority( 1 );
1779
1780 // check if this is the outer zone by simply comparing the BBOX
1781 const auto& outer_plane = m_outer_plane.find( elem.layer );
1782 if( outer_plane == m_outer_plane.end()
1783 || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
1784 {
1785 m_outer_plane[elem.layer] = zone;
1786 }
1787 }
1788
1789 if( elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::SOLID
1790 && elem.hatchstyle != ALTIUM_POLYGON_HATCHSTYLE::UNKNOWN )
1791 {
1792 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
1793 zone->SetHatchThickness( elem.trackwidth );
1794
1795 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::NONE )
1796 {
1797 // use a small hack to get us only an outline (hopefully)
1798 const BOX2I& bbox = zone->GetBoundingBox();
1799 zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
1800 }
1801 else
1802 {
1803 zone->SetHatchGap( elem.gridsize - elem.trackwidth );
1804 }
1805
1806 if( elem.hatchstyle == ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45 )
1808 }
1809
1810 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
1812 }
1813
1814 if( reader.GetRemainingBytes() != 0 )
1815 THROW_IO_ERROR( wxT( "Polygons6 stream is not fully parsed" ) );
1816}
1817
1819 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1820{
1821 if( m_progressReporter )
1822 m_progressReporter->Report( _( "Loading rules..." ) );
1823
1824 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1825
1826 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1827 {
1828 checkpoint();
1829 ARULE6 elem( reader );
1830
1831 m_rules[elem.kind].emplace_back( elem );
1832 }
1833
1834 // sort rules by priority
1835 for( std::pair<const ALTIUM_RULE_KIND, std::vector<ARULE6>>& val : m_rules )
1836 {
1837 std::sort( val.second.begin(), val.second.end(),
1838 []( const ARULE6& lhs, const ARULE6& rhs )
1839 {
1840 return lhs.priority < rhs.priority;
1841 } );
1842 }
1843
1844 const ARULE6* soldermaskRule = GetRuleDefault( ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION );
1845 const ARULE6* pastemaskRule = GetRuleDefault( ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION );
1846
1847 if( soldermaskRule )
1849
1850 if( pastemaskRule )
1852
1853 if( reader.GetRemainingBytes() != 0 )
1854 THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
1855}
1856
1858 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1859{
1860 if( m_progressReporter )
1861 m_progressReporter->Report( _( "Loading board regions..." ) );
1862
1863 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1864
1865 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1866 {
1867 checkpoint();
1868 AREGION6 elem( reader, false );
1869
1870 // TODO: implement?
1871 }
1872
1873 if( reader.GetRemainingBytes() != 0 )
1874 THROW_IO_ERROR( wxT( "BoardRegions stream is not fully parsed" ) );
1875}
1876
1878 const CFB::COMPOUND_FILE_ENTRY* aEntry )
1879{
1880 if( m_progressReporter )
1881 m_progressReporter->Report( _( "Loading polygons..." ) );
1882
1883 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
1884
1885 /* TODO: use Header section of file */
1886 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
1887 {
1888 checkpoint();
1889 AREGION6 elem( reader, true );
1890
1892 || elem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
1893 {
1894 // TODO: implement all different types for footprints
1896 }
1897 else
1898 {
1899 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
1900 ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
1901 }
1902 }
1903
1904 if( reader.GetRemainingBytes() != 0 )
1905 THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
1906}
1907
1908
1910{
1911 if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
1912 {
1914 }
1915 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
1916 {
1917 SHAPE_LINE_CHAIN linechain;
1919
1920 if( linechain.PointCount() < 3 )
1921 {
1922 // We have found multiple Altium files with polygon records containing nothing but
1923 // two coincident vertices. These polygons do not appear when opening the file in
1924 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
1925 // Also, polygons with less than 3 points are not supported in KiCad.
1926 return;
1927 }
1928
1929 ZONE* zone = new ZONE( m_board );
1930 m_board->Add( zone, ADD_MODE::APPEND );
1931
1932 zone->SetIsRuleArea( true );
1933
1934 if( aElem.is_keepout )
1935 {
1937 }
1938 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
1939 {
1940 zone->SetDoNotAllowCopperPour( true );
1941 zone->SetDoNotAllowVias( false );
1942 zone->SetDoNotAllowTracks( false );
1943 zone->SetDoNotAllowPads( false );
1944 zone->SetDoNotAllowFootprints( false );
1945 }
1946
1947 zone->SetPosition( aElem.outline.at( 0 ).position );
1948 zone->Outline()->AddOutline( linechain );
1949
1950 HelperSetZoneLayers( zone, aElem.layer );
1951
1952 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
1954 }
1955 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
1956 {
1957 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1958
1959 if( klayer == UNDEFINED_LAYER )
1960 {
1961 wxLogWarning(
1962 _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
1963 "It has been moved to KiCad layer Eco1_User." ),
1964 aElem.layer );
1965 klayer = Eco1_User;
1966 }
1967
1968 SHAPE_LINE_CHAIN linechain;
1970
1971 if( linechain.PointCount() < 3 )
1972 {
1973 // We have found multiple Altium files with polygon records containing nothing but
1974 // two coincident vertices. These polygons do not appear when opening the file in
1975 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
1976 // Also, polygons with less than 3 points are not supported in KiCad.
1977 return;
1978 }
1979
1980 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::POLY );
1981
1982 shape->SetPolyShape( linechain );
1983 shape->SetFilled( false );
1984 shape->SetLayer( klayer );
1985 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), PLOT_DASH_TYPE::DASH ) );
1986
1987 m_board->Add( shape, ADD_MODE::APPEND );
1988 }
1989 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
1990 {
1991 if( aElem.subpolyindex == ALTIUM_POLYGON_NONE )
1992 {
1993 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
1995 }
1996 }
1997 else if( aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
1998 {
2000 }
2001 else
2002 {
2003 wxLogError( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2004 }
2005}
2006
2007
2009 const AREGION6& aElem,
2010 const int aPrimitiveIndex )
2011{
2012 if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
2013 {
2014 SHAPE_LINE_CHAIN linechain;
2016
2017 if( linechain.PointCount() < 3 )
2018 {
2019 // We have found multiple Altium files with polygon records containing nothing but
2020 // two coincident vertices. These polygons do not appear when opening the file in
2021 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2022 // Also, polygons with less than 3 points are not supported in KiCad.
2023 return;
2024 }
2025
2026 ZONE* zone = new ZONE( aFootprint );
2027 aFootprint->Add( zone, ADD_MODE::APPEND );
2028
2029 zone->SetIsRuleArea( true );
2030
2031 if( aElem.is_keepout )
2032 {
2034 }
2035 else if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT )
2036 {
2037 zone->SetDoNotAllowCopperPour( true );
2038 zone->SetDoNotAllowVias( false );
2039 zone->SetDoNotAllowTracks( false );
2040 zone->SetDoNotAllowPads( false );
2041 zone->SetDoNotAllowFootprints( false );
2042 }
2043
2044 zone->SetPosition( aElem.outline.at( 0 ).position );
2045 zone->Outline()->AddOutline( linechain );
2046
2047 HelperSetZoneLayers( zone, aElem.layer );
2048
2049 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2051 }
2052 else if( aElem.kind == ALTIUM_REGION_KIND::COPPER )
2053 {
2054 if( aElem.subpolyindex == ALTIUM_POLYGON_NONE )
2055 {
2056 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2057 {
2058 ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
2059 aPrimitiveIndex );
2060 }
2061 }
2062 }
2063 else if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE
2064 || aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT )
2065 {
2066 PCB_LAYER_ID klayer = aElem.kind == ALTIUM_REGION_KIND::BOARD_CUTOUT
2067 ? Edge_Cuts
2068 : GetKicadLayer( aElem.layer );
2069
2070 if( klayer == UNDEFINED_LAYER )
2071 {
2072 wxLogWarning(
2073 _( "Dashed outline found on an Altium layer (%d) with no KiCad equivalent. "
2074 "It has been moved to KiCad layer Eco1_User." ),
2075 aElem.layer );
2076 klayer = Eco1_User;
2077 }
2078
2079 SHAPE_LINE_CHAIN linechain;
2081
2082 if( linechain.PointCount() < 3 )
2083 {
2084 // We have found multiple Altium files with polygon records containing nothing but
2085 // two coincident vertices. These polygons do not appear when opening the file in
2086 // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2087 // Also, polygons with less than 3 points are not supported in KiCad.
2088 return;
2089 }
2090
2091 PCB_SHAPE* shape = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
2092
2093 shape->SetPolyShape( linechain );
2094 shape->SetFilled( false );
2095 shape->SetLayer( klayer );
2096
2097 if( aElem.kind == ALTIUM_REGION_KIND::DASHED_OUTLINE )
2098 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), PLOT_DASH_TYPE::DASH ) );
2099 else
2100 shape->SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( 0.1 ), PLOT_DASH_TYPE::SOLID ) );
2101
2102 aFootprint->Add( shape, ADD_MODE::APPEND );
2103 }
2104 else
2105 {
2106 wxLogError( _( "Ignored polygon shape of kind %d (not yet supported)." ), aElem.kind );
2107 }
2108}
2109
2110
2112 PCB_LAYER_ID aLayer )
2113{
2114 SHAPE_LINE_CHAIN linechain;
2116
2117 if( linechain.PointCount() < 3 )
2118 {
2119 // We have found multiple Altium files with polygon records containing nothing
2120 // but two coincident vertices. These polygons do not appear when opening the
2121 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2122 // Also, polygons with less than 3 points are not supported in KiCad.
2123 return;
2124 }
2125
2126 SHAPE_POLY_SET polySet;
2127 polySet.AddOutline( linechain );
2128
2129 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2130 {
2131 SHAPE_LINE_CHAIN hole_linechain;
2132 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2133
2134 if( hole_linechain.PointCount() < 3 )
2135 continue;
2136
2137 polySet.AddHole( hole_linechain );
2138 }
2139
2140 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::POLY );
2141
2142 shape->SetPolyShape( polySet );
2143 shape->SetFilled( true );
2144 shape->SetLayer( aLayer );
2145 shape->SetStroke( STROKE_PARAMS( 0 ) );
2146
2147 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2148 {
2149 shape->SetNetCode( GetNetCode( aElem.net ) );
2150 }
2151
2152 m_board->Add( shape, ADD_MODE::APPEND );
2153}
2154
2155
2157 const AREGION6& aElem,
2158 PCB_LAYER_ID aLayer,
2159 const int aPrimitiveIndex )
2160{
2161 SHAPE_LINE_CHAIN linechain;
2163
2164 if( linechain.PointCount() < 3 )
2165 {
2166 // We have found multiple Altium files with polygon records containing nothing
2167 // but two coincident vertices. These polygons do not appear when opening the
2168 // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
2169 // Also, polygons with less than 3 points are not supported in KiCad.
2170 return;
2171 }
2172
2173 SHAPE_POLY_SET polySet;
2174 polySet.AddOutline( linechain );
2175
2176 for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
2177 {
2178 SHAPE_LINE_CHAIN hole_linechain;
2179 HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
2180
2181 if( hole_linechain.PointCount() < 3 )
2182 continue;
2183
2184 polySet.AddHole( hole_linechain );
2185 }
2186
2187 if( aLayer == F_Cu || aLayer == B_Cu )
2188 {
2189 PAD* pad = new PAD( aFootprint );
2190
2191 LSET padLayers;
2192 padLayers.set( aLayer );
2193
2194 pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
2195 pad->SetAttribute( PAD_ATTRIB::SMD );
2196 pad->SetShape( PAD_SHAPE::CUSTOM );
2197
2198 int anchorSize = 1;
2199 VECTOR2I anchorPos = linechain.CPoint( 0 );
2200
2201 pad->SetShape( PAD_SHAPE::CUSTOM );
2202 pad->SetAnchorPadShape( PAD_SHAPE::CIRCLE );
2203 pad->SetSize( { anchorSize, anchorSize } );
2204 pad->SetPosition( anchorPos );
2205
2206 SHAPE_POLY_SET shapePolys = polySet;
2207 shapePolys.Move( -anchorPos );
2208 pad->AddPrimitivePoly( shapePolys, 0, true );
2209
2210 auto& map = m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::REGION];
2211 auto it = map.find( aPrimitiveIndex );
2212
2213 if( it != map.end() )
2214 {
2215 const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
2216
2217 if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2218 {
2219 pad->SetLocalSolderPasteMargin(
2220 info.pastemaskexpansionmanual ? info.pastemaskexpansionmanual : 1 );
2221 }
2222
2223 if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2224 {
2225 pad->SetLocalSolderMaskMargin(
2226 info.soldermaskexpansionmanual ? info.soldermaskexpansionmanual : 1 );
2227 }
2228
2229 if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
2230 padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
2231
2232 if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
2233 padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
2234 }
2235
2236 pad->SetLayerSet( padLayers );
2237
2238 aFootprint->Add( pad, ADD_MODE::APPEND );
2239 }
2240 else
2241 {
2242 PCB_SHAPE* shape = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
2243
2244 shape->SetPolyShape( polySet );
2245 shape->SetFilled( true );
2246 shape->SetLayer( aLayer );
2247 shape->SetStroke( STROKE_PARAMS( 0 ) );
2248
2249 aFootprint->Add( shape, ADD_MODE::APPEND );
2250 }
2251}
2252
2253
2255 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2256{
2257 if( m_progressReporter )
2258 m_progressReporter->Report( _( "Loading zone fills..." ) );
2259
2260 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2261
2262 for( ZONE* zone : m_polygons )
2263 {
2264 if( zone )
2265 zone->UnFill(); // just to be sure
2266 }
2267
2268 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2269 {
2270 checkpoint();
2271 AREGION6 elem( reader, false );
2272
2273 if( elem.subpolyindex != ALTIUM_POLYGON_NONE )
2274 {
2275 if( m_polygons.size() <= elem.subpolyindex )
2276 {
2277 THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
2278 "of %d existing polygons.",
2279 elem.subpolyindex,
2280 m_polygons.size() ) );
2281 }
2282
2283 ZONE *zone = m_polygons.at( elem.subpolyindex );
2284
2285 if( zone == nullptr )
2286 {
2287 continue; // we know the zone id, but because we do not know the layer we did not
2288 // add it!
2289 }
2290
2291 PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2292
2293 if( klayer == UNDEFINED_LAYER )
2294 continue; // Just skip it for now. Users can fill it themselves.
2295
2296 SHAPE_LINE_CHAIN linechain;
2297
2298 for( const ALTIUM_VERTICE& vertice : elem.outline )
2299 linechain.Append( vertice.position );
2300
2301 linechain.Append( elem.outline.at( 0 ).position );
2302 linechain.SetClosed( true );
2303
2304 SHAPE_POLY_SET fill;
2305 fill.AddOutline( linechain );
2306
2307 for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
2308 {
2309 SHAPE_LINE_CHAIN hole_linechain;
2310
2311 for( const ALTIUM_VERTICE& vertice : hole )
2312 hole_linechain.Append( vertice.position );
2313
2314 hole_linechain.Append( hole.at( 0 ).position );
2315 hole_linechain.SetClosed( true );
2316 fill.AddHole( hole_linechain );
2317 }
2318
2319 if( zone->HasFilledPolysForLayer( klayer ) )
2320 fill.BooleanAdd( *zone->GetFill( klayer ), SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
2321
2323
2324 zone->SetFilledPolysList( klayer, fill );
2325 zone->SetIsFilled( true );
2326 zone->SetNeedRefill( false );
2327 }
2328 }
2329
2330 if( reader.GetRemainingBytes() != 0 )
2331 THROW_IO_ERROR( wxT( "Regions6 stream is not fully parsed" ) );
2332}
2333
2334
2336 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2337{
2338 if( m_progressReporter )
2339 m_progressReporter->Report( _( "Loading arcs..." ) );
2340
2341 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2342
2343 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
2344 {
2345 checkpoint();
2346 AARC6 elem( reader );
2347
2348 if( elem.component == ALTIUM_COMPONENT_NONE )
2349 {
2350 ConvertArcs6ToBoardItem( elem, primitiveIndex );
2351 }
2352 else
2353 {
2354 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2355 ConvertArcs6ToFootprintItem( footprint, elem, primitiveIndex, true );
2356 }
2357 }
2358
2359 if( reader.GetRemainingBytes() != 0 )
2360 THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2361}
2362
2363
2365{
2366 if( aElem.startangle == 0. && aElem.endangle == 360. )
2367 {
2368 aShape->SetShape( SHAPE_T::CIRCLE );
2369
2370 // TODO: other variants to define circle?
2371 aShape->SetStart( aElem.center );
2372 aShape->SetEnd( aElem.center - VECTOR2I( 0, aElem.radius ) );
2373 }
2374 else
2375 {
2376 aShape->SetShape( SHAPE_T::ARC );
2377
2378 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2379 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2380
2381 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2382 -KiROUND( startAngle.Sin() * aElem.radius ) );
2383
2384 aShape->SetCenter( aElem.center );
2385 aShape->SetStart( aElem.center + startOffset );
2386 aShape->SetArcAngleAndEnd( includedAngle.Normalize(), true );
2387 }
2388}
2389
2390
2391void ALTIUM_PCB::ConvertArcs6ToBoardItem( const AARC6& aElem, const int aPrimitiveIndex )
2392{
2393 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
2394 return;
2395
2396 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2397 || IsAltiumLayerAPlane( aElem.layer ) )
2398 {
2399 // This is not the actual board item. We can use it to create the polygon for the region
2400 PCB_SHAPE shape( nullptr );
2401
2402 ConvertArcs6ToPcbShape( aElem, &shape );
2403 shape.SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
2404
2406 }
2407 else
2408 {
2409 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2410 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2411 }
2412
2413 for( const auto& layerExpansionMask :
2414 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
2415 {
2416 int width = aElem.width + ( layerExpansionMask.second * 2 );
2417
2418 if( width > 1 )
2419 {
2420 PCB_SHAPE* arc = new PCB_SHAPE( m_board );
2421
2422 ConvertArcs6ToPcbShape( aElem, arc );
2423 arc->SetStroke( STROKE_PARAMS( width, PLOT_DASH_TYPE::SOLID ) );
2424 arc->SetLayer( layerExpansionMask.first );
2425
2426 m_board->Add( arc, ADD_MODE::APPEND );
2427 }
2428 }
2429}
2430
2431
2433 const int aPrimitiveIndex, const bool aIsBoardImport )
2434{
2435 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
2436 return;
2437
2438 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
2439 || IsAltiumLayerAPlane( aElem.layer ) )
2440 {
2441 // This is not the actual board item. We can use it to create the polygon for the region
2442 PCB_SHAPE shape( nullptr );
2443
2444 ConvertArcs6ToPcbShape( aElem, &shape );
2445 shape.SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
2446
2447 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
2448 aElem.keepoutrestrictions );
2449 }
2450 else
2451 {
2452 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
2453 {
2454 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2455 {
2456 // Special case: do to not lose net connections in footprints
2457 ConvertArcs6ToBoardItemOnLayer( aElem, klayer );
2458 }
2459 else
2460 {
2461 ConvertArcs6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
2462 }
2463 }
2464 }
2465
2466 for( const auto& layerExpansionMask :
2467 HelperGetSolderAndPasteMaskExpansions( ALTIUM_RECORD::ARC, aPrimitiveIndex, aElem.layer ) )
2468 {
2469 int width = aElem.width + ( layerExpansionMask.second * 2 );
2470
2471 if( width > 1 )
2472 {
2473 PCB_SHAPE* arc = new PCB_SHAPE( aFootprint );
2474
2475 ConvertArcs6ToPcbShape( aElem, arc );
2476 arc->SetStroke( STROKE_PARAMS( width, PLOT_DASH_TYPE::SOLID ) );
2477 arc->SetLayer( layerExpansionMask.first );
2478
2479 aFootprint->Add( arc, ADD_MODE::APPEND );
2480 }
2481 }
2482}
2483
2484
2486{
2487 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
2488 {
2489 // TODO: This is not the actual board item. We use it for now to calculate the arc points. This could be improved!
2490 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
2491
2492 EDA_ANGLE includedAngle( aElem.endangle - aElem.startangle, DEGREES_T );
2493 EDA_ANGLE startAngle( aElem.endangle, DEGREES_T );
2494
2495 VECTOR2I startOffset = VECTOR2I( KiROUND( startAngle.Cos() * aElem.radius ),
2496 -KiROUND( startAngle.Sin() * aElem.radius ) );
2497
2498 shape.SetCenter( aElem.center );
2499 shape.SetStart( aElem.center + startOffset );
2500 shape.SetArcAngleAndEnd( includedAngle.Normalize(), true );
2501
2502 // Create actual arc
2503 SHAPE_ARC shapeArc( shape.GetCenter(), shape.GetStart(), shape.GetArcAngle(), aElem.width );
2504 PCB_ARC* arc = new PCB_ARC( m_board, &shapeArc );
2505
2506 arc->SetWidth( aElem.width );
2507 arc->SetLayer( aLayer );
2508 arc->SetNetCode( GetNetCode( aElem.net ) );
2509
2510 m_board->Add( arc, ADD_MODE::APPEND );
2511 }
2512 else
2513 {
2514 PCB_SHAPE* arc = new PCB_SHAPE( m_board );
2515
2516 ConvertArcs6ToPcbShape( aElem, arc );
2517 arc->SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
2518 arc->SetLayer( aLayer );
2519
2520 m_board->Add( arc, ADD_MODE::APPEND );
2521 }
2522}
2523
2524
2526 PCB_LAYER_ID aLayer )
2527{
2528 PCB_SHAPE* arc = new PCB_SHAPE( aFootprint );
2529
2530 ConvertArcs6ToPcbShape( aElem, arc );
2531 arc->SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
2532 arc->SetLayer( aLayer );
2533
2534 aFootprint->Add( arc, ADD_MODE::APPEND );
2535}
2536
2537
2539 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2540{
2541 if( m_progressReporter )
2542 m_progressReporter->Report( _( "Loading pads..." ) );
2543
2544 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
2545
2546 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2547 {
2548 checkpoint();
2549 APAD6 elem( reader );
2550
2551 if( elem.component == ALTIUM_COMPONENT_NONE )
2552 {
2554 }
2555 else
2556 {
2557 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
2558 ConvertPads6ToFootprintItem( footprint, elem );
2559 }
2560 }
2561
2562 if( reader.GetRemainingBytes() != 0 )
2563 THROW_IO_ERROR( wxT( "Pads6 stream is not fully parsed" ) );
2564}
2565
2566
2568{
2569 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
2570 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
2571 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
2572 {
2574 }
2575 else
2576 {
2577 // We cannot add a pad directly into the PCB
2578 FOOTPRINT* footprint = new FOOTPRINT( m_board );
2579 footprint->SetPosition( aElem.position );
2580
2581 ConvertPads6ToFootprintItemOnCopper( footprint, aElem );
2582
2583 m_board->Add( footprint, ADD_MODE::APPEND );
2584 }
2585}
2586
2587
2589{
2590 // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
2591 if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
2592 && aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
2593 {
2594 ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
2595 }
2596 else
2597 {
2598 ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
2599 }
2600}
2601
2602
2604{
2605 PAD* pad = new PAD( aFootprint );
2606
2607 pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
2608
2609 pad->SetNumber( aElem.name );
2610 pad->SetNetCode( GetNetCode( aElem.net ) );
2611
2612 pad->SetPosition( aElem.position );
2613 pad->SetOrientationDegrees( aElem.direction );
2614 pad->SetSize( aElem.topsize );
2615
2616 if( aElem.holesize == 0 )
2617 {
2618 pad->SetAttribute( PAD_ATTRIB::SMD );
2619 }
2620 else
2621 {
2622 if( aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
2623 {
2624 // TODO: I assume other values are possible as well?
2625 wxLogError( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
2626 aFootprint->GetReference(),
2627 aElem.name );
2628 }
2629
2630 pad->SetAttribute( aElem.plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
2631
2632 if( !aElem.sizeAndShape || aElem.sizeAndShape->holeshape == ALTIUM_PAD_HOLE_SHAPE::ROUND )
2633 {
2634 pad->SetDrillShape( PAD_DRILL_SHAPE_T::PAD_DRILL_SHAPE_CIRCLE );
2635 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) );
2636 }
2637 else
2638 {
2639 switch( aElem.sizeAndShape->holeshape )
2640 {
2641 case ALTIUM_PAD_HOLE_SHAPE::ROUND:
2642 wxFAIL_MSG( wxT( "Round holes are handled before the switch" ) );
2643 break;
2644
2645 case ALTIUM_PAD_HOLE_SHAPE::SQUARE:
2646 wxLogWarning( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
2647 aFootprint->GetReference(),
2648 aElem.name );
2649
2650 pad->SetDrillShape( PAD_DRILL_SHAPE_T::PAD_DRILL_SHAPE_CIRCLE );
2651 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
2652 // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in
2653 // this case or rect holes have a different id
2654 break;
2655
2656 case ALTIUM_PAD_HOLE_SHAPE::SLOT:
2657 {
2658 pad->SetDrillShape( PAD_DRILL_SHAPE_T::PAD_DRILL_SHAPE_OBLONG );
2659 EDA_ANGLE slotRotation( aElem.sizeAndShape->slotrotation, DEGREES_T );
2660
2661 slotRotation.Normalize();
2662
2663 if( slotRotation.IsHorizontal() )
2664 {
2665 pad->SetDrillSize( VECTOR2I( aElem.sizeAndShape->slotsize, aElem.holesize ) );
2666 }
2667 else if( slotRotation.IsVertical() )
2668 {
2669 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.sizeAndShape->slotsize ) );
2670 }
2671 else
2672 {
2673 wxLogWarning( _( "Footprint %s pad %s has a hole-rotation of %f degrees. "
2674 "KiCad only supports 90 degree rotations." ),
2675 aFootprint->GetReference(),
2676 aElem.name,
2677 slotRotation.AsDegrees() );
2678 }
2679
2680 break;
2681 }
2682
2683 default:
2684 case ALTIUM_PAD_HOLE_SHAPE::UNKNOWN:
2685 wxLogError( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
2686 aFootprint->GetReference(),
2687 aElem.name,
2688 aElem.sizeAndShape->holeshape );
2689
2690 pad->SetDrillShape( PAD_DRILL_SHAPE_T::PAD_DRILL_SHAPE_CIRCLE );
2691 pad->SetDrillSize( VECTOR2I( aElem.holesize, aElem.holesize ) ); // Workaround
2692 break;
2693 }
2694 }
2695
2696 if( aElem.sizeAndShape )
2697 pad->SetOffset( aElem.sizeAndShape->holeoffset[0] );
2698 }
2699
2700 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
2701 {
2702 wxLogError( _( "Footprint %s pad %s uses a complex pad stack (not yet supported.)" ),
2703 aFootprint->GetReference(), aElem.name );
2704 }
2705
2706 switch( aElem.topshape )
2707 {
2708 case ALTIUM_PAD_SHAPE::RECT:
2709 pad->SetShape( PAD_SHAPE::RECTANGLE );
2710 break;
2711
2712 case ALTIUM_PAD_SHAPE::CIRCLE:
2713 if( aElem.sizeAndShape
2714 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
2715 {
2716 pad->SetShape( PAD_SHAPE::ROUNDRECT ); // 100 = round, 0 = rectangular
2717 double ratio = aElem.sizeAndShape->cornerradius[0] / 200.;
2718 pad->SetRoundRectRadiusRatio( ratio );
2719 }
2720 else if( aElem.topsize.x == aElem.topsize.y )
2721 {
2722 pad->SetShape( PAD_SHAPE::CIRCLE );
2723 }
2724 else
2725 {
2726 pad->SetShape( PAD_SHAPE::OVAL );
2727 }
2728
2729 break;
2730
2731 case ALTIUM_PAD_SHAPE::OCTAGONAL:
2732 pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
2733 pad->SetChamferPositions( RECT_CHAMFER_ALL );
2734 pad->SetChamferRectRatio( 0.25 );
2735 break;
2736
2737 case ALTIUM_PAD_SHAPE::UNKNOWN:
2738 default:
2739 wxLogError( _( "Footprint %s pad %s uses an unknown pad-shape." ),
2740 aFootprint->GetReference(), aElem.name );
2741 break;
2742 }
2743
2744 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
2745 {
2746 // KiCad likes NPTH pads to be the same size & shape as their holes
2747 pad->SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ? PAD_SHAPE::CIRCLE
2748 : PAD_SHAPE::OVAL );
2749 pad->SetSize( pad->GetDrillSize() );
2750 }
2751
2752 switch( aElem.layer )
2753 {
2754 case ALTIUM_LAYER::TOP_LAYER:
2755 pad->SetLayer( F_Cu );
2756 pad->SetLayerSet( PAD::SMDMask() );
2757 break;
2758
2759 case ALTIUM_LAYER::BOTTOM_LAYER:
2760 pad->SetLayer( B_Cu );
2761 pad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
2762 break;
2763
2764 case ALTIUM_LAYER::MULTI_LAYER:
2765 pad->SetLayerSet( aElem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
2766 break;
2767
2768 default:
2769 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2770 pad->SetLayer( klayer );
2771 pad->SetLayerSet( LSET( 1, klayer ) );
2772 break;
2773 }
2774
2775 if( aElem.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
2776 pad->SetLocalSolderPasteMargin( aElem.pastemaskexpansionmanual );
2777
2778 if( aElem.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
2779 pad->SetLocalSolderMaskMargin( aElem.soldermaskexpansionmanual );
2780
2781 if( aElem.is_tent_top )
2782 pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
2783
2784 if( aElem.is_tent_bottom )
2785 pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
2786
2787 aFootprint->Add( pad, ADD_MODE::APPEND );
2788}
2789
2790
2792{
2793 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2794
2795 if( klayer == UNDEFINED_LAYER )
2796 {
2797 wxLogWarning( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad "
2798 "equivalent. It has been moved to KiCad layer Eco1_User." ),
2799 aElem.name, aElem.layer );
2800 klayer = Eco1_User;
2801 }
2802
2803 PCB_SHAPE* pad = new PCB_SHAPE( m_board );
2804
2805 HelperParsePad6NonCopper( aElem, klayer, pad );
2806
2807 m_board->Add( pad, ADD_MODE::APPEND );
2808}
2809
2810
2812{
2813 PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2814
2815 if( klayer == UNDEFINED_LAYER )
2816 {
2817 wxLogWarning(
2818 _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad equivalent. "
2819 "It has been moved to KiCad layer Eco1_User." ),
2820 aElem.name, aElem.layer );
2821 klayer = Eco1_User;
2822 }
2823
2824 PCB_SHAPE* pad = new PCB_SHAPE( aFootprint );
2825
2826 HelperParsePad6NonCopper( aElem, klayer, pad );
2827
2828 aFootprint->Add( pad, ADD_MODE::APPEND );
2829}
2830
2831
2833 PCB_SHAPE* aShape )
2834{
2835 if( aElem.net != ALTIUM_NET_UNCONNECTED )
2836 {
2837 wxLogError( _( "Non-copper pad %s is connected to a net, which is not supported." ),
2838 aElem.name );
2839 }
2840
2841 if( aElem.holesize != 0 )
2842 {
2843 wxLogError( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
2844 }
2845
2846 if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
2847 {
2848 wxLogWarning( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
2849 aElem.name );
2850 }
2851
2852 switch( aElem.topshape )
2853 {
2854 case ALTIUM_PAD_SHAPE::RECT:
2855 {
2856 // filled rect
2857 aShape->SetShape( SHAPE_T::POLY );
2858 aShape->SetFilled( true );
2859 aShape->SetLayer( aLayer );
2860 aShape->SetStroke( STROKE_PARAMS( 0 ) );
2861
2862 aShape->SetPolyPoints(
2863 { aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
2864 aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
2865 aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
2866 aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
2867
2868 if( aElem.direction != 0 )
2869 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2870 }
2871 break;
2872
2873 case ALTIUM_PAD_SHAPE::CIRCLE:
2874 if( aElem.sizeAndShape
2875 && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
2876 {
2877 // filled roundrect
2878 int cornerradius = aElem.sizeAndShape->cornerradius[0];
2879 int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
2880
2881 aShape->SetLayer( aLayer );
2882 aShape->SetStroke( STROKE_PARAMS( offset * 2, PLOT_DASH_TYPE::SOLID ) );
2883
2884 if( cornerradius < 100 )
2885 {
2886 int offsetX = aElem.topsize.x / 2 - offset;
2887 int offsetY = aElem.topsize.y / 2 - offset;
2888
2889 VECTOR2I p11 = aElem.position + VECTOR2I( offsetX, offsetY );
2890 VECTOR2I p12 = aElem.position + VECTOR2I( offsetX, -offsetY );
2891 VECTOR2I p22 = aElem.position + VECTOR2I( -offsetX, -offsetY );
2892 VECTOR2I p21 = aElem.position + VECTOR2I( -offsetX, offsetY );
2893
2894 aShape->SetShape( SHAPE_T::POLY );
2895 aShape->SetFilled( true );
2896 aShape->SetPolyPoints( { p11, p12, p22, p21 } );
2897 }
2898 else if( aElem.topsize.x == aElem.topsize.y )
2899 {
2900 // circle
2901 aShape->SetShape( SHAPE_T::CIRCLE );
2902 aShape->SetFilled( true );
2903 aShape->SetStart( aElem.position );
2904 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
2905 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, PLOT_DASH_TYPE::SOLID ) );
2906 }
2907 else if( aElem.topsize.x < aElem.topsize.y )
2908 {
2909 // short vertical line
2910 aShape->SetShape( SHAPE_T::SEGMENT );
2911 VECTOR2I pointOffset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
2912 aShape->SetStart( aElem.position + pointOffset );
2913 aShape->SetEnd( aElem.position - pointOffset );
2914 }
2915 else
2916 {
2917 // short horizontal line
2918 aShape->SetShape( SHAPE_T::SEGMENT );
2919 VECTOR2I pointOffset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
2920 aShape->SetStart( aElem.position + pointOffset );
2921 aShape->SetEnd( aElem.position - pointOffset );
2922 }
2923
2924 if( aElem.direction != 0 )
2925 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2926 }
2927 else if( aElem.topsize.x == aElem.topsize.y )
2928 {
2929 // filled circle
2930 aShape->SetShape( SHAPE_T::CIRCLE );
2931 aShape->SetFilled( true );
2932 aShape->SetLayer( aLayer );
2933 aShape->SetStart( aElem.position );
2934 aShape->SetEnd( aElem.position - VECTOR2I( 0, aElem.topsize.x / 4 ) );
2935 aShape->SetStroke( STROKE_PARAMS( aElem.topsize.x / 2, PLOT_DASH_TYPE::SOLID ) );
2936 }
2937 else
2938 {
2939 // short line
2940 aShape->SetShape( SHAPE_T::SEGMENT );
2941 aShape->SetLayer( aLayer );
2942 aShape->SetStroke( STROKE_PARAMS( std::min( aElem.topsize.x, aElem.topsize.y ),
2943 PLOT_DASH_TYPE::SOLID ) );
2944
2945 if( aElem.topsize.x < aElem.topsize.y )
2946 {
2947 VECTOR2I offset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
2948 aShape->SetStart( aElem.position + offset );
2949 aShape->SetEnd( aElem.position - offset );
2950 }
2951 else
2952 {
2953 VECTOR2I offset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
2954 aShape->SetStart( aElem.position + offset );
2955 aShape->SetEnd( aElem.position - offset );
2956 }
2957
2958 if( aElem.direction != 0 )
2959 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2960 }
2961 break;
2962
2963 case ALTIUM_PAD_SHAPE::OCTAGONAL:
2964 {
2965 // filled octagon
2966 aShape->SetShape( SHAPE_T::POLY );
2967 aShape->SetFilled( true );
2968 aShape->SetLayer( aLayer );
2969 aShape->SetStroke( STROKE_PARAMS( 0 ) );
2970
2971 VECTOR2I p11 = aElem.position + VECTOR2I( aElem.topsize.x / 2, aElem.topsize.y / 2 );
2972 VECTOR2I p12 = aElem.position + VECTOR2I( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2973 VECTOR2I p22 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2974 VECTOR2I p21 = aElem.position + VECTOR2I( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
2975
2976 int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
2977 VECTOR2I chamferX( chamfer, 0 );
2978 VECTOR2I chamferY( 0, chamfer );
2979
2980 aShape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
2981 p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
2982
2983 if( aElem.direction != 0. )
2984 aShape->Rotate( aElem.position, EDA_ANGLE( aElem.direction, DEGREES_T ) );
2985 }
2986 break;
2987
2988 case ALTIUM_PAD_SHAPE::UNKNOWN:
2989 default:
2990 wxLogError( _( "Non-copper pad %s uses an unknown pad-shape." ), aElem.name );
2991 break;
2992 }
2993}
2994
2995
2997 const CFB::COMPOUND_FILE_ENTRY* aEntry )
2998{
2999 if( m_progressReporter )
3000 m_progressReporter->Report( _( "Loading vias..." ) );
3001
3002 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3003
3004 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3005 {
3006 checkpoint();
3007 AVIA6 elem( reader );
3008
3009 PCB_VIA* via = new PCB_VIA( m_board );
3010 m_board->Add( via, ADD_MODE::APPEND );
3011
3012 via->SetPosition( elem.position );
3013 via->SetWidth( elem.diameter );
3014 via->SetDrill( elem.holesize );
3015 via->SetNetCode( GetNetCode( elem.net ) );
3016 via->SetLocked( elem.is_locked );
3017
3018 bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
3019 || elem.layer_start == ALTIUM_LAYER::BOTTOM_LAYER;
3020 bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
3021 || elem.layer_end == ALTIUM_LAYER::BOTTOM_LAYER;
3022
3023 if( start_layer_outside && end_layer_outside )
3024 {
3025 via->SetViaType( VIATYPE::THROUGH );
3026 }
3027 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
3028 {
3029 via->SetViaType( VIATYPE::BLIND_BURIED );
3030 }
3031 else
3032 {
3033 via->SetViaType( VIATYPE::MICROVIA ); // TODO: always a microvia?
3034 }
3035
3036 PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
3037 PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
3038
3039 if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
3040 {
3041 wxLogError( _( "Via from layer %d to %d uses a non-copper layer, which is not "
3042 "supported." ),
3043 elem.layer_start,
3044 elem.layer_end );
3045 continue; // just assume through-hole instead.
3046 }
3047
3048 // we need VIATYPE set!
3049 via->SetLayerPair( start_klayer, end_klayer );
3050 }
3051
3052 if( reader.GetRemainingBytes() != 0 )
3053 THROW_IO_ERROR( wxT( "Vias6 stream is not fully parsed" ) );
3054}
3055
3057 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3058{
3059 if( m_progressReporter )
3060 m_progressReporter->Report( _( "Loading tracks..." ) );
3061
3062 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3063
3064 for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
3065 {
3066 checkpoint();
3067 ATRACK6 elem( reader );
3068
3069 if( elem.component == ALTIUM_COMPONENT_NONE )
3070 {
3071 ConvertTracks6ToBoardItem( elem, primitiveIndex );
3072 }
3073 else
3074 {
3075 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3076 ConvertTracks6ToFootprintItem( footprint, elem, primitiveIndex, true );
3077 }
3078 }
3079
3080 if( reader.GetRemainingBytes() != 0 )
3081 THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
3082}
3083
3084
3085void ALTIUM_PCB::ConvertTracks6ToBoardItem( const ATRACK6& aElem, const int aPrimitiveIndex )
3086{
3087 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
3088 return;
3089
3090 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3091 || IsAltiumLayerAPlane( aElem.layer ) )
3092 {
3093 // This is not the actual board item. We can use it to create the polygon for the region
3094 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3095 shape.SetStart( aElem.start );
3096 shape.SetEnd( aElem.end );
3097 shape.SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
3098
3100 }
3101 else
3102 {
3103 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3104 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
3105 }
3106
3107 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
3108 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
3109 {
3110 int width = aElem.width + ( layerExpansionMask.second * 2 );
3111 if( width > 1 )
3112 {
3113 PCB_SHAPE* seg = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
3114
3115 seg->SetStart( aElem.start );
3116 seg->SetEnd( aElem.end );
3117 seg->SetStroke( STROKE_PARAMS( width, PLOT_DASH_TYPE::SOLID ) );
3118 seg->SetLayer( layerExpansionMask.first );
3119
3120 m_board->Add( seg, ADD_MODE::APPEND );
3121 }
3122 }
3123}
3124
3125
3127 const int aPrimitiveIndex,
3128 const bool aIsBoardImport )
3129{
3130 if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
3131 return;
3132
3133 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER
3134 || IsAltiumLayerAPlane( aElem.layer ) )
3135 {
3136 // This is not the actual board item. We can use it to create the polygon for the region
3137 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3138 shape.SetStart( aElem.start );
3139 shape.SetEnd( aElem.end );
3140 shape.SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
3141
3142 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3143 aElem.keepoutrestrictions );
3144 }
3145 else
3146 {
3147 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3148 {
3149 if( aIsBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3150 {
3151 // Special case: do to not lose net connections in footprints
3152 ConvertTracks6ToBoardItemOnLayer( aElem, klayer );
3153 }
3154 else
3155 {
3156 ConvertTracks6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3157 }
3158 }
3159 }
3160
3161 for( const auto& layerExpansionMask : HelperGetSolderAndPasteMaskExpansions(
3162 ALTIUM_RECORD::TRACK, aPrimitiveIndex, aElem.layer ) )
3163 {
3164 int width = aElem.width + ( layerExpansionMask.second * 2 );
3165 if( width > 1 )
3166 {
3167 PCB_SHAPE* seg = new PCB_SHAPE( aFootprint, SHAPE_T::SEGMENT );
3168
3169 seg->SetStart( aElem.start );
3170 seg->SetEnd( aElem.end );
3171 seg->SetStroke( STROKE_PARAMS( width, PLOT_DASH_TYPE::SOLID ) );
3172 seg->SetLayer( layerExpansionMask.first );
3173
3174 aFootprint->Add( seg, ADD_MODE::APPEND );
3175 }
3176 }
3177}
3178
3179
3181{
3182 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3183 {
3184 PCB_TRACK* track = new PCB_TRACK( m_board );
3185
3186 track->SetStart( aElem.start );
3187 track->SetEnd( aElem.end );
3188 track->SetWidth( aElem.width );
3189 track->SetLayer( aLayer );
3190 track->SetNetCode( GetNetCode( aElem.net ) );
3191
3192 m_board->Add( track, ADD_MODE::APPEND );
3193 }
3194 else
3195 {
3196 PCB_SHAPE* seg = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
3197
3198 seg->SetStart( aElem.start );
3199 seg->SetEnd( aElem.end );
3200 seg->SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
3201 seg->SetLayer( aLayer );
3202
3203 m_board->Add( seg, ADD_MODE::APPEND );
3204 }
3205}
3206
3207
3209 PCB_LAYER_ID aLayer )
3210{
3211 PCB_SHAPE* seg = new PCB_SHAPE( aFootprint, SHAPE_T::SEGMENT );
3212
3213 seg->SetStart( aElem.start );
3214 seg->SetEnd( aElem.end );
3215 seg->SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
3216 seg->SetLayer( aLayer );
3217
3218 aFootprint->Add( seg, ADD_MODE::APPEND );
3219}
3220
3221
3223 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3224{
3225 if( m_progressReporter )
3226 m_progressReporter->Report( _( "Loading unicode strings..." ) );
3227
3228 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3229
3231
3232 if( reader.GetRemainingBytes() != 0 )
3233 THROW_IO_ERROR( wxT( "WideStrings6 stream is not fully parsed" ) );
3234}
3235
3237 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3238{
3239 if( m_progressReporter )
3240 m_progressReporter->Report( _( "Loading text..." ) );
3241
3242 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3243
3244 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3245 {
3246 checkpoint();
3247 ATEXT6 elem( reader, m_unicodeStrings );
3248
3249 if( elem.component == ALTIUM_COMPONENT_NONE )
3250 {
3252 }
3253 else
3254 {
3255 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3256 ConvertTexts6ToFootprintItem( footprint, elem );
3257 }
3258 }
3259
3260 if( reader.GetRemainingBytes() != 0 )
3261 THROW_IO_ERROR( wxT( "Texts6 stream is not fully parsed" ) );
3262}
3263
3264
3266{
3267 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3268 {
3269 wxLogError( _( "Ignored barcode on Altium layer %d (not yet supported)." ), aElem.layer );
3270 return;
3271 }
3272
3273 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3274 ConvertTexts6ToBoardItemOnLayer( aElem, klayer );
3275}
3276
3277
3279{
3280 if( aElem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
3281 {
3282 wxLogError( _( "Ignored barcode on Altium layer %d (not yet supported)." ), aElem.layer );
3283 return;
3284 }
3285
3286 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3287 ConvertTexts6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3288}
3289
3290
3292{
3293 PCB_TEXT* pcbText = new PCB_TEXT( m_board );
3294
3295 // TODO: improve parsing of variables
3296 wxString trimmedText = aElem.text;
3297 trimmedText.Trim();
3298
3299 if( trimmedText.CmpNoCase( wxT( ".Layer_Name" ) ) == 0 )
3300 pcbText->SetText( wxT( "${LAYER}" ) );
3301 else
3302 pcbText->SetText( aElem.text );
3303
3304 pcbText->SetLayer( aLayer );
3305 pcbText->SetPosition( aElem.position );
3306 pcbText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3307
3308 ConvertTexts6ToEdaTextSettings( aElem, pcbText );
3309
3310 m_board->Add( pcbText, ADD_MODE::APPEND );
3311}
3312
3313
3315 PCB_LAYER_ID aLayer )
3316{
3317 PCB_TEXT* fpText;
3318
3319 if( aElem.isDesignator )
3320 {
3321 fpText = &aFootprint->Reference(); // TODO: handle multiple layers
3322 }
3323 else if( aElem.isComment )
3324 {
3325 fpText = &aFootprint->Value(); // TODO: handle multiple layers
3326 }
3327 else
3328 {
3329 fpText = new PCB_TEXT( aFootprint );
3330 aFootprint->Add( fpText, ADD_MODE::APPEND );
3331 }
3332
3333 // TODO: improve parsing of variables
3334 wxString trimmedText = aElem.text;
3335 trimmedText.Trim();
3336
3337 if( !aElem.isDesignator && trimmedText.CmpNoCase( wxT( ".Designator" ) ) == 0 )
3338 fpText->SetText( wxT( "${REFERENCE}" ) );
3339 else if( !aElem.isComment && trimmedText.CmpNoCase( wxT( ".Comment" ) ) == 0 )
3340 fpText->SetText( wxT( "${VALUE}" ) );
3341 else if( trimmedText.CmpNoCase( wxT( ".Layer_Name" ) ) == 0 )
3342 fpText->SetText( wxT( "${LAYER}" ) );
3343 else
3344 fpText->SetText( aElem.text );
3345
3346 fpText->SetKeepUpright( false );
3347 fpText->SetLayer( aLayer );
3348 fpText->SetPosition( aElem.position );
3349 fpText->SetTextAngle( EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3350
3351 ConvertTexts6ToEdaTextSettings( aElem, fpText );
3352}
3353
3354
3356{
3357 aEdaText->SetTextSize( VECTOR2I( aElem.height, aElem.height ) ); // TODO: parse text width
3358
3359 if( aElem.fonttype == ALTIUM_TEXT_TYPE::TRUETYPE )
3360 {
3361 KIFONT::FONT* font = KIFONT::FONT::GetFont( aElem.fontname, aElem.isBold, aElem.isItalic );
3362 aEdaText->SetFont( font );
3363
3364 if( font->IsOutline() )
3365 {
3366 // TODO: why is this required? Somehow, truetype size is calculated differently
3367 if( font->GetName().Contains( wxS( "Arial" ) ) )
3368 aEdaText->SetTextSize( VECTOR2I( aElem.height * 0.63, aElem.height * 0.63 ) );
3369 else
3370 aEdaText->SetTextSize( VECTOR2I( aElem.height * 0.5, aElem.height * 0.5 ) );
3371 }
3372 }
3373
3374 aEdaText->SetTextThickness( aElem.strokewidth );
3375 aEdaText->SetBold( aElem.isBold );
3376 aEdaText->SetItalic( aElem.isItalic );
3377 aEdaText->SetMirrored( aElem.isMirrored );
3378
3379 // Altium position always specifies the bottom left corner
3382
3383 // TODO: correct the position and set proper justification
3384}
3385
3386
3388 const CFB::COMPOUND_FILE_ENTRY* aEntry )
3389{
3390 if( m_progressReporter )
3391 m_progressReporter->Report( _( "Loading rectangles..." ) );
3392
3393 ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
3394
3395 while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
3396 {
3397 checkpoint();
3398 AFILL6 elem( reader );
3399
3400 if( elem.component == ALTIUM_COMPONENT_NONE )
3401 {
3403 }
3404 else
3405 {
3406 FOOTPRINT* footprint = HelperGetFootprint( elem.component );
3407 ConvertFills6ToFootprintItem( footprint, elem, true );
3408 }
3409 }
3410
3411 if( reader.GetRemainingBytes() != 0 )
3412 THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
3413}
3414
3415
3417{
3418 if( aElem.is_keepout || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER )
3419 {
3420 // This is not the actual board item. We can use it to create the polygon for the region
3421 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
3422
3423 shape.SetStart( aElem.pos1 );
3424 shape.SetEnd( aElem.pos2 );
3425 shape.SetFilled( true );
3426 shape.SetStroke( STROKE_PARAMS( 0, PLOT_DASH_TYPE::SOLID ) );
3427
3428 if( aElem.rotation != 0. )
3429 {
3430 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2,
3431 ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3432 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3433 }
3434
3436 }
3437 else
3438 {
3439 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3440 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
3441 }
3442}
3443
3444
3446 const bool aIsBoardImport )
3447{
3448 if( aElem.is_keepout
3449 || aElem.layer == ALTIUM_LAYER::KEEP_OUT_LAYER ) // TODO: what about plane layers?
3450 {
3451 // This is not the actual board item. We can use it to create the polygon for the region
3452 PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
3453
3454 shape.SetStart( aElem.pos1 );
3455 shape.SetEnd( aElem.pos2 );
3456 shape.SetFilled( true );
3457 shape.SetStroke( STROKE_PARAMS( 0, PLOT_DASH_TYPE::SOLID ) );
3458
3459 if( aElem.rotation != 0. )
3460 {
3461 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2,
3462 ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3463 shape.Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3464 }
3465
3466 HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer,
3467 aElem.keepoutrestrictions );
3468 }
3469 else if( aIsBoardImport && IsAltiumLayerCopper( aElem.layer )
3470 && aElem.net != ALTIUM_NET_UNCONNECTED )
3471 {
3472 // Special case: do to not lose net connections in footprints
3473 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3474 ConvertFills6ToBoardItemOnLayer( aElem, klayer );
3475 }
3476 else
3477 {
3478 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
3479 ConvertFills6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
3480 }
3481}
3482
3483
3485{
3486 PCB_SHAPE* fill = new PCB_SHAPE( m_board, SHAPE_T::RECTANGLE );
3487
3488 fill->SetFilled( true );
3489 fill->SetLayer( aLayer );
3490 fill->SetStroke( STROKE_PARAMS( 0 ) );
3491
3492 fill->SetStart( aElem.pos1 );
3493 fill->SetEnd( aElem.pos2 );
3494
3495 if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
3496 {
3497 fill->SetNetCode( GetNetCode( aElem.net ) );
3498 }
3499
3500 if( aElem.rotation != 0. )
3501 {
3502 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
3503 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2, ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3504 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3505 }
3506
3507 m_board->Add( fill, ADD_MODE::APPEND );
3508}
3509
3510
3512 PCB_LAYER_ID aLayer )
3513{
3514 PCB_SHAPE* fill = new PCB_SHAPE( aFootprint, SHAPE_T::RECTANGLE );
3515
3516 fill->SetFilled( true );
3517 fill->SetLayer( aLayer );
3518 fill->SetStroke( STROKE_PARAMS( 0 ) );
3519
3520 fill->SetStart( aElem.pos1 );
3521 fill->SetEnd( aElem.pos2 );
3522
3523 if( aElem.rotation != 0. )
3524 {
3525 // TODO: Do we need SHAPE_T::POLY for non 90° rotations?
3526 VECTOR2I center( ( aElem.pos1.x + aElem.pos2.x ) / 2, ( aElem.pos1.y + aElem.pos2.y ) / 2 );
3527 fill->Rotate( center, EDA_ANGLE( aElem.rotation, DEGREES_T ) );
3528 }
3529
3530 aFootprint->Add( fill, ADD_MODE::APPEND );
3531}
3532
3533
3534void ALTIUM_PCB::HelperSetZoneLayers( ZONE* aZone, const ALTIUM_LAYER aAltiumLayer )
3535{
3536 LSET layerSet;
3537
3538 for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aAltiumLayer ) )
3539 layerSet.set( klayer );
3540
3541 aZone->SetLayerSet( layerSet );
3542}
3543
3544
3545void ALTIUM_PCB::HelperSetZoneKeepoutRestrictions( ZONE* aZone, const uint8_t aKeepoutRestrictions )
3546{
3547 bool keepoutRestrictionVia = ( aKeepoutRestrictions & 0x01 ) != 0;
3548 bool keepoutRestrictionTrack = ( aKeepoutRestrictions & 0x02 ) != 0;
3549 bool keepoutRestrictionCopper = ( aKeepoutRestrictions & 0x04 ) != 0;
3550 bool keepoutRestrictionSMDPad = ( aKeepoutRestrictions & 0x08 ) != 0;
3551 bool keepoutRestrictionTHPad = ( aKeepoutRestrictions & 0x10 ) != 0;
3552
3553 aZone->SetDoNotAllowVias( keepoutRestrictionVia );
3554 aZone->SetDoNotAllowTracks( keepoutRestrictionTrack );
3555 aZone->SetDoNotAllowCopperPour( keepoutRestrictionCopper );
3556 aZone->SetDoNotAllowPads( keepoutRestrictionSMDPad && keepoutRestrictionTHPad );
3557 aZone->SetDoNotAllowFootprints( false );
3558}
3559
3560
3562 const ALTIUM_LAYER aAltiumLayer,
3563 const uint8_t aKeepoutRestrictions )
3564{
3565 ZONE* zone = new ZONE( m_board );
3566
3567 zone->SetIsRuleArea( true );
3568
3569 HelperSetZoneLayers( zone, aAltiumLayer );
3570 HelperSetZoneKeepoutRestrictions( zone, aKeepoutRestrictions );
3571
3572 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
3573
3574 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
3576
3577 m_board->Add( zone, ADD_MODE::APPEND );
3578}
3579
3580
3582 const PCB_SHAPE& aShape,
3583 const ALTIUM_LAYER aAltiumLayer,
3584 const uint8_t aKeepoutRestrictions )
3585{
3586 ZONE* zone = new ZONE( aFootprint );
3587
3588 zone->SetIsRuleArea( true );
3589
3590 HelperSetZoneLayers( zone, aAltiumLayer );
3591 HelperSetZoneKeepoutRestrictions( zone, aKeepoutRestrictions );
3592
3593 aShape.EDA_SHAPE::TransformShapeToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF, ERROR_INSIDE );
3594
3595 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
3597
3598 // TODO: zone->SetLocalCoord(); missing?
3599 aFootprint->Add( zone, ADD_MODE::APPEND );
3600}
3601
3602
3603std::vector<std::pair<PCB_LAYER_ID, int>> ALTIUM_PCB::HelperGetSolderAndPasteMaskExpansions(
3604 const ALTIUM_RECORD aType, const int aPrimitiveIndex, const ALTIUM_LAYER aAltiumLayer )
3605{
3606 if( m_extendedPrimitiveInformationMaps.count( aType ) == 0 )
3607 return {}; // there is nothing to parse
3608
3609 auto elems =
3610 m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::TRACK].equal_range( aPrimitiveIndex );
3611
3612 if( elems.first == elems.second )
3613 return {}; // there is nothing to parse
3614
3615 std::vector<std::pair<PCB_LAYER_ID, int>> layerExpansionPairs;
3616
3617 for( auto it = elems.first; it != elems.second; ++it )
3618 {
3619 const AEXTENDED_PRIMITIVE_INFORMATION& pInf = it->second;
3620
3621 if( pInf.type == AEXTENDED_PRIMITIVE_INFORMATION_TYPE::MASK )
3622 {
3623 if( pInf.soldermaskexpansionmode == ALTIUM_MODE::MANUAL
3624 || pInf.soldermaskexpansionmode == ALTIUM_MODE::RULE )
3625 {
3626 // TODO: what layers can lead to solder or paste mask usage? E.g. KEEP_OUT_LAYER and other top/bottom layers
3627 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
3628 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3629 {
3630 layerExpansionPairs.emplace_back( F_Mask, pInf.soldermaskexpansionmanual );
3631 }
3632
3633 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
3634 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3635 {
3636 layerExpansionPairs.emplace_back( B_Mask, pInf.soldermaskexpansionmanual );
3637 }
3638 }
3639 if( pInf.pastemaskexpansionmode == ALTIUM_MODE::MANUAL
3640 || pInf.pastemaskexpansionmode == ALTIUM_MODE::RULE )
3641 {
3642 if( aAltiumLayer == ALTIUM_LAYER::TOP_LAYER
3643 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3644 {
3645 layerExpansionPairs.emplace_back( F_Paste, pInf.pastemaskexpansionmanual );
3646 }
3647
3648 if( aAltiumLayer == ALTIUM_LAYER::BOTTOM_LAYER
3649 || aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
3650 {
3651 layerExpansionPairs.emplace_back( B_Paste, pInf.pastemaskexpansionmanual );
3652 }
3653 }
3654 }
3655 }
3656
3657 return layerExpansionPairs;
3658}
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
ALTIUM_RECORD
const int ALTIUM_COMPONENT_NONE
LIB_ID AltiumToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
void HelperShapeLineChainFromAltiumVertices(SHAPE_LINE_CHAIN &aLine, const std::vector< ALTIUM_VERTICE > &aVertices)
Definition: altium_pcb.cpp:86
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:58
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:61
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:68
std::function< void(const ALTIUM_COMPOUND_FILE &, const CFB::COMPOUND_FILE_ENTRY *)> PARSE_FUNCTION_POINTER_fp
Definition: altium_pcb.h:104
ALTIUM_PCB_DIR
Definition: altium_pcb.h:36
constexpr int ARC_HIGH_DEF
Definition: base_units.h:121
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:43
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:44
const CFB::CompoundFileReader & GetCompoundFileReader() const
Definition: altium_parser.h:67
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
wxString ReadWxString()
size_t GetRemainingBytes() const
bool HasParsingError()
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
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 >();})
size_t ReadAndSetSubrecordLength()
void SkipSubrecord()
void ParseFileHeader(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:827
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition: altium_pcb.h:253
void HelperSetZoneLayers(ZONE *aZone, const ALTIUM_LAYER aAltiumLayer)
void ParseShapeBasedRegions6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::vector< PCB_DIM_RADIAL * > m_radialDimensions
Definition: altium_pcb.h:242
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:259
void ConvertTexts6ToFootprintItemOnLayer(FOOTPRINT *aFootprint, const ATEXT6 &aElem, PCB_LAYER_ID aLayer)
std::map< ALTIUM_LAYER, PCB_LAYER_ID > m_layermap
Definition: altium_pcb.h:246
void ConvertShapeBasedRegions6ToBoardItemOnLayer(const AREGION6 &aElem, PCB_LAYER_ID aLayer)
void HelperParseDimensions6Leader(const ADIMENSION6 &aElem)
std::vector< FOOTPRINT * > m_components
Definition: altium_pcb.h:240
const ARULE6 * GetRuleDefault(ALTIUM_RULE_KIND aKind) const
Definition: altium_pcb.cpp:811
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:233
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:865
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:298
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:251
std::vector< int > m_altiumToKicadNetcodes
Definition: altium_pcb.h:245
ALTIUM_PCB(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter)
Definition: altium_pcb.cpp:265
unsigned m_totalCount
for progress reporting
Definition: altium_pcb.h:256
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:247
void ParseClasses6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
unsigned m_doneCount
Definition: altium_pcb.h:254
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
std::map< uint32_t, wxString > m_unicodeStrings
Definition: altium_pcb.h:244
void ConvertTexts6ToBoardItem(const ATEXT6 &aElem)
FOOTPRINT * ParseFootprint(ALTIUM_COMPOUND_FILE &altiumLibFile, const wxString &aFootprintName)
Definition: altium_pcb.cpp:607
void ParseArcs6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void HelperParseDimensions6Center(const ADIMENSION6 &aElem)
void HelperParseDimensions6Radial(const ADIMENSION6 &aElem)
void HelperSetZoneKeepoutRestrictions(ZONE *aZone, const uint8_t aKeepoutRestrictions)
void ParsePads6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
BOARD * m_board
Definition: altium_pcb.h:239
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)
void ParseComponents6Data(const ALTIUM_COMPOUND_FILE &aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY *aEntry)
int GetNetCode(uint16_t aId) const
Definition: altium_pcb.cpp:777
void ConvertPads6ToFootprintItemOnNonCopper(FOOTPRINT *aFootprint, const APAD6 &aElem)
void ConvertTexts6ToFootprintItem(FOOTPRINT *aFootprint, const ATEXT6 &aElem)
unsigned m_lastProgressCount
Definition: altium_pcb.h:255
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)
std::vector< ZONE * > m_polygons
Definition: altium_pcb.h:241
std::map< wxString, wxString > m_models
Definition: altium_pcb.h:243
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:73
PCB_LAYER_ID GetKicadLayer(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:127
void checkpoint()
Definition: altium_pcb.cpp:279
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)
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:249
void ConvertTexts6ToEdaTextSettings(const ATEXT6 &aElem, EDA_TEXT *aEdaText)
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:844
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:795
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
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 SetLocked(bool aLocked)
Definition: board_item.h:278
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:238
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:271
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:820
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:861
const PAGE_INFO & GetPageSettings() const
Definition: board.h:641
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:640
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:517
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: board.cpp:427
TRACKS & Tracks()
Definition: board.h:310
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:355
void SetCopperLayerCount(int aCount)
Definition: board.cpp:596
const wxString & GetFileName() const
Definition: board.h:308
DRAWINGS & Drawings()
Definition: board.h:316
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:547
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:731
const Vec GetCenter() const
Definition: box2.h:196
coord_type GetHeight() const
Definition: box2.h:189
coord_type GetY() const
Definition: box2.h:182
coord_type GetWidth() const
Definition: box2.h:188
bool Contains(const Vec &aPoint) const
Definition: box2.h:142
coord_type GetX() const
Definition: box2.h:181
EDA_ANGLE Normalize()
Definition: eda_angle.h:249
double Sin() const
Definition: eda_angle.h:206
double AsDegrees() const
Definition: eda_angle.h:149
bool IsHorizontal() const
Definition: eda_angle.h:174
bool IsVertical() const
Definition: eda_angle.h:179
double Cos() const
Definition: eda_angle.h:221
void SetModified()
Definition: eda_item.cpp:64
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:658
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:540
void SetFilled(bool aFlag)
Definition: eda_shape.h:96
int GetRadius() const
Definition: eda_shape.cpp:588
SHAPE_T GetShape() const
Definition: eda_shape.h:117
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:264
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:128
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:124
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:116
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:153
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:620
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:669
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1170
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:80
BOX2I GetTextBox(int aLine=-1, bool aInvertY=false) const
Useful in multiline texts to calculate the full text or a line area (for zones filling,...
Definition: eda_text.cpp:546
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:219
void SetTextSize(VECTOR2I aNewSize)
Definition: eda_text.cpp:355
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:398
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:233
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:257
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:226
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:194
void SetBold(bool aBold)
Definition: eda_text.cpp:218
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:265
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:180
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:202
int GetTextThickness() const
Definition: eda_text.h:123
void SetItalic(bool aItalic)
Definition: eda_text.cpp:210
void SetFont(KIFONT::FONT *aFont)
Definition: eda_text.cpp:339
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:249
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:1944
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:231
void SetLocked(bool isLocked) override
Set the #MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:372
EDA_ANGLE GetOrientation() const
Definition: footprint.h:209
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:589
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:221
bool IsFlipped() const
Definition: footprint.h:348
void SetReference(const wxString &aReference)
Definition: footprint.h:559
PCB_FIELD & Reference()
Definition: footprint.h:590
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:705
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:202
const wxString & GetReference() const
Definition: footprint.h:553
VECTOR2I GetPosition() const override
Definition: footprint.h:206
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:130
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false)
Definition: font.cpp:146
const wxString & GetName() const
Definition: font.h:146
virtual bool IsOutline() const
Definition: font.h:138
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:552
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
Handle the data for a net.
Definition: netinfo.h:67
int GetNetCode() const
Definition: netinfo.h:119
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:387
Definition: pad.h:58
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:195
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:216
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:202
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition: page_info.h:152
int GetWidthIU(double aIUScale) const
Gets the page width in IU.
Definition: page_info.h:143
double GetRadius() const
Definition: pcb_track.cpp:1143
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:321
void SetUnitsFormat(const DIM_UNITS_FORMAT aFormat)
void SetUnits(EDA_UNITS aUnits)
void SetPrefix(const wxString &aPrefix)
void SetSuffix(const wxString &aSuffix)
void SetLineThickness(int aWidth)
virtual void SetEnd(const VECTOR2I &aPoint)
void SetPrecision(DIM_PRECISION aPrecision)
virtual void SetStart(const VECTOR2I &aPoint)
void SetKeepTextAligned(bool aKeepAligned)
For better understanding of the points that make a dimension:
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
Mark the center of a circle or arc with a cross shape.
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:72
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:306
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:97
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:83
VECTOR2I GetPosition() const override
Definition: pcb_shape.h:70
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:84
void SetWidth(int aWidth)
Definition: pcb_track.h:106
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:109
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:112
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).
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:114
const VECTOR2I & GetP1() const
Definition: shape_arc.h:113
const VECTOR2I & GetP0() const
Definition: shape_arc.h:112
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
int NextShape(int aPointIndex, bool aForwards=true) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
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.
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
SEG Segment(int aIndex)
Return a copy of the aIndex-th segment in the line chain.
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.
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
void Move(const VECTOR2I &aVector) override
Simple container to manage line stroke parameters.
Definition: stroke_params.h:81
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:350
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 SetHatchThickness(int aThickness)
Definition: zone.h:274
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:253
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:721
void SetPosition(const VECTOR2I &aPos) override
Definition: zone.h:109
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
Definition: zone.cpp:879
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:354
void SetMinThickness(int aMinThickness)
Definition: zone.h:259
void SetHatchOrientation(const EDA_ANGLE &aStep)
Definition: zone.h:280
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:718
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition: zone.h:194
SHAPE_POLY_SET * Outline()
Definition: zone.h:325
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition: zone.h:621
void SetIsRuleArea(bool aEnable)
Definition: zone.h:717
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
int GetMinThickness() const
Definition: zone.h:258
void SetIsFilled(bool isFilled)
Definition: zone.h:250
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: zone.h:180
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition: zone.h:607
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:719
void SetLocalClearance(int aClearance)
Definition: zone.h:160
void SetThermalReliefGap(int aThermalReliefGap)
Definition: zone.h:183
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:278
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:722
void SetAssignedPriority(unsigned aPriority)
Definition: zone.h:114
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:256
void SetHatchGap(int aStep)
Definition: zone.h:277
static int GetDefaultHatchPitch()
Definition: zone.cpp:1055
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE & ANGLE_45
Definition: eda_angle.h:438
@ CTX_NETCLASS
@ ERROR_INSIDE
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
#define MAX_CU_LAYERS
Definition: layer_ids.h:141
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:847
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ In22_Cu
Definition: layer_ids.h:87
@ In11_Cu
Definition: layer_ids.h:76
@ In29_Cu
Definition: layer_ids.h:94
@ In30_Cu
Definition: layer_ids.h:95
@ User_8
Definition: layer_ids.h:131
@ In17_Cu
Definition: layer_ids.h:82
@ Edge_Cuts
Definition: layer_ids.h:114
@ Dwgs_User
Definition: layer_ids.h:110
@ F_Paste
Definition: layer_ids.h:102
@ In9_Cu
Definition: layer_ids.h:74
@ User_6
Definition: layer_ids.h:129
@ User_7
Definition: layer_ids.h:130
@ In19_Cu
Definition: layer_ids.h:84
@ In7_Cu
Definition: layer_ids.h:72
@ In28_Cu
Definition: layer_ids.h:93
@ In26_Cu
Definition: layer_ids.h:91
@ B_Mask
Definition: layer_ids.h:107
@ B_Cu
Definition: layer_ids.h:96
@ User_5
Definition: layer_ids.h:128
@ Eco1_User
Definition: layer_ids.h:112
@ F_Mask
Definition: layer_ids.h:108
@ In21_Cu
Definition: layer_ids.h:86
@ In23_Cu
Definition: layer_ids.h:88
@ B_Paste
Definition: layer_ids.h:101
@ In15_Cu
Definition: layer_ids.h:80
@ In2_Cu
Definition: layer_ids.h:67
@ User_9
Definition: layer_ids.h:132
@ F_Fab
Definition: layer_ids.h:121
@ In10_Cu
Definition: layer_ids.h:75
@ Margin
Definition: layer_ids.h:115
@ F_SilkS
Definition: layer_ids.h:105
@ In4_Cu
Definition: layer_ids.h:69
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ Eco2_User
Definition: layer_ids.h:113
@ In16_Cu
Definition: layer_ids.h:81
@ In24_Cu
Definition: layer_ids.h:89
@ In1_Cu
Definition: layer_ids.h:66
@ User_3
Definition: layer_ids.h:126
@ User_1
Definition: layer_ids.h:124
@ B_SilkS
Definition: layer_ids.h:104
@ In13_Cu
Definition: layer_ids.h:78
@ User_4
Definition: layer_ids.h:127
@ In8_Cu
Definition: layer_ids.h:73
@ In14_Cu
Definition: layer_ids.h:79
@ User_2
Definition: layer_ids.h:125
@ In12_Cu
Definition: layer_ids.h:77
@ In27_Cu
Definition: layer_ids.h:92
@ In6_Cu
Definition: layer_ids.h:71
@ In5_Cu
Definition: layer_ids.h:70
@ In3_Cu
Definition: layer_ids.h:68
@ In20_Cu
Definition: layer_ids.h:85
@ F_Cu
Definition: layer_ids.h:65
@ In18_Cu
Definition: layer_ids.h:83
@ In25_Cu
Definition: layer_ids.h:90
@ B_Fab
Definition: layer_ids.h:120
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint.
Definition: lset.cpp:590
@ PAD_DRILL_SHAPE_CIRCLE
Definition: pad_shapes.h:70
DIM_PRECISION
Definition: pcb_dimension.h:47
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:39
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
VECTOR2I center
uint32_t radius
uint16_t net
bool is_polygonoutline
double endangle
uint16_t subpolyindex
VECTOR2I sheetpos
std::vector< ABOARD6_LAYER_STACKUP > stackup
std::vector< ALTIUM_VERTICE > board_vertices
std::vector< wxString > names
ALTIUM_CLASS_KIND kind
wxString name
wxString sourcefootprintlibrary
ALTIUM_LAYER layer
wxString sourcedesignator
int32_t textprecision
ALTIUM_UNIT textunit
wxString textsuffix
uint32_t textlinewidth
wxString textformat
ALTIUM_LAYER layer
std::vector< VECTOR2I > textPoint
wxString textprefix
uint32_t linewidth
ALTIUM_DIMENSION_KIND kind
uint32_t textheight
std::vector< VECTOR2I > referencePoint
AEXTENDED_PRIMITIVE_INFORMATION_TYPE type
VECTOR2I pos2
ALTIUM_LAYER layer
uint16_t net
VECTOR2I pos1
double rotation
uint8_t keepoutrestrictions
uint16_t component
const VECTOR2I position
wxString id
wxString name
wxString name
int32_t soldermaskexpansionmanual
uint16_t net
ALTIUM_LAYER layer
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
ALTIUM_PAD_SHAPE topshape
ALTIUM_PAD_MODE padmode
ALTIUM_MODE pastemaskexpansionmode
uint32_t holesize
double direction
ALTIUM_MODE soldermaskexpansionmode
wxString name
bool is_tent_bottom
uint16_t component
int32_t pastemaskexpansionmanual
bool is_tent_top
VECTOR2I position
VECTOR2I topsize
std::vector< ALTIUM_VERTICE > vertices
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
int32_t pourindex
int32_t trackwidth
ALTIUM_LAYER layer
int32_t gridsize
uint8_t keepoutrestrictions
ALTIUM_LAYER layer
std::vector< ALTIUM_VERTICE > outline
uint16_t subpolyindex
std::vector< std::vector< ALTIUM_VERTICE > > holes
uint16_t component
uint16_t net
ALTIUM_REGION_KIND kind
ALTIUM_RULE_KIND kind
ALTIUM_CONNECT_STYLE polygonconnectStyle
wxString scope1expr
wxString name
int planeclearanceClearance
int32_t polygonconnectReliefconductorwidth
int pastemaskExpansion
wxString scope2expr
int soldermaskExpansion
int32_t polygonconnectAirgapwidth
uint16_t component
wxString text
double rotation
uint32_t height
wxString fontname
ALTIUM_LAYER layer
VECTOR2I position
ALTIUM_TEXT_TYPE fonttype
bool isDesignator
uint32_t strokewidth
VECTOR2I end
uint32_t width
bool is_polygonoutline
uint16_t net
uint16_t subpolyindex
uint8_t keepoutrestrictions
VECTOR2I start
ALTIUM_LAYER layer
uint16_t component
uint32_t diameter
uint16_t net
VECTOR2I position
bool is_locked
ALTIUM_LAYER layer_start
ALTIUM_LAYER layer_end
uint32_t holesize
constexpr double IUTomm(int iu) const
Definition: base_units.h:87
const double IU_PER_MILS
Definition: base_units.h:78
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
double DEG2RAD(double deg)
Definition: trigo.h:195
double GetLineLength(const VECTOR2I &aPointA, const VECTOR2I &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:188
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:98
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:95
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588