zones_by_polygon.cpp 30.9 KB
Newer Older
1 2 3
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
4
 * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 6
 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
7
 * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

27 28 29
/**
 * @file zones_by_polygon.cpp
 */
30

31
#include <fctsys.h>
32
#include <kiface_i.h>
33 34 35
#include <class_drawpanel.h>
#include <confirm.h>
#include <wxPcbStruct.h>
36

37 38
#include <class_board.h>
#include <class_zone.h>
39

40 41 42 43 44 45
#include <pcbnew.h>
#include <zones.h>
#include <pcbnew_id.h>
#include <protos.h>
#include <zones_functions_for_undo_redo.h>
#include <drc_stuff.h>
46

47
// Outline creation:
48
static void Abort_Zone_Create_Outline( EDA_DRAW_PANEL* Panel, wxDC* DC );
49 50
static void Show_New_Edge_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
                                            const wxPoint& aPosition, bool aErase );
51

52
// Corner moving
53
static void Abort_Zone_Move_Corner_Or_Outlines( EDA_DRAW_PANEL* Panel, wxDC* DC );
54 55 56 57
static void Show_Zone_Corner_Or_Outline_While_Move_Mouse( EDA_DRAW_PANEL* aPanel,
                                                          wxDC*           aDC,
                                                          const wxPoint&  aPosition,
                                                          bool            aErase );
58

Dick Hollenbeck's avatar
Dick Hollenbeck committed
59
// Local variables
60
static wxPoint         s_CornerInitialPosition;     // Used to abort a move corner command
61
static bool            s_CornerIsNew;               // Used to abort a move corner command (if it is a new corner, it must be deleted)
62
static bool            s_AddCutoutToCurrentZone;    // if true, the next outline will be added to s_CurrentZone
63 64
static ZONE_CONTAINER* s_CurrentZone;               // if != NULL, these ZONE_CONTAINER params will be used for the next zone
static wxPoint         s_CursorLastPosition;        // in move zone outline, last cursor position. Used to calculate the move vector
65
static PICKED_ITEMS_LIST s_PickedList;              // a picked list to save zones for undo/redo command
66
static PICKED_ITEMS_LIST s_AuxiliaryList;           // a picked list to store zones that are deleted or added when combined
67 68


Dick Hollenbeck's avatar
Dick Hollenbeck committed
69
void PCB_EDIT_FRAME::Add_Similar_Zone( wxDC* DC, ZONE_CONTAINER* aZone )
70
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
71
    if( !aZone )
72
        return;
73

74
    s_AddCutoutToCurrentZone = false;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
75
    s_CurrentZone = aZone;
76

Dick Hollenbeck's avatar
Dick Hollenbeck committed
77 78 79 80
    // set zone settings to the current zone
    ZONE_SETTINGS  zoneInfo = GetZoneSettings();
    zoneInfo << *aZone;
    SetZoneSettings( zoneInfo );
81

Dick Hollenbeck's avatar
Dick Hollenbeck committed
82
    // Use the general event handler to set others params (like toolbar)
83
    wxCommandEvent evt;
84
    evt.SetId( aZone->GetIsKeepout() ? ID_PCB_KEEPOUT_AREA_BUTT : ID_PCB_ZONES_BUTT );
85
    OnSelectTool( evt );
86 87 88
}


Dick Hollenbeck's avatar
Dick Hollenbeck committed
89
void PCB_EDIT_FRAME::Add_Zone_Cutout( wxDC* DC, ZONE_CONTAINER* aZone )
90
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
91
    if( !aZone )
92
        return;
93

94
    s_AddCutoutToCurrentZone = true;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
95
    s_CurrentZone = aZone;
96

Dick Hollenbeck's avatar
Dick Hollenbeck committed
97 98 99 100
    // set zones setup to the current zone
    ZONE_SETTINGS zoneInfo = GetZoneSettings();
    zoneInfo << *aZone;
    SetZoneSettings( zoneInfo );
101

Dick Hollenbeck's avatar
Dick Hollenbeck committed
102
    // Use the general event handle to set others params (like toolbar)
103
    wxCommandEvent evt;
104
    evt.SetId( aZone->GetIsKeepout() ? ID_PCB_KEEPOUT_AREA_BUTT : ID_PCB_ZONES_BUTT );
105
    OnSelectTool( evt );
106 107
}

108

