You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2919 lines
85 KiB

++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
12 years ago
19 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
14 years ago
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
14 years ago
14 years ago
18 years ago
18 years ago
17 years ago
17 years ago
17 years ago
17 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
18 years ago
Changed the way of looking up NETINFO_ITEM using net names (using boost::unordered_map). Added a hash function (wxString) for that. Introduced NETINFO_ITEM::GetNetItem( wxString ). BOARD::FindNet() uses the map. Net codes are updated upon net list update. (BOARD::ReplaceNetlist()) Added in some places (mostly class_board.cpp) pad->SetNet() calls to synchronize net codes. On creation of NETINFO_LIST, the first NETINFO_ITEM is added (the unconnected items net). Removed COMPONENT_NET::m_netNumber, as it was not used anywhere. Added an assert to D_PAD::GetNetname(), checking if net code and net name is consistent for unconnected pads. Added an assert for NETINFO_LIST::AppendNet() to assure that appended nets are unique. It seems that at this point: - Updating net lists works fine. The only difference between the file ouput is that after changes it contains empty nets as well. - Nets are not saved in the lexical order. Still, net names and net codes are properly assigned to all items in the .kicad_pcb file. It is going to be addressed in the next commit. I believe it should not create any problems, as pads are sorted by their net names anyway (NETINFO_LIST::buildPadsFullList()) Performed tests: - Created a blank PCB, saved as pic_programmer.kicad_pcb (from demos folder). Updated net lists. .kicad_pcb file (comparing to the results from master branch) differ with net order (as mentioned before), net codes and timestamps. - Removed some of components from the above .kicad_pcb file and updated net lists. Modules reappeared. .kicad_pcb file differs in the same way as described above. - Trying to change a pad net name (via properties dialog) results in assert being fired. It is done on purpose (as there is a call to GetNetname() and net name and net code do not match). This will not happen after the next commit. - Prepared a simple project (starting with schematics). Imported net list, changed schematic, reimported net list - changes are applied. - Eagle & KiCad legacy boards seem to load without any problem.
12 years ago
18 years ago
18 years ago
18 years ago
11 years ago
11 years ago
11 years ago
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
12 years ago
11 years ago
8 years ago
8 years ago
8 years ago
8 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
Changed the way of looking up NETINFO_ITEM using net names (using boost::unordered_map). Added a hash function (wxString) for that. Introduced NETINFO_ITEM::GetNetItem( wxString ). BOARD::FindNet() uses the map. Net codes are updated upon net list update. (BOARD::ReplaceNetlist()) Added in some places (mostly class_board.cpp) pad->SetNet() calls to synchronize net codes. On creation of NETINFO_LIST, the first NETINFO_ITEM is added (the unconnected items net). Removed COMPONENT_NET::m_netNumber, as it was not used anywhere. Added an assert to D_PAD::GetNetname(), checking if net code and net name is consistent for unconnected pads. Added an assert for NETINFO_LIST::AppendNet() to assure that appended nets are unique. It seems that at this point: - Updating net lists works fine. The only difference between the file ouput is that after changes it contains empty nets as well. - Nets are not saved in the lexical order. Still, net names and net codes are properly assigned to all items in the .kicad_pcb file. It is going to be addressed in the next commit. I believe it should not create any problems, as pads are sorted by their net names anyway (NETINFO_LIST::buildPadsFullList()) Performed tests: - Created a blank PCB, saved as pic_programmer.kicad_pcb (from demos folder). Updated net lists. .kicad_pcb file (comparing to the results from master branch) differ with net order (as mentioned before), net codes and timestamps. - Removed some of components from the above .kicad_pcb file and updated net lists. Modules reappeared. .kicad_pcb file differs in the same way as described above. - Trying to change a pad net name (via properties dialog) results in assert being fired. It is done on purpose (as there is a call to GetNetname() and net name and net code do not match). This will not happen after the next commit. - Prepared a simple project (starting with schematics). Imported net list, changed schematic, reimported net list - changes are applied. - Eagle & KiCad legacy boards seem to load without any problem.
12 years ago
Changed the way of looking up NETINFO_ITEM using net names (using boost::unordered_map). Added a hash function (wxString) for that. Introduced NETINFO_ITEM::GetNetItem( wxString ). BOARD::FindNet() uses the map. Net codes are updated upon net list update. (BOARD::ReplaceNetlist()) Added in some places (mostly class_board.cpp) pad->SetNet() calls to synchronize net codes. On creation of NETINFO_LIST, the first NETINFO_ITEM is added (the unconnected items net). Removed COMPONENT_NET::m_netNumber, as it was not used anywhere. Added an assert to D_PAD::GetNetname(), checking if net code and net name is consistent for unconnected pads. Added an assert for NETINFO_LIST::AppendNet() to assure that appended nets are unique. It seems that at this point: - Updating net lists works fine. The only difference between the file ouput is that after changes it contains empty nets as well. - Nets are not saved in the lexical order. Still, net names and net codes are properly assigned to all items in the .kicad_pcb file. It is going to be addressed in the next commit. I believe it should not create any problems, as pads are sorted by their net names anyway (NETINFO_LIST::buildPadsFullList()) Performed tests: - Created a blank PCB, saved as pic_programmer.kicad_pcb (from demos folder). Updated net lists. .kicad_pcb file (comparing to the results from master branch) differ with net order (as mentioned before), net codes and timestamps. - Removed some of components from the above .kicad_pcb file and updated net lists. Modules reappeared. .kicad_pcb file differs in the same way as described above. - Trying to change a pad net name (via properties dialog) results in assert being fired. It is done on purpose (as there is a call to GetNetname() and net name and net code do not match). This will not happen after the next commit. - Prepared a simple project (starting with schematics). Imported net list, changed schematic, reimported net list - changes are applied. - Eagle & KiCad legacy boards seem to load without any problem.
12 years ago
Changed the way of looking up NETINFO_ITEM using net names (using boost::unordered_map). Added a hash function (wxString) for that. Introduced NETINFO_ITEM::GetNetItem( wxString ). BOARD::FindNet() uses the map. Net codes are updated upon net list update. (BOARD::ReplaceNetlist()) Added in some places (mostly class_board.cpp) pad->SetNet() calls to synchronize net codes. On creation of NETINFO_LIST, the first NETINFO_ITEM is added (the unconnected items net). Removed COMPONENT_NET::m_netNumber, as it was not used anywhere. Added an assert to D_PAD::GetNetname(), checking if net code and net name is consistent for unconnected pads. Added an assert for NETINFO_LIST::AppendNet() to assure that appended nets are unique. It seems that at this point: - Updating net lists works fine. The only difference between the file ouput is that after changes it contains empty nets as well. - Nets are not saved in the lexical order. Still, net names and net codes are properly assigned to all items in the .kicad_pcb file. It is going to be addressed in the next commit. I believe it should not create any problems, as pads are sorted by their net names anyway (NETINFO_LIST::buildPadsFullList()) Performed tests: - Created a blank PCB, saved as pic_programmer.kicad_pcb (from demos folder). Updated net lists. .kicad_pcb file (comparing to the results from master branch) differ with net order (as mentioned before), net codes and timestamps. - Removed some of components from the above .kicad_pcb file and updated net lists. Modules reappeared. .kicad_pcb file differs in the same way as described above. - Trying to change a pad net name (via properties dialog) results in assert being fired. It is done on purpose (as there is a call to GetNetname() and net name and net code do not match). This will not happen after the next commit. - Prepared a simple project (starting with schematics). Imported net list, changed schematic, reimported net list - changes are applied. - Eagle & KiCad legacy boards seem to load without any problem.
12 years ago
Changed the way of looking up NETINFO_ITEM using net names (using boost::unordered_map). Added a hash function (wxString) for that. Introduced NETINFO_ITEM::GetNetItem( wxString ). BOARD::FindNet() uses the map. Net codes are updated upon net list update. (BOARD::ReplaceNetlist()) Added in some places (mostly class_board.cpp) pad->SetNet() calls to synchronize net codes. On creation of NETINFO_LIST, the first NETINFO_ITEM is added (the unconnected items net). Removed COMPONENT_NET::m_netNumber, as it was not used anywhere. Added an assert to D_PAD::GetNetname(), checking if net code and net name is consistent for unconnected pads. Added an assert for NETINFO_LIST::AppendNet() to assure that appended nets are unique. It seems that at this point: - Updating net lists works fine. The only difference between the file ouput is that after changes it contains empty nets as well. - Nets are not saved in the lexical order. Still, net names and net codes are properly assigned to all items in the .kicad_pcb file. It is going to be addressed in the next commit. I believe it should not create any problems, as pads are sorted by their net names anyway (NETINFO_LIST::buildPadsFullList()) Performed tests: - Created a blank PCB, saved as pic_programmer.kicad_pcb (from demos folder). Updated net lists. .kicad_pcb file (comparing to the results from master branch) differ with net order (as mentioned before), net codes and timestamps. - Removed some of components from the above .kicad_pcb file and updated net lists. Modules reappeared. .kicad_pcb file differs in the same way as described above. - Trying to change a pad net name (via properties dialog) results in assert being fired. It is done on purpose (as there is a call to GetNetname() and net name and net code do not match). This will not happen after the next commit. - Prepared a simple project (starting with schematics). Imported net list, changed schematic, reimported net list - changes are applied. - Eagle & KiCad legacy boards seem to load without any problem.
12 years ago
8 years ago
  1. /**
  2. * @file class_board.cpp
  3. * @brief BOARD class functions.
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  9. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  10. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
  11. *
  12. * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
  13. *
  14. * This program is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU General Public License
  16. * as published by the Free Software Foundation; either version 2
  17. * of the License, or (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program; if not, you may find one here:
  26. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  27. * or you may search the http://www.gnu.org website for the version 2 license,
  28. * or you may write to the Free Software Foundation, Inc.,
  29. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  30. */
  31. #include <limits.h>
  32. #include <algorithm>
  33. #include <iterator>
  34. #include <fctsys.h>
  35. #include <common.h>
  36. #include <kicad_string.h>
  37. #include <wxBasePcbFrame.h>
  38. #include <msgpanel.h>
  39. #include <pcb_netlist.h>
  40. #include <reporter.h>
  41. #include <base_units.h>
  42. #include <ratsnest_data.h>
  43. #include <ratsnest_viewitem.h>
  44. #include <worksheet_viewitem.h>
  45. #include <pcbnew.h>
  46. #include <collectors.h>
  47. #include <class_board.h>
  48. #include <class_module.h>
  49. #include <class_track.h>
  50. #include <class_zone.h>
  51. #include <class_marker_pcb.h>
  52. #include <class_drawsegment.h>
  53. #include <class_pcb_text.h>
  54. #include <class_mire.h>
  55. #include <class_dimension.h>
  56. #include <connectivity.h>
  57. /* This is an odd place for this, but CvPcb won't link if it is
  58. * in class_board_item.cpp like I first tried it.
  59. */
  60. wxPoint BOARD_ITEM::ZeroOffset( 0, 0 );
  61. BOARD::BOARD() :
  62. BOARD_ITEM_CONTAINER( (BOARD_ITEM*) NULL, PCB_T ),
  63. m_paper( PAGE_INFO::A4 ), m_NetInfo( this )
  64. {
  65. // we have not loaded a board yet, assume latest until then.
  66. m_fileFormatVersionAtLoad = LEGACY_BOARD_FILE_VERSION;
  67. m_Status_Pcb = 0; // Status word: bit 1 = calculate.
  68. m_CurrentZoneContour = NULL; // This ZONE_CONTAINER handle the
  69. // zone contour currently in progress
  70. BuildListOfNets(); // prepare pad and netlist containers.
  71. for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  72. {
  73. m_Layer[layer].m_name = GetStandardLayerName( ToLAYER_ID( layer ) );
  74. if( IsCopperLayer( layer ) )
  75. m_Layer[layer].m_type = LT_SIGNAL;
  76. else
  77. m_Layer[layer].m_type = LT_UNDEFINED;
  78. }
  79. // Initialize default netclass.
  80. NETCLASSPTR defaultClass = m_designSettings.GetDefault();
  81. defaultClass->SetDescription( _( "This is the default net class." ) );
  82. m_designSettings.SetCurrentNetClass( defaultClass->GetName() );
  83. // Set sensible initial values for custom track width & via size
  84. m_designSettings.UseCustomTrackViaSize( false );
  85. m_designSettings.SetCustomTrackWidth( m_designSettings.GetCurrentTrackWidth() );
  86. m_designSettings.SetCustomViaSize( m_designSettings.GetCurrentViaSize() );
  87. m_designSettings.SetCustomViaDrill( m_designSettings.GetCurrentViaDrill() );
  88. // Initialize ratsnest
  89. m_connectivity.reset( new CONNECTIVITY_DATA() );
  90. m_connectivity->Build( this );
  91. }
  92. BOARD::~BOARD()
  93. {
  94. while( m_ZoneDescriptorList.size() )
  95. {
  96. ZONE_CONTAINER* area_to_remove = m_ZoneDescriptorList[0];
  97. Delete( area_to_remove );
  98. }
  99. DeleteMARKERs();
  100. DeleteZONEOutlines();
  101. delete m_CurrentZoneContour;
  102. m_CurrentZoneContour = NULL;
  103. }
  104. void BOARD::BuildConnectivity()
  105. {
  106. GetConnectivity()->Build( this );
  107. }
  108. const wxPoint& BOARD::GetPosition() const
  109. {
  110. wxLogWarning( wxT( "This should not be called on the BOARD object") );
  111. return ZeroOffset;
  112. }
  113. void BOARD::SetPosition( const wxPoint& aPos )
  114. {
  115. wxLogWarning( wxT( "This should not be called on the BOARD object") );
  116. }
  117. void BOARD::Move( const wxPoint& aMoveVector ) // overload
  118. {
  119. // @todo : anything like this elsewhere? maybe put into GENERAL_COLLECTOR class.
  120. static const KICAD_T top_level_board_stuff[] = {
  121. PCB_MARKER_T,
  122. PCB_TEXT_T,
  123. PCB_LINE_T,
  124. PCB_DIMENSION_T,
  125. PCB_TARGET_T,
  126. PCB_VIA_T,
  127. PCB_TRACE_T,
  128. // PCB_PAD_T, Can't be at board level
  129. // PCB_MODULE_TEXT_T, Can't be at board level
  130. PCB_MODULE_T,
  131. PCB_ZONE_AREA_T,
  132. EOT
  133. };
  134. INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
  135. {
  136. BOARD_ITEM* brd_item = (BOARD_ITEM*) item;
  137. // aMoveVector was snapshotted, don't need "data".
  138. brd_item->Move( aMoveVector );
  139. return SEARCH_CONTINUE;
  140. };
  141. Visit( inspector, NULL, top_level_board_stuff );
  142. }
  143. TRACKS BOARD::TracksInNet( int aNetCode )
  144. {
  145. TRACKS ret;
  146. INSPECTOR_FUNC inspector = [aNetCode,&ret] ( EDA_ITEM* item, void* testData )
  147. {
  148. TRACK* t = (TRACK*) item;
  149. if( t->GetNetCode() == aNetCode )
  150. ret.push_back( t );
  151. return SEARCH_CONTINUE;
  152. };
  153. // visit this BOARD's TRACKs and VIAs with above TRACK INSPECTOR which
  154. // appends all in aNetCode to ret.
  155. Visit( inspector, NULL, GENERAL_COLLECTOR::Tracks );
  156. return ret;
  157. }
  158. /**
  159. * Function removeTrack
  160. * removes aOneToRemove from aList, which is a non-owning std::vector
  161. */
  162. static void removeTrack( TRACKS* aList, TRACK* aOneToRemove )
  163. {
  164. aList->erase( std::remove( aList->begin(), aList->end(), aOneToRemove ), aList->end() );
  165. }
  166. static void otherEnd( const TRACK& aTrack, const wxPoint& aNotThisEnd, wxPoint* aOtherEnd )
  167. {
  168. if( aTrack.GetStart() == aNotThisEnd )
  169. {
  170. *aOtherEnd = aTrack.GetEnd();
  171. }
  172. else
  173. {
  174. wxASSERT( aTrack.GetEnd() == aNotThisEnd );
  175. *aOtherEnd = aTrack.GetStart();
  176. }
  177. }
  178. /**
  179. * Function find_vias_and_tracks_at
  180. * collects TRACKs and VIAs at aPos and returns the @a track_count which excludes vias.
  181. */
  182. static int find_vias_and_tracks_at( TRACKS& at_next, TRACKS& in_net, LSET& lset, const wxPoint& next )
  183. {
  184. // first find all vias (in this net) at 'next' location, and expand LSET with each
  185. for( TRACKS::iterator it = in_net.begin(); it != in_net.end(); )
  186. {
  187. TRACK* t = *it;
  188. if( t->Type() == PCB_VIA_T && (t->GetLayerSet() & lset).any() &&
  189. ( t->GetStart() == next || t->GetEnd() == next ) )
  190. {
  191. lset |= t->GetLayerSet();
  192. at_next.push_back( t );
  193. it = in_net.erase( it );
  194. }
  195. else
  196. ++it;
  197. }
  198. int track_count = 0;
  199. // with expanded lset, find all tracks with an end on any of the layers in lset
  200. for( TRACKS::iterator it = in_net.begin(); it != in_net.end(); /* iterates in the loop body */ )
  201. {
  202. TRACK* t = *it;
  203. if( ( t->GetLayerSet() & lset ).any() && ( t->GetStart() == next || t->GetEnd() == next ) )
  204. {
  205. at_next.push_back( t );
  206. it = in_net.erase( it );
  207. ++track_count;
  208. }
  209. else
  210. {
  211. ++it;
  212. }
  213. }
  214. return track_count;
  215. }
  216. /**
  217. * Function checkConnectedTo
  218. * returns if aTracksInNet contains a copper pathway to aGoal when starting with
  219. * aFirstTrack. aFirstTrack should have one end situated on aStart, and the
  220. * traversal testing begins from the other end of aFirstTrack.
  221. * <p>
  222. * The function throws an exception instead of returning bool so that detailed
  223. * information can be provided about a possible failure in the track layout.
  224. *
  225. * @throw IO_ERROR - if points are not connected, with text saying why.
  226. */
  227. static void checkConnectedTo( BOARD* aBoard, TRACKS* aList, const TRACKS& aTracksInNet,
  228. const wxPoint& aGoal, const wxPoint& aStart, TRACK* aFirstTrack )
  229. {
  230. TRACKS in_net = aTracksInNet; // copy source list so the copy can be modified
  231. wxPoint next;
  232. otherEnd( *aFirstTrack, aStart, &next );
  233. aList->push_back( aFirstTrack );
  234. removeTrack( &in_net, aFirstTrack );
  235. LSET lset( aFirstTrack->GetLayer() );
  236. while( in_net.size() )
  237. {
  238. if( next == aGoal )
  239. return; // success
  240. // Want an exact match on the position of next, i.e. pad at next,
  241. // not a forgiving HitTest() with tolerance type of match, otherwise the overall
  242. // algorithm will not work. GetPadFast() is an exact match as I write this.
  243. if( aBoard->GetPadFast( next, lset ) )
  244. {
  245. std::string m = StrPrintf(
  246. "intervening pad at:(xy %s) between start:(xy %s) and goal:(xy %s)",
  247. BOARD_ITEM::FormatInternalUnits( next ).c_str(),
  248. BOARD_ITEM::FormatInternalUnits( aStart ).c_str(),
  249. BOARD_ITEM::FormatInternalUnits( aGoal ).c_str()
  250. );
  251. THROW_IO_ERROR( m );
  252. }
  253. int track_count = find_vias_and_tracks_at( *aList, in_net, lset, next );
  254. if( track_count != 1 )
  255. {
  256. std::string m = StrPrintf(
  257. "found %d tracks intersecting at (xy %s), exactly 2 would be acceptable.",
  258. track_count + aList->size() == 1 ? 1 : 0,
  259. BOARD_ITEM::FormatInternalUnits( next ).c_str()
  260. );
  261. THROW_IO_ERROR( m );
  262. }
  263. // reduce lset down to the layer that the last track at 'next' is on.
  264. lset = aList->back()->GetLayerSet();
  265. otherEnd( *aList->back(), next, &next );
  266. }
  267. std::string m = StrPrintf(
  268. "not enough tracks connecting start:(xy %s) and goal:(xy %s).",
  269. BOARD_ITEM::FormatInternalUnits( aStart ).c_str(),
  270. BOARD_ITEM::FormatInternalUnits( aGoal ).c_str()
  271. );
  272. THROW_IO_ERROR( m );
  273. }
  274. TRACKS BOARD::TracksInNetBetweenPoints( const wxPoint& aStartPos, const wxPoint& aGoalPos, int aNetCode )
  275. {
  276. TRACKS in_between_pts;
  277. TRACKS on_start_point;
  278. TRACKS in_net = TracksInNet( aNetCode ); // a small subset of TRACKs and VIAs
  279. for( auto t : in_net )
  280. {
  281. if( t->Type() == PCB_TRACE_T && ( t->GetStart() == aStartPos || t->GetEnd() == aStartPos ) )
  282. on_start_point.push_back( t );
  283. }
  284. wxString per_path_problem_text;
  285. for( auto t : on_start_point ) // explore each trace (path) leaving aStartPos
  286. {
  287. // checkConnectedTo() fills in_between_pts on every attempt. For failures
  288. // this set needs to be cleared.
  289. in_between_pts.clear();
  290. try
  291. {
  292. checkConnectedTo( this, &in_between_pts, in_net, aGoalPos, aStartPos, t );
  293. }
  294. catch( const IO_ERROR& ioe ) // means not connected
  295. {
  296. per_path_problem_text += "\n\t";
  297. per_path_problem_text += ioe.Problem();
  298. continue; // keep trying, there may be other paths leaving from aStartPos
  299. }
  300. // success, no exception means a valid connection,
  301. // return this set of TRACKS without throwing.
  302. return in_between_pts;
  303. }
  304. wxString m = wxString::Format(
  305. "no clean path connecting start:(xy %s) with goal:(xy %s)",
  306. BOARD_ITEM::FormatInternalUnits( aStartPos ).c_str(),
  307. BOARD_ITEM::FormatInternalUnits( aGoalPos ).c_str()
  308. );
  309. THROW_IO_ERROR( m + per_path_problem_text );
  310. }
  311. void BOARD::chainMarkedSegments( wxPoint aPosition, const LSET& aLayerSet, TRACKS* aList )
  312. {
  313. LSET layer_set = aLayerSet;
  314. if( !m_Track ) // no tracks at all in board
  315. return;
  316. /* Set the BUSY flag of all connected segments, first search starting at
  317. * aPosition. The search ends when a pad is found (end of a track), a
  318. * segment end has more than one other segment end connected, or when no
  319. * connected item found.
  320. *
  321. * Vias are a special case because they must look for segments connected
  322. * on other layers and they change the layer mask. They can be a track
  323. * end or not. They will be analyzer later and vias on terminal points
  324. * of the track will be considered as part of this track if they do not
  325. * connect segments of another track together and will be considered as
  326. * part of an other track when removing the via, the segments of that other
  327. * track are disconnected.
  328. */
  329. for( ; ; )
  330. {
  331. if( GetPad( aPosition, layer_set ) != NULL )
  332. return;
  333. /* Test for a via: a via changes the layer mask and can connect a lot
  334. * of segments at location aPosition. When found, the via is just
  335. * pushed in list. Vias will be examined later, when all connected
  336. * segment are found and push in list. This is because when a via
  337. * is found we do not know at this time the number of connected items
  338. * and we do not know if this via is on the track or finish the track
  339. */
  340. TRACK* via = m_Track->GetVia( NULL, aPosition, layer_set );
  341. if( via )
  342. {
  343. layer_set = via->GetLayerSet();
  344. aList->push_back( via );
  345. }
  346. int seg_count = 0;
  347. TRACK* candidate = NULL;
  348. /* Search all segments connected to point aPosition.
  349. * if only 1 segment at aPosition: then this segment is "candidate"
  350. * if > 1 segment:
  351. * then end of "track" (because more than 2 segments are connected at aPosition)
  352. */
  353. TRACK* segment = m_Track;
  354. while( ( segment = ::GetTrack( segment, NULL, aPosition, layer_set ) ) != NULL )
  355. {
  356. if( segment->GetState( BUSY ) ) // already found and selected: skip it
  357. {
  358. segment = segment->Next();
  359. continue;
  360. }
  361. if( segment == via ) // just previously found: skip it
  362. {
  363. segment = segment->Next();
  364. continue;
  365. }
  366. if( ++seg_count == 1 ) // if first connected item: then segment is candidate
  367. {
  368. candidate = segment;
  369. segment = segment->Next();
  370. }
  371. else // More than 1 segment connected -> location is end of track
  372. {
  373. return;
  374. }
  375. }
  376. if( candidate ) // A candidate is found: flag it and push it in list
  377. {
  378. /* Initialize parameters to search items connected to this
  379. * candidate:
  380. * we must analyze connections to its other end
  381. */
  382. if( aPosition == candidate->GetStart() )
  383. {
  384. aPosition = candidate->GetEnd();
  385. }
  386. else
  387. {
  388. aPosition = candidate->GetStart();
  389. }
  390. layer_set = candidate->GetLayerSet();
  391. // flag this item and push it in list of selected items
  392. aList->push_back( candidate );
  393. candidate->SetState( BUSY, true );
  394. }
  395. else
  396. {
  397. return;
  398. }
  399. }
  400. }
  401. void BOARD::PushHighLight()
  402. {
  403. m_highLightPrevious = m_highLight;
  404. }
  405. void BOARD::PopHighLight()
  406. {
  407. m_highLight = m_highLightPrevious;
  408. m_highLightPrevious.Clear();
  409. }
  410. bool BOARD::SetLayerDescr( PCB_LAYER_ID aIndex, const LAYER& aLayer )
  411. {
  412. if( unsigned( aIndex ) < DIM( m_Layer ) )
  413. {
  414. m_Layer[ aIndex ] = aLayer;
  415. return true;
  416. }
  417. return false;
  418. }
  419. #include <stdio.h>
  420. const PCB_LAYER_ID BOARD::GetLayerID( const wxString& aLayerName ) const
  421. {
  422. // Look for the BOARD specific copper layer names
  423. for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  424. {
  425. if ( IsCopperLayer( layer ) && ( m_Layer[ layer ].m_name == aLayerName ) )
  426. {
  427. return ToLAYER_ID( layer );
  428. }
  429. }
  430. // Otherwise fall back to the system standard layer names
  431. for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  432. {
  433. if( GetStandardLayerName( ToLAYER_ID( layer ) ) == aLayerName )
  434. {
  435. return ToLAYER_ID( layer );
  436. }
  437. }
  438. return UNDEFINED_LAYER;
  439. }
  440. const wxString BOARD::GetLayerName( PCB_LAYER_ID aLayer ) const
  441. {
  442. // All layer names are stored in the BOARD.
  443. if( IsLayerEnabled( aLayer ) )
  444. {
  445. // Standard names were set in BOARD::BOARD() but they may be
  446. // over-ridden by BOARD::SetLayerName().
  447. // For copper layers, return the actual copper layer name,
  448. // otherwise return the Standard English layer name.
  449. if( IsCopperLayer( aLayer ) )
  450. return m_Layer[aLayer].m_name;
  451. }
  452. return GetStandardLayerName( aLayer );
  453. }
  454. bool BOARD::SetLayerName( PCB_LAYER_ID aLayer, const wxString& aLayerName )
  455. {
  456. if( !IsCopperLayer( aLayer ) )
  457. return false;
  458. if( aLayerName == wxEmptyString || aLayerName.Len() > 20 )
  459. return false;
  460. // no quote chars in the name allowed
  461. if( aLayerName.Find( wxChar( '"' ) ) != wxNOT_FOUND )
  462. return false;
  463. wxString nameTemp = aLayerName;
  464. // replace any spaces with underscores before we do any comparing
  465. nameTemp.Replace( wxT( " " ), wxT( "_" ) );
  466. if( IsLayerEnabled( aLayer ) )
  467. {
  468. #if 0
  469. for( LAYER_NUM i = FIRST_COPPER_LAYER; i < NB_COPPER_LAYERS; ++i )
  470. {
  471. if( i != aLayer && IsLayerEnabled( i ) && nameTemp == m_Layer[i].m_Name )
  472. return false;
  473. }
  474. #else
  475. for( LSEQ cu = GetEnabledLayers().CuStack(); cu; ++cu )
  476. {
  477. PCB_LAYER_ID id = *cu;
  478. // veto changing the name if it exists elsewhere.
  479. if( id != aLayer && nameTemp == m_Layer[id].m_name )
  480. // if( id != aLayer && nameTemp == wxString( m_Layer[id].m_name ) )
  481. return false;
  482. }
  483. #endif
  484. m_Layer[aLayer].m_name = nameTemp;
  485. return true;
  486. }
  487. return false;
  488. }
  489. LAYER_T BOARD::GetLayerType( PCB_LAYER_ID aLayer ) const
  490. {
  491. if( !IsCopperLayer( aLayer ) )
  492. return LT_SIGNAL;
  493. //@@IMB: The original test was broken due to the discontinuity
  494. // in the layer sequence.
  495. if( IsLayerEnabled( aLayer ) )
  496. return m_Layer[aLayer].m_type;
  497. return LT_SIGNAL;
  498. }
  499. bool BOARD::SetLayerType( PCB_LAYER_ID aLayer, LAYER_T aLayerType )
  500. {
  501. if( !IsCopperLayer( aLayer ) )
  502. return false;
  503. //@@IMB: The original test was broken due to the discontinuity
  504. // in the layer sequence.
  505. if( IsLayerEnabled( aLayer ) )
  506. {
  507. m_Layer[aLayer].m_type = aLayerType;
  508. return true;
  509. }
  510. return false;
  511. }
  512. const char* LAYER::ShowType( LAYER_T aType )
  513. {
  514. const char* cp;
  515. switch( aType )
  516. {
  517. default:
  518. case LT_SIGNAL:
  519. cp = "signal";
  520. break;
  521. case LT_POWER:
  522. cp = "power";
  523. break;
  524. case LT_MIXED:
  525. cp = "mixed";
  526. break;
  527. case LT_JUMPER:
  528. cp = "jumper";
  529. break;
  530. }
  531. return cp;
  532. }
  533. LAYER_T LAYER::ParseType( const char* aType )
  534. {
  535. if( strcmp( aType, "signal" ) == 0 )
  536. return LT_SIGNAL;
  537. else if( strcmp( aType, "power" ) == 0 )
  538. return LT_POWER;
  539. else if( strcmp( aType, "mixed" ) == 0 )
  540. return LT_MIXED;
  541. else if( strcmp( aType, "jumper" ) == 0 )
  542. return LT_JUMPER;
  543. else
  544. return LT_UNDEFINED;
  545. }
  546. int BOARD::GetCopperLayerCount() const
  547. {
  548. return m_designSettings.GetCopperLayerCount();
  549. }
  550. void BOARD::SetCopperLayerCount( int aCount )
  551. {
  552. m_designSettings.SetCopperLayerCount( aCount );
  553. }
  554. LSET BOARD::GetEnabledLayers() const
  555. {
  556. return m_designSettings.GetEnabledLayers();
  557. }
  558. LSET BOARD::GetVisibleLayers() const
  559. {
  560. return m_designSettings.GetVisibleLayers();
  561. }
  562. void BOARD::SetEnabledLayers( LSET aLayerSet )
  563. {
  564. m_designSettings.SetEnabledLayers( aLayerSet );
  565. }
  566. void BOARD::SetVisibleLayers( LSET aLayerSet )
  567. {
  568. m_designSettings.SetVisibleLayers( aLayerSet );
  569. }
  570. void BOARD::SetVisibleElements( int aMask )
  571. {
  572. // Call SetElementVisibility for each item
  573. // to ensure specific calculations that can be needed by some items,
  574. // just changing the visibility flags could be not sufficient.
  575. for( GAL_LAYER_ID ii = GAL_LAYER_ID_START; ii < GAL_LAYER_ID_BITMASK_END; ++ii )
  576. {
  577. int item_mask = 1 << GAL_LAYER_INDEX( ii );
  578. SetElementVisibility( ii, aMask & item_mask );
  579. }
  580. }
  581. void BOARD::SetVisibleAlls()
  582. {
  583. SetVisibleLayers( LSET().set() );
  584. // Call SetElementVisibility for each item,
  585. // to ensure specific calculations that can be needed by some items
  586. for( GAL_LAYER_ID ii = GAL_LAYER_ID_START; ii < GAL_LAYER_ID_BITMASK_END; ++ii )
  587. SetElementVisibility( ii, true );
  588. }
  589. int BOARD::GetVisibleElements() const
  590. {
  591. return m_designSettings.GetVisibleElements();
  592. }
  593. bool BOARD::IsElementVisible( GAL_LAYER_ID LAYER_aPCB ) const
  594. {
  595. return m_designSettings.IsElementVisible( LAYER_aPCB );
  596. }
  597. void BOARD::SetElementVisibility( GAL_LAYER_ID LAYER_aPCB, bool isEnabled )
  598. {
  599. m_designSettings.SetElementVisibility( LAYER_aPCB, isEnabled );
  600. switch( LAYER_aPCB )
  601. {
  602. case LAYER_RATSNEST:
  603. {
  604. bool visible = IsElementVisible( LAYER_RATSNEST );
  605. // we must clear or set the CH_VISIBLE flags to hide/show ratsnest
  606. // because we have a tool to show/hide ratsnest relative to a pad or a module
  607. // so the hide/show option is a per item selection
  608. for( unsigned int net = 1; net < GetNetCount(); net++ )
  609. {
  610. auto rn = GetConnectivity()->GetRatsnestForNet( net );
  611. if( rn )
  612. rn->SetVisible( visible );
  613. }
  614. for( auto track : Tracks() )
  615. track->SetLocalRatsnestVisible( isEnabled );
  616. for( auto mod : Modules() )
  617. {
  618. for( auto pad : mod->Pads() )
  619. pad->SetLocalRatsnestVisible( isEnabled );
  620. }
  621. for( int i = 0; i<GetAreaCount(); i++ )
  622. {
  623. auto zone = GetArea( i );
  624. zone->SetLocalRatsnestVisible( isEnabled );
  625. }
  626. m_Status_Pcb = 0;
  627. break;
  628. }
  629. default:
  630. ;
  631. }
  632. }
  633. bool BOARD::IsModuleLayerVisible( PCB_LAYER_ID layer )
  634. {
  635. switch( layer )
  636. {
  637. case F_Cu:
  638. return IsElementVisible( LAYER_MOD_FR );
  639. case B_Cu:
  640. return IsElementVisible( LAYER_MOD_BK );
  641. default:
  642. wxFAIL_MSG( wxT( "BOARD::IsModuleLayerVisible() param error: bad layer" ) );
  643. return true;
  644. }
  645. }
  646. void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
  647. {
  648. if( aBoardItem == NULL )
  649. {
  650. wxFAIL_MSG( wxT( "BOARD::Add() param error: aBoardItem NULL" ) );
  651. return;
  652. }
  653. switch( aBoardItem->Type() )
  654. {
  655. case PCB_NETINFO_T:
  656. m_NetInfo.AppendNet( (NETINFO_ITEM*) aBoardItem );
  657. break;
  658. // this one uses a vector
  659. case PCB_MARKER_T:
  660. m_markers.push_back( (MARKER_PCB*) aBoardItem );
  661. break;
  662. // this one uses a vector
  663. case PCB_ZONE_AREA_T:
  664. m_ZoneDescriptorList.push_back( (ZONE_CONTAINER*) aBoardItem );
  665. break;
  666. case PCB_TRACE_T:
  667. case PCB_VIA_T:
  668. if( aMode == ADD_APPEND )
  669. {
  670. m_Track.PushBack( (TRACK*) aBoardItem );
  671. }
  672. else
  673. {
  674. TRACK* insertAid;
  675. insertAid = ( (TRACK*) aBoardItem )->GetBestInsertPoint( this );
  676. m_Track.Insert( (TRACK*) aBoardItem, insertAid );
  677. }
  678. break;
  679. case PCB_ZONE_T:
  680. if( aMode == ADD_APPEND )
  681. m_Zone.PushBack( (SEGZONE*) aBoardItem );
  682. else
  683. m_Zone.PushFront( (SEGZONE*) aBoardItem );
  684. break;
  685. case PCB_MODULE_T:
  686. if( aMode == ADD_APPEND )
  687. m_Modules.PushBack( (MODULE*) aBoardItem );
  688. else
  689. m_Modules.PushFront( (MODULE*) aBoardItem );
  690. // Because the list of pads has changed, reset the status
  691. // This indicate the list of pad and nets must be recalculated before use
  692. m_Status_Pcb = 0;
  693. break;
  694. case PCB_DIMENSION_T:
  695. case PCB_LINE_T:
  696. case PCB_TEXT_T:
  697. case PCB_TARGET_T:
  698. if( aMode == ADD_APPEND )
  699. m_Drawings.PushBack( aBoardItem );
  700. else
  701. m_Drawings.PushFront( aBoardItem );
  702. break;
  703. // other types may use linked list
  704. default:
  705. {
  706. wxString msg;
  707. msg.Printf( wxT( "BOARD::Add() needs work: BOARD_ITEM type (%d) not handled" ),
  708. aBoardItem->Type() );
  709. wxFAIL_MSG( msg );
  710. return;
  711. }
  712. break;
  713. }
  714. aBoardItem->SetParent( this );
  715. m_connectivity->Add( aBoardItem );
  716. }
  717. void BOARD::Remove( BOARD_ITEM* aBoardItem )
  718. {
  719. // find these calls and fix them! Don't send me no stinking' NULL.
  720. wxASSERT( aBoardItem );
  721. switch( aBoardItem->Type() )
  722. {
  723. case PCB_NETINFO_T:
  724. {
  725. NETINFO_ITEM* item = (NETINFO_ITEM*) aBoardItem;
  726. m_NetInfo.RemoveNet( item );
  727. break;
  728. }
  729. case PCB_MARKER_T:
  730. // find the item in the vector, then remove it
  731. for( unsigned i = 0; i<m_markers.size(); ++i )
  732. {
  733. if( m_markers[i] == (MARKER_PCB*) aBoardItem )
  734. {
  735. m_markers.erase( m_markers.begin() + i );
  736. break;
  737. }
  738. }
  739. break;
  740. case PCB_ZONE_AREA_T: // this one uses a vector
  741. // find the item in the vector, then delete then erase it.
  742. for( unsigned i = 0; i<m_ZoneDescriptorList.size(); ++i )
  743. {
  744. if( m_ZoneDescriptorList[i] == (ZONE_CONTAINER*) aBoardItem )
  745. {
  746. m_ZoneDescriptorList.erase( m_ZoneDescriptorList.begin() + i );
  747. break;
  748. }
  749. }
  750. break;
  751. case PCB_MODULE_T:
  752. m_Modules.Remove( (MODULE*) aBoardItem );
  753. break;
  754. case PCB_TRACE_T:
  755. case PCB_VIA_T:
  756. m_Track.Remove( (TRACK*) aBoardItem );
  757. break;
  758. case PCB_ZONE_T:
  759. m_Zone.Remove( (SEGZONE*) aBoardItem );
  760. break;
  761. case PCB_DIMENSION_T:
  762. case PCB_LINE_T:
  763. case PCB_TEXT_T:
  764. case PCB_TARGET_T:
  765. m_Drawings.Remove( aBoardItem );
  766. break;
  767. // other types may use linked list
  768. default:
  769. wxFAIL_MSG( wxT( "BOARD::Remove() needs more ::Type() support" ) );
  770. }
  771. m_connectivity->Remove( aBoardItem );
  772. }
  773. void BOARD::DeleteMARKERs()
  774. {
  775. // the vector does not know how to delete the MARKER_PCB, it holds pointers
  776. for( unsigned i = 0; i<m_markers.size(); ++i )
  777. delete m_markers[i];
  778. m_markers.clear();
  779. }
  780. void BOARD::DeleteZONEOutlines()
  781. {
  782. // the vector does not know how to delete the ZONE Outlines, it holds
  783. // pointers
  784. for( unsigned i = 0; i<m_ZoneDescriptorList.size(); ++i )
  785. delete m_ZoneDescriptorList[i];
  786. m_ZoneDescriptorList.clear();
  787. }
  788. int BOARD::GetNumSegmTrack() const
  789. {
  790. return m_Track.GetCount();
  791. }
  792. int BOARD::GetNumSegmZone() const
  793. {
  794. return m_Zone.GetCount();
  795. }
  796. unsigned BOARD::GetNodesCount() const
  797. {
  798. return m_connectivity->GetPadCount();
  799. }
  800. unsigned BOARD::GetUnconnectedNetCount() const
  801. {
  802. return m_connectivity->GetUnconnectedCount();
  803. }
  804. EDA_RECT BOARD::ComputeBoundingBox( bool aBoardEdgesOnly ) const
  805. {
  806. bool hasItems = false;
  807. EDA_RECT area;
  808. // Check segments, dimensions, texts, and fiducials
  809. for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() )
  810. {
  811. if( aBoardEdgesOnly && (item->Type() != PCB_LINE_T || item->GetLayer() != Edge_Cuts ) )
  812. continue;
  813. if( !hasItems )
  814. area = item->GetBoundingBox();
  815. else
  816. area.Merge( item->GetBoundingBox() );
  817. hasItems = true;
  818. }
  819. if( !aBoardEdgesOnly )
  820. {
  821. // Check modules
  822. for( MODULE* module = m_Modules; module; module = module->Next() )
  823. {
  824. if( !hasItems )
  825. area = module->GetBoundingBox();
  826. else
  827. area.Merge( module->GetBoundingBox() );
  828. hasItems = true;
  829. }
  830. // Check tracks
  831. for( TRACK* track = m_Track; track; track = track->Next() )
  832. {
  833. if( !hasItems )
  834. area = track->GetBoundingBox();
  835. else
  836. area.Merge( track->GetBoundingBox() );
  837. hasItems = true;
  838. }
  839. // Check segment zones
  840. for( TRACK* track = m_Zone; track; track = track->Next() )
  841. {
  842. if( !hasItems )
  843. area = track->GetBoundingBox();
  844. else
  845. area.Merge( track->GetBoundingBox() );
  846. hasItems = true;
  847. }
  848. // Check polygonal zones
  849. for( unsigned int i = 0; i < m_ZoneDescriptorList.size(); i++ )
  850. {
  851. ZONE_CONTAINER* aZone = m_ZoneDescriptorList[i];
  852. if( !hasItems )
  853. area = aZone->GetBoundingBox();
  854. else
  855. area.Merge( aZone->GetBoundingBox() );
  856. area.Merge( aZone->GetBoundingBox() );
  857. hasItems = true;
  858. }
  859. }
  860. return area;
  861. }
  862. // virtual, see pcbstruct.h
  863. void BOARD::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
  864. {
  865. wxString txt;
  866. int viasCount = 0;
  867. int trackSegmentsCount = 0;
  868. for( BOARD_ITEM* item = m_Track; item; item = item->Next() )
  869. {
  870. if( item->Type() == PCB_VIA_T )
  871. viasCount++;
  872. else
  873. trackSegmentsCount++;
  874. }
  875. txt.Printf( wxT( "%d" ), GetPadCount() );
  876. aList.push_back( MSG_PANEL_ITEM( _( "Pads" ), txt, DARKGREEN ) );
  877. txt.Printf( wxT( "%d" ), viasCount );
  878. aList.push_back( MSG_PANEL_ITEM( _( "Vias" ), txt, DARKGREEN ) );
  879. txt.Printf( wxT( "%d" ), trackSegmentsCount );
  880. aList.push_back( MSG_PANEL_ITEM( _( "Track Segments" ), txt, DARKGREEN ) );
  881. txt.Printf( wxT( "%d" ), GetNodesCount() );
  882. aList.push_back( MSG_PANEL_ITEM( _( "Nodes" ), txt, DARKCYAN ) );
  883. txt.Printf( wxT( "%d" ), m_NetInfo.GetNetCount() );
  884. aList.push_back( MSG_PANEL_ITEM( _( "Nets" ), txt, RED ) );
  885. txt.Printf( wxT( "%d" ), GetConnectivity()->GetUnconnectedCount() );
  886. aList.push_back( MSG_PANEL_ITEM( _( "Unconnected" ), txt, BLUE ) );
  887. }
  888. // virtual, see pcbstruct.h
  889. SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
  890. {
  891. KICAD_T stype;
  892. SEARCH_RESULT result = SEARCH_CONTINUE;
  893. const KICAD_T* p = scanTypes;
  894. bool done = false;
  895. #if 0 && defined(DEBUG)
  896. std::cout << GetClass().mb_str() << ' ';
  897. #endif
  898. while( !done )
  899. {
  900. stype = *p;
  901. switch( stype )
  902. {
  903. case PCB_T:
  904. result = inspector( this, testData ); // inspect me
  905. // skip over any types handled in the above call.
  906. ++p;
  907. break;
  908. /* Instances of the requested KICAD_T live in a list, either one
  909. * that I manage, or that my modules manage. If it's a type managed
  910. * by class MODULE, then simply pass it on to each module's
  911. * MODULE::Visit() function by way of the
  912. * IterateForward( m_Modules, ... ) call.
  913. */
  914. case PCB_MODULE_T:
  915. case PCB_PAD_T:
  916. case PCB_MODULE_TEXT_T:
  917. case PCB_MODULE_EDGE_T:
  918. // this calls MODULE::Visit() on each module.
  919. result = IterateForward( m_Modules, inspector, testData, p );
  920. // skip over any types handled in the above call.
  921. for( ; ; )
  922. {
  923. switch( stype = *++p )
  924. {
  925. case PCB_MODULE_T:
  926. case PCB_PAD_T:
  927. case PCB_MODULE_TEXT_T:
  928. case PCB_MODULE_EDGE_T:
  929. continue;
  930. default:
  931. ;
  932. }
  933. break;
  934. }
  935. break;
  936. case PCB_LINE_T:
  937. case PCB_TEXT_T:
  938. case PCB_DIMENSION_T:
  939. case PCB_TARGET_T:
  940. result = IterateForward( m_Drawings, inspector, testData, p );
  941. // skip over any types handled in the above call.
  942. for( ; ; )
  943. {
  944. switch( stype = *++p )
  945. {
  946. case PCB_LINE_T:
  947. case PCB_TEXT_T:
  948. case PCB_DIMENSION_T:
  949. case PCB_TARGET_T:
  950. continue;
  951. default:
  952. ;
  953. }
  954. break;
  955. }
  956. ;
  957. break;
  958. #if 0 // both these are on same list, so we must scan it twice in order
  959. // to get VIA priority, using new #else code below.
  960. // But we are not using separate lists for TRACKs and VIA, because
  961. // items are ordered (sorted) in the linked
  962. // list by netcode AND by physical distance:
  963. // when created, if a track or via is connected to an existing track or
  964. // via, it is put in linked list after this existing track or via
  965. // So usually, connected tracks or vias are grouped in this list
  966. // So the algorithm (used in ratsnest computations) which computes the
  967. // track connectivity is faster (more than 100 time regarding to
  968. // a non ordered list) because when it searches for a connection, first
  969. // it tests the near (near in term of linked list) 50 items
  970. // from the current item (track or via) in test.
  971. // Usually, because of this sort, a connected item (if exists) is
  972. // found.
  973. // If not found (and only in this case) an exhaustive (and time
  974. // consuming) search is made, but this case is statistically rare.
  975. case PCB_VIA_T:
  976. case PCB_TRACE_T:
  977. result = IterateForward( m_Track, inspector, testData, p );
  978. // skip over any types handled in the above call.
  979. for( ; ; )
  980. {
  981. switch( stype = *++p )
  982. {
  983. case PCB_VIA_T:
  984. case PCB_TRACE_T:
  985. continue;
  986. default:
  987. ;
  988. }
  989. break;
  990. }
  991. break;
  992. #else
  993. case PCB_VIA_T:
  994. result = IterateForward( m_Track, inspector, testData, p );
  995. ++p;
  996. break;
  997. case PCB_TRACE_T:
  998. result = IterateForward( m_Track, inspector, testData, p );
  999. ++p;
  1000. break;
  1001. #endif
  1002. case PCB_MARKER_T:
  1003. // MARKER_PCBS are in the m_markers std::vector
  1004. for( unsigned i = 0; i<m_markers.size(); ++i )
  1005. {
  1006. result = m_markers[i]->Visit( inspector, testData, p );
  1007. if( result == SEARCH_QUIT )
  1008. break;
  1009. }
  1010. ++p;
  1011. break;
  1012. case PCB_ZONE_AREA_T:
  1013. // PCB_ZONE_AREA_T are in the m_ZoneDescriptorList std::vector
  1014. for( unsigned i = 0; i< m_ZoneDescriptorList.size(); ++i )
  1015. {
  1016. result = m_ZoneDescriptorList[i]->Visit( inspector, testData, p );
  1017. if( result == SEARCH_QUIT )
  1018. break;
  1019. }
  1020. ++p;
  1021. break;
  1022. case PCB_ZONE_T:
  1023. result = IterateForward( m_Zone, inspector, testData, p );
  1024. ++p;
  1025. break;
  1026. default: // catch EOT or ANY OTHER type here and return.
  1027. done = true;
  1028. break;
  1029. }
  1030. if( result == SEARCH_QUIT )
  1031. break;
  1032. }
  1033. return result;
  1034. }
  1035. NETINFO_ITEM* BOARD::FindNet( int aNetcode ) const
  1036. {
  1037. // the first valid netcode is 1 and the last is m_NetInfo.GetCount()-1.
  1038. // zero is reserved for "no connection" and is not actually a net.
  1039. // NULL is returned for non valid netcodes
  1040. wxASSERT( m_NetInfo.GetNetCount() > 0 ); // net zero should exist
  1041. if( aNetcode == NETINFO_LIST::UNCONNECTED && m_NetInfo.GetNetCount() == 0 )
  1042. return &NETINFO_LIST::ORPHANED_ITEM;
  1043. else
  1044. return m_NetInfo.GetNetItem( aNetcode );
  1045. }
  1046. NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname ) const
  1047. {
  1048. return m_NetInfo.GetNetItem( aNetname );
  1049. }
  1050. MODULE* BOARD::FindModuleByReference( const wxString& aReference ) const
  1051. {
  1052. MODULE* found = nullptr;
  1053. // search only for MODULES
  1054. static const KICAD_T scanTypes[] = { PCB_MODULE_T, EOT };
  1055. INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
  1056. {
  1057. MODULE* module = (MODULE*) item;
  1058. if( aReference == module->GetReference() )
  1059. {
  1060. found = module;
  1061. return SEARCH_QUIT;
  1062. }
  1063. return SEARCH_CONTINUE;
  1064. };
  1065. // visit this BOARD with the above inspector
  1066. BOARD* nonconstMe = (BOARD*) this;
  1067. nonconstMe->Visit( inspector, NULL, scanTypes );
  1068. return found;
  1069. }
  1070. MODULE* BOARD::FindModule( const wxString& aRefOrTimeStamp, bool aSearchByTimeStamp ) const
  1071. {
  1072. if( aSearchByTimeStamp )
  1073. {
  1074. for( MODULE* module = m_Modules; module; module = module->Next() )
  1075. {
  1076. if( aRefOrTimeStamp.CmpNoCase( module->GetPath() ) == 0 )
  1077. return module;
  1078. }
  1079. }
  1080. else
  1081. {
  1082. return FindModuleByReference( aRefOrTimeStamp );
  1083. }
  1084. return NULL;
  1085. }
  1086. // Sort nets by decreasing pad count. For same pad count, sort by alphabetic names
  1087. static bool sortNetsByNodes( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
  1088. {
  1089. auto connectivity = a->GetParent()->GetConnectivity();
  1090. int countA = connectivity->GetPadCount( a->GetNet() );
  1091. int countB = connectivity->GetPadCount( b->GetNet() );
  1092. if( countA == countB )
  1093. return a->GetNetname() < b->GetNetname();
  1094. else
  1095. return countB < countA;
  1096. }
  1097. // Sort nets by alphabetic names
  1098. static bool sortNetsByNames( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
  1099. {
  1100. return a->GetNetname() < b->GetNetname();
  1101. }
  1102. int BOARD::SortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount )
  1103. {
  1104. if( m_NetInfo.GetNetCount() == 0 )
  1105. return 0;
  1106. // Build the list
  1107. std::vector <NETINFO_ITEM*> netBuffer;
  1108. netBuffer.reserve( m_NetInfo.GetNetCount() );
  1109. for( NETINFO_LIST::iterator net( m_NetInfo.begin() ), netEnd( m_NetInfo.end() );
  1110. net != netEnd; ++net )
  1111. {
  1112. if( net->GetNet() > 0 )
  1113. netBuffer.push_back( *net );
  1114. }
  1115. // sort the list
  1116. if( aSortbyPadsCount )
  1117. sort( netBuffer.begin(), netBuffer.end(), sortNetsByNodes );
  1118. else
  1119. sort( netBuffer.begin(), netBuffer.end(), sortNetsByNames );
  1120. for( unsigned ii = 0; ii < netBuffer.size(); ii++ )
  1121. aNames.Add( netBuffer[ii]->GetNetname() );
  1122. return netBuffer.size();
  1123. }
  1124. void BOARD::RedrawAreasOutlines( EDA_DRAW_PANEL* panel, wxDC* aDC, GR_DRAWMODE aDrawMode, PCB_LAYER_ID aLayer )
  1125. {
  1126. if( !aDC )
  1127. return;
  1128. for( int ii = 0; ii < GetAreaCount(); ii++ )
  1129. {
  1130. ZONE_CONTAINER* edge_zone = GetArea( ii );
  1131. if( (aLayer < 0) || ( aLayer == edge_zone->GetLayer() ) )
  1132. edge_zone->Draw( panel, aDC, aDrawMode );
  1133. }
  1134. }
  1135. void BOARD::RedrawFilledAreas( EDA_DRAW_PANEL* panel, wxDC* aDC, GR_DRAWMODE aDrawMode, PCB_LAYER_ID aLayer )
  1136. {
  1137. if( !aDC )
  1138. return;
  1139. for( int ii = 0; ii < GetAreaCount(); ii++ )
  1140. {
  1141. ZONE_CONTAINER* edge_zone = GetArea( ii );
  1142. if( (aLayer < 0) || ( aLayer == edge_zone->GetLayer() ) )
  1143. edge_zone->DrawFilledArea( panel, aDC, aDrawMode );
  1144. }
  1145. }
  1146. ZONE_CONTAINER* BOARD::HitTestForAnyFilledArea( const wxPoint& aRefPos,
  1147. PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer, int aNetCode )
  1148. {
  1149. if( aEndLayer < 0 )
  1150. aEndLayer = aStartLayer;
  1151. if( aEndLayer < aStartLayer )
  1152. std::swap( aEndLayer, aStartLayer );
  1153. for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ )
  1154. {
  1155. ZONE_CONTAINER* area = m_ZoneDescriptorList[ia];
  1156. LAYER_NUM layer = area->GetLayer();
  1157. if( layer < aStartLayer || layer > aEndLayer )
  1158. continue;
  1159. // In locate functions we must skip tagged items with BUSY flag set.
  1160. if( area->GetState( BUSY ) )
  1161. continue;
  1162. if( aNetCode >= 0 && area->GetNetCode() != aNetCode )
  1163. continue;
  1164. if( area->HitTestFilledArea( aRefPos ) )
  1165. return area;
  1166. }
  1167. return NULL;
  1168. }
  1169. int BOARD::SetAreasNetCodesFromNetNames()
  1170. {
  1171. int error_count = 0;
  1172. for( int ii = 0; ii < GetAreaCount(); ii++ )
  1173. {
  1174. ZONE_CONTAINER* it = GetArea( ii );
  1175. if( !it->IsOnCopperLayer() )
  1176. {
  1177. it->SetNetCode( NETINFO_LIST::UNCONNECTED );
  1178. continue;
  1179. }
  1180. if( it->GetNetCode() != 0 ) // i.e. if this zone is connected to a net
  1181. {
  1182. const NETINFO_ITEM* net = it->GetNet();
  1183. if( net )
  1184. {
  1185. it->SetNetCode( net->GetNet() );
  1186. }
  1187. else
  1188. {
  1189. error_count++;
  1190. // keep Net Name and set m_NetCode to -1 : error flag.
  1191. it->SetNetCode( -1 );
  1192. }
  1193. }
  1194. }
  1195. return error_count;
  1196. }
  1197. VIA* BOARD::GetViaByPosition( const wxPoint& aPosition, PCB_LAYER_ID aLayer) const
  1198. {
  1199. for( VIA *via = GetFirstVia( m_Track); via; via = GetFirstVia( via->Next() ) )
  1200. {
  1201. if( (via->GetStart() == aPosition) &&
  1202. (via->GetState( BUSY | IS_DELETED ) == 0) &&
  1203. ((aLayer == UNDEFINED_LAYER) || (via->IsOnLayer( aLayer ))) )
  1204. return via;
  1205. }
  1206. return NULL;
  1207. }
  1208. D_PAD* BOARD::GetPad( const wxPoint& aPosition, LSET aLayerSet )
  1209. {
  1210. if( !aLayerSet.any() )
  1211. aLayerSet = LSET::AllCuMask();
  1212. for( MODULE* module = m_Modules; module; module = module->Next() )
  1213. {
  1214. D_PAD* pad = module->GetPad( aPosition, aLayerSet );
  1215. if( pad )
  1216. return pad;
  1217. }
  1218. return NULL;
  1219. }
  1220. D_PAD* BOARD::GetPad( TRACK* aTrace, ENDPOINT_T aEndPoint )
  1221. {
  1222. const wxPoint& aPosition = aTrace->GetEndPoint( aEndPoint );
  1223. LSET lset( aTrace->GetLayer() );
  1224. for( MODULE* module = m_Modules; module; module = module->Next() )
  1225. {
  1226. D_PAD* pad = module->GetPad( aPosition, lset );
  1227. if( pad )
  1228. return pad;
  1229. }
  1230. return NULL;
  1231. }
  1232. std::list<TRACK*> BOARD::GetTracksByPosition( const wxPoint& aPosition, PCB_LAYER_ID aLayer ) const
  1233. {
  1234. std::list<TRACK*> tracks;
  1235. for( TRACK* track = GetFirstTrack( m_Track ); track; track = GetFirstTrack( track->Next() ) )
  1236. {
  1237. if( ( ( track->GetStart() == aPosition ) || track->GetEnd() == aPosition ) &&
  1238. ( track->GetState( BUSY | IS_DELETED ) == 0 ) &&
  1239. ( ( aLayer == UNDEFINED_LAYER ) || ( track->IsOnLayer( aLayer ) ) ) )
  1240. tracks.push_back( track );
  1241. }
  1242. return tracks;
  1243. }
  1244. D_PAD* BOARD::GetPadFast( const wxPoint& aPosition, LSET aLayerSet )
  1245. {
  1246. for( auto mod : Modules() )
  1247. {
  1248. for ( auto pad : mod->Pads() )
  1249. {
  1250. if( pad->GetPosition() != aPosition )
  1251. continue;
  1252. // Pad found, it must be on the correct layer
  1253. if( ( pad->GetLayerSet() & aLayerSet ).any() )
  1254. return pad;
  1255. }
  1256. }
  1257. return nullptr;
  1258. }
  1259. D_PAD* BOARD::GetPad( std::vector<D_PAD*>& aPadList, const wxPoint& aPosition, LSET aLayerSet )
  1260. {
  1261. // Search aPadList for aPosition
  1262. // aPadList is sorted by X then Y values, and a fast binary search is used
  1263. int idxmax = aPadList.size()-1;
  1264. int delta = aPadList.size();
  1265. int idx = 0; // Starting index is the beginning of list
  1266. while( delta )
  1267. {
  1268. // Calculate half size of remaining interval to test.
  1269. // Ensure the computed value is not truncated (too small)
  1270. if( (delta & 1) && ( delta > 1 ) )
  1271. delta++;
  1272. delta /= 2;
  1273. D_PAD* pad = aPadList[idx];
  1274. if( pad->GetPosition() == aPosition ) // candidate found
  1275. {
  1276. // The pad must match the layer mask:
  1277. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1278. return pad;
  1279. // More than one pad can be at aPosition
  1280. // search for a pad at aPosition that matched this mask
  1281. // search next
  1282. for( int ii = idx+1; ii <= idxmax; ii++ )
  1283. {
  1284. pad = aPadList[ii];
  1285. if( pad->GetPosition() != aPosition )
  1286. break;
  1287. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1288. return pad;
  1289. }
  1290. // search previous
  1291. for( int ii = idx-1 ;ii >=0; ii-- )
  1292. {
  1293. pad = aPadList[ii];
  1294. if( pad->GetPosition() != aPosition )
  1295. break;
  1296. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1297. return pad;
  1298. }
  1299. // Not found:
  1300. return 0;
  1301. }
  1302. if( pad->GetPosition().x == aPosition.x ) // Must search considering Y coordinate
  1303. {
  1304. if( pad->GetPosition().y < aPosition.y ) // Must search after this item
  1305. {
  1306. idx += delta;
  1307. if( idx > idxmax )
  1308. idx = idxmax;
  1309. }
  1310. else // Must search before this item
  1311. {
  1312. idx -= delta;
  1313. if( idx < 0 )
  1314. idx = 0;
  1315. }
  1316. }
  1317. else if( pad->GetPosition().x < aPosition.x ) // Must search after this item
  1318. {
  1319. idx += delta;
  1320. if( idx > idxmax )
  1321. idx = idxmax;
  1322. }
  1323. else // Must search before this item
  1324. {
  1325. idx -= delta;
  1326. if( idx < 0 )
  1327. idx = 0;
  1328. }
  1329. }
  1330. return NULL;
  1331. }
  1332. /**
  1333. * Function SortPadsByXCoord
  1334. * is used by GetSortedPadListByXCoord to Sort a pad list by x coordinate value.
  1335. * This function is used to build ordered pads lists
  1336. */
  1337. bool sortPadsByXthenYCoord( D_PAD* const & ref, D_PAD* const & comp )
  1338. {
  1339. if( ref->GetPosition().x == comp->GetPosition().x )
  1340. return ref->GetPosition().y < comp->GetPosition().y;
  1341. return ref->GetPosition().x < comp->GetPosition().x;
  1342. }
  1343. void BOARD::GetSortedPadListByXthenYCoord( std::vector<D_PAD*>& aVector, int aNetCode )
  1344. {
  1345. for ( auto mod : Modules() )
  1346. {
  1347. for ( auto pad : mod->Pads( ) )
  1348. {
  1349. if( aNetCode < 0 || pad->GetNetCode() == aNetCode )
  1350. {
  1351. aVector.push_back( pad );
  1352. }
  1353. }
  1354. }
  1355. std::sort( aVector.begin(), aVector.end(), sortPadsByXthenYCoord );
  1356. }
  1357. void BOARD::PadDelete( D_PAD* aPad )
  1358. {
  1359. aPad->DeleteStructure();
  1360. }
  1361. TRACK* BOARD::GetVisibleTrack( TRACK* aStartingTrace, const wxPoint& aPosition,
  1362. LSET aLayerSet ) const
  1363. {
  1364. for( TRACK* track = aStartingTrace; track; track = track->Next() )
  1365. {
  1366. PCB_LAYER_ID layer = track->GetLayer();
  1367. if( track->GetState( BUSY | IS_DELETED ) )
  1368. continue;
  1369. // track's layer is not visible
  1370. if( m_designSettings.IsLayerVisible( layer ) == false )
  1371. continue;
  1372. if( track->Type() == PCB_VIA_T ) // VIA encountered.
  1373. {
  1374. if( track->HitTest( aPosition ) )
  1375. return track;
  1376. }
  1377. else
  1378. {
  1379. if( !aLayerSet[layer] )
  1380. continue; // track's layer is not in aLayerSet
  1381. if( track->HitTest( aPosition ) )
  1382. return track;
  1383. }
  1384. }
  1385. return NULL;
  1386. }
  1387. #if defined(DEBUG) && 0
  1388. static void dump_tracks( const char* aName, const TRACKS& aList )
  1389. {
  1390. printf( "%s: count=%zd\n", aName, aList.size() );
  1391. for( unsigned i = 0; i < aList.size(); ++i )
  1392. {
  1393. TRACK* seg = aList[i];
  1394. ::VIA* via = dynamic_cast< ::VIA* >( seg );
  1395. if( via )
  1396. printf( " via[%u]: (%d, %d)\n", i, via->GetStart().x, via->GetStart().y );
  1397. else
  1398. printf( " seg[%u]: (%d, %d) (%d, %d)\n", i,
  1399. seg->GetStart().x, seg->GetStart().y,
  1400. seg->GetEnd().x, seg->GetEnd().y );
  1401. }
  1402. }
  1403. #endif
  1404. TRACK* BOARD::MarkTrace( TRACK* aTrace, int* aCount,
  1405. double* aTraceLength, double* aPadToDieLength,
  1406. bool aReorder )
  1407. {
  1408. TRACKS trackList;
  1409. if( aCount )
  1410. *aCount = 0;
  1411. if( aTraceLength )
  1412. *aTraceLength = 0;
  1413. if( aTrace == NULL )
  1414. return NULL;
  1415. // Ensure the flag BUSY of all tracks of the board is cleared
  1416. // because we use it to mark segments of the track
  1417. for( TRACK* track = m_Track; track; track = track->Next() )
  1418. track->SetState( BUSY, false );
  1419. // Set flags of the initial track segment
  1420. aTrace->SetState( BUSY, true );
  1421. LSET layer_set = aTrace->GetLayerSet();
  1422. trackList.push_back( aTrace );
  1423. /* Examine the initial track segment : if it is really a segment, this is
  1424. * easy.
  1425. * If it is a via, one must search for connected segments.
  1426. * If <=2, this via connect 2 segments (or is connected to only one
  1427. * segment) and this via and these 2 segments are a part of a track.
  1428. * If > 2 only this via is flagged (the track has only this via)
  1429. */
  1430. if( aTrace->Type() == PCB_VIA_T )
  1431. {
  1432. TRACK* segm1 = ::GetTrack( m_Track, NULL, aTrace->GetStart(), layer_set );
  1433. TRACK* segm2 = NULL;
  1434. TRACK* segm3 = NULL;
  1435. if( segm1 )
  1436. {
  1437. segm2 = ::GetTrack( segm1->Next(), NULL, aTrace->GetStart(), layer_set );
  1438. }
  1439. if( segm2 )
  1440. {
  1441. segm3 = ::GetTrack( segm2->Next(), NULL, aTrace->GetStart(), layer_set );
  1442. }
  1443. if( segm3 )
  1444. {
  1445. // More than 2 segments are connected to this via.
  1446. // The "track" is only this via.
  1447. if( aCount )
  1448. *aCount = 1;
  1449. return aTrace;
  1450. }
  1451. if( segm1 ) // search for other segments connected to the initial segment start point
  1452. {
  1453. layer_set = segm1->GetLayerSet();
  1454. chainMarkedSegments( aTrace->GetStart(), layer_set, &trackList );
  1455. }
  1456. if( segm2 ) // search for other segments connected to the initial segment end point
  1457. {
  1458. layer_set = segm2->GetLayerSet();
  1459. chainMarkedSegments( aTrace->GetStart(), layer_set, &trackList );
  1460. }
  1461. }
  1462. else // mark the chain using both ends of the initial segment
  1463. {
  1464. TRACKS from_start;
  1465. TRACKS from_end;
  1466. chainMarkedSegments( aTrace->GetStart(), layer_set, &from_start );
  1467. chainMarkedSegments( aTrace->GetEnd(), layer_set, &from_end );
  1468. // DBG( dump_tracks( "first_clicked", trackList ); )
  1469. // DBG( dump_tracks( "from_start", from_start ); )
  1470. // DBG( dump_tracks( "from_end", from_end ); )
  1471. // combine into one trackList:
  1472. trackList.insert( trackList.end(), from_start.begin(), from_start.end() );
  1473. trackList.insert( trackList.end(), from_end.begin(), from_end.end() );
  1474. }
  1475. // Now examine selected vias and flag them if they are on the track
  1476. // If a via is connected to only one or 2 segments, it is flagged (is on the track)
  1477. // If a via is connected to more than 2 segments, it is a track end, and it
  1478. // is removed from the list.
  1479. // Go through the list backwards.
  1480. for( int i = trackList.size() - 1; i>=0; --i )
  1481. {
  1482. ::VIA* via = dynamic_cast< ::VIA* >( trackList[i] );
  1483. if( !via )
  1484. continue;
  1485. if( via == aTrace )
  1486. continue;
  1487. via->SetState( BUSY, true ); // Try to flag it. the flag will be cleared later if needed
  1488. layer_set = via->GetLayerSet();
  1489. TRACK* track = ::GetTrack( m_Track, NULL, via->GetStart(), layer_set );
  1490. // GetTrace does not consider tracks flagged BUSY.
  1491. // So if no connected track found, this via is on the current track
  1492. // only: keep it
  1493. if( track == NULL )
  1494. continue;
  1495. /* If a track is found, this via connects also other segments of
  1496. * another track. This case happens when a via ends the selected
  1497. * track but must we consider this via is on the selected track, or
  1498. * on another track.
  1499. * (this is important when selecting a track for deletion: must this
  1500. * via be deleted or not?)
  1501. * We consider this via to be on our track if other segments connected
  1502. * to this via remain connected when removing this via.
  1503. * We search for all other segments connected together:
  1504. * if they are on the same layer, then the via is on the selected track;
  1505. * if they are on different layers, the via is on another track.
  1506. */
  1507. LAYER_NUM layer = track->GetLayer();
  1508. while( ( track = ::GetTrack( track->Next(), NULL, via->GetStart(), layer_set ) ) != NULL )
  1509. {
  1510. if( layer != track->GetLayer() )
  1511. {
  1512. // The via connects segments of another track: it is removed
  1513. // from list because it is member of another track
  1514. DBG(printf( "%s: omit track (%d, %d) (%d, %d) on layer:%d (!= our_layer:%d)\n",
  1515. __func__,
  1516. track->GetStart().x, track->GetStart().y,
  1517. track->GetEnd().x, track->GetEnd().y,
  1518. track->GetLayer(), layer
  1519. ); )
  1520. via->SetState( BUSY, false );
  1521. break;
  1522. }
  1523. }
  1524. }
  1525. /* Rearrange the track list in order to have flagged segments linked
  1526. * from firstTrack so the NbSegmBusy segments are consecutive segments
  1527. * in list, the first item in the full track list is firstTrack, and
  1528. * the NbSegmBusy-1 next items (NbSegmBusy when including firstTrack)
  1529. * are the flagged segments
  1530. */
  1531. int busy_count = 0;
  1532. TRACK* firstTrack;
  1533. for( firstTrack = m_Track; firstTrack; firstTrack = firstTrack->Next() )
  1534. {
  1535. // Search for the first flagged BUSY segments
  1536. if( firstTrack->GetState( BUSY ) )
  1537. {
  1538. busy_count = 1;
  1539. break;
  1540. }
  1541. }
  1542. if( firstTrack == NULL )
  1543. return NULL;
  1544. // First step: calculate the track length and find the pads (when exist)
  1545. // at each end of the trace.
  1546. double full_len = 0;
  1547. double lenPadToDie = 0;
  1548. // Because we have a track (a set of track segments between 2 nodes),
  1549. // only 2 pads (maximum) will be taken in account:
  1550. // that are on each end of the track, if any.
  1551. // keep trace of them, to know the die length and the track length ibside each pad.
  1552. D_PAD* s_pad = NULL; // the pad on one end of the trace
  1553. D_PAD* e_pad = NULL; // the pad on the other end of the trace
  1554. int dist_fromstart = INT_MAX;
  1555. int dist_fromend = INT_MAX;
  1556. for( TRACK* track = firstTrack; track; track = track->Next() )
  1557. {
  1558. if( !track->GetState( BUSY ) )
  1559. continue;
  1560. layer_set = track->GetLayerSet();
  1561. D_PAD * pad_on_start = GetPad( track->GetStart(), layer_set );
  1562. D_PAD * pad_on_end = GetPad( track->GetEnd(), layer_set );
  1563. // a segment fully inside a pad does not contribute to the track len
  1564. // (an other track end inside this pad will contribute to this lenght)
  1565. if( pad_on_start && ( pad_on_start == pad_on_end ) )
  1566. continue;
  1567. full_len += track->GetLength();
  1568. if( pad_on_start == NULL && pad_on_end == NULL )
  1569. // This most of time the case
  1570. continue;
  1571. // At this point, we can have one track end on a pad, or the 2 track ends on
  1572. // 2 different pads.
  1573. // We don't know what pad (s_pad or e_pad) must be used to store the
  1574. // start point and the end point of the track, so if a pad is already set,
  1575. // use the other
  1576. if( pad_on_start )
  1577. {
  1578. SEG segm( track->GetStart(), pad_on_start->GetPosition() );
  1579. int dist = segm.Length();
  1580. if( s_pad == NULL )
  1581. {
  1582. dist_fromstart = dist;
  1583. s_pad = pad_on_start;
  1584. }
  1585. else if( e_pad == NULL )
  1586. {
  1587. dist_fromend = dist;
  1588. e_pad = pad_on_start;
  1589. }
  1590. else // Should not occur, at least for basic pads
  1591. {
  1592. // wxLogMessage( "BOARD::MarkTrace: multiple pad_on_start" );
  1593. }
  1594. }
  1595. if( pad_on_end )
  1596. {
  1597. SEG segm( track->GetEnd(), pad_on_end->GetPosition() );
  1598. int dist = segm.Length();
  1599. if( s_pad == NULL )
  1600. {
  1601. dist_fromstart = dist;
  1602. s_pad = pad_on_end;
  1603. }
  1604. else if( e_pad == NULL )
  1605. {
  1606. dist_fromend = dist;
  1607. e_pad = pad_on_end;
  1608. }
  1609. else // Should not occur, at least for basic pads
  1610. {
  1611. // wxLogMessage( "BOARD::MarkTrace: multiple pad_on_end" );
  1612. }
  1613. }
  1614. }
  1615. if( aReorder )
  1616. {
  1617. DLIST<TRACK>* list = (DLIST<TRACK>*)firstTrack->GetList();
  1618. wxASSERT( list );
  1619. /* Rearrange the chain starting at firstTrack
  1620. * All other BUSY flagged items are moved from their position to the end
  1621. * of the flagged list
  1622. */
  1623. TRACK* next;
  1624. for( TRACK* track = firstTrack->Next(); track; track = next )
  1625. {
  1626. next = track->Next();
  1627. if( track->GetState( BUSY ) ) // move it!
  1628. {
  1629. busy_count++;
  1630. track->UnLink();
  1631. list->Insert( track, firstTrack->Next() );
  1632. }
  1633. }
  1634. }
  1635. else if( aTraceLength )
  1636. {
  1637. busy_count = 0;
  1638. for( TRACK* track = firstTrack; track; track = track->Next() )
  1639. {
  1640. if( track->GetState( BUSY ) )
  1641. {
  1642. busy_count++;
  1643. track->SetState( BUSY, false );
  1644. }
  1645. }
  1646. DBG( printf( "%s: busy_count:%d\n", __func__, busy_count ); )
  1647. }
  1648. if( s_pad )
  1649. {
  1650. full_len += dist_fromstart;
  1651. lenPadToDie += (double) s_pad->GetPadToDieLength();
  1652. }
  1653. if( e_pad )
  1654. {
  1655. full_len += dist_fromend;
  1656. lenPadToDie += (double) e_pad->GetPadToDieLength();
  1657. }
  1658. if( aTraceLength )
  1659. *aTraceLength = full_len;
  1660. if( aPadToDieLength )
  1661. *aPadToDieLength = lenPadToDie;
  1662. if( aCount )
  1663. *aCount = busy_count;
  1664. return firstTrack;
  1665. }
  1666. MODULE* BOARD::GetFootprint( const wxPoint& aPosition, PCB_LAYER_ID aActiveLayer,
  1667. bool aVisibleOnly, bool aIgnoreLocked )
  1668. {
  1669. MODULE* pt_module;
  1670. MODULE* module = NULL;
  1671. MODULE* alt_module = NULL;
  1672. int min_dim = 0x7FFFFFFF;
  1673. int alt_min_dim = 0x7FFFFFFF;
  1674. bool current_layer_back = IsBackLayer( aActiveLayer );
  1675. for( pt_module = m_Modules; pt_module; pt_module = pt_module->Next() )
  1676. {
  1677. // is the ref point within the module's bounds?
  1678. if( !pt_module->HitTest( aPosition ) )
  1679. continue;
  1680. // if caller wants to ignore locked modules, and this one is locked, skip it.
  1681. if( aIgnoreLocked && pt_module->IsLocked() )
  1682. continue;
  1683. PCB_LAYER_ID layer = pt_module->GetLayer();
  1684. // Filter non visible modules if requested
  1685. if( !aVisibleOnly || IsModuleLayerVisible( layer ) )
  1686. {
  1687. EDA_RECT bb = pt_module->GetFootprintRect();
  1688. int offx = bb.GetX() + bb.GetWidth() / 2;
  1689. int offy = bb.GetY() + bb.GetHeight() / 2;
  1690. // off x & offy point to the middle of the box.
  1691. int dist = ( aPosition.x - offx ) * ( aPosition.x - offx ) +
  1692. ( aPosition.y - offy ) * ( aPosition.y - offy );
  1693. if( current_layer_back == IsBackLayer( layer ) )
  1694. {
  1695. if( dist <= min_dim )
  1696. {
  1697. // better footprint shown on the active side
  1698. module = pt_module;
  1699. min_dim = dist;
  1700. }
  1701. }
  1702. else if( aVisibleOnly && IsModuleLayerVisible( layer ) )
  1703. {
  1704. if( dist <= alt_min_dim )
  1705. {
  1706. // better footprint shown on the other side
  1707. alt_module = pt_module;
  1708. alt_min_dim = dist;
  1709. }
  1710. }
  1711. }
  1712. }
  1713. if( module )
  1714. {
  1715. return module;
  1716. }
  1717. if( alt_module)
  1718. {
  1719. return alt_module;
  1720. }
  1721. return NULL;
  1722. }
  1723. BOARD_CONNECTED_ITEM* BOARD::GetLockPoint( const wxPoint& aPosition, LSET aLayerSet )
  1724. {
  1725. for( MODULE* module = m_Modules; module; module = module->Next() )
  1726. {
  1727. D_PAD* pad = module->GetPad( aPosition, aLayerSet );
  1728. if( pad )
  1729. return pad;
  1730. }
  1731. // No pad has been located so check for a segment of the trace.
  1732. TRACK* segment = ::GetTrack( m_Track, NULL, aPosition, aLayerSet );
  1733. if( !segment )
  1734. segment = GetVisibleTrack( m_Track, aPosition, aLayerSet );
  1735. return segment;
  1736. }
  1737. TRACK* BOARD::CreateLockPoint( wxPoint& aPosition, TRACK* aSegment, PICKED_ITEMS_LIST* aList )
  1738. {
  1739. /* creates an intermediate point on aSegment and break it into two segments
  1740. * at aPosition.
  1741. * The new segment starts from aPosition and ends at the end point of
  1742. * aSegment. The original segment now ends at aPosition.
  1743. */
  1744. if( aSegment->GetStart() == aPosition || aSegment->GetEnd() == aPosition )
  1745. return NULL;
  1746. // A via is a good lock point
  1747. if( aSegment->Type() == PCB_VIA_T )
  1748. {
  1749. aPosition = aSegment->GetStart();
  1750. return aSegment;
  1751. }
  1752. // Calculation coordinate of intermediate point relative to the start point of aSegment
  1753. wxPoint delta = aSegment->GetEnd() - aSegment->GetStart();
  1754. // calculate coordinates of aPosition relative to aSegment->GetStart()
  1755. wxPoint lockPoint = aPosition - aSegment->GetStart();
  1756. // lockPoint must be on aSegment:
  1757. // Ensure lockPoint.y/lockPoint.y = delta.y/delta.x
  1758. if( delta.x == 0 )
  1759. lockPoint.x = 0; // horizontal segment
  1760. else
  1761. lockPoint.y = KiROUND( ( (double)lockPoint.x * delta.y ) / delta.x );
  1762. /* Create the intermediate point (that is to say creation of a new
  1763. * segment, beginning at the intermediate point.
  1764. */
  1765. lockPoint += aSegment->GetStart();
  1766. TRACK* newTrack = (TRACK*)aSegment->Clone();
  1767. // The new segment begins at the new point,
  1768. newTrack->SetStart(lockPoint);
  1769. newTrack->start = aSegment;
  1770. newTrack->SetState( BEGIN_ONPAD, false );
  1771. DLIST<TRACK>* list = (DLIST<TRACK>*)aSegment->GetList();
  1772. wxASSERT( list );
  1773. list->Insert( newTrack, aSegment->Next() );
  1774. if( aList )
  1775. {
  1776. // Prepare the undo command for the now track segment
  1777. ITEM_PICKER picker( newTrack, UR_NEW );
  1778. aList->PushItem( picker );
  1779. // Prepare the undo command for the old track segment
  1780. // before modifications
  1781. picker.SetItem( aSegment );
  1782. picker.SetStatus( UR_CHANGED );
  1783. picker.SetLink( aSegment->Clone() );
  1784. aList->PushItem( picker );
  1785. }
  1786. // Old track segment now ends at new point.
  1787. aSegment->SetEnd(lockPoint);
  1788. aSegment->end = newTrack;
  1789. aSegment->SetState( END_ONPAD, false );
  1790. D_PAD * pad = GetPad( newTrack, ENDPOINT_START );
  1791. if( pad )
  1792. {
  1793. newTrack->start = pad;
  1794. newTrack->SetState( BEGIN_ONPAD, true );
  1795. aSegment->end = pad;
  1796. aSegment->SetState( END_ONPAD, true );
  1797. }
  1798. aPosition = lockPoint;
  1799. return newTrack;
  1800. }
  1801. ZONE_CONTAINER* BOARD::AddArea( PICKED_ITEMS_LIST* aNewZonesList, int aNetcode,
  1802. PCB_LAYER_ID aLayer, wxPoint aStartPointPosition, int aHatch )
  1803. {
  1804. ZONE_CONTAINER* new_area = InsertArea( aNetcode,
  1805. m_ZoneDescriptorList.size( ) - 1,
  1806. aLayer, aStartPointPosition.x,
  1807. aStartPointPosition.y, aHatch );
  1808. if( aNewZonesList )
  1809. {
  1810. ITEM_PICKER picker( new_area, UR_NEW );
  1811. aNewZonesList->PushItem( picker );
  1812. }
  1813. return new_area;
  1814. }
  1815. void BOARD::RemoveArea( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_to_remove )
  1816. {
  1817. if( area_to_remove == NULL )
  1818. return;
  1819. if( aDeletedList )
  1820. {
  1821. ITEM_PICKER picker( area_to_remove, UR_DELETED );
  1822. aDeletedList->PushItem( picker );
  1823. Remove( area_to_remove ); // remove from zone list, but does not delete it
  1824. }
  1825. else
  1826. {
  1827. Delete( area_to_remove );
  1828. }
  1829. }
  1830. ZONE_CONTAINER* BOARD::InsertArea( int aNetcode, int aAreaIdx, PCB_LAYER_ID aLayer,
  1831. int aCornerX, int aCornerY, int aHatch )
  1832. {
  1833. ZONE_CONTAINER* new_area = new ZONE_CONTAINER( this );
  1834. new_area->SetNetCode( aNetcode );
  1835. new_area->SetLayer( aLayer );
  1836. new_area->SetTimeStamp( GetNewTimeStamp() );
  1837. if( aAreaIdx < (int) ( m_ZoneDescriptorList.size() - 1 ) )
  1838. m_ZoneDescriptorList.insert( m_ZoneDescriptorList.begin() + aAreaIdx + 1, new_area );
  1839. else
  1840. m_ZoneDescriptorList.push_back( new_area );
  1841. new_area->SetHatchStyle( (ZONE_CONTAINER::HATCH_STYLE) aHatch );
  1842. // Add the first corner to the new zone
  1843. new_area->AppendCorner( wxPoint( aCornerX, aCornerY ), -1 );
  1844. return new_area;
  1845. }
  1846. bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAINER* aCurrArea )
  1847. {
  1848. // mark all areas as unmodified except this one, if modified
  1849. for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ )
  1850. m_ZoneDescriptorList[ia]->SetLocalFlags( 0 );
  1851. aCurrArea->SetLocalFlags( 1 );
  1852. if( aCurrArea->Outline()->IsSelfIntersecting() )
  1853. {
  1854. aCurrArea->UnHatch();
  1855. // Normalize copied area and store resulting number of polygons
  1856. int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines();
  1857. // If clipping has created some polygons, we must add these new copper areas.
  1858. if( n_poly > 1 )
  1859. {
  1860. ZONE_CONTAINER* NewArea;
  1861. // Move the newly created polygons to new areas, removing them from the current area
  1862. for( int ip = 1; ip < n_poly; ip++ )
  1863. {
  1864. // Create new copper area and copy poly into it
  1865. SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( ip ) );
  1866. NewArea = AddArea( aNewZonesList, aCurrArea->GetNetCode(), aCurrArea->GetLayer(),
  1867. wxPoint(0, 0), aCurrArea->GetHatchStyle() );
  1868. // remove the poly that was automatically created for the new area
  1869. // and replace it with a poly from NormalizeAreaOutlines
  1870. delete NewArea->Outline();
  1871. NewArea->SetOutline( new_p );
  1872. NewArea->Hatch();
  1873. NewArea->SetLocalFlags( 1 );
  1874. }
  1875. SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( 0 ) );
  1876. delete aCurrArea->Outline();
  1877. aCurrArea->SetOutline( new_p );
  1878. }
  1879. }
  1880. aCurrArea->Hatch();
  1881. return true;
  1882. }
  1883. void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets,
  1884. std::vector<MODULE*>* aNewFootprints, REPORTER* aReporter )
  1885. {
  1886. unsigned i;
  1887. wxPoint bestPosition;
  1888. wxString msg;
  1889. std::vector<MODULE*> newFootprints;
  1890. if( !IsEmpty() )
  1891. {
  1892. // Position new components below any existing board features.
  1893. EDA_RECT bbbox = GetBoardEdgesBoundingBox();
  1894. if( bbbox.GetWidth() || bbbox.GetHeight() )
  1895. {
  1896. bestPosition.x = bbbox.Centre().x;
  1897. bestPosition.y = bbbox.GetBottom() + Millimeter2iu( 10 );
  1898. }
  1899. }
  1900. else
  1901. {
  1902. // Position new components in the center of the page when the board is empty.
  1903. wxSize pageSize = m_paper.GetSizeIU();
  1904. bestPosition.x = pageSize.GetWidth() / 2;
  1905. bestPosition.y = pageSize.GetHeight() / 2;
  1906. }
  1907. m_Status_Pcb = 0;
  1908. for( i = 0; i < aNetlist.GetCount(); i++ )
  1909. {
  1910. COMPONENT* component = aNetlist.GetComponent( i );
  1911. MODULE* footprint;
  1912. if( aReporter )
  1913. {
  1914. msg.Printf( _( "Checking netlist component footprint \"%s:%s:%s\".\n" ),
  1915. GetChars( component->GetReference() ),
  1916. GetChars( component->GetTimeStamp() ),
  1917. GetChars( component->GetFPID().Format() ) );
  1918. aReporter->Report( msg, REPORTER::RPT_INFO );
  1919. }
  1920. if( aNetlist.IsFindByTimeStamp() )
  1921. footprint = FindModule( aNetlist.GetComponent( i )->GetTimeStamp(), true );
  1922. else
  1923. footprint = FindModule( aNetlist.GetComponent( i )->GetReference() );
  1924. if( footprint == NULL ) // A new footprint.
  1925. {
  1926. if( aReporter )
  1927. {
  1928. if( component->GetModule() != NULL )
  1929. {
  1930. msg.Printf( _( "Adding new component \"%s:%s\" footprint \"%s\".\n" ),
  1931. GetChars( component->GetReference() ),
  1932. GetChars( component->GetTimeStamp() ),
  1933. GetChars( component->GetFPID().Format() ) );
  1934. aReporter->Report( msg, REPORTER::RPT_ACTION );
  1935. }
  1936. else
  1937. {
  1938. msg.Printf( _( "Cannot add new component \"%s:%s\" due to missing "
  1939. "footprint \"%s\".\n" ),
  1940. GetChars( component->GetReference() ),
  1941. GetChars( component->GetTimeStamp() ),
  1942. GetChars( component->GetFPID().Format() ) );
  1943. aReporter->Report( msg, REPORTER::RPT_ERROR );
  1944. }
  1945. }
  1946. if( !aNetlist.IsDryRun() && (component->GetModule() != NULL) )
  1947. {
  1948. // Owned by NETLIST, can only copy it.
  1949. footprint = new MODULE( *component->GetModule() );
  1950. footprint->SetParent( this );
  1951. footprint->SetPosition( bestPosition );
  1952. footprint->SetTimeStamp( GetNewTimeStamp() );
  1953. newFootprints.push_back( footprint );
  1954. Add( footprint, ADD_APPEND );
  1955. m_connectivity->Add( footprint );
  1956. }
  1957. }
  1958. else // An existing footprint.
  1959. {
  1960. // Test for footprint change.
  1961. if( !component->GetFPID().empty() &&
  1962. footprint->GetFPID() != component->GetFPID() )
  1963. {
  1964. if( aNetlist.GetReplaceFootprints() )
  1965. {
  1966. if( aReporter )
  1967. {
  1968. if( component->GetModule() != NULL )
  1969. {
  1970. msg.Printf( _( "Replacing component \"%s:%s\" footprint \"%s\" with "
  1971. "\"%s\".\n" ),
  1972. GetChars( footprint->GetReference() ),
  1973. GetChars( footprint->GetPath() ),
  1974. GetChars( footprint->GetFPID().Format() ),
  1975. GetChars( component->GetFPID().Format() ) );
  1976. aReporter->Report( msg, REPORTER::RPT_ACTION );
  1977. }
  1978. else
  1979. {
  1980. msg.Printf( _( "Cannot replace component \"%s:%s\" due to missing "
  1981. "footprint \"%s\".\n" ),
  1982. GetChars( footprint->GetReference() ),
  1983. GetChars( footprint->GetPath() ),
  1984. GetChars( component->GetFPID().Format() ) );
  1985. aReporter->Report( msg, REPORTER::RPT_ERROR );
  1986. }
  1987. }
  1988. if( !aNetlist.IsDryRun() && (component->GetModule() != NULL) )
  1989. {
  1990. wxASSERT( footprint != NULL );
  1991. MODULE* newFootprint = new MODULE( *component->GetModule() );
  1992. if( aNetlist.IsFindByTimeStamp() )
  1993. newFootprint->SetReference( footprint->GetReference() );
  1994. else
  1995. newFootprint->SetPath( footprint->GetPath() );
  1996. // Copy placement and pad net names.
  1997. // optionally, copy or not local settings (like local clearances)
  1998. // if the second parameter is "true", previous values will be used.
  1999. // if "false", the default library values of the new footprint
  2000. // will be used
  2001. footprint->CopyNetlistSettings( newFootprint, false );
  2002. // Compare the footprint name only, in case the nickname is empty or in case
  2003. // user moved the footprint to a new library. Chances are if footprint name is
  2004. // same then the footprint is very nearly the same and the two texts should
  2005. // be kept at same size, position, and rotation.
  2006. if( newFootprint->GetFPID().GetLibItemName() == footprint->GetFPID().GetLibItemName() )
  2007. {
  2008. newFootprint->Reference().SetEffects( footprint->Reference() );
  2009. newFootprint->Value().SetEffects( footprint->Value() );
  2010. }
  2011. m_connectivity->Remove( footprint );
  2012. Remove( footprint );
  2013. Add( newFootprint, ADD_APPEND );
  2014. m_connectivity->Add( footprint );
  2015. footprint = newFootprint;
  2016. }
  2017. }
  2018. }
  2019. // Test for reference designator field change.
  2020. if( footprint->GetReference() != component->GetReference() )
  2021. {
  2022. if( aReporter )
  2023. {
  2024. msg.Printf( _( "Changing component \"%s:%s\" reference to \"%s\".\n" ),
  2025. GetChars( footprint->GetReference() ),
  2026. GetChars( footprint->GetPath() ),
  2027. GetChars( component->GetReference() ) );
  2028. aReporter->Report( msg, REPORTER::RPT_ACTION );
  2029. }
  2030. if( !aNetlist.IsDryRun() )
  2031. footprint->SetReference( component->GetReference() );
  2032. }
  2033. // Test for value field change.
  2034. if( footprint->GetValue() != component->GetValue() )
  2035. {
  2036. if( aReporter )
  2037. {
  2038. msg.Printf( _( "Changing component \"%s:%s\" value from \"%s\" to \"%s\".\n" ),
  2039. GetChars( footprint->GetReference() ),
  2040. GetChars( footprint->GetPath() ),
  2041. GetChars( footprint->GetValue() ),
  2042. GetChars( component->GetValue() ) );
  2043. aReporter->Report( msg, REPORTER::RPT_ACTION );
  2044. }
  2045. if( !aNetlist.IsDryRun() )
  2046. footprint->SetValue( component->GetValue() );
  2047. }
  2048. // Test for time stamp change.
  2049. if( footprint->GetPath() != component->GetTimeStamp() )
  2050. {
  2051. if( aReporter )
  2052. {
  2053. msg.Printf( _( "Changing component path \"%s:%s\" to \"%s\".\n" ),
  2054. GetChars( footprint->GetReference() ),
  2055. GetChars( footprint->GetPath() ),
  2056. GetChars( component->GetTimeStamp() ) );
  2057. aReporter->Report( msg, REPORTER::RPT_INFO );
  2058. }
  2059. if( !aNetlist.IsDryRun() )
  2060. footprint->SetPath( component->GetTimeStamp() );
  2061. }
  2062. }
  2063. if( footprint == NULL )
  2064. continue;
  2065. // At this point, the component footprint is updated. Now update the nets.
  2066. for( auto pad : footprint->Pads() )
  2067. {
  2068. COMPONENT_NET net = component->GetNet( pad->GetName() );
  2069. if( !net.IsValid() ) // Footprint pad had no net.
  2070. {
  2071. if( aReporter && !pad->GetNetname().IsEmpty() )
  2072. {
  2073. msg.Printf( _( "Clearing component \"%s:%s\" pin \"%s\" net name.\n" ),
  2074. GetChars( footprint->GetReference() ),
  2075. GetChars( footprint->GetPath() ),
  2076. GetChars( pad->GetName() ) );
  2077. aReporter->Report( msg, REPORTER::RPT_ACTION );
  2078. }
  2079. if( !aNetlist.IsDryRun() )
  2080. {
  2081. m_connectivity->Remove( pad );
  2082. pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
  2083. }
  2084. }
  2085. else // Footprint pad has a net.
  2086. {
  2087. if( net.GetNetName() != pad->GetNetname() )
  2088. {
  2089. if( aReporter )
  2090. {
  2091. msg.Printf( _( "Changing component \"%s:%s\" pin \"%s\" net name from "
  2092. "\"%s\" to \"%s\".\n" ),
  2093. GetChars( footprint->GetReference() ),
  2094. GetChars( footprint->GetPath() ),
  2095. GetChars( pad->GetName() ),
  2096. GetChars( pad->GetNetname() ),
  2097. GetChars( net.GetNetName() ) );
  2098. aReporter->Report( msg, REPORTER::RPT_ACTION );
  2099. }
  2100. if( !aNetlist.IsDryRun() )
  2101. {
  2102. NETINFO_ITEM* netinfo = FindNet( net.GetNetName() );
  2103. if( netinfo == NULL )
  2104. {
  2105. // It is a new net, we have to add it
  2106. netinfo = new NETINFO_ITEM( this, net.GetNetName() );
  2107. Add( netinfo );
  2108. }
  2109. m_connectivity->Remove( pad );
  2110. pad->SetNetCode( netinfo->GetNet() );
  2111. m_connectivity->Add( pad );
  2112. }
  2113. }
  2114. }
  2115. }
  2116. }
  2117. // Remove all components not in the netlist.
  2118. if( aNetlist.GetDeleteExtraFootprints() )
  2119. {
  2120. MODULE* nextModule;
  2121. const COMPONENT* component;
  2122. for( MODULE* module = m_Modules; module != NULL; module = nextModule )
  2123. {
  2124. nextModule = module->Next();
  2125. if( module->IsLocked() )
  2126. continue;
  2127. if( aNetlist.IsFindByTimeStamp() )
  2128. component = aNetlist.GetComponentByTimeStamp( module->GetPath() );
  2129. else
  2130. component = aNetlist.GetComponentByReference( module->GetReference() );
  2131. if( component == NULL )
  2132. {
  2133. if( aReporter )
  2134. {
  2135. msg.Printf( _( "Removing unused component \"%s:%s\".\n" ),
  2136. GetChars( module->GetReference() ),
  2137. GetChars( module->GetPath() ) );
  2138. aReporter->Report( msg, REPORTER::RPT_ACTION );
  2139. }
  2140. if( !aNetlist.IsDryRun() )
  2141. {
  2142. m_connectivity->Remove( module );
  2143. module->DeleteStructure();
  2144. }
  2145. }
  2146. }
  2147. }
  2148. BuildListOfNets();
  2149. std::vector<D_PAD*> padlist = GetPads();
  2150. auto connAlgo = m_connectivity->GetConnectivityAlgo();
  2151. // If needed, remove the single pad nets:
  2152. if( aDeleteSinglePadNets && !aNetlist.IsDryRun() )
  2153. {
  2154. std::vector<unsigned int> padCount( connAlgo->NetCount() );
  2155. for( const auto cnItem : connAlgo->PadList() )
  2156. {
  2157. int net = cnItem->Parent()->GetNetCode();
  2158. if( net > 0 )
  2159. ++padCount[net];
  2160. }
  2161. for( i = 0; i < (unsigned)connAlgo->NetCount(); ++i )
  2162. {
  2163. // First condition: only one pad in the net
  2164. if( padCount[i] == 1 )
  2165. {
  2166. // Second condition, no zones attached to the pad
  2167. D_PAD* pad = nullptr;
  2168. int zoneCount = 0;
  2169. const KICAD_T types[] = { PCB_PAD_T, PCB_ZONE_AREA_T, EOT };
  2170. auto netItems = m_connectivity->GetNetItems( i, types );
  2171. for( const auto item : netItems )
  2172. {
  2173. if( item->Type() == PCB_ZONE_AREA_T )
  2174. {
  2175. wxASSERT( !pad || pad->GetNet() == item->GetNet() );
  2176. ++zoneCount;
  2177. }
  2178. else if( item->Type() == PCB_PAD_T )
  2179. {
  2180. wxASSERT( !pad );
  2181. pad = static_cast<D_PAD*>( item );
  2182. }
  2183. }
  2184. wxASSERT( pad ); // pad = 0 means the pad list is not up to date
  2185. if( pad && zoneCount == 0 )
  2186. {
  2187. if( aReporter )
  2188. {
  2189. msg.Printf( _( "Remove single pad net \"%s\" on \"%s\" pad '%s'\n" ),
  2190. GetChars( pad->GetNetname() ),
  2191. GetChars( pad->GetParent()->GetReference() ),
  2192. GetChars( pad->GetName() ) );
  2193. aReporter->Report( msg, REPORTER::RPT_ACTION );
  2194. }
  2195. m_connectivity->Remove( pad );
  2196. pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
  2197. }
  2198. }
  2199. }
  2200. }
  2201. // Last step: Some tests:
  2202. // verify all pads found in netlist:
  2203. // They should exist in footprints, otherwise the footprint is wrong
  2204. // note also references or time stamps are updated, so we use only
  2205. // the reference to find a footprint
  2206. //
  2207. // Also verify if zones have acceptable nets, i.e. nets with pads.
  2208. // Zone with no pad belongs to a "dead" net which happens after changes in schematic
  2209. // when no more pad use this net name.
  2210. if( aReporter )
  2211. {
  2212. wxString padname;
  2213. for( i = 0; i < aNetlist.GetCount(); i++ )
  2214. {
  2215. const COMPONENT* component = aNetlist.GetComponent( i );
  2216. MODULE* footprint = FindModuleByReference( component->GetReference() );
  2217. if( footprint == NULL ) // It can be missing in partial designs
  2218. continue;
  2219. // Explore all pins/pads in component
  2220. for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
  2221. {
  2222. COMPONENT_NET net = component->GetNet( jj );
  2223. padname = net.GetPinName();
  2224. if( footprint->FindPadByName( padname ) )
  2225. continue; // OK, pad found
  2226. // not found: bad footprint, report error
  2227. msg.Printf( _( "Component '%s' pad '%s' not found in footprint '%s'\n" ),
  2228. GetChars( component->GetReference() ),
  2229. GetChars( padname ),
  2230. GetChars( footprint->GetFPID().Format() ) );
  2231. aReporter->Report( msg, REPORTER::RPT_ERROR );
  2232. }
  2233. }
  2234. // Test copper zones to detect "dead" nets (nets without any pad):
  2235. for( int ii = 0; ii < GetAreaCount(); ii++ )
  2236. {
  2237. ZONE_CONTAINER* zone = GetArea( ii );
  2238. if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() )
  2239. continue;
  2240. if( m_connectivity->GetPadCount( zone->GetNetCode() ) == 0 )
  2241. {
  2242. msg.Printf( _( "Copper zone (net name '%s'): net has no pads connected." ),
  2243. GetChars( zone->GetNet()->GetNetname() ) );
  2244. aReporter->Report( msg, REPORTER::RPT_WARNING );
  2245. }
  2246. }
  2247. }
  2248. m_connectivity->RecalculateRatsnest();
  2249. std::swap( newFootprints, *aNewFootprints );
  2250. }
  2251. BOARD_ITEM* BOARD::Duplicate( const BOARD_ITEM* aItem,
  2252. bool aAddToBoard )
  2253. {
  2254. BOARD_ITEM* new_item = NULL;
  2255. switch( aItem->Type() )
  2256. {
  2257. case PCB_MODULE_T:
  2258. case PCB_TEXT_T:
  2259. case PCB_LINE_T:
  2260. case PCB_TRACE_T:
  2261. case PCB_VIA_T:
  2262. case PCB_ZONE_AREA_T:
  2263. case PCB_TARGET_T:
  2264. case PCB_DIMENSION_T:
  2265. new_item = static_cast<BOARD_ITEM*>( aItem->Clone() );
  2266. break;
  2267. default:
  2268. // Un-handled item for duplication
  2269. new_item = NULL;
  2270. break;
  2271. }
  2272. if( new_item && aAddToBoard )
  2273. Add( new_item );
  2274. return new_item;
  2275. }
  2276. /* Extracts the board outlines and build a closed polygon
  2277. * from lines, arcs and circle items on edge cut layer
  2278. * Any closed outline inside the main outline is a hole
  2279. * All contours should be closed, i.e. are valid vertices for a closed polygon
  2280. * return true if success, false if a contour is not valid
  2281. */
  2282. extern bool BuildBoardPolygonOutlines( BOARD* aBoard,
  2283. SHAPE_POLY_SET& aOutlines,
  2284. wxString* aErrorText );
  2285. bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
  2286. wxString* aErrorText )
  2287. {
  2288. bool success = BuildBoardPolygonOutlines( this, aOutlines, aErrorText );
  2289. // Make polygon strictly simple to avoid issues (especially in 3D viewer)
  2290. aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  2291. return success;
  2292. }
  2293. const std::vector<D_PAD*> BOARD::GetPads()
  2294. {
  2295. std::vector<D_PAD*> rv;
  2296. for ( auto mod: Modules() )
  2297. {
  2298. for ( auto pad: mod->Pads() )
  2299. rv.push_back ( pad );
  2300. }
  2301. return rv;
  2302. }
  2303. unsigned BOARD::GetPadCount() const
  2304. {
  2305. return m_connectivity->GetPadCount();
  2306. }
  2307. /**
  2308. * Function GetPad
  2309. * @return D_PAD* - at the \a aIndex
  2310. */
  2311. D_PAD* BOARD::GetPad( unsigned aIndex ) const
  2312. {
  2313. unsigned count = 0;
  2314. for( MODULE* mod = m_Modules; mod ; mod = mod->Next() ) // FIXME: const DLIST_ITERATOR
  2315. {
  2316. for( D_PAD* pad = mod->PadsList(); pad; pad = pad->Next() )
  2317. {
  2318. if( count == aIndex )
  2319. return pad;
  2320. count++;
  2321. }
  2322. }
  2323. return nullptr;
  2324. }
  2325. void BOARD::ClearAllNetCodes()
  2326. {
  2327. for ( auto zone : Zones() )
  2328. zone->SetNetCode( 0 );
  2329. for ( auto mod : Modules() )
  2330. for ( auto pad : mod->Pads() )
  2331. pad->SetNetCode( 0 );
  2332. for ( auto track : Tracks() )
  2333. track->SetNetCode( 0 );
  2334. }