7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-11 16:10:10 +00:00

Add SCH_RULE_AREA shapes to eeschema

Includes:
 - Fix GAL to draw closed polygons in eeschema
 - Add functionality to eeschema to draw arbitary polygons
 - Update polygon item previews to have customisable edge colour
 - Add new SCH_RULE_AREA class, derived from a poly SCH_SHAPE
 - Add SCH_RULE_AREA to paint and plot methods
 - Add new rule area color preference to themes
This commit is contained in:
James J 2024-04-25 14:24:46 +00:00 committed by Seth Hillbrand
parent da1926d723
commit be8744176c
57 changed files with 3860 additions and 112 deletions

View File

@ -374,6 +374,7 @@ static struct EDA_ITEM_DESC
.Map( SCH_LINE_T, _HKI( "Line" ) )
.Map( SCH_BITMAP_T, _HKI( "Bitmap" ) )
.Map( SCH_SHAPE_T, _HKI( "Graphic" ) )
.Map( SCH_RULE_AREA_T, _HKI( "Rule Area" ) )
.Map( SCH_TEXT_T, _HKI( "Text" ) )
.Map( SCH_TEXTBOX_T, _HKI( "Text Box" ) )
.Map( SCH_TABLE_T, _HKI( "Table" ) )

View File

@ -122,6 +122,7 @@ wxString LayerName( int aLayer )
case LAYER_FIELDS: return _( "Symbol fields" );
case LAYER_INTERSHEET_REFS: return _( "Sheet references" );
case LAYER_NETCLASS_REFS: return _( "Net class references" );
case LAYER_RULE_AREAS: return _( "Rule areas" );
case LAYER_DEVICE: return _( "Symbol body outlines" );
case LAYER_DEVICE_BACKGROUND: return _( "Symbol body fills" );
case LAYER_NOTES: return _( "Schematic text && graphics" );

View File

@ -33,6 +33,20 @@ const double POLYGON_ITEM::POLY_LINE_WIDTH = 1;
POLYGON_ITEM::POLYGON_ITEM() :
SIMPLE_OVERLAY_ITEM()
{
m_lineColor = KIGFX::COLOR4D::UNSPECIFIED;
m_leaderColor = KIGFX::COLOR4D::UNSPECIFIED;
}
void POLYGON_ITEM::SetLineColor( KIGFX::COLOR4D lineColor )
{
m_lineColor = lineColor;
}
void POLYGON_ITEM::SetLeaderColor( KIGFX::COLOR4D leaderColor )
{
m_leaderColor = leaderColor;
}
@ -62,8 +76,13 @@ void POLYGON_ITEM::drawPreviewShape( KIGFX::VIEW* aView ) const
KIGFX::GAL& gal = *aView->GetGAL();
RENDER_SETTINGS* renderSettings = aView->GetPainter()->GetSettings();
gal.SetIsStroke( true );
if( m_lockedChain.PointCount() >= 2 )
{
if( m_lineColor != KIGFX::COLOR4D::UNSPECIFIED )
gal.SetStrokeColor( m_lineColor );
gal.SetLineWidth( (float) aView->ToWorld( POLY_LINE_WIDTH ) );
gal.DrawPolyline( m_lockedChain );
}
@ -71,7 +90,11 @@ void POLYGON_ITEM::drawPreviewShape( KIGFX::VIEW* aView ) const
// draw the leader line in a different color
if( m_leaderChain.PointCount() >= 2 )
{
gal.SetStrokeColor( renderSettings->GetLayerColor( LAYER_AUX_ITEMS ) );
if( m_leaderColor != KIGFX::COLOR4D::UNSPECIFIED )
gal.SetStrokeColor( m_leaderColor );
else
gal.SetStrokeColor( renderSettings->GetLayerColor( LAYER_AUX_ITEMS ) );
gal.DrawPolyline( m_leaderChain );
}

View File

@ -50,6 +50,7 @@ static const std::map<int, COLOR4D> s_defaultTheme =
{ LAYER_HIERLABEL, CSS_COLOR( 114, 86, 0, 1 ) },
{ LAYER_LOCLABEL, CSS_COLOR( 15, 15, 15, 1 ) },
{ LAYER_NETCLASS_REFS, CSS_COLOR( 72, 72, 72, 1 ) },
{ LAYER_RULE_AREAS, CSS_COLOR( 255, 0, 0, 1 ) },
{ LAYER_NOCONNECT, CSS_COLOR( 0, 0, 132, 1 ) },
{ LAYER_NOTES, CSS_COLOR( 0, 0, 194, 1 ) },
{ LAYER_PRIVATE_NOTES, CSS_COLOR( 72, 72, 255, 1 ) },
@ -268,6 +269,7 @@ static const std::map<int, COLOR4D> s_classicTheme =
{ LAYER_HIERLABEL, COLOR4D( BROWN ) },
{ LAYER_LOCLABEL, COLOR4D( BLACK ) },
{ LAYER_NETCLASS_REFS, COLOR4D( BLACK ) },
{ LAYER_RULE_AREAS, COLOR4D( RED ) },
{ LAYER_NOCONNECT, COLOR4D( BLUE ) },
{ LAYER_NOTES, COLOR4D( LIGHTBLUE ) },
{ LAYER_PRIVATE_NOTES, COLOR4D( LIGHTBLUE ) },

