24#include <unordered_map>
30#include <wx/translation.h>
63 wxString upper = aExpr.Upper();
64 return upper.Contains( wxT(
"INPOLY" ) ) || upper.Contains( wxT(
"ISPOLY" ) );
70 for(
const ARULE6& rule : aRulesByPriorityAsc )
85 int32_t aMaskExpansion,
int aLandDiameter )
90 if( aManual && aFromHole )
92 int64_t opening =
static_cast<int64_t
>( aHoleSize ) + 2LL * aMaskExpansion;
94 return opening <= static_cast<int64_t>( aLandDiameter );
103 double slotRotation = std::fmod( aSlotRotation, 360.0 );
105 if( slotRotation < 0.0 )
106 slotRotation += 360.0;
108 aRotationSupported =
true;
110 if(
std::abs( slotRotation ) < 1e-9 ||
std::abs( slotRotation - 180.0 ) < 1e-9 )
111 return {
static_cast<int>( aSlotSize ),
static_cast<int>( aHoleSize ) };
113 if(
std::abs( slotRotation - 90.0 ) < 1e-9 ||
std::abs( slotRotation - 270.0 ) < 1e-9 )
114 return {
static_cast<int>( aHoleSize ),
static_cast<int>( aSlotSize ) };
116 aRotationSupported =
false;
117 return {
static_cast<int>( aSlotSize ),
static_cast<int>( aHoleSize ) };
126 if( aName.IsEmpty() )
129 static const std::unordered_map<std::string, ALTIUM_LAYER> hash_map = {
221 auto it = hash_map.find( std::string( aName.c_str() ) );
223 if( it != hash_map.end() )
227 const wxString mechanicalStr(
"MECHANICAL" );
229 if( aName.StartsWith( mechanicalStr ) )
231 unsigned long val = 0;
233 if( aName.Mid( mechanicalStr.length() ).ToULong( &val ) )
237 wxLogTrace(
traceAltiumIo, wxT(
"Unknown mapping of the Altium layer '%s'." ), aName );
244 static const std::unordered_map<std::string, ALTIUM_MECHKIND> hash_map = {
291 auto it = hash_map.find( std::string( aName.c_str() ) );
293 if( it != hash_map.end() )
299 wxLogTrace(
traceAltiumIo, wxT(
"Unknown mapping of the Altium layer kind '%s'." ), aName );
306 std::vector<ALTIUM_VERTICE>& aVertices,
int* aDiscarded )
308 for(
size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
310 const wxString si = std::to_string( i );
312 const wxString vxi = wxT(
"VX" ) + si;
313 const wxString vyi = wxT(
"VY" ) + si;
315 if( aProps.find( vxi ) == aProps.end() || aProps.find( vyi ) == aProps.end() )
318 bool clamped =
false;
319 bool anyClamped =
false;
322 [&](
const wxString& aKey ) -> int32_t
326 anyClamped |= clamped;
332 const int32_t
radius = readUnit( wxT(
"R" ) + si );
337 -readUnit( wxT(
"CY" ) + si ) );
349 aVertices.emplace_back( isRound,
radius, sa, ea, vp, cp );
359 if( mode == wxT(
"None" ) )
361 else if( mode == wxT(
"Rule" ) )
363 else if( mode == wxT(
"Manual" ) )
366 wxLogTrace(
traceAltiumIo, wxT(
"Unknown Mode string: '%s'." ), mode );
376 if( record == wxT(
"Arc" ) )
378 else if( record == wxT(
"Pad" ) )
380 else if( record == wxT(
"Via" ) )
382 else if( record == wxT(
"Track" ) )
384 else if( record == wxT(
"Text" ) )
386 else if( record == wxT(
"Fill" ) )
388 else if( record == wxT(
"Region" ) )
390 else if( record == wxT(
"Model" ) )
393 wxLogTrace(
traceAltiumIo, wxT(
"Unknown Record name string: '%s'." ), record );
400 const std::map<wxString, wxString>& aProps, wxString aKey )
404 if( parsedType == wxT(
"Mask" ) )
407 wxLogTrace(
traceAltiumIo, wxT(
"Unknown Extended Primitive Information type: '%s'." ), parsedType );
421 const std::string& aSubrecordName,
size_t aExpectedLength,
422 size_t aActualLength )
424 if( aActualLength < aExpectedLength )
426 THROW_IO_ERRORF( wxT(
"%s stream %s has length %d, which is unexpected (expected at least %d)" ),
437 const std::map<wxString, wxString> props = aReader.
ReadProperties();
440 THROW_IO_ERROR( wxT(
"ExtendedPrimitiveInformation stream has no properties!" ) );
456 uint32_t aLayerIdFallback )
474 mechenabled = !mechEnabled.Contains( wxS(
"FALSE" ) );
480 if( !mechKind.IsEmpty() )
488 return wxString::Format( wxT(
"%s|%d|%d" ), aMaterial, aHeight,
489 static_cast<int>( std::lround( aConst * 1000.0 ) ) );
501 std::map<wxString, double> tangents;
502 std::set<wxString> ambiguous;
504 auto scan = [&](
const wxString& aFamily )
506 for(
size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
508 const wxString prefix = aFamily + std::to_string( i );
510 if( aProps.find( prefix + wxT(
"NAME" ) ) == aProps.end() )
513 if( aProps.find( prefix + wxT(
"DIELLOSSTANGENT" ) ) == aProps.end() )
526 auto [it, inserted] = tangents.insert( { key, tangent } );
528 if( !inserted &&
std::abs( it->second - tangent ) > 1e-9 )
529 ambiguous.insert( key );
535 scan( wxT(
"V9_STACK_LAYER" ) );
536 scan( wxT(
"LAYER_V8_" ) );
538 for(
const wxString& key : ambiguous )
539 tangents.erase( key );
547 std::vector<ABOARD6_LAYER_STACKUP> stackup;
549 for(
size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ )
551 const wxString layeri = wxString( wxT(
"LAYER" ) ) << std::to_string( i );
552 const wxString layername = layeri + wxT(
"NAME" );
554 auto layernameit = aProps.find( layername );
556 if( layernameit == aProps.end() )
560 stackup.push_back( l );
564 for(
size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
566 const wxString layeri = wxString( wxT(
"LAYERV7_" ) ) << std::to_string( i );
567 const wxString layername = layeri + wxT(
"NAME" );
569 auto layernameit = aProps.find( layername );
571 if( layernameit == aProps.end() )
575 stackup.push_back( l );
582 if( !tangents.empty() )
589 auto it = tangents.find( key );
591 if( it != tangents.end() )
592 l.dielectriclosstangent = it->second;
613 wxString originalName = l.name;
616 for(
int ii = 2; !
layerNames.insert( l.name ).second; ii++ )
617 l.name = wxString::Format( wxT(
"%s %d" ), originalName, ii );
621 THROW_IO_ERROR( wxT(
"Library stream was not parsed correctly!" ) );
645 wxString originalName = l.name;
648 for(
int ii = 2; !
layerNames.insert( l.name ).second; ii++ )
649 l.name = wxString::Format( wxT(
"%s %d" ), originalName, ii );
655 THROW_IO_ERROR( wxT(
"Board6 stream was not parsed correctly!" ) );
660 std::map<wxString, wxString> properties = aReader.
ReadProperties();
662 if( properties.empty() )
669 for(
size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
671 auto mit = properties.find( wxT(
"M" ) + wxString( std::to_string( i ) ) );
673 if( mit == properties.end() )
676 names.push_back( mit->second );
680 THROW_IO_ERROR( wxT(
"Classes6 stream was not parsed correctly" ) );
719 THROW_IO_ERROR( wxT(
"Components6 stream was not parsed correctly" ) );
759 for(
int i = 0; i < refcount; i++ )
761 const std::string refi =
"REFERENCE" + std::to_string( i ) +
"POINT";
762 const wxString ref( refi );
767 for(
size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ )
769 const std::string texti =
"TEXT" + std::to_string( i );
770 const std::string textix = texti +
"X";
771 const std::string textiy = texti +
"Y";
773 if( props.find( textix ) == props.end() || props.find( textiy ) == props.end() )
791 THROW_IO_ERROR( wxT(
"Dimensions6 stream was not parsed correctly" ) );
796 std::map<wxString, wxString> properties = aReader.
ReadProperties();
798 if( properties.empty() )
818 std::map<wxString, wxString> properties = aReader.
ReadProperties();
820 if( properties.empty() )
831 std::map<wxString, wxString> properties = aReader.
ReadProperties();
833 if( properties.empty() )
865 THROW_IO_ERROR( wxT(
"Polygons6 stream was not parsed correctly" ) );
885 if( rulekind == wxT(
"Clearance" ) )
890 else if( rulekind == wxT(
"BoardOutlineClearance" ) )
895 else if( rulekind == wxT(
"DiffPairsRouting" ) )
899 else if( rulekind == wxT(
"Height" ) )
903 else if( rulekind == wxT(
"HoleSize" ) )
909 else if( rulekind == wxT(
"HoleToHoleClearance" ) )
914 else if( rulekind == wxT(
"RoutingVias" ) )
924 else if( rulekind == wxT(
"Width" ) )
931 props, wxT(
"PREFEREDWIDTH" ),
934 else if( rulekind == wxT(
"PasteMaskExpansion" ) )
939 else if( rulekind == wxT(
"SolderMaskExpansion" ) )
944 else if( rulekind == wxT(
"PlaneClearance" ) )
949 else if( rulekind == wxT(
"PolygonConnect" ) )
969 THROW_IO_ERROR( wxT(
"Rules6 stream was not parsed correctly" ) );
984 wxT(
"" ) ).IsEmpty();
995 auto readBaseline = [&props](
const wxString& aPrefix, std::vector<VECTOR2I>& aOut )
997 for(
int i = 0;; ++i )
999 wxString prefix = wxString::Format( wxT(
"%s%d." ), aPrefix, i );
1000 wxString firstX = prefix + wxT(
"X1" );
1002 if( props.find( firstX ) == props.end() )
1010 for(
const VECTOR2I& pt : { a, b } )
1012 if( aOut.empty() || aOut.back() != pt )
1013 aOut.push_back( pt );
1018 readBaseline( wxT(
"LINE" ),
baseline );
1022 THROW_IO_ERROR( wxT(
"SmartUnions stream was not parsed correctly" ) );
1038 uint8_t flags1 = aReader.
Read<uint8_t>();
1042 uint8_t flags2 = aReader.
Read<uint8_t>();
1045 net = aReader.
Read<uint16_t>();
1058 if( remaining >= 9 )
1065 if( remaining >= 10 )
1076 THROW_IO_ERROR( wxT(
"Arcs6 stream was not parsed correctly" ) );
1085 THROW_IO_ERROR( wxT(
"ComponentsBodies6 stream has invalid recordtype" ) );
1093 std::map<wxString, wxString> properties = aReader.
ReadProperties();
1095 if( properties.empty() )
1096 THROW_IO_ERROR( wxT(
"ComponentsBodies6 stream has no properties" ) );
1118 THROW_IO_ERROR( wxT(
"Components6 stream was not parsed correctly" ) );
1131 if( subrecord1 == 0 )
1137 THROW_IO_ERROR( wxT(
"Pads6 stream has invalid subrecord1 length" ) );
1162 uint8_t flags1 = aReader.
Read<uint8_t>();
1168 uint8_t flags2 = aReader.
Read<uint8_t>();
1171 net = aReader.
Read<uint16_t>();
1201 if( subrecord5 == 110 )
1219 if( subrecord5 >= 120 )
1226 else if( subrecord5 == 171 )
1230 if( subrecord5 >= 202 )
1244 if( subrecord6 >= 596 )
1246 sizeAndShape = std::make_unique<APAD6_SIZE_AND_SHAPE>();
1277 else if( subrecord6 != 0 )
1279 wxLogTrace(
traceAltiumIo, wxT(
"Pads6 stream has unexpected length for subrecord 6: %d." ), subrecord6 );
1287 THROW_IO_ERROR( wxT(
"Pads6 stream was not parsed correctly" ) );
1301 uint8_t flags1 = aReader.
Read<uint8_t>();
1307 uint8_t flags2 = aReader.
Read<uint8_t>();
1310 net = aReader.
Read<uint16_t>();
1319 if( subrecord1 <= 74 )
1325 uint8_t temp_byte = aReader.
Read<uint8_t>();
1344 temp_byte = aReader.
Read<uint8_t>();
1353 for(
int ii = 0; ii < 32; ++ii )
1359 if( subrecord1 >= 246 )
1369 if( subrecord1 >= 307 )
1380 THROW_IO_ERROR( wxT(
"Vias6 stream was not parsed correctly" ) );
1388 THROW_IO_ERROR( wxT(
"Tracks6 stream has invalid recordtype" ) );
1395 uint8_t flags1 = aReader.
Read<uint8_t>();
1399 uint8_t flags2 = aReader.
Read<uint8_t>();
1402 net = aReader.
Read<uint16_t>();
1414 if( remaining >= 9 )
1421 if( remaining >= 10 )
1431 THROW_IO_ERROR( wxT(
"Tracks6 stream was not parsed correctly" ) );
1457 if( subrecord1 < 123 )
1471 char fontData[64] = { 0 };
1472 aReader.
ReadBytes( fontData,
sizeof( fontData ) );
1473 fontname = wxString( fontData, wxMBConvUTF16LE(),
sizeof( fontData ) ).BeforeFirst(
'\0' );
1475 char tmpbyte = aReader.
Read<uint8_t>();
1491 if( remaining >= 93 )
1502 uint8_t unk8 = aReader.
Read<uint8_t>();
1508 aReader.
ReadBytes( fontData,
sizeof( fontData ) );
1509 barcode_fontname = wxString( fontData, wxMBConvUTF16LE(),
sizeof( fontData ) ).BeforeFirst(
'\0' );
1511 aReader.
Read<uint8_t>();
1517 if( remaining >= 103 )
1525 for(
size_t ii = 0; ii < 8; ++ii )
1527 uint8_t temp = aReader.
Peek<uint8_t>();
1529 wxLogTrace(
traceAltiumImport,
"3ATEXT6 %zu:\t Byte:%u, Kicad:%u\n", ii, temp, temp32 );
1539 if( remaining >= 115 )
1557 if( entry != aStringTable.end() )
1558 text = entry->second;
1563 text.Replace( wxT(
"\r\n" ), wxT(
"\n" ) );
1577 THROW_IO_ERROR( wxT(
"Texts6 stream was not parsed correctly" ) );
1592 uint8_t flags1 = aReader.
Read<uint8_t>();
1595 uint8_t flags2 = aReader.
Read<uint8_t>();
1598 net = aReader.
Read<uint16_t>();
1608 if( remaining >= 9 )
1614 if( remaining >= 10 )
1624 THROW_IO_ERROR( wxT(
"Fills6 stream was not parsed correctly" ) );
1632 THROW_IO_ERROR( wxT(
"Regions6 stream has invalid recordtype" ) );
1639 uint8_t flags1 = aReader.
Read<uint8_t>();
1643 uint8_t flags2 = aReader.
Read<uint8_t>();
1646 net = aReader.
Read<uint16_t>();
1653 std::map<wxString, wxString> properties = aReader.
ReadProperties();
1655 if( properties.empty() )
1710 uint32_t num_outline_vertices = aReader.
Read<uint32_t>();
1712 if( aExtendedVertices )
1713 num_outline_vertices++;
1715 for( uint32_t i = 0; i < num_outline_vertices; i++ )
1717 if( aExtendedVertices )
1719 bool isRound = aReader.
Read<uint8_t>() != 0;
1723 double angle1 = aReader.
Read<
double>();
1724 double angle2 = aReader.
Read<
double>();
1737 for( uint16_t k = 0; k <
holecount; k++ )
1739 uint32_t num_hole_vertices = aReader.
Read<uint32_t>();
1740 holes.at( k ).reserve( num_hole_vertices );
1742 for( uint32_t i = 0; i < num_hole_vertices; i++ )
1755 THROW_IO_ERROR( wxT(
"Regions6 stream was not parsed correctly" ) );
bool altiumScopeExprMatchesPolygon(const wxString &aExpr)
Return true if an Altium rule scope expression targets polygon pour primitives (matches InPolygon,...
void altium_parse_polygons(std::map< wxString, wxString > &aProps, std::vector< ALTIUM_VERTICE > &aVertices, int *aDiscarded)
Read the VX/VY/KIND/R/SA/EA/CX/CY vertex series out of a polygon or board outline record.
ALTIUM_MECHKIND altium_mechkind_from_name(const wxString &aName)
static std::map< wxString, double > ReadAltiumDielectricLossTangents(const std::map< wxString, wxString > &aProps)
static void ExpectSubrecordLengthAtLeast(const std::string &aStreamType, const std::string &aSubrecordName, size_t aExpectedLength, size_t aActualLength)
Throw an IO_ERROR if the actual length is less than the expected length.
ALTIUM_LAYER altium_layer_from_name(const wxString &aName)
static wxString MakeAltiumDielectricKey(const wxString &aMaterial, int32_t aHeight, double aConst)
VECTOR2I altiumSlotDrillSize(uint32_t aHoleSize, uint32_t aSlotSize, double aSlotRotation, bool &aRotationSupported)
Convert an Altium slot's independent rotation and dimensions to KiCad's cardinal drill shape.
static AEXTENDED_PRIMITIVE_INFORMATION_TYPE ReadAltiumExtendedPrimitiveInformationTypeFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
ALTIUM_LAYER altium_versioned_layer(ALTIUM_LAYER aV6Layer, ALTIUM_LAYER aV7Layer)
const ARULE6 * selectAltiumPolygonRule(const std::vector< ARULE6 > &aRulesByPriorityAsc)
Select the highest Altium-priority rule whose scope references polygons.
static ALTIUM_MODE ReadAltiumModeFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
static std::vector< ABOARD6_LAYER_STACKUP > ReadAltiumStackupFromProperties(const std::map< wxString, wxString > &aProps)
static ALTIUM_RECORD ReadAltiumRecordFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
bool altiumViaSideIsTented(bool aTentFlag, bool aManual, bool aFromHole, uint32_t aHoleSize, int32_t aMaskExpansion, int aLandDiameter)
Decide whether one side of an Altium via should be tented when imported into KiCad.
@ BOARD_OUTLINE_CLEARANCE
void altium_parse_polygons(std::map< wxString, wxString > &aProps, std::vector< ALTIUM_VERTICE > &aVertices, int *aDiscarded=nullptr)
Read the VX/VY/KIND/R/SA/EA/CX/CY vertex series out of a polygon or board outline record.
const uint8_t ALTIUM_KEEPOUT_ALL
const uint16_t ALTIUM_NET_UNCONNECTED
const uint16_t ALTIUM_POLYGON_NONE
AEXTENDED_PRIMITIVE_INFORMATION_TYPE
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
void Skip(size_t aLength)
size_t ReadAndSetSubrecordLength()
VECTOR2I ReadVector2ISize()
VECTOR2I ReadVector2IPos()
size_t GetRemainingSubrecordBytes() const
std::map< wxString, wxString > ReadProperties(std::function< std::map< wxString, wxString >(const std::string &)> handleBinaryData=[](const std::string &) { return std::map< wxString, wxString >();})
int ReadBytes(char *aOut, size_t aSize)
static int ReadInt(const std::map< wxString, wxString > &aProps, const wxString &aKey, int aDefault)
static int32_t ConvertToKicadUnit(const double aValue, bool *aOutOfRange=nullptr)
Convert a value in Altium's internal unit (0.1 uinch) to KiCad IU, clamped to the representable range...
static bool ReadBool(const std::map< wxString, wxString > &aProps, const wxString &aKey, bool aDefault)
static wxString ReadUnicodeString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
static int32_t ReadKicadUnit(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault, bool *aOutOfRange=nullptr)
static double ReadDouble(const std::map< wxString, wxString > &aProps, const wxString &aKey, double aDefault)
static const wxChar * traceAltiumImport
Flag to enable Altium importer logging.
const wxChar *const traceAltiumIo
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
AARC6(ALTIUM_BINARY_PARSER &aReader)
uint8_t keepoutrestrictions
ABOARD6_LAYER_STACKUP(const std::map< wxString, wxString > &aProps, const wxString &aPrefix, uint32_t aLayerIdFallback)
wxString dielectricmaterial
std::set< wxString > layerNames
ABOARD6(ALTIUM_BINARY_PARSER &aReader)
std::vector< ABOARD6_LAYER_STACKUP > stackup
std::vector< ALTIUM_VERTICE > board_vertices
std::vector< wxString > names
ACLASS6(ALTIUM_BINARY_PARSER &aReader)
wxString sourceHierachicalPath
ALTIUM_TEXT_POSITION commentautoposition
ALTIUM_TEXT_POSITION nameautoposition
ACOMPONENT6(ALTIUM_BINARY_PARSER &aReader)
wxString sourcefootprintlibrary
wxString sourcelibreference
wxString sourcedesignator
wxString sourcecomponentlibrary
ACOMPONENTBODY6(ALTIUM_BINARY_PARSER &aReader)
std::vector< VECTOR2I > textPoint
ALTIUM_DIMENSION_KIND kind
ADIMENSION6(ALTIUM_BINARY_PARSER &aReader)
std::vector< VECTOR2I > referencePoint
uint8_t keepoutrestrictions
AFILL6(ALTIUM_BINARY_PARSER &aReader)
std::set< wxString > layerNames
std::vector< ABOARD6_LAYER_STACKUP > stackup
ALIBRARY(ALTIUM_BINARY_PARSER &aReader)
AMODEL(ALTIUM_BINARY_PARSER &aReader)
ANET6(ALTIUM_BINARY_PARSER &aReader)
int32_t soldermaskexpansionmanual
APAD6(ALTIUM_BINARY_PARSER &aReader)
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
ALTIUM_PAD_SHAPE topshape
ALTIUM_MODE pastemaskexpansionmode
ALTIUM_MODE soldermaskexpansionmode
ALTIUM_PAD_SHAPE botshape
ALTIUM_PAD_SHAPE midshape
int32_t pastemaskexpansionmanual
int32_t pad_to_die_length
std::vector< ALTIUM_VERTICE > vertices
APOLYGON6(ALTIUM_BINARY_PARSER &aReader)
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
uint8_t keepoutrestrictions
AREGION6(ALTIUM_BINARY_PARSER &aReader, bool aExtendedVertices)
std::vector< ALTIUM_VERTICE > outline
std::vector< std::vector< ALTIUM_VERTICE > > holes
ALTIUM_CONNECT_STYLE polygonconnectStyle
int planeclearanceClearance
int32_t polygonconnectReliefconductorwidth
int32_t polygonconnectAirgapwidth
int polygonconnectReliefentries
std::vector< VECTOR2I > baselinecoupled
std::vector< VECTOR2I > baseline
ASMARTUNION6(ALTIUM_BINARY_PARSER &aReader)
uint32_t text_offset_width
uint32_t textbox_rect_height
ALTIUM_TEXT_POSITION textbox_rect_justification
uint32_t widestring_index
uint32_t textbox_rect_width
uint32_t margin_border_width
bool isJustificationValid
ALTIUM_BARCODE_TYPE barcode_type
ALTIUM_TEXT_TYPE fonttype
STROKE_FONT_TYPE strokefonttype
wxString barcode_fontname
ATEXT6(ALTIUM_BINARY_PARSER &aReader, std::map< uint32_t, wxString > &aStringTable)
ATRACK6(ALTIUM_BINARY_PARSER &aReader)
uint8_t keepoutrestrictions
bool soldermask_expansion_linked
uint32_t thermal_relief_conductorcount
int32_t thermal_relief_airgap
bool soldermask_expansion_from_hole
int32_t soldermask_expansion_front
bool soldermask_expansion_manual
AVIA6(ALTIUM_BINARY_PARSER &aReader)
uint32_t thermal_relief_conductorwidth
int32_t soldermask_expansion_back
uint32_t diameter_by_layer[32]
wxLogTrace helper definitions.
VECTOR2< int32_t > VECTOR2I