109 110 111 112 113 114 115 116
void PCB_EDIT_FRAME::duplicateZone( wxDC* aDC, ZONE_CONTAINER* aZone )
{
    ZONE_CONTAINER* newZone = new ZONE_CONTAINER( GetBoard() );
    newZone->Copy( aZone );
    newZone->UnFill();
    ZONE_SETTINGS zoneSettings;
    zoneSettings << *aZone;

117 118 119 120 121 122 123 124 125 126
    bool success;

    if( aZone->GetIsKeepout() )
        success = InvokeKeepoutAreaEditor( this, &zoneSettings );
    else if( aZone->IsOnCopperLayer() )
        success = InvokeCopperZonesEditor( this, &zoneSettings );
    else
        success = InvokeNonCopperZonesEditor( this, aZone, &zoneSettings );

    if( success )
127 128
    {
        zoneSettings.ExportSetting( *newZone );
129
        newZone->Outline()->Hatch();
130

131
        s_AuxiliaryList.ClearListAndDeleteItems();
132
        s_PickedList.ClearListAndDeleteItems();
133
        SaveCopyOfZones( s_PickedList, GetBoard(), newZone->GetNetCode(), newZone->GetLayer() );
134 135 136 137 138 139 140 141
        GetBoard()->Add( newZone );

        ITEM_PICKER picker( newZone, UR_NEW );
        s_PickedList.PushItem( picker );

        GetScreen()->SetCurItem( NULL );       // This outline may be deleted when merging outlines

        // Combine zones if possible
142
        GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, newZone );
143 144 145 146 147 148 149 150 151 152 153

        // Redraw zones
        GetBoard()->RedrawAreasOutlines( m_canvas, aDC, GR_OR, newZone->GetLayer() );
        GetBoard()->RedrawFilledAreas( m_canvas, aDC, GR_OR, newZone->GetLayer() );

        if( GetBoard()->GetAreaIndex( newZone ) >= 0
           && GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( newZone, true ) )
        {
            DisplayError( this, _( "Duplicate Zone: The outline of the duplicated zone fails DRC check!" ) );
        }

154
        UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
155 156 157 158 159 160 161 162 163 164
        SaveCopyInUndoList( s_PickedList, UR_UNSPECIFIED );
        s_PickedList.ClearItemsList();

        OnModify();
    }
    else
        delete newZone;
}


165
int PCB_EDIT_FRAME::Delete_LastCreatedCorner( wxDC* DC )
166
{
167
    ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour;
168

Dick Hollenbeck's avatar
Dick Hollenbeck committed
169
    if( !zone )
170
        return 0;
171

Dick Hollenbeck's avatar
Dick Hollenbeck committed
172
    if( !zone->GetNumCorners() )
173
        return 0;
174

175
    zone->DrawWhileCreateOutline( m_canvas, DC, GR_XOR );
176

177
    if( zone->GetNumCorners() > 2 )
178
    {
179
        zone->Outline()->DeleteCorner( zone->GetNumCorners() - 1 );
180

181
        if( m_canvas->IsMouseCaptured() )
182
            m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
183 184 185
    }
    else
    {
186
        m_canvas->SetMouseCapture( NULL, NULL );
187
        SetCurItem( NULL );
188
        zone->RemoveAllContours();
189
        zone->ClearFlags();
190
    }
191

192
    return zone->GetNumCorners();
193 194 195 196
}


/**
197
 * Function Abort_Zone_Create_Outline
198
 * cancels the Begin_Zone command if at least one EDGE_ZONE was created.
199
 */
200
static void Abort_Zone_Create_Outline( EDA_DRAW_PANEL* Panel, wxDC* DC )
201
{
202 203
    PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) Panel->GetParent();
    ZONE_CONTAINER* zone = pcbframe->GetBoard()->m_CurrentZoneContour;
204

205
    if( zone )
206
    {
207 208
        zone->DrawWhileCreateOutline( Panel, DC, GR_XOR );
        zone->RemoveAllContours();
209 210 211 212 213 214 215
        if( zone->IsNew() )
        {
            delete zone;
            pcbframe->GetBoard()->m_CurrentZoneContour = NULL;
        }
        else
            zone->ClearFlags();
216 217 218
    }

    pcbframe->SetCurItem( NULL );
219 220
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;
221
    Panel->SetMouseCapture( NULL, NULL );
222 223 224
}


Dick Hollenbeck's avatar
Dick Hollenbeck committed
225
void PCB_EDIT_FRAME::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone,
226
                                             int corner_id, bool IsNewCorner )
227
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
228
    if( aZone->IsOnCopperLayer() ) // Show the Net
229
    {
230
        if( GetBoard()->IsHighLightNetON() && DC )
231
        {
232
            HighLight( DC );  // Remove old highlight selection
233
        }
234

Dick Hollenbeck's avatar
Dick Hollenbeck committed
235
        ZONE_SETTINGS zoneInfo = GetZoneSettings();
236
        zoneInfo.m_NetcodeSelection = aZone->GetNetCode();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
237 238
        SetZoneSettings( zoneInfo );

239
        GetBoard()->SetHighLightNet( aZone->GetNetCode() );
240

241
        if( DC )
242
            HighLight( DC );
243
    }
244

245 246 247

    // Prepare copy of old zones, for undo/redo.
    // if the corner is new, remove it from list, save and insert it in list
