KiCad PCB EDA Suite
Loading...
Searching...
No Matches
allegro_builder.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 Quilter
5 * Copyright The 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 3
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/gpl-3.0.html
20 * or you may search the http://www.gnu.org website for the version 3 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 "allegro_builder.h"
26
27#include <cmath>
28#include <limits>
29#include <set>
30#include <tuple>
31#include <unordered_set>
32
34
35#include <wx/log.h>
36
37#include <core/profile.h>
38#include <core/throttle.h>
39
40#include <base_units.h>
45#include <footprint.h>
46#include <netclass.h>
47#include <pad.h>
48#include <pcb_group.h>
49#include <pcb_text.h>
50#include <pcb_shape.h>
51#include <pcb_track.h>
53#include <zone.h>
54#include <zone_utils.h>
55
56
57using namespace ALLEGRO;
58
59
67static const wxChar* const traceAllegroBuilder = wxT( "KICAD_ALLEGRO_BUILDER" );
68static const wxChar* const traceAllegroPerf = wxT( "KICAD_ALLEGRO_PERF" );
69
70
71template <typename BLK_T>
72const BLK_T& BlockDataAs( const BLOCK_BASE& aBlock )
73{
74 return static_cast<const BLOCK<BLK_T>&>( aBlock ).GetData();
75}
76
77
78#define BLK_FIELD( BLK_T, FIELD ) BlockDataAs<BLK_T>( aBlock ).FIELD
79
80
91static uint32_t GetPrimaryNext( const BLOCK_BASE& aBlock )
92{
93 const uint8_t type = aBlock.GetBlockType();
94
95 switch( type )
96 {
97 case 0x01: return BLK_FIELD( BLK_0x01_ARC, m_Next );
98 case 0x03: return BLK_FIELD( BLK_0x03_FIELD, m_Next );
99 case 0x04: return BLK_FIELD( BLK_0x04_NET_ASSIGNMENT, m_Next );
100 case 0x05: return BLK_FIELD( BLK_0x05_TRACK, m_Next );
101 case 0x0E: return BLK_FIELD( BLK_0x0E_RECT, m_Next );
102 case 0x14: return BLK_FIELD( BLK_0x14_GRAPHIC, m_Next );
103 case 0x15:
104 case 0x16:
105 case 0x17: return BLK_FIELD( BLK_0x15_16_17_SEGMENT, m_Next );
106 case 0x1B: return BLK_FIELD( BLK_0x1B_NET, m_Next );
107 case 0x1D: return BLK_FIELD( BLK_0x1D_CONSTRAINT_SET, m_Next );
108 case 0x1E: return BLK_FIELD( BLK_0x1E_SI_MODEL, m_Next );
109 case 0x1F: return BLK_FIELD( BLK_0x1F_PADSTACK_DIM, m_Next );
110 case 0x2B: return BLK_FIELD( BLK_0x2B_FOOTPRINT_DEF, m_Next );
111 case 0x2D: return BLK_FIELD( BLK_0x2D_FOOTPRINT_INST, m_Next );
112 case 0x2E: return BLK_FIELD( BLK_0x2E_CONNECTION, m_Next );
113 case 0x30: return BLK_FIELD( BLK_0x30_STR_WRAPPER, m_Next );
114 case 0x31: return 0; // Doesn't exist
115 case 0x32: return BLK_FIELD( BLK_0x32_PLACED_PAD, m_Next );
116 case 0x24: return BLK_FIELD( BLK_0x24_RECT, m_Next );
117 case 0x28: return BLK_FIELD( BLK_0x28_SHAPE, m_Next );
118 case 0x2C: return BLK_FIELD( BLK_0x2C_TABLE, m_Next );
119 case 0x33: return BLK_FIELD( BLK_0x33_VIA, m_Next );
120 case 0x36: return BLK_FIELD( BLK_0x36_DEF_TABLE, m_Next );
121 case 0x37: return BLK_FIELD( BLK_0x37_PTR_ARRAY, m_Next );
122 default: return 0;
123 }
124}
125
126
130static uint32_t PadGetNextInFootprint( const BLOCK_BASE& aBlock )
131{
132 const uint8_t type = aBlock.GetBlockType();
133
134 if( type != 0x32 )
135 {
137 wxString::Format( "Unexpected next item in 0x32 pad list: block type %#04x, offset %#lx, key %#010x",
138 type, aBlock.GetOffset(), aBlock.GetKey() ) );
139 }
140
141 // When iterating in a footprint use this field, not m_Next.
142 return BLK_FIELD( BLK_0x32_PLACED_PAD, m_NextInFp );
143}
144
145
147{
148public:
149
150 using NEXT_FUNC_T = std::function<uint32_t( const BLOCK_BASE& )>;
151
153 {
154 public:
155 iterator( uint32_t aCurrent, uint32_t aTail, const BRD_DB& aBoard, NEXT_FUNC_T aNextFunc ) :
156 m_current( aCurrent ), m_tail( aTail ), m_board( aBoard ), m_NextFunc( aNextFunc )
157 {
158 m_currBlock = m_board.GetObjectByKey( m_current );
159
160 if( !m_currBlock )
161 m_current = 0;
162 }
163
164 const BLOCK_BASE* operator*() const { return m_currBlock; }
165
167 {
168 if( m_current == m_tail || !m_currBlock )
169 {
170 m_current = 0;
171 }
172 else
173 {
175
176 if( m_current == m_tail || m_board.IsSentinel( m_current ) )
177 {
178 m_current = 0;
179 }
180 else
181 {
182 m_currBlock = m_board.GetObjectByKey( m_current );
183
184 if( m_currBlock == nullptr )
185 {
186 m_current = 0;
187 }
188 }
189 }
190 return *this;
191 }
192
193 bool operator!=( const iterator& other ) const { return m_current != other.m_current; }
194
195 private:
196 uint32_t m_current;
198 uint32_t m_tail;
201 };
202
203 LL_WALKER( uint32_t aHead, uint32_t aTail, const BRD_DB& aBoard ) :
204 m_head( aHead ), m_tail( aTail ), m_board( aBoard )
205 {
206 // The default next function
208 }
209
210 LL_WALKER( const FILE_HEADER::LINKED_LIST& aList, const BRD_DB& aBoard ) :
211 LL_WALKER( aList.m_Head, aList.m_Tail, aBoard )
212 {
213 }
214
216 iterator end() const { return iterator( 0, m_tail, m_board, m_nextFunction ); }
217
218 void SetNextFunc( NEXT_FUNC_T aNextFunc ) { m_nextFunction = aNextFunc; }
219
220private:
221 uint32_t m_head;
222 uint32_t m_tail;
224
225 // This is the function that can get the next item in a list. By default
227};
228
229
230template <>
231struct std::hash<LAYER_INFO>
232{
233 size_t operator()( const LAYER_INFO& aLayerInfo ) const noexcept
234 {
235 return ( aLayerInfo.m_Class << 8 ) + aLayerInfo.m_Subclass;
236 }
237};
238
239
247// clang-format off
248static const std::unordered_map<LAYER_INFO, PCB_LAYER_ID> s_LayerKiMap = {
249
256
259
262
269
274
277};
278
286static const std::unordered_map<LAYER_INFO, wxString> s_OptionalFixedMappings = {
291
293
295
300
305
310
315
317};
318
319// clang-format on
320
321
328static wxString layerInfoDisplayName( const LAYER_INFO& aLayerInfo )
329{
330 // clang-format off
331 static const std::unordered_map<uint8_t, wxString> s_ClassNames = {
332 { LAYER_INFO::CLASS::BOARD_GEOMETRY, wxS( "Board Geometry" ) },
333 { LAYER_INFO::CLASS::COMPONENT_VALUE, wxS( "Component Value" ) },
334 { LAYER_INFO::CLASS::DEVICE_TYPE, wxS( "Device Type" ) },
335 { LAYER_INFO::CLASS::DRAWING_FORMAT, wxS( "Drawing Format" ) },
336 { LAYER_INFO::CLASS::ETCH, wxS( "Etch" ) },
337 { LAYER_INFO::CLASS::MANUFACTURING, wxS( "Manufacturing" ) },
338 { LAYER_INFO::CLASS::PACKAGE_GEOMETRY, wxS( "Package Geometry" ) },
339 { LAYER_INFO::CLASS::PACKAGE_KEEPIN, wxS( "Package Keepin" ) },
340 { LAYER_INFO::CLASS::PACKAGE_KEEPOUT, wxS( "Package Keepout" ) },
341 { LAYER_INFO::CLASS::PIN, wxS( "Pin" ) },
342 { LAYER_INFO::CLASS::REF_DES, wxS( "Ref Des" ) },
343 { LAYER_INFO::CLASS::ROUTE_KEEPIN, wxS( "Route Keepin" ) },
344 { LAYER_INFO::CLASS::ROUTE_KEEPOUT, wxS( "Route Keepout" ) },
345 { LAYER_INFO::CLASS::TOLERANCE, wxS( "Tolerance" ) },
346 { LAYER_INFO::CLASS::USER_PART_NUMBER, wxS( "User Part Number" ) },
347 { LAYER_INFO::CLASS::VIA_CLASS, wxS( "Via Class" ) },
348 { LAYER_INFO::CLASS::VIA_KEEPOUT, wxS( "Via Keepout" ) },
349 { LAYER_INFO::CLASS::ANTI_ETCH, wxS( "Anti Etch" ) },
350 { LAYER_INFO::CLASS::BOUNDARY, wxS( "Boundary" ) },
351 { LAYER_INFO::CLASS::CONSTRAINTS_REGION, wxS( "Constraints Region" ) },
352 };
353
354 static const std::unordered_map<uint8_t, wxString> s_BoardGeomSubclassNames = {
355 { LAYER_INFO::SUBCLASS::BGEOM_OUTLINE, wxS( "Outline" ) },
356 { LAYER_INFO::SUBCLASS::BGEOM_CONSTRAINT_AREA, wxS( "Constraint Area" ) },
357 { LAYER_INFO::SUBCLASS::BGEOM_OFF_GRID_AREA, wxS( "Off Grid Area" ) },
358 { LAYER_INFO::SUBCLASS::BGEOM_SOLDERMASK_BOTTOM, wxS( "Soldermask Bottom" ) },
359 { LAYER_INFO::SUBCLASS::BGEOM_SOLDERMASK_TOP, wxS( "Soldermask Top" ) },
360 { LAYER_INFO::SUBCLASS::BGEOM_ASSEMBLY_DETAIL, wxS( "Assembly Detail" ) },
361 { LAYER_INFO::SUBCLASS::BGEOM_SILKSCREEN_BOTTOM, wxS( "Silkscreen Bottom" ) },
362 { LAYER_INFO::SUBCLASS::BGEOM_SILKSCREEN_TOP, wxS( "Silkscreen Top" ) },
363 { LAYER_INFO::SUBCLASS::BGEOM_SWITCH_AREA_BOTTOM, wxS( "Switch Area Bottom" ) },
364 { LAYER_INFO::SUBCLASS::BGEOM_SWITCH_AREA_TOP, wxS( "Switch Area Top" ) },
365 { LAYER_INFO::SUBCLASS::BGEOM_BOTH_ROOMS, wxS( "Both Rooms" ) },
366 { LAYER_INFO::SUBCLASS::BGEOM_BOTTOM_ROOM, wxS( "Bottom Room" ) },
367 { LAYER_INFO::SUBCLASS::BGEOM_TOP_ROOM, wxS( "Top Room" ) },
368 { LAYER_INFO::SUBCLASS::BGEOM_PLACE_GRID_BOTTOM, wxS( "Place Grid Bottom" ) },
369 { LAYER_INFO::SUBCLASS::BGEOM_PLACE_GRID_TOP, wxS( "Place Grid Top" ) },
370 { LAYER_INFO::SUBCLASS::BGEOM_DIMENSION, wxS( "Dimension" ) },
371 { LAYER_INFO::SUBCLASS::BGEOM_TOOLING_CORNERS, wxS( "Tooling Corners" ) },
372 { LAYER_INFO::SUBCLASS::BGEOM_ASSEMBLY_NOTES, wxS( "Assembly Notes" ) },
373 { LAYER_INFO::SUBCLASS::BGEOM_PLATING_BAR, wxS( "Plating Bar" ) },
374 { LAYER_INFO::SUBCLASS::BGEOM_DESIGN_OUTLINE, wxS( "Design Outline" ) },
375
376 };
377
378 static const std::unordered_map<uint8_t, wxString> s_ComponentValueSubclassNames = {
379 { LAYER_INFO::SUBCLASS::DISPLAY_BOTTOM, wxS( "Display Bottom" ) },
380 { LAYER_INFO::SUBCLASS::DISPLAY_TOP, wxS( "Display Top" ) },
381 { LAYER_INFO::SUBCLASS::SILKSCREEN_BOTTOM, wxS( "Silkscreen Bottom" ) },
382 { LAYER_INFO::SUBCLASS::SILKSCREEN_TOP, wxS( "Silkscreen Top" ) },
383 { LAYER_INFO::SUBCLASS::ASSEMBLY_BOTTOM, wxS( "Assembly Bottom" ) },
384 { LAYER_INFO::SUBCLASS::ASSEMBLY_TOP, wxS( "Assembly Top" ) },
385 };
386
387 static const std::unordered_map<uint8_t, wxString> s_DrawingFormatSubclassNames = {
388 { LAYER_INFO::SUBCLASS::DFMT_REVISION_DATA, wxS( "Revision Data" ) },
389 { LAYER_INFO::SUBCLASS::DFMT_REVISION_BLOCK, wxS( "Revision Block" ) },
390 { LAYER_INFO::SUBCLASS::DFMT_TITLE_DATA, wxS( "Title Data" ) },
391 { LAYER_INFO::SUBCLASS::DFMT_TITLE_BLOCK, wxS( "Title Block" ) },
392 { LAYER_INFO::SUBCLASS::DFMT_OUTLINE, wxS( "Outline" ) },
393 };
394
395 static const std::unordered_map<uint8_t, wxString> s_PackageGeometrySubclassNames = {
396 { LAYER_INFO::SUBCLASS::DFA_BOUND_BOTTOM, wxS( "DFA Bound Bottom" ) },
397 { LAYER_INFO::SUBCLASS::DFA_BOUND_TOP, wxS( "DFA Bound Top" ) },
398 { LAYER_INFO::SUBCLASS::PGEOM_DISPLAY_BOTTOM, wxS( "Display Bottom" ) },
399 { LAYER_INFO::SUBCLASS::PGEOM_DISPLAY_TOP, wxS( "Display Top" ) },
400 { LAYER_INFO::SUBCLASS::PGEOM_SOLDERMASK_BOTTOM, wxS( "Soldermask Bottom" ) },
401 { LAYER_INFO::SUBCLASS::PGEOM_SOLDERMASK_TOP, wxS( "Soldermask Top" ) },
402 { LAYER_INFO::SUBCLASS::PGEOM_BODY_CENTER, wxS( "Body Center" ) },
403 { LAYER_INFO::SUBCLASS::PGEOM_SILKSCREEN_BOTTOM, wxS( "Silkscreen Bottom" ) },
404 { LAYER_INFO::SUBCLASS::PGEOM_SILKSCREEN_TOP, wxS( "Silkscreen Top" ) },
405 { LAYER_INFO::SUBCLASS::PGEOM_PAD_STACK_NAME, wxS( "Pad Stack Name" ) },
406 { LAYER_INFO::SUBCLASS::PGEOM_PIN_NUMBER, wxS( "Pin Number" ) },
407 { LAYER_INFO::SUBCLASS::PGEOM_PLACE_BOUND_BOTTOM, wxS( "Place Bound Bottom" ) },
408 { LAYER_INFO::SUBCLASS::PGEOM_PLACE_BOUND_TOP, wxS( "Place Bound Top" ) },
409 { LAYER_INFO::SUBCLASS::PGEOM_ASSEMBLY_BOTTOM, wxS( "Assembly Bottom" ) },
410 { LAYER_INFO::SUBCLASS::PGEOM_ASSEMBLY_TOP, wxS( "Assembly Top" ) },
411 };
412
413 static const std::unordered_map<uint8_t, wxString> s_ManufacturingSubclassNames = {
414 { LAYER_INFO::SUBCLASS::MFR_XSECTION_CHART, wxS( "X-Section Chart" ) },
415 { LAYER_INFO::SUBCLASS::MFR_NO_PROBE_BOTTOM, wxS( "No Probe Bottom" ) },
416 { LAYER_INFO::SUBCLASS::MFR_NO_PROBE_TOP, wxS( "No Probe Top" ) },
417 { LAYER_INFO::SUBCLASS::MFR_AUTOSILK_BOTTOM, wxS( "AutoSilk Bottom" ) },
418 { LAYER_INFO::SUBCLASS::MFR_AUTOSILK_TOP, wxS( "AutoSilk Top" ) },
419 { LAYER_INFO::SUBCLASS::MFR_PROBE_BOTTOM, wxS( "Probe Bottom" ) },
420 { LAYER_INFO::SUBCLASS::MFR_PROBE_TOP, wxS( "Probe Top" ) },
421 { LAYER_INFO::SUBCLASS::MFR_NCDRILL_FIGURE, wxS( "NC Drill Figure" ) },
422 { LAYER_INFO::SUBCLASS::MFR_NCDRILL_LEGEND, wxS( "NC Drill Legend" ) },
423 { LAYER_INFO::SUBCLASS::MFR_NO_GLOSS_INTERNAL, wxS( "No Gloss Internal" ) },
424 { LAYER_INFO::SUBCLASS::MFR_NO_GLOSS_BOTTOM, wxS( "No Gloss Bottom" ) },
425 { LAYER_INFO::SUBCLASS::MFR_NO_GLOSS_TOP, wxS( "No Gloss Top" ) },
426 { LAYER_INFO::SUBCLASS::MFR_NO_GLOSS_ALL, wxS( "No Gloss All" ) },
427 { LAYER_INFO::SUBCLASS::MFR_PHOTOPLOT_OUTLINE, wxS( "Photoplot Outline" ) },
428 };
429
430 static const std::unordered_map<uint8_t, wxString> s_AnalysisSubclassNames = {
431 { LAYER_INFO::SUBCLASS::ANALYSIS_PCB_TEMPERATURE, wxS( "PCB Temperature" ) },
432 { LAYER_INFO::SUBCLASS::ANALYSIS_HIGH_ISOCONTOUR, wxS( "High IsoContour" ) },
433 { LAYER_INFO::SUBCLASS::ANALYSIS_MEDIUM3_ISOCONTOUR, wxS( "Medium3 IsoContour" ) },
434 { LAYER_INFO::SUBCLASS::ANALYSIS_MEDIUM2_ISOCONTOUR, wxS( "Medium2 IsoContour" ) },
435 { LAYER_INFO::SUBCLASS::ANALYSIS_MEDIUM1_ISOCONTOUR, wxS( "Medium1 IsoContour" ) },
436 { LAYER_INFO::SUBCLASS::ANALYSIS_LOW_ISOCONTOUR, wxS( "Low IsoContour" ) },
437 };
438
439 static const std::unordered_map<uint8_t, wxString> s_ConstraintSubclassNames = {
440 { LAYER_INFO::SUBCLASS::CREG_ALL, wxS( "All" ) },
441 };
442
443 static const std::unordered_map<uint8_t, wxString> s_KeepinSubclassNames = {
444 { LAYER_INFO::SUBCLASS::KEEPIN_ALL, wxS( "All" ) },
445 };
446
447 static const std::unordered_map<uint8_t, wxString> s_KeepoutSubclassNames = {
448 { LAYER_INFO::SUBCLASS::KEEPOUT_ALL, wxS( "All" ) },
449 { LAYER_INFO::SUBCLASS::KEEPOUT_TOP, wxS( "Top" ) },
450 { LAYER_INFO::SUBCLASS::KEEPOUT_BOTTOM, wxS( "Bottom" ) },
451 };
452
453 static const std::unordered_map<uint8_t, const std::unordered_map<uint8_t, wxString>&> s_SubclassNameMaps = {
454 { LAYER_INFO::CLASS::BOARD_GEOMETRY, s_BoardGeomSubclassNames },
455
456 // These classes all share the same subclass names
457 { LAYER_INFO::CLASS::COMPONENT_VALUE, s_ComponentValueSubclassNames },
458 { LAYER_INFO::CLASS::DEVICE_TYPE, s_ComponentValueSubclassNames },
459 { LAYER_INFO::CLASS::REF_DES, s_ComponentValueSubclassNames },
460 { LAYER_INFO::CLASS::TOLERANCE, s_ComponentValueSubclassNames },
461 { LAYER_INFO::CLASS::USER_PART_NUMBER, s_ComponentValueSubclassNames },
462
463 { LAYER_INFO::CLASS::DRAWING_FORMAT, s_DrawingFormatSubclassNames },
464 { LAYER_INFO::CLASS::PACKAGE_GEOMETRY, s_PackageGeometrySubclassNames },
465 { LAYER_INFO::CLASS::MANUFACTURING, s_ManufacturingSubclassNames },
466 { LAYER_INFO::CLASS::ANALYSIS, s_AnalysisSubclassNames },
467 { LAYER_INFO::CLASS::CONSTRAINTS_REGION, s_ConstraintSubclassNames },
468 { LAYER_INFO::CLASS::PACKAGE_KEEPIN, s_KeepinSubclassNames },
469 { LAYER_INFO::CLASS::PACKAGE_KEEPOUT, s_KeepoutSubclassNames },
470 { LAYER_INFO::CLASS::ROUTE_KEEPIN, s_KeepinSubclassNames },
471 { LAYER_INFO::CLASS::ROUTE_KEEPOUT, s_KeepoutSubclassNames },
472 { LAYER_INFO::CLASS::VIA_KEEPOUT, s_KeepoutSubclassNames },
473 };
474 // clang-format on
475
476 wxString className;
477 const auto classIt = s_ClassNames.find( aLayerInfo.m_Class );
478
479 if( classIt != s_ClassNames.end() )
480 className = classIt->second;
481 else
482 className = wxString::Format( wxS( "Class_%02X" ), aLayerInfo.m_Class );
483
484 wxString subclassName;
485
486 // Find the right subclass name map for this class
487 auto classMapIt = s_SubclassNameMaps.find( aLayerInfo.m_Class );
488
489 if( classMapIt != s_SubclassNameMaps.end() )
490 {
491 const std::unordered_map<uint8_t, wxString>& subclassMap = classMapIt->second;
492
493 const auto subIt = subclassMap.find( aLayerInfo.m_Subclass );
494
495 if( subIt != subclassMap.end() )
496 subclassName = subIt->second;
497 else
498 {
499 // This subclass seems not to have a known name
500 subclassName = wxString::Format( wxS( "Subclass_%02X" ), aLayerInfo.m_Subclass );
501 }
502 }
503 else
504 {
505 // Don't have a specific map for this class, just do a generic one.
506 subclassName = wxString::Format( wxS( "Subclass_%02X" ), aLayerInfo.m_Subclass );
507 }
508
509 return className + wxS( "/" ) + subclassName;
510}
511
512
520static bool layerIsZone( const LAYER_INFO& aLayerInfo )
521{
522 if ( aLayerInfo.m_Class == LAYER_INFO::CLASS::PACKAGE_KEEPIN ||
527 return true;
528
529 return false;
530}
531
532
536static std::optional<LAYER_INFO> tryLayerFromBlock( const BLOCK_BASE& aBlock )
537{
538 switch( aBlock.GetBlockType() )
539 {
540 case 0x0e:
541 {
542 const auto& net = BlockDataAs<BLK_0x0E_RECT>( aBlock );
543 return net.m_Layer;
544 }
545 case 0x14:
546 {
547 const auto& trace = BlockDataAs<BLK_0x14_GRAPHIC>( aBlock );
548 return trace.m_Layer;
549 }
550 case 0x24:
551 {
552 const auto& rect = BlockDataAs<BLK_0x24_RECT>( aBlock );
553 return rect.m_Layer;
554 }
555 case 0x28:
556 {
557 const auto& shape = BlockDataAs<BLK_0x28_SHAPE>( aBlock );
558 return shape.m_Layer;
559 }
560 }
561
562 return std::nullopt;
563}
564
565
572{
573 std::optional<LAYER_INFO> layerInfo = tryLayerFromBlock( aBlock );
574
575 // Programming error - should only call this function if we're sure the block has layer info
576 wxCHECK( layerInfo.has_value(), LAYER_INFO() );
577
578 return layerInfo.value();
579}
580
581
586{
593 {
594 wxString m_Name;
595 // LAYER_ARTWORK: POSITIVE/NEGATIVE
596 // LAYER_USE: empty, EMBEDDED_PLANE, ...?
597 // bool m_IsConductor;
598 };
599
600public:
601 LAYER_MAPPER( const BRD_DB& aRawBoard, BOARD& aBoard, const LAYER_MAPPING_HANDLER& aLayerMappingHandler ) :
602 m_layerMappingHandler( aLayerMappingHandler ),
603 m_brdDb( aRawBoard ),
604 m_board( aBoard )
605 {}
606
607 void ProcessLayerList( uint8_t aClass, const BLK_0x2A_LAYER_LIST& aList )
608 {
609 // If we haven't seen this list yet, create and store the CUSTOM_LAYER list
610 if( m_Lists.count( &aList ) == 0 )
611 {
612 std::vector<CUSTOM_LAYER>& classLayers = m_Lists[&aList];
613
614 if( aList.m_RefEntries.has_value() )
615 {
616 for( const BLK_0x2A_LAYER_LIST::REF_ENTRY& entry : aList.m_RefEntries.value() )
617 {
618 const wxString& layerName = m_brdDb.GetString( entry.mLayerNameId );
619
620 classLayers.emplace_back( CUSTOM_LAYER( layerName ) );
621 }
622 }
623 else if( aList.m_NonRefEntries.has_value() )
624 {
625 for( const BLK_0x2A_LAYER_LIST::NONREF_ENTRY& entry : aList.m_NonRefEntries.value() )
626 {
627 classLayers.emplace_back( CUSTOM_LAYER( entry.m_Name ) );
628 }
629 }
630 else
631 {
632 // Presumably a parsing error.
633 THROW_IO_ERROR( "No ETCH layer list found." );
634 }
635
636 wxLogTrace( traceAllegroBuilder, "Added %zu layers for class %#04x, from 0x2A key %#010x",
637 classLayers.size(), aClass, aList.m_Key );
638 }
639
640 // Store the class ID -> 0x2A mapping
641 m_ClassCustomLayerLists[aClass] = &m_Lists[&aList];
642 }
643
650 {
651 auto customLayerIt = m_ClassCustomLayerLists.find( LAYER_INFO::CLASS::ETCH );
652
653 if( customLayerIt == m_ClassCustomLayerLists.end() || !customLayerIt->second )
654 {
655 wxLogTrace( traceAllegroBuilder, "No ETCH layer class found; cannot finalize layers" );
656 return;
657 }
658
659 const std::vector<CUSTOM_LAYER>& etchLayers = *customLayerIt->second;
660 const size_t numCuLayers = etchLayers.size();
661
662 m_board.GetDesignSettings().SetCopperLayerCount( numCuLayers );
663
664 std::vector<INPUT_LAYER_DESC> inputLayers;
665
666 for( size_t li = 0; li < numCuLayers; ++li )
667 {
668 INPUT_LAYER_DESC desc;
669 desc.Name = etchLayers[li].m_Name;
670 desc.AutoMapLayer = getNthCopperLayer( li, numCuLayers );
672 desc.Required = true;
673 inputLayers.push_back( desc );
674 }
675
676 // Add non-ETCH custom layers so they appear in the layer mapping dialog
677 const std::vector<CUSTOM_LAYER>* etchList = m_ClassCustomLayerLists[LAYER_INFO::CLASS::ETCH];
678 int nextAutoUser = 0;
679
680 for( const auto& [classId, layerList] : m_ClassCustomLayerLists )
681 {
682 if( classId == LAYER_INFO::CLASS::ETCH || layerList == etchList )
683 continue;
684
685 for( size_t si = 0; si < layerList->size(); ++si )
686 {
687 const LAYER_INFO li{ classId, static_cast<uint8_t>( si ) };
688
689 // Skip entries already covered by s_LayerKiMap
690 if( s_LayerKiMap.count( li ) )
691 continue;
692
693 INPUT_LAYER_DESC desc;
694 desc.Name = layerInfoDisplayName( li );
695
696 if( layerList->at( si ).m_Name.length() > 0 )
697 desc.Name = layerList->at( si ).m_Name;
698
699 desc.AutoMapLayer = getNthUserLayer( nextAutoUser++ );
701 desc.Required = false;
702 inputLayers.push_back( desc );
703
705 }
706 }
707
708 // The layers that maybe lump together multiple Allegro class:subclasses
709 // into a single, named, KiCad layer
710 for( const auto& [layerName, kiLayer] : m_MappedOptionalLayers )
711 {
712 INPUT_LAYER_DESC desc;
713 desc.Name = layerName;
714 desc.AutoMapLayer = kiLayer;
716 desc.Required = false;
717 inputLayers.push_back( desc );
718 }
719
720 for( const auto& [layerInfo, kiLayer] : s_LayerKiMap )
721 {
722 INPUT_LAYER_DESC desc;
723 desc.Name = layerInfoDisplayName( layerInfo );
724 desc.AutoMapLayer = kiLayer;
726 desc.Required = false;
727 inputLayers.push_back( desc );
728 }
729
730 std::map<wxString, PCB_LAYER_ID> resolvedMapping = m_layerMappingHandler( inputLayers );
731
732 // Apply copper layer mapping
733 for( size_t li = 0; li < numCuLayers; ++li )
734 {
735 const LAYER_INFO layerInfo{ LAYER_INFO::CLASS::ETCH, static_cast<uint8_t>( li ) };
736 const wxString& layerName = etchLayers[li].m_Name;
737
738 auto it = resolvedMapping.find( layerName );
739 PCB_LAYER_ID lId = ( it != resolvedMapping.end() ) ? it->second
740 : getNthCopperLayer( li, numCuLayers );
741
742 m_customLayerToKiMap[layerInfo] = lId;
743 m_board.SetLayerName( lId, layerName );
744 }
745
746 // Apply non-copper static layer mapping from the handler result
747 for( const auto& [layerInfo, defaultKiLayer] : s_LayerKiMap )
748 {
749 const wxString displayName = layerInfoDisplayName( layerInfo );
750
751 auto rmIt = resolvedMapping.find( displayName );
752
753 if( rmIt != resolvedMapping.end() && rmIt->second != PCB_LAYER_ID::UNDEFINED_LAYER )
754 {
755 m_staticLayerOverrides[layerInfo] = rmIt->second;
756 }
757 }
758
759 // Apply custom layer mapping from the handler result
760 for( const auto& [layerInfo, dialogName] : m_customLayerDialogNames )
761 {
762 auto rmIt = resolvedMapping.find( dialogName );
763
764 if( rmIt != resolvedMapping.end() && rmIt->second != PCB_LAYER_ID::UNDEFINED_LAYER )
765 {
766 m_customLayerToKiMap[layerInfo] = rmIt->second;
767 m_board.SetLayerName( rmIt->second, dialogName );
768 }
769 }
770
771 // Enable all the layers we ended up mapping to
772 LSET enabledLayersMask = m_board.GetEnabledLayers();
773 int userLayers = 0;
774 for( const auto& [name, layerId] : resolvedMapping )
775 {
776 if( layerId != PCB_LAYER_ID::UNDEFINED_LAYER )
777 enabledLayersMask |= LSET{ layerId };
778
779 if( IsUserLayer( layerId ) )
780 userLayers++;
781
782 wxLogTrace( traceAllegroBuilder, "Mapping Allegro layer '%s' to KiCad layer '%s' (%d)", name,
783 m_board.GetLayerName( layerId ), layerId );
784
785 m_board.SetLayerName( layerId, name );
786 }
787 m_board.SetEnabledLayers( enabledLayersMask );
788 wxLogTrace( traceAllegroBuilder, "After mapping, there are %d user layers", userLayers );
789 m_board.GetDesignSettings().SetUserDefinedLayerCount( userLayers );
790 }
791
792 PCB_LAYER_ID GetLayer( const LAYER_INFO& aLayerInfo )
793 {
794 // We already mapped and created the layer
795 if( m_customLayerToKiMap.count( aLayerInfo ) )
796 return m_customLayerToKiMap.at( aLayerInfo );
797
798 // Check for user-remapped static layers first
799 if( m_staticLayerOverrides.count( aLayerInfo ) )
800 return m_staticLayerOverrides.at( aLayerInfo );
801
802 // Next, have a look and see if the class:subclass was recorded as a custom layer
803 if( m_ClassCustomLayerLists.count( aLayerInfo.m_Class ) )
804 {
805 const std::vector<CUSTOM_LAYER>* cLayerList = m_ClassCustomLayerLists.at( aLayerInfo.m_Class );
806
807 // If it is using the copper layer list and the subclass is within the
808 // copper layer range, return the mapped copper layer. Non-ETCH classes
809 // can share the same layer list pointer, but their subclass values may
810 // exceed the copper layer count and must fall through to the custom
811 // layer mapping below.
812 const auto etchIt = m_ClassCustomLayerLists.find( LAYER_INFO::CLASS::ETCH );
813 if( etchIt != m_ClassCustomLayerLists.end()
814 && cLayerList == etchIt->second
815 && aLayerInfo.m_Subclass < cLayerList->size() )
816 {
817 const PCB_LAYER_ID cuLayer = getNthCopperLayer( aLayerInfo.m_Subclass, cLayerList->size() );
818 // Remember this mapping
819 m_customLayerToKiMap[aLayerInfo] = cuLayer;
820 return cuLayer;
821 }
822
823 if( aLayerInfo.m_Subclass < cLayerList->size() )
824 {
825 // This subclass maps to a custom layer in this class
826 const CUSTOM_LAYER& cLayer = cLayerList->at( aLayerInfo.m_Subclass );
827 return MapCustomLayer( aLayerInfo, cLayer.m_Name );
828 }
829 }
830
831 // Now, there may be layers that map to custom layers in KiCad, but are fixed in Allegro
832 // (perhaps, DFA_BOUND_TOP), which means we won't find them in the layer lists.
833 // We add them if we encounter them, with the names defined.
834 if( s_OptionalFixedMappings.count( aLayerInfo ) )
835 {
836 const wxString& layerName = s_OptionalFixedMappings.at( aLayerInfo );
837 return MapCustomLayer( aLayerInfo, layerName );
838 }
839
840 // Finally, fallback to the static mapping for any layers we haven't got a custom map for
841 // We do this last so that it can be overridden for example if we want to remap
842 // OUTLINE and DESIGN_OUTLINE to different layers.
843 if( s_LayerKiMap.count( aLayerInfo ) )
844 return s_LayerKiMap.at( aLayerInfo );
845
846 // Keep a record of what we failed to map
847 if( m_unknownLayers.count( aLayerInfo ) == 0 )
848 {
849 wxLogTrace( traceAllegroBuilder, "Failed to map class:subclass to layer: %#04x:%#04x", aLayerInfo.m_Class,
850 aLayerInfo.m_Subclass );
851 m_unknownLayers[aLayerInfo] = 1;
852 }
853 m_unknownLayers[aLayerInfo]++;
854
855 // Dump everything else here
856 return m_unmappedLayer;
857 }
858
862 bool IsLayerMapped( PCB_LAYER_ID aLayerId ) const
863 {
864 return aLayerId != m_unmappedLayer;
865 }
866
874 {
875 const wxString name = aTop ? "PLACE_BOUND_TOP" : "PLACE_BOUND_BOTTOM";
876 return mapCustomLayerByName( name );
877 }
878
883 bool IsOutlineLayer( const LAYER_INFO& aLayerInfo ) const
884 {
887 {
888 return false;
889 }
890
893 }
894
902 PCB_LAYER_ID MapCustomLayer( const LAYER_INFO& aLayerInfo, const wxString& aLayerName )
903 {
904 // See if we have mapped this layer name under a different class:subclass
905 if( m_MappedOptionalLayers.count( aLayerName ) )
906 {
907 const PCB_LAYER_ID existingLId = m_MappedOptionalLayers.at( aLayerName );
908 // Record the reuse
909 m_customLayerToKiMap[aLayerInfo] = existingLId;
910 return existingLId;
911 }
912
913 // First time we needed this name:
914 // Add as a user layer and store for next time
915 const PCB_LAYER_ID lId = addUserLayer( aLayerName );
916 m_customLayerToKiMap[aLayerInfo] = lId;
917 m_MappedOptionalLayers[aLayerName] = lId;
918
919 wxLogTrace( traceAllegroBuilder, "Adding mapping for %#04x:%#04x to %s", aLayerInfo.m_Class,
920 aLayerInfo.m_Subclass, aLayerName );
921 return lId;
922 }
923
924private:
925 static PCB_LAYER_ID getNthCopperLayer( int aNum, int aTotal )
926 {
927 if( aNum == 0 )
928 return F_Cu;
929 if( aNum == aTotal - 1 )
930 return B_Cu;
931 return ToLAYER_ID( 2 * ( aNum + 1 ) );
932 }
933
934 static PCB_LAYER_ID getNthUserLayer( int aNum )
935 {
936 aNum = std::min( aNum, MAX_USER_DEFINED_LAYERS - 1 );
937 return ToLAYER_ID( static_cast<int>( User_1 ) + 2 * aNum );
938 }
939
946 PCB_LAYER_ID mapCustomLayerByName( const wxString& aLayerName )
947 {
948 // If it's been added already, use it
949 if( m_MappedOptionalLayers.count( aLayerName ) )
950 {
951 return m_MappedOptionalLayers.at( aLayerName );
952 }
953
954 const PCB_LAYER_ID newLId = addUserLayer( aLayerName );
955 m_MappedOptionalLayers[aLayerName] = newLId;
956 return newLId;
957 }
958
959 PCB_LAYER_ID addUserLayer( const wxString& aName )
960 {
962 m_board.GetDesignSettings().SetUserDefinedLayerCount( m_board.GetDesignSettings().GetUserDefinedLayerCount() + 1 );
963 m_board.SetLayerName( lId, aName );
964 wxLogTrace( traceAllegroBuilder, "Adding user layer %s: %s", LayerName( lId ), aName );
965 return lId;
966 }
967
968 // Map of original layer list - we use this to store the CUSTOM_LAYERs, as well
969 // as check that we only handle each one once
970 std::unordered_map<const BLK_0x2A_LAYER_LIST*, std::vector<CUSTOM_LAYER>> m_Lists;
971
972 // Which classes point to which layer lists (more than one class can point to one list.
973 std::unordered_map<uint8_t, std::vector<CUSTOM_LAYER>*> m_ClassCustomLayerLists;
974
981 std::unordered_map<LAYER_INFO, PCB_LAYER_ID> m_customLayerToKiMap;
982
988 std::unordered_map<wxString, PCB_LAYER_ID> m_MappedOptionalLayers;
989
993 std::unordered_map<LAYER_INFO, PCB_LAYER_ID> m_staticLayerOverrides;
994
999 std::unordered_map<LAYER_INFO, wxString> m_customLayerDialogNames;
1000
1004 std::unordered_map<LAYER_INFO, int> m_unknownLayers;
1005
1007
1008 // The layer to use for mapping failures;
1010
1012
1015};
1016
1017
1018BOARD_BUILDER::BOARD_BUILDER( const BRD_DB& aRawBoard, BOARD& aBoard, REPORTER& aReporter,
1019 PROGRESS_REPORTER* aProgressReporter,
1020 const LAYER_MAPPING_HANDLER& aLayerMappingHandler ) :
1021 m_brdDb( aRawBoard ), m_board( aBoard ), m_reporter( aReporter ), m_progressReporter( aProgressReporter ),
1022 m_layerMappingHandler( aLayerMappingHandler ),
1024{
1025 // Internal coordinates are stored in <base> / <divisor> units.
1026
1027 const std::map<BOARD_UNITS, int> c_baseScales = { { BOARD_UNITS::MILS, pcbIUScale.MilsToIU( 1 ) },
1028 { BOARD_UNITS::INCHES, pcbIUScale.MilsToIU( 1000 ) },
1029 { BOARD_UNITS::MILLIMETERS, pcbIUScale.mmToIU( 1 ) },
1030 { BOARD_UNITS::CENTIMETERS, pcbIUScale.mmToIU( 10 ) },
1031 { BOARD_UNITS::MICROMETERS, pcbIUScale.mmToIU( 0.001 ) } };
1032
1033 if( m_brdDb.m_Header->m_UnitsDivisor == 0 )
1034 THROW_IO_ERROR( "Board units divisor is 0" );
1035
1036 if( !c_baseScales.contains( m_brdDb.m_Header->m_BoardUnits ) )
1037 THROW_IO_ERROR( "Unknown board units" );
1038
1039 double baseScale( c_baseScales.at( m_brdDb.m_Header->m_BoardUnits ) );
1040
1041 m_scale = baseScale / m_brdDb.m_Header->m_UnitsDivisor;
1042}
1043
1044
1048
1049
1050static int clampForScale( double aValue )
1051{
1052 double result = std::round( aValue );
1053
1054 if( result > std::numeric_limits<int>::max() )
1055 return std::numeric_limits<int>::max();
1056
1057 if( result < std::numeric_limits<int>::min() )
1058 return std::numeric_limits<int>::min();
1059
1060 return static_cast<int>( result );
1061}
1062
1063
1065{
1066 return VECTOR2I{
1067 clampForScale( aVector.x * m_scale ),
1068 clampForScale( -aVector.y * m_scale ),
1069 };
1070}
1071
1072int BOARD_BUILDER::scale( int aValue ) const
1073{
1074 return clampForScale( aValue * m_scale );
1075}
1076
1077
1079{
1080 return VECTOR2I{
1081 clampForScale( std::abs( aSize.x ) * m_scale ),
1082 clampForScale( std::abs( aSize.y ) * m_scale ),
1083 };
1084}
1085
1086
1087void BOARD_BUILDER::reportMissingBlock( uint32_t aKey, uint8_t aType ) const
1088{
1089 m_reporter.Report( wxString::Format( "Could not find expected block with key %#010x and type %#04x", aKey, aType ),
1091}
1092
1093
1094void BOARD_BUILDER::reportUnexpectedBlockType( uint8_t aGot, uint8_t aExpected, uint32_t aKey, size_t aOffset,
1095 const wxString& aName ) const
1096{
1097 wxString name = aName.IsEmpty() ? wxString( "Object" ) : aName;
1098 wxString withKey = ( aKey == 0 ) ? wxString( "" ) : wxString::Format( ", with key %#010x ", aKey );
1099 wxString withOffset = ( aOffset == 0 ) ? wxString( "" ) : wxString::Format( ", at offset %#lx ", aOffset );
1100
1101 wxString s = wxString::Format( "%s has unexpected type %#04x (expected %#04x)%s%s", name, aGot, aExpected, withKey,
1102 withOffset );
1103
1104 m_reporter.Report( s, RPT_SEVERITY_WARNING );
1105}
1106
1107
1108wxString BOARD_BUILDER::get0x30StringValue( uint32_t a0x30Key ) const
1109{
1110 const BLK_0x30_STR_WRAPPER* blk0x30 = expectBlockByKey<BLK_0x30_STR_WRAPPER>( a0x30Key, 0x30 );
1111
1112 if( blk0x30 == nullptr )
1113 THROW_IO_ERROR( "Failed to get 0x30 for string lookup" );
1114
1116
1117 if( blk0x31 == nullptr )
1118 THROW_IO_ERROR( "Failed to get 0x31 for string lookup" );
1119
1120 return blk0x31->m_Value;
1121}
1122
1123
1125{
1126 LL_WALKER x36_walker{ m_brdDb.m_Header->m_LL_0x36.m_Head, m_brdDb.m_Header->m_LL_0x36.m_Tail, m_brdDb };
1127
1128 bool encountered = false;
1129
1130 for( const BLOCK_BASE* block : x36_walker )
1131 {
1132 if( block->GetBlockType() != 0x36 )
1133 continue;
1134
1135 const BLK_0x36_DEF_TABLE& blk0x36 = static_cast<const BLOCK<BLK_0x36_DEF_TABLE>&>( *block ).GetData();
1136
1137 if( blk0x36.m_Code != 0x08 )
1138 continue;
1139
1140 if( encountered )
1141 {
1142 // This would be bad, because we won't get the indexes into the list right if there
1143 // it's made up of entries from more than one list of entries.
1144 m_reporter.Report( "Found more than one font definition lists in the 0x36 list.", RPT_SEVERITY_WARNING );
1145 break;
1146 }
1147
1148 for( const auto& item : blk0x36.m_Items )
1149 {
1150 const auto& fontDef = std::get<BLK_0x36_DEF_TABLE::FontDef_X08>( item );
1151 m_fontDefList.push_back( &fontDef );
1152 }
1153
1154 encountered = true;
1155 }
1156}
1157
1158
1160{
1161 wxLogTrace( traceAllegroBuilder, "Creating nets from Allegro data" );
1162
1163 // Incrementing netcode. We could also choose to, say, use the 0x1B key if we wanted
1164 int netCode = 1;
1165
1166 std::vector<BOARD_ITEM*> bulkAdded;
1167
1168 LL_WALKER netWalker{ m_brdDb.m_Header->m_LL_0x1B_Nets, m_brdDb };
1169
1170 for( const BLOCK_BASE* block : netWalker )
1171 {
1172 const uint8_t type = block->GetBlockType();
1173
1174 if( type != BLOCK_TYPE::x1B_NET )
1175 {
1176 reportUnexpectedBlockType( type, BLOCK_TYPE::x1B_NET, 0, block->GetOffset(), "Net" );
1177 continue;
1178 }
1179
1180 const auto& netBlk = static_cast<const BLOCK<BLK_0x1B_NET>&>( *block ).GetData();
1181
1182 wxString netName = m_brdDb.GetString( netBlk.m_NetName );
1183
1184 // Allegro allows unnamed nets. KiCad's NETINFO_LIST matches nets by name, and all
1185 // empty-named nets would collapse to the unconnected net (code 0). Generate a unique
1186 // name so each Allegro net gets its own KiCad net code.
1187 if( netName.IsEmpty() )
1188 netName = wxString::Format( wxS( "Net_%d" ), netCode );
1189
1190 auto kiNetInfo = std::make_unique<NETINFO_ITEM>( &m_board, netName, netCode );
1191 netCode++;
1192
1193 m_netCache[netBlk.m_Key] = kiNetInfo.get();
1194 bulkAdded.push_back( kiNetInfo.get() );
1195 m_board.Add( kiNetInfo.release(), ADD_MODE::BULK_APPEND );
1196 }
1197
1198 m_board.FinalizeBulkAdd( bulkAdded );
1199
1200 wxLogTrace( traceAllegroBuilder, "Added %zu nets", m_netCache.size() );
1201}
1202
1203
1204wxString BOARD_BUILDER::resolveConstraintSetNameFromField( uint32_t aFieldKey ) const
1205{
1206 const BLOCK_BASE* fieldBlock = m_brdDb.GetObjectByKey( aFieldKey );
1207
1208 if( !fieldBlock || fieldBlock->GetBlockType() != 0x03 )
1209 return wxEmptyString;
1210
1211 const BLK_0x03_FIELD& field = static_cast<const BLOCK<BLK_0x03_FIELD>&>( *fieldBlock ).GetData();
1212 const std::string* str = std::get_if<std::string>( &field.m_Substruct );
1213
1214 if( !str )
1215 return wxEmptyString;
1216
1217 // Extract name from schematic cross-reference format: @lib.xxx(view):\NAME\.
1218 // Find the last colon-backslash separator in the raw std::string and extract from there.
1219 size_t sep = str->find( ":\\" );
1220
1221 if( sep == std::string::npos )
1222 return wxEmptyString;
1223
1224 std::string extracted = str->substr( sep + 2 );
1225
1226 if( !extracted.empty() && extracted.back() == '\\' )
1227 extracted.pop_back();
1228
1229 return wxString( extracted );
1230}
1231
1232
1234{
1235 wxLogTrace( traceAllegroBuilder, "Importing physical constraint sets from 0x1D blocks" );
1236
1237 BOARD_DESIGN_SETTINGS& bds = m_board.GetDesignSettings();
1238 std::shared_ptr<NET_SETTINGS> netSettings = bds.m_NetSettings;
1239
1240 bool isV172Plus = ( m_brdDb.m_FmtVer >= FMT_VER::V_172 );
1241
1242 struct CS_DEF
1243 {
1244 wxString name;
1245 int lineWidth = 0;
1246 int clearance = 0;
1247 int diffPairGap = 0;
1248 };
1249
1250 // Map from constraint set name to its definition
1251 std::map<wxString, CS_DEF> constraintSets;
1252
1253 // Also map string table keys to set names for net lookup
1254 std::map<uint32_t, wxString> keyToSetName;
1255
1256 int csIndex = 0;
1257 const LL_WALKER csWalker( m_brdDb.m_Header->m_LL_0x1D_0x1E_0x1F, m_brdDb );
1258
1259 for( const BLOCK_BASE* block : csWalker )
1260 {
1261 if( block->GetBlockType() != 0x1D )
1262 continue;
1263
1264 const BLK_0x1D_CONSTRAINT_SET& csBlock = static_cast<const BLOCK<BLK_0x1D_CONSTRAINT_SET>&>( *block ).GetData();
1265
1266 wxString setName;
1267 const wxString* resolved = m_brdDb.ResolveString( csBlock.m_NameStrKey );
1268
1269 if( resolved && !resolved->IsEmpty() )
1270 {
1271 setName = *resolved;
1272 }
1273 else if( csBlock.m_FieldPtr != 0 )
1274 {
1275 // Some boards store the name in a 0x03 FIELD block as a schematic cross-reference
1276 setName = resolveConstraintSetNameFromField( csBlock.m_FieldPtr );
1277 }
1278
1279 if( setName.IsEmpty() )
1280 setName = wxString::Format( wxS( "CS_%d" ), csIndex );
1281
1282 csIndex++;
1283
1284 if( csBlock.m_DataB.empty() )
1285 {
1286 wxLogTrace( traceAllegroBuilder, "Constraint set '%s' has no DataB records, skipping", setName );
1287 continue;
1288 }
1289
1290 // Parse first DataB record (first copper layer) as 14 x int32
1291 const auto& record = csBlock.m_DataB[0];
1292 int32_t fields[14];
1293 static_assert( sizeof( fields ) == std::tuple_size_v<std::decay_t<decltype( record )>> );
1294 memcpy( fields, record.data(), sizeof( fields ) );
1295
1296 CS_DEF def;
1297 def.name = setName;
1298
1299 if( isV172Plus )
1300 {
1301 def.lineWidth = scale( fields[1] );
1302 def.clearance = scale( fields[4] );
1303 }
1304 else
1305 {
1306 // Pre-V172: f[0] is preferred line width, f[1] is line spacing (used as clearance).
1307 // f[4] is sometimes also clearance when non-zero, but f[1] is the primary source.
1308 def.lineWidth = scale( fields[0] );
1309 def.clearance = scale( fields[1] );
1310 }
1311
1312 def.diffPairGap = scale( fields[7] );
1313
1314 constraintSets[setName] = def;
1315 keyToSetName[csBlock.m_NameStrKey] = setName;
1316
1317 wxLogTrace( traceAllegroBuilder,
1318 "Constraint set '%s': line_width=%d nm, clearance=%d nm, dp_gap=%d nm",
1319 setName, def.lineWidth, def.clearance, def.diffPairGap );
1320 }
1321
1322 if( constraintSets.empty() )
1323 {
1324 wxLogTrace( traceAllegroBuilder, "No physical constraint sets found" );
1325 return;
1326 }
1327
1328 // Create a netclass for each constraint set that has nonzero values
1329 for( const auto& [name, def] : constraintSets )
1330 {
1331 wxString ncName = name;
1332
1333 if( ncName.CmpNoCase( NETCLASS::Default ) == 0 )
1334 ncName = wxS( "Allegro_Default" );
1335
1336 if( netSettings->HasNetclass( ncName ) )
1337 continue;
1338
1339 auto nc = std::make_shared<NETCLASS>( ncName );
1340
1341 if( def.lineWidth > 0 )
1342 nc->SetTrackWidth( def.lineWidth );
1343
1344 if( def.clearance > 0 )
1345 nc->SetClearance( def.clearance );
1346
1347 if( def.diffPairGap > 0 )
1348 {
1349 nc->SetDiffPairGap( def.diffPairGap );
1350
1351 // Diff pair width is the same as track width for the pair's netclass
1352 if( def.lineWidth > 0 )
1353 nc->SetDiffPairWidth( def.lineWidth );
1354 }
1355
1356 netSettings->SetNetclass( ncName, nc );
1357
1358 wxLogTrace( traceAllegroBuilder, "Created netclass '%s' from constraint set '%s'", ncName, name );
1359 }
1360
1361 // Walk all NETs and assign them to constraint set netclasses via field 0x1a0
1362 wxString defaultSetName;
1363
1364 for( const auto& [name, def] : constraintSets )
1365 {
1366 if( name.CmpNoCase( wxS( "DEFAULT" ) ) == 0 )
1367 {
1368 defaultSetName = name;
1369 break;
1370 }
1371 }
1372
1373 m_brdDb.VisitNets( [&]( const VIEW_OBJS& aView )
1374 {
1375 if( !aView.m_Net )
1376 return;
1377
1378 const NET& net = *aView.m_Net;
1379
1380 // Field 0x1a0 references the constraint set. It can be an integer (string table key
1381 // that matches 0x1D.m_NameStrKey) or a direct string (the constraint set name).
1383
1384 wxString assignedSetName;
1385
1386 if( csField.has_value() )
1387 {
1388 if( auto* intVal = std::get_if<uint32_t>( &csField.value() ) )
1389 {
1390 auto it = keyToSetName.find( *intVal );
1391
1392 if( it != keyToSetName.end() )
1393 assignedSetName = it->second;
1394 }
1395 else if( auto* strVal = std::get_if<wxString>( &csField.value() ) )
1396 {
1397 if( constraintSets.count( *strVal ) )
1398 assignedSetName = *strVal;
1399 }
1400 }
1401
1402 // Nets without field 0x1a0 use the DEFAULT constraint set
1403 if( assignedSetName.IsEmpty() && !defaultSetName.IsEmpty() )
1404 assignedSetName = defaultSetName;
1405
1406 if( assignedSetName.IsEmpty() )
1407 return;
1408
1409 wxString ncName = assignedSetName;
1410
1411 if( ncName.CmpNoCase( NETCLASS::Default ) == 0 )
1412 ncName = wxS( "Allegro_Default" );
1413
1414 if( !netSettings->HasNetclass( ncName ) )
1415 return;
1416
1417 auto netIt = m_netCache.find( net.GetKey() );
1418
1419 if( netIt == m_netCache.end() )
1420 return;
1421
1422 NETINFO_ITEM* kiNet = netIt->second;
1423 netSettings->SetNetclassPatternAssignment( kiNet->GetNetname(), ncName );
1424 kiNet->SetNetClass( netSettings->GetNetClassByName( ncName ) );
1425 } );
1426
1427 wxLogTrace( traceAllegroBuilder, "Applied %zu physical constraint sets", constraintSets.size() );
1428}
1429
1430
1432{
1433 wxLogTrace( traceAllegroBuilder, "Applying per-net trace width constraints" );
1434
1435 BOARD_DESIGN_SETTINGS& bds = m_board.GetDesignSettings();
1436 std::shared_ptr<NET_SETTINGS> netSettings = bds.m_NetSettings;
1437
1438 // Group nets by their minimum trace width to create netclasses.
1439 // Allegro stores per-net min/max trace width in FIELD blocks attached to each NET.
1440 std::map<int, std::vector<uint32_t>> widthToNetKeys;
1441
1442 m_brdDb.VisitNets( [&]( const VIEW_OBJS& aView )
1443 {
1444 if( !aView.m_Net )
1445 return;
1446
1447 std::optional<int> minWidth = aView.m_Net->GetNetMinLineWidth();
1448
1449 if( !minWidth.has_value() || minWidth.value() <= 0 )
1450 return;
1451
1452 int widthNm = scale( minWidth.value() );
1453 widthToNetKeys[widthNm].push_back( aView.m_Net->GetKey() );
1454 } );
1455
1456 if( widthToNetKeys.empty() )
1457 {
1458 wxLogTrace( traceAllegroBuilder, "No per-net trace width constraints found" );
1459 return;
1460 }
1461
1462 for( const auto& [widthNm, netKeys] : widthToNetKeys )
1463 {
1464 int widthMils = ( widthNm + 12700 ) / 25400;
1465 wxString ncName = wxString::Format( wxS( "W%dmil" ), widthMils );
1466
1467 if( netSettings->HasNetclass( ncName ) )
1468 continue;
1469
1470 auto nc = std::make_shared<NETCLASS>( ncName );
1471 nc->SetTrackWidth( widthNm );
1472 netSettings->SetNetclass( ncName, nc );
1473
1474 for( uint32_t netKey : netKeys )
1475 {
1476 auto it = m_netCache.find( netKey );
1477
1478 if( it == m_netCache.end() )
1479 continue;
1480
1481 NETINFO_ITEM* kiNet = it->second;
1482 netSettings->SetNetclassPatternAssignment( kiNet->GetNetname(), ncName );
1483 kiNet->SetNetClass( nc );
1484 }
1485
1486 wxLogTrace( traceAllegroBuilder, "Created netclass '%s' (track width %d nm) with %zu nets",
1487 ncName, widthNm, netKeys.size() );
1488 }
1489
1490 wxLogTrace( traceAllegroBuilder, "Applied trace width constraints from %zu unique width groups",
1491 widthToNetKeys.size() );
1492}
1493
1494
1496{
1497 if( aNet.m_MatchGroupPtr == 0 )
1498 return wxEmptyString;
1499
1500 const BLOCK_BASE* block = m_brdDb.GetObjectByKey( aNet.m_MatchGroupPtr );
1501
1502 if( !block )
1503 return wxEmptyString;
1504
1505 uint32_t tableKey = 0;
1506
1507 if( block->GetBlockType() == 0x26 )
1508 {
1509 // V172+ path: NET -> 0x26 -> m_GroupPtr -> 0x2C TABLE
1510 const auto& x26 = static_cast<const BLOCK<BLK_0x26_MATCH_GROUP>&>( *block ).GetData();
1511 tableKey = x26.m_GroupPtr;
1512
1513 // Some boards have chained 0x26 blocks (m_GroupPtr -> another 0x26 -> 0x2C)
1514 if( tableKey != 0 )
1515 {
1516 const BLOCK_BASE* next = m_brdDb.GetObjectByKey( tableKey );
1517
1518 if( next && next->GetBlockType() == 0x26 )
1519 {
1520 const auto& x26b = static_cast<const BLOCK<BLK_0x26_MATCH_GROUP>&>( *next ).GetData();
1521 tableKey = x26b.m_GroupPtr;
1522 }
1523 }
1524 }
1525 else if( block->GetBlockType() == 0x2C )
1526 {
1527 // Pre-V172 path: NET -> 0x2C TABLE directly
1528 tableKey = aNet.m_MatchGroupPtr;
1529 }
1530 else
1531 {
1532 return wxEmptyString;
1533 }
1534
1535 if( tableKey == 0 )
1536 return wxEmptyString;
1537
1538 // Verify the target is actually a 0x2C TABLE before calling expectBlockByKey to
1539 // avoid noisy warnings on boards with unexpected pointer chain configurations.
1540 const BLOCK_BASE* tableBlock = m_brdDb.GetObjectByKey( tableKey );
1541
1542 if( !tableBlock || tableBlock->GetBlockType() != 0x2C )
1543 return wxEmptyString;
1544
1545 const BLK_0x2C_TABLE* tbl = expectBlockByKey<BLK_0x2C_TABLE>( tableKey, 0x2C );
1546
1547 if( !tbl || tbl->m_StringPtr == 0 )
1548 return wxEmptyString;
1549
1550 const wxString& name = m_brdDb.GetString( tbl->m_StringPtr );
1551
1552 wxLogTrace( traceAllegroBuilder, "Resolving match group name for NET '%s': found table at key %#010x, subtype %#x, name '%s'",
1553 m_brdDb.GetString( aNet.m_NetName ), tableKey, tbl->m_SubType, name );
1554
1555 return name;
1556}
1557
1558
1560{
1561 wxLogTrace( traceAllegroBuilder, "Applying match group / differential pair assignments" );
1562
1563 BOARD_DESIGN_SETTINGS& bds = m_board.GetDesignSettings();
1564 std::shared_ptr<NET_SETTINGS> netSettings = bds.m_NetSettings;
1565
1566 // Group NET keys by their match group name
1567 std::map<wxString, std::vector<uint32_t>> groupToNetKeys;
1568
1569 LL_WALKER netWalker{ m_brdDb.m_Header->m_LL_0x1B_Nets, m_brdDb };
1570
1571 for( const BLOCK_BASE* block : netWalker )
1572 {
1573 if( block->GetBlockType() != BLOCK_TYPE::x1B_NET )
1574 continue;
1575
1576 const auto& netBlk = static_cast<const BLOCK<BLK_0x1B_NET>&>( *block ).GetData();
1577 wxString groupName = resolveMatchGroupName( netBlk );
1578
1579 if( groupName.empty() )
1580 continue;
1581
1582 groupToNetKeys[groupName].push_back( netBlk.m_Key );
1583 }
1584
1585 if( groupToNetKeys.empty() )
1586 {
1587 wxLogTrace( traceAllegroBuilder, "No match groups found" );
1588 return;
1589 }
1590
1591 int dpCount = 0;
1592 int mgCount = 0;
1593
1594 for( const auto& [groupName, netKeys] : groupToNetKeys )
1595 {
1596 // A diff pair has exactly 2 nets. We don't check P/N naming because Allegro doesn't
1597 // require any naming convention for paired nets.
1598 bool isDiffPair = ( netKeys.size() == 2 );
1599 wxString ncPrefix = isDiffPair ? wxS( "DP_" ) : wxS( "MG_" );
1600 wxString ncName = ncPrefix + groupName;
1601
1602 if( netSettings->HasNetclass( ncName ) )
1603 continue;
1604
1605 auto nc = std::make_shared<NETCLASS>( ncName );
1606
1607 // Inherit constraint set values from the first net's current netclass so that
1608 // clearance and track width from the underlying constraint set are not lost.
1609 for( uint32_t netKey : netKeys )
1610 {
1611 auto it = m_netCache.find( netKey );
1612
1613 if( it == m_netCache.end() )
1614 continue;
1615
1616 NETCLASS* existing = it->second->GetNetClass();
1617
1618 if( existing && existing->GetName() != NETCLASS::Default )
1619 {
1620 if( existing->HasClearance() )
1621 nc->SetClearance( existing->GetClearance() );
1622
1623 if( existing->HasTrackWidth() )
1624 nc->SetTrackWidth( existing->GetTrackWidth() );
1625
1626 if( existing->HasDiffPairGap() )
1627 nc->SetDiffPairGap( existing->GetDiffPairGap() );
1628
1629 if( existing->HasDiffPairWidth() )
1630 nc->SetDiffPairWidth( existing->GetDiffPairWidth() );
1631
1632 break;
1633 }
1634 }
1635
1636 netSettings->SetNetclass( ncName, nc );
1637
1638 for( uint32_t netKey : netKeys )
1639 {
1640 auto it = m_netCache.find( netKey );
1641
1642 if( it == m_netCache.end() )
1643 continue;
1644
1645 NETINFO_ITEM* kiNet = it->second;
1646 netSettings->SetNetclassPatternAssignment( kiNet->GetNetname(), ncName );
1647 kiNet->SetNetClass( nc );
1648 }
1649
1650 if( isDiffPair )
1651 dpCount++;
1652 else
1653 mgCount++;
1654
1655 wxLogTrace( traceAllegroBuilder, "%s group '%s' -> netclass '%s' with %zu nets",
1656 isDiffPair ? wxS( "Diff pair" ) : wxS( "Match" ),
1657 groupName, ncName, netKeys.size() );
1658 }
1659
1660 wxLogTrace( traceAllegroBuilder,
1661 "Applied match groups: %d diff pairs, %d match groups (%zu total groups)",
1662 dpCount, mgCount, groupToNetKeys.size() );
1663}
1664
1665
1679static std::unordered_set<LAYER_INFO> ScanForLayers( const BRD_DB& aDb )
1680{
1681 std::unordered_set<LAYER_INFO> layersFound;
1682
1683 const auto& addLayer = [&]( std::optional<LAYER_INFO>& info )
1684 {
1685 if( info.has_value() )
1686 {
1687 layersFound.insert( std::move( info.value() ) );
1688 }
1689 };
1690
1691 const auto& simpleWalker = [&]( const FILE_HEADER::LINKED_LIST& aLL )
1692 {
1693 LL_WALKER walker{ aLL, aDb };
1694 for( const BLOCK_BASE* block : walker )
1695 {
1696 std::optional<LAYER_INFO> info = tryLayerFromBlock( *block );
1697 addLayer( info );
1698 }
1699 };
1700
1701 simpleWalker( aDb.m_Header->m_LL_Shapes );
1702 simpleWalker( aDb.m_Header->m_LL_0x24_0x28 );
1703 simpleWalker( aDb.m_Header->m_LL_0x14 );
1704
1705 return layersFound;
1706}
1707
1708
1710{
1711 wxLogTrace( traceAllegroBuilder, "Setting up layer mapping from Allegro to KiCad" );
1712
1713 const auto& layerMap = m_brdDb.m_Header->m_LayerMap;
1714
1715 for( size_t i = 0; i < layerMap.size(); ++i )
1716 {
1717 const uint8_t classNum = static_cast<uint8_t>( i );
1718
1719 const uint32_t x2aKey = layerMap[i].m_LayerList0x2A;
1720
1721 if( x2aKey == 0 )
1722 continue;
1723
1724 const BLK_0x2A_LAYER_LIST* layerList = expectBlockByKey<BLK_0x2A_LAYER_LIST>( x2aKey, 0x2A );
1725
1726 // Probably an error
1727 if( !layerList )
1728 continue;
1729
1730 m_layerMapper->ProcessLayerList( classNum, *layerList );
1731 }
1732
1733 std::unordered_set<LAYER_INFO> layersFound = ScanForLayers( m_brdDb );
1734
1735 wxLogTrace( traceAllegroBuilder, "Scanned %zu layers", layersFound.size() );
1736 for( const LAYER_INFO& info : layersFound )
1737 {
1738 wxLogTrace( traceAllegroBuilder, " - %#02x:%#02x (%s)", info.m_Class, info.m_Subclass,
1740 }
1741
1742 // The outline is sometimes on OUTLINE and sometimes on DESIGN_OUTLINE, and sometimes
1743 // on both. In the first two cases, whichever it is goes to Edge.Cuts, but in the both case,
1744 // we send one to a User layer
1747
1748 if( layersFound.count( outlineInfo ) && layersFound.count( designOutlineInfo ) )
1749 {
1750 // Both layers found, remap DESIGN_OUTLINE to a user layer
1751 wxLogTrace( traceAllegroBuilder,
1752 "Both OUTLINE and DESIGN_OUTLINE layers found, remapping DESIGN_OUTLINE to a user layer" );
1753 m_layerMapper->MapCustomLayer( designOutlineInfo, layerInfoDisplayName( designOutlineInfo ) );
1754 }
1755
1756 m_layerMapper->FinalizeLayers();
1757}
1758
1759
1761{
1762 if( aIndex == 0 || aIndex > m_fontDefList.size() )
1763 {
1764 m_reporter.Report(
1765 wxString::Format( "Font def index %u requested, have %zu entries", aIndex, m_fontDefList.size() ),
1767 return nullptr;
1768 }
1769
1770 // The index appears to be 1-indexed (maybe 0 means something special?)
1771 aIndex -= 1;
1772
1773 return m_fontDefList[aIndex];
1774}
1775
1776
1777std::unique_ptr<PCB_SHAPE> BOARD_BUILDER::buildLineSegment( const BLK_0x15_16_17_SEGMENT& aSegment,
1778 const LAYER_INFO& aLayerInfo, PCB_LAYER_ID aLayer,
1779 BOARD_ITEM_CONTAINER& aParent )
1780{
1781 VECTOR2I start = scale( { aSegment.m_StartX, aSegment.m_StartY } );
1782 VECTOR2I end = scale( { aSegment.m_EndX, aSegment.m_EndY } );
1783 const int width = scale( aSegment.m_Width );
1784
1785 if( !m_layerMapper->IsLayerMapped( aLayer ) )
1786 {
1787 wxLogTrace( traceAllegroBuilder, "Unmapped Seg: %#04x %#04x %s, %s", aLayerInfo.m_Class, aLayerInfo.m_Subclass,
1788 start.Format(), end.Format() );
1789 }
1790
1791 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent, SHAPE_T::SEGMENT );
1792 shape->SetLayer( aLayer );
1793 shape->SetStart( start );
1794 shape->SetEnd( end );
1795
1796 {
1797 int adjustedWidth = width;
1798
1799 if( adjustedWidth <= 0 )
1800 adjustedWidth = m_board.GetDesignSettings().GetLineThickness( aLayer );
1801
1802 shape->SetWidth( adjustedWidth );
1803 }
1804
1805 return shape;
1806}
1807
1808
1809std::unique_ptr<PCB_SHAPE> BOARD_BUILDER::buildArc( const BLK_0x01_ARC& aArc, const LAYER_INFO& aLayerInfo,
1810 PCB_LAYER_ID aLayer, BOARD_ITEM_CONTAINER& aParent )
1811{
1812 VECTOR2I start{ aArc.m_StartX, aArc.m_StartY };
1813 VECTOR2I end{ aArc.m_EndX, aArc.m_EndY };
1814
1815 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent, SHAPE_T::ARC );
1816
1817 shape->SetLayer( aLayer );
1818
1819 if( !m_layerMapper->IsLayerMapped( aLayer ) )
1820 {
1821 wxLogTrace( traceAllegroBuilder, "Unmapped Arc: %#04x %#04x %s, %s", aLayerInfo.m_Class, aLayerInfo.m_Subclass,
1822 start.Format(), end.Format() );
1823 }
1824
1825 start = scale( start );
1826 end = scale( end );
1827
1828 VECTOR2I c = scale( KiROUND( VECTOR2D{ aArc.m_CenterX, aArc.m_CenterY } ) );
1829
1830 int radius = scale( KiROUND( aArc.m_Radius ) );
1831
1832 bool clockwise = ( aArc.m_SubType & 0x40 ) != 0;
1833
1834 {
1835 int arcWidth = scale( aArc.m_Width );
1836
1837 if( arcWidth <= 0 )
1838 arcWidth = m_board.GetDesignSettings().GetLineThickness( aLayer );
1839
1840 shape->SetWidth( arcWidth );
1841 }
1842
1843 if( start == end )
1844 {
1845 shape->SetShape( SHAPE_T::CIRCLE );
1846 shape->SetCenter( c );
1847 shape->SetRadius( radius );
1848 }
1849 else
1850 {
1851 shape->SetShape( SHAPE_T::ARC );
1852 EDA_ANGLE startangle( start - c );
1853 EDA_ANGLE endangle( end - c );
1854
1855 startangle.Normalize();
1856 endangle.Normalize();
1857
1858 EDA_ANGLE angle = endangle - startangle;
1859
1860 if( clockwise && angle < ANGLE_0 )
1861 angle += ANGLE_360;
1862 if( !clockwise && angle > ANGLE_0 )
1863 angle -= ANGLE_360;
1864
1865 if( start == end )
1866 angle = -ANGLE_360;
1867
1868 VECTOR2I mid = start;
1869 RotatePoint( mid, c, -angle / 2.0 );
1870
1871 shape->SetArcGeometry( start, mid, end );
1872 }
1873
1874 return shape;
1875}
1876
1877
1878std::unique_ptr<PCB_TEXT> BOARD_BUILDER::buildPcbText( const BLK_0x30_STR_WRAPPER& aStrWrapper,
1879 BOARD_ITEM_CONTAINER& aParent )
1880{
1881 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( &aParent );
1882
1883 VECTOR2I textPos = scale( VECTOR2I{ aStrWrapper.m_CoordsX, aStrWrapper.m_CoordsY } );
1884 PCB_LAYER_ID layer = getLayer( aStrWrapper.m_Layer );
1885
1886 text->SetPosition( textPos );
1887 text->SetLayer( layer );
1888
1889 const BLK_0x31_SGRAPHIC* strGraphic = expectBlockByKey<BLK_0x31_SGRAPHIC>( aStrWrapper.m_StrGraphicPtr, 0x31 );
1890
1891 if( !strGraphic )
1892 {
1893 m_reporter.Report( wxString::Format( "Failed to find string graphic (0x31) with key %#010x "
1894 "in string wrapper (0x30) with key %#010x",
1895 aStrWrapper.m_StrGraphicPtr, aStrWrapper.m_Key ),
1897 return nullptr;
1898 }
1899
1900 const BLK_0x30_STR_WRAPPER::TEXT_PROPERTIES* props = nullptr;
1901
1902 if( aStrWrapper.m_Font.has_value() )
1903 props = &aStrWrapper.m_Font.value();
1904
1905 if( !props && aStrWrapper.m_Font16x.has_value() )
1906 props = &aStrWrapper.m_Font16x.value();
1907
1908 if( !props )
1909 {
1910 m_reporter.Report(
1911 wxString::Format( "Expected one of the font properties fields in 0x30 object (key %#010x) to be set.",
1912 aStrWrapper.m_Key ),
1914 return nullptr;
1915 }
1916
1917 const BLK_0x36_DEF_TABLE::FontDef_X08* fontDef = getFontDef( props->m_Key );
1918
1919 if( !fontDef )
1920 return nullptr;
1921
1922 text->SetText( strGraphic->m_Value );
1923 text->SetTextWidth( scale( fontDef->m_CharWidth ) );
1924 text->SetTextHeight( scale( fontDef->m_CharHeight ) );
1925 text->SetTextThickness( std::max( 1, scale( fontDef->m_CharHeight ) / 8 ) );
1926
1927 const EDA_ANGLE textAngle{ static_cast<double>( aStrWrapper.m_Rotation ) / 1000.0, DEGREES_T };
1928 text->SetTextAngle( textAngle );
1929
1931 text->SetMirrored( true );
1932
1933 switch( props->m_Alignment )
1934 {
1936 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1937 break;
1939 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
1940 break;
1942 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
1943 break;
1944 default:
1945 break;
1946 }
1947
1948 return text;
1949}
1950
1951
1952std::vector<std::unique_ptr<BOARD_ITEM>> BOARD_BUILDER::buildDrillMarker( const BLK_0x0C_PIN_DEF& aPinDef,
1953 BOARD_ITEM_CONTAINER& aParent )
1954{
1956 std::vector<std::unique_ptr<PCB_SHAPE>> shapes;
1957
1958 const uint32_t markerShape = aPinDef.GetShape();
1959
1960 PCB_LAYER_ID layer = getLayer( aPinDef.m_Layer );
1961 const VECTOR2I center = scale( VECTOR2I{ aPinDef.m_Coords[0], aPinDef.m_Coords[1] } );
1962 const VECTOR2I size = scaleSize( VECTOR2I{ aPinDef.m_Size[0], aPinDef.m_Size[1] } );
1963
1964 const auto addLine = [&]( const SEG& aSeg )
1965 {
1966 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent, SHAPE_T::SEGMENT );
1967 shape->SetStart( aSeg.A );
1968 shape->SetEnd( aSeg.B );
1969 shapes.push_back( std::move( shape ) );
1970 };
1971
1972 const auto addPolyPts = [&]( const std::vector<VECTOR2I>& aPts )
1973 {
1974 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent, SHAPE_T::POLY );
1975 shape->SetPolyPoints( aPts );
1976 shapes.push_back( std::move( shape ) );
1977 };
1978
1979 switch( markerShape )
1980 {
1981 case MS::CIRCLE:
1982 {
1983 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent, SHAPE_T::CIRCLE );
1984 shape->SetCenter( center );
1985 shape->SetRadius( size.x / 2 );
1986 shapes.push_back( std::move( shape ) );
1987 break;
1988 }
1989 case MS::SQUARE:
1990 case MS::RECTANGLE:
1991 {
1992 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent, SHAPE_T::RECTANGLE );
1993 shape->SetStart( center - size / 2 );
1994 shape->SetEnd( center + size / 2 );
1995 shapes.push_back( std::move( shape ) );
1996 break;
1997 }
1998 case MS::CROSS:
1999 {
2000 std::unique_ptr<PCB_SHAPE> shape;
2001
2002 std::vector<SEG> segs = KIGEOM::MakeCrossSegments( center, size, ANGLE_0 );
2003
2004 for( const SEG& seg : segs )
2005 {
2006 addLine( seg );
2007 }
2008 break;
2009 }
2010 case MS::OBLONG_X:
2011 case MS::OBLONG_Y:
2012 {
2013 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent, SHAPE_T::RECTANGLE );
2014 shape->SetStart( center - size / 2 );
2015 shape->SetEnd( center + size / 2 );
2016
2017 int minSize = std::min( size.x, size.y );
2018 shape->SetCornerRadius( minSize / 2 );
2019 shapes.push_back( std::move( shape ) );
2020 break;
2021 }
2022 case MS::TRIANGLE:
2023 {
2024 // This triangle is point-up
2025 // Size follows fabmaster - the circumscribed circle of the triangle
2026 std::vector<VECTOR2I> pts = KIGEOM::MakeRegularPolygonPoints( center, 3, size.x / 2, true, ANGLE_90 );
2027 addPolyPts( pts );
2028 break;
2029 }
2030 case MS::DIAMOND:
2031 {
2032 std::vector<VECTOR2I> pts = KIGEOM::MakeRegularPolygonPoints( center, 4, size.x / 2, true, ANGLE_90 );
2033 addPolyPts( pts );
2034 break;
2035 }
2036 case MS::PENTAGON:
2037 {
2038 // Not 100% sure which way this should point
2039 std::vector<VECTOR2I> pts = KIGEOM::MakeRegularPolygonPoints( center, 5, size.x / 2, true, ANGLE_90 );
2040 addPolyPts( pts );
2041 break;
2042 }
2043 case MS::HEXAGON_X:
2044 case MS::HEXAGON_Y:
2045 {
2046 EDA_ANGLE startAngle = ( markerShape == MS::HEXAGON_X ) ? ANGLE_0 : ANGLE_90;
2047 std::vector<VECTOR2I> pts = KIGEOM::MakeRegularPolygonPoints( center, 6, size.x / 2, true, startAngle );
2048 addPolyPts( pts );
2049 break;
2050 }
2051 case MS::OCTAGON:
2052 {
2053 EDA_ANGLE startAngle = FULL_CIRCLE / 16; // Start at 22.5 degrees to align flat sides with axes
2054 // Octagons are measured across flats
2055 std::vector<VECTOR2I> pts = KIGEOM::MakeRegularPolygonPoints( center, 8, size.x / 2, false, startAngle );
2056 addPolyPts( pts );
2057 break;
2058 }
2059 default:
2060 {
2061 wxLogTrace( traceAllegroBuilder, "Unsupported drill marker shape type %#04x for pin definition with key %#010x",
2062 markerShape, aPinDef.m_Key );
2063 break;
2064 }
2065 }
2066
2067 std::vector<std::unique_ptr<BOARD_ITEM>> items;
2068 for( std::unique_ptr<PCB_SHAPE>& shape : shapes )
2069 {
2070 shape->SetLayer( layer );
2071 shape->SetWidth( 0 );
2072
2073 items.push_back( std::move( shape ) );
2074 }
2075
2076 return items;
2077}
2078
2079
2080std::vector<std::unique_ptr<BOARD_ITEM>> BOARD_BUILDER::buildGraphicItems( const BLOCK_BASE& aBlock,
2081 BOARD_ITEM_CONTAINER& aParent )
2082{
2083 std::vector<std::unique_ptr<BOARD_ITEM>> newItems;
2084
2085 switch( aBlock.GetBlockType() )
2086 {
2087 case 0x0c:
2088 {
2089 const auto& pinDef = static_cast<const BLOCK<BLK_0x0C_PIN_DEF>&>( aBlock ).GetData();
2090 newItems = buildDrillMarker( pinDef, aParent );
2091 break;
2092 }
2093 case 0x0e:
2094 {
2095 const auto& rect = static_cast<const BLOCK<BLK_0x0E_RECT>&>( aBlock ).GetData();
2096 std::unique_ptr<PCB_SHAPE> shape = buildRect( rect, aParent );
2097 if( shape )
2098 newItems.push_back( std::move( shape ) );
2099 break;
2100 }
2101 case 0x14:
2102 {
2103 const auto& graphicContainer = static_cast<const BLOCK<BLK_0x14_GRAPHIC>&>( aBlock ).GetData();
2104 std::vector<std::unique_ptr<PCB_SHAPE>> shapes = buildShapes( graphicContainer, aParent );
2105 for( std::unique_ptr<PCB_SHAPE>& shape : shapes )
2106 newItems.push_back( std::move( shape ) );
2107 break;
2108 }
2109 case 0x24:
2110 {
2111 const auto& rect = static_cast<const BLOCK<BLK_0x24_RECT>&>( aBlock ).GetData();
2112 std::unique_ptr<PCB_SHAPE> shape = buildRect( rect, aParent );
2113 if( shape )
2114 newItems.push_back( std::move( shape ) );
2115 break;
2116 }
2117 case 0x28:
2118 {
2119 const auto& shapeData = static_cast<const BLOCK<BLK_0x28_SHAPE>&>( aBlock ).GetData();
2120 std::unique_ptr<PCB_SHAPE> shape = buildPolygon( shapeData, aParent );
2121 if( shape )
2122 newItems.push_back( std::move( shape ) );
2123 break;
2124 }
2125 case 0x30:
2126 {
2127 const auto& strWrapper = static_cast<const BLOCK<BLK_0x30_STR_WRAPPER>&>( aBlock ).GetData();
2128
2129 std::unique_ptr<BOARD_ITEM> newItem = buildPcbText( strWrapper, aParent );
2130 if( newItem )
2131 newItems.push_back( std::move( newItem ) );
2132 break;
2133 }
2134 default:
2135 {
2136 wxLogTrace( traceAllegroBuilder, " Unhandled block type for buildItems: %#04x", aBlock.GetBlockType() );
2137 break;
2138 }
2139 }
2140
2141 return newItems;
2142};
2143
2144
2146{
2147 return m_layerMapper->GetLayer( aLayerInfo );
2148}
2149
2150
2151std::vector<std::unique_ptr<PCB_SHAPE>> BOARD_BUILDER::buildShapes( const BLK_0x14_GRAPHIC& aGraphic,
2152 BOARD_ITEM_CONTAINER& aParent )
2153{
2154 std::vector<std::unique_ptr<PCB_SHAPE>> shapes;
2155
2156 PCB_LAYER_ID layer = getLayer( aGraphic.m_Layer );
2157
2158 // Within the graphics list, we can get various lines and arcs on PLACE_BOUND_TOP, which
2159 // aren't actually the courtyard, which is a polygon in the 0x28 list. So, if we see such items,
2160 // remap them now to a specific other layer
2161 if( layer == F_CrtYd )
2162 layer = m_layerMapper->GetPlaceBounds( true );
2163 else if( layer == B_CrtYd )
2164 layer = m_layerMapper->GetPlaceBounds( false );
2165
2166 const LL_WALKER segWalker{ aGraphic.m_SegmentPtr, aGraphic.m_Key, m_brdDb };
2167
2168 for( const BLOCK_BASE* segBlock : segWalker )
2169 {
2170 std::unique_ptr<PCB_SHAPE> shape;
2171
2172 switch( segBlock->GetBlockType() )
2173 {
2174 case 0x01:
2175 {
2176 const auto& arc = static_cast<const BLOCK<BLK_0x01_ARC>&>( *segBlock ).GetData();
2177 shape = buildArc( arc, aGraphic.m_Layer, layer, aParent );
2178 break;
2179 }
2180 case 0x15:
2181 case 0x16:
2182 case 0x17:
2183 {
2184 const auto& seg = static_cast<const BLOCK<BLK_0x15_16_17_SEGMENT>&>( *segBlock ).GetData();
2185 shape = buildLineSegment( seg, aGraphic.m_Layer, layer, aParent );
2186 break;
2187 }
2188 default:
2189 {
2190 wxLogTrace( traceAllegroBuilder, " Unhandled block type in BLK_0x14_GRAPHIC: %#04x",
2191 segBlock->GetBlockType() );
2192 break;
2193 }
2194 }
2195
2196 if( shape )
2197 shapes.push_back( std::move( shape ) );
2198 }
2199
2200 return shapes;
2201}
2202
2203
2204std::unique_ptr<PCB_SHAPE> BOARD_BUILDER::buildRect( const BLK_0x24_RECT& aRect, BOARD_ITEM_CONTAINER& aParent )
2205{
2206 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent );
2207
2208 PCB_LAYER_ID layer = getLayer( aRect.m_Layer );
2209 shape->SetLayer( layer );
2210
2211 shape->SetShape( SHAPE_T::RECTANGLE );
2212
2213 const VECTOR2I cornerA = scale( VECTOR2I{ aRect.m_Coords[0], aRect.m_Coords[1] } );
2214 const VECTOR2I cornerB = scale( VECTOR2I{ aRect.m_Coords[2], aRect.m_Coords[3] } );
2215
2216 shape->SetStart( cornerA );
2217 shape->SetEnd( cornerB );
2218
2219 const EDA_ANGLE angle{ static_cast<double>( aRect.m_Rotation ) / 1000.0, DEGREES_T };
2220 shape->Rotate( cornerA, angle );
2221
2222 const int lineWidth = 0;
2223 shape->SetWidth( lineWidth );
2224
2225 return shape;
2226}
2227
2228
2229std::unique_ptr<PCB_SHAPE> BOARD_BUILDER::buildRect( const BLK_0x0E_RECT& aRect, BOARD_ITEM_CONTAINER& aParent )
2230{
2231 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent );
2232
2233 PCB_LAYER_ID layer = getLayer( aRect.m_Layer );
2234 shape->SetLayer( layer );
2235
2236 shape->SetShape( SHAPE_T::RECTANGLE );
2237
2238 const VECTOR2I cornerA = scale( VECTOR2I{ aRect.m_Coords[0], aRect.m_Coords[1] } );
2239 const VECTOR2I cornerB = scale( VECTOR2I{ aRect.m_Coords[2], aRect.m_Coords[3] } );
2240
2241 shape->SetStart( cornerA );
2242 shape->SetEnd( cornerB );
2243
2244 const EDA_ANGLE angle{ static_cast<double>( aRect.m_Rotation ) / 1000.0, DEGREES_T };
2245 shape->Rotate( cornerA, angle );
2246
2247 const int lineWidth = 0;
2248 shape->SetWidth( lineWidth );
2249
2250 return shape;
2251}
2252
2253
2254std::unique_ptr<PCB_SHAPE> BOARD_BUILDER::buildPolygon( const BLK_0x28_SHAPE& aPolygon, BOARD_ITEM_CONTAINER& aParent )
2255{
2256 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &aParent );
2257
2258 PCB_LAYER_ID layer = getLayer( aPolygon.m_Layer );
2259 shape->SetLayer( layer );
2260
2261 shape->SetShape( SHAPE_T::POLY );
2262
2263 SHAPE_LINE_CHAIN chain = buildOutline( aPolygon );
2264
2265 if( chain.PointCount() < 3 )
2266 {
2267 wxLogTrace( traceAllegroBuilder, "Polygon (0x28) with key %#010x has fewer than 3 points, skipping",
2268 aPolygon.m_Key );
2269 return nullptr;
2270 }
2271
2272 chain.SetClosed( true );
2273 shape->SetPolyShape( chain );
2274
2275 const int lineWidth = 0;
2276 shape->SetWidth( lineWidth );
2277
2278 return shape;
2279}
2280
2281
2282std::vector<std::unique_ptr<PCB_SHAPE>> BOARD_BUILDER::buildPolygonShapes( const BLK_0x28_SHAPE& aShapeData,
2283 BOARD_ITEM_CONTAINER& aParent )
2284{
2285 std::vector<std::unique_ptr<PCB_SHAPE>> shapes;
2286
2287 PCB_LAYER_ID layer = getLayer( aShapeData.m_Layer );
2288
2289 // Walk the segments in this shape and create PCB_SHAPE objects on Edge_Cuts
2290 const LL_WALKER segWalker{ aShapeData.m_FirstSegmentPtr, aShapeData.m_Key, m_brdDb };
2291
2292 for( const BLOCK_BASE* segBlock : segWalker )
2293 {
2294 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( &m_board );
2295 shape->SetLayer( layer );
2296 shape->SetWidth( m_board.GetDesignSettings().GetLineThickness( layer ) );
2297
2298 switch( segBlock->GetBlockType() )
2299 {
2300 case 0x01:
2301 {
2302 const auto& arc = static_cast<const BLOCK<BLK_0x01_ARC>&>( *segBlock ).GetData();
2303
2304 VECTOR2I start = scale( { arc.m_StartX, arc.m_StartY } );
2305 VECTOR2I end = scale( { arc.m_EndX, arc.m_EndY } );
2306 VECTOR2I c = scale( KiROUND( VECTOR2D{ arc.m_CenterX, arc.m_CenterY } ) );
2307
2308 int radius = scale( arc.m_Radius );
2309 if( start == end )
2310 {
2311 shape->SetShape( SHAPE_T::CIRCLE );
2312 shape->SetCenter( c );
2313 shape->SetRadius( radius );
2314 }
2315 else
2316 {
2317 shape->SetShape( SHAPE_T::ARC );
2318
2319 bool clockwise = ( arc.m_SubType & 0x40 ) != 0;
2320
2321 EDA_ANGLE startangle( start - c );
2322 EDA_ANGLE endangle( end - c );
2323
2324 startangle.Normalize();
2325 endangle.Normalize();
2326
2327 EDA_ANGLE angle = endangle - startangle;
2328
2329 if( clockwise && angle < ANGLE_0 )
2330 angle += ANGLE_360;
2331
2332 if( !clockwise && angle > ANGLE_0 )
2333 angle -= ANGLE_360;
2334
2335 VECTOR2I mid = start;
2336 RotatePoint( mid, c, -angle / 2.0 );
2337
2338 shape->SetArcGeometry( start, mid, end );
2339 }
2340 break;
2341 }
2342 case 0x15:
2343 case 0x16:
2344 case 0x17:
2345 {
2346 const auto& seg = static_cast<const BLOCK<BLK_0x15_16_17_SEGMENT>&>( *segBlock ).GetData();
2347 VECTOR2I start = scale( { seg.m_StartX, seg.m_StartY } );
2348 VECTOR2I end = scale( { seg.m_EndX, seg.m_EndY } );
2349
2350 shape->SetShape( SHAPE_T::SEGMENT );
2351 shape->SetStart( start );
2352 shape->SetEnd( end );
2353 shape->SetWidth( m_board.GetDesignSettings().GetLineThickness( layer ) );
2354 break;
2355 }
2356 default:
2357 wxLogTrace( traceAllegroBuilder, " Unhandled segment type in outline: %#04x", segBlock->GetBlockType() );
2358 continue;
2359 }
2360
2361 shapes.push_back( std::move( shape ) );
2362 }
2363
2364 return shapes;
2365}
2366
2367
2369{
2370 uint32_t refKey = 0x00;
2371
2372 if( aFpInstance.m_InstRef.has_value() )
2373 refKey = aFpInstance.m_InstRef.value();
2374
2375 if( !refKey && aFpInstance.m_InstRef16x.has_value() )
2376 refKey = aFpInstance.m_InstRef16x.value();
2377
2378 // This can happen, for example for dimension "symbols".
2379 if( refKey == 0 )
2380 return nullptr;
2381
2383 return blk07;
2384}
2385
2386
2387std::vector<std::unique_ptr<BOARD_ITEM>> BOARD_BUILDER::buildPadItems( const BLK_0x1C_PADSTACK& aPadstack,
2388 FOOTPRINT& aFp, const wxString& aPadName, int aNetcode )
2389{
2390 // Not all Allegro PADSTACKS can be represented by a single KiCad pad. For example, the
2391 // paste and mask layers can have completely independent shapes in Allegro, but in KiCad that
2392 // would require a separate aperture pad.
2393 // Also if there are multiple drills, we will need to make a pad for each
2394 std::vector<std::unique_ptr<BOARD_ITEM>> padItems;
2395
2396 std::vector<std::unique_ptr<PADSTACK::COPPER_LAYER_PROPS>> copperLayers( aPadstack.m_LayerCount );
2397
2398 // Thermal relief gap from antipad/pad size difference on the first layer that has both.
2399 std::optional<int> thermalGap;
2400
2401 const wxString& padStackName = m_brdDb.GetString( aPadstack.m_PadStr );
2402
2403 wxLogTrace( traceAllegroBuilder, "Building pad '%s' with %u layers", padStackName, aPadstack.m_LayerCount );
2404
2405 // First, gather all the copper layers into a set of shape props, which we can then use to decide on the padstack mode
2406 for( size_t i = 0; i < aPadstack.m_LayerCount; ++i )
2407 {
2408 const size_t layerBaseIndex = aPadstack.m_NumFixedCompEntries + i * aPadstack.m_NumCompsPerLayer;
2409 const ALLEGRO::PADSTACK_COMPONENT& padComp = aPadstack.m_Components[layerBaseIndex + BLK_0x1C_PADSTACK::LAYER_COMP_SLOT::PAD];
2410 const ALLEGRO::PADSTACK_COMPONENT& antiPadComp = aPadstack.m_Components[layerBaseIndex + BLK_0x1C_PADSTACK::LAYER_COMP_SLOT::ANTIPAD];
2412
2413 // If this is zero just skip entirely - I don't think we can usefully make pads with just thermal relief
2414 // Flag up if that happens.
2416 {
2417 if( antiPadComp.m_Type != PADSTACK_COMPONENT::TYPE_NULL )
2418 {
2419 m_reporter.Report(
2420 wxString::Format( "Padstack %s: Copper layer %zu has no pad component, but has antipad",
2421 padStackName, i ),
2423 }
2424 if( thermalComp.m_Type != PADSTACK_COMPONENT::TYPE_NULL )
2425 {
2426 m_reporter.Report(
2427 wxString::Format( "Copper layer %zu has no pad component, but has thermal relief", i ),
2429 }
2430 continue;
2431 }
2432
2433 auto& layerCuProps = copperLayers[i];
2434 wxLogTrace( traceAllegroBuilder, " Adding copper layer %zu with pad type %d", i, (int) padComp.m_Type );
2435 layerCuProps = std::make_unique<PADSTACK::COPPER_LAYER_PROPS>();
2436
2437 switch( padComp.m_Type )
2438 {
2440 layerCuProps->shape.shape = PAD_SHAPE::RECTANGLE;
2441 layerCuProps->shape.size = scaleSize( VECTOR2I{ padComp.m_W, padComp.m_H } );
2442 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2443 break;
2445 layerCuProps->shape.shape = PAD_SHAPE::RECTANGLE;
2446 layerCuProps->shape.size = scaleSize( VECTOR2I{ padComp.m_W, padComp.m_W } );
2447 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2448 break;
2450 layerCuProps->shape.shape = PAD_SHAPE::CIRCLE;
2451 layerCuProps->shape.size = scaleSize( VECTOR2I{ padComp.m_W, padComp.m_H } );
2452 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2453 break;
2456 layerCuProps->shape.shape = PAD_SHAPE::OVAL;
2457 layerCuProps->shape.size = scaleSize( VECTOR2I{ padComp.m_W, padComp.m_H } );
2458 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2459 break;
2461 {
2462 layerCuProps->shape.shape = PAD_SHAPE::ROUNDRECT;
2463 layerCuProps->shape.size = scaleSize( VECTOR2I{ padComp.m_W, padComp.m_H } );
2464 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2465
2466 int minDim = std::min( std::abs( padComp.m_W ), std::abs( padComp.m_H ) );
2467
2468 if( padComp.m_Z1.has_value() && padComp.m_Z1.value() > 0 && minDim > 0 )
2469 layerCuProps->shape.round_rect_radius_ratio = padComp.m_Z1.value() / (double) minDim;
2470 else
2471 layerCuProps->shape.round_rect_radius_ratio = 0.25;
2472
2473 break;
2474 }
2476 {
2477 layerCuProps->shape.shape = PAD_SHAPE::CHAMFERED_RECT;
2478 layerCuProps->shape.size = scaleSize( VECTOR2I{ padComp.m_W, padComp.m_H } );
2479 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2480
2481 int minDim = std::min( std::abs( padComp.m_W ), std::abs( padComp.m_H ) );
2482
2483 if( padComp.m_Z1.has_value() && padComp.m_Z1.value() > 0 && minDim > 0 )
2484 layerCuProps->shape.chamfered_rect_ratio = padComp.m_Z1.value() / (double) minDim;
2485 else
2486 layerCuProps->shape.chamfered_rect_ratio = 0.25;
2487
2488 layerCuProps->shape.chamfered_rect_positions = RECT_CHAMFER_ALL;
2489 break;
2490 }
2492 {
2493 // Approximate octagon as a round rectangle with ~29.3% corner radius
2494 // (tan(22.5°) ≈ 0.414, half of that as ratio ≈ 0.207, but visually 0.293 is closer)
2495 layerCuProps->shape.shape = PAD_SHAPE::CHAMFERED_RECT;
2496 layerCuProps->shape.size = scaleSize( VECTOR2I{ padComp.m_W, padComp.m_H } );
2497 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2498 layerCuProps->shape.chamfered_rect_ratio = 1.0 - 1.0 / sqrt( 2.0 );
2499 layerCuProps->shape.chamfered_rect_positions = RECT_CHAMFER_ALL;
2500 break;
2501 }
2503 {
2504 // Custom shape defined by a 0x28 polygon. Walk the shape's segments and build
2505 // a polygon primitive for this pad.
2506 const BLK_0x28_SHAPE* shapeData =
2508
2509 if( !shapeData )
2510 {
2511 wxLogTrace( traceAllegroBuilder,
2512 "Padstack %s: SHAPE_SYMBOL on layer %zu has no 0x28 shape at %#010x",
2513 padStackName, i, padComp.m_StrPtr );
2514 break;
2515 }
2516
2518
2519 if( outline.PointCount() >= 3 )
2520 {
2521 outline.SetClosed( true );
2522
2523 layerCuProps->shape.shape = PAD_SHAPE::CUSTOM;
2524 layerCuProps->shape.anchor_shape = PAD_SHAPE::CIRCLE;
2525
2526 // Anchor size based on the shape's bounding box center
2527 BOX2I bbox = outline.BBox();
2528 int anchorSize = static_cast<int>(
2529 std::min( bbox.GetWidth(), bbox.GetHeight() ) / 4 );
2530
2531 if( anchorSize < 1 )
2532 anchorSize = 1;
2533
2534 layerCuProps->shape.size = VECTOR2I( anchorSize, anchorSize );
2535
2536 auto poly = std::make_shared<PCB_SHAPE>( nullptr, SHAPE_T::POLY );
2537 poly->SetPolyShape( SHAPE_POLY_SET( outline ) );
2538 poly->SetFilled( true );
2539 poly->SetWidth( 0 );
2540 layerCuProps->custom_shapes.push_back( poly );
2541 }
2542 else
2543 {
2544 wxLogTrace( traceAllegroBuilder,
2545 "Padstack %s: SHAPE_SYMBOL on layer %zu produced only %d points",
2546 padStackName, i, outline.PointCount() );
2547 }
2548
2549 break;
2550 }
2552 {
2553 layerCuProps->shape.shape = PAD_SHAPE::CUSTOM;
2554 layerCuProps->shape.anchor_shape = PAD_SHAPE::CIRCLE;
2555 layerCuProps->shape.offset = scale( VECTOR2I{ padComp.m_X3, padComp.m_X4 } );
2556
2557 const int w = std::max( padComp.m_W, 300 );
2558 const int h = std::max( padComp.m_H, 220 );
2559
2560 SHAPE_LINE_CHAIN outline;
2561 auto S = [&]( int x, int y )
2562 {
2563 return scale( VECTOR2I{ x, y } );
2564 };
2565
2566 // Regular pentagon with flat bottom edge
2567 outline.Append( S( 0, -h / 2 ) );
2568 outline.Append( S( w / 2, -h / 6 ) );
2569 outline.Append( S( w / 3, h / 2 ) );
2570 outline.Append( S( -w / 3, h / 2 ) );
2571 outline.Append( S( -w / 2, -h / 6 ) );
2572 outline.SetClosed( true );
2573
2574 BOX2I bbox = outline.BBox();
2575 int anchorSize = static_cast<int>(
2576 std::min( bbox.GetWidth(), bbox.GetHeight() ) / 7 );
2577
2578 if( anchorSize < 1 )
2579 anchorSize = 1;
2580
2581 layerCuProps->shape.size = VECTOR2I( anchorSize, anchorSize );
2582
2583 auto poly = std::make_shared<PCB_SHAPE>( nullptr, SHAPE_T::POLY );
2584 poly->SetPolyShape( SHAPE_POLY_SET( outline ) );
2585 poly->SetFilled( true );
2586 poly->SetWidth( 0 );
2587 layerCuProps->custom_shapes.push_back( poly );
2588 break;
2589 }
2590 default:
2591 m_reporter.Report(
2592 wxString::Format( "Padstack %s: unhandled copper pad shape type %d on layer %zu",
2593 padStackName, static_cast<int>( padComp.m_Type ), i ),
2595 break;
2596 }
2597
2598 if( antiPadComp.m_Type != PADSTACK_COMPONENT::TYPE_NULL)
2599 {
2600 if( antiPadComp.m_Type != padComp.m_Type )
2601 {
2602 wxLogTrace( traceAllegroBuilder, "Padstack %s: copper layer %zu antipad shape %d "
2603 "differs from pad shape %d",
2604 padStackName, i, antiPadComp.m_Type, padComp.m_Type );
2605 }
2606
2607 int clearanceX = scale( ( antiPadComp.m_W - padComp.m_W ) / 2 );
2608 int clearanceY = scale( ( antiPadComp.m_H - padComp.m_H ) / 2 );
2609
2610 if( clearanceX && clearanceX != clearanceY )
2611 {
2612 wxLogTrace( traceAllegroBuilder, "Padstack %s: copper layer %zu unequal antipad "
2613 "clearance X=%d Y=%d",
2614 padStackName, i, clearanceX, clearanceY );
2615 }
2616
2617 if( antiPadComp.m_X3 != 0 || antiPadComp.m_X4 != 0 )
2618 {
2619 wxLogTrace( traceAllegroBuilder, "Padstack %s: copper layer %zu antipad offset "
2620 "%d, %d",
2621 padStackName, i, antiPadComp.m_X3, antiPadComp.m_X4 );
2622 }
2623
2624 layerCuProps->clearance = clearanceX;
2625 }
2626
2627 if( thermalComp.m_Type != PADSTACK_COMPONENT::TYPE_NULL && !thermalGap.has_value() )
2628 {
2629 // The thermal gap is the clearance between the pad copper and the surrounding zone.
2630 // We derive it from the antipad-to-pad size difference.
2631 if( antiPadComp.m_Type != PADSTACK_COMPONENT::TYPE_NULL && padComp.m_W > 0 )
2632 {
2633 int gap = scale( ( antiPadComp.m_W - padComp.m_W ) / 2 );
2634
2635 if( gap > 0 )
2636 thermalGap = gap;
2637 }
2638
2639 wxLogTrace( traceAllegroBuilder,
2640 "Padstack %s: thermal relief type=%d, gap=%snm",
2641 padStackName, thermalComp.m_Type,
2642 thermalGap.has_value() ? wxString::Format( "%d", thermalGap.value() )
2643 : wxString( "N/A" ) );
2644 }
2645
2646 // Padstack-level keepouts (antipad relief geometry) are handled via the
2647 // antipad slot in copperLayers above. Board-level keepouts use BLK_0x34.
2648 }
2649
2650 // We have now constructed a list of copper props. We can determine the PADSTACK mode now and assign the shapes
2651 PADSTACK padStack( &aFp );
2652
2653 if( copperLayers.size() == 0 )
2654 {
2655 // SMD aperture PAD or something?
2656 }
2657 else
2658 {
2659 const auto layersEqual = [&](size_t aFrom, size_t aTo) -> bool
2660 {
2661 bool eq = true;
2662 for( size_t i = aFrom + 1; i < aTo; ++i )
2663 {
2664 if( !copperLayers[i - 1] || !copperLayers[i] || *copperLayers[i - 1] != *copperLayers[i] )
2665 {
2666 eq = false;
2667 break;
2668 }
2669 }
2670 return eq;
2671 };
2672
2673 for(size_t i = 0; i < copperLayers.size(); ++i )
2674 {
2675 wxLogTrace( traceAllegroBuilder, " Layer %zu: %s", i, copperLayers[i] ? "present" : "null" );
2676 }
2677
2678 padStack.SetLayerSet( PAD::PTHMask() );
2679
2680 if( copperLayers.front() && copperLayers.back() && layersEqual( 0, copperLayers.size() ) )
2681 {
2682 wxLogTrace( traceAllegroBuilder, " Using NORMAL padstack mode (all layers identical)" );
2683 padStack.SetMode( PADSTACK::MODE::NORMAL );
2684 PADSTACK::COPPER_LAYER_PROPS& layerProps = padStack.CopperLayer( F_Cu );
2685 layerProps = *copperLayers.front();
2686 }
2687 else if( copperLayers.front() && copperLayers.back()
2688 && layersEqual( 1, copperLayers.size() - 1 ) )
2689 {
2690 wxLogTrace( traceAllegroBuilder, " Using FRONT_INNER_BACK padstack mode (inner layers identical)" );
2692 padStack.CopperLayer( F_Cu ) = *copperLayers.front();
2693 padStack.CopperLayer( B_Cu ) = *copperLayers.back();
2694
2695 // May be B_Cu if layers = 2, but that's OK
2696 if( copperLayers.size() > 2 && copperLayers[1] )
2697 padStack.CopperLayer( In1_Cu ) = *copperLayers[1];
2698 }
2699 else
2700 {
2701 wxLogTrace( traceAllegroBuilder, " Using CUSTOM padstack mode (layers differ)" );
2702 padStack.SetMode( PADSTACK::MODE::CUSTOM );
2703
2704 for( size_t i = 0; i < copperLayers.size(); ++i )
2705 {
2706 if( !copperLayers[i] )
2707 continue;
2708
2709 PCB_LAYER_ID layer = F_Cu;
2710
2711 if( i == 0 )
2712 layer = F_Cu;
2713 else if( i == copperLayers.size() - 1 )
2714 layer = B_Cu;
2715 else
2716 layer = ToLAYER_ID( In1_Cu + static_cast<int>( i - 1 ) * 2 );
2717
2718 padStack.CopperLayer( layer ) = *copperLayers[i];
2719 }
2720 }
2721 }
2722
2723 // The drill/slot dimensions are extracted in priority order:
2724 //
2725 // 1. V172+ m_SlotAndUnknownArr[0] and [3] hold the true slot outline dimensions (X and Y).
2726 // For routed slots (round drill bit + routing path), m_DrillArr only has the bit diameter,
2727 // while m_SlotAndUnknownArr has the full slot envelope.
2728 //
2729 // 2. V172+ m_DrillArr[4] (width) and m_DrillArr[7] (height, 0 for round).
2730 // These match m_SlotAndUnknownArr for punched oblong drills but only have the bit
2731 // diameter for routed slots.
2732 //
2733 // 3. Pre-V172 m_Drill field (drill diameter, always round).
2734 int drillW = 0;
2735 int drillH = 0;
2736
2737 if( m_brdDb.m_FmtVer >= FMT_VER::V_172 )
2738 {
2739 if( aPadstack.m_SlotAndUnknownArr.has_value() )
2740 {
2741 const auto& slotArr = aPadstack.m_SlotAndUnknownArr.value();
2742 int slotX = scale( static_cast<int>( slotArr[0] ) );
2743 int slotY = scale( static_cast<int>( slotArr[3] ) );
2744
2745 if( slotX > 0 && slotY > 0 )
2746 {
2747 drillW = slotX;
2748 drillH = slotY;
2749 }
2750 }
2751
2752 if( drillW == 0 )
2753 {
2754 drillW = scale( static_cast<int>( aPadstack.m_DrillArr[4] ) );
2755 drillH = scale( static_cast<int>( aPadstack.m_DrillArr[7] ) );
2756 }
2757 }
2758 else
2759 {
2760 drillW = scale( static_cast<int>( aPadstack.m_Drill ) );
2761 }
2762
2763 if( drillH == 0 )
2764 drillH = drillW;
2765
2766 // Allegro stores slot dimensions as (primary, secondary) regardless of orientation,
2767 // not as (X, Y). Compare the first copper layer pad's aspect ratio to determine if
2768 // the drill needs to be rotated 90 degrees.
2769 if( drillW != drillH && aPadstack.m_LayerCount > 0 )
2770 {
2771 size_t firstCopperIdx = aPadstack.m_NumFixedCompEntries;
2772 const ALLEGRO::PADSTACK_COMPONENT& firstPadComp =
2773 aPadstack.m_Components[firstCopperIdx + BLK_0x1C_PADSTACK::LAYER_COMP_SLOT::PAD];
2774
2775 bool padIsTaller = ( std::abs( firstPadComp.m_H ) > std::abs( firstPadComp.m_W ) );
2776 bool drillIsTaller = ( drillH > drillW );
2777
2778 if( padIsTaller != drillIsTaller )
2779 std::swap( drillW, drillH );
2780 }
2781
2782 bool isSmd = ( drillW == 0 ) || ( aPadstack.m_LayerCount == 1 );
2783
2784 if( isSmd )
2785 {
2786 padStack.Drill().size = VECTOR2I( 0, 0 );
2787 }
2788 else
2789 {
2790 padStack.Drill().size = VECTOR2I( drillW, drillH );
2791
2792 if( drillW != drillH )
2793 padStack.Drill().shape = PAD_DRILL_SHAPE::OBLONG;
2794 else
2795 padStack.Drill().shape = PAD_DRILL_SHAPE::CIRCLE;
2796 }
2797
2798 std::unique_ptr<PAD> pad = std::make_unique<PAD>( &aFp );
2799 pad->SetPadstack( padStack );
2800 pad->SetNumber( aPadName );
2801 pad->SetNetCode( aNetcode );
2802
2803 if( isSmd )
2804 {
2805 pad->SetAttribute( PAD_ATTRIB::SMD );
2806 pad->SetLayerSet( PAD::SMDMask() );
2807 }
2808 else
2809 {
2810 pad->SetAttribute( PAD_ATTRIB::PTH );
2811 pad->SetLayerSet( PAD::PTHMask() );
2812 }
2813
2814 if( thermalGap.has_value() )
2815 pad->SetThermalGap( thermalGap.value() );
2816
2817 padItems.push_back( std::move( pad ) );
2818
2819 // Now, for each technical layer, we see if we can include it into the existing padstack, or if we need to add
2820 // it as a standalone pad
2821 for( size_t i = 0; i < aPadstack.m_NumFixedCompEntries; ++i )
2822 {
2823 const ALLEGRO::PADSTACK_COMPONENT& psComp = aPadstack.m_Components[i];
2824
2827 continue;
2828
2829 // All fixed slots are technical layers (solder mask, paste mask, film mask,
2830 // assembly variant, etc). Custom mask expansion extraction is not yet implemented;
2831 // KiCad's default pad-matches-mask behavior applies.
2832 wxLogTrace( traceAllegroBuilder,
2833 "Fixed padstack slot %zu: type=%d, W=%d, H=%d",
2834 i, static_cast<int>( psComp.m_Type ), psComp.m_W, psComp.m_H );
2835 }
2836
2837 return padItems;
2838}
2839
2840
2841std::unique_ptr<FOOTPRINT> BOARD_BUILDER::buildFootprint( const BLK_0x2D_FOOTPRINT_INST& aFpInstance )
2842{
2843 auto fp = std::make_unique<FOOTPRINT>( &m_board );
2844
2845 const BLK_0x07_COMPONENT_INST* fpInstData = getFpInstRef( aFpInstance );
2846
2847 wxLogTrace( traceAllegroBuilder, "Building footprint from 0x2D block key %#010x", aFpInstance.m_Key );
2848
2849 wxString refDesStr;
2850 if( fpInstData )
2851 {
2852 refDesStr = m_brdDb.GetString( fpInstData->m_RefDesStrPtr );
2853
2854 if( refDesStr.IsEmpty() )
2855 {
2856 // Does this happen even when there's an 0x07 block?
2857 m_reporter.Report( wxString::Format( "Empty ref des for 0x2D key %#010x", aFpInstance.m_Key ),
2859 }
2860 }
2861
2862 // We may update the PCB_FIELD layer if it's specified explicitly (e.g. with font size and so on),
2863 // but if not, set the refdes at least, but make it invisible
2864 fp->SetReference( refDesStr );
2865 fp->GetField( FIELD_T::REFERENCE )->SetVisible( false );
2866
2867 wxLogTrace( traceAllegroBuilder, " Footprint reference: '%s'", refDesStr );
2868
2869 const VECTOR2I fpPos = scale( VECTOR2I{ aFpInstance.m_CoordX, aFpInstance.m_CoordY } );
2870 const EDA_ANGLE rotation{ aFpInstance.m_Rotation / 1000., DEGREES_T };
2871
2872 fp->SetPosition( fpPos );
2873 fp->SetOrientation( rotation );
2874
2875 // Allegro stores placed instance data in board-absolute form: bottom-side
2876 // components already have shapes on bottom layers with bottom-side positions.
2877 // Allegro stores placed footprints in board-absolute form with final layers.
2878 // KiCad stores footprints in canonical front-side form and uses Flip() to
2879 // mirror both positions and layers to the back side.
2880 //
2881 // Move back-layer items to their front-side counterpart so that fp->Flip()
2882 // consistently mirrors positions AND layers for all children. Without this,
2883 // bottom-side footprints would have their back-layer graphics double-flipped
2884 // to the front.
2885 const auto canonicalizeLayer = []( BOARD_ITEM* aItem )
2886 {
2887 if( IsBackLayer( aItem->GetLayer() ) )
2888 aItem->SetLayer( FlipLayer( aItem->GetLayer() ) );
2889 };
2890
2891 const LL_WALKER graphicsWalker{ aFpInstance.m_GraphicPtr, aFpInstance.m_Key, m_brdDb };
2892
2893 for( const BLOCK_BASE* graphicsBlock : graphicsWalker )
2894 {
2895 const uint8_t type = graphicsBlock->GetBlockType();
2896
2897 if( type == 0x14 )
2898 {
2899 const auto& graphics = static_cast<const BLOCK<BLK_0x14_GRAPHIC>&>( *graphicsBlock ).GetData();
2900
2901 std::vector<std::unique_ptr<PCB_SHAPE>> shapes = buildShapes( graphics, *fp );
2902
2903 for( std::unique_ptr<PCB_SHAPE>& shape : shapes )
2904 {
2905 canonicalizeLayer( shape.get() );
2906 fp->Add( shape.release() );
2907 }
2908 }
2909 else
2910 {
2911 m_reporter.Report( wxString::Format( "Unexpected type in graphics list: %#04x", type ),
2913 }
2914 }
2915
2916 bool valueFieldSet = false;
2917
2918 const LL_WALKER textWalker{ aFpInstance.m_TextPtr, aFpInstance.m_Key, m_brdDb };
2919
2920 for( const BLOCK_BASE* textBlock : textWalker )
2921 {
2922 const uint8_t type = textBlock->GetBlockType();
2923
2924 if( type != 0x30 )
2925 continue;
2926
2927 const auto& strWrapper = static_cast<const BLOCK<BLK_0x30_STR_WRAPPER>&>( *textBlock ).GetData();
2928
2929 std::unique_ptr<PCB_TEXT> text = buildPcbText( strWrapper, *fp );
2930
2931 if( !text )
2932 continue;
2933
2934 canonicalizeLayer( text.get() );
2935
2936 const uint8_t textClass = strWrapper.m_Layer.m_Class;
2937 const uint8_t textSubclass = strWrapper.m_Layer.m_Subclass;
2938
2939 bool isSilk = ( textSubclass == LAYER_INFO::SUBCLASS::SILKSCREEN_TOP
2940 || textSubclass == LAYER_INFO::SUBCLASS::SILKSCREEN_BOTTOM );
2941 bool isAssembly = ( textSubclass == LAYER_INFO::SUBCLASS::ASSEMBLY_TOP
2942 || textSubclass == LAYER_INFO::SUBCLASS::ASSEMBLY_BOTTOM );
2943
2944 if( textClass == LAYER_INFO::CLASS::REF_DES && isSilk )
2945 {
2946 // Visible silkscreen refdes updates the built-in REFERENCE field.
2947 PCB_FIELD* const refDes = fp->GetField( FIELD_T::REFERENCE );
2948
2949 // KiCad netlisting requires non-digit + digit annotation.
2950 if( !text->GetText().IsEmpty() && !wxIsalpha( text->GetText()[0] ) )
2951 text->SetText( wxString( "UNK" ) + text->GetText() );
2952
2953 *refDes = PCB_FIELD( *text, FIELD_T::REFERENCE );
2954 }
2955 else if( textClass == LAYER_INFO::CLASS::REF_DES && isAssembly )
2956 {
2957 // Assembly refdes becomes a user field with the KiCad reference variable
2958 PCB_FIELD* field = new PCB_FIELD( *text, FIELD_T::USER, wxS( "Reference" ) );
2959 field->SetText( wxS( "${REFERENCE}" ) );
2960 field->SetVisible( false );
2961 fp->Add( field, ADD_MODE::APPEND );
2962 }
2963 else if( textClass == LAYER_INFO::CLASS::COMPONENT_VALUE && isAssembly )
2964 {
2965 if( !valueFieldSet )
2966 {
2967 // First COMPONENT_VALUE on assembly updates the built-in VALUE field
2968 PCB_FIELD* valField = fp->GetField( FIELD_T::VALUE );
2969 *valField = PCB_FIELD( *text, FIELD_T::VALUE );
2970 valField->SetVisible( false );
2971 valueFieldSet = true;
2972 }
2973 else
2974 {
2975 PCB_FIELD* field = new PCB_FIELD( *text, FIELD_T::USER, wxS( "Component Value" ) );
2976 field->SetVisible( false );
2977 fp->Add( field, ADD_MODE::APPEND );
2978 }
2979 }
2980 else if( textClass == LAYER_INFO::CLASS::DEVICE_TYPE )
2981 {
2982 PCB_FIELD* field = new PCB_FIELD( *text, FIELD_T::USER, wxS( "Device Type" ) );
2983 field->SetVisible( !isAssembly );
2984 fp->Add( field, ADD_MODE::APPEND );
2985 }
2986 else if( textClass == LAYER_INFO::CLASS::TOLERANCE )
2987 {
2988 PCB_FIELD* field = new PCB_FIELD( *text, FIELD_T::USER, wxS( "Tolerance" ) );
2989 field->SetVisible( isSilk );
2990 fp->Add( field, ADD_MODE::APPEND );
2991 }
2992 else if( textClass == LAYER_INFO::CLASS::USER_PART_NUMBER )
2993 {
2994 PCB_FIELD* field = new PCB_FIELD( *text, FIELD_T::USER, wxS( "User Part Number" ) );
2995 field->SetVisible( isSilk );
2996 fp->Add( field, ADD_MODE::APPEND );
2997 }
2998 else if( textClass == LAYER_INFO::CLASS::COMPONENT_VALUE && isSilk )
2999 {
3000 PCB_FIELD* field = new PCB_FIELD( *text, FIELD_T::USER, wxS( "Component Value" ) );
3001 field->SetVisible( true );
3002 fp->Add( field, ADD_MODE::APPEND );
3003 }
3004 else
3005 {
3006 fp->Add( text.release() );
3007 }
3008 }
3009
3010 // Assembly drawing
3011 LL_WALKER assemblyWalker{ aFpInstance.m_AssemblyPtr, aFpInstance.m_Key, m_brdDb };
3012
3013 for( const BLOCK_BASE* assemblyBlock : assemblyWalker )
3014 {
3015 std::vector<std::unique_ptr<BOARD_ITEM>> shapes = buildGraphicItems( *assemblyBlock, *fp );
3016
3017 for( std::unique_ptr<BOARD_ITEM>& item : shapes )
3018 {
3019 canonicalizeLayer( item.get() );
3020 fp->Add( item.release() );
3021 }
3022 }
3023
3024 // Areas (courtyards, etc)
3025 LL_WALKER areaWalker{ aFpInstance.m_AreasPtr, aFpInstance.m_Key, m_brdDb };
3026 for( const BLOCK_BASE* areaBlock : areaWalker )
3027 {
3028 std::vector<std::unique_ptr<BOARD_ITEM>> shapes = buildGraphicItems( *areaBlock, *fp );
3029
3030 for( std::unique_ptr<BOARD_ITEM>& item : shapes )
3031 {
3032 canonicalizeLayer( item.get() );
3033 fp->Add( item.release() );
3034 }
3035 }
3036
3037 // Find the pads
3038 LL_WALKER padWalker{ aFpInstance.m_FirstPadPtr, aFpInstance.m_Key, m_brdDb };
3040 for( const BLOCK_BASE* padBlock : padWalker )
3041 {
3042 const auto& placedPadInfo = static_cast<const BLOCK<BLK_0x32_PLACED_PAD>&>( *padBlock ).GetData();
3043
3044 const BLK_0x04_NET_ASSIGNMENT* netAssignment =
3045 expectBlockByKey<BLK_0x04_NET_ASSIGNMENT>( placedPadInfo.m_NetPtr, 0x04 );
3046 const BLK_0x0D_PAD* padInfo = expectBlockByKey<BLK_0x0D_PAD>( placedPadInfo.m_PadPtr, 0x0D );
3047
3048 if( !netAssignment || !padInfo )
3049 continue;
3050
3051 const BLK_0x1C_PADSTACK* padStack = expectBlockByKey<BLK_0x1C_PADSTACK>( padInfo->m_PadStack, 0x1C );
3052
3053 if( !padStack )
3054 continue;
3055
3056 auto netIt = m_netCache.find( netAssignment->m_Net );
3057 const int netCode = ( netIt != m_netCache.end() ) ? netIt->second->GetNetCode()
3059 const wxString padName = m_brdDb.GetString( padInfo->m_NameStrId );
3060
3061 // 0x0D coordinates and rotation are in the footprint's local (unrotated) space.
3062 // Use SetFPRelativePosition/Orientation to let KiCad handle the transform to
3063 // board-absolute coordinates (rotating by FP orientation and adding FP position).
3064 const VECTOR2I padLocalPos = scale( VECTOR2I{ padInfo->m_CoordsX, padInfo->m_CoordsY } );
3065 const EDA_ANGLE padLocalRot{ static_cast<double>( padInfo->m_Rotation ) / 1000.0, DEGREES_T };
3066
3067 std::vector<std::unique_ptr<BOARD_ITEM>> padItems = buildPadItems( *padStack, *fp, padName, netCode );
3068
3069 for( std::unique_ptr<BOARD_ITEM>& item : padItems )
3070 {
3071 if( item->Type() == PCB_PAD_T )
3072 {
3073 PAD* pad = static_cast<PAD*>( item.get() );
3074 pad->SetFPRelativeOrientation( padLocalRot );
3075 }
3076
3077 item->SetFPRelativePosition( padLocalPos );
3078 fp->Add( item.release() );
3079 }
3080 }
3081
3082 // Flip AFTER adding all children so that graphics, text, and pads all get
3083 // their layers and positions mirrored correctly for bottom-layer footprints.
3084 //
3085 // Allegro mirrors bottom-side components via X-mirror (flip around Y axis),
3086 // then rotates by R. KiCad's Flip(TOP_BOTTOM) is a Y-mirror which negates
3087 // the orientation. Since X-mirror = Y-mirror + Rotate(180), we need the
3088 // final orientation to be R+180. Flip negates what we set before it, so
3089 // set -(R+180) to get R+180 after negation.
3090 if( aFpInstance.m_Layer != 0 )
3091 {
3092 fp->SetOrientation( -rotation - ANGLE_180 );
3093 fp->Flip( fpPos, FLIP_DIRECTION::TOP_BOTTOM );
3094 }
3095
3096 return fp;
3097}
3098
3099std::vector<std::unique_ptr<BOARD_ITEM>> BOARD_BUILDER::buildTrack( const BLK_0x05_TRACK& aTrackBlock, int aNetCode )
3100{
3101 std::vector<std::unique_ptr<BOARD_ITEM>> items;
3102
3103 // Anti-etch tracks are thermal relief patterns generated by Allegro.
3104 // These are handled by pad-level thermal relief properties instead.
3105 if( aTrackBlock.m_Layer.m_Class == LAYER_INFO::CLASS::ANTI_ETCH )
3106 {
3107 wxLogTrace( traceAllegroBuilder, "Skipping ANTI_ETCH track (class=%#04x, subclass=%#04x)",
3108 aTrackBlock.m_Layer.m_Class, aTrackBlock.m_Layer.m_Subclass );
3109 return items;
3110 }
3111
3112 const PCB_LAYER_ID layer = getLayer( aTrackBlock.m_Layer );
3113
3114 LL_WALKER segWalker{ aTrackBlock.m_FirstSegPtr, aTrackBlock.m_Key, m_brdDb };
3115 for( const BLOCK_BASE* block : segWalker )
3116 {
3117 const uint8_t segType = block->GetBlockType();
3118
3119 switch( segType )
3120 {
3121 case 0x15:
3122 case 0x16:
3123 case 0x17:
3124 {
3125 const BLK_0x15_16_17_SEGMENT& segInfo =
3126 static_cast<const BLOCK<BLK_0x15_16_17_SEGMENT>&>( *block ).GetData();
3127
3128 VECTOR2I start{ segInfo.m_StartX, segInfo.m_StartY };
3129 VECTOR2I end{ segInfo.m_EndX, segInfo.m_EndY };
3130 int width = static_cast<int>( segInfo.m_Width );
3131
3132 std::unique_ptr<PCB_TRACK> seg = std::make_unique<PCB_TRACK>( &m_board );
3133
3134 seg->SetNetCode( aNetCode );
3135 seg->SetLayer( layer );
3136
3137 seg->SetStart( scale( start ) );
3138 seg->SetEnd( scale( end ) );
3139 seg->SetWidth( scale( width ) );
3140
3141 items.push_back( std::move( seg ) );
3142 break;
3143 }
3144 case 0x01:
3145 {
3146 const BLK_0x01_ARC& arcInfo = static_cast<const BLOCK<BLK_0x01_ARC>&>( *block ).GetData();
3147
3148 VECTOR2I start = scale( { arcInfo.m_StartX, arcInfo.m_StartY } );
3149 VECTOR2I end = scale( { arcInfo.m_EndX, arcInfo.m_EndY } );
3150 VECTOR2I c = scale( KiROUND( VECTOR2D{ arcInfo.m_CenterX, arcInfo.m_CenterY } ) );
3151 int width = scale( static_cast<int>( arcInfo.m_Width ) );
3152
3153 bool clockwise = ( arcInfo.m_SubType & 0x40 ) != 0;
3154
3155 EDA_ANGLE startAngle( start - c );
3156 EDA_ANGLE endAngle( end - c );
3157 startAngle.Normalize();
3158 endAngle.Normalize();
3159
3160 EDA_ANGLE angle = endAngle - startAngle;
3161
3162 if( clockwise && angle < ANGLE_0 )
3163 angle += ANGLE_360;
3164
3165 if( !clockwise && angle > ANGLE_0 )
3166 angle -= ANGLE_360;
3167
3168 VECTOR2I mid = start;
3169 RotatePoint( mid, c, -angle / 2.0 );
3170
3171 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( &m_board );
3172
3173 arc->SetNetCode( aNetCode );
3174 arc->SetLayer( layer );
3175
3176 arc->SetStart( start );
3177 arc->SetMid( mid );
3178 arc->SetEnd( end );
3179 arc->SetWidth( width );
3180
3181 items.push_back( std::move( arc ) );
3182 break;
3183 }
3184 default:
3185 wxLogTrace( traceAllegroBuilder, "Unhandled segment type in track: %#04x", segType );
3186 break;
3187 }
3188 }
3189 return items;
3190}
3191
3192
3193std::unique_ptr<BOARD_ITEM> BOARD_BUILDER::buildVia( const BLK_0x33_VIA& aViaData, int aNetCode )
3194{
3195 VECTOR2I viaPos{ aViaData.m_CoordsX, aViaData.m_CoordsY };
3196
3197 const BLK_0x1C_PADSTACK* viaPadstack = expectBlockByKey<BLK_0x1C_PADSTACK>( aViaData.m_Padstack, 0x1C );
3198
3199 if( !viaPadstack )
3200 return nullptr;
3201
3202 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( &m_board );
3203 via->SetPosition( scale( viaPos ) );
3204 via->SetNetCode( aNetCode );
3205
3206 via->SetTopLayer( F_Cu );
3207 via->SetBottomLayer( B_Cu );
3208
3209 // Extract via size from the first copper layer's pad component
3210 int viaWidth = 0;
3211
3212 if( viaPadstack->m_LayerCount > 0 )
3213 {
3214 const size_t layerBaseIndex = viaPadstack->m_NumFixedCompEntries;
3215 const ALLEGRO::PADSTACK_COMPONENT& padComp =
3216 viaPadstack->m_Components[layerBaseIndex + BLK_0x1C_PADSTACK::LAYER_COMP_SLOT::PAD];
3217
3218 if( padComp.m_Type != PADSTACK_COMPONENT::TYPE_NULL )
3219 {
3220 viaWidth = scale( padComp.m_W );
3221 }
3222 }
3223
3224 int viaDrill = 0;
3225
3226 if( m_brdDb.m_FmtVer >= FMT_VER::V_172 )
3227 viaDrill = scale( static_cast<int>( viaPadstack->m_DrillArr[4] ) );
3228 else
3229 viaDrill = scale( static_cast<int>( viaPadstack->m_Drill ) );
3230
3231 if( viaDrill == 0 )
3232 {
3233 viaDrill = viaWidth / 2;
3234 wxLogTrace( traceAllegroBuilder, "Via at (%d, %d): no drill in padstack, using fallback %d",
3235 aViaData.m_CoordsX, aViaData.m_CoordsY, viaDrill );
3236 }
3237
3238 if( viaWidth <= 0 )
3239 {
3240 wxLogTrace( traceAllegroBuilder, "Via at (%d, %d) has no valid pad component, using drill-based fallback",
3241 aViaData.m_CoordsX, aViaData.m_CoordsY );
3242 viaWidth = viaDrill * 2;
3243 }
3244
3245 via->SetWidth( F_Cu, viaWidth );
3246 via->SetDrill( viaDrill );
3247
3248 return via;
3249}
3250
3251
3253{
3254 wxLogTrace( traceAllegroBuilder, "Creating tracks, vias, and other routed items" );
3255
3256 std::vector<BOARD_ITEM*> newItems;
3257
3258 // We need to walk this list again - we could do this all in createNets, but this seems tidier.
3259 LL_WALKER netWalker{ m_brdDb.m_Header->m_LL_0x1B_Nets, m_brdDb };
3260 for( const BLOCK_BASE* block : netWalker )
3261 {
3262 const uint8_t type = block->GetBlockType();
3263 if( type != BLOCK_TYPE::x1B_NET )
3264 {
3265 reportUnexpectedBlockType( type, BLOCK_TYPE::x1B_NET, 0, block->GetOffset(), "Net" );
3266 continue;
3267 }
3268
3269 const auto& net = static_cast<const BLOCK<BLK_0x1B_NET>&>( *block ).GetData();
3270
3271 auto netIt = m_netCache.find( net.m_Key );
3272
3273 if( netIt == m_netCache.end() )
3274 continue;
3275
3276 const int netCode = netIt->second->GetNetCode();
3277
3278 LL_WALKER assignmentWalker{ net.m_Assignment, net.m_Key, m_brdDb };
3279 for( const BLOCK_BASE* assignBlock : assignmentWalker )
3280 {
3281 if( assignBlock->GetBlockType() != 0x04 )
3282 {
3283 reportUnexpectedBlockType( assignBlock->GetBlockType(), 0x04, 0, block->GetOffset(), "Net assignment" );
3284 continue;
3285 }
3286
3287 const auto& assign = static_cast<const BLOCK<BLK_0x04_NET_ASSIGNMENT>&>( *assignBlock ).GetData();
3288
3289 // Walk the 0x05/0x32/... list
3290 LL_WALKER connWalker{ assign.m_ConnItem, assign.m_Key, m_brdDb };
3291 for( const BLOCK_BASE* connItemBlock : connWalker )
3292 {
3293 const uint8_t connType = connItemBlock->GetBlockType();
3294
3295 // One connected item can be multiple KiCad objects, e.g.
3296 // 0x05 track -> list of segments/arcs
3297 std::vector<std::unique_ptr<BOARD_ITEM>> newItemList;
3298
3299 switch( connType )
3300 {
3301 // Track
3302 case 0x05:
3303 {
3304 const BLK_0x05_TRACK& trackData =
3305 static_cast<const BLOCK<BLK_0x05_TRACK>&>( *connItemBlock ).GetData();
3306 newItemList = buildTrack( trackData, netCode );
3307 break;
3308 }
3309 case 0x33:
3310 {
3311 const BLK_0x33_VIA& viaData = static_cast<const BLOCK<BLK_0x33_VIA>&>( *connItemBlock ).GetData();
3312 newItemList.push_back( buildVia( viaData, netCode ) );
3313 break;
3314 }
3315 case 0x32:
3316 {
3317 // This is a pad in a footprint - we don't need to handle this here, as we do all the footprint
3318 // pads, connected or not, in the footprint step.
3319 break;
3320 }
3321 case 0x28:
3322 {
3323 // 0x28 shapes on the net chain are computed copper fills.
3324 // Collect them for teardrop and polygon import.
3325 const BLK_0x28_SHAPE& fillShape =
3326 static_cast<const BLOCK<BLK_0x28_SHAPE>&>( *connItemBlock ).GetData();
3327
3328 PCB_LAYER_ID fillLayer = getLayer( fillShape.m_Layer );
3329
3330 if( fillLayer != UNDEFINED_LAYER )
3331 m_zoneFillShapes[fillShape.m_Key] = { &fillShape, netCode, fillLayer };
3332
3333 break;
3334 }
3335 case 0x2E:
3336 default:
3337 {
3338 wxLogTrace( traceAllegroBuilder, " Unhandled connected item code: %#04x",
3339 (int) connType );
3340 }
3341 }
3342
3343 for( std::unique_ptr<BOARD_ITEM>& newItem : newItemList )
3344 {
3345 newItems.push_back( newItem.get() );
3346 m_board.Add( newItem.release(), ADD_MODE::BULK_APPEND );
3347 }
3348 }
3349 }
3350 }
3351
3352 m_board.FinalizeBulkAdd( newItems );
3353
3354 wxLogTrace( traceAllegroBuilder, "Finished creating %zu track/via items", newItems.size() );
3355}
3356
3357
3359{
3360 wxLogTrace( traceAllegroBuilder, "Creating shapes" );
3361
3362 // Walk through LL_0x24_0x28 which contains rectangles (0x24) and shapes (0x28)
3363 const LL_WALKER shapeWalker( m_brdDb.m_Header->m_LL_0x24_0x28, m_brdDb );
3364 int blockCount = 0;
3365
3366 std::vector<std::unique_ptr<BOARD_ITEM>> newItems;
3367
3368 for( const BLOCK_BASE* block : shapeWalker )
3369 {
3370 blockCount++;
3371
3372 switch( block->GetBlockType() )
3373 {
3374 case 0x24:
3375 {
3376 const BLK_0x24_RECT& rectData = BlockDataAs<BLK_0x24_RECT>( *block );
3377
3378 // These are zones, we don't handle them here
3379 if( layerIsZone( rectData.m_Layer ) )
3380 continue;
3381
3382 std::unique_ptr<PCB_SHAPE> rectShape = buildRect( rectData, m_board );
3383 newItems.push_back( std::move( rectShape ) );
3384 break;
3385 }
3386 case 0x28:
3387 {
3388 const BLK_0x28_SHAPE& shapeData = BlockDataAs<BLK_0x28_SHAPE>( *block );
3389
3390 // These are zones, we don't handle them here
3391 if( layerIsZone( shapeData.m_Layer ) )
3392 continue;
3393
3394 std::vector<std::unique_ptr<PCB_SHAPE>> shapeItems = buildPolygonShapes( shapeData, m_board );
3395
3396 for( auto& shapeItem : shapeItems )
3397 newItems.push_back( std::move( shapeItem ) );
3398 break;
3399 }
3400 default:
3401 {
3402 wxLogTrace( traceAllegroBuilder, " Unhandled block type in outline walker: %#04x", block->GetBlockType() );
3403 break;
3404 }
3405 }
3406 }
3407
3408 wxLogTrace( traceAllegroBuilder, " Found %d shape blocks", blockCount, newItems.size() );
3409 blockCount = 0;
3410
3411 LL_WALKER outline2Walker( m_brdDb.m_Header->m_LL_Shapes, m_brdDb );
3412 for( const BLOCK_BASE* block : outline2Walker )
3413 {
3414 blockCount++;
3415
3416 // Skip boundary layer shapes - these are handled in the zones phase
3417 const auto shouldSkip = []( const LAYER_INFO& aLayer ) -> bool
3418 {
3419 return aLayer.m_Class == LAYER_INFO::CLASS::BOUNDARY;
3420 };
3421
3422 switch( block->GetBlockType() )
3423 {
3424 case 0x0E:
3425 {
3426 const BLK_0x0E_RECT& rectData = BlockDataAs<BLK_0x0E_RECT>( *block );
3427
3428 if( shouldSkip( rectData.m_Layer ) )
3429 continue;
3430
3431 std::unique_ptr<PCB_SHAPE> rectShape = buildRect( rectData, m_board );
3432 newItems.push_back( std::move( rectShape ) );
3433 break;
3434 }
3435 case 0x24:
3436 {
3437 const BLK_0x24_RECT& rectData = BlockDataAs<BLK_0x24_RECT>( *block );
3438
3439 if( shouldSkip( rectData.m_Layer ) )
3440 continue;
3441
3442 std::unique_ptr<PCB_SHAPE> rectShape = buildRect( rectData, m_board );
3443 newItems.push_back( std::move( rectShape ) );
3444 break;
3445 }
3446 case 0x28:
3447 {
3448 const BLK_0x28_SHAPE& shapeData = BlockDataAs<BLK_0x28_SHAPE>( *block );
3449
3450 if( shouldSkip( shapeData.m_Layer ) )
3451 continue;
3452
3453 std::vector<std::unique_ptr<PCB_SHAPE>> shapeItems = buildPolygonShapes( shapeData, m_board );
3454
3455 for( auto& shapeItem : shapeItems )
3456 newItems.push_back( std::move( shapeItem ) );
3457 break;
3458 }
3459 default:
3460 {
3461 wxLogTrace( traceAllegroBuilder, " Unhandled block type in outline walker: %#04x", block->GetBlockType() );
3462 break;
3463 }
3464 }
3465 }
3466
3467 wxLogTrace( traceAllegroBuilder, " Found %d outline items in m_LL_Shapes", blockCount );
3468 blockCount = 0;
3469
3470 LL_WALKER graphicContainerWalker( m_brdDb.m_Header->m_LL_0x14, m_brdDb );
3471 for( const BLOCK_BASE* block : graphicContainerWalker )
3472 {
3473 blockCount++;
3474
3475 switch( block->GetBlockType() )
3476 {
3477 case 0x14:
3478 {
3479 const auto& graphicContainer = BlockDataAs<BLK_0x14_GRAPHIC>( *block );
3480
3481 std::vector<std::unique_ptr<PCB_SHAPE>> graphicItems = buildShapes( graphicContainer, m_board );
3482
3483 for( auto& item : graphicItems )
3484 newItems.push_back( std::move( item ) );
3485 break;
3486 }
3487 default:
3488 {
3489 wxLogTrace( traceAllegroBuilder, " Unhandled block type in graphic container walker: %#04x",
3490 block->GetBlockType() );
3491 break;
3492 }
3493 }
3494 }
3495
3496 wxLogTrace( traceAllegroBuilder, " Found %d graphic container items", blockCount );
3497
3498 std::vector<BOARD_ITEM*> addedItems;
3499 for( std::unique_ptr<BOARD_ITEM>& item : newItems )
3500 {
3501 addedItems.push_back( item.get() );
3502 m_board.Add( item.release(), ADD_MODE::BULK_APPEND );
3503 }
3504
3505 m_board.FinalizeBulkAdd( addedItems );
3506
3507 wxLogTrace( traceAllegroBuilder, "Created %zu board shapes", addedItems.size() );
3508}
3509
3510
3511const SHAPE_LINE_CHAIN& BOARD_BUILDER::buildSegmentChain( uint32_t aStartKey ) const
3512{
3513 auto cacheIt = m_segChainCache.find( aStartKey );
3514
3515 if( cacheIt != m_segChainCache.end() )
3516 return cacheIt->second;
3517
3518 SHAPE_LINE_CHAIN& outline = m_segChainCache[aStartKey];
3519 uint32_t currentKey = aStartKey;
3520
3521 // Safety limit to prevent infinite loops on corrupt data
3522 static constexpr int MAX_CHAIN_LENGTH = 50000;
3523 int visited = 0;
3524
3525 while( currentKey != 0 && visited < MAX_CHAIN_LENGTH )
3526 {
3527 const BLOCK_BASE* block = m_brdDb.GetObjectByKey( currentKey );
3528
3529 if( !block )
3530 break;
3531
3532 visited++;
3533
3534 switch( block->GetBlockType() )
3535 {
3536 case 0x01:
3537 {
3538 const auto& arc = static_cast<const BLOCK<BLK_0x01_ARC>&>( *block ).GetData();
3539 VECTOR2I start = scale( { arc.m_StartX, arc.m_StartY } );
3540 VECTOR2I end = scale( { arc.m_EndX, arc.m_EndY } );
3541 VECTOR2I center = scale( KiROUND( VECTOR2D{ arc.m_CenterX, arc.m_CenterY } ) );
3542
3543 if( start == end )
3544 {
3545 SHAPE_ARC shapeArc( center, start, ANGLE_360 );
3546 outline.Append( shapeArc );
3547 }
3548 else
3549 {
3550 bool clockwise = ( arc.m_SubType & 0x40 ) != 0;
3551
3552 EDA_ANGLE startAngle( start - center );
3553 EDA_ANGLE endAngle( end - center );
3554 startAngle.Normalize();
3555 endAngle.Normalize();
3556
3557 EDA_ANGLE arcAngle = endAngle - startAngle;
3558
3559 if( clockwise && arcAngle < ANGLE_0 )
3560 arcAngle += ANGLE_360;
3561
3562 if( !clockwise && arcAngle > ANGLE_0 )
3563 arcAngle -= ANGLE_360;
3564
3565 VECTOR2I mid = start;
3566 RotatePoint( mid, center, -arcAngle / 2.0 );
3567
3568 SHAPE_ARC shapeArc( start, mid, end, 0 );
3569 outline.Append( shapeArc );
3570 }
3571
3572 currentKey = arc.m_Next;
3573 break;
3574 }
3575 case 0x15:
3576 case 0x16:
3577 case 0x17:
3578 {
3579 const auto& seg =
3580 static_cast<const BLOCK<BLK_0x15_16_17_SEGMENT>&>( *block ).GetData();
3581 VECTOR2I start = scale( { seg.m_StartX, seg.m_StartY } );
3582
3583 if( outline.PointCount() == 0 || outline.CLastPoint() != start )
3584 outline.Append( start );
3585
3586 VECTOR2I end = scale( { seg.m_EndX, seg.m_EndY } );
3587 outline.Append( end );
3588 currentKey = seg.m_Next;
3589 break;
3590 }
3591 default:
3592 currentKey = 0;
3593 break;
3594 }
3595 }
3596
3597 return outline;
3598}
3599
3600
3602{
3603 SHAPE_LINE_CHAIN outline;
3604
3605 VECTOR2I topLeft = scale( VECTOR2I{ aRect.m_Coords[0], aRect.m_Coords[1] } );
3606 VECTOR2I botRight = scale( VECTOR2I{ aRect.m_Coords[2], aRect.m_Coords[3] } );
3607 VECTOR2I topRight{ botRight.x, topLeft.y };
3608 VECTOR2I botLeft{ topLeft.x, botRight.y };
3609
3610 outline.Append( topLeft );
3611 outline.Append( topRight );
3612 outline.Append( botRight );
3613 outline.Append( botLeft );
3614
3615 return outline;
3616}
3617
3618
3620{
3621 SHAPE_LINE_CHAIN outline;
3622
3623 VECTOR2I topLeft = scale( VECTOR2I{ aRect.m_Coords[0], aRect.m_Coords[1] } );
3624 VECTOR2I botRight = scale( VECTOR2I{ aRect.m_Coords[2], aRect.m_Coords[3] } );
3625 VECTOR2I topRight{ botRight.x, topLeft.y };
3626 VECTOR2I botLeft{ topLeft.x, botRight.y };
3627
3628 outline.Append( topLeft );
3629 outline.Append( topRight );
3630 outline.Append( botRight );
3631 outline.Append( botLeft );
3632
3633 return outline;
3634}
3635
3636
3638{
3639 SHAPE_LINE_CHAIN outline;
3640 const LL_WALKER segWalker{ aShape.m_FirstSegmentPtr, aShape.m_Key, m_brdDb };
3641
3642 for( const BLOCK_BASE* segBlock : segWalker )
3643 {
3644 switch( segBlock->GetBlockType() )
3645 {
3646 case 0x01:
3647 {
3648 const auto& arc = static_cast<const BLOCK<BLK_0x01_ARC>&>( *segBlock ).GetData();
3649 VECTOR2I start = scale( { arc.m_StartX, arc.m_StartY } );
3650 VECTOR2I end = scale( { arc.m_EndX, arc.m_EndY } );
3651 VECTOR2I center = scale( KiROUND( VECTOR2D{ arc.m_CenterX, arc.m_CenterY } ) );
3652
3653 if( start == end )
3654 {
3655 SHAPE_ARC shapeArc( center, start, ANGLE_360 );
3656 outline.Append( shapeArc );
3657 }
3658 else
3659 {
3660 bool clockwise = ( arc.m_SubType & 0x40 ) != 0;
3661
3662 EDA_ANGLE startAngle( start - center );
3663 EDA_ANGLE endAngle( end - center );
3664 startAngle.Normalize();
3665 endAngle.Normalize();
3666
3667 EDA_ANGLE arcAngle = endAngle - startAngle;
3668
3669 if( clockwise && arcAngle < ANGLE_0 )
3670 arcAngle += ANGLE_360;
3671
3672 if( !clockwise && arcAngle > ANGLE_0 )
3673 arcAngle -= ANGLE_360;
3674
3675 VECTOR2I mid = start;
3676 RotatePoint( mid, center, -arcAngle / 2.0 );
3677
3678 SHAPE_ARC shapeArc( start, mid, end, 0 );
3679 outline.Append( shapeArc );
3680 }
3681
3682 break;
3683 }
3684 case 0x15:
3685 case 0x16:
3686 case 0x17:
3687 {
3688 const auto& seg = static_cast<const BLOCK<BLK_0x15_16_17_SEGMENT>&>( *segBlock ).GetData();
3689 VECTOR2I start = scale( { seg.m_StartX, seg.m_StartY } );
3690
3691 if( outline.PointCount() == 0 || outline.CLastPoint() != start )
3692 outline.Append( start );
3693
3694 VECTOR2I end = scale( { seg.m_EndX, seg.m_EndY } );
3695 outline.Append( end );
3696 break;
3697 }
3698 default:
3699 wxLogTrace( traceAllegroBuilder, " Unhandled segment type in shape outline: %#04x",
3700 segBlock->GetBlockType() );
3701 break;
3702 }
3703 }
3704
3705 return outline;
3706}
3707
3708
3710{
3711 SHAPE_POLY_SET polySet;
3713
3714 if( outline.PointCount() < 3 )
3715 {
3716 wxLogTrace( traceAllegroBuilder, " Not enough points for polygon (%d)", outline.PointCount() );
3717 return polySet;
3718 }
3719
3720 outline.SetClosed( true );
3721 polySet.AddOutline( outline );
3722
3723 // Walk 0x34 KEEPOUT chain from m_Ptr4 for holes
3724 uint32_t holeKey = aShape.m_FirstKeepoutPtr;
3725
3726 while( holeKey != 0 )
3727 {
3728 const BLOCK_BASE* holeBlock = m_brdDb.GetObjectByKey( holeKey );
3729
3730 if( !holeBlock || holeBlock->GetBlockType() != 0x34 )
3731 break;
3732
3733 const auto& keepout = static_cast<const BLOCK<BLK_0x34_KEEPOUT>&>( *holeBlock ).GetData();
3734
3735 SHAPE_LINE_CHAIN holeOutline = buildSegmentChain( keepout.m_FirstSegmentPtr );
3736
3737 if( holeOutline.PointCount() >= 3 )
3738 {
3739 holeOutline.SetClosed( true );
3740 polySet.AddHole( holeOutline );
3741 }
3742
3743 holeKey = keepout.m_Next;
3744 }
3745
3746 return polySet;
3747}
3748
3749
3751{
3752 SHAPE_POLY_SET polySet;
3753
3754 switch( aBlock.GetBlockType() )
3755 {
3756 case 0x0E:
3757 {
3758 const auto& rectData = BlockDataAs<BLK_0x0E_RECT>( aBlock );
3759
3760 SHAPE_LINE_CHAIN chain( buildOutline( rectData ) );
3761 chain.SetClosed( true );
3762
3763 polySet = SHAPE_POLY_SET( chain );
3764 break;
3765 }
3766 case 0x24:
3767 {
3768 const auto& rectData = BlockDataAs<BLK_0x24_RECT>( aBlock );
3769
3770 SHAPE_LINE_CHAIN chain( buildOutline( rectData ) );
3771 chain.SetClosed( true );
3772
3773 polySet = SHAPE_POLY_SET( chain );
3774 break;
3775 }
3776 case 0x28:
3777 {
3778 const auto& shapeData = BlockDataAs<BLK_0x28_SHAPE>( aBlock );
3779 polySet = shapeToPolySet( shapeData );
3780 break;
3781 }
3782 default:
3783 wxLogTrace( traceAllegroBuilder, " Unhandled block type in tryBuildZoneShape: %#04x", aBlock.GetBlockType() );
3784 }
3785
3786 return polySet;
3787}
3788
3789
3790static LSET getRuleAreaLayers( const LAYER_INFO& aLayerInfo, PCB_LAYER_ID aDefault )
3791{
3792 LSET layerSet{ aDefault };
3793
3794 switch( aLayerInfo.m_Class )
3795 {
3799 {
3800 switch( aLayerInfo.m_Subclass )
3801 {
3803 layerSet = LSET::AllCuMask();
3804 break;
3806 layerSet = LSET{ F_Cu };
3807 break;
3809 layerSet = LSET{ B_Cu };
3810 break;
3811 default:
3812 wxLogTrace( traceAllegroBuilder, " Unhandled keepout layer subclass %#02x, using default layers",
3813 aLayerInfo.m_Subclass );
3814 }
3815 break;
3816 }
3819 {
3820 // This can be ALL, but can it be anything else?
3822 layerSet = LSET::AllCuMask();
3823 else
3824 wxLogTrace( traceAllegroBuilder, " Unhandled keepin layer subclass %#02x, using default layers",
3825 aLayerInfo.m_Subclass );
3826 break;
3827 }
3828 default:
3829 wxLogTrace( traceAllegroBuilder, " Unhandled non-copper zone layer class %#02x, using default layers",
3830 aLayerInfo.m_Class );
3831 break;
3832 }
3833
3834 return layerSet;
3835}
3836
3837
3852{
3853public:
3864
3874 class COMPLEX_FIRST_FILL_TASK: public PRIORITY_THREAD_POOL_TASK<std::vector<FILL_INFO>>
3875 {
3876 public:
3877 COMPLEX_FIRST_FILL_TASK( bool aSimplify ) : m_simplify( aSimplify ) {}
3878
3879 private:
3880 int computePriorityKey( const FILL_INFO& a ) const override
3881 {
3882 return static_cast<int>( a.m_CombinedFill.TotalVertices() );
3883 }
3884
3885 size_t task( FILL_INFO& fillInfo ) override
3886 {
3887 SHAPE_POLY_SET finalFillPolys = *fillInfo.m_Zone->Outline();
3888
3889 finalFillPolys.ClearArcs();
3890 fillInfo.m_CombinedFill.ClearArcs();
3891
3892 // Intersect the zone outline with the combined fill that was assembled
3893 // from all the related objects.
3894 finalFillPolys.BooleanIntersection( fillInfo.m_CombinedFill );
3895 finalFillPolys.Fracture( m_simplify );
3896
3897 // This is already mutex-ed, so this is safe
3898 fillInfo.m_Zone->SetFilledPolysList( fillInfo.m_Layer, finalFillPolys );
3899 return 1;
3900 }
3901
3903 };
3904
3908 void ProcessPolygons( bool aSimplify )
3909 {
3910 PROF_TIMER timer( "Zone fill processing" );
3911
3912 COMPLEX_FIRST_FILL_TASK fillTask( aSimplify );
3913 fillTask.Execute( m_FillInfos );
3914
3915 wxLogTrace( traceAllegroPerf, wxT( " Intersected and fractured zone fills in %.3f ms" ), timer.msecs() ); // format:allow
3916 }
3917
3918 void QueuePolygonForZone( ZONE& aZone, SHAPE_POLY_SET aFilledArea, PCB_LAYER_ID aLayer )
3919 {
3920 m_FillInfos.emplace_back( &aZone, aLayer, std::move( aFilledArea ) );
3921 }
3922
3923private:
3924 std::vector<FILL_INFO> m_FillInfos;
3925};
3926
3927
3928std::unique_ptr<ZONE> BOARD_BUILDER::buildZone( const BLOCK_BASE& aBoundaryBlock,
3929 const std::vector<const BLOCK_BASE*>& aRelatedBlocks,
3930 ZONE_FILL_HANDLER& aZoneFillHandler )
3931{
3932 int netCode = NETINFO_LIST::UNCONNECTED;
3933 const LAYER_INFO layerInfo = expectLayerFromBlock( aBoundaryBlock );
3934
3935 bool isCopperZone = ( layerInfo.m_Class == LAYER_INFO::CLASS::ETCH
3936 || layerInfo.m_Class == LAYER_INFO::CLASS::BOUNDARY );
3937
3939
3940 if( isCopperZone )
3941 {
3942 // BOUNDARY shares the ETCH layer list, so resolve subclass via ETCH class
3943 if( layerInfo.m_Class == LAYER_INFO::CLASS::BOUNDARY )
3944 {
3945 LAYER_INFO etchLayer{};
3946 etchLayer.m_Class = LAYER_INFO::CLASS::ETCH;
3947 etchLayer.m_Subclass = layerInfo.m_Subclass;
3948 layer = getLayer( etchLayer );
3949 }
3950 else
3951 {
3952 layer = getLayer( layerInfo );
3953 }
3954 }
3955 else
3956 {
3957 layer = F_Cu;
3958 }
3959
3960 if( isCopperZone && layer == UNDEFINED_LAYER )
3961 {
3962 wxLogTrace( traceAllegroBuilder, " Skipping shape on layer %#02x:%#02x - unmapped copper layer",
3963 layerInfo.m_Class, layerInfo.m_Subclass );
3964 return nullptr;
3965 }
3966
3967 const SHAPE_POLY_SET zoneShape = tryBuildZoneShape( aBoundaryBlock );
3968
3969 if( zoneShape.OutlineCount() != 1 )
3970 {
3971 wxLogTrace( traceAllegroBuilder, " Skipping zone with type %#04x, key %#010x - failed to build outline",
3972 aBoundaryBlock.GetBlockType(), aBoundaryBlock.GetKey() );
3973 return nullptr;
3974 }
3975
3976 auto zone = std::make_unique<ZONE>( &m_board );
3977 zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
3978
3979 if( isCopperZone )
3980 {
3981 zone->SetLayer( layer );
3982 zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
3983 }
3984 else
3985 {
3986 LSET layerSet = getRuleAreaLayers( layerInfo, Cmts_User );
3987
3988 bool isRouteKeepout = ( layerInfo.m_Class == LAYER_INFO::CLASS::ROUTE_KEEPOUT );
3989 bool isViaKeepout = ( layerInfo.m_Class == LAYER_INFO::CLASS::VIA_KEEPOUT );
3990 bool isPackageKeepout = ( layerInfo.m_Class == LAYER_INFO::CLASS::PACKAGE_KEEPOUT );
3991 bool isRouteKeepin = ( layerInfo.m_Class == LAYER_INFO::CLASS::ROUTE_KEEPIN );
3992 bool isPackageKeepin = ( layerInfo.m_Class == LAYER_INFO::CLASS::PACKAGE_KEEPIN );
3993
3994 zone->SetIsRuleArea( true );
3995 zone->SetLayerSet( layerSet );
3996 zone->SetDoNotAllowTracks( isRouteKeepout );
3997 zone->SetDoNotAllowVias( isViaKeepout );
3998 zone->SetDoNotAllowZoneFills( isRouteKeepout || isViaKeepout );
3999 zone->SetDoNotAllowPads( false );
4000 zone->SetDoNotAllowFootprints( isPackageKeepout );
4001
4002 // Zones don't have native keepin functions, so we leave a note for the user here
4003 // Later, we could consider adding a custom DRC rule for this (or KiCad could add native keepin
4004 // zone support)
4005 if( isRouteKeepin )
4006 zone->SetZoneName( "Route Keepin" );
4007 else if( isPackageKeepin )
4008 zone->SetZoneName( "Package Keepin" );
4009 }
4010
4011 SHAPE_POLY_SET combinedFill;
4012
4013 for( const BLOCK_BASE* block : aRelatedBlocks )
4014 {
4015 if( !block )
4016 continue;
4017
4018 switch( block->GetBlockType() )
4019 {
4020 case 0x1B:
4021 {
4022 const auto it = m_netCache.find( block->GetKey() );
4023
4024 if( it != m_netCache.end() )
4025 {
4026 wxLogTrace( traceAllegroBuilder, " Resolved BOUNDARY %#010x -> net '%s' (code %d)",
4027 aBoundaryBlock.GetKey(), it->second->GetNetname(), it->second->GetNetCode() );
4028
4029 netCode = it->second->GetNetCode();
4030 }
4031 else
4032 {
4033 m_reporter.Report( wxString::Format( "Could not find net key %#010x in cache for BOUNDARY %#010x",
4034 block->GetKey(), aBoundaryBlock.GetKey() ),
4036 }
4037 break;
4038 }
4039 case 0x28:
4040 {
4041 const BLK_0x28_SHAPE& shapeData = static_cast<const BLOCK<BLK_0x28_SHAPE>&>( *block ).GetData();
4042
4043 SHAPE_POLY_SET fillPolySet = shapeToPolySet( shapeData );
4044 combinedFill.Append( fillPolySet );
4045 m_usedZoneFillShapes.emplace( block->GetKey() );
4046 break;
4047 }
4048 default: break;
4049 }
4050 }
4051
4052 // Set net code AFTER layer assignment. SetNetCode checks IsOnCopperLayer() and
4053 // forces net=0 if the zone isn't on a copper layer yet.
4054 zone->SetNetCode( netCode );
4055
4056 for( const SHAPE_LINE_CHAIN& chain : zoneShape.CPolygon( 0 ) )
4057 zone->AddPolygon( chain );
4058
4059 // Add zone fills
4060 if( isCopperZone && !combinedFill.IsEmpty() )
4061 {
4062 // We don't do this here, though it feels like we should. We collect the
4063 // information for batch processing later on (which is conceptually
4064 // easier to parallelise compared to this function). But there is room
4065 // for improvement by threading more of this work: shapeToPolySet
4066 // accounts for about 40% of the remaining single-threaded time in the
4067 // building process.
4068
4069 // combinedFill.ClearArcs();
4070 // zoneOutline.ClearArcs();
4071 // combinedFill.BooleanIntersection( zoneOutline );
4072 // zone->SetFilledPolysList( layer, combinedFill );
4073
4074 zone->SetIsFilled( true );
4075 zone->SetNeedRefill( false );
4076
4077 // Poke these relevant context in here for batch processing later on
4078 aZoneFillHandler.QueuePolygonForZone( *zone, std::move( combinedFill ), layer );
4079 }
4080
4081 return zone;
4082}
4083
4084
4085std::vector<const BLOCK_BASE*> BOARD_BUILDER::getShapeRelatedBlocks( const BLK_0x28_SHAPE& aShape ) const
4086{
4087 // Follow pointer chain: BOUNDARY.Ptr7 -> 0x2C TABLE -> Ptr1 -> 0x37 -> m_Ptrs
4088 std::vector<const BLOCK_BASE*> ret;
4089 uint32_t ptr7Key = 0;
4090
4091 if( aShape.m_Ptr7.has_value() )
4092 ptr7Key = aShape.m_Ptr7.value();
4093 else if( aShape.m_Ptr7_16x.has_value() )
4094 ptr7Key = aShape.m_Ptr7_16x.value();
4095
4096 if( ptr7Key == 0 )
4097 return ret;
4098
4099 const BLK_0x2C_TABLE* tbl = expectBlockByKey<BLK_0x2C_TABLE>( ptr7Key, 0x2C );
4100
4101 if( !tbl )
4102 return ret;
4103
4104 const BLK_0x37_PTR_ARRAY* ptrArray = expectBlockByKey<BLK_0x37_PTR_ARRAY>( tbl->m_Ptr1, 0x37 );
4105
4106 if( !ptrArray || ptrArray->m_Count == 0 )
4107 return ret;
4108
4109 const size_t count = std::min( std::min( ptrArray->m_Count, ptrArray->m_Capacity ), 100u );
4110 ret.resize( count );
4111
4112 for( size_t i = 0; i < count; i++ )
4113 ret[i] = m_brdDb.GetObjectByKey( ptrArray->m_Ptrs[i] );
4114
4115 return ret;
4116}
4117
4118
4120{
4121 const LL_WALKER textWalker( m_brdDb.m_Header->m_LL_0x03_0x30, m_brdDb );
4122 int textCount = 0;
4123
4124 for( const BLOCK_BASE* block : textWalker )
4125 {
4126 if( block->GetBlockType() != 0x30 )
4127 continue;
4128
4129 const auto& strWrapper =
4130 static_cast<const BLOCK<BLK_0x30_STR_WRAPPER>&>( *block ).GetData();
4131
4132 std::unique_ptr<PCB_TEXT> text = buildPcbText( strWrapper, m_board );
4133
4134 if( !text )
4135 continue;
4136
4137 // If the text is referenced from a group, it's not board-level text,
4138 // and we'll pick up up while iterating the group elsewhere.
4139 if( strWrapper.GetGroupPtr() != 0 )
4140 {
4141 // In a group
4142 continue;
4143 }
4144
4145 wxLogTrace( traceAllegroBuilder, " Board text '%s' on layer %s at (%d, %d)",
4146 text->GetText(), m_board.GetLayerName( text->GetLayer() ),
4147 text->GetPosition().x, text->GetPosition().y );
4148
4149 m_board.Add( text.release(), ADD_MODE::APPEND );
4150 textCount++;
4151 }
4152
4153 wxLogTrace( traceAllegroBuilder, "Created %d board-level text objects", textCount );
4154}
4155
4156
4157template <std::derived_from<BOARD_ITEM> T>
4158void BulkAddToBoard( BOARD& aBoard, std::vector<std::unique_ptr<T>>&& aItems )
4159{
4160 std::vector<BOARD_ITEM*> rawPointers;
4161 rawPointers.reserve( aItems.size() );
4162
4163 for( std::unique_ptr<T>& item : aItems )
4164 {
4165 rawPointers.push_back( item.get() );
4166 aBoard.Add( item.release(), ADD_MODE::BULK_APPEND );
4167 }
4168
4169 aBoard.FinalizeBulkAdd( rawPointers );
4170}
4171
4172
4174{
4175 wxLogTrace( traceAllegroBuilder, "Creating zones from m_LL_Shapes and m_LL_0x24_0x28" );
4176
4177 std::vector<std::unique_ptr<ZONE>> boundaryZones;
4178 std::vector<std::unique_ptr<ZONE>> keepoutZones;
4179
4180 ZONE_FILL_HANDLER zoneFillHandler;
4181
4182 // Walk m_LL_Shapes to find BOUNDARY shapes (zone outlines).
4183 // BOUNDARY shapes use class 0x15 with copper layer subclass indices.
4184 const LL_WALKER shapeWalker( m_brdDb.m_Header->m_LL_Shapes, m_brdDb );
4185
4186 for( const BLOCK_BASE* block : shapeWalker )
4187 {
4188 if( block->GetBlockType() != 0x28 )
4189 continue;
4190
4191 const BLK_0x28_SHAPE& shapeData = static_cast<const BLOCK<BLK_0x28_SHAPE>&>( *block ).GetData();
4192
4194 continue;
4195
4196 std::unique_ptr<ZONE> zone = buildZone( *block, getShapeRelatedBlocks( shapeData ), zoneFillHandler );
4197
4198 if( zone )
4199 {
4200 wxLogTrace( traceAllegroBuilder, " Zone %#010x net=%d layer=%s (subclass=%#04x)", shapeData.m_Key,
4201 zone->GetNetCode(), m_board.GetLayerName( zone->GetFirstLayer() ),
4202 shapeData.m_Layer.m_Subclass );
4203
4204 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
4205 boundaryZones.push_back( std::move( zone ) );
4206 }
4207 }
4208
4209 // Walk m_LL_0x24_0x28 for keepout/in shapes
4210 const LL_WALKER keepoutWalker( m_brdDb.m_Header->m_LL_0x24_0x28, m_brdDb );
4211
4212 for( const BLOCK_BASE* block : keepoutWalker )
4213 {
4214 std::unique_ptr<ZONE> zone;
4215
4216 switch( block->GetBlockType() )
4217 {
4218 case 0x24:
4219 {
4220 const BLK_0x24_RECT& rectData = static_cast<const BLOCK<BLK_0x24_RECT>&>( *block ).GetData();
4221
4222 if( !layerIsZone( rectData.m_Layer ) )
4223 continue;
4224
4225 wxLogTrace( traceAllegroBuilder, " Processing %s rect %#010x", layerInfoDisplayName( rectData.m_Layer ),
4226 rectData.m_Key );
4227
4228 zone = buildZone( *block, {}, zoneFillHandler );
4229 break;
4230 }
4231 case 0x28:
4232 {
4233 const BLK_0x28_SHAPE& shapeData = static_cast<const BLOCK<BLK_0x28_SHAPE>&>( *block ).GetData();
4234
4235 if( !layerIsZone( shapeData.m_Layer ) )
4236 continue;
4237
4238 wxLogTrace( traceAllegroBuilder, " Processing %s shape %#010x", layerInfoDisplayName( shapeData.m_Layer ),
4239 shapeData.m_Key );
4240
4241 zone = buildZone( *block, {}, zoneFillHandler );
4242 break;
4243 }
4244 default:
4245 break;
4246 }
4247
4248 if( zone )
4249 {
4250 keepoutZones.push_back( std::move( zone ) );
4251 }
4252 }
4253
4254 // Deal with all the collected zone fill polygons now, all at once
4255 zoneFillHandler.ProcessPolygons( true );
4256
4257 int keepoutCount = keepoutZones.size();
4258 int boundaryCount = boundaryZones.size();
4259
4260 // Merge zones with identical polygons and same net into multi-layer zones.
4261 // Allegro often defines the same zone outline on multiple copper layers (e.g.
4262 // a ground pour spanning all layers). KiCad represents this as a single zone
4263 // with multiple fill layers.
4264 std::vector<std::unique_ptr<ZONE>> mergedZones = MergeZonesWithSameOutline( std::move( boundaryZones ) );
4265 int mergedCount = mergedZones.size();
4266
4267 BulkAddToBoard( m_board, std::move( mergedZones ) );
4268 BulkAddToBoard( m_board, std::move( keepoutZones ) );
4269
4270 wxLogTrace( traceAllegroBuilder, "Created %d zone outlines (%d merged away), %d keepout areas", mergedCount,
4271 boundaryCount - mergedCount, keepoutCount );
4272}
4273
4274
4276{
4277 wxLogTrace( traceAllegroBuilder, "Creating tables from m_LL_0x2C" );
4278
4279 const LL_WALKER tableWalker( m_brdDb.m_Header->m_LL_0x2C, m_brdDb );
4280 for( const BLOCK_BASE* block : tableWalker )
4281 {
4282 if( block->GetBlockType() != 0x2C )
4283 continue;
4284
4285 const BLK_0x2C_TABLE& tableData = static_cast<const BLOCK<BLK_0x2C_TABLE>&>( *block ).GetData();
4286
4288 {
4289 // 0x2c tables can have lots of subtypes. Only 0x110 seems useful to iterate in this way for now.
4290 continue;
4291 }
4292
4293 const wxString& tableName = m_brdDb.GetString( tableData.m_StringPtr );
4294
4295 std::vector<std::unique_ptr<BOARD_ITEM>> newItems;
4296
4297 LL_WALKER keyTableWalker{ tableData.m_Ptr1, block->GetKey(), m_brdDb };
4298
4299 for( const BLOCK_BASE* keyTable : keyTableWalker )
4300 {
4301 wxLogTrace( traceAllegroBuilder, " Table '%s' (key %#010x, table block key %#010x)", tableName,
4302 tableData.m_Key, tableData.m_Ptr1 );
4303
4304 if( !keyTable )
4305 {
4306 wxLogTrace( traceAllegroBuilder, " Key table pointer %#010x is invalid", tableData.m_Ptr1 );
4307 continue;
4308 }
4309
4310 switch( keyTable->GetBlockType() )
4311 {
4312 case 0x37:
4313 {
4314 const BLK_0x37_PTR_ARRAY& ptrArray =
4315 static_cast<const BLOCK<BLK_0x37_PTR_ARRAY>&>( *keyTable ).GetData();
4316
4317 uint32_t count = std::min( ptrArray.m_Count, static_cast<uint32_t>( ptrArray.m_Ptrs.size() ) );
4318
4319 wxLogTrace( traceAllegroBuilder, " Pointer array with %zu entries", static_cast<size_t>( count ) );
4320
4321 for( uint32_t ptrIndex = 0; ptrIndex < count; ptrIndex++ )
4322 {
4323 uint32_t ptrKey = ptrArray.m_Ptrs[ptrIndex];
4324
4325 if( ptrKey == 0 )
4326 continue;
4327
4328 const BLOCK_BASE* entryBlock = m_brdDb.GetObjectByKey( ptrKey );
4329
4330 if( !entryBlock )
4331 {
4332 wxLogTrace( traceAllegroBuilder, " Entry pointer %#010x is invalid", ptrKey );
4333 continue;
4334 }
4335
4336 for( std::unique_ptr<BOARD_ITEM>& newItem : buildGraphicItems( *entryBlock, m_board ) )
4337 {
4338 newItems.push_back( std::move( newItem ) );
4339 }
4340 }
4341
4342 break;
4343 }
4344 case 0x3c:
4345 {
4346 const BLK_0x3C_KEY_LIST& keyList = static_cast<const BLOCK<BLK_0x3C_KEY_LIST>&>( *keyTable ).GetData();
4347
4348 wxLogTrace( traceAllegroBuilder, " Key list with %zu entries",
4349 static_cast<size_t>( keyList.m_NumEntries ) );
4350 break;
4351 }
4352 default:
4353 {
4354 wxLogTrace( traceAllegroBuilder, " Table has unhandled key table type %#04x",
4355 keyTable->GetBlockType() );
4356 break;
4357 }
4358 }
4359 }
4360
4361 if( newItems.size() > 0 )
4362 {
4363 wxLogTrace( traceAllegroBuilder, " Creating group '%s' with %zu items", tableName, newItems.size() );
4364
4365 std::unique_ptr<PCB_GROUP> group = std::make_unique<PCB_GROUP>( &m_board );
4366 group->SetName( tableName );
4367
4368 for( const auto& item : newItems )
4369 group->AddItem( item.get() );
4370
4371 newItems.push_back( std::move( group ) );
4372
4373 BulkAddToBoard( m_board, std::move( newItems ) );
4374 }
4375 }
4376}
4377
4378
4380{
4381 if( m_zoneFillShapes.empty() )
4382 return;
4383
4384 PROF_TIMER fillTimer;
4385
4386 wxLogTrace( traceAllegroBuilder, "Applying zone fill polygons from %zu collected fills",
4387 m_zoneFillShapes.size() );
4388
4389
4390 // Unmatched ETCH shapes are either standalone copper polygons or dynamic copper
4391 // (teardrops/fillets). On V172+ boards, m_Unknown2 bit 12 (0x1000) marks auto-generated
4392 // dynamic copper that maps to KiCad teardrop zones. Shapes without this flag are genuine
4393 // standalone copper imported as filled PCB_SHAPE.
4394 int copperShapeCount = 0;
4395 int teardropCount = 0;
4396
4397 for( const auto& [fillKey, fill] : m_zoneFillShapes )
4398 {
4399 if( m_usedZoneFillShapes.contains( fillKey ) )
4400 continue;
4401
4402 SHAPE_POLY_SET polySet = shapeToPolySet( *fill.shape );
4403 polySet.Simplify();
4404
4405 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
4406 {
4407 SHAPE_POLY_SET fractured( poly );
4408 fractured.Fracture( /* aSimplify */ false );
4409
4410 const bool isDynCopperShape = ( fill.shape->m_Unknown2.value_or( 0 ) & 0x1000 ) != 0;
4411
4412 if( isDynCopperShape )
4413 {
4414 auto zone = std::make_unique<ZONE>( &m_board );
4415
4416 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_VIAPAD );
4417 zone->SetLayer( fill.layer );
4418 zone->SetNetCode( fill.netCode );
4419 zone->SetLocalClearance( 0 );
4420 zone->SetPadConnection( ZONE_CONNECTION::FULL );
4421 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
4422 zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER );
4423
4424 for( const SHAPE_LINE_CHAIN& chain : poly )
4425 zone->AddPolygon( chain );
4426
4427 zone->SetFilledPolysList( fill.layer, fractured );
4428 zone->SetIsFilled( true );
4429 zone->SetNeedRefill( false );
4430 zone->CalculateFilledArea();
4431
4432 m_board.Add( zone.release(), ADD_MODE::APPEND );
4433 teardropCount++;
4434 }
4435 else
4436 {
4437 auto shape = std::make_unique<PCB_SHAPE>( &m_board, SHAPE_T::POLY );
4438 shape->SetPolyShape( fractured );
4439 shape->SetFilled( true );
4440 shape->SetLayer( fill.layer );
4441 shape->SetNetCode( fill.netCode );
4442 shape->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
4443
4444 m_board.Add( shape.release(), ADD_MODE::APPEND );
4445 copperShapeCount++;
4446 }
4447 }
4448 }
4449
4450 wxLogTrace( traceAllegroPerf, wxT( " applyZoneFills unmatched loop: %.3f ms (%d shapes, %d teardrops)" ), //format:allow
4451 fillTimer.msecs( true ), copperShapeCount, teardropCount );
4452}
4453
4454
4456{
4457 std::unordered_map<int, std::vector<ZONE*>> teardropsByNet;
4458
4459 for( ZONE* zone : m_board.Zones() )
4460 {
4461 if( zone->IsTeardropArea() )
4462 teardropsByNet[zone->GetNetCode()].push_back( zone );
4463 }
4464
4465 if( teardropsByNet.empty() )
4466 return;
4467
4468 int padCount = 0;
4469 int viaCount = 0;
4470
4471 for( FOOTPRINT* fp : m_board.Footprints() )
4472 {
4473 for( PAD* pad : fp->Pads() )
4474 {
4475 auto it = teardropsByNet.find( pad->GetNetCode() );
4476
4477 if( it == teardropsByNet.end() )
4478 continue;
4479
4480 for( ZONE* tdZone : it->second )
4481 {
4482 if( !pad->IsOnLayer( tdZone->GetLayer() ) )
4483 continue;
4484
4485 if( tdZone->Outline()->Contains( pad->GetPosition() ) )
4486 {
4487 pad->SetTeardropsEnabled( true );
4488 padCount++;
4489 break;
4490 }
4491 }
4492 }
4493 }
4494
4495 for( PCB_TRACK* track : m_board.Tracks() )
4496 {
4497 if( track->Type() != PCB_VIA_T )
4498 continue;
4499
4500 PCB_VIA* via = static_cast<PCB_VIA*>( track );
4501 auto it = teardropsByNet.find( via->GetNetCode() );
4502
4503 if( it == teardropsByNet.end() )
4504 continue;
4505
4506 for( ZONE* tdZone : it->second )
4507 {
4508 if( !via->IsOnLayer( tdZone->GetLayer() ) )
4509 continue;
4510
4511 if( tdZone->Outline()->Contains( via->GetPosition() ) )
4512 {
4513 via->SetTeardropsEnabled( true );
4514 viaCount++;
4515 break;
4516 }
4517 }
4518 }
4519
4520 wxLogTrace( traceAllegroBuilder, "Enabled teardrops on %d pads and %d vias", padCount, viaCount );
4521}
4522
4523
4525{
4526 wxLogTrace( traceAllegroBuilder, "Starting BuildBoard() - Phase 2 of Allegro import" );
4527 wxLogTrace( traceAllegroBuilder, " Format version: %d (V172+ = %s)",
4528 static_cast<int>( m_brdDb.m_FmtVer ),
4529 ( m_brdDb.m_FmtVer >= FMT_VER::V_172 ) ? "yes" : "no" );
4530 wxLogTrace( traceAllegroBuilder, " Allegro version string: %.60s",
4531 m_brdDb.m_Header->m_AllegroVersion.data() );
4532
4533 if( m_progressReporter )
4534 {
4535 m_progressReporter->AddPhases( 4 );
4536 m_progressReporter->AdvancePhase( _( "Constructing caches" ) );
4537 m_progressReporter->KeepRefreshing();
4538 }
4539
4540 PROF_TIMER buildTimer;
4541
4542 wxLogTrace( traceAllegroBuilder, "Caching font definitions and setting up layers" );
4543 cacheFontDefs();
4544 wxLogTrace( traceAllegroPerf, wxT( " cacheFontDefs: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4545
4546 setupLayers();
4547 wxLogTrace( traceAllegroPerf, wxT( " setupLayers: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4548
4549 if( m_progressReporter )
4550 {
4551 m_progressReporter->AdvancePhase( _( "Creating nets" ) );
4552 m_progressReporter->KeepRefreshing();
4553 }
4554
4555 createNets();
4556 wxLogTrace( traceAllegroPerf, wxT( " createNets: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4557
4558 if( m_progressReporter )
4559 {
4560 m_progressReporter->AdvancePhase( _( "Creating tracks" ) );
4561 m_progressReporter->KeepRefreshing();
4562 }
4563
4564 createTracks();
4565 wxLogTrace( traceAllegroPerf, wxT( " createTracks: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4566
4567 if( m_progressReporter )
4568 m_progressReporter->KeepRefreshing();
4569
4571 wxLogTrace( traceAllegroPerf, wxT( " createBoardShapes: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4572
4574 wxLogTrace( traceAllegroPerf, wxT( " createBoardText: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4575
4576 createZones();
4577 wxLogTrace( traceAllegroPerf, wxT( " createZones: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4578
4579 createTables();
4580 wxLogTrace( traceAllegroPerf, wxT( " createTables: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4581
4582 if( m_progressReporter )
4583 m_progressReporter->KeepRefreshing();
4584
4586 wxLogTrace( traceAllegroPerf, wxT( " applyZoneFills: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4587
4589 wxLogTrace( traceAllegroPerf, wxT( " applyConstraintSets: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4590
4592 wxLogTrace( traceAllegroPerf, wxT( " applyNetConstraints: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4593
4595 wxLogTrace( traceAllegroPerf, wxT( " applyMatchGroups: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4596
4597 if( m_progressReporter )
4598 {
4599 m_progressReporter->AdvancePhase( _( "Converting footprints" ) );
4600 m_progressReporter->KeepRefreshing();
4601 }
4602
4603 const LL_WALKER fpWalker( m_brdDb.m_Header->m_LL_0x2B, m_brdDb );
4604 std::vector<BOARD_ITEM*> bulkAddedItems;
4605
4606 THROTTLE refreshThrottle( std::chrono::milliseconds( 100 ) );
4607
4608 for( const BLOCK_BASE* fpContainer : fpWalker )
4609 {
4610 if( fpContainer->GetBlockType() == 0x2B )
4611 {
4612 const BLK_0x2B_FOOTPRINT_DEF& fpBlock =
4613 static_cast<const BLOCK<BLK_0x2B_FOOTPRINT_DEF>&>( *fpContainer ).GetData();
4614
4615 const LL_WALKER instWalker( fpBlock.m_FirstInstPtr, fpBlock.m_Key, m_brdDb );
4616
4617 for( const BLOCK_BASE* instBlock : instWalker )
4618 {
4619 if( instBlock->GetBlockType() != 0x2D )
4620 {
4621 m_reporter.Report(
4622 wxString::Format( "Unexpected object of type %#04x found in footprint %#010x",
4623 instBlock->GetBlockType(), fpBlock.m_Key ),
4625 }
4626 else
4627 {
4628 const auto& inst =
4629 static_cast<const BLOCK<BLK_0x2D_FOOTPRINT_INST>&>( *instBlock ).GetData();
4630
4631 std::unique_ptr<FOOTPRINT> fp = buildFootprint( inst );
4632
4633 if( fp )
4634 {
4635 bulkAddedItems.push_back( fp.get() );
4636 m_board.Add( fp.release(), ADD_MODE::BULK_APPEND, true );
4637 }
4638 else
4639 {
4640 m_reporter.Report(
4641 wxString::Format( "Failed to construct footprint for 0x2D key %#010x",
4642 inst.m_Key ),
4644 }
4645 }
4646
4647 if( m_progressReporter && refreshThrottle.Ready() )
4648 m_progressReporter->KeepRefreshing();
4649 }
4650 }
4651 }
4652
4653 wxLogTrace( traceAllegroPerf, wxT( " convertFootprints (%zu footprints): %.3f ms" ), //format:allow
4654 bulkAddedItems.size(), buildTimer.msecs( true ) );
4655
4656 if( !bulkAddedItems.empty() )
4657 m_board.FinalizeBulkAdd( bulkAddedItems );
4658
4659 wxLogTrace( traceAllegroPerf, wxT( " FinalizeBulkAdd: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4660 wxLogTrace( traceAllegroBuilder, "Converted %zu footprints", bulkAddedItems.size() );
4661
4663 wxLogTrace( traceAllegroPerf, wxT( " enablePadTeardrops: %.3f ms" ), buildTimer.msecs( true ) ); //format:allow
4664 wxLogTrace( traceAllegroPerf, wxT( " Phase 2 total: %.3f ms" ), buildTimer.msecs() ); //format:allow
4665
4666 wxLogTrace( traceAllegroBuilder, "Board construction completed successfully" );
4667 return true;
4668}
const char * name
static LSET getRuleAreaLayers(const LAYER_INFO &aLayerInfo, PCB_LAYER_ID aDefault)
static std::unordered_set< LAYER_INFO > ScanForLayers(const BRD_DB &aDb)
Look through some lists for a list of layers used.
static uint32_t GetPrimaryNext(const BLOCK_BASE &aBlock)
Gets the next block in the linked list.
static std::optional< LAYER_INFO > tryLayerFromBlock(const BLOCK_BASE &aBlock)
Some blocks report layer info - if they do, return it else std::nullopt.
static wxString layerInfoDisplayName(const LAYER_INFO &aLayerInfo)
Build a unique display name for a LAYER_INFO entry from the static maps above, suitable for presentat...
static bool layerIsZone(const LAYER_INFO &aLayerInfo)
Some layers map to KiCad rule areas (zones) - for example a package keepout on ALL maps to a rule are...
const BLK_T & BlockDataAs(const BLOCK_BASE &aBlock)
#define BLK_FIELD(BLK_T, FIELD)
static const std::unordered_map< LAYER_INFO, PCB_LAYER_ID > s_LayerKiMap
Map of the pre-set class:subclass pairs to standard layers.
static const std::unordered_map< LAYER_INFO, wxString > s_OptionalFixedMappings
Names for custom KiCad layers that correspond to pre-defined Allegro layers.
void BulkAddToBoard(BOARD &aBoard, std::vector< std::unique_ptr< T > > &&aItems)
static uint32_t PadGetNextInFootprint(const BLOCK_BASE &aBlock)
"Get Next" function for the pad list in a footprint's 0x32 list.
LAYER_INFO expectLayerFromBlock(const BLOCK_BASE &aBlock)
Get a layer from a block that has layer info.
static int clampForScale(double aValue)
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
The base class for all blocks in the main body of an Allegro file.
uint32_t GetKey() const
uint8_t GetBlockType() const
const T & GetData() const
VECTOR2I scale(const VECTOR2I &aVector) const
SHAPE_POLY_SET shapeToPolySet(const BLK_0x28_SHAPE &aShape) const
wxString resolveMatchGroupName(const BLK_0x1B_NET &aNet) const
Follow m_MatchGroupPtr through the 0x26/0x2C pointer chain to get the match group name for a NET.
const SHAPE_LINE_CHAIN & buildSegmentChain(uint32_t aStartKey) const
Walk a geometry chain (0x01 arcs and 0x15-17 segments) starting from the given key,...
LAYER_MAPPING_HANDLER m_layerMappingHandler
PROGRESS_REPORTER * m_progressReporter
void reportUnexpectedBlockType(uint8_t aGot, uint8_t aExpected, uint32_t aKey=0, size_t aOffset=0, const wxString &aName=wxEmptyString) const
const BLK_0x07_COMPONENT_INST * getFpInstRef(const BLK_0x2D_FOOTPRINT_INST &aFpInstance) const
Look up 0x07 FP instance data (0x07) for a given 0x2D FP instance.
std::vector< const BLOCK_BASE * > getShapeRelatedBlocks(const BLK_0x28_SHAPE &aShape) const
Get blocks that are related to the BOUNDARY shape, i.e.
VECTOR2I scaleSize(const VECTOR2I &aSize) const
PCB_LAYER_ID getLayer(const LAYER_INFO &aLayerInfo) const
std::unordered_map< uint32_t, SHAPE_LINE_CHAIN > m_segChainCache
std::vector< std::unique_ptr< PCB_SHAPE > > buildShapes(const BLK_0x14_GRAPHIC &aGraphicList, BOARD_ITEM_CONTAINER &aParent)
Build the shapes from an 0x14 shape list.
BOARD_BUILDER(const BRD_DB &aBrdDb, BOARD &aBoard, REPORTER &aReporter, PROGRESS_REPORTER *aProgressReporter, const LAYER_MAPPING_HANDLER &aLayerMappingHandler)
std::unique_ptr< LAYER_MAPPER > m_layerMapper
std::vector< std::unique_ptr< BOARD_ITEM > > buildDrillMarker(const BLK_0x0C_PIN_DEF &aPinDef, BOARD_ITEM_CONTAINER &aParent)
Build a drill marker from a 0x0C PIN_DEF block.
std::unique_ptr< PCB_SHAPE > buildRect(const BLK_0x24_RECT &aRect, BOARD_ITEM_CONTAINER &aParent)
Build a rectangular shape from a 0x24 RECT block.
std::vector< std::unique_ptr< PCB_SHAPE > > buildPolygonShapes(const BLK_0x28_SHAPE &aShape, BOARD_ITEM_CONTAINER &aParent)
Build graphics from an 0x28 SHAPE, with separate items per segment/arc.
std::unique_ptr< PCB_SHAPE > buildArc(const BLK_0x01_ARC &aArc, const LAYER_INFO &aLayerInfo, PCB_LAYER_ID aLayer, BOARD_ITEM_CONTAINER &aParent)
const T * expectBlockByKey(uint32_t aKey, uint8_t aType) const
std::unique_ptr< PCB_TEXT > buildPcbText(const BLK_0x30_STR_WRAPPER &aStrWrapper, BOARD_ITEM_CONTAINER &aParent)
std::unique_ptr< FOOTPRINT > buildFootprint(const BLK_0x2D_FOOTPRINT_INST &aFpInstance)
void reportMissingBlock(uint32_t aKey, uint8_t aType) const
std::unordered_map< uint32_t, NETINFO_ITEM * > m_netCache
std::unique_ptr< ZONE > buildZone(const BLOCK_BASE &aBoundaryBlock, const std::vector< const BLOCK_BASE * > &aRelatedBlocks, ZONE_FILL_HANDLER &aZoneFillHandler)
Build a ZONE from an 0x0E, 0x24 or 0x28 block.
SHAPE_LINE_CHAIN buildOutline(const BLK_0x0E_RECT &aRect) const
std::unique_ptr< PCB_SHAPE > buildLineSegment(const BLK_0x15_16_17_SEGMENT &aSegment, const LAYER_INFO &aLayerInfo, PCB_LAYER_ID aLayer, BOARD_ITEM_CONTAINER &aParent)
Build a single line segment.
std::vector< std::unique_ptr< BOARD_ITEM > > buildTrack(const BLK_0x05_TRACK &aBlock, int aNetcode)
std::vector< const BLK_0x36_DEF_TABLE::FontDef_X08 * > m_fontDefList
std::unordered_map< uint32_t, ZoneFillEntry > m_zoneFillShapes
SHAPE_POLY_SET tryBuildZoneShape(const BLOCK_BASE &aBlock)
Try to build a zone shape for the given block, with holes.
wxString get0x30StringValue(uint32_t a0x30Key) const
Get just the string value from a 0x31 STRING WRAPPER -> 0x30 STRING GRAPHIC pair.
const BLK_0x36_DEF_TABLE::FontDef_X08 * getFontDef(unsigned aIndex) const
Get the font definition for a given index in a 0x30, etc.
std::vector< std::unique_ptr< BOARD_ITEM > > buildGraphicItems(const BLOCK_BASE &aBlock, BOARD_ITEM_CONTAINER &aParent)
Build a list of graphic items, e.g.
std::unique_ptr< BOARD_ITEM > buildVia(const BLK_0x33_VIA &aBlock, int aNetcode)
std::unique_ptr< PCB_SHAPE > buildPolygon(const BLK_0x28_SHAPE &aShape, BOARD_ITEM_CONTAINER &aParent)
Build a graphic polygon from a 0x28 SHAPE block.
wxString resolveConstraintSetNameFromField(uint32_t aFieldKey) const
Extract constraint set name from a 0x03 FIELD block pointer.
std::unordered_set< uint32_t > m_usedZoneFillShapes
std::vector< std::unique_ptr< BOARD_ITEM > > buildPadItems(const BLK_0x1C_PADSTACK &aPadstack, FOOTPRINT &aFp, const wxString &aPadName, int aNetcode)
Construct "pad" items for a given 0x1C PADSTACK block.
An Allegro database that represents a .brd file (amd presumably .dra)
Definition allegro_db.h:948
std::unique_ptr< FILE_HEADER > m_Header
std::optional< std::variant< wxString, uint32_t > > GetOptField(uint16_t aFieldCode) const
Get the raw variant value of the field with the given code, if present.
Class to handle the mapping for Allegro CLASS/SUBCLASS idiom to KiCad layers.
PCB_LAYER_ID GetPlaceBounds(bool aTop)
Allegro puts more graphics than just the polygon on PBT/B, but we don't want to always make a static ...
void FinalizeLayers()
Called after all the custom layers are loaded.
void ProcessLayerList(uint8_t aClass, const BLK_0x2A_LAYER_LIST &aList)
LAYER_MAPPER(const BRD_DB &aRawBoard, BOARD &aBoard, const LAYER_MAPPING_HANDLER &aLayerMappingHandler)
PCB_LAYER_ID addUserLayer(const wxString &aName)
std::unordered_map< LAYER_INFO, wxString > m_customLayerDialogNames
Names used in the layer mapping dialog for custom (non-ETCH, non-static) layers.
static PCB_LAYER_ID getNthUserLayer(int aNum)
std::unordered_map< const BLK_0x2A_LAYER_LIST *, std::vector< CUSTOM_LAYER > > m_Lists
PCB_LAYER_ID mapCustomLayerByName(const wxString &aLayerName)
Create or find a mapped layer with a given name, but not specifically bound to a specific class:subcl...
std::unordered_map< LAYER_INFO, PCB_LAYER_ID > m_staticLayerOverrides
Overrides for the static s_LayerKiMap entries, populated by the layer mapping handler.
std::unordered_map< wxString, PCB_LAYER_ID > m_MappedOptionalLayers
This is a map of optional, Allegro layers that we have mapped to KiCad layers with given names.
PCB_LAYER_ID GetLayer(const LAYER_INFO &aLayerInfo)
bool IsOutlineLayer(const LAYER_INFO &aLayerInfo) const
Resolve the subclass name for a given class:subclass pair using the per-class custom layer list.
bool IsLayerMapped(PCB_LAYER_ID aLayerId) const
Return whether this layer ID is something we mapped to, or the catch-all unmapped layer.
std::unordered_map< LAYER_INFO, int > m_unknownLayers
A record of what we failed to map.
std::unordered_map< uint8_t, std::vector< CUSTOM_LAYER > * > m_ClassCustomLayerLists
static PCB_LAYER_ID getNthCopperLayer(int aNum, int aTotal)
std::unordered_map< LAYER_INFO, PCB_LAYER_ID > m_customLayerToKiMap
The main map from CLASS:SUBCLASS custom mappings to KiCadLayers.
PCB_LAYER_ID MapCustomLayer(const LAYER_INFO &aLayerInfo, const wxString &aLayerName)
Record a specific class:subclass layer as mapping to some KiCad user layer, with a given name.
const LAYER_MAPPING_HANDLER & m_layerMappingHandler
Priority task dispatcher for zone fills - we want to do the biggest ones first.
Filled zones have their own outline and the fill itself comes from a bunch of "related" spaces.
void ProcessPolygons(bool aSimplify)
Process the polygons in a thread pool for more fans, more faster.
void QueuePolygonForZone(ZONE &aZone, SHAPE_POLY_SET aFilledArea, PCB_LAYER_ID aLayer)
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1223
void FinalizeBulkAdd(std::vector< BOARD_ITEM * > &aNewItems)
Must be used if Add() is used using a BULK_x ADD_MODE to generate a change event for listeners.
Definition board.cpp:1353
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr size_type GetHeight() const
Definition box2.h:215
EDA_ANGLE Normalize()
Definition eda_angle.h:229
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:284
iterator(uint32_t aCurrent, uint32_t aTail, const BRD_DB &aBoard, NEXT_FUNC_T aNextFunc)
bool operator!=(const iterator &other) const
const BLOCK_BASE * m_currBlock
const BLOCK_BASE * operator*() const
void SetNextFunc(NEXT_FUNC_T aNextFunc)
const BRD_DB & m_board
iterator begin() const
iterator end() const
std::function< uint32_t(const BLOCK_BASE &)> NEXT_FUNC_T
NEXT_FUNC_T m_nextFunction
LL_WALKER(uint32_t aHead, uint32_t aTail, const BRD_DB &aBoard)
LL_WALKER(const FILE_HEADER::LINKED_LIST &aList, const BRD_DB &aBoard)
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static const LSET & AllLayersMask()
Definition lset.cpp:641
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:45
static const char Default[]
the name of the default NETCLASS
Definition netclass.h:47
int GetDiffPairGap() const
Definition netclass.h:173
bool HasDiffPairWidth() const
Definition netclass.h:164
const wxString GetName() const
Gets the name of this (maybe aggregate) netclass in a format for internal usage or for export to exte...
Definition netclass.cpp:328
bool HasTrackWidth() const
Definition netclass.h:124
int GetDiffPairWidth() const
Definition netclass.h:165
bool HasDiffPairGap() const
Definition netclass.h:172
int GetTrackWidth() const
Definition netclass.h:125
int GetClearance() const
Definition netclass.h:117
bool HasClearance() const
Definition netclass.h:116
Handle the data for a net.
Definition netinfo.h:54
const wxString & GetNetname() const
Definition netinfo.h:112
void SetNetClass(const std::shared_ptr< NETCLASS > &aNetClass)
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:247
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:157
void SetMode(MODE aMode)
COPPER_LAYER_PROPS & CopperLayer(PCB_LAYER_ID aLayer)
DRILL_PROPS & Drill()
Definition padstack.h:348
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
@ CUSTOM
Shapes can be defined on arbitrary layers.
Definition padstack.h:173
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:172
void SetLayerSet(const LSET &aSet)
Definition padstack.h:324
Definition pad.h:55
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:369
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:376
void Execute(ContainerT &aItems)
Call this to execute the task on all items in aItems, using the thread pool and dispatching the tasks...
A small class to help profiling.
Definition profile.h:49
double msecs(bool aSinceLast=false)
Definition profile.h:149
A progress reporter interface for use in multi-threaded environments.
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
Definition seg.h:42
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
int TotalVertices() const
Return total number of vertices stored in the set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int AddPolygon(const POLYGON &apolygon)
Adds a polygon to the set.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
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 BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
const POLYGON & CPolygon(int aIndex) const
const std::vector< POLYGON > & CPolygons() const
Simple container to manage line stroke parameters.
Rate-limiter that fires at most once per interval.
Definition throttle.h:35
bool Ready()
Definition throttle.h:48
const std::string Format() const
Return the vector formatted as a string.
Definition vector2d.h:423
Handle a list of polygons defining a copper zone.
Definition zone.h:73
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition zone.cpp:501
SHAPE_POLY_SET * Outline()
Definition zone.h:340
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:634
const int minSize
Push and Shove router track width and via size dialog.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE FULL_CIRCLE
Definition eda_angle.h:409
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
static const wxChar *const traceAllegroBuilder
Flag to enable debug output of Allegro board construction.
@ S
Solder (HASL/SMOBC)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition layer_id.cpp:172
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition layer_id.cpp:31
#define MAX_USER_DEFINED_LAYERS
Definition layer_ids.h:177
bool IsUserLayer(PCB_LAYER_ID aLayerId)
Test whether a layer is a non copper and a non tech layer.
Definition layer_ids.h:759
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:803
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ Edge_Cuts
Definition layer_ids.h:112
@ Cmts_User
Definition layer_ids.h:108
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ In1_Cu
Definition layer_ids.h:66
@ User_1
Definition layer_ids.h:124
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:754
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
@ PHYS_CONSTRAINT_SET
Physical Constraint Set assignment.
std::vector< VECTOR2I > MakeRegularPolygonPoints(const VECTOR2I &aCenter, size_t aN, const VECTOR2I &aPt0)
Get the corners of a regular polygon from the centre, one point and the number of sides.
std::vector< SEG > MakeCrossSegments(const VECTOR2I &aCenter, const VECTOR2I &aSize, EDA_ANGLE aAngle)
Create the two segments for a cross.
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ RECTANGLE
Definition padstack.h:54
Class to handle a set of BOARD_ITEMs.
static const wxChar *const traceAllegroPerf
std::function< std::map< wxString, PCB_LAYER_ID >(const std::vector< INPUT_LAYER_DESC > &)> LAYER_MAPPING_HANDLER
Pointer to a function that takes a map of source and KiCad layers and returns a re-mapped version.
CITER next(CITER it)
Definition ptree.cpp:124
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
Utility functions for working with shapes.
const int scale
Arc segment used in tracks, zone outlines, and shape boundaries.
uint8_t m_SubType
Bit 6 (0x40) = clockwise direction.
Field/property references with variable-typed substructs.
std::variant< uint32_t, std::array< uint32_t, 2 >, std::string, SUB_0x6C, SUB_0x70_0x74, SUB_0xF6 > m_Substruct
Net assignment linking a net (0x1B) to its member objects.
Track segment container.
Component instance reference data.
Pin definition with shape type, drill character, coordinates, and size.
std::array< int32_t, 2 > m_Size
std::array< int32_t, 2 > m_Coords
Pad geometry and placement in board-absolute coordinates.
uint32_t m_Rotation
Board-absolute millidegrees. Subtract footprint rotation for FP-local orientation.
int32_t m_CoordsX
Board coordinates. Use SetFPRelativePosition() for KiCad FP-local space.
int32_t m_CoordsY
Board coordinates. Use SetFPRelativePosition() for KiCad FP-local space.
uint32_t m_Rotation
Rotation in millidegrees.
std::array< int32_t, 4 > m_Coords
Graphics container holding a chain of line segments and arcs.
0x15 , 0x16, 0x17 are segments:
0x1B objects are nets.
uint32_t m_MatchGroupPtr
Diff pair / match group pointer (0x26 or 0x2C)
Padstack definition containing drill dimensions and a table of per-layer pad/antipad/thermal componen...
std::array< uint32_t, 8 > m_DrillArr
In >= V172, elements [4] and [7] hold drill dimensions: [4] = drill diameter (or width for oblong dri...
uint32_t m_Drill
In < V172, this is the drill diameter in internal coordinates.
COND_GE< FMT_VER::V_172, std::array< uint32_t, 28 > > m_SlotAndUnknownArr
In V172+, elements [0] and [3] hold the true slot outline dimensions (X, Y) in internal coordinate un...
size_t m_NumFixedCompEntries
How many of the entries are fixed roles (after this is n*layers)
std::vector< PADSTACK_COMPONENT > m_Components
Collection of components that make up the padstack.
Physical constraint sets containing trace width, clearance, and routing rules.
uint32_t m_FieldPtr
Pointer to 0x03 FIELD with CS name (fallback when m_NameStrKey fails)
uint32_t m_NameStrKey
String table key for constraint set name.
std::vector< std::array< uint8_t, 56 > > m_DataB
Per-copper-layer dimension values, 14 x int32 per record.
Signal integrity and simulation model data (IBIS netlists).
Per-padstack dimension records with name and value.
Rectangle defined by four coordinates.
uint32_t m_Rotation
Rotation in millidegrees.
std::array< int32_t, 4 > m_Coords
Polygon shape defined by a linked list of segments starting at m_FirstSegmentPtr (0x15/0x16/0x17 line...
COND_LT< FMT_VER::V_172, uint32_t > m_Ptr7_16x
COND_GE< FMT_VER::V_172, uint32_t > m_Ptr7
Represents a list of layers.
COND_GE< FMT_VER::V_165, std::vector< REF_ENTRY > > m_RefEntries
COND_LT< FMT_VER::V_165, std::vector< NONREF_ENTRY > > m_NonRefEntries
Footprint definition (template) shared by multiple placed instances.
Lookup table used for named associations.
@ SUBTYPE_GRAPHICAL_GROUP
Used for drill charts and x-section charts.
Placed footprint instance on the board.
uint32_t m_Rotation
Millidegrees (divide by 1000 for degrees)
COND_LT< FMT_VER::V_172, uint32_t > m_InstRef16x
COND_GE< FMT_VER::V_172, uint32_t > m_InstRef
Connection point at a track junction or pad-to-track transition.
Text object with position, rotation, layer, font properties, and alignment.
COND_GE< FMT_VER::V_172, TEXT_PROPERTIES > m_Font
COND_LT< FMT_VER::V_172, TEXT_PROPERTIES > m_Font16x
String graphic content holding the actual text value and its display layer category.
Placed pad instance linking a pad definition (0x0D via m_PadPtr) to its parent footprint (m_ParentFp)...
Via instance with board position, padstack reference (m_Padstack for drill/annular ring definitions),...
Heterogeneous definition table containing font metrics (FontDef_X08), layer name definitions (X03),...
std::vector< SubstructVariant > m_Items
Fixed-capacity pointer array (100 entries).
std::array< uint32_t, 100 > m_Ptrs
Ordered list of block keys.
uint32_t GetKey() const
Definition allegro_db.h:219
This is apparently some kind of linked list that chains though subsets objects in the file.
Represents the information found in a single entry of a layer list.
0x1B NET objects
Definition allegro_db.h:705
std::optional< int > GetNetMinLineWidth() const
FIELD_LIST m_Fields
Definition allegro_db.h:724
Substruct in a padstack object.
uint32_t m_StrPtr
Seems to point to various things:
COND_GE< FMT_VER::V_172, int16_t > m_Z1
When processing a view, some objects are available and some are not.
Definition allegro_db.h:914
const NET * m_Net
Definition allegro_db.h:937
This is all the info needed to do the fill of one layer of one zone.
SHAPE_POLY_SET m_CombinedFill
The wider filled area we will chop a piece out of for this layer of this zone.
Describes an imported layer and how it could be mapped to KiCad Layers.
PCB_LAYER_ID AutoMapLayer
Best guess as to what the equivalent KiCad layer might be.
bool Required
Should we require the layer to be assigned?
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
wxString Name
Imported layer name as displayed in original application.
The features of a padstack that can vary between copper layers All parameters are optional; leaving t...
Definition padstack.h:227
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:267
PAD_DRILL_SHAPE shape
Definition padstack.h:268
size_t operator()(const LAYER_INFO &aLayerInfo) const noexcept
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
int clearance
wxString result
Test unit parsing edge cases and error handling.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694
std::vector< std::unique_ptr< ZONE > > MergeZonesWithSameOutline(std::vector< std::unique_ptr< ZONE > > &&aZones)
Merges zones with identical outlines and nets on different layers into single multi-layer zones.
@ FULL
pads are covered by copper
Definition zones.h:51