7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-03-30 06:26:55 +00:00

Add new DRC expression function isMemberOfSheetOrChildren

This allows the multichannel tool to search for footprints that are in nested
hierarchical sheets.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/20339
This commit is contained in:
JamesJCode 2025-03-17 15:54:15 +00:00
parent d0518c96b2
commit 4a99a3060d
4 changed files with 187 additions and 108 deletions

View File

@ -1,108 +1,118 @@
// Do not edit this file, it is autogenerated by CMake from the .md file
_HKI( "### Expression functions\n"
"\n"
"All function parameters support simple wildcards (`*` and `?`).\n"
"<br><br>\n"
"\n"
" A.enclosedByArea('<zone_name>')\n"
"True if all of `A` lies within the given zone's outline.\n"
"\n"
"NB: this is potentially a more expensive call than `intersectsArea()`. Use `intersectsArea()`\n"
"where possible.\n"
"<br><br>\n"
"\n"
" A.existsOnLayer('<layer_name>')\n"
"True if `A` exists on the given layer. The layer name can be\n"
"either the name assigned in Board Setup > Board Editor Layers or\n"
"the canonical name (ie: `F.Cu`).\n"
"\n"
"NB: this returns true if `A` is on the given layer, independently\n"
"of whether or not the rule is being evaluated for that layer.\n"
"For the latter use a `(layer \"layer_name\")` clause in the rule.\n"
"<br><br>\n"
"\n"
" A.fromTo('x', 'y')\n"
"True if the object exists on the copper path between the given \n"
"pads. `x` and `y` are the full names of pads in the design, such as \n"
"`R1-Pad1`.\n"
"<br><br>\n"
"\n"
" A.getField('<field_name>')\n"
"The value of the given field. Only footprints have fields, so a field is only returned if\n"
"`A` is a footprint.\n"
"<br><br>\n"
"\n"
" A.hasComponentClass('<component_class_name>')\n"
"True if the set of component classes assigned to `A` contains the named \n"
"component class.\n"
"<br><br>\n"
"\n"
" A.hasNetclass('<netclass_name>')\n"
"True if `A` has had the given netclass assigned to it, either by an explicit netclass label\n"
"or through a pattern match assignment.\n"
"<br><br>\n"
"\n"
" A.inDiffPair('<net_name>')\n"
"True if `A` has a net that is part of the specified differential pair.\n"
"`<net_name>` is the base name of the differential pair. For example, `inDiffPair('/CLK')`\n"
"matches items in the `/CLK_P` and `/CLK_N` nets.\n"
"<br><br>\n"
"\n"
" A.intersectsArea('<zone_name>')\n"
"True if any part of `A` lies within the given zone's outline.\n"
"<br><br>\n"
"\n"
" A.intersectsCourtyard('<footprint_identifier>')\n"
"True if any part of `A` lies within the given footprint's principal courtyard.\n"
"<br><br>\n"
"\n"
" A.intersectsFrontCourtyard('<footprint_identifier>')\n"
"True if any part of `A` lies within the given footprint's front courtyard.\n"
"<br><br>\n"
"\n"
" A.intersectsBackCourtyard('<footprint_identifier>')\n"
"True if any part of `A` lies within the given footprint's back courtyard.\n"
"<br><br>\n"
"\n"
"The `footprint_identifier` listed above can be one of the following:\n"
"\n"
"1. A reference designator, possibly containing wildcards `*` and `?`\n"
"2. A footprint library identifier such as `LibName:FootprintName`. In this case,\n"
" the library identifier must contain the `:` character to separate the library\n"
" name from the footprint name, and either name may contain wildcards.\n"
"3. A component class, in the form `${Class:ClassName}`. The keyword `Class` is not\n"
" case-sensitive, but component class names are case-sensitive.\n"
"\n"
"<br>\n"
"\n"
" A.isBlindBuriedVia()\n"
"True if `A` is a blind/buried via.\n"
"<br><br>\n"
"\n"
" AB.isCoupledDiffPair()\n"
"True if `A` and `B` are members of the same diff pair.\n"
"<br><br>\n"
"\n"
" A.isMicroVia()\n"
"True if `A` is a microvia.\n"
"<br><br>\n"
"\n"
" A.isPlated()\n"
"True if `A` has a hole which is plated.\n"
"<br><br>\n"
"\n"
" A.memberOfGroup('<group_name>')\n"
"True if `A` is a member of the given group. The name can contain wildcards.\n"
"Includes nested membership.\n"
"<br><br>\n"
"\n"
" A.memberOfFootprint('<footprint_identifier>')\n"
"True if `A` is a member of a given footprint (for example, a pad or graphic shape defined\n"
"inside that footprint). The various ways of specifying `footprint_identifier` are described above.\n"
"<br><br>\n"
"\n"
" A.memberOfSheet('<sheet_path>')\n"
"True if `A` is a member of the given schematic sheet. The sheet path can contain wildcards.\n"
"<br><br>\n"
"\n"
"" );
"\n"
"All function parameters support simple wildcards (`*` and `?`).\n"
"<br><br>\n"
"\n"
" A.enclosedByArea('<zone_name>')\n"
"True if all of `A` lies within the given zone's outline.\n"
"\n"
"NB: this is potentially a more expensive call than `intersectsArea()`. Use "
"`intersectsArea()`\n"
"where possible.\n"
"<br><br>\n"
"\n"
" A.existsOnLayer('<layer_name>')\n"
"True if `A` exists on the given layer. The layer name can be\n"
"either the name assigned in Board Setup > Board Editor Layers or\n"
"the canonical name (ie: `F.Cu`).\n"
"\n"
"NB: this returns true if `A` is on the given layer, independently\n"
"of whether or not the rule is being evaluated for that layer.\n"
"For the latter use a `(layer \"layer_name\")` clause in the rule.\n"
"<br><br>\n"
"\n"
" A.fromTo('x', 'y')\n"
"True if the object exists on the copper path between the given \n"
"pads. `x` and `y` are the full names of pads in the design, such as \n"
"`R1-Pad1`.\n"
"<br><br>\n"
"\n"
" A.getField('<field_name>')\n"
"The value of the given field. Only footprints have fields, so a field is only returned if\n"
"`A` is a footprint.\n"
"<br><br>\n"
"\n"
" A.hasComponentClass('<component_class_name>')\n"
"True if the set of component classes assigned to `A` contains the named \n"
"component class.\n"
"<br><br>\n"
"\n"
" A.hasNetclass('<netclass_name>')\n"
"True if `A` has had the given netclass assigned to it, either by an explicit netclass "
"label\n"
"or through a pattern match assignment.\n"
"<br><br>\n"
"\n"
" A.inDiffPair('<net_name>')\n"
"True if `A` has a net that is part of the specified differential pair.\n"
"`<net_name>` is the base name of the differential pair. For example, `inDiffPair('/CLK')`\n"
"matches items in the `/CLK_P` and `/CLK_N` nets.\n"
"<br><br>\n"
"\n"
" A.intersectsArea('<zone_name>')\n"
"True if any part of `A` lies within the given zone's outline.\n"
"<br><br>\n"
"\n"
" A.intersectsCourtyard('<footprint_identifier>')\n"
"True if any part of `A` lies within the given footprint's principal courtyard.\n"
"<br><br>\n"
"\n"
" A.intersectsFrontCourtyard('<footprint_identifier>')\n"
"True if any part of `A` lies within the given footprint's front courtyard.\n"
"<br><br>\n"
"\n"
" A.intersectsBackCourtyard('<footprint_identifier>')\n"
"True if any part of `A` lies within the given footprint's back courtyard.\n"
"<br><br>\n"
"\n"
"The `footprint_identifier` listed above can be one of the following:\n"
"\n"
"1. A reference designator, possibly containing wildcards `*` and `?`\n"
"2. A footprint library identifier such as `LibName:FootprintName`. In this case,\n"
" the library identifier must contain the `:` character to separate the library\n"
" name from the footprint name, and either name may contain wildcards.\n"
"3. A component class, in the form `${Class:ClassName}`. The keyword `Class` is not\n"
" case-sensitive, but component class names are case-sensitive.\n"
"\n"
"<br>\n"
"\n"
" A.isBlindBuriedVia()\n"
"True if `A` is a blind/buried via.\n"
"<br><br>\n"
"\n"
" AB.isCoupledDiffPair()\n"
"True if `A` and `B` are members of the same diff pair.\n"
"<br><br>\n"
"\n"
" A.isMicroVia()\n"
"True if `A` is a microvia.\n"
"<br><br>\n"
"\n"
" A.isPlated()\n"
"True if `A` has a hole which is plated.\n"
"<br><br>\n"
"\n"
" A.memberOfGroup('<group_name>')\n"
"True if `A` is a member of the given group. The name can contain wildcards.\n"
"Includes nested membership.\n"
"<br><br>\n"
"\n"
" A.memberOfFootprint('<footprint_identifier>')\n"
"True if `A` is a member of a given footprint (for example, a pad or graphic shape defined\n"
"inside that footprint). The various ways of specifying `footprint_identifier` are "
"described above.\n"
"<br><br>\n"
"\n"
" A.memberOfSheet('<sheet_path>')\n"
"True if `A` is a member of the given schematic sheet. The sheet path can contain "
"wildcards.\n"
"<br><br>\n"
"\n"
" A.memberOfSheetOrChildren('<sheet_path>')\n"
"True if `A` is a member of the given schematic sheet, or any of its child hierarchical "
"sheets. The sheet path can \n"
"contain wildcards.\n"
"<br><br>\n"
"\n"
"" );

