Compare commits

...

26 Commits

Author SHA1 Message Date
Alihossein Sepahvand
8baa1ac8bc Merge branch 'master' into 'master'
Refactor pad thickness calculations to use actual copper thickness instead of...

See merge request kicad/code/kicad!2262
2025-09-12 07:52:57 -06:00
Seth Hillbrand
c64f99c57a ADDED: VCSHASH and VCSSHORTHASH
Right now, git hashes only resolved by the variables ${VCSHASH} or
${VCSSHORTHASH}
2025-09-12 06:00:27 -07:00
Seth Hillbrand
5952c2fb9a ADDED: Pad edit table
Allows editing common pad properties in table format

Fixes https://gitlab.com/kicad/code/kicad/issues/10789
2025-09-12 05:39:25 -07:00
Jeff Young
0b6de964fd Fix initialization order. 2025-09-12 13:32:17 +01:00
Jeff Young
976278e5a6 ADDED: extra info for shape, pin and field differences.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21647
2025-09-12 13:32:17 +01:00
Roberto Fernandez Bautista
8d8bd7253f Windows CI: custom vcpkg triplet to ensure shareable binary cache 2025-09-12 11:18:50 +00:00
Seth Hillbrand
5dc6d43f43 Add via tenting/plugging representation to 3d-viewer
Fixes https://gitlab.com/kicad/code/kicad/issues/21704
2025-09-12 01:23:41 -07:00
jean-pierre charras
98dd5a68eb Fix a compil issue on gcc/msys2
gcc does not like implicit conversion from wxString to char*
2025-09-12 08:20:49 +02:00
Alihossein Sepahvand
4cb9016711 Merge branch kicad:master into master 2025-07-10 08:24:00 -06:00
Alihossein Sepahvand
d4e6915cc7 Merge branch kicad:master into master 2025-06-22 13:00:39 -06:00
Alihossein Sepahvand
cde0f2bc6b Merge branch kicad:master into master 2025-06-19 11:07:18 -06:00
Alihossein Sepahvand
4ede7ac89b Merge branch kicad:master into master 2025-06-17 15:12:55 -06:00
“Alihossein
0cac688d0e fix: added back the constant for extra pad thickness in AddPadShape method 2025-06-17 15:12:39 -06:00
Alihossein Sepahvand
42431925cb Merge branch kicad:master into master 2025-06-17 07:58:38 -06:00
“Alihossein
32be56b3f4 reverting pad adding extra pad thickness to be the same as copper thinckness. I left enabling or disabling extrapad thickness in there. 2025-06-17 07:57:09 -06:00
Alihossein Sepahvand
6b5d345647 Merge branch kicad:master into master 2025-06-13 17:30:20 -06:00
Alihossein Sepahvand
f31231c1ca Merge branch kicad:master into master 2025-06-13 07:22:40 -06:00
Alihossein Sepahvand
4100128f4d Merge branch kicad:master into master 2025-06-12 23:28:48 -06:00
Alihossein Sepahvand
d0c1ef1805 Merge branch kicad:master into master 2025-06-11 19:21:30 -06:00
Alihossein Sepahvand
6c2b4a23ec Thanks 2025-06-11 08:07:26 -06:00
Alihossein Sepahvand
03e4505eda Merge branch kicad:master into master 2025-06-11 08:06:21 -06:00
Alihossein Sepahvand
84b4eebe76 Merge branch kicad:master into master 2025-06-10 12:46:53 -06:00
Alihossein Sepahvand
62a10ca715 Merge branch kicad:master into master 2025-06-10 11:58:00 -06:00
“Alihossein
340edcf071 Add extra pad thickness option for 3D PCB export
Introduced a new parameter to control extra pad thickness during 3D exports. This allows users to disable the additional thickness, reverting to normal pad dimensions. Updated relevant classes and methods to accommodate this feature, enhancing flexibility in PCB modeling.
2025-06-10 11:55:17 -06:00
Alihossein Sepahvand
d0a3401338 Merge branch kicad:master into master 2025-06-10 16:23:11 +00:00
“Alihossein
b1866776af Refactor pad thickness calculations to use actual copper thickness instead of a fixed value. This change improves accuracy in FEM simulations and margin adjustments for pads. 2025-06-10 10:07:58 -06:00
31 changed files with 1139 additions and 73 deletions

View File

