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