248 249
    int cx = aZone->Outline()->GetX( corner_id );
    int cy = aZone->Outline()->GetY( corner_id );
250 251

    if ( IsNewCorner )
252
        aZone->Outline()->DeleteCorner( corner_id );
253

254
    s_AuxiliaryList.ClearListAndDeleteItems();
255 256
    s_PickedList.ClearListAndDeleteItems();

257
    SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
258

259
    if ( IsNewCorner )
260
        aZone->Outline()->InsertCorner(corner_id-1, cx, cy );
261

Dick Hollenbeck's avatar
Dick Hollenbeck committed
262
    aZone->SetFlags( IN_EDIT );
263
    m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse,
264
                                Abort_Zone_Move_Corner_Or_Outlines );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
265
    s_CornerInitialPosition = aZone->GetCornerPosition( corner_id );
266
    s_CornerIsNew = IsNewCorner;
267 268
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;
269 270
}

271

272
void PCB_EDIT_FRAME::Start_Move_Zone_Drag_Outline_Edge( wxDC*           DC,
Dick Hollenbeck's avatar
Dick Hollenbeck committed
273
                                                        ZONE_CONTAINER* aZone,
274
                                                        int             corner_id )
275
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
276
    aZone->SetFlags( IS_DRAGGED );
277
    aZone->SetSelectedCorner( corner_id );
278
    m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse,
279
                                Abort_Zone_Move_Corner_Or_Outlines );
280
    s_CursorLastPosition     = s_CornerInitialPosition = GetCrossHairPosition();
281 282
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;
283 284

    s_PickedList.ClearListAndDeleteItems();
285
    s_AuxiliaryList.ClearListAndDeleteItems();
286
    SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
287 288 289
}


Dick Hollenbeck's avatar
Dick Hollenbeck committed
290
void PCB_EDIT_FRAME::Start_Move_Zone_Outlines( wxDC* DC, ZONE_CONTAINER* aZone )
CHARRAS's avatar
CHARRAS committed
291
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
292 293
    // Show the Net
    if( aZone->IsOnCopperLayer() ) // Show the Net
CHARRAS's avatar
CHARRAS committed
294
    {
295
        if( GetBoard()->IsHighLightNetON() )
296
        {
297
            HighLight( DC );  // Remove old highlight selection
298
        }
CHARRAS's avatar
CHARRAS committed
299

Dick Hollenbeck's avatar
Dick Hollenbeck committed
300
        ZONE_SETTINGS zoneInfo = GetZoneSettings();
301
        zoneInfo.m_NetcodeSelection = aZone->GetNetCode();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
302 303
        SetZoneSettings( zoneInfo );

304
        GetBoard()->SetHighLightNet( aZone->GetNetCode() );
305
        HighLight( DC );
306
    }
CHARRAS's avatar
CHARRAS committed
307

308
    s_PickedList.ClearListAndDeleteItems();
309
    s_AuxiliaryList.ClearListAndDeleteItems();
310
    SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
311

Dick Hollenbeck's avatar
Dick Hollenbeck committed
312
    aZone->SetFlags( IS_MOVED );
313
    m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse,
314
                                Abort_Zone_Move_Corner_Or_Outlines );
315
    s_CursorLastPosition = s_CornerInitialPosition = GetCrossHairPosition();
CHARRAS's avatar
CHARRAS committed
316 317 318 319 320
    s_CornerIsNew = false;
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;
}

321

Dick Hollenbeck's avatar
Dick Hollenbeck committed
322
void PCB_EDIT_FRAME::End_Move_Zone_Corner_Or_Outlines( wxDC* DC, ZONE_CONTAINER* aZone )
323
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
324
    aZone->ClearFlags();
325
    m_canvas->SetMouseCapture( NULL, NULL );
326

327
    if( DC )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
328
        aZone->Draw( m_canvas, DC, GR_OR );
329

330
    OnModify();
331 332 333
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;

334
    SetCurItem( NULL );       // This outline can be deleted when merging outlines
335

Dick Hollenbeck's avatar
Dick Hollenbeck committed
336
    // Combine zones if possible
337
    wxBusyCursor dummy;
338
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );
339
    m_canvas->Refresh();
340

341

Dick Hollenbeck's avatar
Dick Hollenbeck committed
342
    int ii = GetBoard()->GetAreaIndex( aZone );     // test if aZone exists
343

344
    if( ii < 0 )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
345
        aZone = NULL;                          // was removed by combining zones
346

347
    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
348 349 350
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
    s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

Dick Hollenbeck's avatar
Dick Hollenbeck committed
351
    int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( aZone, true );
352

353 354 355 356
    if( error_count )
    {
        DisplayError( this, _( "Area: DRC outline error" ) );
    }
357 358 359
}


