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
51
52
56
57
59{
60}
61
62
63void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
64{
65 if( !getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty || m_fillInProgress )
66 return;
67
68 m_fillInProgress = true;
69
70 std::vector<ZONE*> toFill;
71
72 for( ZONE* zone : board()->Zones() )
73 toFill.push_back( zone );
74
75 BOARD_COMMIT commit( this );
76 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
77
78 m_filler = std::make_unique<ZONE_FILLER>( frame()->GetBoard(), &commit );
79
80 if( aReporter )
81 {
82 m_filler->SetProgressReporter( aReporter );
83 }
84 else
85 {
86 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Check Zones" ), 4, PR_CAN_ABORT );
87 m_filler->SetProgressReporter( reporter.get() );
88 }
89
90 if( m_filler->Fill( toFill, true, aCaller ) )
91 {
92 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
93 getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty = false;
94 }
95 else
96 {
97 commit.Revert();
98 }
99
101
102 m_fillInProgress = false;
103 m_filler.reset( nullptr );
104}
105
106
108{
109 canvas()->SetFocus();
110 canvas()->Unbind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
111}
112
113
114void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter, bool aHeadless )
115{
116 if( m_fillInProgress )
117 return;
118
119 m_fillInProgress = true;
120
121 PCB_EDIT_FRAME* frame = nullptr;
122 if( !aHeadless )
124
125 BOARD_COMMIT commit( this );
126 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
127 TEARDROP_MANAGER teardropMgr( board(), m_toolMgr );
128 std::vector<ZONE*> toFill;
129
130 teardropMgr.UpdateTeardrops( commit, nullptr, nullptr, true /* forceFullUpdate */ );
131
132 board()->IncrementTimeStamp(); // Clear caches
133
134 for( ZONE* zone : board()->Zones() )
135 toFill.push_back( zone );
136
137 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
138
139 if( !aHeadless && !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
140 {
141 WX_INFOBAR* infobar = frame->GetInfoBar();
142 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ), wxEmptyString );
143
144 button->Bind( wxEVT_COMMAND_HYPERLINK,
145 std::function<void( wxHyperlinkEvent& aEvent )>(
146 [frame]( wxHyperlinkEvent& aEvent )
147 {
148 frame->ShowBoardSetupDialog( _( "Rules" ) );
149 } ) );
150
151 infobar->RemoveAllButtons();
152 infobar->AddButton( button );
153
154 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), 10000,
155 wxICON_WARNING );
156 }
157
158 if( aReporter )
159 {
160 m_filler->SetProgressReporter( aReporter );
161 }
162 else if( !aHeadless )
163 {
164 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Fill All Zones" ), 5, PR_CAN_ABORT );
165 m_filler->SetProgressReporter( reporter.get() );
166 }
167
168 if( m_filler->Fill( toFill ) )
169 {
170 if( m_filler->GetProgressReporter() )
171 m_filler->GetProgressReporter()->AdvancePhase();
172
173 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
174 if( !aHeadless )
175 frame->m_ZoneFillsDirty = false;
176 }
177 else
178 {
179 commit.Revert();
180 }
181
182 PostFillRefresh( aHeadless );
183
184 if( !aHeadless && m_filler->IsDebug() )
185 frame->UpdateUserInterface();
186
187 m_fillInProgress = false;
188 m_filler.reset( nullptr );
189
190 if( !aHeadless )
191 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
192 // here doesn't work, so we delay it to an idle event.
193 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
194}
195
196
198{
200 std::vector<ZONE*> toFill;
201
202 for( ZONE* zone : board()->Zones() )
203 {
204 if( !zone->IsFilled() || m_dirtyZoneIDs.count( zone->m_Uuid ) )
205 toFill.push_back( zone );
206 }
207
208 if( toFill.empty() )
209 return 0;
210
211 if( m_fillInProgress )
212 return 0;
213
214 int64_t startTime = GetRunningMicroSecs();
215 m_fillInProgress = true;
216
217 m_dirtyZoneIDs.clear();
218
219 board()->IncrementTimeStamp(); // Clear caches
220
221 BOARD_COMMIT commit( this );
222 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
223 int pts = 0;
224
225 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
226
227 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
228 {
229 WX_INFOBAR* infobar = frame->GetInfoBar();
230 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ), wxEmptyString );
231
232 button->Bind( wxEVT_COMMAND_HYPERLINK,
233 std::function<void( wxHyperlinkEvent& aLocEvent )>(
234 [frame]( wxHyperlinkEvent& aLocEvent )
235 {
236 frame->ShowBoardSetupDialog( _( "Rules" ) );
237 } ) );
238
239 infobar->RemoveAllButtons();
240 infobar->AddButton( button );
241
242 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), 10000,
243 wxICON_WARNING );
244 }
245
246 for( ZONE* zone : toFill )
247 {
248 zone->GetLayerSet().RunOnLayers(
249 [&]( PCB_LAYER_ID layer )
250 {
251 pts += zone->GetFilledPolysList( layer )->FullPointCount();
252 } );
253
254 if( pts > 1000 )
255 {
256 wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() );
257
258 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame, title, 5, PR_CAN_ABORT );
259 m_filler->SetProgressReporter( reporter.get() );
260 break;
261 }
262 }
263
264 if( m_filler->Fill( toFill ) )
265 commit.Push( _( "Auto-fill Zone(s)" ), APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
266 else
267 commit.Revert();
268
270
271 if( GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
272 {
273 WX_INFOBAR* infobar = frame->GetInfoBar();
274
275 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ),
276 wxEmptyString );
277
278 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& )>(
279 [this]( wxHyperlinkEvent& )
280 {
281 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ), _( "PCB Editor" ) );
282 } ) );
283
284 infobar->RemoveAllButtons();
285 infobar->AddButton( button );
286 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences if it becomes "
287 "too slow." ),
288 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
289 }
290
291 if( m_filler->IsDebug() )
292 frame->UpdateUserInterface();
293
294 m_fillInProgress = false;
295 m_filler.reset( nullptr );
296
297 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
298 // here doesn't work, so we delay it to an idle event.
299 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
300
301 return 0;
302}
303
304
306{
307 if( m_fillInProgress )
308 {
309 wxBell();
310 return -1;
311 }
312
313 std::vector<ZONE*> toFill;
314
315 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
316 {
317 toFill.push_back( passedZone );
318 }
319 else
320 {
321 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
322 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
323 {
324 } );
325
326 for( EDA_ITEM* item : sel )
327 {
328 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
329 toFill.push_back( zone );
330 }
331 }
332
333 // Bail out of the filler if there is nothing to fill
334 if( toFill.empty() )
335 {
336 wxBell();
337 return -1;
338 }
339
340 m_fillInProgress = true;
341
342 BOARD_COMMIT commit( this );
343 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
344
345 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
346
347 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5, PR_CAN_ABORT );
348 m_filler->SetProgressReporter( reporter.get() );
349
350 if( m_filler->Fill( toFill ) )
351 {
352 reporter->AdvancePhase();
353 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
354 }
355 else
356 {
357 commit.Revert();
358 }
359
361
362 m_fillInProgress = false;
363 m_filler.reset( nullptr );
364 return 0;
365}
366
367
369{
370 FillAllZones( frame() );
371 return 0;
372}
373
374
376{
377 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
378 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
379 {
380 } );
381
382 std::vector<ZONE*> toUnfill;
383
384 for( EDA_ITEM* item : sel )
385 {
386 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
387 toUnfill.push_back( zone );
388 }
389
390 // Bail out if there are no zones
391 if( toUnfill.empty() )
392 {
393 wxBell();
394 return -1;
395 }
396
397 BOARD_COMMIT commit( this );
398
399 for( ZONE* zone : toUnfill )
400 {
401 commit.Modify( zone );
402
403 zone->UnFill();
404 }
405
406 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
407
408 refresh();
409
410 return 0;
411}
412
413
415{
416 BOARD_COMMIT commit( this );
417
418 for( ZONE* zone : board()->Zones() )
419 {
420 commit.Modify( zone );
421
422 zone->UnFill();
423 }
424
425 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
426
427 refresh();
428
429 return 0;
430}
431
432
434{
436 return m_filler->GetProgressReporter();
437 else
438 return nullptr;
439}
440
441
443{
444 rebuildConnectivity( aHeadless );
445
446 if( !aHeadless )
447 refresh();
448}
449
450
452{
455 if( !aHeadless )
457}
458
459
461{
462 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
463 // they won't be found in the view layer's itemset for re-painting.
465 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
466 {
467 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
468 {
469 return via->GetRemoveUnconnected();
470 }
471 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
472 {
473 return pad->GetRemoveUnconnected();
474 }
475
476 return false;
477 } );
478
479 canvas()->Refresh();
480}
481
482
484{
485 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
487 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
489
490 // Don't include zoneFillDirty; that's a system action not a user action
491}
492
493
#define SKIP_CONNECTIVITY
#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.
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:201
void IncrementTimeStamp()
Definition board.cpp:283
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:96
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition actions.h:345
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
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:1702
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:90
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:225
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:77
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 AddButton(wxButton *aButton)
Add an already created button 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.
void refresh()
Set up handlers for various events.
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 rebuildConnectivity(bool aHeadless=false)
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 APPEND_UNDO
Definition sch_commit.h:37
IbisParser parser & reporter
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
#define PR_CAN_ABORT
#define ZONE_FILLER_TOOL_NAME