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