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