kicad/pcbnew/zones_by_polygon.cpp

654 lines
20 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: zones_by_polygon.cpp
// Licence: GNU License
/////////////////////////////////////////////////////////////////////////////
#if defined (__GNUG__) && !defined (NO_GCC_PRAGMA)
#pragma implementation "dialog_zones_by_polygon.h"
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
using namespace std;
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "pcbnew.h"
#include "autorout.h"
#include "cell.h"
#include "trigo.h"
#include "protos.h"
/* Local functions */
// Outile creation:
static void Abort_Zone_Create_Outline( WinEDA_DrawPanel* Panel, wxDC* DC );
static void Show_New_Zone_Edge_While_Move_Mouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase );
// Corner moving
static void Abort_Zone_Move_Corner( WinEDA_DrawPanel* Panel, wxDC* DC );
static void Show_Zone_Corner_While_Move_Mouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase );
/* Local variables */
static bool Zone_45_Only = FALSE;
static ZONE_CONTAINER::m_PadInZone s_Zone_Pad_Options = ZONE_CONTAINER::THERMAL_PAD;
static int s_Zone_Layer; // Layer used to create the current zone
static int s_Zone_Hatching; // Option to show the zone area (outlines only, short hatches or full hatches
static int s_NetcodeSelection; // Net code selection for the current zone
static wxPoint s_CornerInitialPosition; // Used to abort a move corner command
static bool s_CornerIsNew; // Used to abort a move corner command (if it is a new corner, it must be deleted)
// key used to store net sort option in config file :
#define ZONE_NET_SORT_OPTION_KEY wxT( "Zone_NetSort_Opt" )
enum zone_cmd {
ZONE_ABORT,
ZONE_OK
};
#include "dialog_zones_by_polygon.cpp"
/*****************************************************************************/
void WinEDA_PcbFrame::Delete_Zone( wxDC* DC, SEGZONE* aZone, long aTimestamp )
/******************************************************************************/
/** Function Delete_Zone
* Remove the zone which include the segment aZone, or the zone which have the given time stamp.
* A zone is a group of segments which have the same TimeStamp
* @param DC = current Device Context (can be NULL)
* @param aZone = zone segment within the zone to delete. Can be NULL
* @param aTimestamp = Timestamp for the zone to delete, used if aZone == NULL
*/
{
int nb_segm = 0;
bool modify = FALSE;
unsigned long TimeStamp;
if( aZone == NULL )
TimeStamp = aTimestamp;
else
TimeStamp = aZone->m_TimeStamp; // Save reference time stamp (aZone will be deleted)
SEGZONE* next;
for( SEGZONE* zone = m_Pcb->m_Zone; zone != NULL; zone = next )
{
next = zone->Next();
if( zone->m_TimeStamp == TimeStamp )
{
modify = TRUE;
/* Erase segment from screen */
if( DC )
Trace_Une_Piste( DrawPanel, DC, zone, nb_segm, GR_XOR );
/* remove item from linked list and free memory */
zone->DeleteStructure();
}
}
if( modify )
{
GetScreen()->SetModify();
GetScreen()->SetRefreshReq();
}
}
/*****************************************************************************/
EDGE_ZONE* WinEDA_PcbFrame::Del_SegmEdgeZone( wxDC* DC, EDGE_ZONE* edge_zone )
/*****************************************************************************/
/* Used only while creating a new zonz outline
* Remove and delete the current outline segment in progress
*/
{
EDGE_ZONE* segm;
if( m_Pcb->m_CurrentLimitZone )
segm = m_Pcb->m_CurrentLimitZone;
else
segm = edge_zone;
if( segm == NULL )
return NULL;
Trace_DrawSegmentPcb( DrawPanel, DC, segm, GR_XOR );
m_Pcb->m_CurrentLimitZone = segm->Next();
delete segm;
segm = m_Pcb->m_CurrentLimitZone;
SetCurItem( segm );
if( segm )
{
segm->Pback = NULL;
if( DrawPanel->ManageCurseur )
DrawPanel->ManageCurseur( DrawPanel, DC, TRUE );
}
else
{
DrawPanel->ManageCurseur = NULL;
DrawPanel->ForceCloseManageCurseur = NULL;
SetCurItem( NULL );
}
return segm;
}
/*************************************************************************/
static void Abort_Zone_Create_Outline( WinEDA_DrawPanel* Panel, wxDC* DC )
/*************************************************************************/
/**
* Function Abort_Zone_Create_Outline
* cancels the Begin_Zone command if at least one EDGE_ZONE has been created.
*/
{
WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) Panel->m_Parent;
if( pcbframe->m_Pcb->m_CurrentLimitZone )
{
if( Panel->ManageCurseur ) // trace in progress
{
Panel->ManageCurseur( Panel, DC, 0 );
}
pcbframe->DelLimitesZone( DC, TRUE );
}
Panel->ManageCurseur = NULL;
Panel->ForceCloseManageCurseur = NULL;
pcbframe->SetCurItem( NULL );
}
/**************************************************************/
void WinEDA_BasePcbFrame::DelLimitesZone( wxDC* DC, bool Redraw )
/**************************************************************/
{
EDGE_ZONE* segment;
EDGE_ZONE* next;
if( m_Pcb->m_CurrentLimitZone == NULL )
return;
// erase the old zone outline, one segment at a time
for( segment = m_Pcb->m_CurrentLimitZone; segment; segment = next )
{
next = segment->Next();
if( Redraw && DC )
Trace_DrawSegmentPcb( DrawPanel, DC, segment, GR_XOR );
delete segment;
}
m_Pcb->m_CurrentLimitZone = NULL;
SetCurItem( NULL );
}
/*******************************************************************************************************/
void WinEDA_PcbFrame::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* zone_container,
int corner_id, bool IsNewCorner )
/*******************************************************************************************************/
/**
* Function Start_Move_Zone_Corner
* Initialise parametres to move an existing corner of a zone.
* if IsNewCorner is true, the Abort_Zone_Move_Corner will remove this corner, if called
*/
{
/* Show the Net */
if( (g_HightLigth_NetCode > 0) && (g_HightLigth_NetCode != s_NetcodeSelection) )
{
Hight_Light( DC ); // Remove old hightlight selection
}
g_HightLigth_NetCode = s_NetcodeSelection;
if( !g_HightLigt_Status )
Hight_Light( DC );
zone_container->m_Flags = IN_EDIT;
DrawPanel->ManageCurseur = Show_Zone_Corner_While_Move_Mouse;
DrawPanel->ForceCloseManageCurseur = Abort_Zone_Move_Corner;
s_CornerInitialPosition.x = zone_container->GetX( corner_id );
s_CornerInitialPosition.y = zone_container->GetY( corner_id );
s_CornerIsNew = IsNewCorner;
}
/***************************************************************************************/
void WinEDA_PcbFrame::End_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* zone_container )
/****************************************************************************************/
/**
* Function End_Move_Zone_Corner
* Terminates a move corner in a zone outline
*/
{
zone_container->m_Flags = 0;
DrawPanel->ManageCurseur = NULL;
DrawPanel->ForceCloseManageCurseur = NULL;
zone_container->Draw( DrawPanel, DC, wxPoint( 0, 0 ), GR_OR );
GetScreen()->SetModify();
}
/**************************************************************/
void Abort_Zone_Move_Corner( WinEDA_DrawPanel* Panel, wxDC* DC )
/**************************************************************/
/**
* Function Abort_Zone_Move_Corner
* cancels the Begin_Zone state if at least one EDGE_ZONE has been created.
*/
{
WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) Panel->m_Parent;
ZONE_CONTAINER* zone_container = (ZONE_CONTAINER*) pcbframe->GetCurItem();
zone_container->Draw( Panel, DC, wxPoint( 0, 0 ), GR_XOR );
if( s_CornerIsNew )
{
zone_container->DeleteCorner( zone_container->m_CornerSelection );
}
else
{
wxPoint pos = s_CornerInitialPosition;
zone_container->MoveCorner( zone_container->m_CornerSelection, pos.x, pos.y );
}
zone_container->Draw( Panel, DC, wxPoint( 0, 0 ), GR_XOR );
Panel->ManageCurseur = NULL;
Panel->ForceCloseManageCurseur = NULL;
pcbframe->SetCurItem( NULL );
zone_container->m_Flags = 0;
}
/**************************************************************************************/
void Show_Zone_Corner_While_Move_Mouse( WinEDA_DrawPanel* Panel, wxDC* DC, bool erase )
/**************************************************************************************/
/* Redraws the zone outline when moving a corner according to the cursor position
*/
{
WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) Panel->m_Parent;
ZONE_CONTAINER* zone_container = (ZONE_CONTAINER*) pcbframe->GetCurItem();
// if( erase ) /* Undraw edge in old position*/
{
zone_container->Draw( Panel, DC, wxPoint( 0, 0 ), GR_XOR );
}
wxPoint pos = pcbframe->GetScreen()->m_Curseur;
zone_container->MoveCorner( zone_container->m_CornerSelection, pos.x, pos.y );
zone_container->Draw( Panel, DC, wxPoint( 0, 0 ), GR_XOR );
}
/*************************************************/
EDGE_ZONE* WinEDA_PcbFrame::Begin_Zone( wxDC* DC )
/*************************************************/
/**
* Function Begin_Zone
* either initializes the first segment of a new zone, or adds an
* intermediate segment.
*/
{
EDGE_ZONE* oldedge;
EDGE_ZONE* newedge = NULL;
oldedge = m_Pcb->m_CurrentLimitZone;
if( m_Pcb->m_CurrentLimitZone == NULL ) /* Start a new contour: init zone params (net and layer) */
{
DrawPanel->m_IgnoreMouseEvents = TRUE;
WinEDA_ZoneFrame* frame = new WinEDA_ZoneFrame( this );
int diag = frame->ShowModal();
frame->Destroy();
DrawPanel->MouseToCursorSchema();
DrawPanel->m_IgnoreMouseEvents = FALSE;
if( diag == ZONE_ABORT )
return NULL;
GetScreen()->m_Active_Layer = s_Zone_Layer;
/* Show the Net */
if( (g_HightLigth_NetCode > 0) && (g_HightLigth_NetCode != s_NetcodeSelection) )
{
Hight_Light( DC ); // Remove old hightlight selection
}
g_HightLigth_NetCode = s_NetcodeSelection;
if( !g_HightLigt_Status )
Hight_Light( DC );
}
// if first segment
if( (m_Pcb->m_CurrentLimitZone == NULL ) /* Initial startt of a new outline */
|| (DrawPanel->ManageCurseur == NULL) ) /* reprise d'un trace complementaire */
{
newedge = new EDGE_ZONE( m_Pcb );
newedge->m_Flags = IS_NEW | STARTPOINT | IS_MOVED;
newedge->m_Start = newedge->m_End = GetScreen()->m_Curseur;
newedge->SetLayer( GetScreen()->m_Active_Layer );
// link into list:
newedge->Pnext = oldedge;
if( oldedge )
oldedge->Pback = newedge;
m_Pcb->m_CurrentLimitZone = newedge;
DrawPanel->ManageCurseur = Show_New_Zone_Edge_While_Move_Mouse;
DrawPanel->ForceCloseManageCurseur = Abort_Zone_Create_Outline;
}
// edge in progress:
else
{
/* edge in progress : the ending point coordinate was set by Show_New_Zone_Edge_While_Move_Mouse */
if( oldedge->m_Start != oldedge->m_End )
{
oldedge->m_Flags &= ~(IS_NEW | IS_MOVED);
newedge = new EDGE_ZONE( oldedge );
newedge->m_Flags = IS_NEW | IS_MOVED;
newedge->m_Start = newedge->m_End = oldedge->m_End;
newedge->SetLayer( GetScreen()->m_Active_Layer );
// link into list:
newedge->Pnext = oldedge;
oldedge->Pback = newedge;
m_Pcb->m_CurrentLimitZone = newedge;
}
}
return newedge;
}
/*********************************************/
void WinEDA_PcbFrame::End_Zone( wxDC* DC )
/*********************************************/
/** Function End_Zone
* Terminates a zone outline creation
* Close the current zone outline considered as a polygon
* put it in the main list m_Pcb->m_ZoneDescriptorList (a vector<ZONE_CONTAINER*>)
*/
{
EDGE_ZONE* edge;
if( m_Pcb->m_CurrentLimitZone )
{
Begin_Zone( DC );
/* The last segment is a stub: its lenght is 0.
* Use it to close the polygon by setting its ending point coordinate = start point of first segment
*/
edge = m_Pcb->m_CurrentLimitZone;
edge->m_Flags &= ~(IS_NEW | IS_MOVED);
while( edge && edge->Next() )
{
edge = edge->Next();
if( edge->m_Flags & STARTPOINT )
break;
edge->m_Flags &= ~(IS_NEW | IS_MOVED);
}
if( edge )
{
edge->m_Flags &= ~(IS_NEW | IS_MOVED);
m_Pcb->m_CurrentLimitZone->m_End = edge->m_Start;
}
Trace_DrawSegmentPcb( DrawPanel, DC, m_Pcb->m_CurrentLimitZone, GR_XOR );
}
DrawPanel->ManageCurseur = NULL;
DrawPanel->ForceCloseManageCurseur = NULL;
/* Put edges in list */
ZONE_CONTAINER* polygon = new ZONE_CONTAINER( m_Pcb );
polygon->SetLayer( GetScreen()->m_Active_Layer );
polygon->SetNet( g_HightLigth_NetCode );
polygon->m_TimeStamp = GetTimeStamp();
EQUIPOT* net = m_Pcb->FindNet( g_HightLigth_NetCode );
if( net )
polygon->m_Netname = net->m_Netname;
edge = m_Pcb->m_CurrentLimitZone;
polygon->Start( GetScreen()->m_Active_Layer, 0, NULL,
edge->m_Start.x, edge->m_Start.y,
s_Zone_Hatching );
edge = edge->Next();
while( edge )
{
polygon->AppendCorner( edge->m_Start.x, edge->m_Start.y );
edge = edge->Next();
}
polygon->Close(); // Close the current corner list
polygon->SetHatch( s_Zone_Hatching );
polygon->m_PadOption = s_Zone_Pad_Options;
polygon->m_ZoneClearance = g_DesignSettings.m_ZoneClearence;
polygon->m_GridFillValue = g_GridRoutingSize;
m_Pcb->m_ZoneDescriptorList.push_back( polygon );
/* Remove the current temporary list */
DelLimitesZone( DC, TRUE );
/* Redraw the real edge zone */
polygon->Draw( DrawPanel, DC, wxPoint( 0, 0 ), GR_OR );
GetScreen()->SetModify();
}
/******************************************************************************************/
static void Show_New_Zone_Edge_While_Move_Mouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase )
/******************************************************************************************/
/* Redraws the edge zone when moving mouse
*/
{
EDGE_ZONE* edge;
EDGE_ZONE* currentEdge;
WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) panel->m_Parent;
if( pcbframe->m_Pcb->m_CurrentLimitZone == NULL )
return;
if( erase ) /* Undraw edge in old position*/
{
edge = pcbframe->m_Pcb->m_CurrentLimitZone;
// for( ; edge; edge = edge->Next() )
{
Trace_DrawSegmentPcb( panel, DC, edge, GR_XOR );
}
}
/* Reinit layer (which can be changed) */
for( edge = pcbframe->m_Pcb->m_CurrentLimitZone; edge; edge = edge->Next() )
{
edge->SetLayer( pcbframe->GetScreen()->m_Active_Layer );
}
/* Redraw the curent edge in its new position */
currentEdge = pcbframe->m_Pcb->m_CurrentLimitZone;
if( Zone_45_Only )
{
// calculate the new position as allowed
currentEdge->m_End = pcbframe->GetScreen()->m_Curseur;
Calcule_Coord_Extremite_45( currentEdge->m_Start.x, currentEdge->m_Start.y,
&currentEdge->m_End.x, &currentEdge->m_End.y );
}
else /* all orientations are allowed */
{
currentEdge->m_End = pcbframe->GetScreen()->m_Curseur;
}
// for( ; currentEdge; currentEdge = currentEdge->Next() )
{
Trace_DrawSegmentPcb( panel, DC, currentEdge, GR_XOR );
}
}
/***********************************************************************************/
void WinEDA_PcbFrame::Edit_Zone_Params( wxDC* DC, ZONE_CONTAINER* zone_container )
/***********************************************************************************/
/**
* Function Edit_Zone_Params
* Edit params (layer, clearance, ...) for a zone outline
*/
{
DrawPanel->m_IgnoreMouseEvents = TRUE;
WinEDA_ZoneFrame* frame = new WinEDA_ZoneFrame( this, zone_container );
int diag = frame->ShowModal();
frame->Destroy();
DrawPanel->MouseToCursorSchema();
DrawPanel->m_IgnoreMouseEvents = FALSE;
if( diag == ZONE_ABORT )
return;
zone_container->Draw( DrawPanel, DC, wxPoint( 0, 0 ), GR_XOR );
zone_container->SetLayer( s_Zone_Layer );
zone_container->SetNet( s_NetcodeSelection );
EQUIPOT* net = m_Pcb->FindNet( s_NetcodeSelection );
if( net )
zone_container->m_Netname = net->m_Netname;
zone_container->SetHatch( s_Zone_Hatching );
zone_container->m_PadOption = s_Zone_Pad_Options;
zone_container->m_ZoneClearance = g_DesignSettings.m_ZoneClearence;
zone_container->m_GridFillValue = g_GridRoutingSize;
zone_container->Draw( DrawPanel, DC, wxPoint( 0, 0 ), GR_OR );
GetScreen()->SetModify();
}
/***************************************************************************************/
int WinEDA_PcbFrame::Fill_Zone( wxDC* DC, ZONE_CONTAINER* zone_container, bool verbose )
/***************************************************************************************/
/** Function Fill_Zone()
* Calculate the zone filling for the outline zone_container
* The zone outline is a frontier, and can be complex (with holes)
* The filling starts from starting points like pads, tracks.
* If exists the old filling is removed
* @param DC = current Device Context
* @param zone_container = zone to fill
* @param verbose = true to show error messages
* @return error level (0 = no error)
*/
{
wxString msg;
MsgPanel->EraseMsgBox();
if( m_Pcb->ComputeBoundaryBox() == FALSE )
{
if( verbose )
DisplayError( this, wxT( "Board is empty!" ), 10 );
return -1;
}
/* Show the Net */
s_NetcodeSelection = zone_container->GetNet();
if( (g_HightLigth_NetCode > 0) && (g_HightLigth_NetCode != s_NetcodeSelection) )
{
Hight_Light( DC ); // Remove old hightlight selection
}
g_HightLigth_NetCode = s_NetcodeSelection;
if( !g_HightLigt_Status )
Hight_Light( DC );
if( g_HightLigth_NetCode > 0 )
{
EQUIPOT* net = m_Pcb->FindNet( g_HightLigth_NetCode );
if( net == NULL )
{
if( g_HightLigth_NetCode > 0 )
{
if( verbose )
DisplayError( this, wxT( "Unable to find Net name" ) );
return -2;
}
}
else
msg = net->m_Netname;
}
else
msg = _( "No Net" );
Affiche_1_Parametre( this, 22, _( "NetName" ), msg, RED );
zone_container->m_PadOption = s_Zone_Pad_Options;
zone_container->m_ZoneClearance = g_DesignSettings.m_ZoneClearence;
zone_container->m_GridFillValue = g_GridRoutingSize;
int error_level = zone_container->Fill_Zone( this, DC, verbose );
GetScreen()->SetModify();
return error_level;
}
/************************************************************/
int WinEDA_PcbFrame::Fill_All_Zones( wxDC* DC, bool verbose )
/************************************************************/
/** Function Fill_All_Zones()
* Fill all zones on the board
* The old fillings are removed
* @param frame = reference to the main frame
* @param DC = current Device Context
* @param verbose = true to show error messages
* @return error level (0 = no error)
*/
{
ZONE_CONTAINER* zone_container;
int error_level = 0;
for( unsigned ii = 0; ii < m_Pcb->m_ZoneDescriptorList.size(); ii++ )
{
zone_container = m_Pcb->m_ZoneDescriptorList[ii];
error_level = Fill_Zone( DC, zone_container, verbose );
if( error_level && ! verbose )
break;
}
return error_level;
}