View File

@ -75,6 +75,7 @@ COLOR_SETTINGS::COLOR_SETTINGS( const wxString& aFilename, bool aAbsolutePath )
CLR( "schematic.label_hier", LAYER_HIERLABEL );
CLR( "schematic.label_local", LAYER_LOCLABEL );
CLR( "schematic.netclass_flag", LAYER_NETCLASS_REFS );
CLR( "schematic.rule_area", LAYER_RULE_AREAS );
CLR( "schematic.no_connect", LAYER_NOCONNECT );
CLR( "schematic.note", LAYER_NOTES );
CLR( "schematic.private_note", LAYER_PRIVATE_NOTES );

View File

@ -383,6 +383,7 @@ set( EESCHEMA_SRCS
sch_render_settings.cpp
sch_screen.cpp
sch_shape.cpp
sch_rule_area.cpp
sch_sheet.cpp
sch_sheet_path.cpp
sch_sheet_pin.cpp
@ -431,6 +432,7 @@ set( EESCHEMA_SRCS
tools/ee_point_editor.cpp
tools/ee_selection.cpp
tools/ee_selection_tool.cpp
tools/rule_area_create_helper.cpp
tools/sch_drawing_tools.cpp
tools/sch_edit_table_tool.cpp
tools/sch_edit_tool.cpp

View File

@ -36,6 +36,7 @@
#include <sch_line.h>
#include <sch_marker.h>
#include <sch_pin.h>
#include <sch_rule_area.h>
#include <sch_sheet.h>
#include <sch_sheet_path.h>
#include <sch_sheet_pin.h>
@ -430,10 +431,35 @@ const wxString& CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem ) const
}
const wxString CONNECTION_SUBGRAPH::GetNetclassForDriver( SCH_ITEM* aItem ) const
const std::vector<std::pair<wxString, SCH_ITEM*>>
CONNECTION_SUBGRAPH::GetNetclassesForDriver( SCH_ITEM* aItem, bool returnAll ) const
{
wxString netclass;
std::vector<std::pair<wxString, SCH_ITEM*>> foundNetclasses;
const std::unordered_set<SCH_RULE_AREA*>& ruleAreaCache = aItem->GetRuleAreaCache();
// Get netclasses on attached rule areas
for( SCH_RULE_AREA* ruleArea : ruleAreaCache )
{
const std::vector<std::pair<wxString, SCH_ITEM*>> ruleNetclasses =
ruleArea->GetResolvedNetclasses();
if( ruleNetclasses.size() > 0 )
{
if( returnAll )
{
foundNetclasses.insert( foundNetclasses.end(), ruleNetclasses.begin(),
ruleNetclasses.end() );
}
else
{
foundNetclasses.push_back( ruleNetclasses[0] );
return foundNetclasses;
}
}
}
// Get netclasses on child fields
aItem->RunOnChildren(
[&]( SCH_ITEM* aChild )
{
@ -443,15 +469,19 @@ const wxString CONNECTION_SUBGRAPH::GetNetclassForDriver( SCH_ITEM* aItem ) cons
if( field->GetCanonicalName() == wxT( "Netclass" ) )
{
netclass = field->GetText();
return false;
wxString netclass = field->GetText();
if( netclass != wxEmptyString )
foundNetclasses.push_back( { field->GetText(), aItem } );
return returnAll;
}
}
return true;
} );
return netclass;
return foundNetclasses;
}
@ -742,7 +772,9 @@ void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnco
}
else if( item->Type() == SCH_SHEET_T )
{
for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
for( SCH_SHEET_PIN* pin : sheetItem->GetPins() )
{
if( pin->IsConnectivityDirty() )
{
@ -2347,10 +2379,14 @@ void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* a
{
for( SCH_ITEM* item : subgraph->m_items )
{
netclass = subgraph->GetNetclassForDriver( item );
const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProviders =
subgraph->GetNetclassesForDriver( item, false );
if( !netclass.IsEmpty() )
if( netclassesWithProviders.size() > 0 )
{
netclass = netclassesWithProviders[0].first;
break;
}
}
if( !netclass.IsEmpty() )
@ -3188,40 +3224,52 @@ bool CONNECTION_GRAPH::ercCheckNetclassConflicts( const std::vector<CONNECTION_S
wxString firstNetclass;
SCH_ITEM* firstNetclassDriver = nullptr;
const SCH_SHEET_PATH* firstNetclassDriverSheet = nullptr;
bool conflictFound = false;
for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
{
for( SCH_ITEM* item : subgraph->m_items )
{
const wxString netclass = subgraph->GetNetclassForDriver( item );
const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProvider =
subgraph->GetNetclassesForDriver( item, true );
if( netclass.IsEmpty() )
if( netclassesWithProvider.size() == 0 )
continue;
if( netclass != firstNetclass )
auto checkNetclass = [&]( const std::pair<wxString, SCH_ITEM*>& netclass )
{
if( !firstNetclassDriver )
if( netclass.first != firstNetclass )
{
firstNetclass = netclass;
firstNetclassDriver = item;
firstNetclassDriverSheet = &subgraph->GetSheet();
continue;
if( !firstNetclassDriver )
{
firstNetclass = netclass.first;
firstNetclassDriver = netclass.second;
firstNetclassDriverSheet = &subgraph->GetSheet();
}
else
{
conflictFound = true;
std::shared_ptr<ERC_ITEM> ercItem =
ERC_ITEM::Create( ERCE_NETCLASS_CONFLICT );
ercItem->SetItems( firstNetclassDriver, netclass.second );
ercItem->SetSheetSpecificPath( subgraph->GetSheet() );
ercItem->SetItemsSheetPaths( *firstNetclassDriverSheet,
subgraph->GetSheet() );
SCH_MARKER* marker =
new SCH_MARKER( ercItem, netclass.second->GetPosition() );
subgraph->m_sheet.LastScreen()->Append( marker );
}
}
};
std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NETCLASS_CONFLICT );
ercItem->SetItems( firstNetclassDriver, item );
ercItem->SetSheetSpecificPath( subgraph->GetSheet() );
ercItem->SetItemsSheetPaths( *firstNetclassDriverSheet, subgraph->GetSheet() );
SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
subgraph->m_sheet.LastScreen()->Append( marker );
return false;
}
for( const std::pair<wxString, SCH_ITEM*>& netclass : netclassesWithProvider )
checkNetclass( netclass );
}
}
return true;
return conflictFound;
}

View File

@ -23,6 +23,7 @@
#define _CONNECTION_GRAPH_H
#include <mutex>
#include <utility>
#include <vector>
#include <erc_settings.h>
@ -122,7 +123,12 @@ public:
/// Return the candidate net name for a driver.
const wxString& GetNameForDriver( SCH_ITEM* aItem ) const;
const wxString GetNetclassForDriver( SCH_ITEM* aItem ) const;
/// Return the resolved netclasses for the item, and the source item providing the netclass
/// @param aItem the item to query for netclass assignments
/// @param returnAll If true, return all assigned netclasses (for ERC). If false, stop on first
/// netclass (for connectivity).
const std::vector<std::pair<wxString, SCH_ITEM*>>
GetNetclassesForDriver( SCH_ITEM* aItem, bool returnAll ) const;
/// Combine another subgraph on the same sheet into this one.
void Absorb( CONNECTION_SUBGRAPH* aOther );

View File

@ -51,7 +51,8 @@ const std::vector<KICAD_T> EE_COLLECTOR::EditableItems = {
SCH_BITMAP_T,
SCH_LINE_T,
SCH_BUS_WIRE_ENTRY_T,
SCH_JUNCTION_T
SCH_JUNCTION_T,
SCH_RULE_AREA_T
};
@ -76,7 +77,8 @@ const std::vector<KICAD_T> EE_COLLECTOR::MovableItems =
SCH_FIELD_T,
SCH_SYMBOL_T,
SCH_SHEET_PIN_T,
SCH_SHEET_T
SCH_SHEET_T,
SCH_RULE_AREA_T
};

View File

@ -41,6 +41,7 @@
#include <sch_edit_frame.h>
#include <sch_marker.h>
#include <sch_reference_list.h>
#include <sch_rule_area.h>
#include <sch_sheet.h>
#include <sch_sheet_pin.h>
#include <sch_textbox.h>
@ -1229,6 +1230,73 @@ int ERC_TESTER::TestSimModelIssues()
}
int ERC_TESTER::RunRuleAreaERC()
{
int numErrors = 0;
ERC_SETTINGS& settings = m_schematic->ErcSettings();
if( !settings.IsTestEnabled( ERCE_OVERLAPPING_RULE_AREAS ) )
return 0;
std::map<SCH_SCREEN*, std::vector<SCH_RULE_AREA*>> allScreenRuleAreas;
SCH_SCREENS screens( m_schematic->Root() );
for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
{
for( SCH_ITEM* item : screen->Items().OfType( SCH_RULE_AREA_T ) )
{
allScreenRuleAreas[screen].push_back( static_cast<SCH_RULE_AREA*>( item ) );
}
}
if( settings.IsTestEnabled( ERCE_OVERLAPPING_RULE_AREAS ) )
numErrors += TestRuleAreaOverlappingRuleAreasERC( allScreenRuleAreas );
return numErrors;
}
int ERC_TESTER::TestRuleAreaOverlappingRuleAreasERC(
std::map<SCH_SCREEN*, std::vector<SCH_RULE_AREA*>>& allScreenRuleAreas )
{
int numErrors = 0;
for( auto screenRuleAreas : allScreenRuleAreas )
{
std::vector<SCH_RULE_AREA*>& ruleAreas = screenRuleAreas.second;
for( std::size_t i = 0; i < ruleAreas.size(); ++i )
{
SHAPE_POLY_SET& polyFirst = ruleAreas[i]->GetPolyShape();
for( std::size_t j = i + 1; j < ruleAreas.size(); ++j )
{
SHAPE_POLY_SET polySecond = ruleAreas[j]->GetPolyShape();
if( polyFirst.Collide( &polySecond ) )
{
numErrors++;
SCH_SCREEN* screen = screenRuleAreas.first;
SCH_SHEET_PATH firstSheet = screen->GetClientSheetPaths()[0];
std::shared_ptr<ERC_ITEM> ercItem =
ERC_ITEM::Create( ERCE_OVERLAPPING_RULE_AREAS );
ercItem->SetItems( ruleAreas[i], ruleAreas[j] );
ercItem->SetSheetSpecificPath( firstSheet );
ercItem->SetItemsSheetPaths( firstSheet, firstSheet );
SCH_MARKER* marker = new SCH_MARKER( ercItem, ruleAreas[i]->GetPosition() );
screen->Append( marker );
}
}
}
}
return numErrors;
}
void ERC_TESTER::RunTests( DS_PROXY_VIEW_ITEM* aDrawingSheet, SCH_EDIT_FRAME* aEditFrame,
KIFACE* aCvPcb, PROJECT* aProject, PROGRESS_REPORTER* aProgressReporter )
{
@ -1267,6 +1335,14 @@ void ERC_TESTER::RunTests( DS_PROXY_VIEW_ITEM* aDrawingSheet, SCH_EDIT_FRAME* aE
m_schematic->ConnectionGraph()->RunERC();
if( aProgressReporter )
aProgressReporter->AdvancePhase( _( "Checking rule areas..." ) );
if( settings.IsTestEnabled( ERCE_OVERLAPPING_RULE_AREAS ) )
{
RunRuleAreaERC();
}
if( aProgressReporter )
aProgressReporter->AdvancePhase( _( "Checking units..." ) );

View File

@ -27,6 +27,8 @@
#define ERC_H
#include <erc_settings.h>
#include <vector>
#include <map>
class SCH_SHEET_LIST;
@ -36,6 +38,8 @@ class SCH_EDIT_FRAME;
class PROGRESS_REPORTER;
struct KIFACE;
class PROJECT;
class SCREEN;
class SCH_RULE_AREA;
extern const wxString CommentERC_H[];
@ -141,6 +145,17 @@ public:
*/
int TestMissingNetclasses();
/**
* Tests for rule area ERC issues
*/
int RunRuleAreaERC();
/**
* Runs ERC to check for overlapping rule areas
*/
int TestRuleAreaOverlappingRuleAreasERC(
std::map<SCH_SCREEN*, std::vector<SCH_RULE_AREA*>>& allScreenRuleAreas );
void RunTests( DS_PROXY_VIEW_ITEM* aDrawingSheet, SCH_EDIT_FRAME* aEditFrame,
KIFACE* aCvPcb, PROJECT* aProject, PROGRESS_REPORTER* aProgressReporter );

View File

@ -119,6 +119,10 @@ ERC_ITEM ERC_ITEM::netclassConflict( ERCE_NETCLASS_CONFLICT,
_( "Conflicting netclass assignments" ),
wxT( "conflicting_netclasses" ) );
ERC_ITEM ERC_ITEM::overlappingRuleAreas( ERCE_OVERLAPPING_RULE_AREAS,
_( "Overlapping rule areas" ),
wxT( "overlapping_rule_areas" ) );
ERC_ITEM ERC_ITEM::netNotBusMember( ERCE_BUS_ENTRY_CONFLICT,
_( "Net is graphically connected to a bus but not a bus member" ),
wxT( "net_not_bus_member" ) );
@ -239,7 +243,8 @@ std::vector<std::reference_wrapper<RC_ITEM>> ERC_ITEM::allItemTypes( {
ERC_ITEM::missingUnits,
ERC_ITEM::missingInputPin,
ERC_ITEM::missingBidiPin,
ERC_ITEM::missingPowerInputPin
ERC_ITEM::missingPowerInputPin,
ERC_ITEM::overlappingRuleAreas
} );
@ -269,6 +274,7 @@ std::shared_ptr<ERC_ITEM> ERC_ITEM::Create( int aErrorCode )
case ERCE_BUS_TO_BUS_CONFLICT: return std::make_shared<ERC_ITEM>( busToBusConflict );
case ERCE_BUS_TO_NET_CONFLICT: return std::make_shared<ERC_ITEM>( busToNetConflict );
case ERCE_NETCLASS_CONFLICT: return std::make_shared<ERC_ITEM>( netclassConflict );
case ERCE_OVERLAPPING_RULE_AREAS: return std::make_shared<ERC_ITEM>( overlappingRuleAreas );
case ERCE_GLOBLABEL: return std::make_shared<ERC_ITEM>( globalLabelDangling );
case ERCE_UNRESOLVED_VARIABLE: return std::make_shared<ERC_ITEM>( unresolvedVariable );
case ERCE_UNDEFINED_NETCLASS: return std::make_shared<ERC_ITEM>( undefinedNetclass );

View File

@ -202,6 +202,7 @@ private:
static ERC_ITEM busDefinitionConflict;
static ERC_ITEM multipleNetNames;
static ERC_ITEM netclassConflict;
static ERC_ITEM overlappingRuleAreas;
static ERC_ITEM netNotBusMember;
static ERC_ITEM busToBusConflict;
static ERC_ITEM busToNetConflict;

View File

@ -67,6 +67,7 @@ enum ERCE_T
ERCE_BUS_TO_NET_CONFLICT, ///< A bus wire is graphically connected to a net port/pin
///< (or vice versa).
ERCE_NETCLASS_CONFLICT, ///< Multiple labels assign different netclasses to same net.
ERCE_OVERLAPPING_RULE_AREAS, ///< Rule areas are overlapping
ERCE_GLOBLABEL, ///< A global label is unique.
ERCE_UNRESOLVED_VARIABLE, ///< A text variable could not be resolved.
ERCE_UNDEFINED_NETCLASS, ///< A netclass was referenced but not defined.

View File

@ -248,8 +248,9 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
placeMenu->Add( EE_ACTIONS::placeNoConnect );
placeMenu->Add( EE_ACTIONS::placeJunction );
placeMenu->Add( EE_ACTIONS::placeLabel );
placeMenu->Add( EE_ACTIONS::placeClassLabel );
placeMenu->Add( EE_ACTIONS::placeGlobalLabel );
placeMenu->Add( EE_ACTIONS::placeClassLabel );
placeMenu->Add( EE_ACTIONS::drawRuleArea );
placeMenu->AppendSeparator();
placeMenu->Add( EE_ACTIONS::placeHierLabel );

View File

@ -221,22 +221,21 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
} );
}
auto updateConnectivityFlag =
[&]()
{
if( schItem->IsConnectable() )
{
dirtyConnectivity = true;
auto updateConnectivityFlag = [&]()
{
if( schItem->IsConnectable() || ( schItem->Type() == SCH_RULE_AREA_T ) )
{
dirtyConnectivity = true;
// Do a local clean up if there are any connectable objects in the commit.
if( connectivityCleanUp == NO_CLEANUP )
connectivityCleanUp = LOCAL_CLEANUP;
// Do a local clean up if there are any connectable objects in the commit.
if( connectivityCleanUp == NO_CLEANUP )
connectivityCleanUp = LOCAL_CLEANUP;
// Do a full rebauild of the connectivity if there is a sheet in the commit.
if( schItem->Type() == SCH_SHEET_T )
connectivityCleanUp = GLOBAL_CLEANUP;
}
};
// Do a full rebauild of the connectivity if there is a sheet in the commit.
if( schItem->Type() == SCH_SHEET_T )
connectivityCleanUp = GLOBAL_CLEANUP;
}
};
switch( changeType )
{
@ -318,8 +317,11 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
if( frame )
currentSheet = frame->GetCurrentSheet();
if( itemCopy->HasConnectivityChanges( schItem, &currentSheet ) )
if( itemCopy->HasConnectivityChanges( schItem, &currentSheet )
|| ( itemCopy->Type() == SCH_RULE_AREA_T ) )
{
updateConnectivityFlag();
}
undoList.PushItem( itemWrapper );
ent.m_copy = nullptr; // We've transferred ownership to the undo list

View File

@ -22,6 +22,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <algorithm>
#include <api/api_handler_sch.h>
#include <api/api_server.h>
#include <base_units.h>
@ -36,6 +37,7 @@
#include <eeschema_id.h>
#include <executable_names.h>
#include <gal/graphics_abstraction_layer.h>
#include <geometry/shape_segment.h>
#include <gestfich.h>
#include <dialogs/html_message_box.h>
#include <core/ignore.h>
@ -59,6 +61,7 @@
#include <sch_sheet_pin.h>
#include <schematic.h>
#include <sch_commit.h>
#include <sch_rule_area.h>
#include <settings/settings_manager.h>
#include <advanced_config.h>
#include <sim/simulator_frame.h>
@ -85,6 +88,7 @@
#include <tools/sch_move_tool.h>
#include <tools/sch_navigate_tool.h>
#include <tools/sch_find_replace_tool.h>
#include <unordered_set>
#include <view/view_controls.h>
#include <widgets/wx_infobar.h>
#include <widgets/hierarchy_pane.h>
@ -756,6 +760,7 @@ void SCH_EDIT_FRAME::setupUIConditions()
CURRENT_TOOL( EE_ACTIONS::placeClassLabel );
CURRENT_TOOL( EE_ACTIONS::placeGlobalLabel );
CURRENT_TOOL( EE_ACTIONS::placeHierLabel );
CURRENT_TOOL( EE_ACTIONS::drawRuleArea );
CURRENT_TOOL( EE_ACTIONS::drawSheet );
CURRENT_TOOL( EE_ACTIONS::placeSheetPin );
CURRENT_TOOL( EE_ACTIONS::syncSheetPins );
@ -1763,52 +1768,150 @@ void SCH_EDIT_FRAME::RecalculateConnections( SCH_COMMIT* aCommit, SCH_CLEANUP_FL
if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP
|| m_undoList.m_CommandsList.empty() )
{
// Update all rule areas so we can cascade implied connectivity changes
std::unordered_set<SCH_SCREEN*> all_screens;
for( const SCH_SHEET_PATH& path : list )
all_screens.insert( path.LastScreen() );
SCH_RULE_AREA::UpdateRuleAreasInScreens( all_screens, GetCanvas()->GetView() );
// Recalculate all connectivity
Schematic().ConnectionGraph()->Recalculate( list, true, &changeHandler );
}
else
{
struct CHANGED_ITEM
{
SCH_ITEM* item;
SCH_ITEM* linked_item;
SCH_SCREEN* screen;
};
PICKED_ITEMS_LIST* changed_list = m_undoList.m_CommandsList.back();
std::set<SCH_ITEM*> changed_items;
std::set<VECTOR2I> pts;
// Final change sets
std::set<SCH_ITEM*> changed_items;
std::set<VECTOR2I> pts;
std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> item_paths;
for( unsigned ii = 0; ii < changed_list->GetCount(); ++ii )
// Working change sets
std::unordered_set<SCH_SCREEN*> changed_screens;
std::set<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> changed_rule_areas;
std::vector<CHANGED_ITEM> changed_connectable_items;
// Lambda to add an item to the connectivity update sets
auto addItemToChangeSet =
[&changed_items, &pts, &item_paths, &changed_screens]( CHANGED_ITEM itemData )
{
SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( changed_list->GetPickedItem( ii ) );
SCH_SHEET_PATHS& paths = itemData.screen->GetClientSheetPaths();
// Ignore objects that are not connectable.
if( !item || !item->IsConnectable() )
continue;
SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( changed_list->GetScreenForItem( ii ) );
SCH_SHEET_PATHS& paths = screen->GetClientSheetPaths();
std::vector<VECTOR2I> tmp_pts = item->GetConnectionPoints();
std::vector<VECTOR2I> tmp_pts = itemData.item->GetConnectionPoints();
pts.insert( tmp_pts.begin(), tmp_pts.end() );
changed_items.insert( item );
changed_items.insert( itemData.item );
for( SCH_SHEET_PATH& path : paths )
item_paths.insert( std::make_pair( path, item ) );
item_paths.insert( std::make_pair( path, itemData.item ) );
item = dynamic_cast<SCH_ITEM*>( changed_list->GetPickedItemLink( ii ) );
if( !itemData.linked_item || !itemData.linked_item->IsConnectable() )
return;
if( !item || !item->IsConnectable() )
continue;
tmp_pts = item->GetConnectionPoints();
tmp_pts = itemData.linked_item->GetConnectionPoints();
pts.insert( tmp_pts.begin(), tmp_pts.end() );
changed_items.insert( item );
changed_items.insert( itemData.linked_item );
// We have to directly add the pins here because the link may not exist on the schematic
// anymore and so won't be picked up by GetScreen()->Items().Overlapping() below.
if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( itemData.linked_item ) )
{
std::vector<SCH_PIN*> pins = symbol->GetPins();
changed_items.insert( pins.begin(), pins.end() );
}
for( SCH_SHEET_PATH& path : paths )
item_paths.insert( std::make_pair( path, item ) );
item_paths.insert( std::make_pair( path, itemData.linked_item ) );
};
// Get all changed connectable items and determine all changed screens
for( unsigned ii = 0; ii < changed_list->GetCount(); ++ii )
{
SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( changed_list->GetPickedItem( ii ) );
if( item )
{
SCH_SCREEN* screen =
static_cast<SCH_SCREEN*>( changed_list->GetScreenForItem( ii ) );
changed_screens.insert( screen );
if( item->Type() == SCH_RULE_AREA_T )
{
SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( item );
// Clear item and directive associations for this rule area
ruleArea->ResetDirectivesAndItems( GetCanvas()->GetView() );
changed_rule_areas.insert( { ruleArea, screen } );
}
else if( item->IsConnectable() )
{
SCH_ITEM* linked_item =
dynamic_cast<SCH_ITEM*>( changed_list->GetPickedItemLink( ii ) );
changed_connectable_items.push_back( { item, linked_item, screen } );
}
}
}
// Update rule areas in changed screens to propagate any directive connectivity changes
std::vector<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> forceUpdateRuleAreas =
SCH_RULE_AREA::UpdateRuleAreasInScreens( changed_screens, GetCanvas()->GetView() );
std::for_each( forceUpdateRuleAreas.begin(), forceUpdateRuleAreas.end(),
[&]( std::pair<SCH_RULE_AREA*, SCH_SCREEN*>& updatedRuleArea )
{
changed_rule_areas.insert( updatedRuleArea );
} );
// If a SCH_RULE_AREA was changed, we need to add all past and present contained items to
// update their connectivity
for( const std::pair<SCH_RULE_AREA*, SCH_SCREEN*>& changedRuleArea : changed_rule_areas )
{
for( SCH_ITEM* containedItem :
changedRuleArea.first->GetPastAndPresentContainedItems() )
{
addItemToChangeSet( { containedItem, nullptr, changedRuleArea.second } );
}
}
// Add all changed items, and associated items, to the change set
for( CHANGED_ITEM& changed_item_data : changed_connectable_items )
{
addItemToChangeSet( changed_item_data );
// If a SCH_DIRECTIVE_LABEL was changed which is attached to a SCH_RULE_AREA, we need
// to add the contained items to the change set to force update of their connectivity
if( changed_item_data.item->Type() == SCH_DIRECTIVE_LABEL_T )
{
const std::vector<VECTOR2I> labelConnectionPoints =
changed_item_data.item->GetConnectionPoints();
EE_RTREE::EE_TYPE candidateRuleAreas =
changed_item_data.screen->Items().Overlapping(
SCH_RULE_AREA_T, changed_item_data.item->GetBoundingBox() );
for( SCH_ITEM* candidateRuleArea : candidateRuleAreas )
{
SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( candidateRuleArea );
std::vector<SHAPE*> borderShapes = ruleArea->MakeEffectiveShapes( true );
if( ruleArea->GetPolyShape().CollideEdge( labelConnectionPoints[0], nullptr,
5 ) )
{
for( SCH_ITEM* containedItem : ruleArea->GetPastAndPresentContainedItems() )
addItemToChangeSet(
{ containedItem, nullptr, changed_item_data.screen } );
}
}
}
}
for( const VECTOR2I& pt: pts )

View File

@ -103,4 +103,5 @@
//#define SEXPR_SCHEMATIC_FILE_VERSION 20230808 // Move Sim.Enable field to exclude_from_sim attr
//#define SEXPR_SCHEMATIC_FILE_VERSION 20230819 // Allow multiple library symbol inheritance depth.
//#define SEXPR_SCHEMATIC_FILE_VERSION 20231120 // generator_version; V8 cleanups
#define SEXPR_SCHEMATIC_FILE_VERSION 20240101 // Tables.
//#define SEXPR_SCHEMATIC_FILE_VERSION 20240101 // Tables.
#define SEXPR_SCHEMATIC_FILE_VERSION 20240417 // Rule areas

View File

@ -42,6 +42,7 @@
#include <sch_pin.h>
#include <sch_shape.h>
#include <sch_no_connect.h>
#include <sch_rule_area.h>
#include <sch_text.h>
#include <sch_textbox.h>
#include <sch_table.h>
@ -470,6 +471,10 @@ void SCH_IO_KICAD_SEXPR::Format( SCH_SHEET* aSheet )
case SCH_SHAPE_T:
saveShape( static_cast<SCH_SHAPE*>( item ), 1 );
break;
case SCH_RULE_AREA_T:
saveRuleArea( static_cast<SCH_RULE_AREA*>( item ), 1 );
break;
case SCH_TEXT_T:
case SCH_LABEL_T:
@ -599,6 +604,10 @@ void SCH_IO_KICAD_SEXPR::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSele
saveShape( static_cast<SCH_SHAPE*>( item ), 0 );
break;
case SCH_RULE_AREA_T:
saveRuleArea( static_cast<SCH_RULE_AREA*>( item ), 0 );
break;
case SCH_TEXT_T:
case SCH_LABEL_T:
case SCH_GLOBAL_LABEL_T:
@ -1234,6 +1243,16 @@ void SCH_IO_KICAD_SEXPR::saveShape( SCH_SHAPE* aShape, int aNestLevel )
}
void SCH_IO_KICAD_SEXPR::saveRuleArea( SCH_RULE_AREA* aRuleArea, int aNestLevel )
{
wxCHECK_RET( aRuleArea != nullptr && m_out != nullptr, "" );
m_out->Print( aNestLevel, "(rule_area " );
saveShape( aRuleArea, aNestLevel + 1 );
m_out->Print( aNestLevel, ")\n" );
}
void SCH_IO_KICAD_SEXPR::saveLine( SCH_LINE* aLine, int aNestLevel )
{
wxCHECK_RET( aLine != nullptr && m_out != nullptr, "" );

View File

@ -43,6 +43,7 @@ class SCH_JUNCTION;
class SCH_NO_CONNECT;
class SCH_LINE;
class SCH_SHAPE;
class SCH_RULE_AREA;
class SCH_BUS_ENTRY_BASE;
class SCH_TEXT;
class SCH_TEXTBOX;
@ -151,6 +152,7 @@ private:
void saveBusEntry( SCH_BUS_ENTRY_BASE* aBusEntry, int aNestLevel );
void saveLine( SCH_LINE* aLine, int aNestLevel );
void saveShape( SCH_SHAPE* aShape, int aNestLevel );
void saveRuleArea( SCH_RULE_AREA* aRuleArea, int aNestLevel );
void saveText( SCH_TEXT* aText, int aNestLevel );
void saveTextBox( SCH_TEXTBOX* aText, int aNestLevel );
void saveTable( SCH_TABLE* aTable, int aNestLevel );

View File

@ -46,6 +46,7 @@
#include <sch_edit_frame.h> // SYM_ORIENT_XXX
#include <sch_field.h>
#include <sch_line.h>
#include <sch_rule_area.h>
#include <sch_textbox.h>
#include <sch_table.h>
#include <sch_tablecell.h>
@ -2745,6 +2746,10 @@ void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopya
screen->Append( parseSchBezier() );
break;
case T_rule_area:
screen->Append( parseSchRuleArea() );
break;
case T_netclass_flag: // present only during early development of 7.0
KI_FALLTHROUGH;
@ -2782,7 +2787,7 @@ void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopya
default:
Expecting( "symbol, paper, page, title_block, bitmap, sheet, junction, no_connect, "
"bus_entry, line, bus, text, label, class_label, global_label, "
"hierarchical_label, symbol_instances, or bus_alias" );
"hierarchical_label, symbol_instances, rule_area, or bus_alias" );
}
}
@ -3927,6 +3932,43 @@ SCH_SHAPE* SCH_IO_KICAD_SEXPR_PARSER::parseSchRectangle()
}
SCH_RULE_AREA* SCH_IO_KICAD_SEXPR_PARSER::parseSchRuleArea()
{
wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
T token;
STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
FILL_PARAMS fill;
std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_polyline:
{
std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
ruleArea->SetPolyShape( poly->GetPolyShape() );
ruleArea->SetStroke( poly->GetStroke() );
ruleArea->SetFillMode( poly->GetFillMode() );
ruleArea->SetFillColor( poly->GetFillColor() );
break;
}
default:
Expecting( "polyline" );
}
}
return ruleArea.release();
}
SCH_SHAPE* SCH_IO_KICAD_SEXPR_PARSER::parseSchBezier()
{
wxCHECK_MSG( CurTok() == T_bezier, nullptr,

View File

@ -45,6 +45,7 @@ class SCH_SYMBOL;
class SCH_FIELD;
class SCH_ITEM;
class SCH_SHAPE;
class SCH_RULE_AREA;
class SCH_JUNCTION;
class SCH_LINE;
class SCH_NO_CONNECT;
@ -221,6 +222,7 @@ private:
SCH_SHAPE* parseSchCircle();
SCH_SHAPE* parseSchRectangle();
SCH_SHAPE* parseSchBezier();
SCH_RULE_AREA* parseSchRuleArea();
SCH_TEXT* parseSchText();
SCH_TEXTBOX* parseSchTextBox();
void parseSchTextBoxContent( SCH_TEXTBOX* aTextBox );

View File

@ -26,6 +26,7 @@
#define SCH_ITEM_H
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
@ -44,6 +45,7 @@ class SCHEMATIC;
class SYMBOL;
class LINE_READER;
class SCH_EDIT_FRAME;
class SCH_RULE_AREA;
struct SCH_PLOT_OPTS;
namespace KIFONT
@ -628,6 +630,24 @@ public:
wxCHECK_MSG( false, /*void*/, wxT( "Plot not implemented in " ) + GetClass() );
}
/**
* Reset the cache of rule areas (called prior to schematic connectivity recomputation)
*/
void ClearRuleAreasCache() { m_rule_areas_cache.clear(); }
/**
* Adds a rule area to the item's cache
*/
void AddRuleAreaToCache( SCH_RULE_AREA* aRuleArea ) { m_rule_areas_cache.insert( aRuleArea ); }
/**
* Gets the cache of rule areas enclosing this item
*/
const std::unordered_set<SCH_RULE_AREA*>& GetRuleAreaCache() const
{
return m_rule_areas_cache;
}
/**
* The list of flags used by the #compare function.
*
@ -721,6 +741,9 @@ protected:
bool m_connectivity_dirty;
/// Store pointers to rule areas which this item is contained within
std::unordered_set<SCH_RULE_AREA*> m_rule_areas_cache;
private:
friend class LIB_SYMBOL;
};

View File

@ -1808,6 +1808,30 @@ wxString SCH_DIRECTIVE_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider
}
void SCH_DIRECTIVE_LABEL::AddConnectedRuleArea( SCH_RULE_AREA* aRuleArea )
{
m_connected_rule_areas.insert( aRuleArea );
}
void SCH_DIRECTIVE_LABEL::ClearConnectedRuleAreas()
{
m_connected_rule_areas.clear();
}
void SCH_DIRECTIVE_LABEL::RemoveConnectedRuleArea( SCH_RULE_AREA* aRuleArea )
{
m_connected_rule_areas.erase( aRuleArea );
}
bool SCH_DIRECTIVE_LABEL::IsDangling() const
{
return m_isDangling && m_connected_rule_areas.empty();
}
SCH_GLOBALLABEL::SCH_GLOBALLABEL( const VECTOR2I& pos, const wxString& text ) :
SCH_LABEL_BASE( pos, text, SCH_GLOBAL_LABEL_T )
{

View File

@ -30,6 +30,8 @@
#include <sch_field.h>
#include <sch_connection.h> // for CONNECTION_TYPE
class SCH_RULE_AREA;
/*
* Spin style for labels of all kinds on schematics
@ -474,9 +476,24 @@ public:
void MirrorHorizontally( int aCenter ) override;
void MirrorVertically( int aCenter ) override;
/// @brief Adds an entry to the connected rule area cache
void AddConnectedRuleArea( SCH_RULE_AREA* aRuleArea );
/// @brief Removes all rule areas from the cache
void ClearConnectedRuleAreas();
/// @brief Removes a specific rule area from the cache
void RemoveConnectedRuleArea( SCH_RULE_AREA* aRuleArea );
/// @brief Determines dangling state from connectivity and cached connected rule areas
virtual bool IsDangling() const override;
private:
int m_pinLength;
int m_symbolSize;
/// Cache of any rule areas with borders which this label connects to
std::unordered_set<SCH_RULE_AREA*> m_connected_rule_areas;
};

Some files were not shown because too many files have changed in this diff Show More