KiCad PCB EDA Suite
Loading...
Searching...
No Matches
zone_filler_tool.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 (C) 2014-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21#include <cstdint>
22#include <thread>
23#include <zone.h>
25#include <board_commit.h>
26#include <footprint.h>
27#include <pcb_track.h>
28#include <pad.h>
29#include <pcb_group.h>
31#include <drc/drc_engine.h>
32#include <progress_reporter.h>
33#include <widgets/wx_infobar.h>
35#include <wx/event.h>
36#include <wx/hyperlink.h>
37#include <tool/tool_manager.h>
38#include <tool/actions.h>
40#include "pcb_actions.h"
41#include "zone_filler_tool.h"
42#include "zone_filler.h"
43#include "teardrop/teardrop.h"
44#include <core/profile.h>
45#include <pcb_generator.h>
47
53
54
58
59
61{
62}
63
64
65std::vector<PCB_GENERATOR*> ZONE_FILLER_TOOL::regenerateDirtyGenerators( BOARD_COMMIT& aCommit )
66{
67 std::vector<PCB_GENERATOR*> regenerated;
68
69 BOARD* brd = board();
70 GENERATOR_TOOL* genTool = m_toolMgr ? m_toolMgr->GetTool<GENERATOR_TOOL>() : nullptr;
71
72 if( !brd || !genTool )
73 return regenerated;
74
75 for( PCB_GENERATOR* gen : brd->Generators() )
76 {
77 if( !gen->IsDirty() )
78 continue;
79
80 gen->EditStart( genTool, brd, &aCommit );
81 gen->Update( genTool, brd, &aCommit );
82 gen->EditFinish( genTool, brd, &aCommit );
83 regenerated.push_back( gen );
84 }
85
86 return regenerated;
87}
88
89
90void ZONE_FILLER_TOOL::refillAroundGenerators( const std::vector<PCB_GENERATOR*>& aRegenerated )
91{
92 if( aRegenerated.empty() )
93 return;
94
95 BOARD* brd = board();
96
97 if( !brd )
98 return;
99
100 // Build a list of zones that need refill after wacking the generators
101 // and deduplicate
102 std::set<ZONE*> uniqueZones;
103
104 for( PCB_GENERATOR* gen : aRegenerated )
105 {
106 std::vector<ZONE*> zones = gen->GetZonesNeedingRefillAfterUpdate();
107
108 for( ZONE* zone : zones )
109 uniqueZones.insert( zone );
110 }
111
112 if( uniqueZones.empty() )
113 return;
114
115 std::vector<ZONE*> toRefill( uniqueZones.begin(), uniqueZones.end() );
116
117 brd->IncrementTimeStamp(); // make the just-pushed children visible to the refill
118
119 BOARD_COMMIT refillCommit( this );
120 ZONE_FILLER refiller( brd, &refillCommit );
121
122 if( refiller.Fill( toRefill ) )
123 refillCommit.Push( wxEmptyString, APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
124 else
125 refillCommit.Revert();
126}
127
128
129void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
130{
131 if( !getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty || m_fillInProgress )
132 return;
133
134 m_fillInProgress = true;
135
136 std::vector<ZONE*> toFill;
137
138 for( ZONE* zone : board()->Zones() )
139 toFill.push_back( zone );
140
141 BOARD_COMMIT commit( this );
142 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
143
144 m_filler = std::make_unique<ZONE_FILLER>( frame()->GetBoard(), &commit );
145
146 if( aReporter )
147 {
148 m_filler->SetProgressReporter( aReporter );
149 }
150 else
151 {
152 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Check Zones" ), 4, PR_CAN_ABORT );
153 m_filler->SetProgressReporter( reporter.get() );
154 }
155
156 if( m_filler->Fill( toFill, true, aCaller ) )
157 {
158 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
159 getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty = false;
160 }
161 else
162 {
163 commit.Revert();
164 }
165
167
168 m_fillInProgress = false;
169 m_filler.reset( nullptr );
170}
171
172
174{
175 canvas()->SetFocus();
176 canvas()->Unbind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
177}
178
179
180void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter, bool aHeadless )
181{
182 if( m_fillInProgress )
183 return;
184
185 m_fillInProgress = true;
186
187 PCB_EDIT_FRAME* frame = nullptr;
188 if( !aHeadless )
190
191 BOARD_COMMIT commit( this );
192 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
193 TEARDROP_MANAGER teardropMgr( board(), m_toolMgr );
194 std::vector<ZONE*> toFill;
195
196 teardropMgr.UpdateTeardrops( commit, nullptr, nullptr, true /* forceFullUpdate */ );
197
198 board()->IncrementTimeStamp(); // Clear caches
199
200 for( ZONE* zone : board()->Zones() )
201 toFill.push_back( zone );
202
203 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
204
205 if( !aHeadless && !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
206 {
207 WX_INFOBAR* infobar = frame->GetInfoBar();
208
209 infobar->RemoveAllButtons();
210 infobar->AddLink( _( "Show DRC rules" ),
211 [frame]( wxHyperlinkEvent& aEvent )
212 {
213 frame->ShowBoardSetupDialog( _( "Rules" ) );
214 } );
215
216 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), 10000,
217 wxICON_WARNING );
218 }
219
220 if( aReporter )
221 {
222 m_filler->SetProgressReporter( aReporter );
223 }
224 else if( !aHeadless )
225 {
226 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Fill All Zones" ), 5, PR_CAN_ABORT );
227 m_filler->SetProgressReporter( reporter.get() );
228 }
229
230 if( m_filler->Fill( toFill ) )
231 {
232 if( m_filler->GetProgressReporter() )
233 m_filler->GetProgressReporter()->AdvancePhase();
234
235 // First pass on allowing generators like via-stitch to update
236 board()->OnZonesFilled( toFill );
237 std::vector<PCB_GENERATOR*> regenerated = regenerateDirtyGenerators( commit );
238
239 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
240
241 if( !aHeadless )
242 frame->m_ZoneFillsDirty = false;
243
244 // Refill again as generators like via stitches can cause punch outs
245 refillAroundGenerators( regenerated );
246 }
247 else
248 {
249 commit.Revert();
250 }
251
252 PostFillRefresh( aHeadless );
253
254 if( !aHeadless && m_filler->IsDebug() )
255 frame->UpdateUserInterface();
256
257 m_fillInProgress = false;
258 m_filler.reset( nullptr );
259
260 if( !aHeadless )
261 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
262 // here doesn't work, so we delay it to an idle event.
263 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
264}
265
266
268{
270 std::vector<ZONE*> toFill;
271
272 for( ZONE* zone : board()->Zones() )
273 {
274 if( !zone->IsFilled() || m_dirtyZoneIDs.count( zone->m_Uuid ) )
275 toFill.push_back( zone );
276 }
277
278 if( toFill.empty() )
279 return 0;
280
281 if( m_fillInProgress )
282 return 0;
283
284 int64_t startTime = GetRunningMicroSecs();
285 m_fillInProgress = true;
286
287 m_dirtyZoneIDs.clear();
288
289 board()->IncrementTimeStamp(); // Clear caches
290
291 BOARD_COMMIT commit( this );
292 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
293 int pts = 0;
294
295 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
296
297 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
298 {
299 WX_INFOBAR* infobar = frame->GetInfoBar();
300
301 infobar->RemoveAllButtons();
302
303 infobar->AddLink( _( "Show DRC rules" ),
304 [frame]( wxHyperlinkEvent& aLocEvent )
305 {
306 frame->ShowBoardSetupDialog( _( "Rules" ) );
307 } );
308
309 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), 10000,
310 wxICON_WARNING );
311 }
312
313 for( ZONE* zone : toFill )
314 {
315 zone->GetLayerSet().RunOnLayers(
316 [&]( PCB_LAYER_ID layer )
317 {
318 pts += zone->GetFilledPolysList( layer )->FullPointCount();
319 } );
320
321 if( pts > 1000 )
322 {
323 wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() );
324
325 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame, title, 5, PR_CAN_ABORT );
326 m_filler->SetProgressReporter( reporter.get() );
327 break;
328 }
329 }
330
331 bool filled = m_filler->Fill( toFill );
332 std::vector<PCB_GENERATOR*> regenerated;
333
334 if( filled )
335 {
336 board()->OnZonesFilled( toFill );
337 regenerated = regenerateDirtyGenerators( commit );
338
339 commit.Push( _( "Auto-fill Zone(s)" ), APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
340 }
341 else
342 {
343 commit.Revert();
344 }
345
346 if( filled )
347 refillAroundGenerators( regenerated );
348
351
352 if( GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
353 {
354 WX_INFOBAR* infobar = frame->GetInfoBar();
355
356 infobar->RemoveAllButtons();
357 infobar->AddLink( _( "Open Preferences" ),
358 [this]( wxHyperlinkEvent& )
359 {
360 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ), _( "PCB Editor" ) );
361 } );
362
363 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences if it becomes "
364 "too slow." ),
365 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
366 }
367
368 if( m_filler->IsDebug() )
369 frame->UpdateUserInterface();
370
371 m_fillInProgress = false;
372 m_filler.reset( nullptr );
373
374 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
375 // here doesn't work, so we delay it to an idle event.
376 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
377
378 return 0;
379}
380
381
383{
384 if( m_fillInProgress )
385 {
386 wxBell();
387 return -1;
388 }
389
390 std::vector<ZONE*> toFill;
391
392 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
393 {
394 toFill.push_back( passedZone );
395 }
396 else
397 {
398 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
399 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
400 {
401 } );
402
403 for( EDA_ITEM* item : sel )
404 {
405 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
406 toFill.push_back( zone );
407 }
408 }
409
410 // Bail out of the filler if there is nothing to fill
411 if( toFill.empty() )
412 {
413 wxBell();
414 return -1;
415 }
416
417 m_fillInProgress = true;
418
419 BOARD_COMMIT commit( this );
420 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
421
422 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
423
424 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5, PR_CAN_ABORT );
425 m_filler->SetProgressReporter( reporter.get() );
426
427 if( m_filler->Fill( toFill ) )
428 {
429 reporter->AdvancePhase();
430 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
431 }
432 else
433 {
434 commit.Revert();
435 }
436
438
439 m_fillInProgress = false;
440 m_filler.reset( nullptr );
441 return 0;
442}
443
444
446{
447 FillAllZones( frame() );
448 return 0;
449}
450
451
453{
454 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
455 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
456 {
457 } );
458
459 std::vector<ZONE*> toUnfill;
460
461 for( EDA_ITEM* item : sel )
462 {
463 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
464 toUnfill.push_back( zone );
465 }
466
467 // Bail out if there are no zones
468 if( toUnfill.empty() )
469 {
470 wxBell();
471 return -1;
472 }
473
474 BOARD_COMMIT commit( this );
475
476 for( ZONE* zone : toUnfill )
477 {
478 commit.Modify( zone );
479
480 zone->UnFill();
481 }
482
483 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
484
485 refresh();
486
487 return 0;
488}
489
490
492{
493 BOARD_COMMIT commit( this );
494
495 for( ZONE* zone : board()->Zones() )
496 {
497 commit.Modify( zone );
498
499 zone->UnFill();
500 }
501
502 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
503
504 refresh();
505
506 return 0;
507}
508
509
511{
513 return m_filler->GetProgressReporter();
514 else
515 return nullptr;
516}
517
518
520{
521 rebuildConnectivity( aHeadless );
522
523 if( !aHeadless )
524 refresh();
525}
526
527
529{
532 if( !aHeadless )
534}
535
536
538{
539 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
540 // they won't be found in the view layer's itemset for re-painting.
542 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
543 {
544 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
545 {
546 return via->GetRemoveUnconnected();
547 }
548 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
549 {
550 return pad->GetRemoveUnconnected();
551 }
552
553 return false;
554 } );
555
556 canvas()->Refresh();
557}
558
559
561{
562 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
564 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
566
567 // Don't include zoneFillDirty; that's a system action not a user action
568}
569
570
#define ZONE_FILL_OP
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
const GENERATORS & Generators() const
Definition board.h:476
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition board.cpp:364
void IncrementTimeStamp()
Definition board.cpp:446
void OnZonesFilled(const std::vector< ZONE * > &aZones)
Notify the board that the listed zones were just refilled.
Definition board.cpp:3964
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
void SetFocus() override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition actions.h:347
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
Handle actions specific to filling copper zones.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
void UpdateAllItemsConditionally(int aUpdateFlags, std::function< bool(VIEW_ITEM *)> aCondition)
Update items in the view according to the given flags and condition.
Definition view.cpp:1719
Definition pad.h:61
static TOOL_ACTION zoneFillAll
static TOOL_ACTION zoneFill
static TOOL_ACTION zoneUnfill
static TOOL_ACTION zoneUnfillAll
static TOOL_ACTION zoneFillDirty
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
void RedrawRatsnest()
Return the bounding box of the view that should be used if model is not valid.
The main frame for Pcbnew.
The selection tool: currently supports:
T * frame() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
A progress reporter interface for use in multi-threaded environments.
TEARDROP_MANAGER manage and build teardrop areas A teardrop area is a polygonal area (a copper ZONE) ...
Definition teardrop.h:86
void UpdateTeardrops(BOARD_COMMIT &aCommit, const std::vector< BOARD_ITEM * > *dirtyPadsAndVias, const std::set< PCB_TRACK * > *dirtyTracks, bool aForceFullUpdate=false)
Update teardrops on a list of items.
Definition teardrop.cpp:422
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
Generic, UI-independent tool event.
Definition tool_event.h:167
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
void ShowMessageFor(const wxString &aMessage, int aTime, int aFlags=wxICON_INFORMATION, MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the infobar with the provided message and icon for a specific period of time.
void AddLink(const wxString &aLinkText, const std::function< void(wxHyperlinkEvent &)> &aFn)
Add a hypertext link to the infobar.
void CheckAllZones(wxWindow *aCaller, PROGRESS_REPORTER *aReporter=nullptr)
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int ZoneFill(const TOOL_EVENT &aEvent)
void FillAllZones(wxWindow *aCaller, PROGRESS_REPORTER *aReporter=nullptr, bool aHeadless=false)
void PostFillRefresh(bool aHeadless=false)
Run the shared post-fill refresh once a fill commit has been pushed with connectivity updates skipped...
std::unique_ptr< ZONE_FILLER > m_filler
PROGRESS_REPORTER * GetProgressReporter()
int ZoneUnfillAll(const TOOL_EVENT &aEvent)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
std::vector< PCB_GENERATOR * > regenerateDirtyGenerators(BOARD_COMMIT &aCommit)
Kicks all the generators as they may need to update after a zone fill update.
std::set< KIID > m_dirtyZoneIDs
int ZoneFillAll(const TOOL_EVENT &aEvent)
int ZoneFillDirty(const TOOL_EVENT &aEvent)
void singleShotRefocus(wxIdleEvent &)
< Refocus on an idle event (used after the Progress Reporter messes up the focus).
int ZoneUnfill(const TOOL_EVENT &aEvent)
void refillAroundGenerators(const std::vector< PCB_GENERATOR * > &aRegenerated)
Triggers refill of zones near the given generators.
void rebuildConnectivity(bool aHeadless=false)
bool Fill(const std::vector< ZONE * > &aZones, bool aCheck=false, wxWindow *aParent=nullptr)
Fills the given list of zones.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ ALL
All except INITIAL_ADD.
Definition view_item.h:55
Class to handle a set of BOARD_ITEMs.
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
#define APPEND_UNDO
Definition sch_commit.h:39
IbisParser parser & reporter
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
#define PR_CAN_ABORT
#define ZONE_FILLER_TOOL_NAME