Dick Hollenbeck's avatar
Dick Hollenbeck committed
360
void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone )
361
{
362
    OnModify();
363

364
    if( aZone->Outline()->GetCornersCount() <= 3 )
365
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
366
        m_canvas->RefreshDrawingRect( aZone->GetBoundingBox() );
367

368
        if( DC )
369
        {  // Remove the full zone because this is no more an area
Dick Hollenbeck's avatar
Dick Hollenbeck committed
370 371
            aZone->UnFill();
            aZone->DrawFilledArea( m_canvas, DC, GR_XOR );
372
        }
373

Dick Hollenbeck's avatar
Dick Hollenbeck committed
374
        GetBoard()->Delete( aZone );
375 376
        return;
    }
377

Dick Hollenbeck's avatar
Dick Hollenbeck committed
378
    LAYER_ID layer = aZone->GetLayer();
379

380 381
    if( DC )
    {
382 383
        GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_XOR, layer );
        GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_XOR, layer );
384
    }
385

386
    s_AuxiliaryList.ClearListAndDeleteItems();
387
    s_PickedList. ClearListAndDeleteItems();
388
    SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
389
    aZone->Outline()->DeleteCorner( aZone->GetSelectedCorner() );
390

Dick Hollenbeck's avatar
Dick Hollenbeck committed
391
    // modify zones outlines according to the new aZone shape
392
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );
393

394 395
    if( DC )
    {
396 397
        GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, layer );
        GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_OR, layer );
398
    }
399

400
    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
401 402 403
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
    s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

Dick Hollenbeck's avatar
Dick Hollenbeck committed
404
    int ii = GetBoard()->GetAreaIndex( aZone );     // test if aZone exists
405

406
    if( ii < 0 )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
407
        aZone = NULL;   // aZone does not exist anymore, after combining zones
408

Dick Hollenbeck's avatar
Dick Hollenbeck committed
409
    int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( aZone, true );
410

411 412 413 414
    if( error_count )
    {
        DisplayError( this, _( "Area: DRC outline error" ) );
    }
415 416 417 418
}


/**
CHARRAS's avatar
CHARRAS committed
419
 * Function Abort_Zone_Move_Corner_Or_Outlines
420 421
 * cancels the Begin_Zone state if at least one EDGE_ZONE has been created.
 */
422
void Abort_Zone_Move_Corner_Or_Outlines( EDA_DRAW_PANEL* Panel, wxDC* DC )
423
{
424
    PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) Panel->GetParent();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
425
    ZONE_CONTAINER* zone     = (ZONE_CONTAINER*) pcbframe->GetCurItem();
426

Dick Hollenbeck's avatar
Dick Hollenbeck committed
427
    if( zone->IsMoving() )
428 429 430
    {
        wxPoint offset;
        offset = s_CornerInitialPosition - s_CursorLastPosition;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
431
        zone->Move( offset );
432
    }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
433
    else if( zone->IsDragging() )
434
    {
435 436 437
        wxPoint offset = s_CornerInitialPosition - s_CursorLastPosition;
        int selection = zone->GetSelectedCorner();
        zone->MoveEdge( offset, selection );
438
    }
439 440 441 442
    else
    {
        if( s_CornerIsNew )
        {
443
            zone->Outline()->DeleteCorner( zone->GetSelectedCorner() );
444 445 446 447
        }
        else
        {
            wxPoint pos = s_CornerInitialPosition;
448
            zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y );
449 450
        }
    }
451

452
    Panel->SetMouseCapture( NULL, NULL );
453
    s_AuxiliaryList.ClearListAndDeleteItems();
454
    s_PickedList. ClearListAndDeleteItems();
455 456
    Panel->Refresh();

457
    pcbframe->SetCurItem( NULL );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
458
    zone->ClearFlags();
459 460
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;
461 462 463
}


Dick Hollenbeck's avatar
Dick Hollenbeck committed
464
/// Redraws the zone outline when moving a corner according to the cursor position
465 466
void Show_Zone_Corner_Or_Outline_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
                                                   const wxPoint& aPosition, bool aErase )
467
{
468 469
    PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) aPanel->GetParent();
    ZONE_CONTAINER* zone = (ZONE_CONTAINER*) pcbframe->GetCurItem();
470

Dick Hollenbeck's avatar
Dick Hollenbeck committed
471
    if( aErase )    // Undraw edge in old position
472
    {
473
        zone->Draw( aPanel, aDC, GR_XOR );
474 475
    }

476
    wxPoint pos = pcbframe->GetCrossHairPosition();
477

478
    if( zone->IsMoving() )
479 480
    {
        wxPoint offset;
481 482 483 484
        offset = pos - s_CursorLastPosition;
        zone->Move( offset );
        s_CursorLastPosition = pos;
    }
485
    else if( zone->IsDragging() )
