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, see <https://www.gnu.org/licenses/>.
20 */
21#include <cstdint>
22#include <thread>
23#include <zone.h>
25#include <board_commit.h>
26#include <footprint.h>
27#include <pcb_track.h>
28#include <pad.h>
29#include <pcb_group.h>
31#include <drc/drc_engine.h>
32#include <progress_reporter.h>
33#include <widgets/wx_infobar.h>
35#include <wx/event.h>
36#include <wx/hyperlink.h>
37#include <tool/tool_manager.h>
38#include <tool/actions.h>
40#include "pcb_actions.h"
41#include "zone_filler_tool.h"
42#include "zone_filler.h"
43#include "teardrop/teardrop.h"
44#include <core/profile.h>
45
51
52
56
57
59{
60}
61
62
63void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter )
64{
65 if( !getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty || m_fillInProgress )
66 return;
67
68 m_fillInProgress = true;
69
70 std::vector<ZONE*> toFill;
71
72 for( ZONE* zone : board()->Zones() )
73 toFill.push_back( zone );
74
75 BOARD_COMMIT commit( this );
76 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
77
78 m_filler = std::make_unique<ZONE_FILLER>( frame()->GetBoard(), &commit );
79
80 if( aReporter )
81 {
82 m_filler->SetProgressReporter( aReporter );
83 }
84 else
85 {
86 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Check Zones" ), 4, PR_CAN_ABORT );
87 m_filler->SetProgressReporter( reporter.get() );
88 }
89
90 if( m_filler->Fill( toFill, true, aCaller ) )
91 {
92 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
93 getEditFrame<PCB_EDIT_FRAME>()->m_ZoneFillsDirty = false;
94 }
95 else
96 {
97 commit.Revert();
98 }
99
101 refresh();
102
103 m_fillInProgress = false;
104 m_filler.reset( nullptr );
105}
106
107
109{
110 canvas()->SetFocus();
111 canvas()->Unbind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
112}
113
114
115void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aReporter, bool aHeadless )
116{
117 if( m_fillInProgress )
118 return;
119
120 m_fillInProgress = true;
121
122 PCB_EDIT_FRAME* frame = nullptr;
123 if( !aHeadless )
125
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( !aHeadless && !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
141 {
142 WX_INFOBAR* infobar = frame->GetInfoBar();
143 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ), 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." ), 10000,
156 wxICON_WARNING );
157 }
158
159 if( aReporter )
160 {
161 m_filler->SetProgressReporter( aReporter );
162 }
163 else if( !aHeadless )
164 {
165 reporter = std::make_unique<WX_PROGRESS_REPORTER>( aCaller, _( "Fill All Zones" ), 5, PR_CAN_ABORT );
166 m_filler->SetProgressReporter( reporter.get() );
167 }
168
169 if( m_filler->Fill( toFill ) )
170 {
171 if( m_filler->GetProgressReporter() )
172 m_filler->GetProgressReporter()->AdvancePhase();
173
174 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
175 if( !aHeadless )
176 frame->m_ZoneFillsDirty = false;
177 }
178 else
179 {
180 commit.Revert();
181 }
182
183 rebuildConnectivity( aHeadless );
184
185 if( !aHeadless )
186 {
187 refresh();
188
189 if( m_filler->IsDebug() )
190 frame->UpdateUserInterface();
191 }
192
193 m_fillInProgress = false;
194 m_filler.reset( nullptr );
195
196 if( !aHeadless )
197 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
198 // here doesn't work, so we delay it to an idle event.
199 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
200}
201
202
204{
206 std::vector<ZONE*> toFill;
207
208 for( ZONE* zone : board()->Zones() )
209 {
210 if( !zone->IsFilled() || m_dirtyZoneIDs.count( zone->m_Uuid ) )
211 toFill.push_back( zone );
212 }
213
214 if( toFill.empty() )
215 return 0;
216
217 if( m_fillInProgress )
218 return 0;
219
220 int64_t startTime = GetRunningMicroSecs();
221 m_fillInProgress = true;
222
223 m_dirtyZoneIDs.clear();
224
225 board()->IncrementTimeStamp(); // Clear caches
226
227 BOARD_COMMIT commit( this );
228 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
229 int pts = 0;
230
231 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
232
233 if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() )
234 {
235 WX_INFOBAR* infobar = frame->GetInfoBar();
236 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ), wxEmptyString );
237
238 button->Bind( wxEVT_COMMAND_HYPERLINK,
239 std::function<void( wxHyperlinkEvent& aLocEvent )>(
240 [frame]( wxHyperlinkEvent& aLocEvent )
241 {
242 frame->ShowBoardSetupDialog( _( "Rules" ) );
243 } ) );
244
245 infobar->RemoveAllButtons();
246 infobar->AddButton( button );
247
248 infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), 10000,
249 wxICON_WARNING );
250 }
251
252 for( ZONE* zone : toFill )
253 {
254 zone->GetLayerSet().RunOnLayers(
255 [&]( PCB_LAYER_ID layer )
256 {
257 pts += zone->GetFilledPolysList( layer )->FullPointCount();
258 } );
259
260 if( pts > 1000 )
261 {
262 wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() );
263
264 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame, title, 5, PR_CAN_ABORT );
265 m_filler->SetProgressReporter( reporter.get() );
266 break;
267 }
268 }
269
270 if( m_filler->Fill( toFill ) )
271 commit.Push( _( "Auto-fill Zone(s)" ), APPEND_UNDO | SKIP_CONNECTIVITY | ZONE_FILL_OP );
272 else
273 commit.Revert();
274
276 refresh();
277
278 if( GetRunningMicroSecs() - startTime > 3000000 ) // 3 seconds
279 {
280 WX_INFOBAR* infobar = frame->GetInfoBar();
281
282 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ),
283 wxEmptyString );
284
285 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& )>(
286 [this]( wxHyperlinkEvent& )
287 {
288 getEditFrame<PCB_EDIT_FRAME>()->ShowPreferences( _( "Editing Options" ), _( "PCB Editor" ) );
289 } ) );
290
291 infobar->RemoveAllButtons();
292 infobar->AddButton( button );
293 infobar->ShowMessageFor( _( "Automatic refill of zones can be turned off in Preferences if it becomes "
294 "too slow." ),
295 10000, wxICON_INFORMATION, WX_INFOBAR::MESSAGE_TYPE::GENERIC );
296 }
297
298 if( m_filler->IsDebug() )
299 frame->UpdateUserInterface();
300
301 m_fillInProgress = false;
302 m_filler.reset( nullptr );
303
304 // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus
305 // here doesn't work, so we delay it to an idle event.
306 canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this );
307
308 return 0;
309}
310
311
313{
314 if( m_fillInProgress )
315 {
316 wxBell();
317 return -1;
318 }
319
320 std::vector<ZONE*> toFill;
321
322 if( ZONE* passedZone = aEvent.Parameter<ZONE*>() )
323 {
324 toFill.push_back( passedZone );
325 }
326 else
327 {
328 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
329 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
330 {
331 } );
332
333 for( EDA_ITEM* item : sel )
334 {
335 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
336 toFill.push_back( zone );
337 }
338 }
339
340 // Bail out of the filler if there is nothing to fill
341 if( toFill.empty() )
342 {
343 wxBell();
344 return -1;
345 }
346
347 m_fillInProgress = true;
348
349 BOARD_COMMIT commit( this );
350 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
351
352 m_filler = std::make_unique<ZONE_FILLER>( board(), &commit );
353
354 reporter = std::make_unique<WX_PROGRESS_REPORTER>( frame(), _( "Fill Zone" ), 5, PR_CAN_ABORT );
355 m_filler->SetProgressReporter( reporter.get() );
356
357 if( m_filler->Fill( toFill ) )
358 {
359 reporter->AdvancePhase();
360 commit.Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
361 }
362 else
363 {
364 commit.Revert();
365 }
366
368 refresh();
369
370 m_fillInProgress = false;
371 m_filler.reset( nullptr );
372 return 0;
373}
374
375
377{
378 FillAllZones( frame() );
379 return 0;
380}
381
382
384{
385 const PCB_SELECTION& sel = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->RequestSelection(
386 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
387 {
388 } );
389
390 std::vector<ZONE*> toUnfill;
391
392 for( EDA_ITEM* item : sel )
393 {
394 if( ZONE* zone = dynamic_cast<ZONE*>( item ) )
395 toUnfill.push_back( zone );
396 }
397
398 // Bail out if there are no zones
399 if( toUnfill.empty() )
400 {
401 wxBell();
402 return -1;
403 }
404
405 BOARD_COMMIT commit( this );
406
407 for( ZONE* zone : toUnfill )
408 {
409 commit.Modify( zone );
410
411 zone->UnFill();
412 }
413
414 commit.Push( _( "Unfill Zone" ), ZONE_FILL_OP );
415
416 refresh();
417
418 return 0;
419}
420
421
423{
424 BOARD_COMMIT commit( this );
425
426 for( ZONE* zone : board()->Zones() )
427 {
428 commit.Modify( zone );
429
430 zone->UnFill();
431 }
432
433 commit.Push( _( "Unfill All Zones" ), ZONE_FILL_OP );
434
435 refresh();
436
437 return 0;
438}
439
440
442{
444 return m_filler->GetProgressReporter();
445 else
446 return nullptr;
447}
448
449
451{
454 if( !aHeadless )
456}
457
458
460{
461 // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
462 // they won't be found in the view layer's itemset for re-painting.
464 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
465 {
466 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
467 {
468 return via->GetRemoveUnconnected();
469 }
470 else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
471 {
472 return pad->GetRemoveUnconnected();
473 }
474
475 return false;
476 } );
477
478 canvas()->Refresh();
479}
480
481
483{
484 return aEvent->IsAction( &PCB_ACTIONS::zoneFill )
486 || aEvent->IsAction( &PCB_ACTIONS::zoneUnfill )
488
489 // Don't include zoneFillDirty; that's a system action not a user action
490}
491
492
#define SKIP_CONNECTIVITY
#define ZONE_FILL_OP
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:201
void IncrementTimeStamp()
Definition board.cpp:283
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:102
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:96
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition actions.h:345
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
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:1702
Definition pad.h:61
static TOOL_ACTION zoneFillAll
static TOOL_ACTION zoneFill
static TOOL_ACTION zoneUnfill
static TOOL_ACTION zoneUnfillAll
static TOOL_ACTION zoneFillDirty
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
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
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:90
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:225
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
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.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
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).
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:77
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
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.
void AddButton(wxButton *aButton)
Add an already created button to the infobar.
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:70
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ ALL
All except INITIAL_ADD.
Definition view_item.h:55
Class to handle a set of BOARD_ITEMs.
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
#define APPEND_UNDO
Definition sch_commit.h:37
IbisParser parser & reporter
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
#define PR_CAN_ABORT
#define ZONE_FILLER_TOOL_NAME