Compare commits
135 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 567c5bc8db | |||
| 1fdf7d0cf4 | |||
| 5ffe28eec2 | |||
| 4e855b4cb0 | |||
| ae886c824c | |||
| 46b5442214 | |||
| c3deafd271 | |||
| de2da52941 | |||
| 8aa3d183ef | |||
| 6623b19b70 | |||
| 7a40b6e484 | |||
| d63dc32e71 | |||
| 54aa3bc5d4 | |||
| 6ebd616049 | |||
| c0439feef8 | |||
| 0e550322db | |||
| 7398d47728 | |||
| 9f0f7e8641 | |||
| f26e36440d | |||
| 8ff0aca6b4 | |||
| d4b564ee7a | |||
| 22a6bf999f | |||
| 09bae9bb14 | |||
| 874f7fbdff | |||
| 6d17d7d7a1 | |||
| 98dc21360a | |||
| 0d330051ed | |||
| f17c4cceac | |||
| a3072ffade | |||
| f60c544478 | |||
| fc320e0ab4 | |||
| 91c861e8d6 | |||
| 2640f4ad41 | |||
| 06f5828990 | |||
| 85604e1707 | |||
| ad7bc215d0 | |||
| f005676c90 | |||
| 17961165cf | |||
| 6ba2230804 | |||
| cacfaac52e | |||
| 4f09e18894 | |||
| 05f1912d19 | |||
| e15306464f | |||
| 28f75d5ad3 | |||
| 3021764401 | |||
| 2766e90fe0 | |||
| 10d5baa862 | |||
| 28a8997603 | |||
| 83d4771e6b | |||
| 0f4cd449b9 | |||
| 27843728d7 | |||
| 676ad491fe | |||
| 9a91537968 | |||
| 3eba0d9bab | |||
| b4995aa711 | |||
| 55353536e5 | |||
| e3d45fb37b | |||
| e3369608fd | |||
| 1c6390f829 | |||
| df64a6c583 | |||
| 59c638c64c | |||
| 9850541433 | |||
| 5140fd7819 | |||
| 5ae1b271e7 | |||
| c5cdff6213 | |||
| e05b3955a5 | |||
| e1c0bddff3 | |||
| 62297e63a2 | |||
| 54c325a631 | |||
| af99c9da34 | |||
| dbf5455656 | |||
| 4cb9edd9f0 | |||
| 60d23b14e7 | |||
| 12fdeac31c | |||
| 69a1a4611b | |||
| a46501b279 | |||
| bc1953dded | |||
| 789a695737 | |||
| 5f8a0b0728 | |||
| 9fdda223f5 | |||
| 1ff4b2b4eb | |||
| 0751a3ae7f | |||
| e6e08c111b | |||
| 004b96ec21 | |||
| 7cb98f6856 | |||
| fc86de34f8 | |||
| fe69da4cb8 | |||
| 27434e38f9 | |||
| 60ddafa4ff | |||
| ef8b6bf9e1 | |||
| 59cc7b2d3e | |||
| 6245bbff50 | |||
| 6331a0ab7a | |||
| c43ae314e0 | |||
| 250720ca4a | |||
| a5ac1fc34f | |||
| 1740aeca5e | |||
| 4f060d9384 | |||
| acd244f873 | |||
| dfef20658c | |||
| 8255e091f0 | |||
| 96037591c5 | |||
| 692949fd0a | |||
| 4bf75de6fd | |||
| 05890480e2 | |||
| 70d7bc2099 | |||
| e2d8d53a10 | |||
| 402c139957 | |||
| 31e79ef54f | |||
| 39788e7e6c | |||
| 932c908075 | |||
| 443da9943a | |||
| cc1071fe7a | |||
| 3ac4fd59c5 | |||
| 5e93814283 | |||
| d29c07a663 | |||
| a558a67fbd | |||
| 153e1190ea | |||
| a80a641427 | |||
| acd7d5ad2f | |||
| 81ba4fa9a6 | |||
| 57f007c46e | |||
| d513640572 | |||
| 8c695e5044 | |||
| 395a966802 | |||
| 643894c882 | |||
| c7c28c0471 | |||
| 20ada81d43 | |||
| 07736e59bd | |||
| 3195ba646f | |||
| 2deee01592 | |||
| 890b09499e | |||
| 01c5fab84b | |||
| e9b7737692 | |||
| fe1072a06b |
@@ -49,6 +49,7 @@
|
||||
#include <dialogs/dialog_color_picker.h>
|
||||
|
||||
class COLOR_SETTINGS;
|
||||
class PCB_TEXTBOX;
|
||||
|
||||
/// A type that stores a container of 2d objects for each layer id
|
||||
typedef std::map<PCB_LAYER_ID, BVH_CONTAINER_2D*> MAP_CONTAINER_2D_BASE;
|
||||
@@ -356,6 +357,9 @@ private:
|
||||
void addShape( const PCB_DIMENSION_BASE* aDimension, CONTAINER_2D_BASE* aDstContainer,
|
||||
const BOARD_ITEM* aOwner );
|
||||
|
||||
void addShape( const PCB_TEXTBOX* aTextBox, CONTAINER_2D_BASE* aContainer,
|
||||
const BOARD_ITEM* aOwner );
|
||||
|
||||
void addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aDstContainer,
|
||||
PCB_LAYER_ID aLayerId );
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <pcb_painter.h> // for PCB_RENDER_SETTINGS
|
||||
#include <zone.h>
|
||||
#include <fp_text.h>
|
||||
#include <pcb_textbox.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <trigo.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
@@ -720,6 +721,11 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta
|
||||
aShape->TransformShapeToPolygon( polyList, UNDEFINED_LAYER, 0, ARC_HIGH_DEF,
|
||||
ERROR_INSIDE );
|
||||
|
||||
// Some polygons can be a bit complex (especially when coming from a
|
||||
// picture ot a text converted to a polygon
|
||||
// So call Simplify before calling ConvertPolygonToTriangles, just in case.
|
||||
polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
if( polyList.IsEmpty() ) // Just for caution
|
||||
break;
|
||||
|
||||
@@ -767,6 +773,32 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addShape( const PCB_TEXTBOX* aTextBox, CONTAINER_2D_BASE* aContainer,
|
||||
const BOARD_ITEM* aOwner )
|
||||
{
|
||||
addText( aTextBox, aContainer, aOwner );
|
||||
|
||||
if( !aTextBox->IsBorderEnabled() )
|
||||
return;
|
||||
|
||||
// We cannot use PCB_TEXTBOX::TransformShapeToPolygon because it convert the textbox
|
||||
// as filled polygon even if there's no background colour.
|
||||
// So for polygon, we use PCB_SHAPE::TransformShapeToPolygon
|
||||
|
||||
if( aTextBox->GetShape() == SHAPE_T::RECT )
|
||||
addShape( static_cast<const PCB_SHAPE*>( aTextBox ), aContainer, aOwner );
|
||||
else
|
||||
{
|
||||
SHAPE_POLY_SET polyList;
|
||||
|
||||
aTextBox->PCB_SHAPE::TransformShapeToPolygon( polyList, UNDEFINED_LAYER, 0,
|
||||
ARC_HIGH_DEF, ERROR_INSIDE );
|
||||
|
||||
ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aOwner );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aContainer,
|
||||
PCB_LAYER_ID aLayerId )
|
||||
{
|
||||
|
||||
@@ -604,9 +604,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
break;
|
||||
|
||||
case PCB_TEXTBOX_T:
|
||||
addText( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
|
||||
if( static_cast<PCB_TEXTBOX*>( item )->IsBorderEnabled() )
|
||||
addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
|
||||
addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
|
||||
break;
|
||||
|
||||
case PCB_DIM_ALIGNED_T:
|
||||
@@ -924,7 +922,6 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
break;
|
||||
|
||||
case PCB_TEXTBOX_T:
|
||||
addText( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
|
||||
addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
|
||||
break;
|
||||
|
||||
|
||||
@@ -637,6 +637,9 @@ void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
|
||||
|
||||
void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
|
||||
{
|
||||
if( m_3d_render && m_3d_render->IsReloadRequestPending() )
|
||||
return; // Prevents using invalid m_3d_render_raytracing data
|
||||
|
||||
if( m_camera_is_moving )
|
||||
return;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
# KiCad.
|
||||
#
|
||||
# Note: This version string should follow the semantic versioning system
|
||||
set( KICAD_SEMANTIC_VERSION "7.0.9" )
|
||||
set( KICAD_SEMANTIC_VERSION "7.0.10" )
|
||||
|
||||
# Default the version to the semantic version.
|
||||
# This is overridden by the git repository tag though (if using git)
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
/// Allows scripts install directory to be referenced by the program code.
|
||||
#define PYTHON_DEST "@PYTHON_DEST@"
|
||||
|
||||
#define PYTHON_SITE_PACKAGE_PATH "@PYTHON_SITE_PACKAGE_PATH@"
|
||||
|
||||
/// Allows scripts install directory to be referenced by the program code.
|
||||
#define KICAD_DATA "@KICAD_DATA@"
|
||||
|
||||
|
||||
@@ -444,7 +444,16 @@ bool DATABASE_CONNECTION::SelectOne( const std::string& aTable,
|
||||
case SQL_DECIMAL:
|
||||
case SQL_NUMERIC:
|
||||
{
|
||||
aResult[column] = fmt::format( "{:G}", results.get<double>( i ) );
|
||||
try
|
||||
{
|
||||
aResult[column] = fmt::format( "{:G}", results.get<double>( i ) );
|
||||
}
|
||||
catch( nanodbc::null_access_error& e )
|
||||
{
|
||||
// Column was empty (null)
|
||||
aResult[column] = std::string();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -540,43 +549,92 @@ bool DATABASE_CONNECTION::SelectAll( const std::string& aTable, const std::strin
|
||||
|
||||
timer.Stop();
|
||||
|
||||
try
|
||||
{
|
||||
while( results.next() )
|
||||
{
|
||||
ROW result;
|
||||
|
||||
for( short j = 0; j < results.columns(); ++j )
|
||||
auto handleException =
|
||||
[&]( std::runtime_error& aException, const std::string& aExtraContext = "" )
|
||||
{
|
||||
std::string column = toUTF8( results.column_name( j ) );
|
||||
m_lastError = aException.what();
|
||||
std::string extra = aExtraContext.empty() ? "" : ": " + aExtraContext;
|
||||
wxLogTrace( traceDatabase,
|
||||
wxT( "Exception while parsing result %d from SelectAll: %s%s" ),
|
||||
aResults.size(), m_lastError, extra );
|
||||
};
|
||||
|
||||
switch( results.column_datatype( j ) )
|
||||
{
|
||||
case SQL_DOUBLE:
|
||||
case SQL_FLOAT:
|
||||
case SQL_REAL:
|
||||
case SQL_DECIMAL:
|
||||
case SQL_NUMERIC:
|
||||
while( results.next() )
|
||||
{
|
||||
short columnCount = 0;
|
||||
ROW result;
|
||||
|
||||
try
|
||||
{
|
||||
columnCount = results.columns();
|
||||
}
|
||||
catch( nanodbc::database_error& e )
|
||||
{
|
||||
handleException( e );
|
||||
return false;
|
||||
}
|
||||
|
||||
for( short j = 0; j < columnCount; ++j )
|
||||
{
|
||||
std::string column;
|
||||
std::string columnExtraDbgInfo;
|
||||
int datatype = SQL_UNKNOWN_TYPE;
|
||||
|
||||
try
|
||||
{
|
||||
column = toUTF8( results.column_name( j ) );
|
||||
datatype = results.column_datatype( j );
|
||||
columnExtraDbgInfo = fmt::format( "column index {}, name '{}', type {}", j, column,
|
||||
datatype );
|
||||
}
|
||||
catch( nanodbc::index_range_error& e )
|
||||
{
|
||||
handleException( e, columnExtraDbgInfo );
|
||||
return false;
|
||||
}
|
||||
|
||||
switch( datatype )
|
||||
{
|
||||
case SQL_DOUBLE:
|
||||
case SQL_FLOAT:
|
||||
case SQL_REAL:
|
||||
case SQL_DECIMAL:
|
||||
case SQL_NUMERIC:
|
||||
{
|
||||
try
|
||||
{
|
||||
result[column] = fmt::format( "{:G}", results.get<double>( j ) );
|
||||
break;
|
||||
}
|
||||
catch( nanodbc::null_access_error& e )
|
||||
{
|
||||
// Column was empty (null)
|
||||
result[column] = std::string();
|
||||
}
|
||||
catch( std::runtime_error& e )
|
||||
{
|
||||
handleException( e, columnExtraDbgInfo );
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
default:
|
||||
{
|
||||
try
|
||||
{
|
||||
result[column] = toUTF8( results.get<nanodbc::string>( j,
|
||||
NANODBC_TEXT( "" ) ) );
|
||||
}
|
||||
catch( std::runtime_error& e )
|
||||
{
|
||||
handleException( e, columnExtraDbgInfo );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aResults.emplace_back( std::move( result ) );
|
||||
}
|
||||
}
|
||||
catch( nanodbc::database_error& e )
|
||||
{
|
||||
m_lastError = e.what();
|
||||
wxLogTrace( traceDatabase, wxT( "Exception while parsing results from SelectAll: %s" ),
|
||||
m_lastError );
|
||||
return false;
|
||||
|
||||
aResults.emplace_back( std::move( result ) );
|
||||
}
|
||||
|
||||
wxLogTrace( traceDatabase, wxT( "SelectAll from %s completed in %0.1f ms" ), aTable,
|
||||
|
||||
@@ -288,6 +288,14 @@ bool DIALOG_SHIM::Show( bool show )
|
||||
std::max( wxDialog::GetSize().y, savedDialogRect.GetSize().y ),
|
||||
0 );
|
||||
}
|
||||
#ifdef __WXMAC__
|
||||
if( m_parent != nullptr )
|
||||
{
|
||||
if( wxDisplay::GetFromPoint( m_parent->GetPosition() )
|
||||
!= wxDisplay::GetFromPoint( savedDialogRect.GetPosition() ) )
|
||||
Centre();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if( m_initialSize != wxDefaultSize )
|
||||
SetSize( m_initialSize );
|
||||
|
||||
@@ -29,7 +29,7 @@ static PASTE_MODE g_paste_mode = PASTE_MODE::UNIQUE_ANNOTATIONS;
|
||||
|
||||
DIALOG_PASTE_SPECIAL::DIALOG_PASTE_SPECIAL( wxWindow* aParent,
|
||||
PASTE_MODE* aMode,
|
||||
wxString aReplacement ) :
|
||||
const wxString& aReplacement ) :
|
||||
DIALOG_PASTE_SPECIAL_BASE( aParent ),
|
||||
m_mode( aMode )
|
||||
{
|
||||
@@ -45,7 +45,7 @@ DIALOG_PASTE_SPECIAL::DIALOG_PASTE_SPECIAL( wxWindow* aParent,
|
||||
"with '%s'." ),
|
||||
aReplacement ) );
|
||||
|
||||
m_sdbSizerOK->SetFocus();
|
||||
m_pasteOptions->SetFocus();
|
||||
|
||||
// Now all widgets have the size fixed, call FinishDialogSettings
|
||||
finishDialogSettings();
|
||||
@@ -64,3 +64,11 @@ bool DIALOG_PASTE_SPECIAL::TransferDataFromWindow()
|
||||
g_paste_mode = *m_mode = static_cast<PASTE_MODE>( m_pasteOptions->GetSelection() );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_PASTE_SPECIAL::onRadioBoxEvent( wxCommandEvent& event )
|
||||
{
|
||||
event.Skip();
|
||||
|
||||
m_sdbSizerOK->SetFocus();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
|
||||
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
@@ -45,12 +45,12 @@ DIALOG_PASTE_SPECIAL_BASE::DIALOG_PASTE_SPECIAL_BASE( wxWindow* parent, wxWindow
|
||||
this->Centre( wxBOTH );
|
||||
|
||||
// Connect Events
|
||||
m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PASTE_SPECIAL_BASE::onOKButton ), NULL, this );
|
||||
m_pasteOptions->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_PASTE_SPECIAL_BASE::onRadioBoxEvent ), NULL, this );
|
||||
}
|
||||
|
||||
DIALOG_PASTE_SPECIAL_BASE::~DIALOG_PASTE_SPECIAL_BASE()
|
||||
{
|
||||
// Disconnect Events
|
||||
m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PASTE_SPECIAL_BASE::onOKButton ), NULL, this );
|
||||
m_pasteOptions->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_PASTE_SPECIAL_BASE::onRadioBoxEvent ), NULL, this );
|
||||
|
||||
}
|
||||
|
||||
@@ -1,163 +1,165 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<wxFormBuilder_Project>
|
||||
<FileVersion major="1" minor="16" />
|
||||
<object class="Project" expanded="1">
|
||||
<property name="class_decoration"></property>
|
||||
<property name="code_generation">C++</property>
|
||||
<property name="disconnect_events">1</property>
|
||||
<property name="disconnect_mode">source_name</property>
|
||||
<property name="disconnect_php_events">0</property>
|
||||
<property name="disconnect_python_events">0</property>
|
||||
<property name="embedded_files_path">res</property>
|
||||
<property name="encoding">UTF-8</property>
|
||||
<property name="event_generation">connect</property>
|
||||
<property name="file">dialog_paste_special_base</property>
|
||||
<property name="first_id">1000</property>
|
||||
<property name="help_provider">none</property>
|
||||
<property name="image_path_wrapper_function_name"></property>
|
||||
<property name="indent_with_spaces"></property>
|
||||
<property name="internationalize">1</property>
|
||||
<property name="name">dialog_paste_special</property>
|
||||
<property name="namespace"></property>
|
||||
<property name="path">.</property>
|
||||
<property name="precompiled_header"></property>
|
||||
<property name="relative_path">1</property>
|
||||
<property name="skip_lua_events">1</property>
|
||||
<property name="skip_php_events">1</property>
|
||||
<property name="skip_python_events">1</property>
|
||||
<property name="ui_table">UI</property>
|
||||
<property name="use_array_enum">0</property>
|
||||
<property name="use_enum">0</property>
|
||||
<property name="use_microsoft_bom">0</property>
|
||||
<object class="Dialog" expanded="1">
|
||||
<property name="aui_managed">0</property>
|
||||
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
|
||||
<property name="bg"></property>
|
||||
<property name="center">wxBOTH</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="event_handler">impl_virtual</property>
|
||||
<property name="extra_style"></property>
|
||||
<property name="fg"></property>
|
||||
<property name="font"></property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="minimum_size">-1,-1</property>
|
||||
<property name="name">DIALOG_PASTE_SPECIAL_BASE</property>
|
||||
<property name="pos"></property>
|
||||
<property name="size">-1,-1</property>
|
||||
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
|
||||
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
|
||||
<property name="title">Paste Special</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="two_step_creation">0</property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<FileVersion major="1" minor="17"/>
|
||||
<object class="Project" expanded="true">
|
||||
<property name="class_decoration"></property>
|
||||
<property name="code_generation">C++</property>
|
||||
<property name="disconnect_events">1</property>
|
||||
<property name="disconnect_mode">source_name</property>
|
||||
<property name="disconnect_php_events">0</property>
|
||||
<property name="disconnect_python_events">0</property>
|
||||
<property name="embedded_files_path">res</property>
|
||||
<property name="encoding">UTF-8</property>
|
||||
<property name="event_generation">connect</property>
|
||||
<property name="file">dialog_paste_special_base</property>
|
||||
<property name="first_id">1000</property>
|
||||
<property name="help_provider">none</property>
|
||||
<property name="image_path_wrapper_function_name"></property>
|
||||
<property name="indent_with_spaces"></property>
|
||||
<property name="internationalize">1</property>
|
||||
<property name="name">dialog_paste_special</property>
|
||||
<property name="namespace"></property>
|
||||
<property name="path">.</property>
|
||||
<property name="precompiled_header"></property>
|
||||
<property name="relative_path">1</property>
|
||||
<property name="skip_lua_events">1</property>
|
||||
<property name="skip_php_events">1</property>
|
||||
<property name="skip_python_events">1</property>
|
||||
<property name="ui_table">UI</property>
|
||||
<property name="use_array_enum">0</property>
|
||||
<property name="use_enum">0</property>
|
||||
<property name="use_microsoft_bom">0</property>
|
||||
<object class="Dialog" expanded="true">
|
||||
<property name="aui_managed">0</property>
|
||||
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
|
||||
<property name="bg"></property>
|
||||
<property name="center">wxBOTH</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="drag_accept_files">0</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="event_handler">impl_virtual</property>
|
||||
<property name="extra_style"></property>
|
||||
<property name="fg"></property>
|
||||
<property name="font"></property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="minimum_size">-1,-1</property>
|
||||
<property name="name">DIALOG_PASTE_SPECIAL_BASE</property>
|
||||
<property name="pos"></property>
|
||||
<property name="size">-1,-1</property>
|
||||
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
|
||||
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
|
||||
<property name="title">Paste Special</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="two_step_creation">0</property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<object class="wxBoxSizer" expanded="true">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">m_mainSizer</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="true">
|
||||
<property name="border">10</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxBoxSizer" expanded="true">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">optionsSizer</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="true">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxRadioBox" expanded="true">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="choices">"Assign unique reference designators to pasted symbols" "Keep existing reference designators, even if they are duplicated" "Clear reference designators on all pasted symbols"</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="drag_accept_files">0</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Paste Options</property>
|
||||
<property name="majorDimension">1</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">m_mainSizer</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">10</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">optionsSizer</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxRadioBox" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="choices">"Assign unique reference designators to pasted symbols" "Keep existing reference designators, even if they are duplicated" "Clear reference designators on all pasted symbols"</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Paste Options</property>
|
||||
<property name="majorDimension">1</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_pasteOptions</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="selection">1</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxRA_SPECIFY_COLS</property>
|
||||
<property name="subclass">; ; forward_declare</property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">6</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxStdDialogButtonSizer" expanded="1">
|
||||
<property name="Apply">0</property>
|
||||
<property name="Cancel">1</property>
|
||||
<property name="ContextHelp">0</property>
|
||||
<property name="Help">0</property>
|
||||
<property name="No">0</property>
|
||||
<property name="OK">1</property>
|
||||
<property name="Save">0</property>
|
||||
<property name="Yes">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">m_sdbSizer</property>
|
||||
<property name="permission">protected</property>
|
||||
<event name="OnOKButtonClick">onOKButton</event>
|
||||
</object>
|
||||
</object>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_pasteOptions</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="selection">1</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxRA_SPECIFY_COLS</property>
|
||||
<property name="subclass">; ; forward_declare</property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnRadioBox">onRadioBoxEvent</event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="true">
|
||||
<property name="border">6</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxStdDialogButtonSizer" expanded="true">
|
||||
<property name="Apply">0</property>
|
||||
<property name="Cancel">1</property>
|
||||
<property name="ContextHelp">0</property>
|
||||
<property name="Help">0</property>
|
||||
<property name="No">0</property>
|
||||
<property name="OK">1</property>
|
||||
<property name="Save">0</property>
|
||||
<property name="Yes">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">m_sdbSizer</property>
|
||||
<property name="permission">protected</property>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</wxFormBuilder_Project>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
|
||||
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
@@ -38,7 +38,7 @@ class DIALOG_PASTE_SPECIAL_BASE : public DIALOG_SHIM
|
||||
wxButton* m_sdbSizerCancel;
|
||||
|
||||
// Virtual event handlers, override them in your derived class
|
||||
virtual void onOKButton( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onRadioBoxEvent( wxCommandEvent& event ) { event.Skip(); }
|
||||
|
||||
|
||||
public:
|
||||
|
||||
@@ -356,5 +356,6 @@ void KIGFX::DS_PAINTER::DrawBorder( const PAGE_INFO* aPageInfo, int aScaleFactor
|
||||
// Use a gray color for the border color
|
||||
m_gal->SetStrokeColor( m_renderSettings.m_pageBorderColor );
|
||||
m_gal->SetIsFill( false );
|
||||
m_gal->SetLineWidth( m_renderSettings.GetDefaultPenWidth() );
|
||||
m_gal->DrawRectangle( origin, end );
|
||||
}
|
||||
|
||||
@@ -349,14 +349,14 @@ VECTOR2I OUTLINE_FONT::getTextAsGlyphsUnlocked( BOX2I* aBBox,
|
||||
hb_buffer_guess_segment_properties( buf ); // guess direction, script, and language based on
|
||||
// contents
|
||||
|
||||
unsigned int glyphCount;
|
||||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount );
|
||||
hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
|
||||
|
||||
hb_font_t* referencedFont = hb_ft_font_create_referenced( face );
|
||||
hb_ft_font_set_funcs( referencedFont );
|
||||
hb_shape( referencedFont, buf, nullptr, 0 );
|
||||
|
||||
unsigned int glyphCount;
|
||||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount );
|
||||
hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
|
||||
|
||||
VECTOR2D scaleFactor( glyphSize.x / faceSize(), -glyphSize.y / faceSize() );
|
||||
scaleFactor = scaleFactor * m_outlineFontSizeCompensation;
|
||||
|
||||
|
||||
+6
-6
@@ -191,22 +191,22 @@ int ExecuteFile( const wxString& aEditorName, const wxString& aFileName, wxProce
|
||||
if( wxFileExists( fullEditorName ) )
|
||||
{
|
||||
int i = 0;
|
||||
const wchar_t* args[4];
|
||||
std::vector<const wchar_t*> args;
|
||||
|
||||
args[i++] = fullEditorName.wc_str();
|
||||
args.emplace_back( fullEditorName.wc_str() );
|
||||
|
||||
if( !params.empty() )
|
||||
{
|
||||
for( const wxString& p : params )
|
||||
args[i++] = p.wc_str();
|
||||
args.emplace_back( p.wc_str() );
|
||||
}
|
||||
|
||||
if( !aFileName.IsEmpty() )
|
||||
args[i++] = aFileName.wc_str();
|
||||
args.emplace_back( aFileName.wc_str() );
|
||||
|
||||
args[i] = nullptr;
|
||||
args.emplace_back( nullptr );
|
||||
|
||||
return wxExecute( const_cast<wchar_t**>( args ), wxEXEC_ASYNC, aCallback );
|
||||
return wxExecute( const_cast<wchar_t**>( args.data() ), wxEXEC_ASYNC, aCallback );
|
||||
}
|
||||
|
||||
wxString msg;
|
||||
|
||||
+5
-2
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2020 Ian McInerney <ian.s.mcinerney@ieee.org>
|
||||
* Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.TXT for contributors.
|
||||
* Copyright (C) 1992-2023 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
|
||||
@@ -324,8 +324,11 @@ bool KIID_PATH::MakeRelativeTo( const KIID_PATH& aPath )
|
||||
|
||||
for( size_t i = 0; i < aPath.size(); ++i )
|
||||
{
|
||||
if( copy.at( i ).AsString() != aPath.at( i ).AsString() )
|
||||
if( copy.at( i ) != aPath.at( i ) )
|
||||
{
|
||||
*this = copy;
|
||||
return false; // this path is not contained within aPath
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = aPath.size(); i < copy.size(); ++i )
|
||||
|
||||
@@ -322,6 +322,8 @@ LIB_TREE_NODE_LIB_ID& LIB_TREE_NODE_LIB::AddItem( LIB_TREE_ITEM* aItem )
|
||||
|
||||
void LIB_TREE_NODE_LIB::UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wxString& aLib )
|
||||
{
|
||||
m_Score = 0;
|
||||
|
||||
// We need to score leaf nodes, which are usually (but not always) children.
|
||||
|
||||
if( m_Children.size() )
|
||||
|
||||
@@ -821,7 +821,8 @@ void GERBER_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill,
|
||||
|
||||
void GERBER_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFill, int aWidth )
|
||||
{
|
||||
Arc( aCenter, ANGLE_0, ANGLE_360, aDiameter / 2, aFill, aWidth );
|
||||
Arc( aCenter, ANGLE_0, ANGLE_180, aDiameter / 2, aFill, aWidth );
|
||||
Arc( aCenter, ANGLE_180, ANGLE_360, aDiameter / 2, aFill, aWidth );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1409,10 +1409,11 @@ void PDF_PLOTTER::Text( const VECTOR2I& aPos,
|
||||
double wideningFactor, heightFactor;
|
||||
|
||||
VECTOR2I t_size( std::abs( aSize.x ), std::abs( aSize.y ) );
|
||||
bool textMirrored = aSize.x < 0;
|
||||
|
||||
computeTextParameters( aPos, aText, aOrient, t_size, m_plotMirror, aH_justify,
|
||||
aV_justify, aWidth, aItalic, aBold, &wideningFactor, &ctm_a,
|
||||
&ctm_b, &ctm_c, &ctm_d, &ctm_e, &ctm_f, &heightFactor );
|
||||
computeTextParameters( aPos, aText, aOrient, t_size, textMirrored, aH_justify, aV_justify,
|
||||
aWidth, aItalic, aBold, &wideningFactor, &ctm_a, &ctm_b, &ctm_c, &ctm_d,
|
||||
&ctm_e, &ctm_f, &heightFactor );
|
||||
|
||||
SetColor( aColor );
|
||||
SetCurrentLineWidth( aWidth, aData );
|
||||
@@ -1424,6 +1425,10 @@ void PDF_PLOTTER::Text( const VECTOR2I& aPos,
|
||||
aFont = KIFONT::FONT::GetFont();
|
||||
|
||||
VECTOR2I full_box( aFont->StringBoundaryLimits( aText, t_size, aWidth, aBold, aItalic ) );
|
||||
|
||||
if( textMirrored )
|
||||
full_box.x *= -1;
|
||||
|
||||
VECTOR2I box_x( full_box.x, 0 );
|
||||
VECTOR2I box_y( 0, full_box.y );
|
||||
|
||||
@@ -1444,13 +1449,17 @@ void PDF_PLOTTER::Text( const VECTOR2I& aPos,
|
||||
{
|
||||
wxString word = str_tok.GetNextToken();
|
||||
|
||||
computeTextParameters( pos, word, aOrient, t_size, m_plotMirror, GR_TEXT_H_ALIGN_LEFT,
|
||||
GR_TEXT_V_ALIGN_BOTTOM, aWidth, aItalic, aBold, &wideningFactor, &ctm_a,
|
||||
&ctm_b, &ctm_c, &ctm_d, &ctm_e, &ctm_f, &heightFactor );
|
||||
computeTextParameters( pos, word, aOrient, t_size, textMirrored, GR_TEXT_H_ALIGN_LEFT,
|
||||
GR_TEXT_V_ALIGN_BOTTOM, aWidth, aItalic, aBold, &wideningFactor,
|
||||
&ctm_a, &ctm_b, &ctm_c, &ctm_d, &ctm_e, &ctm_f, &heightFactor );
|
||||
|
||||
// Extract the changed width and rotate by the orientation to get the offset for the
|
||||
// next word
|
||||
VECTOR2I bbox( aFont->StringBoundaryLimits( word, t_size, aWidth, aBold, aItalic ).x, 0 );
|
||||
|
||||
if( textMirrored )
|
||||
bbox.x *= -1;
|
||||
|
||||
RotatePoint( bbox, aOrient );
|
||||
pos += bbox;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2020-2023 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
|
||||
@@ -423,7 +423,7 @@ void PSLIKE_PLOTTER::computeTextParameters( const VECTOR2I& aPos,
|
||||
// This is an approximation of the text bounds (in IUs)
|
||||
int tw = returnPostscriptTextWidth( aText, aSize.x, aItalic, aWidth );
|
||||
int th = aSize.y;
|
||||
int dx, dy;
|
||||
int dx = 0, dy = 0;
|
||||
|
||||
switch( aH_justify )
|
||||
{
|
||||
@@ -450,7 +450,7 @@ void PSLIKE_PLOTTER::computeTextParameters( const VECTOR2I& aPos,
|
||||
*wideningFactor = sz_dev.x / sz_dev.y;
|
||||
|
||||
// Mirrored texts must be plotted as mirrored!
|
||||
if( m_plotMirror )
|
||||
if( m_plotMirror ^ aMirror )
|
||||
*wideningFactor = -*wideningFactor;
|
||||
|
||||
// The CTM transformation matrix
|
||||
|
||||
@@ -825,16 +825,16 @@ void SVG_PLOTTER::Text( const VECTOR2I& aPos,
|
||||
|
||||
if( !aOrient.IsZero() )
|
||||
{
|
||||
fprintf( m_outputFile,
|
||||
"<g transform=\"rotate(%f %.*f %.*f)\">\n",
|
||||
- aOrient.AsDegrees(), m_precision, anchor_pos_dev.x, m_precision, anchor_pos_dev.y );
|
||||
fprintf( m_outputFile, "<g transform=\"rotate(%f %.*f %.*f)\">\n",
|
||||
m_plotMirror ? aOrient.AsDegrees() : -aOrient.AsDegrees(), m_precision,
|
||||
anchor_pos_dev.x, m_precision, anchor_pos_dev.y );
|
||||
}
|
||||
|
||||
fprintf( m_outputFile, "<text x=\"%.*f\" y=\"%.*f\"\n",
|
||||
m_precision, text_pos_dev.x, m_precision, text_pos_dev.y );
|
||||
|
||||
/// If the text is mirrored, we should also mirror the hidden text to match
|
||||
if( aSize.x < 0 )
|
||||
if( m_plotMirror != ( aSize.x < 0 ) )
|
||||
fprintf( m_outputFile, "transform=\"scale(-1 1) translate(%f 0)\"\n", -2 * text_pos_dev.x );
|
||||
|
||||
fprintf( m_outputFile,
|
||||
|
||||
@@ -177,8 +177,16 @@ bool PROJECT_ARCHIVER::Archive( const wxString& aSrcDir, const wxString& aDestFi
|
||||
|
||||
unsigned long uncompressedBytes = 0;
|
||||
|
||||
// Our filename collector can store duplicate filenames. for instance *.gm2
|
||||
// matches both *.g?? and *.gm??.
|
||||
// So skip duplicate filenames (they are sorted, so it is easy).
|
||||
wxString lastStoredFile;
|
||||
|
||||
for( unsigned ii = 0; ii < files.GetCount(); ii++ )
|
||||
{
|
||||
if( lastStoredFile == files[ii] ) // duplicate name: already stored
|
||||
continue;
|
||||
|
||||
wxFileSystem fsfile;
|
||||
|
||||
wxFileName curr_fn( files[ii] );
|
||||
|
||||
@@ -361,6 +361,12 @@ bool DIALOG_LIB_SYMBOL_PROPERTIES::TransferDataFromWindow()
|
||||
wxString newName = EscapeString( m_SymbolNameCtrl->GetValue(), CTX_LIBID );
|
||||
wxString oldName = m_libEntry->GetName();
|
||||
|
||||
if( newName.IsEmpty() )
|
||||
{
|
||||
wxMessageBox( _( "Symbol must have a name." ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( oldName != newName )
|
||||
{
|
||||
wxString libName = m_Parent->GetCurLib();
|
||||
|
||||
@@ -465,6 +465,8 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
|
||||
screen->MigrateSimModels();
|
||||
}
|
||||
|
||||
schematic.PruneOrphanedSymbolInstances( Prj().GetProjectName(), Schematic().GetSheets() );
|
||||
|
||||
Schematic().ConnectionGraph()->Reset();
|
||||
|
||||
SetScreen( GetCurrentSheet().LastScreen() );
|
||||
|
||||
@@ -465,7 +465,7 @@ void LIB_TEXTBOX::Plot( PLOTTER* aPlotter, bool aBackground, const VECTOR2I& aOf
|
||||
|
||||
text.GetLinePositions( positions, (int) strings_list.Count() );
|
||||
|
||||
TEXT_ATTRIBUTES attrs = GetAttributes();
|
||||
TEXT_ATTRIBUTES attrs = text.GetAttributes();
|
||||
attrs.m_StrokeWidth = penWidth;
|
||||
attrs.m_Multiline = false;
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ void SCH_CONNECTION::ConfigureFromLabel( const wxString& aLabel )
|
||||
auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
|
||||
member->SetPrefix( prefix );
|
||||
member->SetGraph( m_graph );
|
||||
member->ConfigureFromLabel( alias_member );
|
||||
member->ConfigureFromLabel( EscapeString( alias_member, CTX_NETNAME ) );
|
||||
m_members.push_back( member );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,8 +499,17 @@ LIB_SYMBOL* SCH_DATABASE_PLUGIN::loadSymbolFromRow( const wxString& aSymbolName,
|
||||
continue;
|
||||
}
|
||||
|
||||
wxString value( std::any_cast<std::string>( aRow.at( mapping.column ) ).c_str(),
|
||||
wxConvUTF8 );
|
||||
std:: string strValue;
|
||||
|
||||
try
|
||||
{
|
||||
strValue = std::any_cast<std::string>( aRow.at( mapping.column ) );
|
||||
}
|
||||
catch( std::bad_any_cast& e )
|
||||
{
|
||||
}
|
||||
|
||||
wxString value( strValue.c_str(), wxConvUTF8 );
|
||||
|
||||
if( mapping.name == wxT( "Value" ) )
|
||||
{
|
||||
|
||||
@@ -1694,6 +1694,8 @@ LIB_TEXTBOX* SCH_SEXPR_PARSER::parseTextBox()
|
||||
FILL_PARAMS fill;
|
||||
std::unique_ptr<LIB_TEXTBOX> textBox = std::make_unique<LIB_TEXTBOX>( nullptr );
|
||||
|
||||
textBox->SetUnit( m_unit );
|
||||
textBox->SetConvert( m_convert );
|
||||
token = NextTok();
|
||||
|
||||
if( token == T_private )
|
||||
|
||||
@@ -550,10 +550,6 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelect
|
||||
m_out->Print( 0, ")\n\n" );
|
||||
}
|
||||
|
||||
// Store the selected sheets instance information
|
||||
SCH_SHEET_LIST selectedSheets;
|
||||
SCH_REFERENCE_LIST selectedSymbols;
|
||||
|
||||
for( i = 0; i < aSelection->GetSize(); ++i )
|
||||
{
|
||||
item = (SCH_ITEM*) aSelection->GetItem( i );
|
||||
@@ -561,10 +557,8 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelect
|
||||
switch( item->Type() )
|
||||
{
|
||||
case SCH_SYMBOL_T:
|
||||
saveSymbol( static_cast<SCH_SYMBOL*>( item ), aSchematic, 0, aForClipboard );
|
||||
|
||||
aSelectionPath->AppendSymbol( selectedSymbols, static_cast<SCH_SYMBOL*>( item ),
|
||||
true, true );
|
||||
saveSymbol( static_cast<SCH_SYMBOL*>( item ), aSchematic, 0, aForClipboard,
|
||||
aSelectionPath );
|
||||
break;
|
||||
|
||||
case SCH_BITMAP_T:
|
||||
@@ -573,15 +567,6 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelect
|
||||
|
||||
case SCH_SHEET_T:
|
||||
saveSheet( static_cast< SCH_SHEET* >( item ), 0 );
|
||||
|
||||
{
|
||||
SCH_SHEET_PATH subSheetPath = *aSelectionPath;
|
||||
subSheetPath.push_back( static_cast<SCH_SHEET*>( item ) );
|
||||
|
||||
fullHierarchy.GetSheetsWithinPath( selectedSheets, subSheetPath );
|
||||
fullHierarchy.GetSymbolsWithinPath( selectedSymbols, subSheetPath, true, true );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SCH_JUNCTION_T:
|
||||
@@ -621,33 +606,12 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelect
|
||||
wxASSERT( "Unexpected schematic object type in SCH_SEXPR_PLUGIN::Format()" );
|
||||
}
|
||||
}
|
||||
|
||||
// Make all instance information relative to the selection path
|
||||
KIID_PATH selectionPath = aSelectionPath->Path();
|
||||
|
||||
selectedSheets.SortByPageNumbers();
|
||||
std::vector<SCH_SHEET_INSTANCE> sheetinstances = selectedSheets.GetSheetInstances();
|
||||
|
||||
for( SCH_SHEET_INSTANCE& sheetInstance : sheetinstances )
|
||||
{
|
||||
wxASSERT_MSG( sheetInstance.m_Path.MakeRelativeTo( selectionPath ),
|
||||
"Sheet is not inside the selection path?" );
|
||||
}
|
||||
|
||||
selectionPath = aSelectionPath->Path();
|
||||
selectedSymbols.SortByReferenceOnly();
|
||||
std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = selectedSymbols.GetSymbolInstances();
|
||||
|
||||
for( SCH_SYMBOL_INSTANCE& symbolInstance : symbolInstances )
|
||||
{
|
||||
wxASSERT_MSG( symbolInstance.m_Path.MakeRelativeTo( selectionPath ),
|
||||
"Symbol is not inside the selection path?" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_SEXPR_PLUGIN::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSchematic,
|
||||
int aNestLevel, bool aForClipboard )
|
||||
int aNestLevel, bool aForClipboard,
|
||||
const SCH_SHEET_PATH* aRelativePath )
|
||||
{
|
||||
wxCHECK_RET( aSymbol != nullptr && m_out != nullptr, "" );
|
||||
|
||||
@@ -717,6 +681,14 @@ void SCH_SEXPR_PLUGIN::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSchema
|
||||
aSymbol->GetUnit() :
|
||||
aSymbol->GetInstanceReferences()[0].m_Unit;
|
||||
|
||||
if( aForClipboard && aRelativePath )
|
||||
{
|
||||
SCH_SYMBOL_INSTANCE unitInstance;
|
||||
|
||||
if( aSymbol->GetInstance( unitInstance, aRelativePath->Path() ) )
|
||||
unit = unitInstance.m_Unit;
|
||||
}
|
||||
|
||||
m_out->Print( 0, " (unit %d)", unit );
|
||||
|
||||
if( aSymbol->GetConvert() == LIB_ITEM::LIB_CONVERT::DEMORGAN )
|
||||
@@ -759,6 +731,14 @@ void SCH_SEXPR_PLUGIN::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSchema
|
||||
field.SetText( aSymbol->GetField( FOOTPRINT_FIELD )->GetText() );
|
||||
}
|
||||
}
|
||||
else if( aForClipboard && aSymbol->GetInstanceReferences().size() && aRelativePath
|
||||
&& ( id == REFERENCE_FIELD ) )
|
||||
{
|
||||
SCH_SYMBOL_INSTANCE instance;
|
||||
|
||||
if( aSymbol->GetInstance( instance, aRelativePath->Path() ) )
|
||||
field.SetText( instance.m_Reference );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -805,6 +785,9 @@ void SCH_SEXPR_PLUGIN::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSchema
|
||||
|
||||
for( size_t i = 0; i < aSymbol->GetInstanceReferences().size(); i++ )
|
||||
{
|
||||
// Zero length KIID_PATH objects are not valid and will cause a crash below.
|
||||
wxCHECK2( aSymbol->GetInstanceReferences()[i].m_Path.size(), continue );
|
||||
|
||||
// If the instance data is part of this design but no longer has an associated sheet
|
||||
// path, don't save it. This prevents large amounts of orphaned instance data for the
|
||||
// current project from accumulating in the schematic files.
|
||||
@@ -839,7 +822,13 @@ void SCH_SEXPR_PLUGIN::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSchema
|
||||
project_open = true;
|
||||
}
|
||||
|
||||
wxString path = aSymbol->GetInstanceReferences()[i].m_Path.AsString();
|
||||
wxString path;
|
||||
KIID_PATH tmp = aSymbol->GetInstanceReferences()[i].m_Path;
|
||||
|
||||
if( aForClipboard && aRelativePath )
|
||||
tmp.MakeRelativeTo( aRelativePath->Path() );
|
||||
|
||||
path = tmp.AsString();
|
||||
|
||||
m_out->Print( aNestLevel + 3, "(path %s\n",
|
||||
m_out->Quotew( path ).c_str() );
|
||||
|
||||
@@ -146,7 +146,7 @@ private:
|
||||
void loadFile( const wxString& aFileName, SCH_SHEET* aSheet );
|
||||
|
||||
void saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSchematic, int aNestLevel,
|
||||
bool aForClipboard );
|
||||
bool aForClipboard, const SCH_SHEET_PATH* aRelativePath = nullptr );
|
||||
void saveField( SCH_FIELD* aField, int aNestLevel );
|
||||
void saveBitmap( SCH_BITMAP* aBitmap, int aNestLevel );
|
||||
void saveSheet( SCH_SHEET* aSheet, int aNestLevel );
|
||||
|
||||
@@ -95,12 +95,23 @@ void SCH_LEGACY_PLUGIN_CACHE::Load()
|
||||
|
||||
m_versionMajor = parseInt( reader, line, &line );
|
||||
|
||||
if( *line != '.' )
|
||||
SCH_PARSE_ERROR( "invalid file version formatting in header", reader, line );
|
||||
if( *line == '/' )
|
||||
{
|
||||
// Some old libraries use a version syntax like
|
||||
// EESchema-LIBRARY Version 2/10/2006-18:49:15
|
||||
// use 2.3 version numer to read the file
|
||||
m_versionMajor = 2;
|
||||
m_versionMinor = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( *line != '.' )
|
||||
SCH_PARSE_ERROR( "invalid file version formatting in header", reader, line );
|
||||
|
||||
line++;
|
||||
line++;
|
||||
|
||||
m_versionMinor = parseInt( reader, line, &line );
|
||||
m_versionMinor = parseInt( reader, line, &line );
|
||||
}
|
||||
|
||||
if( m_versionMajor < 1 || m_versionMinor < 0 || m_versionMinor > 99 )
|
||||
SCH_PARSE_ERROR( "invalid file version in header", reader, line );
|
||||
@@ -746,6 +757,68 @@ FILL_T SCH_LEGACY_PLUGIN_CACHE::parseFillMode( LINE_READER& aReader, const char*
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function based on version 6.0 is required for reading legacy arcs.
|
||||
* Changing it in any way will likely break arcs.
|
||||
*/
|
||||
static bool MapAnglesV6( int* aAngle1, int* aAngle2 )
|
||||
{
|
||||
auto DECIDEG2RAD = []( double deg ) -> double
|
||||
{
|
||||
return deg * M_PI / 1800.0;
|
||||
};
|
||||
|
||||
int angle, delta;
|
||||
double x, y;
|
||||
bool swap = false;
|
||||
|
||||
delta = *aAngle2 - *aAngle1;
|
||||
|
||||
if( delta >= 1800 )
|
||||
{
|
||||
*aAngle1 -= 1;
|
||||
*aAngle2 += 1;
|
||||
}
|
||||
|
||||
x = cos( DECIDEG2RAD( *aAngle1 ) );
|
||||
y = -sin( DECIDEG2RAD( *aAngle1 ) );
|
||||
*aAngle1 = KiROUND( RAD2DECIDEG( atan2( y, x ) ) );
|
||||
|
||||
x = cos( DECIDEG2RAD( *aAngle2 ) );
|
||||
y = -sin( DECIDEG2RAD( *aAngle2 ) );
|
||||
*aAngle2 = KiROUND( RAD2DECIDEG( atan2( y, x ) ) );
|
||||
|
||||
NORMALIZE_ANGLE_POS( *aAngle1 );
|
||||
NORMALIZE_ANGLE_POS( *aAngle2 );
|
||||
|
||||
if( *aAngle2 < *aAngle1 )
|
||||
*aAngle2 += 3600;
|
||||
|
||||
if( *aAngle2 - *aAngle1 > 1800 ) // Need to swap the two angles
|
||||
{
|
||||
angle = ( *aAngle1 );
|
||||
*aAngle1 = ( *aAngle2 );
|
||||
*aAngle2 = angle;
|
||||
|
||||
NORMALIZE_ANGLE_POS( *aAngle1 );
|
||||
NORMALIZE_ANGLE_POS( *aAngle2 );
|
||||
|
||||
if( *aAngle2 < *aAngle1 )
|
||||
*aAngle2 += 3600;
|
||||
|
||||
swap = true;
|
||||
}
|
||||
|
||||
if( delta >= 1800 )
|
||||
{
|
||||
*aAngle1 += 1;
|
||||
*aAngle2 -= 1;
|
||||
}
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
|
||||
LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr<LIB_SYMBOL>& aSymbol,
|
||||
LINE_READER& aReader )
|
||||
{
|
||||
@@ -762,10 +835,12 @@ LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr<LIB_SYMBOL>& aSymbo
|
||||
|
||||
arc->SetPosition( center );
|
||||
|
||||
(void) schIUScale.MilsToIU( parseInt( aReader, line, &line ) );
|
||||
int radius = schIUScale.MilsToIU( parseInt( aReader, line, &line ) );
|
||||
int angle1 = parseInt( aReader, line, &line );
|
||||
int angle2 = parseInt( aReader, line, &line );
|
||||
|
||||
EDA_ANGLE angle1( parseInt( aReader, line, &line ), TENTHS_OF_A_DEGREE_T );
|
||||
EDA_ANGLE angle2( parseInt( aReader, line, &line ), TENTHS_OF_A_DEGREE_T );
|
||||
NORMALIZE_ANGLE_POS( angle1 );
|
||||
NORMALIZE_ANGLE_POS( angle2 );
|
||||
|
||||
arc->SetUnit( parseInt( aReader, line, &line ) );
|
||||
arc->SetConvert( parseInt( aReader, line, &line ) );
|
||||
@@ -792,38 +867,33 @@ LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr<LIB_SYMBOL>& aSymbo
|
||||
arc->SetStart( arcStart );
|
||||
arc->SetEnd( arcEnd );
|
||||
}
|
||||
// Actual Coordinates of arc ends are not read from file (old library), calculate them
|
||||
else
|
||||
{
|
||||
arc->SetArcAngleAndEnd( angle2 - angle1, true );
|
||||
// Actual Coordinates of arc ends are not read from file
|
||||
// (old library), calculate them
|
||||
VECTOR2I arcStart( radius, 0 );
|
||||
VECTOR2I arcEnd( radius, 0 );
|
||||
|
||||
RotatePoint( arcStart, EDA_ANGLE( -angle1, EDA_ANGLE_T::TENTHS_OF_A_DEGREE_T ) );
|
||||
arcStart += arc->GetCenter();
|
||||
arc->SetStart( arcStart );
|
||||
RotatePoint( arcEnd, EDA_ANGLE( -angle2, EDA_ANGLE_T::TENTHS_OF_A_DEGREE_T ) );
|
||||
arcEnd += arc->GetCenter();
|
||||
arc->SetEnd( arcEnd );
|
||||
}
|
||||
|
||||
/*
|
||||
* Current file format stores start-mid-end and so doesn't care about winding. We
|
||||
* store start-end with an implied winding internally though.
|
||||
* This issue is only for 180 deg arcs, because 180 deg are a limit to handle arcs in
|
||||
* legacy libs.
|
||||
*
|
||||
* So a workaround is to slightly change the arc angle to
|
||||
* avoid 180 deg arc after correction
|
||||
/**
|
||||
* This accounts for an oddity in the old library format, where the symbol is overdefined.
|
||||
* The previous draw (based on wxwidgets) used start point and end point and always drew
|
||||
* counter-clockwise. The new GAL draw takes center, radius and start/end angles. All of
|
||||
* these points were stored in the file, so we need to mimic the swapping of start/end
|
||||
* points rather than using the stored angles in order to properly map edge cases.
|
||||
*/
|
||||
EDA_ANGLE arc_angle = arc->GetArcAngle();
|
||||
|
||||
if( arc_angle == ANGLE_180 )
|
||||
if( !MapAnglesV6( &angle1, &angle2 ) )
|
||||
{
|
||||
VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
|
||||
EDA_ANGLE( 179.5, DEGREES_T ) );
|
||||
arc->SetCenter( new_center );
|
||||
}
|
||||
|
||||
// In legacy libraries, an arc angle is always <= 180.0 degrees
|
||||
// So if the created arc is > 180 degrees, swap arc ends to have a < 180 deg arc.
|
||||
if( arc->GetArcAngle() > ANGLE_180 )
|
||||
{
|
||||
VECTOR2I new_end = arc->GetStart();
|
||||
VECTOR2I new_start = arc->GetEnd();
|
||||
arc->SetStart( new_start );
|
||||
arc->SetEnd( new_end );
|
||||
VECTOR2I temp = arc->GetStart();
|
||||
arc->SetStart( arc->GetEnd() );
|
||||
arc->SetEnd( temp );
|
||||
}
|
||||
|
||||
return arc;
|
||||
|
||||
+59
-1
@@ -34,6 +34,7 @@
|
||||
#include <project.h>
|
||||
#include <reporter.h>
|
||||
#include <sch_draw_panel.h>
|
||||
#include <trace_helpers.h>
|
||||
#include <sch_edit_frame.h>
|
||||
#include <sch_item.h>
|
||||
|
||||
@@ -1029,7 +1030,8 @@ void SCH_SCREEN::UpdateSymbolLinks( REPORTER* aReporter )
|
||||
}
|
||||
}
|
||||
|
||||
symbol->SetLibSymbol( libSymbol.release() );
|
||||
if( libSymbol.get() ) // Only change the old link if the new link exists
|
||||
symbol->SetLibSymbol( libSymbol.release() );
|
||||
}
|
||||
|
||||
// Changing the symbol may adjust the bbox of the symbol. This re-inserts the
|
||||
@@ -1626,6 +1628,51 @@ size_t SCH_SCREEN::getLibSymbolNameMatches( const SCH_SYMBOL& aSymbol,
|
||||
}
|
||||
|
||||
|
||||
void SCH_SCREEN::PruneOrphanedSymbolInstances( const wxString& aProjectName,
|
||||
const SCH_SHEET_LIST& aValidSheetPaths )
|
||||
{
|
||||
// The project name cannot be empty. Projects older than 7.0 did not save project names
|
||||
// when saving instance data. Running this algorithm with an empty project name would
|
||||
// clobber all instance data for projects other than the current one when a schematic
|
||||
// file is shared across multiple projects.
|
||||
wxCHECK( !aProjectName.IsEmpty(), /* void */ );
|
||||
|
||||
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) )
|
||||
{
|
||||
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
|
||||
|
||||
wxCHECK2( symbol, continue );
|
||||
|
||||
std::set<KIID_PATH> pathsToPrune;
|
||||
const std::vector<SCH_SYMBOL_INSTANCE> instances = symbol->GetInstanceReferences();
|
||||
|
||||
for( const SCH_SYMBOL_INSTANCE& instance : instances )
|
||||
{
|
||||
// Ignore instance paths from other projects.
|
||||
if( aProjectName != instance.m_ProjectName )
|
||||
continue;
|
||||
|
||||
std::optional<SCH_SHEET_PATH> pathFound =
|
||||
aValidSheetPaths.GetSheetPathByKIIDPath( instance.m_Path );
|
||||
|
||||
// Check for paths that do not exist in the current project and paths that do
|
||||
// not contain the current symbol.
|
||||
if( !pathFound )
|
||||
pathsToPrune.emplace( instance.m_Path );
|
||||
else if( pathFound.value().LastScreen() != this )
|
||||
pathsToPrune.emplace( pathFound.value().Path() );
|
||||
}
|
||||
|
||||
for( const KIID_PATH& sheetPath : pathsToPrune )
|
||||
{
|
||||
wxLogTrace( traceSchSheetPaths, wxS( "Pruning project '%s' symbol instance %s." ),
|
||||
aProjectName, sheetPath.AsString() );
|
||||
symbol->RemoveInstance( sheetPath );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
void SCH_SCREEN::Show( int nestLevel, std::ostream& os ) const
|
||||
{
|
||||
@@ -2042,3 +2089,14 @@ void SCH_SCREEN::MigrateSimModels()
|
||||
SIM_MODEL::MigrateSimModel<SCH_SYMBOL, SCH_FIELD>( *symbol, &Schematic()->Prj() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_SCREENS::PruneOrphanedSymbolInstances( const wxString& aProjectName,
|
||||
|
||||
const SCH_SHEET_LIST& aValidSheetPaths )
|
||||
{
|
||||
wxCHECK( !aProjectName.IsEmpty(), /* void */ );
|
||||
|
||||
for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() )
|
||||
screen->PruneOrphanedSymbolInstances( aProjectName, aValidSheetPaths );
|
||||
}
|
||||
|
||||
@@ -545,6 +545,21 @@ public:
|
||||
*/
|
||||
void MigrateSimModels();
|
||||
|
||||
/**
|
||||
* Remove all invalid symbol instance data in this screen object for the project defined
|
||||
* by \a aProjectName and the list of \a aValidSheetPaths.
|
||||
*
|
||||
* @warning This method will assert and exit on debug builds when \a aProjectName is empty.
|
||||
*
|
||||
* @note This method does not affect instance data for any other projects.
|
||||
*
|
||||
* @param aProjectName is the name of the current project.
|
||||
* @param aValidSheetPaths is the list of valid #SCH_SHEET_PATH objects for the current
|
||||
* project.
|
||||
*/
|
||||
void PruneOrphanedSymbolInstances( const wxString& aProjectName,
|
||||
const SCH_SHEET_LIST& aValidSheetPaths );
|
||||
|
||||
private:
|
||||
friend SCH_EDIT_FRAME; // Only to populate m_symbolInstances.
|
||||
friend SCH_SEXPR_PARSER; // Only to load instance information from schematic file.
|
||||
@@ -793,6 +808,9 @@ public:
|
||||
*/
|
||||
void SetLegacySymbolInstanceData();
|
||||
|
||||
void PruneOrphanedSymbolInstances( const wxString& aProjectName,
|
||||
const SCH_SHEET_LIST& aValidSheetPaths );
|
||||
|
||||
private:
|
||||
void addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet );
|
||||
void buildScreenList( SCH_SHEET* aSheet);
|
||||
|
||||
@@ -1222,6 +1222,39 @@ bool SCH_SHEET::operator <( const SCH_ITEM& aItem ) const
|
||||
}
|
||||
|
||||
|
||||
void SCH_SHEET::RemoveInstance( const KIID_PATH& aInstancePath )
|
||||
{
|
||||
// Search for an existing path and remove it if found (should not occur)
|
||||
for( unsigned ii = 0; ii < m_instances.size(); ii++ )
|
||||
{
|
||||
if( m_instances[ii].m_Path == aInstancePath )
|
||||
{
|
||||
wxLogTrace( traceSchSheetPaths, "Removing sheet instance:\n"
|
||||
" sheet path %s\n"
|
||||
" page %s, from project %s.",
|
||||
aInstancePath.AsString(),
|
||||
m_instances[ii].m_PageNumber,
|
||||
m_instances[ii].m_ProjectName );
|
||||
|
||||
m_instances.erase( m_instances.begin() + ii );
|
||||
ii--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_SHEET::AddInstance( const SCH_SHEET_INSTANCE& aInstance )
|
||||
{
|
||||
SCH_SHEET_INSTANCE oldInstance;
|
||||
|
||||
if( getInstance( oldInstance, aInstance.m_Path ) )
|
||||
RemoveInstance( aInstance.m_Path );
|
||||
|
||||
m_instances.emplace_back( aInstance );
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool SCH_SHEET::addInstance( const SCH_SHEET_PATH& aSheetPath )
|
||||
{
|
||||
wxCHECK( aSheetPath.IsFullPath(), false );
|
||||
|
||||
@@ -409,6 +409,10 @@ public:
|
||||
*/
|
||||
const SCH_SHEET_INSTANCE& GetRootInstance() const;
|
||||
|
||||
void RemoveInstance( const KIID_PATH& aInstancePath );
|
||||
|
||||
void AddInstance( const SCH_SHEET_INSTANCE& aInstance );
|
||||
|
||||
/**
|
||||
* Compares page numbers of schematic sheets.
|
||||
*
|
||||
|
||||
@@ -698,6 +698,11 @@ void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are not performing a full recursion test, at least check if we are in
|
||||
// a simple recursion scenario to prevent stack overflow crashes
|
||||
wxCHECK2_MSG( sheet->GetFileName() != aSheet->GetFileName(), continue,
|
||||
wxT( "Recursion prevented in SCH_SHEET_LIST::BuildSheetList" ) );
|
||||
|
||||
BuildSheetList( sheet, false );
|
||||
}
|
||||
}
|
||||
|
||||
+21
-3
@@ -547,6 +547,17 @@ void SCH_SYMBOL::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffse
|
||||
tempPin->SetShape( symbolPin->GetShape() );
|
||||
}
|
||||
|
||||
for( LIB_ITEM& item : tempSymbol.GetDrawItems() )
|
||||
{
|
||||
if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( &item ) )
|
||||
{
|
||||
// Use SCH_FIELD's text resolver
|
||||
SCH_FIELD dummy( (SCH_ITEM*) this, -1 );
|
||||
dummy.SetText( text->GetText() );
|
||||
text->SetText( dummy.GetShownText( false ) );
|
||||
}
|
||||
}
|
||||
|
||||
tempSymbol.Print( aSettings, m_pos + aOffset, m_unit, m_convert, opts, GetDNP() );
|
||||
}
|
||||
else // Use dummy() part if the actual cannot be found.
|
||||
@@ -600,16 +611,22 @@ bool SCH_SYMBOL::GetInstance( SCH_SYMBOL_INSTANCE& aInstance,
|
||||
|
||||
|
||||
void SCH_SYMBOL::RemoveInstance( const SCH_SHEET_PATH& aInstancePath )
|
||||
{
|
||||
RemoveInstance( aInstancePath.Path() );
|
||||
}
|
||||
|
||||
|
||||
void SCH_SYMBOL::RemoveInstance( const KIID_PATH& aInstancePath )
|
||||
{
|
||||
// Search for an existing path and remove it if found (should not occur)
|
||||
for( unsigned ii = 0; ii < m_instanceReferences.size(); ii++ )
|
||||
{
|
||||
if( m_instanceReferences[ii].m_Path == aInstancePath.Path() )
|
||||
if( m_instanceReferences[ii].m_Path == aInstancePath )
|
||||
{
|
||||
wxLogTrace( traceSchSheetPaths, "Removing symbol instance:\n"
|
||||
" sheet path %s\n"
|
||||
" reference %s, unit %d from symbol %s.",
|
||||
aInstancePath.Path().AsString(),
|
||||
aInstancePath.AsString(),
|
||||
m_instanceReferences[ii].m_Reference,
|
||||
m_instanceReferences[ii].m_Unit,
|
||||
m_Uuid.AsString() );
|
||||
@@ -624,7 +641,8 @@ void SCH_SYMBOL::RemoveInstance( const SCH_SHEET_PATH& aInstancePath )
|
||||
void SCH_SYMBOL::SortInstances( bool (*aSortFunction)( const SCH_SYMBOL_INSTANCE& aLhs,
|
||||
const SCH_SYMBOL_INSTANCE& aRhs ) )
|
||||
{
|
||||
std::sort( m_instanceReferences.begin(), m_instanceReferences.end(), aSortFunction );
|
||||
if( m_instanceReferences.size() > 1 )
|
||||
std::sort( m_instanceReferences.begin(), m_instanceReferences.end(), aSortFunction );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ public:
|
||||
const KIID_PATH& aSheetPath, bool aTestFromEnd = false ) const;
|
||||
|
||||
void RemoveInstance( const SCH_SHEET_PATH& aInstancePath );
|
||||
void RemoveInstance( const KIID_PATH& aInstancePath );
|
||||
|
||||
void RemoveAllInstances() { m_instanceReferences.clear(); }
|
||||
|
||||
|
||||
@@ -1135,8 +1135,8 @@ bool IbisParser::readString( std::string& aDest )
|
||||
|
||||
if( len < 1 )
|
||||
{
|
||||
Report( _( "Unable to read string, input is empty." ), RPT_SEVERITY_ERROR );
|
||||
return false;
|
||||
// The data is likely on the next line. Skip.
|
||||
return true;
|
||||
}
|
||||
|
||||
char c = aDest[len - 1];
|
||||
@@ -1436,6 +1436,20 @@ bool IbisParser::readRamp()
|
||||
}
|
||||
|
||||
|
||||
bool IbisParser::readModelSpec()
|
||||
{
|
||||
bool status = true;
|
||||
|
||||
m_continue = IBIS_PARSER_CONTINUE::MODEL_SPEC;
|
||||
|
||||
// TODO
|
||||
// readTypMinMaxValueSubparam( std::string( "Vmeas" ), m_currentModel->... )
|
||||
// ...
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
bool IbisParser::parseModel( std::string& aKeyword )
|
||||
{
|
||||
bool status = false;
|
||||
@@ -1458,6 +1472,8 @@ bool IbisParser::parseModel( std::string& aKeyword )
|
||||
status = readWaveform( nullptr, IBIS_WAVEFORM_TYPE::FALLING );
|
||||
else if( compareIbisWord( aKeyword.c_str(), "Ramp" ) )
|
||||
status = readRamp();
|
||||
else if( compareIbisWord( aKeyword.c_str(), "Model_Spec" ) )
|
||||
status = readModelSpec();
|
||||
else if( compareIbisWord( aKeyword.c_str(), "Pullup_Reference" ) )
|
||||
status = readTypMinMaxValue( m_currentModel->m_pullupReference );
|
||||
else if( compareIbisWord( aKeyword.c_str(), "Pulldown_Reference" ) )
|
||||
@@ -1930,39 +1946,39 @@ bool IbisParser::readModel()
|
||||
{
|
||||
if( readWord( subparam ) )
|
||||
{
|
||||
if( !compareIbisWord( subparam.c_str(), "Input" ) )
|
||||
if( compareIbisWord( subparam.c_str(), "Input" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::INPUT_STD;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Output" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Output" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::OUTPUT;
|
||||
else if( !compareIbisWord( subparam.c_str(), "I/O" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "I/O" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::IO;
|
||||
else if( !compareIbisWord( subparam.c_str(), "3-state" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "3-state" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::THREE_STATE;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Open_drain" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Open_drain" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_DRAIN;
|
||||
else if( !compareIbisWord( subparam.c_str(), "I/O_Open_drain" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "I/O_Open_drain" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_DRAIN;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Open_sink" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Open_sink" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_SINK;
|
||||
else if( !compareIbisWord( subparam.c_str(), "I/O_open_sink" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "I/O_open_sink" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_SINK;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Open_source" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Open_source" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_SOURCE;
|
||||
else if( !compareIbisWord( subparam.c_str(), "I/O_open_source" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "I/O_open_source" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_SOURCE;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Input_ECL" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Input_ECL" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::INPUT_ECL;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Output_ECL" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Output_ECL" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::OUTPUT_ECL;
|
||||
else if( !compareIbisWord( subparam.c_str(), "I/O_ECL" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "I/O_ECL" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::IO_ECL;
|
||||
else if( !compareIbisWord( subparam.c_str(), "3-state_ECL" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "3-state_ECL" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::THREE_STATE_ECL;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Terminator" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Terminator" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::TERMINATOR;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Series" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Series" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::SERIES;
|
||||
else if( !compareIbisWord( subparam.c_str(), "Series_switch" ) )
|
||||
else if( compareIbisWord( subparam.c_str(), "Series_switch" ) )
|
||||
m_currentModel->m_type = IBIS_MODEL_TYPE::SERIES_SWITCH;
|
||||
else
|
||||
{
|
||||
@@ -2577,6 +2593,7 @@ bool IbisParser::onNewLine()
|
||||
status &= readWaveform( m_currentWaveform, m_currentWaveform->m_type );
|
||||
break;
|
||||
case IBIS_PARSER_CONTINUE::RAMP: status &= readRamp(); break;
|
||||
case IBIS_PARSER_CONTINUE::MODEL_SPEC: status &= readModelSpec(); break;
|
||||
case IBIS_PARSER_CONTINUE::PACKAGEMODEL_PINS: status &= readPackageModelPins(); break;
|
||||
case IBIS_PARSER_CONTINUE::MATRIX: status &= readMatrix( m_currentMatrix ); break;
|
||||
case IBIS_PARSER_CONTINUE::NONE:
|
||||
|
||||
@@ -632,6 +632,7 @@ enum class IBIS_PARSER_CONTINUE
|
||||
MATRIX,
|
||||
MODELSELECTOR,
|
||||
MODEL,
|
||||
MODEL_SPEC,
|
||||
IV_TABLE,
|
||||
VT_TABLE,
|
||||
RAMP,
|
||||
@@ -772,6 +773,7 @@ private:
|
||||
bool readMatrixSparse( std::string, IBIS_MATRIX_SPARSE& aDest );
|
||||
bool readRampdvdt( dvdtTypMinMax& aDest );
|
||||
bool readRamp();
|
||||
bool readModelSpec();
|
||||
bool readWaveform( IbisWaveform* aDest, IBIS_WAVEFORM_TYPE aType );
|
||||
bool readString( std::string& aDest );
|
||||
bool storeString( std::string& aDest, bool aMultiline );
|
||||
|
||||
@@ -187,17 +187,20 @@ bool SIM_STRING_PROPERTY::StringToValue( wxVariant& aVariant, const wxString& aT
|
||||
|
||||
wxString text = aText;
|
||||
|
||||
if( !aText.IsEmpty() && allowEval() && m_needsEval && m_eval.Process( aText ) )
|
||||
if( allowEval() && m_needsEval && m_eval.Process( aText ) )
|
||||
{
|
||||
double value = SIM_VALUE::ToDouble( m_eval.Result().ToStdString() );
|
||||
if( !aText.IsEmpty() )
|
||||
{
|
||||
double value = SIM_VALUE::ToDouble( m_eval.Result().ToStdString() );
|
||||
|
||||
if( isnan( value ) || SIM_VALUE::Equal( value, aText.ToStdString() ) )
|
||||
{
|
||||
// Don't mess up user formatting if eval'ing didn't actually change the value.
|
||||
}
|
||||
else
|
||||
{
|
||||
text = SIM_VALUE::Normalize( value );
|
||||
if( std::isnan( value ) || SIM_VALUE::Equal( value, aText.ToStdString() ) )
|
||||
{
|
||||
// Don't mess up user formatting if eval'ing didn't actually change the value.
|
||||
}
|
||||
else
|
||||
{
|
||||
text = SIM_VALUE::Normalize( value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,14 @@ std::vector<SYMBOL_ASYNC_LOADER::LOADED_PAIR> SYMBOL_ASYNC_LOADER::worker()
|
||||
std::lock_guard<std::mutex> lock( m_errorMutex );
|
||||
m_errors += msg;
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
wxString msg = wxString::Format( _( "Error loading symbol library %s.\n\n%s\n" ),
|
||||
nickname, e.what() );
|
||||
|
||||
std::lock_guard<std::mutex> lock( m_errorMutex );
|
||||
m_errors += msg;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1956,6 +1956,9 @@ void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGro
|
||||
aChild->ClearSelected();
|
||||
else if( aMode == BRIGHTENED )
|
||||
aChild->ClearBrightened();
|
||||
|
||||
if( aGroup )
|
||||
aGroup->Remove( aChild );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
@@ -76,12 +76,7 @@ SCH_DRAWING_TOOLS::SCH_DRAWING_TOOLS() :
|
||||
m_lastTextboxStroke( 0, PLOT_DASH_TYPE::DEFAULT, COLOR4D::UNSPECIFIED ),
|
||||
m_mruPath( wxEmptyString ),
|
||||
m_lastAutoLabelRotateOnPlacement( false ),
|
||||
m_inPlaceSymbol( false ),
|
||||
m_inDrawShape( false ),
|
||||
m_inPlaceImage( false ),
|
||||
m_inSingleClickPlace( false ),
|
||||
m_inTwoClickPlace( false ),
|
||||
m_inDrawSheet( false )
|
||||
m_inDrawingTool( false )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -112,10 +107,10 @@ int SCH_DRAWING_TOOLS::PlaceSymbol( const TOOL_EVENT& aEvent )
|
||||
COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
|
||||
EE_GRID_HELPER grid( m_toolMgr );
|
||||
|
||||
if( m_inPlaceSymbol )
|
||||
if( m_inDrawingTool )
|
||||
return 0;
|
||||
|
||||
REENTRANCY_GUARD guard( &m_inPlaceSymbol );
|
||||
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
||||
|
||||
// First we need to get all instances of this sheet so we can annotate
|
||||
// whatever symbols we place on all copies
|
||||
@@ -473,10 +468,10 @@ int SCH_DRAWING_TOOLS::PlaceImage( const TOOL_EVENT& aEvent )
|
||||
bool ignorePrimePosition = false;
|
||||
COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
|
||||
|
||||
if( m_inPlaceImage )
|
||||
if( m_inDrawingTool )
|
||||
return 0;
|
||||
|
||||
REENTRANCY_GUARD guard( &m_inPlaceImage );
|
||||
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
||||
|
||||
m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
|
||||
|
||||
@@ -709,10 +704,10 @@ int SCH_DRAWING_TOOLS::SingleClickPlace( const TOOL_EVENT& aEvent )
|
||||
SCH_ITEM* previewItem;
|
||||
bool loggedInfoBarError = false;
|
||||
|
||||
if( m_inSingleClickPlace )
|
||||
if( m_inDrawingTool )
|
||||
return 0;
|
||||
|
||||
REENTRANCY_GUARD guard( &m_inSingleClickPlace );
|
||||
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
||||
|
||||
if( type == SCH_JUNCTION_T && aEvent.HasPosition() )
|
||||
{
|
||||
@@ -1129,10 +1124,10 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
|
||||
COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
|
||||
SCH_SHEET* sheet = nullptr;
|
||||
|
||||
if( m_inTwoClickPlace )
|
||||
if( m_inDrawingTool )
|
||||
return 0;
|
||||
|
||||
REENTRANCY_GUARD guard( &m_inTwoClickPlace );
|
||||
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
||||
|
||||
bool isText = aEvent.IsAction( &EE_ACTIONS::placeSchematicText );
|
||||
bool isGlobalLabel = aEvent.IsAction( &EE_ACTIONS::placeGlobalLabel );
|
||||
@@ -1520,10 +1515,10 @@ int SCH_DRAWING_TOOLS::DrawShape( const TOOL_EVENT& aEvent )
|
||||
bool isTextBox = aEvent.IsAction( &EE_ACTIONS::drawTextBox );
|
||||
SHAPE_T type = aEvent.Parameter<SHAPE_T>();
|
||||
|
||||
if( m_inDrawShape )
|
||||
if( m_inDrawingTool )
|
||||
return 0;
|
||||
|
||||
REENTRANCY_GUARD guard( &m_inDrawShape );
|
||||
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
||||
|
||||
// We might be running as the same shape in another co-routine. Make sure that one
|
||||
// gets whacked.
|
||||
@@ -1739,10 +1734,10 @@ int SCH_DRAWING_TOOLS::DrawSheet( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
SCH_SHEET* sheet = nullptr;
|
||||
|
||||
if( m_inDrawSheet )
|
||||
if( m_inDrawingTool )
|
||||
return 0;
|
||||
|
||||
REENTRANCY_GUARD guard( &m_inDrawSheet );
|
||||
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
||||
|
||||
m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
|
||||
|
||||
|
||||
@@ -94,14 +94,7 @@ private:
|
||||
wxString m_mruPath;
|
||||
bool m_lastAutoLabelRotateOnPlacement;
|
||||
|
||||
///< Re-entrancy guards
|
||||
bool m_inPlaceSymbol;
|
||||
bool m_inDrawShape;
|
||||
bool m_inPlaceImage;
|
||||
bool m_inSingleClickPlace;
|
||||
bool m_inTwoClickPlace;
|
||||
bool m_inDrawSheet;
|
||||
|
||||
bool m_inDrawingTool; // Re-entrancy guard
|
||||
std::unique_ptr<STATUS_TEXT_POPUP> m_statusPopup;
|
||||
};
|
||||
|
||||
|
||||
@@ -1119,7 +1119,28 @@ int SCH_EDIT_TOOL::RepeatDrawItem( const TOOL_EVENT& aEvent )
|
||||
schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_y ) ) );
|
||||
}
|
||||
|
||||
// If cloning a sheet, check that we aren't going to create recursion
|
||||
if( newItem->Type() == SCH_SHEET_T )
|
||||
{
|
||||
SCH_SHEET_PATH* currentSheet = &m_frame->GetCurrentSheet();
|
||||
SCH_SHEET* sheet = static_cast<SCH_SHEET*>( newItem );
|
||||
|
||||
if( m_frame->CheckSheetForRecursion( sheet, currentSheet ) )
|
||||
{
|
||||
// Clear out the filename so that the user can pick a new one
|
||||
sheet->SetFileName( wxEmptyString );
|
||||
|
||||
if( !m_frame->EditSheetProperties( sheet, currentSheet, nullptr ) )
|
||||
{
|
||||
// User canceled renaming the sheet; skip it
|
||||
delete newItem;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_toolMgr->RunAction( EE_ACTIONS::addItemToSel, true, newItem );
|
||||
|
||||
newItem->SetFlags( IS_NEW );
|
||||
m_frame->AddToScreen( newItem, m_frame->GetScreen() );
|
||||
m_frame->SaveCopyInUndoList( m_frame->GetScreen(), newItem, UNDO_REDO::NEWITEM, appendUndo );
|
||||
|
||||
@@ -332,10 +332,13 @@ void SCH_EDITOR_CONTROL::doCrossProbeSchToPcb( const TOOL_EVENT& aEvent, bool aF
|
||||
|
||||
int SCH_EDITOR_CONTROL::ExportSymbolsToLibrary( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
bool savePowerSymbols = IsOK( m_frame,
|
||||
_( "Include power symbols in schematic to the library?" ) );
|
||||
|
||||
bool createNew = aEvent.IsAction( &EE_ACTIONS::exportSymbolsToNewLibrary );
|
||||
|
||||
SCH_REFERENCE_LIST symbols;
|
||||
m_frame->Schematic().GetSheets().GetSymbols( symbols, false );
|
||||
m_frame->Schematic().GetSheets().GetSymbols( symbols, savePowerSymbols );
|
||||
|
||||
std::map<LIB_ID, LIB_SYMBOL*> libSymbols;
|
||||
std::map<LIB_ID, std::vector<SCH_SYMBOL*>> symbolMap;
|
||||
@@ -423,17 +426,29 @@ int SCH_EDITOR_CONTROL::ExportSymbolsToLibrary( const TOOL_EVENT& aEvent )
|
||||
LIB_SYMBOL* origSym = it.second;
|
||||
LIB_SYMBOL* newSym = origSym->Flatten().release();
|
||||
|
||||
pi->SaveSymbol( dest.GetFullPath(), newSym );
|
||||
try
|
||||
{
|
||||
pi->SaveSymbol( dest.GetFullPath(), newSym );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
wxString msg;
|
||||
msg.Printf( _( "Error saving symbol %s to library '%s'." ),
|
||||
newSym->GetName(), row->GetNickName() );
|
||||
msg += wxS( "\n\n" ) + ioe.What();
|
||||
wxLogWarning( msg );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( map )
|
||||
{
|
||||
LIB_ID id = it.first;
|
||||
id.SetLibNickname( targetLib );
|
||||
|
||||
|
||||
for( SCH_SYMBOL* symbol : symbolMap[it.first] )
|
||||
{
|
||||
m_frame->SaveCopyInUndoList( m_frame->GetScreen(), symbol, UNDO_REDO::CHANGED, append, false);
|
||||
m_frame->SaveCopyInUndoList( m_frame->GetScreen(), symbol, UNDO_REDO::CHANGED,
|
||||
append, false );
|
||||
symbol->SetLibId( id );
|
||||
append = true;
|
||||
}
|
||||
@@ -487,6 +502,7 @@ int SCH_EDITOR_CONTROL::ExportSymbolsToLibrary( const TOOL_EVENT& aEvent )
|
||||
for( SCH_SHEET_PATH& sheet : sheets )
|
||||
{
|
||||
SCH_SCREEN* screen = sheet.LastScreen();
|
||||
|
||||
if( processedScreens.find( ( screen ) ) == processedScreens.end() )
|
||||
{
|
||||
processedScreens.insert( screen );
|
||||
@@ -806,18 +822,27 @@ static bool highlightNet( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition )
|
||||
}
|
||||
}
|
||||
|
||||
wxString connectionName = ( conn ) ? conn->Name() : wxS( "" );
|
||||
wxString connectionName = ( conn ) ? conn->Name() : wxString( wxS( "" ) );
|
||||
|
||||
if( !conn || connectionName == editFrame->GetHighlightedConnection() )
|
||||
if( !conn )
|
||||
{
|
||||
editFrame->SetStatusText( wxT( "" ) );
|
||||
editFrame->SendCrossProbeClearHighlight();
|
||||
editFrame->SetHighlightedConnection( wxEmptyString );
|
||||
editorControl->SetHighlightBusMembers( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
editFrame->SetCrossProbeConnection( conn );
|
||||
editFrame->SetHighlightedConnection( connectionName );
|
||||
if( connectionName != editFrame->GetHighlightedConnection() )
|
||||
{
|
||||
editorControl->SetHighlightBusMembers( false );
|
||||
editFrame->SetCrossProbeConnection( conn );
|
||||
editFrame->SetHighlightedConnection( connectionName );
|
||||
}
|
||||
else
|
||||
{
|
||||
editorControl->SetHighlightBusMembers( !editorControl->GetHighlightBusMembers() );
|
||||
}
|
||||
}
|
||||
|
||||
editFrame->UpdateNetHighlightStatus();
|
||||
@@ -1014,155 +1039,162 @@ int SCH_EDITOR_CONTROL::AssignNetclass( const TOOL_EVENT& aEvent )
|
||||
|
||||
int SCH_EDITOR_CONTROL::UpdateNetHighlighting( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
wxCHECK( m_frame, 0 );
|
||||
|
||||
const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
|
||||
SCH_SCREEN* screen = m_frame->GetCurrentSheet().LastScreen();
|
||||
CONNECTION_GRAPH* connectionGraph = m_frame->Schematic().ConnectionGraph();
|
||||
wxString selectedName = m_frame->GetHighlightedConnection();
|
||||
|
||||
std::set<wxString> connNames;
|
||||
std::vector<EDA_ITEM*> itemsToRedraw;
|
||||
|
||||
wxCHECK( screen && connectionGraph, 0 );
|
||||
|
||||
bool selectedIsBus = false;
|
||||
wxString selectedName = m_frame->GetHighlightedConnection();
|
||||
|
||||
bool selectedIsNoNet = false;
|
||||
CONNECTION_SUBGRAPH* selectedSubgraph = nullptr;
|
||||
|
||||
for( SCH_ITEM* item : screen->Items() )
|
||||
if( !selectedName.IsEmpty() )
|
||||
{
|
||||
bool redraw = item->IsBrightened();
|
||||
bool highlight = false;
|
||||
connNames.emplace( selectedName );
|
||||
|
||||
if( !selectedName.IsEmpty() )
|
||||
CONNECTION_SUBGRAPH* sg = connectionGraph->FindSubgraphByName( selectedName, sheetPath );
|
||||
|
||||
if( sg && m_highlightBusMembers )
|
||||
{
|
||||
SCH_CONNECTION* itemConn = nullptr;
|
||||
SCH_SYMBOL* symbol = nullptr;
|
||||
|
||||
if( item->Type() == SCH_SYMBOL_T )
|
||||
symbol = static_cast<SCH_SYMBOL*>( item );
|
||||
|
||||
if( symbol && symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->IsPower() )
|
||||
itemConn = symbol->Connection();
|
||||
else
|
||||
itemConn = item->Connection();
|
||||
|
||||
if( itemConn && ( selectedName == itemConn->Name() ) )
|
||||
for( const SCH_ITEM* item : sg->GetItems() )
|
||||
{
|
||||
selectedIsBus = itemConn->IsBus();
|
||||
wxCHECK2( item, continue );
|
||||
|
||||
if( itemConn->Driver() == nullptr )
|
||||
{
|
||||
selectedIsNoNet = true;
|
||||
selectedSubgraph = connectionGraph->GetSubgraphForItem( itemConn->Parent() );
|
||||
}
|
||||
SCH_CONNECTION* connection = item->Connection();
|
||||
|
||||
if( selectedIsNoNet && selectedSubgraph )
|
||||
wxCHECK2( connection, continue );
|
||||
|
||||
for( const std::shared_ptr<SCH_CONNECTION>& member : connection->AllMembers() )
|
||||
{
|
||||
for( SCH_ITEM* subgraphItem : selectedSubgraph->GetItems() )
|
||||
{
|
||||
if( item == subgraphItem )
|
||||
{
|
||||
highlight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( selectedIsBus && itemConn && itemConn->IsNet() )
|
||||
{
|
||||
for( const std::shared_ptr<SCH_CONNECTION>& member : itemConn->Members() )
|
||||
{
|
||||
if( member->Name() == itemConn->Name() )
|
||||
{
|
||||
highlight = true;
|
||||
break;
|
||||
}
|
||||
else if( member->IsBus() )
|
||||
{
|
||||
for( const std::shared_ptr<SCH_CONNECTION>& bus_member :
|
||||
member->Members() )
|
||||
{
|
||||
if( bus_member->Name() == itemConn->Name() )
|
||||
{
|
||||
highlight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( !selectedName.IsEmpty() && itemConn
|
||||
&& ( selectedName == itemConn->Name() ) )
|
||||
{
|
||||
highlight = true;
|
||||
if( member )
|
||||
connNames.emplace( member->Name() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( highlight )
|
||||
item->SetBrightened();
|
||||
else
|
||||
item->ClearBrightened();
|
||||
for( SCH_ITEM* item : screen->Items() )
|
||||
{
|
||||
wxCHECK2( item, continue );
|
||||
|
||||
redraw |= item->IsBrightened();
|
||||
if( !item->IsConnectable() )
|
||||
continue;
|
||||
|
||||
SCH_ITEM* redrawItem = nullptr;
|
||||
|
||||
if( item->Type() == SCH_SYMBOL_T )
|
||||
{
|
||||
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
|
||||
|
||||
redraw |= symbol->HasBrightenedPins();
|
||||
|
||||
symbol->ClearBrightenedPins();
|
||||
wxCHECK2( symbol, continue );
|
||||
|
||||
for( SCH_PIN* pin : symbol->GetPins() )
|
||||
{
|
||||
SCH_CONNECTION* pin_conn = pin->Connection();
|
||||
|
||||
if( pin_conn && pin_conn->Name() == selectedName )
|
||||
wxCHECK2( pin_conn, continue );
|
||||
|
||||
if( !pin->IsBrightened() && connNames.count( pin_conn->Name() ) )
|
||||
{
|
||||
pin->SetBrightened();
|
||||
redraw = true;
|
||||
redrawItem = symbol;
|
||||
}
|
||||
else if( pin->IsBrightened() && !connNames.count( pin_conn->Name() ) )
|
||||
{
|
||||
pin->ClearBrightened();
|
||||
redrawItem = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
if( symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->IsPower() )
|
||||
if( symbol->IsPower() )
|
||||
{
|
||||
wxCHECK2( symbol->GetPins().size(), continue );
|
||||
|
||||
SCH_CONNECTION* pinConn = symbol->GetPins()[0]->Connection();
|
||||
|
||||
wxCHECK2( pinConn, continue );
|
||||
|
||||
std::vector<SCH_FIELD>& fields = symbol->GetFields();
|
||||
|
||||
for( int id : { REFERENCE_FIELD, VALUE_FIELD } )
|
||||
{
|
||||
if( item->IsBrightened() && fields[id].IsVisible() )
|
||||
if( !fields[id].IsVisible() )
|
||||
continue;
|
||||
|
||||
if( !fields[id].IsBrightened() && connNames.count( pinConn->Name() ) )
|
||||
{
|
||||
fields[id].SetBrightened();
|
||||
else
|
||||
redrawItem = symbol;
|
||||
}
|
||||
else if( fields[id].IsBrightened() && !connNames.count( pinConn->Name() ) )
|
||||
{
|
||||
fields[id].ClearBrightened();
|
||||
redrawItem = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( item->Type() == SCH_SHEET_T )
|
||||
{
|
||||
for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
|
||||
SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
|
||||
|
||||
wxCHECK2( sheet, continue );
|
||||
|
||||
for( SCH_SHEET_PIN* pin : sheet->GetPins() )
|
||||
{
|
||||
wxCHECK2( pin, continue );
|
||||
|
||||
SCH_CONNECTION* pin_conn = pin->Connection();
|
||||
bool redrawPin = pin->IsBrightened();
|
||||
|
||||
if( pin_conn && pin_conn->Name() == selectedName )
|
||||
wxCHECK2( pin_conn, continue );
|
||||
|
||||
if( !pin->IsBrightened() && connNames.count( pin_conn->Name() ) )
|
||||
{
|
||||
pin->SetBrightened();
|
||||
else
|
||||
redrawItem = sheet;
|
||||
}
|
||||
else if( pin->IsBrightened() && !connNames.count( pin_conn->Name() ) )
|
||||
{
|
||||
pin->ClearBrightened();
|
||||
redrawItem = sheet;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SCH_CONNECTION* itemConn = item->Connection();
|
||||
|
||||
redrawPin ^= pin->IsBrightened();
|
||||
redraw |= redrawPin;
|
||||
wxCHECK2( itemConn, continue );
|
||||
|
||||
if( !item->IsBrightened() && connNames.count( itemConn->Name() ) )
|
||||
{
|
||||
item->SetBrightened();
|
||||
redrawItem = item;
|
||||
}
|
||||
else if( item->IsBrightened() && !connNames.count( itemConn->Name() ) )
|
||||
{
|
||||
item->ClearBrightened();
|
||||
redrawItem = item;
|
||||
}
|
||||
}
|
||||
|
||||
if( redraw )
|
||||
itemsToRedraw.push_back( item );
|
||||
if( redrawItem )
|
||||
itemsToRedraw.push_back( redrawItem );
|
||||
}
|
||||
|
||||
// Be sure highlight change will be redrawn
|
||||
KIGFX::VIEW* view = getView();
|
||||
if( itemsToRedraw.size() )
|
||||
{
|
||||
// Be sure highlight change will be redrawn
|
||||
KIGFX::VIEW* view = getView();
|
||||
|
||||
for( EDA_ITEM* redrawItem : itemsToRedraw )
|
||||
view->Update( (KIGFX::VIEW_ITEM*)redrawItem, KIGFX::VIEW_UPDATE_FLAGS::REPAINT );
|
||||
for( EDA_ITEM* redrawItem : itemsToRedraw )
|
||||
view->Update( (KIGFX::VIEW_ITEM*)redrawItem, KIGFX::VIEW_UPDATE_FLAGS::REPAINT );
|
||||
|
||||
m_frame->GetCanvas()->Refresh();
|
||||
m_frame->GetCanvas()->Refresh();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1374,19 +1406,19 @@ void SCH_EDITOR_CONTROL::updatePastedSymbol( SCH_SYMBOL* aSymbol, SCH_SCREEN* aP
|
||||
const KIID_PATH& aClipPath,
|
||||
bool aForceKeepAnnotations )
|
||||
{
|
||||
wxCHECK( aSymbol && aPasteScreen, /* void */ );
|
||||
wxCHECK( m_frame && aSymbol && aPasteScreen, /* void */ );
|
||||
|
||||
KIID_PATH clipItemPath = aClipPath;
|
||||
SCH_SYMBOL_INSTANCE instance;
|
||||
KIID_PATH pasteLookupPath = aClipPath;
|
||||
|
||||
wxString reference, value, footprint;
|
||||
int unit;
|
||||
m_pastedSymbols.insert( aSymbol );
|
||||
|
||||
if( m_clipboardSymbolInstances.count( clipItemPath ) > 0 )
|
||||
// The pasted symbol look up paths include the symbol UUID.
|
||||
pasteLookupPath.push_back( aSymbol->m_Uuid );
|
||||
|
||||
if( m_clipboardSymbolInstances.count( pasteLookupPath ) > 0 )
|
||||
{
|
||||
SCH_SYMBOL_INSTANCE instance = m_clipboardSymbolInstances.at( clipItemPath );
|
||||
|
||||
unit = instance.m_Unit;
|
||||
reference = instance.m_Reference;
|
||||
instance = m_clipboardSymbolInstances.at( pasteLookupPath );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1397,20 +1429,20 @@ void SCH_EDITOR_CONTROL::updatePastedSymbol( SCH_SYMBOL* aSymbol, SCH_SCREEN* aP
|
||||
|
||||
// Pasted from notepad or an older instance of eeschema. Use the values in the fields
|
||||
// instead.
|
||||
reference = aSymbol->GetField( REFERENCE_FIELD )->GetText();
|
||||
value = aSymbol->GetField( VALUE_FIELD )->GetText();
|
||||
footprint = aSymbol->GetField( FOOTPRINT_FIELD )->GetText();
|
||||
unit = aSymbol->GetUnit();
|
||||
instance.m_Reference = aSymbol->GetField( REFERENCE_FIELD )->GetText();
|
||||
instance.m_Unit = aSymbol->GetUnit();
|
||||
}
|
||||
|
||||
if( aForceKeepAnnotations && !reference.IsEmpty() )
|
||||
aSymbol->SetRef( &aPastePath, reference );
|
||||
else
|
||||
instance.m_Path = aPastePath.Path() + aClipPath;
|
||||
instance.m_ProjectName = m_frame->Prj().GetProjectName();
|
||||
|
||||
aSymbol->AddHierarchicalReference( instance );
|
||||
|
||||
if( !aForceKeepAnnotations )
|
||||
aSymbol->ClearAnnotation( &aPastePath, false );
|
||||
|
||||
// We might clear annotations but always leave the original unit number from the paste.
|
||||
aSymbol->SetUnitSelection( &aPastePath, unit );
|
||||
aSymbol->SetUnit( unit );
|
||||
aSymbol->SetUnit( instance.m_Unit );
|
||||
}
|
||||
|
||||
|
||||
@@ -1460,17 +1492,82 @@ SCH_SHEET_PATH SCH_EDITOR_CONTROL::updatePastedSheet( const SCH_SHEET_PATH& aPas
|
||||
}
|
||||
|
||||
|
||||
void SCH_EDITOR_CONTROL::setClipboardInstances( const SCH_SCREEN* aPastedScreen )
|
||||
void SCH_EDITOR_CONTROL::setPastedSheetInstances( const SCH_SHEET* aPastedSheet )
|
||||
{
|
||||
m_clipboardSheetInstances.clear();
|
||||
wxCHECK( aPastedSheet, /* void */ );
|
||||
|
||||
for( const SCH_SHEET_INSTANCE& sheet : aPastedScreen->GetSheetInstances() )
|
||||
m_clipboardSheetInstances[sheet.m_Path] = sheet;
|
||||
for( const SCH_SHEET_INSTANCE& sheetInstance : aPastedSheet->GetInstances() )
|
||||
{
|
||||
KIID_PATH pathWithSheet = sheetInstance.m_Path;
|
||||
|
||||
m_clipboardSymbolInstances.clear();
|
||||
pathWithSheet.push_back( aPastedSheet->m_Uuid );
|
||||
m_clipboardSheetInstances[pathWithSheet] = sheetInstance;
|
||||
}
|
||||
|
||||
for( const SCH_SYMBOL_INSTANCE& symbol : aPastedScreen->GetSymbolInstances() )
|
||||
m_clipboardSymbolInstances[symbol.m_Path] = symbol;
|
||||
const SCH_SCREEN* screen = aPastedSheet->GetScreen();
|
||||
|
||||
wxCHECK( screen, /* void */ );
|
||||
|
||||
for( SCH_ITEM* item : screen->Items() )
|
||||
{
|
||||
if( item->Type() == SCH_SHEET_T )
|
||||
{
|
||||
const SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
|
||||
|
||||
wxCHECK2( sheet, continue );
|
||||
|
||||
setPastedSheetInstances( sheet );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_EDITOR_CONTROL::setPastedSymbolInstances( SCH_SCREENS& aScreenList )
|
||||
{
|
||||
for( SCH_SCREEN* screen = aScreenList.GetFirst(); screen; screen = aScreenList.GetNext() )
|
||||
{
|
||||
for( SCH_ITEM* item : screen->Items() )
|
||||
{
|
||||
if( item->Type() == SCH_SYMBOL_T )
|
||||
{
|
||||
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
|
||||
|
||||
wxCHECK2( symbol, continue );
|
||||
|
||||
for( const SCH_SYMBOL_INSTANCE& symbolInstance : symbol->GetInstanceReferences() )
|
||||
{
|
||||
KIID_PATH pathWithSymbol = symbolInstance.m_Path;
|
||||
|
||||
pathWithSymbol.push_back( symbol->m_Uuid );
|
||||
|
||||
m_clipboardSymbolInstances[pathWithSymbol] = symbolInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_EDITOR_CONTROL::prunePastedSymbolInstances()
|
||||
{
|
||||
wxCHECK( m_frame, /* void */ );
|
||||
|
||||
for( SCH_SYMBOL* symbol : m_pastedSymbols )
|
||||
{
|
||||
wxCHECK2( symbol, continue );
|
||||
|
||||
std::vector<KIID_PATH> instancePathsToRemove;
|
||||
|
||||
for( const SCH_SYMBOL_INSTANCE& instance : symbol->GetInstanceReferences() )
|
||||
{
|
||||
if( ( instance.m_ProjectName != m_frame->Prj().GetProjectName() )
|
||||
|| instance.m_Path.empty() )
|
||||
instancePathsToRemove.emplace_back( instance.m_Path );
|
||||
}
|
||||
|
||||
for( const KIID_PATH& path : instancePathsToRemove )
|
||||
symbol->RemoveInstance( path );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1523,8 +1620,15 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
||||
tempScreen->Append( text_item );
|
||||
}
|
||||
|
||||
// Save loaded screen instances to m_clipboardSheetInstances
|
||||
setClipboardInstances( tempScreen );
|
||||
SCH_SCREENS tempScreens( tempSheet );
|
||||
|
||||
m_pastedSymbols.clear();
|
||||
m_clipboardSheetInstances.clear();
|
||||
m_clipboardSymbolInstances.clear();
|
||||
|
||||
// Save pasted sheet and symbol instances.
|
||||
setPastedSheetInstances( &tempSheet );
|
||||
setPastedSymbolInstances( tempScreens );
|
||||
|
||||
tempScreen->MigrateSimModels();
|
||||
|
||||
@@ -1870,6 +1974,16 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
||||
|
||||
m_frame->GetCurrentSheet().UpdateAllScreenReferences();
|
||||
|
||||
prunePastedSymbolInstances();
|
||||
|
||||
// The copy operation creates instance paths that are not valid for the current project or
|
||||
// saved as part of another project. Prune them now so they do not accumulate in the saved
|
||||
// schematic file.
|
||||
SCH_SCREENS allScreens( m_frame->Schematic().Root() );
|
||||
|
||||
allScreens.PruneOrphanedSymbolInstances( m_frame->Prj().GetProjectName(),
|
||||
m_frame->Schematic().GetSheets() );
|
||||
|
||||
// Now clear the previous selection, select the pasted items, and fire up the "move" tool.
|
||||
m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
|
||||
m_toolMgr->RunAction( EE_ACTIONS::addItemsToSel, true, &loadedItems );
|
||||
|
||||
@@ -42,7 +42,8 @@ public:
|
||||
EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.EditorControl" ),
|
||||
m_probingPcbToSch( false ),
|
||||
m_pickerItem( nullptr ),
|
||||
m_duplicateIsHoverSelection( false )
|
||||
m_duplicateIsHoverSelection( false ),
|
||||
m_highlightBusMembers( false )
|
||||
{ }
|
||||
|
||||
~SCH_EDITOR_CONTROL() { }
|
||||
@@ -157,6 +158,13 @@ public:
|
||||
bool aSearchHierarchy, SCH_SEARCH_T aSearchType,
|
||||
const wxString& aSearchText );
|
||||
|
||||
void SetHighlightBusMembers( bool aHighlightBusMembers )
|
||||
{
|
||||
m_highlightBusMembers = aHighlightBusMembers;
|
||||
}
|
||||
|
||||
bool GetHighlightBusMembers() const { return m_highlightBusMembers; }
|
||||
|
||||
private:
|
||||
///< copy selection to clipboard or to m_duplicateClipboard
|
||||
bool doCopy( bool aUseDuplicateClipboard = false );
|
||||
@@ -176,7 +184,18 @@ private:
|
||||
SCH_SHEET_LIST* aPastedSheetsSoFar,
|
||||
SCH_REFERENCE_LIST* aPastedSymbolsSoFar );
|
||||
|
||||
void setClipboardInstances( const SCH_SCREEN* aPastedScreen );
|
||||
void setPastedSheetInstances( const SCH_SHEET* aPastedSheet );
|
||||
void setPastedSymbolInstances( SCH_SCREENS& aScreenList );
|
||||
|
||||
/**
|
||||
* Remove all pasted symbol instances that do not belong to the current project.
|
||||
*
|
||||
* @warning This should **only** be called when cleaning up after a paste. Otherwise it
|
||||
* could clobber symbol instances for schematics shared across projects. Use
|
||||
* SCH_SCREENS::PruneOrphanedSymbolInstances() to clean up invalid instance for
|
||||
* the current project.
|
||||
*/
|
||||
void prunePastedSymbolInstances();
|
||||
|
||||
/**
|
||||
* Read the footprint info from each line in the stuff file by reference designator.
|
||||
@@ -211,6 +230,8 @@ private:
|
||||
std::string m_duplicateClipboard; // Temporary storage for Duplicate action
|
||||
bool m_duplicateIsHoverSelection;
|
||||
|
||||
bool m_highlightBusMembers;
|
||||
|
||||
// A map of sheet filename --> screens for the clipboard contents. We use these to hook up
|
||||
// cut/paste operations for unsaved sheet content.
|
||||
std::map<wxString, SCH_SCREEN*> m_supplementaryClipboard;
|
||||
@@ -220,6 +241,8 @@ private:
|
||||
|
||||
// A map of KIID_PATH --> sheet instances for the clipboard contents.
|
||||
std::map<KIID_PATH, SCH_SHEET_INSTANCE> m_clipboardSheetInstances;
|
||||
|
||||
std::set<SCH_SYMBOL*> m_pastedSymbols;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -937,6 +937,11 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
|
||||
m_view->AddToPreview( wire->Clone() );
|
||||
}
|
||||
}
|
||||
else if( evt->IsAction( &ACTIONS::undo ) )
|
||||
{
|
||||
// Dispatch as normal undo event
|
||||
evt->SetPassEvent();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxBell();
|
||||
|
||||
@@ -823,7 +823,7 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||
}
|
||||
else if( evt->IsAction( &ACTIONS::duplicate ) )
|
||||
{
|
||||
if( selection.Front()->IsNew() )
|
||||
if( selection.Front() && selection.Front()->IsNew() )
|
||||
{
|
||||
// This doesn't really make sense; we'll just end up dragging a stack of
|
||||
// objects so we ignore the duplicate and just carry on.
|
||||
|
||||
@@ -92,6 +92,7 @@ void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
||||
SHAPE_POLY_SET& aShapeBuffer )
|
||||
{
|
||||
// Draw the primitive shape for flashed items.
|
||||
// Note: rotation of primitives inside a macro must be always done around the macro origin.
|
||||
// Create a static buffer to avoid a lot of memory reallocation.
|
||||
static std::vector<VECTOR2I> polybuffer;
|
||||
polybuffer.clear();
|
||||
@@ -218,15 +219,17 @@ void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
||||
for( int ii = 0; ii < 4; ii++ )
|
||||
{
|
||||
polybuffer = subshape_poly;
|
||||
EDA_ANGLE sub_rotation = rotation + ANGLE_90 * ii;
|
||||
EDA_ANGLE sub_rotation = ANGLE_90 * ii;
|
||||
|
||||
for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
|
||||
RotatePoint( polybuffer[jj], -sub_rotation );
|
||||
|
||||
// Move to center position given by the tool:
|
||||
// Move to center position given by the tool, and rotate the full shape around
|
||||
// the center position (origin of the macro):
|
||||
for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
|
||||
{
|
||||
polybuffer[jj] += center;
|
||||
RotatePoint( polybuffer[jj], -rotation );
|
||||
}
|
||||
|
||||
aShapeBuffer.NewOutline();
|
||||
@@ -264,6 +267,8 @@ void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
||||
VECTOR2I center( mapPt( m_Params[0].GetValueFromMacro( aApertMacro ),
|
||||
m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric ) );
|
||||
|
||||
EDA_ANGLE rotation( m_Params[8].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
||||
|
||||
// adjust outerDiam by this on each nested circle
|
||||
int diamAdjust = ( gap + penThickness ) * 2;
|
||||
|
||||
@@ -272,17 +277,21 @@ void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
||||
if( outerDiam <= 0 )
|
||||
break;
|
||||
|
||||
// calculate the rotated position of the center:
|
||||
VECTOR2I circle_center = center;
|
||||
RotatePoint( circle_center, -rotation );
|
||||
|
||||
// Note: outerDiam is the outer diameter of the ring.
|
||||
// the ring graphic diameter is (outerDiam - penThickness)
|
||||
if( outerDiam <= penThickness )
|
||||
{ // No room to draw a ring (no room for the hole):
|
||||
// draw a circle instead (with no hole), with the right diameter
|
||||
TransformCircleToPolygon( aShapeBuffer, center, outerDiam / 2, arc_to_seg_error,
|
||||
TransformCircleToPolygon( aShapeBuffer, circle_center, outerDiam / 2, arc_to_seg_error,
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
else
|
||||
{
|
||||
TransformRingToPolygon( aShapeBuffer, center, ( outerDiam - penThickness ) / 2,
|
||||
TransformRingToPolygon( aShapeBuffer, circle_center, ( outerDiam - penThickness ) / 2,
|
||||
penThickness, arc_to_seg_error, ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
@@ -290,13 +299,11 @@ void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
||||
// Draw the cross:
|
||||
ConvertShapeToPolygon( aApertMacro, polybuffer );
|
||||
|
||||
EDA_ANGLE rotation( m_Params[8].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
||||
|
||||
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
||||
{
|
||||
// move crossair shape to center and rotate shape:
|
||||
RotatePoint( polybuffer[ii], -rotation );
|
||||
polybuffer[ii] += center;
|
||||
RotatePoint( polybuffer[ii], -rotation );
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -375,13 +382,13 @@ void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
||||
// Creates the shape:
|
||||
ConvertShapeToPolygon( aApertMacro, polybuffer );
|
||||
|
||||
// rotate polygon
|
||||
// move and rotate polygonal shape
|
||||
EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
||||
|
||||
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
||||
{
|
||||
RotatePoint( polybuffer[ii], -rotation );
|
||||
polybuffer[ii] += curPos;
|
||||
RotatePoint( polybuffer[ii], -rotation );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -99,6 +99,10 @@ public:
|
||||
// and the actual coordinates calculation must handle this
|
||||
};
|
||||
|
||||
// size of a single line of text from a gerber file.
|
||||
// warning: some files can have *very long* lines, so the buffer must be large.
|
||||
#define GERBER_BUFZ 1000000
|
||||
|
||||
/**
|
||||
* Hold the image data and parameters for one gerber file and layer parameters.
|
||||
*
|
||||
@@ -455,6 +459,9 @@ public:
|
||||
GERBER_LAYER m_GBRLayerParams; // hold params for the current gerber layer
|
||||
GERBER_DRAW_ITEMS m_drawings; // linked list of Gerber Items to draw
|
||||
|
||||
// A large buffer to store one line
|
||||
static char m_LineBuffer[GERBER_BUFZ+1];
|
||||
|
||||
private:
|
||||
wxArrayString m_messagesList; // A list of messages created when reading a file
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Jean-Pierre Charras jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 1992-2023 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
|
||||
@@ -223,11 +223,9 @@ bool GERBER_FILE_IMAGE::TestFileIsRS274( const wxString& aFullFileName )
|
||||
return false;
|
||||
}
|
||||
|
||||
// size of a single line of text from a gerber file.
|
||||
// warning: some files can have *very long* lines, so the buffer must be large.
|
||||
#define GERBER_BUFZ 1000000
|
||||
|
||||
// A large buffer to store one line
|
||||
static char lineBuffer[GERBER_BUFZ+1];
|
||||
char GERBER_FILE_IMAGE::m_LineBuffer[GERBER_BUFZ+1];
|
||||
|
||||
bool GERBER_FILE_IMAGE::LoadGerberFile( const wxString& aFullFileName )
|
||||
{
|
||||
@@ -252,11 +250,11 @@ bool GERBER_FILE_IMAGE::LoadGerberFile( const wxString& aFullFileName )
|
||||
|
||||
while( true )
|
||||
{
|
||||
if( fgets( lineBuffer, GERBER_BUFZ, m_Current_File ) == nullptr )
|
||||
if( fgets( m_LineBuffer, GERBER_BUFZ, m_Current_File ) == nullptr )
|
||||
break;
|
||||
|
||||
m_LineNum++;
|
||||
text = StrPurge( lineBuffer );
|
||||
text = StrPurge( m_LineBuffer );
|
||||
|
||||
while( text && *text )
|
||||
{
|
||||
@@ -313,7 +311,7 @@ bool GERBER_FILE_IMAGE::LoadGerberFile( const wxString& aFullFileName )
|
||||
if( m_CommandState != ENTER_RS274X_CMD )
|
||||
{
|
||||
m_CommandState = ENTER_RS274X_CMD;
|
||||
ReadRS274XCommand( lineBuffer, GERBER_BUFZ, text );
|
||||
ReadRS274XCommand( m_LineBuffer, GERBER_BUFZ, text );
|
||||
}
|
||||
else //Error
|
||||
{
|
||||
|
||||
+6
-7
@@ -1,8 +1,3 @@
|
||||
/**
|
||||
* @file rs274d.cpp
|
||||
* @brief functions to read the rs274d commands from a rs274d/rs274x file
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
@@ -26,6 +21,11 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief functions to read the rs274d commands from a rs274d/rs274x file
|
||||
*/
|
||||
|
||||
|
||||
#include <gerbview.h>
|
||||
#include <gerbview_frame.h>
|
||||
#include <trigo.h>
|
||||
@@ -464,8 +464,7 @@ bool GERBER_FILE_IMAGE::Execute_G_Command( char*& text, int G_command )
|
||||
ExecuteRS274XCommand( code_command, nullptr, 0, cptr );
|
||||
}
|
||||
|
||||
while( *text && (*text != '*') )
|
||||
text++;
|
||||
GetEndOfBlock( m_LineBuffer, GERBER_BUFZ, text, m_Current_File );
|
||||
|
||||
break;
|
||||
|
||||
|
||||
+2
-6
@@ -2,7 +2,7 @@
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2007-2018 Jean-Pierre Charras jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 1992-2023 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
|
||||
@@ -22,9 +22,6 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file rs274x.cpp
|
||||
*/
|
||||
|
||||
#include <base_units.h>
|
||||
#include <math/util.h> // for KiROUND
|
||||
@@ -1049,8 +1046,7 @@ bool GERBER_FILE_IMAGE::ReadApertureMacro( char *aBuff, unsigned int aBuffSize,
|
||||
is_comment = true;
|
||||
|
||||
// Skip comment
|
||||
while( *aText && ( *aText != '*' ) )
|
||||
aText++;
|
||||
GetEndOfBlock( m_LineBuffer, GERBER_BUFZ, aText, m_Current_File );
|
||||
|
||||
break;
|
||||
|
||||
|
||||
@@ -45,11 +45,14 @@ class DIALOG_PASTE_SPECIAL : public DIALOG_PASTE_SPECIAL_BASE
|
||||
|
||||
public:
|
||||
DIALOG_PASTE_SPECIAL( wxWindow* aParent, PASTE_MODE* aMode,
|
||||
wxString aReplacement = wxT( "?" ) );
|
||||
const wxString& aReplacement = wxS( "?" ) );
|
||||
|
||||
bool TransferDataToWindow() override;
|
||||
bool TransferDataFromWindow() override;
|
||||
|
||||
protected:
|
||||
virtual void onRadioBoxEvent( wxCommandEvent& event ) override;
|
||||
|
||||
private:
|
||||
PASTE_MODE* m_mode;
|
||||
};
|
||||
|
||||
+15
-1
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2020 Ian McInerney <ian.s.mcinerney@ieee.org>
|
||||
* Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.TXT for contributors.
|
||||
* Copyright (C) 1992-2023 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
|
||||
@@ -190,6 +190,20 @@ public:
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
KIID_PATH& operator+=( const KIID_PATH& aRhs )
|
||||
{
|
||||
for( const KIID& kiid : aRhs )
|
||||
emplace_back( kiid );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend KIID_PATH operator+( KIID_PATH aLhs, const KIID_PATH& aRhs )
|
||||
{
|
||||
aLhs += aRhs;
|
||||
return aLhs;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,11 +111,12 @@ int CLI::EXPORT_PCB_DRILL_COMMAND::doPerform( KIWAY& aKiway )
|
||||
}
|
||||
|
||||
wxString format = FROM_UTF8( m_argParser.get<std::string>( ARG_FORMAT ).c_str() );
|
||||
if( format == "excellon" )
|
||||
|
||||
if( format == wxS( "excellon" ) )
|
||||
{
|
||||
drillJob->m_format = JOB_EXPORT_PCB_DRILL::DRILL_FORMAT::EXCELLON;
|
||||
}
|
||||
else if( format == "gerber" )
|
||||
else if( format == wxS( "gerber" ) )
|
||||
{
|
||||
drillJob->m_format = JOB_EXPORT_PCB_DRILL::DRILL_FORMAT::GERBER;
|
||||
}
|
||||
@@ -229,6 +230,7 @@ int CLI::EXPORT_PCB_DRILL_COMMAND::doPerform( KIWAY& aKiway )
|
||||
drillJob->m_excellonMinimalHeader = m_argParser.get<bool>( ARG_EXCELLON_MINIMALHEAD );
|
||||
drillJob->m_excellonCombinePTHNPTH = !m_argParser.get<bool>( ARG_EXCELLON_SEPARATE_TH );
|
||||
drillJob->m_generateMap = m_argParser.get<bool>( ARG_GENERATE_MAP );
|
||||
drillJob->m_gerberPrecision = m_argParser.get<int>( ARG_GERBER_PRECISION );
|
||||
|
||||
if( drillJob->m_gerberPrecision != 5 && drillJob->m_gerberPrecision != 6 )
|
||||
{
|
||||
|
||||
+4
-4
@@ -215,7 +215,7 @@ PLUGIN_CONTENT_MANAGER::PLUGIN_CONTENT_MANAGER(
|
||||
std::for_each( m_installed.begin(), m_installed.end(),
|
||||
[&]( std::pair<const wxString, PCM_INSTALLATION_ENTRY>& entry )
|
||||
{
|
||||
preparePackage( entry.second.package );
|
||||
PreparePackage( entry.second.package );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -478,7 +478,7 @@ const bool PLUGIN_CONTENT_MANAGER::CacheRepository( const wxString& aRepositoryI
|
||||
|
||||
for( size_t i = 0; i < saved_repo.package_list.size(); i++ )
|
||||
{
|
||||
preparePackage( saved_repo.package_list[i] );
|
||||
PreparePackage( saved_repo.package_list[i] );
|
||||
saved_repo.package_map[saved_repo.package_list[i].identifier] = i;
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ const bool PLUGIN_CONTENT_MANAGER::CacheRepository( const wxString& aRepositoryI
|
||||
|
||||
for( size_t i = 0; i < current_repo.package_list.size(); i++ )
|
||||
{
|
||||
preparePackage( current_repo.package_list[i] );
|
||||
PreparePackage( current_repo.package_list[i] );
|
||||
current_repo.package_map[current_repo.package_list[i].identifier] = i;
|
||||
}
|
||||
|
||||
@@ -648,7 +648,7 @@ void PLUGIN_CONTENT_MANAGER::updateInstalledPackagesMetadata( const wxString& aR
|
||||
}
|
||||
|
||||
|
||||
void PLUGIN_CONTENT_MANAGER::preparePackage( PCM_PACKAGE& aPackage )
|
||||
void PLUGIN_CONTENT_MANAGER::PreparePackage( PCM_PACKAGE& aPackage )
|
||||
{
|
||||
// Parse package version strings
|
||||
for( PACKAGE_VERSION& ver : aPackage.versions )
|
||||
|
||||
+10
-10
@@ -341,6 +341,16 @@ public:
|
||||
*/
|
||||
void ReadEnvVar();
|
||||
|
||||
/**
|
||||
* @brief Parses version strings and calculates compatibility
|
||||
*
|
||||
* This should be called after loading package metadata from repository or from
|
||||
* installation entries
|
||||
*
|
||||
* @param aPackage package metadata object
|
||||
*/
|
||||
static void PreparePackage( PCM_PACKAGE& aPackage );
|
||||
|
||||
private:
|
||||
///< Default download limit of 10 Mb to not use too much memory
|
||||
static constexpr size_t DEFAULT_DOWNLOAD_MEM_LIMIT = 10 * 1024 * 1024;
|
||||
@@ -377,16 +387,6 @@ private:
|
||||
*/
|
||||
void updateInstalledPackagesMetadata( const wxString& aRepositoryId );
|
||||
|
||||
/**
|
||||
* @brief Parses version strings and calculates compatibility
|
||||
*
|
||||
* This should be called after loading package metadata from repository or from
|
||||
* installation entries
|
||||
*
|
||||
* @param aPackage package metadata object
|
||||
*/
|
||||
static void preparePackage( PCM_PACKAGE& aPackage );
|
||||
|
||||
///< Returns current UTC timestamp
|
||||
time_t getCurrentTimestamp() const;
|
||||
|
||||
|
||||
@@ -371,6 +371,7 @@ void PCM_TASK_MANAGER::InstallFromFile( wxWindow* aParent, const wxString& aFile
|
||||
}
|
||||
|
||||
PCM_PACKAGE package = metadata.get<PCM_PACKAGE>();
|
||||
PLUGIN_CONTENT_MANAGER::PreparePackage( package );
|
||||
|
||||
if( package.versions.size() != 1 )
|
||||
{
|
||||
@@ -378,6 +379,15 @@ void PCM_TASK_MANAGER::InstallFromFile( wxWindow* aParent, const wxString& aFile
|
||||
return;
|
||||
}
|
||||
|
||||
if( !package.versions[0].compatible
|
||||
&& wxMessageBox( _( "This package version is incompatible with your kicad version or "
|
||||
"platform. Are you sure you want to install it anyway?" ),
|
||||
_( "Install package" ), wxICON_EXCLAMATION | wxYES_NO, aParent )
|
||||
== wxNO )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isUpdate = false;
|
||||
// wxRegEx is not CopyConstructible hence the weird choice of forward_list
|
||||
std::forward_list<wxRegEx> keep_on_update;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012 Brian Sidebotham <brian.sidebotham@gmail.com>
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 1992-2023 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
|
||||
@@ -81,7 +81,11 @@ public:
|
||||
|
||||
virtual wxDirTraverseResult OnFile( const wxString& filename ) override
|
||||
{
|
||||
if( !filename.StartsWith( m_exclude ) )
|
||||
wxFileName fn( filename );
|
||||
|
||||
bool exclude = filename.StartsWith( m_exclude ) || fn.GetName().StartsWith( wxS( "." ) );
|
||||
|
||||
if( !exclude )
|
||||
m_files.emplace_back( wxFileName( filename ) );
|
||||
|
||||
return wxDIR_CONTINUE;
|
||||
@@ -89,7 +93,12 @@ public:
|
||||
|
||||
virtual wxDirTraverseResult OnDir( const wxString& dirname ) override
|
||||
{
|
||||
if( !dirname.StartsWith( m_exclude ) )
|
||||
wxFileName fn( dirname, wxEmptyString );
|
||||
wxArrayString dirs = fn.GetDirs();
|
||||
wxString lastDir = dirs[ fn.GetDirCount() - 1 ];
|
||||
bool exclude = dirname.StartsWith( m_exclude ) || lastDir.StartsWith( wxS( "." ) );
|
||||
|
||||
if( !exclude )
|
||||
m_files.emplace_back( wxFileName::DirName( dirname ) );
|
||||
|
||||
return wxDIR_CONTINUE;
|
||||
|
||||
@@ -499,7 +499,7 @@ bool SHAPE_POLY_SET::IsPolygonSelfIntersecting( int aPolygonIndex ) const
|
||||
break;
|
||||
|
||||
int index_diff = std::abs( firstSegment.Index() - secondSegment.Index() );
|
||||
bool adjacent = ( index_diff == 1) || (index_diff == (segments.size() - 1) );
|
||||
bool adjacent = ( index_diff == 1) || (index_diff == ((int)segments.size() - 1) );
|
||||
|
||||
// Check whether the two segments built collide, only when they are not adjacent.
|
||||
if( !adjacent && firstSegment.Collide( secondSegment, 0 ) )
|
||||
@@ -1009,28 +1009,27 @@ void SHAPE_POLY_SET::inflate2( int aAmount, int aCircleSegCount, CORNER_STRATEGY
|
||||
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/JoinType.htm
|
||||
JoinType joinType = JoinType::Round; // The way corners are offsetted
|
||||
double miterLimit = 2.0; // Smaller value when using jtMiter for joinType
|
||||
JoinType miterFallback = JoinType::Square;
|
||||
|
||||
switch( aCornerStrategy )
|
||||
{
|
||||
case ALLOW_ACUTE_CORNERS:
|
||||
case CORNER_STRATEGY::ALLOW_ACUTE_CORNERS:
|
||||
joinType = JoinType::Miter;
|
||||
miterLimit = 10; // Allows large spikes
|
||||
break;
|
||||
|
||||
case CHAMFER_ACUTE_CORNERS: // Acute angles are chamfered
|
||||
case CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS: // Acute angles are chamfered
|
||||
joinType = JoinType::Miter;
|
||||
break;
|
||||
|
||||
case ROUND_ACUTE_CORNERS: // Acute angles are rounded
|
||||
case CORNER_STRATEGY::ROUND_ACUTE_CORNERS: // Acute angles are rounded
|
||||
joinType = JoinType::Miter;
|
||||
break;
|
||||
|
||||
case CHAMFER_ALL_CORNERS: // All angles are chamfered.
|
||||
case CORNER_STRATEGY::CHAMFER_ALL_CORNERS: // All angles are chamfered.
|
||||
joinType = JoinType::Square;
|
||||
break;
|
||||
|
||||
case ROUND_ALL_CORNERS: // All angles are rounded.
|
||||
case CORNER_STRATEGY::ROUND_ALL_CORNERS: // All angles are rounded.
|
||||
joinType = JoinType::Round;
|
||||
break;
|
||||
}
|
||||
@@ -1079,11 +1078,11 @@ void SHAPE_POLY_SET::inflate2( int aAmount, int aCircleSegCount, CORNER_STRATEGY
|
||||
Paths64 paths;
|
||||
c.Execute( aAmount, paths );
|
||||
|
||||
Clipper2Lib::SimplifyPaths( paths, std::abs( aAmount ) * coeff, false );
|
||||
Clipper2Lib::SimplifyPaths( paths, std::abs( aAmount ) * coeff, true );
|
||||
|
||||
Clipper64 c2;
|
||||
c2.PreserveCollinear = false;
|
||||
c2.ReverseSolution = false;
|
||||
c2.PreserveCollinear( false );
|
||||
c2.ReverseSolution( false );
|
||||
c2.AddSubject( paths );
|
||||
c2.Execute(ClipType::Union, FillRule::Positive, tree);
|
||||
}
|
||||
|
||||
@@ -478,13 +478,13 @@ const VECTOR2I CalcArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, cons
|
||||
|
||||
VECTOR2I iCenter;
|
||||
|
||||
iCenter.x = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() / 2.0 ),
|
||||
iCenter.x = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() + 100 ),
|
||||
dCenter.x,
|
||||
double( std::numeric_limits<int>::max() / 2.0 ) ) );
|
||||
double( std::numeric_limits<int>::max() - 100 ) ) );
|
||||
|
||||
iCenter.y = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() / 2.0 ),
|
||||
iCenter.y = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() + 100 ),
|
||||
dCenter.y,
|
||||
double( std::numeric_limits<int>::max() / 2.0 ) ) );
|
||||
double( std::numeric_limits<int>::max() - 100 ) ) );
|
||||
|
||||
return iCenter;
|
||||
}
|
||||
|
||||
@@ -79,15 +79,30 @@ void ARRAY_CREATOR::Invoke()
|
||||
for( int ptN = 0; ptN < array_opts->GetArraySize(); ptN++ )
|
||||
{
|
||||
PCB_SELECTION items_for_this_block;
|
||||
std::set<FOOTPRINT*> fpDeDupe;
|
||||
|
||||
for ( int i = 0; i < m_selection.Size(); ++i )
|
||||
{
|
||||
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( m_selection[ i ] );
|
||||
|
||||
if( item->Type() == PCB_PAD_T && !m_isFootprintEditor )
|
||||
FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( item->GetParentFootprint() );
|
||||
|
||||
// If it is not the footprint editor, then duplicate the parent footprint instead.
|
||||
// This check assumes that the footprint child objects are correctly parented, if
|
||||
// they are not, this will segfault.
|
||||
if( !m_isFootprintEditor && parentFootprint )
|
||||
{
|
||||
// If it is not the footprint editor, then duplicate the parent footprint instead
|
||||
item = static_cast<FOOTPRINT*>( item )->GetParent();
|
||||
// It is possible to select multiple footprint child objects in the board editor.
|
||||
// Do not create multiple copies of the same footprint when this occurs.
|
||||
if( fpDeDupe.count( parentFootprint ) == 0 )
|
||||
{
|
||||
fpDeDupe.emplace( parentFootprint );
|
||||
item = parentFootprint;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
BOARD_ITEM* this_item = nullptr;
|
||||
|
||||
@@ -216,6 +216,13 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
|
||||
}
|
||||
|
||||
|
||||
void CN_CONNECTIVITY_ALGO::RemoveInvalidRefs()
|
||||
{
|
||||
for( CN_ITEM* item : m_itemList )
|
||||
item->RemoveInvalidRefs();
|
||||
}
|
||||
|
||||
|
||||
void CN_CONNECTIVITY_ALGO::searchConnections()
|
||||
{
|
||||
#ifdef PROFILE
|
||||
@@ -224,6 +231,11 @@ void CN_CONNECTIVITY_ALGO::searchConnections()
|
||||
std::vector<CN_ITEM*> garbage;
|
||||
garbage.reserve( 1024 );
|
||||
|
||||
m_parentConnectivityData->RemoveInvalidRefs();
|
||||
|
||||
if( m_isLocal )
|
||||
m_globalConnectivityData->RemoveInvalidRefs();
|
||||
|
||||
m_itemList.RemoveInvalidItems( garbage );
|
||||
|
||||
for( CN_ITEM* item : garbage )
|
||||
@@ -542,9 +554,13 @@ void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
|
||||
}
|
||||
|
||||
|
||||
void CN_CONNECTIVITY_ALGO::LocalBuild( const std::vector<BOARD_ITEM*>& aItems )
|
||||
void CN_CONNECTIVITY_ALGO::LocalBuild( std::shared_ptr<CONNECTIVITY_DATA> aGlobalConnectivity,
|
||||
const std::vector<BOARD_ITEM*>& aLocalItems )
|
||||
{
|
||||
for( BOARD_ITEM* item : aItems )
|
||||
m_isLocal = true;
|
||||
m_globalConnectivityData = aGlobalConnectivity;
|
||||
|
||||
for( BOARD_ITEM* item : aLocalItems )
|
||||
{
|
||||
switch( item->Type() )
|
||||
{
|
||||
|
||||
@@ -93,6 +93,15 @@ public:
|
||||
void SetSourceNode( const std::shared_ptr<const CN_ANCHOR>& aNode ) { m_source = aNode; }
|
||||
void SetTargetNode( const std::shared_ptr<const CN_ANCHOR>& aNode ) { m_target = aNode; }
|
||||
|
||||
void RemoveInvalidRefs()
|
||||
{
|
||||
if( m_source && !m_source->Valid() )
|
||||
m_source.reset();
|
||||
|
||||
if( m_target && !m_target->Valid() )
|
||||
m_target.reset();
|
||||
}
|
||||
|
||||
void SetWeight( unsigned weight ) { m_weight = weight; }
|
||||
unsigned GetWeight() const { return m_weight; }
|
||||
|
||||
@@ -154,8 +163,15 @@ public:
|
||||
std::list<CN_ITEM*> m_items;
|
||||
};
|
||||
|
||||
CN_CONNECTIVITY_ALGO() {}
|
||||
~CN_CONNECTIVITY_ALGO() { Clear(); }
|
||||
CN_CONNECTIVITY_ALGO( CONNECTIVITY_DATA* aParentConnectivityData ) :
|
||||
m_parentConnectivityData( aParentConnectivityData ),
|
||||
m_isLocal( false )
|
||||
{}
|
||||
|
||||
~CN_CONNECTIVITY_ALGO()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
bool ItemExists( const BOARD_CONNECTED_ITEM* aItem ) const
|
||||
{
|
||||
@@ -197,8 +213,9 @@ public:
|
||||
return m_dirtyNets.size();
|
||||
}
|
||||
|
||||
void Build( BOARD* aZoneLayer, PROGRESS_REPORTER* aReporter = nullptr );
|
||||
void LocalBuild( const std::vector<BOARD_ITEM*>& aItems );
|
||||
void Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter = nullptr );
|
||||
void LocalBuild( std::shared_ptr<CONNECTIVITY_DATA> aGlobalConnectivity,
|
||||
const std::vector<BOARD_ITEM*>& aLocalItems );
|
||||
|
||||
void Clear();
|
||||
|
||||
@@ -247,6 +264,8 @@ public:
|
||||
}
|
||||
|
||||
void MarkNetAsDirty( int aNet );
|
||||
void RemoveInvalidRefs();
|
||||
|
||||
void SetProgressReporter( PROGRESS_REPORTER* aReporter );
|
||||
|
||||
private:
|
||||
@@ -265,6 +284,7 @@ private:
|
||||
void markItemNetAsDirty( const BOARD_ITEM* aItem );
|
||||
|
||||
private:
|
||||
CONNECTIVITY_DATA* m_parentConnectivityData;
|
||||
CN_LIST m_itemList;
|
||||
std::unordered_map<const BOARD_ITEM*, ITEM_MAP_ENTRY> m_itemMap;
|
||||
|
||||
@@ -272,6 +292,9 @@ private:
|
||||
std::vector<std::shared_ptr<CN_CLUSTER>> m_ratsnestClusters;
|
||||
std::vector<bool> m_dirtyNets;
|
||||
|
||||
bool m_isLocal;
|
||||
std::shared_ptr<CONNECTIVITY_DATA> m_globalConnectivityData;
|
||||
|
||||
PROGRESS_REPORTER* m_progressReporter = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -44,18 +44,21 @@
|
||||
#include <trigo.h>
|
||||
#include <drc/drc_rtree.h>
|
||||
|
||||
CONNECTIVITY_DATA::CONNECTIVITY_DATA()
|
||||
CONNECTIVITY_DATA::CONNECTIVITY_DATA() :
|
||||
m_skipRatsnestUpdate( false )
|
||||
{
|
||||
m_connAlgo.reset( new CN_CONNECTIVITY_ALGO );
|
||||
m_connAlgo.reset( new CN_CONNECTIVITY_ALGO( this ) );
|
||||
m_progressReporter = nullptr;
|
||||
m_fromToCache.reset( new FROM_TO_CACHE );
|
||||
}
|
||||
|
||||
|
||||
CONNECTIVITY_DATA::CONNECTIVITY_DATA( const std::vector<BOARD_ITEM*>& aItems, bool aSkipRatsnest )
|
||||
: m_skipRatsnest( aSkipRatsnest )
|
||||
CONNECTIVITY_DATA::CONNECTIVITY_DATA( std::shared_ptr<CONNECTIVITY_DATA> aGlobalConnectivity,
|
||||
const std::vector<BOARD_ITEM*>& aLocalItems,
|
||||
bool aSkipRatsnestUpdate ) :
|
||||
m_skipRatsnestUpdate( aSkipRatsnestUpdate )
|
||||
{
|
||||
Build( aItems );
|
||||
Build( aGlobalConnectivity, aLocalItems );
|
||||
m_progressReporter = nullptr;
|
||||
m_fromToCache.reset( new FROM_TO_CACHE );
|
||||
}
|
||||
@@ -109,7 +112,7 @@ bool CONNECTIVITY_DATA::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
|
||||
|
||||
std::shared_ptr<NET_SETTINGS>& netSettings = aBoard->GetDesignSettings().m_NetSettings;
|
||||
|
||||
m_connAlgo.reset( new CN_CONNECTIVITY_ALGO );
|
||||
m_connAlgo.reset( new CN_CONNECTIVITY_ALGO( this ) );
|
||||
m_connAlgo->Build( aBoard, aReporter );
|
||||
|
||||
m_netclassMap.clear();
|
||||
@@ -140,15 +143,16 @@ bool CONNECTIVITY_DATA::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
|
||||
}
|
||||
|
||||
|
||||
void CONNECTIVITY_DATA::Build( const std::vector<BOARD_ITEM*>& aItems )
|
||||
void CONNECTIVITY_DATA::Build( std::shared_ptr<CONNECTIVITY_DATA>& aGlobalConnectivity,
|
||||
const std::vector<BOARD_ITEM*>& aLocalItems )
|
||||
{
|
||||
std::unique_lock<KISPINLOCK> lock( m_lock, std::try_to_lock );
|
||||
|
||||
if( !lock )
|
||||
return;
|
||||
|
||||
m_connAlgo.reset( new CN_CONNECTIVITY_ALGO );
|
||||
m_connAlgo->LocalBuild( aItems );
|
||||
m_connAlgo.reset( new CN_CONNECTIVITY_ALGO( this ) );
|
||||
m_connAlgo->LocalBuild( aGlobalConnectivity, aLocalItems );
|
||||
|
||||
internalRecalculateRatsnest();
|
||||
}
|
||||
@@ -272,7 +276,7 @@ void CONNECTIVITY_DATA::internalRecalculateRatsnest( BOARD_COMMIT* aCommit )
|
||||
|
||||
m_connAlgo->ClearDirtyFlags();
|
||||
|
||||
if( !m_skipRatsnest )
|
||||
if( !m_skipRatsnestUpdate )
|
||||
updateRatsnest();
|
||||
}
|
||||
|
||||
@@ -381,6 +385,10 @@ void CONNECTIVITY_DATA::ComputeLocalRatsnest( const std::vector<BOARD_ITEM*>& aI
|
||||
{
|
||||
const std::shared_ptr<const CN_ANCHOR>& nodeA = edge.GetSourceNode();
|
||||
const std::shared_ptr<const CN_ANCHOR>& nodeB = edge.GetTargetNode();
|
||||
|
||||
if( !nodeA || nodeA->Dirty() || !nodeB || nodeB->Dirty() )
|
||||
continue;
|
||||
|
||||
RN_DYNAMIC_LINE l;
|
||||
|
||||
// Use the parents' positions
|
||||
@@ -910,6 +918,15 @@ void CONNECTIVITY_DATA::MarkItemNetAsDirty( BOARD_ITEM *aItem )
|
||||
}
|
||||
|
||||
|
||||
void CONNECTIVITY_DATA::RemoveInvalidRefs()
|
||||
{
|
||||
m_connAlgo->RemoveInvalidRefs();
|
||||
|
||||
for( RN_NET* rnNet : m_nets )
|
||||
rnNet->RemoveInvalidRefs();
|
||||
}
|
||||
|
||||
|
||||
void CONNECTIVITY_DATA::SetProgressReporter( PROGRESS_REPORTER* aReporter )
|
||||
{
|
||||
m_progressReporter = aReporter;
|
||||
@@ -951,6 +968,9 @@ const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForItems( std::vector<B
|
||||
std::shared_ptr<const CN_ANCHOR> srcNode = edge.GetSourceNode();
|
||||
std::shared_ptr<const CN_ANCHOR> dstNode = edge.GetTargetNode();
|
||||
|
||||
if( !srcNode || srcNode->Dirty() || !dstNode || dstNode->Dirty() )
|
||||
continue;
|
||||
|
||||
BOARD_CONNECTED_ITEM* srcParent = srcNode->Parent();
|
||||
BOARD_CONNECTED_ITEM* dstParent = dstNode->Parent();
|
||||
|
||||
@@ -973,6 +993,12 @@ const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForPad( const PAD* aPad
|
||||
|
||||
for( const CN_EDGE& edge : net->GetEdges() )
|
||||
{
|
||||
if( !edge.GetSourceNode() || edge.GetSourceNode()->Dirty() )
|
||||
continue;
|
||||
|
||||
if( !edge.GetTargetNode() || edge.GetTargetNode()->Dirty() )
|
||||
continue;
|
||||
|
||||
if( edge.GetSourceNode()->Parent() == aPad || edge.GetTargetNode()->Parent() == aPad )
|
||||
edges.push_back( edge );
|
||||
}
|
||||
@@ -1002,6 +1028,9 @@ const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForComponent( FOOTPRINT
|
||||
auto srcNode = edge.GetSourceNode();
|
||||
auto dstNode = edge.GetTargetNode();
|
||||
|
||||
if( !srcNode || srcNode->Dirty() || !dstNode || dstNode->Dirty() )
|
||||
continue;
|
||||
|
||||
const PAD* srcParent = static_cast<const PAD*>( srcNode->Parent() );
|
||||
const PAD* dstParent = static_cast<const PAD*>( dstNode->Parent() );
|
||||
|
||||
|
||||
@@ -88,7 +88,8 @@ public:
|
||||
CONNECTIVITY_DATA();
|
||||
~CONNECTIVITY_DATA();
|
||||
|
||||
CONNECTIVITY_DATA( const std::vector<BOARD_ITEM*>& aItems, bool aSkipItems = false );
|
||||
CONNECTIVITY_DATA( std::shared_ptr<CONNECTIVITY_DATA> aGlobalConnectivity,
|
||||
const std::vector<BOARD_ITEM*>& aLocalItems, bool aSkipRatsnestUpdate = false );
|
||||
|
||||
/**
|
||||
* Function Build()
|
||||
@@ -100,7 +101,8 @@ public:
|
||||
* Function Build()
|
||||
* Builds the connectivity database for a set of items aItems.
|
||||
*/
|
||||
void Build( const std::vector<BOARD_ITEM*>& aItems );
|
||||
void Build( std::shared_ptr<CONNECTIVITY_DATA>& aGlobalConnectivity,
|
||||
const std::vector<BOARD_ITEM*>& aLocalItems );
|
||||
|
||||
/**
|
||||
* Function Add()
|
||||
@@ -263,6 +265,8 @@ public:
|
||||
KISPINLOCK& GetLock() { return m_lock; }
|
||||
|
||||
void MarkItemNetAsDirty( BOARD_ITEM* aItem );
|
||||
void RemoveInvalidRefs();
|
||||
|
||||
void SetProgressReporter( PROGRESS_REPORTER* aReporter );
|
||||
|
||||
const std::map<int, wxString>& GetNetclassMap() const { return m_netclassMap; }
|
||||
@@ -297,7 +301,7 @@ private:
|
||||
std::vector<RN_NET*> m_nets;
|
||||
|
||||
/// Used to suppress ratsnest calculations on dynamic ratsnests
|
||||
bool m_skipRatsnest = false;
|
||||
bool m_skipRatsnestUpdate;
|
||||
|
||||
KISPINLOCK m_lock;
|
||||
|
||||
|
||||
@@ -266,9 +266,6 @@ void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
|
||||
|
||||
m_items.resize( lastItem - m_items.begin() );
|
||||
|
||||
for( CN_ITEM* item : m_items )
|
||||
item->RemoveInvalidRefs();
|
||||
|
||||
for( CN_ITEM* item : aGarbage )
|
||||
m_index.Remove( item );
|
||||
|
||||
|
||||
@@ -479,6 +479,14 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
|
||||
m_frame->GetBoard()->GetConnectivity()->RunOnUnconnectedEdges(
|
||||
[&]( CN_EDGE& edge )
|
||||
{
|
||||
// Connectivity was valid when DRC was run, but this is a modeless dialog
|
||||
// so it might not be now.
|
||||
if( !edge.GetSourceNode() || edge.GetSourceNode()->Dirty() )
|
||||
return true;
|
||||
|
||||
if( !edge.GetTargetNode() || edge.GetTargetNode()->Dirty() )
|
||||
return true;
|
||||
|
||||
if( edge.GetSourceNode()->Parent() == a
|
||||
&& edge.GetTargetNode()->Parent() == b )
|
||||
{
|
||||
|
||||
@@ -151,6 +151,9 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
|
||||
if( !reportProgress( ii++, count, progressDelta ) )
|
||||
return false; // DRC cancelled
|
||||
|
||||
wxCHECK( edge.GetSourceNode() && !edge.GetSourceNode()->Dirty(), true );
|
||||
wxCHECK( edge.GetTargetNode() && !edge.GetTargetNode()->Dirty(), true );
|
||||
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNCONNECTED_ITEMS );
|
||||
drcItem->SetItems( edge.GetSourceNode()->Parent(), edge.GetTargetNode()->Parent() );
|
||||
reportViolation( drcItem, edge.GetSourceNode()->Pos(), UNDEFINED_LAYER );
|
||||
|
||||
@@ -218,6 +218,14 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
|
||||
for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
|
||||
{
|
||||
SEG seg = poly.CSegment( ii );
|
||||
|
||||
// The polygon iterator returns coordinates relative to the parent
|
||||
if( FOOTPRINT* fp = shape->GetParentFootprint() )
|
||||
{
|
||||
seg.A += fp->GetPosition();
|
||||
seg.B += fp->GetPosition();
|
||||
}
|
||||
|
||||
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
edges.back()->SetShape( SHAPE_T::SEGMENT );
|
||||
edges.back()->SetStart( seg.A );
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <pcbnew_settings.h>
|
||||
#include <pgm_base.h>
|
||||
#include <project/project_file.h> // LAST_PATH_TYPE
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/filedlg.h>
|
||||
@@ -1140,21 +1141,15 @@ static void CreateBoardSection( FILE* aFile, BOARD* aPcb )
|
||||
{
|
||||
fputs( "$BOARD\n", aFile );
|
||||
|
||||
// Extract the board edges
|
||||
for( BOARD_ITEM* drawing : aPcb->Drawings() )
|
||||
{
|
||||
if( drawing->Type() == PCB_SHAPE_T )
|
||||
{
|
||||
PCB_SHAPE* drawseg = static_cast<PCB_SHAPE*>( drawing );
|
||||
SHAPE_POLY_SET outline;
|
||||
aPcb->GetBoardPolygonOutlines( outline );
|
||||
|
||||
if( drawseg->GetLayer() == Edge_Cuts )
|
||||
{
|
||||
// XXX GenCAD supports arc boundaries but I've seen nothing that reads them
|
||||
fprintf( aFile, "LINE %g %g %g %g\n",
|
||||
MapXTo( drawseg->GetStart().x ), MapYTo( drawseg->GetStart().y ),
|
||||
MapXTo( drawseg->GetEnd().x ), MapYTo( drawseg->GetEnd().y ) );
|
||||
}
|
||||
}
|
||||
for( auto seg1 = outline.IterateSegmentsWithHoles(); seg1; seg1++ )
|
||||
{
|
||||
SEG seg = *seg1;
|
||||
fprintf( aFile, "LINE %g %g %g %g\n",
|
||||
MapXTo( seg.A.x ), MapYTo( seg.A.y ),
|
||||
MapXTo( seg.B.x ), MapYTo( seg.B.y ) );
|
||||
}
|
||||
|
||||
fputs( "$ENDBOARD\n\n", aFile );
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <math/util.h> // for KiROUND
|
||||
|
||||
#include <board.h>
|
||||
#include <footprint.h>
|
||||
|
||||
#include <pcbnew.h>
|
||||
#include <pcbplot.h>
|
||||
@@ -258,6 +259,31 @@ bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_
|
||||
}
|
||||
}
|
||||
|
||||
// Plot edge cuts in footprints
|
||||
for( const FOOTPRINT* footprint : m_pcb->Footprints() )
|
||||
{
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
{
|
||||
if( item-> GetLayer() != Edge_Cuts )
|
||||
continue;
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_FP_SHAPE_T:
|
||||
{
|
||||
PCB_SHAPE dummy_shape( *static_cast<PCB_SHAPE*>( item ) );
|
||||
dummy_shape.SetLayer( Dwgs_User );
|
||||
dummy_shape.SetParentGroup( nullptr ); // Remove group association, not needed for plotting
|
||||
itemplotter.PlotPcbShape( &dummy_shape );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int plotX, plotY, TextWidth;
|
||||
int intervalle = 0;
|
||||
char line[1024];
|
||||
|
||||
@@ -575,6 +575,6 @@ void EXCELLON_WRITER::writeEXCELLONHeader( DRILL_LAYER_PAIR aLayerPair, TYPE_FIL
|
||||
void EXCELLON_WRITER::writeEXCELLONEndOfFile()
|
||||
{
|
||||
// add if minimal here
|
||||
fputs( "T0\nM30\n", m_file );
|
||||
fputs( "M30\n", m_file );
|
||||
fclose( m_file );
|
||||
}
|
||||
|
||||
@@ -189,14 +189,14 @@ STEP_PCB_MODEL::STEP_PCB_MODEL( const wxString& aPcbName )
|
||||
m_mergeOCCMaxDist = OCC_MAX_DISTANCE_TO_MERGE_POINTS;
|
||||
m_minx = 1.0e10; // absurdly large number; any valid PCB X value will be smaller
|
||||
m_pcbName = aPcbName;
|
||||
BRepBuilderAPI::Precision( m_mergeOCCMaxDist );
|
||||
m_maxError = pcbIUScale.mmToIU( ARC_TO_SEGMENT_MAX_ERROR_MM );
|
||||
}
|
||||
|
||||
|
||||
STEP_PCB_MODEL::~STEP_PCB_MODEL()
|
||||
{
|
||||
m_doc->Close();
|
||||
if( m_doc->CanClose() == CDM_CCS_OK )
|
||||
m_doc->Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -328,7 +328,6 @@ void STEP_PCB_MODEL::OCCSetMergeMaxDistance( double aDistance )
|
||||
{
|
||||
// Ensure a minimal value (in mm)
|
||||
m_mergeOCCMaxDist = aDistance;
|
||||
BRepBuilderAPI::Precision( m_mergeOCCMaxDist );
|
||||
}
|
||||
|
||||
|
||||
@@ -1048,14 +1047,18 @@ bool STEP_PCB_MODEL::readIGES( Handle( TDocStd_Document )& doc, const char* fnam
|
||||
|
||||
if( !reader.Transfer( doc ) )
|
||||
{
|
||||
doc->Close();
|
||||
if( doc->CanClose() == CDM_CCS_OK )
|
||||
doc->Close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// are there any shapes to translate?
|
||||
if( reader.NbShapes() < 1 )
|
||||
{
|
||||
doc->Close();
|
||||
if( doc->CanClose() == CDM_CCS_OK )
|
||||
doc->Close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1081,19 +1084,23 @@ bool STEP_PCB_MODEL::readSTEP( Handle( TDocStd_Document )& doc, const char* fnam
|
||||
|
||||
// set other translation options
|
||||
reader.SetColorMode( true ); // use model colors
|
||||
reader.SetNameMode( false ); // don't use label names
|
||||
reader.SetNameMode( true ); // use label names
|
||||
reader.SetLayerMode( false ); // ignore LAYER data
|
||||
|
||||
if( !reader.Transfer( doc ) )
|
||||
{
|
||||
doc->Close();
|
||||
if( doc->CanClose() == CDM_CCS_OK )
|
||||
doc->Close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// are there any shapes to translate?
|
||||
if( reader.NbRootsForTransfer() < 1 )
|
||||
{
|
||||
doc->Close();
|
||||
if( doc->CanClose() == CDM_CCS_OK )
|
||||
doc->Close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1133,10 +1140,17 @@ TDF_Label STEP_PCB_MODEL::transferModel( Handle( TDocStd_Document )& source,
|
||||
|
||||
while( id <= nshapes )
|
||||
{
|
||||
TopoDS_Shape shape = s_assy->GetShape( frshapes.Value( id ) );
|
||||
const TDF_Label& s_shapeLabel = frshapes.Value( id );
|
||||
TopoDS_Shape shape = s_assy->GetShape( s_shapeLabel );
|
||||
|
||||
if( !shape.IsNull() )
|
||||
{
|
||||
Handle( TDataStd_Name ) s_nameAttr;
|
||||
s_shapeLabel.FindAttribute( TDataStd_Name::GetID(), s_nameAttr );
|
||||
|
||||
TCollection_ExtendedString s_labelName =
|
||||
s_nameAttr ? s_nameAttr->Get() : TCollection_ExtendedString();
|
||||
|
||||
TopoDS_Shape scaled_shape( shape );
|
||||
|
||||
if( aScale.x != 1.0 || aScale.y != 1.0 || aScale.z != 1.0 )
|
||||
@@ -1155,7 +1169,13 @@ TDF_Label STEP_PCB_MODEL::transferModel( Handle( TDocStd_Document )& source,
|
||||
}
|
||||
}
|
||||
|
||||
TDF_Label niulab = d_assy->AddComponent( component, scaled_shape, Standard_False );
|
||||
TDF_Label d_shapeLabel = d_assy->AddShape( scaled_shape, Standard_False );
|
||||
|
||||
if( s_labelName.Length() > 0 )
|
||||
TDataStd_Name::Set( d_shapeLabel, s_labelName );
|
||||
|
||||
TDF_Label niulab =
|
||||
d_assy->AddComponent( component, d_shapeLabel, scaled_shape.Location() );
|
||||
|
||||
// check for per-surface colors
|
||||
stop.Init( shape, TopAbs_FACE );
|
||||
|
||||
@@ -762,12 +762,14 @@ int FOOTPRINT::GetLikelyAttribute() const
|
||||
}
|
||||
}
|
||||
|
||||
if( smd_count > 0 )
|
||||
return FP_SMD;
|
||||
|
||||
// Footprints with plated through-hole pads should usually be marked through hole even if they
|
||||
// also have SMD because they might not be auto-placed. Exceptions to this might be shielded
|
||||
if( tht_count > 0 )
|
||||
return FP_THROUGH_HOLE;
|
||||
|
||||
if( smd_count > 0 )
|
||||
return FP_SMD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -415,7 +415,7 @@ std::shared_ptr<SHAPE_SEGMENT> PAD::GetEffectiveHoleShape() const
|
||||
|
||||
int PAD::GetBoundingRadius() const
|
||||
{
|
||||
if( m_polyDirty )
|
||||
if( m_polyDirty[ERROR_OUTSIDE] )
|
||||
BuildEffectivePolygon( ERROR_OUTSIDE );
|
||||
|
||||
return m_effectiveBoundingRadius;
|
||||
|
||||
+5
-4
@@ -33,6 +33,7 @@
|
||||
#include <geometry/shape_compound.h>
|
||||
#include <pad_shapes.h>
|
||||
#include <geometry/eda_angle.h>
|
||||
#include <geometry/geometry_utils.h>
|
||||
#include <core/arraydim.h>
|
||||
|
||||
class PCB_SHAPE;
|
||||
@@ -436,7 +437,7 @@ public:
|
||||
* @param ignoreLineWidth used for edge cuts where the line width is only for visualization.
|
||||
*/
|
||||
void TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aMaxError, ERROR_LOC aErrorLoc,
|
||||
int aMaxError, ERROR_LOC aErrorLoc = ERROR_INSIDE,
|
||||
bool ignoreLineWidth = false ) const override;
|
||||
|
||||
/**
|
||||
@@ -449,7 +450,7 @@ public:
|
||||
* @return false if the pad has no hole, true otherwise.
|
||||
*/
|
||||
bool TransformHoleToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
|
||||
ERROR_LOC aErrorLoc ) const;
|
||||
ERROR_LOC aErrorLoc = ERROR_INSIDE ) const;
|
||||
|
||||
/**
|
||||
* Some pad shapes can be complex (rounded/chamfered rectangle), even without considering
|
||||
@@ -470,7 +471,7 @@ public:
|
||||
GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
|
||||
FLASHING flashPTHPads = FLASHING::DEFAULT ) const override;
|
||||
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& GetEffectivePolygon( ERROR_LOC aErrorLoc ) const;
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& GetEffectivePolygon( ERROR_LOC aErrorLoc = ERROR_INSIDE ) const;
|
||||
|
||||
/**
|
||||
* Return a SHAPE_SEGMENT object representing the pad's hole.
|
||||
@@ -730,7 +731,7 @@ public:
|
||||
* the dirty bit.
|
||||
*/
|
||||
void BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const;
|
||||
void BuildEffectivePolygon( ERROR_LOC aErrorLoc ) const;
|
||||
void BuildEffectivePolygon( ERROR_LOC aErrorLoc = ERROR_INSIDE ) const;
|
||||
|
||||
virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
|
||||
|
||||
|
||||
@@ -1655,7 +1655,9 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
|
||||
m_gal->SetFillColor( color );
|
||||
m_gal->SetStrokeColor( color );
|
||||
|
||||
if( lineStyle <= PLOT_DASH_TYPE::FIRST_TYPE )
|
||||
// Note: on LAYER_LOCKED_ITEM_SHADOW always draw shadow shapes as continuous lines
|
||||
// otherwise the look is very strange and ugly
|
||||
if( lineStyle <= PLOT_DASH_TYPE::FIRST_TYPE || aLayer == LAYER_LOCKED_ITEM_SHADOW )
|
||||
{
|
||||
switch( aShape->GetShape() )
|
||||
{
|
||||
|
||||
@@ -506,6 +506,9 @@ LSET PCB_VIA::GetLayerSet() const
|
||||
{
|
||||
LSET layermask;
|
||||
|
||||
if( m_layer < PCBNEW_LAYER_ID_START )
|
||||
return layermask;
|
||||
|
||||
if( GetViaType() == VIATYPE::THROUGH )
|
||||
layermask = LSET::AllCuMask();
|
||||
else
|
||||
|
||||
@@ -461,6 +461,15 @@ int PCBNEW_JOBS_HANDLER::JobExportDrill( JOB* aJob )
|
||||
|
||||
BOARD* brd = LoadBoard( aDrillJob->m_filename );
|
||||
|
||||
// ensure output dir exists
|
||||
wxFileName fn( aDrillJob->m_outputDir + wxT( "/" ) );
|
||||
if( !fn.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
|
||||
{
|
||||
if( aJob->IsCli() )
|
||||
wxFprintf( stderr, _( "Failed to create output directory\n" ) );
|
||||
return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT;
|
||||
}
|
||||
|
||||
std::unique_ptr<GENDRILL_WRITER_BASE> drillWriter;
|
||||
|
||||
if( aDrillJob->m_format == JOB_EXPORT_PCB_DRILL::DRILL_FORMAT::EXCELLON )
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/**
|
||||
* @file plot_board_layers.cpp
|
||||
* @brief Functions to plot one board layer (silkscreen layers or other layers).
|
||||
* Silkscreen layers have specific requirement for pads (not filled) and texts
|
||||
* (with option to remove them from some copper areas (pads...)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
@@ -28,6 +21,12 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Functions to plot one board layer (silkscreen layers or other layers).
|
||||
* Silkscreen layers have specific requirement for pads (not filled) and texts
|
||||
* (with option to remove them from some copper areas (pads...)
|
||||
*/
|
||||
|
||||
|
||||
#include <eda_item.h>
|
||||
#include <layer_ids.h>
|
||||
@@ -448,11 +447,12 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
||||
|
||||
case PAD_SHAPE::ROUNDRECT:
|
||||
{
|
||||
// rounding is stored as a percent, but we have to change the new radius
|
||||
// to initial_radius + clearance to have a inflated/deflated similar shape
|
||||
int initial_radius = pad->GetRoundRectCornerRadius();
|
||||
// rounding is stored as a percent, but we have to update this ratio
|
||||
// to force recalculation of other values after size changing (we do not
|
||||
// really change the rounding percent value)
|
||||
double radius_ratio = pad->GetRoundRectRadiusRatio();
|
||||
pad->SetSize( padPlotsSize );
|
||||
pad->SetRoundRectCornerRadius( std::max( initial_radius + mask_clearance, 0 ) );
|
||||
pad->SetRoundRectRadiusRatio( radius_ratio );
|
||||
|
||||
itemplotter.PlotPad( pad, color, padPlotMode );
|
||||
break;
|
||||
|
||||
@@ -531,15 +531,31 @@ ARULE6::ARULE6( ALTIUM_PARSER& aReader )
|
||||
else if( rulekind == wxT( "HoleSize" ) )
|
||||
{
|
||||
kind = ALTIUM_RULE_KIND::HOLE_SIZE;
|
||||
minLimit = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MINLIMIT" ), wxT( "1mil" ) );
|
||||
maxLimit = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MAXLIMIT" ), wxT( "150mil" ) );
|
||||
}
|
||||
else if( rulekind == wxT( "HoleToHoleClearance" ) )
|
||||
{
|
||||
kind = ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE;
|
||||
clearanceGap = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "GAP" ), wxT( "10mil" ) );
|
||||
}
|
||||
else if( rulekind == wxT( "RoutingVias" ) )
|
||||
{
|
||||
kind = ALTIUM_RULE_KIND::ROUTING_VIAS;
|
||||
width = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "WIDTH" ), wxT( "20mil" ) );
|
||||
minWidth = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MINWIDTH" ), wxT( "20mil" ) );
|
||||
maxWidth = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MAXWIDTH" ), wxT( "50mil" ) );
|
||||
holeWidth = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "HOLEWIDTH" ), wxT( "10mil" ) );
|
||||
minHoleWidth = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MINHOLEWIDTH" ), wxT( "10mil" ) );
|
||||
maxHoleWidth = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MAXHOLEWIDTH" ), wxT( "28mil" ) );
|
||||
}
|
||||
else if( rulekind == wxT( "Width" ) )
|
||||
{
|
||||
kind = ALTIUM_RULE_KIND::WIDTH;
|
||||
}
|
||||
minLimit = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MINLIMIT" ), wxT( "6mil" ) );
|
||||
maxLimit = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "MAXLIMIT" ), wxT( "40mil" ) );
|
||||
preferredWidth = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "PREFERREDWIDTH" ), wxT( "6mil" ) );
|
||||
}
|
||||
else if( rulekind == wxT( "PasteMaskExpansion" ) )
|
||||
{
|
||||
kind = ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION;
|
||||
@@ -548,8 +564,7 @@ ARULE6::ARULE6( ALTIUM_PARSER& aReader )
|
||||
else if( rulekind == wxT( "SolderMaskExpansion" ) )
|
||||
{
|
||||
kind = ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION;
|
||||
soldermaskExpansion =
|
||||
ALTIUM_PARSER::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "4mil" ) );
|
||||
soldermaskExpansion = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "4mil" ) );
|
||||
}
|
||||
else if( rulekind == wxT( "PlaneClearance" ) )
|
||||
{
|
||||
|
||||
@@ -108,6 +108,7 @@ enum class ALTIUM_RULE_KIND
|
||||
SOLDER_MASK_EXPANSION = 8,
|
||||
PLANE_CLEARANCE = 9,
|
||||
POLYGON_CONNECT = 10,
|
||||
ROUTING_VIAS = 11
|
||||
};
|
||||
|
||||
enum class ALTIUM_CONNECT_STYLE
|
||||
@@ -499,8 +500,25 @@ struct ARULE6
|
||||
wxString scope2expr;
|
||||
|
||||
// ALTIUM_RULE_KIND::CLEARANCE
|
||||
// ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE
|
||||
int clearanceGap;
|
||||
|
||||
// ALTIUM_RULE_KIND::WIDTH
|
||||
// ALTIUM_RULE_KIND::HOLE_SIZE
|
||||
int minLimit;
|
||||
int maxLimit;
|
||||
|
||||
// ALTIUM_RULE_KIND::WIDTH
|
||||
int preferredWidth;
|
||||
|
||||
// ALTIUM_RULE_KIND::ROUTING_VIAS
|
||||
int width;
|
||||
int minWidth;
|
||||
int maxWidth;
|
||||
int holeWidth;
|
||||
int minHoleWidth;
|
||||
int maxHoleWidth;
|
||||
|
||||
// ALTIUM_RULE_KIND::PLANE_CLEARANCE
|
||||
int planeclearanceClearance;
|
||||
|
||||
|
||||
@@ -1901,6 +1901,35 @@ void ALTIUM_PCB::ParseRules6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile
|
||||
} );
|
||||
}
|
||||
|
||||
const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::CLEARANCE );
|
||||
const ARULE6* trackWidthRule = GetRuleDefault( ALTIUM_RULE_KIND::WIDTH );
|
||||
const ARULE6* routingViasRule = GetRuleDefault( ALTIUM_RULE_KIND::ROUTING_VIAS );
|
||||
const ARULE6* holeSizeRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_SIZE );
|
||||
const ARULE6* holeToHoleRule = GetRuleDefault( ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE );
|
||||
|
||||
if( clearanceRule )
|
||||
m_board->GetDesignSettings().m_MinClearance = clearanceRule->clearanceGap;
|
||||
|
||||
if( trackWidthRule )
|
||||
{
|
||||
m_board->GetDesignSettings().m_TrackMinWidth = trackWidthRule->minLimit;
|
||||
// TODO: construct a custom rule for preferredWidth and maxLimit values
|
||||
}
|
||||
|
||||
if( routingViasRule )
|
||||
{
|
||||
m_board->GetDesignSettings().m_ViasMinSize = routingViasRule->minWidth;
|
||||
m_board->GetDesignSettings().m_MinThroughDrill = routingViasRule->minHoleWidth;
|
||||
}
|
||||
|
||||
if( holeSizeRule )
|
||||
{
|
||||
// TODO: construct a custom rule for minLimit / maxLimit values
|
||||
}
|
||||
|
||||
if( holeToHoleRule )
|
||||
m_board->GetDesignSettings().m_HoleToHoleMin = holeToHoleRule->clearanceGap;
|
||||
|
||||
const ARULE6* soldermaskRule = GetRuleDefault( ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION );
|
||||
const ARULE6* pastemaskRule = GetRuleDefault( ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION );
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import os.path
|
||||
from pcbnew import *
|
||||
|
||||
size_025_160mm = wxSizeMM(0.25,1.6)
|
||||
size_150_200mm = wxSizeMM(1.50,2.0)
|
||||
size_025_160mm = VECTOR2I_MM(0.25,1.6)
|
||||
size_150_200mm = VECTOR2I_MM(1.50,2.0)
|
||||
pads = 40
|
||||
|
||||
# create a blank board
|
||||
@@ -12,9 +12,9 @@ pcb = BOARD()
|
||||
# create a new module, it's parent is our previously created pcb
|
||||
module = FOOTPRINT(pcb)
|
||||
module.SetReference("FPC"+str(pads)) # give it a reference name
|
||||
module.Reference().SetPos0(wxPointMM(-1,-1))
|
||||
module.Reference().SetPos0(VECTOR2I_MM(-1,-1))
|
||||
pcb.Add(module) # add it to our pcb
|
||||
m_pos = wxPointMM(50,50)
|
||||
m_pos = VECTOR2I_MM(50,50)
|
||||
module.SetPosition(m_pos)
|
||||
|
||||
# create a pad array and add it to the module
|
||||
@@ -31,18 +31,18 @@ def smdRectPad(module,size,pos,name):
|
||||
return pad
|
||||
|
||||
for n in range (0,pads):
|
||||
pad = smdRectPad(module,size_025_160mm,wxPointMM(0.5*n,0),str(n+1))
|
||||
pad = smdRectPad(module,size_025_160mm,VECTOR2I_MM(0.5*n,0),str(n+1))
|
||||
module.Add(pad)
|
||||
|
||||
|
||||
pad_s0 = smdRectPad(module,size_150_200mm,wxPointMM(-1.6,1.3),"0")
|
||||
pad_s1 = smdRectPad(module,size_150_200mm,wxPointMM((pads-1)*0.5+1.6,1.3),"0")
|
||||
pad_s0 = smdRectPad(module,size_150_200mm,VECTOR2I_MM(-1.6,1.3),"0")
|
||||
pad_s1 = smdRectPad(module,size_150_200mm,VECTOR2I_MM((pads-1)*0.5+1.6,1.3),"0")
|
||||
module.Add(pad_s0)
|
||||
module.Add(pad_s1)
|
||||
|
||||
e = FP_SHAPE(module)
|
||||
e.SetStart0(wxPointMM(-1,0))
|
||||
e.SetEnd0(wxPointMM(0,0))
|
||||
e.SetStart0(VECTOR2I_MM(-1,0))
|
||||
e.SetEnd0(VECTOR2I_MM(0,0))
|
||||
e.SetWidth(FromMM(0.2))
|
||||
e.SetLayer(F_SilkS)
|
||||
e.SetShape(S_SEGMENT)
|
||||
|
||||
@@ -12,8 +12,8 @@ def fromUTF8Text( aText ):
|
||||
return aText
|
||||
|
||||
def GenerateBoard():
|
||||
size_0_6mm = wxSizeMM(0.6,0.6)
|
||||
size_1_0mm = wxSizeMM(1.0,1.0)
|
||||
size_0_6mm = VECTOR2I_MM(0.6,0.6)
|
||||
size_1_0mm = VECTOR2I_MM(1.0,1.0)
|
||||
|
||||
# create a blank board
|
||||
pcb = CreateEmptyBoard()
|
||||
@@ -21,11 +21,11 @@ def GenerateBoard():
|
||||
# create a new footprint, it's parent is our previously created pcb
|
||||
footprint = FOOTPRINT(pcb)
|
||||
footprint.SetReference("M1") # give it a reference name
|
||||
footprint.Reference().SetPos0(wxPointMM(6,-2))
|
||||
footprint.Reference().SetPos0(VECTOR2I_MM(3,-2))
|
||||
footprint.Reference().SetDrawCoord()
|
||||
pcb.Add(footprint) # add it to our pcb
|
||||
m_pos = wxPointMM(50,50)
|
||||
footprint.SetPosition(m_pos)
|
||||
mod_pos = VECTOR2I_MM(50,50)
|
||||
footprint.SetPosition(mod_pos)
|
||||
|
||||
# create a pad array and add it to the footprint
|
||||
n = 1
|
||||
@@ -34,7 +34,7 @@ def GenerateBoard():
|
||||
pad = PAD(footprint)
|
||||
pad.SetDrillSize(size_0_6mm)
|
||||
pad.SetSize(size_1_0mm)
|
||||
pt = wxPointMM(1.27*x,1.27*y)
|
||||
pt = VECTOR2I_MM(1.27*x,1.27*y)
|
||||
pad.SetPos0(pt);
|
||||
pad.SetDrawCoord()
|
||||
pad.SetName(str(n))
|
||||
|
||||
@@ -25,20 +25,23 @@ print("List vias:")
|
||||
|
||||
for item in pcb.GetTracks():
|
||||
if type(item) is PCB_VIA:
|
||||
|
||||
pos = item.GetPosition()
|
||||
drill = item.GetDrillValue()
|
||||
width = item.GetWidth()
|
||||
print(" * Via: %s - %f/%f " % (ToUnits(pos), ToUnits(drill), ToUnits(width)))
|
||||
|
||||
elif type(item) is PCB_TRACK:
|
||||
|
||||
start = item.GetStart()
|
||||
end = item.GetEnd()
|
||||
width = item.GetWidth()
|
||||
|
||||
print(" * Track: %s to %s, width %f" % (ToUnits(start), ToUnits(end), ToUnits(width)))
|
||||
|
||||
elif type(item) is PCB_ARC:
|
||||
start = item.GetStart()
|
||||
end = item.GetEnd()
|
||||
width = item.GetWidth()
|
||||
print(" * Track arc: %s to %s, width %f" % (ToUnits(start), ToUnits(end), ToUnits(width)))
|
||||
|
||||
else:
|
||||
print("Unknown type %s" % type(item))
|
||||
|
||||
@@ -69,4 +72,4 @@ print("List zones:", pcb.GetAreaCount())
|
||||
|
||||
for idx in range(0, pcb.GetAreaCount()):
|
||||
zone=pcb.GetArea(idx)
|
||||
print("zone:", idx, "priority:", zone.GetPriority(), "netname", fromUTF8Text( zone.GetNetname() ) )
|
||||
print("zone:", idx, "priority:", zone.GetAssignedPriority(), "netname", fromUTF8Text( zone.GetNetname() ) )
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
%pythoncode
|
||||
%{
|
||||
def GetShapeStr(self):
|
||||
return self.ShowShape(self.GetShape())
|
||||
return self.ShowShape()
|
||||
%}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,8 @@ void RN_NET::kruskalMST( const std::vector<CN_EDGE> &aEdges )
|
||||
const std::shared_ptr<const CN_ANCHOR>& source = tmp.GetSourceNode();
|
||||
const std::shared_ptr<const CN_ANCHOR>& target = tmp.GetTargetNode();
|
||||
|
||||
wxCHECK2( source && !source->Dirty() && target && !target->Dirty(), continue );
|
||||
|
||||
if( dset.unite( source->GetTag(), target->GetTag() ) )
|
||||
{
|
||||
if( tmp.GetWeight() > 0 )
|
||||
@@ -416,6 +418,8 @@ void RN_NET::OptimizeRNEdges()
|
||||
const std::shared_ptr<const CN_ANCHOR>& source = edge.GetSourceNode();
|
||||
const std::shared_ptr<const CN_ANCHOR>& target = edge.GetTargetNode();
|
||||
|
||||
wxCHECK2( source && !source->Dirty() && target && !target->Dirty(), continue );
|
||||
|
||||
if( source->ConnectedItemsCount() == 0 )
|
||||
{
|
||||
optimizeZoneAnchor( source->Pos(), source->Parent()->GetLayerSet(), target,
|
||||
@@ -456,6 +460,16 @@ void RN_NET::UpdateNet()
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::RemoveInvalidRefs()
|
||||
{
|
||||
for( CN_EDGE& edge : m_rnEdges )
|
||||
edge.RemoveInvalidRefs();
|
||||
|
||||
for( CN_EDGE& edge : m_boardEdges )
|
||||
edge.RemoveInvalidRefs();
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::Clear()
|
||||
{
|
||||
m_rnEdges.clear();
|
||||
|
||||
@@ -77,6 +77,8 @@ public:
|
||||
*/
|
||||
void UpdateNet();
|
||||
|
||||
void RemoveInvalidRefs();
|
||||
|
||||
/**
|
||||
* Find optimal ends of RNEdges. The MST will have found the closest anchors, but when
|
||||
* zones are involved we might have points closer than the anchors.
|
||||
|
||||
@@ -196,12 +196,13 @@ void RATSNEST_VIEW_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
|
||||
|
||||
const std::shared_ptr<const CN_ANCHOR>& sourceNode = edge.GetSourceNode();
|
||||
const std::shared_ptr<const CN_ANCHOR>& targetNode = edge.GetTargetNode();
|
||||
const VECTOR2I source( sourceNode->Pos() );
|
||||
const VECTOR2I target( targetNode->Pos() );
|
||||
|
||||
if( !sourceNode->Valid() || !targetNode->Valid() )
|
||||
if( !sourceNode || sourceNode->Dirty() || !targetNode || targetNode->Dirty() )
|
||||
continue;
|
||||
|
||||
const VECTOR2I source( sourceNode->Pos() );
|
||||
const VECTOR2I target( targetNode->Pos() );
|
||||
|
||||
bool enable = !sourceNode->GetNoLine() && !targetNode->GetNoLine();
|
||||
bool show;
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ bool DIFF_PAIR_PLACER::propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNe
|
||||
if( !obs || handled.count( obs->m_item ) )
|
||||
break;
|
||||
|
||||
int clearance = m_currentNode->GetClearance( obs->m_item, &m_currentTrace.PLine() );
|
||||
int clearance = m_currentNode->GetClearance( obs->m_item, &m_currentTrace.PLine(), false );
|
||||
|
||||
if( obs->m_item->Shape()->Collide( virtHead.Shape(), clearance, &force ) )
|
||||
{
|
||||
|
||||
@@ -126,6 +126,7 @@ public:
|
||||
|
||||
void ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems ) override;
|
||||
void ClearCaches() override;
|
||||
void ClearTemporaryCaches() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -148,6 +149,7 @@ private:
|
||||
int m_clearanceEpsilon;
|
||||
|
||||
std::unordered_map<CLEARANCE_CACHE_KEY, int> m_clearanceCache;
|
||||
std::unordered_map<CLEARANCE_CACHE_KEY, int> m_tempClearanceCache;
|
||||
};
|
||||
|
||||
|
||||
@@ -447,6 +449,13 @@ void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItems( std::vector<const PNS::ITEM*>
|
||||
void PNS_PCBNEW_RULE_RESOLVER::ClearCaches()
|
||||
{
|
||||
m_clearanceCache.clear();
|
||||
m_tempClearanceCache.clear();
|
||||
}
|
||||
|
||||
|
||||
void PNS_PCBNEW_RULE_RESOLVER::ClearTemporaryCaches()
|
||||
{
|
||||
m_tempClearanceCache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -454,11 +463,17 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
||||
bool aUseClearanceEpsilon )
|
||||
{
|
||||
CLEARANCE_CACHE_KEY key = { aA, aB, aUseClearanceEpsilon };
|
||||
auto it = m_clearanceCache.find( key );
|
||||
|
||||
// Search cache (used for actual board items)
|
||||
auto it = m_clearanceCache.find( key );
|
||||
if( it != m_clearanceCache.end() )
|
||||
return it->second;
|
||||
|
||||
// Search cache (used for temporary items within an algorithm)
|
||||
it = m_tempClearanceCache.find( key );
|
||||
if( it != m_tempClearanceCache.end() )
|
||||
return it->second;
|
||||
|
||||
PNS::CONSTRAINT constraint;
|
||||
int rv = 0;
|
||||
LAYER_RANGE layers;
|
||||
@@ -519,10 +534,17 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
||||
|
||||
/* It makes no sense to put items that have no owning NODE in the cache - they can be allocated on stack
|
||||
and we can't really invalidate them in the cache when they are destroyed. Probably a better idea would be
|
||||
to use a static unique counter in PNS::ITEM constructor to generate the cache keys. */
|
||||
if( aA && aB && aA->Owner() && aB->Owner() )
|
||||
to use a static unique counter in PNS::ITEM constructor to generate the cache keys. */
|
||||
/* However, algorithms DO greatly benefit from using the cache, so ownerless items need to be cached.
|
||||
In order to easily clear those only, a temporary cache is created. If this doesn't seem nice, an alternative
|
||||
is clearing the full cache once it reaches a certain size. Also not pretty, but VERY effective
|
||||
to keep things interactive. */
|
||||
if( aA && aB )
|
||||
{
|
||||
m_clearanceCache[ key ] = rv;
|
||||
if ( aA->Owner() && aB->Owner() )
|
||||
m_clearanceCache[ key ] = rv;
|
||||
else
|
||||
m_tempClearanceCache[ key ] = rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
||||
@@ -281,21 +281,17 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles,
|
||||
NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts )
|
||||
{
|
||||
const int clearanceEpsilon = GetRuleResolver()->ClearanceEpsilon();
|
||||
OBSTACLES obstacleList;
|
||||
std::vector<SEGMENT> tmpSegs;
|
||||
|
||||
tmpSegs.reserve( aLine->CLine().SegmentCount() );
|
||||
|
||||
const int clearanceEpsilon = GetRuleResolver()->ClearanceEpsilon();
|
||||
OBSTACLES obstacleList;
|
||||
|
||||
for( int i = 0; i < aLine->CLine().SegmentCount(); i++ )
|
||||
{
|
||||
// Note: Clearances between tmpSegs.back() and other items are cached,
|
||||
// Note: Clearances between &s and other items are cached,
|
||||
// which means they'll be the same for all segments in the line.
|
||||
// Disabling the cache will lead to slowness.
|
||||
|
||||
tmpSegs.emplace_back( *aLine, aLine->CLine().CSegment( i ) );
|
||||
QueryColliding( &tmpSegs.back(), obstacleList, aOpts );
|
||||
const SEGMENT s( *aLine, aLine->CLine().CSegment( i ) );
|
||||
QueryColliding( &s, obstacleList, aOpts );
|
||||
}
|
||||
|
||||
if( aLine->EndsWithVia() )
|
||||
@@ -703,7 +699,7 @@ void NODE::doRemove( ITEM* aItem )
|
||||
if( aItem->BelongsTo( m_root ) && !isRoot() )
|
||||
{
|
||||
m_override.insert( aItem );
|
||||
|
||||
|
||||
if( aItem->HasHole() )
|
||||
m_override.insert( aItem->Hole() );
|
||||
}
|
||||
|
||||
@@ -156,6 +156,7 @@ public:
|
||||
|
||||
virtual void ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems ) {}
|
||||
virtual void ClearCaches() {}
|
||||
virtual void ClearTemporaryCaches() {}
|
||||
|
||||
virtual int ClearanceEpsilon() const { return 0; }
|
||||
};
|
||||
|
||||
@@ -494,6 +494,8 @@ bool ROUTER::Move( const VECTOR2I& aP, ITEM* endItem )
|
||||
break;
|
||||
}
|
||||
|
||||
GetRuleResolver()->ClearTemporaryCaches();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace PNS {
|
||||
|
||||
bool VIA::PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce )
|
||||
{
|
||||
int clearance = aNode->GetClearance( this, aOther );
|
||||
int clearance = aNode->GetClearance( this, aOther, false );
|
||||
VECTOR2I elementForces[4], force;
|
||||
size_t nf = 0;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user