altium: Refactor track parsing to make it reusable for footprint import

Also refactor the code to import MULTI_LAYER items really on multiple layers.
This commit is contained in:
Thomas Pointhuber 2022-01-10 17:45:37 +01:00
parent 5aff3ef949
commit f5720d6e9a
2 changed files with 246 additions and 111 deletions

View File

@ -69,6 +69,17 @@ bool IsAltiumLayerAPlane( ALTIUM_LAYER aLayer )
return aLayer >= ALTIUM_LAYER::INTERNAL_PLANE_1 && aLayer <= ALTIUM_LAYER::INTERNAL_PLANE_16;
}
FOOTPRINT* ALTIUM_PCB::HelperGetFootprint( uint16_t aComponent ) const
{
if( aComponent == ALTIUM_COMPONENT_NONE || m_components.size() <= aComponent )
{
THROW_IO_ERROR( wxString::Format( "Component creator tries to access component id %d "
"of %d existing components",
aComponent, m_components.size() ) );
}
return m_components.at( aComponent );
}
PCB_SHAPE* ALTIUM_PCB::HelperCreateAndAddShape( uint16_t aComponent )
{
@ -268,6 +279,37 @@ PCB_LAYER_ID ALTIUM_PCB::GetKicadLayer( ALTIUM_LAYER aAltiumLayer ) const
}
std::vector<PCB_LAYER_ID> ALTIUM_PCB::GetKicadLayersToIterate( ALTIUM_LAYER aAltiumLayer ) const
{
static std::set<ALTIUM_LAYER> altiumLayersWithWarning;
if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
{
std::vector<PCB_LAYER_ID> layers;
layers.reserve( MAX_CU_LAYERS ); // TODO: only use Cu layers which are on the board
for( PCB_LAYER_ID layer = PCB_LAYER_ID::F_Cu; layer <= PCB_LAYER_ID::B_Cu;
layer = static_cast<PCB_LAYER_ID>( static_cast<int>( layer ) + 1 ) )
{
layers.emplace_back( layer );
}
return layers;
}
PCB_LAYER_ID klayer = GetKicadLayer( aAltiumLayer );
if( klayer == UNDEFINED_LAYER )
{
wxLogWarning( _( "Altium layer (%d) has no KiCad equivalent. It has been moved to KiCad "
"layer Eco1_User." ),
aAltiumLayer );
klayer = Eco1_User;
}
return { klayer };
}
ALTIUM_PCB::ALTIUM_PCB( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
{
m_board = aBoard;
@ -590,8 +632,21 @@ void ALTIUM_PCB::Parse( const ALTIUM_COMPOUND_FILE& altiumPcbFi
FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile,
const wxString& aFootprintName )
{
std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
std::map<uint32_t, wxString> stringTable; // TODO
std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
// TODO: what should we do with those layers?
m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_14, Eco2_User );
m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_15, Eco2_User );
m_layermap.emplace( ALTIUM_LAYER::MECHANICAL_16, Eco2_User );
m_unicodeStrings.clear();
// TODO: WideStrings are stored as parameterMap in the case of footprints, not as binary
// std::string unicodeStringsStreamName = aFootprintName.ToStdString() + "\\WideStrings";
// const CFB::COMPOUND_FILE_ENTRY* unicodeStringsData = altiumLibFile.FindStream( unicodeStringsStreamName );
// if( unicodeStringsData != nullptr )
// {
// ParseWideStrings6Data( altiumLibFile, unicodeStringsData );
// }
std::string streamName = aFootprintName.ToStdString() + "\\Data";
const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( streamName );
@ -634,12 +689,12 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
case ALTIUM_RECORD::TRACK:
{
ATRACK6 track( parser );
ParseTracks6OfFootprint( footprint.get(), track );
ParseTracks6DataFootprint( footprint.get(), track, false );
break;
}
case ALTIUM_RECORD::TEXT:
{
ATEXT6 text( parser, stringTable );
ATEXT6 text( parser, m_unicodeStrings );
break;
}
case ALTIUM_RECORD::FILL:
@ -2570,32 +2625,6 @@ void ALTIUM_PCB::ParseVias6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
}
}
void ALTIUM_PCB::ParseTracks6OfFootprint( FOOTPRINT* footprint, const ATRACK6& elem )
{
PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
if( klayer == UNDEFINED_LAYER )
{
/*wxLogWarning( _( "Track found on an Altium layer (%d) with no KiCad "
"equivalent. It has been moved to KiCad layer Eco1_User." ),
elem.layer );*/
klayer = Eco1_User;
}
if( elem.is_polygonoutline || elem.subpolyindex != ALTIUM_POLYGON_NONE )
return;
FP_SHAPE* shape = new FP_SHAPE( footprint, SHAPE_T::SEGMENT );
shape->SetStart0( elem.start );
shape->SetEnd0( elem.end );
shape->SetStroke( STROKE_PARAMS( elem.width, PLOT_DASH_TYPE::SOLID ) );
shape->SetLayer( klayer );
shape->SetDrawCoord(); // TODO: use HelperShapeSetLocalCoord
footprint->Add( shape, ADD_MODE::APPEND );
}
void ALTIUM_PCB::ParseTracks6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry )
{
@ -2609,91 +2638,15 @@ void ALTIUM_PCB::ParseTracks6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFil
checkpoint();
ATRACK6 elem( reader );
if( elem.is_polygonoutline || elem.subpolyindex != ALTIUM_POLYGON_NONE )
continue;
// element in plane is in fact substracted from the plane. Already done by Altium?
//if( IsAltiumLayerAPlane( elem.layer ) )
// continue;
PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
if( elem.is_keepout || IsAltiumLayerAPlane( elem.layer ) )
if( elem.component == ALTIUM_COMPONENT_NONE )
{
PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
shape.SetStart( elem.start );
shape.SetEnd( elem.end );
shape.SetStroke( STROKE_PARAMS( elem.width, PLOT_DASH_TYPE::SOLID ) );
ZONE* zone = new ZONE( m_board );
m_board->Add( zone, ADD_MODE::APPEND );
zone->SetFillVersion( 6 );
zone->SetIsRuleArea( true );
zone->SetDoNotAllowTracks( false );
zone->SetDoNotAllowVias( false );
zone->SetDoNotAllowPads( false );
zone->SetDoNotAllowFootprints( false );
zone->SetDoNotAllowCopperPour( true );
if( elem.layer == ALTIUM_LAYER::MULTI_LAYER )
{
zone->SetLayer( F_Cu );
zone->SetLayerSet( LSET::AllCuMask() );
}
else
{
PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
if( klayer == UNDEFINED_LAYER )
{
wxLogWarning( _( "Track keepout found on an Altium layer (%d) with no KiCad "
"equivalent. It has been moved to KiCad layer Eco1_User." ),
elem.layer );
klayer = Eco1_User;
}
zone->SetLayer( klayer );
}
shape.TransformShapeWithClearanceToPolygon( *zone->Outline(), klayer, 0, ARC_HIGH_DEF,
ERROR_INSIDE );
zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
ZONE::GetDefaultHatchPitch(), true );
continue;
}
if( klayer == UNDEFINED_LAYER )
{
wxLogWarning( _( "Track found on an Altium layer (%d) with no KiCad equivalent. "
"It has been moved to KiCad layer Eco1_User." ),
elem.layer );
klayer = Eco1_User;
}
if( IsCopperLayer( klayer ) )
{
PCB_TRACK* track = new PCB_TRACK( m_board );
m_board->Add( track, ADD_MODE::APPEND );
track->SetStart( elem.start );
track->SetEnd( elem.end );
track->SetWidth( elem.width );
track->SetLayer( klayer );
track->SetNetCode( GetNetCode( elem.net ) );
ParseTracks6DataBoard( elem );
}
else
{
PCB_SHAPE* shape = HelperCreateAndAddShape( elem.component );
shape->SetShape( SHAPE_T::SEGMENT );
shape->SetStart( elem.start );
shape->SetEnd( elem.end );
shape->SetStroke( STROKE_PARAMS( elem.width, PLOT_DASH_TYPE::SOLID ) );
shape->SetLayer( klayer );
HelperShapeSetLocalCoord( shape, elem.component );
FOOTPRINT* footprint = HelperGetFootprint( elem.component );
ParseTracks6DataFootprint( footprint, elem, true );
}
reader.SkipSubrecord();
}
if( reader.GetRemainingBytes() != 0 )
@ -2702,6 +2655,109 @@ void ALTIUM_PCB::ParseTracks6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFil
}
}
void ALTIUM_PCB::ParseTracks6DataBoard( const ATRACK6& aElem )
{
if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
return;
if( aElem.is_keepout || IsAltiumLayerAPlane( aElem.layer ) )
{
// This is not the actual board item. We can use it to create the polygon for the region
PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
shape.SetStart( aElem.start );
shape.SetEnd( aElem.end );
shape.SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
HelperPcpShapeAsBoardKeepoutRegion( shape, aElem.layer );
}
else
{
for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
{
ParseTracks6DataBoardLayer( aElem, klayer );
}
}
}
void ALTIUM_PCB::ParseTracks6DataFootprint( FOOTPRINT* aFootprint, const ATRACK6& aElem,
const bool aBoardImport )
{
if( aElem.is_polygonoutline || aElem.subpolyindex != ALTIUM_POLYGON_NONE )
return;
if( aElem.is_keepout || IsAltiumLayerAPlane( aElem.layer ) )
{
// This is not the actual board item. We can use it to create the polygon for the region
PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
shape.SetStart( aElem.start );
shape.SetEnd( aElem.end );
shape.SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
HelperPcpShapeAsFootprintKeepoutRegion( aFootprint, shape, aElem.layer );
}
else
{
for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
{
if( aBoardImport && IsCopperLayer( klayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
{
// Special case: do to not lose net connections in footprints
ParseTracks6DataBoardLayer( aElem, klayer );
}
else
{
ParseTracks6DataFootprintLayer( aFootprint, aElem, klayer );
}
}
}
}
void ALTIUM_PCB::ParseTracks6DataBoardLayer( const ATRACK6& aElem, PCB_LAYER_ID aLayer )
{
if( IsCopperLayer( aLayer ) && aElem.net != ALTIUM_NET_UNCONNECTED )
{
PCB_TRACK* track = new PCB_TRACK( m_board );
track->SetStart( aElem.start );
track->SetEnd( aElem.end );
track->SetWidth( aElem.width );
track->SetLayer( aLayer );
track->SetNetCode( GetNetCode( aElem.net ) );
m_board->Add( track, ADD_MODE::APPEND );
}
else
{
PCB_SHAPE* seg = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
seg->SetStart( aElem.start );
seg->SetEnd( aElem.end );
seg->SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
seg->SetLayer( aLayer );
m_board->Add( seg, ADD_MODE::APPEND );
}
}
void ALTIUM_PCB::ParseTracks6DataFootprintLayer( FOOTPRINT* aFootprint, const ATRACK6& aElem,
PCB_LAYER_ID aLayer )
{
FP_SHAPE* seg = new FP_SHAPE( aFootprint, SHAPE_T::SEGMENT );
seg->SetStart( aElem.start );
seg->SetEnd( aElem.end );
seg->SetStroke( STROKE_PARAMS( aElem.width, PLOT_DASH_TYPE::SOLID ) );
seg->SetLayer( aLayer );
seg->SetLocalCoord();
aFootprint->Add( seg, ADD_MODE::APPEND );
}
void ALTIUM_PCB::ParseWideStrings6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry )
{
@ -2988,3 +3044,71 @@ void ALTIUM_PCB::ParseFills6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile
THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
}
}
void ALTIUM_PCB::HelperPcpShapeAsBoardKeepoutRegion( const PCB_SHAPE& aShape,
ALTIUM_LAYER aAltiumLayer )
{
ZONE* zone = new ZONE( m_board );
zone->SetFillVersion( 6 );
zone->SetIsRuleArea( true );
zone->SetDoNotAllowTracks( false );
zone->SetDoNotAllowVias( false );
zone->SetDoNotAllowPads( false );
zone->SetDoNotAllowFootprints( false );
zone->SetDoNotAllowCopperPour( true );
if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
{
zone->SetLayer( F_Cu );
zone->SetLayerSet( LSET::AllCuMask() );
}
else
{
std::vector<PCB_LAYER_ID> klayers = GetKicadLayersToIterate( aAltiumLayer );
zone->SetLayer( klayers.at( 0 ) );
}
aShape.EDA_SHAPE::TransformShapeWithClearanceToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF,
ERROR_INSIDE, false );
zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
ZONE::GetDefaultHatchPitch(), true );
m_board->Add( zone, ADD_MODE::APPEND );
}
void ALTIUM_PCB::HelperPcpShapeAsFootprintKeepoutRegion( FOOTPRINT* aFootprint,
const PCB_SHAPE& aShape,
ALTIUM_LAYER aAltiumLayer )
{
FP_ZONE* zone = new FP_ZONE( aFootprint );
zone->SetFillVersion( 6 );
zone->SetIsRuleArea( true );
zone->SetDoNotAllowTracks( false );
zone->SetDoNotAllowVias( false );
zone->SetDoNotAllowPads( false );
zone->SetDoNotAllowFootprints( false );
zone->SetDoNotAllowCopperPour( true );
if( aAltiumLayer == ALTIUM_LAYER::MULTI_LAYER )
{
zone->SetLayer( F_Cu );
zone->SetLayerSet( LSET::AllCuMask() );
}
else
{
std::vector<PCB_LAYER_ID> klayers = GetKicadLayersToIterate( aAltiumLayer );
zone->SetLayer( klayers.at( 0 ) );
}
aShape.EDA_SHAPE::TransformShapeWithClearanceToPolygon( *zone->Outline(), 0, ARC_HIGH_DEF,
ERROR_INSIDE, false );
zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
ZONE::GetDefaultHatchPitch(), true );
aFootprint->Add( zone, ADD_MODE::APPEND );
}