@ -9,7 +9,6 @@ win64_build:
image: registry.gitlab.com/kicad/kicad-ci/windows-build-image/ltsc2022-msvc:latest
variables:
VCPKG_BINARY_SOURCES: 'nuget,gitlab,readwrite'
VCPKG_DISABLE_COMPILER_TRACKING: '1'
# Switch the compressor to fastzip and reduce the compression level
FF_USE_FASTZIP: "true"
CACHE_COMPRESSION_LEVEL: "fast"
@ -37,6 +36,8 @@ win64_build:
-DKICAD_BUILD_PNS_DEBUG_TOOL=ON `
-DKICAD_USE_3DCONNEXION=ON `
-DVCPKG_BUILD_TYPE=debug `
-DVCPKG_INSTALL_OPTIONS="--x-abi-tools-use-exact-versions" `
-DVCPKG_OVERLAY_TRIPLETS="$Env:CI_PROJECT_DIR/tools/custom_vcpkg_triplets" `
../../
- cmake --build . 2>&1 | tee compilation_log.txt
- cd ../../

View File

@ -26,6 +26,7 @@
#include "render_3d_opengl.h"
#include <board.h>
#include <footprint.h>
#include <pcb_track.h>
#include "../../3d_math.h"
#include "convert_basic_shapes_to_polygon.h"
#include <lset.h>
@ -726,6 +727,54 @@ void RENDER_3D_OPENGL::generateCylinder( const SFVEC2F& aCenter, float aInnerRad
}
void RENDER_3D_OPENGL::generateDisk( const SFVEC2F& aCenter, float aRadius, float aZ,
unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
bool aTop )
{
const float delta = 2.0f * glm::pi<float>() / (float) aNr_sides_per_circle;
for( unsigned int i = 0; i < aNr_sides_per_circle; ++i )
{
float a0 = delta * i;
float a1 = delta * ( i + 1 );
const SFVEC3F p0( aCenter.x + cosf( a0 ) * aRadius,
aCenter.y + sinf( a0 ) * aRadius, aZ );
const SFVEC3F p1( aCenter.x + cosf( a1 ) * aRadius,
aCenter.y + sinf( a1 ) * aRadius, aZ );
const SFVEC3F c( aCenter.x, aCenter.y, aZ );
if( aTop )
aDstLayer->m_layer_top_triangles->AddTriangle( p1, p0, c );
else
aDstLayer->m_layer_bot_triangles->AddTriangle( p0, p1, c );
}
}
void RENDER_3D_OPENGL::generateDimple( const SFVEC2F& aCenter, float aRadius, float aZ,
float aDepth, unsigned int aNr_sides_per_circle,
TRIANGLE_DISPLAY_LIST* aDstLayer, bool aTop )
{
const float delta = 2.0f * glm::pi<float>() / (float) aNr_sides_per_circle;
const SFVEC3F c( aCenter.x, aCenter.y, aTop ? aZ - aDepth : aZ + aDepth );
for( unsigned int i = 0; i < aNr_sides_per_circle; ++i )
{
float a0 = delta * i;
float a1 = delta * ( i + 1 );
const SFVEC3F p0( aCenter.x + cosf( a0 ) * aRadius,
aCenter.y + sinf( a0 ) * aRadius, aZ );
const SFVEC3F p1( aCenter.x + cosf( a1 ) * aRadius,
aCenter.y + sinf( a1 ) * aRadius, aZ );
if( aTop )
aDstLayer->m_layer_top_triangles->AddTriangle( p0, p1, c );
else
aDstLayer->m_layer_bot_triangles->AddTriangle( p1, p0, c );
}
}
void RENDER_3D_OPENGL::generateViasAndPads()
{
if( !m_boardAdapter.GetBoard() )
@ -882,6 +931,63 @@ void RENDER_3D_OPENGL::generateViasAndPads()
delete layerTriangles;
}
}
TRIANGLE_DISPLAY_LIST* frontCover = new TRIANGLE_DISPLAY_LIST( m_boardAdapter.GetViaCount() );
TRIANGLE_DISPLAY_LIST* backCover = new TRIANGLE_DISPLAY_LIST( m_boardAdapter.GetViaCount() );
for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
{
if( track->Type() != PCB_VIA_T )
continue;
const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3dUnits();
const float hole_radius = holediameter / 2.0f + 2.0 * platingThickness3d;
const SFVEC2F center( via->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
-via->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
unsigned int seg = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
PCB_LAYER_ID top_layer, bottom_layer;
via->LayerPair( &top_layer, &bottom_layer );
float ztop, zbot, dummy;
getLayerZPos( top_layer, ztop, dummy );
getLayerZPos( bottom_layer, dummy, zbot );
bool frontCovering = via->GetFrontCoveringMode() == COVERING_MODE::COVERED || via->IsTented( F_Mask );
bool backCovering = via->GetBackCoveringMode() == COVERING_MODE::COVERED || via->IsTented( B_Mask );
bool frontPlugged = via->GetFrontPluggingMode() == PLUGGING_MODE::PLUGGED;
bool backPlugged = via->GetBackPluggingMode() == PLUGGING_MODE::PLUGGED;
bool filled = via->GetFillingMode() == FILLING_MODE::FILLED
|| via->GetCappingMode() == CAPPING_MODE::CAPPED;
const float depth = hole_radius * 0.3f;
if( frontCovering )
{
if( filled || !frontPlugged )
generateDisk( center, hole_radius, ztop, seg, frontCover, true );
else
generateDimple( center, hole_radius, ztop, depth, seg, frontCover, true );
}
if( backCovering )
{
if( filled || !backPlugged )
generateDisk( center, hole_radius, zbot, seg, backCover, false );
else
generateDimple( center, hole_radius, zbot, depth, seg, backCover, false );
}
}
if( frontCover->m_layer_top_triangles->GetVertexSize() > 0 )
m_viaFrontCover = new OPENGL_RENDER_LIST( *frontCover, 0, 0.0f, 0.0f );
if( backCover->m_layer_bot_triangles->GetVertexSize() > 0 )
m_viaBackCover = new OPENGL_RENDER_LIST( *backCover, 0, 0.0f, 0.0f );
delete frontCover;
delete backCover;
}

View File

@ -72,6 +72,8 @@ RENDER_3D_OPENGL::RENDER_3D_OPENGL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdap
m_outerViaThroughHoles = nullptr;
m_microviaHoles = nullptr;
m_padHoles = nullptr;
m_viaFrontCover = nullptr;
m_viaBackCover = nullptr;
m_circleTexture = 0;
m_grid = 0;
@ -947,6 +949,8 @@ void RENDER_3D_OPENGL::freeAllLists()
DELETE_AND_FREE( m_microviaHoles )
DELETE_AND_FREE( m_padHoles )
DELETE_AND_FREE( m_viaFrontCover )
DELETE_AND_FREE( m_viaBackCover )
}
@ -968,6 +972,17 @@ void RENDER_3D_OPENGL::renderSolderMaskLayer( PCB_LAYER_ID aLayerID, float aZPos
setLayerMaterial( aLayerID );
m_board->SetItIsTransparent( true );
m_board->DrawCulled( aShowThickness, solder_mask, via_holes );
if( aLayerID == F_Mask && m_viaFrontCover )
{
m_viaFrontCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
m_viaFrontCover->DrawTop();
}
else if( aLayerID == B_Mask && m_viaBackCover )
{
m_viaBackCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
m_viaBackCover->DrawBot();
}
}
}

View File

@ -123,6 +123,14 @@ private:
float aZtop, float aZbot, unsigned int aNr_sides_per_circle,
TRIANGLE_DISPLAY_LIST* aDstLayer );
void generateDisk( const SFVEC2F& aCenter, float aRadius, float aZ,
unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
bool aTop );
void generateDimple( const SFVEC2F& aCenter, float aRadius, float aZ, float aDepth,
unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
bool aTop );
void generateViasAndPads();
/**
@ -236,6 +244,8 @@ private:
OPENGL_RENDER_LIST* m_microviaHoles;
OPENGL_RENDER_LIST* m_padHoles;
OPENGL_RENDER_LIST* m_viaFrontCover;
OPENGL_RENDER_LIST* m_viaBackCover;
// Caches
std::map<wxString, MODEL_3D*> m_3dModelMap;

View File

@ -6,7 +6,12 @@
},
{
"environment": "vcpkg",
"VcPkgDir": "D:/vcpkg/"
"VcPkgDir": "D:/vcpkg/",
"VCPKG_BINARY_SOURCES": "nuget,kicad-gitlab,read"
},
{
"environment": "swig",
"SwigExePath": "D:/swigwin-4.1.1/swig.exe"
}
],
"configurations": [
@ -14,7 +19,7 @@
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg" ],
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg", "swig"],
"buildRoot": "${env.BuildDir}\\${name}",
"installRoot": "${env.InstallDir}\\${name}",
"cmakeCommandArgs": "",
@ -30,6 +35,21 @@
"name": "KICAD_WIN32_DPI_AWARE",
"value": "ON",
"type": "BOOL"
},
{
"name": "SWIG_EXECUTABLE",
"value": "${env.SwigExePath}",
"type": "STRING"
},
{
"name": "VCPKG_OVERLAY_TRIPLETS",
"value": "${workspaceRoot}/tools/custom_vcpkg_triplets",
"type": "STRING"
},
{
"name": "VCPKG_INSTALL_OPTIONS",
"value": "--x-abi-tools-use-exact-versions",
"type": "STRING"
}
],
"cmakeToolchain": "${env.VcPkgDir}/scripts/buildsystems/vcpkg.cmake"
@ -38,7 +58,7 @@
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg" ],
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg", "swig"],
"buildRoot": "${env.BuildDir}\\${name}",
"installRoot": "${env.InstallDir}\\${name}",
"cmakeCommandArgs": "",
@ -54,6 +74,21 @@
"name": "KICAD_WIN32_DPI_AWARE",
"value": "ON",
"type": "BOOL"
},
{
"name": "SWIG_EXECUTABLE",
"value": "${env.SwigExePath}",
"type": "STRING"
},
{
"name": "VCPKG_OVERLAY_TRIPLETS",
"value": "${workspaceRoot}/tools/custom_vcpkg_triplets",
"type": "STRING"
},
{
"name": "VCPKG_INSTALL_OPTIONS",
"value": "--x-abi-tools-use-exact-versions",
"type": "STRING"
}
],
"cmakeToolchain": "${env.VcPkgDir}/scripts/buildsystems/vcpkg.cmake"

View File

@ -24,6 +24,9 @@
#include "project_git_utils.h"
#include "git_backend.h"
#include <wx/string.h>
#include <string_utils.h>
namespace KIGIT
{
@ -43,4 +46,34 @@ bool PROJECT_GIT_UTILS::RemoveVCS( git_repository*& aRepo, const wxString& aProj
return GetGitBackend()->RemoveVCS( aRepo, aProjectPath, aRemoveGitDir, aErrors );
}
wxString PROJECT_GIT_UTILS::GetCurrentHash( const wxString& aProjectFile, bool aShort )
{
wxString result = wxT( "no hash" );
git_repository* repo = PROJECT_GIT_UTILS::GetRepositoryForFile( TO_UTF8( aProjectFile ) );
if( repo )
{
git_reference* head = nullptr;
if( git_repository_head( &head, repo ) == 0 )
{
const git_oid* oid = git_reference_target( head );
if( oid )
{
char buf[41];
size_t len = aShort ? 8 : 41;
git_oid_tostr( buf, len, oid );
result = wxString::FromUTF8( buf );
}
git_reference_free( head );
}
git_repository_free( repo );
}
return result;
}
} // namespace KIGIT

View File

@ -51,6 +51,16 @@ public:
*/
static int CreateBranch( git_repository* aRepo, const wxString& aBranchName );
/**
* Return the current HEAD commit hash for the repository containing aProjectFile.
*
* @param aProjectFile Absolute path to any file within the repository (typically the
* project file path).
* @param aShort If true, return the short (8 char) hash, otherwise full hash.
* @return wxString containing the hash or "no hash" if unavailable.
*/
static wxString GetCurrentHash( const wxString& aProjectFile, bool aShort );
/**
* Remove version control from a directory by freeing the repository and
* optionally removing the .git directory.

View File

@ -56,6 +56,7 @@ public:
m_FuseShapes( false ),
m_FillAllVias( false ),
m_OptimizeStep( true ),
m_ExtraPadThickness( true ),
m_Format( FORMAT::STEP ),
m_OutputFile()
{};
@ -98,6 +99,7 @@ public:
bool m_FuseShapes;
bool m_FillAllVias;
bool m_OptimizeStep;
bool m_ExtraPadThickness;
FORMAT m_Format;
wxString m_OutputFile;

View File

@ -36,6 +36,8 @@
#include <kiway.h>
#include <lockfile.h>
#include <macros.h>
#include <git/project_git_utils.h>
#include <git2.h>
#include <project.h>
#include <project/project_file.h>
#include <trace_helpers.h>
@ -45,6 +47,7 @@
#include <title_block.h>
PROJECT::PROJECT() :
m_readOnly( false ),
m_textVarsTicker( 0 ),
@ -85,6 +88,16 @@ bool PROJECT::TextVarResolver( wxString* aToken ) const
*aToken = TITLE_BLOCK::GetCurrentDate();
return true;
}
else if( aToken->IsSameAs( wxT( "VCSHASH" ) ) )
{
*aToken = KIGIT::PROJECT_GIT_UTILS::GetCurrentHash( GetProjectFullName(), false );
return true;
}
else if( aToken->IsSameAs( wxT( "VCSSHORTHASH" ) ) )
{
*aToken = KIGIT::PROJECT_GIT_UTILS::GetCurrentHash( GetProjectFullName(), true );
return true;
}
else if( GetTextVars().count( *aToken ) > 0 )
{
*aToken = GetTextVars().at( *aToken );

View File

@ -152,10 +152,10 @@ const wxAuiPaneInfo& defaultDesignBlocksPaneInfo( wxWindow* aWindow )
EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
APP_SETTINGS_BASE( "eeschema", eeschemaSchemaVersion ),
m_Appearance(),
m_AutoplaceFields(),
m_Drawing(),
m_FindReplaceExtra(),
m_Input(),
m_AutoplaceFields(),
m_Selection(),
m_PageSettings(),
m_AnnotatePanel(),
m_BomPanel(),
@ -163,8 +163,9 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
m_LibViewPanel(),
m_NetlistPanel(),
m_SymChooserPanel(),
m_FindReplaceExtra(),
m_ERCDialog(),
m_ImportGraphics(),
m_Selection(),
m_Simulator(),
m_RescueNeverShow( false )
{

View File

@ -1542,7 +1542,7 @@ std::vector<LIB_SYMBOL_UNIT> LIB_SYMBOL::GetUnitDrawItems()
#define REPORT( msg ) { if( aReporter ) aReporter->Report( msg ); }
#define ITEM_DESC( item ) ( item )->GetItemDescription( &unitsProvider, true )
#define ITEM_DESC( item ) ( item )->GetItemDescription( &unitsProvider, false )
int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aReporter ) const
{
@ -1586,35 +1586,34 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
return retv;
}
// Make sure shapes and pins are sorted. No need with fields as those are
// matched by id/name.
// Make sure shapes are sorted. No need with fields or pins as those are matched by id/name and number.
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> aShapes;
std::set<const SCH_ITEM*> aFields;
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> aPins;
std::set<const SCH_FIELD*> aFields;
std::set<const SCH_PIN*> aPins;
for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
{
if( it->Type() == SCH_SHAPE_T )
aShapes.insert( &(*it) );
else if( it->Type() == SCH_FIELD_T )
aFields.insert( &(*it) );
aFields.insert( static_cast<const SCH_FIELD*>( &(*it) ) );
else if( it->Type() == SCH_PIN_T )
aPins.insert( &(*it) );
aPins.insert( static_cast<const SCH_PIN*>( &(*it) ) );
}
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> bShapes;
std::set<const SCH_ITEM*> bFields;
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> bPins;
std::set<const SCH_FIELD*> bFields;
std::set<const SCH_PIN*> bPins;
for( auto it = aRhs.m_drawings.begin(); it != aRhs.m_drawings.end(); ++it )
{
if( it->Type() == SCH_SHAPE_T )
bShapes.insert( &(*it) );
else if( it->Type() == SCH_FIELD_T )
bFields.insert( &(*it) );
bFields.insert( static_cast<const SCH_FIELD*>( &(*it) ) );
else if( it->Type() == SCH_PIN_T )
bPins.insert( &(*it) );
bPins.insert( static_cast<const SCH_PIN*>( &(*it) ) );
}
if( int tmp = static_cast<int>( aShapes.size() - bShapes.size() ) )
@ -1632,7 +1631,9 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
if( int tmp2 = (*aIt)->compare( *(*bIt), aCompareFlags ) )
{
retv = tmp2;
REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
REPORT( wxString::Format( _( "Graphic item differs: %s; %s." ),
ITEM_DESC( *aIt ),
ITEM_DESC( *bIt ) ) );
if( !aReporter )
return retv;
@ -1640,46 +1641,48 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
}
}
if( int tmp = static_cast<int>( aPins.size() - bPins.size() ) )
for( const SCH_PIN* aPin : aPins )
{
retv = tmp;
REPORT( _( "Pin count differs." ) );
const SCH_PIN* bPin = aRhs.GetPin( aPin->GetNumber(), aPin->GetUnit(), aPin->GetBodyStyle() );
if( !aReporter )
return retv;
}
else
{
for( const SCH_ITEM* aPinItem : aPins )
if( !bPin )
{
const SCH_PIN* aPin = static_cast<const SCH_PIN*>( aPinItem );
const SCH_PIN* bPin = aRhs.GetPin( aPin->GetNumber(), aPin->GetUnit(),
aPin->GetBodyStyle() );
retv = 1;
REPORT( wxString::Format( _( "Extra pin in schematic symbol: %s." ), ITEM_DESC( aPin ) ) );
if( !bPin )
{
retv = 1;
REPORT( wxString::Format( _( "Pin %s not found." ), aPin->GetNumber() ) );
if( !aReporter )
return retv;
}
else if( int tmp = aPin->SCH_ITEM::compare( *bPin, aCompareFlags ) )
{
retv = tmp;
REPORT( wxString::Format( _( "Pin %s differs: %s; %s" ),
aPin->GetNumber(),
ITEM_DESC( aPin ),
ITEM_DESC( bPin ) ) );
if( !aReporter )
return retv;
}
else if( int tmp2 = aPinItem->compare( *bPin, aCompareFlags ) )
{
retv = tmp2;
REPORT( wxString::Format( _( "Pin %s differs." ), aPin->GetNumber() ) );
if( !aReporter )
return retv;
}
if( !aReporter )
return retv;
}
}
for( const SCH_ITEM* aFieldItem : aFields )
for( const SCH_PIN* bPin : bPins )
{
const SCH_PIN* aPin = aRhs.GetPin( bPin->GetNumber(), bPin->GetUnit(), bPin->GetBodyStyle() );
if( !aPin )
{
retv = 1;
REPORT( wxString::Format( _( "Missing pin in schematic symbol: %s." ), ITEM_DESC( bPin ) ) );
if( !aReporter )
return retv;
}
}
for( const SCH_FIELD* aField : aFields )
{
const SCH_FIELD* aField = static_cast<const SCH_FIELD*>( aFieldItem );
const SCH_FIELD* bField = nullptr;
int tmp = 0;
if( aField->IsMandatory() )
bField = aRhs.GetField( aField->GetId() );
@ -1687,33 +1690,49 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
bField = aRhs.GetField( aField->GetName() );
if( !bField )
tmp = 1;
else
tmp = aFieldItem->compare( *bField, aCompareFlags );
{
retv = 1;
REPORT( wxString::Format( _( "Extra field in schematic symbol: %s." ), ITEM_DESC( aField ) ) );
if( tmp )
if( !aReporter )
return retv;
}
else if( int tmp = aField->SCH_ITEM::compare( *bField, aCompareFlags ) )
{
retv = tmp;
REPORT( wxString::Format( _( "%s field differs." ), aField->GetName( false ) ) );
REPORT( wxString::Format( _( "Field '%s' differs: %s; %s." ),
aField->GetName( false ),
ITEM_DESC( aField ),
ITEM_DESC( bField ) ) );
if( !aReporter )
return retv;
}
}
if( int tmp = static_cast<int>( aFields.size() - bFields.size() ) )
for( const SCH_FIELD* bField : bFields )
{
retv = tmp;
REPORT( _( "Field count differs." ) );
const SCH_FIELD* aField = nullptr;
if( !aReporter )
return retv;
if( bField->IsMandatory() )
aField = aRhs.GetField( bField->GetId() );
else
aField = aRhs.GetField( bField->GetName() );
if( !aField )
{
retv = 1;
REPORT( wxString::Format( _( "Missing field in schematic symbol: %s." ), ITEM_DESC( bField ) ) );
if( !aReporter )
return retv;
}
}
if( int tmp = static_cast<int>( m_fpFilters.GetCount() - aRhs.m_fpFilters.GetCount() ) )
{
retv = tmp;
REPORT( _( "Footprint filters differs." ) );
REPORT( _( "Footprint filter count differs." ) );
if( !aReporter )
return retv;

View File

@ -974,9 +974,19 @@ void SCH_FIELD::CalcEdit( const VECTOR2I& aPosition )
wxString SCH_FIELD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
{
return wxString::Format( _( "Field %s '%s'" ),
UnescapeString( GetName() ),
aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
wxString content = aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() );
if( content.IsEmpty() )
{
return wxString::Format( _( "Field %s (empty)" ),
UnescapeString( GetName() ) );
}
else
{
return wxString::Format( _( "Field %s '%s'" ),
UnescapeString( GetName() ),
content );
}
}

View File

@ -1937,10 +1937,20 @@ wxString SCH_DIRECTIVE_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider
}
else
{
return wxString::Format( _( "Directive Label [%s %s]" ),
UnescapeString( m_fields[0].GetName() ),
aFull ? m_fields[0].GetShownText( false )
: KIUI::EllipsizeMenuText( m_fields[0].GetText() ) );
const SCH_FIELD& firstField = m_fields[0];
wxString content = aFull ? firstField.GetShownText( false ) : KIUI::EllipsizeMenuText( firstField.GetText() );
if( content.IsEmpty() )
{
return wxString::Format( _( "Directive Label [%s (empty)]" ),
UnescapeString( m_fields[0].GetName() ) );
}
else
{
return wxString::Format( _( "Directive Label [%s %s]" ),
UnescapeString( m_fields[0].GetName() ),
content );
}
}
}

View File

@ -1169,7 +1169,7 @@ wxString SCH_SHEET::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFu
{
const SCH_FIELD* sheetnameField = GetField( FIELD_T::SHEET_NAME );
return wxString::Format( _( "Hierarchical Sheet %s" ),
return wxString::Format( _( "Hierarchical Sheet '%s'" ),
aFull ? sheetnameField->GetShownText( false )
: KIUI::EllipsizeMenuText( sheetnameField->GetText() ) );
}

View File

@ -328,7 +328,7 @@ void SCH_SHEET_PIN::GetEndPoints( std::vector<DANGLING_END_ITEM>& aItemList )
wxString SCH_SHEET_PIN::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
{
return wxString::Format( _( "Hierarchical Sheet Pin %s" ),
return wxString::Format( _( "Hierarchical Sheet Pin '%s'" ),
aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
}

View File

@ -50,6 +50,7 @@
#define ARG_FUSE_SHAPES "--fuse-shapes"
#define ARG_FILL_ALL_VIAS "--fill-all-vias"
#define ARG_NO_OPTIMIZE_STEP "--no-optimize-step"
#define ARG_NO_EXTRA_PAD_THICKNESS "--no-extra-pad-thickness"
#define ARG_NET_FILTER "--net-filter"
#define ARG_FORMAT "--format"
#define ARG_VRML_UNITS "--units"
@ -165,6 +166,10 @@ CLI::PCB_EXPORT_3D_COMMAND::PCB_EXPORT_3D_COMMAND( const std::string& aNa
.help( UTF8STDSTR( _( "Don't cut via holes in conductor layers." ) ) )
.flag();
m_argParser.add_argument( ARG_NO_EXTRA_PAD_THICKNESS )
.help( UTF8STDSTR( _( "Disable extra pad thickness (pads will have normal thickness)" ) ) )
.flag();
m_argParser.add_argument( ARG_MIN_DISTANCE )
.default_value( std::string( "0.01mm" ) )
.help( UTF8STDSTR( _( "Minimum distance between points to treat them as separate "
@ -231,6 +236,7 @@ int CLI::PCB_EXPORT_3D_COMMAND::doPerform( KIWAY& aKiway )
params.m_ExportSoldermask = m_argParser.get<bool>( ARG_INCLUDE_SOLDERMASK );
params.m_FuseShapes = m_argParser.get<bool>( ARG_FUSE_SHAPES );
params.m_FillAllVias = m_argParser.get<bool>( ARG_FILL_ALL_VIAS );
params.m_ExtraPadThickness = !m_argParser.get<bool>( ARG_NO_EXTRA_PAD_THICKNESS );
params.m_BoardOnly = m_argParser.get<bool>( ARG_BOARD_ONLY );
params.m_NetFilter = From_UTF8( m_argParser.get<std::string>( ARG_NET_FILTER ).c_str() );
params.m_ComponentFilter =

View File

@ -120,6 +120,7 @@ set( PCBNEW_DIALOGS
dialogs/dialog_outset_items_base.cpp
dialogs/dialog_pad_properties.cpp
dialogs/dialog_pad_properties_base.cpp
dialogs/dialog_fp_edit_pad_table.cpp
dialogs/dialog_plot.cpp
dialogs/dialog_plot_base.cpp
dialogs/dialog_pns_diff_pair_dimensions.cpp

View File

@ -0,0 +1,689 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "dialog_fp_edit_pad_table.h"
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/dcclient.h>
#include <pcb_shape.h>
#include <widgets/wx_grid.h>
#include <widgets/grid_text_helpers.h>
#include <widgets/grid_combobox.h>
#include <base_units.h>
#include <units_provider.h>
#include <board.h>
#include <footprint.h>
#include <footprint_edit_frame.h>
// Helper to map shape string to PAD_SHAPE
static PAD_SHAPE ShapeFromString( const wxString& shape )
{
if( shape == _( "Oval" ) ) return PAD_SHAPE::OVAL;
if( shape == _( "Rectangle" ) ) return PAD_SHAPE::RECTANGLE;
if( shape == _( "Trapezoid" ) ) return PAD_SHAPE::TRAPEZOID;
if( shape == _( "Rounded rectangle" ) ) return PAD_SHAPE::ROUNDRECT;
if( shape == _( "Chamfered rectangle" ) ) return PAD_SHAPE::CHAMFERED_RECT;
if( shape == _( "Custom shape" ) ) return PAD_SHAPE::CUSTOM;
return PAD_SHAPE::CIRCLE;
}
DIALOG_FP_EDIT_PAD_TABLE::DIALOG_FP_EDIT_PAD_TABLE( PCB_BASE_FRAME* aParent, FOOTPRINT* aFootprint ) :
DIALOG_SHIM( (wxWindow*)aParent, wxID_ANY, _( "Pad Table" ), wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
m_grid( nullptr ),
m_footprint( aFootprint ),
m_unitsProvider( std::make_unique<UNITS_PROVIDER>( pcbIUScale, GetUserUnits() ) )
{
wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
m_grid = new WX_GRID( this, wxID_ANY );
m_grid->CreateGrid( 0, 11 );
m_grid->SetColLabelValue( COL_NUMBER, _( "Number" ) );
m_grid->SetColLabelValue( COL_TYPE, _( "Type" ) );
m_grid->SetColLabelValue( COL_SHAPE, _( "Shape" ) );
m_grid->SetColLabelValue( COL_POS_X, _( "X Position" ) );
m_grid->SetColLabelValue( COL_POS_Y, _( "Y Position" ) );
m_grid->SetColLabelValue( COL_SIZE_X, _( "Size X" ) );
m_grid->SetColLabelValue( COL_SIZE_Y, _( "Size Y" ) );
m_grid->SetColLabelValue( COL_DRILL_X, _( "Drill X" ) );
m_grid->SetColLabelValue( COL_DRILL_Y, _( "Drill Y" ) );
m_grid->SetColLabelValue( COL_P2D_LENGTH, _( "Pad->Die Length" ) );
m_grid->SetColLabelValue( COL_P2D_DELAY, _( "Pad->Die Delay" ) );
m_grid->EnableEditing( true );
wxGridCellAttr* attr;
// Type column editor (attribute)
attr = new wxGridCellAttr;
{
wxArrayString typeNames;
typeNames.push_back( _( "Through-hole" ) ); // PTH
typeNames.push_back( _( "SMD" ) ); // SMD
typeNames.push_back( _( "Connector" ) ); // CONN SMD? (use CONN?)
typeNames.push_back( _( "NPTH" ) ); // NPTH
typeNames.push_back( _( "Aperture" ) ); // inferred copper-less
attr->SetEditor( new GRID_CELL_COMBOBOX( typeNames ) );
}
m_grid->SetColAttr( COL_TYPE, attr );
attr = new wxGridCellAttr;
wxArrayString shapeNames;
shapeNames.push_back( _( "Circle" ) );
shapeNames.push_back( _( "Oval" ) );
shapeNames.push_back( _( "Rectangle" ) );
shapeNames.push_back( _( "Trapezoid" ) );
shapeNames.push_back( _( "Rounded rectangle" ) );
shapeNames.push_back( _( "Chamfered rectangle" ) );
shapeNames.push_back( _( "Custom shape" ) );
attr->SetEditor( new GRID_CELL_COMBOBOX( shapeNames ) );
m_grid->SetColAttr( COL_SHAPE, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_POS_X, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_POS_Y, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_SIZE_X, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_SIZE_Y, attr );
// Drill X
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_DRILL_X, attr );
// Drill Y
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_DRILL_Y, attr );
// Pad->Die Length
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_P2D_LENGTH, attr );
// Pad->Die Delay
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_P2D_DELAY, attr );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_X );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_Y );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_X );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_Y );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_X );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_Y );
m_grid->SetAutoEvalCols( { COL_POS_X, COL_POS_Y, COL_SIZE_X, COL_SIZE_Y, COL_DRILL_X, COL_DRILL_Y } );
topSizer->Add( m_grid, 1, wxEXPAND | wxALL, 5 );
wxStdDialogButtonSizer* buttons = new wxStdDialogButtonSizer();
buttons->AddButton( new wxButton( this, wxID_OK ) );
buttons->AddButton( new wxButton( this, wxID_CANCEL ) );
buttons->Realize();
topSizer->Add( buttons, 0, wxALIGN_RIGHT | wxALL, 5 );
SetSizerAndFit( topSizer );
CaptureOriginalPadState();
Populate();
// Bind cell change handlers for real-time updates
m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_FP_EDIT_PAD_TABLE::OnCellChanged, this );
m_grid->Bind( wxEVT_GRID_SELECT_CELL, &DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell, this );
// Listen for cancel
Bind(
wxEVT_BUTTON,
[this]( wxCommandEvent& aEvt )
{
m_cancelled = true;
aEvt.Skip();
},
wxID_CANCEL );
finishDialogSettings();
}
DIALOG_FP_EDIT_PAD_TABLE::~DIALOG_FP_EDIT_PAD_TABLE()
{
if( m_cancelled )
RestoreOriginalPadState();
}
void DIALOG_FP_EDIT_PAD_TABLE::Populate()
{
if( !m_footprint )
return;
int row = 0;
for( PAD* pad : m_footprint->Pads() )
{
m_grid->AppendRows( 1 );
m_grid->SetCellValue( row, COL_NUMBER, pad->GetNumber() );
// Pad attribute to string
wxString attrStr;
switch( pad->GetAttribute() )
{
case PAD_ATTRIB::PTH: attrStr = _( "Through-hole" ); break;
case PAD_ATTRIB::SMD: attrStr = _( "SMD" ); break;
case PAD_ATTRIB::CONN: attrStr = _( "Connector" ); break;
case PAD_ATTRIB::NPTH: attrStr = _( "NPTH" ); break;
default: attrStr = _( "Through-hole" ); break;
}
VECTOR2I size = pad->GetSize( PADSTACK::ALL_LAYERS );
if( pad->IsAperturePad() )
attrStr = _( "Aperture" );
m_grid->SetCellValue( row, COL_TYPE, attrStr );
m_grid->SetCellValue( row, COL_SHAPE, pad->ShowPadShape( PADSTACK::ALL_LAYERS ) );
m_grid->SetCellValue( row, COL_POS_X, m_unitsProvider->StringFromValue( pad->GetPosition().x, true ) );
m_grid->SetCellValue( row, COL_POS_Y, m_unitsProvider->StringFromValue( pad->GetPosition().y, true ) );
m_grid->SetCellValue( row, COL_SIZE_X, m_unitsProvider->StringFromValue( size.x, true ) );
m_grid->SetCellValue( row, COL_SIZE_Y, m_unitsProvider->StringFromValue( size.y, true ) );
// Drill values (only meaningful for PTH or NPTH). Leave empty otherwise.
if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
{
VECTOR2I drill = pad->GetDrillSize();
if( drill.x > 0 )
m_grid->SetCellValue( row, COL_DRILL_X, m_unitsProvider->StringFromValue( drill.x, true ) );
if( drill.y > 0 )
m_grid->SetCellValue( row, COL_DRILL_Y, m_unitsProvider->StringFromValue( drill.y, true ) );
}
else
m_grid->SetCellValue( row, COL_SIZE_X, m_unitsProvide
{
m_grid->SetReadOnly( row, COL_DRILL_X, true );
m_grid->SetReadOnly( row, COL_DRILL_Y, true );
}
// Pad to die metrics
if( pad->GetPadToDieLength() )
m_grid->SetCellValue( row, COL_P2D_LENGTH, m_unitsProvider->StringFromValue( pad->GetPadToDieLength(), true ) );
if( pad->GetPadToDieDelay() )
m_grid->SetCellValue( row, COL_P2D_DELAY, wxString::Format( "%d", pad->GetPadToDieDelay() ) );
row++;
}
// Hide row labels per requirements
m_grid->HideRowLabels();
// Auto size the data columns first to get reasonable initial widths
m_grid->AutoSizeColumns();
// Ensure the Shape column (index 1) is wide enough for the longest translated
// shape text plus the dropdown arrow / padding. We compute a max text width
// using a device context and add a platform neutral padding.
{
wxClientDC dc( m_grid );
dc.SetFont( m_grid->GetFont() );
wxArrayString shapeNames;
shapeNames.push_back( _( "Circle" ) );
shapeNames.push_back( _( "Oval" ) );
shapeNames.push_back( _( "Rectangle" ) );
shapeNames.push_back( _( "Trapezoid" ) );
shapeNames.push_back( _( "Rounded rectangle" ) );
shapeNames.push_back( _( "Chamfered rectangle" ) );
shapeNames.push_back( _( "Custom shape" ) );
int maxWidth = 0;
for( const wxString& str : shapeNames )
{
int w, h;
dc.GetTextExtent( str, &w, &h );
maxWidth = std::max( maxWidth, w );
}
// Add padding for internal cell margins + dropdown control.
int padding = FromDIP( 30 ); // heuristic: 2*margin + arrow button
m_grid->SetColSize( COL_SHAPE, maxWidth + padding );
}
// Record initial proportions for proportional resizing later.
InitColumnProportions();
Bind( wxEVT_SIZE, &DIALOG_FP_EDIT_PAD_TABLE::OnSize, this );
// Run an initial proportional resize using current client size so columns
// respect proportions immediately.
wxSizeEvent sizeEvt( GetSize(), GetId() );
CallAfter(
[this, sizeEvt]
{
wxSizeEvent evt( sizeEvt );
this->OnSize( evt );
} );
// If pads exist, select the first row to show initial highlight
if( m_grid->GetNumberRows() > 0 )
{
m_grid->SetGridCursor( 0, 0 );
// Construct event with required parameters (id, type, obj, row, col,...)
wxGridEvent ev( m_grid->GetId(), wxEVT_GRID_SELECT_CELL, m_grid, 0, 0, -1, -1, true );
OnSelectCell( ev );
}
}
void DIALOG_FP_EDIT_PAD_TABLE::CaptureOriginalPadState()
{
m_originalPads.clear();
if( !m_footprint )
return;
for( PAD* pad : m_footprint->Pads() )
{
PAD_SNAPSHOT snap;
snap.number = pad->GetNumber();
snap.shape = pad->GetShape( PADSTACK::ALL_LAYERS );
snap.position = pad->GetPosition();
snap.size = pad->GetSize( PADSTACK::ALL_LAYERS );
snap.attribute = pad->GetAttribute();
snap.drillSize = pad->GetDrillSize();
snap.padToDieLength= pad->GetPadToDieLength();
snap.padToDieDelay = pad->GetPadToDieDelay();
m_originalPads.push_back( snap );
}
}
void DIALOG_FP_EDIT_PAD_TABLE::RestoreOriginalPadState()
{
if( !m_footprint )
return;
size_t idx = 0;
PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
for( PAD* pad : m_footprint->Pads() )
{
if( idx >= m_originalPads.size() )
break;
const PAD_SNAPSHOT& snap = m_originalPads[idx++];
pad->SetNumber( snap.number );
pad->SetShape( PADSTACK::ALL_LAYERS, snap.shape );
pad->SetAttribute( snap.attribute );
pad->SetPosition( snap.position );
pad->SetSize( PADSTACK::ALL_LAYERS, snap.size );
pad->SetDrillSize( snap.drillSize );
pad->SetPadToDieLength( snap.padToDieLength );
pad->SetPadToDieDelay( snap.padToDieDelay );
pad->ClearBrightened();
if( canvas )
canvas->GetView()->Update( pad, KIGFX::REPAINT );
}
if( canvas )
{
canvas->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
canvas->ForceRefresh();
}
}
bool DIALOG_FP_EDIT_PAD_TABLE::TransferDataFromWindow()
{
if( !m_grid->CommitPendingChanges() )
return false;
if( !m_footprint )
return true;
int row = 0;
for( PAD* pad : m_footprint->Pads() )
{
pad->SetNumber( m_grid->GetCellValue( row, COL_NUMBER ) );
wxString typeStr = m_grid->GetCellValue( row, COL_TYPE );
if( typeStr == _( "Through-hole" ) )
pad->SetAttribute( PAD_ATTRIB::PTH );
else if( typeStr == _( "SMD" ) )
pad->SetAttribute( PAD_ATTRIB::SMD );
else if( typeStr == _( "Connector" ) )
pad->SetAttribute( PAD_ATTRIB::CONN );
else if( typeStr == _( "NPTH" ) )
pad->SetAttribute( PAD_ATTRIB::NPTH );
// Aperture derived by copper-less layers; do not overwrite attribute here.
wxString shape = m_grid->GetCellValue( row, COL_SHAPE );
PAD_SHAPE newShape = ShapeFromString( shape );
pad->SetShape( PADSTACK::ALL_LAYERS, newShape );
VECTOR2I pos;
pos.x = m_grid->GetUnitValue( row, COL_POS_X );
pos.y = m_grid->GetUnitValue( row, COL_POS_Y );
pad->SetPosition( pos );
VECTOR2I size;
size.x = m_grid->GetUnitValue( row, COL_SIZE_X );
size.y = m_grid->GetUnitValue( row, COL_SIZE_Y );
pad->SetSize( PADSTACK::ALL_LAYERS, size );
// Drill sizes (only if attribute allows)
if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
{
int dx = m_grid->GetUnitValue( row, COL_DRILL_X );
int dy = m_grid->GetUnitValue( row, COL_DRILL_Y );
if( dx > 0 || dy > 0 )
{
if( dx <= 0 ) dx = dy;
if( dy <= 0 ) dy = dx;
pad->SetDrillSize( { dx, dy } );
}
}
// Pad->Die
wxString delayStr = m_grid->GetCellValue( row, COL_P2D_DELAY );
wxString lenStr = m_grid->GetCellValue( row, COL_P2D_LENGTH );
if( !lenStr.IsEmpty() )
pad->SetPadToDieLength( m_grid->GetUnitValue( row, COL_P2D_LENGTH ) );
else
pad->SetPadToDieLength( 0 );
if( !delayStr.IsEmpty() )
{
long delayVal;
if( delayStr.ToLong( &delayVal ) )
pad->SetPadToDieDelay( (int) delayVal );
else
pad->SetPadToDieDelay( 0 );
}
else
pad->SetPadToDieDelay( 0 );
row++;
}
return true;
}
void DIALOG_FP_EDIT_PAD_TABLE::InitColumnProportions()
{
m_colProportions.clear();
m_minColWidths.clear();
if( !m_grid )
return;
// Only consider the actual data columns (all of them since row labels are hidden)
int cols = m_grid->GetNumberCols();
int total = 0;
std::vector<int> widths;
widths.reserve( cols );
for( int c = 0; c < cols; ++c )
{
int w = m_grid->GetColSize( c );
widths.push_back( w );
total += w;
}
if( total <= 0 )
return;
for( int w : widths )
{
m_colProportions.push_back( (double) w / (double) total );
m_minColWidths.push_back( w );
}
}
void DIALOG_FP_EDIT_PAD_TABLE::OnSize( wxSizeEvent& aEvent )
{
if( m_colProportions.empty() )
{
aEvent.Skip();
return;
}
// Compute available total width for columns and resize keeping proportions.
int cols = m_grid->GetNumberCols();
int available = 0;
for( int c = 0; c < cols; ++c )
available += m_grid->GetColSize( c );
// Use client size of grid minus scrollbar estimate to better distribute.
int clientW = m_grid->GetClientSize().x;
if( clientW > 0 )
available = clientW; // prefer actual client width
int used = 0;
for( int c = 0; c < cols; ++c )
{
int target = (int) std::round( m_colProportions[c] * available );
target = std::max( target, m_minColWidths[c] );
// Defer last column to absorb rounding diff.
if( c == cols - 1 )
target = std::max( available - used, m_minColWidths[c] );
m_grid->SetColSize( c, target );
used += target;
}
aEvent.Skip();
}
void DIALOG_FP_EDIT_PAD_TABLE::OnCellChanged( wxGridEvent& aEvent )
{
int row = aEvent.GetRow();
int col = aEvent.GetCol();
if( !m_footprint )
return;
// row -> pad mapping is current order of Pads() iteration; rebuild each time (pads count small)
int idx = 0;
PAD* target = nullptr;
for( PAD* pad : m_footprint->Pads() )
{
if( idx == row ) { target = pad; break; }
++idx;
}
if( !target )
return;
bool needCanvasRefresh = false;
switch( col )
{
case COL_NUMBER:
target->SetNumber( m_grid->GetCellValue( row, col ) );
break;
case COL_TYPE:
{
wxString typeStr = m_grid->GetCellValue( row, col );
PAD_ATTRIB newAttr = target->GetAttribute();
if( typeStr == _( "Through-hole" ) )
newAttr = PAD_ATTRIB::PTH;
else if( typeStr == _( "SMD" ) )
newAttr = PAD_ATTRIB::SMD;
else if( typeStr == _( "Connector" ) )
newAttr = PAD_ATTRIB::CONN;
else if( typeStr == _( "NPTH" ) )
newAttr = PAD_ATTRIB::NPTH;
if( newAttr != target->GetAttribute() )
{
target->SetAttribute( newAttr );
// Toggle drill columns read-only state dynamically.
bool drillsEditable = ( newAttr == PAD_ATTRIB::PTH || newAttr == PAD_ATTRIB::NPTH );
m_grid->SetReadOnly( row, COL_DRILL_X, !drillsEditable );
m_grid->SetReadOnly( row, COL_DRILL_Y, !drillsEditable );
needCanvasRefresh = true;
}
break;
}
case COL_SHAPE:
target->SetShape( PADSTACK::ALL_LAYERS, ShapeFromString( m_grid->GetCellValue( row, col ) ) );
needCanvasRefresh = true;
break;
case COL_POS_X:
case COL_POS_Y:
{
VECTOR2I pos = target->GetPosition();
if( col == COL_POS_X )
pos.x = m_grid->GetUnitValue( row, col );
else
pos.y = m_grid->GetUnitValue( row, col );
target->SetPosition( pos );
needCanvasRefresh = true;
break;
}
case COL_SIZE_X:
case COL_SIZE_Y:
{
VECTOR2I size = target->GetSize( PADSTACK::ALL_LAYERS );
if( col == COL_SIZE_X )
size.x = m_grid->GetUnitValue( row, col );
else
size.y = m_grid->GetUnitValue( row, col );
target->SetSize( PADSTACK::ALL_LAYERS, size );
needCanvasRefresh = true;
break;
}
case COL_DRILL_X:
case COL_DRILL_Y:
{
if( target->GetAttribute() == PAD_ATTRIB::PTH || target->GetAttribute() == PAD_ATTRIB::NPTH )
{
int dx = m_grid->GetUnitValue( row, COL_DRILL_X );
int dy = m_grid->GetUnitValue( row, COL_DRILL_Y );
if( dx > 0 || dy > 0 )
{
if( dx <= 0 ) dx = dy;
if( dy <= 0 ) dy = dx;
target->SetDrillSize( { dx, dy } );
needCanvasRefresh = true;
}
}
break;
}
case COL_P2D_LENGTH:
if( !m_grid->GetCellValue( row, col ).IsEmpty() )
target->SetPadToDieLength( m_grid->GetUnitValue( row, col ) );
break;
case COL_P2D_DELAY:
{
wxString d = m_grid->GetCellValue( row, col );
long val;
if( d.ToLong( &val ) )
target->SetPadToDieDelay( (int) val );
break;
}
default:
break;
}
// Request redraw (simple approach)
target->SetDirty();
if( needCanvasRefresh )
{
if( PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() ) )
base->GetCanvas()->ForceRefresh();
}
}
void DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell( wxGridEvent& aEvent )
{
int row = aEvent.GetRow();
if( !m_footprint )
return;
PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
// Clear existing pad selections
for( PAD* pad : m_footprint->Pads() )
{
if( pad->IsBrightened() )
{
pad->ClearBrightened();
if( canvas )
canvas->GetView()->Update( pad, KIGFX::REPAINT );
}
}
int idx = 0;
for( PAD* pad : m_footprint->Pads() )
{
if( idx == row )
{
pad->SetBrightened();
if( canvas )
{
canvas->GetView()->Update( pad, KIGFX::REPAINT );
canvas->ForceRefresh();
}
break;
}
++idx;
}
}

View File

@ -0,0 +1,59 @@
#pragma once
#include "dialog_shim.h"
#include <memory>
#include <vector>
#include <pad.h>
class PCB_BASE_FRAME;
class FOOTPRINT;
class WX_GRID;
class UNITS_PROVIDER;
class DIALOG_FP_EDIT_PAD_TABLE : public DIALOG_SHIM
{
public:
DIALOG_FP_EDIT_PAD_TABLE( PCB_BASE_FRAME* aParent, FOOTPRINT* aFootprint );
~DIALOG_FP_EDIT_PAD_TABLE() override;
bool TransferDataFromWindow() override;
private:
void Populate();
void OnSize( wxSizeEvent& aEvent );
// Proportional resize support
std::vector<double> m_colProportions; // relative widths captured after init
std::vector<int> m_minColWidths; // initial (minimum) widths
void InitColumnProportions();
struct PAD_SNAPSHOT
{
wxString number;
PAD_SHAPE shape;
VECTOR2I position;
VECTOR2I size;
PAD_ATTRIB attribute;
VECTOR2I drillSize;
int padToDieLength = 0;
int padToDieDelay = 0;
};
std::vector<PAD_SNAPSHOT> m_originalPads; // original pad data for cancel rollback
bool m_cancelled = false; // set if user hit cancel
void CaptureOriginalPadState();
void RestoreOriginalPadState();
void OnCellChanged( wxGridEvent& aEvent );
void OnSelectCell( wxGridEvent& aEvent );
// Column indices (after adding Type column)
enum COLS { COL_NUMBER = 0, COL_TYPE, COL_SHAPE, COL_POS_X, COL_POS_Y, COL_SIZE_X, COL_SIZE_Y,
COL_DRILL_X, COL_DRILL_Y, COL_P2D_LENGTH, COL_P2D_DELAY };
WX_GRID* m_grid;
FOOTPRINT* m_footprint;
std::unique_ptr<UNITS_PROVIDER> m_unitsProvider;
};

View File

@ -542,7 +542,8 @@ void DRC_ENGINE::compileRules()
}
if( error_semaphore.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name, rule->m_Condition->GetExpression(), 0, 0 );
THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name,
TO_UTF8( rule->m_Condition->GetExpression() ), 0, 0 );
for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
{

View File

@ -725,6 +725,7 @@ bool EXPORTER_STEP::buildBoard3DShapes()
m_pcbModel->SetEnabledLayers( m_layersToExport );
m_pcbModel->SetFuseShapes( m_params.m_FuseShapes );
m_pcbModel->SetNetFilter( m_params.m_NetFilter );
m_pcbModel->SetExtraPadThickness( m_params.m_ExtraPadThickness );
// Note: m_params.m_BoardOutlinesChainingEpsilon is used only to build the board outlines,
// not to set OCC chaining epsilon (much smaller)

View File

@ -786,6 +786,7 @@ STEP_PCB_MODEL::STEP_PCB_MODEL( const wxString& aPcbName, REPORTER* aReporter )
m_minx = 1.0e10; // absurdly large number; any valid PCB X value will be smaller
m_pcbName = aPcbName;
m_fuseShapes = false;
m_extraPadThickness = true;
m_outFmt = OUTPUT_FORMAT::FMT_OUT_UNKNOWN;
}
@ -819,7 +820,7 @@ bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin, bool
double Zpos, thickness;
getLayerZPlacement( pcb_layer, Zpos, thickness );
if( !aVia )
if( !aVia && m_extraPadThickness )
{
// Pad surface as a separate face for FEM simulations.
if( pcb_layer == F_Cu )
@ -883,7 +884,7 @@ bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin, bool
getLayerZPlacement( F_Cu, f_pos, f_thickness );
getLayerZPlacement( B_Cu, b_pos, b_thickness );
if( !aVia )
if( !aVia && m_extraPadThickness )
{
// Pad surface is slightly thicker
f_thickness += c_padExtraThickness;
@ -979,7 +980,7 @@ bool STEP_PCB_MODEL::AddHole( const SHAPE_SEGMENT& aShape, int aPlatingThickness
// must be > OCC_MAX_DISTANCE_TO_MERGE_POINTS
// Pads are taller by 0.01 mm
if( !aVia )
if( !aVia && m_extraPadThickness)
margin += 0.01;
double f_pos, f_thickness;
@ -1295,6 +1296,12 @@ void STEP_PCB_MODEL::SetNetFilter( const wxString& aFilter )
}
void STEP_PCB_MODEL::SetExtraPadThickness( bool aValue )
{
m_extraPadThickness = aValue;
}
void STEP_PCB_MODEL::SetCopperColor( double r, double g, double b )
{
m_copperColor[0] = r;

View File

@ -128,6 +128,7 @@ public:
void SetSimplifyShapes( bool aValue );
void SetStackup( const BOARD_STACKUP& aStackup );
void SetNetFilter( const wxString& aFilter );
void SetExtraPadThickness( bool aValue );
// Set the max distance (in mm) to consider 2 points have the same coordinates
// and can be merged
@ -260,6 +261,7 @@ private:
bool m_hasPCB; // set true if CreatePCB() has been invoked
bool m_simplifyShapes; // convert parts of outlines to arcs where possible
bool m_fuseShapes; // fuse geometry together
bool m_extraPadThickness; // add extra thickness to pads equal to copper thickness
std::vector<TDF_Label> m_pcb_labels; // labels for the PCB model (one by main outline)
MODEL_MAP m_models; // map of file names to model labels
int m_components; // number of successfully loaded components;

View File

@ -1421,6 +1421,7 @@ void FOOTPRINT_EDIT_FRAME::setupUIConditions()
mgr->SetConditions( PCB_ACTIONS::placeImportedGraphics, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::footprintProperties, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::padTable, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::editTextAndGraphics, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::checkFootprint, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::repairFootprint, ENABLE( haveFootprintCond ) );

View File

@ -119,6 +119,7 @@ void FOOTPRINT_EDIT_FRAME::doReCreateMenuBar()
editMenu->AppendSeparator();
editMenu->Add( PCB_ACTIONS::editTextAndGraphics );
editMenu->Add( PCB_ACTIONS::padTable );
editMenu->Add( PCB_ACTIONS::defaultPadProperties );
editMenu->Add( PCB_ACTIONS::enumeratePads );
editMenu->Add( ACTIONS::gridOrigin );

View File

@ -166,6 +166,7 @@ std::optional<TOOLBAR_CONFIGURATION> FOOTPRINT_EDIT_TOOLBAR_SETTINGS::DefaultToo
config.AppendSeparator()
.AppendAction( PCB_ACTIONS::footprintProperties )
.AppendAction( PCB_ACTIONS::padTable )
.AppendAction( PCB_ACTIONS::defaultPadProperties )
.AppendAction( ACTIONS::showDatasheet )
.AppendAction( PCB_ACTIONS::checkFootprint );

View File

@ -37,6 +37,7 @@
#include <pcbnew_settings.h>
#include <board_commit.h>
#include <dialogs/dialog_push_pad_properties.h>
#include <dialogs/dialog_fp_edit_pad_table.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_grid_helper.h>
#include <tools/pcb_selection_tool.h>
@ -109,6 +110,7 @@ bool PAD_TOOL::Init()
if( m_isFootprintEditor )
{
menu.AddItem( PCB_ACTIONS::padTable, SELECTION_CONDITIONS::ShowAlways, 400 );
menu.AddItem( PCB_ACTIONS::enumeratePads, SELECTION_CONDITIONS::ShowAlways, 400 );
menu.AddItem( PCB_ACTIONS::recombinePad, recombineCondition, 400 );
menu.AddItem( PCB_ACTIONS::explodePad, explodeCondition, 400 );
@ -911,6 +913,22 @@ std::vector<PCB_SHAPE*> PAD_TOOL::RecombinePad( PAD* aPad, bool aIsDryRun )
return aPad->Recombine( aIsDryRun, maxError );
}
int PAD_TOOL::PadTable( const TOOL_EVENT& aEvent )
{
if( !m_isFootprintEditor || InPadEditMode() )
return 0;
FOOTPRINT* footprint = board()->GetFirstFootprint();
if( !footprint )
return 0;
DIALOG_FP_EDIT_PAD_TABLE dlg( frame(), footprint );
dlg.ShowQuasiModal();
return 0;
}
void PAD_TOOL::setTransitions()
{
@ -920,6 +938,7 @@ void PAD_TOOL::setTransitions()
Go( &PAD_TOOL::PlacePad, PCB_ACTIONS::placePad.MakeEvent() );
Go( &PAD_TOOL::EnumeratePads, PCB_ACTIONS::enumeratePads.MakeEvent() );
Go( &PAD_TOOL::PadTable, PCB_ACTIONS::padTable.MakeEvent() );
Go( &PAD_TOOL::EditPad, PCB_ACTIONS::explodePad.MakeEvent() );
Go( &PAD_TOOL::EditPad, PCB_ACTIONS::recombinePad.MakeEvent() );

View File

@ -46,6 +46,8 @@ public:
*/
int EnumeratePads( const TOOL_EVENT& aEvent );
int PadTable( const TOOL_EVENT& aEvent );
/**
* Place a pad in footprint editor.
*/

View File

@ -899,6 +899,13 @@ TOOL_ACTION PCB_ACTIONS::footprintProperties( TOOL_ACTION_ARGS()
.FriendlyName( _( "Footprint Properties..." ) )
.Icon( BITMAPS::module_options ) );
TOOL_ACTION PCB_ACTIONS::padTable( TOOL_ACTION_ARGS()
.Name( "pcbnew.ModuleEditor.padTable" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Pad Table..." ) )
.Tooltip( _( "Displays pad table for bulk editing of pads" ) )
.Icon( BITMAPS::pin_table ) );
TOOL_ACTION PCB_ACTIONS::checkFootprint( TOOL_ACTION_ARGS()
.Name( "pcbnew.ModuleEditor.checkFootprint" )
.Scope( AS_GLOBAL )

View File

@ -488,6 +488,7 @@ public:
static TOOL_ACTION footprintProperties;
static TOOL_ACTION defaultPadProperties;
static TOOL_ACTION padTable;
static TOOL_ACTION checkFootprint;

View File

@ -0,0 +1,3 @@
include("${VCPKG_ROOT_DIR}/triplets/x64-windows.cmake")
set(VCPKG_DISABLE_COMPILER_TRACKING ON)