7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2024-11-25 09:25:01 +00:00
kicad/pcbnew/pcb_io/odbpp/odb_util.cpp
2024-09-18 18:51:02 -04:00

356 lines
8.7 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string>
#include <algorithm>
#include <locale>
#include "odb_util.h"
#include <wx/chartype.h>
#include <wx/dir.h>
#include "idf_helpers.h"
#include "odb_defines.h"
#include "pcb_io_odbpp.h"
namespace ODB
{
wxString GenODBString( const wxString& aStr )
{
wxString str;
for( size_t ii = 0; ii < aStr.Len(); ++ii )
{
// Rule: we can only use the standard ASCII, control excluded
wxUniChar ch = aStr[ii];
if( ch > 126 || !std::isgraph( static_cast<unsigned char>( ch ) ) )
ch = '?';
str += ch;
}
// Rule: only uppercase
str.MakeUpper();
return str;
}
wxString GenLegalNetName( const wxString& aStr )
{
std::string str = aStr.ToStdString();
wxString out;
out.reserve( str.size() );
for( auto c : str )
{
if( ( c >= 33 && c <= 126 ) && c != ';' )
{
out.append( 1, c );
}
else
{
out.append( 1, '_' ); // Replace invalid characters with underscore
}
}
return out;
}
// The names of these ODB++ entities must comply with
// the rules for legal entity names:
// product, model, step, layer, symbol, and attribute.
wxString GenLegalEntityName( const wxString& aStr )
{
std::string str = aStr.ToStdString();
wxString out;
out.reserve( str.size() );
for( auto c : str )
{
if( isalpha( c ) )
c = tolower( c );
else if( isdigit( c ) || c == '-' || c == '_' || c == '+' || c == '.' )
;
else
c = '_';
out.append( 1, c );
}
if( out.length() > 64 )
{
out.Truncate( 64 );
}
while( !out.IsEmpty() && ( out[0] == '.' || out[0] == '-' || out[0] == '+' ) )
{
out.erase( 0, 1 );
}
while( !out.IsEmpty() && out.Last() == '.' )
{
out.RemoveLast();
}
return out;
}
wxString Double2String( double aVal )
{
// We don't want to output -0.0 as this value is just 0 for fabs
if( aVal == -0.0 )
aVal = 0.0;
wxString str = wxString::FromCDouble( aVal, PCB_IO_ODBPP::m_sigfig );
// Remove all but the last trailing zeros from str
while( str.EndsWith( wxT( "00" ) ) )
str.RemoveLast();
return str;
}
std::string Double2String( double aVal, int32_t aDigits )
{
// We don't want to output -0.0 as this value is just 0 for fabs
if( aVal == -0.0 )
aVal = 0.0;
wxString str = wxString::FromCDouble( aVal, aDigits );
return str.ToStdString();
}
wxString SymDouble2String( double aVal )
{
return Double2String( PCB_IO_ODBPP::m_symbolScale * aVal );
}
wxString Data2String( double aVal )
{
return Double2String( PCB_IO_ODBPP::m_scale * aVal );
}
std::pair<wxString, wxString> AddXY( const VECTOR2I& aVec )
{
// TODO: to deal with user preference x y increment setting
std::pair<wxString, wxString> xy =
std::pair<wxString, wxString>( Double2String( PCB_IO_ODBPP::m_scale * aVec.x ),
Double2String( -PCB_IO_ODBPP::m_scale * aVec.y ) );
return xy;
}
VECTOR2I GetShapePosition( const PCB_SHAPE& aShape )
{
VECTOR2D pos{};
switch( aShape.GetShape() )
{
// Rectangles in KiCad are mapped by their corner while ODBPP uses the center
case SHAPE_T::RECTANGLE:
pos = aShape.GetPosition()
+ VECTOR2I( aShape.GetRectangleWidth() / 2.0, aShape.GetRectangleHeight() / 2.0 );
break;
// Both KiCad and ODBPP use the center of the circle
case SHAPE_T::CIRCLE:
// KiCad uses the exact points on the board
case SHAPE_T::POLY:
case SHAPE_T::BEZIER:
case SHAPE_T::SEGMENT:
case SHAPE_T::ARC:
case SHAPE_T::UNDEFINED:
pos = aShape.GetPosition();
break;
}
return pos;
}
} // namespace ODB
void ODB_TREE_WRITER::CreateEntityDirectory( const wxString& aPareDir,
const wxString& aSubDir /*= wxEmptyString*/ )
{
wxFileName path = wxFileName::DirName( aPareDir );
wxArrayString subDirs = wxFileName::DirName( aSubDir.Lower() ).GetDirs();
for( size_t i = 0; i < subDirs.GetCount(); i++ )
path.AppendDir( subDirs[i] );
if( !path.DirExists() )
{
if( !path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
{
throw( std::runtime_error( "Could not create directory" + path.GetPath() ) );
}
}
m_currentPath = path.GetPath();
}
ODB_FILE_WRITER::ODB_FILE_WRITER( ODB_TREE_WRITER& aTreeWriter, const wxString& aFileName ) :
m_treeWriter( aTreeWriter )
{
CreateFile( aFileName );
}
void ODB_FILE_WRITER::CreateFile( const wxString& aFileName )
{
if( aFileName.IsEmpty() || m_treeWriter.GetCurrentPath().IsEmpty() )
return;
wxFileName fn;
fn.SetPath( m_treeWriter.GetCurrentPath() );
fn.SetFullName( aFileName );
wxString dirPath = fn.GetPath();
if( !wxDir::Exists( dirPath ) )
{
if( !wxDir::Make( dirPath ) )
throw( std::runtime_error( "Could not create directory" + dirPath ) );
}
if( !fn.IsDirWritable() || ( fn.Exists() && !fn.IsFileWritable() ) )
return;
if( m_ostream.is_open() )
m_ostream.close();
m_ostream.open( TO_UTF8( fn.GetFullPath() ),
std::ios_base::out | std::ios_base::trunc | std::ios_base::binary );
m_ostream.imbue( std::locale::classic() );
if( !m_ostream.is_open() || !m_ostream.good() )
throw std::runtime_error( "Failed to open file: " + fn.GetFullPath() );
}
bool ODB_FILE_WRITER::CloseFile()
{
if( m_ostream.is_open() )
{
m_ostream.close();
if( !m_ostream.good() )
{
throw std::runtime_error( "close file failed" );
return false;
}
}
return true;
}
void ODB_TEXT_WRITER::WriteEquationLine( const std::string& var, int value )
{
WriteIndent();
m_ostream << var << "=" << value << std::endl;
}
void ODB_TEXT_WRITER::WriteEquationLine( const wxString& var, const wxString& value )
{
WriteIndent();
m_ostream << var << "=" << value << std::endl;
}
void ODB_TEXT_WRITER::WriteIndent()
{
if( in_array )
m_ostream << " ";
}
void ODB_TEXT_WRITER::BeginArray( const std::string& a )
{
if( in_array )
throw std::runtime_error( "already in array" );
in_array = true;
m_ostream << a << " {" << std::endl;
}
void ODB_TEXT_WRITER::EndArray()
{
if( !in_array )
throw std::runtime_error( "not in array" );
in_array = false;
m_ostream << "}" << std::endl << std::endl;
}
ODB_TEXT_WRITER::ARRAY_PROXY::ARRAY_PROXY( ODB_TEXT_WRITER& aWriter, const std::string& aStr ) :
m_writer( aWriter )
{
m_writer.BeginArray( aStr );
}
ODB_TEXT_WRITER::ARRAY_PROXY::~ARRAY_PROXY()
{
m_writer.EndArray();
}
ODB_DRILL_TOOLS::ODB_DRILL_TOOLS( const wxString& aUnits, const wxString& aThickness,
const wxString& aUserParams ) :
m_units( aUnits ), m_thickness( aThickness ), m_userParams( aUserParams )
{
}
void ODB_DRILL_TOOLS::GenerateFile( std::ostream& aStream )
{
ODB_TEXT_WRITER twriter( aStream );
twriter.WriteEquationLine( "UNITS", m_units );
twriter.WriteEquationLine( "THICKNESS", m_thickness );
twriter.WriteEquationLine( "USER_PARAMS", m_userParams );
for( const auto& tool : m_tools )
{
const auto array_proxy = twriter.MakeArrayProxy( "TOOLS" );
twriter.WriteEquationLine( "NUM", tool.m_num );
twriter.WriteEquationLine( "TYPE", tool.m_type );
twriter.WriteEquationLine( "TYPE2", tool.m_type2 );
twriter.WriteEquationLine( "MIN_TOL", tool.m_minTol );
twriter.WriteEquationLine( "MAX_TOL", tool.m_maxTol );
twriter.WriteEquationLine( "BIT", tool.m_bit );
twriter.WriteEquationLine( "FINISH_SIZE", tool.m_finishSize );
twriter.WriteEquationLine( "DRILL_SIZE", tool.m_drillSize );
}
}