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 (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>
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, _( "Checking Zones" ), 4 );
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 )
119{
120 if( m_fillInProgress )
121 return;
122
123 m_fillInProgress = true;
124
125 PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
126 BOARD_COMMIT commit( this );
127 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
128 TEARDROP_MANAGER teardropMgr( board(), m_toolMgr );
129 std::vector<ZONE*> toFill;
130
131 teardropMgr.UpdateTeardrops( commit, nullptr, nullptr, true /* forceFullUpdate */ );
132
133 board()->IncrementTimeStamp(); // Clear caches
134
135 for( ZONE* zone : board()->Zones() )
136 toFill.push_back( zone );
137
138 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
139
140 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
141 {
142 WX_INFOBAR* infobar = frame->GetInfoBar();
143 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ),
144 wxEmptyString );
145
146 button->Bind( wxEVT_COMMAND_HYPERLINK,
147 std::function<void( wxHyperlinkEvent& aEvent )>(
148 [frame]( wxHyperlinkEvent& aEvent )
149 {
150 frame->ShowBoardSetupDialog( _( "Rules" ) );
151 } ) );
152
153 infobar->RemoveAllButtons();
154 infobar->AddButton( button );
155
156 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ),
157 10000, wxICON_WARNING );
158 }
159
160 if( aReporter )
161 {
162 m_filler->SetProgressReporter( aReporter );
163 }
164 else
165 {
166 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Fill All Zones" ), 5 );
167 m_filler->SetProgressReporter( reporter.get() );
168 }
169
170 if( m_filler->Fill( toFill ) )
171 {
172 m_filler->GetProgressReporter()->AdvancePhase();
173
174 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
175 frame->m_ZoneFillsDirty = false;
176 }
177 else
178 {
179 commit.Revert();
180 }
181
183 refresh();
184
185 if( m_filler->IsDebug() )
186 frame->UpdateUserInterface();
187
188 m_fillInProgress = false;
189 m_filler.reset( nullptr );
190
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{
199 PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
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" ),
231 wxEmptyString );
232
233 button->Bind( wxEVT_COMMAND_HYPERLINK,
234 std::function<void( wxHyperlinkEvent& aLocEvent )>(
235 [frame]( wxHyperlinkEvent& aLocEvent )
236 {
237 frame->ShowBoardSetupDialog( _( "Rules" ) );
238 } ) );
239
240 infobar->RemoveAllButtons();
241 infobar->AddButton( button );
242
243 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ),
244 10000, wxICON_WARNING );
245 }
246
247 for( ZONE* zone : toFill )
248 {
249 zone->GetLayerSet().RunOnLayers(
250 [&]( PCB_LAYER_ID layer )
251 {
252 pts += zone->GetFilledPolysList( layer )->FullPointCount();
253 } );
254
255 if( pts > 1000 )
256 {
257 wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() );
258
259 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame, title, 5 );
260 m_filler->SetProgressReporter( reporter.get() );
261 break;
262 }
263 }
264
265 if( m_filler->Fill( toFill ) )
266 commit.Push( _( "Auto-fill Zone(s)" ), APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
267 else
268 commit.Revert();
269
271 refresh();
272
273 if( GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
274 {
275 WX_INFOBAR* infobar = frame->GetInfoBar();
276
277 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ),
278 wxEmptyString );
279
280 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& )>(
281 [this]( wxHyperlinkEvent& )
282 {
283 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ),
284 _( "PCB Editor" ) );
285 } ) );
286
287 infobar->RemoveAllButtons();
288 infobar->AddButton( button );
289 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences "
290 "if it becomes too slow." ),
291 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
292 }
293
294 if( m_filler->IsDebug() )
295 frame->UpdateUserInterface();
296
297 m_fillInProgress = false;
298 m_filler.reset( nullptr );
299
300 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
301 // here doesn't work, so we delay it to an idle event.
302 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
303
304 return 0;
305}
306
307
309{
310 if( m_fillInProgress )
311 {
312 wxBell();
313 return -1;
314 }
315
316 std::vector<ZONE*> toFill;
317
318 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
319 {
320 toFill.push_back( passedZone );
321 }
322 else
323 {
324 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
325 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
326 {
327 } );
328
329 for( EDA_ITEM* item : sel )
330 {
331 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
332 toFill.push_back( zone );
333 }
334 }
335
336 // Bail out of the filler if there is nothing to fill
337 if( toFill.empty() )
338 {
339 wxBell();
340 return -1;
341 }
342
343 m_fillInProgress = true;
344
345 BOARD_COMMIT commit( this );
346 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
347
348 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
349
350 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5 );
351 m_filler->SetProgressReporter( reporter.get() );
352
353 if( m_filler->Fill( toFill ) )
354 {
355 reporter->AdvancePhase();
356 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
357 }
358 else
359 {
360 commit.Revert();
361 }
362
364 refresh();
365
366 m_fillInProgress = false;
367 m_filler.reset( nullptr );
368 return 0;
369}
370
371
373{
374 FillAllZones( frame() );
375 return 0;
376}
377
378
380{
381 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
382 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
383 {
384 } );
385
386 std::vector<ZONE*> toUnfill;
387
388 for( EDA_ITEM* item : sel )
389 {
390 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
391 toUnfill.push_back( zone );
392 }
393
394 // Bail out if there are no zones
395 if( toUnfill.empty() )
396 {
397 wxBell();
398 return -1;
399 }
400
401 BOARD_COMMIT commit( this );
402
403 for( ZONE* zone : toUnfill )
404 {
405 commit.Modify( zone );
406
407 zone->UnFill();
408 }
409
410 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
411
412 refresh();
413
414 return 0;
415}
416
417
419{
420 BOARD_COMMIT commit( this );
421
422 for( ZONE* zone : board()->Zones() )
423 {
424 commit.Modify( zone );
425
426 zone->UnFill();
427 }
428
429 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
430
431 refresh();
432
433 return 0;
434}
435
436
438{
440 return m_filler->GetProgressReporter();
441 else
442 return nullptr;
443}
444
445
447{
451}
452
453
455{
456 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
457 // they won't be found in the view layer's itemset for re-painting.
459 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
460 {
461 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
462 {
463 return via->GetRemoveUnconnected();
464 }
465 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
466 {
467 return pad->GetRemoveUnconnected();
468 }
469
470 return false;
471 } );
472
473 canvas()->Refresh();
474}
475
476
478{
479 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
481 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
483
484 // Don't include zoneFillDirty; that's a system action not a user action
485}
486
487
489{
490 // Zone actions
496}
#define SKIP_CONNECTIVITY
Definition: board_commit.h:42
#define ZONE_FILL_OP
Definition: board_commit.h:43
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
virtual void Revert() override
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:179
void IncrementTimeStamp()
Definition: board.cpp:248
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
WX_INFOBAR * GetInfoBar()
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:88
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:268
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:206
An abstract base class for deriving all objects that can be added to a VIEW.
Definition: view_item.h:84
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:1523
Definition: pad.h:53
static TOOL_ACTION zoneFillAll
Definition: pcb_actions.h:397
static TOOL_ACTION zoneFill
Definition: pcb_actions.h:396
static TOOL_ACTION zoneUnfill
Definition: pcb_actions.h:399
static TOOL_ACTION zoneUnfillAll
Definition: pcb_actions.h:400
static TOOL_ACTION zoneFillDirty
Definition: pcb_actions.h:398
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:
PCB_BASE_EDIT_FRAME * 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:97
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:151
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:217
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
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.
Definition: tool_event.cpp:82
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:460
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:301
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:260
@ 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:72
#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:58
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