2012-06-09 17:00:13 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 CERN
|
|
|
|
* Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* 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 2
|
|
|
|
* 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 <fctsys.h>
|
|
|
|
#include <kicad_string.h>
|
|
|
|
#include <common.h>
|
|
|
|
#include <build_version.h> // LEGACY_BOARD_FILE_VERSION
|
|
|
|
#include <macros.h>
|
|
|
|
#include <3d_struct.h>
|
2012-10-07 15:37:25 +00:00
|
|
|
#include <wildcards_and_files_ext.h>
|
2013-07-19 18:27:22 +00:00
|
|
|
#include <base_units.h>
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
#include <class_board.h>
|
|
|
|
#include <class_module.h>
|
|
|
|
#include <class_pcb_text.h>
|
|
|
|
#include <class_dimension.h>
|
|
|
|
#include <class_track.h>
|
|
|
|
#include <class_zone.h>
|
|
|
|
#include <class_drawsegment.h>
|
|
|
|
#include <class_mire.h>
|
|
|
|
#include <class_edge_mod.h>
|
2012-06-11 00:47:15 +00:00
|
|
|
#include <pcb_plot_params.h>
|
2012-06-09 17:00:13 +00:00
|
|
|
#include <zones.h>
|
|
|
|
#include <kicad_plugin.h>
|
|
|
|
#include <pcb_parser.h>
|
|
|
|
|
2012-10-07 15:37:25 +00:00
|
|
|
#include <wx/dir.h>
|
|
|
|
#include <wx/filename.h>
|
2012-06-09 17:00:13 +00:00
|
|
|
#include <wx/wfstream.h>
|
2012-10-07 15:37:25 +00:00
|
|
|
#include <boost/ptr_container/ptr_map.hpp>
|
|
|
|
#include <memory.h>
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
#define FMTIU BOARD_ITEM::FormatInternalUnits
|
|
|
|
|
2012-10-07 22:26:17 +00:00
|
|
|
/**
|
|
|
|
* Definition for enabling and disabling footprint library trace output. See the
|
2013-09-24 18:45:57 +00:00
|
|
|
* wxWidgets documentation on using the WXTRACE environment variable.
|
2012-10-07 22:26:17 +00:00
|
|
|
*/
|
|
|
|
static const wxString traceFootprintLibrary( wxT( "KicadFootprintLib" ) );
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2012-10-07 15:37:25 +00:00
|
|
|
/**
|
|
|
|
* Class FP_CACHE_ITEM
|
|
|
|
* is helper class for creating a footprint library cache.
|
|
|
|
*
|
|
|
|
* The new footprint library design is a file path of individual module files
|
|
|
|
* that contain a single module per file. This class is a helper only for the
|
|
|
|
* footprint portion of the PLUGIN API, and only for the #PCB_IO plugin. It is
|
|
|
|
* private to this implementation file so it is not placed into a header.
|
|
|
|
*/
|
|
|
|
class FP_CACHE_ITEM
|
|
|
|
{
|
2012-11-14 07:15:59 +00:00
|
|
|
wxFileName m_file_name; ///< The the full file name and path of the footprint to cache.
|
|
|
|
bool m_writable; ///< Writability status of the footprint file.
|
|
|
|
wxDateTime m_mod_time; ///< The last file modified time stamp.
|
2013-12-13 16:27:30 +00:00
|
|
|
std::auto_ptr<MODULE> m_module;
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
FP_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName );
|
|
|
|
|
2012-10-22 20:41:26 +00:00
|
|
|
wxString GetName() const { return m_file_name.GetDirs().Last(); }
|
|
|
|
wxFileName GetFileName() const { return m_file_name; }
|
2013-11-27 06:04:04 +00:00
|
|
|
|
|
|
|
/// Tell if the disk content or the lib_path has changed.
|
2012-10-22 20:41:26 +00:00
|
|
|
bool IsModified() const;
|
2013-11-27 06:04:04 +00:00
|
|
|
|
2012-10-22 20:41:26 +00:00
|
|
|
MODULE* GetModule() const { return m_module.get(); }
|
|
|
|
void UpdateModificationTime() { m_mod_time = m_file_name.GetModificationTime(); }
|
2012-10-07 15:37:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-11-14 07:15:59 +00:00
|
|
|
FP_CACHE_ITEM::FP_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
|
|
|
|
m_module( aModule )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
m_file_name = aFileName;
|
2013-09-24 18:45:57 +00:00
|
|
|
|
|
|
|
if( m_file_name.FileExists() )
|
|
|
|
m_mod_time = m_file_name.GetModificationTime();
|
|
|
|
else
|
|
|
|
m_mod_time.Now();
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool FP_CACHE_ITEM::IsModified() const
|
|
|
|
{
|
2013-09-24 18:45:57 +00:00
|
|
|
if( !m_file_name.FileExists() )
|
|
|
|
return false;
|
|
|
|
|
2013-10-31 21:30:57 +00:00
|
|
|
wxLogTrace( traceFootprintLibrary, wxT( "File '%s', m_mod_time %s-%s, file mod time: %s-%s." ),
|
2013-09-24 18:45:57 +00:00
|
|
|
GetChars( m_file_name.GetFullPath() ),
|
|
|
|
GetChars( m_mod_time.FormatDate() ), GetChars( m_mod_time.FormatTime() ),
|
|
|
|
GetChars( m_file_name.GetModificationTime().FormatDate() ),
|
|
|
|
GetChars( m_file_name.GetModificationTime().FormatTime() ) );
|
|
|
|
|
2012-10-07 15:37:25 +00:00
|
|
|
return m_file_name.GetModificationTime() != m_mod_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
typedef boost::ptr_map< std::string, FP_CACHE_ITEM > MODULE_MAP;
|
|
|
|
typedef MODULE_MAP::iterator MODULE_ITER;
|
|
|
|
typedef MODULE_MAP::const_iterator MODULE_CITER;
|
|
|
|
|
|
|
|
|
|
|
|
class FP_CACHE
|
|
|
|
{
|
|
|
|
PCB_IO* m_owner; /// Plugin object that owns the cache.
|
|
|
|
wxFileName m_lib_path; /// The path of the library.
|
|
|
|
wxDateTime m_mod_time; /// Footprint library path modified time stamp.
|
|
|
|
MODULE_MAP m_modules; /// Map of footprint file name per MODULE*.
|
|
|
|
|
|
|
|
public:
|
|
|
|
FP_CACHE( PCB_IO* aOwner, const wxString& aLibraryPath );
|
|
|
|
|
2013-07-28 16:30:02 +00:00
|
|
|
wxString GetPath() const { return m_lib_path.GetPath(); }
|
|
|
|
wxDateTime GetLastModificationTime() const { return m_mod_time; }
|
|
|
|
bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
|
2012-10-07 15:37:25 +00:00
|
|
|
MODULE_MAP& GetModules() { return m_modules; }
|
|
|
|
|
|
|
|
// Most all functions in this class throw IO_ERROR exceptions. There are no
|
|
|
|
// error codes nor user interface calls from here, nor in any PLUGIN.
|
|
|
|
// Catch these exceptions higher up please.
|
|
|
|
|
|
|
|
/// save the entire legacy library to m_lib_name;
|
|
|
|
void Save();
|
|
|
|
|
|
|
|
void Load();
|
|
|
|
|
|
|
|
void Remove( const wxString& aFootprintName );
|
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
wxDateTime GetLibModificationTime() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function IsModified
|
|
|
|
* check if the footprint cache has been modified relative to \a aLibPath
|
|
|
|
* and \a aFootprintName.
|
|
|
|
*
|
|
|
|
* @param aLibPath is a path to test the current cache library path against.
|
|
|
|
* @param aFootprintName is the footprint name in the cache to test. If the footprint
|
|
|
|
* name is empty, the all the footprint files in the library are
|
|
|
|
* checked to see if they have been modified.
|
|
|
|
* @return true if the cache has been modified.
|
|
|
|
*/
|
|
|
|
bool IsModified( const wxString& aLibPath,
|
|
|
|
const wxString& aFootprintName = wxEmptyString ) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function IsPath
|
|
|
|
* checks if \a aPath is the same as the current cache path.
|
|
|
|
*
|
|
|
|
* This tests paths by converting \a aPath using the native separators. Internally
|
|
|
|
* #FP_CACHE stores the current path using native separators. This prevents path
|
|
|
|
* miscompares on Windows due to the fact that paths can be stored with / instead of \\
|
|
|
|
* in the footprint library table.
|
|
|
|
*
|
|
|
|
* @param aPath is the library path to test against.
|
|
|
|
* @return true if \a aPath is the same as the cache path.
|
|
|
|
*/
|
|
|
|
bool IsPath( const wxString& aPath ) const;
|
2012-10-07 15:37:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
FP_CACHE::FP_CACHE( PCB_IO* aOwner, const wxString& aLibraryPath )
|
|
|
|
{
|
|
|
|
m_owner = aOwner;
|
|
|
|
m_lib_path.SetPath( aLibraryPath );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
wxDateTime FP_CACHE::GetLibModificationTime() const
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
return m_lib_path.GetModificationTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FP_CACHE::Save()
|
|
|
|
{
|
|
|
|
if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "Cannot create footprint library path '%s'" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
m_lib_path.GetPath().GetData() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !m_lib_path.IsDirWritable() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "Footprint library path '%s' is read only" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
GetChars( m_lib_path.GetPath() ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( MODULE_ITER it = m_modules.begin(); it != m_modules.end(); ++it )
|
|
|
|
{
|
|
|
|
wxFileName fn = it->second->GetFileName();
|
|
|
|
|
|
|
|
if( fn.FileExists() && !it->second->IsModified() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wxString tempFileName = fn.CreateTempFileName( fn.GetPath() );
|
|
|
|
|
|
|
|
// Allow file output stream to go out of scope to close the file stream before
|
|
|
|
// renaming the file.
|
|
|
|
{
|
2013-09-24 18:45:57 +00:00
|
|
|
wxLogTrace( traceFootprintLibrary, wxT( "Creating temporary library file %s" ),
|
|
|
|
GetChars( tempFileName ) );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2012-10-25 07:10:59 +00:00
|
|
|
FILE_OUTPUTFORMATTER formatter( tempFileName );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2012-10-25 05:52:55 +00:00
|
|
|
m_owner->SetOutputFormatter( &formatter );
|
|
|
|
m_owner->Format( (BOARD_ITEM*) it->second->GetModule() );
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wxRemove( fn.GetFullPath() ); // it is not an error if this does not exist
|
|
|
|
|
|
|
|
if( wxRename( tempFileName, fn.GetFullPath() ) )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
wxString msg = wxString::Format(
|
|
|
|
_( "Cannot rename temporary file '%s' to footprint library file '%s'" ),
|
|
|
|
GetChars( tempFileName ),
|
|
|
|
GetChars( fn.GetFullPath() )
|
|
|
|
);
|
|
|
|
THROW_IO_ERROR( msg );
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
it->second->UpdateModificationTime();
|
|
|
|
m_mod_time = GetLibModificationTime();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FP_CACHE::Load()
|
|
|
|
{
|
|
|
|
wxDir dir( m_lib_path.GetPath() );
|
|
|
|
|
|
|
|
if( !dir.IsOpened() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
wxString msg = wxString::Format(
|
|
|
|
_( "Footprint library path '%s' does not exist" ),
|
|
|
|
GetChars( m_lib_path.GetPath() )
|
|
|
|
);
|
|
|
|
|
|
|
|
THROW_IO_ERROR( msg );
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wxString fpFileName;
|
2012-11-19 16:19:38 +00:00
|
|
|
wxString wildcard = wxT( "*." ) + KiCadFootprintFileExtension;
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2013-07-28 16:30:02 +00:00
|
|
|
if( dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2013-07-28 16:30:02 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
// prepend the libpath into fullPath
|
|
|
|
wxFileName fullPath( m_lib_path.GetPath(), fpFileName );
|
|
|
|
|
|
|
|
FILE_LINE_READER reader( fullPath.GetFullPath() );
|
2012-10-24 18:20:33 +00:00
|
|
|
|
2013-07-28 16:30:02 +00:00
|
|
|
m_owner->m_parser->SetLineReader( &reader );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
std::string name = TO_UTF8( fullPath.GetName() );
|
|
|
|
MODULE* footprint = (MODULE*) m_owner->m_parser->Parse();
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
// The footprint name is the file name without the extension.
|
|
|
|
footprint->SetFPID( fullPath.GetName() );
|
|
|
|
m_modules.insert( name, new FP_CACHE_ITEM( footprint, fullPath ) );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2013-07-28 16:30:02 +00:00
|
|
|
} while( dir.GetNext( &fpFileName ) );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2013-07-28 16:30:02 +00:00
|
|
|
// Remember the file modification time of library file when the
|
|
|
|
// cache snapshot was made, so that in a networked environment we will
|
|
|
|
// reload the cache as needed.
|
|
|
|
m_mod_time = GetLibModificationTime();
|
|
|
|
}
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FP_CACHE::Remove( const wxString& aFootprintName )
|
|
|
|
{
|
|
|
|
|
|
|
|
std::string footprintName = TO_UTF8( aFootprintName );
|
|
|
|
|
|
|
|
MODULE_CITER it = m_modules.find( footprintName );
|
|
|
|
|
|
|
|
if( it == m_modules.end() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
wxString msg = wxString::Format(
|
|
|
|
_( "library '%s' has no footprint '%s' to delete" ),
|
|
|
|
GetChars( m_lib_path.GetPath() ),
|
|
|
|
GetChars( aFootprintName )
|
|
|
|
);
|
|
|
|
THROW_IO_ERROR( msg );
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the module from the cache and delete the module file from the library.
|
|
|
|
wxString fullPath = it->second->GetFileName().GetFullPath();
|
|
|
|
m_modules.erase( footprintName );
|
|
|
|
wxRemoveFile( fullPath );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
bool FP_CACHE::IsPath( const wxString& aPath ) const
|
|
|
|
{
|
|
|
|
// Converts path separators to native path separators
|
|
|
|
wxFileName newPath;
|
|
|
|
newPath.AssignDir( aPath );
|
|
|
|
|
|
|
|
return m_lib_path == newPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool FP_CACHE::IsModified( const wxString& aLibPath, const wxString& aFootprintName ) const
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2013-09-24 18:45:57 +00:00
|
|
|
// The library is modified if the library path got deleted or changed.
|
|
|
|
if( !m_lib_path.DirExists() || !IsPath( aLibPath ) )
|
2012-10-07 15:37:25 +00:00
|
|
|
return true;
|
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
// If no footprint was specified, check every file modification time against the time
|
|
|
|
// it was loaded.
|
|
|
|
if( aFootprintName.IsEmpty() )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2013-09-24 18:45:57 +00:00
|
|
|
for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2013-09-24 18:45:57 +00:00
|
|
|
wxFileName fn = m_lib_path;
|
2013-11-27 06:04:04 +00:00
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
fn.SetName( it->second->GetFileName().GetName() );
|
|
|
|
fn.SetExt( KiCadFootprintFileExtension );
|
|
|
|
|
|
|
|
if( !fn.FileExists() )
|
|
|
|
{
|
|
|
|
wxLogTrace( traceFootprintLibrary,
|
|
|
|
wxT( "Footprint cache file '%s' does not exist." ),
|
|
|
|
fn.GetFullPath().GetData() );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( it->second->IsModified() )
|
|
|
|
{
|
|
|
|
wxLogTrace( traceFootprintLibrary,
|
|
|
|
wxT( "Footprint cache file '%s' has been modified." ),
|
|
|
|
fn.GetFullPath().GetData() );
|
|
|
|
return true;
|
|
|
|
}
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
2013-09-24 18:45:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MODULE_CITER it = m_modules.find( TO_UTF8( aFootprintName ) );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
if( it == m_modules.end() || it->second->IsModified() )
|
2012-10-07 15:37:25 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-25 19:17:06 +00:00
|
|
|
void PCB_IO::Save( const wxString& aFileName, BOARD* aBoard, const PROPERTIES* aProperties )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-11-14 07:15:59 +00:00
|
|
|
LOCALE_IO toggle; // toggles on, then off, the C locale.
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-06-24 05:30:22 +00:00
|
|
|
init( aProperties );
|
|
|
|
|
|
|
|
m_board = aBoard; // after init()
|
2012-06-09 17:00:13 +00:00
|
|
|
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
// Prepare net mapping that assures that net codes saved in a file are consecutive integers
|
|
|
|
m_mapping->SetBoard( aBoard );
|
|
|
|
|
2012-10-25 07:10:59 +00:00
|
|
|
FILE_OUTPUTFORMATTER formatter( aFileName );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out = &formatter; // no ownership
|
|
|
|
|
|
|
|
m_out->Print( 0, "(kicad_pcb (version %d) (host pcbnew %s)\n", SEXPR_BOARD_FILE_VERSION,
|
|
|
|
formatter.Quotew( GetBuildVersion() ).c_str() );
|
|
|
|
|
|
|
|
Format( aBoard, 1 );
|
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-27 19:57:38 +00:00
|
|
|
BOARD_ITEM* PCB_IO::Parse( const wxString& aClipboardSourceInput ) throw( PARSE_ERROR, IO_ERROR )
|
2012-10-22 20:41:26 +00:00
|
|
|
{
|
|
|
|
std::string input = TO_UTF8( aClipboardSourceInput );
|
|
|
|
|
|
|
|
STRING_LINE_READER reader( input, wxT( "clipboard" ) );
|
|
|
|
|
2012-10-24 18:20:33 +00:00
|
|
|
m_parser->SetLineReader( &reader );
|
2012-10-22 20:41:26 +00:00
|
|
|
|
2012-10-24 18:20:33 +00:00
|
|
|
return m_parser->Parse();
|
2012-10-22 20:41:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
void PCB_IO::Format( BOARD_ITEM* aItem, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
2012-11-14 07:15:59 +00:00
|
|
|
LOCALE_IO toggle; // public API function, perform anything convenient for caller
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
switch( aItem->Type() )
|
|
|
|
{
|
|
|
|
case PCB_T:
|
|
|
|
format( (BOARD*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_DIMENSION_T:
|
|
|
|
format( ( DIMENSION*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_LINE_T:
|
|
|
|
format( (DRAWSEGMENT*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_MODULE_EDGE_T:
|
|
|
|
format( (EDGE_MODULE*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_TARGET_T:
|
|
|
|
format( (PCB_TARGET*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_MODULE_T:
|
|
|
|
format( (MODULE*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_PAD_T:
|
|
|
|
format( (D_PAD*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_TEXT_T:
|
|
|
|
format( (TEXTE_PCB*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_MODULE_TEXT_T:
|
|
|
|
format( (TEXTE_MODULE*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_TRACE_T:
|
|
|
|
case PCB_VIA_T:
|
|
|
|
format( (TRACK*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_ZONE_AREA_T:
|
|
|
|
format( (ZONE_CONTAINER*) aItem, aNestLevel );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::formatLayer( const BOARD_ITEM* aItem ) const
|
|
|
|
{
|
2013-03-20 04:46:32 +00:00
|
|
|
if( m_ctl & CTL_STD_LAYER_NAMES )
|
2012-11-10 06:39:18 +00:00
|
|
|
{
|
2013-03-31 13:27:46 +00:00
|
|
|
LAYER_NUM layer = aItem->GetLayer();
|
2012-11-10 06:39:18 +00:00
|
|
|
|
|
|
|
// English layer names should never need quoting.
|
2013-03-20 04:46:32 +00:00
|
|
|
m_out->Print( 0, " (layer %s)", TO_UTF8( BOARD::GetStandardLayerName( layer ) ) );
|
2012-11-10 06:39:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
m_out->Print( 0, " (layer %s)", m_out->Quotew( aItem->GetLayerName() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel, "(general\n" );
|
|
|
|
m_out->Print( aNestLevel+1, "(links %d)\n", aBoard->GetRatsnestsCount() );
|
2013-01-09 18:52:44 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(no_connects %d)\n", aBoard->GetUnconnectedNetCount() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
// Write Bounding box info
|
|
|
|
m_out->Print( aNestLevel+1, "(area %s %s %s %s)\n",
|
|
|
|
FMTIU( aBoard->GetBoundingBox().GetX() ).c_str(),
|
|
|
|
FMTIU( aBoard->GetBoundingBox().GetY() ).c_str(),
|
|
|
|
FMTIU( aBoard->GetBoundingBox().GetRight() ).c_str(),
|
|
|
|
FMTIU( aBoard->GetBoundingBox().GetBottom() ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(thickness %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().GetBoardThickness() ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(drawings %d)\n", aBoard->m_Drawings.GetCount() );
|
|
|
|
m_out->Print( aNestLevel+1, "(tracks %d)\n", aBoard->GetNumSegmTrack() );
|
|
|
|
m_out->Print( aNestLevel+1, "(zones %d)\n", aBoard->GetNumSegmZone() );
|
|
|
|
m_out->Print( aNestLevel+1, "(modules %d)\n", aBoard->m_Modules.GetCount() );
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(nets %d)\n", (int) m_mapping->GetSize() );
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel, ")\n\n" );
|
|
|
|
|
|
|
|
aBoard->GetPageSettings().Format( m_out, aNestLevel, m_ctl );
|
|
|
|
aBoard->GetTitleBlock().Format( m_out, aNestLevel, m_ctl );
|
|
|
|
|
|
|
|
// Layers.
|
|
|
|
m_out->Print( aNestLevel, "(layers\n" );
|
|
|
|
|
|
|
|
// Save only the used copper layers from front to back.
|
2013-04-10 15:08:01 +00:00
|
|
|
for( LAYER_NUM layer = LAST_COPPER_LAYER; layer >= FIRST_COPPER_LAYER; --layer)
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2013-04-04 21:35:01 +00:00
|
|
|
LAYER_MSK mask = GetLayerMask( layer );
|
2012-06-09 17:00:13 +00:00
|
|
|
if( mask & aBoard->GetEnabledLayers() )
|
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(%d %s %s", layer,
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str(),
|
|
|
|
LAYER::ShowType( aBoard->GetLayerType( layer ) ) );
|
|
|
|
|
|
|
|
if( !( aBoard->GetVisibleLayers() & mask ) )
|
2012-06-27 20:07:37 +00:00
|
|
|
m_out->Print( 0, " hide" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save used non-copper layers in the order they are defined.
|
2013-04-04 21:35:01 +00:00
|
|
|
for( LAYER_NUM layer = FIRST_NON_COPPER_LAYER; layer <= LAST_NON_COPPER_LAYER; ++layer)
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2013-04-04 21:35:01 +00:00
|
|
|
LAYER_MSK mask = GetLayerMask( layer );
|
2012-06-09 17:00:13 +00:00
|
|
|
if( mask & aBoard->GetEnabledLayers() )
|
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+1, "(%d %s user", layer,
|
|
|
|
m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str() );
|
|
|
|
|
|
|
|
if( !( aBoard->GetVisibleLayers() & mask ) )
|
2012-06-27 20:07:37 +00:00
|
|
|
m_out->Print( 0, " hide" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel, ")\n\n" );
|
|
|
|
|
|
|
|
// Setup
|
|
|
|
m_out->Print( aNestLevel, "(setup\n" );
|
|
|
|
|
|
|
|
// Save current default track width, for compatibility with older Pcbnew version;
|
|
|
|
m_out->Print( aNestLevel+1, "(last_trace_width %s)\n",
|
|
|
|
FMTIU( aBoard->GetCurrentTrackWidth() ).c_str() );
|
|
|
|
|
|
|
|
// Save custom tracks width list (the first is not saved here: this is the netclass value
|
|
|
|
for( unsigned ii = 1; ii < aBoard->m_TrackWidthList.size(); ii++ )
|
|
|
|
m_out->Print( aNestLevel+1, "(user_trace_width %s)\n",
|
|
|
|
FMTIU( aBoard->m_TrackWidthList[ii] ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(trace_clearance %s)\n",
|
|
|
|
FMTIU( aBoard->m_NetClasses.GetDefault()->GetClearance() ).c_str() );
|
|
|
|
|
|
|
|
// ZONE_SETTINGS
|
|
|
|
m_out->Print( aNestLevel+1, "(zone_clearance %s)\n",
|
|
|
|
FMTIU( aBoard->GetZoneSettings().m_ZoneClearance ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(zone_45_only %s)\n",
|
|
|
|
aBoard->GetZoneSettings().m_Zone_45_Only ? "yes" : "no" );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(trace_min %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_TrackMinWidth ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(segment_width %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_DrawSegmentWidth ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(edge_width %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_EdgeSegmentWidth ).c_str() );
|
|
|
|
|
|
|
|
// Save current default via size, for compatibility with older Pcbnew version;
|
|
|
|
m_out->Print( aNestLevel+1, "(via_size %s)\n",
|
|
|
|
FMTIU( aBoard->m_NetClasses.GetDefault()->GetViaDiameter() ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(via_drill %s)\n",
|
|
|
|
FMTIU( aBoard->m_NetClasses.GetDefault()->GetViaDrill() ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(via_min_size %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_ViasMinSize ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(via_min_drill %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_ViasMinDrill ).c_str() );
|
|
|
|
|
|
|
|
// Save custom vias diameters list (the first is not saved here: this is
|
|
|
|
// the netclass value
|
|
|
|
for( unsigned ii = 1; ii < aBoard->m_ViasDimensionsList.size(); ii++ )
|
|
|
|
m_out->Print( aNestLevel+1, "(user_via %s %s)\n",
|
|
|
|
FMTIU( aBoard->m_ViasDimensionsList[ii].m_Diameter ).c_str(),
|
|
|
|
FMTIU( aBoard->m_ViasDimensionsList[ii].m_Drill ).c_str() );
|
|
|
|
|
|
|
|
// for old versions compatibility:
|
2013-08-28 16:14:39 +00:00
|
|
|
if( aBoard->GetDesignSettings().m_BlindBuriedViaAllowed )
|
|
|
|
m_out->Print( aNestLevel+1, "(blind_buried_vias_allowed yes)\n" );
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(uvia_size %s)\n",
|
|
|
|
FMTIU( aBoard->m_NetClasses.GetDefault()->GetuViaDiameter() ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(uvia_drill %s)\n",
|
|
|
|
FMTIU( aBoard->m_NetClasses.GetDefault()->GetuViaDrill() ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(uvias_allowed %s)\n",
|
|
|
|
( aBoard->GetDesignSettings().m_MicroViasAllowed ) ? "yes" : "no" );
|
|
|
|
m_out->Print( aNestLevel+1, "(uvia_min_size %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_MicroViasMinSize ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(uvia_min_drill %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_MicroViasMinDrill ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(pcb_text_width %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_PcbTextWidth ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(pcb_text_size %s %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_PcbTextSize.x ).c_str(),
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_PcbTextSize.y ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(mod_edge_width %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_ModuleSegmentWidth ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(mod_text_size %s %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_ModuleTextSize.x ).c_str(),
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_ModuleTextSize.y ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(mod_text_width %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_ModuleTextWidth ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(pad_size %s %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetSize().x ).c_str(),
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetSize().y ).c_str() );
|
|
|
|
m_out->Print( aNestLevel+1, "(pad_drill %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetDrillSize().x ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(pad_to_mask_clearance %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_SolderMaskMargin ).c_str() );
|
|
|
|
|
2012-11-05 20:20:34 +00:00
|
|
|
if( aBoard->GetDesignSettings().m_SolderMaskMinWidth )
|
|
|
|
m_out->Print( aNestLevel+1, "(solder_mask_min_width %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_SolderMaskMinWidth ).c_str() );
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
if( aBoard->GetDesignSettings().m_SolderPasteMargin != 0 )
|
|
|
|
m_out->Print( aNestLevel+1, "(pad_to_paste_clearance %s)\n",
|
|
|
|
FMTIU( aBoard->GetDesignSettings().m_SolderPasteMargin ).c_str() );
|
|
|
|
|
|
|
|
if( aBoard->GetDesignSettings().m_SolderPasteMarginRatio != 0 )
|
2013-05-30 19:32:00 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(pad_to_paste_clearance_ratio %s)\n",
|
2013-07-19 18:27:22 +00:00
|
|
|
Double2Str( aBoard->GetDesignSettings().m_SolderPasteMarginRatio ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(aux_axis_origin %s %s)\n",
|
2013-08-03 05:15:23 +00:00
|
|
|
FMTIU( aBoard->GetAuxOrigin().x ).c_str(),
|
|
|
|
FMTIU( aBoard->GetAuxOrigin().y ).c_str() );
|
|
|
|
|
2013-08-03 16:16:57 +00:00
|
|
|
if( aBoard->GetGridOrigin().x || aBoard->GetGridOrigin().y )
|
|
|
|
m_out->Print( aNestLevel+1, "(grid_origin %s %s)\n",
|
|
|
|
FMTIU( aBoard->GetGridOrigin().x ).c_str(),
|
|
|
|
FMTIU( aBoard->GetGridOrigin().y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(visible_elements %X)\n",
|
|
|
|
aBoard->GetDesignSettings().GetVisibleElements() );
|
|
|
|
|
2012-06-13 23:04:42 +00:00
|
|
|
aBoard->GetPlotOptions().Format( m_out, aNestLevel+1 );
|
2012-06-11 00:47:15 +00:00
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel, ")\n\n" );
|
|
|
|
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
// Save net codes and names
|
|
|
|
for( NETINFO_MAPPING::iterator net = m_mapping->begin(), netEnd = m_mapping->end();
|
2014-01-16 15:47:31 +00:00
|
|
|
net != netEnd; ++net )
|
2013-03-14 22:54:47 +00:00
|
|
|
{
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
m_out->Print( aNestLevel, "(net %d %s)\n",
|
|
|
|
m_mapping->Translate( net->GetNet() ),
|
|
|
|
m_out->Quotew( net->GetNetname() ).c_str() );
|
|
|
|
}
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
|
|
|
|
// Save the default net class first.
|
|
|
|
aBoard->m_NetClasses.GetDefault()->Format( m_out, aNestLevel, m_ctl );
|
|
|
|
|
|
|
|
// Save the rest of the net classes alphabetically.
|
|
|
|
for( NETCLASSES::const_iterator it = aBoard->m_NetClasses.begin();
|
|
|
|
it != aBoard->m_NetClasses.end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
NETCLASS* netclass = it->second;
|
|
|
|
netclass->Format( m_out, aNestLevel, m_ctl );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the modules.
|
|
|
|
for( MODULE* module = aBoard->m_Modules; module; module = (MODULE*) module->Next() )
|
|
|
|
{
|
|
|
|
Format( module, aNestLevel );
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
}
|
|
|
|
|
2012-06-13 23:04:42 +00:00
|
|
|
// Save the graphical items on the board (not owned by a module)
|
|
|
|
for( BOARD_ITEM* item = aBoard->m_Drawings; item; item = item->Next() )
|
|
|
|
Format( item, aNestLevel );
|
|
|
|
|
2012-12-14 16:54:54 +00:00
|
|
|
if( aBoard->m_Drawings.GetCount() )
|
|
|
|
m_out->Print( 0, "\n" );
|
2012-06-13 23:04:42 +00:00
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
// Do not save MARKER_PCBs, they can be regenerated easily.
|
|
|
|
|
|
|
|
// Save the tracks and vias.
|
|
|
|
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
|
|
|
|
Format( track, aNestLevel );
|
|
|
|
|
2012-12-14 16:54:54 +00:00
|
|
|
if( aBoard->m_Track.GetCount() )
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
/// @todo Add warning here that the old segment filed zones are no longer supported and
|
|
|
|
/// will not be saved.
|
|
|
|
|
|
|
|
// Save the polygon (which are the newer technology) zones.
|
|
|
|
for( int i=0; i < aBoard->GetAreaCount(); ++i )
|
|
|
|
Format( aBoard->GetArea( i ), aNestLevel );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( DIMENSION* aDimension, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel, "(dimension %s (width %s)",
|
2013-03-13 18:53:58 +00:00
|
|
|
FMT_IU( aDimension->GetValue() ).c_str(),
|
|
|
|
FMT_IU( aDimension->GetWidth() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
formatLayer( aDimension );
|
|
|
|
|
|
|
|
if( aDimension->GetTimeStamp() )
|
|
|
|
m_out->Print( 0, " (tstamp %lX)", aDimension->GetTimeStamp() );
|
|
|
|
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
Format( (TEXTE_PCB*) &aDimension->Text(), aNestLevel+1 );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(feature1 (pts (xy %s %s) (xy %s %s)))\n",
|
2012-12-19 19:31:36 +00:00
|
|
|
FMT_IU( aDimension->m_featureLineDO.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_featureLineDO.y ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_featureLineDF.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_featureLineDF.y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(feature2 (pts (xy %s %s) (xy %s %s)))\n",
|
2012-12-19 19:31:36 +00:00
|
|
|
FMT_IU( aDimension->m_featureLineGO.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_featureLineGO.y ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_featureLineGF.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_featureLineGF.y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(crossbar (pts (xy %s %s) (xy %s %s)))\n",
|
2012-12-19 19:31:36 +00:00
|
|
|
FMT_IU( aDimension->m_crossBarO.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_crossBarO.y ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_crossBarF.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_crossBarF.y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(arrow1a (pts (xy %s %s) (xy %s %s)))\n",
|
2012-12-19 19:31:36 +00:00
|
|
|
FMT_IU( aDimension->m_arrowD1O.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowD1O.y ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowD1F.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowD1F.y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(arrow1b (pts (xy %s %s) (xy %s %s)))\n",
|
2012-12-19 19:31:36 +00:00
|
|
|
FMT_IU( aDimension->m_arrowD2O.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowD2O.y ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowD2F.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowD2F.y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(arrow2a (pts (xy %s %s) (xy %s %s)))\n",
|
2012-12-19 19:31:36 +00:00
|
|
|
FMT_IU( aDimension->m_arrowG1O.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowG1O.y ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowG1F.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowG1F.y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(arrow2b (pts (xy %s %s) (xy %s %s)))\n",
|
2012-12-19 19:31:36 +00:00
|
|
|
FMT_IU( aDimension->m_arrowG2O.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowG2O.y ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowG2F.x ).c_str(),
|
|
|
|
FMT_IU( aDimension->m_arrowG2F.y ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( DRAWSEGMENT* aSegment, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
switch( aSegment->GetShape() )
|
|
|
|
{
|
|
|
|
case S_SEGMENT: // Line
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(gr_line (start %s) (end %s)",
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aSegment->GetStart() ).c_str(),
|
2012-06-16 22:49:24 +00:00
|
|
|
FMT_IU( aSegment->GetEnd() ).c_str() );
|
|
|
|
|
|
|
|
if( aSegment->GetAngle() != 0.0 )
|
|
|
|
m_out->Print( 0, " (angle %s)", FMT_ANGLE( aSegment->GetAngle() ).c_str() );
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case S_CIRCLE: // Circle
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(gr_circle (center %s) (end %s)",
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aSegment->GetStart() ).c_str(),
|
|
|
|
FMT_IU( aSegment->GetEnd() ).c_str() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_ARC: // Arc
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(gr_arc (start %s) (end %s) (angle %s)",
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aSegment->GetStart() ).c_str(),
|
|
|
|
FMT_IU( aSegment->GetEnd() ).c_str(),
|
|
|
|
FMT_ANGLE( aSegment->GetAngle() ).c_str() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_POLYGON: // Polygon
|
|
|
|
m_out->Print( aNestLevel, "(gr_poly (pts" );
|
|
|
|
|
|
|
|
for( i = 0; i < aSegment->GetPolyPoints().size(); ++i )
|
|
|
|
m_out->Print( 0, " (xy %s)", FMT_IU( aSegment->GetPolyPoints()[i] ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( 0, ")" );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_CURVE: // Bezier curve
|
|
|
|
m_out->Print( aNestLevel, "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
|
|
|
|
FMT_IU( aSegment->GetStart() ).c_str(),
|
|
|
|
FMT_IU( aSegment->GetBezControl1() ).c_str(),
|
|
|
|
FMT_IU( aSegment->GetBezControl2() ).c_str(),
|
|
|
|
FMT_IU( aSegment->GetEnd() ).c_str() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxFAIL_MSG( wxT( "Cannot format invalid DRAWSEGMENT type." ) );
|
|
|
|
};
|
|
|
|
|
|
|
|
formatLayer( aSegment );
|
|
|
|
|
|
|
|
if( aSegment->GetWidth() != 0 )
|
|
|
|
m_out->Print( 0, " (width %s)", FMT_IU( aSegment->GetWidth() ).c_str() );
|
|
|
|
|
|
|
|
if( aSegment->GetTimeStamp() )
|
|
|
|
m_out->Print( 0, " (tstamp %lX)", aSegment->GetTimeStamp() );
|
|
|
|
|
|
|
|
if( aSegment->GetStatus() )
|
|
|
|
m_out->Print( 0, " (status %X)", aSegment->GetStatus() );
|
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( EDGE_MODULE* aModuleDrawing, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
|
|
|
switch( aModuleDrawing->GetShape() )
|
|
|
|
{
|
|
|
|
case S_SEGMENT: // Line
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(fp_line (start %s) (end %s)",
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
|
|
|
|
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_CIRCLE: // Circle
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(fp_circle (center %s) (end %s)",
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
|
|
|
|
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_ARC: // Arc
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(fp_arc (start %s) (end %s) (angle %s)",
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
|
|
|
|
FMT_IU( aModuleDrawing->GetEnd0() ).c_str(),
|
|
|
|
FMT_ANGLE( aModuleDrawing->GetAngle() ).c_str() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_POLYGON: // Polygon
|
|
|
|
m_out->Print( aNestLevel, "(fp_poly (pts" );
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < aModuleDrawing->GetPolyPoints().size(); ++i )
|
2012-08-11 05:50:17 +00:00
|
|
|
{
|
|
|
|
int nestLevel = 0;
|
|
|
|
|
|
|
|
if( i && !(i%4) ) // newline every 4(pts)
|
|
|
|
{
|
|
|
|
nestLevel = aNestLevel + 1;
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
}
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2012-08-11 05:50:17 +00:00
|
|
|
m_out->Print( nestLevel, "%s(xy %s)",
|
|
|
|
nestLevel ? "" : " ",
|
|
|
|
FMT_IU( aModuleDrawing->GetPolyPoints()[i] ).c_str() );
|
|
|
|
}
|
|
|
|
m_out->Print( 0, ")" );
|
2012-06-09 17:00:13 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case S_CURVE: // Bezier curve
|
|
|
|
m_out->Print( aNestLevel, "(fp_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
|
|
|
|
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
|
|
|
|
FMT_IU( aModuleDrawing->GetBezControl1() ).c_str(),
|
|
|
|
FMT_IU( aModuleDrawing->GetBezControl2() ).c_str(),
|
|
|
|
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxFAIL_MSG( wxT( "Cannot format invalid DRAWSEGMENT type." ) );
|
|
|
|
};
|
|
|
|
|
|
|
|
formatLayer( aModuleDrawing );
|
|
|
|
|
|
|
|
if( aModuleDrawing->GetWidth() != 0 )
|
|
|
|
m_out->Print( 0, " (width %s)", FMT_IU( aModuleDrawing->GetWidth() ).c_str() );
|
|
|
|
|
2012-11-14 07:15:59 +00:00
|
|
|
/* 11-Nov-2021 remove if no one whines after a couple of months. Simple graphic items
|
|
|
|
perhaps do not need these.
|
2012-06-09 17:00:13 +00:00
|
|
|
if( aModuleDrawing->GetTimeStamp() )
|
|
|
|
m_out->Print( 0, " (tstamp %lX)", aModuleDrawing->GetTimeStamp() );
|
|
|
|
|
|
|
|
if( aModuleDrawing->GetStatus() )
|
|
|
|
m_out->Print( 0, " (status %X)", aModuleDrawing->GetStatus() );
|
2012-11-14 07:15:59 +00:00
|
|
|
*/
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( PCB_TARGET* aTarget, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel, "(target %s (at %s) (size %s)",
|
|
|
|
( aTarget->GetShape() ) ? "x" : "plus",
|
|
|
|
FMT_IU( aTarget->GetPosition() ).c_str(),
|
|
|
|
FMT_IU( aTarget->GetSize() ).c_str() );
|
|
|
|
|
|
|
|
if( aTarget->GetWidth() != 0 )
|
2012-06-13 23:04:42 +00:00
|
|
|
m_out->Print( 0, " (width %s)", FMT_IU( aTarget->GetWidth() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
formatLayer( aTarget );
|
|
|
|
|
|
|
|
if( aTarget->GetTimeStamp() )
|
2012-06-13 23:04:42 +00:00
|
|
|
m_out->Print( 0, " (tstamp %lX)", aTarget->GetTimeStamp() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2012-06-13 23:04:42 +00:00
|
|
|
m_out->Print( 0, ")\n" );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( MODULE* aModule, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
2013-06-24 05:30:22 +00:00
|
|
|
if( !( m_ctl & CTL_OMIT_INITIAL_COMMENTS ) )
|
2013-06-23 19:18:33 +00:00
|
|
|
{
|
2013-06-24 05:30:22 +00:00
|
|
|
const wxArrayString* initial_comments = aModule->GetInitialComments();
|
|
|
|
|
|
|
|
if( initial_comments )
|
|
|
|
{
|
|
|
|
for( unsigned i=0; i<initial_comments->GetCount(); ++i )
|
|
|
|
m_out->Print( aNestLevel, "%s\n", TO_UTF8( (*initial_comments)[i] ) );
|
|
|
|
|
|
|
|
m_out->Print( 0, "\n" ); // improve readability?
|
|
|
|
}
|
2013-06-23 19:18:33 +00:00
|
|
|
}
|
|
|
|
|
2013-09-08 18:31:21 +00:00
|
|
|
m_out->Print( aNestLevel, "(module %s",
|
2013-09-17 00:52:08 +00:00
|
|
|
m_out->Quotes( aModule->GetFPID().Format() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aModule->IsLocked() )
|
2013-08-08 16:09:49 +00:00
|
|
|
m_out->Print( 0, " locked" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aModule->IsPlaced() )
|
2013-08-08 16:09:49 +00:00
|
|
|
m_out->Print( 0, " placed" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
formatLayer( aModule );
|
|
|
|
|
2012-11-14 07:15:59 +00:00
|
|
|
if( !( m_ctl & CTL_OMIT_TSTAMPS ) )
|
|
|
|
{
|
|
|
|
m_out->Print( 0, " (tedit %lX) (tstamp %lX)\n",
|
2012-06-09 17:00:13 +00:00
|
|
|
aModule->GetLastEditTime(), aModule->GetTimeStamp() );
|
2012-11-14 07:15:59 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
m_out->Print( 0, "\n" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-11-28 16:40:23 +00:00
|
|
|
if( !( m_ctl & CTL_OMIT_AT ) )
|
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+1, "(at %s", FMT_IU( aModule->GetPosition() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-11-28 16:40:23 +00:00
|
|
|
if( aModule->GetOrientation() != 0.0 )
|
|
|
|
m_out->Print( 0, " %s", FMT_ANGLE( aModule->GetOrientation() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-11-28 16:40:23 +00:00
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( !aModule->GetDescription().IsEmpty() )
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(descr %s)\n",
|
2013-03-13 18:53:58 +00:00
|
|
|
m_out->Quotew( aModule->GetDescription() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( !aModule->GetKeywords().IsEmpty() )
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(tags %s)\n",
|
2013-03-13 18:53:58 +00:00
|
|
|
m_out->Quotew( aModule->GetKeywords() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-10-04 22:23:04 +00:00
|
|
|
if( !( m_ctl & CTL_OMIT_PATH ) && !!aModule->GetPath() )
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(path %s)\n",
|
2013-03-13 18:53:58 +00:00
|
|
|
m_out->Quotew( aModule->GetPath() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetPlacementCost90() != 0 )
|
|
|
|
m_out->Print( aNestLevel+1, "(autoplace_cost90 %d)\n", aModule->GetPlacementCost90() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetPlacementCost180() != 0 )
|
|
|
|
m_out->Print( aNestLevel+1, "(autoplace_cost180 %d)\n", aModule->GetPlacementCost180() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aModule->GetLocalSolderMaskMargin() != 0 )
|
|
|
|
m_out->Print( aNestLevel+1, "(solder_mask_margin %s)\n",
|
|
|
|
FMT_IU( aModule->GetLocalSolderMaskMargin() ).c_str() );
|
|
|
|
|
|
|
|
if( aModule->GetLocalSolderPasteMargin() != 0 )
|
|
|
|
m_out->Print( aNestLevel+1, "(solder_paste_margin %s)\n",
|
|
|
|
FMT_IU( aModule->GetLocalSolderPasteMargin() ).c_str() );
|
|
|
|
|
|
|
|
if( aModule->GetLocalSolderPasteMarginRatio() != 0 )
|
2013-05-30 19:32:00 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(solder_paste_ratio %s)\n",
|
2013-07-19 18:27:22 +00:00
|
|
|
Double2Str( aModule->GetLocalSolderPasteMarginRatio() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aModule->GetLocalClearance() != 0 )
|
|
|
|
m_out->Print( aNestLevel+1, "(clearance %s)\n",
|
|
|
|
FMT_IU( aModule->GetLocalClearance() ).c_str() );
|
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetZoneConnection() != UNDEFINED_CONNECTION )
|
|
|
|
m_out->Print( aNestLevel+1, "(zone_connect %d)\n", aModule->GetZoneConnection() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetThermalWidth() != 0 )
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(thermal_width %s)\n",
|
2013-03-13 18:53:58 +00:00
|
|
|
FMT_IU( aModule->GetThermalWidth() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetThermalGap() != 0 )
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(thermal_gap %s)\n",
|
2013-03-13 18:53:58 +00:00
|
|
|
FMT_IU( aModule->GetThermalGap() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
// Attributes
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetAttributes() != MOD_DEFAULT )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+1, "(attr" );
|
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetAttributes() & MOD_CMS )
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( 0, " smd" );
|
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
if( aModule->GetAttributes() & MOD_VIRTUAL )
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( 0, " virtual" );
|
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
Format( (BOARD_ITEM*) &aModule->Reference(), aNestLevel+1 );
|
|
|
|
Format( (BOARD_ITEM*) &aModule->Value(), aNestLevel+1 );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
// Save drawing elements.
|
2013-03-18 19:36:07 +00:00
|
|
|
for( BOARD_ITEM* gr = aModule->GraphicalItems(); gr; gr = gr->Next() )
|
2012-06-09 17:00:13 +00:00
|
|
|
Format( gr, aNestLevel+1 );
|
|
|
|
|
|
|
|
// Save pads.
|
2013-03-18 19:36:07 +00:00
|
|
|
for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
|
2013-09-26 05:29:54 +00:00
|
|
|
format( pad, aNestLevel+1 );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
// Save 3D info.
|
2013-03-18 19:36:07 +00:00
|
|
|
for( S3D_MASTER* t3D = aModule->Models(); t3D; t3D = t3D->Next() )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2014-01-25 12:23:29 +00:00
|
|
|
if( !t3D->GetShape3DName().IsEmpty() )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+1, "(model %s\n",
|
2014-01-25 12:23:29 +00:00
|
|
|
m_out->Quotew( t3D->GetShape3DName() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-30 19:32:00 +00:00
|
|
|
m_out->Print( aNestLevel+2, "(at (xyz %s %s %s))\n",
|
2013-07-19 18:27:22 +00:00
|
|
|
Double2Str( t3D->m_MatPosition.x ).c_str(),
|
|
|
|
Double2Str( t3D->m_MatPosition.y ).c_str(),
|
|
|
|
Double2Str( t3D->m_MatPosition.z ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-30 19:32:00 +00:00
|
|
|
m_out->Print( aNestLevel+2, "(scale (xyz %s %s %s))\n",
|
2013-07-19 18:27:22 +00:00
|
|
|
Double2Str( t3D->m_MatScale.x ).c_str(),
|
|
|
|
Double2Str( t3D->m_MatScale.y ).c_str(),
|
|
|
|
Double2Str( t3D->m_MatScale.z ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-30 19:32:00 +00:00
|
|
|
m_out->Print( aNestLevel+2, "(rotate (xyz %s %s %s))\n",
|
2013-07-19 18:27:22 +00:00
|
|
|
Double2Str( t3D->m_MatRotation.x ).c_str(),
|
|
|
|
Double2Str( t3D->m_MatRotation.y ).c_str(),
|
|
|
|
Double2Str( t3D->m_MatRotation.z ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, ")\n" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-30 17:24:04 +00:00
|
|
|
void PCB_IO::formatLayers( LAYER_MSK aLayerMask, int aNestLevel ) const
|
2012-11-14 07:15:59 +00:00
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
2013-09-26 05:29:54 +00:00
|
|
|
std::string output;
|
|
|
|
|
|
|
|
if( aNestLevel == 0 )
|
|
|
|
output += ' ';
|
|
|
|
|
|
|
|
output += "(layers";
|
2012-11-14 07:15:59 +00:00
|
|
|
|
2013-03-30 17:24:04 +00:00
|
|
|
LAYER_MSK cuMask = ALL_CU_LAYERS;
|
2012-11-14 07:15:59 +00:00
|
|
|
|
|
|
|
if( m_board )
|
|
|
|
cuMask &= m_board->GetEnabledLayers();
|
|
|
|
|
|
|
|
// output copper layers first, then non copper
|
|
|
|
|
|
|
|
if( ( aLayerMask & cuMask ) == cuMask )
|
|
|
|
{
|
2013-09-26 05:29:54 +00:00
|
|
|
output += " *.Cu";
|
2012-11-14 07:15:59 +00:00
|
|
|
aLayerMask &= ~ALL_CU_LAYERS; // clear bits, so they are not output again below
|
|
|
|
}
|
|
|
|
else if( ( aLayerMask & cuMask ) == (LAYER_BACK | LAYER_FRONT) )
|
|
|
|
{
|
2013-09-26 05:29:54 +00:00
|
|
|
output += " F&B.Cu";
|
2012-11-14 07:15:59 +00:00
|
|
|
aLayerMask &= ~(LAYER_BACK | LAYER_FRONT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( aLayerMask & (ADHESIVE_LAYER_BACK | ADHESIVE_LAYER_FRONT)) == (ADHESIVE_LAYER_BACK | ADHESIVE_LAYER_FRONT) )
|
|
|
|
{
|
2013-09-26 05:29:54 +00:00
|
|
|
output += " *.Adhes";
|
2012-11-14 07:15:59 +00:00
|
|
|
aLayerMask &= ~(ADHESIVE_LAYER_BACK | ADHESIVE_LAYER_FRONT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( aLayerMask & (SOLDERPASTE_LAYER_BACK | SOLDERPASTE_LAYER_FRONT)) == (SOLDERPASTE_LAYER_BACK | SOLDERPASTE_LAYER_FRONT) )
|
|
|
|
{
|
2013-09-26 05:29:54 +00:00
|
|
|
output += " *.Paste";
|
2012-11-14 07:15:59 +00:00
|
|
|
aLayerMask &= ~(SOLDERPASTE_LAYER_BACK | SOLDERPASTE_LAYER_FRONT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( aLayerMask & (SILKSCREEN_LAYER_BACK | SILKSCREEN_LAYER_FRONT)) == (SILKSCREEN_LAYER_BACK | SILKSCREEN_LAYER_FRONT) )
|
|
|
|
{
|
2013-09-26 05:29:54 +00:00
|
|
|
output += " *.SilkS";
|
2012-11-14 07:15:59 +00:00
|
|
|
aLayerMask &= ~(SILKSCREEN_LAYER_BACK | SILKSCREEN_LAYER_FRONT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( aLayerMask & (SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT)) == (SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT) )
|
|
|
|
{
|
2013-09-26 05:29:54 +00:00
|
|
|
output += " *.Mask";
|
2012-11-14 07:15:59 +00:00
|
|
|
aLayerMask &= ~(SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT);
|
|
|
|
}
|
|
|
|
|
|
|
|
// output any individual layers not handled in wildcard combos above
|
|
|
|
|
|
|
|
if( m_board )
|
2013-04-04 21:35:01 +00:00
|
|
|
aLayerMask &= m_board->GetEnabledLayers();
|
2012-11-14 07:15:59 +00:00
|
|
|
|
|
|
|
wxString layerName;
|
|
|
|
|
2013-04-04 21:35:01 +00:00
|
|
|
for( LAYER_NUM layer = FIRST_LAYER; layer < NB_PCB_LAYERS; ++layer )
|
2012-11-14 07:15:59 +00:00
|
|
|
{
|
2013-04-04 21:35:01 +00:00
|
|
|
if( aLayerMask & GetLayerMask( layer ) )
|
2012-11-14 07:15:59 +00:00
|
|
|
{
|
2013-06-24 05:30:22 +00:00
|
|
|
if( m_board && !( m_ctl & CTL_STD_LAYER_NAMES ) )
|
2012-11-14 07:15:59 +00:00
|
|
|
layerName = m_board->GetLayerName( layer );
|
|
|
|
|
|
|
|
else // I am being called from FootprintSave()
|
2013-03-20 04:46:32 +00:00
|
|
|
layerName = BOARD::GetStandardLayerName( layer );
|
2012-11-14 07:15:59 +00:00
|
|
|
|
2013-09-26 05:29:54 +00:00
|
|
|
output += ' ';
|
|
|
|
output += m_out->Quotew( layerName );
|
2012-11-14 07:15:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-26 05:29:54 +00:00
|
|
|
m_out->Print( aNestLevel, "%s)", output.c_str() );
|
2012-11-14 07:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
2012-10-24 18:42:39 +00:00
|
|
|
const char* shape;
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
switch( aPad->GetShape() )
|
|
|
|
{
|
2012-10-24 18:42:39 +00:00
|
|
|
case PAD_CIRCLE: shape = "circle"; break;
|
|
|
|
case PAD_RECT: shape = "rect"; break;
|
|
|
|
case PAD_OVAL: shape = "oval"; break;
|
|
|
|
case PAD_TRAPEZOID: shape = "trapezoid"; break;
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"), aPad->GetShape() ) );
|
|
|
|
}
|
|
|
|
|
2012-10-24 18:42:39 +00:00
|
|
|
const char* type;
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
switch( aPad->GetAttribute() )
|
|
|
|
{
|
|
|
|
case PAD_STANDARD: type = "thru_hole"; break;
|
|
|
|
case PAD_SMD: type = "smd"; break;
|
|
|
|
case PAD_CONN: type = "connect"; break;
|
|
|
|
case PAD_HOLE_NOT_PLATED: type = "np_thru_hole"; break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
THROW_IO_ERROR( wxString::Format( _( "unknown pad attribute: %d" ),
|
|
|
|
aPad->GetAttribute() ) );
|
|
|
|
}
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(pad %s %s %s",
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Quotew( aPad->GetPadName() ).c_str(),
|
2012-10-24 18:42:39 +00:00
|
|
|
type, shape );
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, " (at %s", FMT_IU( aPad->GetPos0() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetOrientation() != 0.0 )
|
|
|
|
m_out->Print( 0, " %s", FMT_ANGLE( aPad->GetOrientation() ).c_str() );
|
|
|
|
|
2012-06-13 23:04:42 +00:00
|
|
|
m_out->Print( 0, ")" );
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, " (size %s)", FMT_IU( aPad->GetSize() ).c_str() );
|
2012-06-13 23:04:42 +00:00
|
|
|
|
|
|
|
if( (aPad->GetDelta().GetWidth()) != 0 || (aPad->GetDelta().GetHeight() != 0 ) )
|
|
|
|
m_out->Print( 0, " (rect_delta %s )", FMT_IU( aPad->GetDelta() ).c_str() );
|
|
|
|
|
|
|
|
wxSize sz = aPad->GetDrillSize();
|
2013-04-10 19:20:35 +00:00
|
|
|
wxPoint shapeoffset = aPad->GetOffset();
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-04-10 19:20:35 +00:00
|
|
|
if( (sz.GetWidth() > 0) || (sz.GetHeight() > 0) ||
|
2013-05-25 16:10:19 +00:00
|
|
|
(shapeoffset.x != 0) || (shapeoffset.y != 0) )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, " (drill" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2014-01-26 14:20:58 +00:00
|
|
|
if( aPad->GetDrillShape() == PAD_DRILL_OBLONG )
|
2012-06-13 23:04:42 +00:00
|
|
|
m_out->Print( 0, " oval" );
|
|
|
|
|
2012-06-24 01:19:41 +00:00
|
|
|
if( sz.GetWidth() > 0 )
|
|
|
|
m_out->Print( 0, " %s", FMT_IU( sz.GetWidth() ).c_str() );
|
|
|
|
|
|
|
|
if( sz.GetHeight() > 0 && sz.GetWidth() != sz.GetHeight() )
|
|
|
|
m_out->Print( 0, " %s", FMT_IU( sz.GetHeight() ).c_str() );
|
2012-06-13 23:04:42 +00:00
|
|
|
|
2013-04-10 19:20:35 +00:00
|
|
|
if( (shapeoffset.x != 0) || (shapeoffset.y != 0) )
|
2012-06-13 23:04:42 +00:00
|
|
|
m_out->Print( 0, " (offset %s)", FMT_IU( aPad->GetOffset() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, ")" );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 05:29:54 +00:00
|
|
|
formatLayers( aPad->GetLayerMask(), 0 );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-09-26 05:29:54 +00:00
|
|
|
std::string output;
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2012-06-24 01:19:41 +00:00
|
|
|
// Unconnected pad is default net so don't save it.
|
2012-11-10 06:39:18 +00:00
|
|
|
if( !(m_ctl & CTL_OMIT_NETS) && aPad->GetNet() != 0 )
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
StrPrintf( &output, " (net %d %s)", m_mapping->Translate( aPad->GetNet() ),
|
|
|
|
m_out->Quotew( aPad->GetNetname() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2012-12-12 11:57:17 +00:00
|
|
|
if( aPad->GetPadToDieLength() != 0 )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (die_length %s)", FMT_IU( aPad->GetPadToDieLength() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetLocalSolderMaskMargin() != 0 )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (solder_mask_margin %s)", FMT_IU( aPad->GetLocalSolderMaskMargin() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetLocalSolderPasteMargin() != 0 )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (solder_paste_margin %s)", FMT_IU( aPad->GetLocalSolderPasteMargin() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetLocalSolderPasteMarginRatio() != 0 )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (solder_paste_margin_ratio %s)",
|
2013-09-26 05:29:54 +00:00
|
|
|
Double2Str( aPad->GetLocalSolderPasteMarginRatio() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetLocalClearance() != 0 )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (clearance %s)", FMT_IU( aPad->GetLocalClearance() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetZoneConnection() != UNDEFINED_CONNECTION )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (zone_connect %d)", aPad->GetZoneConnection() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetThermalWidth() != 0 )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (thermal_width %s)", FMT_IU( aPad->GetThermalWidth() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aPad->GetThermalGap() != 0 )
|
2013-09-26 15:02:46 +00:00
|
|
|
StrPrintf( &output, " (thermal_gap %s)", FMT_IU( aPad->GetThermalGap() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-09-26 05:29:54 +00:00
|
|
|
if( output.size() )
|
|
|
|
{
|
|
|
|
m_out->Print( 0, "\n" );
|
2013-09-26 15:02:46 +00:00
|
|
|
m_out->Print( aNestLevel+1, "%s", output.c_str()+1 ); // +1 skips 1st space on 1st element
|
2013-09-26 05:29:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( TEXTE_PCB* aText, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(gr_text %s (at %s",
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Quotew( aText->GetText() ).c_str(),
|
2013-03-18 19:36:07 +00:00
|
|
|
FMT_IU( aText->GetTextPosition() ).c_str() );
|
2012-06-16 22:49:24 +00:00
|
|
|
|
|
|
|
if( aText->GetOrientation() != 0.0 )
|
|
|
|
m_out->Print( 0, " %s", FMT_ANGLE( aText->GetOrientation() ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( 0, ")" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
formatLayer( aText );
|
|
|
|
|
|
|
|
if( aText->GetTimeStamp() )
|
|
|
|
m_out->Print( 0, " (tstamp %lX)", aText->GetTimeStamp() );
|
|
|
|
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
|
|
|
|
aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( TEXTE_MODULE* aText, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
|
|
|
MODULE* parent = (MODULE*) aText->GetParent();
|
|
|
|
double orient = aText->GetOrientation();
|
|
|
|
wxString type;
|
|
|
|
|
|
|
|
switch( aText->GetType() )
|
|
|
|
{
|
2013-04-07 11:55:18 +00:00
|
|
|
case TEXTE_MODULE::TEXT_is_REFERENCE: type = wxT( "reference" ); break;
|
|
|
|
case TEXTE_MODULE::TEXT_is_VALUE: type = wxT( "value" ); break;
|
|
|
|
default: type = wxT( "user" );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Due to the Pcbnew history, m_Orient is saved in screen value
|
|
|
|
// but it is handled as relative to its parent footprint
|
|
|
|
if( parent )
|
|
|
|
orient += parent->GetOrientation();
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(fp_text %s %s (at %s",
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Quotew( type ).c_str(),
|
|
|
|
m_out->Quotew( aText->GetText() ).c_str(),
|
2012-06-16 22:49:24 +00:00
|
|
|
FMT_IU( aText->GetPos0() ).c_str() );
|
|
|
|
|
|
|
|
if( orient != 0.0 )
|
|
|
|
m_out->Print( 0, " %s", FMT_ANGLE( orient ).c_str() );
|
2012-06-11 00:47:15 +00:00
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, ")" );
|
2012-06-11 00:47:15 +00:00
|
|
|
formatLayer( aText );
|
|
|
|
|
|
|
|
if( !aText->IsVisible() )
|
|
|
|
m_out->Print( 0, " hide" );
|
|
|
|
|
|
|
|
m_out->Print( 0, "\n" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl );
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( TRACK* aTrack, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
|
|
|
if( aTrack->Type() == PCB_VIA_T )
|
|
|
|
{
|
2013-03-31 13:27:46 +00:00
|
|
|
LAYER_NUM layer1, layer2;
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
SEGVIA* via = (SEGVIA*) aTrack;
|
|
|
|
BOARD* board = (BOARD*) via->GetParent();
|
|
|
|
|
|
|
|
wxCHECK_RET( board != 0, wxT( "Via " ) + via->GetSelectMenuText() +
|
|
|
|
wxT( " has no parent." ) );
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel, "(via" );
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
via->ReturnLayerPair( &layer1, &layer2 );
|
|
|
|
|
|
|
|
switch( aTrack->GetShape() )
|
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
case VIA_THROUGH: // Default shape not saved.
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIA_BLIND_BURIED:
|
|
|
|
m_out->Print( 0, " blind" );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIA_MICROVIA:
|
|
|
|
m_out->Print( 0, " micro" );
|
|
|
|
break;
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
default:
|
|
|
|
THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), aTrack->GetShape() ) );
|
|
|
|
}
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, " (at %s) (size %s)",
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aTrack->GetStart() ).c_str(),
|
|
|
|
FMT_IU( aTrack->GetWidth() ).c_str() );
|
|
|
|
|
|
|
|
if( aTrack->GetDrill() != UNDEFINED_DRILL_DIAMETER )
|
|
|
|
m_out->Print( 0, " (drill %s)", FMT_IU( aTrack->GetDrill() ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( 0, " (layers %s %s)",
|
|
|
|
m_out->Quotew( m_board->GetLayerName( layer1 ) ).c_str(),
|
|
|
|
m_out->Quotew( m_board->GetLayerName( layer2 ) ).c_str() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel, "(segment (start %s) (end %s) (width %s)",
|
|
|
|
FMT_IU( aTrack->GetStart() ).c_str(), FMT_IU( aTrack->GetEnd() ).c_str(),
|
|
|
|
FMT_IU( aTrack->GetWidth() ).c_str() );
|
|
|
|
|
|
|
|
m_out->Print( 0, " (layer %s)", m_out->Quotew( aTrack->GetLayerName() ).c_str() );
|
|
|
|
}
|
|
|
|
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
m_out->Print( 0, " (net %d)", m_mapping->Translate( aTrack->GetNet() ) );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aTrack->GetTimeStamp() != 0 )
|
|
|
|
m_out->Print( 0, " (tstamp %lX)", aTrack->GetTimeStamp() );
|
|
|
|
|
|
|
|
if( aTrack->GetStatus() != 0 )
|
|
|
|
m_out->Print( 0, " (status %X)", aTrack->GetStatus() );
|
|
|
|
|
|
|
|
m_out->Print( 0, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|
|
|
throw( IO_ERROR )
|
|
|
|
{
|
2013-03-01 19:59:29 +00:00
|
|
|
// Save the NET info; For keepout zones, net code and net name are irrelevant
|
|
|
|
// so be sure a dummy value is stored, just for ZONE_CONTAINER compatibility
|
|
|
|
// (perhaps netcode and netname should be not stored)
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel, "(zone (net %d) (net_name %s)",
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
aZone->GetIsKeepout() ? 0 : m_mapping->Translate( aZone->GetNet() ),
|
2014-01-14 10:41:06 +00:00
|
|
|
m_out->Quotew( aZone->GetIsKeepout() ? wxT("") : aZone->GetNetname() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
formatLayer( aZone );
|
|
|
|
|
|
|
|
m_out->Print( 0, " (tstamp %lX)", aZone->GetTimeStamp() );
|
|
|
|
|
|
|
|
// Save the outline aux info
|
|
|
|
std::string hatch;
|
|
|
|
|
|
|
|
switch( aZone->GetHatchStyle() )
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case CPolyLine::NO_HATCH: hatch = "none"; break;
|
|
|
|
case CPolyLine::DIAGONAL_EDGE: hatch = "edge"; break;
|
|
|
|
case CPolyLine::DIAGONAL_FULL: hatch = "full"; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( 0, " (hatch %s %s)\n", hatch.c_str(),
|
2013-03-20 14:50:12 +00:00
|
|
|
FMT_IU( aZone->Outline()->GetHatchPitch() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( aZone->GetPriority() > 0 )
|
2012-07-13 18:55:29 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(priority %d)\n", aZone->GetPriority() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(connect_pads" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
switch( aZone->GetPadConnection() )
|
|
|
|
{
|
|
|
|
default:
|
2012-06-16 22:49:24 +00:00
|
|
|
case THERMAL_PAD: // Default option not saved or loaded.
|
|
|
|
break;
|
|
|
|
|
2012-07-09 07:10:07 +00:00
|
|
|
case THT_THERMAL:
|
|
|
|
m_out->Print( 0, " thru_hole_only" );
|
|
|
|
break;
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
case PAD_IN_ZONE:
|
|
|
|
m_out->Print( 0, " yes" );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PAD_NOT_IN_ZONE:
|
|
|
|
m_out->Print( 0, " no" );
|
|
|
|
break;
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, " (clearance %s))\n",
|
|
|
|
FMT_IU( aZone->GetZoneClearance() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, "(min_thickness %s)\n",
|
|
|
|
FMT_IU( aZone->GetMinThickness() ).c_str() );
|
|
|
|
|
2012-07-13 18:55:29 +00:00
|
|
|
if( aZone->GetIsKeepout() )
|
|
|
|
{
|
2012-07-14 16:27:25 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(keepout (tracks %s) (vias %s) (copperpour %s))\n",
|
2012-07-13 18:55:29 +00:00
|
|
|
aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
|
|
|
|
aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
|
2012-07-14 16:27:25 +00:00
|
|
|
aZone->GetDoNotAllowCopperPour() ? "not_allowed" : "allowed" );
|
2012-07-13 18:55:29 +00:00
|
|
|
}
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel+1, "(fill" );
|
|
|
|
|
|
|
|
// Default is not filled.
|
|
|
|
if( aZone->IsFilled() )
|
|
|
|
m_out->Print( 0, " yes" );
|
|
|
|
|
|
|
|
// Default is polygon filled.
|
|
|
|
if( aZone->GetFillMode() )
|
2012-06-19 04:23:42 +00:00
|
|
|
m_out->Print( 0, " (mode segment)" );
|
2012-06-16 22:49:24 +00:00
|
|
|
|
2012-06-19 04:08:00 +00:00
|
|
|
m_out->Print( 0, " (arc_segments %d) (thermal_gap %s) (thermal_bridge_width %s)",
|
2013-03-18 19:36:07 +00:00
|
|
|
aZone->GetArcSegmentCount(),
|
2012-06-09 17:00:13 +00:00
|
|
|
FMT_IU( aZone->GetThermalReliefGap() ).c_str(),
|
|
|
|
FMT_IU( aZone->GetThermalReliefCopperBridge() ).c_str() );
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
if( aZone->GetCornerSmoothingType() != ZONE_SETTINGS::SMOOTHING_NONE )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-19 04:23:42 +00:00
|
|
|
m_out->Print( 0, " (smoothing" );
|
2012-06-16 22:49:24 +00:00
|
|
|
|
|
|
|
switch( aZone->GetCornerSmoothingType() )
|
|
|
|
{
|
|
|
|
case ZONE_SETTINGS::SMOOTHING_CHAMFER:
|
|
|
|
m_out->Print( 0, " chamfer" );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_SETTINGS::SMOOTHING_FILLET:
|
|
|
|
m_out->Print( 0, " fillet" );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
|
|
|
|
aZone->GetCornerSmoothingType() ) );
|
|
|
|
}
|
2012-06-19 04:08:00 +00:00
|
|
|
m_out->Print( 0, ")" );
|
2012-06-16 22:49:24 +00:00
|
|
|
|
|
|
|
if( aZone->GetCornerRadius() != 0 )
|
2012-06-19 04:08:00 +00:00
|
|
|
m_out->Print( 0, " (radius %s)",
|
2012-06-16 22:49:24 +00:00
|
|
|
FMT_IU( aZone->GetCornerRadius() ).c_str() );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( 0, ")\n" );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-03 17:51:10 +00:00
|
|
|
const CPOLYGONS_LIST& cv = aZone->Outline()->m_CornersList;
|
2012-06-16 22:49:24 +00:00
|
|
|
int newLine = 0;
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-09 19:08:12 +00:00
|
|
|
if( cv.GetCornersCount() )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+1, "(polygon\n");
|
|
|
|
m_out->Print( aNestLevel+2, "(pts\n" );
|
|
|
|
|
2013-05-09 19:08:12 +00:00
|
|
|
for( unsigned it = 0; it < cv.GetCornersCount(); ++it )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
if( newLine == 0 )
|
|
|
|
m_out->Print( aNestLevel+3, "(xy %s %s)",
|
2013-05-08 18:20:58 +00:00
|
|
|
FMT_IU( cv.GetX( it ) ).c_str(), FMT_IU( cv.GetY( it ) ).c_str() );
|
2012-06-16 22:49:24 +00:00
|
|
|
else
|
|
|
|
m_out->Print( 0, " (xy %s %s)",
|
2013-05-08 18:20:58 +00:00
|
|
|
FMT_IU( cv.GetX( it ) ).c_str(), FMT_IU( cv.GetY( it ) ).c_str() );
|
2012-06-16 22:49:24 +00:00
|
|
|
|
|
|
|
if( newLine < 4 )
|
|
|
|
{
|
|
|
|
newLine += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newLine = 0;
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
}
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-08 18:20:58 +00:00
|
|
|
if( cv.IsEndContour( it ) )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
if( newLine != 0 )
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+2, ")\n" );
|
|
|
|
|
2013-05-09 19:08:12 +00:00
|
|
|
if( it+1 != cv.GetCornersCount() )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
newLine = 0;
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, ")\n" );
|
|
|
|
m_out->Print( aNestLevel+1, "(polygon\n" );
|
2012-06-16 22:49:24 +00:00
|
|
|
m_out->Print( aNestLevel+2, "(pts" );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the PolysList
|
2013-05-03 17:51:10 +00:00
|
|
|
const CPOLYGONS_LIST& fv = aZone->GetFilledPolysList();
|
2012-06-16 22:49:24 +00:00
|
|
|
newLine = 0;
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-09 19:08:12 +00:00
|
|
|
if( fv.GetCornersCount() )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+1, "(filled_polygon\n" );
|
|
|
|
m_out->Print( aNestLevel+2, "(pts\n" );
|
|
|
|
|
2013-05-09 19:08:12 +00:00
|
|
|
for( unsigned it = 0; it < fv.GetCornersCount(); ++it )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
if( newLine == 0 )
|
|
|
|
m_out->Print( aNestLevel+3, "(xy %s %s)",
|
2013-05-08 18:20:58 +00:00
|
|
|
FMT_IU( fv.GetX( it ) ).c_str(), FMT_IU( fv.GetY( it ) ).c_str() );
|
2012-06-16 22:49:24 +00:00
|
|
|
else
|
|
|
|
m_out->Print( 0, " (xy %s %s)",
|
2013-05-08 18:20:58 +00:00
|
|
|
FMT_IU( fv.GetX( it ) ).c_str(), FMT_IU( fv.GetY( it ) ).c_str() );
|
2012-06-16 22:49:24 +00:00
|
|
|
|
|
|
|
if( newLine < 4 )
|
|
|
|
{
|
|
|
|
newLine += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newLine = 0;
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
}
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-05-08 18:20:58 +00:00
|
|
|
if( fv.IsEndContour( it ) )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
if( newLine != 0 )
|
|
|
|
m_out->Print( 0, "\n" );
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+2, ")\n" );
|
|
|
|
|
2013-05-09 19:08:12 +00:00
|
|
|
if( it+1 != fv.GetCornersCount() )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
newLine = 0;
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out->Print( aNestLevel+1, ")\n" );
|
|
|
|
m_out->Print( aNestLevel+1, "(filled_polygon\n" );
|
|
|
|
m_out->Print( aNestLevel+2, "(pts\n" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the filling segments list
|
2013-03-20 14:50:12 +00:00
|
|
|
const std::vector< SEGMENT >& segs = aZone->FillSegments();
|
2012-06-09 17:00:13 +00:00
|
|
|
|
|
|
|
if( segs.size() )
|
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+1, "(fill_segments\n" );
|
|
|
|
|
|
|
|
for( std::vector< SEGMENT >::const_iterator it = segs.begin(); it != segs.end(); ++it )
|
|
|
|
{
|
|
|
|
m_out->Print( aNestLevel+2, "(pts (xy %s) (xy %s))\n",
|
|
|
|
FMT_IU( it->m_Start ).c_str(),
|
|
|
|
FMT_IU( it->m_End ).c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel+1, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_out->Print( aNestLevel, ")\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-22 20:41:26 +00:00
|
|
|
PCB_IO::PCB_IO( int aControlFlags ) :
|
|
|
|
m_cache( 0 ),
|
2012-10-24 18:20:33 +00:00
|
|
|
m_ctl( aControlFlags ),
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
m_parser( new PCB_PARSER() ),
|
|
|
|
m_mapping( new NETINFO_MAPPING() )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-10-22 20:41:26 +00:00
|
|
|
init( 0 );
|
2012-06-09 17:00:13 +00:00
|
|
|
m_out = &m_sf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-07 15:37:25 +00:00
|
|
|
PCB_IO::~PCB_IO()
|
|
|
|
{
|
|
|
|
delete m_cache;
|
2012-10-24 18:20:33 +00:00
|
|
|
delete m_parser;
|
Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).
Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).
Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.
- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).
- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.
- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok
Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
2014-01-28 09:19:51 +00:00
|
|
|
delete m_mapping;
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-25 19:17:06 +00:00
|
|
|
BOARD* PCB_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2012-11-27 14:14:15 +00:00
|
|
|
FILE_LINE_READER reader( aFileName );
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2013-06-24 05:30:22 +00:00
|
|
|
init( aProperties );
|
|
|
|
|
2012-10-24 18:20:33 +00:00
|
|
|
m_parser->SetLineReader( &reader );
|
|
|
|
m_parser->SetBoard( aAppendToMe );
|
|
|
|
|
|
|
|
BOARD* board = dynamic_cast<BOARD*>( m_parser->Parse() );
|
2012-08-29 16:59:50 +00:00
|
|
|
wxASSERT( board );
|
|
|
|
|
|
|
|
// Give the filename to the board if it's new
|
|
|
|
if( !aAppendToMe )
|
|
|
|
board->SetFileName( aFileName );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
return board;
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
|
2013-09-25 19:17:06 +00:00
|
|
|
void PCB_IO::init( const PROPERTIES* aProperties )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
m_board = NULL;
|
|
|
|
m_props = aProperties;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
void PCB_IO::cacheLib( const wxString& aLibraryPath, const wxString& aFootprintName )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2013-09-24 18:45:57 +00:00
|
|
|
if( !m_cache || m_cache->IsModified( aLibraryPath, aFootprintName ) )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
// a spectacular episode in memory management:
|
|
|
|
delete m_cache;
|
|
|
|
m_cache = new FP_CACHE( this, aLibraryPath );
|
|
|
|
m_cache->Load();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-13 23:01:42 +00:00
|
|
|
wxArrayString PCB_IO::FootprintEnumerate( const wxString& aLibraryPath,
|
|
|
|
const PROPERTIES* aProperties )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2013-12-13 23:01:42 +00:00
|
|
|
LOCALE_IO toggle; // toggles on, then off, the C locale.
|
|
|
|
wxArrayString ret;
|
|
|
|
wxDir dir( aLibraryPath );
|
|
|
|
|
|
|
|
if( !dir.IsOpened() )
|
|
|
|
{
|
|
|
|
THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ),
|
|
|
|
GetChars( aLibraryPath ) ) );
|
|
|
|
}
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
init( aProperties );
|
|
|
|
|
2013-12-13 23:14:30 +00:00
|
|
|
#if 1 // Set to 0 to only read directory contents, not load cache.
|
2012-10-07 15:37:25 +00:00
|
|
|
cacheLib( aLibraryPath );
|
|
|
|
|
|
|
|
const MODULE_MAP& mods = m_cache->GetModules();
|
|
|
|
|
|
|
|
|
|
|
|
for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
|
|
|
|
{
|
|
|
|
ret.Add( FROM_UTF8( it->first.c_str() ) );
|
|
|
|
}
|
2013-12-13 23:01:42 +00:00
|
|
|
#else
|
|
|
|
wxString fpFileName;
|
|
|
|
wxString wildcard = wxT( "*." ) + KiCadFootprintFileExtension;
|
|
|
|
|
|
|
|
if( dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
wxFileName fn( aLibraryPath, fpFileName );
|
|
|
|
ret.Add( fn.GetName() );
|
|
|
|
} while( dir.GetNext( &fpFileName ) );
|
|
|
|
}
|
|
|
|
#endif
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MODULE* PCB_IO::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
|
2013-09-25 19:17:06 +00:00
|
|
|
const PROPERTIES* aProperties )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2012-11-14 07:15:59 +00:00
|
|
|
LOCALE_IO toggle; // toggles on, then off, the C locale.
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
init( aProperties );
|
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
cacheLib( aLibraryPath, aFootprintName );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
const MODULE_MAP& mods = m_cache->GetModules();
|
|
|
|
|
|
|
|
MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
|
|
|
|
|
|
|
|
if( it == mods.end() )
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy constructor to clone the already loaded MODULE
|
|
|
|
return new MODULE( *it->second->GetModule() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_IO::FootprintSave( const wxString& aLibraryPath, const MODULE* aFootprint,
|
2013-09-25 19:17:06 +00:00
|
|
|
const PROPERTIES* aProperties )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2012-11-14 07:15:59 +00:00
|
|
|
LOCALE_IO toggle; // toggles on, then off, the C locale.
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
init( aProperties );
|
|
|
|
|
2012-11-14 07:15:59 +00:00
|
|
|
// In this public PLUGIN API function, we can safely assume it was
|
|
|
|
// called for saving into a library path.
|
|
|
|
m_ctl = CTL_FOR_LIBRARY;
|
|
|
|
|
2012-10-07 15:37:25 +00:00
|
|
|
cacheLib( aLibraryPath );
|
|
|
|
|
|
|
|
if( !m_cache->IsWritable() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
wxString msg = wxString::Format(
|
|
|
|
_( "Library '%s' is read only" ),
|
|
|
|
GetChars( aLibraryPath )
|
|
|
|
);
|
|
|
|
|
|
|
|
THROW_IO_ERROR( msg );
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
2013-09-08 18:31:21 +00:00
|
|
|
std::string footprintName = aFootprint->GetFPID().GetFootprintName();
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
MODULE_MAP& mods = m_cache->GetModules();
|
|
|
|
|
|
|
|
// Quietly overwrite module and delete module file from path for any by same name.
|
2014-01-02 02:17:07 +00:00
|
|
|
wxFileName fn( aLibraryPath, aFootprint->GetFPID().GetFootprintName(), KiCadFootprintFileExtension );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
if( !fn.IsOk() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "Footprint file name '%s' is not valid." ),
|
2012-10-07 15:37:25 +00:00
|
|
|
GetChars( fn.GetFullPath() ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fn.FileExists() && !fn.IsFileWritable() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "user does not have write permission to delete file '%s' " ),
|
2012-10-07 15:37:25 +00:00
|
|
|
GetChars( fn.GetFullPath() ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_CITER it = mods.find( footprintName );
|
|
|
|
|
|
|
|
if( it != mods.end() )
|
|
|
|
{
|
2012-10-07 22:26:17 +00:00
|
|
|
wxLogTrace( traceFootprintLibrary, wxT( "Removing footprint library file '%s'." ),
|
|
|
|
fn.GetFullPath().GetData() );
|
2012-10-07 15:37:25 +00:00
|
|
|
mods.erase( footprintName );
|
|
|
|
wxRemoveFile( fn.GetFullPath() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// I need my own copy for the cache
|
|
|
|
MODULE* module = new MODULE( *aFootprint );
|
|
|
|
|
|
|
|
// and it's time stamp must be 0, it should have no parent, orientation should
|
|
|
|
// be zero, and it should be on the front layer.
|
|
|
|
module->SetTimeStamp( 0 );
|
|
|
|
module->SetParent( 0 );
|
|
|
|
module->SetOrientation( 0 );
|
|
|
|
|
|
|
|
if( module->GetLayer() != LAYER_N_FRONT )
|
|
|
|
module->Flip( module->GetPosition() );
|
|
|
|
|
2012-10-07 22:26:17 +00:00
|
|
|
wxLogTrace( traceFootprintLibrary, wxT( "Creating s-expression footprint file: %s." ),
|
|
|
|
fn.GetFullPath().GetData() );
|
2012-10-07 15:37:25 +00:00
|
|
|
mods.insert( footprintName, new FP_CACHE_ITEM( module, fn ) );
|
|
|
|
m_cache->Save();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-25 19:17:06 +00:00
|
|
|
void PCB_IO::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
LOCALE_IO toggle; // toggles on, then off, the C locale.
|
|
|
|
|
2013-11-27 06:04:04 +00:00
|
|
|
init( aProperties );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
cacheLib( aLibraryPath );
|
|
|
|
|
|
|
|
if( !m_cache->IsWritable() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
aLibraryPath.GetData() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_cache->Remove( aFootprintName );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-25 19:17:06 +00:00
|
|
|
void PCB_IO::FootprintLibCreate( const wxString& aLibraryPath, const PROPERTIES* aProperties )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
if( wxDir::Exists( aLibraryPath ) )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "cannot overwrite library path '%s'" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
aLibraryPath.GetData() ) );
|
|
|
|
}
|
|
|
|
|
2012-11-14 07:15:59 +00:00
|
|
|
LOCALE_IO toggle;
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
init( aProperties );
|
|
|
|
|
|
|
|
delete m_cache;
|
|
|
|
m_cache = new FP_CACHE( this, aLibraryPath );
|
|
|
|
m_cache->Save();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-25 19:17:06 +00:00
|
|
|
bool PCB_IO::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
wxFileName fn;
|
|
|
|
fn.SetPath( aLibraryPath );
|
|
|
|
|
|
|
|
// Return if there is no library path to delete.
|
|
|
|
if( !fn.DirExists() )
|
2012-11-19 16:19:38 +00:00
|
|
|
return false;
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
if( !fn.IsDirWritable() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory '%s'" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
aLibraryPath.GetData() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
wxDir dir( aLibraryPath );
|
|
|
|
|
|
|
|
if( dir.HasSubDirs() )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "library directory '%s' has unexpected sub-directories" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
aLibraryPath.GetData() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// All the footprint files must be deleted before the directory can be deleted.
|
|
|
|
if( dir.HasFiles() )
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
wxFileName tmp;
|
|
|
|
wxArrayString files;
|
|
|
|
|
|
|
|
wxDir::GetAllFiles( aLibraryPath, &files );
|
|
|
|
|
|
|
|
for( i = 0; i < files.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
tmp = files[i];
|
|
|
|
|
2012-11-19 16:19:38 +00:00
|
|
|
if( tmp.GetExt() != KiCadFootprintFileExtension )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "unexpected file '%s' was found in library path '%s'" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
files[i].GetData(), aLibraryPath.GetData() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = 0; i < files.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
wxRemoveFile( files[i] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-07 22:26:17 +00:00
|
|
|
wxLogTrace( traceFootprintLibrary, wxT( "Removing footprint library '%s'" ),
|
|
|
|
aLibraryPath.GetData() );
|
2012-10-07 15:37:25 +00:00
|
|
|
|
|
|
|
// Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
|
|
|
|
// we don't want that. we want bare metal portability with no UI here.
|
|
|
|
if( !wxRmdir( aLibraryPath ) )
|
|
|
|
{
|
2013-10-31 21:30:57 +00:00
|
|
|
THROW_IO_ERROR( wxString::Format( _( "footprint library '%s' cannot be deleted" ),
|
2012-10-07 15:37:25 +00:00
|
|
|
aLibraryPath.GetData() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// For some reason removing a directory in Windows is not immediately updated. This delay
|
|
|
|
// prevents an error when attempting to immediately recreate the same directory when over
|
|
|
|
// writing an existing library.
|
|
|
|
#ifdef __WINDOWS__
|
|
|
|
wxMilliSleep( 250L );
|
|
|
|
#endif
|
|
|
|
|
2013-09-24 18:45:57 +00:00
|
|
|
if( m_cache && !m_cache->IsPath( aLibraryPath ) )
|
2012-10-07 15:37:25 +00:00
|
|
|
{
|
|
|
|
delete m_cache;
|
|
|
|
m_cache = NULL;
|
|
|
|
}
|
2012-11-19 16:19:38 +00:00
|
|
|
|
|
|
|
return true;
|
2012-10-07 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PCB_IO::IsFootprintLibWritable( const wxString& aLibraryPath )
|
|
|
|
{
|
|
|
|
LOCALE_IO toggle;
|
|
|
|
|
|
|
|
init( NULL );
|
|
|
|
|
|
|
|
cacheLib( aLibraryPath );
|
|
|
|
|
|
|
|
return m_cache->IsWritable();
|
|
|
|
}
|