1400 lines
49 KiB
C++
1400 lines
49 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2022 Chetan Subhash Shinde<chetanshinde2001@gmail.com>
|
|
* Copyright (C) 2023 CERN
|
|
* Copyright (C) 2022-2024 KiCad Developers, see AUTHORS.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 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/>.
|
|
*/
|
|
|
|
/**
|
|
* @brief Parses the datastructure produced by the LTSPICE_SCHEMATIC into a KiCad
|
|
* schematic file.
|
|
*/
|
|
|
|
#include <sch_io/ltspice/sch_io_ltspice_parser.h>
|
|
#include <sch_io/ltspice/ltspice_schematic.h>
|
|
#include <sch_io/sch_io_mgr.h>
|
|
#include <schematic.h>
|
|
#include <sch_sheet.h>
|
|
#include <sch_sheet_pin.h>
|
|
#include <sch_line.h>
|
|
#include <lib_shape.h>
|
|
#include <sch_label.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <sch_shape.h>
|
|
#include <sch_bus_entry.h>
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::Parse( SCH_SHEET_PATH* aSheet,
|
|
std::vector<LTSPICE_SCHEMATIC::LT_ASC>& outLT_ASCs,
|
|
const std::vector<wxString>& aAsyFileNames )
|
|
{
|
|
// Center created objects in Kicad page
|
|
BOX2I bbox;
|
|
|
|
for( const LTSPICE_SCHEMATIC::LT_ASC& asc : outLT_ASCs )
|
|
bbox.Merge( asc.BoundingBox );
|
|
|
|
m_originOffset = { 0, 0 };
|
|
bbox.SetOrigin( ToKicadCoords( bbox.GetOrigin() ) );
|
|
bbox.SetSize( ToKicadCoords( bbox.GetSize() ) );
|
|
|
|
VECTOR2I pageSize = aSheet->LastScreen()->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
|
|
int grid = schIUScale.MilsToIU( 50 );
|
|
int margin = grid * 10;
|
|
|
|
m_originOffset = ( pageSize / 2 ) - bbox.GetCenter();
|
|
|
|
if( bbox.GetWidth() > pageSize.x - margin )
|
|
m_originOffset.x = margin - bbox.GetLeft();
|
|
|
|
if( bbox.GetHeight() > pageSize.y - margin )
|
|
m_originOffset.y = margin - bbox.GetTop();
|
|
|
|
m_originOffset = ( m_originOffset / grid ) * grid;
|
|
|
|
readIncludes( outLT_ASCs );
|
|
CreateKicadSYMBOLs( aSheet, outLT_ASCs, aAsyFileNames );
|
|
CreateKicadSCH_ITEMs( aSheet, outLT_ASCs );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::readIncludes( std::vector<LTSPICE_SCHEMATIC::LT_ASC>& outLT_ASCs )
|
|
{
|
|
wxFileName ltSubDir( m_lt_schematic->GetLTspiceDataDir().GetFullPath(), wxEmptyString );
|
|
ltSubDir.AppendDir( wxS( "sub" ) );
|
|
|
|
for( const LTSPICE_SCHEMATIC::LT_ASC& asc : outLT_ASCs )
|
|
{
|
|
for( const LTSPICE_SCHEMATIC::TEXT& lt_text : asc.Texts )
|
|
{
|
|
for( wxString& line : wxSplit( lt_text.Value, '\n' ) )
|
|
{
|
|
if( line.StartsWith( wxS( ".include " ) ) || line.StartsWith( wxS( ".inc " ) )
|
|
|| line.StartsWith( wxS( ".lib " ) ) )
|
|
{
|
|
wxString path = line.AfterFirst( ' ' );
|
|
|
|
path.Replace( '\\', '/' );
|
|
wxFileName fileName( path );
|
|
|
|
if( fileName.IsAbsolute() )
|
|
{
|
|
m_includes[fileName.GetName()] = fileName.GetFullPath();
|
|
}
|
|
else
|
|
{
|
|
fileName.MakeAbsolute( ltSubDir.GetFullPath() );
|
|
m_includes[fileName.GetName()] = fileName.GetFullPath();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateLines( LIB_SYMBOL* aSymbol,
|
|
LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol,
|
|
int aIndex, LIB_SHAPE* shape )
|
|
{
|
|
LTSPICE_SCHEMATIC::LINE& lt_line = aLTSymbol.Lines[aIndex];
|
|
|
|
shape->AddPoint( ToInvertedKicadCoords( lt_line.End ) );
|
|
shape->AddPoint( ToInvertedKicadCoords( lt_line.Start ) );
|
|
shape->SetStroke( getStroke( lt_line.LineWidth, lt_line.LineStyle ) );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateLines( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::LINE& lt_line = aLTSymbol.Lines[aIndex];
|
|
SCH_SHAPE* shape = new SCH_SHAPE( SHAPE_T::POLY );
|
|
|
|
shape->AddPoint( ToKicadCoords( lt_line.End ) );
|
|
shape->AddPoint( ToKicadCoords( lt_line.Start ) );
|
|
shape->SetStroke( getStroke( lt_line.LineWidth, lt_line.LineStyle ) );
|
|
|
|
shape->Move( ToKicadCoords( aLTSymbol.Offset ) + m_originOffset );
|
|
RotateMirrorShape( aLTSymbol, shape );
|
|
|
|
aSheet->LastScreen()->Append( shape );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateKicadSYMBOLs( SCH_SHEET_PATH* aSheet,
|
|
std::vector<LTSPICE_SCHEMATIC::LT_ASC>& outLT_ASCs,
|
|
const std::vector<wxString>& aAsyFiles )
|
|
{
|
|
for( LTSPICE_SCHEMATIC::LT_ASC& lt_asc : outLT_ASCs )
|
|
{
|
|
std::vector<LTSPICE_SCHEMATIC::LT_SYMBOL> symbols = lt_asc.Symbols;
|
|
std::map<wxString, LIB_SYMBOL*> existingSymbol;
|
|
std::map<wxString, SCH_SYMBOL*> existingSchematicSymbol;
|
|
|
|
for( LTSPICE_SCHEMATIC::LT_SYMBOL& lt_symbol : symbols )
|
|
{
|
|
if( !alg::contains( aAsyFiles, lt_symbol.Name ) )
|
|
{
|
|
LIB_SYMBOL* lib_symbol;
|
|
|
|
if( existingSymbol.count( lt_symbol.Name ) == 0 )
|
|
{
|
|
lib_symbol = new LIB_SYMBOL( lt_symbol.Name );
|
|
|
|
CreateSymbol( lt_symbol, lib_symbol );
|
|
|
|
existingSymbol.emplace( lt_symbol.Name, lib_symbol );
|
|
}
|
|
else
|
|
{
|
|
lib_symbol = existingSymbol[lt_symbol.Name];
|
|
}
|
|
|
|
LIB_ID libId( wxS( "ltspice" ), lt_symbol.Name );
|
|
SCH_SYMBOL* sch_symbol = new SCH_SYMBOL( *lib_symbol, libId, aSheet, 1 );
|
|
|
|
CreateFields( lt_symbol, sch_symbol, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_symbol.Wires.size(); j++ )
|
|
CreateWires( lt_symbol, j, aSheet );
|
|
|
|
sch_symbol->Move( ToKicadCoords( lt_symbol.Offset ) + m_originOffset );
|
|
RotateMirror( lt_symbol, sch_symbol );
|
|
|
|
aSheet->LastScreen()->Append( sch_symbol );
|
|
}
|
|
else
|
|
{
|
|
for( int j = 0; j < (int) lt_symbol.Lines.size(); j++ )
|
|
CreateLines( lt_symbol, j, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_symbol.Circles.size(); j++ )
|
|
CreateCircle( lt_symbol, j, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_symbol.Arcs.size(); j++ )
|
|
CreateArc( lt_symbol, j, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_symbol.Rectangles.size(); j++ )
|
|
CreateRect( lt_symbol, j, aSheet );
|
|
|
|
// Calculating bounding box
|
|
BOX2I bbox;
|
|
|
|
LTSPICE_SCHEMATIC::LT_SYMBOL tempSymbol;
|
|
LTSPICE_FILE tempAsyFile( lt_symbol.Name + ".asy", { 0, 0 } );
|
|
LTSPICE_SCHEMATIC::LT_ASC dummyAsc;
|
|
|
|
tempSymbol = m_lt_schematic->SymbolBuilder( lt_symbol.Name, dummyAsc );
|
|
|
|
LIB_SYMBOL* tempLibSymbol = new LIB_SYMBOL( lt_symbol.Name );
|
|
CreateSymbol( tempSymbol, tempLibSymbol );
|
|
|
|
bbox = tempLibSymbol->GetBoundingBox();
|
|
|
|
int topLeftX = lt_symbol.Offset.x + ToLtSpiceCoords( bbox.GetOrigin().x );
|
|
int topLeftY = lt_symbol.Offset.y + ToLtSpiceCoords( bbox.GetOrigin().y );
|
|
int botRightX = lt_symbol.Offset.x
|
|
+ ToLtSpiceCoords( bbox.GetOrigin().x )
|
|
+ ToLtSpiceCoords( bbox.GetSize().x );
|
|
int botRightY = lt_symbol.Offset.y
|
|
+ ToLtSpiceCoords( bbox.GetOrigin().y )
|
|
+ ToLtSpiceCoords( bbox.GetSize().y );
|
|
|
|
for( LTSPICE_SCHEMATIC::LT_PIN& pin : lt_symbol.Pins )
|
|
{
|
|
VECTOR2I pinPos = pin.PinLocation;
|
|
|
|
for( LTSPICE_SCHEMATIC::WIRE& wire : lt_asc.Wires )
|
|
{
|
|
if( wire.Start == ( pinPos + lt_symbol.Offset ) )
|
|
{
|
|
//wire is vertical
|
|
if( wire.End.x == ( pinPos + lt_symbol.Offset ).x )
|
|
{
|
|
if( wire.End.y <= topLeftY )
|
|
wire.Start = VECTOR2I( wire.Start.x, topLeftY + 3 );
|
|
else if( wire.End.y >= botRightY )
|
|
wire.Start = VECTOR2I( wire.Start.x, botRightY );
|
|
else if( wire.End.y < botRightY && wire.End.y > topLeftY )
|
|
wire.Start = VECTOR2I( topLeftX, wire.Start.y );
|
|
}
|
|
//wire is horizontal
|
|
else if( wire.End.y == ( pinPos + lt_symbol.Offset ).y )
|
|
{
|
|
if( wire.End.x <= topLeftX )
|
|
wire.Start = VECTOR2I( topLeftX, wire.Start.y );
|
|
else if( wire.End.x >= botRightX )
|
|
wire.Start = VECTOR2I( botRightX, wire.Start.y );
|
|
else if( wire.End.x < botRightX && wire.End.x > topLeftX )
|
|
wire.Start = VECTOR2I( botRightX, wire.Start.y );
|
|
}
|
|
}
|
|
else if( wire.End == ( pinPos + lt_symbol.Offset ) )
|
|
{
|
|
//wire is Vertical
|
|
if( wire.Start.x == ( pinPos + lt_symbol.Offset ).x )
|
|
{
|
|
if( wire.Start.y <= topLeftY )
|
|
wire.End = VECTOR2I( wire.End.x, topLeftY );
|
|
else if( wire.Start.y > botRightY )
|
|
wire.End = VECTOR2I( wire.End.x, botRightY );
|
|
else if( wire.Start.y < botRightY && wire.End.y > topLeftY )
|
|
wire.End = VECTOR2I( wire.End.x, botRightY );
|
|
}
|
|
//wire is Horizontal
|
|
else if( wire.Start.y == ( pinPos + lt_symbol.Offset ).y )
|
|
{
|
|
if( wire.Start.x <= topLeftX )
|
|
wire.End = VECTOR2I( topLeftX, wire.End.y );
|
|
else if( wire.Start.x >= botRightX )
|
|
wire.End = VECTOR2I( botRightX, wire.End.y );
|
|
else if( wire.Start.x < botRightX && wire.Start.x > topLeftX )
|
|
wire.End = VECTOR2I( botRightX, wire.End.y );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateSymbol( LTSPICE_SCHEMATIC::LT_SYMBOL& aLtSymbol,
|
|
LIB_SYMBOL* aLibSymbol )
|
|
{
|
|
for( int j = 0; j < (int) aLtSymbol.Lines.size(); j++ )
|
|
{
|
|
LIB_SHAPE* line = new LIB_SHAPE( aLibSymbol, SHAPE_T::POLY );
|
|
|
|
CreateLines( aLibSymbol, aLtSymbol, j, line );
|
|
aLibSymbol->AddDrawItem( line );
|
|
}
|
|
|
|
for( int j = 0; j < (int) aLtSymbol.Circles.size(); j++ )
|
|
{
|
|
LIB_SHAPE* circle = new LIB_SHAPE( aLibSymbol, SHAPE_T::CIRCLE );
|
|
|
|
CreateCircle( aLtSymbol, j, circle );
|
|
aLibSymbol->AddDrawItem( circle );
|
|
}
|
|
|
|
for( int j = 0; j < (int) aLtSymbol.Arcs.size(); j++ )
|
|
{
|
|
LIB_SHAPE* arc = new LIB_SHAPE( aLibSymbol, SHAPE_T::ARC );
|
|
|
|
CreateArc( aLtSymbol, j, arc );
|
|
aLibSymbol->AddDrawItem( arc );
|
|
}
|
|
|
|
for( int j = 0; j < (int) aLtSymbol.Rectangles.size(); j++ )
|
|
{
|
|
LIB_SHAPE* rectangle = new LIB_SHAPE( aLibSymbol, SHAPE_T::RECTANGLE );
|
|
|
|
CreateRect( aLtSymbol, j, rectangle );
|
|
aLibSymbol->AddDrawItem( rectangle );
|
|
}
|
|
|
|
for( int j = 0; j < (int) aLtSymbol.Pins.size(); j++ )
|
|
{
|
|
LIB_PIN* pin = new LIB_PIN( aLibSymbol );
|
|
|
|
CreatePin( aLtSymbol, j, pin );
|
|
aLibSymbol->AddDrawItem( pin );
|
|
}
|
|
|
|
aLibSymbol->SetShowPinNumbers( false );
|
|
}
|
|
|
|
|
|
int SCH_IO_LTSPICE_PARSER::ToKicadCoords( int aCoordinate )
|
|
{
|
|
return schIUScale.MilsToIU( rescale( 50, aCoordinate, 16 ) );
|
|
}
|
|
|
|
|
|
VECTOR2I SCH_IO_LTSPICE_PARSER::ToKicadCoords( const VECTOR2I& aPos )
|
|
{
|
|
return VECTOR2I( ToKicadCoords( aPos.x ), ToKicadCoords( aPos.y ) );
|
|
}
|
|
|
|
|
|
VECTOR2I SCH_IO_LTSPICE_PARSER::ToInvertedKicadCoords( const VECTOR2I& aPos )
|
|
{
|
|
return VECTOR2I( ToKicadCoords( aPos.x ), -ToKicadCoords( aPos.y ) );
|
|
}
|
|
|
|
|
|
VECTOR2I SCH_IO_LTSPICE_PARSER::ToKicadFontSize( int aLTFontSize )
|
|
{
|
|
auto MILS_SIZE =
|
|
[]( int mils )
|
|
{
|
|
return VECTOR2I( schIUScale.MilsToIU( mils ), schIUScale.MilsToIU( mils ) );
|
|
};
|
|
|
|
if( aLTFontSize == 1 ) return MILS_SIZE( 36 );
|
|
else if( aLTFontSize == 2 ) return MILS_SIZE( 42 );
|
|
else if( aLTFontSize == 3 ) return MILS_SIZE( 50 );
|
|
else if( aLTFontSize == 4 ) return MILS_SIZE( 60 );
|
|
else if( aLTFontSize == 5 ) return MILS_SIZE( 72 );
|
|
else if( aLTFontSize == 6 ) return MILS_SIZE( 88 );
|
|
else if( aLTFontSize == 7 ) return MILS_SIZE( 108 );
|
|
else return ToKicadFontSize( 2 );
|
|
}
|
|
|
|
|
|
int SCH_IO_LTSPICE_PARSER::ToLtSpiceCoords( int aCoordinate )
|
|
{
|
|
return schIUScale.IUToMils( rescale( 16, aCoordinate, 50 ) );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::RotateMirrorShape( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol,
|
|
SCH_SHAPE* aShape )
|
|
{
|
|
if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::R90 )
|
|
{
|
|
aShape->Rotate( VECTOR2I() );
|
|
aShape->Rotate( VECTOR2I() );
|
|
aShape->Rotate( VECTOR2I() );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::R180 )
|
|
{
|
|
aShape->Rotate( VECTOR2I() );
|
|
aShape->Rotate( VECTOR2I() );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::R270 )
|
|
{
|
|
aShape->Rotate( VECTOR2I() );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M0 )
|
|
{
|
|
aShape->MirrorVertically( 0 );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M90 )
|
|
{
|
|
aShape->MirrorVertically( 0 );
|
|
aShape->Rotate( VECTOR2I() );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M180 )
|
|
{
|
|
aShape->MirrorHorizontally( 0 );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M270 )
|
|
{
|
|
aShape->MirrorVertically( 0 );
|
|
aShape->Rotate( VECTOR2I() );
|
|
aShape->Rotate( VECTOR2I() );
|
|
aShape->Rotate( VECTOR2I() );
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::RotateMirror( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol,
|
|
SCH_SYMBOL* aSchSymbol )
|
|
{
|
|
if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::R0 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_ORIENT_0 );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::R90 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_ORIENT_180 );
|
|
aSchSymbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::R180 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_ORIENT_180 );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::R270 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M0 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_MIRROR_Y );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M90 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_MIRROR_Y );
|
|
aSchSymbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M180 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_MIRROR_X );
|
|
}
|
|
else if( aLTSymbol.SymbolOrientation == LTSPICE_SCHEMATIC::ORIENTATION::M270 )
|
|
{
|
|
aSchSymbol->SetOrientation( SYM_MIRROR_Y );
|
|
aSchSymbol->SetOrientation( SYM_ROTATE_CLOCKWISE );
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateWires( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
SCH_LINE* segment = new SCH_LINE();
|
|
|
|
segment->SetLineWidth( getLineWidth( LTSPICE_SCHEMATIC::LINEWIDTH::Normal ) );
|
|
segment->SetLineStyle( LINE_STYLE::SOLID );
|
|
|
|
segment->SetStartPoint( aLTSymbol.Wires[aIndex].Start );
|
|
segment->SetEndPoint( aLTSymbol.Wires[aIndex].End );
|
|
|
|
aSheet->LastScreen()->Append( segment );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateKicadSCH_ITEMs( SCH_SHEET_PATH* aSheet,
|
|
std::vector<LTSPICE_SCHEMATIC::LT_ASC>& outLT_ASCs )
|
|
{
|
|
SCH_SCREEN* screen = aSheet->LastScreen();
|
|
|
|
for( LTSPICE_SCHEMATIC::LT_ASC& lt_asc : outLT_ASCs )
|
|
{
|
|
for( int j = 0; j < (int) lt_asc.Lines.size(); j++ )
|
|
CreateLine( lt_asc, j, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_asc.Circles.size(); j++ )
|
|
CreateCircle( lt_asc, j, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_asc.Arcs.size(); j++ )
|
|
CreateArc( lt_asc, j, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_asc.Rectangles.size(); j++ )
|
|
CreateRect( lt_asc, j, aSheet );
|
|
|
|
for( int j = 0; j < (int) lt_asc.Bustap.size(); j++ )
|
|
CreateBusEntry( lt_asc, j, aSheet );
|
|
|
|
/**
|
|
*
|
|
*NOTE: This current code is used for plotting sch sheet pins on the sheet, we are working on
|
|
* finding how to place the pin on the intersection of line and sheet.
|
|
*
|
|
*
|
|
if( aSubSchematicStructure )
|
|
{
|
|
SCH_HIERLABEL* sheetPin =
|
|
new SCH_SHEET_PIN( aRootSheet, VECTOR2I(), lt_asc.Flags[index].Value );
|
|
|
|
sheetPin->SetText( "PIN" );
|
|
sheetPin->SetPosition( VECTOR2I(
|
|
schIUScale.MilsToIU( rescale( 50, 592, 16 ) ),
|
|
schIUScale.MilsToIU( rescale( 50, 133, 16 ) ) ) );
|
|
sheetPin->SetVisible( true );
|
|
aRootSheet->AddPin( (SCH_SHEET_PIN*) sheetPin );
|
|
}*/
|
|
|
|
for( int j = 0; j < (int) lt_asc.Wires.size(); j++ )
|
|
CreateWire( lt_asc, j, aSheet, SCH_LAYER_ID::LAYER_WIRE );
|
|
|
|
for( int j = 0; j < (int) lt_asc.Iopins.size(); j++ )
|
|
CreatePin( lt_asc, j, aSheet );
|
|
|
|
for( const LTSPICE_SCHEMATIC::FLAG& lt_flag : lt_asc.Flags )
|
|
{
|
|
if( lt_flag.Value == wxS( "0" ) )
|
|
{
|
|
screen->Append( CreatePowerSymbol( lt_flag.Offset, lt_flag.Value, lt_flag.FontSize,
|
|
aSheet, lt_asc.Wires ) );
|
|
}
|
|
else
|
|
{
|
|
screen->Append( CreateSCH_LABEL( SCH_GLOBAL_LABEL_T, lt_flag.Offset, lt_flag.Value,
|
|
lt_flag.FontSize ) );
|
|
}
|
|
}
|
|
|
|
for( const LTSPICE_SCHEMATIC::TEXT& lt_text : lt_asc.Texts )
|
|
{
|
|
wxString textVal = lt_text.Value;
|
|
|
|
// Includes are already handled through Sim.Library, comment them out
|
|
if( textVal.StartsWith( ".include " ) || textVal.StartsWith( ".inc " )
|
|
|| textVal.StartsWith( ".lib " ) )
|
|
{
|
|
textVal = wxS( "* " ) + textVal;
|
|
}
|
|
|
|
screen->Append( CreateSCH_TEXT( lt_text.Offset, textVal, lt_text.FontSize,
|
|
lt_text.Justification ) );
|
|
}
|
|
|
|
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 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateBusEntry( LTSPICE_SCHEMATIC::LT_ASC& aAscfile, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::BUSTAP& bustap = aAscfile.Bustap[aIndex];
|
|
|
|
for( int k = 0; k < (int) aAscfile.Wires.size(); k++ )
|
|
{
|
|
if( ( aAscfile.Wires[k].Start == bustap.Start )
|
|
|| ( aAscfile.Wires[k].End == bustap.Start ) )
|
|
{
|
|
CreateWire( aAscfile, k, aSheet, SCH_LAYER_ID::LAYER_BUS );
|
|
aAscfile.Wires.erase( aAscfile.Wires.begin() + k );
|
|
}
|
|
}
|
|
|
|
SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( ToKicadCoords( { bustap.Start.x,
|
|
bustap.Start.y - 16 } ) );
|
|
|
|
busEntry->SetSize( { ToKicadCoords( 16 ), ToKicadCoords( 16 ) } );
|
|
|
|
aSheet->LastScreen()->Append( busEntry );
|
|
}
|
|
|
|
|
|
LABEL_FLAG_SHAPE getLabelShape( LTSPICE_SCHEMATIC::POLARITY aPolarity )
|
|
{
|
|
if( aPolarity == LTSPICE_SCHEMATIC::POLARITY::INPUT )
|
|
return LABEL_FLAG_SHAPE::L_INPUT;
|
|
else if( aPolarity == LTSPICE_SCHEMATIC::POLARITY::OUTPUT )
|
|
return LABEL_FLAG_SHAPE::L_OUTPUT;
|
|
else
|
|
return LABEL_FLAG_SHAPE::L_BIDI;
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreatePin( LTSPICE_SCHEMATIC::LT_ASC& aAscfile, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::IOPIN& iopin = aAscfile.Iopins[aIndex];
|
|
wxString ioPinName;
|
|
|
|
for( unsigned int k = 0; k < aAscfile.Flags.size(); k++ )
|
|
{
|
|
if( ( aAscfile.Flags[k].Offset.x == iopin.Location.x )
|
|
&& ( aAscfile.Flags[k].Offset.y == iopin.Location.y ) )
|
|
{
|
|
ioPinName = aAscfile.Flags[k].Value;
|
|
aAscfile.Flags.erase( aAscfile.Flags.begin() + k );
|
|
}
|
|
}
|
|
|
|
SCH_HIERLABEL* sheetPin =
|
|
new SCH_HIERLABEL( ToKicadCoords( iopin.Location ), ioPinName, SCH_HIER_LABEL_T );
|
|
|
|
sheetPin->Move( m_originOffset );
|
|
|
|
sheetPin->SetShape( getLabelShape( iopin.Polarity ) );
|
|
aSheet->LastScreen()->Append( sheetPin );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateLine( LTSPICE_SCHEMATIC::LT_ASC& aAscfile, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::LINE& lt_line = aAscfile.Lines[aIndex];
|
|
SCH_LINE* line = new SCH_LINE( ToKicadCoords( lt_line.Start ), SCH_LAYER_ID::LAYER_NOTES );
|
|
|
|
line->SetEndPoint( ToKicadCoords( lt_line.End ) );
|
|
line->SetStroke( getStroke( lt_line.LineWidth, lt_line.LineStyle ) );
|
|
line->Move( m_originOffset );
|
|
|
|
aSheet->LastScreen()->Append( line );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateCircle( LTSPICE_SCHEMATIC::LT_ASC& aAscfile, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::CIRCLE& lt_circle = aAscfile.Circles[aIndex];
|
|
SCH_SHAPE* circle = new SCH_SHAPE( SHAPE_T::CIRCLE );
|
|
|
|
VECTOR2I c = ( lt_circle.TopLeft + lt_circle.BotRight ) / 2;
|
|
int r = ( lt_circle.TopLeft.x - lt_circle.BotRight.x ) / 2;
|
|
|
|
circle->SetPosition( ToKicadCoords( c ) );
|
|
circle->SetEnd( ToKicadCoords( c ) + VECTOR2I( abs( ToKicadCoords( r ) ), 0 ) );
|
|
circle->SetStroke( getStroke( lt_circle.LineWidth, lt_circle.LineStyle ) );
|
|
circle->Move( m_originOffset );
|
|
|
|
aSheet->LastScreen()->Append( circle );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateArc( LTSPICE_SCHEMATIC::LT_ASC& aAscfile, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::ARC& lt_arc = aAscfile.Arcs[aIndex];
|
|
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->SetStroke( getStroke( lt_arc.LineWidth, lt_arc.LineStyle ) );
|
|
arc->Move( m_originOffset );
|
|
|
|
aSheet->LastScreen()->Append( arc );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateRect( LTSPICE_SCHEMATIC::LT_ASC& aAscfile, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::RECTANGLE& lt_rect = aAscfile.Rectangles[aIndex];
|
|
SCH_SHAPE* rectangle = new SCH_SHAPE( SHAPE_T::RECTANGLE );
|
|
|
|
rectangle->SetPosition( ToKicadCoords( lt_rect.TopLeft ) );
|
|
rectangle->SetEnd( ToKicadCoords( lt_rect.BotRight ) );
|
|
rectangle->SetStroke( getStroke( lt_rect.LineWidth, lt_rect.LineStyle ) );
|
|
rectangle->Move( m_originOffset );
|
|
|
|
aSheet->LastScreen()->Append( rectangle );
|
|
}
|
|
|
|
|
|
int SCH_IO_LTSPICE_PARSER::getLineWidth( const LTSPICE_SCHEMATIC::LINEWIDTH& aLineWidth )
|
|
{
|
|
if( aLineWidth == LTSPICE_SCHEMATIC::LINEWIDTH::Normal )
|
|
return schIUScale.MilsToIU( 6 );
|
|
else if( aLineWidth == LTSPICE_SCHEMATIC::LINEWIDTH::Wide )
|
|
return schIUScale.MilsToIU( 12 );
|
|
else
|
|
return schIUScale.MilsToIU( 6 );
|
|
}
|
|
|
|
|
|
LINE_STYLE SCH_IO_LTSPICE_PARSER::getLineStyle( const LTSPICE_SCHEMATIC::LINESTYLE& aLineStyle )
|
|
{
|
|
switch( aLineStyle )
|
|
{
|
|
case LTSPICE_SCHEMATIC::LINESTYLE::SOLID: return LINE_STYLE::SOLID;
|
|
case LTSPICE_SCHEMATIC::LINESTYLE::DOT: return LINE_STYLE::DOT;
|
|
case LTSPICE_SCHEMATIC::LINESTYLE::DASHDOTDOT: return LINE_STYLE::DASHDOTDOT;
|
|
case LTSPICE_SCHEMATIC::LINESTYLE::DASHDOT: return LINE_STYLE::DASHDOT;
|
|
case LTSPICE_SCHEMATIC::LINESTYLE::DASH: return LINE_STYLE::DASH;
|
|
default: return LINE_STYLE::SOLID;
|
|
}
|
|
}
|
|
|
|
|
|
STROKE_PARAMS SCH_IO_LTSPICE_PARSER::getStroke( const LTSPICE_SCHEMATIC::LINEWIDTH& aLineWidth,
|
|
const LTSPICE_SCHEMATIC::LINESTYLE& aLineStyle )
|
|
{
|
|
return STROKE_PARAMS( getLineWidth( aLineWidth ), getLineStyle( aLineStyle ) );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::setTextJustification( EDA_TEXT* aText,
|
|
LTSPICE_SCHEMATIC::JUSTIFICATION aJustification )
|
|
{
|
|
switch( aJustification )
|
|
{
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::LEFT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VLEFT:
|
|
aText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
|
|
aText->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::CENTER:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VCENTER:
|
|
aText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
|
|
aText->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::RIGHT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VRIGHT:
|
|
aText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
|
|
aText->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::BOTTOM:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VBOTTOM:
|
|
aText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
|
|
aText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::TOP:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VTOP:
|
|
aText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
|
|
aText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
switch( aJustification )
|
|
{
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::LEFT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::CENTER:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::RIGHT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::BOTTOM:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::TOP:
|
|
aText->SetTextAngle( ANGLE_HORIZONTAL );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VLEFT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VCENTER:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VRIGHT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VBOTTOM:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VTOP:
|
|
aText->SetTextAngle( ANGLE_VERTICAL );
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
// Center, Left, Right aligns by first line in multiline text
|
|
if( wxSplit( aText->GetText(), '\n', '\0' ).size() > 1 )
|
|
{
|
|
switch( aJustification )
|
|
{
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::LEFT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::CENTER:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::RIGHT:
|
|
aText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
|
|
aText->Offset( VECTOR2I( 0, -aText->GetTextHeight() / 2 ) );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VLEFT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VCENTER:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VRIGHT:
|
|
aText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
|
|
aText->Offset( VECTOR2I( -aText->GetTextHeight() / 2, 0 ) );
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SCH_TEXT* SCH_IO_LTSPICE_PARSER::CreateSCH_TEXT( VECTOR2I aOffset, const wxString& aText,
|
|
int aFontSize,
|
|
LTSPICE_SCHEMATIC::JUSTIFICATION aJustification )
|
|
{
|
|
VECTOR2I pos = ToKicadCoords( aOffset ) + m_originOffset;
|
|
SCH_TEXT* textItem = new SCH_TEXT( pos, aText, SCH_TEXT_T );
|
|
|
|
textItem->SetTextSize( ToKicadFontSize( aFontSize ) );
|
|
textItem->SetVisible( true );
|
|
textItem->SetMultilineAllowed( true );
|
|
|
|
setTextJustification( textItem, aJustification );
|
|
|
|
return textItem;
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateWire( LTSPICE_SCHEMATIC::LT_ASC& aAscfile, int aIndex,
|
|
SCH_SHEET_PATH* aSheet, SCH_LAYER_ID aLayer )
|
|
{
|
|
SCH_LINE* segment = new SCH_LINE();
|
|
|
|
segment->SetLineWidth( getLineWidth( LTSPICE_SCHEMATIC::LINEWIDTH::Normal ) );
|
|
segment->SetLineStyle( LINE_STYLE::SOLID );
|
|
segment->SetLayer( aLayer );
|
|
|
|
segment->SetStartPoint( ToKicadCoords( aAscfile.Wires[aIndex].Start ) + m_originOffset );
|
|
segment->SetEndPoint( ToKicadCoords( aAscfile.Wires[aIndex].End ) + m_originOffset );
|
|
|
|
aSheet->LastScreen()->Append( segment );
|
|
}
|
|
|
|
|
|
SCH_SYMBOL* SCH_IO_LTSPICE_PARSER::CreatePowerSymbol( const VECTOR2I& aOffset,
|
|
const wxString& aValue,
|
|
int aFontSize, SCH_SHEET_PATH* aSheet,
|
|
std::vector<LTSPICE_SCHEMATIC::WIRE>& aWires )
|
|
{
|
|
LIB_SYMBOL* lib_symbol = new LIB_SYMBOL( wxS( "GND" ) );
|
|
LIB_SHAPE* shape = new LIB_SHAPE( lib_symbol, SHAPE_T::POLY );
|
|
|
|
shape->AddPoint( ToInvertedKicadCoords( { 16, 0 } ) );
|
|
shape->AddPoint( ToInvertedKicadCoords( { -16, 0 } ) );
|
|
shape->AddPoint( ToInvertedKicadCoords( { 0, 15 } ) );
|
|
shape->AddPoint( ToInvertedKicadCoords( { 16, 0 } ) );
|
|
shape->AddPoint( ToInvertedKicadCoords( { -16, 0 } ) );
|
|
shape->AddPoint( ToInvertedKicadCoords( { 0, 15 } ) );
|
|
|
|
shape->SetStroke( STROKE_PARAMS( getLineWidth( LTSPICE_SCHEMATIC::LINEWIDTH::Normal ),
|
|
LINE_STYLE::SOLID ) );
|
|
|
|
lib_symbol->AddDrawItem( shape );
|
|
lib_symbol->SetPower();
|
|
|
|
LIB_PIN* pin = new LIB_PIN( lib_symbol );
|
|
|
|
pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
|
|
pin->SetPosition( ToInvertedKicadCoords( { 0, 0 } ) );
|
|
pin->SetLength( 5 );
|
|
pin->SetShape( GRAPHIC_PINSHAPE::LINE );
|
|
lib_symbol->AddDrawItem( pin );
|
|
|
|
LIB_ID libId( wxS( "ltspice" ), wxS( "GND" ) );
|
|
SCH_SYMBOL* sch_symbol = new SCH_SYMBOL( *lib_symbol, libId, aSheet, 1 );
|
|
|
|
sch_symbol->SetRef( aSheet, wxString::Format( wxS( "#GND%03d" ), m_powerSymbolIndex++ ) );
|
|
sch_symbol->GetField( REFERENCE_FIELD )->SetVisible( false );
|
|
sch_symbol->SetValueFieldText( wxS( "0" ) );
|
|
sch_symbol->GetField( VALUE_FIELD )->SetTextSize( ToKicadFontSize( aFontSize ) );
|
|
sch_symbol->GetField( VALUE_FIELD )->SetVisible( false );
|
|
|
|
sch_symbol->Move( ToKicadCoords( aOffset ) + m_originOffset );
|
|
|
|
for( LTSPICE_SCHEMATIC::WIRE& wire : aWires )
|
|
{
|
|
if( aOffset == wire.Start )
|
|
{
|
|
if( wire.Start.x == wire.End.x )
|
|
{
|
|
if( wire.Start.y < wire.End.y )
|
|
{
|
|
sch_symbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
sch_symbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( wire.Start.x < wire.End.x )
|
|
sch_symbol->SetOrientation( SYM_ROTATE_CLOCKWISE );
|
|
else if( wire.Start.x > wire.End.x )
|
|
sch_symbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
}
|
|
}
|
|
else if( aOffset == wire.End )
|
|
{
|
|
if( wire.Start.x == wire.End.x )
|
|
{
|
|
if( wire.Start.y > wire.End.y )
|
|
{
|
|
sch_symbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
sch_symbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( wire.Start.x < wire.End.x )
|
|
sch_symbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
|
|
else if( wire.Start.x > wire.End.x )
|
|
sch_symbol->SetOrientation( SYM_ROTATE_CLOCKWISE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return sch_symbol;
|
|
}
|
|
|
|
|
|
SCH_LABEL_BASE* SCH_IO_LTSPICE_PARSER::CreateSCH_LABEL( KICAD_T aType, const VECTOR2I& aOffset,
|
|
const wxString& aValue, int aFontSize )
|
|
{
|
|
SCH_LABEL_BASE* label = nullptr;
|
|
|
|
if( aType == SCH_GLOBAL_LABEL_T )
|
|
{
|
|
label = new SCH_GLOBALLABEL();
|
|
|
|
label->SetText( aValue );
|
|
label->SetTextSize( ToKicadFontSize( aFontSize ) );
|
|
label->SetSpinStyle( SPIN_STYLE::UP );
|
|
}
|
|
else if( aType == SCH_DIRECTIVE_LABEL_T )
|
|
{
|
|
label = new SCH_DIRECTIVE_LABEL();
|
|
|
|
label->SetSpinStyle( SPIN_STYLE::RIGHT );
|
|
|
|
SCH_FIELD field( { 0, 0 }, -1, label, wxS( "DATAFLAG" ) );
|
|
field.SetText( aValue );
|
|
field.SetTextSize( ToKicadFontSize( aFontSize ) );
|
|
field.SetVisible( true );
|
|
|
|
label->AddField( field );
|
|
label->AutoplaceFields( nullptr, false );
|
|
}
|
|
else
|
|
{
|
|
UNIMPLEMENTED_FOR( aType );
|
|
}
|
|
|
|
if( label )
|
|
{
|
|
label->SetPosition( ToKicadCoords( aOffset ) + m_originOffset );
|
|
label->SetVisible( true );
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateFields( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol,
|
|
SCH_SYMBOL* aSymbol, SCH_SHEET_PATH* aSheet )
|
|
{
|
|
wxString libPath = m_lt_schematic->GetLTspiceDataDir().GetFullPath();
|
|
wxString symbolName = aLTSymbol.Name.Upper();
|
|
wxString type = aLTSymbol.SymAttributes[wxS( "TYPE" )].Upper();
|
|
wxString prefix = aLTSymbol.SymAttributes[wxS( "PREFIX" )].Upper();
|
|
wxString instName = aLTSymbol.SymAttributes[wxS( "INSTNAME" )].Upper();
|
|
wxString value = aLTSymbol.SymAttributes[wxS( "VALUE" )];
|
|
wxString value2 = aLTSymbol.SymAttributes[wxS( "VALUE2" )];
|
|
|
|
if( value.IsEmpty() )
|
|
{
|
|
value = value2;
|
|
value2 = wxEmptyString;
|
|
}
|
|
|
|
aSymbol->SetRef( aSheet, instName );
|
|
aSymbol->SetValueFieldText( value );
|
|
|
|
if( !value2.IsEmpty() )
|
|
{
|
|
SCH_FIELD paramsField( { 0, 0 }, -1, aSymbol, wxS( "Value2" ) );
|
|
paramsField.SetText( value2 );
|
|
aSymbol->AddField( paramsField );
|
|
}
|
|
|
|
auto setupNonInferredPassive = [&]( const wxString& aDevice, const wxString& aValueKey )
|
|
{
|
|
SCH_FIELD deviceField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Device" ) );
|
|
deviceField.SetText( aDevice );
|
|
aSymbol->AddField( deviceField );
|
|
|
|
SCH_FIELD paramsField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Params" ) );
|
|
paramsField.SetText( aValueKey + wxS( "=${VALUE}" ) );
|
|
aSymbol->AddField( paramsField );
|
|
};
|
|
|
|
auto setupBehavioral = [&]( const wxString& aDevice, const wxString& aType )
|
|
{
|
|
aSymbol->SetValueFieldText( wxS( "${Sim.Params}" ) );
|
|
|
|
SCH_FIELD deviceField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Device" ) );
|
|
deviceField.SetText( aDevice );
|
|
aSymbol->AddField( deviceField );
|
|
|
|
SCH_FIELD typeField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Type" ) );
|
|
typeField.SetText( aType );
|
|
aSymbol->AddField( typeField );
|
|
|
|
SCH_FIELD paramsField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Params" ) );
|
|
paramsField.SetText( value );
|
|
aSymbol->AddField( paramsField );
|
|
};
|
|
|
|
static const std::set<wxString> prefixWithGain = { wxS( "E" ), wxS( "F" ), wxS( "G" ),
|
|
wxS( "H" ) };
|
|
|
|
if( prefix == wxS( "R" ) )
|
|
{
|
|
setupNonInferredPassive( prefix, wxS( "R" ) );
|
|
}
|
|
else if( prefix == wxS( "C" ) )
|
|
{
|
|
setupNonInferredPassive( prefix, wxS( "C" ) );
|
|
}
|
|
else if( prefix == wxS( "L" ) )
|
|
{
|
|
setupNonInferredPassive( prefix, wxS( "L" ) );
|
|
}
|
|
else if( prefixWithGain.count( prefix ) > 0 )
|
|
{
|
|
setupNonInferredPassive( prefix, wxS( "gain" ) );
|
|
}
|
|
else if( prefix == wxS( "B" ) )
|
|
{
|
|
if( symbolName.StartsWith( wxS( "BV" ) ) )
|
|
{
|
|
setupBehavioral( wxS( "V" ), wxS( "=" ) );
|
|
}
|
|
else if( symbolName.StartsWith( wxS( "BI" ) ) )
|
|
{
|
|
setupBehavioral( wxS( "I" ), wxS( "=" ) );
|
|
}
|
|
}
|
|
else if( prefix == wxS( "V" ) || symbolName == wxS( "I" ) )
|
|
{
|
|
SCH_FIELD deviceField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Device" ) );
|
|
deviceField.SetText( wxS( "SPICE" ) );
|
|
aSymbol->AddField( deviceField );
|
|
|
|
wxString simParams;
|
|
simParams << "type=" << '"' << prefix << '"' << ' ';
|
|
|
|
if( value2.IsEmpty() )
|
|
simParams << "model=" << '"' << "${VALUE}" << '"' << ' ';
|
|
else
|
|
simParams << "model=" << '"' << "${VALUE} ${VALUE2}" << '"' << ' ';
|
|
|
|
SCH_FIELD paramsField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Params" ) );
|
|
paramsField.SetText( simParams );
|
|
aSymbol->AddField( paramsField );
|
|
}
|
|
else
|
|
{
|
|
wxString libFile = aLTSymbol.SymAttributes[wxS( "MODELFILE" )];
|
|
|
|
if( prefix == wxS( "X" ) )
|
|
{
|
|
// A prefix of X overrides the simulation model for other symbols (npn, etc.)
|
|
type = wxS( "X" );
|
|
}
|
|
else if( libFile.IsEmpty() )
|
|
{
|
|
if( type.IsEmpty() )
|
|
type = symbolName;
|
|
|
|
if( value == "DIODE" )
|
|
libFile = libPath + wxS( "cmp/standard.dio" );
|
|
else if( value == "NPN" || value == "PNP" )
|
|
libFile = libPath + wxS( "cmp/standard.bjt" );
|
|
else if( value == "NJF" || value == "PJF" )
|
|
libFile = libPath + wxS( "cmp/standard.jft" );
|
|
else if( value == "NMOS" || value == "PMOS" )
|
|
libFile = libPath + wxS( "cmp/standard.mos" );
|
|
}
|
|
|
|
if( libFile.IsEmpty() )
|
|
libFile = m_includes[value];
|
|
|
|
if( !libFile.IsEmpty() )
|
|
{
|
|
SCH_FIELD libField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Library" ) );
|
|
libField.SetText( libFile );
|
|
aSymbol->AddField( libField );
|
|
}
|
|
|
|
if( type == wxS( "X" ) )
|
|
{
|
|
SCH_FIELD deviceField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Device" ) );
|
|
deviceField.SetText( wxS( "SUBCKT" ) );
|
|
aSymbol->AddField( deviceField );
|
|
}
|
|
else
|
|
{
|
|
SCH_FIELD deviceField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Device" ) );
|
|
deviceField.SetText( wxS( "SPICE" ) );
|
|
aSymbol->AddField( deviceField );
|
|
}
|
|
|
|
wxString spiceLine = aLTSymbol.SymAttributes[wxS( "SPICELINE" )];
|
|
|
|
if( !spiceLine.IsEmpty() )
|
|
{
|
|
// TODO: append value
|
|
SCH_FIELD paramsField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Params" ) );
|
|
paramsField.SetText( spiceLine );
|
|
aSymbol->AddField( paramsField );
|
|
}
|
|
else
|
|
{
|
|
SCH_FIELD modelField( { 0, 0 }, -1, aSymbol, wxS( "Sim.Params" ) );
|
|
modelField.SetText( "model=\"" + value + "\"" );
|
|
aSymbol->AddField( modelField );
|
|
}
|
|
}
|
|
|
|
for( LTSPICE_SCHEMATIC::LT_WINDOW& lt_window : aLTSymbol.Windows )
|
|
{
|
|
SCH_FIELD* field = nullptr;
|
|
|
|
switch( lt_window.WindowNumber )
|
|
{
|
|
case -1: /* PartNum */ break;
|
|
case 0: /* InstName */ field = aSymbol->GetField( REFERENCE_FIELD ); break;
|
|
case 1: /* Type */ break;
|
|
case 2: /* RefName */ break;
|
|
case 3: /* Value */ field = aSymbol->GetField( VALUE_FIELD ); break;
|
|
|
|
case 5: /* QArea */ break;
|
|
|
|
case 8: /* Width */ break;
|
|
case 9: /* Length */ break;
|
|
case 10: /* Multi */ break;
|
|
|
|
case 16: /* Nec */ break;
|
|
|
|
case 38: /* SpiceModel */ field = aSymbol->FindField( wxS( "Sim.Name" ) ); break;
|
|
case 39: /* SpiceLine */ field = aSymbol->FindField( wxS( "Sim.Params" ) ); break;
|
|
case 40: /* SpiceLine2 */ break;
|
|
|
|
/*
|
|
47 Def_Sub
|
|
|
|
50 Digital_Timing_Model
|
|
51 Digital_Extracts
|
|
52 Digital_IO_Model
|
|
53 Digital_Line
|
|
54 Digital_Primitive
|
|
55 Digital_MNTYMXDLY
|
|
56 Digital_IO_LEVEL
|
|
57 Digital_StdCell
|
|
58 Digital_File
|
|
|
|
105 Cell
|
|
106 W/L
|
|
107 PSIZE
|
|
108 NSIZE
|
|
109 sheets
|
|
110 sh#
|
|
111 Em_Scale
|
|
112 Epi
|
|
113 Sinker
|
|
114 Multi5
|
|
|
|
118 AQ
|
|
119 AQSUB
|
|
120 ZSIZE
|
|
121 ESR
|
|
123 Value2
|
|
124 COUPLE
|
|
125 Voltage
|
|
126 Area1
|
|
127 Area2
|
|
128 Area3
|
|
129 Area4
|
|
130 Multi1
|
|
131 Multi2
|
|
132 Multi3
|
|
133 Multi4
|
|
134 DArea
|
|
135 DPerim
|
|
136 CArea
|
|
137 CPerim
|
|
138 Shrink
|
|
139 Gate_Resize
|
|
|
|
142 BP
|
|
143 BN
|
|
144 Sim_Level
|
|
|
|
146 G_Voltage
|
|
|
|
150 SpiceLine3
|
|
|
|
153 D_VOLTAGES
|
|
|
|
156 Version
|
|
157 Comment
|
|
158 XDef_Sub
|
|
159 LVS_Area
|
|
|
|
162 User1
|
|
163 User2
|
|
164 User3
|
|
165 User4
|
|
166 User5
|
|
167 Root
|
|
168 Class
|
|
169 Geometry
|
|
170 WL_Delimiter
|
|
|
|
175 T1
|
|
176 T2
|
|
|
|
184 DsgnName
|
|
185 Designer
|
|
|
|
190 RTN
|
|
191 PWR
|
|
192 BW
|
|
|
|
201 CAPROWS
|
|
202 CAPCOLS
|
|
203 NF
|
|
204 SLICES
|
|
205 CUR
|
|
206 TEMPRISE
|
|
207 STRIPS
|
|
208 WEM
|
|
209 LEM
|
|
210 BASES
|
|
211 COLS
|
|
212 XDef_Tub
|
|
*/
|
|
default: break;
|
|
}
|
|
|
|
if( field )
|
|
{
|
|
field->SetPosition( ToKicadCoords( lt_window.Position ) );
|
|
field->SetTextSize( ToKicadFontSize( lt_window.FontSize ) );
|
|
|
|
if( lt_window.FontSize == 0 )
|
|
field->SetVisible( false );
|
|
|
|
setTextJustification( field, lt_window.Justification );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateRect( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
LIB_SHAPE* aRectangle )
|
|
{
|
|
LTSPICE_SCHEMATIC::RECTANGLE& lt_rect = aLTSymbol.Rectangles[aIndex];
|
|
|
|
aRectangle->SetPosition( ToInvertedKicadCoords( lt_rect.BotRight ) );
|
|
aRectangle->SetEnd( ToInvertedKicadCoords( lt_rect.TopLeft ) );
|
|
aRectangle->SetStroke( getStroke( lt_rect.LineWidth, lt_rect.LineStyle ) );
|
|
|
|
if( aLTSymbol.SymAttributes[wxS( "Prefix" )] == wxS( "X" ) )
|
|
aRectangle->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateRect( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::RECTANGLE& lt_rect = aLTSymbol.Rectangles[aIndex];
|
|
SCH_SHAPE* rectangle = new SCH_SHAPE( SHAPE_T::RECTANGLE );
|
|
|
|
rectangle->SetPosition( ToInvertedKicadCoords( lt_rect.BotRight ) );
|
|
rectangle->SetEnd( ToInvertedKicadCoords( lt_rect.TopLeft ) );
|
|
rectangle->SetStroke( getStroke( lt_rect.LineWidth, lt_rect.LineStyle ) );
|
|
|
|
rectangle->Move( aLTSymbol.Offset );
|
|
RotateMirrorShape( aLTSymbol, rectangle );
|
|
|
|
aSheet->LastScreen()->Append( rectangle );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreatePin( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
LIB_PIN* aPin )
|
|
{
|
|
LTSPICE_SCHEMATIC::LT_PIN& lt_pin = aLTSymbol.Pins[aIndex];
|
|
wxString device = aLTSymbol.Name.Lower();
|
|
|
|
if( aLTSymbol.Pins.size() == 2 && ( device == wxS( "res" )
|
|
|| device == wxS( "cap" )
|
|
|| device == wxS( "ind" ) ) )
|
|
{
|
|
// drop A/B pin names from simple LRCs as they're not terribly useful (and prevent
|
|
// other pin names on the net from driving the net name).
|
|
}
|
|
else
|
|
{
|
|
aPin->SetName( lt_pin.PinAttribute[ wxS( "PinName" ) ] );
|
|
|
|
if( lt_pin.PinJustification == LTSPICE_SCHEMATIC::JUSTIFICATION::NONE )
|
|
aPin->SetNameTextSize( 0 );
|
|
}
|
|
|
|
aPin->SetNumber( wxString::Format( wxS( "%d" ), aIndex + 1 ) );
|
|
aPin->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE );
|
|
aPin->SetPosition( ToInvertedKicadCoords( lt_pin.PinLocation ) );
|
|
aPin->SetLength( 5 );
|
|
aPin->SetShape( GRAPHIC_PINSHAPE::LINE );
|
|
|
|
switch( lt_pin.PinJustification )
|
|
{
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::LEFT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VLEFT:
|
|
aPin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::RIGHT:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VRIGHT:
|
|
aPin->SetOrientation( PIN_ORIENTATION::PIN_LEFT );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::BOTTOM:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VBOTTOM:
|
|
aPin->SetOrientation( PIN_ORIENTATION::PIN_UP );
|
|
break;
|
|
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::TOP:
|
|
case LTSPICE_SCHEMATIC::JUSTIFICATION::VTOP:
|
|
aPin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateArc( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
LIB_SHAPE* aArc )
|
|
{
|
|
LTSPICE_SCHEMATIC::ARC& lt_arc = aLTSymbol.Arcs[aIndex];
|
|
|
|
aArc->SetCenter( ToInvertedKicadCoords( ( lt_arc.TopLeft + lt_arc.BotRight ) / 2 ) );
|
|
aArc->SetEnd( ToInvertedKicadCoords( lt_arc.ArcEnd ) );
|
|
aArc->SetStart( ToInvertedKicadCoords( lt_arc.ArcStart ) );
|
|
aArc->SetStroke( getStroke( lt_arc.LineWidth, lt_arc.LineStyle ) );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateArc( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::ARC& lt_arc = aLTSymbol.Arcs[aIndex];
|
|
SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
|
|
|
|
arc->SetCenter( ToInvertedKicadCoords( ( lt_arc.TopLeft + lt_arc.BotRight ) / 2 ) );
|
|
arc->SetEnd( ToInvertedKicadCoords( lt_arc.ArcEnd ) );
|
|
arc->SetStart( ToInvertedKicadCoords( lt_arc.ArcStart ) );
|
|
arc->SetStroke( getStroke( lt_arc.LineWidth, lt_arc.LineStyle ) );
|
|
|
|
arc->Move( ToKicadCoords( aLTSymbol.Offset ) + m_originOffset );
|
|
RotateMirrorShape( aLTSymbol, arc );
|
|
|
|
aSheet->LastScreen()->Append( arc );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateCircle( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
SCH_SHEET_PATH* aSheet )
|
|
{
|
|
LTSPICE_SCHEMATIC::CIRCLE& lt_circle = aLTSymbol.Circles[aIndex];
|
|
SCH_SHAPE* circle = new SCH_SHAPE( SHAPE_T::CIRCLE );
|
|
|
|
VECTOR2I c = ( lt_circle.TopLeft + lt_circle.BotRight ) / 2;
|
|
int r = ( lt_circle.TopLeft.x - lt_circle.BotRight.x ) / 2;
|
|
|
|
circle->SetPosition( ToInvertedKicadCoords( c ) );
|
|
circle->SetEnd( ToInvertedKicadCoords( c ) + VECTOR2I( abs( ToKicadCoords( r ) ), 0 ) );
|
|
circle->SetStroke( getStroke( lt_circle.LineWidth, lt_circle.LineStyle ) );
|
|
|
|
circle->Move( aLTSymbol.Offset );
|
|
RotateMirrorShape( aLTSymbol, circle );
|
|
|
|
aSheet->LastScreen()->Append( circle );
|
|
}
|
|
|
|
|
|
void SCH_IO_LTSPICE_PARSER::CreateCircle( LTSPICE_SCHEMATIC::LT_SYMBOL& aLTSymbol, int aIndex,
|
|
LIB_SHAPE* aCircle )
|
|
{
|
|
LTSPICE_SCHEMATIC::CIRCLE& lt_circle = aLTSymbol.Circles[aIndex];
|
|
|
|
VECTOR2I c = ( lt_circle.TopLeft + lt_circle.BotRight ) / 2;
|
|
int r = ( lt_circle.TopLeft.x - lt_circle.BotRight.x ) / 2;
|
|
|
|
aCircle->SetPosition( ToInvertedKicadCoords( c ) );
|
|
aCircle->SetEnd( ToInvertedKicadCoords( c ) + VECTOR2I( abs( ToKicadCoords( r ) ), 0 ) );
|
|
aCircle->SetStroke( getStroke( lt_circle.LineWidth, lt_circle.LineStyle ) );
|
|
}
|
|
|
|
|