486
    {
487 488 489
        wxPoint offset = pos - s_CursorLastPosition;
        int selection = zone->GetSelectedCorner();
        zone->MoveEdge( offset, selection );
490 491 492
        s_CursorLastPosition = pos;
    }
    else
493
    {
494
        zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y );
495
    }
CHARRAS's avatar
CHARRAS committed
496

497
    zone->Draw( aPanel, aDC, GR_XOR );
498 499 500
}


501

502
int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC )
503
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
504 505
    ZONE_SETTINGS zoneInfo = GetZoneSettings();

506
    // verify if s_CurrentZone exists (could be deleted since last selection) :
507
    int ii;
508
    for( ii = 0; ii < GetBoard()->GetAreaCount(); ii++ )
509
    {
510
        if( s_CurrentZone == GetBoard()->GetArea( ii ) )
511 512 513
            break;
    }

514
    if( ii >= GetBoard()->GetAreaCount() ) // Not found: could be deleted since last selection
515 516 517 518
    {
        s_AddCutoutToCurrentZone = false;
        s_CurrentZone = NULL;
    }
519

520 521 522 523
    ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour;

    // Verify if a new zone is allowed on this layer:
    if( zone == NULL  )
524
    {
Maciej Suminski's avatar
Maciej Suminski committed
525
        if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT && !IsCopperLayer( GetActiveLayer() ) )
526 527 528 529 530 531
        {
            DisplayError( this,
                          _( "Error: a keepout area is allowed only on copper layers" ) );
            return 0;
        }
    }
532

533 534 535 536 537 538 539
    // If no zone contour in progress, a new zone is being created,
    if( zone == NULL )
    {
        zone = GetBoard()->m_CurrentZoneContour = new ZONE_CONTAINER( GetBoard() );
        zone->SetFlags( IS_NEW );
        zone->SetTimeStamp( GetNewTimeStamp() );
    }
540 541

    if( zone->GetNumCorners() == 0 )    // Start a new contour: init zone params (net, layer ...)
542
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
543
        if( !s_CurrentZone )            // A new outline is created, from scratch
544
        {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
545 546
            ZONE_EDIT_T edited;

547
            // Init zone params to reasonable values
548
            zone->SetLayer( GetActiveLayer() );
549

550
            // Prompt user for parameters:
551
            m_canvas->SetIgnoreMouseEvents( true );
552

553
            if( zone->IsOnCopperLayer() )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
554 555 556
            {
                // Put a zone on a copper layer
                if( GetBoard()->GetHighLightNetCode() > 0 )
557
                {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
558
                    zoneInfo.m_NetcodeSelection = GetBoard()->GetHighLightNetCode();
559
                    zone->SetNetCode( zoneInfo.m_NetcodeSelection );
560
                }
561

562
                double tmp = ZONE_THERMAL_RELIEF_GAP_MIL;
563 564 565

                wxConfigBase* cfg = Kiface().KifaceSettings();
                cfg->Read( ZONE_THERMAL_RELIEF_GAP_STRING_KEY, &tmp );
566
                zoneInfo.m_ThermalReliefGap = KiROUND( tmp * IU_PER_MILS);
567

568
                tmp = ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL;
569
                cfg->Read( ZONE_THERMAL_RELIEF_COPPER_WIDTH_STRING_KEY, &tmp );
570 571 572
                zoneInfo.m_ThermalReliefCopperBridge = KiROUND( tmp * IU_PER_MILS );

                tmp = ZONE_CLEARANCE_MIL;
573
                cfg->Read( ZONE_CLEARANCE_WIDTH_STRING_KEY, &tmp );
574 575 576
                zoneInfo.m_ZoneClearance = KiROUND( tmp * IU_PER_MILS );

                tmp = ZONE_THICKNESS_MIL;
577
                cfg->Read( ZONE_MIN_THICKNESS_WIDTH_STRING_KEY, &tmp );
578
                zoneInfo.m_ZoneMinThickness = KiROUND( tmp * IU_PER_MILS );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
579 580

                zoneInfo.m_CurrentZone_Layer = zone->GetLayer();
581

582 583 584
                if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT )
                {
                    zoneInfo.SetIsKeepout( true );
585 586
                    // Netcode and netname are irrelevant,
                    // so ensure they are cleared
587
                    zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
588 589 590
                    edited = InvokeKeepoutAreaEditor( this, &zoneInfo );
                }
                else
591 592
                {
                    zoneInfo.SetIsKeepout( false );
593
                    edited = InvokeCopperZonesEditor( this, &zoneInfo );
594
                }
595 596 597
            }
            else   // Put a zone on a non copper layer (technical layer)
            {
598
                zoneInfo.SetIsKeepout( false );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
599 600
                zoneInfo.m_NetcodeSelection = 0;     // No net for non copper zones
                edited = InvokeNonCopperZonesEditor( this, zone, &zoneInfo );
601
            }
602

603
            m_canvas->MoveCursorToCrossHair();
604
            m_canvas->SetIgnoreMouseEvents( false );
605

Dick Hollenbeck's avatar
Dick Hollenbeck committed
606
            if( edited == ZONE_ABORT )
607 608 609
            {
                GetBoard()->m_CurrentZoneContour = NULL;
                delete zone;
610
                return 0;
611
            }
dickelbeck's avatar
dickelbeck committed
612

613
            // Switch active layer to the selected zone layer
614
            SetActiveLayer( zoneInfo.m_CurrentZone_Layer );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
615
            SetZoneSettings( zoneInfo );
616
        }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
617 618 619 620 621 622
        else
        {
            // Start a new contour: init zone params (net and layer) from an existing
            // zone (add cutout or similar zone)

            zoneInfo.m_CurrentZone_Layer = s_CurrentZone->GetLayer();
623
            SetActiveLayer( s_CurrentZone->GetLayer() );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
624 625 626 627

            zoneInfo << *s_CurrentZone;

            SetZoneSettings( zoneInfo );
628
        }
629

Dick Hollenbeck's avatar
Dick Hollenbeck committed
630
        // Show the Net for zones on copper layers
631 632
        if( IsCopperLayer( zoneInfo.m_CurrentZone_Layer ) &&
            !zoneInfo.GetIsKeepout() )
633
        {
634
            if( s_CurrentZone )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
635
            {
636
                zoneInfo.m_NetcodeSelection = s_CurrentZone->GetNetCode();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
637 638
                GetBoard()->SetZoneSettings( zoneInfo );
            }
639

640
            if( GetBoard()->IsHighLightNetON() )
641
            {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
642
                HighLight( DC );    // Remove old highlight selection
643
            }
644

Dick Hollenbeck's avatar
Dick Hollenbeck committed
645
            GetBoard()->SetHighLightNet( zoneInfo.m_NetcodeSelection );
646
            HighLight( DC );
647
        }
648

649
        if( !s_AddCutoutToCurrentZone )
650
            s_CurrentZone = NULL; // the zone is used only once ("add similar zone" command)
651
    }
652 653

    // if first segment
Dick Hollenbeck's avatar
Dick Hollenbeck committed
654
    if( zone->GetNumCorners() == 0 )
655
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
656 657
        zoneInfo.ExportSetting( *zone );

658
        zone->Outline()->Start( zoneInfo.m_CurrentZone_Layer,
659 660
                                GetCrossHairPosition().x,
                                GetCrossHairPosition().y,
661
                                zone->GetHatchStyle() );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
662

663
        zone->AppendCorner( GetCrossHairPosition() );
664

665
        if( g_Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() )
666
        {
667
            zone->ClearFlags();
668 669 670 671
            zone->RemoveAllContours();

            // use the form of SetCurItem() which does not write to the msg panel,
            // SCREEN::SetCurItem(), so the DRC error remains on screen.
672
            // PCB_EDIT_FRAME::SetCurItem() calls DisplayInfo().
673 674
            GetScreen()->SetCurItem( NULL );
            DisplayError( this,
675
                          _( "DRC error: this start point is inside or too close an other area" ) );
676 677
            return 0;
        }
678

679
        SetCurItem( zone );
680
        m_canvas->SetMouseCapture( Show_New_Edge_While_Move_Mouse, Abort_Zone_Create_Outline );
681
    }
682
    else    // edge in progress:
683
    {
684 685
        ii = zone->GetNumCorners() - 1;

686 687
        // edge in progress : the current corner coordinate was set
        // by Show_New_Edge_While_Move_Mouse
688
        if( zone->GetCornerPosition( ii - 1 ) != zone->GetCornerPosition( ii ) )
689
        {
690
            if( !g_Drc_On || !zone->IsOnCopperLayer() || ( m_drc->Drc( zone, ii - 1 ) == OK_DRC ) )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
691 692
            {
                // Ok, we can add a new corner
693 694
                if( m_canvas->IsMouseCaptured() )
                    m_canvas->CallMouseCapture( DC, wxPoint(0,0), false );
695
                zone->AppendCorner( GetCrossHairPosition() );
696
                SetCurItem( zone );     // calls DisplayInfo().
697 698
                if( m_canvas->IsMouseCaptured() )
                    m_canvas->CallMouseCapture( DC, wxPoint(0,0), false );
699
            }
700 701 702
        }
    }

703
    return zone->GetNumCorners();
704 705 706
}


707
bool PCB_EDIT_FRAME::End_Zone( wxDC* DC )
708
{
709
    ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour;
710

Dick Hollenbeck's avatar
Dick Hollenbeck committed
711
    if( !zone )
712 713
        return true;

714
    // Validate the current outline:
715 716
    if( zone->GetNumCorners() <= 2 )   // An outline must have 3 corners or more
    {
717
        Abort_Zone_Create_Outline( m_canvas, DC );
718 719 720
        return true;
    }

721
    // Remove the last corner if is is at the same location as the prevoius corner
722
    zone->Outline()->RemoveNullSegments();
723

724 725
    // Validate the current edge:
    int icorner = zone->GetNumCorners() - 1;
726
    if( zone->IsOnCopperLayer() )
727
    {
728
        if( g_Drc_On && m_drc->Drc( zone, icorner - 1 ) == BAD_DRC )  // we can't validate last edge
729
            return false;
730

731
        if( g_Drc_On && m_drc->Drc( zone, icorner ) == BAD_DRC )      // we can't validate the closing edge
732 733
        {
            DisplayError( this,
734
                          _( "DRC error: closing this area creates a drc error with an other area" ) );
735
            m_canvas->MoveCursorToCrossHair();
736 737
            return false;
        }
738 739
    }

740
    zone->ClearFlags();
741

742
    zone->DrawWhileCreateOutline( m_canvas, DC, GR_XOR );
743

744
    m_canvas->SetMouseCapture( NULL, NULL );
745

746
    // Undraw old drawings, because they can have important changes
Dick Hollenbeck's avatar
Dick Hollenbeck committed
747
    LAYER_ID layer = zone->GetLayer();
748 749
    GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_XOR, layer );
    GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_XOR, layer );
750

751
    // Save initial zones configuration, for undo/redo, before adding new zone
752
    s_AuxiliaryList.ClearListAndDeleteItems();
753
    s_PickedList.ClearListAndDeleteItems();
754
    SaveCopyOfZones(s_PickedList, GetBoard(), zone->GetNetCode(), zone->GetLayer() );
755

Dick Hollenbeck's avatar
Dick Hollenbeck committed
756 757
    // Put new zone in list
    if( !s_CurrentZone )
758
    {
759
        zone->Outline()->CloseLastContour(); // Close the current corner list
760 761
        GetBoard()->Add( zone );
        GetBoard()->m_CurrentZoneContour = NULL;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
762

763 764 765
        // Add this zone in picked list, as new item
        ITEM_PICKER picker( zone, UR_NEW );
        s_PickedList.PushItem( picker );
766
    }
767 768
    else    // Append this outline as a cutout to an existing zone
    {
769
        for( int ii = 0; ii < zone->GetNumCorners(); ii++ )
770
        {
771
            s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) );
772
        }
773

774
        s_CurrentZone->Outline()->CloseLastContour(); // Close the current corner list
775 776
        zone->RemoveAllContours();      // All corners are copied in s_CurrentZone. Free corner list.
        zone = s_CurrentZone;
777
    }
778

779 780
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;
781

782
    GetScreen()->SetCurItem( NULL );       // This outline can be deleted when merging outlines
783 784

    // Combine zones if possible :
785
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, zone );
786 787

    // Redraw the real edge zone :
788 789
    GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, layer );
    GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_OR, layer );
790

Dick Hollenbeck's avatar
Dick Hollenbeck committed
791
    int ii = GetBoard()->GetAreaIndex( zone );   // test if zone exists
792

793 794
    if( ii < 0 )
        zone = NULL;                        // was removed by combining zones
795

796
    int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( zone, true );
797

798 799 800 801
    if( error_count )
    {
        DisplayError( this, _( "Area: DRC outline error" ) );
    }
802

803
    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
804 805 806
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
    s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

807
    OnModify();
808
    return true;
809 810 811
}


812
/* Redraws the zone outlines when moving mouse
813
 */
814 815
static void Show_New_Edge_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
                                            const wxPoint& aPosition, bool aErase )
816
{
817
    PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) aPanel->GetParent();
818
    wxPoint         c_pos    = pcbframe->GetCrossHairPosition();
819
    ZONE_CONTAINER* zone = pcbframe->GetBoard()->m_CurrentZoneContour;
820

Dick Hollenbeck's avatar
Dick Hollenbeck committed
821
    if( !zone )
822 823
        return;

824
    int icorner = zone->GetNumCorners() - 1;
825

Dick Hollenbeck's avatar
Dick Hollenbeck committed
826
    if( icorner < 1 )
827
        return;     // We must have 2 (or more) corners
828

Dick Hollenbeck's avatar
Dick Hollenbeck committed
829
    if( aErase )    // Undraw edge in old position
830
    {
831
        zone->DrawWhileCreateOutline( aPanel, aDC );
832 833
    }

Dick Hollenbeck's avatar
Dick Hollenbeck committed
834 835
    // Redraw the current edge in its new position
    if( pcbframe->GetZoneSettings().m_Zone_45_Only )
836
    {
837
        // calculate the new position as allowed
838
        wxPoint StartPoint = zone->GetCornerPosition( icorner - 1 );
839
        CalculateSegmentEndPoint( c_pos, StartPoint.x, StartPoint.y, &c_pos.x, &c_pos.y );
840 841
    }

842 843
    zone->SetCornerPosition( icorner, c_pos );

844
    zone->DrawWhileCreateOutline( aPanel, aDC );
845 846 847
}


Dick Hollenbeck's avatar
Dick Hollenbeck committed
848
void PCB_EDIT_FRAME::Edit_Zone_Params( wxDC* DC, ZONE_CONTAINER* aZone )
849
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
850 851 852
    ZONE_EDIT_T     edited;
    ZONE_SETTINGS   zoneInfo = GetZoneSettings();

853
    m_canvas->SetIgnoreMouseEvents( true );
854

Dick Hollenbeck's avatar
Dick Hollenbeck committed
855 856
    // Save initial zones configuration, for undo/redo, before adding new zone
    // note the net name and the layer can be changed, so we must save all zones
857
    s_AuxiliaryList.ClearListAndDeleteItems();
858
    s_PickedList.ClearListAndDeleteItems();
859
    SaveCopyOfZones(s_PickedList, GetBoard(), -1, UNDEFINED_LAYER );
860

861 862 863 864 865 866
    if( aZone->GetIsKeepout() )
    {
        // edit a keepout area on a copper layer
        zoneInfo << *aZone;
        edited = InvokeKeepoutAreaEditor( this, &zoneInfo );
    }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
867
    else if( IsCopperLayer( aZone->GetLayer() ) )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
868 869 870 871 872 873
    {
        // edit a zone on a copper layer

        zoneInfo << *aZone;

        edited = InvokeCopperZonesEditor( this, &zoneInfo );
874
    }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
875
    else
876
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
877
        edited = InvokeNonCopperZonesEditor( this, aZone, &zoneInfo );
878
    }
879

880
    m_canvas->MoveCursorToCrossHair();
881
    m_canvas->SetIgnoreMouseEvents( false );
882

Dick Hollenbeck's avatar
Dick Hollenbeck committed
883
    if( edited == ZONE_ABORT )
884
    {
885
        s_AuxiliaryList.ClearListAndDeleteItems();
886 887 888
        s_PickedList.ClearListAndDeleteItems();
        return;
    }
889

Dick Hollenbeck's avatar
Dick Hollenbeck committed
890 891 892
    SetZoneSettings( zoneInfo );

    if( edited == ZONE_EXPORT_VALUES )
893
    {
894
        UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
895 896
        SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
        s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items
897
        return;
898
    }
899

900
    // Undraw old zone outlines
901
    for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ )
902
    {
903
        ZONE_CONTAINER* edge_zone = GetBoard()->GetArea( ii );
904
        edge_zone->Draw( m_canvas, DC, GR_XOR );
905
    }
906

Dick Hollenbeck's avatar
Dick Hollenbeck committed
907 908 909
    zoneInfo.ExportSetting( *aZone );

    NETINFO_ITEM* net = GetBoard()->FindNet( zoneInfo.m_NetcodeSelection );
910

911
    if( net )   // net == NULL should not occur
912
        aZone->SetNetCode( net->GetNet() );
charras's avatar
charras committed
913

Dick Hollenbeck's avatar
Dick Hollenbeck committed
914
    // Combine zones if possible
915
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );
916

Dick Hollenbeck's avatar
Dick Hollenbeck committed
917
    // Redraw the real new zone outlines
918
    GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, UNDEFINED_LAYER );
919

920
    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
921
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
Dick Hollenbeck's avatar
Dick Hollenbeck committed
922 923

    s_PickedList.ClearItemsList();  // s_ItemsListPicker is no longer owner of picked items
924

925
    OnModify();
926 927
}

928

Dick Hollenbeck's avatar
Dick Hollenbeck committed
929
void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
930
{
931
    int      ncont = aZone->Outline()->GetContour( aZone->GetSelectedCorner() );
932

Dick Hollenbeck's avatar
Dick Hollenbeck committed
933
    EDA_RECT dirty = aZone->GetBoundingBox();
934

935
    // For compatibility with old boards: remove old SEGZONE fill segments
Dick Hollenbeck's avatar
Dick Hollenbeck committed
936
    Delete_OldZone_Fill( NULL, aZone->GetTimeStamp() );
937 938

    // Remove current filling:
Dick Hollenbeck's avatar
Dick Hollenbeck committed
939
    aZone->UnFill();
940

941 942
    if( ncont == 0 )    // This is the main outline: remove all
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
943 944
        SaveCopyInUndoList( aZone, UR_DELETED );
        GetBoard()->Remove( aZone );
945
    }
946 947 948

    else
    {
949
        SaveCopyInUndoList( aZone, UR_CHANGED );
950
        aZone->Outline()->RemoveContour( ncont );
951
    }
952

953
    m_canvas->RefreshDrawingRect( dirty );
954

955
    OnModify();
956
}