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 WX_INFOBAR* infobar = frame->GetInfoBar();
353
354 // Only hint after a completed fill, and never over another tool's message and links
355 if( filled && !infobar->IsShownOnScreen() && GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
356 {
357 infobar->RemoveAllButtons();
358 infobar->AddLink( _( "Open Preferences" ),
359 [this]( wxHyperlinkEvent& )
360 {
361 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ), _( "PCB Editor" ) );
362 } );
363
364 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences if it becomes "
365 "too slow." ),
366 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
367 }
368
369 if( m_filler->IsDebug() )
370 frame->UpdateUserInterface();
371
372 m_fillInProgress = false;
373 m_filler.reset( nullptr );
374
375 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
376 // here doesn't work, so we delay it to an idle event.
377 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
378
379 return 0;
380}
381
382
384{
385 if( m_fillInProgress )
386 {
387 wxBell();
388 return -1;
389 }
390
391 std::vector<ZONE*> toFill;
392
393 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
394 {
395 toFill.push_back( passedZone );
396 }
397 else
398 {
399 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
400 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
401 {
402 } );
403
404 for( EDA_ITEM* item : sel )
405 {
406 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
407 toFill.push_back( zone );
408 }
409 }
410
411 // Bail out of the filler if there is nothing to fill
412 if( toFill.empty() )
413 {
414 wxBell();
415 return -1;
416 }
417
418 m_fillInProgress = true;
419
420 BOARD_COMMIT commit( this );
421 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
422
423 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
424
425 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5, PR_CAN_ABORT );
426 m_filler->SetProgressReporter( reporter.get() );
427
428 if( m_filler->Fill( toFill ) )
429 {
430 reporter->AdvancePhase();
431 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
432 }
433 else
434 {
435 commit.Revert();
436 }
437
439
440 m_fillInProgress = false;
441 m_filler.reset( nullptr );
442 return 0;
443}
444
445
447{
448 FillAllZones( frame() );
449 return 0;
450}
451
452
454{
455 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
456 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
457 {
458 } );
459
460 std::vector<ZONE*> toUnfill;
461
462 for( EDA_ITEM* item : sel )
463 {
464 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
465 toUnfill.push_back( zone );
466 }
467
468 // Bail out if there are no zones
469 if( toUnfill.empty() )
470 {
471 wxBell();
472 return -1;
473 }
474
475 BOARD_COMMIT commit( this );
476
477 for( ZONE* zone : toUnfill )
478 {
479 commit.Modify( zone );
480
481 zone->UnFill();
482 }
483
484 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
485
486 refresh();
487
488 return 0;
489}
490
491
493{
494 BOARD_COMMIT commit( this );
495
496 for( ZONE* zone : board()->Zones() )
497 {
498 commit.Modify( zone );
499
500 zone->UnFill();
501 }
502
503 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
504
505 refresh();
506
507 return 0;
508}
509
510
512{
514 return m_filler->GetProgressReporter();
515 else
516 return nullptr;
517}
518
519
521{
522 rebuildConnectivity( aHeadless );
523
524 if( !aHeadless )
525 refresh();
526}
527
528
530{
533 if( !aHeadless )
535}
536
537
539{
540 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
541 // they won't be found in the view layer's itemset for re-painting.
543 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
544 {
545 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
546 {
547 return via->GetRemoveUnconnected();
548 }
549 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
550 {
551 return pad->GetRemoveUnconnected();
552 }
553
554 return false;
555 } );
556
557 canvas()->Refresh();
558}
559
560
562{
563 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
565 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
567
568 // Don't include zoneFillDirty; that's a system action not a user action
569}
570
571
#define ZONE_FILL_OP
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
void Revert() override
Revert the commit by restoring the modified items state.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const GENERATORS & Generators() const
Definition board.h:484
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:367
void IncrementTimeStamp()
Definition board.cpp:449
void OnZonesFilled(const std::vector< ZONE * > &aZones)
Notify the board that the listed zones were just refilled.
Definition board.cpp:3985
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
Definition actions.h:349
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:1759
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 KIGFX::VIEW instance used in the panel.
void RedrawRatsnest()
Force refresh of the ratsnest visual representation.
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:434
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:484
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
Set up handlers for various 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:708
#define PR_CAN_ABORT
#define ZONE_FILLER_TOOL_NAME