View File

@ -117,6 +117,7 @@ private:
void checkpoint();
PCB_LAYER_ID GetKicadLayer( ALTIUM_LAYER aAltiumLayer ) const;
std::vector<PCB_LAYER_ID> GetKicadLayersToIterate( ALTIUM_LAYER aAltiumLayer ) const;
int GetNetCode( uint16_t aId ) const;
const ARULE6* GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const;
const ARULE6* GetRuleDefault( ALTIUM_RULE_KIND aKind ) const;
@ -151,9 +152,14 @@ private:
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseVias6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseTracks6OfFootprint( FOOTPRINT* footprint, const ATRACK6& elem );
void ParseTracks6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseTracks6DataBoard( const ATRACK6& aElem );
void ParseTracks6DataFootprint( FOOTPRINT* aFootprint, const ATRACK6& aElem,
const bool aBoardImport );
void ParseTracks6DataBoardLayer( const ATRACK6& aElem, PCB_LAYER_ID aLayer );
void ParseTracks6DataFootprintLayer( FOOTPRINT* aFootprint, const ATRACK6& aElem,
PCB_LAYER_ID aLayer );
void ParseTexts6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseFills6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
@ -178,6 +184,11 @@ private:
void HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices );
void HelperPcpShapeAsBoardKeepoutRegion( const PCB_SHAPE& aShape, ALTIUM_LAYER aAltiumLayer );
void HelperPcpShapeAsFootprintKeepoutRegion( FOOTPRINT* aFootprint, const PCB_SHAPE& aShape,
ALTIUM_LAYER aAltiumLayer );
FOOTPRINT* HelperGetFootprint( uint16_t aComponent ) const;
PCB_SHAPE* HelperCreateAndAddShape( uint16_t aComponent );
void HelperShapeSetLocalCoord( PCB_SHAPE* aShape, uint16_t aComponent );