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