View File

@ -102,5 +102,10 @@ inside that footprint). The various ways of specifying `footprint_identifier` a
A.memberOfSheet('<sheet_path>')
True if `A` is a member of the given schematic sheet. The sheet path can contain wildcards.
<br><br>
A.memberOfSheetOrChildren('<sheet_path>')
True if `A` is a member of the given schematic sheet, or any of its child hierarchical sheets. The sheet path can
contain wildcards.
<br><br>

View File

@ -995,6 +995,69 @@ static void memberOfSheetFunc( LIBEVAL::CONTEXT* aCtx, void* self )
}
static void memberOfSheetOrChildrenFunc( LIBEVAL::CONTEXT* aCtx, void* self )
{
LIBEVAL::VALUE* arg = aCtx->Pop();
LIBEVAL::VALUE* result = aCtx->AllocValue();
result->Set( 0.0 );
aCtx->Push( result );
if( !arg || arg->AsString().IsEmpty() )
{
if( aCtx->HasErrorCallback() )
aCtx->ReportError( MISSING_SHEET_ARG( wxT( "memberOfSheetOrChildren()" ) ) );
return;
}
PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
if( !item )
return;
result->SetDeferredEval(
[item, arg]() -> double
{
FOOTPRINT* fp = item->GetParentFootprint();
if( !fp && item->Type() == PCB_FOOTPRINT_T )
fp = static_cast<FOOTPRINT*>( item );
if( !fp )
return 0.0;
wxString sheetName = fp->GetSheetname();
wxString refName = arg->AsString();
if( sheetName.EndsWith( wxT( "/" ) ) )
sheetName.RemoveLast();
if( refName.EndsWith( wxT( "/" ) ) )
refName.RemoveLast();
wxArrayString sheetPath = wxSplit( sheetName, '/' );
wxArrayString refPath = wxSplit( refName, '/' );
if( refPath.size() > sheetPath.size() )
return 0.0;
if( ( refName.Matches( wxT( "/" ) ) || refName.IsEmpty() ) && sheetName.IsEmpty() )
{
return 1.0;
}
for( size_t i = 0; i < refPath.size(); i++ )
{
if( !sheetPath[i].Matches( refPath[i] ) )
return 0.0;
}
return 1.0;
} );
}
#define MISSING_REF_ARG( f ) \
wxString::Format( _( "Missing footprint argument (reference designator) to %s." ), f )
@ -1316,6 +1379,7 @@ void PCBEXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions()
RegisterFunc( wxT( "memberOfGroup('x')" ), memberOfGroupFunc );
RegisterFunc( wxT( "memberOfFootprint('x')" ), memberOfFootprintFunc );
RegisterFunc( wxT( "memberOfSheet('x')" ), memberOfSheetFunc );
RegisterFunc( wxT( "memberOfSheetOrChildren('x')" ), memberOfSheetOrChildrenFunc );
RegisterFunc( wxT( "fromTo('x','y')" ), fromToFunc );
RegisterFunc( wxT( "isCoupledDiffPair()" ), isCoupledDiffPairFunc );

View File

@ -99,8 +99,8 @@ bool MULTICHANNEL_TOOL::identifyComponentsInRuleArea( ZONE* aRul
{
case RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME:
{
ruleText =
wxT( "A.memberOfSheet('" ) + aRuleArea->GetRuleAreaPlacementSource() + wxT( "')" );
ruleText = wxT( "A.memberOfSheetOrChildren('" ) + aRuleArea->GetRuleAreaPlacementSource()
+ wxT( "')" );
break;
}
case RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS: