///////////////////////////////////////////////////////////////////////////// // Name: zones.cpp // Purpose: // Author: jean-pierre Charras // Modified by: // Created: 25/01/2006 11:35:19 // RCS-ID: // Copyright: GNU License // Licence: GNU License ///////////////////////////////////////////////////////////////////////////// // Generated by DialogBlocks (unregistered), 25/01/2006 11:35:19 #if defined (__GNUG__) && !defined (NO_GCC_PRAGMA) #pragma implementation "zones.h" #endif // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/wx.h" #endif ////@begin includes ////@end includes #include "zones.h" ////@begin XPM images ////@end XPM images /* Local functions */ static void Display_Zone_Netname( WinEDA_PcbFrame* frame ); static void Exit_Zones( WinEDA_DrawPanel* Panel, wxDC* DC ); static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase ); static void Genere_Segments_Zone( WinEDA_PcbFrame* frame, wxDC* DC, int net_code ); /* Local variables */ static bool Zone_Debug = FALSE; static bool Zone_45_Only = FALSE; static bool Zone_Exclude_Pads = TRUE; static bool s_Zone_Create_Thermal_Relief = TRUE; /* Time stamp comon to all segments relative to the new created zone */ static unsigned long s_TimeStamp; /*! * WinEDA_ZoneFrame type definition */ IMPLEMENT_DYNAMIC_CLASS( WinEDA_ZoneFrame, wxDialog ) /*! * WinEDA_ZoneFrame event table definition */ BEGIN_EVENT_TABLE( WinEDA_ZoneFrame, wxDialog ) ////@begin WinEDA_ZoneFrame event table entries EVT_BUTTON( ID_FILL_ZONE, WinEDA_ZoneFrame::ExecFillZone ) EVT_BUTTON( wxID_CANCEL, WinEDA_ZoneFrame::OnCancelClick ) EVT_BUTTON( ID_SET_OPTIONS_ZONE, WinEDA_ZoneFrame::ExecFillZone ) ////@end WinEDA_ZoneFrame event table entries END_EVENT_TABLE() /*! * WinEDA_ZoneFrame constructors */ WinEDA_ZoneFrame::WinEDA_ZoneFrame() { } WinEDA_ZoneFrame::WinEDA_ZoneFrame( WinEDA_PcbFrame* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) { m_Parent = parent; Create( parent, id, caption, pos, size, style ); } /*! * WinEDA_ZoneFrame creator */ bool WinEDA_ZoneFrame::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) { ////@begin WinEDA_ZoneFrame member initialisation m_GridCtrl = NULL; m_ClearanceValueTitle = NULL; m_ZoneClearanceCtrl = NULL; m_FillOpt = NULL; m_OrientEdgesOpt = NULL; ////@end WinEDA_ZoneFrame member initialisation ////@begin WinEDA_ZoneFrame creation SetExtraStyle( GetExtraStyle() | wxWS_EX_BLOCK_EVENTS ); wxDialog::Create( parent, id, caption, pos, size, style ); CreateControls(); GetSizer()->Fit( this ); GetSizer()->SetSizeHints( this ); Centre(); ////@end WinEDA_ZoneFrame creation return true; } /*! * Control creation for WinEDA_ZoneFrame */ void WinEDA_ZoneFrame::CreateControls() { SetFont( *g_DialogFont ); ////@begin WinEDA_ZoneFrame content construction // Generated by DialogBlocks, 03/03/2006 13:36:21 (unregistered) WinEDA_ZoneFrame* itemDialog1 = this; wxBoxSizer* itemBoxSizer2 = new wxBoxSizer( wxHORIZONTAL ); itemDialog1->SetSizer( itemBoxSizer2 ); wxBoxSizer* itemBoxSizer3 = new wxBoxSizer( wxVERTICAL ); itemBoxSizer2->Add( itemBoxSizer3, 0, wxGROW | wxALL, 5 ); static const wxString m_GridCtrlStrings[] = { _( "0.00000" ), _( "0.00000" ), _( "0.00000" ), _( "0.00000" ) }; m_GridCtrl = new wxRadioBox( itemDialog1, ID_RADIOBOX, _( "Grid size:" ), wxDefaultPosition, wxDefaultSize, 4, m_GridCtrlStrings, 1, wxRA_SPECIFY_COLS ); itemBoxSizer3->Add( m_GridCtrl, 0, wxALIGN_LEFT | wxALL, 5 ); m_ClearanceValueTitle = new wxStaticText( itemDialog1, wxID_STATIC, _( "Zone clearance value (mm):" ), wxDefaultPosition, wxDefaultSize, 0 ); itemBoxSizer3->Add( m_ClearanceValueTitle, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP | wxADJUST_MINSIZE, 5 ); m_ZoneClearanceCtrl = new wxTextCtrl( itemDialog1, ID_TEXTCTRL, _T( "" ), wxDefaultPosition, wxDefaultSize, 0 ); itemBoxSizer3->Add( m_ZoneClearanceCtrl, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); itemBoxSizer2->Add( 5, 5, 0, wxGROW | wxALL, 5 ); wxBoxSizer* itemBoxSizer8 = new wxBoxSizer( wxVERTICAL ); itemBoxSizer2->Add( itemBoxSizer8, 0, wxGROW | wxALL, 5 ); static const wxString m_FillOptStrings[] = { _( "Include Pads" ), _( "Thermal" ), _( "Exclude Pads" ) }; m_FillOpt = new wxRadioBox( itemDialog1, ID_RADIOBOX1, _( "Pad options:" ), wxDefaultPosition, wxDefaultSize, 3, m_FillOptStrings, 1, wxRA_SPECIFY_COLS ); itemBoxSizer8->Add( m_FillOpt, 0, wxALIGN_LEFT | wxALL, 5 ); static const wxString m_OrientEdgesOptStrings[] = { _( "Any" ), _( "H , V and 45 deg" ) }; m_OrientEdgesOpt = new wxRadioBox( itemDialog1, ID_RADIOBOX2, _( "Zone edges orient:" ), wxDefaultPosition, wxDefaultSize, 2, m_OrientEdgesOptStrings, 1, wxRA_SPECIFY_COLS ); itemBoxSizer8->Add( m_OrientEdgesOpt, 0, wxALIGN_RIGHT | wxALL, 5 ); itemBoxSizer2->Add( 5, 5, 0, wxGROW | wxALL, 5 ); wxBoxSizer* itemBoxSizer12 = new wxBoxSizer( wxVERTICAL ); itemBoxSizer2->Add( itemBoxSizer12, 0, wxALIGN_TOP | wxALL, 5 ); wxButton* itemButton13 = new wxButton( itemDialog1, ID_FILL_ZONE, _( "Fill" ), wxDefaultPosition, wxDefaultSize, 0 ); itemButton13->SetDefault(); itemButton13->SetForegroundColour( wxColour( 204, 0, 0 ) ); itemBoxSizer12->Add( itemButton13, 0, wxGROW | wxALL, 5 ); wxButton* itemButton14 = new wxButton( itemDialog1, wxID_CANCEL, _( "&Cancel" ), wxDefaultPosition, wxDefaultSize, 0 ); itemButton14->SetForegroundColour( wxColour( 0, 0, 255 ) ); itemBoxSizer12->Add( itemButton14, 0, wxGROW | wxALL, 5 ); wxButton* itemButton15 = new wxButton( itemDialog1, ID_SET_OPTIONS_ZONE, _( "Update Options" ), wxDefaultPosition, wxDefaultSize, 0 ); itemButton15->SetForegroundColour( wxColour( 0, 100, 0 ) ); itemBoxSizer12->Add( itemButton15, 0, wxGROW | wxALL, 5 ); itemBoxSizer2->Add( 5, 5, 0, wxGROW | wxALL, 5 ); ////@end WinEDA_ZoneFrame content construction wxString title = _( "Zone clearance value:" ) + ReturnUnitSymbol( g_UnitMetric ); m_ClearanceValueTitle->SetLabel( title ); title = _( "Grid :" ) + ReturnUnitSymbol( g_UnitMetric );; m_GridCtrl->SetLabel( title ); if( g_DesignSettings.m_ZoneClearence == 0 ) g_DesignSettings.m_ZoneClearence = g_DesignSettings.m_TrackClearence; title = ReturnStringFromValue( g_UnitMetric, g_DesignSettings.m_ZoneClearence, m_Parent->m_InternalUnits ); m_ZoneClearanceCtrl->SetValue( title ); if( Zone_45_Only ) m_OrientEdgesOpt->SetSelection( 1 ); static const int GridList[4] = { 50, 100, 250, 500 }; int selection = 0; for( unsigned ii = 0; ii < (unsigned)m_GridCtrl->GetCount(); ii++ ) { wxString msg = ReturnStringFromValue( g_UnitMetric, GridList[ii], m_Parent->m_InternalUnits ); m_GridCtrl->SetString( ii, msg ); if( g_GridRoutingSize == GridList[ii] ) selection = ii; } m_GridCtrl->SetSelection( selection ); if( Zone_Exclude_Pads ) { if( s_Zone_Create_Thermal_Relief ) m_FillOpt->SetSelection( 1 ); else m_FillOpt->SetSelection( 2 ); } } /*! * Should we show tooltips? */ bool WinEDA_ZoneFrame::ShowToolTips() { return true; } /*! * Get bitmap resources */ wxBitmap WinEDA_ZoneFrame::GetBitmapResource( const wxString& name ) { // Bitmap retrieval ////@begin WinEDA_ZoneFrame bitmap retrieval wxUnusedVar( name ); return wxNullBitmap; ////@end WinEDA_ZoneFrame bitmap retrieval } /*! * Get icon resources */ wxIcon WinEDA_ZoneFrame::GetIconResource( const wxString& name ) { // Icon retrieval ////@begin WinEDA_ZoneFrame icon retrieval wxUnusedVar( name ); return wxNullIcon; ////@end WinEDA_ZoneFrame icon retrieval } /*! * wxEVT_COMMAND_BUTTON_CLICKED event handler for wxID_CANCEL */ void WinEDA_ZoneFrame::OnCancelClick( wxCommandEvent& event ) { ////@begin wxEVT_COMMAND_BUTTON_CLICKED event handler for wxID_CANCEL in WinEDA_ZoneFrame. // Before editing this code, remove the block markers. event.Skip(); ////@end wxEVT_COMMAND_BUTTON_CLICKED event handler for wxID_CANCEL in WinEDA_ZoneFrame. } /*! * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_BUTTON2 */ /***********************************************************/ void WinEDA_ZoneFrame::ExecFillZone( wxCommandEvent& event ) /***********************************************************/ { switch( m_FillOpt->GetSelection() ) { case 0: Zone_Exclude_Pads = FALSE; s_Zone_Create_Thermal_Relief = FALSE; break; case 1: Zone_Exclude_Pads = TRUE; s_Zone_Create_Thermal_Relief = TRUE; break; case 2: Zone_Exclude_Pads = TRUE; s_Zone_Create_Thermal_Relief = FALSE; break; } switch( m_GridCtrl->GetSelection() ) { case 0: g_GridRoutingSize = 50; break; case 1: g_GridRoutingSize = 100; break; case 2: g_GridRoutingSize = 250; break; case 3: g_GridRoutingSize = 500; break; } wxString txtvalue = m_ZoneClearanceCtrl->GetValue(); g_DesignSettings.m_ZoneClearence = ReturnValueFromString( g_UnitMetric, txtvalue, m_Parent->m_InternalUnits ); if( m_OrientEdgesOpt->GetSelection() == 0 ) Zone_45_Only = FALSE; else Zone_45_Only = TRUE; if( event.GetId() == ID_SET_OPTIONS_ZONE ) EndModal( 1 ); else EndModal( 0 ); } /**************************************************************/ void WinEDA_PcbFrame::Edit_Zone_Width( wxDC* DC, SEGZONE* aZone ) /**************************************************************/ /* Edite (change la largeur des segments) la zone Zone. * La zone est constituee des segments zones de meme TimeStamp */ { bool modify = FALSE; double f_new_width; int w_tmp; wxString Line; wxString Msg( _( "New zone segment width: " ) ); if( aZone == NULL ) return; f_new_width = To_User_Unit( g_UnitMetric, aZone->m_Width, GetScreen()->GetInternalUnits() ); Line.Printf( wxT( "%.4f" ), f_new_width ); Msg += g_UnitMetric ? wxT( "(mm)" ) : wxT( "(\")" ); if( Get_Message( Msg, Line, this ) != 0 ) return; w_tmp = g_DesignSettings.m_CurrentTrackWidth; Line.ToDouble( &f_new_width ); g_DesignSettings.m_CurrentTrackWidth = From_User_Unit( g_UnitMetric, f_new_width, GetScreen()->GetInternalUnits() ); for( SEGZONE* zone = m_Pcb->m_Zone; zone; zone = zone->Next() ) { if( zone->m_TimeStamp == aZone->m_TimeStamp ) { modify = TRUE; Edit_TrackSegm_Width( DC, zone ); } } g_DesignSettings.m_CurrentTrackWidth = w_tmp; if( modify ) { GetScreen()->SetModify(); DrawPanel->Refresh(); } } /**********************************************************/ void WinEDA_PcbFrame::Delete_Zone( wxDC* DC, SEGZONE* aZone ) /**********************************************************/ /* Remove the zone which include the segment aZone. * A zone is a group of segments which have the same TimeStamp */ { if ( aZone == NULL ) return; int nb_segm = 0; bool modify = FALSE; unsigned long 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 */ 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 ) /*****************************************************************************/ /* Routine d'effacement du segment de limite zone en cours de trace */ { 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; } /*********************************************/ void WinEDA_PcbFrame::CaptureNetName( wxDC* DC ) /*********************************************/ /* routine permettant de capturer le nom net net (netcode) d'un pad * ou d'une piste pour l'utiliser comme netcode de zone */ { D_PAD* pt_pad = 0; TRACK* adrpiste; MODULE* Module; int masquelayer = g_TabOneLayerMask[GetScreen()->m_Active_Layer]; int netcode; netcode = -1; MsgPanel->EraseMsgBox(); adrpiste = Locate_Pistes( m_Pcb->m_Track, masquelayer, CURSEUR_OFF_GRILLE ); if( adrpiste == NULL ) { pt_pad = Locate_Any_Pad( m_Pcb, CURSEUR_OFF_GRILLE ); if( pt_pad ) /* Verif qu'il est bien sur la couche active */ { Module = (MODULE*) pt_pad->m_Parent; pt_pad = Locate_Pads( Module, g_TabOneLayerMask[GetScreen()->m_Active_Layer], CURSEUR_OFF_GRILLE ); } if( pt_pad ) { pt_pad->Display_Infos( this ); netcode = pt_pad->GetNet(); } } else { adrpiste->Display_Infos( this ); netcode = adrpiste->GetNet(); } // Mise en surbrillance du net if( g_HightLigt_Status ) Hight_Light( DC ); g_HightLigth_NetCode = netcode; if( g_HightLigth_NetCode >= 0 ) { Hight_Light( DC ); } /* Affichage du net selectionne pour la zone a tracer */ Display_Zone_Netname( this ); } /*******************************************************/ static void Display_Zone_Netname( WinEDA_PcbFrame* frame ) /*******************************************************/ /* * Affiche le net_code et le nom de net couramment selectionne */ { EQUIPOT* pt_equipot; wxString line; pt_equipot = frame->m_Pcb->m_Equipots; if( g_HightLigth_NetCode > 0 ) { for( ; pt_equipot != NULL; pt_equipot = (EQUIPOT*) pt_equipot->Pnext ) { if( pt_equipot->GetNet() == g_HightLigth_NetCode ) break; } if( pt_equipot ) { line.Printf( wxT( "Zone: Net[%d] <%s>" ), g_HightLigth_NetCode, pt_equipot->m_Netname.GetData() ); } else line.Printf( wxT( "Zone: NetCode[%d], Equipot not found" ), g_HightLigth_NetCode ); } line = _( "Zone: No net selected" ); frame->Affiche_Message( line ); } /********************************************************/ static void Exit_Zones( WinEDA_DrawPanel* Panel, wxDC* DC ) /********************************************************/ /** * Function Exit_Zones * cancels the Begin_Zone state 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, FALSE ); } 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; if( !IsOK( this, _( "Delete Current Zone Edges" ) ) ) return; // erase the old zone border, 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 ); } /** * Function Begin_Zone * either initializes the first segment of a new zone, or adds an * intermediate segment. */ EDGE_ZONE* WinEDA_PcbFrame::Begin_Zone() { EDGE_ZONE* oldedge; EDGE_ZONE* newedge = NULL; oldedge = m_Pcb->m_CurrentLimitZone; // if first segment if( (m_Pcb->m_CurrentLimitZone == NULL ) /* debut reel du trace */ || (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_Zone_Edge_While_MoveMouse; DrawPanel->ForceCloseManageCurseur = Exit_Zones; } // edge in progress: else /* piste en cours : les coord du point d'arrivee ont ete mises * a jour par la routine Show_Zone_Edge_While_MoveMouse*/ { if( oldedge->m_Start != oldedge->m_End ) { 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 ) /*********************************************/ /* * Routine de fin de trace d'une zone (succession de segments) */ { EDGE_ZONE* edge; if( m_Pcb->m_CurrentLimitZone ) { Begin_Zone(); /* le dernier point genere est de longueur tj nulle donc inutile. */ /* il sera raccorde au point de depart */ 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; } /******************************************************************************************/ static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase ) /******************************************************************************************/ /* redessin du contour de la piste lors des deplacements de la souris */ { EDGE_ZONE* edge; EDGE_ZONE* currentEdge; WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) panel->m_Parent; if( pcbframe->m_Pcb->m_CurrentLimitZone == NULL ) return; /* efface ancienne position si elle a ete deja dessinee */ if( erase ) { edge = pcbframe->m_Pcb->m_CurrentLimitZone; // for( ; edge; edge = edge->Next() ) { Trace_DrawSegmentPcb( panel, DC, edge, GR_XOR ); } } /* mise a jour de la couche */ for( edge = pcbframe->m_Pcb->m_CurrentLimitZone; edge; edge = edge->Next() ) { edge->SetLayer( pcbframe->GetScreen()->m_Active_Layer ); } /* dessin de la nouvelle piste : mise a jour du point d'arrivee */ currentEdge = pcbframe->m_Pcb->m_CurrentLimitZone; if( Zone_45_Only ) { // Calcul de l'extremite de la piste pour orientations permises: // horiz,vertical ou 45 degre currentEdge->m_End = pcbframe->GetScreen()->m_Curseur; Calcule_Coord_Extremite_45( currentEdge->m_Start.x, currentEdge->m_Start.y, ¤tEdge->m_End.x, ¤tEdge->m_End.y ); } else /* ici l'angle d'inclinaison est quelconque */ { currentEdge->m_End = pcbframe->GetScreen()->m_Curseur; } // for( ; currentEdge; currentEdge = currentEdge->Next() ) { Trace_DrawSegmentPcb( panel, DC, currentEdge, GR_XOR ); } } /**********************************************/ void WinEDA_PcbFrame::Fill_Zone( wxDC* DC ) /**********************************************/ /** Function Fill_Zone() * Init the zone filling * If a zone edge is found, it is used. * Otherwise the whole board is filled by the zone * The zone edge is a frontier, and can be complex. So non filled zones can be achieved * The zone is put on the active layer * If a net is hightlighted, the zone will be attached to this net * The filling start from a starting point. * If a net is selected, all tracks attached to this net are also starting points */ { int ii, jj; EDGE_ZONE* PtLim; int lp_tmp, lay_tmp_TOP, lay_tmp_BOTTOM; EQUIPOT* pt_equipot; int save_isol = g_DesignSettings.m_TrackClearence; wxPoint ZoneStartFill; wxString msg; MsgPanel->EraseMsgBox(); if( m_Pcb->ComputeBoundaryBox() == FALSE ) { DisplayError( this, wxT( "Board is empty!" ), 10 ); return; } DrawPanel->m_IgnoreMouseEvents = TRUE; WinEDA_ZoneFrame* frame = new WinEDA_ZoneFrame( this ); ii = frame->ShowModal(); frame->Destroy(); DrawPanel->MouseToCursorSchema(); DrawPanel->m_IgnoreMouseEvents = FALSE; if( ii ) return; g_DesignSettings.m_TrackClearence = g_DesignSettings.m_ZoneClearence; // set all the EDGE_ZONEs to the currently active layer and redraw them // on that layer. PtLim = m_Pcb->m_CurrentLimitZone; for( ; PtLim != NULL; PtLim = PtLim->Next() ) { Trace_DrawSegmentPcb( DrawPanel, DC, PtLim, GR_XOR ); PtLim->SetLayer( GetScreen()->m_Active_Layer ); Trace_DrawSegmentPcb( DrawPanel, DC, PtLim, GR_XOR ); } s_TimeStamp = time( NULL ); // calculate the fixed step of the routing matrix as 5 mils or more E_scale = g_GridRoutingSize / 50; if( g_GridRoutingSize < 1 ) g_GridRoutingSize = 1; // calculate the Ncols and Nrows, size of the routing matrix ComputeMatriceSize( this, g_GridRoutingSize ); // Determine the cell pointed to by the mouse ZoneStartFill.x = ( GetScreen()->m_Curseur.x - m_Pcb->m_BoundaryBox.m_Pos.x + (g_GridRoutingSize / 2) ) / g_GridRoutingSize; ZoneStartFill.y = ( GetScreen()->m_Curseur.y - m_Pcb->m_BoundaryBox.m_Pos.y + (g_GridRoutingSize / 2) ) / g_GridRoutingSize; if( ZoneStartFill.x < 0 ) ZoneStartFill.x = 0; if( ZoneStartFill.x >= Ncols ) ZoneStartFill.x = Ncols - 1; if( ZoneStartFill.y < 0 ) ZoneStartFill.y = 0; if( ZoneStartFill.y >= Nrows ) ZoneStartFill.y = Nrows - 1; // create the routing matrix in autorout.h's eda_global BOARDHEAD Board Nb_Sides = ONE_SIDE; if( Board.InitBoard() < 0 ) { DisplayError( this, wxT( "Mo memory for creating zones" ) ); return; } msg.Printf( wxT( "%d" ), Ncols ); Affiche_1_Parametre( this, 1, wxT( "Cols" ), msg, GREEN ); msg.Printf( wxT( "%d" ), Nrows ); Affiche_1_Parametre( this, 7, wxT( "Lines" ), msg, GREEN ); msg.Printf( wxT( "%d" ), Board.m_MemSize / 1024 ); Affiche_1_Parametre( this, 14, wxT( "Mem(Ko)" ), msg, CYAN ); lay_tmp_BOTTOM = Route_Layer_BOTTOM; lay_tmp_TOP = Route_Layer_TOP; Route_Layer_BOTTOM = Route_Layer_TOP = GetScreen()->m_Active_Layer; lp_tmp = g_DesignSettings.m_CurrentTrackWidth; g_DesignSettings.m_CurrentTrackWidth = g_GridRoutingSize; /* Shos the NetName */ if( g_HightLigth_NetCode > 0 ) { pt_equipot = m_Pcb->FindNet( g_HightLigth_NetCode ); if( pt_equipot == NULL ) { if( g_HightLigth_NetCode > 0 ) DisplayError( this, wxT( "Unable to find Net name" ) ); } else msg = pt_equipot->m_Netname; } else msg = _( "No Net" ); Affiche_1_Parametre( this, 22, _( "NetName" ), msg, RED ); /* Create the starting point for thz zone: * The starting point and all the tracks are suitable "starting points" */ TRACK* pt_segm = m_Pcb->m_Track; for( ; pt_segm != NULL; pt_segm = pt_segm->Next() ) { if( g_HightLigth_NetCode != pt_segm->GetNet() ) continue; if( pt_segm->GetLayer() != GetScreen()->m_Active_Layer ) continue; if( pt_segm->Type() != TYPETRACK ) continue; TraceSegmentPcb( m_Pcb, pt_segm, CELL_is_FRIEND, 0, WRITE_CELL ); } // trace the pcb edges (pcb contour) into the routing matrix Route_Layer_BOTTOM = Route_Layer_TOP = EDGE_N; PlaceCells( m_Pcb, -1, 0 ); Route_Layer_BOTTOM = Route_Layer_TOP = GetScreen()->m_Active_Layer; // trace the zone edges into the routing matrix for( PtLim = m_Pcb->m_CurrentLimitZone; PtLim; PtLim=PtLim->Next() ) { int ux0, uy0, ux1, uy1; ux0 = PtLim->m_Start.x - m_Pcb->m_BoundaryBox.m_Pos.x; uy0 = PtLim->m_Start.y - m_Pcb->m_BoundaryBox.m_Pos.y; ux1 = PtLim->m_End.x - m_Pcb->m_BoundaryBox.m_Pos.x; uy1 = PtLim->m_End.y - m_Pcb->m_BoundaryBox.m_Pos.y; TraceLignePcb( ux0, uy0, ux1, uy1, -1, HOLE | CELL_is_EDGE, WRITE_CELL ); } OrCell( ZoneStartFill.y, ZoneStartFill.x, BOTTOM, CELL_is_ZONE ); // mark the cells forming part of the zone ii = 1; jj = 1; while( ii ) { msg.Printf( wxT( "%d" ), jj++ ); Affiche_1_Parametre( this, 50, wxT( "Iter." ), msg, CYAN ); ii = Propagation( this ); } // selection of the suitable cells for the points of anchoring of the zone for( ii = 0; ii < Nrows; ii++ ) { for( jj = 0; jj < Ncols; jj++ ) { long cell = GetCell( ii, jj, BOTTOM ); if( (cell & CELL_is_ZONE) ) { if( (cell & CELL_is_FRIEND) == 0 ) AndCell( ii, jj, BOTTOM, (BoardCell) ~(CELL_is_FRIEND | CELL_is_ZONE) ); } } } // now, all the cell candidates are marked // place all the obstacles into the matrix, such as (pads, tracks, vias, // pcb edges or segments) ii = 0; if( Zone_Exclude_Pads ) ii = FORCE_PADS; Affiche_1_Parametre( this, 42, wxT( "GenZone" ), wxEmptyString, RED ); PlaceCells( m_Pcb, g_HightLigth_NetCode, ii ); Affiche_1_Parametre( this, -1, wxEmptyString, _( "Ok" ), RED ); /* Create zone limits on the routing matrix * (colud be deleted by PlaceCells()) : */ for( PtLim = m_Pcb->m_CurrentLimitZone; PtLim; PtLim = PtLim->Next() ) { int ux0, uy0, ux1, uy1; ux0 = PtLim->m_Start.x - m_Pcb->m_BoundaryBox.m_Pos.x; uy0 = PtLim->m_Start.y - m_Pcb->m_BoundaryBox.m_Pos.y; ux1 = PtLim->m_End.x - m_Pcb->m_BoundaryBox.m_Pos.x; uy1 = PtLim->m_End.y - m_Pcb->m_BoundaryBox.m_Pos.y; TraceLignePcb( ux0, uy0, ux1, uy1, -1, HOLE | CELL_is_EDGE, WRITE_CELL ); } /* Init the starting point for zone filling : this is the mouse position * (could be deleted by PlaceCells()) : */ OrCell( ZoneStartFill.y, ZoneStartFill.x, BOTTOM, CELL_is_ZONE ); if( Zone_Debug ) DisplayBoard( DrawPanel, DC ); /* Filling the cells of the matrix (tjis is the zone building)*/ ii = 1; jj = 1; while( ii ) { msg.Printf( wxT( "%d" ), jj++ ); Affiche_1_Parametre( this, 50, wxT( "Iter." ), msg, CYAN ); ii = Propagation( this ); } if( Zone_Debug ) DisplayBoard( DrawPanel, DC ); /* Convert the matrix information (cells) to segments which are actually the zone */ if( g_HightLigth_NetCode < 0 ) Genere_Segments_Zone( this, DC, 0 ); else Genere_Segments_Zone( this, DC, g_HightLigth_NetCode ); /* Create the thermal reliefs */ g_DesignSettings.m_CurrentTrackWidth = lp_tmp; if( Zone_Exclude_Pads && s_Zone_Create_Thermal_Relief ) Genere_Pad_Connexion( DC, GetScreen()->m_Active_Layer ); g_DesignSettings.m_TrackClearence = save_isol; GetScreen()->SetModify(); // free the memory Board.UnInitBoard(); // restore original values unchanged Route_Layer_TOP = lay_tmp_TOP; Route_Layer_BOTTOM = lay_tmp_BOTTOM; } /*******************************************************************************/ static void Genere_Segments_Zone( WinEDA_PcbFrame* frame, wxDC* DC, int net_code ) /*******************************************************************************/ /** Function Genere_Segments_Zone() * Create the zone segments from the routing matrix structure * Algorithm: * Search for consecutive cells (flagged "zone") , and create segments * from the first cell to the last cell in the matrix * 2 searchs are made * 1 - From left to right and create horizontal zone segments * 2 - From top to bottom, and create vertical zone segmùents * @param net_code = net_code common to all segment zone created * @param DC = current device context * @param frame = current WinEDA_PcbFrame * global: parameter TimeStamp: time stamp common to all segment zone created */ { int row, col; long current_cell, old_cell; int ux0 = 0, uy0 = 0, ux1 = 0, uy1 = 0; int Xmin = frame->m_Pcb->m_BoundaryBox.m_Pos.x; int Ymin = frame->m_Pcb->m_BoundaryBox.m_Pos.y; SEGZONE* pt_track; int layer = frame->GetScreen()->m_Active_Layer; int nbsegm = 0; wxString msg; /* balayage Gauche-> droite */ Affiche_1_Parametre( frame, 64, wxT( "Segm H" ), wxT( "0" ), BROWN ); for( row = 0; row < Nrows; row++ ) { old_cell = 0; uy0 = uy1 = (row * g_GridRoutingSize) + Ymin; for( col = 0; col < Ncols; col++ ) { current_cell = GetCell( row, col, BOTTOM ) & CELL_is_ZONE; if( current_cell ) /* ce point doit faire partie d'un segment */ { ux1 = (col * g_GridRoutingSize) + Xmin; if( old_cell == 0 ) ux0 = ux1; } if( !current_cell || (col == Ncols - 1) ) /* peut etre fin d'un segment */ { if( (old_cell) && (ux0 != ux1) ) { /* un segment avait debute de longueur > 0 */ pt_track = new SEGZONE( frame->m_Pcb ); pt_track->SetLayer( layer ); pt_track->SetNet( net_code ); pt_track->m_Width = g_GridRoutingSize; pt_track->m_Start.x = ux0; pt_track->m_Start.y = uy0; pt_track->m_End.x = ux1; pt_track->m_End.y = uy1; pt_track->m_TimeStamp = s_TimeStamp; pt_track->Insert( frame->m_Pcb, NULL ); pt_track->Draw( frame->DrawPanel, DC, GR_OR ); nbsegm++; } } old_cell = current_cell; } msg.Printf( wxT( "%d" ), nbsegm ); Affiche_1_Parametre( frame, -1, wxEmptyString, msg, BROWN ); } Affiche_1_Parametre( frame, 72, wxT( "Segm V" ), wxT( "0" ), BROWN ); for( col = 0; col < Ncols; col++ ) { old_cell = 0; ux0 = ux1 = (col * g_GridRoutingSize) + Xmin; for( row = 0; row < Nrows; row++ ) { current_cell = GetCell( row, col, BOTTOM ) & CELL_is_ZONE; if( current_cell ) /* ce point doit faire partie d'un segment */ { uy1 = (row * g_GridRoutingSize) + Ymin; if( old_cell == 0 ) uy0 = uy1; } if( !current_cell || (row == Nrows - 1) ) /* peut etre fin d'un segment */ { if( (old_cell) && (uy0 != uy1) ) { /* un segment avait debute de longueur > 0 */ pt_track = new SEGZONE( frame->m_Pcb ); pt_track->SetLayer( layer ); pt_track->m_Width = g_GridRoutingSize; pt_track->SetNet( net_code ); pt_track->m_Start.x = ux0; pt_track->m_Start.y = uy0; pt_track->m_End.x = ux1; pt_track->m_End.y = uy1; pt_track->m_TimeStamp = s_TimeStamp; pt_track->Insert( frame->m_Pcb, NULL ); pt_track->Draw( frame->DrawPanel, DC, GR_OR ); nbsegm++; } } old_cell = current_cell; } msg.Printf( wxT( "%d" ), nbsegm ); Affiche_1_Parametre( frame, -1, wxEmptyString, msg, BROWN ); } } /********************************************/ int Propagation( WinEDA_PcbFrame* frame ) /********************************************/ /** Function Propagation() * An important function to calculate zones * Uses the routing matrix to fill the cells within the zone * Search and mark cells within the zone, and agree with DRC options. * Requirements: * Start from an initial point, to fill zone * The zone must have no "copper island" * Algorithm: * If the current cell has a neightbour flagged as "cell in the zone", it * become a cell in the zone * The first point in the zone is the starting point * 4 searches within the matrix are made: * 1 - Left to right and top to bottom * 2 - Right to left and top to bottom * 3 - bottom to top and Right to left * 4 - bottom to top and Left to right * Given the current cell, for each search, we consider the 2 neightbour cells * the previous cell on the same line and the previous cell on the same column. * * This funtion can request some iterations * Iterations are made until no cell is added to the zone. * @return: added cells count (i.e. which the attribute CELL_is_ZONE is set) */ { int row, col, nn; long current_cell, old_cell_H; int long* pt_cell_V; int nbpoints = 0; #define NO_CELL_ZONE (HOLE | CELL_is_EDGE | CELL_is_ZONE) wxString msg; Affiche_1_Parametre( frame, 57, wxT( "Detect" ), msg, CYAN ); Affiche_1_Parametre( frame, -1, wxEmptyString, wxT( "1" ), CYAN ); // Alloc memory to handle 1 line or 1 colunmn on the routing matrix nn = MAX( Nrows, Ncols ) * sizeof(*pt_cell_V); pt_cell_V = (long*) MyMalloc( nn ); /* search 1 : from left to right and top to bottom */ memset( pt_cell_V, 0, nn ); for( row = 0; row < Nrows; row++ ) { old_cell_H = 0; for( col = 0; col < Ncols; col++ ) { current_cell = GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) /* a free cell is found */ { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[col] & CELL_is_ZONE) ) { OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[col] = old_cell_H = current_cell; } } /* search 2 : from right to left and top to bottom */ Affiche_1_Parametre( frame, -1, wxEmptyString, wxT( "2" ), CYAN ); memset( pt_cell_V, 0, nn ); for( row = 0; row < Nrows; row++ ) { old_cell_H = 0; for( col = Ncols - 1; col >= 0; col-- ) { current_cell = GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) /* a free cell is found */ { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[col] & CELL_is_ZONE) ) { OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[col] = old_cell_H = current_cell; } } /* search 3 : from bottom to top and right to left balayage */ Affiche_1_Parametre( frame, -1, wxEmptyString, wxT( "3" ), CYAN ); memset( pt_cell_V, 0, nn ); for( col = Ncols - 1; col >= 0; col-- ) { old_cell_H = 0; for( row = Nrows - 1; row >= 0; row-- ) { current_cell = GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) /* a free cell is found */ { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[row] & CELL_is_ZONE) ) { OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[row] = old_cell_H = current_cell; } } /* search 4 : from bottom to top and left to right */ Affiche_1_Parametre( frame, -1, wxEmptyString, wxT( "4" ), CYAN ); memset( pt_cell_V, 0, nn ); for( col = 0; col < Ncols; col++ ) { old_cell_H = 0; for( row = Nrows - 1; row >= 0; row-- ) { current_cell = GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) /* a free cell is found */ { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[row] & CELL_is_ZONE) ) { OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[row] = old_cell_H = current_cell; } } MyFree( pt_cell_V ); return nbpoints; } /*****************************************************************************/ bool WinEDA_PcbFrame::Genere_Pad_Connexion( wxDC* DC, int layer ) /*****************************************************************************/ /* Create the thermal relief for each pad in the zone: * this is 4 small segments from the pad to the zone */ { int ii, jj, Npads; D_PAD* pt_pad; LISTE_PAD* pt_liste_pad; TRACK* pt_track, * loctrack; int angle; int cX, cY, dx, dy; int sommet[4][2]; wxString msg; if( m_Pcb->m_Zone == NULL ) return FALSE; /* error: no zone */ if( m_Pcb->m_Zone->m_TimeStamp != s_TimeStamp ) /* error: this is not the new zone */ return FALSE; /* Count the pads, i.e. the thermal relief to create count, and displays it */ Affiche_1_Parametre( this, 50, wxT( "NPads" ), wxT( " " ), CYAN ); pt_liste_pad = (LISTE_PAD*) m_Pcb->m_Pads; for( ii = 0, Npads = 0; ii < m_Pcb->m_NbPads; ii++, pt_liste_pad++ ) { pt_pad = *pt_liste_pad; /* Search pads relative to the selected net code */ if( pt_pad->GetNet() != g_HightLigth_NetCode ) continue; /* Is the pad on the active layer ? */ if( (pt_pad->m_Masque_Layer & g_TabOneLayerMask[layer]) == 0 ) continue; Npads++; } msg.Printf( wxT( "%d" ), Npads ); Affiche_1_Parametre( this, -1, wxEmptyString, msg, CYAN ); /* Create the thermal reliefs */ Affiche_1_Parametre( this, 57, wxT( "Pads" ), wxT( " " ), CYAN ); pt_liste_pad = (LISTE_PAD*) m_Pcb->m_Pads; for( ii = 0, Npads = 0; ii < m_Pcb->m_NbPads; ii++, pt_liste_pad++ ) { pt_pad = *pt_liste_pad; /* Search pads relative to the selected net code */ if( pt_pad->GetNet() != g_HightLigth_NetCode ) continue; /* Is the pad on the active layer ? */ if( (pt_pad->m_Masque_Layer & g_TabOneLayerMask[layer]) == 0 ) continue; /* Create the theram relief for the current pad */ Npads++; msg.Printf( wxT( "%d" ), Npads ); Affiche_1_Parametre( this, -1, wxEmptyString, msg, CYAN ); cX = pt_pad->GetPosition().x; cY = pt_pad->GetPosition().y; dx = pt_pad->m_Size.x / 2; dy = pt_pad->m_Size.y / 2; dx += g_DesignSettings.m_TrackClearence + g_GridRoutingSize; dy += g_DesignSettings.m_TrackClearence + g_GridRoutingSize; if( pt_pad->m_PadShape == TRAPEZE ) { dx += abs( pt_pad->m_DeltaSize.y ) / 2; dy += abs( pt_pad->m_DeltaSize.x ) / 2; } /* calculate the 4 segment coordintes (starting from the pad centre cX,cY) */ sommet[0][0] = 0; sommet[0][1] = -dy; sommet[1][0] = -dx; sommet[1][1] = 0; sommet[2][0] = 0; sommet[2][1] = dy; sommet[3][0] = dx; sommet[3][1] = 0; angle = pt_pad->m_Orient; for( jj = 0; jj < 4; jj++ ) { RotatePoint( &sommet[jj][0], &sommet[jj][1], angle ); pt_track = new SEGZONE( m_Pcb ); pt_track->SetLayer( layer ); pt_track->m_Width = g_DesignSettings.m_CurrentTrackWidth; pt_track->SetNet( g_HightLigth_NetCode ); pt_track->start = pt_pad; pt_track->m_Start.x = cX; pt_track->m_Start.y = cY; pt_track->m_End.x = cX + sommet[jj][0]; pt_track->m_End.y = cY + sommet[jj][1]; pt_track->m_TimeStamp = s_TimeStamp; /* Test if the segment is allowed */ if( BAD_DRC==m_drc->DrcBlind( pt_track, m_Pcb->m_Track ) ) { delete pt_track; continue; } /* Search for a zone segment */ loctrack = Locate_Zone( m_Pcb->m_Zone, pt_track->m_End, layer ); if( (loctrack == NULL) || (loctrack->m_TimeStamp != s_TimeStamp) ) { delete pt_track; continue; } pt_track->Insert( m_Pcb, NULL ); pt_track->Draw( DrawPanel, DC, GR_OR ); } } return TRUE; }