Eeschema: Eagle schematic plugin parser improvements.
* Fix multi line text alignment. * Parse schematic frame objects and convert them to lines. * Make implicit global labels normal size so they can easily be seen. * Move sheet fields to a more sensible position. * Parse schematic symbol instances before wires so that the implicit power connections can properly be tested. This will be used at a later time. * Scale label text size down to allow for differences in label offset and graphical items.
This commit is contained in:
parent
d93ba0a06a
commit
089b7afacf
|
@ -2,8 +2,9 @@
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
||||||
* Copyright (C) 2012-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
* Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
* Copyright (C) 2017 CERN.
|
* Copyright (C) 2017 CERN.
|
||||||
|
*
|
||||||
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -59,17 +60,21 @@ OPTIONAL_XML_ATTRIBUTE<wxString>::OPTIONAL_XML_ATTRIBUTE( wxString aData )
|
||||||
|
|
||||||
ECOORD::ECOORD( const wxString& aValue, enum ECOORD::EAGLE_UNIT aUnit )
|
ECOORD::ECOORD( const wxString& aValue, enum ECOORD::EAGLE_UNIT aUnit )
|
||||||
{
|
{
|
||||||
// this array is used to adjust the fraction part value basing on the number of digits in the fraction
|
// This array is used to adjust the fraction part value basing on the number of digits
|
||||||
|
// in the fraction.
|
||||||
constexpr int DIVIDERS[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
constexpr int DIVIDERS[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||||
constexpr unsigned int DIVIDERS_MAX_IDX = sizeof( DIVIDERS ) / sizeof( DIVIDERS[0] ) - 1;
|
constexpr unsigned int DIVIDERS_MAX_IDX = sizeof( DIVIDERS ) / sizeof( DIVIDERS[0] ) - 1;
|
||||||
|
|
||||||
int integer, fraction, pre_fraction, post_fraction;
|
int integer, fraction, pre_fraction, post_fraction;
|
||||||
|
|
||||||
// the following check is needed to handle correctly negative fractions where the integer part == 0
|
// The following check is needed to handle correctly negative fractions where the integer
|
||||||
|
// part == 0.
|
||||||
bool negative = ( aValue[0] == '-' );
|
bool negative = ( aValue[0] == '-' );
|
||||||
|
|
||||||
// %n is used to find out how many digits contains the fraction part, e.g. 0.001 contains 3 digits
|
// %n is used to find out how many digits contains the fraction part, e.g. 0.001 contains 3
|
||||||
int ret = sscanf( aValue.c_str(), "%d.%n%d%n", &integer, &pre_fraction, &fraction, &post_fraction );
|
// digits.
|
||||||
|
int ret = sscanf( aValue.c_str(), "%d.%n%d%n", &integer, &pre_fraction, &fraction,
|
||||||
|
&post_fraction );
|
||||||
|
|
||||||
if( ret == 0 )
|
if( ret == 0 )
|
||||||
throw XML_PARSER_ERROR( "Invalid coordinate" );
|
throw XML_PARSER_ERROR( "Invalid coordinate" );
|
||||||
|
@ -82,7 +87,8 @@ ECOORD::ECOORD( const wxString& aValue, enum ECOORD::EAGLE_UNIT aUnit )
|
||||||
{
|
{
|
||||||
int digits = post_fraction - pre_fraction;
|
int digits = post_fraction - pre_fraction;
|
||||||
|
|
||||||
// adjust the number of digits if necessary as we cannot handle anything smaller than nanometers (rounding)
|
// adjust the number of digits if necessary as we cannot handle anything smaller than
|
||||||
|
// nanometers (rounding).
|
||||||
if( (unsigned) digits > DIVIDERS_MAX_IDX )
|
if( (unsigned) digits > DIVIDERS_MAX_IDX )
|
||||||
{
|
{
|
||||||
int diff = digits - DIVIDERS_MAX_IDX;
|
int diff = digits - DIVIDERS_MAX_IDX;
|
||||||
|
@ -204,8 +210,8 @@ ECOORD Convert<ECOORD>( const wxString& aCoord )
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function parseRequiredAttribute
|
* Parse \a aAttribute of the XML node \a aNode.
|
||||||
* parsese the aAttribute of the XML node aNode.
|
*
|
||||||
* @param aNode is the node whose attribute will be parsed.
|
* @param aNode is the node whose attribute will be parsed.
|
||||||
* @param aAttribute is the attribute that will be parsed.
|
* @param aAttribute is the attribute that will be parsed.
|
||||||
* @throw XML_PARSER_ERROR - exception thrown if the required attribute is missing
|
* @throw XML_PARSER_ERROR - exception thrown if the required attribute is missing
|
||||||
|
@ -224,8 +230,8 @@ T parseRequiredAttribute( wxXmlNode* aNode, const wxString& aAttribute )
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function parseOptionalAttribute
|
* Parse option \a aAttribute of the XML node \a aNode.
|
||||||
* parses the aAttribute of the XML node aNode.
|
*
|
||||||
* @param aNode is the node whose attribute will be parsed.
|
* @param aNode is the node whose attribute will be parsed.
|
||||||
* @param aAttribute is the attribute that will be parsed.
|
* @param aAttribute is the attribute that will be parsed.
|
||||||
* @return OPTIONAL_XML_ATTRIBUTE<T> - an optional XML attribute, parsed as the specified type if
|
* @return OPTIONAL_XML_ATTRIBUTE<T> - an optional XML attribute, parsed as the specified type if
|
||||||
|
@ -610,6 +616,43 @@ wxSize ETEXT::ConvertSize() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EFRAME::EFRAME( wxXmlNode* aFrameNode )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* <!ELEMENT frame EMPTY>
|
||||||
|
* <!ATTLIST frame
|
||||||
|
* x1 %Coord; #REQUIRED
|
||||||
|
* y1 %Coord; #REQUIRED
|
||||||
|
* x2 %Coord; #REQUIRED
|
||||||
|
* y2 %Coord; #REQUIRED
|
||||||
|
* columns %Int; #REQUIRED
|
||||||
|
* rows %Int; #REQUIRED
|
||||||
|
* layer %Layer; #REQUIRED
|
||||||
|
* border-left %Bool; "yes"
|
||||||
|
* border-top %Bool; "yes"
|
||||||
|
* border-right %Bool; "yes"
|
||||||
|
* border-bottom %Bool; "yes"
|
||||||
|
* >
|
||||||
|
*/
|
||||||
|
border_left = true;
|
||||||
|
border_top = true;
|
||||||
|
border_right = true;
|
||||||
|
border_bottom = true;
|
||||||
|
|
||||||
|
x1 = parseRequiredAttribute<ECOORD>( aFrameNode, "x1" );
|
||||||
|
y1 = parseRequiredAttribute<ECOORD>( aFrameNode, "y1" );
|
||||||
|
x2 = parseRequiredAttribute<ECOORD>( aFrameNode, "x2" );
|
||||||
|
y2 = parseRequiredAttribute<ECOORD>( aFrameNode, "y2" );
|
||||||
|
columns = parseRequiredAttribute<int>( aFrameNode, "columns" );
|
||||||
|
rows = parseRequiredAttribute<int>( aFrameNode, "rows" );
|
||||||
|
layer = parseRequiredAttribute<int>( aFrameNode, "layer" );
|
||||||
|
border_left = parseOptionalAttribute<bool>( aFrameNode, "border-left" );
|
||||||
|
border_top = parseOptionalAttribute<bool>( aFrameNode, "border-top" );
|
||||||
|
border_right = parseOptionalAttribute<bool>( aFrameNode, "border-right" );
|
||||||
|
border_bottom = parseOptionalAttribute<bool>( aFrameNode, "border-bottom" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
EPAD_COMMON::EPAD_COMMON( wxXmlNode* aPad )
|
EPAD_COMMON::EPAD_COMMON( wxXmlNode* aPad )
|
||||||
{
|
{
|
||||||
// #REQUIRED says DTD, throw exception if not found
|
// #REQUIRED says DTD, throw exception if not found
|
||||||
|
@ -753,7 +796,8 @@ EPOLYGON::EPOLYGON( wxXmlNode* aPolygon )
|
||||||
isolate %Dimension; #IMPLIED -- only in <signal> or <package> context --
|
isolate %Dimension; #IMPLIED -- only in <signal> or <package> context --
|
||||||
orphans %Bool; "no" -- only in <signal> context --
|
orphans %Bool; "no" -- only in <signal> context --
|
||||||
thermals %Bool; "yes" -- only in <signal> context --
|
thermals %Bool; "yes" -- only in <signal> context --
|
||||||
rank %Int; "0" -- 1..6 in <signal> context, 0 or 7 in <package> context --
|
rank %Int; "0" -- 1..6 in <signal> context, 0 or 7 in
|
||||||
|
<package> context --
|
||||||
>
|
>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
||||||
* Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
* Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
* Copyright (C) 2017 CERN
|
* Copyright (C) 2017 CERN
|
||||||
|
*
|
||||||
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -69,9 +70,9 @@ static inline wxXmlNode* getChildrenNodes( NODE_MAP& aMap, const wxString& aName
|
||||||
struct XML_PARSER_ERROR : std::runtime_error
|
struct XML_PARSER_ERROR : std::runtime_error
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructor XML_PARSER_ERROR
|
* Build an XML error by just calling its parent class constructor, std::runtime_error, with
|
||||||
* build an XML error by just calling its parent class constructor, std::runtime_error, with
|
|
||||||
* the passed message.
|
* the passed message.
|
||||||
|
*
|
||||||
* @param aMessage is an explanatory error message.
|
* @param aMessage is an explanatory error message.
|
||||||
*/
|
*/
|
||||||
XML_PARSER_ERROR( const wxString& aMessage ) noexcept :
|
XML_PARSER_ERROR( const wxString& aMessage ) noexcept :
|
||||||
|
@ -669,6 +670,27 @@ struct ETEXT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an Eagle frame element.
|
||||||
|
*/
|
||||||
|
struct EFRAME
|
||||||
|
{
|
||||||
|
ECOORD x1;
|
||||||
|
ECOORD y1;
|
||||||
|
ECOORD x2;
|
||||||
|
ECOORD y2;
|
||||||
|
int columns;
|
||||||
|
int rows;
|
||||||
|
int layer;
|
||||||
|
opt_bool border_left;
|
||||||
|
opt_bool border_top;
|
||||||
|
opt_bool border_right;
|
||||||
|
opt_bool border_bottom;
|
||||||
|
|
||||||
|
EFRAME( wxXmlNode* aFrameNode );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Structure holding common properties for through-hole and SMD pads
|
/// Structure holding common properties for through-hole and SMD pads
|
||||||
struct EPAD_COMMON
|
struct EPAD_COMMON
|
||||||
{
|
{
|
||||||
|
@ -997,11 +1019,11 @@ struct ECONNECT
|
||||||
struct EDEVICE
|
struct EDEVICE
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
<!ELEMENT device (connects?, technologies?)>
|
* <!ELEMENT device (connects?, technologies?)>
|
||||||
<!ATTLIST device
|
* <!ATTLIST device
|
||||||
name %String; ""
|
* name %String; ""
|
||||||
package %String; #IMPLIED
|
* package %String; #IMPLIED
|
||||||
>
|
* >
|
||||||
*/
|
*/
|
||||||
wxString name;
|
wxString name;
|
||||||
opt_wxString package;
|
opt_wxString package;
|
||||||
|
|
|
@ -195,7 +195,6 @@ void SCH_EAGLE_PLUGIN::loadLayerDefs( wxXmlNode* aLayers )
|
||||||
* </layers>
|
* </layers>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
if( elayer.name == "Nets" )
|
if( elayer.name == "Nets" )
|
||||||
{
|
{
|
||||||
m_layerMap[elayer.number] = LAYER_WIRE;
|
m_layerMap[elayer.number] = LAYER_WIRE;
|
||||||
|
@ -515,13 +514,14 @@ void SCH_EAGLE_PLUGIN::countNets( wxXmlNode* aSchematicNode )
|
||||||
{
|
{
|
||||||
// Map all children into a readable dictionary
|
// Map all children into a readable dictionary
|
||||||
NODE_MAP schematicChildren = MapChildren( aSchematicNode );
|
NODE_MAP schematicChildren = MapChildren( aSchematicNode );
|
||||||
// Loop through all the sheets
|
|
||||||
|
|
||||||
|
// Loop through all the sheets
|
||||||
wxXmlNode* sheetNode = getChildrenNodes( schematicChildren, "sheets" );
|
wxXmlNode* sheetNode = getChildrenNodes( schematicChildren, "sheets" );
|
||||||
|
|
||||||
while( sheetNode )
|
while( sheetNode )
|
||||||
{
|
{
|
||||||
NODE_MAP sheetChildren = MapChildren( sheetNode );
|
NODE_MAP sheetChildren = MapChildren( sheetNode );
|
||||||
|
|
||||||
// Loop through all nets
|
// Loop through all nets
|
||||||
// From the DTD: "Net is an electrical connection in a schematic."
|
// From the DTD: "Net is an electrical connection in a schematic."
|
||||||
wxXmlNode* netNode = getChildrenNodes( sheetChildren, "nets" );
|
wxXmlNode* netNode = getChildrenNodes( sheetChildren, "nets" );
|
||||||
|
@ -548,9 +548,9 @@ void SCH_EAGLE_PLUGIN::loadSchematic( wxXmlNode* aSchematicNode )
|
||||||
{
|
{
|
||||||
// Map all children into a readable dictionary
|
// Map all children into a readable dictionary
|
||||||
NODE_MAP schematicChildren = MapChildren( aSchematicNode );
|
NODE_MAP schematicChildren = MapChildren( aSchematicNode );
|
||||||
auto partNode = getChildrenNodes( schematicChildren, "parts" );
|
wxXmlNode* partNode = getChildrenNodes( schematicChildren, "parts" );
|
||||||
auto libraryNode = getChildrenNodes( schematicChildren, "libraries" );
|
wxXmlNode* libraryNode = getChildrenNodes( schematicChildren, "libraries" );
|
||||||
auto sheetNode = getChildrenNodes( schematicChildren, "sheets" );
|
wxXmlNode* sheetNode = getChildrenNodes( schematicChildren, "sheets" );
|
||||||
|
|
||||||
if( !partNode || !libraryNode || !sheetNode )
|
if( !partNode || !libraryNode || !sheetNode )
|
||||||
return;
|
return;
|
||||||
|
@ -630,7 +630,6 @@ void SCH_EAGLE_PLUGIN::loadSchematic( wxXmlNode* aSchematicNode )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Handle the missing component units that need to be instantiated
|
// Handle the missing component units that need to be instantiated
|
||||||
// to create the missing implicit connections
|
// to create the missing implicit connections
|
||||||
|
|
||||||
|
@ -716,6 +715,17 @@ void SCH_EAGLE_PLUGIN::loadSheet( wxXmlNode* aSheetNode, int aSheetIndex )
|
||||||
filenameField.SetText( fn );
|
filenameField.SetText( fn );
|
||||||
wxFileName fileName( fn );
|
wxFileName fileName( fn );
|
||||||
m_currentSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
|
m_currentSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
|
||||||
|
m_currentSheet->AutoplaceFields( m_currentSheet->GetScreen(), true );
|
||||||
|
|
||||||
|
|
||||||
|
// Loop through all of the symbol instances.
|
||||||
|
wxXmlNode* instanceNode = getChildrenNodes( sheetChildren, "instances" );
|
||||||
|
|
||||||
|
while( instanceNode )
|
||||||
|
{
|
||||||
|
loadInstance( instanceNode );
|
||||||
|
instanceNode = instanceNode->GetNext();
|
||||||
|
}
|
||||||
|
|
||||||
// Loop through all buses
|
// Loop through all buses
|
||||||
// From the DTD: "Buses receive names which determine which signals they include.
|
// From the DTD: "Buses receive names which determine which signals they include.
|
||||||
|
@ -755,15 +765,6 @@ void SCH_EAGLE_PLUGIN::loadSheet( wxXmlNode* aSheetNode, int aSheetIndex )
|
||||||
adjustNetLabels(); // needs to be called before addBusEntries()
|
adjustNetLabels(); // needs to be called before addBusEntries()
|
||||||
addBusEntries();
|
addBusEntries();
|
||||||
|
|
||||||
// Loop through all instances
|
|
||||||
wxXmlNode* instanceNode = getChildrenNodes( sheetChildren, "instances" );
|
|
||||||
|
|
||||||
while( instanceNode )
|
|
||||||
{
|
|
||||||
loadInstance( instanceNode );
|
|
||||||
instanceNode = instanceNode->GetNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* moduleinst is a design block definition and is an EagleCad 8 feature,
|
/* moduleinst is a design block definition and is an EagleCad 8 feature,
|
||||||
*
|
*
|
||||||
* // Loop through all moduleinsts
|
* // Loop through all moduleinsts
|
||||||
|
@ -790,6 +791,15 @@ void SCH_EAGLE_PLUGIN::loadSheet( wxXmlNode* aSheetNode, int aSheetIndex )
|
||||||
{
|
{
|
||||||
m_currentSheet->GetScreen()->Append( loadWire( plainNode ) );
|
m_currentSheet->GetScreen()->Append( loadWire( plainNode ) );
|
||||||
}
|
}
|
||||||
|
else if( nodeName == "frame" )
|
||||||
|
{
|
||||||
|
std::vector<SCH_LINE*> lines;
|
||||||
|
|
||||||
|
loadFrame( plainNode, lines );
|
||||||
|
|
||||||
|
for( SCH_LINE* line : lines )
|
||||||
|
m_currentSheet->GetScreen()->Append( line );
|
||||||
|
}
|
||||||
|
|
||||||
plainNode = plainNode->GetNext();
|
plainNode = plainNode->GetNext();
|
||||||
}
|
}
|
||||||
|
@ -845,6 +855,41 @@ void SCH_EAGLE_PLUGIN::loadSheet( wxXmlNode* aSheetNode, int aSheetIndex )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SCH_EAGLE_PLUGIN::loadFrame( wxXmlNode* aFrameNode, std::vector<SCH_LINE*>& aLines )
|
||||||
|
{
|
||||||
|
EFRAME eframe( aFrameNode );
|
||||||
|
|
||||||
|
wxPoint corner1( eframe.x1.ToSchUnits(), -eframe.y1.ToSchUnits() );
|
||||||
|
wxPoint corner3( eframe.x2.ToSchUnits(), -eframe.y2.ToSchUnits() );
|
||||||
|
wxPoint corner2( corner3.x, corner1.y );
|
||||||
|
wxPoint corner4( corner1.x, corner3.y );
|
||||||
|
|
||||||
|
SCH_LINE* line = new SCH_LINE();
|
||||||
|
line->SetLineStyle( PLOT_DASH_TYPE::SOLID );
|
||||||
|
line->SetStartPoint( corner1 );
|
||||||
|
line->SetEndPoint( corner2 );
|
||||||
|
aLines.push_back( line );
|
||||||
|
|
||||||
|
line = new SCH_LINE();
|
||||||
|
line->SetLineStyle( PLOT_DASH_TYPE::SOLID );
|
||||||
|
line->SetStartPoint( corner2 );
|
||||||
|
line->SetEndPoint( corner3 );
|
||||||
|
aLines.push_back( line );
|
||||||
|
|
||||||
|
line = new SCH_LINE();
|
||||||
|
line->SetLineStyle( PLOT_DASH_TYPE::SOLID );
|
||||||
|
line->SetStartPoint( corner3 );
|
||||||
|
line->SetEndPoint( corner4 );
|
||||||
|
aLines.push_back( line );
|
||||||
|
|
||||||
|
line = new SCH_LINE();
|
||||||
|
line->SetLineStyle( PLOT_DASH_TYPE::SOLID );
|
||||||
|
line->SetStartPoint( corner4 );
|
||||||
|
line->SetEndPoint( corner1 );
|
||||||
|
aLines.push_back( line );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SCH_EAGLE_PLUGIN::loadSegments(
|
void SCH_EAGLE_PLUGIN::loadSegments(
|
||||||
wxXmlNode* aSegmentsNode, const wxString& netName, const wxString& aNetClass )
|
wxXmlNode* aSegmentsNode, const wxString& netName, const wxString& aNetClass )
|
||||||
{
|
{
|
||||||
|
@ -937,8 +982,9 @@ void SCH_EAGLE_PLUGIN::loadSegments(
|
||||||
segmentAttribute = segmentAttribute->GetNext();
|
segmentAttribute = segmentAttribute->GetNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a small label to the net segment if it hasn't been labeled already
|
// Add a small label to the net segment if it hasn't been labeled already or is not
|
||||||
// this preserves the named net feature of Eagle schematics.
|
// connect to a power symbol with a pin on the same net. This preserves the named net
|
||||||
|
// feature of Eagle schematics.
|
||||||
if( !labelled && firstWire )
|
if( !labelled && firstWire )
|
||||||
{
|
{
|
||||||
std::unique_ptr<SCH_TEXT> label;
|
std::unique_ptr<SCH_TEXT> label;
|
||||||
|
@ -953,7 +999,7 @@ void SCH_EAGLE_PLUGIN::loadSegments(
|
||||||
{
|
{
|
||||||
label->SetPosition( firstWire->GetStartPoint() );
|
label->SetPosition( firstWire->GetStartPoint() );
|
||||||
label->SetText( escapeName( netName ) );
|
label->SetText( escapeName( netName ) );
|
||||||
label->SetTextSize( wxSize( Mils2iu( 10 ), Mils2iu( 10 ) ) );
|
label->SetTextSize( wxSize( Mils2iu( 40 ), Mils2iu( 40 ) ) );
|
||||||
label->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
|
label->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
|
||||||
screen->Append( label.release() );
|
screen->Append( label.release() );
|
||||||
}
|
}
|
||||||
|
@ -1012,14 +1058,24 @@ SCH_TEXT* SCH_EAGLE_PLUGIN::loadLabel( wxXmlNode* aLabelNode, const wxString& aN
|
||||||
bool global = m_netCounts[aNetName] > 1;
|
bool global = m_netCounts[aNetName] > 1;
|
||||||
std::unique_ptr<SCH_TEXT> label;
|
std::unique_ptr<SCH_TEXT> label;
|
||||||
|
|
||||||
|
wxSize textSize;
|
||||||
|
|
||||||
if( global )
|
if( global )
|
||||||
|
{
|
||||||
label = std::make_unique<SCH_GLOBALLABEL>();
|
label = std::make_unique<SCH_GLOBALLABEL>();
|
||||||
|
textSize = wxSize( KiROUND( elabel.size.ToSchUnits() * 0.75 ),
|
||||||
|
KiROUND( elabel.size.ToSchUnits() * 0.75 ) );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
label = std::make_unique<SCH_LABEL>();
|
label = std::make_unique<SCH_LABEL>();
|
||||||
|
textSize = wxSize( KiROUND( elabel.size.ToSchUnits() * 0.85 ),
|
||||||
|
KiROUND( elabel.size.ToSchUnits() * 0.85 ) );
|
||||||
|
}
|
||||||
|
|
||||||
label->SetPosition( elabelpos );
|
label->SetPosition( elabelpos );
|
||||||
label->SetText( escapeName( elabel.netname ) );
|
label->SetText( escapeName( elabel.netname ) );
|
||||||
label->SetTextSize( wxSize( elabel.size.ToSchUnits(), elabel.size.ToSchUnits() ) );
|
label->SetTextSize( textSize );
|
||||||
label->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
|
label->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
|
||||||
|
|
||||||
if( elabel.rot )
|
if( elabel.rot )
|
||||||
|
@ -1514,6 +1570,18 @@ bool SCH_EAGLE_PLUGIN::loadSymbol( wxXmlNode* aSymbolNode, std::unique_ptr<LIB_P
|
||||||
{
|
{
|
||||||
aPart->AddDrawItem( loadSymbolWire( aPart, currentNode, aGateNumber ) );
|
aPart->AddDrawItem( loadSymbolWire( aPart, currentNode, aGateNumber ) );
|
||||||
}
|
}
|
||||||
|
else if( nodeName == "frame" )
|
||||||
|
{
|
||||||
|
std::vector<LIB_ITEM*> frameItems;
|
||||||
|
|
||||||
|
loadFrame( currentNode, frameItems );
|
||||||
|
|
||||||
|
for( LIB_ITEM* item : frameItems )
|
||||||
|
{
|
||||||
|
item->SetParent( aPart.get() );
|
||||||
|
aPart->AddDrawItem( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* else if( nodeName == "description" )
|
* else if( nodeName == "description" )
|
||||||
|
@ -1522,9 +1590,6 @@ bool SCH_EAGLE_PLUGIN::loadSymbol( wxXmlNode* aSymbolNode, std::unique_ptr<LIB_P
|
||||||
* else if( nodeName == "dimension" )
|
* else if( nodeName == "dimension" )
|
||||||
* {
|
* {
|
||||||
* }
|
* }
|
||||||
* else if( nodeName == "frame" )
|
|
||||||
* {
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
currentNode = currentNode->GetNext();
|
currentNode = currentNode->GetNext();
|
||||||
|
@ -1568,6 +1633,7 @@ LIB_RECTANGLE* SCH_EAGLE_PLUGIN::loadSymbolRectangle(
|
||||||
rectangle->SetEnd( wxPoint( rect.x2.ToSchUnits(), rect.y2.ToSchUnits() ) );
|
rectangle->SetEnd( wxPoint( rect.x2.ToSchUnits(), rect.y2.ToSchUnits() ) );
|
||||||
|
|
||||||
rectangle->SetUnit( aGateNumber );
|
rectangle->SetUnit( aGateNumber );
|
||||||
|
|
||||||
// Eagle rectangles are filled by definition.
|
// Eagle rectangles are filled by definition.
|
||||||
rectangle->SetFillMode( FILL_TYPE::FILLED_SHAPE );
|
rectangle->SetFillMode( FILL_TYPE::FILLED_SHAPE );
|
||||||
|
|
||||||
|
@ -1667,8 +1733,6 @@ LIB_POLYLINE* SCH_EAGLE_PLUGIN::loadSymbolPolyLine(
|
||||||
|
|
||||||
EPOLYGON epoly( aPolygonNode );
|
EPOLYGON epoly( aPolygonNode );
|
||||||
wxXmlNode* vertex = aPolygonNode->GetChildren();
|
wxXmlNode* vertex = aPolygonNode->GetChildren();
|
||||||
|
|
||||||
|
|
||||||
wxPoint pt;
|
wxPoint pt;
|
||||||
|
|
||||||
while( vertex )
|
while( vertex )
|
||||||
|
@ -1723,27 +1787,21 @@ LIB_PIN* SCH_EAGLE_PLUGIN::loadPin(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pin->SetLength( Mils2iu( 300 ) ); // Default pin length when not defined.
|
||||||
|
|
||||||
if( aEPin->length )
|
if( aEPin->length )
|
||||||
{
|
{
|
||||||
wxString length = aEPin->length.Get();
|
wxString length = aEPin->length.Get();
|
||||||
|
|
||||||
if( length == "short" )
|
if( length == "short" )
|
||||||
{
|
|
||||||
pin->SetLength( Mils2iu( 100 ) );
|
pin->SetLength( Mils2iu( 100 ) );
|
||||||
}
|
|
||||||
else if( length == "middle" )
|
else if( length == "middle" )
|
||||||
{
|
|
||||||
pin->SetLength( Mils2iu( 200 ) );
|
pin->SetLength( Mils2iu( 200 ) );
|
||||||
}
|
|
||||||
else if( length == "long" )
|
else if( length == "long" )
|
||||||
{
|
|
||||||
pin->SetLength( Mils2iu( 300 ) );
|
pin->SetLength( Mils2iu( 300 ) );
|
||||||
}
|
|
||||||
else if( length == "point" )
|
else if( length == "point" )
|
||||||
{
|
|
||||||
pin->SetLength( Mils2iu( 0 ) );
|
pin->SetLength( Mils2iu( 0 ) );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// emulate the visibility of pin elements
|
// emulate the visibility of pin elements
|
||||||
if( aEPin->visible )
|
if( aEPin->visible )
|
||||||
|
@ -1818,13 +1876,49 @@ LIB_TEXT* SCH_EAGLE_PLUGIN::loadSymbolText(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SCH_EAGLE_PLUGIN::loadFrame( wxXmlNode* aFrameNode, std::vector<LIB_ITEM*>& aItems )
|
||||||
|
{
|
||||||
|
EFRAME eframe( aFrameNode );
|
||||||
|
|
||||||
|
wxPoint corner1( eframe.x1.ToSchUnits(), eframe.y1.ToSchUnits() );
|
||||||
|
wxPoint corner3( eframe.x2.ToSchUnits(), eframe.y2.ToSchUnits() );
|
||||||
|
wxPoint corner2( corner3.x, corner1.y );
|
||||||
|
wxPoint corner4( corner1.x, corner3.y );
|
||||||
|
|
||||||
|
LIB_POLYLINE* lines = new LIB_POLYLINE( nullptr );
|
||||||
|
lines->AddPoint( corner1 );
|
||||||
|
lines->AddPoint( corner2 );
|
||||||
|
lines->AddPoint( corner3 );
|
||||||
|
lines->AddPoint( corner4 );
|
||||||
|
lines->AddPoint( corner1 );
|
||||||
|
aItems.push_back( lines );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SCH_TEXT* SCH_EAGLE_PLUGIN::loadPlainText( wxXmlNode* aSchText )
|
SCH_TEXT* SCH_EAGLE_PLUGIN::loadPlainText( wxXmlNode* aSchText )
|
||||||
{
|
{
|
||||||
std::unique_ptr<SCH_TEXT> schtext = std::make_unique<SCH_TEXT>();
|
std::unique_ptr<SCH_TEXT> schtext = std::make_unique<SCH_TEXT>();
|
||||||
ETEXT etext = ETEXT( aSchText );
|
ETEXT etext = ETEXT( aSchText );
|
||||||
|
|
||||||
const wxString& thetext = aSchText->GetNodeContent();
|
const wxString& thetext = aSchText->GetNodeContent();
|
||||||
schtext->SetText( thetext.IsEmpty() ? "\" \"" : escapeName( thetext ) );
|
|
||||||
|
wxString adjustedText;
|
||||||
|
wxStringTokenizer tokenizer( thetext, "\r\n" );
|
||||||
|
|
||||||
|
// Strip the whitespace from both ends of each line.
|
||||||
|
while( tokenizer.HasMoreTokens() )
|
||||||
|
{
|
||||||
|
wxString tmp = tokenizer.GetNextToken().Trim();
|
||||||
|
|
||||||
|
tmp = tmp.Trim( false );
|
||||||
|
|
||||||
|
if( tokenizer.HasMoreTokens() )
|
||||||
|
tmp += wxT( "\n" );
|
||||||
|
|
||||||
|
adjustedText += tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
schtext->SetText( adjustedText.IsEmpty() ? "\" \"" : escapeName( adjustedText ) );
|
||||||
schtext->SetPosition( wxPoint( etext.x.ToSchUnits(), -etext.y.ToSchUnits() ) );
|
schtext->SetPosition( wxPoint( etext.x.ToSchUnits(), -etext.y.ToSchUnits() ) );
|
||||||
loadTextAttributes( schtext.get(), etext );
|
loadTextAttributes( schtext.get(), etext );
|
||||||
schtext->SetItalic( false );
|
schtext->SetItalic( false );
|
||||||
|
@ -1876,8 +1970,11 @@ void SCH_EAGLE_PLUGIN::adjustNetLabels()
|
||||||
// Sort the intersection points to speed up the search process
|
// Sort the intersection points to speed up the search process
|
||||||
std::sort( m_wireIntersections.begin(), m_wireIntersections.end() );
|
std::sort( m_wireIntersections.begin(), m_wireIntersections.end() );
|
||||||
|
|
||||||
auto onIntersection = [&]( const VECTOR2I& aPos ) {
|
auto onIntersection =
|
||||||
return std::binary_search( m_wireIntersections.begin(), m_wireIntersections.end(), aPos );
|
[&]( const VECTOR2I& aPos )
|
||||||
|
{
|
||||||
|
return std::binary_search( m_wireIntersections.begin(),
|
||||||
|
m_wireIntersections.end(), aPos );
|
||||||
};
|
};
|
||||||
|
|
||||||
for( auto& segDesc : m_segments )
|
for( auto& segDesc : m_segments )
|
||||||
|
@ -1948,6 +2045,7 @@ bool SCH_EAGLE_PLUGIN::CheckHeader( const wxString& aFileName )
|
||||||
|
|
||||||
tempFile.Open( aFileName );
|
tempFile.Open( aFileName );
|
||||||
wxString firstline;
|
wxString firstline;
|
||||||
|
|
||||||
// read the first line
|
// read the first line
|
||||||
firstline = tempFile.GetFirstLine();
|
firstline = tempFile.GetFirstLine();
|
||||||
wxString secondline = tempFile.GetNextLine();
|
wxString secondline = tempFile.GetNextLine();
|
||||||
|
@ -1992,12 +2090,12 @@ void SCH_EAGLE_PLUGIN::addBusEntries()
|
||||||
if( bus->GetLayer() != LAYER_BUS )
|
if( bus->GetLayer() != LAYER_BUS )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
wxPoint busstart = bus->GetStartPoint();
|
wxPoint busstart = bus->GetStartPoint();
|
||||||
wxPoint busend = bus->GetEndPoint();
|
wxPoint busend = bus->GetEndPoint();
|
||||||
|
|
||||||
auto it2 = it1;
|
auto it2 = it1;
|
||||||
++it2;
|
++it2;
|
||||||
|
|
||||||
for( ; it2 != m_currentSheet->GetScreen()->Items().end(); ++it2 )
|
for( ; it2 != m_currentSheet->GetScreen()->Items().end(); ++it2 )
|
||||||
{
|
{
|
||||||
SCH_LINE* line = static_cast<SCH_LINE*>( *it2 );
|
SCH_LINE* line = static_cast<SCH_LINE*>( *it2 );
|
||||||
|
@ -2117,7 +2215,8 @@ void SCH_EAGLE_PLUGIN::addBusEntries()
|
||||||
lineend + wxPoint( 0, -100 ), busstart, busend, 0 ) )
|
lineend + wxPoint( 0, -100 ), busstart, busend, 0 ) )
|
||||||
{
|
{
|
||||||
SCH_BUS_WIRE_ENTRY* busEntry =
|
SCH_BUS_WIRE_ENTRY* busEntry =
|
||||||
new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( -100, 0 ), true );
|
new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( -100, 0 ),
|
||||||
|
true );
|
||||||
busEntry->SetFlags( IS_NEW );
|
busEntry->SetFlags( IS_NEW );
|
||||||
m_currentSheet->GetScreen()->Append( busEntry );
|
m_currentSheet->GetScreen()->Append( busEntry );
|
||||||
moveLabels( line, lineend + wxPoint( -100, 0 ) );
|
moveLabels( line, lineend + wxPoint( -100, 0 ) );
|
||||||
|
@ -2257,7 +2356,8 @@ void SCH_EAGLE_PLUGIN::addBusEntries()
|
||||||
lineend + wxPoint( -100, 0 ), busstart, busend, 0 ) )
|
lineend + wxPoint( -100, 0 ), busstart, busend, 0 ) )
|
||||||
{
|
{
|
||||||
SCH_BUS_WIRE_ENTRY* busEntry =
|
SCH_BUS_WIRE_ENTRY* busEntry =
|
||||||
new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( -100, 0 ), true );
|
new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( -100, 0 ),
|
||||||
|
true );
|
||||||
busEntry->SetFlags( IS_NEW );
|
busEntry->SetFlags( IS_NEW );
|
||||||
m_currentSheet->GetScreen()->Append( busEntry );
|
m_currentSheet->GetScreen()->Append( busEntry );
|
||||||
moveLabels( line, lineend + wxPoint( 0, -100 ) );
|
moveLabels( line, lineend + wxPoint( 0, -100 ) );
|
||||||
|
@ -2371,7 +2471,8 @@ void SCH_EAGLE_PLUGIN::addBusEntries()
|
||||||
{
|
{
|
||||||
if( wirevector.y > 0 )
|
if( wirevector.y > 0 )
|
||||||
{
|
{
|
||||||
SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( linestart, true );
|
SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( linestart,
|
||||||
|
true );
|
||||||
busEntry->SetFlags( IS_NEW );
|
busEntry->SetFlags( IS_NEW );
|
||||||
m_currentSheet->GetScreen()->Append( busEntry );
|
m_currentSheet->GetScreen()->Append( busEntry );
|
||||||
|
|
||||||
|
@ -2552,18 +2653,20 @@ void SCH_EAGLE_PLUGIN::addImplicitConnections(
|
||||||
bool pinInUnit = !unit || pin->GetUnit() == unit; // pin belongs to the tested unit
|
bool pinInUnit = !unit || pin->GetUnit() == unit; // pin belongs to the tested unit
|
||||||
|
|
||||||
// Create a global net label only if there are no other wires/pins attached
|
// Create a global net label only if there are no other wires/pins attached
|
||||||
if( pinInUnit && !checkConnections( aComponent, pin ) )
|
if( pinInUnit )
|
||||||
|
{
|
||||||
|
if( !checkConnections( aComponent, pin ) )
|
||||||
{
|
{
|
||||||
// Create a net label to force the net name on the pin
|
// Create a net label to force the net name on the pin
|
||||||
SCH_GLOBALLABEL* netLabel = new SCH_GLOBALLABEL;
|
SCH_GLOBALLABEL* netLabel = new SCH_GLOBALLABEL;
|
||||||
netLabel->SetPosition( aComponent->GetPinPhysicalPosition( pin ) );
|
netLabel->SetPosition( aComponent->GetPinPhysicalPosition( pin ) );
|
||||||
netLabel->SetText( extractNetName( pin->GetName() ) );
|
netLabel->SetText( extractNetName( pin->GetName() ) );
|
||||||
netLabel->SetTextSize( wxSize( Mils2iu( 10 ), Mils2iu( 10 ) ) );
|
netLabel->SetTextSize( wxSize( Mils2iu( 40 ), Mils2iu( 40 ) ) );
|
||||||
netLabel->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
|
netLabel->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
|
||||||
aScreen->Append( netLabel );
|
aScreen->Append( netLabel );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if( !pinInUnit && aUpdateSet )
|
else if( aUpdateSet )
|
||||||
{
|
{
|
||||||
// Found a pin creating implicit connection information in another unit.
|
// Found a pin creating implicit connection information in another unit.
|
||||||
// Such units will be instantiated if they do not appear in another sheet and
|
// Such units will be instantiated if they do not appear in another sheet and
|
||||||
|
@ -2574,25 +2677,37 @@ void SCH_EAGLE_PLUGIN::addImplicitConnections(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( aUpdateSet )
|
if( aUpdateSet && aComponent->GetPartRef()->GetUnitCount() > 1 )
|
||||||
{
|
{
|
||||||
auto cmpIt = m_missingCmps.find( reference );
|
auto cmpIt = m_missingCmps.find( reference );
|
||||||
|
|
||||||
// Set the flag indicating this unit has been processed
|
// The first unit found has always already been processed.
|
||||||
if( cmpIt != m_missingCmps.end() )
|
if( cmpIt == m_missingCmps.end() )
|
||||||
|
{
|
||||||
|
EAGLE_MISSING_CMP& entry = m_missingCmps[reference];
|
||||||
|
entry.cmp = aComponent;
|
||||||
|
entry.units.emplace( unit, false );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set the flag indicating this unit has been processed.
|
||||||
cmpIt->second.units[unit] = false;
|
cmpIt->second.units[unit] = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Save the units that need later processing
|
if( !missingUnits.empty() ) // Save the units that need later processing
|
||||||
else if( !missingUnits.empty() )
|
|
||||||
{
|
{
|
||||||
EAGLE_MISSING_CMP& entry = m_missingCmps[reference];
|
EAGLE_MISSING_CMP& entry = m_missingCmps[reference];
|
||||||
entry.cmp = aComponent;
|
entry.cmp = aComponent;
|
||||||
|
|
||||||
|
// Add units that haven't already been processed.
|
||||||
for( int i : missingUnits )
|
for( int i : missingUnits )
|
||||||
|
{
|
||||||
|
if( entry.units.find( i ) != entry.units.end() )
|
||||||
entry.units.emplace( i, true );
|
entry.units.emplace( i, true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString SCH_EAGLE_PLUGIN::fixSymbolName( const wxString& aName )
|
wxString SCH_EAGLE_PLUGIN::fixSymbolName( const wxString& aName )
|
||||||
|
|
|
@ -127,6 +127,7 @@ private:
|
||||||
SCH_TEXT* loadLabel( wxXmlNode* aLabelNode, const wxString& aNetName );
|
SCH_TEXT* loadLabel( wxXmlNode* aLabelNode, const wxString& aNetName );
|
||||||
SCH_JUNCTION* loadJunction( wxXmlNode* aJunction );
|
SCH_JUNCTION* loadJunction( wxXmlNode* aJunction );
|
||||||
SCH_TEXT* loadPlainText( wxXmlNode* aSchText );
|
SCH_TEXT* loadPlainText( wxXmlNode* aSchText );
|
||||||
|
void loadFrame( wxXmlNode* aFrameNode, std::vector<SCH_LINE*>& aLines );
|
||||||
|
|
||||||
bool loadSymbol( wxXmlNode* aSymbolNode, std::unique_ptr<LIB_PART>& aPart,
|
bool loadSymbol( wxXmlNode* aSymbolNode, std::unique_ptr<LIB_PART>& aPart,
|
||||||
EDEVICE* aDevice, int aGateNumber, const wxString& aGateName );
|
EDEVICE* aDevice, int aGateNumber, const wxString& aGateName );
|
||||||
|
@ -142,6 +143,7 @@ private:
|
||||||
int aGateNumber );
|
int aGateNumber );
|
||||||
LIB_TEXT* loadSymbolText( std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aLibText,
|
LIB_TEXT* loadSymbolText( std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aLibText,
|
||||||
int aGateNumber );
|
int aGateNumber );
|
||||||
|
void loadFrame( wxXmlNode* aFrameNode, std::vector<LIB_ITEM*>& aLines );
|
||||||
|
|
||||||
void loadTextAttributes( EDA_TEXT* aText, const ETEXT& aAttribs ) const;
|
void loadTextAttributes( EDA_TEXT* aText, const ETEXT& aAttribs ) const;
|
||||||
void loadFieldAttributes( LIB_FIELD* aField, const LIB_TEXT* aText ) const;
|
void loadFieldAttributes( LIB_FIELD* aField, const LIB_TEXT* aText ) const;
|
||||||
|
@ -179,6 +181,8 @@ private:
|
||||||
*/
|
*/
|
||||||
void addImplicitConnections( SCH_COMPONENT* aComponent, SCH_SCREEN* aScreen, bool aUpdateSet );
|
void addImplicitConnections( SCH_COMPONENT* aComponent, SCH_SCREEN* aScreen, bool aUpdateSet );
|
||||||
|
|
||||||
|
bool netHasPowerDriver( SCH_LINE* aLine, const wxString& aNetName ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix invalid characters in Eagle symbol names.
|
* Fix invalid characters in Eagle symbol names.
|
||||||
*
|
*
|
||||||
|
@ -244,6 +248,9 @@ private:
|
||||||
///< Segments representing wires for intersection checking
|
///< Segments representing wires for intersection checking
|
||||||
std::vector<SEG_DESC> m_segments;
|
std::vector<SEG_DESC> m_segments;
|
||||||
|
|
||||||
|
///< Nets as defined in the <nets> sections of an Eagle schematic file.
|
||||||
|
std::map<wxString, ENET> m_nets;
|
||||||
|
|
||||||
///< Positions of pins and wire endings mapped to its parent
|
///< Positions of pins and wire endings mapped to its parent
|
||||||
std::map<wxPoint, std::set<const EDA_ITEM*>> m_connPoints;
|
std::map<wxPoint, std::set<const EDA_ITEM*>> m_connPoints;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue