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>
42#include "pcb_actions.h"
43#include "zone_filler_tool.h"
44#include "zone_filler.h"
45#include "teardrop/teardrop.h"
46#include <profile.h>
47
49 PCB_TOOL_BASE( "pcbnew.ZoneFiller" ),
50 m_fillInProgress( false )
51{
52}
53
54
56{
57}
58
59
61{
62}
63
64
65void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
66{
67 if( !getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty || m_fillInProgress )
68 return;
69
70 m_fillInProgress = true;
71
72 std::vector<ZONE*> toFill;
73
74 for( ZONE* zone : board()->Zones() )
75 toFill.push_back( zone );
76
77 BOARD_COMMIT commit( this );
78 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
79
80 m_filler = std::make_unique<ZONE_FILLER>( frame()->GetBoard(), &commit );
81
82 if( aReporter )
83 {
84 m_filler->SetProgressReporter( aReporter );
85 }
86 else
87 {
88 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Checking Zones" ), 4 );
89 m_filler->SetProgressReporter( reporter.get() );
90 }
91
92 if( m_filler->Fill( toFill, true, aCaller ) )
93 {
94 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
95 getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty = false;
96 }
97 else
98 {
99 commit.Revert();
100 }
101
103 refresh();
104
105 m_fillInProgress = false;
106 m_filler.reset( nullptr );
107}
108
109
111{
112 canvas()->SetFocus();
113 canvas()->Unbind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
114}
115
116
117void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
118{
119 if( m_fillInProgress )
120 return;
121
122 m_fillInProgress = true;
123
124 PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
125 BOARD_COMMIT commit( this );
126 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
127 TEARDROP_MANAGER teardropMgr( board(), m_toolMgr );
128 std::vector<ZONE*> toFill;
129
130 teardropMgr.UpdateTeardrops( commit, nullptr, nullptr, true /* forceFullUpdate */ );
131
132 board()->IncrementTimeStamp(); // Clear caches
133
134 for( ZONE* zone : board()->Zones() )
135 toFill.push_back( zone );
136
137 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
138
139 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
140 {
141 WX_INFOBAR* infobar = frame->GetInfoBar();
142 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ),
143 wxEmptyString );
144
145 button->Bind( wxEVT_COMMAND_HYPERLINK,
146 std::function<void( wxHyperlinkEvent& aEvent )>(
147 [frame]( wxHyperlinkEvent& aEvent )
148 {
149 frame->ShowBoardSetupDialog( _( "Rules" ) );
150 } ) );
151
152 infobar->RemoveAllButtons();
153 infobar->AddButton( button );
154
155 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ),
156 10000, wxICON_WARNING );
157 }
158
159 if( aReporter )
160 {
161 m_filler->SetProgressReporter( aReporter );
162 }
163 else
164 {
165 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Fill All Zones" ), 5 );
166 m_filler->SetProgressReporter( reporter.get() );
167 }
168
169 if( m_filler->Fill( toFill ) )
170 {
171 m_filler->GetProgressReporter()->AdvancePhase();
172
173 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
174 frame->m_ZoneFillsDirty = false;
175 }
176 else
177 {
178 commit.Revert();
179 }
180
182 refresh();
183
184 if( m_filler->IsDebug() )
185 frame->UpdateUserInterface();
186
187 m_fillInProgress = false;
188 m_filler.reset( nullptr );
189
190 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
191 // here doesn't work, so we delay it to an idle event.
192 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
193}
194
195
197{
198 PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
199 std::vector<ZONE*> toFill;
200
201 for( ZONE* zone : board()->Zones() )
202 {
203 if( m_dirtyZoneIDs.count( zone->m_Uuid ) )
204 toFill.push_back( zone );
205 }
206
207 if( toFill.empty() )
208 return 0;
209
210 if( m_fillInProgress )
211 return 0;
212
213 unsigned startTime = GetRunningMicroSecs();
214 m_fillInProgress = true;
215
216 m_dirtyZoneIDs.clear();
217
218 board()->IncrementTimeStamp(); // Clear caches
219
220 BOARD_COMMIT commit( this );
221 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
222 int pts = 0;
223
224 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
225
226 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
227 {
228 WX_INFOBAR* infobar = frame->GetInfoBar();
229 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ),
230 wxEmptyString );
231
232 button->Bind( wxEVT_COMMAND_HYPERLINK,
233 std::function<void( wxHyperlinkEvent& aLocEvent )>(
234 [frame]( wxHyperlinkEvent& aLocEvent )
235 {
236 frame->ShowBoardSetupDialog( _( "Rules" ) );
237 } ) );
238
239 infobar->RemoveAllButtons();
240 infobar->AddButton( button );
241
242 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ),
243 10000, wxICON_WARNING );
244 }
245
246 for( ZONE* zone : toFill )
247 {
248 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
249 pts += zone->GetFilledPolysList( layer )->FullPointCount();
250
251 if( pts > 1000 )
252 {
253 wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() );
254
255 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame, title, 5 );
256 m_filler->SetProgressReporter( reporter.get() );
257 break;
258 }
259 }
260
261 if( m_filler->Fill( toFill ) )
262 commit.Push( _( "Auto-fill Zone(s)" ), APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
263 else
264 commit.Revert();
265
267 refresh();
268
269 if( GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
270 {
271 WX_INFOBAR* infobar = frame->GetInfoBar();
272
273 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ),
274 wxEmptyString );
275
276 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& )>(
277 [this]( wxHyperlinkEvent& )
278 {
279 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ),
280 _( "PCB Editor" ) );
281 } ) );
282
283 infobar->RemoveAllButtons();
284 infobar->AddButton( button );
285 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences "
286 "if it becomes too slow." ),
287 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
288 }
289
290 if( m_filler->IsDebug() )
291 frame->UpdateUserInterface();
292
293 m_fillInProgress = false;
294 m_filler.reset( nullptr );
295
296 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
297 // here doesn't work, so we delay it to an idle event.
298 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
299
300 return 0;
301}
302
303
305{
306 if( m_fillInProgress )
307 {
308 wxBell();
309 return -1;
310 }
311
312 m_fillInProgress = true;
313
314 std::vector<ZONE*> toFill;
315
316 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
317 {
318 toFill.push_back( passedZone );
319 }
320 else
321 {
322 for( EDA_ITEM* item : selection() )
323 {
324 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
325 toFill.push_back( zone );
326 }
327 }
328
329 BOARD_COMMIT commit( this );
330 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
331
332 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
333
334 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5 );
335 m_filler->SetProgressReporter( reporter.get() );
336
337 if( m_filler->Fill( toFill ) )
338 {
339 reporter->AdvancePhase();
340 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
341 }
342 else
343 {
344 commit.Revert();
345 }
346
348 refresh();
349
350 m_fillInProgress = false;
351 m_filler.reset( nullptr );
352 return 0;
353}
354
355
357{
358 FillAllZones( frame() );
359 return 0;
360}
361
362
364{
365 BOARD_COMMIT commit( this );
366
367 for( EDA_ITEM* item : selection() )
368 {
369 assert( item->Type() == PCB_ZONE_T );
370
371 ZONE* zone = static_cast<ZONE*>( item );
372
373 commit.Modify( zone );
374
375 zone->UnFill();
376 }
377
378 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
379
380 refresh();
381
382 return 0;
383}
384
385
387{
388 BOARD_COMMIT commit( this );
389
390 for( ZONE* zone : board()->Zones() )
391 {
392 commit.Modify( zone );
393
394 zone->UnFill();
395 }
396
397 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
398
399 refresh();
400
401 return 0;
402}
403
404
406{
408 return m_filler->GetProgressReporter();
409 else
410 return nullptr;
411}
412
413
415{
419}
420
421
423{
424 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
425 // they won't be found in the view layer's itemset for re-painting.
427 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
428 {
429 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
430 {
431 return via->GetRemoveUnconnected();
432 }
433 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
434 {
435 return pad->GetRemoveUnconnected();
436 }
437
438 return false;
439 } );
440
441 canvas()->Refresh();
442}
443
444
446{
447 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
449 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
451
452 // Don't include zoneFillDirty; that's a system action not a user action
453}
454
455
457{
458 // Zone actions
464}
#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:168
void IncrementTimeStamp()
Definition: board.cpp:237
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:104
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:85
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:211
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:1511
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.
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:102
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
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:81
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
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:218
#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
Class to handle a set of BOARD_ITEMs.
BOARD * GetBoard()
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
#define SKIP_CONNECTIVITY
#define APPEND_UNDO
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:103