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, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25#include <cstdint>
26#include <thread>
27#include <zone.h>
29#include <board_commit.h>
30#include <footprint.h>
31#include <pcb_track.h>
32#include <pad.h>
33#include <pcb_group.h>
35#include <drc/drc_engine.h>
36#include <progress_reporter.h>
37#include <widgets/wx_infobar.h>
39#include <wx/event.h>
40#include <wx/hyperlink.h>
41#include <tool/tool_manager.h>
42#include <tool/actions.h>
44#include "pcb_actions.h"
45#include "zone_filler_tool.h"
46#include "zone_filler.h"
47#include "teardrop/teardrop.h"
48#include <core/profile.h>
49
55
56
60
61
63{
64}
65
66
67void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
68{
69 if( !getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty || m_fillInProgress )
70 return;
71
72 m_fillInProgress = true;
73
74 std::vector<ZONE*> toFill;
75
76 for( ZONE* zone : board()->Zones() )
77 toFill.push_back( zone );
78
79 BOARD_COMMIT commit( this );
80 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
81
82 m_filler = std::make_unique<ZONE_FILLER>( frame()->GetBoard(), &commit );
83
84 if( aReporter )
85 {
86 m_filler->SetProgressReporter( aReporter );
87 }
88 else
89 {
90 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Check Zones" ), 4, PR_CAN_ABORT );
91 m_filler->SetProgressReporter( reporter.get() );
92 }
93
94 if( m_filler->Fill( toFill, true, aCaller ) )
95 {
96 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
97 getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty = false;
98 }
99 else
100 {
101 commit.Revert();
102 }
103
105 refresh();
106
107 m_fillInProgress = false;
108 m_filler.reset( nullptr );
109}
110
111
113{
114 canvas()->SetFocus();
115 canvas()->Unbind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
116}
117
118
119void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter, bool aHeadless )
120{
121 if( m_fillInProgress )
122 return;
123
124 m_fillInProgress = true;
125
126 PCB_EDIT_FRAME* frame = nullptr;
127 if( !aHeadless )
129
130 BOARD_COMMIT commit( this );
131 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
132 TEARDROP_MANAGER teardropMgr( board(), m_toolMgr );
133 std::vector<ZONE*> toFill;
134
135 teardropMgr.UpdateTeardrops( commit, nullptr, nullptr, true /* forceFullUpdate */ );
136
137 board()->IncrementTimeStamp(); // Clear caches
138
139 for( ZONE* zone : board()->Zones() )
140 toFill.push_back( zone );
141
142 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
143
144 if( !aHeadless && !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
145 {
146 WX_INFOBAR* infobar = frame->GetInfoBar();
147 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ), wxEmptyString );
148
149 button->Bind( wxEVT_COMMAND_HYPERLINK,
150 std::function<void( wxHyperlinkEvent& aEvent )>(
151 [frame]( wxHyperlinkEvent& aEvent )
152 {
153 frame->ShowBoardSetupDialog( _( "Rules" ) );
154 } ) );
155
156 infobar->RemoveAllButtons();
157 infobar->AddButton( button );
158
159 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), 10000,
160 wxICON_WARNING );
161 }
162
163 if( aReporter )
164 {
165 m_filler->SetProgressReporter( aReporter );
166 }
167 else if( !aHeadless )
168 {
169 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Fill All Zones" ), 5, PR_CAN_ABORT );
170 m_filler->SetProgressReporter( reporter.get() );
171 }
172
173 if( m_filler->Fill( toFill ) )
174 {
175 if( m_filler->GetProgressReporter() )
176 m_filler->GetProgressReporter()->AdvancePhase();
177
178 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
179 if( !aHeadless )
180 frame->m_ZoneFillsDirty = false;
181 }
182 else
183 {
184 commit.Revert();
185 }
186
187 rebuildConnectivity( aHeadless );
188
189 if( !aHeadless )
190 {
191 refresh();
192
193 if( m_filler->IsDebug() )
194 frame->UpdateUserInterface();
195 }
196
197 m_fillInProgress = false;
198 m_filler.reset( nullptr );
199
200 if( !aHeadless )
201 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
202 // here doesn't work, so we delay it to an idle event.
203 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
204}
205
206
208{
210 std::vector<ZONE*> toFill;
211
212 for( ZONE* zone : board()->Zones() )
213 {
214 if( !zone->IsFilled() || m_dirtyZoneIDs.count( zone->m_Uuid ) )
215 toFill.push_back( zone );
216 }
217
218 if( toFill.empty() )
219 return 0;
220
221 if( m_fillInProgress )
222 return 0;
223
224 int64_t startTime = GetRunningMicroSecs();
225 m_fillInProgress = true;
226
227 m_dirtyZoneIDs.clear();
228
229 board()->IncrementTimeStamp(); // Clear caches
230
231 BOARD_COMMIT commit( this );
232 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
233 int pts = 0;
234
235 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
236
237 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
238 {
239 WX_INFOBAR* infobar = frame->GetInfoBar();
240 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ), wxEmptyString );
241
242 button->Bind( wxEVT_COMMAND_HYPERLINK,
243 std::function<void( wxHyperlinkEvent& aLocEvent )>(
244 [frame]( wxHyperlinkEvent& aLocEvent )
245 {
246 frame->ShowBoardSetupDialog( _( "Rules" ) );
247 } ) );
248
249 infobar->RemoveAllButtons();
250 infobar->AddButton( button );
251
252 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), 10000,
253 wxICON_WARNING );
254 }
255
256 for( ZONE* zone : toFill )
257 {
258 zone->GetLayerSet().RunOnLayers(
259 [&]( PCB_LAYER_ID layer )
260 {
261 pts += zone->GetFilledPolysList( layer )->FullPointCount();
262 } );
263
264 if( pts > 1000 )
265 {
266 wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() );
267
268 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame, title, 5, PR_CAN_ABORT );
269 m_filler->SetProgressReporter( reporter.get() );
270 break;
271 }
272 }
273
274 if( m_filler->Fill( toFill ) )
275 commit.Push( _( "Auto-fill Zone(s)" ), APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
276 else
277 commit.Revert();
278
280 refresh();
281
282 if( GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
283 {
284 WX_INFOBAR* infobar = frame->GetInfoBar();
285
286 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ),
287 wxEmptyString );
288
289 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& )>(
290 [this]( wxHyperlinkEvent& )
291 {
292 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ), _( "PCB Editor" ) );
293 } ) );
294
295 infobar->RemoveAllButtons();
296 infobar->AddButton( button );
297 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences if it becomes "
298 "too slow." ),
299 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
300 }
301
302 if( m_filler->IsDebug() )
303 frame->UpdateUserInterface();
304
305 m_fillInProgress = false;
306 m_filler.reset( nullptr );
307
308 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
309 // here doesn't work, so we delay it to an idle event.
310 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
311
312 return 0;
313}
314
315
317{
318 if( m_fillInProgress )
319 {
320 wxBell();
321 return -1;
322 }
323
324 std::vector<ZONE*> toFill;
325
326 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
327 {
328 toFill.push_back( passedZone );
329 }
330 else
331 {
332 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
333 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
334 {
335 } );
336
337 for( EDA_ITEM* item : sel )
338 {
339 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
340 toFill.push_back( zone );
341 }
342 }
343
344 // Bail out of the filler if there is nothing to fill
345 if( toFill.empty() )
346 {
347 wxBell();
348 return -1;
349 }
350
351 m_fillInProgress = true;
352
353 BOARD_COMMIT commit( this );
354 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
355
356 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
357
358 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5, PR_CAN_ABORT );
359 m_filler->SetProgressReporter( reporter.get() );
360
361 if( m_filler->Fill( toFill ) )
362 {
363 reporter->AdvancePhase();
364 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
365 }
366 else
367 {
368 commit.Revert();
369 }
370
372 refresh();
373
374 m_fillInProgress = false;
375 m_filler.reset( nullptr );
376 return 0;
377}
378
379
381{
382 FillAllZones( frame() );
383 return 0;
384}
385
386
388{
389 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
390 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
391 {
392 } );
393
394 std::vector<ZONE*> toUnfill;
395
396 for( EDA_ITEM* item : sel )
397 {
398 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
399 toUnfill.push_back( zone );
400 }
401
402 // Bail out if there are no zones
403 if( toUnfill.empty() )
404 {
405 wxBell();
406 return -1;
407 }
408
409 BOARD_COMMIT commit( this );
410
411 for( ZONE* zone : toUnfill )
412 {
413 commit.Modify( zone );
414
415 zone->UnFill();
416 }
417
418 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
419
420 refresh();
421
422 return 0;
423}
424
425
427{
428 BOARD_COMMIT commit( this );
429
430 for( ZONE* zone : board()->Zones() )
431 {
432 commit.Modify( zone );
433
434 zone->UnFill();
435 }
436
437 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
438
439 refresh();
440
441 return 0;
442}
443
444
446{
448 return m_filler->GetProgressReporter();
449 else
450 return nullptr;
451}
452
453
455{
458 if( !aHeadless )
460}
461
462
464{
465 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
466 // they won't be found in the view layer's itemset for re-painting.
468 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
469 {
470 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
471 {
472 return via->GetRemoveUnconnected();
473 }
474 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
475 {
476 return pad->GetRemoveUnconnected();
477 }
478
479 return false;
480 } );
481
482 canvas()->Refresh();
483}
484
485
487{
488 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
490 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
492
493 // Don't include zoneFillDirty; that's a system action not a user action
494}
495
496
#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:190
void IncrementTimeStamp()
Definition board.cpp:258
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:106
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:99
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition actions.h:349
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:207
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:86
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:1586
Definition pad.h:55
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:95
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:230
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:78
Generic, UI-independent tool event.
Definition tool_event.h:171
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:473
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)
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:73
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ ALL
All except INITIAL_ADD.
Definition view_item.h:59
Class to handle a set of BOARD_ITEMs.
BOARD * GetBoard()
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
#define APPEND_UNDO
Definition sch_commit.h:41
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
#define PR_CAN_ABORT
#define ZONE_FILLER_TOOL_NAME