altium: add support for non-copper pads, draw them instead using drawsegments
OTHER CHANGES: * Simplify handling of Drawsegments * Add missing SetWidth( 0 ) TODO: * SMD pads on inner layers are not supported yet by KiCad
This commit is contained in:
parent
8ef66732e4
commit
0542ce2ae2
|
@ -86,12 +86,54 @@ void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName,
|
|||
}
|
||||
|
||||
|
||||
bool IsAltiumLayerCopper( ALTIUM_LAYER aLayer )
|
||||
{
|
||||
return aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER;
|
||||
}
|
||||
|
||||
|
||||
bool IsAltiumLayerAPlane( ALTIUM_LAYER aLayer )
|
||||
{
|
||||
return aLayer >= ALTIUM_LAYER::INTERNAL_PLANE_1 && aLayer <= ALTIUM_LAYER::INTERNAL_PLANE_16;
|
||||
}
|
||||
|
||||
|
||||
DRAWSEGMENT* ALTIUM_PCB::HelperCreateAndAddDrawsegment( uint16_t aComponent )
|
||||
{
|
||||
if( aComponent == ALTIUM_COMPONENT_NONE )
|
||||
{
|
||||
DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
|
||||
m_board->Add( ds, ADD_MODE::APPEND );
|
||||
return ds;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( 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() ) );
|
||||
}
|
||||
MODULE* module = m_components.at( aComponent );
|
||||
DRAWSEGMENT* ds = new EDGE_MODULE( module );
|
||||
module->Add( ds, ADD_MODE::APPEND );
|
||||
return ds;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ALTIUM_PCB::HelperDrawsegmentSetLocalCoord( DRAWSEGMENT* aDs, uint16_t aComponent )
|
||||
{
|
||||
if( aComponent != ALTIUM_COMPONENT_NONE )
|
||||
{
|
||||
auto em = dynamic_cast<EDGE_MODULE*>( aDs );
|
||||
|
||||
if( em )
|
||||
em->SetLocalCoord();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PCB_LAYER_ID ALTIUM_PCB::GetKicadLayer( ALTIUM_LAYER aAltiumLayer ) const
|
||||
{
|
||||
auto override = m_layermap.find( aAltiumLayer );
|
||||
|
@ -1378,26 +1420,7 @@ void ALTIUM_PCB::ParseArcs6Data(
|
|||
}
|
||||
else
|
||||
{
|
||||
// TODO: better approach to select if item belongs to a MODULE
|
||||
DRAWSEGMENT* ds = nullptr;
|
||||
if( elem.component == ALTIUM_COMPONENT_NONE )
|
||||
{
|
||||
ds = new DRAWSEGMENT( m_board );
|
||||
m_board->Add( ds, ADD_MODE::APPEND );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_components.size() <= elem.component )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format(
|
||||
"Arcs6 stream tries to access component id %d of %d existing components",
|
||||
elem.component, m_components.size() ) );
|
||||
}
|
||||
MODULE* module = m_components.at( elem.component );
|
||||
ds = new EDGE_MODULE( module );
|
||||
module->Add( ds, ADD_MODE::APPEND );
|
||||
}
|
||||
|
||||
DRAWSEGMENT* ds = HelperCreateAndAddDrawsegment( elem.component );
|
||||
ds->SetCenter( elem.center );
|
||||
ds->SetWidth( elem.width );
|
||||
ds->SetLayer( klayer );
|
||||
|
@ -1418,13 +1441,7 @@ void ALTIUM_PCB::ParseArcs6Data(
|
|||
ds->SetArcStart( elem.center + arcStartOffset );
|
||||
}
|
||||
|
||||
if( elem.component != ALTIUM_COMPONENT_NONE )
|
||||
{
|
||||
auto em = dynamic_cast<EDGE_MODULE*>( ds );
|
||||
|
||||
if( em )
|
||||
em->SetLocalCoord();
|
||||
}
|
||||
HelperDrawsegmentSetLocalCoord( ds, elem.component );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1443,6 +1460,14 @@ void ALTIUM_PCB::ParsePads6Data(
|
|||
{
|
||||
APAD6 elem( reader );
|
||||
|
||||
// It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
|
||||
if( !IsAltiumLayerCopper( elem.layer ) && !IsAltiumLayerAPlane( elem.layer )
|
||||
&& elem.layer != ALTIUM_LAYER::MULTI_LAYER )
|
||||
{
|
||||
HelperParsePad6NonCopper( elem );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create Pad
|
||||
MODULE* module = nullptr;
|
||||
if( elem.component == ALTIUM_COMPONENT_NONE )
|
||||
|
@ -1477,12 +1502,6 @@ void ALTIUM_PCB::ParsePads6Data(
|
|||
|
||||
if( elem.holesize == 0 )
|
||||
{
|
||||
if( elem.layer == ALTIUM_LAYER::MULTI_LAYER )
|
||||
{
|
||||
wxLogError( wxString::Format(
|
||||
"Pad '%s' of Footprint %s marked as multilayer, but it is an SMT pad",
|
||||
elem.name, module->GetReference() ) );
|
||||
}
|
||||
pad->SetAttribute( PAD_ATTR_T::PAD_ATTRIB_SMD );
|
||||
}
|
||||
else
|
||||
|
@ -1605,9 +1624,16 @@ void ALTIUM_PCB::ParsePads6Data(
|
|||
pad->SetLayerSet( FlipLayerMask( D_PAD::SMDMask() ) );
|
||||
break;
|
||||
case ALTIUM_LAYER::MULTI_LAYER:
|
||||
default:
|
||||
pad->SetLayerSet( elem.plated ? D_PAD::StandardMask() : D_PAD::UnplatedHoleMask() );
|
||||
break;
|
||||
default:
|
||||
PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
|
||||
wxLogError( wxString::Format(
|
||||
"Pad '%s' of Footprint %s uses the unsupported layer %s. Put it on Eco2_User instead",
|
||||
elem.name, module->GetReference(), LSET::Name( klayer ) ) );
|
||||
pad->SetLayer( klayer );
|
||||
pad->SetLayerSet( LSET( 2, klayer, Eco2_User ) ); // TODO: support inner Cu as pad layer
|
||||
break;
|
||||
}
|
||||
|
||||
if( elem.pastemaskexpansionmode == ALTIUM_PAD_RULE::MANUAL )
|
||||
|
@ -1636,6 +1662,189 @@ void ALTIUM_PCB::ParsePads6Data(
|
|||
}
|
||||
}
|
||||
|
||||
void ALTIUM_PCB::HelperParsePad6NonCopper( const APAD6& aElem )
|
||||
{
|
||||
PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
|
||||
if( klayer == UNDEFINED_LAYER )
|
||||
{
|
||||
wxLogInfo( wxString::Format(
|
||||
_( "Non-Copper Pad on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
|
||||
aElem.layer ) );
|
||||
klayer = Eco1_User;
|
||||
}
|
||||
|
||||
if( aElem.net != ALTIUM_NET_UNCONNECTED )
|
||||
{
|
||||
wxLogError( wxString::Format(
|
||||
"Non-Copper Pad '%s' is connected to a net. This is not supported", aElem.name ) );
|
||||
}
|
||||
|
||||
if( aElem.holesize != 0 )
|
||||
{
|
||||
wxLogError( wxString::Format(
|
||||
"Non-Copper Pad '%s' has a hole. This should not happen", aElem.name ) );
|
||||
}
|
||||
|
||||
if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
|
||||
{
|
||||
wxLogWarning( wxString::Format(
|
||||
_( "Non-Copper Pad '%s' uses a complex pad stack (kind %d). This should not happen" ),
|
||||
aElem.name, aElem.padmode ) );
|
||||
}
|
||||
|
||||
switch( aElem.topshape )
|
||||
{
|
||||
case ALTIUM_PAD_SHAPE::RECT:
|
||||
{
|
||||
// filled rect
|
||||
DRAWSEGMENT* ds = HelperCreateAndAddDrawsegment( aElem.component );
|
||||
ds->SetShape( STROKE_T::S_POLYGON );
|
||||
ds->SetLayer( klayer );
|
||||
ds->SetWidth( 0 );
|
||||
|
||||
ds->SetPolyPoints( { aElem.position + wxPoint( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
|
||||
aElem.position + wxPoint( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
|
||||
aElem.position + wxPoint( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
|
||||
aElem.position + wxPoint( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
|
||||
|
||||
if( aElem.direction != 0 )
|
||||
{
|
||||
ds->Rotate( aElem.position, aElem.direction * 10 );
|
||||
}
|
||||
|
||||
HelperDrawsegmentSetLocalCoord( ds, aElem.component );
|
||||
}
|
||||
break;
|
||||
case ALTIUM_PAD_SHAPE::CIRCLE:
|
||||
if( aElem.sizeAndShape
|
||||
&& aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
|
||||
{
|
||||
// filled roundrect
|
||||
int cornerradius = aElem.sizeAndShape->cornerradius[0];
|
||||
int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
|
||||
|
||||
DRAWSEGMENT* ds = HelperCreateAndAddDrawsegment( aElem.component );
|
||||
ds->SetLayer( klayer );
|
||||
ds->SetWidth( offset * 2 );
|
||||
|
||||
if( cornerradius < 100 )
|
||||
{
|
||||
int offsetX = aElem.topsize.x / 2 - offset;
|
||||
int offsetY = aElem.topsize.y / 2 - offset;
|
||||
|
||||
wxPoint p11 = aElem.position + wxPoint( offsetX, offsetY );
|
||||
wxPoint p12 = aElem.position + wxPoint( offsetX, -offsetY );
|
||||
wxPoint p22 = aElem.position + wxPoint( -offsetX, -offsetY );
|
||||
wxPoint p21 = aElem.position + wxPoint( -offsetX, offsetY );
|
||||
|
||||
ds->SetShape( STROKE_T::S_POLYGON );
|
||||
ds->SetPolyPoints( { p11, p12, p22, p21 } );
|
||||
}
|
||||
else if( aElem.topsize.x == aElem.topsize.y )
|
||||
{
|
||||
// circle
|
||||
ds->SetShape( STROKE_T::S_CIRCLE );
|
||||
ds->SetCenter( aElem.position );
|
||||
ds->SetWidth( aElem.topsize.x / 2 );
|
||||
ds->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
|
||||
}
|
||||
else if( aElem.topsize.x < aElem.topsize.y )
|
||||
{
|
||||
// short vertical line
|
||||
ds->SetShape( STROKE_T::S_SEGMENT );
|
||||
wxPoint pointOffset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
|
||||
ds->SetStart( aElem.position + pointOffset );
|
||||
ds->SetEnd( aElem.position - pointOffset );
|
||||
}
|
||||
else
|
||||
{
|
||||
// short horizontal line
|
||||
ds->SetShape( STROKE_T::S_SEGMENT );
|
||||
wxPoint pointOffset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
|
||||
ds->SetStart( aElem.position + pointOffset );
|
||||
ds->SetEnd( aElem.position - pointOffset );
|
||||
}
|
||||
|
||||
if( aElem.direction != 0 )
|
||||
{
|
||||
ds->Rotate( aElem.position, aElem.direction * 10 );
|
||||
}
|
||||
|
||||
HelperDrawsegmentSetLocalCoord( ds, aElem.component );
|
||||
}
|
||||
else if( aElem.topsize.x == aElem.topsize.y )
|
||||
{
|
||||
// filled circle
|
||||
DRAWSEGMENT* ds = HelperCreateAndAddDrawsegment( aElem.component );
|
||||
ds->SetShape( STROKE_T::S_CIRCLE );
|
||||
ds->SetLayer( klayer );
|
||||
ds->SetCenter( aElem.position );
|
||||
ds->SetWidth( aElem.topsize.x / 2 );
|
||||
ds->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
|
||||
HelperDrawsegmentSetLocalCoord( ds, aElem.component );
|
||||
}
|
||||
else
|
||||
{
|
||||
// short line
|
||||
DRAWSEGMENT* ds = HelperCreateAndAddDrawsegment( aElem.component );
|
||||
ds->SetShape( STROKE_T::S_SEGMENT );
|
||||
ds->SetLayer( klayer );
|
||||
ds->SetWidth( std::min( aElem.topsize.x, aElem.topsize.y ) );
|
||||
if( aElem.topsize.x < aElem.topsize.y )
|
||||
{
|
||||
wxPoint offset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
|
||||
ds->SetStart( aElem.position + offset );
|
||||
ds->SetEnd( aElem.position - offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxPoint offset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
|
||||
ds->SetStart( aElem.position + offset );
|
||||
ds->SetEnd( aElem.position - offset );
|
||||
}
|
||||
if( aElem.direction != 0 )
|
||||
{
|
||||
ds->Rotate( aElem.position, aElem.direction * 10. );
|
||||
}
|
||||
HelperDrawsegmentSetLocalCoord( ds, aElem.component );
|
||||
}
|
||||
break;
|
||||
case ALTIUM_PAD_SHAPE::OCTAGONAL:
|
||||
{
|
||||
// filled octagon
|
||||
DRAWSEGMENT* ds = HelperCreateAndAddDrawsegment( aElem.component );
|
||||
ds->SetShape( STROKE_T::S_POLYGON );
|
||||
ds->SetLayer( klayer );
|
||||
ds->SetWidth( 0 );
|
||||
|
||||
wxPoint p11 = aElem.position + wxPoint( aElem.topsize.x / 2, aElem.topsize.y / 2 );
|
||||
wxPoint p12 = aElem.position + wxPoint( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
|
||||
wxPoint p22 = aElem.position + wxPoint( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
|
||||
wxPoint p21 = aElem.position + wxPoint( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
|
||||
|
||||
int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
|
||||
wxPoint chamferX( chamfer, 0 );
|
||||
wxPoint chamferY( 0, chamfer );
|
||||
|
||||
ds->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
|
||||
p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
|
||||
|
||||
if( aElem.direction != 0. )
|
||||
{
|
||||
ds->Rotate( aElem.position, aElem.direction * 10 );
|
||||
}
|
||||
|
||||
HelperDrawsegmentSetLocalCoord( ds, aElem.component );
|
||||
}
|
||||
break;
|
||||
case ALTIUM_PAD_SHAPE::UNKNOWN:
|
||||
default:
|
||||
wxLogError(
|
||||
wxString::Format( "Non-Copper Pad '%s' uses a unknown pad-shape", aElem.name ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ALTIUM_PCB::ParseVias6Data(
|
||||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
|
||||
{
|
||||
|
@ -1758,39 +1967,13 @@ void ALTIUM_PCB::ParseTracks6Data(
|
|||
}
|
||||
else
|
||||
{
|
||||
DRAWSEGMENT* ds = nullptr;
|
||||
|
||||
if( elem.component == ALTIUM_COMPONENT_NONE )
|
||||
{
|
||||
ds = new DRAWSEGMENT( m_board );
|
||||
DRAWSEGMENT* ds = HelperCreateAndAddDrawsegment( elem.component );
|
||||
ds->SetShape( STROKE_T::S_SEGMENT );
|
||||
m_board->Add( ds, ADD_MODE::APPEND );
|
||||
|
||||
ds->SetStart( elem.start );
|
||||
ds->SetEnd( elem.end );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_components.size() <= elem.component )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format(
|
||||
"Tracks6 stream tries to access component id %d of %d existing components",
|
||||
elem.component, m_components.size() ) );
|
||||
}
|
||||
MODULE* module = m_components.at( elem.component );
|
||||
EDGE_MODULE* em = new EDGE_MODULE( module, STROKE_T::S_SEGMENT );
|
||||
module->Add( em, ADD_MODE::APPEND );
|
||||
|
||||
em->SetStart( elem.start );
|
||||
em->SetEnd( elem.end );
|
||||
em->SetLocalCoord();
|
||||
|
||||
ds = em;
|
||||
}
|
||||
|
||||
ds->SetWidth( elem.width );
|
||||
|
||||
ds->SetLayer( klayer );
|
||||
HelperDrawsegmentSetLocalCoord( ds, elem.component );
|
||||
}
|
||||
|
||||
reader.SkipSubrecord();
|
||||
|
@ -2027,6 +2210,7 @@ void ALTIUM_PCB::ParseFills6Data(
|
|||
|
||||
ds->SetShape( STROKE_T::S_POLYGON );
|
||||
ds->SetLayer( klayer );
|
||||
ds->SetWidth( 0 );
|
||||
|
||||
ds->SetPolyPoints( { p11, p12, p22, p21 } );
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ enum class ALTIUM_PCB_DIR
|
|||
|
||||
|
||||
class BOARD;
|
||||
class DRAWSEGMENT;
|
||||
class MODULE;
|
||||
class ZONE_CONTAINER;
|
||||
|
||||
|
@ -170,8 +171,13 @@ private:
|
|||
void HelperParseDimensions6Datum( const ADIMENSION6& aElem );
|
||||
void HelperParseDimensions6Center( const ADIMENSION6& aElem );
|
||||
|
||||
void HelperParsePad6NonCopper( const APAD6& aElem );
|
||||
|
||||
void HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices );
|
||||
|
||||
DRAWSEGMENT* HelperCreateAndAddDrawsegment( uint16_t aComponent );
|
||||
void HelperDrawsegmentSetLocalCoord( DRAWSEGMENT* aDs, uint16_t aComponent );
|
||||
|
||||
BOARD* m_board;
|
||||
std::vector<MODULE*> m_components;
|
||||
std::vector<ZONE_CONTAINER*> m_polygons;
|
||||
|
|
Loading…
Reference in New Issue