Browse Source

LTspice import fixes:

- Support UTF-16 LE file encoding
- Support Invisible text justification
- Make added SCH_FIELD invisible by default, like in 8.0
- Adjust label orientations based on wire placements
- Fix arc windings in symbols
revert-0c36e162
Alex Shvartzkop 6 months ago
parent
commit
496fc3b6bf
  1. 51
      common/richio.cpp
  2. 4
      eeschema/sch_io/ltspice/ltspice_schematic.cpp
  3. 3
      eeschema/sch_io/ltspice/ltspice_schematic.h
  4. 73
      eeschema/sch_io/ltspice/sch_io_ltspice_parser.cpp
  5. 2
      eeschema/sch_io/ltspice/sch_io_ltspice_parser.h

51
common/richio.cpp

@ -34,6 +34,7 @@
#include <io/kicad/kicad_io_utils.h>
#include <wx/translation.h>
#include <wx/ffile.h>
// Fall back to getc() when getc_unlocked() is not available on the target platform.
@ -94,40 +95,36 @@ std::string StrPrintf( const char* format, ... )
wxString SafeReadFile( const wxString& aFilePath, const wxString& aReadType )
{
auto From_UTF8_WINE =
[]( const char* cstring )
{
wxString line = wxString::FromUTF8( cstring );
if( line.IsEmpty() ) // happens when cstring is not a valid UTF8 sequence
line = wxConvCurrent->cMB2WC( cstring ); // try to use locale conversion
// We have trouble here *probably* because Wine-hosted LTSpice writes out MSW
// encoded text on a macOS, where it isn't expected. In any case, wxWidgets'
// wxSafeConvert() appears to get around it.
if( line.IsEmpty() )
line = wxSafeConvertMB2WX( cstring );
wxString contents;
wxFFile ff( aFilePath );
// I'm not sure what the source of this style of line-endings is, but it can be
// found in some Fairchild Semiconductor SPICE files.
line.Replace( wxS( "\r\r\n" ), wxS( "\n" ) );
if( !ff.IsOpened() )
THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'." ), aFilePath ) );
return line;
};
// Try to determine encoding
char bytes[2]{ 0 };
ff.Read( bytes, 2 );
bool utf16le = bytes[1] == 0;
// Open file
FILE* fp = wxFopen( aFilePath, aReadType );
ff.Seek( 0 );
if( !fp )
THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'." ), aFilePath ) );
if( utf16le )
ff.ReadAll( &contents, wxMBConvUTF16LE() );
else
ff.ReadAll( &contents, wxMBConvUTF8() );
FILE_LINE_READER fileReader( fp, aFilePath );
if( contents.empty() )
{
ff.Seek( 0 );
ff.ReadAll( &contents, wxConvAuto( wxFONTENCODING_CP1252 ) );
}
wxString contents;
if( contents.empty() )
THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ), aFilePath ) );
while( fileReader.ReadLine() )
contents += From_UTF8_WINE( fileReader.Line() );
// I'm not sure what the source of this style of line-endings is, but it can be
// found in some Fairchild Semiconductor SPICE files.
contents.Replace( wxS( "\r\r\n" ), wxS( "\n" ) );
return contents;
}

4
eeschema/sch_io/ltspice/ltspice_schematic.cpp

@ -488,9 +488,11 @@ LTSPICE_SCHEMATIC::JUSTIFICATION LTSPICE_SCHEMATIC::getTextJustification( const
justificationMap["TOP"] = JUSTIFICATION::TOP;
justificationMap["VBOTTOM"] = JUSTIFICATION::VBOTTOM;
justificationMap["VTOP"] = JUSTIFICATION::VTOP;
justificationMap["INVISIBLE"] = JUSTIFICATION::INVISIBLE;
if( justificationMap.find( aValue.Upper() ) == justificationMap.end() )
THROW_IO_ERROR( _( "Expecting LEFT, CENTER, RIGHT, TOP, BOTTOM, VLEFT, VRIGHT, VCENTER, VTOP or VBOTTOM" ) );
THROW_IO_ERROR( _( "Expecting LEFT, CENTER, RIGHT, TOP, BOTTOM, VLEFT, VRIGHT, VCENTER, "
"VTOP, VBOTTOM or INVISIBLE" ) );
return justificationMap[ aValue.Upper() ];
}

3
eeschema/sch_io/ltspice/ltspice_schematic.h

