KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_auxiliary.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "conn_auxiliary.h"
21#include <algorithm>
22#include <iterator>
23#include <memory>
24#include <stdexcept>
25#include <unordered_map>
26
28{
30 m_keys( aKeys ),
31 m_islands( KEY_LESS{ aKeys } ),
32 m_areas( KEY_LESS{ aKeys } ),
33 m_islandOf( KEY_LESS{ aKeys } ),
34 m_ruleAreasOf( KEY_LESS{ aKeys } ),
35 m_neighborsOf( KEY_LESS{ aKeys } )
36{
37}
38
39void AUXILIARY::Update( std::span<const FRAME_INSTANCE> aFrame, const INPUT_STORE& aInputs,
40 const RECORD_STORE::RECORD_CACHE& aRecords, CHANGE_SET& aChanges )
41{
42 struct ISLAND_STAGE
43 {
44 uint64_t islandVersion = 0;
45 uint64_t recordVersion = 0;
46 std::unique_ptr<ISLAND_AUX> value;
47 };
48 struct AREA_STAGE
49 {
50 uint64_t version = 0;
51 std::optional<RULE_AREA_RESULT> value;
52 };
53 std::vector<std::pair<RECORD_KEY, ISLAND_STAGE>> islands;
54 std::map<ITEM_KEY, AREA_STAGE, KEY_LESS> areas( KEY_LESS{ m_keys } );
55 std::set<INST_ID> instances;
56 uint64_t sourceVersion = m_sourceVersion;
57 std::map<SCREEN_ID, std::vector<KIID>> sourceChanges;
58 islands.reserve( m_islands.size() );
59 std::vector<const FRAME_INSTANCE*> frames;
60 frames.reserve( aFrame.size() );
61
62 for( const FRAME_INSTANCE& frame : aFrame )
63 frames.push_back( &frame );
64
65 // Each screen's islands are anchor-sorted; canonical instance order also orders the staged records
66 std::sort( frames.begin(), frames.end(),
67 [&]( const FRAME_INSTANCE* a, const FRAME_INSTANCE* b )
68 {
69 return m_keys.Instance( a->scope.instance ) < m_keys.Instance( b->scope.instance );
70 } );
71 const auto less = m_islands.key_comp();
72 auto recordPosition = aRecords.Entries().begin();
73 auto oldPosition = m_islands.cbegin();
74
75 for( const FRAME_INSTANCE* captured : frames )
76 {
77 const FRAME_INSTANCE& frame = *captured;
78 const INST_ID instance = frame.scope.instance;
79 const auto* facts = aInputs.FindInstance( instance );
80
81 if( !facts )
82 throw std::invalid_argument( "Auxiliary publication requires captured instance facts" );
83
84 const auto* screen = aInputs.FindScreen( frame.screen );
85
86 if( !screen )
87 throw std::invalid_argument( "Auxiliary publication requires captured screen facts" );
88
89 sourceVersion = std::max( { sourceVersion, screen->version, facts->version } );
90
91 instances.insert( instance );
92
93 // A new instance has new item identities even when its shared screen is unchanged
94 if( !m_instances.contains( instance ) )
95 {
96 for( const ITEM_FACT& item : screen->value.items )
97 {
98 aChanges.changedItems.push_back( { item.id, instance } );
99
100 for( const PIN_FACT& pin : item.pins )
101 aChanges.changedItems.push_back( { pin.id, instance } );
102 }
103 }
104 else if( screen->version > m_sourceVersion )
105 {
106 auto [changed, inserted] = sourceChanges.try_emplace( frame.screen );
107
108 if( inserted )
109 {
110 for( const ITEM_FACT& item : screen->value.items )
111 {
112 const auto* source = aInputs.Fact( { frame.screen, item.id } );
113
114 if( source && source->version > m_sourceVersion )
115 {
116 changed->second.push_back( item.id );
117
118 for( const PIN_FACT& pin : item.pins )
119 changed->second.push_back( pin.id );
120 }
121 }
122 }
123
124 for( const KIID& item : changed->second )
125 aChanges.changedItems.push_back( { item, instance } );
126 }
127
128 // Aggregate entries are versioned after their rows on the same monotonic clock
129 if( facts->version > m_sourceVersion )
130 {
131 for( const ITEM_TEXT_FACT& item : facts->value.items )
132 {
133 const auto* text = aInputs.Text( { item.id, instance } );
134
135 if( text && text->version > m_sourceVersion )
136 aChanges.changedItems.push_back( { item.id, instance } );
137 }
138 }
139
140 const auto* geometry = aInputs.FindIslands( frame.screen, facts->value.units );
141
142 if( !geometry )
143 throw std::invalid_argument( "Auxiliary publication requires captured islands" );
144
145 for( const ISLAND& island : geometry->value.islands )
146 {
147 const RECORD_KEY key{ instance, island.anchor };
148
149 while( recordPosition != aRecords.Entries().end() && less( recordPosition->first, key ) )
150 ++recordPosition;
151
152 if( recordPosition == aRecords.Entries().end() || recordPosition->first != key )
153 throw std::invalid_argument( "Auxiliary island has no electrical record" );
154
155 const auto* record = recordPosition->second.get();
156 auto& stage = islands.emplace_back( key, ISLAND_STAGE{} ).second;
157 stage.islandVersion = geometry->version;
158 stage.recordVersion = record->version;
159
160 while( oldPosition != m_islands.cend() && less( oldPosition->first, key ) )
161 ++oldPosition;
162
163 if( oldPosition != m_islands.cend() && oldPosition->first == key )
164 {
165 if( oldPosition->second.islandVersion == stage.islandVersion
166 && oldPosition->second.recordVersion == stage.recordVersion )
167 continue;
168
169 const ISLAND_AUX& value = oldPosition->second.value;
170
171 if( value.items == island.items && value.adjacency == island.adjacency
172 && value.dangling == island.dangling && value.busEntryLinks == island.busEntryLinks
173 && value.ncContacts == island.ncContacts && value.atoms == record->value.atoms )
174 continue;
175 }
176
177 stage.value = std::make_unique<ISLAND_AUX>( island.items, island.adjacency, island.dangling,
178 island.busEntryLinks, island.ncContacts, record->value.atoms );
179 }
180
181 for( const auto& area : facts->value.ruleAreas )
182 {
183 const ITEM_KEY key{ area.id, instance };
184 const auto* source = aInputs.Area( key );
185
186 if( !source )
187 throw std::invalid_argument( "Auxiliary rule area has no captured source" );
188
189 auto& stage = areas[key];
190 stage.version = source->version;
191 const auto old = m_areas.find( key );
192
193 if( old != m_areas.end() && old->second.version == source->version )
194 continue;
195
196 RULE_AREA_RESULT value{ source->value.containedItems, source->value.attachedDirectives, {} };
197
198 for( const wxString& name : source->value.netclasses )
199 value.netclasses.push_back( m_keys.InternName( name ) );
200
201 std::ranges::sort( value.netclasses, NAME_LESS{ &m_keys } );
202 value.netclasses.erase( std::ranges::unique( value.netclasses ).begin(), value.netclasses.end() );
203
204 if( old == m_areas.end() || old->second.value != value )
205 stage.value = std::move( value );
206 }
207 }
208
209 auto replacement = islands.cbegin();
210
211 // Remove old memberships before adding replacements, including items whose island anchor changed
212 for( auto it = m_islands.begin(); it != m_islands.end(); )
213 {
214 while( replacement != islands.cend() && less( replacement->first, it->first ) )
215 ++replacement;
216
217 const bool removed = replacement == islands.cend() || less( it->first, replacement->first );
218
219 if( removed || bool( replacement->second.value ) )
220 {
221 aChanges.changedIslands.push_back( it->first );
222
223 for( const KIID& id : it->second.value.items )
224 {
225 const ITEM_KEY item{ id, it->first.inst };
226 m_islandOf.erase( item );
227 m_neighborsOf.erase( item );
228 aChanges.changedItems.push_back( item );
229 }
230 }
231
232 if( removed )
233 it = m_islands.erase( it );
234 else
235 ++it;
236 }
237
238 auto hint = m_islands.cbegin();
239
240 for( auto& [key, entry] : islands )
241 {
242 if( entry.value )
243 {
244 aChanges.changedIslands.push_back( key );
245 std::unordered_map<KIID, std::vector<KIID>*> neighborLists;
246
247 // An endpoint's map value remains stable while its island's adjacency is appended
248 const auto appendNeighbor = [&]( const KIID& item, const KIID& neighbor )
249 {
250 auto [found, inserted] = neighborLists.try_emplace( item, nullptr );
251
252 if( inserted )
253 found->second = &m_neighborsOf[{ item, key.inst }];
254
255 found->second->push_back( neighbor );
256 };
257
258 for( const auto& [first, second] : entry.value->adjacency )
259 {
260 appendNeighbor( first, second );
261 appendNeighbor( second, first );
262 }
263
264 for( const KIID& id : entry.value->items )
265 {
266 const ITEM_KEY item{ id, key.inst };
267 const auto neighbors = m_neighborsOf.find( item );
268
269 if( neighbors != m_neighborsOf.end() )
270 {
271 auto& values = neighbors->second;
272 std::sort( values.begin(), values.end() );
273 values.erase( std::unique( values.begin(), values.end() ), values.end() );
274 }
275
276 m_islandOf.insert_or_assign( item, key );
277 aChanges.changedItems.push_back( item );
278 }
279 }
280
281 const auto current = m_islands.try_emplace( hint, key );
282 hint = std::next( current );
283 auto& published = current->second;
284 published.islandVersion = entry.islandVersion;
285 published.recordVersion = entry.recordVersion;
286
287 if( entry.value )
288 published.value = std::move( *entry.value );
289 }
290
291 const auto areaItems = []( const RULE_AREA_RESULT& value )
292 {
293 std::vector<KIID> items;
294 items.reserve( value.containedItems.size() + value.attachedDirectives.size() );
295 std::set_union( value.containedItems.begin(), value.containedItems.end(), value.attachedDirectives.begin(),
296 value.attachedDirectives.end(), std::back_inserter( items ) );
297 return items;
298 };
299
300 for( auto it = m_areas.begin(); it != m_areas.end(); )
301 {
302 const auto updatedArea = areas.find( it->first );
303 const bool removed = updatedArea == areas.end();
304
305 if( removed || updatedArea->second.value.has_value() )
306 {
307 aChanges.changedRuleAreas.push_back( it->first );
308
309 for( const KIID& id : areaItems( it->second.value ) )
310 {
311 const ITEM_KEY item{ id, it->first.inst };
312 auto found = m_ruleAreasOf.find( item );
313
314 if( found != m_ruleAreasOf.end() )
315 {
316 std::erase( found->second, it->first.item );
317
318 if( found->second.empty() )
319 m_ruleAreasOf.erase( found );
320 }
321
322 aChanges.changedItems.push_back( item );
323 }
324 }
325
326 if( removed )
327 it = m_areas.erase( it );
328 else
329 ++it;
330 }
331
332 for( auto& [key, entry] : areas )
333 {
334 if( entry.value )
335 {
336 aChanges.changedRuleAreas.push_back( key );
337
338 for( const KIID& id : areaItems( *entry.value ) )
339 {
340 const ITEM_KEY item{ id, key.inst };
341 auto& memberships = m_ruleAreasOf[item];
342 memberships.insert( std::lower_bound( memberships.begin(), memberships.end(), key.item ), key.item );
343 aChanges.changedItems.push_back( item );
344 }
345 }
346
347 auto& published = m_areas.try_emplace( key ).first->second;
348 published.version = entry.version;
349
350 if( entry.value )
351 published.value = std::move( *entry.value );
352 }
353
354 const auto canonicalize = [&]( auto& values )
355 {
356 std::ranges::sort( values, KEY_LESS{ m_keys } );
357 values.erase( std::ranges::unique( values ).begin(), values.end() );
358 };
359 canonicalize( aChanges.changedItems );
360 canonicalize( aChanges.changedIslands );
361 canonicalize( aChanges.changedRuleAreas );
363 m_instances = std::move( instances );
364 m_sourceVersion = sourceVersion;
365}
366
368{
369 m_islands.clear();
370 m_areas.clear();
371 m_islandOf.clear();
372 m_ruleAreasOf.clear();
373 m_neighborsOf.clear();
374 m_screenRevisions.clear();
375 m_instances.clear();
376 m_sourceVersion = 0;
377}
378} // namespace SCH_CONNECTIVITY
const char * name
Definition kiid.h:46
std::set< INST_ID > m_instances
AUXILIARY(SESSION_KEYS &aKeys)
std::map< SCREEN_ID, uint64_t > m_screenRevisions
void Update(std::span< const FRAME_INSTANCE > aFrame, const INPUT_STORE &aInputs, const RECORD_STORE::RECORD_CACHE &aRecords, CHANGE_SET &aChanges)
Publish the changed islands and rule areas.
const auto & Entries() const
Definition conn_cache.h:105
Main-thread extraction cache.
Definition conn_inputs.h:41
const AREA_CACHE::ENTRY * Area(const ITEM_KEY &aKey) const
Definition conn_inputs.h:93
const TEXT_CACHE::ENTRY * Text(const ITEM_KEY &aKey) const
Definition conn_inputs.h:92
const SCREEN_CACHE::ENTRY * FindScreen(SCREEN_ID aScreen) const
Definition conn_inputs.h:94
const ISLAND_CACHE::ENTRY * FindIslands(SCREEN_ID aScreen, UNIT_VIEW aUnits) const
const INSTANCE_CACHE::ENTRY * FindInstance(INST_ID aInstance) const
Definition conn_inputs.h:95
const std::map< SCREEN_ID, uint64_t > & ScreenRevisions() const
const FACT_CACHE::ENTRY * Fact(const SOURCE_KEY &aKey) const
Definition conn_inputs.h:91
CACHE_TABLE< RECORD_KEY, ISLAND_RECORD, KEY_LESS > RECORD_CACHE
Session IDs are dense handles, never a canonical ordering.
Definition conn_keys.h:146
Value keys and the key session of the schematic connectivity engine.
uint32_t INST_ID
Session handle of a sheet instance KIID_PATH.
Definition conn_keys.h:41
Difference between two publications.
std::vector< ITEM_KEY > changedRuleAreas
Removed, replaced and new rule area keys.
std::vector< RECORD_KEY > changedIslands
Removed, replaced and new island keys.
std::vector< ITEM_KEY > changedItems
Items whose row, island, rule area, source or text changed.
One entry of the captured hierarchy.
Definition conn_frame.h:48
SCREEN_ID screen
The screen that the instance shows. Many instances can share it.
Definition conn_frame.h:50
INST_ID instance
The session handle of the instance KIID_PATH.
Definition conn_frame.h:36
std::vector< std::pair< KIID, KIID > > ncContacts
std::vector< std::pair< KIID, KIID > > adjacency
std::vector< std::pair< KIID, KIID > > busEntryLinks
std::vector< KIID > items
std::map< KIID, uint8_t > dangling
The smallest set of items on one screen that the drawing connects.
std::vector< std::pair< KIID, KIID > > adjacency
std::vector< std::pair< KIID, KIID > > busEntryLinks
std::vector< std::pair< KIID, KIID > > ncContacts
std::vector< KIID > items
std::map< KIID, uint8_t > dangling
KIID anchor
The smallest KIID in items, which names the island.
A value copy of one connectable item, or of one group of pins that share a number and a position.
Definition conn_facts.h:101
KIID id
The item KIID, or the smallest member KIID of a pin group.
Definition conn_facts.h:102
std::vector< PIN_FACT > pins
Definition conn_facts.h:108
One item or pin in one sheet instance.
Definition conn_keys.h:77
Orders keys by value through SESSION_KEYS::Less().
Definition conn_keys.h:255
Orders name handles by UTF-8 value.
Definition conn_keys.h:246
One island in one sheet instance.
Definition conn_keys.h:87
std::vector< NAME_ID > netclasses
KIBIS_PIN * pin