KiCad PCB EDA Suite
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 (C) 2014-2023 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 <progress_reporter.h>
36#include <widgets/wx_infobar.h>
38#include <wx/event.h>
39#include <wx/hyperlink.h>
40#include <tool/tool_manager.h>
41#include <tool/actions.h>
42#include "pcb_actions.h"
43#include "zone_filler_tool.h"
44#include "zone_filler.h"
45#include <profile.h>
46
48 PCB_TOOL_BASE( "pcbnew.ZoneFiller" ),
49 m_fillInProgress( false )
50{
51}
52
53
55{
56}
57
58
60{
61}
62
63
64void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
65{
66 if( !getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty || m_fillInProgress )
67 return;
68
69 m_fillInProgress = true;
70
71 std::vector<ZONE*> toFill;
72
73 for( ZONE* zone : board()->Zones() )
74 toFill.push_back( zone );
75
76 BOARD_COMMIT commit( this );
77 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
78
79 m_filler = std::make_unique<ZONE_FILLER>( frame()->GetBoard(), &commit );
80
81 if( aReporter )
82 {
83 m_filler->SetProgressReporter( aReporter );
84 }
85 else
86 {
87 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Checking Zones" ), 4 );
88 m_filler->SetProgressReporter( reporter.get() );
89 }
90
91 if( m_filler->Fill( toFill, true, aCaller ) )
92 {
93 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
94 getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty = false;
95 }
96 else
97 {
98 commit.Revert();
99 }
100
102 refresh();
103
104 m_fillInProgress = false;
105 m_filler.reset( nullptr );
106}
107
108
110{
111 canvas()->SetFocus();
112 canvas()->Unbind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
113}
114
115
116void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
117{
118 PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
119 std::vector<ZONE*> toFill;
120
121 if( m_fillInProgress )
122 return;
123
124 m_fillInProgress = true;
125
126 for( ZONE* zone : board()->Zones() )
127 toFill.push_back( zone );
128
129 board()->IncrementTimeStamp(); // Clear caches
130
131 BOARD_COMMIT commit( this );
132 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
133
134 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
135
136 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
137 {
138 WX_INFOBAR* infobar = frame->GetInfoBar();
139 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ),
140 wxEmptyString );
141
142 button->Bind( wxEVT_COMMAND_HYPERLINK,
143 std::function<void( wxHyperlinkEvent& aEvent )>(
144 [frame]( wxHyperlinkEvent& aEvent )
145 {
146 frame->ShowBoardSetupDialog( _( "Rules" ) );
147 } ) );
148
149 infobar->RemoveAllButtons();
150 infobar->AddButton( button );
151
152 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ),
153 10000, wxICON_WARNING );
154 }
155
156 if( aReporter )
157 {
158 m_filler->SetProgressReporter( aReporter );
159 }
160 else
161 {
162 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Fill All Zones" ), 5 );
163 m_filler->SetProgressReporter( reporter.get() );
164 }
165
166 if( m_filler->Fill( toFill ) )
167 {
168 m_filler->GetProgressReporter()->AdvancePhase();
169
170 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
171 frame->m_ZoneFillsDirty = false;
172 }
173 else
174 {
175 commit.Revert();
176 }
177
179 refresh();
180
181 if( m_filler->IsDebug() )
182 frame->UpdateUserInterface();
183
184 m_fillInProgress = false;
185 m_filler.reset( nullptr );
186
187 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
188 // here doesn't work, so we delay it to an idle event.
189 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
190}
191
192
194{
195 PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
196 std::vector<ZONE*> toFill;
197
198 for( ZONE* zone : board()->Zones() )
199 {
200 if( m_dirtyZoneIDs.count( zone->m_Uuid ) )
201 toFill.push_back( zone );
202 }
203
204 if( toFill.empty() )
205 return 0;
206
207 if( m_fillInProgress )
208 return 0;
209
210 unsigned startTime = GetRunningMicroSecs();
211 m_fillInProgress = true;
212
213 m_dirtyZoneIDs.clear();
214
215 board()->IncrementTimeStamp(); // Clear caches
216
217 BOARD_COMMIT commit( this );
218 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
219 int pts = 0;
220
221 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
222
223 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
224 {
225 WX_INFOBAR* infobar = frame->GetInfoBar();
226 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ),
227 wxEmptyString );
228
229 button->Bind( wxEVT_COMMAND_HYPERLINK,
230 std::function<void( wxHyperlinkEvent& aLocEvent )>(
231 [frame]( wxHyperlinkEvent& aLocEvent )
232 {
233 frame->ShowBoardSetupDialog( _( "Rules" ) );
234 } ) );
235
236 infobar->RemoveAllButtons();
237 infobar->AddButton( button );
238
239 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ),
240 10000, wxICON_WARNING );
241 }
242
243 for( ZONE* zone : toFill )
244 {
245 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
246 pts += zone->GetFilledPolysList( layer )->FullPointCount();
247
248 if( pts > 1000 )
249 {
250 wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() );
251
252 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame, title, 5 );
253 m_filler->SetProgressReporter( reporter.get() );
254 break;
255 }
256 }
257
258 if( m_filler->Fill( toFill ) )
259 commit.Push( _( "Auto-fill Zone(s)" ), APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
260 else
261 commit.Revert();
262
264 refresh();
265
266 if( GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
267 {
268 WX_INFOBAR* infobar = frame->GetInfoBar();
269
270 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ),
271 wxEmptyString );
272
273 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& )>(
274 [this]( wxHyperlinkEvent& )
275 {
276 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ),
277 _( "PCB Editor" ) );
278 } ) );
279
280 infobar->RemoveAllButtons();
281 infobar->AddButton( button );
282 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences "
283 "if it becomes too slow." ),
284 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
285 }
286
287 if( m_filler->IsDebug() )
288 frame->UpdateUserInterface();
289
290 m_fillInProgress = false;
291 m_filler.reset( nullptr );
292
293 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
294 // here doesn't work, so we delay it to an idle event.
295 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
296
297 return 0;
298}
299
300
302{
303 if( m_fillInProgress )
304 {
305 wxBell();
306 return -1;
307 }
308
309 m_fillInProgress = true;
310
311 std::vector<ZONE*> toFill;
312
313 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
314 {
315 toFill.push_back( passedZone );
316 }
317 else
318 {
319 for( EDA_ITEM* item : selection() )
320 {
321 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
322 toFill.push_back( zone );
323 }
324 }
325
326 BOARD_COMMIT commit( this );
327 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
328
329 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
330
331 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5 );
332 m_filler->SetProgressReporter( reporter.get() );
333
334 if( m_filler->Fill( toFill ) )
335 {
336 reporter->AdvancePhase();
337 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
338 }
339 else
340 {
341 commit.Revert();
342 }
343
345 refresh();
346
347 m_fillInProgress = false;
348 m_filler.reset( nullptr );
349 return 0;
350}
351
352
354{
355 FillAllZones( frame() );
356 return 0;
357}
358
359
361{
362 BOARD_COMMIT commit( this );
363
364 for( EDA_ITEM* item : selection() )
365 {
366 assert( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T );
367
368 ZONE* zone = static_cast<ZONE*>( item );
369
370 commit.Modify( zone );
371
372 zone->UnFill();
373 }
374
375 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
376
377 refresh();
378
379 return 0;
380}
381
382
384{
385 BOARD_COMMIT commit( this );
386
387 for( ZONE* zone : board()->Zones() )
388 {
389 commit.Modify( zone );
390
391 zone->UnFill();
392 }
393
394 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
395
396 refresh();
397
398 return 0;
399}
400
401
403{
405 return m_filler->GetProgressReporter();
406 else
407 return nullptr;
408}
409
410
412{
416}
417
418
420{
421 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
422 // they won't be found in the view layer's itemset for re-painting.
424 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
425 {
426 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
427 {
428 return via->GetRemoveUnconnected();
429 }
430 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
431 {
432 return pad->GetRemoveUnconnected();
433 }
434
435 return false;
436 } );
437
438 canvas()->Refresh();
439}
440
441
443{
444 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
446 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
448
449 // Don't include zoneFillDirty; that's a system action not a user action
450}
451
452
454{
455 // Zone actions
461}
#define SKIP_CONNECTIVITY
Definition: board_commit.h:42
#define APPEND_UNDO
Definition: board_commit.h:40
#define ZONE_FILL_OP
Definition: board_commit.h:43
virtual void Revert() override
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) 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:162
void IncrementTimeStamp()
Definition: board.cpp:226
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
WX_INFOBAR * GetInfoBar()
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
void SetFocus() override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:210
An abstract base class for deriving all objects that can be added to a VIEW.
Definition: view_item.h:77
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:1494
Definition: pad.h:59
static TOOL_ACTION zoneFillAll
Definition: pcb_actions.h:344
static TOOL_ACTION zoneFill
Definition: pcb_actions.h:343
static TOOL_ACTION zoneUnfill
Definition: pcb_actions.h:346
static TOOL_ACTION zoneUnfillAll
Definition: pcb_actions.h:347
static TOOL_ACTION zoneFillDirty
Definition: pcb_actions.h:345
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.
PCB_BASE_EDIT_FRAME * frame() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
const PCB_SELECTION & selection() const
A progress reporter interface for use in multi-threaded environments.
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
Generic, UI-independent tool event.
Definition: tool_event.h:156
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:442
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:88
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).
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
A modified version of the wxInfoBar class that allows us to:
Definition: wx_infobar.h:75
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
Definition: wx_infobar.cpp:289
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.
Definition: wx_infobar.cpp:128
void AddButton(wxButton *aButton)
Add an already created button to the infobar.
Definition: wx_infobar.cpp:248
@ GENERIC
GENERIC Are messages that do not have special handling.
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.
void FillAllZones(wxWindow *aCaller, PROGRESS_REPORTER *aReporter=nullptr)
int ZoneFill(const TOOL_EVENT &aEvent)
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)
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:209
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ ALL
All except INITIAL_ADD.
Definition: view_item.h:53
BOARD * GetBoard()
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112
@ PCB_FP_ZONE_T
class ZONE, managed by a footprint
Definition: typeinfo.h:100