@ -110,7 +110,8 @@ public:
VLEFT, // Vertical Left Justification
VRIGHT, // Vertical Right Justification
VCENTER, // Vertical Center Justification
VTOP // Vertical Top Justification.
VTOP, // Vertical Top Justification.
INVISIBLE
};
/**

73
eeschema/sch_io/ltspice/sch_io_ltspice_parser.cpp

@ -505,7 +505,7 @@ void SCH_IO_LTSPICE_PARSER::CreateKicadSCH_ITEMs( SCH_SHEET_PATH* aSheet,
else
{
screen->Append( CreateSCH_LABEL( SCH_GLOBAL_LABEL_T, lt_flag.Offset, lt_flag.Value,
lt_flag.FontSize ) );
lt_flag.FontSize, lt_asc.Wires ) );
}
}
@ -527,7 +527,7 @@ void SCH_IO_LTSPICE_PARSER::CreateKicadSCH_ITEMs( SCH_SHEET_PATH* aSheet,
for( const LTSPICE_SCHEMATIC::DATAFLAG& lt_flag : lt_asc.DataFlags )
{
screen->Append( CreateSCH_LABEL( SCH_DIRECTIVE_LABEL_T, lt_flag.Offset,
lt_flag.Expression, lt_flag.FontSize ) );
lt_flag.Expression, lt_flag.FontSize, lt_asc.Wires ) );
}
}
}
@ -694,6 +694,10 @@ void SCH_IO_LTSPICE_PARSER::setTextJustification( EDA_TEXT*
{
switch( aJustification )
{
case LTSPICE_SCHEMATIC::JUSTIFICATION::INVISIBLE:
aText->SetVisible( false );
break;
case LTSPICE_SCHEMATIC::JUSTIFICATION::LEFT:
case LTSPICE_SCHEMATIC::JUSTIFICATION::VLEFT:
aText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
@ -890,8 +894,10 @@ SCH_SYMBOL* SCH_IO_LTSPICE_PARSER::CreatePowerSymbol( const VECTOR2I& aOffset,
}
SCH_LABEL_BASE* SCH_IO_LTSPICE_PARSER::CreateSCH_LABEL( KICAD_T aType, const VECTOR2I& aOffset,
const wxString& aValue, int aFontSize )
SCH_LABEL_BASE*
SCH_IO_LTSPICE_PARSER::CreateSCH_LABEL( KICAD_T aType, const VECTOR2I& aOffset,
const wxString& aValue, int aFontSize,
std::vector<LTSPICE_SCHEMATIC::WIRE>& aWires )
{
SCH_LABEL_BASE* label = nullptr;
@ -928,6 +934,44 @@ SCH_LABEL_BASE* SCH_IO_LTSPICE_PARSER::CreateSCH_LABEL( KICAD_T aType, const VEC
label->SetVisible( true );
}
for( LTSPICE_SCHEMATIC::WIRE& wire : aWires )
{
if( aOffset == wire.Start )
{
if( wire.Start.x == wire.End.x )
{
if( wire.Start.y < wire.End.y )
label->SetSpinStyle( SPIN_STYLE::UP );
else if( wire.Start.y > wire.End.y )
label->SetSpinStyle( SPIN_STYLE::BOTTOM );
}
else
{
if( wire.Start.x < wire.End.x )
label->SetSpinStyle( SPIN_STYLE::LEFT );
else if( wire.Start.x > wire.End.x )
label->SetSpinStyle( SPIN_STYLE::RIGHT );
}
}
else if( aOffset == wire.End )
{
if( wire.Start.x == wire.End.x )
{
if( wire.Start.y > wire.End.y )
label->SetSpinStyle( SPIN_STYLE::UP );
else if( wire.Start.y < wire.End.y )
label->SetSpinStyle( SPIN_STYLE::BOTTOM );
}
else
{
if( wire.Start.x > wire.End.x )
label->SetSpinStyle( SPIN_STYLE::LEFT );
else if( wire.Start.x < wire.End.x )
label->SetSpinStyle( SPIN_STYLE::RIGHT );
}
}
}
return label;
}
@ -955,6 +999,7 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
if( !value2.IsEmpty() )
{
SCH_FIELD paramsField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Value2" ) );
paramsField.SetVisible( false );
paramsField.SetText( value2 );
aSymbol->AddField( paramsField );
}
@ -962,10 +1007,12 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
auto setupNonInferredPassive = [&]( const wxString& aDevice, const wxString& aValueKey )
{
SCH_FIELD deviceField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Device" ) );
deviceField.SetVisible( false );
deviceField.SetText( aDevice );
aSymbol->AddField( deviceField );
SCH_FIELD paramsField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Params" ) );
paramsField.SetVisible( false );
paramsField.SetText( aValueKey + wxS( "=${VALUE}" ) );
aSymbol->AddField( paramsField );
};
@ -975,14 +1022,17 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
aSymbol->SetValueFieldText( wxS( "${Sim.Params}" ) );
SCH_FIELD deviceField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Device" ) );
deviceField.SetVisible( false );
deviceField.SetText( aDevice );
aSymbol->AddField( deviceField );
SCH_FIELD typeField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Type" ) );
typeField.SetVisible( false );
typeField.SetText( aType );
aSymbol->AddField( typeField );
SCH_FIELD paramsField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Params" ) );
paramsField.SetVisible( false );
paramsField.SetText( value );
aSymbol->AddField( paramsField );
};
@ -1020,6 +1070,7 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
else if( prefix == wxS( "V" ) || symbolName == wxS( "I" ) )
{
SCH_FIELD deviceField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Device" ) );
deviceField.SetVisible( false );
deviceField.SetText( wxS( "SPICE" ) );
aSymbol->AddField( deviceField );
@ -1032,6 +1083,7 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
simParams << "model=" << '"' << "${VALUE} ${VALUE2}" << '"' << ' ';
SCH_FIELD paramsField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Params" ) );
paramsField.SetVisible( false );
paramsField.SetText( simParams );
aSymbol->AddField( paramsField );
}
@ -1065,6 +1117,7 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
if( !libFile.IsEmpty() )
{
SCH_FIELD libField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Library" ) );
libField.SetVisible( false );
libField.SetText( libFile );
aSymbol->AddField( libField );
}
@ -1072,12 +1125,14 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
if( type == wxS( "X" ) )
{
SCH_FIELD deviceField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Device" ) );
deviceField.SetVisible( false );
deviceField.SetText( wxS( "SUBCKT" ) );
aSymbol->AddField( deviceField );
}
else
{
SCH_FIELD deviceField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Device" ) );
deviceField.SetVisible( false );
deviceField.SetText( wxS( "SPICE" ) );
aSymbol->AddField( deviceField );
}
@ -1088,12 +1143,14 @@ void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbo
{
// TODO: append value
SCH_FIELD paramsField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Params" ) );
paramsField.SetVisible( false );
paramsField.SetText( spiceLine );
aSymbol->AddField( paramsField );
}
else
{
SCH_FIELD modelField( { 0, 0 }, FIELD_T::USER, aSymbol, wxS( "Sim.Params" ) );
modelField.SetVisible( false );
modelField.SetText( "model=\"" + value + "\"" );
aSymbol->AddField( modelField );
}
@ -1325,8 +1382,8 @@ void SCH_IO_LTSPICE_PARSER::CreateArc( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol,
LTSPICE_SCHEMATIC::ARC& lt_arc = aLTSymbol.Arcs[aIndex];
aArc->SetCenter( ToKicadCoords( ( lt_arc.TopLeft + lt_arc.BotRight ) / 2 ) );
aArc->SetEnd( ToKicadCoords( lt_arc.ArcEnd ) );
aArc->SetStart( ToKicadCoords( lt_arc.ArcStart ) );
aArc->SetStart( ToKicadCoords( lt_arc.ArcEnd ) );
aArc->SetEnd( ToKicadCoords( lt_arc.ArcStart ) );
aArc->SetStroke( getStroke( lt_arc.LineWidth, lt_arc.LineStyle ) );
}
@ -1338,8 +1395,8 @@ void SCH_IO_LTSPICE_PARSER::CreateArc( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol,
SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
arc->SetCenter( ToKicadCoords( ( lt_arc.TopLeft + lt_arc.BotRight ) / 2 ) );
arc->SetEnd( ToKicadCoords( lt_arc.ArcEnd ) );
arc->SetStart( ToKicadCoords( lt_arc.ArcStart ) );
arc->SetStart( ToKicadCoords( lt_arc.ArcEnd ) );
arc->SetEnd( ToKicadCoords( lt_arc.ArcStart ) );
arc->SetStroke( getStroke( lt_arc.LineWidth, lt_arc.LineStyle ) );
arc->Move( ToKicadCoords( aLTSymbol.Offset ) + m_originOffset );

2
eeschema/sch_io/ltspice/sch_io_ltspice_parser.h

@ -234,7 +234,7 @@ public:
* @param aType Currently supported types: SCH_LABEL_T, SCH_DIRECTIVE_LABEL_T
*/
SCH_LABEL_BASE* CreateSCH_LABEL( KICAD_T aType, const VECTOR2I& aOffset, const wxString& aValue,
int aFontSize );
int aFontSize, std::vector<LTSPICE_SCHEMATIC::WIRE>& aWires );
void CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, SCH_SYMBOL* aSymbol,
SCH_SHEET_PATH* aSheet );

Loading…
Cancel
Save