Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 59f308850e | |||
| e775a7581a | |||
| ec99e10b31 | |||
| 240d7921ba | |||
| 9ac006b114 | |||
| c3fef1cae2 | |||
| ca1c99a0d1 | |||
| 06ae7a20df | |||
| f536ecdc00 | |||
| a4bdd38367 | |||
| 6fb6d3e444 | |||
| a3952c37bf | |||
| c24ee72fe3 | |||
| f988b807c4 | |||
| cb1a596b10 | |||
| c41cd75b62 | |||
| 0df48f3002 | |||
| 2d58184080 | |||
| 1ef6e69d83 | |||
| dbd4af16c6 | |||
| 2263535d3a | |||
| 3ce4a5bf7f | |||
| f62c3431ff | |||
| a652084922 | |||
| ed30599db8 | |||
| d8752970e9 | |||
| 57a4c93cf5 | |||
| a116f5783d | |||
| 2730098c42 | |||
| 836d17359f | |||
| a4406bb137 | |||
| bbb67b415a | |||
| 0842360dbb | |||
| e58b23a54b | |||
| 72342bd8a5 | |||
| fa77fc3310 | |||
| 53fe2a7203 | |||
| 480dfafb4f | |||
| f5e13c5ceb | |||
| eadc902f84 | |||
| b461a3b64d | |||
| 75c758e5f4 | |||
| 05912b0381 | |||
| fb387a2c09 | |||
| 1380feef72 | |||
| 6c91a3022e | |||
| e55989120e | |||
| 14f12ccf51 | |||
| 82a1b02b4a | |||
| e99a2fbc56 | |||
| d3d23d18fb | |||
| cbf1b2af47 | |||
| 4f5fca4e67 | |||
| e0158a8ed7 | |||
| 40e7ba67d1 | |||
| e664605c90 | |||
| 5a51a6b179 | |||
| eb7929ccb6 | |||
| e85eec2e6b | |||
| 121394c0c0 | |||
| 5ad74d58f8 | |||
| afe2516d57 | |||
| ccf6e78cd5 | |||
| fef91fc8b9 | |||
| 4156587abe | |||
| 629eccaf09 | |||
| f89803eb64 | |||
| da12746a3a | |||
| b023019b0d | |||
| 1fdaa1083e | |||
| 2f8e846b9e | |||
| e42db52d14 | |||
| d4ec455ce0 | |||
| 0547c7ba91 | |||
| 8f4b809599 | |||
| 8432fb4fea | |||
| 84df2c86ae | |||
| 49ced23ba8 | |||
| 00bf2ca36f | |||
| b92861c7e3 | |||
| a189102628 | |||
| 58a6eb653b | |||
| 9159f5a004 | |||
| f1c570d793 | |||
| 832af39f34 | |||
| 9260f58803 | |||
| 1a5c515e45 | |||
| 87c8688f70 | |||
| d13179f9e4 | |||
| 75de21314c | |||
| 239f8e0905 | |||
| f4ac624bb3 | |||
| 2be0b98135 | |||
| 36c5719535 | |||
| 1f870bf76c | |||
| d5cc334e20 | |||
| fd98188ee4 | |||
| c3446f9c2d | |||
| e6e29b6c4c | |||
| 6096b5c4d8 | |||
| 838eadd47f | |||
| afb43531e8 | |||
| 3533e8b119 | |||
| 0e6ad13db8 | |||
| 0d725fcc2c | |||
| 7e747085d3 | |||
| f1376396c2 | |||
| 4eb9281f4d | |||
| c895816cb0 | |||
| 9f814c0f6a | |||
| e14aef9bd5 | |||
| 2b106a2b34 | |||
| f6494d2980 | |||
| 78a6c1a909 | |||
| c858643100 | |||
| 17b0bdc9fe | |||
| 2e0cdebb0c | |||
| a5f8955d78 | |||
| 0eca698a39 | |||
| 16f259da81 | |||
| c3ee477a1b | |||
| 33da35db04 | |||
| 533be1ec70 | |||
| b64573f5d4 | |||
| 1d6caa9a35 | |||
| c74b0943f3 | |||
| bb579294a0 | |||
| ad9f5f4dd7 | |||
| bf2e34f32e | |||
| bd57957df7 | |||
| 093db600ff | |||
| 131013f291 | |||
| 909d199379 | |||
| 5f16f4ab99 | |||
| fff95fa4e6 | |||
| a38327a39c | |||
| 686048dcce | |||
| 330c0f86de | |||
| 83317f1b80 | |||
| a416eb2cf6 | |||
| 2959c94bd3 |
@@ -792,7 +792,7 @@ void BOARD_ADAPTER::buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BAS
|
||||
}
|
||||
|
||||
// For other shapes, add outlines as thick segments in polygon buffer
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon();
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon( ERROR_INSIDE );
|
||||
const SHAPE_LINE_CHAIN& path = corners->COutline( 0 );
|
||||
|
||||
for( int j = 0; j < path.PointCount(); j++ )
|
||||
|
||||
@@ -51,7 +51,7 @@ void BOARD_ADAPTER::buildPadOutlineAsPolygon( const PAD* aPad, SHAPE_POLY_SET& a
|
||||
}
|
||||
|
||||
// For other shapes, add outlines as thick segments in polygon buffer
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon();
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon( ERROR_INSIDE );
|
||||
const SHAPE_LINE_CHAIN& path = corners->COutline( 0 );
|
||||
|
||||
for( int ii = 0; ii < path.PointCount(); ++ii )
|
||||
|
||||
@@ -483,23 +483,21 @@ void MODEL_3D::Draw( bool aTransparent, float aOpacity, bool aUseSelectedMateria
|
||||
|
||||
// Sort from back to front
|
||||
std::sort( materialsSorted.begin(), materialsSorted.end(),
|
||||
[&]( std::pair<const MODEL_3D::MATERIAL*, float>& a,
|
||||
std::pair<const MODEL_3D::MATERIAL*, float>& b ) {
|
||||
// If A is inside B, then A is rendered first
|
||||
if( b.first->m_bbox.Inside( a.first->m_bbox ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( a.first->m_bbox.Inside( b.first->m_bbox ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
[&]( std::pair<const MODEL_3D::MATERIAL*, float>& a,
|
||||
std::pair<const MODEL_3D::MATERIAL*, float>& b )
|
||||
{
|
||||
bool aInsideB = a.first->m_bbox.Inside( b.first->m_bbox );
|
||||
bool bInsideA = b.first->m_bbox.Inside( a.first->m_bbox );
|
||||
|
||||
return a.second > b.second;
|
||||
} );
|
||||
// If A is inside B, then A is rendered first
|
||||
if( aInsideB != bInsideA )
|
||||
return bInsideA;
|
||||
|
||||
if( a.second != b.second )
|
||||
return a.second > b.second;
|
||||
|
||||
return a.first > b.first; // compare pointers as a last resort
|
||||
} );
|
||||
|
||||
for( const std::pair<const MODEL_3D::MATERIAL*, float>& mat : materialsSorted )
|
||||
{
|
||||
|
||||
@@ -1388,10 +1388,14 @@ void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatr
|
||||
|
||||
// Sort from back to front
|
||||
std::sort( transparentModelList.begin(), transparentModelList.end(),
|
||||
[&]( std::pair<const MODELTORENDER *, float>& a,
|
||||
std::pair<const MODELTORENDER *, float>& b ) {
|
||||
return a.second > b.second;
|
||||
} );
|
||||
[&]( std::pair<const MODELTORENDER *, float>& a,
|
||||
std::pair<const MODELTORENDER *, float>& b )
|
||||
{
|
||||
if( a.second != b.second )
|
||||
return a.second > b.second;
|
||||
|
||||
return a.first > b.first; // use pointers as a last resort
|
||||
} );
|
||||
|
||||
// Start rendering calls
|
||||
glPushMatrix();
|
||||
|
||||
@@ -1911,9 +1911,19 @@ void RENDER_3D_RAYTRACE::initializeBlockPositions()
|
||||
|
||||
const SFVEC2UI center( m_realBufferSize.x / 2, m_realBufferSize.y / 2 );
|
||||
std::sort( m_blockPositions.begin(), m_blockPositions.end(),
|
||||
[&]( const SFVEC2UI& a, const SFVEC2UI& b ) {
|
||||
[&]( const SFVEC2UI& a, const SFVEC2UI& b )
|
||||
{
|
||||
// Sort order: inside out.
|
||||
return distance( a, center ) < distance( b, center );
|
||||
float distanceA = distance( a, center );
|
||||
float distanceB = distance( b, center );
|
||||
|
||||
if( distanceA != distanceB )
|
||||
return distanceA < distanceB;
|
||||
|
||||
if( a[0] != b[0] )
|
||||
return a[0] < b[0];
|
||||
|
||||
return a[1] < b[1];
|
||||
} );
|
||||
|
||||
// Create m_shader buffer
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
# KiCad.
|
||||
#
|
||||
# Note: This version string should follow the semantic versioning system
|
||||
set( KICAD_SEMANTIC_VERSION "7.0.8" )
|
||||
set( KICAD_SEMANTIC_VERSION "7.0.9" )
|
||||
|
||||
# Default the version to the semantic version.
|
||||
# This is overridden by the git repository tag though (if using git)
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ long KIDIALOG::getStyle( KD_TYPE aType )
|
||||
}
|
||||
|
||||
|
||||
bool OverrideLock( wxWindow* aParent, const wxString& aMessage )
|
||||
bool AskOverrideLock( wxWindow* aParent, const wxString& aMessage )
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// wxMessageDialog gets the button spacing wrong on Mac so we have to use wxRichMessageDialog.
|
||||
|
||||
@@ -435,7 +435,22 @@ bool DATABASE_CONNECTION::SelectOne( const std::string& aTable,
|
||||
for( short i = 0; i < results.columns(); ++i )
|
||||
{
|
||||
std::string column = toUTF8( results.column_name( i ) );
|
||||
aResult[ column ] = toUTF8( results.get<nanodbc::string>( i, NANODBC_TEXT( "" ) ) );
|
||||
|
||||
switch( results.column_datatype( i ) )
|
||||
{
|
||||
case SQL_DOUBLE:
|
||||
case SQL_FLOAT:
|
||||
case SQL_REAL:
|
||||
case SQL_DECIMAL:
|
||||
case SQL_NUMERIC:
|
||||
{
|
||||
aResult[column] = fmt::format( "{:G}", results.get<double>( i ) );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
aResult[column] = toUTF8( results.get<nanodbc::string>( i, NANODBC_TEXT( "" ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( nanodbc::database_error& e )
|
||||
@@ -534,7 +549,23 @@ bool DATABASE_CONNECTION::SelectAll( const std::string& aTable, const std::strin
|
||||
for( short j = 0; j < results.columns(); ++j )
|
||||
{
|
||||
std::string column = toUTF8( results.column_name( j ) );
|
||||
result[column] = toUTF8( results.get<nanodbc::string>( j, NANODBC_TEXT( "" ) ) );
|
||||
|
||||
switch( results.column_datatype( j ) )
|
||||
{
|
||||
case SQL_DOUBLE:
|
||||
case SQL_FLOAT:
|
||||
case SQL_REAL:
|
||||
case SQL_DECIMAL:
|
||||
case SQL_NUMERIC:
|
||||
{
|
||||
result[column] = fmt::format( "{:G}", results.get<double>( j ) );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
result[column] = toUTF8( results.get<nanodbc::string>( j,
|
||||
NANODBC_TEXT( "" ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
aResults.emplace_back( std::move( result ) );
|
||||
|
||||
+36
-1
@@ -133,12 +133,41 @@ DIALOG_SHIM::DIALOG_SHIM( wxWindow* aParent, wxWindowID id, const wxString& titl
|
||||
Pgm().App().SetTopWindow( (EDA_BASE_FRAME*) kiwayHolder );
|
||||
#endif
|
||||
|
||||
Connect( wxEVT_PAINT, wxPaintEventHandler( DIALOG_SHIM::OnPaint ) );
|
||||
Bind( wxEVT_PAINT, &DIALOG_SHIM::OnPaint, this );
|
||||
}
|
||||
|
||||
|
||||
DIALOG_SHIM::~DIALOG_SHIM()
|
||||
{
|
||||
Unbind( wxEVT_CLOSE_WINDOW, &DIALOG_SHIM::OnCloseWindow, this );
|
||||
Unbind( wxEVT_BUTTON, &DIALOG_SHIM::OnButton, this );
|
||||
Unbind( wxEVT_PAINT, &DIALOG_SHIM::OnPaint, this );
|
||||
|
||||
std::function<void( wxWindowList& )> disconnectFocusHandlers = [&]( wxWindowList& children )
|
||||
{
|
||||
for( wxWindow* child : children )
|
||||
{
|
||||
if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( child ) )
|
||||
{
|
||||
textCtrl->Disconnect( wxEVT_SET_FOCUS,
|
||||
wxFocusEventHandler( DIALOG_SHIM::onChildSetFocus ), nullptr,
|
||||
this );
|
||||
}
|
||||
else if( wxStyledTextCtrl* scintilla = dynamic_cast<wxStyledTextCtrl*>( child ) )
|
||||
{
|
||||
scintilla->Disconnect( wxEVT_SET_FOCUS,
|
||||
wxFocusEventHandler( DIALOG_SHIM::onChildSetFocus ), nullptr,
|
||||
this );
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnectFocusHandlers( child->GetChildren() );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
disconnectFocusHandlers( GetChildren() );
|
||||
|
||||
// if the dialog is quasi-modal, this will end its event loop
|
||||
if( IsQuasiModal() )
|
||||
EndQuasiModal( wxID_CANCEL );
|
||||
@@ -282,6 +311,9 @@ bool DIALOG_SHIM::Show( bool show )
|
||||
ret = wxDialog::Show( show );
|
||||
}
|
||||
|
||||
if( m_parent )
|
||||
m_parent->SetFocus();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -483,6 +515,9 @@ int DIALOG_SHIM::ShowQuasiModal()
|
||||
|
||||
m_qmodal_showing = false;
|
||||
|
||||
if( parent )
|
||||
parent->SetFocus();
|
||||
|
||||
return GetReturnCode();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,14 +106,22 @@ void DS_PROXY_VIEW_ITEM::ViewDraw( int aLayer, VIEW* aView ) const
|
||||
|
||||
buildDrawList( aView, m_properties, &drawList );
|
||||
|
||||
BOX2I viewport( aView->GetViewport().GetOrigin(), aView->GetViewport().GetSize() );
|
||||
|
||||
// Draw the title block normally even if the view is flipped
|
||||
bool flipped = gal->IsFlippedX();
|
||||
|
||||
if( flipped )
|
||||
{
|
||||
int pageWidth = m_pageInfo->GetWidthMils() * m_mils2IUscalefactor;
|
||||
|
||||
gal->Save();
|
||||
gal->Translate( VECTOR2D( m_pageInfo->GetWidthMils() * m_mils2IUscalefactor, 0 ) );
|
||||
gal->Translate( VECTOR2D( pageWidth, 0 ) );
|
||||
gal->Scale( VECTOR2D( -1.0, 1.0 ) );
|
||||
|
||||
int right = pageWidth - viewport.GetLeft();
|
||||
int left = right - viewport.GetWidth();
|
||||
viewport.SetOrigin( left, viewport.GetTop() );
|
||||
}
|
||||
|
||||
DS_PAINTER ws_painter( gal );
|
||||
@@ -126,8 +134,6 @@ void DS_PROXY_VIEW_ITEM::ViewDraw( int aLayer, VIEW* aView ) const
|
||||
ws_settings->SetDefaultFont( settings->GetDefaultFont() );
|
||||
|
||||
// Draw all the components that make the drawing sheet
|
||||
BOX2I viewport( aView->GetViewport().GetOrigin(), aView->GetViewport().GetSize() );
|
||||
|
||||
for( DS_DRAW_ITEM_BASE* item = drawList.GetFirst(); item; item = drawList.GetNext() )
|
||||
{
|
||||
if( viewport.Intersects( item->GetApproxBBox() ) )
|
||||
|
||||
@@ -230,6 +230,15 @@ bool EDA_DRAW_FRAME::LockFile( const wxString& aFileName )
|
||||
|
||||
m_file_checker = std::make_unique<LOCKFILE>( aFileName );
|
||||
|
||||
if( !m_file_checker->Valid() && m_file_checker->IsLockedByMe() )
|
||||
{
|
||||
// If we cannot acquire the lock but we appear to be the one who
|
||||
// locked it, check to see if there is another KiCad instance running.
|
||||
// If there is not, then we can override the lock. This could happen if
|
||||
// KiCad crashed or was interrupted
|
||||
if( !Pgm().SingleInstance()->IsAnotherRunning() )
|
||||
m_file_checker->OverrideLock();
|
||||
}
|
||||
// If the file is valid, return true. This could mean that the file is
|
||||
// locked or it could mean that the file is read-only
|
||||
return m_file_checker->Valid();
|
||||
|
||||
+10
-6
@@ -881,7 +881,8 @@ void EDA_TEXT::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControl
|
||||
|
||||
|
||||
std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( bool aTriangulate,
|
||||
bool aUseTextRotation ) const
|
||||
const BOX2I& aBBox,
|
||||
const EDA_ANGLE& aAngle ) const
|
||||
{
|
||||
std::shared_ptr<SHAPE_COMPOUND> shape = std::make_shared<SHAPE_COMPOUND>();
|
||||
KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
|
||||
@@ -893,17 +894,20 @@ std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( bool aTriangula
|
||||
|
||||
std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
|
||||
|
||||
if( aUseTextRotation )
|
||||
if( aBBox.GetWidth() )
|
||||
{
|
||||
drawPos = aBBox.GetCenter();
|
||||
attrs.m_Halign = GR_TEXT_H_ALIGN_CENTER;
|
||||
attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER;
|
||||
attrs.m_Angle = aAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
attrs.m_Angle = GetDrawRotation();
|
||||
|
||||
if( font->IsOutline() )
|
||||
cache = GetRenderCache( font, shownText, VECTOR2I() );
|
||||
}
|
||||
else
|
||||
{
|
||||
attrs.m_Angle = ANGLE_0;
|
||||
}
|
||||
|
||||
if( aTriangulate )
|
||||
{
|
||||
|
||||
@@ -253,11 +253,6 @@ wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName, const wxStri
|
||||
// first attempt to use the name as specified:
|
||||
wxString tname = aFileName;
|
||||
|
||||
#ifdef _WIN32
|
||||
// translate from KiCad's internal UNIX-like path to MSWin paths
|
||||
tname.Replace( wxT( "/" ), wxT( "\\" ) );
|
||||
#endif
|
||||
|
||||
// Note: variable expansion must preferably be performed via a threadsafe wrapper for the
|
||||
// getenv() system call. If we allow the wxFileName::Normalize() routine to perform expansion
|
||||
// then we will have a race condition since wxWidgets does not assure a threadsafe wrapper
|
||||
|
||||
@@ -94,9 +94,10 @@ public:
|
||||
private:
|
||||
struct CACHED_BITMAP
|
||||
{
|
||||
GLuint id;
|
||||
int w, h;
|
||||
size_t size;
|
||||
GLuint id;
|
||||
int w, h;
|
||||
size_t size;
|
||||
long long int accessTime;
|
||||
};
|
||||
|
||||
GLuint cacheBitmap( const BITMAP_BASE* aBitmap );
|
||||
@@ -105,9 +106,9 @@ private:
|
||||
const size_t m_cacheMaxSize = 256 * 1024 * 1024;
|
||||
|
||||
std::map<const KIID, CACHED_BITMAP> m_bitmaps;
|
||||
std::list<KIID> m_cacheLru;
|
||||
size_t m_cacheSize;
|
||||
std::list<GLuint> m_freedTextureIds;
|
||||
std::list<KIID> m_cacheLru;
|
||||
size_t m_cacheSize;
|
||||
std::list<GLuint> m_freedTextureIds;
|
||||
};
|
||||
|
||||
}; // namespace KIGFX
|
||||
@@ -130,6 +131,7 @@ GLuint GL_BITMAP_CACHE::RequestBitmap( const BITMAP_BASE* aBitmap )
|
||||
// A bitmap is found in cache bitmap. Ensure the associated texture is still valid.
|
||||
if( glIsTexture( it->second.id ) )
|
||||
{
|
||||
it->second.accessTime = wxGetUTCTimeMillis().GetValue();
|
||||
return it->second.id;
|
||||
}
|
||||
else
|
||||
@@ -182,56 +184,115 @@ GLuint GL_BITMAP_CACHE::cacheBitmap( const BITMAP_BASE* aBitmap )
|
||||
m_freedTextureIds.pop_front();
|
||||
}
|
||||
|
||||
bmp.size = bmp.w * bmp.h * 4;
|
||||
auto buf = std::make_unique<uint8_t[]>( bmp.size );
|
||||
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
|
||||
|
||||
for( int y = 0; y < bmp.h; y++ )
|
||||
if( imgData.HasAlpha() || imgData.HasMask() )
|
||||
{
|
||||
for( int x = 0; x < bmp.w; x++ )
|
||||
bmp.size = bmp.w * bmp.h * 4;
|
||||
auto buf = std::make_unique<uint8_t[]>( bmp.size );
|
||||
|
||||
uint8_t* dstP = buf.get();
|
||||
uint8_t* srcP = imgData.GetData();
|
||||
|
||||
long long pxCount = static_cast<long long>( bmp.w ) * bmp.h;
|
||||
|
||||
if( imgData.HasAlpha() )
|
||||
{
|
||||
uint8_t* p = buf.get() + ( bmp.w * y + x ) * 4;
|
||||
uint8_t* srcAlpha = imgData.GetAlpha();
|
||||
|
||||
p[0] = imgData.GetRed( x, y );
|
||||
p[1] = imgData.GetGreen( x, y );
|
||||
p[2] = imgData.GetBlue( x, y );
|
||||
for( long long px = 0; px < pxCount; px++ )
|
||||
{
|
||||
memcpy( dstP, srcP, 3 );
|
||||
dstP[3] = *srcAlpha;
|
||||
|
||||
if( imgData.HasAlpha() )
|
||||
p[3] = imgData.GetAlpha( x, y );
|
||||
else if( imgData.HasMask() && p[0] == imgData.GetMaskRed()
|
||||
&& p[1] == imgData.GetMaskGreen() && p[2] == imgData.GetMaskBlue() )
|
||||
p[3] = wxALPHA_TRANSPARENT;
|
||||
else
|
||||
p[3] = wxALPHA_OPAQUE;
|
||||
srcAlpha += 1;
|
||||
srcP += 3;
|
||||
dstP += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( imgData.HasMask() )
|
||||
{
|
||||
uint8_t maskRed = imgData.GetMaskRed();
|
||||
uint8_t maskGreen = imgData.GetMaskGreen();
|
||||
uint8_t maskBlue = imgData.GetMaskBlue();
|
||||
|
||||
glBindTexture( GL_TEXTURE_2D, textureID );
|
||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buf.get() );
|
||||
for( long long px = 0; px < pxCount; px++ )
|
||||
{
|
||||
memcpy( dstP, srcP, 3 );
|
||||
|
||||
if( srcP[0] == maskRed && srcP[1] == maskGreen && srcP[2] == maskBlue )
|
||||
dstP[3] = wxALPHA_TRANSPARENT;
|
||||
else
|
||||
dstP[3] = wxALPHA_OPAQUE;
|
||||
|
||||
srcP += 3;
|
||||
dstP += 4;
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture( GL_TEXTURE_2D, textureID );
|
||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buf.get() );
|
||||
}
|
||||
else
|
||||
{
|
||||
bmp.size = bmp.w * bmp.h * 3;
|
||||
|
||||
uint8_t* srcP = imgData.GetData();
|
||||
|
||||
glBindTexture( GL_TEXTURE_2D, textureID );
|
||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, bmp.w, bmp.h, 0, GL_RGB, GL_UNSIGNED_BYTE, srcP );
|
||||
}
|
||||
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
||||
|
||||
long long currentTime = wxGetUTCTimeMillis().GetValue();
|
||||
|
||||
bmp.id = textureID;
|
||||
bmp.accessTime = currentTime;
|
||||
|
||||
#ifndef DISABLE_BITMAP_CACHE
|
||||
m_cacheLru.emplace_back( aBitmap->GetImageID() );
|
||||
m_cacheSize += bmp.size;
|
||||
m_bitmaps.emplace( aBitmap->GetImageID(), std::move( bmp ) );
|
||||
|
||||
if( m_cacheLru.size() > m_cacheMaxElements
|
||||
|| m_cacheSize > m_cacheMaxSize )
|
||||
if( m_cacheLru.size() + 1 > m_cacheMaxElements || m_cacheSize + bmp.size > m_cacheMaxSize )
|
||||
{
|
||||
KIID last = m_cacheLru.front();
|
||||
CACHED_BITMAP& cachedBitmap = m_bitmaps[last];
|
||||
KIID toRemove( 0 );
|
||||
auto toRemoveLru = m_cacheLru.end();
|
||||
|
||||
// Remove entries accessed > 1s ago first
|
||||
for( const auto& [kiid, cachedBmp] : m_bitmaps )
|
||||
{
|
||||
const int cacheTimeoutMillis = 1000L;
|
||||
|
||||
if( currentTime - cachedBmp.accessTime > cacheTimeoutMillis )
|
||||
{
|
||||
toRemove = kiid;
|
||||
toRemoveLru = std::find( m_cacheLru.begin(), m_cacheLru.end(), toRemove );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, remove the latest entry (it's less likely to be needed soon)
|
||||
if( toRemove == niluuid )
|
||||
{
|
||||
toRemoveLru = m_cacheLru.end();
|
||||
toRemoveLru--;
|
||||
|
||||
toRemove = *toRemoveLru;
|
||||
}
|
||||
|
||||
CACHED_BITMAP& cachedBitmap = m_bitmaps[toRemove];
|
||||
|
||||
m_cacheSize -= cachedBitmap.size;
|
||||
glDeleteTextures( 1, &cachedBitmap.id );
|
||||
m_freedTextureIds.emplace_back( cachedBitmap.id );
|
||||
|
||||
m_bitmaps.erase( last );
|
||||
m_cacheLru.pop_front();
|
||||
m_bitmaps.erase( toRemove );
|
||||
m_cacheLru.erase( toRemoveLru );
|
||||
}
|
||||
|
||||
m_cacheLru.emplace_back( aBitmap->GetImageID() );
|
||||
m_cacheSize += bmp.size;
|
||||
m_bitmaps.emplace( aBitmap->GetImageID(), std::move( bmp ) );
|
||||
#endif
|
||||
|
||||
return textureID;
|
||||
@@ -1111,13 +1172,21 @@ void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEn
|
||||
m_currentManager->Color( m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
|
||||
m_strokeColor.a );
|
||||
|
||||
std::deque<VECTOR2D> pointList;
|
||||
pointList.push_back( aStartPoint );
|
||||
pointList.push_back( diagonalPointA );
|
||||
pointList.push_back( aEndPoint );
|
||||
pointList.push_back( diagonalPointB );
|
||||
pointList.push_back( aStartPoint );
|
||||
DrawPolyline( pointList );
|
||||
// DrawLine (and DrawPolyline )
|
||||
// has problem with 0 length lines so enforce minimum
|
||||
if( aStartPoint == aEndPoint )
|
||||
DrawLine( aStartPoint + VECTOR2D( 1.0, 0.0 ), aEndPoint );
|
||||
else
|
||||
{
|
||||
std::deque<VECTOR2D> pointList;
|
||||
|
||||
pointList.push_back( aStartPoint );
|
||||
pointList.push_back( diagonalPointA );
|
||||
pointList.push_back( aEndPoint );
|
||||
pointList.push_back( diagonalPointB );
|
||||
pointList.push_back( aStartPoint );
|
||||
DrawPolyline( pointList );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+72
-12
@@ -38,6 +38,7 @@
|
||||
#include <gestfich.h>
|
||||
#include <string_utils.h>
|
||||
#include <launch_ext.h>
|
||||
#include "wx/tokenzr.h"
|
||||
|
||||
void QuoteString( wxString& string )
|
||||
{
|
||||
@@ -114,22 +115,78 @@ wxString FindKicadFile( const wxString& shortname )
|
||||
|
||||
int ExecuteFile( const wxString& aEditorName, const wxString& aFileName, wxProcess *aCallback )
|
||||
{
|
||||
wxString fullEditorName;
|
||||
wxString param;
|
||||
wxString fullEditorName;
|
||||
std::vector<wxString> params;
|
||||
|
||||
#ifdef __UNIX__
|
||||
int space = aEditorName.Find( ' ' );
|
||||
wxString param;
|
||||
bool inSingleQuotes = false;
|
||||
bool inDoubleQuotes = false;
|
||||
|
||||
if( space > 0 && !aEditorName.Contains( "\"" ) && !aEditorName.Contains( "\'" ) )
|
||||
auto pushParam =
|
||||
[&]()
|
||||
{
|
||||
if( !param.IsEmpty() )
|
||||
{
|
||||
params.push_back( param );
|
||||
param.clear();
|
||||
}
|
||||
};
|
||||
|
||||
for( wxUniChar ch : aEditorName )
|
||||
{
|
||||
fullEditorName = FindKicadFile( aEditorName.Mid( 0, space ) );
|
||||
param = aEditorName.Mid( space + 1 );
|
||||
if( inSingleQuotes )
|
||||
{
|
||||
if( ch == '\'' )
|
||||
{
|
||||
pushParam();
|
||||
inSingleQuotes = false;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
param += ch;
|
||||
}
|
||||
}
|
||||
else if( inDoubleQuotes )
|
||||
{
|
||||
if( ch == '"' )
|
||||
{
|
||||
pushParam();
|
||||
inDoubleQuotes = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
param += ch;
|
||||
}
|
||||
}
|
||||
else if( ch == '\'' )
|
||||
{
|
||||
pushParam();
|
||||
inSingleQuotes = true;
|
||||
}
|
||||
else if( ch == '"' )
|
||||
{
|
||||
pushParam();
|
||||
inDoubleQuotes = true;
|
||||
}
|
||||
else if( ch == ' ' )
|
||||
{
|
||||
pushParam();
|
||||
}
|
||||
else
|
||||
{
|
||||
param += ch;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
pushParam();
|
||||
|
||||
fullEditorName = FindKicadFile( params[0] );
|
||||
params.erase( params.begin() );
|
||||
#else
|
||||
fullEditorName = FindKicadFile( aEditorName );
|
||||
#endif
|
||||
{
|
||||
fullEditorName = FindKicadFile( aEditorName );
|
||||
}
|
||||
|
||||
if( wxFileExists( fullEditorName ) )
|
||||
{
|
||||
@@ -138,8 +195,11 @@ int ExecuteFile( const wxString& aEditorName, const wxString& aFileName, wxProce
|
||||
|
||||
args[i++] = fullEditorName.wc_str();
|
||||
|
||||
if( !param.IsEmpty() )
|
||||
args[i++] = param.wc_str();
|
||||
if( !params.empty() )
|
||||
{
|
||||
for( const wxString& p : params )
|
||||
args[i++] = p.wc_str();
|
||||
}
|
||||
|
||||
if( !aFileName.IsEmpty() )
|
||||
args[i++] = aFileName.wc_str();
|
||||
|
||||
@@ -322,8 +322,6 @@ 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() )
|
||||
|
||||
@@ -288,6 +288,24 @@ wxString PATHS::GetStockPlugins3DPath()
|
||||
fn.AppendDir( wxT( "plugins" ) );
|
||||
#elif defined( __WXMAC__ )
|
||||
fn.Assign( wxStandardPaths::Get().GetPluginsDir(), wxEmptyString );
|
||||
|
||||
// This must be mapped to main bundle for everything but kicad.app
|
||||
const wxArrayString dirs = fn.GetDirs();
|
||||
|
||||
// Check if we are the main kicad binary. in this case, the path will be
|
||||
// /path/to/bundlename.app/Contents/PlugIns
|
||||
// If we are an aux binary, the path will be something like
|
||||
// /path/to/bundlename.app/Contents/Applications/<standalone>.app/Contents/PlugIns
|
||||
if( dirs.GetCount() >= 6 &&
|
||||
dirs[dirs.GetCount() - 4] == wxT( "Applications" ) &&
|
||||
dirs[dirs.GetCount() - 6].Lower().EndsWith( wxT( "app" ) ) )
|
||||
{
|
||||
fn.RemoveLastDir();
|
||||
fn.RemoveLastDir();
|
||||
fn.RemoveLastDir();
|
||||
fn.RemoveLastDir();
|
||||
fn.AppendDir( wxT( "PlugIns" ) );
|
||||
}
|
||||
#else
|
||||
if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
|
||||
{
|
||||
|
||||
@@ -161,6 +161,8 @@ void PGM_BASE::Destroy()
|
||||
// unlike a normal destructor, this is designed to be called more than once safely:
|
||||
delete m_locale;
|
||||
m_locale = nullptr;
|
||||
|
||||
m_pgm_checker.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -443,6 +445,8 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest )
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
m_pgm_checker = std::make_unique<wxSingleInstanceChecker>();
|
||||
m_pgm_checker->Create( pgm_name, wxStandardPaths::Get().GetTempDir() );
|
||||
|
||||
// Init KiCad environment
|
||||
// the environment variable KICAD (if exists) gives the kicad path:
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <string_utils.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <trigo.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
/**
|
||||
* Oblique angle for DXF native text
|
||||
@@ -146,6 +147,26 @@ void DXF_PLOTTER::SetUnits( DXF_UNITS aUnit )
|
||||
}
|
||||
|
||||
|
||||
// convert aValue to a string, and remove trailing zeros
|
||||
// In DXF files coordinates need a high precision: at least 9 digits when given
|
||||
// in inches and 7 digits when in mm.
|
||||
// So we use 16 digits and remove trailing 0 (if any)
|
||||
static std::string formatCoord( double aValue )
|
||||
{
|
||||
std::string buf;
|
||||
|
||||
buf = fmt::format( "{:.16f}", aValue );
|
||||
|
||||
// remove trailing zeros
|
||||
while( !buf.empty() && buf[buf.size() - 1] == '0' )
|
||||
{
|
||||
buf.pop_back();
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
void DXF_PLOTTER::SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil,
|
||||
double aScale, bool aMirror )
|
||||
{
|
||||
@@ -417,11 +438,24 @@ void DXF_PLOTTER::SetColor( const COLOR4D& color )
|
||||
void DXF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
|
||||
{
|
||||
wxASSERT( m_outputFile );
|
||||
MoveTo( p1 );
|
||||
LineTo( VECTOR2I( p1.x, p2.y ) );
|
||||
LineTo( VECTOR2I( p2.x, p2.y ) );
|
||||
LineTo( VECTOR2I( p2.x, p1.y ) );
|
||||
FinishTo( VECTOR2I( p1.x, p1.y ) );
|
||||
|
||||
if( p1 != p2 )
|
||||
{
|
||||
MoveTo( p1 );
|
||||
LineTo( VECTOR2I( p1.x, p2.y ) );
|
||||
LineTo( VECTOR2I( p2.x, p2.y ) );
|
||||
LineTo( VECTOR2I( p2.x, p1.y ) );
|
||||
FinishTo( VECTOR2I( p1.x, p1.y ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw as a point
|
||||
wxString cname = getDXFColorName( m_currentColor );
|
||||
VECTOR2D point_dev = userToDeviceCoordinates( p1 );
|
||||
|
||||
fprintf( m_outputFile, "0\nPOINT\n8\n%s\n10\n%g\n20\n%g\n", TO_UTF8( cname ), point_dev.x,
|
||||
point_dev.y );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -431,31 +465,43 @@ void DXF_PLOTTER::Circle( const VECTOR2I& centre, int diameter, FILL_T fill, int
|
||||
double radius = userToDeviceSize( diameter / 2 );
|
||||
VECTOR2D centre_dev = userToDeviceCoordinates( centre );
|
||||
|
||||
wxString cname = getDXFColorName( m_currentColor );
|
||||
|
||||
if( radius > 0 )
|
||||
{
|
||||
wxString cname = getDXFColorName( m_currentColor );
|
||||
|
||||
if( fill == FILL_T::NO_FILL )
|
||||
{
|
||||
fprintf( m_outputFile, "0\nCIRCLE\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n",
|
||||
fprintf( m_outputFile, "0\nCIRCLE\n8\n%s\n10\n%s\n20\n%s\n40\n%s\n",
|
||||
TO_UTF8( cname ),
|
||||
centre_dev.x, centre_dev.y, radius );
|
||||
formatCoord( centre_dev.x ).c_str(),
|
||||
formatCoord( centre_dev.y ).c_str(),
|
||||
formatCoord( radius ).c_str() );
|
||||
}
|
||||
else if( fill == FILL_T::FILLED_SHAPE )
|
||||
{
|
||||
double r = radius*0.5;
|
||||
double r = radius * 0.5;
|
||||
fprintf( m_outputFile, "0\nPOLYLINE\n" );
|
||||
fprintf( m_outputFile, "8\n%s\n66\n1\n70\n1\n", TO_UTF8( cname ) );
|
||||
fprintf( m_outputFile, "40\n%g\n41\n%g\n", radius, radius);
|
||||
fprintf( m_outputFile, "40\n%s\n41\n%s\n",
|
||||
formatCoord( radius ).c_str(),
|
||||
formatCoord( radius ).c_str() );
|
||||
fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) );
|
||||
fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
|
||||
centre_dev.x-r, centre_dev.y );
|
||||
fprintf( m_outputFile, "10\n%s\n 20\n%s\n42\n1.0\n",
|
||||
formatCoord( centre_dev.x-r ).c_str(),
|
||||
formatCoord( centre_dev.y ).c_str() );
|
||||
fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) );
|
||||
fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
|
||||
centre_dev.x+r, centre_dev.y );
|
||||
fprintf( m_outputFile, "10\n%s\n 20\n%s\n42\n1.0\n",
|
||||
formatCoord( centre_dev.x+r ).c_str(),
|
||||
formatCoord( centre_dev.y ).c_str() );
|
||||
fprintf( m_outputFile, "0\nSEQEND\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw as a point
|
||||
fprintf( m_outputFile, "0\nPOINT\n8\n%s\n10\n%g\n20\n%g\n", TO_UTF8( cname ), centre_dev.x,
|
||||
centre_dev.y );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -578,9 +624,12 @@ void DXF_PLOTTER::PenTo( const VECTOR2I& pos, char plume )
|
||||
// DXF LINE
|
||||
wxString cname = getDXFColorName( m_currentColor );
|
||||
const char* lname = getDXFLineType( static_cast<PLOT_DASH_TYPE>( m_currentLineType ) );
|
||||
fprintf( m_outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%g\n20\n%g\n11\n%g\n21\n%g\n",
|
||||
fprintf( m_outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%s\n20\n%s\n11\n%s\n21\n%s\n",
|
||||
TO_UTF8( cname ), lname,
|
||||
pen_lastpos_dev.x, pen_lastpos_dev.y, pos_dev.x, pos_dev.y );
|
||||
formatCoord( pen_lastpos_dev.x ).c_str(),
|
||||
formatCoord( pen_lastpos_dev.y ).c_str(),
|
||||
formatCoord( pos_dev.x ).c_str(),
|
||||
formatCoord( pos_dev.y ).c_str() );
|
||||
}
|
||||
|
||||
m_penLastpos = pos;
|
||||
@@ -626,8 +675,8 @@ void DXF_PLOTTER::ThickSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, in
|
||||
}
|
||||
|
||||
|
||||
void DXF_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth )
|
||||
void DXF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
{
|
||||
wxASSERT( m_outputFile );
|
||||
|
||||
@@ -648,9 +697,11 @@ void DXF_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
// Emit a DXF ARC entity
|
||||
wxString cname = getDXFColorName( m_currentColor );
|
||||
fprintf( m_outputFile,
|
||||
"0\nARC\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n50\n%g\n51\n%g\n",
|
||||
"0\nARC\n8\n%s\n10\n%s\n20\n%s\n40\n%s\n50\n%.8f\n51\n%.8f\n",
|
||||
TO_UTF8( cname ),
|
||||
centre_device.x, centre_device.y, radius_device,
|
||||
formatCoord( centre_device.x ).c_str(),
|
||||
formatCoord( centre_device.y ).c_str(),
|
||||
formatCoord( radius_device ).c_str(),
|
||||
startAngle.AsDegrees(), endAngle.AsDegrees() );
|
||||
}
|
||||
|
||||
@@ -936,21 +987,21 @@ void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor
|
||||
" 8\n"
|
||||
"%s\n" // Layer name
|
||||
" 10\n"
|
||||
"%g\n" // First point X
|
||||
"%s\n" // First point X
|
||||
" 11\n"
|
||||
"%g\n" // Second point X
|
||||
"%s\n" // Second point X
|
||||
" 20\n"
|
||||
"%g\n" // First point Y
|
||||
"%s\n" // First point Y
|
||||
" 21\n"
|
||||
"%g\n" // Second point Y
|
||||
"%s\n" // Second point Y
|
||||
" 40\n"
|
||||
"%g\n" // Text height
|
||||
"%s\n" // Text height
|
||||
" 41\n"
|
||||
"%g\n" // Width factor
|
||||
"%s\n" // Width factor
|
||||
" 50\n"
|
||||
"%g\n" // Rotation
|
||||
"%.8f\n" // Rotation
|
||||
" 51\n"
|
||||
"%g\n" // Oblique angle
|
||||
"%.8f\n" // Oblique angle
|
||||
" 71\n"
|
||||
"%d\n" // Mirror flags
|
||||
" 72\n"
|
||||
@@ -960,9 +1011,9 @@ void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor
|
||||
aAttributes.m_Bold ?
|
||||
(aAttributes.m_Italic ? "KICADBI" : "KICADB")
|
||||
: (aAttributes.m_Italic ? "KICADI" : "KICAD"), TO_UTF8( cname ),
|
||||
origin_dev.x, origin_dev.x,
|
||||
origin_dev.y, origin_dev.y,
|
||||
size_dev.y, fabs( size_dev.x / size_dev.y ),
|
||||
formatCoord( origin_dev.x ).c_str(), formatCoord( origin_dev.x ).c_str(),
|
||||
formatCoord( origin_dev.y ).c_str(), formatCoord( origin_dev.y ).c_str(),
|
||||
formatCoord( size_dev.y ).c_str(), formatCoord( fabs( size_dev.x / size_dev.y ) ).c_str(),
|
||||
aAttributes.m_Angle.AsDegrees(),
|
||||
aAttributes.m_Italic ? DXF_OBLIQUE_ANGLE : 0,
|
||||
aAttributes.m_Mirrored ? 2 : 0, // X mirror flag
|
||||
|
||||
@@ -826,8 +826,8 @@ void GERBER_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFil
|
||||
|
||||
|
||||
|
||||
void GERBER_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth )
|
||||
void GERBER_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
{
|
||||
SetCurrentLineWidth( aWidth );
|
||||
|
||||
@@ -1149,8 +1149,8 @@ void GERBER_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, i
|
||||
}
|
||||
}
|
||||
|
||||
void GERBER_PLOTTER::ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, int aWidth,
|
||||
void GERBER_PLOTTER::ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, int aWidth,
|
||||
OUTLINE_MODE aTraceMode, void* aData )
|
||||
{
|
||||
GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
|
||||
|
||||
@@ -394,22 +394,16 @@ void HPGL_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T aFill, i
|
||||
VECTOR2D p1_device = userToDeviceCoordinates( p1 );
|
||||
VECTOR2D p2_device = userToDeviceCoordinates( p2 );
|
||||
|
||||
MoveTo( p1 );
|
||||
// EA command seems to always fill the rectangle, so plot as a polygon instead
|
||||
std::vector<VECTOR2I> cornerList;
|
||||
|
||||
if( aFill == FILL_T::FILLED_SHAPE )
|
||||
{
|
||||
startOrAppendItem( p1_device, wxString::Format( "RA %.0f,%.0f;",
|
||||
p2_device.x,
|
||||
p2_device.y ) );
|
||||
}
|
||||
cornerList.emplace_back( p1.x, p1.y );
|
||||
cornerList.emplace_back( p2.x, p1.y );
|
||||
cornerList.emplace_back( p2.x, p2.y );
|
||||
cornerList.emplace_back( p1.x, p2.y );
|
||||
cornerList.emplace_back( p1.x, p1.y );
|
||||
|
||||
startOrAppendItem( p1_device, wxString::Format( "EA %.0f,%.0f;",
|
||||
p2_device.x,
|
||||
p2_device.y ) );
|
||||
|
||||
m_current_item->loc_end = m_current_item->loc_start;
|
||||
m_current_item->bbox.Merge( p2_device );
|
||||
PenFinish();
|
||||
PlotPoly( cornerList, aFill, aWidth, nullptr );
|
||||
}
|
||||
|
||||
|
||||
@@ -435,7 +429,6 @@ void HPGL_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFill,
|
||||
chord_angle.AsDegrees(),
|
||||
hpgl_end_polygon_cmd ) );
|
||||
m_current_item->lift_before = true;
|
||||
m_current_item->pen_returns = true;
|
||||
m_current_item->bbox.Merge( BOX2D( center_dev - radius,
|
||||
VECTOR2D( 2 * radius, 2 * radius ) ) );
|
||||
PenFinish();
|
||||
@@ -448,7 +441,6 @@ void HPGL_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFill,
|
||||
radius,
|
||||
chord_angle.AsDegrees() ) );
|
||||
m_current_item->lift_before = true;
|
||||
m_current_item->pen_returns = true;
|
||||
m_current_item->bbox.Merge( BOX2D( center_dev - radius,
|
||||
VECTOR2D( 2 * radius, 2 * radius ) ) );
|
||||
PenFinish();
|
||||
@@ -567,8 +559,8 @@ void HPGL_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end,
|
||||
}
|
||||
|
||||
|
||||
void HPGL_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth )
|
||||
void HPGL_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
{
|
||||
if( aRadius <= 0 )
|
||||
return;
|
||||
|
||||
@@ -217,12 +217,43 @@ void PDF_PLOTTER::SetDash( int aLineWidth, PLOT_DASH_TYPE aLineStyle )
|
||||
void PDF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
|
||||
{
|
||||
wxASSERT( m_workFile );
|
||||
|
||||
SetCurrentLineWidth( width );
|
||||
|
||||
VECTOR2I size = p2 - p1;
|
||||
|
||||
if( size.x == 0 && size.y == 0 )
|
||||
{
|
||||
// Can't draw zero-sized rectangles
|
||||
SetCurrentLineWidth( width );
|
||||
|
||||
MoveTo( VECTOR2I( p1.x, p1.y ) );
|
||||
FinishTo( VECTOR2I( p1.x, p1.y ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( std::min( std::abs( size.x ), std::abs( size.y ) ) < width )
|
||||
{
|
||||
// Too thick stroked rectangles are buggy, draw as polygon
|
||||
std::vector<VECTOR2I> cornerList;
|
||||
|
||||
cornerList.emplace_back( p1.x, p1.y );
|
||||
cornerList.emplace_back( p2.x, p1.y );
|
||||
cornerList.emplace_back( p2.x, p2.y );
|
||||
cornerList.emplace_back( p1.x, p2.y );
|
||||
cornerList.emplace_back( p1.x, p1.y );
|
||||
|
||||
PlotPoly( cornerList, fill, width, nullptr );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
VECTOR2D p1_dev = userToDeviceCoordinates( p1 );
|
||||
VECTOR2D p2_dev = userToDeviceCoordinates( p2 );
|
||||
|
||||
SetCurrentLineWidth( width );
|
||||
fprintf( m_workFile, "%g %g %g %g re %c\n", p1_dev.x, p1_dev.y,
|
||||
p2_dev.x - p1_dev.x, p2_dev.y - p1_dev.y, fill == FILL_T::NO_FILL ? 'S' : 'B' );
|
||||
fprintf( m_workFile, "%g %g %g %g re %c\n", p1_dev.x, p1_dev.y, p2_dev.x - p1_dev.x,
|
||||
p2_dev.y - p1_dev.y, fill == FILL_T::NO_FILL ? 'S' : 'B' );
|
||||
}
|
||||
|
||||
|
||||
@@ -336,8 +367,8 @@ void PDF_PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VE
|
||||
}
|
||||
|
||||
|
||||
void PDF_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth )
|
||||
void PDF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
{
|
||||
wxASSERT( m_workFile );
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ void PSLIKE_PLOTTER::FlashPadOval( const VECTOR2I& aPadPos, const VECTOR2I& aSiz
|
||||
if( aTraceMode == FILLED )
|
||||
ThickSegment( a + aPadPos, b + aPadPos, size.x, aTraceMode, nullptr );
|
||||
else
|
||||
sketchOval( aPadPos, size, orient, -1 );
|
||||
sketchOval( aPadPos, size, orient, USE_DEFAULT_LINE_WIDTH );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -443,8 +443,8 @@ void SVG_PLOTTER::Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int wi
|
||||
}
|
||||
|
||||
|
||||
void SVG_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth )
|
||||
void SVG_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
{
|
||||
/* Draws an arc of a circle, centered on (xc,yc), with starting point (x1, y1) and ending
|
||||
* at (x2, y2). The current pen is used for the outline and the current brush for filling
|
||||
|
||||
@@ -145,13 +145,31 @@ double PLOTTER::GetDashGapLenIU( int aLineWidth ) const
|
||||
return userToDeviceSize( m_renderSettings->GetGapLength( aLineWidth ) );
|
||||
}
|
||||
|
||||
|
||||
#include <wx/log.h>
|
||||
void PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR2I& aEnd,
|
||||
FILL_T aFill, int aWidth, int aMaxError )
|
||||
{
|
||||
EDA_ANGLE startAngle( aStart - aCenter );
|
||||
EDA_ANGLE endAngle( aEnd - aCenter );
|
||||
int radius = ( aStart - aCenter ).EuclideanNorm();
|
||||
// Recalculate aCenter using double to be sure we will use a exact value, from aStart and aEnd
|
||||
// it must be on the line passing by the middle of segment {aStart, aEnd}
|
||||
// To simplify calculations, use aStart as origin in intermediate calculations
|
||||
VECTOR2D center = aCenter - aStart;
|
||||
VECTOR2D end = aEnd - aStart;
|
||||
EDA_ANGLE segAngle( end );
|
||||
|
||||
// Rotate end and center, to make segment {aStart, aEnd} horizontal
|
||||
RotatePoint( end, segAngle );
|
||||
RotatePoint( center, segAngle );
|
||||
|
||||
// center.x must be at end.x/2 coordinate
|
||||
center.x = end.x / 2;
|
||||
|
||||
// Now calculate the right center position
|
||||
RotatePoint( center, -segAngle );
|
||||
center += aStart;
|
||||
|
||||
EDA_ANGLE startAngle( VECTOR2D( aStart ) - center );
|
||||
EDA_ANGLE endAngle( VECTOR2D( aEnd ) - center );
|
||||
double radius = ( VECTOR2D( aStart ) - center ).EuclideanNorm();
|
||||
|
||||
if( startAngle > endAngle )
|
||||
{
|
||||
@@ -168,12 +186,12 @@ void PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR
|
||||
startAngle = -startAngle;
|
||||
endAngle = -endAngle;
|
||||
|
||||
Arc( aCenter, startAngle, endAngle, radius, aFill, aWidth );
|
||||
Arc( center, startAngle, endAngle, radius, aFill, aWidth );
|
||||
}
|
||||
|
||||
|
||||
void PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth )
|
||||
void PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth )
|
||||
{
|
||||
EDA_ANGLE startAngle( aStartAngle );
|
||||
EDA_ANGLE endAngle( aEndAngle );
|
||||
@@ -570,8 +588,8 @@ void PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int widt
|
||||
}
|
||||
|
||||
|
||||
void PLOTTER::ThickArc( const VECTOR2I& centre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, int aWidth,
|
||||
void PLOTTER::ThickArc( const VECTOR2D& centre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, int aWidth,
|
||||
OUTLINE_MODE aTraceMode, void* aData )
|
||||
{
|
||||
if( aTraceMode == FILLED )
|
||||
|
||||
@@ -569,9 +569,42 @@ bool PROJECT_FILE::SaveToFile( const wxString& aDirectory, bool aForce )
|
||||
|
||||
bool PROJECT_FILE::SaveAs( const wxString& aDirectory, const wxString& aFile )
|
||||
{
|
||||
wxFileName oldFilename( GetFilename() );
|
||||
wxString oldProjectName = oldFilename.GetName();
|
||||
wxString oldProjectPath = oldFilename.GetPath();
|
||||
|
||||
Set( "meta.filename", aFile + "." + ProjectFileExtension );
|
||||
SetFilename( aFile );
|
||||
|
||||
auto updatePath =
|
||||
[&]( wxString& aPath )
|
||||
{
|
||||
if( aPath.StartsWith( oldProjectName + wxS( "." ) ) )
|
||||
aPath.Replace( oldProjectName, aFile, false );
|
||||
else if( aPath.StartsWith( oldProjectPath + wxS( "/" ) ) )
|
||||
aPath.Replace( oldProjectPath, aDirectory, false );
|
||||
};
|
||||
|
||||
updatePath( m_BoardDrawingSheetFile );
|
||||
|
||||
for( int ii = LAST_PATH_FIRST; ii < (int) LAST_PATH_SIZE; ++ii )
|
||||
updatePath( m_PcbLastPath[ ii ] );
|
||||
|
||||
auto updatePathByPtr =
|
||||
[&]( const std::string& aPtr )
|
||||
{
|
||||
if( std::optional<wxString> path = Get<wxString>( aPtr ) )
|
||||
{
|
||||
updatePath( path.value() );
|
||||
Set( aPtr, path.value() );
|
||||
}
|
||||
};
|
||||
|
||||
updatePathByPtr( "schematic.page_layout_descr_file" );
|
||||
updatePathByPtr( "schematic.plot_directory" );
|
||||
updatePathByPtr( "schematic.ngspice.workbook_filename" );
|
||||
updatePathByPtr( "pcbnew.page_layout_descr_file" );
|
||||
|
||||
// While performing Save As, we have already checked that we can write to the directory
|
||||
// so don't carry the previous flag
|
||||
SetReadOnly( false );
|
||||
|
||||
+2
-1
@@ -63,7 +63,8 @@ void RC_ITEM::SetItems( const EDA_ITEM* aItem, const EDA_ITEM* bItem,
|
||||
{
|
||||
m_ids.clear();
|
||||
|
||||
m_ids.push_back( aItem->m_Uuid );
|
||||
if( aItem )
|
||||
m_ids.push_back( aItem->m_Uuid );
|
||||
|
||||
if( bItem )
|
||||
m_ids.push_back( bItem->m_Uuid );
|
||||
|
||||
+10
-28
@@ -48,35 +48,17 @@
|
||||
|
||||
static int vprint( std::string* result, const char* format, va_list ap )
|
||||
{
|
||||
char msg[512];
|
||||
// This function can call vsnprintf twice.
|
||||
// But internally, vsnprintf retrieves arguments from the va_list identified by arg as if
|
||||
// va_arg was used on it, and thus the state of the va_list is likely to be altered by the call.
|
||||
// see: www.cplusplus.com/reference/cstdio/vsnprintf
|
||||
// we make a copy of va_list ap for the second call, if happens
|
||||
va_list tmp;
|
||||
va_copy( tmp, ap );
|
||||
size_t len = vsnprintf( nullptr, 0, format, tmp );
|
||||
va_end( tmp );
|
||||
|
||||
size_t len = vsnprintf( msg, sizeof(msg), format, ap );
|
||||
// Resize the output to hold the required data
|
||||
size_t size = result->size();
|
||||
result->resize( size + len );
|
||||
|
||||
if( len < sizeof(msg) ) // the output fit into msg
|
||||
{
|
||||
result->append( msg, msg + len );
|
||||
}
|
||||
else
|
||||
{
|
||||
// output was too big, so now incur the expense of allocating
|
||||
// a buf for holding suffient characters.
|
||||
|
||||
std::vector<char> buf;
|
||||
buf.reserve( len+1 ); // reserve(), not resize() which writes. +1 for trailing nul.
|
||||
|
||||
len = vsnprintf( &buf[0], len+1, format, tmp );
|
||||
|
||||
result->append( &buf[0], &buf[0] + len );
|
||||
}
|
||||
|
||||
va_end( tmp ); // Release the temporary va_list, initialised from ap
|
||||
// Now do the actual printing
|
||||
len = vsnprintf( result->data() + size, len + 1, format, ap );
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -443,18 +425,18 @@ int OUTPUTFORMATTER::vprint( const char* fmt, va_list ap )
|
||||
// we make a copy of va_list ap for the second call, if happens
|
||||
va_list tmp;
|
||||
va_copy( tmp, ap );
|
||||
int ret = vsnprintf( &m_buffer[0], m_buffer.size(), fmt, ap );
|
||||
int ret = vsnprintf( m_buffer.data(), m_buffer.size(), fmt, ap );
|
||||
|
||||
if( ret >= (int) m_buffer.size() )
|
||||
{
|
||||
m_buffer.resize( ret + 1000 );
|
||||
ret = vsnprintf( &m_buffer[0], m_buffer.size(), fmt, tmp );
|
||||
ret = vsnprintf( m_buffer.data(), m_buffer.size(), fmt, tmp );
|
||||
}
|
||||
|
||||
va_end( tmp ); // Release the temporary va_list, initialised from ap
|
||||
|
||||
if( ret > 0 )
|
||||
write( &m_buffer[0], ret );
|
||||
write( m_buffer.data(), ret );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,20 @@ principle should be easily implemented by adapting the current STL containers.
|
||||
%ignore to_json;
|
||||
%ignore from_json;
|
||||
|
||||
%rename(GetEffectiveTextShapeNative) EDA_TEXT::GetEffectiveTextShape;
|
||||
|
||||
%extend EDA_TEXT
|
||||
{
|
||||
%pythoncode
|
||||
%{
|
||||
|
||||
# KiCad 7.0 API Compatibility
|
||||
def GetEffectiveTextShape(self, aTriangulate=True, aUseTextRotation=True):
|
||||
return self.GetEffectiveTextShapeNative(aTriangulate)
|
||||
|
||||
%}
|
||||
}
|
||||
|
||||
// headers/imports that must be included in the _wrapper.cpp at top
|
||||
|
||||
%{
|
||||
|
||||
@@ -59,6 +59,10 @@ ACTION_MENU::ACTION_MENU( bool isContextMenu, TOOL_INTERACTIVE* aTool ) :
|
||||
|
||||
ACTION_MENU::~ACTION_MENU()
|
||||
{
|
||||
Disconnect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( ACTION_MENU::OnMenuEvent ),
|
||||
nullptr, this );
|
||||
Disconnect( wxEVT_IDLE, wxIdleEventHandler( ACTION_MENU::OnIdle ), nullptr, this );
|
||||
|
||||
// Set parent to NULL to prevent submenus from unregistering from a nonexistent object
|
||||
for( ACTION_MENU* menu : m_submenus )
|
||||
menu->SetParent( nullptr );
|
||||
|
||||
@@ -127,16 +127,9 @@ bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers )
|
||||
return a_name < b_name;
|
||||
};
|
||||
|
||||
auto strong_cmp = [this]( SCH_ITEM* a, SCH_ITEM* b ) -> bool
|
||||
{
|
||||
const wxString& a_name = GetNameForDriver( a );
|
||||
const wxString& b_name = GetNameForDriver( b );
|
||||
return a_name < b_name;
|
||||
};
|
||||
|
||||
PRIORITY highest_priority = PRIORITY::INVALID;
|
||||
std::set<SCH_ITEM*, decltype( candidate_cmp )> candidates( candidate_cmp );
|
||||
std::set<SCH_ITEM*, decltype( strong_cmp )> strong_drivers( strong_cmp );
|
||||
std::set<SCH_ITEM*> strong_drivers;
|
||||
|
||||
m_driver = nullptr;
|
||||
|
||||
@@ -239,7 +232,11 @@ void CONNECTION_SUBGRAPH::getAllConnectedItems( std::set<std::pair<SCH_SHEET_PAT
|
||||
sg = sg->m_absorbed_by;
|
||||
}
|
||||
|
||||
aSubgraphs.insert( sg );
|
||||
// If we are unable to insert the subgraph into the set, then we have already
|
||||
// visited it and don't need to add it again.
|
||||
if( aSubgraphs.insert( sg ).second == false )
|
||||
return;
|
||||
|
||||
aSubgraphs.insert( sg->m_absorbed_subgraphs.begin(), sg->m_absorbed_subgraphs.end() );
|
||||
|
||||
for( SCH_ITEM* item : sg->m_items )
|
||||
@@ -1345,11 +1342,15 @@ void CONNECTION_GRAPH::generateBusAliasMembers()
|
||||
wxString name = conn->FullLocalName();
|
||||
|
||||
CONNECTION_SUBGRAPH* new_sg = new CONNECTION_SUBGRAPH( this );
|
||||
|
||||
// This connection cannot form a part of the item because the item is not, itself
|
||||
// connected to this subgraph. It exists as part of a virtual item that may be connected
|
||||
// to other items but is not in the schematic.
|
||||
SCH_CONNECTION* new_conn = new SCH_CONNECTION( item, subgraph->m_sheet );
|
||||
new_conn->SetGraph( this );
|
||||
|
||||
new_conn->SetName( name );
|
||||
new_conn->SetType( CONNECTION_TYPE::NET );
|
||||
subgraph->StoreImplicitConnection( new_conn );
|
||||
int code = assignNewNetCode( *new_conn );
|
||||
|
||||
wxLogTrace( ConnTrace, wxS( "SG(%ld), Adding full local name (%s) with sg (%d) on subsheet %s" ), subgraph->m_code,
|
||||
@@ -3125,7 +3126,7 @@ bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( const CONNECTION_SUBGRAPH
|
||||
bus_name = bus_wire->Connection( &sheet )->Name();
|
||||
|
||||
std::set<wxString> test_names;
|
||||
test_names.insert( bus_entry->Connection( &sheet )->Name() );
|
||||
test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
|
||||
|
||||
wxString baseName = sheet.PathHumanReadable();
|
||||
|
||||
@@ -3138,11 +3139,11 @@ bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( const CONNECTION_SUBGRAPH
|
||||
{
|
||||
for( const auto& sub_member : member->Members() )
|
||||
{
|
||||
if( test_names.count( sub_member->Name() ) )
|
||||
if( test_names.count( sub_member->FullLocalName() ) )
|
||||
conflict = false;
|
||||
}
|
||||
}
|
||||
else if( test_names.count( member->Name() ) )
|
||||
else if( test_names.count( member->FullLocalName() ) )
|
||||
{
|
||||
conflict = false;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,11 @@ public:
|
||||
m_hier_parent( nullptr )
|
||||
{}
|
||||
|
||||
~CONNECTION_SUBGRAPH() = default;
|
||||
~CONNECTION_SUBGRAPH()
|
||||
{
|
||||
for( SCH_CONNECTION* connection : m_bus_element_connections )
|
||||
delete connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which potential driver should drive the subgraph.
|
||||
@@ -162,6 +166,13 @@ public:
|
||||
|
||||
void RemoveItem( SCH_ITEM* aItem );
|
||||
|
||||
// Use this to keep a connection pointer that is not owned by any item
|
||||
// This will be destroyed with the subgraph
|
||||
void StoreImplicitConnection( SCH_CONNECTION* aConnection )
|
||||
{
|
||||
m_bus_element_connections.insert( aConnection );
|
||||
}
|
||||
|
||||
private:
|
||||
wxString driverName( SCH_ITEM* aItem ) const;
|
||||
|
||||
@@ -251,6 +262,10 @@ public:
|
||||
/// A cache of escaped netnames from schematic items
|
||||
mutable std::unordered_map<SCH_ITEM*, wxString> m_driver_name_cache;
|
||||
|
||||
/// A cache of connections that are part of this subgraph but that don't have
|
||||
/// an owning element (i.e. bus members)
|
||||
std::set<SCH_CONNECTION*> m_bus_element_connections;
|
||||
|
||||
};
|
||||
|
||||
struct NET_NAME_CODE_CACHE_KEY
|
||||
|
||||
@@ -49,19 +49,20 @@ using CATEGORY = SIM_MODEL::PARAM::CATEGORY;
|
||||
|
||||
template <typename T_symbol, typename T_field>
|
||||
DIALOG_SIM_MODEL<T_symbol, T_field>::DIALOG_SIM_MODEL( wxWindow* aParent, T_symbol& aSymbol,
|
||||
std::vector<T_field>& aFields )
|
||||
: DIALOG_SIM_MODEL_BASE( aParent ),
|
||||
m_symbol( aSymbol ),
|
||||
m_fields( aFields ),
|
||||
m_libraryModelsMgr( &Prj() ),
|
||||
m_builtinModelsMgr( &Prj() ),
|
||||
m_prevModel( nullptr ),
|
||||
m_curModelType( SIM_MODEL::TYPE::NONE ),
|
||||
m_scintillaTricks( nullptr ),
|
||||
m_firstCategory( nullptr ),
|
||||
m_prevParamGridSelection( nullptr ),
|
||||
m_lastParamGridWidth( 0 ),
|
||||
m_inKillFocus( false )
|
||||
std::vector<T_field>& aFields ) :\
|
||||
DIALOG_SIM_MODEL_BASE( aParent ),
|
||||
m_symbol( aSymbol ),
|
||||
m_fields( aFields ),
|
||||
m_libraryModelsMgr( &Prj() ),
|
||||
m_builtinModelsMgr( &Prj() ),
|
||||
m_prevModel( nullptr ),
|
||||
m_curModelType( SIM_MODEL::TYPE::NONE ),
|
||||
m_scintillaTricksCode( nullptr ),
|
||||
m_scintillaTricksSubckt( nullptr ),
|
||||
m_firstCategory( nullptr ),
|
||||
m_prevParamGridSelection( nullptr ),
|
||||
m_lastParamGridWidth( 0 ),
|
||||
m_inKillFocus( false )
|
||||
{
|
||||
m_browseButton->SetBitmap( KiBitmap( BITMAPS::small_folder ) );
|
||||
|
||||
@@ -81,7 +82,8 @@ DIALOG_SIM_MODEL<T_symbol, T_field>::DIALOG_SIM_MODEL( wxWindow* aParent, T_symb
|
||||
|
||||
m_typeChoice->Clear();
|
||||
|
||||
m_scintillaTricks = new SCINTILLA_TRICKS( m_codePreview, wxT( "{}" ), false );
|
||||
m_scintillaTricksCode = new SCINTILLA_TRICKS( m_codePreview, wxT( "{}" ), false );
|
||||
m_scintillaTricksSubckt = new SCINTILLA_TRICKS( m_subckt, wxT( "()" ), false );
|
||||
|
||||
m_paramGridMgr->Bind( wxEVT_PG_SELECTED, &DIALOG_SIM_MODEL::onParamGridSelectionChange, this );
|
||||
|
||||
@@ -131,7 +133,8 @@ DIALOG_SIM_MODEL<T_symbol, T_field>::~DIALOG_SIM_MODEL()
|
||||
// Delete the GRID_TRICKS.
|
||||
m_pinAssignmentsGrid->PopEventHandler( true );
|
||||
|
||||
delete m_scintillaTricks;
|
||||
delete m_scintillaTricksCode;
|
||||
delete m_scintillaTricksSubckt;
|
||||
}
|
||||
|
||||
|
||||
@@ -663,6 +666,7 @@ void DIALOG_SIM_MODEL<T_symbol, T_field>::updatePinAssignments( SIM_MODEL* aMode
|
||||
{
|
||||
SIM_MODEL_SUBCKT* subckt = static_cast<SIM_MODEL_SUBCKT*>( aModel );
|
||||
m_subckt->SetText( subckt->GetSpiceCode() );
|
||||
m_subckt->SetEditable( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -130,7 +130,8 @@ private:
|
||||
std::map<SIM_MODEL::DEVICE_T, SIM_MODEL::TYPE> m_curModelTypeOfDeviceType;
|
||||
SIM_MODEL::TYPE m_curModelType;
|
||||
|
||||
SCINTILLA_TRICKS* m_scintillaTricks;
|
||||
SCINTILLA_TRICKS* m_scintillaTricksCode;
|
||||
SCINTILLA_TRICKS* m_scintillaTricksSubckt;
|
||||
|
||||
wxPGProperty* m_firstCategory; // Used to add principal parameters to root.
|
||||
wxPGProperty* m_prevParamGridSelection;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 Wayne Stambaugh <stambaughw@gmail.com>
|
||||
* Copyright (C) 2017-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2017-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 as published by the
|
||||
@@ -54,10 +54,16 @@ DIALOG_SYMBOL_REMAP::DIALOG_SYMBOL_REMAP( SCH_EDIT_FRAME* aParent ) :
|
||||
{
|
||||
m_remapped = false;
|
||||
|
||||
if( !wxFileName::IsDirWritable( Prj().GetProjectPath() ) )
|
||||
wxString projectPath = Prj().GetProjectPath();
|
||||
|
||||
if( !wxFileName::IsDirWritable( projectPath ) )
|
||||
{
|
||||
DisplayInfoMessage( this, _( "Remapping is not possible because you have insufficient "
|
||||
"privileges to the project folder '%s'." ) );
|
||||
wxString msg =
|
||||
wxString::Format( _( "Remapping is not possible because you have insufficient "
|
||||
"privileges to the project folder '%s'." ),
|
||||
projectPath );
|
||||
|
||||
DisplayInfoMessage( this, msg );
|
||||
|
||||
// Disable the remap button.
|
||||
m_remapped = true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
@@ -296,10 +296,10 @@ DIALOG_TEXT_PROPERTIES_BASE::DIALOG_TEXT_PROPERTIES_BASE( wxWindow* parent, wxWi
|
||||
bSizer11 = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_hyperlinkCombo = new WX_COMBOBOX( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
|
||||
bSizer11->Add( m_hyperlinkCombo, 1, wxALIGN_CENTER_VERTICAL, 5 );
|
||||
bSizer11->Add( m_hyperlinkCombo, 1, wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE, 5 );
|
||||
|
||||
|
||||
m_textEntrySizer->Add( bSizer11, wxGBPosition( 9, 1 ), wxGBSpan( 1, 6 ), wxEXPAND|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
|
||||
m_textEntrySizer->Add( bSizer11, wxGBPosition( 9, 1 ), wxGBSpan( 1, 5 ), wxEXPAND|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
|
||||
|
||||
|
||||
m_textEntrySizer->AddGrowableCol( 3 );
|
||||
|
||||
@@ -2925,7 +2925,7 @@
|
||||
</object>
|
||||
<object class="gbsizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="colspan">6</property>
|
||||
<property name="colspan">5</property>
|
||||
<property name="column">1</property>
|
||||
<property name="flag">wxEXPAND|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
|
||||
<property name="row">9</property>
|
||||
@@ -2937,7 +2937,7 @@
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALIGN_CENTER_VERTICAL</property>
|
||||
<property name="flag">wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxBitmapComboBox" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
|
||||
+4
-18
@@ -619,8 +619,6 @@ int ERC_TESTER::TestPinToPin()
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::pair<ERC_SCH_PIN_CONTEXT, ERC_SCH_PIN_CONTEXT>> tested;
|
||||
|
||||
ERC_SCH_PIN_CONTEXT needsDriver;
|
||||
bool hasDriver = false;
|
||||
|
||||
@@ -638,8 +636,9 @@ int ERC_TESTER::TestPinToPin()
|
||||
}
|
||||
}
|
||||
|
||||
for( ERC_SCH_PIN_CONTEXT& refPin : pins )
|
||||
for( auto refIt = pins.begin(); refIt != pins.end(); ++refIt )
|
||||
{
|
||||
ERC_SCH_PIN_CONTEXT& refPin = *refIt;
|
||||
ELECTRICAL_PINTYPE refType = refPin.Pin()->GetType();
|
||||
|
||||
if( DrivenPinTypes.count( refType ) )
|
||||
@@ -663,22 +662,9 @@ int ERC_TESTER::TestPinToPin()
|
||||
else
|
||||
hasDriver |= ( DrivingPinTypes.count( refType ) != 0 );
|
||||
|
||||
for( ERC_SCH_PIN_CONTEXT& testPin : pins )
|
||||
for( auto testIt = refIt + 1; testIt != pins.end(); ++testIt )
|
||||
{
|
||||
if( testPin == refPin )
|
||||
continue;
|
||||
|
||||
ERC_SCH_PIN_CONTEXT first_pin = refPin;
|
||||
ERC_SCH_PIN_CONTEXT second_pin = testPin;
|
||||
|
||||
if( second_pin < first_pin )
|
||||
std::swap( first_pin, second_pin );
|
||||
|
||||
std::pair<ERC_SCH_PIN_CONTEXT, ERC_SCH_PIN_CONTEXT> pair =
|
||||
std::make_pair( first_pin, second_pin );
|
||||
|
||||
if( auto [ins_pin, inserted ] = tested.insert( pair ); !inserted )
|
||||
continue;
|
||||
ERC_SCH_PIN_CONTEXT& testPin = *testIt;
|
||||
|
||||
// Multiple pins in the same symbol that share a type,
|
||||
// name and position are considered
|
||||
|
||||
@@ -580,7 +580,7 @@ wxString FIELDS_GRID_TABLE<T>::GetValue( int aRow, int aCol )
|
||||
return StringFromBool( field.IsNameShown() );
|
||||
|
||||
case FDC_H_ALIGN:
|
||||
switch ( field.GetHorizJustify() )
|
||||
switch ( field.GetEffectiveHorizJustify() )
|
||||
{
|
||||
case GR_TEXT_H_ALIGN_LEFT: return _( "Left" );
|
||||
case GR_TEXT_H_ALIGN_CENTER: return _( "Center" );
|
||||
@@ -590,7 +590,7 @@ wxString FIELDS_GRID_TABLE<T>::GetValue( int aRow, int aCol )
|
||||
break;
|
||||
|
||||
case FDC_V_ALIGN:
|
||||
switch ( field.GetVertJustify() )
|
||||
switch ( field.GetEffectiveVertJustify() )
|
||||
{
|
||||
case GR_TEXT_V_ALIGN_TOP: return _( "Top" );
|
||||
case GR_TEXT_V_ALIGN_CENTER: return _( "Center" );
|
||||
@@ -727,28 +727,52 @@ void FIELDS_GRID_TABLE<T>::SetValue( int aRow, int aCol, const wxString &aValue
|
||||
break;
|
||||
|
||||
case FDC_H_ALIGN:
|
||||
{
|
||||
GR_TEXT_H_ALIGN_T horizontalJustification;
|
||||
|
||||
if( value == _( "Left" ) )
|
||||
field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
|
||||
horizontalJustification = GR_TEXT_H_ALIGN_LEFT;
|
||||
else if( value == _( "Center" ) )
|
||||
field.SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
|
||||
horizontalJustification = GR_TEXT_H_ALIGN_CENTER;
|
||||
else if( value == _( "Right" ) )
|
||||
field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
|
||||
horizontalJustification = GR_TEXT_H_ALIGN_RIGHT;
|
||||
else
|
||||
wxFAIL_MSG( wxT( "unknown horizontal alignment: " ) + value );
|
||||
|
||||
// Note that we must set justifications before we can ask if they're flipped. If the old
|
||||
// justification is center then it won't know (whereas if the new justification is center
|
||||
// the we don't care).
|
||||
field.SetHorizJustify( horizontalJustification );
|
||||
|
||||
if( field.IsHorizJustifyFlipped() )
|
||||
field.SetHorizJustify( EDA_TEXT::MapHorizJustify( - horizontalJustification ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case FDC_V_ALIGN:
|
||||
{
|
||||
GR_TEXT_V_ALIGN_T verticalJustification;
|
||||
|
||||
if( value == _( "Top" ) )
|
||||
field.SetVertJustify( GR_TEXT_V_ALIGN_TOP );
|
||||
verticalJustification = GR_TEXT_V_ALIGN_TOP;
|
||||
else if( value == _( "Center" ) )
|
||||
field.SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
|
||||
verticalJustification = GR_TEXT_V_ALIGN_CENTER;
|
||||
else if( value == _( "Bottom" ) )
|
||||
field.SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
|
||||
verticalJustification = GR_TEXT_V_ALIGN_BOTTOM;
|
||||
else
|
||||
wxFAIL_MSG( wxT( "unknown vertical alignment: " ) + value);
|
||||
|
||||
// Note that we must set justifications before we can ask if they're flipped. If the old
|
||||
// justification is center then it won't know (whereas if the new justification is center
|
||||
// the we don't care).
|
||||
field.SetVertJustify( verticalJustification );
|
||||
|
||||
if( field.IsVertJustifyFlipped() )
|
||||
field.SetVertJustify( EDA_TEXT::MapVertJustify( -verticalJustification ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case FDC_ITALIC:
|
||||
field.SetItalic( BoolFromString( value ) );
|
||||
|
||||
+11
-2
@@ -103,7 +103,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
|
||||
{
|
||||
msg.Printf( _( "Schematic '%s' is already open." ), wx_filename.GetFullName() );
|
||||
|
||||
if( !OverrideLock( this, msg ) )
|
||||
if( !AskOverrideLock( this, msg ) )
|
||||
return false;
|
||||
|
||||
m_file_checker->OverrideLock( false );
|
||||
@@ -590,6 +590,15 @@ void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
|
||||
if( !AskToSaveChanges() )
|
||||
return;
|
||||
|
||||
if( Schematic().RootScreen() && !Schematic().RootScreen()->Items().empty() )
|
||||
{
|
||||
wxString msg = _( "This operation replaces the contents of the current project.\n\n"
|
||||
"Do you want to proceed?" );
|
||||
|
||||
if( !IsOK( this, msg ) )
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the project location if none is set or if we are running in standalone mode
|
||||
bool setProject = Prj().GetProjectFullName().IsEmpty() || Kiface().IsSingle();
|
||||
wxString path = wxPathOnly( Prj().GetProjectFullName() );
|
||||
@@ -1234,7 +1243,7 @@ bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
|
||||
msg.Printf( _( "Schematic '%s' is already open by '%s' at '%s'." ), aFileName,
|
||||
m_file_checker->GetUsername(), m_file_checker->GetHostname() );
|
||||
|
||||
if( !OverrideLock( this, msg ) )
|
||||
if( !AskOverrideLock( this, msg ) )
|
||||
return false;
|
||||
|
||||
m_file_checker->OverrideLock();
|
||||
|
||||
@@ -118,6 +118,12 @@ public:
|
||||
|
||||
KIFONT::FONT* getDrawFont() const override;
|
||||
|
||||
bool IsHorizJustifyFlipped() const { return false; }
|
||||
bool IsVertJustifyFlipped() const { return false; }
|
||||
|
||||
GR_TEXT_H_ALIGN_T GetEffectiveHorizJustify() const { return GetHorizJustify(); }
|
||||
GR_TEXT_V_ALIGN_T GetEffectiveVertJustify() const { return GetVertJustify(); }
|
||||
|
||||
/**
|
||||
* Copy parameters of this field to another field. Pointers are not copied.
|
||||
*
|
||||
|
||||
@@ -106,10 +106,14 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
|
||||
ACTION_MENU* submenuImport = new ACTION_MENU( false, selTool );
|
||||
submenuImport->SetTitle( _( "Import" ) );
|
||||
submenuImport->SetIcon( BITMAPS::import );
|
||||
submenuImport->Add( _( "Non-KiCad Schematic..." ),
|
||||
|
||||
if( Kiface().IsSingle() )
|
||||
{
|
||||
submenuImport->Add( _( "Non-KiCad Schematic..." ),
|
||||
_( "Replace current schematic sheet with one imported from another application" ),
|
||||
ID_IMPORT_NON_KICAD_SCH,
|
||||
BITMAPS::import_document );
|
||||
}
|
||||
|
||||
submenuImport->Add( EE_ACTIONS::importFPAssignments, ACTION_MENU::NORMAL, _( "Footprint Assignments..." ) );
|
||||
fileMenu->Add( submenuImport );
|
||||
|
||||
+40
-4
@@ -1037,8 +1037,27 @@ void SCH_LABEL_BASE::Plot( PLOTTER* aPlotter, bool aBackground ) const
|
||||
{
|
||||
aPlotter->PlotText( textpos, color, GetShownText( true ), attrs, font );
|
||||
|
||||
if( s_poly.size() )
|
||||
aPlotter->PlotPoly( s_poly, FILL_T::NO_FILL, penWidth );
|
||||
if( GetShape() == LABEL_FLAG_SHAPE::F_DOT )
|
||||
{
|
||||
aPlotter->MoveTo( s_poly[0] );
|
||||
aPlotter->LineTo( s_poly[1] );
|
||||
|
||||
int diameter = ( s_poly[2] - s_poly[1] ).EuclideanNorm() * 2;
|
||||
aPlotter->FilledCircle( s_poly[2], diameter , FILLED, nullptr );
|
||||
}
|
||||
else if( GetShape() == LABEL_FLAG_SHAPE::F_ROUND )
|
||||
{
|
||||
aPlotter->MoveTo( s_poly[0] );
|
||||
aPlotter->LineTo( s_poly[1] );
|
||||
|
||||
int diameter = ( s_poly[2] - s_poly[1] ).EuclideanNorm() * 2;
|
||||
aPlotter->ThickCircle( s_poly[2], diameter, penWidth, FILLED, nullptr );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !s_poly.empty() )
|
||||
aPlotter->PlotPoly( s_poly, FILL_T::NO_FILL, penWidth );
|
||||
}
|
||||
|
||||
// Plot attributes to a hypertext menu
|
||||
std::vector<wxString> properties;
|
||||
@@ -1092,8 +1111,25 @@ void SCH_LABEL_BASE::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aO
|
||||
|
||||
CreateGraphicShape( aSettings, s_poly, GetTextPos() + aOffset );
|
||||
|
||||
if( !s_poly.empty() )
|
||||
GRPoly( DC, s_poly.size(), &s_poly[0], false, penWidth, color, color );
|
||||
if( GetShape() == LABEL_FLAG_SHAPE::F_DOT )
|
||||
{
|
||||
GRLine( DC, s_poly[0], s_poly[1], penWidth, color );
|
||||
|
||||
int radius = ( s_poly[2] - s_poly[1] ).EuclideanNorm();
|
||||
GRFilledCircle( DC, s_poly[2], radius, penWidth, color, color );
|
||||
}
|
||||
else if( GetShape() == LABEL_FLAG_SHAPE::F_ROUND )
|
||||
{
|
||||
GRLine( DC, s_poly[0], s_poly[1], penWidth, color );
|
||||
|
||||
int radius = ( s_poly[2] - s_poly[1] ).EuclideanNorm();
|
||||
GRCircle( DC, s_poly[2], radius, penWidth, color );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !s_poly.empty() )
|
||||
GRPoly( DC, s_poly.size(), &s_poly[0], false, penWidth, color, color );
|
||||
}
|
||||
|
||||
for( SCH_FIELD& field : m_fields )
|
||||
field.Print( aSettings, aOffset );
|
||||
|
||||
@@ -110,6 +110,8 @@ public:
|
||||
|
||||
void SetIsDangling( bool isDangling ) { m_isDangling = isDangling; }
|
||||
|
||||
void SetLibPin( LIB_PIN* aLibPin ) { m_libPin = aLibPin; }
|
||||
|
||||
/**
|
||||
* @param aPin Comparison Pin
|
||||
* @return True if aPin is stacked with this pin
|
||||
|
||||
@@ -192,6 +192,11 @@ LIB_SYMBOL* SCH_SEXPR_PARSER::ParseSymbol( LIB_SYMBOL_MAP& aSymbolLibMap, int aF
|
||||
|
||||
name = FromUTF8();
|
||||
|
||||
// Some symbol LIB_IDs have the '/' character escaped which can break derived symbol links.
|
||||
// The '/' character is no longer an illegal LIB_ID character so it doesn't need to be
|
||||
// escaped.
|
||||
name.Replace( wxS( "{slash}" ), wxT( "/" ) );
|
||||
|
||||
LIB_ID id;
|
||||
int bad_pos = id.Parse( name );
|
||||
|
||||
@@ -268,6 +273,12 @@ LIB_SYMBOL* SCH_SEXPR_PARSER::ParseSymbol( LIB_SYMBOL_MAP& aSymbolLibMap, int aF
|
||||
}
|
||||
|
||||
name = FromUTF8();
|
||||
|
||||
// Some symbol LIB_IDs have the '/' character escaped which can break derived
|
||||
// symbol links. The '/' character is no longer an illegal LIB_ID character so
|
||||
// it doesn't need to be escaped.
|
||||
name.Replace( wxS( "{slash}" ), wxT( "/" ) );
|
||||
|
||||
auto it = aSymbolLibMap.find( name );
|
||||
|
||||
if( it == aSymbolLibMap.end() )
|
||||
@@ -293,6 +304,11 @@ LIB_SYMBOL* SCH_SEXPR_PARSER::ParseSymbol( LIB_SYMBOL_MAP& aSymbolLibMap, int aF
|
||||
|
||||
name = FromUTF8();
|
||||
|
||||
// Some symbol LIB_IDs have the '/' character escaped which can break derived
|
||||
// symbol links. The '/' character is no longer an illegal LIB_ID character so
|
||||
// it doesn't need to be escaped.
|
||||
name.Replace( wxS( "{slash}" ), wxT( "/" ) );
|
||||
|
||||
if( !name.StartsWith( m_symbolName ) )
|
||||
{
|
||||
error.Printf( _( "Invalid symbol unit name prefix %s" ), name.c_str() );
|
||||
@@ -2593,6 +2609,11 @@ SCH_SYMBOL* SCH_SEXPR_PARSER::parseSchematicSymbol()
|
||||
}
|
||||
|
||||
libName = FromUTF8();
|
||||
// Some symbol LIB_IDs or lib_name have the '/' character escaped which can break
|
||||
// symbol links. The '/' character is no longer an illegal LIB_ID character so
|
||||
// it doesn't need to be escaped.
|
||||
libName.Replace( "{slash}", "/" );
|
||||
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
@@ -2606,6 +2627,11 @@ SCH_SYMBOL* SCH_SEXPR_PARSER::parseSchematicSymbol()
|
||||
|
||||
LIB_ID libId;
|
||||
wxString name = FromUTF8();
|
||||
// Some symbol LIB_IDs have the '/' character escaped which can break
|
||||
// symbol links. The '/' character is no longer an illegal LIB_ID character so
|
||||
// it doesn't need to be escaped.
|
||||
name.Replace( "{slash}", "/" );
|
||||
|
||||
int bad_pos = libId.Parse( name );
|
||||
|
||||
if( bad_pos >= 0 )
|
||||
|
||||
@@ -353,6 +353,8 @@ void SCH_LEGACY_PLUGIN::LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen,
|
||||
aScreen->Append( loadText( aReader ) );
|
||||
else if( strCompare( "BusAlias", line ) )
|
||||
aScreen->AddBusAlias( loadBusAlias( aReader, aScreen ) );
|
||||
else if( strCompare( "Kmarq", line ) )
|
||||
continue; // Ignore legacy (until 2009) ERC marker entry
|
||||
else if( strCompare( "$EndSCHEMATC", line ) )
|
||||
return;
|
||||
else
|
||||
|
||||
@@ -1174,12 +1174,14 @@ void SCH_SCREEN::Plot( PLOTTER* aPlotter ) const
|
||||
item->Plot( aPlotter, background );
|
||||
}
|
||||
|
||||
// Plot the background items
|
||||
for( const SCH_ITEM* item : other )
|
||||
{
|
||||
aPlotter->SetCurrentLineWidth( std::max( item->GetPenWidth(), defaultPenWidth ) );
|
||||
item->Plot( aPlotter, background );
|
||||
}
|
||||
|
||||
// Plot the foreground items
|
||||
for( const SCH_ITEM* item : other )
|
||||
{
|
||||
aPlotter->SetCurrentLineWidth( std::max( item->GetPenWidth(), defaultPenWidth ) );
|
||||
@@ -1193,7 +1195,10 @@ void SCH_SCREEN::Plot( PLOTTER* aPlotter ) const
|
||||
aPlotter->SetCurrentLineWidth( std::max( sym->GetPenWidth(), defaultPenWidth ) );
|
||||
|
||||
for( SCH_FIELD field : sym->GetFields() )
|
||||
{
|
||||
field.ClearRenderCache();
|
||||
field.Plot( aPlotter, false );
|
||||
}
|
||||
|
||||
sym->PlotPins( aPlotter );
|
||||
|
||||
|
||||
+113
-20
@@ -177,9 +177,6 @@ SCH_SYMBOL::SCH_SYMBOL( const SCH_SYMBOL& aSymbol ) :
|
||||
m_onBoard = aSymbol.m_onBoard;
|
||||
m_DNP = aSymbol.m_DNP;
|
||||
|
||||
if( aSymbol.m_part )
|
||||
SetLibSymbol( new LIB_SYMBOL( *aSymbol.m_part.get() ) );
|
||||
|
||||
const_cast<KIID&>( m_Uuid ) = aSymbol.m_Uuid;
|
||||
|
||||
m_transform = aSymbol.m_transform;
|
||||
@@ -191,6 +188,18 @@ SCH_SYMBOL::SCH_SYMBOL( const SCH_SYMBOL& aSymbol ) :
|
||||
for( SCH_FIELD& field : m_fields )
|
||||
field.SetParent( this );
|
||||
|
||||
m_pins.clear();
|
||||
|
||||
// Copy (and re-parent) the pins
|
||||
for( const std::unique_ptr<SCH_PIN>& pin : aSymbol.m_pins )
|
||||
{
|
||||
m_pins.emplace_back( std::make_unique<SCH_PIN>( *pin ) );
|
||||
m_pins.back()->SetParent( this );
|
||||
}
|
||||
|
||||
if( aSymbol.m_part )
|
||||
SetLibSymbol( new LIB_SYMBOL( *aSymbol.m_part ) );
|
||||
|
||||
m_fieldsAutoplaced = aSymbol.m_fieldsAutoplaced;
|
||||
m_schLibSymbolName = aSymbol.m_schLibSymbolName;
|
||||
}
|
||||
@@ -333,49 +342,104 @@ wxString SCH_SYMBOL::GetDatasheet() const
|
||||
|
||||
void SCH_SYMBOL::UpdatePins()
|
||||
{
|
||||
std::map<wxString, wxString> altPinMap;
|
||||
std::map<wxString, KIID> pinUuidMap;
|
||||
std::map<wxString, wxString> altPinMap;
|
||||
std::map<wxString, std::set<SCH_PIN*>> pinUuidMap;
|
||||
std::set<SCH_PIN*> unassignedSchPins;
|
||||
std::set<LIB_PIN*> unassignedLibPins;
|
||||
|
||||
for( const std::unique_ptr<SCH_PIN>& pin : m_pins )
|
||||
{
|
||||
pinUuidMap[ pin->GetNumber() ] = pin->m_Uuid;
|
||||
pinUuidMap[ pin->GetNumber() ].insert( pin.get() );
|
||||
|
||||
unassignedSchPins.insert( pin.get() );
|
||||
|
||||
if( !pin->GetAlt().IsEmpty() )
|
||||
altPinMap[ pin->GetNumber() ] = pin->GetAlt();
|
||||
}
|
||||
|
||||
m_pins.clear();
|
||||
m_pinMap.clear();
|
||||
|
||||
if( !m_part )
|
||||
return;
|
||||
|
||||
unsigned i = 0;
|
||||
|
||||
std::vector<LIB_PIN*> pins = m_part->GetAllLibPins();
|
||||
|
||||
for( LIB_PIN* libPin : pins )
|
||||
{
|
||||
// NW: Don't filter by unit: this data-structure is used for all instances,
|
||||
// some if which might have different units.
|
||||
// some of which might have different units.
|
||||
if( libPin->GetConvert() && m_convert && m_convert != libPin->GetConvert() )
|
||||
continue;
|
||||
|
||||
m_pins.push_back( std::make_unique<SCH_PIN>( libPin, this ) );
|
||||
SCH_PIN* pin = nullptr;
|
||||
|
||||
auto ii = pinUuidMap.find( libPin->GetNumber() );
|
||||
|
||||
if( ii != pinUuidMap.end() )
|
||||
const_cast<KIID&>( m_pins.back()->m_Uuid ) = ii->second;
|
||||
if( ii == pinUuidMap.end() || ii->second.empty() )
|
||||
{
|
||||
unassignedLibPins.insert( libPin );
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = ii->second.begin();
|
||||
pin = *it;
|
||||
ii->second.erase( it );
|
||||
pin->SetLibPin( libPin );
|
||||
pin->SetPosition( libPin->GetPosition() );
|
||||
|
||||
unassignedSchPins.erase( pin );
|
||||
|
||||
auto iii = altPinMap.find( libPin->GetNumber() );
|
||||
|
||||
if( iii != altPinMap.end() )
|
||||
m_pins.back()->SetAlt( iii->second );
|
||||
pin->SetAlt( iii->second );
|
||||
|
||||
m_pinMap[ libPin ] = i;
|
||||
m_pinMap[ libPin ] = pin;
|
||||
}
|
||||
|
||||
++i;
|
||||
// Add any pins that were not found in the symbol
|
||||
for( LIB_PIN* libPin : unassignedLibPins )
|
||||
{
|
||||
SCH_PIN* pin = nullptr;
|
||||
|
||||
// First try to re-use an existing pin
|
||||
if( !unassignedSchPins.empty() )
|
||||
{
|
||||
auto it = unassignedSchPins.begin();
|
||||
pin = *it;
|
||||
unassignedSchPins.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a pin that was not found in the symbol, so create a new one.
|
||||
pin = m_pins.emplace_back( std::make_unique<SCH_PIN>( SCH_PIN( libPin, this ) ) ).get();
|
||||
}
|
||||
|
||||
m_pinMap[ libPin ] = pin;
|
||||
pin->SetLibPin( libPin );
|
||||
pin->SetPosition( libPin->GetPosition() );
|
||||
pin->SetNumber( libPin->GetNumber() );
|
||||
|
||||
auto iii = altPinMap.find( libPin->GetNumber() );
|
||||
|
||||
if( iii != altPinMap.end() )
|
||||
pin->SetAlt( iii->second );
|
||||
}
|
||||
|
||||
// If we have any pins left in the symbol that were not found in the library, remove them.
|
||||
for( auto it1 = m_pins.begin(); it1 != m_pins.end() && !unassignedSchPins.empty(); )
|
||||
{
|
||||
auto it2 = unassignedSchPins.find( it1->get() );
|
||||
|
||||
if( it2 != unassignedSchPins.end() )
|
||||
{
|
||||
it1 = m_pins.erase( it1 );
|
||||
unassignedSchPins.erase( it2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1057,8 +1121,13 @@ std::vector<LIB_PIN*> SCH_SYMBOL::GetAllLibPins() const
|
||||
|
||||
SCH_PIN* SCH_SYMBOL::GetPin( LIB_PIN* aLibPin ) const
|
||||
{
|
||||
wxASSERT( m_pinMap.count( aLibPin ) );
|
||||
return m_pins[ m_pinMap.at( aLibPin ) ].get();
|
||||
auto it = m_pinMap.find( aLibPin );
|
||||
|
||||
if( it != m_pinMap.end() )
|
||||
return it->second;
|
||||
|
||||
wxFAIL_MSG_AT( "Pin not found", __FILE__, __LINE__, __FUNCTION__ );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1666,7 +1735,17 @@ BOX2I SCH_SYMBOL::doGetBoundingBox( bool aIncludePins, bool aIncludeFields ) con
|
||||
|
||||
BOX2I SCH_SYMBOL::GetBodyBoundingBox() const
|
||||
{
|
||||
return doGetBoundingBox( false, false );
|
||||
try
|
||||
{
|
||||
return doGetBoundingBox( false, false );
|
||||
}
|
||||
catch( const boost::bad_pointer& exc )
|
||||
{
|
||||
// This may be overkill and could be an assertion but we are more likely to
|
||||
// find any boost pointer container errors this way.
|
||||
wxLogError( wxT( "Boost bad pointer exception '%s' occurred." ), exc.what() );
|
||||
return BOX2I();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2202,6 +2281,17 @@ void SCH_SYMBOL::Plot( PLOTTER* aPlotter, bool aBackground ) const
|
||||
tempPin->SetFlags( IS_DANGLING );
|
||||
}
|
||||
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
TRANSFORM temp = GetTransform();
|
||||
aPlotter->StartBlock( nullptr );
|
||||
|
||||
@@ -2210,8 +2300,11 @@ void SCH_SYMBOL::Plot( PLOTTER* aPlotter, bool aBackground ) const
|
||||
tempSymbol.Plot( aPlotter, GetUnit(), GetConvert(), local_background, m_pos, temp,
|
||||
GetDNP() );
|
||||
|
||||
for( const SCH_FIELD& field : m_fields )
|
||||
for( SCH_FIELD field : m_fields )
|
||||
{
|
||||
field.ClearRenderCache();
|
||||
field.Plot( aPlotter, local_background );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_DNP )
|
||||
|
||||
@@ -800,7 +800,7 @@ private:
|
||||
std::unique_ptr< LIB_SYMBOL > m_part; ///< a flattened copy of the LIB_SYMBOL
|
||||
///< from the PROJECT's libraries.
|
||||
std::vector<std::unique_ptr<SCH_PIN>> m_pins; ///< a SCH_PIN for every LIB_PIN (all units)
|
||||
std::unordered_map<LIB_PIN*, unsigned> m_pinMap; ///< library pin pointer : SCH_PIN's index
|
||||
std::unordered_map<LIB_PIN*, SCH_PIN*> m_pinMap; ///< library pin pointer : SCH_PIN's index
|
||||
|
||||
bool m_isInNetlist; ///< True if the symbol should appear in the netlist
|
||||
bool m_inBom; ///< True to include in bill of materials export.
|
||||
|
||||
@@ -617,6 +617,7 @@ std::string NGSPICE::findCmPath() const
|
||||
"/../../../../../Contents/PlugIns/sim/ngspice",
|
||||
"../Plugins/sim/ngspice",
|
||||
#endif
|
||||
"../eeschema/ngspice",
|
||||
"../lib/ngspice",
|
||||
"../../lib/ngspice",
|
||||
"lib/ngspice",
|
||||
|
||||
@@ -73,10 +73,11 @@ std::string SPICE_GENERATOR_SOURCE::ItemLine( const SPICE_ITEM& aItem ) const
|
||||
}
|
||||
|
||||
if( m_model.GetSpiceInfo().inlineTypeString != ""
|
||||
&& m_model.GetType() != SIM_MODEL::TYPE::V ) // DC-only sources are already processed
|
||||
&& m_model.GetType() != SIM_MODEL::TYPE::V // DC-only sources are already processed
|
||||
&& m_model.GetType() != SIM_MODEL::TYPE::I )
|
||||
{
|
||||
std::string args = "";
|
||||
|
||||
|
||||
switch( m_model.GetType() )
|
||||
{
|
||||
case SIM_MODEL::TYPE::V_PWL:
|
||||
@@ -136,31 +137,30 @@ std::string SPICE_GENERATOR_SOURCE::ItemLine( const SPICE_ITEM& aItem ) const
|
||||
args.append( getParamValueString( "tcapt", "0" ) + " " );
|
||||
args.append( getParamValueString( "temit", "0" ) + " " );
|
||||
break;
|
||||
|
||||
|
||||
case SIM_MODEL::TYPE::V_RANDUNIFORM:
|
||||
case SIM_MODEL::TYPE::I_RANDUNIFORM:
|
||||
{
|
||||
/* JEY TODO
|
||||
args.append( "1 " );
|
||||
args.append( getParamValueString( "dt", "0" ) + " " );
|
||||
args.append( getParamValueString( "td", "0" ) + " " );
|
||||
std::string min = getParamValueString( "min", "0" );
|
||||
std::string max = getParamValueString( "max", "0" );
|
||||
|
||||
SIM_VALUE_FLOAT min = dynamic_cast<SIM_VALUE_FLOAT&>( m_model.FindParam( "max" )->value );
|
||||
args.append( min + " " );
|
||||
args.append( max + " " );
|
||||
|
||||
if( !min.ToString().empty() )
|
||||
min.FromString( "0" );
|
||||
double minVal = std::stod( min );
|
||||
double maxVal = std::stod( max );
|
||||
|
||||
SIM_VALUE_FLOAT max = dynamic_cast<SIM_VALUE_FLOAT&>( m_model.FindParam( "min" )->value );
|
||||
double rangeVal = maxVal - minVal;
|
||||
double offsetVal = ( maxVal + minVal ) / 2;
|
||||
|
||||
if( !max.ToString().empty() )
|
||||
max.FromString( "0" );
|
||||
std::string range = std::to_string( rangeVal );
|
||||
std::string offset = std::to_string( offsetVal );
|
||||
|
||||
SIM_VALUE_FLOAT range = max - min;
|
||||
SIM_VALUE_FLOAT offset = ( max + min ) / SIM_VALUE_FLOAT( 2 );
|
||||
|
||||
args.append( range.ToSpiceString() + " " );
|
||||
args.append( offset.ToSpiceString() + " " );
|
||||
*/
|
||||
args.append( range + " " );
|
||||
args.append( offset + " " );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -790,7 +790,7 @@ std::vector<SIM_MODEL::PARAM::INFO> SIM_MODEL_SOURCE::makeWhiteNoiseParamInfos(
|
||||
}
|
||||
|
||||
|
||||
std::vector<SIM_MODEL::PARAM::INFO> SIM_MODEL_SOURCE::makePinkNoiseParamInfos( std::string aPrefix,
|
||||
std::vector<SIM_MODEL::PARAM::INFO> SIM_MODEL_SOURCE::makePinkNoiseParamInfos( std::string aPrefix,
|
||||
std::string aUnit )
|
||||
{
|
||||
std::vector<PARAM::INFO> paramInfos;
|
||||
|
||||
@@ -92,29 +92,32 @@ bool SIM_STRING_PROPERTY::OnEvent( wxPropertyGrid* propgrid, wxWindow* wnd_prima
|
||||
{
|
||||
wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( wnd_primary );
|
||||
|
||||
if( textEntry )
|
||||
wxCHECK( textEntry, false );
|
||||
|
||||
wxString oldStr = m_eval.OriginalText();
|
||||
|
||||
if( oldStr.length() && oldStr != textEntry->GetValue() )
|
||||
{
|
||||
wxString oldStr = m_eval.OriginalText();
|
||||
|
||||
if( oldStr.length() && oldStr != textEntry->GetValue() )
|
||||
{
|
||||
SetValueInEvent( oldStr );
|
||||
textEntry->SetValue( oldStr );
|
||||
}
|
||||
|
||||
m_needsEval = true;
|
||||
return true;
|
||||
SetValueInEvent( oldStr );
|
||||
textEntry->SetValue( oldStr );
|
||||
}
|
||||
|
||||
m_needsEval = true;
|
||||
return true;
|
||||
}
|
||||
else if( event.GetEventType() == wxEVT_KILL_FOCUS && allowEval() )
|
||||
{
|
||||
wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( wnd_primary );
|
||||
|
||||
if( textEntry && m_eval.Process( textEntry->GetValue() ) )
|
||||
wxCHECK( textEntry, false );
|
||||
|
||||
wxString strValue = textEntry->GetValue();
|
||||
|
||||
if( !strValue.IsEmpty() && m_eval.Process( strValue ) )
|
||||
{
|
||||
double value = SIM_VALUE::ToDouble( m_eval.Result().ToStdString() );
|
||||
|
||||
if( isnan( value ) || SIM_VALUE::Equal( value, textEntry->GetValue().ToStdString() ) )
|
||||
if( std::isnan( value ) || SIM_VALUE::Equal( value, strValue.ToStdString() ) )
|
||||
{
|
||||
// Don't mess up user formatting if eval'ing didn't actually change the value.
|
||||
}
|
||||
@@ -122,10 +125,10 @@ bool SIM_STRING_PROPERTY::OnEvent( wxPropertyGrid* propgrid, wxWindow* wnd_prima
|
||||
{
|
||||
SetValueInEvent( m_eval.Result() );
|
||||
}
|
||||
|
||||
m_needsEval = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_needsEval = false;
|
||||
return true;
|
||||
}
|
||||
else if( event.GetEventType() == wxEVT_KEY_DOWN )
|
||||
{
|
||||
|
||||
@@ -193,9 +193,10 @@ namespace SPICE_GRAMMAR
|
||||
spiceUnit>>> {};
|
||||
|
||||
|
||||
struct modelUnit : sor<dotModelAko,
|
||||
dotModel,
|
||||
dotSubckt> {};
|
||||
struct modelUnit : seq<star<commentLine>,
|
||||
sor<dotModelAko,
|
||||
dotModel,
|
||||
dotSubckt>> {};
|
||||
|
||||
|
||||
// Intentionally no if_must<>.
|
||||
|
||||
@@ -170,7 +170,7 @@ SYMBOL_EDIT_FRAME::SYMBOL_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
||||
ReCreateVToolbar();
|
||||
ReCreateOptToolbar();
|
||||
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
UpdateSymbolMsgPanelInfo();
|
||||
RebuildSymbolUnitsList();
|
||||
|
||||
@@ -558,7 +558,7 @@ bool SYMBOL_EDIT_FRAME::CanCloseSymbolFromSchematic( bool doClose )
|
||||
if( doClose )
|
||||
{
|
||||
SetCurSymbol( nullptr, false );
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -849,7 +849,7 @@ void SYMBOL_EDIT_FRAME::OnModify()
|
||||
m_treePane->GetLibTree()->RefreshLibTree();
|
||||
|
||||
if( !GetTitle().StartsWith( "*" ) )
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
|
||||
@@ -1224,7 +1224,7 @@ void SYMBOL_EDIT_FRAME::ShowChangedLanguage()
|
||||
GetCanvas()->Refresh();
|
||||
}
|
||||
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
|
||||
@@ -1518,7 +1518,7 @@ void SYMBOL_EDIT_FRAME::LoadSymbolFromSchematic( SCH_SYMBOL* aSymbol )
|
||||
OnToggleSymbolTree( evt );
|
||||
}
|
||||
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
RebuildSymbolUnitsList();
|
||||
SetShowDeMorgan( GetCurSymbol()->HasConversion() );
|
||||
UpdateSymbolMsgPanelInfo();
|
||||
|
||||
@@ -247,6 +247,12 @@ public:
|
||||
UpdateSymbolMsgPanelInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the main window title bar with the current library name and read only status
|
||||
* of the library.
|
||||
*/
|
||||
void UpdateTitle();
|
||||
|
||||
bool IsSymbolFromSchematic() const
|
||||
{
|
||||
// If we've already vetted closing this window, then we have no symbol anymore
|
||||
@@ -427,12 +433,6 @@ private:
|
||||
*/
|
||||
bool saveLibrary( const wxString& aLibrary, bool aNewFile );
|
||||
|
||||
/**
|
||||
* Update the main window title bar with the current library name and read only status
|
||||
* of the library.
|
||||
*/
|
||||
void updateTitle();
|
||||
|
||||
/**
|
||||
* Set the current active library to \a aLibrary.
|
||||
*
|
||||
|
||||
@@ -56,7 +56,7 @@ SYMBOL_SAVEAS_TYPE SYMBOL_LEGACYFILEDLG_SAVE_AS::m_option = SYMBOL_SAVEAS_TYPE::
|
||||
#endif
|
||||
|
||||
|
||||
void SYMBOL_EDIT_FRAME::updateTitle()
|
||||
void SYMBOL_EDIT_FRAME::UpdateTitle()
|
||||
{
|
||||
wxString title;
|
||||
|
||||
@@ -98,7 +98,7 @@ void SYMBOL_EDIT_FRAME::SelectActiveLibrary( const wxString& aLibrary )
|
||||
if( !selectedLib.empty() )
|
||||
SetCurLib( selectedLib );
|
||||
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
|
||||
@@ -124,14 +124,20 @@ bool SYMBOL_EDIT_FRAME::saveCurrentSymbol()
|
||||
}
|
||||
else
|
||||
{
|
||||
LIB_ID libId = GetCurSymbol()->GetLibId();
|
||||
const wxString& libName = libId.GetLibNickname();
|
||||
const wxString& symbolName = libId.GetLibItemName();
|
||||
const wxString& libName = GetCurSymbol()->GetLibId().GetLibNickname();
|
||||
|
||||
if( m_libMgr->FlushSymbol( symbolName, libName ) )
|
||||
if( m_libMgr->IsLibraryReadOnly( libName ) )
|
||||
{
|
||||
m_libMgr->ClearSymbolModified( symbolName, libName );
|
||||
return true;
|
||||
wxString msg = wxString::Format( _( "Symbol library '%s' is not writable." ),
|
||||
libName );
|
||||
wxString msg2 = _( "You must save to a different location." );
|
||||
|
||||
if( OKOrCancelDialog( this, _( "Warning" ), msg, msg2 ) == wxID_OK )
|
||||
return saveLibrary( libName, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
return saveLibrary( libName, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,7 +304,7 @@ bool SYMBOL_EDIT_FRAME::LoadOneLibrarySymbolAux( LIB_SYMBOL* aEntry, const wxStr
|
||||
GetInfoBar()->Dismiss();
|
||||
}
|
||||
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
RebuildSymbolUnitsList();
|
||||
SetShowDeMorgan( GetCurSymbol()->HasConversion() );
|
||||
|
||||
@@ -493,7 +499,7 @@ void SYMBOL_EDIT_FRAME::Save()
|
||||
if( IsSymbolTreeShown() )
|
||||
m_treePane->GetLibTree()->RefreshLibTree();
|
||||
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
|
||||
@@ -605,6 +611,7 @@ void SYMBOL_EDIT_FRAME::saveSymbolAs()
|
||||
LIB_ID old_lib_id = symbol->GetLibId();
|
||||
wxString symbolName = old_lib_id.GetLibItemName();
|
||||
wxString libraryName = old_lib_id.GetLibNickname();
|
||||
bool valueFollowsName = symbol->GetValueField().GetText() == symbolName;
|
||||
bool done = false;
|
||||
|
||||
std::unique_ptr<EDA_LIST_DIALOG> dlg;
|
||||
@@ -677,6 +684,9 @@ void SYMBOL_EDIT_FRAME::saveSymbolAs()
|
||||
LIB_SYMBOL new_symbol( *symbol );
|
||||
new_symbol.SetName( symbolName );
|
||||
|
||||
if( valueFollowsName )
|
||||
new_symbol.GetValueField().SetText( symbolName );
|
||||
|
||||
m_libMgr->UpdateSymbol( &new_symbol, libraryName );
|
||||
SyncLibraries( false );
|
||||
m_treePane->GetLibTree()->SelectLibId( LIB_ID( libraryName, new_symbol.GetName() ) );
|
||||
@@ -713,7 +723,7 @@ void SYMBOL_EDIT_FRAME::UpdateAfterSymbolProperties( wxString* aOldName )
|
||||
|
||||
RebuildSymbolUnitsList();
|
||||
SetShowDeMorgan( GetCurSymbol()->Flatten()->HasConversion() );
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
|
||||
// N.B. The view needs to be rebuilt first as the Symbol Properties change may invalidate
|
||||
// the view pointers by rebuilting the field table
|
||||
@@ -1179,12 +1189,7 @@ bool SYMBOL_EDIT_FRAME::saveAllLibraries( bool aRequireConfirmation )
|
||||
{
|
||||
// If saving under existing name fails then do a Save As..., and if that
|
||||
// fails then cancel close action.
|
||||
if( !m_libMgr->IsLibraryReadOnly( libNickname ) )
|
||||
{
|
||||
if( saveLibrary( libNickname, false ) )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if( m_libMgr->IsLibraryReadOnly( libNickname ) )
|
||||
{
|
||||
msg.Printf( _( "Symbol library '%s' is not writable." ), libNickname );
|
||||
msg2 = _( "You must save to a different location." );
|
||||
@@ -1210,6 +1215,10 @@ bool SYMBOL_EDIT_FRAME::saveAllLibraries( bool aRequireConfirmation )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if( saveLibrary( libNickname, false ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !saveLibrary( libNickname, true ) )
|
||||
retv = false;
|
||||
@@ -1217,7 +1226,7 @@ bool SYMBOL_EDIT_FRAME::saveAllLibraries( bool aRequireConfirmation )
|
||||
}
|
||||
}
|
||||
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
return retv;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ void SYMBOL_EDIT_FRAME::GetSymbolFromRedoList()
|
||||
|
||||
RebuildSymbolUnitsList();
|
||||
SetShowDeMorgan( symbol->HasConversion() );
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
|
||||
RebuildView();
|
||||
OnModify();
|
||||
@@ -145,7 +145,7 @@ void SYMBOL_EDIT_FRAME::GetSymbolFromUndoList()
|
||||
|
||||
RebuildSymbolUnitsList();
|
||||
SetShowDeMorgan( symbol->HasConversion() );
|
||||
updateTitle();
|
||||
UpdateTitle();
|
||||
|
||||
RebuildView();
|
||||
OnModify();
|
||||
|
||||
@@ -884,8 +884,8 @@ void SYMBOL_VIEWER_FRAME::ClickOnLibList( wxCommandEvent& event )
|
||||
void SYMBOL_VIEWER_FRAME::SetSelectedLibrary( const wxString& aLibraryName,
|
||||
const wxString& aSubLibName )
|
||||
{
|
||||
if( m_currentSymbol.GetUniStringLibNickname() == aLibraryName &&
|
||||
wxString( m_currentSymbol.GetSubLibraryName() ) == aSubLibName )
|
||||
if( m_currentSymbol.GetUniStringLibNickname() == aLibraryName
|
||||
&& m_currentSymbol.GetSubLibraryName().wx_str() == aSubLibName )
|
||||
return;
|
||||
|
||||
m_currentSymbol.SetLibNickname( aLibraryName );
|
||||
|
||||
@@ -1127,6 +1127,7 @@ void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const
|
||||
}
|
||||
|
||||
SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
|
||||
SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( item );
|
||||
EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
|
||||
SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
|
||||
|
||||
@@ -1134,36 +1135,32 @@ void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const
|
||||
{
|
||||
dist = DistanceLinePoint( line->GetStartPoint(), line->GetEndPoint(), pos );
|
||||
}
|
||||
else if( text )
|
||||
else if( field )
|
||||
{
|
||||
if( SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( text ) )
|
||||
BOX2I box = field->GetBoundingBox();
|
||||
EDA_ANGLE orient = field->GetTextAngle();
|
||||
|
||||
if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
|
||||
{
|
||||
if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
|
||||
if( static_cast<SCH_SYMBOL*>( field->GetParent() )->GetTransform().y1 )
|
||||
{
|
||||
symbol = static_cast<SCH_SYMBOL*>( field->GetParent() );
|
||||
|
||||
VECTOR2I relPos = pos - symbol->GetPosition();
|
||||
relPos = symbol->GetTransform().InverseTransform().TransformCoordinate( relPos );
|
||||
pos = relPos + symbol->GetPosition();
|
||||
|
||||
poss = SEG( pos, pos );
|
||||
if( orient.IsHorizontal() )
|
||||
orient = ANGLE_VERTICAL;
|
||||
else
|
||||
orient = ANGLE_HORIZONTAL;
|
||||
}
|
||||
}
|
||||
|
||||
field->GetEffectiveTextShape( false, box, orient )->Collide( poss, INT_MAX / 2,
|
||||
&dist );
|
||||
}
|
||||
else if( text )
|
||||
{
|
||||
text->GetEffectiveTextShape( false )->Collide( poss, INT_MAX / 2, &dist );
|
||||
}
|
||||
else if( symbol )
|
||||
{
|
||||
try
|
||||
{
|
||||
bbox = symbol->GetBodyBoundingBox();
|
||||
}
|
||||
catch( const boost::bad_pointer& exc )
|
||||
{
|
||||
// This may be overkill and could be an assertion but we are more likely to
|
||||
// find any boost pointer container errors this way.
|
||||
wxLogError( wxT( "Boost bad pointer exception '%s' occurred." ), exc.what() );
|
||||
}
|
||||
bbox = symbol->GetBodyBoundingBox();
|
||||
|
||||
SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
|
||||
|
||||
|
||||
@@ -355,41 +355,34 @@ int SYMBOL_EDITOR_CONTROL::RenameSymbol( const TOOL_EVENT& aEvent )
|
||||
}
|
||||
|
||||
wxString oldName = symbolName;
|
||||
LIB_SYMBOL* libSymbol = nullptr;
|
||||
LIB_SYMBOL* libSymbol = libMgr.GetBufferedSymbol( oldName, libName );
|
||||
bool isCurrentSymbol = editFrame->IsCurrentSymbol( libId );
|
||||
|
||||
if( editFrame->IsCurrentSymbol( libId ) )
|
||||
{
|
||||
// Update buffered copy
|
||||
libSymbol = libMgr.GetBufferedSymbol( oldName, libName );
|
||||
libSymbol->SetName( newName );
|
||||
|
||||
libSymbol->SetName( newName );
|
||||
if( libSymbol->GetFieldById( VALUE_FIELD )->GetText() == oldName )
|
||||
libSymbol->GetFieldById( VALUE_FIELD )->SetText( newName );
|
||||
|
||||
libMgr.UpdateSymbolAfterRename( libSymbol, newName, libName );
|
||||
libMgr.UpdateSymbolAfterRename( libSymbol, newName, libName );
|
||||
libMgr.SetSymbolModified( newName, libName );
|
||||
|
||||
// Now update canvasy copy
|
||||
if( isCurrentSymbol )
|
||||
{
|
||||
libSymbol = editFrame->GetCurSymbol();
|
||||
|
||||
libSymbol->SetName( newName );
|
||||
libSymbol->GetFieldById( VALUE_FIELD )->SetText( newName );
|
||||
|
||||
if( libSymbol->GetFieldById( VALUE_FIELD )->GetText() == oldName )
|
||||
libSymbol->GetFieldById( VALUE_FIELD )->SetText( newName );
|
||||
|
||||
editFrame->RebuildView();
|
||||
editFrame->OnModify();
|
||||
editFrame->UpdateTitle();
|
||||
|
||||
// N.B. The view needs to be rebuilt first as the Symbol Properties change may
|
||||
// invalidate the view pointers by rebuilting the field table
|
||||
editFrame->UpdateMsgPanel();
|
||||
}
|
||||
else
|
||||
{
|
||||
libSymbol = libMgr.GetBufferedSymbol( oldName, libName );
|
||||
|
||||
libSymbol->SetName( newName );
|
||||
libSymbol->GetFieldById( VALUE_FIELD )->SetText( newName );
|
||||
|
||||
libMgr.UpdateSymbolAfterRename( libSymbol, newName, libName );
|
||||
libMgr.SetSymbolModified( newName, libName );
|
||||
}
|
||||
|
||||
wxDataViewItem treeItem = libMgr.GetAdapter()->FindItem( libId );
|
||||
editFrame->UpdateLibraryTree( treeItem, libSymbol );
|
||||
|
||||
+13
-13
@@ -199,24 +199,24 @@ bool GERBVIEW_FRAME::LoadGerberFiles( const wxString& aFileName )
|
||||
* Now (2014) Ucamco (the company which manages the Gerber format) encourages use of .gbr
|
||||
* only and the Gerber X2 file format.
|
||||
*/
|
||||
filetypes = _( "Gerber files" ) + AddFileExtListToFilter( { "G*", "PHO" } ) + wxT( "|" );
|
||||
filetypes = _( "Gerber files" ) + AddFileExtListToFilter( { "g*", "pho" } ) + wxT( "|" );
|
||||
|
||||
/* Special gerber filetypes */
|
||||
filetypes += _( "Top layer" ) + AddFileExtListToFilter( { "GTL" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom layer" ) + AddFileExtListToFilter( { "GBL" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom solder resist" ) + AddFileExtListToFilter( { "GBS" } ) + wxT( "|" );
|
||||
filetypes += _( "Top solder resist" ) + AddFileExtListToFilter( { "GTS" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom overlay" ) + AddFileExtListToFilter( { "GBO" } ) + wxT( "|" );
|
||||
filetypes += _( "Top overlay" ) + AddFileExtListToFilter( { "GTO" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom paste" ) + AddFileExtListToFilter( { "GBP" } ) + wxT( "|" );
|
||||
filetypes += _( "Top paste" ) + AddFileExtListToFilter( { "GTP" } ) + wxT( "|" );
|
||||
filetypes += _( "Keep-out layer" ) + AddFileExtListToFilter( { "GKO" } ) + wxT( "|" );
|
||||
filetypes += _( "Top layer" ) + AddFileExtListToFilter( { "gtl" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom layer" ) + AddFileExtListToFilter( { "gbl" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom solder resist" ) + AddFileExtListToFilter( { "gbs" } ) + wxT( "|" );
|
||||
filetypes += _( "Top solder resist" ) + AddFileExtListToFilter( { "gts" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom overlay" ) + AddFileExtListToFilter( { "gbo" } ) + wxT( "|" );
|
||||
filetypes += _( "Top overlay" ) + AddFileExtListToFilter( { "gto" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom paste" ) + AddFileExtListToFilter( { "gbp" } ) + wxT( "|" );
|
||||
filetypes += _( "Top paste" ) + AddFileExtListToFilter( { "gtp" } ) + wxT( "|" );
|
||||
filetypes += _( "Keep-out layer" ) + AddFileExtListToFilter( { "gko" } ) + wxT( "|" );
|
||||
filetypes += _( "Mechanical layers" )
|
||||
+ AddFileExtListToFilter(
|
||||
{ "GM1", "GM2", "GM3", "GM4", "GM5", "GM6", "GM7", "GM8", "GM9" } )
|
||||
{ "gm1", "gm2", "gm3", "gm4", "gm5", "gm6", "gm7", "gm8", "gm9" } )
|
||||
+ wxT( "|" );
|
||||
filetypes += _( "Top Pad Master" ) + AddFileExtListToFilter( { "GPT" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom Pad Master" ) + AddFileExtListToFilter( { "GPB" } ) + wxT( "|" );
|
||||
filetypes += _( "Top Pad Master" ) + AddFileExtListToFilter( { "gpt" } ) + wxT( "|" );
|
||||
filetypes += _( "Bottom Pad Master" ) + AddFileExtListToFilter( { "gpb" } ) + wxT( "|" );
|
||||
|
||||
// All filetypes
|
||||
filetypes += AllFilesWildcard();
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ protected:
|
||||
* Display a dialog indicating the file is already open, with an option to reset the lock.
|
||||
* @return true if the lock was reset.
|
||||
*/
|
||||
bool OverrideLock( wxWindow* aParent, const wxString& aMessage );
|
||||
bool AskOverrideLock( wxWindow* aParent, const wxString& aMessage );
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+2
-1
@@ -238,7 +238,8 @@ public:
|
||||
* false to build a list of shape for a not rotated text ("native" shapes).
|
||||
*/
|
||||
std::shared_ptr<SHAPE_COMPOUND> GetEffectiveTextShape( bool aTriangulate = true,
|
||||
bool aUseTextRotation = true ) const;
|
||||
const BOX2I& aBBox = BOX2I(),
|
||||
const EDA_ANGLE& aAngle = ANGLE_0 ) const;
|
||||
|
||||
/**
|
||||
* Test if \a aPoint is within the bounds of this object.
|
||||
|
||||
@@ -207,6 +207,11 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsLockedByMe()
|
||||
{
|
||||
return m_username == wxGetUserId() && m_hostname == wxGetHostName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current username. If we own the lock, this is us. Otherwise, this is the user that does own it
|
||||
*/
|
||||
|
||||
+13
-1
@@ -38,11 +38,11 @@
|
||||
#include <search_stack.h>
|
||||
#include <settings/environment.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/snglinst.h>
|
||||
|
||||
#undef pid_t
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
class wxSingleInstanceChecker;
|
||||
class wxApp;
|
||||
class wxMenu;
|
||||
class wxWindow;
|
||||
@@ -313,6 +313,14 @@ public:
|
||||
*/
|
||||
bool IsGUI();
|
||||
|
||||
/**
|
||||
* Allows access to the wxSingleInstanceChecker to test for other running KiCads
|
||||
*/
|
||||
std::unique_ptr<wxSingleInstanceChecker>& SingleInstance()
|
||||
{
|
||||
return m_pgm_checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* wxWidgets on MSW tends to crash if you spool up more than one print job at a time.
|
||||
*/
|
||||
@@ -347,6 +355,10 @@ protected:
|
||||
|
||||
std::unique_ptr<SCRIPTING> m_python_scripting;
|
||||
|
||||
/// Checks if there is another copy of Kicad running at the same time
|
||||
std::unique_ptr<wxSingleInstanceChecker> m_pgm_checker;
|
||||
|
||||
|
||||
wxString m_bin_dir; /// full path to this program
|
||||
wxString m_kicad_env; /// The KICAD system environment variable.
|
||||
|
||||
|
||||
@@ -550,13 +550,19 @@ public:
|
||||
protected:
|
||||
/**
|
||||
* Generic fallback: arc rendered as a polyline.
|
||||
* Note also aCentre and aRadius are double to avoid creating rounding issues due
|
||||
* to the fact a arc is defined in Kicad by a start point, a end point and third point
|
||||
* not angles and radius.
|
||||
* In some plotters (i.e. dxf) whe need a good precision when calculating an arc
|
||||
* without error introduced by rounding, to avoid moving the end points,
|
||||
* usually important in outlines when plotting an arc given by center, radius and angles
|
||||
*/
|
||||
virtual void Arc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill,
|
||||
virtual void Arc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill,
|
||||
int aWidth = USE_DEFAULT_LINE_WIDTH );
|
||||
|
||||
virtual void ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& StAngle,
|
||||
const EDA_ANGLE& EndAngle, int aRadius, int aWidth,
|
||||
virtual void ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& StAngle,
|
||||
const EDA_ANGLE& EndAngle, double aRadius, int aWidth,
|
||||
OUTLINE_MODE aTraceMode, void* aData );
|
||||
|
||||
// These are marker subcomponents
|
||||
|
||||
@@ -211,8 +211,8 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill,
|
||||
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill,
|
||||
int aWidth = USE_DEFAULT_LINE_WIDTH ) override;
|
||||
|
||||
void plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor,
|
||||
|
||||
@@ -272,12 +272,12 @@ public:
|
||||
APERTURE::APERTURE_TYPE aType, int aApertureAttribute );
|
||||
|
||||
protected:
|
||||
virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill,
|
||||
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill,
|
||||
int aWidth = USE_DEFAULT_LINE_WIDTH ) override;
|
||||
|
||||
virtual void ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, int aWidth,
|
||||
virtual void ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, int aWidth,
|
||||
OUTLINE_MODE aTraceMode, void* aData ) override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -145,8 +145,8 @@ protected:
|
||||
* aEndAngle is end angle the arc.
|
||||
* Radius is the radius of the arc.
|
||||
*/
|
||||
virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill,
|
||||
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill,
|
||||
int aWidth = USE_DEFAULT_LINE_WIDTH ) override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -422,8 +422,8 @@ protected:
|
||||
OUTLINE_NODE* addOutlineNode( OUTLINE_NODE* aParent, int aActionHandle,
|
||||
const wxString& aTitle );
|
||||
|
||||
virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius,
|
||||
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius,
|
||||
FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override;
|
||||
|
||||
/// convert a wxString unicode string to a char string compatible with the accepted
|
||||
@@ -623,8 +623,8 @@ public:
|
||||
void* aData = nullptr ) override;
|
||||
|
||||
protected:
|
||||
virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius,
|
||||
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, double aRadius,
|
||||
FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -136,6 +136,7 @@ struct LAYER_PRESET
|
||||
layers = LSET::AllLayersMask();
|
||||
renderLayers = GAL_SET::DefaultVisible();
|
||||
readOnly = false;
|
||||
flipBoard = false;
|
||||
}
|
||||
|
||||
LAYER_PRESET( const wxString& aName, const LSET& aVisibleLayers ) :
|
||||
@@ -145,6 +146,7 @@ struct LAYER_PRESET
|
||||
{
|
||||
renderLayers = GAL_SET::DefaultVisible();
|
||||
readOnly = false;
|
||||
flipBoard = false;
|
||||
}
|
||||
|
||||
LAYER_PRESET( const wxString& aName, const LSET& aVisibleLayers, const GAL_SET& aVisibleObjects,
|
||||
@@ -154,7 +156,8 @@ struct LAYER_PRESET
|
||||
renderLayers( aVisibleObjects ),
|
||||
activeLayer( aActiveLayer )
|
||||
{
|
||||
readOnly = false;
|
||||
readOnly = false;
|
||||
flipBoard = false;
|
||||
}
|
||||
|
||||
bool LayersMatch( const LAYER_PRESET& aOther )
|
||||
@@ -165,6 +168,7 @@ struct LAYER_PRESET
|
||||
wxString name; ///< A name for this layer set
|
||||
LSET layers; ///< Board layers that are visible
|
||||
GAL_SET renderLayers; ///< Render layers (e.g. object types) that are visible
|
||||
bool flipBoard; ///< True if the flip board is enabled
|
||||
PCB_LAYER_ID activeLayer; ///< Optional layer to set active when this preset is loaded
|
||||
bool readOnly; ///< True if this is a read-only (built-in) preset
|
||||
};
|
||||
|
||||
@@ -45,7 +45,8 @@ typedef std::pair<KIID, wxString> FILE_INFO_PAIR;
|
||||
*/
|
||||
enum LAST_PATH_TYPE : unsigned int
|
||||
{
|
||||
LAST_PATH_NETLIST = 0,
|
||||
LAST_PATH_FIRST = 0,
|
||||
LAST_PATH_NETLIST = LAST_PATH_FIRST,
|
||||
LAST_PATH_STEP,
|
||||
LAST_PATH_IDF,
|
||||
LAST_PATH_VRML,
|
||||
|
||||
@@ -184,6 +184,10 @@ DIALOG_PCM::DIALOG_PCM( wxWindow* parent, std::shared_ptr<PLUGIN_CONTENT_MANAGER
|
||||
|
||||
DIALOG_PCM::~DIALOG_PCM()
|
||||
{
|
||||
Unbind( wxEVT_CLOSE_WINDOW, &DIALOG_PCM::OnCloseWindow, this );
|
||||
m_sdbSizer1Cancel->Unbind( wxEVT_UPDATE_UI, &DIALOG_PCM::OnUpdateEventButtons, this );
|
||||
m_sdbSizer1Apply->Unbind( wxEVT_UPDATE_UI, &DIALOG_PCM::OnUpdateEventButtons, this );
|
||||
|
||||
m_pcm->SaveInstalledPackages();
|
||||
m_pcm->SetDialogWindow( nullptr );
|
||||
m_pcm->RunBackgroundUpdate();
|
||||
|
||||
+6
-4
@@ -428,8 +428,9 @@ const bool PLUGIN_CONTENT_MANAGER::CacheRepository( const wxString& aRepositoryI
|
||||
|
||||
wxString url = std::get<2>( *repository_tuple );
|
||||
|
||||
nlohmann::json js;
|
||||
PCM_REPOSITORY current_repo;
|
||||
nlohmann::json js;
|
||||
PCM_REPOSITORY current_repo;
|
||||
PCM_REPOSITORY& current_repo_ref = current_repo;
|
||||
|
||||
std::shared_ptr<PROGRESS_REPORTER> reporter;
|
||||
|
||||
@@ -522,12 +523,13 @@ const bool PLUGIN_CONTENT_MANAGER::CacheRepository( const wxString& aRepositoryI
|
||||
packages_cache_stream << std::setw( 4 ) << js << std::endl;
|
||||
|
||||
m_repository_cache[aRepositoryId] = std::move( current_repo );
|
||||
current_repo_ref = m_repository_cache[aRepositoryId];
|
||||
}
|
||||
|
||||
if( current_repo.resources )
|
||||
if( current_repo_ref.resources )
|
||||
{
|
||||
// Check resources file date, redownload if needed
|
||||
PCM_RESOURCE_REFERENCE& resources = *current_repo.resources;
|
||||
PCM_RESOURCE_REFERENCE& resources = *current_repo_ref.resources;
|
||||
|
||||
wxFileName resource_file( repo_cache.GetPath(), wxT( "resources.zip" ) );
|
||||
|
||||
|
||||
@@ -123,10 +123,11 @@ bool PROJECT_TREE_ITEM::Rename( const wxString& name, bool check )
|
||||
if( newFile == GetFileName() )
|
||||
return false;
|
||||
|
||||
// If required, prompt the user if the filename extension has changed:
|
||||
wxString ext = PROJECT_TREE_PANE::GetFileExt( GetType() );
|
||||
wxRegEx reg( wxT( "^.*\\" ) + ext + wxT( "$" ), wxRE_ICASE );
|
||||
wxString full_ext = wxT( "." ) + ext;
|
||||
|
||||
if( check && !ext.IsEmpty() && !reg.Matches( newFile ) )
|
||||
if( check && !ext.IsEmpty() && !newFile.Lower().EndsWith( full_ext.Lower() ) )
|
||||
{
|
||||
wxMessageDialog dialog( m_parent, _( "Changing file extension will change file type.\n"
|
||||
"Do you want to continue ?" ),
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
* or inside of the curve? (Generally speaking filled shape errors go on the inside
|
||||
* and knockout errors go on the outside. This preserves minimum clearances.)
|
||||
*/
|
||||
enum ERROR_LOC { ERROR_OUTSIDE, ERROR_INSIDE };
|
||||
enum ERROR_LOC { ERROR_OUTSIDE = 0, ERROR_INSIDE };
|
||||
|
||||
/**
|
||||
* @return the number of segments to approximate a arc by segments
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at>
|
||||
* Copyright (C) CERN
|
||||
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -58,7 +58,7 @@ void kimathLogDebug( const char* aFormatString, ... )
|
||||
void kimathLogOverflow( double v, const char* aTypeName )
|
||||
{
|
||||
wxString typeName( aTypeName );
|
||||
wxLogWarning( wxString::Format( wxT( "Overflow converting value %f to %s." ), v, typeName ) );
|
||||
wxFAIL_MSG( wxString::Format( wxT( "\n\nOverflow converting value %f to %s." ), v, typeName ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Copyright (C) 2002 Claudio Girardi <claudio.girardi@ieee.org>
|
||||
* Copyright (C) 2005, 2006 Stefan Jahn <stefan@lkcc.org>
|
||||
* Modified for Kicad: 2018 Jean-Pierre Charras <jp.charras at wanadoo.fr>
|
||||
* 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 as published by
|
||||
@@ -516,18 +517,19 @@ void MICROSTRIP::showAnalyze()
|
||||
*/
|
||||
void MICROSTRIP::calcSynthesize()
|
||||
{
|
||||
double const er_eff = m_parameters[EPSILON_EFF_PRM];
|
||||
double angl_dest, z0_dest;
|
||||
z0_dest = m_parameters[Z0_PRM];
|
||||
angl_dest = m_parameters[ANG_L_PRM];
|
||||
double z0_dest = m_parameters[Z0_PRM];
|
||||
double angl_dest = m_parameters[ANG_L_PRM];
|
||||
/* calculate width and use for initial value in Newton's method */
|
||||
m_parameters[PHYS_WIDTH_PRM] = synth_width();
|
||||
minimizeZ0Error1D( &( m_parameters[PHYS_WIDTH_PRM] ) );
|
||||
|
||||
m_parameters[Z0_PRM] = z0_dest;
|
||||
m_parameters[ANG_L_PRM] = angl_dest;
|
||||
double const er_eff = m_parameters[EPSILON_EFF_PRM];
|
||||
m_parameters[PHYS_LEN_PRM] = C0 / m_parameters[FREQUENCY_PRM] / sqrt( er_eff * mur_eff )
|
||||
* m_parameters[ANG_L_PRM] / 2.0 / M_PI; /* in m */
|
||||
calcAnalyze();
|
||||
|
||||
m_parameters[Z0_PRM] = z0_dest;
|
||||
m_parameters[ANG_L_PRM] = angl_dest;
|
||||
m_parameters[PHYS_LEN_PRM] = C0 / m_parameters[FREQUENCY_PRM] / sqrt( er_eff * mur_eff )
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
#include "stripline.h"
|
||||
#include "units.h"
|
||||
|
||||
// const to convert a attenuation / loss from log to decibel
|
||||
const double LOG2DB = 20.0 / log( 10.0 );
|
||||
|
||||
STRIPLINE::STRIPLINE() : TRANSLINE()
|
||||
{
|
||||
m_Name = "StripLine";
|
||||
@@ -38,7 +41,7 @@ STRIPLINE::STRIPLINE() : TRANSLINE()
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// calculate characteristic impedance and conductor loss
|
||||
// calculate characteristic impedance and conductor loss (in log/meter)
|
||||
double STRIPLINE::lineImpedance( double height, double& ac )
|
||||
{
|
||||
double ZL;
|
||||
@@ -95,8 +98,8 @@ void STRIPLINE::calcAnalyze()
|
||||
m_parameters[Z0_PRM] = 2.0
|
||||
/ ( 1.0 / lineImpedance( 2.0 * a + t, ac1 )
|
||||
+ 1.0 / lineImpedance( 2.0 * ( h - a ) - t, ac2 ) );
|
||||
m_parameters[LOSS_CONDUCTOR_PRM] = m_parameters[PHYS_LEN_PRM] * 0.5 * ( ac1 + ac2 );
|
||||
m_parameters[LOSS_DIELECTRIC_PRM] = 20.0 / log( 10.0 ) * m_parameters[PHYS_LEN_PRM]
|
||||
m_parameters[LOSS_CONDUCTOR_PRM] = LOG2DB * m_parameters[PHYS_LEN_PRM] * ( ac1 + ac2 );
|
||||
m_parameters[LOSS_DIELECTRIC_PRM] = LOG2DB * m_parameters[PHYS_LEN_PRM]
|
||||
* ( M_PI / C0 ) * m_parameters[FREQUENCY_PRM]
|
||||
* sqrt( m_parameters[EPSILONR_PRM] )
|
||||
* m_parameters[TAND_PRM];
|
||||
|
||||
+13
-10
@@ -97,15 +97,18 @@ BOARD::BOARD() :
|
||||
m_layers[layer].m_type = LT_UNDEFINED;
|
||||
}
|
||||
|
||||
m_SolderMask = new ZONE( this );
|
||||
m_SolderMask->SetLayerSet( LSET().set( F_Mask ).set( B_Mask ) );
|
||||
// Creates a zone to show sloder mask bridges created by a min web value
|
||||
// it it just to show them
|
||||
m_SolderMaskBridges = new ZONE( this );
|
||||
m_SolderMaskBridges->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER );
|
||||
m_SolderMaskBridges->SetLayerSet( LSET().set( F_Mask ).set( B_Mask ) );
|
||||
int infinity = ( std::numeric_limits<int>::max() / 2 ) - pcbIUScale.mmToIU( 1 );
|
||||
m_SolderMask->Outline()->NewOutline();
|
||||
m_SolderMask->Outline()->Append( VECTOR2I( -infinity, -infinity ) );
|
||||
m_SolderMask->Outline()->Append( VECTOR2I( -infinity, +infinity ) );
|
||||
m_SolderMask->Outline()->Append( VECTOR2I( +infinity, +infinity ) );
|
||||
m_SolderMask->Outline()->Append( VECTOR2I( +infinity, -infinity ) );
|
||||
m_SolderMask->SetMinThickness( 0 );
|
||||
m_SolderMaskBridges->Outline()->NewOutline();
|
||||
m_SolderMaskBridges->Outline()->Append( VECTOR2I( -infinity, -infinity ) );
|
||||
m_SolderMaskBridges->Outline()->Append( VECTOR2I( -infinity, +infinity ) );
|
||||
m_SolderMaskBridges->Outline()->Append( VECTOR2I( +infinity, +infinity ) );
|
||||
m_SolderMaskBridges->Outline()->Append( VECTOR2I( +infinity, -infinity ) );
|
||||
m_SolderMaskBridges->SetMinThickness( 0 );
|
||||
|
||||
BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
|
||||
|
||||
@@ -141,7 +144,7 @@ BOARD::~BOARD()
|
||||
|
||||
m_zones.clear();
|
||||
|
||||
delete m_SolderMask;
|
||||
delete m_SolderMaskBridges;
|
||||
|
||||
for( FOOTPRINT* footprint : m_footprints )
|
||||
delete footprint;
|
||||
@@ -1916,7 +1919,7 @@ std::tuple<int, double, double> BOARD::GetTrackLength( const PCB_TRACK& aTrack )
|
||||
VECTOR2I loc;
|
||||
|
||||
// We may not collide even if we passed the bounding-box hit test
|
||||
if( pad->GetEffectivePolygon()->Collide( trackSeg, 0, nullptr, &loc ) )
|
||||
if( pad->GetEffectivePolygon( ERROR_INSIDE )->Collide( trackSeg, 0, nullptr, &loc ) )
|
||||
{
|
||||
// Part 1: length of the seg to the intersection with the pad poly
|
||||
if( hitStart )
|
||||
|
||||
+1
-1
@@ -1193,7 +1193,7 @@ public:
|
||||
std::vector<ZONE*> m_DRCCopperZones;
|
||||
int m_DRCMaxClearance;
|
||||
int m_DRCMaxPhysicalClearance;
|
||||
ZONE* m_SolderMask;
|
||||
ZONE* m_SolderMaskBridges; // A container to build bridges on solder mask layers
|
||||
std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> m_ZoneIsolatedIslandsMap;
|
||||
|
||||
private:
|
||||
|
||||
@@ -448,7 +448,7 @@ bool CONNECTIVITY_DATA::IsConnectedOnLayer( const BOARD_CONNECTED_ITEM *aItem, i
|
||||
if( zone->IsFilled() )
|
||||
{
|
||||
const SHAPE_POLY_SET* zoneFill = zone->GetFill( ToLAYER_ID( aLayer ) );
|
||||
const SHAPE_LINE_CHAIN& padHull = pad->GetEffectivePolygon()->Outline( 0 );
|
||||
const SHAPE_LINE_CHAIN& padHull = pad->GetEffectivePolygon( ERROR_INSIDE )->Outline( 0 );
|
||||
|
||||
for( const VECTOR2I& pt : zoneFill->COutline( islandIdx ).CPoints() )
|
||||
{
|
||||
|
||||
@@ -140,7 +140,8 @@ bool isCopperOutside( const FOOTPRINT* aFootprint, SHAPE_POLY_SET& aShape )
|
||||
{
|
||||
SHAPE_POLY_SET poly = aShape.CloneDropTriangulation();
|
||||
|
||||
poly.BooleanIntersection( *pad->GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST );
|
||||
poly.BooleanIntersection( *pad->GetEffectivePolygon( ERROR_INSIDE ),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
if( poly.OutlineCount() == 0 )
|
||||
{
|
||||
|
||||
@@ -294,6 +294,7 @@ bool DIALOG_COPPER_ZONE::TransferDataToWindow()
|
||||
case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: m_OutlineDisplayCtrl->SetSelection( 0 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: m_OutlineDisplayCtrl->SetSelection( 1 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: m_OutlineDisplayCtrl->SetSelection( 2 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER: break; // Not used for standard zones
|
||||
}
|
||||
|
||||
m_outlineHatchPitch.SetValue( m_settings.m_BorderHatchPitch );
|
||||
|
||||
@@ -178,6 +178,7 @@ bool DIALOG_NON_COPPER_ZONES_EDITOR::TransferDataToWindow()
|
||||
case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: m_OutlineDisplayCtrl->SetSelection( 0 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: m_OutlineDisplayCtrl->SetSelection( 1 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: m_OutlineDisplayCtrl->SetSelection( 2 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER: break; // Not used for standard zones
|
||||
}
|
||||
|
||||
m_outlineHatchPitch.SetValue( m_settings.m_BorderHatchPitch );
|
||||
|
||||
@@ -156,6 +156,7 @@ bool DIALOG_RULE_AREA_PROPERTIES::TransferDataToWindow()
|
||||
|
||||
switch( m_zonesettings.m_ZoneBorderDisplayStyle )
|
||||
{
|
||||
case ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER: // Not used for standard zones. Here use NO_HATCH
|
||||
case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: m_OutlineDisplayCtrl->SetSelection( 0 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: m_OutlineDisplayCtrl->SetSelection( 1 ); break;
|
||||
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: m_OutlineDisplayCtrl->SetSelection( 2 ); break;
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <core/kicad_algo.h>
|
||||
#include <pcbnew.h>
|
||||
#include <pcb_edit_frame.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <tools/pcb_actions.h>
|
||||
#include <board.h>
|
||||
#include <collectors.h>
|
||||
#include <footprint.h>
|
||||
@@ -486,6 +488,8 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow()
|
||||
|
||||
if( !removedLayers.empty() )
|
||||
{
|
||||
m_frame->GetToolManager()->RunAction( PCB_ACTIONS::selectionClear );
|
||||
|
||||
PCB_LAYER_COLLECTOR collector;
|
||||
|
||||
for( PCB_LAYER_ID layer_id : removedLayers )
|
||||
@@ -545,11 +549,10 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow()
|
||||
}
|
||||
}
|
||||
|
||||
for( PCB_TRACK* via : m_pcb->Tracks() )
|
||||
{
|
||||
if( via->HasHole() )
|
||||
via->SetLayerSet( via->GetLayerSet() | LSET::InternalCuMask() );
|
||||
}
|
||||
// Tracks do not change their layer
|
||||
// Vias layers are defined by the starting layer and the ending layer, so
|
||||
// they are not modified by adding a layer.
|
||||
// So do nothing for tracks/vias
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -741,7 +741,8 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
|
||||
|
||||
for( auto& [ zone, rtree ] : board->m_CopperZoneRTreeCache )
|
||||
{
|
||||
if( !rtree->GetObjectsAt( location, aLayer, aMinWidth ).empty()
|
||||
if( rtree
|
||||
&& !rtree->GetObjectsAt( location, aLayer, aMinWidth ).empty()
|
||||
&& zone->HitTestFilledArea( aLayer, location, aMinWidth ) )
|
||||
{
|
||||
contributingItems.push_back( zone );
|
||||
|
||||
@@ -948,6 +948,9 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testGraphicClearances( )
|
||||
auto testGraphicAgainstZone =
|
||||
[&]( BOARD_ITEM* item )
|
||||
{
|
||||
if( item->Type() == PCB_BITMAP_T )
|
||||
return;
|
||||
|
||||
if( !IsCopperLayer( item->GetLayer() ) )
|
||||
return;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021-2022 KiCad Developers.
|
||||
* Copyright (C) 2021-2023 KiCad Developers.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -216,9 +216,11 @@ bool shapesNeedUpdate( const FP_SHAPE* a, const FP_SHAPE* b )
|
||||
TEST( a->GetStart0(), b->GetStart0() );
|
||||
TEST( a->GetEnd0(), b->GetEnd0() );
|
||||
|
||||
// Arc center is calculated and so may have round-off errors when parents are
|
||||
// differentially rotated.
|
||||
if( ( a->GetCenter0() - b->GetCenter0() ).EuclideanNorm() > pcbIUScale.mmToIU( 0.0001 ) )
|
||||
// Arc center is calculated to the nearest 100nm increment and may have round-off errors
|
||||
// when parents are differentially rotated. See CalcArcCenter() function in
|
||||
// ./libs/kimath/src/trigo.cpp.
|
||||
if( ( std::abs( a->GetCenter0().x - b->GetCenter0().x ) > pcbIUScale.mmToIU( 0.0002 ) ) ||
|
||||
( std::abs( a->GetCenter0().y - b->GetCenter0().y ) > pcbIUScale.mmToIU( 0.0002 ) ) )
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
@@ -112,7 +112,7 @@ private:
|
||||
|
||||
void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* aItem )
|
||||
{
|
||||
ZONE* solderMask = m_board->m_SolderMask;
|
||||
ZONE* solderMask = m_board->m_SolderMaskBridges;
|
||||
|
||||
if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T )
|
||||
{
|
||||
@@ -207,7 +207,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* aItem )
|
||||
|
||||
void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
|
||||
{
|
||||
ZONE* solderMask = m_board->m_SolderMask;
|
||||
ZONE* solderMask = m_board->m_SolderMaskBridges;
|
||||
LSET layers = { 4, F_Mask, B_Mask, F_Cu, B_Cu };
|
||||
|
||||
const size_t progressDelta = 500;
|
||||
|
||||
@@ -55,9 +55,31 @@ inline double diameter_in_mm( double ius )
|
||||
|
||||
|
||||
// return a pen size to plot markers and having a readable shape
|
||||
inline int getMarkerBestPenSize( int aMarkerDiameter )
|
||||
// clamped to be >= MIN_SIZE_MM to avoid too small line width
|
||||
static int getMarkerBestPenSize( int aMarkerDiameter )
|
||||
{
|
||||
return aMarkerDiameter / 10;
|
||||
int bestsize = aMarkerDiameter / 10;
|
||||
|
||||
const double MIN_SIZE_MM = 0.1;
|
||||
bestsize = std::max( bestsize, pcbIUScale.mmToIU( MIN_SIZE_MM ) );
|
||||
|
||||
return bestsize;
|
||||
}
|
||||
|
||||
|
||||
// return a pen size to plot outlines for oval holes
|
||||
inline int getSketchOvalBestPenSize()
|
||||
{
|
||||
const double SKETCH_LINE_WIDTH_MM = 0.1;
|
||||
return pcbIUScale.mmToIU( SKETCH_LINE_WIDTH_MM );
|
||||
}
|
||||
|
||||
|
||||
// return a default pen size to plot items with no specific line thickness
|
||||
inline int getDefaultPenSize()
|
||||
{
|
||||
const double DEFAULT_LINE_WIDTH_MM = 0.2;
|
||||
return pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH_MM );
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +193,7 @@ bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_
|
||||
plotter->SetColorMode( false );
|
||||
|
||||
KIGFX::PCB_RENDER_SETTINGS renderSettings;
|
||||
renderSettings.SetDefaultPenWidth( pcbIUScale.mmToIU( 0.2 ) );
|
||||
renderSettings.SetDefaultPenWidth( getDefaultPenSize() );
|
||||
|
||||
plotter->SetRenderSettings( &renderSettings );
|
||||
|
||||
@@ -474,7 +496,16 @@ bool GENDRILL_WRITER_BASE::plotDrillMarks( PLOTTER* aPlotter )
|
||||
aPlotter->Marker( pos, hole.m_Hole_Diameter, hole.m_Tool_Reference - 1 );
|
||||
|
||||
if( hole.m_Hole_Shape != 0 )
|
||||
{
|
||||
aPlotter->SetCurrentLineWidth( getSketchOvalBestPenSize() );
|
||||
// FlashPadOval uses also the render settings default pen size, so we need
|
||||
// to initialize it:
|
||||
KIGFX::RENDER_SETTINGS* renderSettings = aPlotter->RenderSettings();
|
||||
int curr_default_pensize = renderSettings->GetDefaultPenWidth();
|
||||
renderSettings->SetDefaultPenWidth( getSketchOvalBestPenSize() );
|
||||
aPlotter->FlashPadOval( pos, hole.m_Hole_Size, hole.m_Hole_Orient, SKETCH, nullptr );
|
||||
renderSettings->SetDefaultPenWidth( curr_default_pensize );
|
||||
}
|
||||
}
|
||||
|
||||
aPlotter->SetCurrentLineWidth( -1 );
|
||||
|
||||
@@ -132,6 +132,7 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
|
||||
brd_plotter.SetLayerSet( LSET( aLayer ) );
|
||||
int cmp_count = 0;
|
||||
bool allowUtf8 = true;
|
||||
const bool quoteOption = false;
|
||||
|
||||
// Plot components data: position, outlines, pad1 and other pads.
|
||||
for( FOOTPRINT* footprint : fp_list )
|
||||
@@ -141,9 +142,9 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
|
||||
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_POSITION );
|
||||
|
||||
// Add object attribute: component reference to flash (mainly useful for users)
|
||||
// using quoted UTF8 string
|
||||
// using not quoted UTF8 string
|
||||
wxString ref = ConvertNotAllowedCharsInGerber( footprint->Reference().GetShownText( false ),
|
||||
allowUtf8, true );
|
||||
allowUtf8, quoteOption );
|
||||
|
||||
gbr_metadata.SetCmpReference( ref );
|
||||
gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
|
||||
@@ -164,15 +165,15 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
|
||||
|
||||
// Add component value info:
|
||||
pnpAttrib.m_Value = ConvertNotAllowedCharsInGerber( footprint->Value().GetShownText( false ),
|
||||
allowUtf8, true );
|
||||
allowUtf8, quoteOption );
|
||||
|
||||
// Add component footprint info:
|
||||
wxString fp_info = FROM_UTF8( footprint->GetFPID().GetLibItemName().c_str() );
|
||||
pnpAttrib.m_Footprint = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, true );
|
||||
pnpAttrib.m_Footprint = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
|
||||
|
||||
// Add footprint lib name:
|
||||
fp_info = FROM_UTF8( footprint->GetFPID().GetLibNickname().c_str() );
|
||||
pnpAttrib.m_LibraryName = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, true );
|
||||
pnpAttrib.m_LibraryName = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
|
||||
|
||||
gbr_metadata.m_NetlistMetadata.SetExtraData( pnpAttrib.FormatCmpPnPMetadata() );
|
||||
|
||||
@@ -247,9 +248,9 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
|
||||
gbr_metadata.SetApertureAttrib(
|
||||
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_PAD1_POSITION );
|
||||
|
||||
gbr_metadata.SetPadName( pad1->GetNumber(), allowUtf8, true );
|
||||
gbr_metadata.SetPadName( pad1->GetNumber(), allowUtf8, quoteOption );
|
||||
|
||||
gbr_metadata.SetPadPinFunction( pad1->GetPinFunction(), allowUtf8, true );
|
||||
gbr_metadata.SetPadPinFunction( pad1->GetPinFunction(), allowUtf8, quoteOption );
|
||||
|
||||
gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_PAD );
|
||||
|
||||
@@ -286,9 +287,9 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
|
||||
if( !pad->IsOnLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
gbr_metadata.SetPadName( pad->GetNumber(), allowUtf8, true );
|
||||
gbr_metadata.SetPadName( pad->GetNumber(), allowUtf8, quoteOption );
|
||||
|
||||
gbr_metadata.SetPadPinFunction( pad->GetPinFunction(), allowUtf8, true );
|
||||
gbr_metadata.SetPadPinFunction( pad->GetPinFunction(), allowUtf8, quoteOption );
|
||||
|
||||
// Flashes a round, 0 sized round shape at pad position
|
||||
plotter.FlashPadCircle( pad->GetPosition(), other_pads_mark_size, FILLED,
|
||||
|
||||
@@ -322,6 +322,7 @@ void EXPORTER_STEP::determinePcbThickness()
|
||||
break;
|
||||
|
||||
case BS_ITEM_TYPE_COPPER:
|
||||
case BS_ITEM_TYPE_SOLDERMASK:
|
||||
if( item->IsEnabled() )
|
||||
thickness += item->GetThickness();
|
||||
|
||||
|
||||
+14
-2
@@ -53,6 +53,7 @@
|
||||
#include <widgets/wx_progress_reporters.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <paths.h>
|
||||
#include <pgm_base.h>
|
||||
#include <project/project_file.h>
|
||||
#include <project/project_local_settings.h>
|
||||
#include <project/net_settings.h>
|
||||
@@ -611,12 +612,23 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
|
||||
|
||||
std::unique_ptr<LOCKFILE> lock = std::make_unique<LOCKFILE>( fullFileName );
|
||||
|
||||
if( !lock->Locked() )
|
||||
if( !lock->Valid() && lock->IsLockedByMe() )
|
||||
{
|
||||
// If we cannot acquire the lock but we appear to be the one who
|
||||
// locked it, check to see if there is another KiCad instance running.
|
||||
// If there is not, then we can override the lock. This could happen if
|
||||
// KiCad crashed or was interrupted
|
||||
|
||||
if( !Pgm().SingleInstance()->IsAnotherRunning() )
|
||||
lock->OverrideLock();
|
||||
}
|
||||
|
||||
if( !lock->Valid() )
|
||||
{
|
||||
msg.Printf( _( "PCB '%s' is already open by '%s' at '%s'." ), wx_filename.GetFullName(),
|
||||
lock->GetUsername(), lock->GetHostname() );
|
||||
|
||||
if( !OverrideLock( this, msg ) )
|
||||
if( !AskOverrideLock( this, msg ) )
|
||||
return false;
|
||||
|
||||
lock->OverrideLock();
|
||||
|
||||
+125
-6
@@ -947,6 +947,51 @@ const BOX2I FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisible
|
||||
}
|
||||
|
||||
|
||||
const BOX2I FOOTPRINT::GetLayerBoundingBox( LSET aLayers ) const
|
||||
{
|
||||
std::vector<PCB_TEXT*> texts;
|
||||
const BOARD* board = GetBoard();
|
||||
bool isFPEdit = board && board->IsFootprintHolder();
|
||||
|
||||
// Start with an uninitialized bounding box
|
||||
BOX2I bbox;
|
||||
|
||||
for( BOARD_ITEM* item : m_drawings )
|
||||
{
|
||||
if( m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
|
||||
continue;
|
||||
|
||||
if( ( aLayers & item->GetLayerSet() ).none() )
|
||||
continue;
|
||||
|
||||
// We want the bitmap bounding box just in the footprint editor
|
||||
// so it will start with the correct initial zoom
|
||||
if( item->Type() == PCB_BITMAP_T && !isFPEdit )
|
||||
continue;
|
||||
|
||||
bbox.Merge( item->GetBoundingBox() );
|
||||
}
|
||||
|
||||
for( PAD* pad : m_pads )
|
||||
{
|
||||
if( ( aLayers & pad->GetLayerSet() ).none() )
|
||||
continue;
|
||||
|
||||
bbox.Merge( pad->GetBoundingBox() );
|
||||
}
|
||||
|
||||
for( ZONE* zone : m_fp_zones )
|
||||
{
|
||||
if( ( aLayers & zone->GetLayerSet() ).none() )
|
||||
continue;
|
||||
|
||||
bbox.Merge( zone->GetBoundingBox() );
|
||||
}
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET FOOTPRINT::GetBoundingHull() const
|
||||
{
|
||||
const BOARD* board = GetBoard();
|
||||
@@ -1103,23 +1148,97 @@ void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_I
|
||||
bool FOOTPRINT::IsOnLayer( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
// If we have any pads, fall back on normal checking
|
||||
if( !m_pads.empty() )
|
||||
return m_layer == aLayer;
|
||||
for( PAD* pad : m_pads )
|
||||
{
|
||||
if( pad->IsOnLayer( aLayer ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
// No pads? Check if this entire footprint exists on the given layer
|
||||
for( FP_ZONE* zone : m_fp_zones )
|
||||
{
|
||||
if( !zone->IsOnLayer( aLayer ) )
|
||||
return false;
|
||||
if( zone->IsOnLayer( aLayer ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
if( m_reference->IsOnLayer( aLayer ) || m_value->IsOnLayer( aLayer ) )
|
||||
return true;
|
||||
|
||||
for( BOARD_ITEM* item : m_drawings )
|
||||
{
|
||||
if( item->IsOnLayer( aLayer ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool FOOTPRINT::HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy ) const
|
||||
{
|
||||
for( PAD* pad : m_pads )
|
||||
{
|
||||
if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition, aAccuracy ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
for( ZONE* zone : m_fp_zones )
|
||||
{
|
||||
if( zone->IsOnLayer( aLayer ) && zone->HitTest( aPosition, aAccuracy ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
for( BOARD_ITEM* item : m_drawings )
|
||||
{
|
||||
if( !item->IsOnLayer( aLayer ) )
|
||||
if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer )
|
||||
&& item->HitTest( aPosition, aAccuracy ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool FOOTPRINT::HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy ) const
|
||||
{
|
||||
std::vector<BOARD_ITEM*> items;
|
||||
|
||||
for( PAD* pad : m_pads )
|
||||
{
|
||||
if( pad->IsOnLayer( aLayer ) )
|
||||
items.push_back( pad );
|
||||
}
|
||||
|
||||
for( FP_ZONE* zone : m_fp_zones )
|
||||
{
|
||||
if( zone->IsOnLayer( aLayer ) )
|
||||
items.push_back( zone );
|
||||
}
|
||||
|
||||
for( BOARD_ITEM* item : m_drawings )
|
||||
{
|
||||
if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer ) )
|
||||
items.push_back( item );
|
||||
}
|
||||
|
||||
// If we require the elements to be contained in the rect and any of them are not,
|
||||
// we can return false;
|
||||
// Conversely, if we just require any of the elements to have a hit, we can return true
|
||||
// when the first one is found.
|
||||
for( BOARD_ITEM* item : items )
|
||||
{
|
||||
if( !aContained && item->HitTest( aRect, aContained, aAccuracy ) )
|
||||
return true;
|
||||
else if( aContained && !item->HitTest( aRect, aContained, aAccuracy ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// If we didn't exit in the loop, that means that we did not return false for aContained or
|
||||
// we did not return true for !aContained. So we can just return the bool with a test of
|
||||
// whether there were any elements or not.
|
||||
return !items.empty() && aContained;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+18
-3
@@ -167,6 +167,11 @@ public:
|
||||
const BOX2I GetBoundingBox() const override;
|
||||
const BOX2I GetBoundingBox( bool aIncludeText, bool aIncludeInvisibleText ) const;
|
||||
|
||||
/**
|
||||
* Return the bounding box of the footprint on a given set of layers
|
||||
*/
|
||||
const BOX2I GetLayerBoundingBox( LSET aLayers ) const;
|
||||
|
||||
VECTOR2I GetCenter() const override
|
||||
{
|
||||
return GetBoundingBox( false, false ).GetCenter();
|
||||
@@ -329,9 +334,7 @@ public:
|
||||
bool IsFlipped() const { return GetLayer() == B_Cu; }
|
||||
|
||||
/**
|
||||
* A special IsOnLayer for footprints: return true if the footprint contains only items on the
|
||||
* given layer, even if that layer is not one of the valid footprint layers F_Cu and B_Cu.
|
||||
* This allows selection of "graphic" footprints that contain only silkscreen, for example.
|
||||
* @copydoc BOARD_ITEM::IsOnLayer
|
||||
*/
|
||||
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
|
||||
|
||||
@@ -518,6 +521,18 @@ public:
|
||||
|
||||
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
|
||||
|
||||
/**
|
||||
* Test if the point hits one or more of the footprint elements on a given layer.
|
||||
*
|
||||
* @param aPosition is the point to test
|
||||
* @param aAccuracy is the hit test accuracy
|
||||
* @param aLayer is the layer to test
|
||||
* @return true if aPosition hits a footprint element on aLayer
|
||||
*/
|
||||
bool HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy = 0 ) const;
|
||||
|
||||
bool HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy = 0 ) const;
|
||||
|
||||
/**
|
||||
* @return reference designator text.
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <tools/board_editor_control.h>
|
||||
#include <board.h>
|
||||
#include <footprint.h>
|
||||
#include <pcb_group.h>
|
||||
#include <board_commit.h>
|
||||
#include <footprint_edit_frame.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
@@ -764,7 +765,8 @@ void PCB_EDIT_FRAME::ExportFootprintsToLibrary( bool aStoreInNewLib, const wxStr
|
||||
pi->FootprintSave( libPath, fpCopy );
|
||||
|
||||
// Remove reference to the group before deleting
|
||||
fpCopy->SetParentGroup( nullptr );
|
||||
if( PCB_GROUP* parentGroup = fpCopy->GetParentGroup() )
|
||||
parentGroup->RemoveItem( fpCopy );
|
||||
|
||||
delete fpCopy;
|
||||
}
|
||||
|
||||
+61
-7
@@ -176,16 +176,42 @@ void FP_SHAPE::SetCenter0( const VECTOR2I& aCenter )
|
||||
}
|
||||
|
||||
|
||||
void FP_SHAPE::CalcArcAngles0( EDA_ANGLE& aStartAngle0, EDA_ANGLE& aEndAngle0 ) const {
|
||||
VECTOR2D startRadial( GetStart0() - GetCenter0() );
|
||||
VECTOR2D endRadial( GetEnd0() - GetCenter0() );
|
||||
|
||||
aStartAngle0 = EDA_ANGLE( startRadial );
|
||||
aEndAngle0 = EDA_ANGLE( endRadial );
|
||||
|
||||
if( aEndAngle0 == aStartAngle0 )
|
||||
aEndAngle0 = aStartAngle0 + ANGLE_360; // ring, not null
|
||||
|
||||
while( aEndAngle0 < aStartAngle0 )
|
||||
aEndAngle0 += ANGLE_360;
|
||||
}
|
||||
|
||||
|
||||
EDA_ANGLE FP_SHAPE::GetArcAngle0() const
|
||||
{
|
||||
EDA_ANGLE startAngle0;
|
||||
EDA_ANGLE endAngle0;
|
||||
|
||||
CalcArcAngles0( startAngle0, endAngle0 );
|
||||
|
||||
return endAngle0 - startAngle0;
|
||||
}
|
||||
|
||||
|
||||
VECTOR2I FP_SHAPE::GetArcMid0() const
|
||||
{
|
||||
// If none of the input data have changed since we loaded the arc,
|
||||
// keep the original mid point data to minimize churn
|
||||
if( m_arcMidData_0.start == m_start && m_arcMidData_0.end == m_end
|
||||
&& m_arcMidData_0.center == m_arcCenter )
|
||||
if( m_arcMidData_0.start == m_start0 && m_arcMidData_0.end == m_end0
|
||||
&& m_arcMidData_0.center == m_arcCenter0 )
|
||||
return m_arcMidData_0.mid;
|
||||
|
||||
VECTOR2I mid0 = m_start0;
|
||||
RotatePoint( mid0, m_arcCenter0, -GetArcAngle() / 2.0 );
|
||||
RotatePoint( mid0, m_arcCenter0, -GetArcAngle0() / 2.0 );
|
||||
return mid0;
|
||||
}
|
||||
|
||||
@@ -198,21 +224,49 @@ void FP_SHAPE::SetArcAngleAndEnd0( const EDA_ANGLE& aAngle, bool aCheckNegativeA
|
||||
RotatePoint( m_end0, m_arcCenter0, -angle.Normalize720() );
|
||||
|
||||
if( aCheckNegativeAngle && aAngle < ANGLE_0 )
|
||||
{
|
||||
std::swap( m_start0, m_end0 );
|
||||
m_endsSwapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FP_SHAPE::SetCachedArcData0( const VECTOR2I& aStart0, const VECTOR2I& aMid0,
|
||||
const VECTOR2I& aEnd0, const VECTOR2I& aCenter0 )
|
||||
{
|
||||
m_arcMidData_0.start = aStart0;
|
||||
m_arcMidData_0.end = aEnd0;
|
||||
m_arcMidData_0.center = aCenter0;
|
||||
m_arcMidData_0.mid = aMid0;
|
||||
}
|
||||
|
||||
|
||||
void FP_SHAPE::SetArcGeometry0( const VECTOR2I& aStart0, const VECTOR2I& aMid0,
|
||||
const VECTOR2I& aEnd0 )
|
||||
{
|
||||
m_arcMidData_0 = {};
|
||||
m_start0 = aStart0;
|
||||
m_end0 = aEnd0;
|
||||
m_arcCenter0 = CalcArcCenter( aStart0, aMid0, aEnd0 );
|
||||
const VECTOR2I new_mid = GetArcMid0();
|
||||
|
||||
m_arcMidData_0.center = m_arcCenter0;
|
||||
m_arcMidData_0.end = m_end0;
|
||||
m_arcMidData_0.mid = aMid0;
|
||||
m_arcMidData_0.start = m_start0;
|
||||
m_endsSwapped = false;
|
||||
|
||||
SetCachedArcData0( aStart0, aMid0, aEnd0, m_arcCenter0 );
|
||||
|
||||
/*
|
||||
* If the input winding doesn't match our internal winding, the calculated midpoint will end
|
||||
* up on the other side of the arc. In this case, we need to flip the start/end points and
|
||||
* flag this change for the system.
|
||||
*/
|
||||
VECTOR2D dist( new_mid - aMid0 );
|
||||
VECTOR2D dist2( new_mid - m_arcCenter0 );
|
||||
|
||||
if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
|
||||
{
|
||||
std::swap( m_start0, m_end0 );
|
||||
m_endsSwapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user