1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
5 * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * Some code comes from FreePCB.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27 #include <kiface_base.h>
28 #include <confirm.h>
29 #include <pcb_edit_frame.h>
30 #include <pcbnew_settings.h>
31 #include <board_commit.h>
32 #include <zone.h>
33 #include <zones.h>
34 #include <zones_functions_for_undo_redo.h>
35 #include <connectivity/connectivity_data.h>
36 #include <widgets/wx_progress_reporters.h>
37 #include <zone_filler.h>
38
39
Edit_Zone_Params(ZONE * aZone)40 void PCB_EDIT_FRAME::Edit_Zone_Params( ZONE* aZone )
41 {
42 int dialogResult;
43 ZONE_SETTINGS zoneInfo = GetZoneSettings();
44 PICKED_ITEMS_LIST pickedList; // zones for undo/redo command
45 PICKED_ITEMS_LIST deletedList; // zones that have been deleted when combined
46 BOARD_COMMIT commit( this );
47
48 // Save initial zones configuration, for undo/redo, before adding new zone
49 // note the net name and the layer can be changed, so we must save all zones
50 deletedList.ClearListAndDeleteItems();
51 pickedList.ClearListAndDeleteItems();
52 SaveCopyOfZones( pickedList, GetBoard(), -1, UNDEFINED_LAYER );
53
54 if( aZone->GetIsRuleArea() )
55 {
56 // edit a rule area on a copper layer
57 zoneInfo << *aZone;
58 dialogResult = InvokeRuleAreaEditor( this, &zoneInfo );
59 }
60 else if( IsCopperLayer( aZone->GetLayer() ) )
61 {
62 // edit a zone on a copper layer
63 zoneInfo << *aZone;
64 dialogResult = InvokeCopperZonesEditor( this, &zoneInfo );
65 }
66 else
67 {
68 zoneInfo << *aZone;
69 dialogResult = InvokeNonCopperZonesEditor( this, &zoneInfo );
70 }
71
72 if( dialogResult == wxID_CANCEL )
73 {
74 deletedList.ClearListAndDeleteItems();
75 pickedList.ClearListAndDeleteItems();
76 return;
77 }
78
79 SetZoneSettings( zoneInfo );
80 OnModify();
81
82 if( dialogResult == ZONE_EXPORT_VALUES )
83 {
84 UpdateCopyOfZonesList( pickedList, deletedList, GetBoard() );
85 commit.Stage( pickedList );
86 commit.Push( _( "Modify zone properties" ) );
87 pickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items
88 return;
89 }
90
91 wxBusyCursor dummy;
92
93 // Undraw old zone outlines
94 for( ZONE* zone : GetBoard()->Zones() )
95 GetCanvas()->GetView()->Update( zone );
96
97 zoneInfo.ExportSetting( *aZone );
98
99 NETINFO_ITEM* net = GetBoard()->FindNet( zoneInfo.m_NetcodeSelection );
100
101 if( net ) // net == NULL should not occur
102 aZone->SetNetCode( net->GetNetCode() );
103
104 // Combine zones if possible
105 GetBoard()->OnAreaPolygonModified( &deletedList, aZone );
106
107 UpdateCopyOfZonesList( pickedList, deletedList, GetBoard() );
108
109 // refill zones with the new properties applied
110 std::vector<ZONE*> zones_to_refill;
111
112 for( unsigned i = 0; i < pickedList.GetCount(); ++i )
113 {
114 ZONE* zone = dyn_cast<ZONE*>( pickedList.GetPickedItem( i ) );
115
116 if( zone == nullptr )
117 {
118 wxASSERT_MSG( false, "Expected a zone after zone properties edit" );
119 continue;
120 }
121
122 // aZone won't be filled if the layer set was modified, but it needs to be updated
123 if( zone->IsFilled() || zone == aZone )
124 zones_to_refill.push_back( zone );
125 }
126
127 commit.Stage( pickedList );
128
129 // Only auto-refill zones here if in user preferences
130 if( Settings().m_AutoRefillZones )
131 {
132 if( zones_to_refill.size() )
133 {
134 ZONE_FILLER filler( GetBoard(), &commit );
135 wxString title = wxString::Format( _( "Refill %d Zones" ),
136 (int) zones_to_refill.size() );
137
138 std::unique_ptr<WX_PROGRESS_REPORTER> reporter;
139 reporter = std::make_unique<WX_PROGRESS_REPORTER>( this, title, 4 );
140 filler.SetProgressReporter( reporter.get() );
141
142 if( !filler.Fill( zones_to_refill ) )
143 {
144 GetBoard()->GetConnectivity()->Build( GetBoard() );
145 // User has already OK'ed dialog so we're going to go ahead and commit even if the
146 // fill was cancelled.
147 }
148 }
149 }
150
151 commit.Push( _( "Modify zone properties" ) );
152 GetBoard()->GetConnectivity()->RecalculateRatsnest();
153
154 pickedList.ClearItemsList(); // s_ItemsListPicker is no longer owner of picked items
155 }
156
157
OnAreaPolygonModified(PICKED_ITEMS_LIST * aModifiedZonesList,ZONE * modified_area)158 bool BOARD::OnAreaPolygonModified( PICKED_ITEMS_LIST* aModifiedZonesList, ZONE* modified_area )
159 {
160 // clip polygon against itself
161 bool modified = NormalizeAreaPolygon( aModifiedZonesList, modified_area );
162
163 // Test for bad areas: all zones must have more than 2 corners:
164 // Note: should not happen, but just in case.
165 for( ZONE* zone : m_zones )
166 {
167 if( zone->GetNumCorners() < 3 )
168 {
169 ITEM_PICKER picker( nullptr, zone, UNDO_REDO::DELETED );
170 aModifiedZonesList->PushItem( picker );
171 zone->SetFlags( STRUCT_DELETED );
172 }
173 }
174
175 return modified;
176 }
177
178
TestZoneIntersection(ZONE * aZone1,ZONE * aZone2)179 bool BOARD::TestZoneIntersection( ZONE* aZone1, ZONE* aZone2 )
180 {
181 // see if areas are on same layer
182 if( aZone1->GetLayer() != aZone2->GetLayer() )
183 return false;
184
185 SHAPE_POLY_SET* poly1 = aZone1->Outline();
186 SHAPE_POLY_SET* poly2 = aZone2->Outline();
187
188 // test bounding rects
189 BOX2I b1 = poly1->BBox();
190 BOX2I b2 = poly2->BBox();
191
192 if( ! b1.Intersects( b2 ) )
193 return false;
194
195 // Now test for intersecting segments
196 for( auto segIterator1 = poly1->IterateSegmentsWithHoles(); segIterator1; segIterator1++ )
197 {
198 // Build segment
199 SEG firstSegment = *segIterator1;
200
201 for( auto segIterator2 = poly2->IterateSegmentsWithHoles(); segIterator2; segIterator2++ )
202 {
203 // Build second segment
204 SEG secondSegment = *segIterator2;
205
206 // Check whether the two segments built collide
207 if( firstSegment.Collide( secondSegment, 0 ) )
208 return true;
209 }
210 }
211
212 // If a contour is inside another contour, no segments intersects, but the zones
213 // can be combined if a corner is inside an outline (only one corner is enough)
214 for( auto iter = poly2->IterateWithHoles(); iter; iter++ )
215 {
216 if( poly1->Contains( *iter ) )
217 return true;
218 }
219
220 for( auto iter = poly1->IterateWithHoles(); iter; iter++ )
221 {
222 if( poly2->Contains( *iter ) )
223 return true;
224 }
225
226 return false;
227 }
228
229
230
231