kicad-source/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp
Wayne Stambaugh 795a9eea60 Coding policy fixes.
This is primarily to change all instances of wxLogDebug with wxLogTrace
so developers do not have to sift through debugging output that is always
dumped.  The only exception is for code blocks built in debug builds and
called on demand for dumping object states.
2024-05-23 07:59:45 -04:00

3189 lines
105 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 BeagleBoard Foundation
* Copyright (C) 2020-2023, 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: Seth Hillbrand <hillbrand@kipro-pcb.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "import_fabmaster.h"
#include <algorithm>
#include <array>
#include <iostream>
#include <fstream>
#include <map>
#include <memory>
#include <string>
#include <sstream>
#include <vector>
#include <utility>
#include <wx/log.h>
#include <board.h>
#include <board_design_settings.h>
#include <board_item.h>
#include <footprint.h>
#include <pad.h>
#include <padstack.h>
#include <pcb_shape.h>
#include <pcb_text.h>
#include <pcb_track.h>
#include <zone.h>
#include <common.h>
#include <geometry/shape_arc.h>
#include <string_utils.h>
#include <progress_reporter.h>
#include <math/util.h>
#include <wx/filename.h>
/**
* Flag to enable #FABMASTER plugin debugging output.
*
* @ingroup trace_env_vars
*/
static const wxChar traceFabmaster[] = wxT( "KICAD_FABMASTER" );
void FABMASTER::checkpoint()
{
const unsigned PROGRESS_DELTA = 250;
if( m_progressReporter )
{
if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
{
m_progressReporter->SetCurrentProgress( ( (double) m_doneCount )
/ std::max( 1U, m_totalCount ) );
if( !m_progressReporter->KeepRefreshing() )
THROW_IO_ERROR( _( "Open cancelled by user." ) );
m_lastProgressCount = m_doneCount;
}
}
}
double FABMASTER::readDouble( const std::string& aStr ) const
{
std::istringstream istr( aStr );
istr.imbue( std::locale::classic() );
double doubleValue;
istr >> doubleValue;
return doubleValue;
}
int FABMASTER::readInt( const std::string& aStr ) const
{
std::istringstream istr( aStr );
istr.imbue( std::locale::classic() );
int intValue;
istr >> intValue;
return intValue;
}
bool FABMASTER::Read( const std::string& aFile )
{
std::ifstream ifs( aFile, std::ios::in | std::ios::binary );
if( !ifs.is_open() )
return false;
m_filename = aFile;
// Read/ignore all bytes in the file to find the size and then go back to the beginning
ifs.ignore( std::numeric_limits<std::streamsize>::max() );
std::streamsize length = ifs.gcount();
ifs.clear();
ifs.seekg( 0, std::ios_base::beg );
std::string buffer( std::istreambuf_iterator<char>{ ifs }, {} );
std::vector < std::string > row;
// Reserve an estimate of the number of rows to prevent continual re-allocation
// crashing (Looking at you MSVC)
row.reserve( length / 100 );
std::string cell;
cell.reserve( 100 );
bool quoted = false;
for( auto& ch : buffer )
{
switch( ch )
{
case '"':
if( cell.empty() || cell[0] == '"' )
quoted = !quoted;
cell += ch;
break;
case '!':
if( !quoted )
{
row.push_back( cell );
cell.clear();
}
else
cell += ch;
break;
case '\n':
/// Rows end with "!" and we don't want to keep the empty cell
if( !cell.empty() )
row.push_back( cell );
cell.clear();
rows.push_back( row );
row.clear();
quoted = false;
break;
case '\r':
break;
default:
cell += std::toupper( ch );
}
}
// Handle last line without linebreak
if( !cell.empty() || !row.empty() )
{
row.push_back( cell );
cell.clear();
rows.push_back( row );
row.clear();
}
return true;
}
FABMASTER::section_type FABMASTER::detectType( size_t aOffset )
{
single_row row;
try
{
row = rows.at( aOffset );
}
catch( std::out_of_range& )
{
return UNKNOWN_EXTRACT;
}
if( row.size() < 3 )
return UNKNOWN_EXTRACT;
if( row[0].back() != 'A' )
return UNKNOWN_EXTRACT;
std::string row1 = row[1];
std::string row2 = row[2];
std::string row3{};
/// We strip the underscores from all column names as some export variants use them and
// some do not
alg::delete_if( row1, []( char c ){ return c == '_'; } );
alg::delete_if( row2, []( char c ){ return c == '_'; } );
if( row.size() > 3 )
{
row3 = row[3];
alg::delete_if( row3, []( char c ){ return c == '_'; } );
}
if( row1 == "REFDES" && row2 == "COMPCLASS" )
return EXTRACT_REFDES;
if( row1 == "NETNAME" && row2 == "REFDES" )
return EXTRACT_NETS;
if( row1 == "CLASS" && row2 == "SUBCLASS" && row3.empty() )
return EXTRACT_BASIC_LAYERS;
if( row1 == "GRAPHICDATANAME" && row2 == "GRAPHICDATANUMBER" )
return EXTRACT_GRAPHICS;
if( row1 == "CLASS" && row2 == "SUBCLASS" && row3 == "GRAPHICDATANAME" )
return EXTRACT_TRACES;
if( row1 == "SYMNAME" && row2 == "PINNAME" )
return FABMASTER_EXTRACT_PINS;
if( row1 == "SYMNAME" && row2 == "SYMMIRROR" && row3 == "PINNAME" )
return EXTRACT_PINS;
if( row1 == "VIAX" && row2 == "VIAY" )
return EXTRACT_VIAS;
if( row1 == "SUBCLASS" && row2 == "PADSHAPENAME" )
return EXTRACT_PAD_SHAPES;
if( row1 == "PADNAME" )
return EXTRACT_PADSTACKS;
if( row1 == "LAYERSORT" )
return EXTRACT_FULL_LAYERS;
wxLogError( _( "Unknown FABMASTER section %s:%s at row %zu." ),
row1.c_str(),
row2.c_str(),
aOffset );
return UNKNOWN_EXTRACT;
}
double FABMASTER::processScaleFactor( size_t aRow )
{
double retval = 0.0;
if( aRow >= rows.size() )
return -1.0;
if( rows[aRow].size() < 11 )
{
wxLogError( _( "Invalid row size in J row %zu. Expecting 11 elements but found %zu." ),
aRow,
rows[aRow].size() );
return -1.0;
}
for( int i = 7; i < 10 && retval < 1.0; ++i )
{
std::string units = rows[aRow][i];
std::transform(units.begin(), units.end(),units.begin(), ::toupper);
if( units == "MILS" )
retval = pcbIUScale.IU_PER_MILS;
else if( units == "MILLIMETERS" )
retval = pcbIUScale.IU_PER_MM;
else if( units == "MICRONS" )
retval = pcbIUScale.IU_PER_MM * 10.0;
else if( units == "INCHES" )
retval = pcbIUScale.IU_PER_MILS * 1000.0;
}
if( retval < 1.0 )
{
wxLogError( _( "Could not find units value, defaulting to mils." ) );
retval = pcbIUScale.IU_PER_MILS;
}
return retval;
}
int FABMASTER::getColFromName( size_t aRow, const std::string& aStr )
{
if( aRow >= rows.size() )
return -1;
std::vector<std::string> header = rows[aRow];
for( size_t i = 0; i < header.size(); i++ )
{
/// Some Fabmaster headers include the underscores while others do not
/// so we strip them uniformly before comparing
alg::delete_if( header[i], []( const char c ) { return c == '_'; } );
if( header[i] == aStr )
return i;
}
THROW_IO_ERROR( wxString::Format( _( "Could not find column label %s." ), aStr.c_str() ) );
return -1;
}
PCB_LAYER_ID FABMASTER::getLayer( const std::string& aLayerName )
{
const auto& kicad_layer = layers.find( aLayerName);
if( kicad_layer == layers.end() )
return UNDEFINED_LAYER;
else
return static_cast<PCB_LAYER_ID>( kicad_layer->second.layerid );
}
size_t FABMASTER::processPadStackLayers( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
int pad_name_col = getColFromName( aRow, "PADNAME" );
int pad_num_col = getColFromName( aRow, "RECNUMBER" );
int pad_lay_col = getColFromName( aRow, "LAYER" );
int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
int pad_via_col = getColFromName( aRow, "VIAFLAG" );
int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
int pad_width_col = getColFromName( aRow, "PADWIDTH" );
int pad_height_col = getColFromName( aRow, "PADHGHT" );
int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
int pad_flash_col = getColFromName( aRow, "PADFLASH" );
int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto& pad_name = row[pad_name_col];
auto& pad_num = row[pad_num_col];
auto& pad_layer = row[pad_lay_col];
auto& pad_is_fixed = row[pad_fix_col];
auto& pad_is_via = row[pad_via_col];
auto& pad_shape = row[pad_shape_col];
auto& pad_width = row[pad_width_col];
auto& pad_height = row[pad_height_col];
auto& pad_xoff = row[pad_xoff_col];
auto& pad_yoff = row[pad_yoff_col];
auto& pad_flash = row[pad_flash_col];
auto& pad_shapename = row[pad_shape_name_col];
// This layer setting seems to be unused
if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
continue;
// Skip the technical layers
if( pad_layer[0] == '~' )
break;
auto result = layers.emplace( pad_layer, FABMASTER_LAYER{} );
FABMASTER_LAYER& layer = result.first->second;
/// If the layer ids have not yet been assigned
if( layer.id == 0 )
{
layer.name = pad_layer;
layer.id = readInt( pad_num );
layer.conductive = true;
}
}
return 0;
}
/**
* A!PADNAME!RECNUMBER!LAYER!FIXFLAG!VIAFLAG!PADSHAPE1!PADWIDTH!PADHGHT!
* PADXOFF!PADYOFF!PADFLASH!PADSHAPENAME!TRELSHAPE1!TRELWIDTH!TRELHGHT!
* TRELXOFF!TRELYOFF!TRELFLASH!TRELSHAPENAME!APADSHAPE1!APADWIDTH!APADHGHT!
* APADXOFF!APADYOFF!APADFLASH!APADSHAPENAME!
*/
size_t FABMASTER::processPadStacks( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int pad_name_col = getColFromName( aRow, "PADNAME" );
int pad_num_col = getColFromName( aRow, "RECNUMBER" );
int pad_lay_col = getColFromName( aRow, "LAYER" );
int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
int pad_via_col = getColFromName( aRow, "VIAFLAG" );
int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
int pad_width_col = getColFromName( aRow, "PADWIDTH" );
int pad_height_col = getColFromName( aRow, "PADHGHT" );
int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
int pad_flash_col = getColFromName( aRow, "PADFLASH" );
int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
FM_PAD* pad;
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto& pad_name = row[pad_name_col];
auto& pad_num = row[pad_num_col];
auto& pad_layer = row[pad_lay_col];
auto& pad_is_fixed = row[pad_fix_col];
auto& pad_is_via = row[pad_via_col];
auto& pad_shape = row[pad_shape_col];
auto& pad_width = row[pad_width_col];
auto& pad_height = row[pad_height_col];
auto& pad_xoff = row[pad_xoff_col];
auto& pad_yoff = row[pad_yoff_col];
auto& pad_flash = row[pad_flash_col];
auto& pad_shapename = row[pad_shape_name_col];
// This layer setting seems to be unused
if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
continue;
int recnum = KiROUND( readDouble( pad_num ) );
auto new_pad = pads.find( pad_name );
if( new_pad != pads.end() )
pad = &new_pad->second;
else
{
pads[pad_name] = FM_PAD();
pad = &pads[pad_name];
pad->name = pad_name;
}
/// Handle the drill layer
if( pad_layer == "~DRILL" )
{
int drill_hit;
int drill_x;
int drill_y;
try
{
drill_hit = KiROUND( std::fabs( readDouble( pad_shape ) * scale_factor ) );
drill_x = KiROUND( std::fabs( readDouble( pad_width ) * scale_factor ) );
drill_y = KiROUND( std::fabs( readDouble( pad_height ) * scale_factor ) );
}
catch( ... )
{
wxLogError( _( "Expecting drill size value but found %s!%s!%s in row %zu." ),
pad_shape.c_str(),
pad_width.c_str(),
pad_height.c_str(),
rownum );
continue;
}
if( drill_hit == 0 )
{
pad->drill = false;
continue;
}
pad->drill = true;
// This is to account for broken fabmaster outputs where circle drill hits don't
// actually get the drill hit value.
if( drill_x == drill_y )
{
pad->drill_size_x = drill_hit;
pad->drill_size_y = drill_hit;
}
else
{
pad->drill_size_x = drill_x;
pad->drill_size_y = drill_y;
}
if( !pad_shapename.empty() && pad_shapename[0] == 'P' )
pad->plated = true;
continue;
}
if( pad_shape.empty() )
continue;
double w;
double h;
try
{
w = readDouble( pad_width ) * scale_factor;
h = readDouble( pad_height ) * scale_factor;
}
catch( ... )
{
wxLogError( _( "Expecting pad size values but found %s : %s in row %zu." ),
pad_width.c_str(),
pad_height.c_str(),
rownum );
continue;
}
if( w <= 0.0 )
continue;
auto layer = layers.find( pad_layer );
if( layer != layers.end() )
{
if( layer->second.layerid == F_Cu )
pad->top = true;
else if( layer->second.layerid == B_Cu )
pad->bottom = true;
}
if( w > std::numeric_limits<int>::max() || h > std::numeric_limits<int>::max() )
{
wxLogError( _( "Invalid pad size in row %zu." ), rownum );
continue;
}
if( pad_layer == "~TSM" || pad_layer == "~BSM" )
{
if( w > 0.0 && h > 0.0 )
{
pad->mask_width = KiROUND( w );
pad->mask_height = KiROUND( h );
}
continue;
}
if( pad_layer == "~TSP" || pad_layer == "~BSP" )
{
if( w > 0.0 && h > 0.0 )
{
pad->paste_width = KiROUND( w );
pad->paste_height = KiROUND( h );
}
continue;
}
/// All remaining technical layers are not handled
if( pad_layer[0] == '~' )
continue;
try
{
pad->x_offset = KiROUND( readDouble( pad_xoff ) * scale_factor );
pad->y_offset = -KiROUND( readDouble( pad_yoff ) * scale_factor );
}
catch( ... )
{
wxLogError( _( "Expecting pad offset values but found %s:%s in row %zu." ),
pad_xoff.c_str(),
pad_yoff.c_str(),
rownum );
continue;
}
if( w > 0.0 && h > 0.0 && recnum == 1 )
{
pad->width = KiROUND( w );
pad->height = KiROUND( h );
pad->via = ( std::toupper( pad_is_via[0] ) != 'V' );
if( pad_shape == "CIRCLE" )
{
pad->height = pad->width;
pad->shape = PAD_SHAPE::CIRCLE;
}
else if( pad_shape == "RECTANGLE" )
{
pad->shape = PAD_SHAPE::RECTANGLE;
}
else if( pad_shape == "ROUNDED_RECT" )
{
pad->shape = PAD_SHAPE::ROUNDRECT;
}
else if( pad_shape == "SQUARE" )
{
pad->shape = PAD_SHAPE::RECTANGLE;
pad->height = pad->width;
}
else if( pad_shape == "OBLONG" || pad_shape == "OBLONG_X" || pad_shape == "OBLONG_Y" )
pad->shape = PAD_SHAPE::OVAL;
else if( pad_shape == "OCTAGON" )
{
pad->shape = PAD_SHAPE::RECTANGLE;
pad->is_octogon = true;
}
else if( pad_shape == "SHAPE" )
{
pad->shape = PAD_SHAPE::CUSTOM;
pad->custom_name = pad_shapename;
}
else
{
wxLogError( _( "Unknown pad shape name '%s' on layer '%s' in row %zu." ),
pad_shape.c_str(),
pad_layer.c_str(),
rownum );
continue;
}
}
}
return rownum - aRow;
}
size_t FABMASTER::processSimpleLayers( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
auto& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int layer_class_col = getColFromName( aRow, "CLASS" );
int layer_subclass_col = getColFromName( aRow, "SUBCLASS" );
if( layer_class_col < 0 || layer_subclass_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto result = layers.emplace( row[layer_subclass_col], FABMASTER_LAYER{} );
FABMASTER_LAYER& layer = result.first->second;
layer.name = row[layer_subclass_col];
layer.positive = true;
layer.conductive = false;
if( row[layer_class_col] == "ANTI ETCH" )
{
layer.positive = false;
layer.conductive = true;
}
else if( row[layer_class_col] == "ETCH" )
{
layer.conductive = true;
}
}
return rownum - aRow;
}
bool FABMASTER::assignLayers()
{
bool has_l1 = false;
int max_layer = 0;
std::string max_layer_name;
std::vector<std::pair<std::string, int>> extra_layers
{
{ "ASSEMBLY_TOP", F_Fab },
{ "ASSEMBLY_BOTTOM", B_Fab },
{ "PLACE_BOUND_TOP", F_CrtYd },
{ "PLACE_BOUND_BOTTOM", B_CrtYd },
};
std::vector<FABMASTER_LAYER*> layer_order;
for( auto& el : layers )
{
FABMASTER_LAYER& layer = el.second;
layer.layerid = UNSELECTED_LAYER;
if( layer.conductive )
{
layer_order.push_back( &layer );
}
else if( layer.name.find( "SILK" ) != std::string::npos &&
layer.name.find( "AUTOSILK" ) == std::string::npos ) // Skip the autosilk layer
{
if( layer.name.find( "B" ) != std::string::npos )
layer.layerid = B_SilkS;
else
layer.layerid = F_SilkS;
}
else if( layer.name.find( "MASK" ) != std::string::npos ||
layer.name.find( "MSK" ) != std::string::npos )
{
if( layer.name.find( "B" ) != std::string::npos )
layer.layerid = B_Mask;
else
layer.layerid = F_Mask;
}
else if( layer.name.find( "PAST" ) != std::string::npos )
{
if( layer.name.find( "B" ) != std::string::npos )
layer.layerid = B_Paste;
else
layer.layerid = F_Paste;
}
else if( layer.name.find( "NCLEGEND" ) != std::string::npos )
layer.layerid = Dwgs_User;
else
layer.disable = true;
}
std::sort( layer_order.begin(), layer_order.end(), FABMASTER_LAYER::BY_ID() );
int layernum = 0;
for( auto layer : layer_order )
layer->layerid = layernum++;
/// Back copper has a special id number, so assign that to the last copper layer
/// in the stackup
layer_order.back()->layerid = B_Cu;
for( auto& new_pair : extra_layers )
{
FABMASTER_LAYER new_layer;
new_layer.name = new_pair.first;
new_layer.layerid = new_pair.second;
new_layer.conductive = false;
auto result = layers.emplace( new_pair.first, new_layer );
if( !result.second )
{
result.first->second.layerid = new_pair.second;
result.first->second.disable = false;
}
}
return true;
}
/**
* A!LAYER_SORT!LAYER_SUBCLASS!LAYER_ARTWORK!LAYER_USE!LAYER_CONDUCTOR!LAYER_DIELECTRIC_CONSTANT!
* LAYER_ELECTRICAL_CONDUCTIVITY!LAYER_MATERIAL!LAYER_SHIELD_LAYER!LAYER_THERMAL_CONDUCTIVITY!
* LAYER_THICKNESS!
*/
size_t FABMASTER::processLayers( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
auto& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int layer_sort_col = getColFromName( aRow, "LAYERSORT" );
int layer_subclass_col = getColFromName( aRow, "LAYERSUBCLASS" );
int layer_art_col = getColFromName( aRow, "LAYERARTWORK" );
int layer_use_col = getColFromName( aRow, "LAYERUSE" );
int layer_cond_col = getColFromName( aRow, "LAYERCONDUCTOR" );
int layer_er_col = getColFromName( aRow, "LAYERDIELECTRICCONSTANT" );
int layer_rho_col = getColFromName( aRow, "LAYERELECTRICALCONDUCTIVITY" );
int layer_mat_col = getColFromName( aRow, "LAYERMATERIAL" );
if( layer_sort_col < 0 || layer_subclass_col < 0 || layer_art_col < 0 || layer_use_col < 0
|| layer_cond_col < 0 || layer_er_col < 0 || layer_rho_col < 0 || layer_mat_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto& layer_sort = row[layer_sort_col];
auto& layer_subclass = row[layer_subclass_col];
auto& layer_art = row[layer_art_col];
auto& layer_use = row[layer_use_col];
auto& layer_cond = row[layer_cond_col];
auto& layer_er = row[layer_er_col];
auto& layer_rho = row[layer_rho_col];
auto& layer_mat = row[layer_mat_col];
if( layer_mat == "AIR" )
continue;
FABMASTER_LAYER layer;
if( layer_subclass.empty() )
{
if( layer_cond != "NO" )
layer.name = "In.Cu" + layer_sort;
else
layer.name = "Dielectric" + layer_sort;
}
layer.positive = ( layer_art != "NEGATIVE" );
layers.emplace( layer.name, layer );
}
return rownum - aRow;
}
/**
* A!SUBCLASS!PAD_SHAPE_NAME!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!
* GRAPHIC_DATA_2!GRAPHIC_DATA_3!GRAPHIC_DATA_4!GRAPHIC_DATA_5!GRAPHIC_DATA_6!GRAPHIC_DATA_7!
* GRAPHIC_DATA_8!GRAPHIC_DATA_9!PAD_STACK_NAME!REFDES!PIN_NUMBER!
*/
size_t FABMASTER::processCustomPads( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
auto& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int pad_subclass_col = getColFromName( aRow, "SUBCLASS" );
int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
int pad_grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
int pad_grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
int pad_record_tag_col = getColFromName( aRow, "RECORDTAG" );
int pad_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
int pad_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
int pad_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
int pad_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
int pad_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
int pad_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
int pad_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
int pad_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
int pad_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
int pad_stack_name_col = getColFromName( aRow, "PADSTACKNAME" );
int pad_refdes_col = getColFromName( aRow, "REFDES" );
int pad_pin_num_col = getColFromName( aRow, "PINNUMBER" );
if( pad_subclass_col < 0 || pad_shape_name_col < 0 || pad_grdata1_col < 0 || pad_grdata2_col < 0
|| pad_grdata3_col < 0 || pad_grdata4_col < 0 || pad_grdata5_col < 0
|| pad_grdata6_col < 0 || pad_grdata7_col < 0 || pad_grdata8_col < 0
|| pad_grdata9_col < 0 || pad_stack_name_col < 0 || pad_refdes_col < 0
|| pad_pin_num_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto& pad_layer = row[pad_subclass_col];
auto pad_shape_name = row[pad_shape_name_col];
auto& pad_record_tag = row[pad_record_tag_col];
GRAPHIC_DATA gr_data;
gr_data.graphic_dataname = row[pad_grdata_name_col];
gr_data.graphic_datanum = row[pad_grdata_num_col];
gr_data.graphic_data1 = row[pad_grdata1_col];
gr_data.graphic_data2 = row[pad_grdata2_col];
gr_data.graphic_data3 = row[pad_grdata3_col];
gr_data.graphic_data4 = row[pad_grdata4_col];
gr_data.graphic_data5 = row[pad_grdata5_col];
gr_data.graphic_data6 = row[pad_grdata6_col];
gr_data.graphic_data7 = row[pad_grdata7_col];
gr_data.graphic_data8 = row[pad_grdata8_col];
gr_data.graphic_data9 = row[pad_grdata9_col];
auto& pad_stack_name = row[pad_stack_name_col];
auto& pad_refdes = row[pad_refdes_col];
auto& pad_pin_num = row[pad_pin_num_col];
// N.B. We get the FIGSHAPE records as "FIG_SHAPE name". We only want "name"
// and we don't process other pad shape records
std::string prefix( "FIG_SHAPE " );
if( pad_shape_name.length() <= prefix.length()
|| !std::equal( prefix.begin(), prefix.end(), pad_shape_name.begin() ) )
{
continue;
}
// Custom pads are a series of records with the same record ID but incrementing
// Sequence numbers.
int id = -1;
int seq = -1;
if( std::sscanf( pad_record_tag.c_str(), "%d %d", &id, &seq ) != 2 )
{
wxLogError( _( "Invalid format for id string '%s' in custom pad row %zu." ),
pad_record_tag.c_str(),
rownum );
continue;
}
auto name = pad_shape_name.substr( prefix.length() );
name += "_" + pad_refdes + "_" + pad_pin_num;
auto ret = pad_shapes.emplace( name, FABMASTER_PAD_SHAPE{} );
auto& custom_pad = ret.first->second;
// If we were able to insert the pad name, then we need to initialize the
// record
if( ret.second )
{
custom_pad.name = name;
custom_pad.padstack = pad_stack_name;
custom_pad.pinnum = pad_pin_num;
custom_pad.refdes = pad_refdes;
}
// At this point we extract the individual graphical elements for processing the complex
// pad. The coordinates are in board origin format, so we'll need to fix the offset later
// when we assign them to the modules.
auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
if( gr_item )
{
gr_item->layer = pad_layer;
gr_item->refdes = pad_refdes;
gr_item->seq = seq;
gr_item->subseq = 0;
// emplace may fail here, in which case, it returns the correct position to use for
// the existing map
auto pad_it = custom_pad.elements.emplace( id, graphic_element{} );
auto retval = pad_it.first->second.insert( std::move(gr_item ) );
if( !retval.second )
{
wxLogError( _( "Could not insert graphical item %d into padstack '%s'." ),
seq,
pad_stack_name.c_str() );
}
}
else
{
wxLogError( _( "Unrecognized pad shape primitive '%s' in row %zu." ),
gr_data.graphic_dataname,
rownum );
}
}
return rownum - aRow;
}
FABMASTER::GRAPHIC_LINE* FABMASTER::processLine( const FABMASTER::GRAPHIC_DATA& aData,
double aScale )
{
GRAPHIC_LINE* new_line = new GRAPHIC_LINE ;
new_line->shape = GR_SHAPE_LINE;
new_line->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
new_line->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
new_line->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
new_line->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
new_line->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
return new_line;
}
FABMASTER::GRAPHIC_ARC* FABMASTER::processArc( const FABMASTER::GRAPHIC_DATA& aData, double aScale )
{
GRAPHIC_ARC* new_arc = new GRAPHIC_ARC ;
new_arc->shape = GR_SHAPE_ARC;
new_arc->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
new_arc->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
new_arc->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
new_arc->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
new_arc->center_x = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
new_arc->center_y = -KiROUND( readDouble( aData.graphic_data6 ) * aScale );
new_arc->radius = KiROUND( readDouble( aData.graphic_data7 ) * aScale );
new_arc->width = KiROUND( readDouble( aData.graphic_data8 ) * aScale );
new_arc->clockwise = ( aData.graphic_data9 != "COUNTERCLOCKWISE" );
EDA_ANGLE startangle( VECTOR2I( new_arc->start_x, new_arc->start_y )
- VECTOR2I( new_arc->center_x, new_arc->center_y ) );
EDA_ANGLE endangle( VECTOR2I( new_arc->end_x, new_arc->end_y )
- VECTOR2I( new_arc->center_x, new_arc->center_y ) );
EDA_ANGLE angle;
startangle.Normalize();
endangle.Normalize();
VECTOR2I center( new_arc->center_x, new_arc->center_y );
VECTOR2I start( new_arc->start_x, new_arc->start_y );
VECTOR2I mid( new_arc->start_x, new_arc->start_y );
VECTOR2I end( new_arc->end_x, new_arc->end_y );
angle = endangle - startangle;
if( new_arc->clockwise && angle < ANGLE_0 )
angle += ANGLE_360;
if( !new_arc->clockwise && angle > ANGLE_0 )
angle -= ANGLE_360;
if( start == end )
angle = -ANGLE_360;
RotatePoint( mid, center, -angle / 2.0 );
if( start == end )
new_arc->shape = GR_SHAPE_CIRCLE;
new_arc->result = SHAPE_ARC( start, mid, end, 0 );
return new_arc;
}
FABMASTER::GRAPHIC_RECTANGLE* FABMASTER::processRectangle( const FABMASTER::GRAPHIC_DATA& aData,
double aScale )
{
GRAPHIC_RECTANGLE* new_rect = new GRAPHIC_RECTANGLE;
new_rect->shape = GR_SHAPE_RECTANGLE;
new_rect->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
new_rect->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
new_rect->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
new_rect->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
new_rect->fill = aData.graphic_data5 == "1";
new_rect->width = 0;
return new_rect;
}
FABMASTER::GRAPHIC_TEXT* FABMASTER::processText( const FABMASTER::GRAPHIC_DATA& aData,
double aScale )
{
GRAPHIC_TEXT* new_text = new GRAPHIC_TEXT;
new_text->shape = GR_SHAPE_TEXT;
new_text->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
new_text->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
new_text->rotation = KiROUND( readDouble( aData.graphic_data3 ) );
new_text->mirror = ( aData.graphic_data4 == "YES" );
if( aData.graphic_data5 == "RIGHT" )
new_text->orient = GR_TEXT_H_ALIGN_RIGHT;
else if( aData.graphic_data5 == "CENTER" )
new_text->orient = GR_TEXT_H_ALIGN_CENTER;
else
new_text->orient = GR_TEXT_H_ALIGN_LEFT;
std::vector<std::string> toks = split( aData.graphic_data6, " \t" );
if( toks.size() < 8 )
{
// We log the error here but continue in the case of too few tokens
wxLogError( _( "Invalid token count. Expected 8 but found %zu." ), toks.size() );
new_text->height = 0;
new_text->width = 0;
new_text->ital = false;
new_text->thickness = 0;
}
else
{
// 0 = size
// 1 = font
new_text->height = KiROUND( readDouble( toks[2] ) * aScale );
new_text->width = KiROUND( readDouble( toks[3] ) * aScale );
new_text->ital = readDouble( toks[4] ) != 0.0;
// 5 = character spacing
// 6 = line spacing
new_text->thickness = KiROUND( readDouble( toks[7] ) * aScale );
}
new_text->text = aData.graphic_data7;
return new_text;
}
FABMASTER::GRAPHIC_ITEM* FABMASTER::processGraphic( const GRAPHIC_DATA& aData, double aScale )
{
GRAPHIC_ITEM* retval = nullptr;
if( aData.graphic_dataname == "LINE" )
retval = processLine( aData, aScale );
else if( aData.graphic_dataname == "ARC" )
retval = processArc( aData, aScale );
else if( aData.graphic_dataname == "RECTANGLE" )
retval = processRectangle( aData, aScale );
else if( aData.graphic_dataname == "TEXT" )
retval = processText( aData, aScale );
if( retval && !aData.graphic_data10.empty() )
{
if( aData.graphic_data10 == "CONNECT" )
retval->type = GR_TYPE_CONNECT;
else if( aData.graphic_data10 == "NOTCONNECT" )
retval->type = GR_TYPE_NOTCONNECT;
else if( aData.graphic_data10 == "SHAPE" )
retval->type = GR_TYPE_NOTCONNECT;
else if( aData.graphic_data10 == "VOID" )
retval->type = GR_TYPE_NOTCONNECT;
else if( aData.graphic_data10 == "POLYGON" )
retval->type = GR_TYPE_NOTCONNECT;
else
retval->type = GR_TYPE_NONE;
}
return retval;
}
/**
* A!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!GRAPHIC_DATA_3!
* GRAPHIC_DATA_4!GRAPHIC_DATA_5!GRAPHIC_DATA_6!GRAPHIC_DATA_7!GRAPHIC_DATA_8!GRAPHIC_DATA_9!
* SUBCLASS!SYM_NAME!REFDES!
*/
size_t FABMASTER::processGeometry( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int geo_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
int geo_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
int geo_tag_col = getColFromName( aRow, "RECORDTAG" );
int geo_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
int geo_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
int geo_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
int geo_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
int geo_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
int geo_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
int geo_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
int geo_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
int geo_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
int geo_subclass_col = getColFromName( aRow, "SUBCLASS" );
int geo_sym_name_col = getColFromName( aRow, "SYMNAME" );
int geo_refdes_col = getColFromName( aRow, "REFDES" );
if( geo_name_col < 0 || geo_num_col < 0 || geo_grdata1_col < 0 || geo_grdata2_col < 0
|| geo_grdata3_col < 0 || geo_grdata4_col < 0 || geo_grdata5_col < 0
|| geo_grdata6_col < 0 || geo_grdata7_col < 0 || geo_grdata8_col < 0
|| geo_grdata9_col < 0 || geo_subclass_col < 0 || geo_sym_name_col < 0
|| geo_refdes_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto& geo_tag = row[geo_tag_col];
GRAPHIC_DATA gr_data;
gr_data.graphic_dataname = row[geo_name_col];
gr_data.graphic_datanum = row[geo_num_col];
gr_data.graphic_data1 = row[geo_grdata1_col];
gr_data.graphic_data2 = row[geo_grdata2_col];
gr_data.graphic_data3 = row[geo_grdata3_col];
gr_data.graphic_data4 = row[geo_grdata4_col];
gr_data.graphic_data5 = row[geo_grdata5_col];
gr_data.graphic_data6 = row[geo_grdata6_col];
gr_data.graphic_data7 = row[geo_grdata7_col];
gr_data.graphic_data8 = row[geo_grdata8_col];
gr_data.graphic_data9 = row[geo_grdata9_col];
auto& geo_refdes = row[geo_refdes_col];
// Grouped graphics are a series of records with the same record ID but incrementing
// Sequence numbers.
int id = -1;
int seq = -1;
int subseq = 0;
if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
{
wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
geo_tag.c_str(),
rownum );
continue;
}
auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
if( !gr_item )
{
wxLogDebug( wxT( "Unhandled graphic item '%s' in row %zu." ),
gr_data.graphic_dataname.c_str(),
geo_tag.c_str(),
rownum );
continue;
}
gr_item->layer = row[geo_subclass_col];
gr_item->seq = seq;
gr_item->subseq = subseq;
if( geo_refdes.empty() )
{
if( board_graphics.empty() || board_graphics.back().id != id )
{
GEOM_GRAPHIC new_gr;
new_gr.subclass = row[geo_subclass_col];
new_gr.refdes = row[geo_refdes_col];
new_gr.name = row[geo_sym_name_col];
new_gr.id = id;
new_gr.elements = std::make_unique<graphic_element>();
board_graphics.push_back( std::move( new_gr ) );
}
GEOM_GRAPHIC& graphic = board_graphics.back();
graphic.elements->emplace( std::move( gr_item ) );
}
else
{
auto sym_gr_it = comp_graphics.emplace( geo_refdes,
std::map<int, GEOM_GRAPHIC>{} );
auto map_it = sym_gr_it.first->second.emplace( id, GEOM_GRAPHIC{} );
auto& gr = map_it.first;
if( map_it.second )
{
gr->second.subclass = row[geo_subclass_col];
gr->second.refdes = row[geo_refdes_col];
gr->second.name = row[geo_sym_name_col];
gr->second.id = id;
gr->second.elements = std::make_unique<graphic_element>();
}
auto result = gr->second.elements->emplace( std::move( gr_item ) );
}
}
return rownum - aRow;
}
/**
* A!VIA_X!VIA_Y!PAD_STACK_NAME!NET_NAME!TEST_POINT!
*/
size_t FABMASTER::processVias( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int viax_col = getColFromName( aRow, "VIAX" );
int viay_col = getColFromName( aRow, "VIAY" );
int padstack_name_col = getColFromName( aRow, "PADSTACKNAME" );
int net_name_col = getColFromName( aRow, "NETNAME" );
int test_point_col = getColFromName( aRow, "TESTPOINT" );
if( viax_col < 0 || viay_col < 0 || padstack_name_col < 0 || net_name_col < 0
|| test_point_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
vias.emplace_back( std::make_unique<FM_VIA>() );
auto& via = vias.back();
via->x = KiROUND( readDouble( row[viax_col] ) * scale_factor );
via->y = -KiROUND( readDouble( row[viay_col] ) * scale_factor );
via->padstack = row[padstack_name_col];
via->net = row[net_name_col];
via->test_point = ( row[test_point_col] == "YES" );
}
return rownum - aRow;
}
/**
* A!CLASS!SUBCLASS!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!
* GRAPHIC_DATA_3!GRAPHIC_DATA_4!GRAPHIC_DATA_5!GRAPHIC_DATA_6!GRAPHIC_DATA_7!GRAPHIC_DATA_8!
* GRAPHIC_DATA_9!NET_NAME!
*/
size_t FABMASTER::processTraces( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int class_col = getColFromName( aRow, "CLASS" );
int layer_col = getColFromName( aRow, "SUBCLASS" );
int grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
int grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
int tag_col = getColFromName( aRow, "RECORDTAG" );
int grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
int grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
int grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
int grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
int grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
int grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
int grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
int grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
int grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
int netname_col = getColFromName( aRow, "NETNAME" );
if( class_col < 0 || layer_col < 0 || grdata_name_col < 0 || grdata_num_col < 0
|| tag_col < 0 || grdata1_col < 0 || grdata2_col < 0 || grdata3_col < 0
|| grdata4_col < 0 || grdata5_col < 0 || grdata6_col < 0 || grdata7_col < 0
|| grdata8_col < 0 || grdata9_col < 0 || netname_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
GRAPHIC_DATA gr_data;
gr_data.graphic_dataname = row[grdata_name_col];
gr_data.graphic_datanum = row[grdata_num_col];
gr_data.graphic_data1 = row[grdata1_col];
gr_data.graphic_data2 = row[grdata2_col];
gr_data.graphic_data3 = row[grdata3_col];
gr_data.graphic_data4 = row[grdata4_col];
gr_data.graphic_data5 = row[grdata5_col];
gr_data.graphic_data6 = row[grdata6_col];
gr_data.graphic_data7 = row[grdata7_col];
gr_data.graphic_data8 = row[grdata8_col];
gr_data.graphic_data9 = row[grdata9_col];
const std::string& geo_tag = row[tag_col];
// Grouped graphics are a series of records with the same record ID but incrementing
// Sequence numbers.
int id = -1;
int seq = -1;
int subseq = 0;
if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
{
wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
geo_tag.c_str(),
rownum );
continue;
}
auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
if( !gr_item )
{
wxLogTrace( traceFabmaster, _( "Unhandled graphic item '%s' in row %zu." ),
gr_data.graphic_dataname.c_str(),
rownum );
continue;
}
auto new_trace = std::make_unique<TRACE>();
new_trace->id = id;
new_trace->layer = row[layer_col];
new_trace->netname = row[netname_col];
new_trace->lclass = row[class_col];
gr_item->layer = row[layer_col];
gr_item->seq = seq;
gr_item->subseq = subseq;
// Collect the reference designator positions for the footprints later
if( new_trace->lclass == "REF DES" )
{
auto result = refdes.emplace( std::move( new_trace ) );
auto& ref = *result.first;
ref->segment.emplace( std::move( gr_item ) );
}
else if( gr_item->width == 0 )
{
auto result = zones.emplace( std::move( new_trace ) );
auto& zone = *result.first;
auto gr_result = zone->segment.emplace( std::move( gr_item ) );
if( !gr_result.second )
{
wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
id,
seq,
rownum );
}
}
else
{
auto result = traces.emplace( std::move( new_trace ) );
auto& trace = *result.first;
auto gr_result = trace->segment.emplace( std::move( gr_item ) );
if( !gr_result.second )
{
wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
id,
seq,
rownum );
}
}
}
return rownum - aRow;
}
FABMASTER::SYMTYPE FABMASTER::parseSymType( const std::string& aSymType )
{
if( aSymType == "PACKAGE" )
return SYMTYPE_PACKAGE;
else if( aSymType == "DRAFTING")
return SYMTYPE_DRAFTING;
else if( aSymType == "MECHANICAL" )
return SYMTYPE_MECH;
else if( aSymType == "FORMAT" )
return SYMTYPE_FORMAT;
return SYMTYPE_NONE;
}
FABMASTER::COMPCLASS FABMASTER::parseCompClass( const std::string& aCmpClass )
{
if( aCmpClass == "IO" )
return COMPCLASS_IO;
else if( aCmpClass == "IC" )
return COMPCLASS_IC;
else if( aCmpClass == "DISCRETE" )
return COMPCLASS_DISCRETE;
return COMPCLASS_NONE;
}
/**
* A!REFDES!COMP_CLASS!COMP_PART_NUMBER!COMP_HEIGHT!COMP_DEVICE_LABEL!COMP_INSERTION_CODE!SYM_TYPE!
* SYM_NAME!SYM_MIRROR!SYM_ROTATE!SYM_X!SYM_Y!COMP_VALUE!COMP_TOL!COMP_VOLTAGE!
*/
size_t FABMASTER::processFootprints( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int refdes_col = getColFromName( aRow, "REFDES" );
int compclass_col = getColFromName( aRow, "COMPCLASS" );
int comppartnum_col = getColFromName( aRow, "COMPPARTNUMBER" );
int compheight_col = getColFromName( aRow, "COMPHEIGHT" );
int compdevlabelcol = getColFromName( aRow, "COMPDEVICELABEL" );
int compinscode_col = getColFromName( aRow, "COMPINSERTIONCODE" );
int symtype_col = getColFromName( aRow, "SYMTYPE" );
int symname_col = getColFromName( aRow, "SYMNAME" );
int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
int symrotate_col = getColFromName( aRow, "SYMROTATE" );
int symx_col = getColFromName( aRow, "SYMX" );
int symy_col = getColFromName( aRow, "SYMY" );
int compvalue_col = getColFromName( aRow, "COMPVALUE" );
int comptol_col = getColFromName( aRow, "COMPTOL" );
int compvolt_col = getColFromName( aRow, "COMPVOLTAGE" );
if( refdes_col < 0 || compclass_col < 0 || comppartnum_col < 0 || compheight_col < 0
|| compdevlabelcol < 0 || compinscode_col < 0 || symtype_col < 0 || symname_col < 0
|| symmirror_col < 0 || symrotate_col < 0 || symx_col < 0 || symy_col < 0
|| compvalue_col < 0 || comptol_col < 0 || compvolt_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto cmp = std::make_unique<COMPONENT>();
cmp->refdes = row[refdes_col];
cmp->cclass = parseCompClass( row[compclass_col] );
cmp->pn = row[comppartnum_col];
cmp->height = row[compheight_col];
cmp->dev_label = row[compdevlabelcol];
cmp->insert_code = row[compinscode_col];
cmp->type = parseSymType( row[symtype_col] );
cmp->name = row[symname_col];
cmp->mirror = ( row[symmirror_col] == "YES" );
cmp->rotate = readDouble( row[symrotate_col] );
cmp->x = KiROUND( readDouble( row[symx_col] ) * scale_factor );
cmp->y = -KiROUND( readDouble( row[symy_col] ) * scale_factor );
cmp->value = row[compvalue_col];
cmp->tol = row[comptol_col];
cmp->voltage = row[compvolt_col];
auto vec = components.find( cmp->refdes );
if( vec == components.end() )
{
auto retval = components.insert( std::make_pair( cmp->refdes, std::vector<std::unique_ptr<COMPONENT>>{} ) );
vec = retval.first;
}
vec->second.push_back( std::move( cmp ) );
}
return rownum - aRow;
}
/**
* A!SYM_NAME!SYM_MIRROR!PIN_NAME!PIN_NUMBER!PIN_X!PIN_Y!PAD_STACK_NAME!REFDES!PIN_ROTATION!
* TEST_POINT!
*/
size_t FABMASTER::processPins( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int symname_col = getColFromName( aRow, "SYMNAME" );
int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
int pinname_col = getColFromName( aRow, "PINNAME" );
int pinnum_col = getColFromName( aRow, "PINNUMBER" );
int pinx_col = getColFromName( aRow, "PINX" );
int piny_col = getColFromName( aRow, "PINY" );
int padstack_col = getColFromName( aRow, "PADSTACKNAME" );
int refdes_col = getColFromName( aRow, "REFDES" );
int pinrot_col = getColFromName( aRow, "PINROTATION" );
int testpoint_col = getColFromName( aRow, "TESTPOINT" );
if( symname_col < 0 ||symmirror_col < 0 || pinname_col < 0 || pinnum_col < 0 || pinx_col < 0
|| piny_col < 0 || padstack_col < 0 || refdes_col < 0 || pinrot_col < 0
|| testpoint_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
auto pin = std::make_unique<PIN>();
pin->name = row[symname_col];
pin->mirror = ( row[symmirror_col] == "YES" );
pin->pin_name = row[pinname_col];
pin->pin_number = row[pinnum_col];
pin->pin_x = KiROUND( readDouble( row[pinx_col] ) * scale_factor );
pin->pin_y = -KiROUND( readDouble( row[piny_col] ) * scale_factor );
pin->padstack = row[padstack_col];
pin->refdes = row[refdes_col];
pin->rotation = readDouble( row[pinrot_col] );
auto map_it = pins.find( pin->refdes );
if( map_it == pins.end() )
{
auto retval = pins.insert( std::make_pair( pin->refdes, std::set<std::unique_ptr<PIN>,
PIN::BY_NUM>{} ) );
map_it = retval.first;
}
map_it->second.insert( std::move( pin ) );
}
return rownum - aRow;
}
/**
* A!NET_NAME!REFDES!PIN_NUMBER!PIN_NAME!PIN_GROUND!PIN_POWER!
*/
size_t FABMASTER::processNets( size_t aRow )
{
size_t rownum = aRow + 2;
if( rownum >= rows.size() )
return -1;
const single_row& header = rows[aRow];
double scale_factor = processScaleFactor( aRow + 1 );
if( scale_factor <= 0.0 )
return -1;
int netname_col = getColFromName( aRow, "NETNAME" );
int refdes_col = getColFromName( aRow, "REFDES" );
int pinnum_col = getColFromName( aRow, "PINNUMBER" );
int pinname_col = getColFromName( aRow, "PINNAME" );
int pingnd_col = getColFromName( aRow, "PINGROUND" );
int pinpwr_col = getColFromName( aRow, "PINPOWER" );
if( netname_col < 0 || refdes_col < 0 || pinnum_col < 0 || pinname_col < 0 || pingnd_col < 0
|| pinpwr_col < 0 )
return -1;
for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
{
const single_row& row = rows[rownum];
if( row.size() != header.size() )
{
wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
rownum,
header.size(),
row.size() );
continue;
}
NETNAME new_net;
new_net.name = row[netname_col];
new_net.refdes = row[refdes_col];
new_net.pin_num = row[pinnum_col];
new_net.pin_name = row[pinname_col];
new_net.pin_gnd = ( row[pingnd_col] == "YES" );
new_net.pin_pwr = ( row[pinpwr_col] == "YES" );
pin_nets.emplace( std::make_pair( new_net.refdes, new_net.pin_num ), new_net );
netnames.insert( row[netname_col] );
}
return rownum - aRow;
}
bool FABMASTER::Process()
{
for( size_t i = 0; i < rows.size(); )
{
auto type = detectType( i );
switch( type )
{
case EXTRACT_PADSTACKS:
{
/// We extract the basic layers from the padstacks first as this is the only place
/// the stackup is kept in the basic fabmaster export
processPadStackLayers( i );
assignLayers();
int retval = processPadStacks( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_FULL_LAYERS:
{
int retval = processLayers( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_BASIC_LAYERS:
{
int retval = processSimpleLayers( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_VIAS:
{
int retval = processVias( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_TRACES:
{
int retval = processTraces( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_REFDES:
{
int retval = processFootprints( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_NETS:
{
int retval = processNets( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_GRAPHICS:
{
int retval = processGeometry( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_PINS:
{
int retval = processPins( i );
i += std::max( retval, 1 );
break;
}
case EXTRACT_PAD_SHAPES:
{
int retval = processCustomPads( i );
i += std::max( retval, 1 );
break;
}
default:
++i;
break;
}
}
return true;
}
bool FABMASTER::loadZones( BOARD* aBoard )
{
for( auto& zone : zones )
{
checkpoint();
if( IsCopperLayer( getLayer( zone->layer ) ) || zone->layer == "ALL" )
{
loadZone( aBoard, zone );
}
else
{
if( zone->layer == "OUTLINE" || zone->layer == "DESIGN_OUTLINE" )
{
loadOutline( aBoard, zone );
}
else
{
loadPolygon( aBoard, zone );
}
}
}
/**
* Zones in FABMASTER come in two varieties:
* - Outlines with no net code attached
* - Filled areas with net code attached
*
* In pcbnew, we want the outline with net code attached. To determine which
* outline should have which netcode, we look for overlapping areas. Each unnetted zone
* outline will be assigned the netcode that with the most hits on the edge of their
* outline.
*/
std::set<ZONE*> zones_to_delete;
for( auto zone : aBoard->Zones() )
{
/// Remove the filled areas in favor of the outlines
if( zone->GetNetCode() > 0 )
{
zones_to_delete.insert( zone );
}
}
for( auto zone1 : aBoard->Zones() )
{
/// Zone1 will be the destination zone for the new net
if( zone1->GetNetCode() > 0 )
continue;
SHAPE_LINE_CHAIN& outline1 = zone1->Outline()->Outline( 0 );
std::vector<size_t> overlaps( aBoard->GetNetInfo().GetNetCount() + 1, 0 );
std::vector<std::vector<ZONE*>> possible_deletions( overlaps.size() );
for( auto zone2 : aBoard->Zones() )
{
if( zone2->GetNetCode() <= 0 )
continue;
SHAPE_LINE_CHAIN& outline2 = zone2->Outline()->Outline( 0 );
if( zone1->GetLayer() != zone2->GetLayer() )
continue;
if( !outline1.BBox().Intersects( outline2.BBox() ) )
continue;
for( auto& pt1 : outline1.CPoints() )
{
/// We're looking for the netcode with the most overlaps to the un-netted zone
if( outline2.PointOnEdge( pt1, 1 ) )
overlaps[ zone2->GetNetCode() ]++;
}
for( auto& pt2 : outline2.CPoints() )
{
/// The overlap between outline1 and outline2 isn't perfect, so look for overlaps
/// in both directions
if( outline1.PointOnEdge( pt2, 1 ) )
overlaps[ zone2->GetNetCode() ]++;
}
}
size_t max_net = 0;
size_t max_net_id = 0;
for( size_t el = 1; el < overlaps.size(); ++el )
{
if( overlaps[el] > max_net )
{
max_net = overlaps[el];
max_net_id = el;
}
}
if( max_net > 0 )
zone1->SetNetCode( max_net_id );
}
for( auto zone : zones_to_delete )
{
aBoard->Remove( zone );
delete zone;
}
return true;
}
bool FABMASTER::loadFootprints( BOARD* aBoard )
{
const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
const auto& ds = aBoard->GetDesignSettings();
for( auto& mod : components )
{
checkpoint();
bool has_multiple = mod.second.size() > 1;
for( int i = 0; i < mod.second.size(); ++i )
{
auto& src = mod.second[i];
FOOTPRINT* fp = new FOOTPRINT( aBoard );
wxString mod_ref = src->name;
wxString lib_ref = m_filename.GetName();
if( has_multiple )
mod_ref.Append( wxString::Format( wxT( "_%d" ), i ) );
ReplaceIllegalFileNameChars( lib_ref, '_' );
ReplaceIllegalFileNameChars( mod_ref, '_' );
wxString key = !lib_ref.empty() ? lib_ref + wxT( ":" ) + mod_ref : mod_ref;
LIB_ID fpID;
fpID.Parse( key, true );
fp->SetFPID( fpID );
fp->SetPosition( VECTOR2I( src->x, src->y ) );
fp->SetOrientationDegrees( -src->rotate );
// KiCad netlisting requires parts to have non-digit + digit annotation.
// If the reference begins with a number, we prepend 'UNK' (unknown) for the source
// designator
wxString reference = src->refdes;
if( !std::isalpha( src->refdes[0] ) )
reference.Prepend( "UNK" );
fp->SetReference( reference );
fp->SetValue( src->value );
fp->Value().SetLayer( F_Fab );
fp->Value().SetVisible( false );
for( auto& ref : refdes )
{
const GRAPHIC_TEXT *lsrc =
static_cast<const GRAPHIC_TEXT*>( ( *( ref->segment.begin() ) ).get() );
if( lsrc->text == src->refdes )
{
PCB_TEXT* txt = nullptr;
PCB_LAYER_ID layer = getLayer( ref->layer );
if( !IsPcbLayer( layer ) )
{
wxLogTrace( traceFabmaster, wxS( "The layer %s is not mapped?" ),
ref->layer.c_str() );
continue;
}
if( layer == F_SilkS || layer == B_SilkS )
txt = &( fp->Reference() );
else
txt = new PCB_TEXT( fp );
if( src->mirror )
{
txt->SetLayer( FlipLayer( layer ) );
txt->SetTextPos( VECTOR2I( lsrc->start_x,
2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
}
else
{
txt->SetLayer( layer );
txt->SetTextPos( VECTOR2I( lsrc->start_x,
lsrc->start_y - lsrc->height / 2 ) );
}
txt->SetText( lsrc->text );
txt->SetItalic( lsrc->ital );
txt->SetTextThickness( lsrc->thickness );
txt->SetTextHeight( lsrc->height );
txt->SetTextWidth( lsrc->width );
txt->SetHorizJustify( lsrc->orient );
if( txt != &fp->Reference() )
fp->Add( txt, ADD_MODE::APPEND );
}
}
/// Always set the module to the top and flip later if needed
/// When flipping later, we get the full coordinate transform for free
fp->SetLayer( F_Cu );
auto gr_it = comp_graphics.find( src->refdes );
if( gr_it == comp_graphics.end() )
{
continue;
//TODO: Error
}
for( auto& gr_ref : gr_it->second )
{
auto& graphic = gr_ref.second;
for( auto& seg : *graphic.elements )
{
PCB_LAYER_ID layer = Dwgs_User;
if( IsPcbLayer( getLayer( seg->layer ) ) )
layer = getLayer( seg->layer );
STROKE_PARAMS defaultStroke( ds.GetLineThickness( layer ) );
switch( seg->shape )
{
case GR_SHAPE_LINE:
{
const GRAPHIC_LINE* lsrc = static_cast<const GRAPHIC_LINE*>( seg.get() );
PCB_SHAPE* line = new PCB_SHAPE( fp, SHAPE_T::SEGMENT );
if( src->mirror )
{
line->SetLayer( FlipLayer( layer ) );
line->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
line->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
}
else
{
line->SetLayer( layer );
line->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
line->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
}
line->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
if( lsrc->width == 0 )
line->SetStroke( defaultStroke );
fp->Add( line, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_CIRCLE:
{
const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_SHAPE* circle = new PCB_SHAPE( fp, SHAPE_T::CIRCLE );
circle->SetLayer( layer );
circle->SetCenter( VECTOR2I( lsrc->center_x, lsrc->center_y ) );
circle->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
circle->SetWidth( lsrc->width );
if( lsrc->width == 0 )
circle->SetWidth( ds.GetLineThickness( circle->GetLayer() ) );
if( src->mirror )
circle->Flip( circle->GetCenter(), false );
fp->Add( circle, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_ARC:
{
const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_SHAPE* arc = new PCB_SHAPE( fp, SHAPE_T::ARC );
arc->SetLayer( layer );
arc->SetArcGeometry( lsrc->result.GetP0(),
lsrc->result.GetArcMid(),
lsrc->result.GetP1() );
arc->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
if( lsrc->width == 0 )
arc->SetStroke( defaultStroke );
if( src->mirror )
arc->Flip( arc->GetCenter(), false );
fp->Add( arc, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_RECTANGLE:
{
const GRAPHIC_RECTANGLE *lsrc =
static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
PCB_SHAPE* rect = new PCB_SHAPE( fp, SHAPE_T::RECTANGLE );
if( src->mirror )
{
rect->SetLayer( FlipLayer( layer ) );
rect->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
rect->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
}
else
{
rect->SetLayer( layer );
rect->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
rect->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
}
rect->SetStroke( defaultStroke );
fp->Add( rect, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_TEXT:
{
const GRAPHIC_TEXT *lsrc =
static_cast<const GRAPHIC_TEXT*>( seg.get() );
PCB_TEXT* txt = new PCB_TEXT( fp );
if( src->mirror )
{
txt->SetLayer( FlipLayer( layer ) );
txt->SetTextPos( VECTOR2I( lsrc->start_x,
2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
}
else
{
txt->SetLayer( layer );
txt->SetTextPos( VECTOR2I( lsrc->start_x,
lsrc->start_y - lsrc->height / 2 ) );
}
txt->SetText( lsrc->text );
txt->SetItalic( lsrc->ital );
txt->SetTextThickness( lsrc->thickness );
txt->SetTextHeight( lsrc->height );
txt->SetTextWidth( lsrc->width );
txt->SetHorizJustify( lsrc->orient );
// FABMASTER doesn't have visibility flags but layers that are not silk
// should be hidden by default to prevent clutter.
if( txt->GetLayer() != F_SilkS && txt->GetLayer() != B_SilkS )
txt->SetVisible( false );
fp->Add( txt, ADD_MODE::APPEND );
break;
}
default:
continue;
}
}
}
auto pin_it = pins.find( src->refdes );
if( pin_it != pins.end() )
{
for( auto& pin : pin_it->second )
{
auto pin_net_it = pin_nets.find( std::make_pair( pin->refdes,
pin->pin_number ) );
auto padstack = pads.find( pin->padstack );
std::string netname = "";
if( pin_net_it != pin_nets.end() )
netname = pin_net_it->second.name;
auto net_it = netinfo.find( netname );
std::unique_ptr<PAD> newpad = std::make_unique<PAD>( fp );
if( net_it != netinfo.end() )
newpad->SetNet( net_it->second );
else
newpad->SetNetCode( 0 );
newpad->SetX( pin->pin_x );
if( src->mirror )
newpad->SetY( 2 * src->y - pin->pin_y );
else
newpad->SetY( pin->pin_y );
newpad->SetNumber( pin->pin_number );
if( padstack == pads.end() )
{
wxLogError( _( "Unable to locate padstack %s in file %s\n" ),
pin->padstack.c_str(), aBoard->GetFileName().wc_str() );
continue;
}
else
{
auto& pad = padstack->second;
newpad->SetShape( pad.shape );
if( pad.shape == PAD_SHAPE::CUSTOM )
{
// Choose the smaller dimension to ensure the base pad
// is fully hidden by the custom pad
int pad_size = std::min( pad.width, pad.height );
newpad->SetSize( VECTOR2I( pad_size / 2, pad_size / 2 ) );
std::string custom_name = pad.custom_name + "_" + pin->refdes + "_" +
pin->pin_number;
auto custom_it = pad_shapes.find( custom_name );
if( custom_it != pad_shapes.end() )
{
SHAPE_POLY_SET poly_outline;
int last_subseq = 0;
int hole_idx = -1;
poly_outline.NewOutline();
// Custom pad shapes have a group of elements
// that are a list of graphical polygons
for( const auto& el : (*custom_it).second.elements )
{
// For now, we are only processing the custom pad for the
// top layer
// TODO: Use full padstacks when implementing in KiCad
PCB_LAYER_ID primary_layer = src->mirror ? B_Cu : F_Cu;
if( getLayer( ( *( el.second.begin() ) )->layer ) != primary_layer )
continue;
for( const auto& seg : el.second )
{
if( seg->subseq > 0 || seg->subseq != last_subseq )
{
poly_outline.Polygon(0).back().SetClosed( true );
hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
}
if( seg->shape == GR_SHAPE_LINE )
{
const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
poly_outline.Append( src->start_x, src->start_y,
0, hole_idx );
poly_outline.Append( src->end_x, src->end_y, 0,
hole_idx );
}
else if( seg->shape == GR_SHAPE_ARC )
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
chain.Append( src->result );
}
}
}
if( poly_outline.OutlineCount() < 1
|| poly_outline.Outline( 0 ).PointCount() < 3 )
{
wxLogError( _( "Invalid custom pad '%s'. Replacing with "
"circular pad." ),
custom_name.c_str() );
newpad->SetShape( PAD_SHAPE::CIRCLE );
}
else
{
poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
poly_outline.Move( -newpad->GetPosition() );
if( src->mirror )
{
poly_outline.Mirror( false, true,
VECTOR2I( 0, ( pin->pin_y - src->y ) ) );
poly_outline.Rotate( EDA_ANGLE( src->rotate - pin->rotation,
DEGREES_T ) );
}
else
{
poly_outline.Rotate( EDA_ANGLE( -src->rotate + pin->rotation,
DEGREES_T ) );
}
newpad->AddPrimitivePoly( poly_outline, 0, true );
}
SHAPE_POLY_SET mergedPolygon;
newpad->MergePrimitivesAsPolygon( &mergedPolygon );
if( mergedPolygon.OutlineCount() > 1 )
{
wxLogError( _( "Invalid custom pad '%s'. Replacing with "
"circular pad." ),
custom_name.c_str() );
newpad->SetShape( PAD_SHAPE::CIRCLE );
}
}
else
{
wxLogError( _( "Could not find custom pad '%s'." ),
custom_name.c_str() );
}
}
else
{
newpad->SetSize( VECTOR2I( pad.width, pad.height ) );
}
if( pad.drill )
{
if( pad.plated )
{
newpad->SetAttribute( PAD_ATTRIB::PTH );
newpad->SetLayerSet( PAD::PTHMask() );
}
else
{
newpad->SetAttribute( PAD_ATTRIB::NPTH );
newpad->SetLayerSet( PAD::UnplatedHoleMask() );
}
if( pad.drill_size_x == pad.drill_size_y )
newpad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
else
newpad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
newpad->SetDrillSize( VECTOR2I( pad.drill_size_x, pad.drill_size_y ) );
}
else
{
newpad->SetAttribute( PAD_ATTRIB::SMD );
if( pad.top )
newpad->SetLayerSet( PAD::SMDMask() );
else if( pad.bottom )
newpad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
}
}
if( src->mirror )
newpad->SetOrientation( EDA_ANGLE( -src->rotate + pin->rotation,
DEGREES_T ) );
else
newpad->SetOrientation( EDA_ANGLE( src->rotate - pin->rotation,
DEGREES_T ) );
if( newpad->GetSizeX() > 0 || newpad->GetSizeY() > 0 )
{
fp->Add( newpad.release(), ADD_MODE::APPEND );
}
else
{
wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
aBoard->GetFileName().wc_str() );
}
}
}
if( src->mirror )
{
fp->SetOrientationDegrees( 180.0 - src->rotate );
fp->Flip( fp->GetPosition(), true );
}
aBoard->Add( fp, ADD_MODE::APPEND );
}
}
return true;
}
bool FABMASTER::loadLayers( BOARD* aBoard )
{
LSET layer_set;
/// The basic layers that get enabled for normal boards
layer_set |= LSET::AllTechMask() | LSET::UserMask();
for( auto& layer : layers )
{
checkpoint();
if( layer.second.layerid >= PCBNEW_LAYER_ID_START )
layer_set.set( layer.second.layerid );
}
aBoard->SetEnabledLayers( layer_set );
for( auto& layer : layers )
{
if( layer.second.conductive )
{
aBoard->SetLayerName( static_cast<PCB_LAYER_ID>( layer.second.layerid ),
layer.second.name );
}
}
return true;
}
bool FABMASTER::loadVias( BOARD* aBoard )
{
const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
const auto& ds = aBoard->GetDesignSettings();
for( auto& via : vias )
{
checkpoint();
auto net_it = netinfo.find( via->net );
auto padstack = pads.find( via->padstack );
PCB_VIA* new_via = new PCB_VIA( aBoard );
new_via->SetPosition( VECTOR2I( via->x, via->y ) );
if( net_it != netinfo.end() )
new_via->SetNet( net_it->second );
if( padstack == pads.end() )
{
new_via->SetDrillDefault();
if( !ds.m_ViasDimensionsList.empty() )
{
new_via->SetWidth( ds.m_ViasDimensionsList[0].m_Diameter );
new_via->SetDrill( ds.m_ViasDimensionsList[0].m_Drill );
}
else
{
new_via->SetDrillDefault();
new_via->SetWidth( ds.m_ViasMinSize );
}
}
else
{
new_via->SetDrill( padstack->second.drill_size_x );
new_via->SetWidth( padstack->second.width );
}
aBoard->Add( new_via, ADD_MODE::APPEND );
}
return true;
}
bool FABMASTER::loadNets( BOARD* aBoard )
{
for( auto& net : netnames )
{
checkpoint();
NETINFO_ITEM *newnet = new NETINFO_ITEM( aBoard, net );
aBoard->Add( newnet, ADD_MODE::APPEND );
}
return true;
}
bool FABMASTER::loadEtch( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
{
const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
auto net_it = netinfo.find( aLine->netname );
int last_subseq = 0;
ZONE* new_zone = nullptr;
for( const auto& seg : aLine->segment )
{
PCB_LAYER_ID layer = getLayer( seg->layer );
if( IsCopperLayer( layer ) )
{
if( seg->shape == GR_SHAPE_LINE )
{
const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
PCB_TRACK* trk = new PCB_TRACK( aBoard );
trk->SetLayer( layer );
trk->SetStart( VECTOR2I( src->start_x, src->start_y ) );
trk->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
trk->SetWidth( src->width );
if( net_it != netinfo.end() )
trk->SetNet( net_it->second );
aBoard->Add( trk, ADD_MODE::APPEND );
}
else if( seg->shape == GR_SHAPE_ARC )
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_ARC* trk = new PCB_ARC( aBoard, &src->result );
trk->SetLayer( layer );
trk->SetWidth( src->width );
if( net_it != netinfo.end() )
trk->SetNet( net_it->second );
aBoard->Add( trk, ADD_MODE::APPEND );
}
else if( seg->shape == GR_SHAPE_CIRCLE )
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_SHAPE* circle = new PCB_SHAPE( aBoard, SHAPE_T::CIRCLE );
circle->SetLayer( layer );
circle->SetCenter( VECTOR2I( src->center_x, src->center_y ) );
circle->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
circle->SetWidth( src->width );
aBoard->Add( circle, ADD_MODE::APPEND );
}
else if( seg->shape == GR_SHAPE_RECTANGLE )
{
const GRAPHIC_RECTANGLE *src =
static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
PCB_SHAPE* rect = new PCB_SHAPE( aBoard, SHAPE_T::RECTANGLE );
rect->SetLayer( layer );
rect->SetStart( VECTOR2I( src->start_x, src->start_y ) );
rect->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
rect->SetStroke( STROKE_PARAMS( 0 ) );
rect->SetFilled( true );
aBoard->Add( rect, ADD_MODE::APPEND );
}
else if( seg->shape == GR_SHAPE_TEXT )
{
const GRAPHIC_TEXT *src =
static_cast<const GRAPHIC_TEXT*>( seg.get() );
PCB_TEXT* txt = new PCB_TEXT( aBoard );
txt->SetLayer( layer );
txt->SetTextPos( VECTOR2I( src->start_x, src->start_y - src->height / 2 ) );
txt->SetText( src->text );
txt->SetItalic( src->ital );
txt->SetTextThickness( src->thickness );
txt->SetTextHeight( src->height );
txt->SetTextWidth( src->width );
txt->SetHorizJustify( src->orient );
aBoard->Add( txt, ADD_MODE::APPEND );
}
}
else
{
wxLogError( _( "Expecting etch data to be on copper layer. Row found on layer '%s'" ),
seg->layer.c_str() );
}
}
return true;
}
SHAPE_POLY_SET FABMASTER::loadShapePolySet( const graphic_element& aElement )
{
SHAPE_POLY_SET poly_outline;
int last_subseq = 0;
int hole_idx = -1;
poly_outline.NewOutline();
for( const auto& seg : aElement )
{
if( seg->subseq > 0 || seg->subseq != last_subseq )
hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
if( seg->shape == GR_SHAPE_LINE )
{
const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
}
else if( seg->shape == GR_SHAPE_ARC )
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
chain.Append( src->result );
}
}
return poly_outline;
}
bool FABMASTER::loadPolygon( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
{
if( aLine->segment.empty() )
return false;
PCB_LAYER_ID layer = Cmts_User;
auto new_layer = getLayer( aLine->layer );
if( IsPcbLayer( new_layer ) )
layer = new_layer;
STROKE_PARAMS defaultStroke( aBoard->GetDesignSettings().GetLineThickness( layer ) );
if( aLine->segment.size() < 3 )
{
for( const auto& seg : aLine->segment )
{
PCB_SHAPE* new_shape = new PCB_SHAPE( aBoard );
new_shape->SetLayer( layer );
new_shape->SetStroke(
STROKE_PARAMS( ( *( aLine->segment.begin() ) )->width, LINE_STYLE::SOLID ) );
if( new_shape->GetWidth() == 0 )
new_shape->SetStroke( defaultStroke );
if( seg->shape == GR_SHAPE_LINE )
{
const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
new_shape->SetShape( SHAPE_T::SEGMENT );
new_shape->SetStart( VECTOR2I( src->start_x, src->start_y ) );
new_shape->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
}
else if( seg->shape == GR_SHAPE_ARC )
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
new_shape->SetShape( SHAPE_T::ARC );
new_shape->SetArcGeometry( src->result.GetP0(), src->result.GetArcMid(),
src->result.GetP1() );
}
aBoard->Add( new_shape, ADD_MODE::APPEND );
}
}
SHAPE_POLY_SET poly_outline = loadShapePolySet( aLine->segment );
poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
return false;
PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
new_poly->SetShape( SHAPE_T::POLY );
new_poly->SetLayer( layer );
// Polygons on the silk layer are filled but other layers are not/fill doesn't make sense
if( layer == F_SilkS || layer == B_SilkS )
{
new_poly->SetFilled( true );
new_poly->SetStroke( STROKE_PARAMS( 0 ) );
}
else
{
new_poly->SetStroke( STROKE_PARAMS( ( *( aLine->segment.begin() ) )->width,
LINE_STYLE::SOLID ) );
if( new_poly->GetWidth() == 0 )
new_poly->SetStroke( defaultStroke );
}
new_poly->SetPolyShape( poly_outline );
aBoard->Add( new_poly, ADD_MODE::APPEND );
return true;
}
bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
{
if( aLine->segment.size() < 3 )
return false;
int last_subseq = 0;
int hole_idx = -1;
SHAPE_POLY_SET* zone_outline = nullptr;
ZONE* zone = nullptr;
const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
auto net_it = netinfo.find( aLine->netname );
PCB_LAYER_ID layer = Cmts_User;
auto new_layer = getLayer( aLine->layer );
if( IsPcbLayer( new_layer ) )
layer = new_layer;
zone = new ZONE( aBoard );
zone_outline = new SHAPE_POLY_SET;
if( net_it != netinfo.end() )
zone->SetNet( net_it->second );
if( aLine->layer == "ALL" )
zone->SetLayerSet( aBoard->GetLayerSet() & LSET::AllCuMask() );
else
zone->SetLayer( layer );
zone->SetIsRuleArea( false );
zone->SetDoNotAllowTracks( false );
zone->SetDoNotAllowVias( false );
zone->SetDoNotAllowPads( false );
zone->SetDoNotAllowFootprints( false );
zone->SetDoNotAllowCopperPour( false );
if( aLine->lclass == "ROUTE KEEPOUT")
{
zone->SetIsRuleArea( true );
zone->SetDoNotAllowTracks( true );
}
else if( aLine->lclass == "VIA KEEPOUT")
{
zone->SetIsRuleArea( true );
zone->SetDoNotAllowVias( true );
}
else
{
zone->SetAssignedPriority( 50 );
}
zone->SetLocalClearance( 0 );
zone->SetPadConnection( ZONE_CONNECTION::FULL );
zone_outline->NewOutline();
for( const auto& seg : aLine->segment )
{
if( seg->subseq > 0 && seg->subseq != last_subseq )
{
// Don't knock holes in the BOUNDARY systems. These are the outer layers for
// zone fills.
if( aLine->lclass == "BOUNDARY" )
break;
hole_idx = zone_outline->AddHole( SHAPE_LINE_CHAIN{} );
last_subseq = seg->subseq;
last_subseq = seg->subseq;
}
if( seg->shape == GR_SHAPE_LINE )
{
const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
if( zone_outline->VertexCount( 0, hole_idx ) == 0 )
zone_outline->Append( src->start_x, src->start_y, 0, hole_idx );
zone_outline->Append( src->end_x, src->end_y, 0, hole_idx );
}
else if( seg->shape == GR_SHAPE_ARC )
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
zone_outline->Hole( 0, hole_idx ).Append( src->result );
}
}
if( zone_outline->Outline( 0 ).PointCount() >= 3 )
{
zone->SetOutline( zone_outline );
aBoard->Add( zone, ADD_MODE::APPEND );
}
else
{
delete( zone_outline );
delete( zone );
}
return true;
}
bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
{
PCB_LAYER_ID layer;
if( aLine->lclass == "BOARD GEOMETRY" && aLine->layer != "DIMENSION" )
layer = Edge_Cuts;
else if( aLine->lclass == "DRAWING FORMAT" )
layer = Dwgs_User;
else
layer = Cmts_User;
STROKE_PARAMS defaultStroke( aBoard->GetDesignSettings().GetLineThickness( layer ) );
for( auto& seg : aLine->segment )
{
switch( seg->shape )
{
case GR_SHAPE_LINE:
{
const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
PCB_SHAPE* line = new PCB_SHAPE( aBoard, SHAPE_T::SEGMENT );
line->SetLayer( layer );
line->SetStart( VECTOR2I( src->start_x, src->start_y ) );
line->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
line->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
if( line->GetWidth() == 0 )
line->SetStroke( defaultStroke );
aBoard->Add( line, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_CIRCLE:
{
const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_SHAPE* circle = new PCB_SHAPE( aBoard, SHAPE_T::CIRCLE );
circle->SetLayer( layer );
circle->SetCenter( VECTOR2I( lsrc->center_x, lsrc->center_y ) );
circle->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
circle->SetWidth( lsrc->width );
if( lsrc->width == 0 )
circle->SetWidth( aBoard->GetDesignSettings().GetLineThickness( circle->GetLayer() ) );
aBoard->Add( circle, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_ARC:
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_SHAPE* arc = new PCB_SHAPE( aBoard, SHAPE_T::ARC );
arc->SetLayer( layer );
arc->SetArcGeometry( src->result.GetP0(),
src->result.GetArcMid(),
src->result.GetP1() );
arc->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
if( arc->GetWidth() == 0 )
arc->SetStroke( defaultStroke );
aBoard->Add( arc, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_RECTANGLE:
{
const GRAPHIC_RECTANGLE *src =
static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
PCB_SHAPE* rect = new PCB_SHAPE( aBoard, SHAPE_T::RECTANGLE );
rect->SetLayer( layer );
rect->SetStart( VECTOR2I( src->start_x, src->start_y ) );
rect->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
rect->SetStroke( defaultStroke );
aBoard->Add( rect, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_TEXT:
{
const GRAPHIC_TEXT *src = static_cast<const GRAPHIC_TEXT*>( seg.get() );
PCB_TEXT* txt = new PCB_TEXT( aBoard );
txt->SetLayer( layer );
txt->SetTextPos( VECTOR2I( src->start_x, src->start_y - src->height / 2 ) );
txt->SetText( src->text );
txt->SetItalic( src->ital );
txt->SetTextThickness( src->thickness );
txt->SetTextHeight( src->height );
txt->SetTextWidth( src->width );
txt->SetHorizJustify( src->orient );
aBoard->Add( txt, ADD_MODE::APPEND );
break;
}
default:
return false;
}
}
return true;
}
bool FABMASTER::loadGraphics( BOARD* aBoard )
{
for( auto& geom : board_graphics )
{
checkpoint();
PCB_LAYER_ID layer;
// The pin numbers are not useful for us outside of the footprints
if( geom.subclass == "PIN_NUMBER" )
continue;
layer = getLayer( geom.subclass );
if( !IsPcbLayer( layer ) )
layer = Cmts_User;
if( !geom.elements->empty() )
{
/// Zero-width segments/arcs are polygon outlines
if( ( *( geom.elements->begin() ) )->width == 0 )
{
SHAPE_POLY_SET poly_outline = loadShapePolySet( *( geom.elements ) );
poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
continue;
PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard, SHAPE_T::POLY );
new_poly->SetLayer( layer );
new_poly->SetPolyShape( poly_outline );
new_poly->SetStroke( STROKE_PARAMS( 0 ) );
if( layer == F_SilkS || layer == B_SilkS )
new_poly->SetFilled( true );
aBoard->Add( new_poly, ADD_MODE::APPEND );
}
}
for( auto& seg : *geom.elements )
{
switch( seg->shape )
{
case GR_SHAPE_LINE:
{
const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
PCB_SHAPE* line = new PCB_SHAPE( aBoard, SHAPE_T::SEGMENT );
line->SetLayer( layer );
line->SetStart( VECTOR2I( src->start_x, src->start_y ) );
line->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
line->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
aBoard->Add( line, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_CIRCLE:
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_SHAPE* circle = new PCB_SHAPE( aBoard, SHAPE_T::CIRCLE );
circle->SetLayer( layer );
circle->SetCenter( VECTOR2I( src->center_x, src->center_y ) );
circle->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
circle->SetWidth( src->width );
aBoard->Add( circle, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_ARC:
{
const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
PCB_SHAPE* arc = new PCB_SHAPE( aBoard, SHAPE_T::ARC );
arc->SetLayer( layer );
arc->SetArcGeometry( src->result.GetP0(),
src->result.GetArcMid(),
src->result.GetP1() );
arc->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
aBoard->Add( arc, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_RECTANGLE:
{
const GRAPHIC_RECTANGLE *src =
static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
PCB_SHAPE* rect = new PCB_SHAPE( aBoard, SHAPE_T::RECTANGLE );
rect->SetLayer( layer );
rect->SetStart( VECTOR2I( src->start_x, src->start_y ) );
rect->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
rect->SetStroke( STROKE_PARAMS( 0 ) );
rect->SetFilled( true );
aBoard->Add( rect, ADD_MODE::APPEND );
break;
}
case GR_SHAPE_TEXT:
{
const GRAPHIC_TEXT *src =
static_cast<const GRAPHIC_TEXT*>( seg.get() );
PCB_TEXT* txt = new PCB_TEXT( aBoard );
txt->SetLayer( layer );
txt->SetTextPos( VECTOR2I( src->start_x, src->start_y - src->height / 2 ) );
txt->SetText( src->text );
txt->SetItalic( src->ital );
txt->SetTextThickness( src->thickness );
txt->SetTextHeight( src->height );
txt->SetTextWidth( src->width );
txt->SetHorizJustify( src->orient );
aBoard->Add( txt, ADD_MODE::APPEND );
break;
}
default:
return false;
}
}
}
return true;
}
bool FABMASTER::orderZones( BOARD* aBoard )
{
std::vector<ZONE*> sortedZones;
std::copy( aBoard->Zones().begin(), aBoard->Zones().end(), std::back_inserter( sortedZones ) );
std::sort( sortedZones.begin(), sortedZones.end(),
[&]( const ZONE* a, const ZONE* b )
{
if( a->GetLayer() == b->GetLayer() )
return a->GetBoundingBox().GetArea() > b->GetBoundingBox().GetArea();
return a->GetLayer() < b->GetLayer();
} );
PCB_LAYER_ID layer = UNDEFINED_LAYER;
unsigned int priority = 0;
for( ZONE* zone : sortedZones )
{
/// Rule areas do not have priorities
if( zone->GetIsRuleArea() )
continue;
if( zone->GetLayer() != layer )
{
layer = zone->GetLayer();
priority = 0;
}
zone->SetAssignedPriority( priority );
priority += 10;
}
return true;
}
bool FABMASTER::LoadBoard( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
{
aBoard->SetFileName( m_filename.GetFullPath() );
m_progressReporter = aProgressReporter;
m_totalCount = netnames.size()
+ layers.size()
+ vias.size()
+ components.size()
+ zones.size()
+ board_graphics.size()
+ traces.size();
m_doneCount = 0;
loadNets( aBoard );
loadLayers( aBoard );
loadVias( aBoard );
loadFootprints( aBoard );
loadZones( aBoard );
loadGraphics( aBoard );
for( auto& track : traces )
{
checkpoint();
if( track->lclass == "ETCH" )
loadEtch( aBoard, track);
else if( track->layer == "OUTLINE" || track->layer == "DIMENSION" )
loadOutline( aBoard, track );
}
orderZones( aBoard );
return true;
}