diff --git a/bitmaps_png/CMakeLists.txt b/bitmaps_png/CMakeLists.txt index cbef91207f..254db826e5 100644 --- a/bitmaps_png/CMakeLists.txt +++ b/bitmaps_png/CMakeLists.txt @@ -569,6 +569,12 @@ set( BMAPS_BIG wizard_add_fplib_icon ) +set( BMAPS_OTHER + tune_diff_pair_length_legend + tune_diff_pair_skew_legend + tune_single_track_length_legend + ) + # @todo keep these in sync with .bzrignore set( TMP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tmp" ) @@ -742,5 +748,10 @@ foreach( bmn ${BMAPS_BIG} ) list( APPEND CPP_LIST cpp_48/${bmn}.cpp ) endforeach() +foreach( bmn ${BMAPS_OTHER} ) + #message( "library add cpp_other/${bmn}.cpp" ) + list( APPEND CPP_LIST cpp_other/${bmn}.cpp ) +endforeach() + #add_library( bitmaps SHARED ${CPP_LIST} ) add_library( bitmaps STATIC ${CPP_LIST} ) diff --git a/bitmaps_png/cpp_other/tune_diff_pair_length_legend.cpp b/bitmaps_png/cpp_other/tune_diff_pair_length_legend.cpp new file mode 100644 index 0000000000..58dee697ba --- /dev/null +++ b/bitmaps_png/cpp_other/tune_diff_pair_length_legend.cpp @@ -0,0 +1,871 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xbd, 0x08, 0x02, 0x00, 0x00, 0x01, 0x62, 0xd4, 0xc7, + 0x28, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x1e, 0xc2, 0x00, 0x00, 0x1e, + 0xc2, 0x01, 0x6e, 0xd0, 0x75, 0x3e, 0x00, 0x00, 0x00, 0x09, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x89, 0x2a, 0x8d, 0x06, 0x00, 0x00, 0x20, 0x00, 0x49, + 0x44, 0x41, 0x54, 0x78, 0x9c, 0xec, 0x5d, 0x89, 0x5f, 0x13, 0x47, 0xdf, 0xf7, 0xbf, 0x78, 0x9f, + 0xb7, 0x87, 0x56, 0x6b, 0xa5, 0x9e, 0xd5, 0x5a, 0xab, 0x4f, 0xb5, 0x6a, 0xd5, 0xbe, 0xad, 0x3e, + 0x56, 0xad, 0xad, 0x55, 0x6b, 0xb5, 0x7d, 0xb4, 0x15, 0x10, 0xf0, 0x40, 0x0e, 0x45, 0xee, 0x53, + 0xb9, 0x54, 0x3c, 0x50, 0xf1, 0x00, 0xc5, 0x03, 0x39, 0xaa, 0x56, 0x2e, 0x41, 0x45, 0x2b, 0x2a, + 0x2a, 0xa2, 0x45, 0x41, 0x39, 0xc2, 0x4d, 0x20, 0x1c, 0xe1, 0x0a, 0x10, 0x92, 0x90, 0x77, 0xc2, + 0x84, 0xc9, 0x64, 0xef, 0xdd, 0x84, 0x24, 0x46, 0xbe, 0x9f, 0xf9, 0x2c, 0xcb, 0xec, 0xec, 0x6f, + 0xbe, 0xfb, 0xcd, 0xec, 0xec, 0xdc, 0x33, 0x42, 0x6d, 0x0c, 0x54, 0x36, 0xc9, 0xe8, 0x2e, 0x8d, + 0x80, 0x7f, 0x9a, 0x9b, 0x9b, 0x04, 0xd8, 0xf5, 0x89, 0x7b, 0xba, 0xd9, 0xe3, 0xaf, 0x47, 0x65, + 0xad, 0x5d, 0xbd, 0x0a, 0x70, 0x02, 0x1c, 0x6d, 0x04, 0x02, 0x10, 0x79, 0xf5, 0xd5, 0x9e, 0x73, + 0xff, 0x10, 0x3c, 0xc9, 0x71, 0x08, 0x8f, 0x00, 0xda, 0x2a, 0xf9, 0xe8, 0xfd, 0xca, 0x65, 0xdf, + 0x34, 0x1f, 0x3f, 0x04, 0x4e, 0xc0, 0xbf, 0xee, 0x31, 0x8f, 0x8d, 0x16, 0x81, 0x63, 0x58, 0x0e, + 0x8c, 0x00, 0x1c, 0x3b, 0x3a, 0x3a, 0xfa, 0x15, 0x0a, 0x69, 0x72, 0x02, 0x38, 0x77, 0x39, 0x94, + 0x6b, 0x9c, 0x08, 0xbc, 0xe3, 0x0a, 0xc0, 0xb1, 0x6a, 0xf5, 0x0a, 0xe4, 0xd3, 0x18, 0xe2, 0x07, + 0x8e, 0xf6, 0x81, 0x59, 0xb4, 0x11, 0xf4, 0xf7, 0xf7, 0x73, 0x8f, 0xc0, 0x4b, 0x40, 0x04, 0x32, + 0x99, 0x36, 0xb5, 0x25, 0x27, 0x5e, 0xae, 0xad, 0xad, 0x61, 0x8e, 0xc0, 0x3b, 0xee, 0xe9, 0x40, + 0x04, 0xcb, 0x79, 0x44, 0xc0, 0x0b, 0x14, 0x11, 0xec, 0x1d, 0xb2, 0x08, 0xe6, 0xcd, 0x9e, 0x65, + 0xfc, 0x08, 0xbc, 0x60, 0x04, 0x3f, 0x2d, 0x1f, 0xf5, 0xce, 0xff, 0x00, 0x07, 0xce, 0x2b, 0x16, + 0x7f, 0xa5, 0x26, 0xbd, 0x0a, 0x9a, 0x08, 0x36, 0xfd, 0xba, 0x1e, 0x86, 0x80, 0xb0, 0xf3, 0xcd, + 0x80, 0x6e, 0x4b, 0x60, 0x56, 0x5e, 0x69, 0x2b, 0x6b, 0x04, 0xe0, 0x08, 0x6f, 0x87, 0x49, 0x36, + 0xf0, 0xc2, 0x73, 0x10, 0x87, 0xad, 0x57, 0x9a, 0xad, 0x77, 0x9a, 0x6b, 0xf4, 0x43, 0x96, 0x27, + 0x60, 0xc8, 0x03, 0xbc, 0x62, 0x61, 0x04, 0xcb, 0xc0, 0x31, 0x24, 0xd0, 0x1f, 0x1c, 0x95, 0xb2, + 0xae, 0xf2, 0x59, 0x53, 0x09, 0xc1, 0xb4, 0x11, 0xd8, 0xfe, 0xbe, 0x11, 0x79, 0xd5, 0xbb, 0x38, + 0xd5, 0xbb, 0x6c, 0xad, 0x77, 0xdd, 0xd6, 0x18, 0xe2, 0x8b, 0x3c, 0xc9, 0x71, 0xe0, 0x11, 0x20, + 0x28, 0x9a, 0x9b, 0xc0, 0x73, 0x94, 0xcf, 0xf9, 0xac, 0xc1, 0x67, 0x97, 0xd8, 0xdb, 0xbd, 0x21, + 0xc0, 0x93, 0xe5, 0x09, 0x6a, 0xfe, 0xfb, 0x33, 0x7c, 0x70, 0x4d, 0x1c, 0x9e, 0xa9, 0xac, 0x11, + 0x90, 0xc1, 0xe9, 0x47, 0x86, 0x71, 0x10, 0x1e, 0xc2, 0x98, 0x11, 0x88, 0xbe, 0xfa, 0x37, 0x38, + 0xb6, 0x74, 0xca, 0xab, 0x9b, 0xbb, 0x87, 0x24, 0x02, 0x80, 0xe6, 0xe3, 0x87, 0xc1, 0xd1, 0xe5, + 0xf0, 0x7d, 0x62, 0x04, 0xab, 0xbe, 0x13, 0x12, 0xc1, 0x94, 0x09, 0x36, 0x04, 0x1f, 0x49, 0x78, + 0x30, 0x38, 0x3a, 0xee, 0xbb, 0x6d, 0x9c, 0x08, 0xc8, 0x90, 0x84, 0x07, 0x81, 0xa3, 0x03, 0x39, + 0x02, 0x63, 0x49, 0x44, 0xf8, 0x9d, 0xe1, 0x09, 0xc8, 0x7d, 0xbb, 0x72, 0xef, 0x18, 0x33, 0x02, + 0x68, 0x1a, 0xb8, 0x6b, 0x4f, 0xc4, 0xb8, 0x27, 0x7b, 0x04, 0x78, 0x56, 0xc1, 0x1a, 0x07, 0xfa, + 0x57, 0x2e, 0x2a, 0x63, 0x8f, 0xa0, 0x42, 0x24, 0x4a, 0x4f, 0x4b, 0xe3, 0xe8, 0x92, 0x76, 0x6e, + 0x4b, 0xdc, 0xe1, 0x04, 0x1c, 0x17, 0xee, 0x7a, 0x4f, 0x60, 0x08, 0x8a, 0xeb, 0x3a, 0x98, 0xac, + 0x73, 0x11, 0x87, 0x12, 0xe0, 0x97, 0xd8, 0x1a, 0x79, 0x47, 0xd5, 0xdf, 0x7f, 0x24, 0xad, 0x14, + 0x9c, 0x93, 0x3f, 0xb7, 0x86, 0x16, 0x58, 0x98, 0x7d, 0x04, 0x5a, 0x0f, 0x4a, 0x2a, 0x82, 0x27, + 0xe0, 0x37, 0xe8, 0xca, 0xcb, 0x05, 0xc7, 0x96, 0x53, 0xd1, 0xe4, 0x08, 0x04, 0x5a, 0x47, 0x25, + 0x2d, 0x70, 0xf4, 0xf5, 0xf2, 0x54, 0xd3, 0x64, 0x82, 0x46, 0xb0, 0x0e, 0x31, 0x54, 0xd6, 0xe1, + 0xc7, 0x8b, 0xc5, 0xfa, 0x98, 0xf7, 0xdf, 0xf9, 0x71, 0x05, 0x4b, 0x26, 0x45, 0x69, 0x1d, 0xe2, + 0xcd, 0x54, 0x66, 0xc8, 0xad, 0xcf, 0x9e, 0xf9, 0x19, 0xad, 0x75, 0xca, 0x32, 0x0f, 0x70, 0xbe, + 0xf1, 0xcf, 0xb8, 0x58, 0x5f, 0xb7, 0xe6, 0x27, 0x70, 0x94, 0x26, 0xc4, 0x2b, 0x7b, 0xba, 0xe1, + 0x25, 0xf0, 0xf5, 0x07, 0x65, 0x1e, 0xe0, 0x34, 0xd6, 0xb3, 0xb3, 0x6e, 0xd0, 0x59, 0xd9, 0x7d, + 0xea, 0x09, 0x65, 0x81, 0x87, 0xac, 0x0c, 0xf9, 0x5f, 0x0a, 0xee, 0xf5, 0x3b, 0x1d, 0x35, 0x05, + 0x1e, 0x57, 0x4d, 0x81, 0xa7, 0xfb, 0x59, 0x3e, 0x6e, 0x8b, 0xd5, 0x3a, 0xf4, 0x01, 0xae, 0xde, + 0x7d, 0x07, 0x2c, 0xf0, 0x30, 0xe9, 0xae, 0xea, 0xed, 0x05, 0x41, 0x3b, 0x6f, 0x67, 0x91, 0x23, + 0xa0, 0xb3, 0x4e, 0xc1, 0x9d, 0x19, 0xba, 0xcf, 0xd3, 0x9e, 0xeb, 0xc6, 0xb7, 0xae, 0xa6, 0x4a, + 0x6d, 0x42, 0xac, 0x47, 0x1f, 0x39, 0x3c, 0x54, 0xd6, 0xed, 0xff, 0xd8, 0x84, 0xce, 0x93, 0x93, + 0x12, 0x8d, 0xcf, 0x9d, 0x0e, 0xa6, 0xb0, 0x1e, 0x98, 0x58, 0x94, 0x74, 0xbf, 0xc6, 0xf8, 0xd6, + 0x01, 0xca, 0x66, 0x4c, 0x01, 0x47, 0xb7, 0xa8, 0x7b, 0x78, 0x85, 0xa2, 0x8c, 0x54, 0x5f, 0xa0, + 0xb0, 0xce, 0xb1, 0x48, 0x53, 0xbd, 0x76, 0x25, 0xfa, 0x57, 0xd9, 0xd9, 0xc1, 0xa9, 0xcc, 0xc4, + 0xbd, 0x3c, 0x93, 0x9e, 0x7a, 0x1d, 0x95, 0x67, 0x24, 0x61, 0x81, 0xac, 0xa6, 0x35, 0xd6, 0x8b, + 0x8b, 0x8a, 0xfe, 0x4c, 0x49, 0x36, 0x97, 0x0b, 0x8d, 0x8c, 0x81, 0x0e, 0x0a, 0x0e, 0x9d, 0xff, + 0xc5, 0x42, 0x2e, 0xd4, 0xd5, 0xf8, 0xef, 0x8a, 0xaa, 0xa8, 0x26, 0x80, 0x83, 0x77, 0x2a, 0x60, + 0xe9, 0x10, 0x92, 0x4d, 0x79, 0x55, 0xa9, 0xea, 0x87, 0x8f, 0x91, 0x2f, 0x92, 0x32, 0xdb, 0x31, + 0x42, 0x59, 0x92, 0x2f, 0x08, 0x15, 0x65, 0xd1, 0xc2, 0x39, 0x30, 0xef, 0x86, 0xae, 0xed, 0x5a, + 0x32, 0x1e, 0x12, 0x7c, 0x86, 0x18, 0x4c, 0x99, 0x9a, 0x3d, 0x4e, 0xbd, 0xc1, 0xdf, 0x13, 0xd0, + 0x45, 0x5f, 0x2a, 0x08, 0xf8, 0x0c, 0x78, 0xf8, 0x33, 0x37, 0x2b, 0xe8, 0xac, 0x99, 0x81, 0x3d, + 0x6c, 0xd2, 0x52, 0x0f, 0x10, 0x85, 0x6d, 0x1e, 0x6a, 0xfd, 0x7c, 0x03, 0xf8, 0x37, 0xee, 0xf5, + 0x47, 0xe1, 0xb7, 0xe8, 0xb7, 0xe3, 0xe0, 0xa0, 0x65, 0x9f, 0x92, 0x9c, 0x48, 0x77, 0xc9, 0x10, + 0x10, 0xd9, 0x0f, 0x36, 0xcb, 0x01, 0xf6, 0xa8, 0xa1, 0x4d, 0xc3, 0x7e, 0xa0, 0x05, 0x0d, 0x86, + 0xb7, 0xe7, 0xcb, 0x7e, 0xb7, 0x9b, 0x0b, 0x38, 0x7e, 0x32, 0xf1, 0x63, 0x63, 0x91, 0x46, 0xa0, + 0x63, 0x0f, 0x0b, 0xd5, 0xc8, 0xdf, 0x20, 0xf6, 0x08, 0xf9, 0x4f, 0xb4, 0xcd, 0xb8, 0xaf, 0x5f, + 0x15, 0x6f, 0x77, 0x72, 0xb0, 0x19, 0x33, 0x6a, 0xab, 0x83, 0xbd, 0x60, 0xea, 0x90, 0x0d, 0x6c, + 0xe9, 0x53, 0x6b, 0xd9, 0x2f, 0x27, 0x87, 0x19, 0x48, 0x39, 0xc6, 0x60, 0x6f, 0x74, 0x58, 0x17, + 0x7b, 0xac, 0x29, 0x14, 0x9e, 0x44, 0x1d, 0x88, 0x04, 0xfe, 0x0d, 0xfe, 0x7b, 0x50, 0x78, 0x7b, + 0xff, 0x4c, 0x3a, 0x6b, 0x66, 0xc9, 0x73, 0xf4, 0xd8, 0x93, 0xc3, 0xe0, 0x99, 0x66, 0x73, 0x87, + 0x1c, 0xdc, 0x62, 0xe7, 0x43, 0x9d, 0xeb, 0xeb, 0x7d, 0x6b, 0x3f, 0x78, 0xf7, 0x5f, 0x94, 0xf1, + 0xb1, 0x3a, 0xbc, 0x65, 0x50, 0x18, 0x7b, 0x10, 0x7b, 0xfc, 0xd9, 0x38, 0xca, 0x07, 0x00, 0x68, + 0xed, 0x92, 0x83, 0xcf, 0x16, 0x21, 0x52, 0xd7, 0x23, 0x0f, 0x74, 0xec, 0x55, 0x2a, 0x15, 0x47, + 0x06, 0x64, 0x04, 0x5e, 0x2a, 0xa4, 0x6b, 0x6a, 0xa6, 0x60, 0x1f, 0x8b, 0xb3, 0xa7, 0x6d, 0xdb, + 0x6b, 0x39, 0x73, 0x02, 0x3e, 0x43, 0xcf, 0xeb, 0x62, 0xba, 0x30, 0x5a, 0xf6, 0x4f, 0x1e, 0x3f, + 0xa2, 0x2b, 0xe7, 0xe0, 0x9f, 0x71, 0xb2, 0x2b, 0x9d, 0x34, 0x16, 0x85, 0x54, 0x28, 0x55, 0xac, + 0xcf, 0xc0, 0x9d, 0x3d, 0x82, 0x34, 0xf1, 0x42, 0xc9, 0xb8, 0x91, 0xe4, 0xa8, 0xab, 0x7f, 0x59, + 0x65, 0x84, 0x74, 0xaf, 0x55, 0xa8, 0xf8, 0x05, 0xfc, 0xd7, 0x23, 0xee, 0x19, 0xc3, 0x03, 0x08, + 0x60, 0xcf, 0x00, 0xa3, 0xbd, 0xb5, 0x80, 0x8a, 0x68, 0xd1, 0x1c, 0x78, 0xbe, 0xed, 0xe0, 0x3d, + 0xba, 0x07, 0x30, 0x11, 0xfb, 0x2f, 0x3e, 0x9f, 0xce, 0xcb, 0x90, 0xa2, 0xa5, 0x19, 0xb0, 0x51, + 0x48, 0x1a, 0xe1, 0xbf, 0x80, 0x65, 0x40, 0x62, 0x11, 0x39, 0x18, 0x91, 0x3d, 0x5b, 0xa3, 0x39, + 0x33, 0xa8, 0xd9, 0x8f, 0x1b, 0x3d, 0x72, 0xc3, 0xba, 0x35, 0xf0, 0x35, 0x58, 0x30, 0x77, 0xf6, + 0x57, 0x5f, 0x7e, 0xa1, 0x1e, 0x68, 0xc8, 0x1f, 0x3f, 0x76, 0x0c, 0x83, 0x2d, 0x4d, 0x9d, 0x28, + 0x22, 0x18, 0xb1, 0x74, 0x0c, 0xbd, 0x4d, 0x0e, 0x63, 0x0a, 0xf6, 0xc2, 0xa0, 0x61, 0x1f, 0xae, + 0x63, 0x8f, 0xf7, 0x1e, 0x20, 0x58, 0x34, 0xfb, 0x7a, 0x67, 0x47, 0x78, 0x4e, 0x97, 0xf9, 0x00, + 0xcf, 0x88, 0x6b, 0x25, 0x28, 0xbc, 0xd8, 0x63, 0xa7, 0x21, 0x31, 0x1a, 0x93, 0xbd, 0x78, 0x8f, + 0x2b, 0xa1, 0x62, 0xa1, 0xf9, 0x4c, 0xfa, 0x65, 0x9c, 0xb9, 0x29, 0x0a, 0x4c, 0x2c, 0x82, 0xff, + 0x1e, 0xbc, 0xae, 0xa5, 0x5e, 0xb9, 0xe2, 0x5b, 0xee, 0xbd, 0x09, 0x74, 0x30, 0x72, 0x49, 0xa1, + 0x31, 0xd0, 0x1b, 0xaf, 0x5b, 0xd0, 0x81, 0xf0, 0x29, 0x15, 0x0c, 0xbd, 0x92, 0x42, 0x79, 0x39, + 0x7b, 0xaf, 0x0d, 0x17, 0x80, 0xef, 0x08, 0xe4, 0x57, 0xf3, 0xdb, 0xda, 0xee, 0xc2, 0xe7, 0xea, + 0x81, 0xa6, 0xc4, 0xe6, 0x13, 0x87, 0xd1, 0x87, 0xc6, 0x28, 0xb1, 0xa8, 0x4d, 0x50, 0x4a, 0xeb, + 0xef, 0x93, 0xf7, 0x96, 0xbc, 0xea, 0xab, 0xab, 0x1d, 0x0a, 0xe3, 0x66, 0x68, 0x53, 0x20, 0x43, + 0x2c, 0xed, 0x79, 0x51, 0xd3, 0x01, 0x0a, 0x1a, 0xbc, 0xee, 0x1a, 0x81, 0x5a, 0x85, 0x2e, 0x5d, + 0xbc, 0x60, 0xe2, 0x76, 0xa8, 0xa8, 0x98, 0x78, 0xd8, 0x0e, 0xe5, 0xe0, 0xf9, 0xa7, 0x80, 0xe2, + 0xaa, 0x5e, 0x8a, 0xe7, 0xa7, 0x95, 0x61, 0x40, 0x44, 0x83, 0x12, 0x8b, 0x3a, 0x7a, 0x14, 0xfd, + 0xfd, 0xfd, 0xaf, 0xea, 0x3a, 0xdc, 0x8e, 0xe5, 0x41, 0x4f, 0x07, 0xbf, 0x74, 0x56, 0x0b, 0xba, + 0x2e, 0x59, 0xe0, 0x40, 0x01, 0x73, 0x88, 0x09, 0x6b, 0x01, 0xf9, 0x3d, 0x2e, 0xa3, 0x1e, 0x1a, + 0xb2, 0xfb, 0xe4, 0x13, 0x2e, 0xc5, 0x6c, 0x9d, 0xea, 0xf7, 0xef, 0xdd, 0x33, 0x26, 0x3b, 0x7a, + 0x40, 0x5a, 0xbd, 0x7d, 0x4c, 0x29, 0x7b, 0xdf, 0x95, 0xd7, 0x20, 0xcc, 0xae, 0xa3, 0xf7, 0x19, + 0xc2, 0x98, 0x9a, 0x3a, 0x1c, 0x30, 0x87, 0xfe, 0xed, 0xba, 0xff, 0x37, 0x5e, 0x46, 0x2f, 0x9b, + 0x31, 0x19, 0x5d, 0xda, 0xbe, 0xff, 0x2e, 0xb3, 0xf0, 0xa6, 0xa6, 0x3e, 0xf0, 0xad, 0xd5, 0xd6, + 0xa9, 0xdb, 0x52, 0x12, 0x20, 0xe3, 0xa6, 0xc3, 0x91, 0x5d, 0x0f, 0xfe, 0x46, 0xdf, 0x04, 0x3c, + 0xb0, 0xd3, 0xbe, 0x5b, 0x74, 0xa6, 0x4c, 0x4a, 0x5d, 0xda, 0xd5, 0x07, 0xd8, 0x14, 0xd5, 0x6a, + 0x3b, 0xfb, 0x09, 0x44, 0xdb, 0xa4, 0x52, 0x65, 0x7b, 0xdb, 0x40, 0xaf, 0xd9, 0x76, 0xe8, 0x03, + 0xab, 0xad, 0x74, 0xd6, 0x4c, 0x4a, 0x7d, 0xdf, 0x95, 0x57, 0x88, 0x4a, 0xf7, 0xd3, 0xc7, 0x80, + 0xa5, 0xaa, 0x5b, 0x33, 0xb4, 0xae, 0xb7, 0xa7, 0x47, 0x22, 0x91, 0x40, 0x7f, 0xfc, 0x79, 0x02, + 0x2e, 0xbf, 0xb4, 0x14, 0xea, 0x7e, 0x17, 0x0b, 0x11, 0x15, 0xc9, 0xfe, 0x7d, 0x88, 0xa2, 0x97, + 0xc7, 0xae, 0x0f, 0x47, 0xbe, 0x0b, 0xcf, 0xeb, 0xb6, 0xd9, 0x21, 0xff, 0xe3, 0x37, 0xca, 0x2d, + 0x93, 0xfa, 0x5e, 0xca, 0x22, 0x4d, 0xdd, 0xf6, 0x2d, 0xc8, 0xff, 0x84, 0xe5, 0x53, 0x3f, 0x7a, + 0x28, 0xaa, 0xb5, 0xa5, 0x05, 0x9e, 0x1b, 0x4a, 0x1d, 0xfc, 0x7c, 0x43, 0xf1, 0x71, 0x65, 0xa0, + 0x8e, 0xc2, 0x18, 0x4a, 0x3d, 0x74, 0x6f, 0xf0, 0xdd, 0x9c, 0x1c, 0x23, 0x92, 0x86, 0xe0, 0x46, + 0xdd, 0x5e, 0x38, 0xf5, 0xaa, 0xca, 0x4a, 0xd9, 0x00, 0xf6, 0xec, 0x72, 0x73, 0x77, 0x71, 0xfe, + 0x61, 0xf9, 0xd2, 0x69, 0x93, 0x27, 0x28, 0x14, 0x8a, 0x98, 0xe3, 0xd1, 0xe0, 0x64, 0x28, 0xa8, + 0xe3, 0x30, 0x88, 0x3a, 0x4a, 0x2a, 0xe0, 0x64, 0x7f, 0x78, 0xd8, 0xfa, 0x9f, 0x57, 0x03, 0xc6, + 0x53, 0x27, 0x8d, 0x77, 0xb0, 0xdb, 0x0c, 0x3c, 0x95, 0x4a, 0xa5, 0xe5, 0x52, 0x1f, 0x3a, 0xd0, + 0x65, 0x8e, 0x38, 0x86, 0xa9, 0x1b, 0x1b, 0x74, 0xd4, 0x9d, 0xb7, 0x3a, 0xa9, 0x07, 0x47, 0xc5, + 0xeb, 0x51, 0xcf, 0xb2, 0x60, 0xea, 0x79, 0x0f, 0x1f, 0x10, 0xba, 0x40, 0xc4, 0x1e, 0x3b, 0x11, + 0xf5, 0xf8, 0xbb, 0x55, 0x96, 0x4b, 0x1d, 0x9c, 0x38, 0xda, 0xdb, 0x22, 0xea, 0x40, 0xf8, 0xa6, + 0xa8, 0x70, 0x42, 0xe1, 0x31, 0xf4, 0xca, 0x2b, 0x4a, 0x6b, 0xe6, 0xa7, 0x4e, 0x80, 0x76, 0x1c, + 0xd5, 0x4d, 0x6d, 0xc1, 0x18, 0xd6, 0x4b, 0xe4, 0x0a, 0x8a, 0x7a, 0x89, 0x5e, 0x05, 0x8f, 0xfc, + 0x05, 0x05, 0xb5, 0x74, 0x7c, 0xe4, 0x9a, 0xd6, 0xf9, 0x65, 0x80, 0x7a, 0x00, 0x9d, 0x18, 0x02, + 0xa8, 0x4f, 0x9f, 0x32, 0xc9, 0xd5, 0x79, 0x3b, 0x0a, 0x46, 0x2e, 0xb5, 0x43, 0xa7, 0x19, 0xf2, + 0x3f, 0x30, 0xea, 0x5f, 0x37, 0x08, 0x0e, 0x60, 0x7f, 0x44, 0x18, 0xe5, 0x97, 0x5f, 0xa1, 0xec, + 0x67, 0xed, 0x6f, 0x0a, 0xbe, 0x44, 0x9c, 0xc8, 0xc3, 0x97, 0xfa, 0x36, 0xc7, 0x2d, 0x63, 0x47, + 0xbd, 0x87, 0x82, 0x35, 0x1f, 0x3d, 0x00, 0x2e, 0xb5, 0x9e, 0xd7, 0xf5, 0x40, 0x85, 0x24, 0x14, + 0x92, 0xe3, 0xd5, 0x25, 0x98, 0xf1, 0x63, 0x47, 0x73, 0x64, 0x80, 0x70, 0x7e, 0xe0, 0x35, 0x82, + 0xee, 0x6a, 0x1e, 0x7b, 0x3b, 0x11, 0x1d, 0x75, 0xa8, 0x9a, 0xdb, 0xce, 0x1d, 0x28, 0x24, 0x14, + 0x5e, 0x56, 0xf0, 0x84, 0xc1, 0x9a, 0x8e, 0xba, 0x87, 0xbb, 0x2b, 0x5f, 0xea, 0x10, 0xdd, 0x72, + 0x25, 0x64, 0xef, 0x1c, 0xc9, 0x32, 0xe4, 0xdc, 0x9f, 0x43, 0x5a, 0x47, 0xd0, 0x36, 0x00, 0x6e, + 0xfa, 0x85, 0x2e, 0x80, 0x6e, 0x52, 0x19, 0x65, 0x82, 0x01, 0xb5, 0x98, 0x81, 0x79, 0x1d, 0x4e, + 0x68, 0xb0, 0x23, 0x70, 0x62, 0x4f, 0xd7, 0xe6, 0xe8, 0x83, 0x84, 0x90, 0xee, 0x31, 0x8f, 0x35, + 0xf5, 0x4e, 0xaf, 0x54, 0xb2, 0x11, 0x04, 0x2e, 0xaf, 0x29, 0x8e, 0x8a, 0xef, 0xbe, 0xd6, 0xf5, + 0x8a, 0xad, 0x5f, 0xd5, 0xe0, 0xb3, 0x0b, 0x3a, 0xb1, 0xb7, 0xbb, 0x6e, 0xd0, 0x24, 0x7c, 0x47, + 0xe1, 0x74, 0x1e, 0x02, 0x75, 0xe6, 0x2e, 0xbe, 0xd6, 0xf3, 0xb1, 0x28, 0xb0, 0xff, 0xb9, 0x02, + 0xc0, 0xcc, 0xf7, 0xd2, 0x0b, 0x63, 0x51, 0x87, 0xa8, 0xf9, 0x6d, 0x2d, 0x65, 0xd4, 0xba, 0x04, + 0x83, 0x06, 0x3f, 0x70, 0x44, 0x6b, 0xfc, 0x19, 0x72, 0xfb, 0x2d, 0xa8, 0xed, 0x33, 0x7c, 0x44, + 0x84, 0x51, 0xa7, 0x83, 0x70, 0xea, 0x5a, 0x06, 0xe1, 0x41, 0x94, 0x79, 0x19, 0x65, 0x60, 0xcb, + 0xa2, 0x0e, 0xd0, 0x71, 0x23, 0x0d, 0x90, 0xa8, 0x73, 0xda, 0x0c, 0xff, 0xf5, 0xb9, 0xf4, 0x82, + 0x96, 0x3a, 0x76, 0xc9, 0x22, 0xa8, 0xab, 0x07, 0x66, 0x1b, 0x11, 0x84, 0xf7, 0x1e, 0xec, 0xfd, + 0xc2, 0x61, 0x89, 0xd4, 0xd5, 0x70, 0xd0, 0x4b, 0x90, 0x0f, 0x3c, 0xa7, 0x4b, 0x33, 0xa6, 0xa0, + 0xde, 0xdb, 0xd3, 0xf3, 0xf5, 0x57, 0xf3, 0x78, 0x19, 0xc2, 0x53, 0xfc, 0x8e, 0xa8, 0x5c, 0xb3, + 0x51, 0x07, 0x19, 0xe5, 0xc6, 0x0d, 0xb4, 0xdf, 0x02, 0x4a, 0x88, 0x16, 0xce, 0x46, 0x54, 0x7c, + 0x69, 0x92, 0xbb, 0xbf, 0x09, 0xa8, 0x2f, 0xff, 0xcf, 0xe2, 0x0d, 0xeb, 0xd6, 0x88, 0xeb, 0xeb, + 0x57, 0x2e, 0x5b, 0x7a, 0x20, 0x22, 0xfc, 0x97, 0xb5, 0xab, 0x1f, 0xe5, 0x3d, 0xfc, 0x7c, 0xda, + 0x14, 0x06, 0x43, 0x30, 0xf7, 0x85, 0xe7, 0x61, 0x58, 0x03, 0x9d, 0x49, 0xa9, 0xfb, 0x78, 0x7a, + 0xc0, 0x13, 0xa0, 0x3d, 0x48, 0x36, 0xe0, 0xf8, 0xf1, 0xd8, 0xd1, 0x80, 0x3a, 0xb3, 0x21, 0x9c, + 0x7a, 0xf8, 0x40, 0xeb, 0xb8, 0x19, 0xa8, 0x0b, 0x43, 0xcd, 0x46, 0xdd, 0xd4, 0xcc, 0xf0, 0xab, + 0x6f, 0x16, 0xf5, 0x4d, 0xbf, 0x20, 0x2a, 0x11, 0xd7, 0xa8, 0xa9, 0x5b, 0xdc, 0x27, 0x09, 0xa2, + 0x31, 0xc8, 0x1b, 0x51, 0x29, 0xac, 0x6e, 0x07, 0x14, 0x1b, 0xdb, 0x7a, 0x09, 0x61, 0xec, 0xfd, + 0x75, 0xc5, 0x04, 0xf1, 0x6e, 0x67, 0x4b, 0xa1, 0xde, 0xfd, 0xf2, 0x1f, 0x40, 0xa5, 0x7f, 0x70, + 0x90, 0x19, 0x39, 0x6b, 0xaf, 0x6a, 0x92, 0x01, 0x1f, 0xd7, 0x28, 0x6d, 0x35, 0xd2, 0xf0, 0xce, + 0x6b, 0xa3, 0x51, 0x87, 0x6c, 0x2a, 0xbf, 0x5f, 0x0c, 0xcf, 0x25, 0xd2, 0x6e, 0xc8, 0xde, 0xf7, + 0xd2, 0xcb, 0xd8, 0x9b, 0x22, 0xc7, 0xb0, 0x1c, 0xc2, 0xc3, 0x30, 0x97, 0xc5, 0xb9, 0xc0, 0x98, + 0xd4, 0x45, 0xf3, 0xf5, 0x8a, 0x03, 0x2f, 0x6b, 0x3a, 0xf0, 0xfa, 0x98, 0x23, 0x36, 0x5a, 0xbf, + 0x64, 0xfc, 0x68, 0x63, 0x0e, 0xd1, 0x30, 0x9c, 0xba, 0x9a, 0x5b, 0x32, 0x68, 0x3a, 0x10, 0xaa, + 0xe9, 0x30, 0x72, 0xdb, 0xce, 0x1c, 0x8c, 0x15, 0x7a, 0x55, 0x8d, 0xd4, 0xbf, 0xae, 0x19, 0x68, + 0x0e, 0xf6, 0x63, 0x01, 0x27, 0xaf, 0xa9, 0xa2, 0x0c, 0x50, 0xb3, 0x61, 0xf5, 0x90, 0x0c, 0x2d, + 0x31, 0xdc, 0x1c, 0x04, 0xaa, 0x82, 0x34, 0x1d, 0xd9, 0xaf, 0xea, 0xd6, 0x74, 0xf8, 0xf7, 0x56, + 0x8a, 0x50, 0x65, 0xa7, 0x62, 0xf1, 0x02, 0xa3, 0xc4, 0xa2, 0x9b, 0xc7, 0x73, 0xe2, 0x58, 0x34, + 0x8f, 0x39, 0x3d, 0x6c, 0xee, 0x4a, 0xd4, 0x7e, 0x38, 0xe9, 0x07, 0xba, 0x14, 0x27, 0x7b, 0xe3, + 0x8e, 0x87, 0x51, 0x9b, 0x60, 0x50, 0x49, 0x5f, 0x83, 0xb8, 0xb7, 0xe4, 0x95, 0xaa, 0x97, 0x98, + 0xc7, 0x1b, 0x0e, 0x8b, 0x18, 0x0f, 0x63, 0x46, 0xfc, 0x95, 0x5f, 0x1f, 0x78, 0xf9, 0xa5, 0x43, + 0x70, 0x36, 0xb9, 0x89, 0xca, 0xce, 0x37, 0xdd, 0xeb, 0x4c, 0xfe, 0xf1, 0x2c, 0x91, 0x52, 0xc5, + 0x63, 0x61, 0x16, 0x8e, 0xa0, 0xd0, 0xbd, 0xe0, 0xe9, 0xd3, 0x4f, 0x27, 0x4f, 0x34, 0x7a, 0x4c, + 0x96, 0x83, 0xd4, 0xfc, 0x7a, 0x5b, 0x2f, 0xe2, 0xd8, 0x73, 0x9d, 0xf3, 0xbc, 0x4e, 0x77, 0x29, + 0x34, 0x99, 0x62, 0x24, 0xad, 0x30, 0x10, 0x75, 0x8f, 0x3f, 0x1b, 0x07, 0xf3, 0x79, 0xbc, 0x0d, + 0xd3, 0x3a, 0xd0, 0x23, 0x57, 0x6e, 0x0d, 0xce, 0xc2, 0x75, 0xf4, 0x8a, 0x2b, 0x90, 0x76, 0xf5, + 0x31, 0xdf, 0xa5, 0x52, 0xf5, 0x07, 0x25, 0x15, 0x13, 0x7e, 0x80, 0x17, 0xd5, 0xed, 0x06, 0x92, + 0xd1, 0xd3, 0x7d, 0x97, 0xeb, 0x4e, 0xd4, 0xdc, 0x6e, 0xca, 0x89, 0x89, 0x26, 0xc0, 0xae, 0x63, + 0x0f, 0x91, 0x6a, 0x1e, 0xb1, 0x05, 0xc2, 0x8c, 0x04, 0x27, 0x63, 0x3f, 0x80, 0x27, 0x53, 0x1b, + 0x2b, 0x2b, 0xa8, 0xf3, 0x77, 0x93, 0x8d, 0x07, 0x33, 0x01, 0xaa, 0x9a, 0xbb, 0x91, 0x58, 0x3b, + 0xf5, 0xd7, 0xef, 0x42, 0xe8, 0xbc, 0x95, 0x25, 0xde, 0xe5, 0x5c, 0x3e, 0x6b, 0xaa, 0xa6, 0x68, + 0x6b, 0x33, 0xaa, 0xec, 0xf3, 0x29, 0xa0, 0x46, 0xd4, 0x9e, 0x7a, 0x95, 0x32, 0x70, 0x48, 0xe2, + 0x4b, 0x64, 0x30, 0x31, 0xb7, 0x5a, 0x18, 0x2b, 0x2b, 0xd7, 0x5d, 0xd2, 0xde, 0xab, 0xeb, 0x42, + 0x4a, 0x21, 0x76, 0x83, 0xb5, 0xc6, 0x9d, 0x64, 0x6e, 0x8c, 0x87, 0xae, 0xf9, 0xf8, 0x21, 0xc2, + 0x8d, 0xe5, 0x0d, 0x9d, 0xc8, 0xec, 0xa1, 0x74, 0x21, 0x03, 0xa3, 0xad, 0x5c, 0x77, 0x3b, 0xbf, + 0x0c, 0xa8, 0x8e, 0x5f, 0x82, 0x5e, 0xe7, 0x85, 0xb2, 0x4d, 0xaa, 0x53, 0xd6, 0x66, 0x54, 0x7b, + 0x1a, 0x45, 0xa5, 0xa5, 0x2a, 0x23, 0x15, 0x57, 0xbf, 0xbf, 0x4f, 0xef, 0x4b, 0x90, 0x57, 0xda, + 0x82, 0xa4, 0xef, 0xea, 0x55, 0xf0, 0x25, 0x66, 0xcd, 0xba, 0x5f, 0x7e, 0x50, 0x4b, 0x97, 0x17, + 0xa3, 0x29, 0x5d, 0x92, 0xb0, 0x20, 0x66, 0x23, 0x4d, 0x87, 0x23, 0x75, 0xd2, 0xcb, 0xe5, 0xf8, + 0x25, 0xd7, 0xc1, 0xf1, 0xc8, 0x01, 0x09, 0xb4, 0x3d, 0x52, 0x74, 0xb0, 0x66, 0xdd, 0x83, 0x06, + 0x27, 0x8f, 0xb8, 0x1d, 0xcb, 0xc3, 0xfd, 0x5b, 0x62, 0x8e, 0x40, 0x1d, 0xcb, 0xe7, 0x7c, 0x86, + 0xfb, 0xaf, 0x5b, 0xbd, 0x8a, 0xb2, 0x34, 0x51, 0xb9, 0xfc, 0x1b, 0x6d, 0xfd, 0xf5, 0x50, 0x04, + 0xee, 0x7f, 0xe0, 0xaf, 0xd7, 0xd0, 0xfe, 0x96, 0x60, 0xea, 0xa5, 0x0a, 0x18, 0x60, 0xcd, 0xba, + 0xc3, 0x46, 0x4f, 0x4d, 0x7f, 0x51, 0x9c, 0x5e, 0x01, 0x06, 0x8e, 0xe6, 0x02, 0xae, 0x6a, 0xcd, + 0x0a, 0xc2, 0x2d, 0xa5, 0x25, 0xaf, 0xc9, 0x76, 0xe0, 0xb0, 0xc0, 0x12, 0x6c, 0xe6, 0x38, 0x04, + 0x1c, 0x7b, 0xb4, 0x99, 0x71, 0x26, 0x33, 0x1d, 0xde, 0x6e, 0xdd, 0x57, 0xeb, 0xe9, 0x7e, 0x21, + 0xfe, 0x5c, 0xce, 0xed, 0x5b, 0x1f, 0x8f, 0x1d, 0xed, 0xbc, 0xcd, 0x09, 0xf7, 0x37, 0xbf, 0xee, + 0xd9, 0x59, 0x37, 0xe6, 0xcc, 0x9c, 0xb1, 0x3f, 0x3c, 0x8c, 0x6f, 0x34, 0x66, 0x01, 0xd2, 0xdd, + 0x8b, 0x9b, 0xee, 0x47, 0x0f, 0x45, 0xad, 0xff, 0x79, 0x35, 0xd9, 0x8e, 0xf9, 0x75, 0x87, 0x79, + 0x9f, 0xcd, 0x98, 0x51, 0x95, 0x15, 0x15, 0x7c, 0x63, 0x32, 0x3d, 0x04, 0xe8, 0x8e, 0x2f, 0x49, + 0x81, 0x60, 0x66, 0xdd, 0x81, 0xe8, 0x1b, 0xd6, 0xad, 0x81, 0x0e, 0x7d, 0x7c, 0xc0, 0x2b, 0xb9, + 0x60, 0xee, 0xec, 0xcc, 0xf4, 0xb4, 0x45, 0xf3, 0xbe, 0x3c, 0x17, 0x17, 0xab, 0x52, 0xa9, 0xc0, + 0x7b, 0x3a, 0x7d, 0xca, 0xa4, 0x36, 0xa9, 0xf4, 0xeb, 0xf9, 0x73, 0xd5, 0x83, 0x2b, 0xd2, 0x9a, + 0x05, 0x7c, 0x75, 0xa7, 0x03, 0x1c, 0x94, 0x69, 0x1e, 0xdd, 0x67, 0x7d, 0x36, 0xed, 0xda, 0x95, + 0x3f, 0xd1, 0xbf, 0x32, 0x99, 0x0c, 0x4a, 0x0f, 0xf3, 0x1c, 0xf8, 0x7a, 0x4e, 0x9b, 0x3c, 0x21, + 0x38, 0xc0, 0xaf, 0xa5, 0xa5, 0x19, 0x9c, 0x47, 0x1f, 0xd6, 0xd4, 0x35, 0xec, 0xff, 0xd8, 0x74, + 0xfe, 0x5c, 0x9c, 0xda, 0x4c, 0xc0, 0xf2, 0x77, 0xbd, 0x71, 0x18, 0x98, 0xee, 0x14, 0x2b, 0x50, + 0x90, 0x61, 0x4e, 0xdd, 0xdf, 0x44, 0xd0, 0xeb, 0xbe, 0x4f, 0xa0, 0xee, 0x7b, 0x87, 0x75, 0xe7, + 0x80, 0x61, 0xdd, 0xcd, 0x83, 0x21, 0xd7, 0x3d, 0x6b, 0x58, 0x77, 0x2a, 0x70, 0xd7, 0xfd, 0xd4, + 0x89, 0xe3, 0x78, 0x4d, 0x15, 0x9e, 0xa3, 0xe5, 0xd5, 0x87, 0x75, 0xe7, 0x07, 0x76, 0xdd, 0x7f, + 0x5a, 0x1e, 0x75, 0x20, 0x12, 0xef, 0x6f, 0x80, 0xd3, 0xb6, 0x70, 0x9f, 0x5f, 0xd7, 0xad, 0xad, + 0x0f, 0xf6, 0x85, 0xe1, 0xeb, 0x9d, 0x1d, 0x70, 0x3b, 0xb9, 0xaf, 0x75, 0x4d, 0x63, 0xbd, 0x7d, + 0xfc, 0x66, 0x46, 0xbd, 0x15, 0xba, 0x7b, 0xd1, 0xeb, 0x8e, 0xfb, 0x2f, 0x9c, 0xf7, 0x25, 0x3c, + 0x19, 0x37, 0x7a, 0xa4, 0xb4, 0x55, 0x37, 0xb5, 0xbd, 0xf3, 0xee, 0x2d, 0xf2, 0x28, 0x67, 0x08, + 0x9f, 0xf8, 0xe7, 0x5a, 0xe9, 0xb1, 0xa5, 0x6a, 0xb9, 0x60, 0x58, 0x77, 0x22, 0x28, 0xd7, 0xf5, + 0xaa, 0xfc, 0x7e, 0x09, 0xbc, 0x05, 0xe4, 0x39, 0x84, 0x4b, 0xc1, 0x29, 0x7a, 0xbd, 0x80, 0x41, + 0x49, 0xc5, 0x92, 0x76, 0xf6, 0xf1, 0x07, 0x44, 0xdd, 0xf1, 0x57, 0x2c, 0x23, 0x8d, 0xbd, 0x2b, + 0x8b, 0xcb, 0x9c, 0x15, 0x06, 0xe7, 0x7a, 0xe4, 0xfe, 0xb1, 0xcc, 0x72, 0x60, 0x84, 0x35, 0x22, + 0x01, 0xe0, 0xab, 0x3b, 0xca, 0xdf, 0x6f, 0x66, 0x67, 0xe1, 0xe9, 0x1d, 0xa2, 0xc4, 0x66, 0x94, + 0x56, 0xfa, 0xad, 0xb6, 0xe4, 0xb8, 0xf6, 0xfd, 0xf9, 0x8a, 0xcf, 0x53, 0x3f, 0xa0, 0x48, 0xef, + 0x50, 0xf4, 0x0a, 0x91, 0x88, 0xcb, 0xb3, 0xf5, 0x29, 0x54, 0x97, 0x73, 0x6b, 0xb8, 0xb8, 0xf8, + 0x9c, 0xca, 0x23, 0xe9, 0x65, 0x81, 0x89, 0x45, 0xde, 0x67, 0x0b, 0xb6, 0x04, 0x66, 0x51, 0x12, + 0x0a, 0x4e, 0x28, 0xec, 0xe1, 0x99, 0x51, 0x32, 0x80, 0x83, 0xee, 0x14, 0x6b, 0x80, 0xd5, 0xd6, + 0xd6, 0xcc, 0x9c, 0x3e, 0x15, 0x9f, 0xdc, 0x83, 0x20, 0x5a, 0xa4, 0x5b, 0xd6, 0xb5, 0xeb, 0xd1, + 0x03, 0xba, 0x78, 0x2b, 0x9b, 0x64, 0x77, 0x8a, 0x9b, 0x33, 0x0a, 0x1a, 0x90, 0x4b, 0x2f, 0x68, + 0x48, 0x7b, 0x2a, 0x4e, 0xcd, 0x1f, 0x74, 0x4f, 0xc5, 0x23, 0x3a, 0x3a, 0x3a, 0xea, 0x48, 0xc8, + 0xb9, 0x7d, 0x8b, 0xec, 0x69, 0x20, 0xda, 0xda, 0xa8, 0x97, 0xcb, 0x95, 0xca, 0xfa, 0x82, 0x93, + 0x8a, 0x08, 0x3f, 0x80, 0xff, 0x05, 0xae, 0xd3, 0xba, 0x18, 0xa0, 0xd3, 0x3d, 0x96, 0x93, 0xee, + 0x0f, 0xee, 0xe7, 0xc2, 0x34, 0xb7, 0xea, 0x7b, 0xda, 0xf2, 0x65, 0x47, 0x66, 0x1a, 0xde, 0x09, + 0xd5, 0x1a, 0x1b, 0x23, 0x8c, 0x1b, 0x45, 0x7a, 0x9f, 0x35, 0x7d, 0x9a, 0xb9, 0x46, 0x12, 0x88, + 0x1a, 0x3a, 0x6d, 0xf7, 0xe8, 0xd4, 0xdf, 0xe2, 0xcb, 0xbe, 0xae, 0x0c, 0x03, 0xf8, 0xea, 0xce, + 0x1d, 0xf8, 0x7c, 0xa2, 0x92, 0xc1, 0xd5, 0xa2, 0xda, 0x92, 0x2e, 0xf6, 0x2b, 0xb8, 0x76, 0xf8, + 0x11, 0x75, 0x1f, 0x3b, 0xea, 0x3d, 0x94, 0xbf, 0x73, 0xd9, 0xc0, 0x8a, 0x75, 0x9e, 0x16, 0x9d, + 0xab, 0x75, 0xf8, 0xbd, 0x23, 0x93, 0xf6, 0xfb, 0xe1, 0x73, 0xee, 0x99, 0x2e, 0x37, 0x8c, 0x66, + 0x19, 0x6e, 0x4f, 0x87, 0xa1, 0xd3, 0x1d, 0xa1, 0x35, 0xee, 0x64, 0xe9, 0x27, 0x36, 0x7c, 0x9f, + 0x9d, 0xb8, 0x46, 0xe4, 0xf3, 0x67, 0x05, 0xd0, 0x25, 0x5c, 0xbc, 0x00, 0x4f, 0x58, 0x23, 0x06, + 0xba, 0x37, 0x06, 0xfb, 0x70, 0x71, 0x62, 0x2f, 0xf7, 0x9a, 0xdf, 0xd7, 0x8b, 0x16, 0xcc, 0x26, + 0xf3, 0xa8, 0x5a, 0xbd, 0x82, 0xd0, 0x6b, 0x0c, 0xe1, 0x7e, 0x3c, 0x0f, 0xa9, 0x9f, 0xfe, 0x54, + 0xcc, 0x57, 0x14, 0x13, 0xe8, 0x4e, 0x40, 0xf7, 0xf3, 0xa7, 0xcd, 0x31, 0x47, 0x81, 0xfd, 0xc6, + 0x20, 0x1f, 0x34, 0xcf, 0x90, 0xc2, 0x05, 0x78, 0x51, 0x97, 0x23, 0x8d, 0x32, 0xa4, 0x99, 0x15, + 0xf2, 0xca, 0x8a, 0x8a, 0x25, 0x0b, 0xf1, 0x1f, 0x40, 0x96, 0x4f, 0x5c, 0x7f, 0xaa, 0x12, 0x1b, + 0x31, 0x11, 0x48, 0xb5, 0xe0, 0x24, 0x03, 0x4c, 0xaf, 0x3b, 0x77, 0x98, 0x53, 0x77, 0x84, 0x8e, + 0xb4, 0x6b, 0xe8, 0x6d, 0x2d, 0x9d, 0x32, 0x8e, 0x1c, 0x00, 0x49, 0x1f, 0x70, 0x99, 0x87, 0xf4, + 0x70, 0xee, 0xd7, 0xb0, 0xee, 0x2c, 0x68, 0x0c, 0xf1, 0xd3, 0x95, 0xd2, 0xfe, 0xd6, 0x5b, 0x1e, + 0x46, 0xae, 0x50, 0x21, 0xe9, 0x2f, 0xde, 0x63, 0xd9, 0xc9, 0x10, 0x61, 0x58, 0x77, 0xae, 0xe8, + 0xc8, 0xce, 0xa0, 0xcb, 0x73, 0xe0, 0xe6, 0x59, 0x9b, 0x35, 0x6b, 0x11, 0x70, 0x1d, 0x98, 0x68, + 0x55, 0xba, 0xa7, 0x24, 0x27, 0x76, 0x75, 0x75, 0x0d, 0x19, 0x1f, 0xed, 0x24, 0x16, 0xe0, 0xca, + 0xa6, 0x13, 0xc7, 0x82, 0xdb, 0xf9, 0xa6, 0x43, 0x1d, 0xcf, 0xe6, 0x54, 0x72, 0x31, 0xc5, 0xae, + 0xbb, 0x61, 0x2b, 0xe0, 0x1a, 0x02, 0x7e, 0xba, 0xaf, 0x5d, 0xf5, 0x03, 0xac, 0x5c, 0x0c, 0x25, + 0x25, 0x75, 0xe9, 0xe4, 0x8f, 0xa0, 0x2e, 0xed, 0x19, 0x7a, 0x8d, 0x4d, 0x9e, 0x67, 0xb5, 0x85, + 0x4b, 0x9f, 0xb3, 0x9c, 0x06, 0xf4, 0xfa, 0x5b, 0x87, 0xee, 0x77, 0x73, 0x72, 0xe0, 0xd2, 0xd1, + 0x72, 0xb9, 0x9c, 0x41, 0xfa, 0xaa, 0x4a, 0x4e, 0x89, 0x91, 0x01, 0xe0, 0xf5, 0x87, 0xba, 0x80, + 0xb4, 0x8f, 0xfb, 0x87, 0x0c, 0x0e, 0x83, 0x76, 0x0a, 0xe7, 0xb4, 0x38, 0x98, 0x95, 0xe8, 0x8e, + 0x9a, 0xa7, 0x01, 0x8e, 0x47, 0x1f, 0x85, 0xb3, 0x9e, 0xa7, 0x8c, 0x1f, 0xa7, 0x1e, 0x6c, 0x51, + 0x1a, 0x37, 0x7a, 0x24, 0x38, 0x3e, 0xca, 0x7b, 0x08, 0x37, 0xe7, 0xbc, 0x10, 0x7f, 0x4e, 0x18, + 0x27, 0x34, 0x85, 0x0b, 0xad, 0xfb, 0x0d, 0x01, 0x67, 0x4f, 0x6f, 0xa6, 0x59, 0x03, 0x9c, 0x0c, + 0x6b, 0xd0, 0x1d, 0x28, 0xdb, 0xa7, 0x5f, 0xb5, 0x99, 0xf5, 0xd9, 0xb4, 0x40, 0x3f, 0x5f, 0x38, + 0xf7, 0x1f, 0xea, 0xfe, 0xf1, 0xc0, 0x4a, 0x35, 0x48, 0x77, 0xc2, 0xc6, 0x61, 0xdc, 0x01, 0xb7, + 0xb3, 0x2d, 0xc1, 0x56, 0x2c, 0x87, 0x80, 0xf3, 0xa7, 0x35, 0xba, 0x53, 0xad, 0x5e, 0x4e, 0x86, + 0x35, 0xe8, 0x6e, 0x4a, 0xd4, 0x6c, 0x5c, 0xc7, 0xac, 0x3b, 0xe5, 0xaa, 0xf1, 0x64, 0x0c, 0xeb, + 0xce, 0x0f, 0xf5, 0x2e, 0x5b, 0xb5, 0xfd, 0x99, 0x41, 0xde, 0xb8, 0xff, 0x89, 0x2c, 0x91, 0xb6, + 0x3f, 0x33, 0x80, 0x76, 0x23, 0x47, 0x1c, 0x6e, 0xc7, 0x1f, 0x69, 0x5b, 0x37, 0xf5, 0x37, 0x40, + 0x93, 0x84, 0x06, 0x68, 0xdb, 0x88, 0x36, 0xff, 0x6a, 0x44, 0xda, 0xbc, 0x60, 0x89, 0xba, 0x37, + 0x47, 0x47, 0x41, 0x5d, 0xca, 0x67, 0x13, 0xf7, 0xb2, 0xd0, 0x35, 0x14, 0x9f, 0x7f, 0xce, 0x6c, + 0xe4, 0x50, 0x7a, 0x19, 0x0a, 0x5c, 0xdf, 0xda, 0x83, 0x5f, 0x42, 0xcd, 0xe8, 0x4d, 0x51, 0xe1, + 0x46, 0xa6, 0xce, 0x19, 0x96, 0xa8, 0xbb, 0x1a, 0xeb, 0xdc, 0x81, 0x9b, 0xf5, 0xe2, 0xc0, 0x9b, + 0xe9, 0x41, 0xce, 0x43, 0xbe, 0xf7, 0x7e, 0x51, 0x23, 0x1e, 0xa6, 0xa0, 0xa2, 0x0d, 0xbf, 0xda, + 0x12, 0x1b, 0x83, 0xaa, 0x66, 0x5c, 0x1a, 0x5c, 0x87, 0x08, 0x16, 0xaa, 0x7b, 0xf7, 0xf3, 0x02, + 0xa4, 0x4e, 0xc7, 0x0d, 0xe2, 0x46, 0x4d, 0xa7, 0x6f, 0x57, 0x72, 0xe9, 0x4e, 0x3b, 0x34, 0xb8, + 0x57, 0x05, 0x82, 0xec, 0xf1, 0x43, 0x64, 0xb6, 0xed, 0x6a, 0x92, 0xa9, 0x9e, 0x86, 0x02, 0x16, + 0xaa, 0x3b, 0x40, 0xd7, 0xc3, 0x5c, 0xa4, 0x91, 0xd8, 0xcb, 0x9d, 0x32, 0xcc, 0xd3, 0x0a, 0xe9, + 0xde, 0xe4, 0xa2, 0x6d, 0x91, 0x77, 0xec, 0xfd, 0x33, 0xed, 0xfc, 0x32, 0x1c, 0xc3, 0x6e, 0xfb, + 0x5e, 0x7c, 0xf1, 0xac, 0xb2, 0x8d, 0x32, 0x30, 0x30, 0x82, 0x0c, 0xb6, 0xa7, 0xb3, 0x6f, 0x29, + 0x35, 0xa4, 0xd0, 0xd3, 0x7d, 0xe5, 0xb2, 0xa5, 0x96, 0x36, 0x7f, 0xb5, 0xe2, 0x9b, 0xf9, 0x48, + 0xac, 0x86, 0x40, 0x2f, 0x61, 0x46, 0x1a, 0x83, 0x7c, 0x74, 0xad, 0xcd, 0xe3, 0x47, 0xf7, 0x1b, + 0xb0, 0x0a, 0xb0, 0xb1, 0x40, 0x4c, 0xef, 0xde, 0x7b, 0x76, 0x5b, 0x8e, 0xe8, 0x10, 0x40, 0x26, + 0x42, 0x9f, 0x4e, 0x63, 0xa0, 0xb7, 0xb2, 0xb3, 0x93, 0xf9, 0x2e, 0x4d, 0x87, 0x0c, 0xd6, 0xc0, + 0xa9, 0x71, 0x36, 0xa3, 0x54, 0x54, 0xbd, 0x2b, 0x66, 0x01, 0x45, 0x3e, 0x93, 0x98, 0x70, 0x49, + 0xc0, 0x5a, 0x9d, 0x26, 0x40, 0xd3, 0x91, 0xfd, 0xc2, 0xfa, 0x14, 0x9b, 0x8f, 0x45, 0xb1, 0x5b, + 0x37, 0x2d, 0xde, 0xd4, 0xf5, 0x38, 0xa4, 0xc9, 0x97, 0xc4, 0x5e, 0x6e, 0x65, 0x9f, 0x4e, 0x20, + 0xab, 0x5c, 0x36, 0x63, 0xb2, 0xd8, 0x7d, 0xbb, 0xf4, 0xf2, 0x79, 0x73, 0x73, 0x64, 0xc2, 0x9b, + 0xaa, 0xbb, 0xd1, 0xd1, 0xd9, 0xa3, 0x28, 0x6f, 0xec, 0x2a, 0xac, 0x6e, 0x2f, 0xae, 0xeb, 0x6c, + 0xe9, 0x94, 0xb3, 0xdf, 0x60, 0x18, 0xde, 0x6a, 0xdd, 0x81, 0xd6, 0x07, 0xaf, 0x97, 0xee, 0x89, + 0x79, 0x64, 0xeb, 0x95, 0x4a, 0x2e, 0x86, 0x3a, 0x85, 0xe7, 0x84, 0x24, 0x15, 0x15, 0xd0, 0x94, + 0x8e, 0x0c, 0xc4, 0x08, 0xf2, 0x1e, 0x4f, 0xa7, 0x4f, 0x9d, 0x34, 0xe3, 0x4e, 0xe7, 0xa6, 0x71, + 0xf1, 0x17, 0x13, 0x0e, 0x1c, 0x8d, 0x45, 0xbb, 0xaa, 0x93, 0xf7, 0x56, 0xc7, 0x9d, 0x63, 0x48, + 0xf6, 0xb1, 0x1b, 0x9c, 0x46, 0xcf, 0xf1, 0xd0, 0x9d, 0xec, 0x05, 0x4a, 0x32, 0x3d, 0xdd, 0x5c, + 0x77, 0x7d, 0x7d, 0x13, 0xe1, 0x1f, 0x9b, 0xcf, 0xa5, 0xda, 0x45, 0x54, 0x3f, 0x30, 0xb3, 0xac, + 0x81, 0xa5, 0x10, 0xc5, 0x1d, 0x44, 0xdd, 0x6f, 0x64, 0x66, 0x00, 0xdd, 0x27, 0xd9, 0x8c, 0xa5, + 0x0c, 0xfd, 0xa6, 0xe3, 0x48, 0xa6, 0x88, 0xa0, 0xa6, 0xad, 0x77, 0x9a, 0xe7, 0xd9, 0x67, 0x67, + 0x6f, 0x55, 0xd4, 0xb4, 0x74, 0xc3, 0x8d, 0xe7, 0x14, 0xca, 0xfe, 0xfa, 0xd6, 0x9e, 0x94, 0x87, + 0x75, 0x7e, 0x97, 0x5e, 0xd8, 0x07, 0xdc, 0x20, 0x84, 0x3f, 0x96, 0x4a, 0xd1, 0x32, 0x21, 0x00, + 0xb4, 0xe3, 0x81, 0x45, 0xe5, 0xe5, 0x46, 0x89, 0xc0, 0x72, 0x70, 0x34, 0xa3, 0x0c, 0x57, 0xd0, + 0x21, 0xe4, 0x66, 0xfc, 0x2d, 0xf6, 0xdc, 0x23, 0xeb, 0x79, 0xe3, 0xce, 0x43, 0xf7, 0xf1, 0x1b, + 0x83, 0xcf, 0x3f, 0x33, 0x9c, 0x8c, 0x9e, 0xee, 0x91, 0x61, 0xa1, 0x48, 0x77, 0xca, 0x9d, 0xa9, + 0xdf, 0x5c, 0x84, 0x5d, 0x2b, 0xc1, 0xb5, 0xf3, 0xe7, 0xb9, 0x82, 0x46, 0x4c, 0x76, 0x85, 0x9e, + 0xf4, 0xe7, 0x28, 0x16, 0xc8, 0xe6, 0x05, 0x3d, 0xdd, 0x09, 0x8d, 0x04, 0x69, 0xd7, 0xcd, 0xdc, + 0x88, 0x61, 0x2c, 0x3c, 0x29, 0x6b, 0xc5, 0x55, 0xab, 0x69, 0x14, 0x92, 0x4d, 0x97, 0x88, 0xbb, + 0xec, 0xb0, 0x9d, 0xc8, 0xf7, 0x26, 0xd3, 0xee, 0xee, 0xcd, 0x05, 0x14, 0xdf, 0xd5, 0xbd, 0xc1, + 0x81, 0x86, 0x58, 0xb4, 0x40, 0xd8, 0xfa, 0xa4, 0x23, 0xbd, 0x24, 0x6d, 0x3d, 0xec, 0x37, 0xd0, + 0x40, 0xa5, 0xea, 0xb7, 0xc3, 0x56, 0xdb, 0xbb, 0x57, 0xdc, 0x24, 0xd8, 0x94, 0xf5, 0xeb, 0x0e, + 0x37, 0x8b, 0xd0, 0x2a, 0x55, 0x24, 0x21, 0x07, 0x50, 0x75, 0xcb, 0x9a, 0x0e, 0x86, 0x56, 0xaf, + 0xf9, 0xbe, 0x74, 0xea, 0xc7, 0x25, 0xe3, 0x46, 0x96, 0x4e, 0xfc, 0xb0, 0x62, 0xe9, 0xd7, 0x4d, + 0x07, 0x42, 0x95, 0x52, 0xea, 0xdd, 0x3b, 0x6d, 0x07, 0x53, 0xbd, 0x43, 0x08, 0xef, 0x65, 0x67, + 0x10, 0xac, 0x5c, 0xf7, 0xaa, 0x26, 0xdd, 0xa2, 0x6e, 0x94, 0xfb, 0x74, 0x48, 0x42, 0x03, 0x19, + 0x1a, 0x76, 0xd0, 0xce, 0x01, 0x38, 0xb2, 0x9f, 0x89, 0x91, 0xcd, 0xa8, 0xd4, 0x52, 0x61, 0xc4, + 0xac, 0x5c, 0x77, 0xef, 0xb3, 0xba, 0x71, 0xf4, 0xe4, 0xab, 0xa2, 0x6f, 0xe6, 0xb1, 0xb6, 0xa9, + 0x95, 0xcd, 0x9c, 0xda, 0x5b, 0x46, 0xec, 0x3f, 0x71, 0x3f, 0xa1, 0xed, 0xb9, 0xb5, 0x15, 0xba, + 0x9a, 0xa1, 0x35, 0xeb, 0xae, 0x54, 0xe9, 0xe6, 0xbc, 0x1d, 0xbe, 0x4e, 0x2c, 0x77, 0xe3, 0x2d, + 0xfb, 0x20, 0x7b, 0x69, 0xf0, 0xf7, 0x6c, 0xbb, 0x9a, 0xd4, 0xf3, 0xf2, 0x9f, 0xf6, 0xd4, 0xab, + 0x70, 0x19, 0x78, 0xe4, 0x4a, 0xa7, 0x8e, 0x27, 0xdc, 0x5b, 0x8d, 0xad, 0x8d, 0x98, 0xca, 0x7f, + 0x60, 0xbe, 0xda, 0xba, 0x75, 0x3f, 0x83, 0x75, 0x07, 0x12, 0xd6, 0xf8, 0x95, 0x84, 0x07, 0x23, + 0x59, 0x6b, 0x6d, 0x7f, 0x23, 0xdf, 0xdb, 0xdf, 0xd7, 0x97, 0xb2, 0x7e, 0x35, 0x43, 0x97, 0xcb, + 0xf6, 0x03, 0x7f, 0x6b, 0xb3, 0xaf, 0x0b, 0x85, 0xe4, 0xdb, 0x59, 0x61, 0xcd, 0xba, 0xfb, 0x5f, + 0xf8, 0x07, 0x4a, 0xe3, 0xa6, 0x3f, 0x53, 0xa7, 0x4f, 0x5c, 0xa7, 0x13, 0x9d, 0x66, 0x28, 0xc7, + 0x86, 0x75, 0x6b, 0xfb, 0x95, 0xca, 0x06, 0x3f, 0x0f, 0x14, 0x92, 0xf0, 0x99, 0x8d, 0x18, 0x1c, + 0xbc, 0x66, 0xef, 0x9f, 0x29, 0x80, 0x9b, 0x35, 0xeb, 0x8e, 0x6a, 0xf9, 0xfb, 0x52, 0xf4, 0xca, + 0xda, 0x68, 0xdc, 0x52, 0x09, 0xcd, 0xaa, 0xe8, 0x2b, 0x96, 0x2e, 0x46, 0xe7, 0x68, 0x90, 0xac, + 0x24, 0x22, 0x04, 0x0f, 0x53, 0x39, 0xb0, 0x6d, 0x03, 0x74, 0x32, 0x39, 0xef, 0x8e, 0x43, 0x6b, + 0xd6, 0x7d, 0xf3, 0xe0, 0xd4, 0xc0, 0xbf, 0xf5, 0x0b, 0xda, 0x95, 0x2b, 0x97, 0xa0, 0xfe, 0x42, + 0xdc, 0xbf, 0xbd, 0xad, 0x6d, 0xc7, 0x56, 0xc7, 0xb5, 0x3f, 0xfd, 0x80, 0x7b, 0xa2, 0x41, 0x4e, + 0x65, 0x33, 0x3f, 0xc1, 0xfd, 0xf1, 0x8f, 0xc7, 0xf3, 0x2a, 0xde, 0xcb, 0x34, 0x5b, 0xad, 0xee, + 0xf8, 0x14, 0x11, 0x91, 0x44, 0x86, 0x5f, 0x2a, 0x9d, 0x32, 0x6e, 0x70, 0x28, 0x47, 0x32, 0xee, + 0x0f, 0x6b, 0xe9, 0x0b, 0xe7, 0xcd, 0xc1, 0x3d, 0x65, 0xf9, 0x8f, 0xd0, 0xb7, 0x97, 0x10, 0x05, + 0x2a, 0xc8, 0xa7, 0x15, 0x34, 0xf0, 0xa5, 0x67, 0xb5, 0xba, 0x77, 0xf5, 0x2a, 0x90, 0xee, 0x0d, + 0xfa, 0x9b, 0xd2, 0xa0, 0x4c, 0xa6, 0xa7, 0xf8, 0x25, 0xee, 0x0f, 0xe7, 0x90, 0x6e, 0xfc, 0x75, + 0x3d, 0xee, 0xd9, 0x57, 0x5f, 0xab, 0x1b, 0xe7, 0xa4, 0xdf, 0x2d, 0x6e, 0xe7, 0xab, 0x5d, 0x04, + 0xf7, 0x72, 0x2e, 0xd7, 0xa9, 0x3f, 0x08, 0xc3, 0xba, 0xeb, 0xe1, 0x45, 0x21, 0x71, 0x9e, 0x78, + 0x5f, 0x7d, 0x1d, 0xab, 0xee, 0x09, 0xf7, 0x78, 0xaf, 0x8a, 0xfd, 0x96, 0xeb, 0xce, 0xde, 0x2a, + 0xc9, 0x25, 0xbd, 0x0f, 0xeb, 0xae, 0x83, 0x00, 0xdd, 0x17, 0xcc, 0x9d, 0xbd, 0x3f, 0x3c, 0x6c, + 0xf4, 0x7b, 0xff, 0x8b, 0x7b, 0x5a, 0x84, 0xee, 0x8f, 0xf3, 0xf2, 0x6e, 0x66, 0xf3, 0x5e, 0xd3, + 0xc9, 0x2c, 0x10, 0xa0, 0x3b, 0xc8, 0xdc, 0x3d, 0xdc, 0xdd, 0x4a, 0xf4, 0x97, 0x08, 0xb6, 0x08, + 0xdd, 0x2d, 0x6a, 0x1c, 0x19, 0x33, 0x84, 0xe9, 0x4e, 0xb6, 0x63, 0x7e, 0xdd, 0x9b, 0x9b, 0x9b, + 0xa6, 0x4d, 0x9e, 0xf0, 0xf3, 0x4f, 0x3f, 0xa6, 0x73, 0x58, 0x0f, 0xc8, 0xec, 0xb0, 0x1e, 0xdd, + 0xbf, 0x9e, 0x3f, 0xf7, 0xde, 0xdd, 0x3b, 0xed, 0xed, 0xed, 0x6f, 0x44, 0x17, 0xa0, 0xac, 0x57, + 0x69, 0x25, 0xba, 0xe3, 0xcb, 0xfc, 0xf5, 0x0e, 0xc1, 0x16, 0x5e, 0xc6, 0x85, 0x95, 0xe8, 0x9e, + 0x9c, 0x94, 0xf8, 0xd1, 0x07, 0xef, 0xc3, 0x45, 0x99, 0x3f, 0xfb, 0x64, 0xf2, 0xca, 0x65, 0x4b, + 0x81, 0xe7, 0xc6, 0x0d, 0xbf, 0x7c, 0x3a, 0x79, 0xa2, 0xcd, 0x87, 0x1f, 0x7c, 0xbb, 0x68, 0xc1, + 0x98, 0xf7, 0xdf, 0x01, 0x3e, 0xae, 0xce, 0xdb, 0x57, 0x2c, 0x5d, 0x02, 0xde, 0x8c, 0xde, 0x9e, + 0x1e, 0x77, 0x17, 0x67, 0x99, 0x4c, 0xb6, 0x2f, 0x84, 0x65, 0x23, 0x87, 0x21, 0x82, 0x00, 0xdd, + 0x29, 0x61, 0x66, 0xdd, 0x41, 0xde, 0x52, 0x5b, 0x5b, 0x83, 0xe6, 0xaf, 0xc2, 0xa4, 0x01, 0x77, + 0x21, 0x86, 0x4b, 0x33, 0xc3, 0x72, 0x4e, 0x5f, 0x5f, 0xdf, 0xeb, 0x57, 0xc5, 0xff, 0x9e, 0xf1, + 0x29, 0x38, 0x8f, 0x3e, 0x72, 0x18, 0xce, 0x6e, 0x35, 0x0b, 0x70, 0xdd, 0xc5, 0x52, 0xbd, 0x3e, + 0xd5, 0x37, 0x46, 0x77, 0x95, 0x4a, 0x45, 0x78, 0x07, 0x27, 0x7c, 0x34, 0x26, 0xfe, 0x6c, 0x1c, + 0xd4, 0x3d, 0xea, 0x40, 0x24, 0x38, 0xde, 0xb9, 0x7d, 0x1b, 0x1c, 0x41, 0xc2, 0x07, 0xc7, 0x2f, + 0x67, 0x7d, 0xae, 0x1e, 0x58, 0x24, 0x7b, 0xc6, 0xd4, 0xc9, 0x7c, 0x09, 0x19, 0x0b, 0xd6, 0xa0, + 0xfb, 0x96, 0xcd, 0xbf, 0xbb, 0xec, 0xd8, 0x86, 0xfb, 0xe4, 0x3f, 0x79, 0x8c, 0x76, 0xdd, 0xc6, + 0x75, 0x07, 0x29, 0x7d, 0xe6, 0xf4, 0xa9, 0x3b, 0x9c, 0x1c, 0x4b, 0x5e, 0xbf, 0xca, 0xbe, 0xa1, + 0x69, 0x98, 0x5e, 0xb6, 0xe4, 0x5b, 0xbe, 0x9c, 0x8c, 0x02, 0x6b, 0xd0, 0xfd, 0x4d, 0x04, 0x93, + 0xee, 0x83, 0x9b, 0x08, 0x0d, 0xeb, 0x6e, 0x7c, 0x0c, 0xeb, 0x6e, 0x1e, 0xc8, 0xe4, 0xc3, 0xba, + 0x9b, 0x03, 0xc3, 0xba, 0x9b, 0x07, 0xc3, 0xba, 0x9b, 0x07, 0x7c, 0x75, 0x07, 0xb5, 0x93, 0x45, + 0xf3, 0x35, 0xeb, 0x90, 0x5f, 0x88, 0x3f, 0xf7, 0xd7, 0x35, 0xdd, 0x0e, 0xa0, 0xc3, 0xba, 0xf3, + 0x03, 0x77, 0xdd, 0xe5, 0x72, 0xf9, 0xd4, 0x89, 0xe3, 0x41, 0xb1, 0x78, 0xc1, 0x5c, 0x4d, 0xcf, + 0x2a, 0xd0, 0x1d, 0x36, 0xbb, 0x96, 0x97, 0x6b, 0x36, 0xf8, 0xe4, 0xa2, 0xfb, 0xa5, 0x61, 0xdd, + 0x11, 0x38, 0xe9, 0x5e, 0xf4, 0x62, 0xc5, 0xd2, 0xc5, 0x68, 0xdc, 0x39, 0x41, 0x77, 0xe0, 0x26, + 0x8e, 0xfb, 0xb0, 0xbd, 0xa2, 0x5c, 0xa7, 0xbb, 0xfe, 0x7e, 0x94, 0xf6, 0xfe, 0x99, 0xd0, 0xfe, + 0xa9, 0x6c, 0xde, 0xb3, 0x9f, 0xde, 0x6a, 0xdd, 0xeb, 0x1f, 0xe4, 0x4e, 0x19, 0x3f, 0x0e, 0xa9, + 0xfc, 0xd5, 0x97, 0x5f, 0xc8, 0x64, 0xb2, 0xd8, 0xd3, 0xa7, 0xf0, 0x49, 0x00, 0x05, 0xb9, 0xf7, + 0x90, 0xee, 0xca, 0xd6, 0x16, 0xdc, 0xce, 0x8e, 0xc8, 0xbb, 0xd0, 0xbe, 0x1f, 0xd5, 0x88, 0x57, + 0x66, 0xbc, 0xd5, 0xba, 0x83, 0xf4, 0x0e, 0xfe, 0xad, 0xae, 0xaa, 0xfa, 0xe0, 0xdd, 0x7f, 0x91, + 0xd3, 0xfb, 0x95, 0x14, 0xed, 0x28, 0x0f, 0xa4, 0x7b, 0xe7, 0x9d, 0x9b, 0xb8, 0x1d, 0xdd, 0x7e, + 0xe7, 0x9e, 0xfc, 0x36, 0x99, 0x50, 0x0f, 0xeb, 0x8e, 0x3c, 0x6f, 0x64, 0x66, 0x20, 0xdd, 0x03, + 0x7c, 0x7d, 0xf0, 0xf0, 0x68, 0x04, 0x6b, 0xe3, 0x3e, 0xbd, 0xdd, 0xfb, 0xda, 0xbb, 0xfb, 0x50, + 0x14, 0x7c, 0xb7, 0xbe, 0x1d, 0xd6, 0x9d, 0x1d, 0x92, 0x88, 0x10, 0x18, 0xbe, 0xe2, 0xff, 0xe6, + 0x12, 0x2e, 0xed, 0x89, 0x7d, 0x8a, 0x62, 0x09, 0x4e, 0xe1, 0x31, 0xf3, 0x66, 0x58, 0x77, 0x3d, + 0xb4, 0x49, 0x29, 0x36, 0x65, 0x90, 0x3d, 0xd2, 0xad, 0x16, 0xa4, 0x18, 0xd8, 0x7d, 0x10, 0x07, + 0x8a, 0x05, 0xb8, 0x9d, 0x47, 0x1f, 0xa6, 0xe5, 0x73, 0x1a, 0x96, 0xcd, 0x34, 0xaf, 0x2c, 0xee, + 0xcc, 0x69, 0xd6, 0xfb, 0xfd, 0x13, 0x5e, 0x82, 0xe2, 0x14, 0x17, 0x07, 0xbe, 0xfe, 0x0e, 0x21, + 0x37, 0x3d, 0x62, 0x1e, 0x81, 0x5b, 0xce, 0xdd, 0xa9, 0x6a, 0xe4, 0xb0, 0xe9, 0x8b, 0x21, 0xe0, + 0xab, 0x3b, 0x28, 0xbf, 0x83, 0x5c, 0xde, 0xcf, 0xdb, 0x73, 0xca, 0x04, 0x9b, 0x2d, 0xb6, 0x7f, + 0x10, 0xac, 0x95, 0xcf, 0x9e, 0x0e, 0x6f, 0x11, 0xef, 0x71, 0x21, 0x5c, 0xea, 0x53, 0x28, 0x71, + 0xe9, 0xa1, 0xb3, 0xf5, 0x4a, 0xd3, 0x3a, 0x6f, 0x0a, 0x57, 0x54, 0xdb, 0xa1, 0xa7, 0xfb, 0x6e, + 0x37, 0x17, 0xbe, 0x8b, 0xfe, 0x00, 0x11, 0xc9, 0xb1, 0x72, 0x74, 0xe0, 0x97, 0xf0, 0xbf, 0xf4, + 0xe2, 0x79, 0xd5, 0x90, 0x2c, 0x00, 0xc0, 0x57, 0x77, 0xb7, 0x9d, 0x3b, 0x4e, 0x9f, 0xd4, 0x6e, + 0xd6, 0x41, 0xee, 0xb8, 0x6f, 0x3a, 0x14, 0x81, 0x92, 0xbc, 0xbc, 0xba, 0x8a, 0x70, 0xb5, 0xb7, + 0x4f, 0xb5, 0x3d, 0x3c, 0x87, 0xfb, 0x83, 0x13, 0x75, 0x57, 0x63, 0x49, 0x9e, 0xe3, 0x52, 0x57, + 0x86, 0xe8, 0x8e, 0x9c, 0x43, 0x50, 0xd6, 0xe5, 0x5c, 0xde, 0x55, 0x0f, 0x66, 0xe0, 0xba, 0xd7, + 0x73, 0xd0, 0x5d, 0xa9, 0x54, 0xc2, 0x07, 0xdf, 0xb0, 0x6e, 0x0d, 0xe5, 0x32, 0x01, 0x48, 0x77, + 0xd1, 0xc2, 0x39, 0xe4, 0xab, 0x6a, 0xcd, 0x36, 0x89, 0xa2, 0x9d, 0x07, 0xef, 0x09, 0xd4, 0xfd, + 0xfc, 0xb9, 0xb3, 0x20, 0xee, 0x8f, 0x3e, 0xa0, 0x1e, 0x18, 0x4e, 0x46, 0x4a, 0x5e, 0x1d, 0x97, + 0x4d, 0x84, 0x40, 0x4d, 0xfa, 0x74, 0xb6, 0x28, 0xe2, 0xea, 0x6b, 0x50, 0xd4, 0x75, 0x8e, 0xba, + 0x67, 0x8b, 0x4d, 0x04, 0x45, 0xce, 0xce, 0x37, 0x3d, 0xe1, 0x3e, 0xef, 0x01, 0x9e, 0x74, 0xe0, + 0xab, 0x3b, 0x42, 0xcc, 0xf1, 0x68, 0xca, 0x77, 0xbd, 0xed, 0x4a, 0x12, 0x92, 0xbe, 0x66, 0xe3, + 0x3a, 0xba, 0x78, 0xe5, 0x0a, 0xd5, 0x13, 0x91, 0xf4, 0xc6, 0xf3, 0x46, 0xc2, 0x26, 0x42, 0xf8, + 0x3e, 0x42, 0x75, 0xad, 0x3d, 0xd4, 0xeb, 0x42, 0xb4, 0x90, 0xbe, 0x1e, 0x46, 0x07, 0xf8, 0xcd, + 0xf7, 0x26, 0x17, 0xdb, 0xf9, 0x65, 0x12, 0xd4, 0xf7, 0x3d, 0x49, 0xdc, 0x6a, 0x42, 0x18, 0xf8, + 0xea, 0xbe, 0x79, 0xd3, 0x7f, 0xc3, 0xf6, 0x6a, 0x67, 0x16, 0xd0, 0xe5, 0xb1, 0x0d, 0xde, 0xbb, + 0x90, 0xf4, 0xd5, 0xeb, 0x29, 0x76, 0xe2, 0xe6, 0x8e, 0x11, 0xe4, 0x8d, 0x96, 0x22, 0xc2, 0x42, + 0x8d, 0xbe, 0x79, 0x13, 0x00, 0x1d, 0x83, 0x5b, 0x85, 0x8d, 0x3b, 0x22, 0x72, 0xf4, 0x13, 0x7e, + 0x46, 0x41, 0x05, 0xf5, 0x66, 0x4f, 0xdc, 0x21, 0x20, 0xbd, 0x2f, 0x5b, 0xf2, 0x2d, 0x5c, 0x20, + 0xa0, 0xf0, 0x1f, 0xda, 0xdd, 0xa3, 0xaa, 0x7e, 0x5c, 0x8a, 0xa4, 0xaf, 0x5a, 0xbd, 0x9c, 0x5c, + 0xbc, 0xe1, 0x08, 0x8a, 0xf4, 0x3e, 0x75, 0x12, 0x71, 0xfa, 0x9a, 0x09, 0x90, 0xf3, 0xa2, 0xd1, + 0xde, 0x2f, 0x03, 0x57, 0x5f, 0x40, 0xa3, 0x07, 0x0e, 0xc1, 0xf9, 0x0c, 0x2b, 0x44, 0xf3, 0x67, + 0x21, 0xe9, 0x81, 0x6b, 0x3a, 0x18, 0x26, 0xc0, 0x08, 0x51, 0x77, 0xf0, 0x49, 0x01, 0xbf, 0xb9, + 0x97, 0xc7, 0x2e, 0x01, 0xb6, 0x0c, 0x47, 0x64, 0x62, 0x21, 0x2e, 0x3d, 0xe5, 0x72, 0xa8, 0x1c, + 0xd1, 0x3d, 0x64, 0xba, 0x03, 0x54, 0xad, 0x5d, 0x89, 0x4b, 0x5f, 0x32, 0x61, 0x8c, 0x24, 0x34, + 0xb0, 0xe7, 0xb5, 0x01, 0xf5, 0xa6, 0x49, 0x36, 0x63, 0x79, 0x0d, 0x3e, 0x95, 0x84, 0x07, 0xd7, + 0xbb, 0x38, 0x11, 0x9d, 0xeb, 0xd6, 0x41, 0xb7, 0x4d, 0xeb, 0xdc, 0xb7, 0x37, 0xf8, 0x7a, 0x48, + 0x22, 0xf7, 0x4a, 0x13, 0xe2, 0x15, 0xcd, 0x4c, 0xd3, 0xfa, 0xcf, 0xdc, 0xd2, 0x5b, 0x0b, 0x35, + 0xe2, 0x1a, 0x71, 0xce, 0x2e, 0x47, 0x0c, 0xa9, 0xee, 0x00, 0x4d, 0x51, 0xe1, 0x7a, 0xd2, 0x0f, + 0xb8, 0xd2, 0x69, 0xe3, 0x2b, 0x97, 0x2e, 0xaa, 0x73, 0xda, 0x2c, 0xf6, 0x76, 0xc7, 0x37, 0x6c, + 0x12, 0x7b, 0x03, 0xe7, 0x8e, 0x5c, 0x6f, 0x49, 0xb1, 0x9e, 0xee, 0xe5, 0xe5, 0x65, 0xa8, 0x1c, + 0xf9, 0xfd, 0x77, 0xff, 0xe1, 0x12, 0x7d, 0x43, 0x80, 0x17, 0x39, 0x7a, 0x56, 0x57, 0xfe, 0xc5, + 0xa7, 0xa0, 0xf2, 0xdd, 0xf7, 0xff, 0xec, 0x9d, 0x89, 0x5f, 0x13, 0xd7, 0x16, 0xc7, 0xfb, 0x5f, + 0xbc, 0xf7, 0xda, 0x5a, 0x14, 0x77, 0xad, 0x5b, 0x15, 0xad, 0xf5, 0xa9, 0x7d, 0xad, 0xda, 0xd6, + 0x7d, 0xa9, 0xd6, 0x3e, 0x6b, 0xfb, 0xac, 0x56, 0x16, 0x51, 0xeb, 0xc6, 0xbe, 0x2f, 0x82, 0xec, + 0x82, 0x80, 0x80, 0x28, 0xe0, 0x02, 0x88, 0x0b, 0x0a, 0x88, 0x82, 0x08, 0xa8, 0xb8, 0x20, 0x04, + 0x65, 0x87, 0x04, 0x42, 0x20, 0x40, 0x16, 0x08, 0x7b, 0xf6, 0xc4, 0x77, 0x71, 0x70, 0x98, 0xcc, + 0x4c, 0x26, 0x93, 0x98, 0x15, 0xe7, 0xfb, 0x39, 0x1f, 0x3f, 0x71, 0x32, 0x73, 0xb9, 0x73, 0xf2, + 0xcb, 0xcd, 0xbd, 0x73, 0xef, 0x3d, 0x87, 0xd3, 0x85, 0x5b, 0x66, 0x45, 0x8b, 0x4a, 0xf8, 0x8c, + 0x4a, 0xba, 0x2e, 0x6d, 0xa8, 0xa1, 0xfd, 0x0e, 0x50, 0x0c, 0x0f, 0x75, 0x9d, 0x70, 0xd4, 0xe1, + 0xf6, 0xc5, 0x4d, 0xaa, 0x7e, 0x87, 0x1e, 0xcb, 0xc1, 0x26, 0x25, 0x11, 0xe5, 0x52, 0x37, 0xbf, + 0xc3, 0xc6, 0xf1, 0x76, 0x95, 0x0f, 0x0e, 0x62, 0x8b, 0xa5, 0x77, 0x0d, 0x21, 0x5d, 0xaf, 0x43, + 0x00, 0x65, 0x23, 0xf8, 0x1d, 0x42, 0xca, 0xe9, 0x06, 0x8d, 0x0c, 0x50, 0xba, 0x8e, 0x7e, 0x07, + 0x5e, 0x86, 0x72, 0xc3, 0x1d, 0x76, 0xb0, 0x83, 0x5e, 0x30, 0x18, 0x9a, 0x83, 0x1e, 0x80, 0xaf, + 0x1b, 0x99, 0x24, 0x71, 0xe0, 0xe3, 0xe9, 0x3a, 0x71, 0xb8, 0x6d, 0xf3, 0x0f, 0xa0, 0x29, 0xc4, + 0xd6, 0x03, 0x37, 0xae, 0x66, 0x35, 0x73, 0x5c, 0xf5, 0xc7, 0x23, 0x4a, 0xb5, 0x75, 0x87, 0x8a, + 0xdf, 0x05, 0x06, 0xf4, 0x3b, 0x0c, 0xe8, 0xdb, 0xf4, 0x65, 0x5d, 0x03, 0x23, 0x5b, 0x5e, 0x68, + 0x20, 0xc7, 0xc7, 0x95, 0x20, 0x4f, 0x1c, 0xba, 0x9d, 0x81, 0xf0, 0xf5, 0xd6, 0x31, 0x0c, 0x2f, + 0x49, 0xa4, 0x5d, 0x9d, 0xdc, 0x20, 0x1f, 0xb4, 0xf0, 0xfd, 0xdc, 0xb1, 0x67, 0xc6, 0xe4, 0x34, + 0x21, 0x1a, 0x7a, 0xed, 0x7e, 0x63, 0x8d, 0xef, 0x77, 0xad, 0x30, 0x81, 0xdf, 0x21, 0x40, 0xd3, + 0x81, 0x0c, 0x12, 0x00, 0x8c, 0x6d, 0xff, 0x27, 0xf6, 0x34, 0xa7, 0xa4, 0x57, 0x63, 0xee, 0xd3, + 0x32, 0x81, 0x35, 0x39, 0xbf, 0xeb, 0x12, 0x5a, 0x40, 0x2f, 0x98, 0xcc, 0xef, 0x10, 0x52, 0x2e, + 0x67, 0x34, 0xda, 0xce, 0x7b, 0xd7, 0x83, 0x9e, 0x00, 0xea, 0x84, 0x11, 0xc4, 0xb6, 0x0d, 0xdf, + 0x4c, 0xf4, 0xb6, 0x47, 0x02, 0x28, 0xbf, 0x6b, 0x86, 0xb1, 0x64, 0xde, 0xf8, 0x30, 0x24, 0x26, + 0x1c, 0xf5, 0xae, 0xe7, 0xd5, 0xf1, 0x4e, 0x3d, 0xf9, 0x32, 0x29, 0xbf, 0x93, 0x82, 0xb5, 0x7b, + 0x1b, 0xec, 0x7a, 0x51, 0x23, 0x3a, 0xe9, 0x24, 0xec, 0xc1, 0xd0, 0x6c, 0xb2, 0xad, 0xbc, 0x88, + 0xf2, 0x3b, 0x19, 0x14, 0x42, 0x21, 0xec, 0x77, 0xd6, 0xcf, 0xe8, 0xf4, 0x3e, 0xae, 0x29, 0x63, + 0x31, 0xc2, 0x1c, 0xfc, 0xc9, 0x06, 0x1d, 0xa1, 0xfc, 0x4e, 0x96, 0xbe, 0x8c, 0xcb, 0xb0, 0xeb, + 0x51, 0x79, 0x20, 0x68, 0x88, 0x91, 0x14, 0xbb, 0x97, 0x54, 0x0c, 0x5d, 0xca, 0xef, 0x5a, 0xc0, + 0x5c, 0xff, 0x1d, 0xe4, 0x91, 0xf6, 0xdd, 0xdb, 0x50, 0x6f, 0xc1, 0x8b, 0xb3, 0xa2, 0x30, 0xd9, + 0x52, 0x70, 0x41, 0xfa, 0xbd, 0x93, 0xf2, 0x3b, 0x31, 0xfd, 0xb7, 0xb3, 0x60, 0xc9, 0x2b, 0xc4, + 0x2a, 0xce, 0x72, 0x49, 0x1e, 0x0b, 0xa6, 0xec, 0x9e, 0x5c, 0x41, 0xa6, 0xa8, 0x09, 0xe5, 0xf7, + 0x4e, 0x36, 0xfb, 0xd6, 0x4d, 0x1d, 0xb3, 0xee, 0x91, 0x84, 0xfe, 0xe5, 0x34, 0xc8, 0x29, 0xbd, + 0x97, 0x12, 0x91, 0xc7, 0xa3, 0xee, 0x8e, 0x8d, 0xa1, 0x48, 0xc6, 0x95, 0x9a, 0x50, 0x7e, 0x9f, + 0x37, 0x6b, 0xba, 0xa1, 0x37, 0x0d, 0x77, 0x9d, 0x3c, 0x0c, 0x39, 0xa5, 0xdb, 0xc3, 0x09, 0x79, + 0xbc, 0xae, 0x63, 0x50, 0xab, 0xc7, 0x35, 0x13, 0xca, 0xef, 0x56, 0x9f, 0xfe, 0xe3, 0x72, 0x6a, + 0xca, 0x51, 0x47, 0x07, 0xc3, 0x55, 0x08, 0x7e, 0xbe, 0xda, 0xb2, 0x72, 0x29, 0xf2, 0xf8, 0x90, + 0x48, 0xed, 0xbe, 0x77, 0x5c, 0x26, 0x8e, 0xdf, 0x23, 0x42, 0x43, 0xdc, 0x9c, 0x47, 0x35, 0x68, + 0xd0, 0xd0, 0x10, 0x83, 0x0f, 0xf2, 0x20, 0xa7, 0xd0, 0xe7, 0x4e, 0x45, 0xbd, 0x05, 0xfb, 0xb1, + 0xba, 0x5d, 0x73, 0x3c, 0xaf, 0x89, 0xe3, 0x77, 0xd8, 0xdd, 0x0b, 0xe6, 0xce, 0x82, 0x76, 0x4d, + 0x62, 0xf1, 0xf3, 0xf6, 0xc2, 0x3d, 0x4e, 0x1e, 0x51, 0x53, 0x3d, 0xfc, 0xd3, 0x8a, 0x7a, 0xeb, + 0xa0, 0xfb, 0x58, 0x4c, 0xe4, 0x17, 0x74, 0xfc, 0xc8, 0xbd, 0x2a, 0xe5, 0x4c, 0x0c, 0xbf, 0xf7, + 0xf7, 0xf5, 0x41, 0x6b, 0x4b, 0x80, 0xfd, 0xb4, 0x76, 0x6c, 0x57, 0x7c, 0x56, 0x66, 0xc6, 0xc6, + 0x1f, 0xd7, 0xce, 0xb4, 0xb6, 0x3a, 0x7e, 0xc4, 0x71, 0xf6, 0x34, 0x6b, 0x70, 0x04, 0xbc, 0x1e, + 0x2d, 0xc1, 0xcb, 0x63, 0xc5, 0xd2, 0x25, 0x8f, 0x4b, 0x48, 0x65, 0x63, 0x46, 0x21, 0xa6, 0x37, + 0xa9, 0xf5, 0xfb, 0xfb, 0x58, 0xd4, 0x4f, 0x1a, 0x34, 0xcf, 0x84, 0x4c, 0x10, 0xbf, 0xaf, 0x5f, + 0xf7, 0x7d, 0x5a, 0xca, 0x25, 0xe4, 0x3e, 0x79, 0xb9, 0x5c, 0x0e, 0xfc, 0x0e, 0xde, 0x6a, 0x6c, + 0xa8, 0xe7, 0xf1, 0x46, 0xe3, 0x4c, 0x97, 0xbf, 0x7c, 0x01, 0xf9, 0xfd, 0xce, 0xed, 0x5b, 0xe0, + 0x5f, 0x28, 0x15, 0xb1, 0xb6, 0x90, 0xf1, 0xfb, 0xe3, 0x7a, 0xcd, 0x01, 0xc0, 0x27, 0x88, 0xdf, + 0x51, 0x6d, 0xba, 0xfd, 0x5f, 0xfb, 0xf7, 0xfc, 0xb2, 0x13, 0xf2, 0x3b, 0x9d, 0xde, 0x2c, 0x14, + 0x8e, 0x06, 0x0a, 0x7c, 0xf9, 0xe2, 0x39, 0xe4, 0x77, 0x48, 0xe9, 0xa8, 0x80, 0x80, 0x24, 0x11, + 0x33, 0x9a, 0x35, 0xfa, 0xbd, 0x54, 0x4b, 0xbf, 0xb3, 0x2d, 0xd4, 0xef, 0x39, 0x77, 0xb2, 0x51, + 0x8b, 0x3b, 0xa0, 0x88, 0x05, 0x94, 0xdf, 0x75, 0xc6, 0xbc, 0xc6, 0xab, 0x6f, 0x49, 0xfa, 0xbd, + 0x8e, 0xf2, 0xbb, 0xbe, 0x11, 0xb7, 0xd0, 0xd5, 0xfb, 0x7d, 0x2c, 0xab, 0x58, 0x49, 0x1d, 0x4e, + 0xda, 0x02, 0x14, 0xa4, 0xfc, 0x5e, 0x47, 0xf9, 0xfd, 0x3d, 0x64, 0xfc, 0x5e, 0x54, 0xc3, 0xd5, + 0x58, 0x0e, 0xa5, 0x77, 0xed, 0x90, 0xb2, 0xdb, 0x61, 0xbf, 0x2b, 0x65, 0x32, 0xe4, 0x5b, 0x76, + 0x3e, 0x63, 0xe9, 0x51, 0x92, 0x1e, 0x30, 0x34, 0x96, 0xd3, 0xd1, 0x3b, 0x1e, 0x1a, 0xbf, 0x7f, + 0x44, 0x65, 0x41, 0xca, 0xf8, 0x7a, 0x0a, 0xba, 0x7e, 0x92, 0x60, 0xe9, 0x80, 0xd9, 0xf9, 0x5d, + 0x21, 0x1c, 0x81, 0xfd, 0x22, 0xe5, 0xa8, 0xec, 0x59, 0x71, 0x8e, 0x7b, 0x0e, 0xf9, 0xd1, 0xed, + 0x62, 0xa5, 0xc6, 0x72, 0x62, 0xf3, 0xde, 0x27, 0x6c, 0x52, 0x9d, 0x10, 0x97, 0xf5, 0xf0, 0xe1, + 0xf2, 0x65, 0x3c, 0xcd, 0xdf, 0x1b, 0x03, 0x61, 0x76, 0x7e, 0x07, 0xd0, 0xe7, 0x4e, 0x85, 0xfc, + 0xd2, 0x97, 0x75, 0x0d, 0x79, 0xfc, 0x4c, 0xf6, 0xf8, 0xb2, 0x0e, 0xae, 0xa6, 0x47, 0x34, 0xb6, + 0x9e, 0x63, 0x4b, 0xec, 0xbd, 0x53, 0xab, 0x90, 0xc7, 0x07, 0x1f, 0xde, 0x1f, 0xf3, 0xfb, 0x4c, + 0x2b, 0xbd, 0xd7, 0x9c, 0x3c, 0xe6, 0xe8, 0x77, 0xd6, 0x7f, 0x77, 0x40, 0xae, 0xc1, 0xa6, 0x93, + 0x81, 0x43, 0xba, 0x1f, 0x0b, 0x7d, 0x44, 0x50, 0x82, 0xe7, 0xe5, 0xf1, 0x74, 0x2a, 0xcf, 0x1a, + 0x55, 0x36, 0xfb, 0x72, 0x4f, 0x8f, 0x2d, 0xdd, 0x61, 0xae, 0xff, 0x4e, 0xef, 0x35, 0x27, 0x8f, + 0x39, 0xfa, 0x9d, 0x17, 0x15, 0x32, 0xf6, 0x48, 0xf2, 0x9b, 0xaf, 0x50, 0x6f, 0x05, 0xdf, 0x6e, + 0x84, 0x1d, 0x7a, 0x22, 0xfe, 0x25, 0x07, 0x2f, 0x09, 0x96, 0x4b, 0xca, 0x78, 0xc2, 0x26, 0x4f, + 0xcc, 0x2e, 0x06, 0xc6, 0xe2, 0xb9, 0x50, 0xe1, 0xbc, 0x88, 0x60, 0x43, 0xdd, 0x00, 0x09, 0xcc, + 0xd1, 0xef, 0xf2, 0x3e, 0x01, 0xdc, 0x04, 0x0f, 0x3d, 0x2a, 0x40, 0xbd, 0xeb, 0x72, 0x71, 0x7c, + 0xcb, 0x28, 0x30, 0xd7, 0xa4, 0x57, 0xe1, 0x77, 0x9b, 0x32, 0x9e, 0xb6, 0x9f, 0xcd, 0x69, 0xf2, + 0x48, 0xa1, 0xc1, 0xcf, 0xce, 0x80, 0xd9, 0x79, 0xdf, 0x43, 0x5d, 0x3b, 0x54, 0xfa, 0x68, 0x7c, + 0xd5, 0x42, 0xcd, 0x1b, 0x63, 0xdd, 0x10, 0x0e, 0xe6, 0xe8, 0x77, 0x40, 0xfb, 0x6f, 0x3b, 0xc7, + 0x5a, 0x83, 0x8d, 0x6b, 0xb0, 0xef, 0xfa, 0xc6, 0x3e, 0x41, 0xba, 0x1e, 0xd7, 0x0e, 0xf9, 0x17, + 0x74, 0x60, 0x66, 0xc0, 0x59, 0xbb, 0xb7, 0xbe, 0x7f, 0xb8, 0x6f, 0x63, 0x94, 0xfb, 0x50, 0x8b, + 0x99, 0xfa, 0x7d, 0x20, 0xe7, 0x16, 0x2c, 0xcc, 0x9e, 0xb8, 0xb3, 0xd8, 0x13, 0x42, 0xee, 0x34, + 0x11, 0x38, 0x3d, 0x38, 0x0b, 0x67, 0xe1, 0x63, 0x4f, 0xfc, 0x59, 0xb8, 0xcc, 0xbe, 0x1b, 0xe9, + 0x86, 0xbf, 0x09, 0x22, 0xcc, 0xd4, 0xef, 0x80, 0x76, 0xc4, 0x4a, 0x26, 0xdc, 0xb5, 0xda, 0x0a, + 0xa5, 0xf2, 0x62, 0x51, 0xab, 0x4f, 0x4a, 0xa5, 0x7d, 0x40, 0xc1, 0xe8, 0xbe, 0xe4, 0x80, 0x42, + 0xb7, 0x0b, 0xa0, 0xcd, 0x69, 0x46, 0xa5, 0x6a, 0x82, 0x40, 0x76, 0x1f, 0x99, 0x3f, 0xfd, 0xc7, + 0xf0, 0xd5, 0xd7, 0x80, 0xf9, 0xfa, 0x5d, 0x21, 0x11, 0xc3, 0x9e, 0xa2, 0xcf, 0x9f, 0x21, 0xac, + 0xae, 0xd2, 0x7c, 0x8d, 0x1a, 0x24, 0x6d, 0x4c, 0xd0, 0xb0, 0x98, 0xc3, 0x70, 0x09, 0x66, 0xdc, + 0xef, 0x4a, 0xa5, 0x12, 0xb5, 0x4f, 0xfe, 0x52, 0xf2, 0x05, 0x13, 0xd6, 0x0c, 0x00, 0xfa, 0xef, + 0xb0, 0xb3, 0x9a, 0xf1, 0xd6, 0xef, 0x91, 0x41, 0x31, 0x32, 0xd2, 0x3c, 0x6b, 0xf2, 0x78, 0xab, + 0x85, 0xb7, 0xd6, 0xde, 0xf8, 0xa8, 0xe8, 0xdd, 0xee, 0xaf, 0xfd, 0xda, 0xee, 0x93, 0x37, 0x34, + 0xfc, 0xf7, 0x7d, 0x4a, 0xc8, 0xf8, 0xd1, 0x61, 0x5a, 0x5d, 0xde, 0x93, 0x70, 0x0e, 0x79, 0x39, + 0x2f, 0xd4, 0x34, 0x41, 0xa2, 0xb1, 0xa8, 0xdd, 0x27, 0xff, 0xaa, 0xfc, 0xa5, 0x49, 0x2a, 0x84, + 0x42, 0xa9, 0x50, 0xf0, 0x11, 0xe9, 0xc5, 0x80, 0x31, 0xbe, 0x5e, 0x88, 0xca, 0xbb, 0x84, 0xcb, + 0x40, 0xde, 0x5d, 0xe6, 0xda, 0x55, 0x2a, 0x4e, 0x0f, 0x0b, 0x54, 0xca, 0xb5, 0x4e, 0x2c, 0x66, + 0x20, 0xd0, 0x7e, 0x8f, 0x8d, 0x3e, 0x0b, 0x9c, 0x3e, 0xcd, 0xea, 0x73, 0x93, 0xd4, 0x46, 0x1d, + 0x3d, 0x09, 0x31, 0x48, 0x0f, 0x8e, 0x7a, 0xdf, 0x66, 0x1e, 0x18, 0x79, 0xf6, 0xdf, 0xcc, 0x90, + 0x71, 0x39, 0xe0, 0xb3, 0x79, 0xfb, 0xee, 0x13, 0x02, 0x1d, 0xff, 0x81, 0xbc, 0x3b, 0x1c, 0x7f, + 0x4f, 0x86, 0xcd, 0x7c, 0xd4, 0xf9, 0xbd, 0xc9, 0xf1, 0xa6, 0xbe, 0x09, 0x15, 0xf0, 0xf7, 0xc9, + 0xf7, 0x10, 0x6e, 0x75, 0x34, 0x09, 0xe2, 0x16, 0x3a, 0x6b, 0xc7, 0x46, 0x94, 0x37, 0xc9, 0x18, + 0x73, 0xf3, 0x3a, 0x31, 0xbd, 0xd1, 0xd4, 0xd5, 0x47, 0xf3, 0x49, 0xfe, 0xbd, 0x7b, 0x28, 0xb3, + 0x3d, 0xb0, 0x1f, 0x7b, 0xd0, 0x4c, 0x2c, 0x27, 0x2d, 0xe5, 0xa6, 0x87, 0x6b, 0xd6, 0xb1, 0xc3, + 0xea, 0x0c, 0xe9, 0xf1, 0xb6, 0xad, 0x3f, 0x0d, 0x95, 0x14, 0x69, 0xf6, 0x81, 0x29, 0xc0, 0xd1, + 0xbb, 0xf9, 0x23, 0x65, 0x77, 0xf0, 0x22, 0xcf, 0xb4, 0x6d, 0x5a, 0x07, 0xcf, 0x60, 0x8c, 0xdb, + 0x8c, 0x2f, 0xda, 0x36, 0xaf, 0xe3, 0x45, 0x85, 0x80, 0xef, 0x87, 0xa9, 0xab, 0x49, 0x84, 0x45, + 0xfa, 0x1d, 0x09, 0x68, 0xd3, 0x25, 0xad, 0x0c, 0x71, 0x53, 0xbd, 0x84, 0xd9, 0x82, 0x3b, 0xbc, + 0x32, 0x4f, 0x2c, 0xde, 0xef, 0x14, 0x1f, 0x8e, 0x50, 0x22, 0x6f, 0xe8, 0x1c, 0xcc, 0x2e, 0xef, + 0x8c, 0xcc, 0x69, 0xf6, 0xcf, 0xaa, 0xf7, 0xcf, 0xa8, 0xf1, 0x4e, 0xab, 0xf2, 0x48, 0x7e, 0xe5, + 0x92, 0xf0, 0xf2, 0xe4, 0xb9, 0x67, 0xce, 0xf1, 0xcf, 0xdd, 0x93, 0xca, 0xbd, 0x53, 0x68, 0x3e, + 0x57, 0xde, 0xf8, 0x67, 0xd6, 0x05, 0xdd, 0x6a, 0x4c, 0x2a, 0x6c, 0x2d, 0x6b, 0xec, 0x69, 0xef, + 0x11, 0x8a, 0xa5, 0xe6, 0xd2, 0x3f, 0x24, 0x09, 0x25, 0xf7, 0x8f, 0x91, 0x4a, 0x66, 0x7f, 0x78, + 0x0e, 0xdd, 0xe7, 0x6a, 0xf5, 0xa1, 0xa0, 0x22, 0x5b, 0xcf, 0x3c, 0x8d, 0xcf, 0x76, 0x09, 0xcc, + 0xd6, 0x3b, 0xdf, 0x29, 0xf6, 0x79, 0x40, 0x56, 0xfd, 0xed, 0x97, 0x9d, 0x03, 0x42, 0xcd, 0x71, + 0x15, 0x4c, 0xcb, 0x27, 0x4a, 0x25, 0x18, 0xc2, 0x69, 0x66, 0xcd, 0xb7, 0x2b, 0x83, 0x02, 0xfc, + 0xc9, 0x9c, 0x49, 0x61, 0x9e, 0xbc, 0x66, 0xf6, 0x85, 0x67, 0x37, 0x38, 0xc7, 0x3d, 0x3b, 0xe8, + 0x76, 0x97, 0x84, 0xe9, 0xa8, 0x7e, 0x3b, 0xef, 0x7b, 0x41, 0x19, 0xd5, 0x29, 0x25, 0xac, 0xde, + 0x21, 0x89, 0x66, 0xf5, 0x19, 0x1d, 0x52, 0xad, 0xfb, 0x2f, 0x3b, 0xb6, 0x41, 0x4f, 0x6c, 0x62, + 0xa3, 0x71, 0xe6, 0x1f, 0x28, 0xcc, 0x99, 0x56, 0xee, 0xf0, 0xe9, 0x9b, 0x0d, 0xa4, 0x62, 0x5b, + 0xba, 0xe7, 0xda, 0x07, 0x14, 0x1e, 0x8d, 0x7c, 0xec, 0x7a, 0xe1, 0x95, 0x47, 0xda, 0x6b, 0xaf, + 0x6b, 0x35, 0x3e, 0x19, 0xb5, 0xfe, 0xd7, 0xeb, 0xfd, 0xae, 0xd7, 0xf9, 0x66, 0xd4, 0xfa, 0x5c, + 0xab, 0xf1, 0x4a, 0xa5, 0x9d, 0x38, 0xf7, 0xcc, 0x21, 0xb0, 0xd0, 0xce, 0x3b, 0x9f, 0x8c, 0xf4, + 0xdd, 0x93, 0x2b, 0xe2, 0x1e, 0xb4, 0x98, 0xda, 0x01, 0x2a, 0x68, 0x90, 0xbb, 0x4c, 0x26, 0xfb, + 0x7e, 0xf5, 0x4a, 0xe4, 0x83, 0x61, 0x2f, 0x77, 0x57, 0xe3, 0xd4, 0x8c, 0xe2, 0x03, 0x49, 0x29, + 0x6e, 0x73, 0x3a, 0xf3, 0x90, 0x68, 0xfa, 0x39, 0xb8, 0xc8, 0xe5, 0x22, 0x2d, 0xe2, 0x4e, 0xe3, + 0xf3, 0xa6, 0xde, 0x41, 0x91, 0x4c, 0x73, 0x89, 0x08, 0xe4, 0x0a, 0x25, 0x83, 0x33, 0x7c, 0xa5, + 0xb4, 0x0d, 0x7c, 0x25, 0x8e, 0x84, 0x15, 0x13, 0xfc, 0x15, 0x87, 0xc0, 0x87, 0xbe, 0x99, 0x75, + 0x8d, 0x9d, 0x43, 0x06, 0xba, 0x4d, 0xad, 0x20, 0x92, 0xfb, 0xf0, 0xf0, 0xf0, 0x92, 0x85, 0xf3, + 0x50, 0xf3, 0x4f, 0xc0, 0xec, 0xfe, 0xda, 0x6f, 0xb4, 0xfa, 0x51, 0xe8, 0xc0, 0xe5, 0xe2, 0x56, + 0xc7, 0xc0, 0x42, 0x75, 0x4d, 0xb8, 0x73, 0xdc, 0xf3, 0xf4, 0x52, 0x26, 0x7f, 0x50, 0xcf, 0x9d, + 0x0d, 0x5a, 0x8b, 0xc0, 0x2f, 0xb3, 0xce, 0xc1, 0xef, 0xbe, 0x3a, 0xdd, 0xbb, 0x5f, 0xa9, 0x16, + 0x4b, 0xb4, 0xfb, 0x52, 0xe9, 0x1d, 0xb5, 0x72, 0xe7, 0x72, 0x38, 0xb3, 0xa7, 0x4d, 0xc1, 0x6a, + 0x1d, 0xb2, 0xed, 0x98, 0xf7, 0xe3, 0xca, 0x00, 0x00, 0x15, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x9b, + 0x37, 0xea, 0x10, 0x65, 0x8d, 0xc2, 0xd0, 0x94, 0x56, 0x73, 0x8e, 0x45, 0x97, 0xe1, 0xaa, 0xcd, + 0x29, 0xb9, 0xf2, 0x5a, 0x09, 0xd3, 0x08, 0x75, 0x60, 0x72, 0x87, 0x42, 0x33, 0x6b, 0xd4, 0x89, + 0xde, 0x3f, 0xa3, 0x76, 0x60, 0xc4, 0x64, 0x23, 0x5a, 0x7c, 0xb9, 0x83, 0x3e, 0x4c, 0x72, 0x62, + 0x42, 0x42, 0x7c, 0x2c, 0x6c, 0xbb, 0x77, 0x6e, 0x77, 0x73, 0x3a, 0x85, 0x3c, 0x92, 0x97, 0x73, + 0xd7, 0xc8, 0x75, 0xa5, 0x20, 0x00, 0xf4, 0x16, 0xfc, 0xd3, 0xaa, 0x70, 0x15, 0xe6, 0x9b, 0x4a, + 0x13, 0x0c, 0x9a, 0x20, 0x21, 0x77, 0x54, 0x4e, 0xb3, 0x63, 0x60, 0x01, 0xb6, 0x3e, 0x87, 0x43, + 0x8a, 0xb3, 0x9f, 0xeb, 0x2d, 0xe2, 0xb9, 0x56, 0x90, 0x7d, 0x10, 0x19, 0x7c, 0x3a, 0xe0, 0xd9, + 0xd3, 0xa7, 0x06, 0xad, 0x0a, 0x85, 0xce, 0xe4, 0x55, 0x74, 0xe1, 0x0e, 0x46, 0x3d, 0x92, 0xca, + 0x69, 0xad, 0x9a, 0xf7, 0xb4, 0x23, 0x91, 0x76, 0xb1, 0x45, 0x75, 0x35, 0x7d, 0x59, 0xe9, 0xbd, + 0x17, 0xe2, 0xf9, 0xe7, 0x22, 0xf9, 0x51, 0x21, 0xfc, 0x73, 0x11, 0xbd, 0x17, 0xe2, 0xfa, 0xb2, + 0xae, 0x0d, 0xbf, 0x28, 0x43, 0xed, 0x00, 0x21, 0x43, 0xc8, 0xed, 0x46, 0x78, 0x23, 0x20, 0xd2, + 0x7c, 0xb4, 0x4f, 0x76, 0xf5, 0xe1, 0x50, 0x72, 0xb7, 0x78, 0xe2, 0xee, 0x33, 0xb0, 0x69, 0x23, + 0x6c, 0xdd, 0x73, 0x42, 0x49, 0x47, 0xb2, 0x1e, 0xc8, 0xcd, 0xe6, 0x06, 0x78, 0xc1, 0xd9, 0xa9, + 0x34, 0x5a, 0xeb, 0xea, 0x65, 0x1c, 0x6f, 0x17, 0x41, 0x7a, 0x9a, 0x8c, 0xaf, 0x79, 0x3f, 0x25, + 0x60, 0x70, 0x44, 0xea, 0x96, 0xfa, 0x1a, 0xab, 0x78, 0xaf, 0xb4, 0xaa, 0x1e, 0x7d, 0x0f, 0x21, + 0x88, 0xa1, 0xe4, 0x6e, 0xd9, 0xc4, 0x03, 0xad, 0x7b, 0xa3, 0xb5, 0xee, 0x94, 0x54, 0xc1, 0xe6, + 0x8f, 0x68, 0xbc, 0xb6, 0x3f, 0xfb, 0x46, 0xc7, 0x81, 0xbd, 0xcd, 0xef, 0xf7, 0x90, 0xe9, 0x66, + 0x6d, 0x1b, 0xd6, 0x08, 0xd2, 0x92, 0xc9, 0x54, 0x35, 0xf8, 0x66, 0x03, 0xb6, 0x99, 0xf7, 0x48, + 0xae, 0x60, 0x70, 0x86, 0x3f, 0xd8, 0x0d, 0x64, 0xa1, 0xe4, 0x6e, 0xc1, 0x5c, 0x2f, 0xeb, 0xb0, + 0x0f, 0x40, 0x3f, 0x81, 0x71, 0x4e, 0x2c, 0x6f, 0xd1, 0x24, 0x20, 0xc1, 0xd5, 0x14, 0xe4, 0x6a, + 0x74, 0xac, 0xd1, 0xe7, 0x4e, 0x6d, 0x5d, 0xb5, 0x0c, 0x48, 0xb9, 0x6d, 0xdb, 0x7a, 0xe6, 0xfa, + 0xef, 0x5b, 0xbf, 0x5f, 0x41, 0x5f, 0x30, 0x93, 0x68, 0x25, 0xde, 0x86, 0xef, 0x05, 0x29, 0x9a, + 0x97, 0x8c, 0x47, 0xe7, 0x36, 0x63, 0x9f, 0xd9, 0xfb, 0xa4, 0xd7, 0x48, 0x64, 0x0a, 0x3d, 0xb9, + 0x44, 0x03, 0x94, 0xdc, 0x2d, 0x95, 0x3a, 0xf6, 0xa0, 0x7b, 0xe2, 0x4b, 0x94, 0x74, 0x4e, 0xc4, + 0x94, 0x55, 0xb6, 0x12, 0xa5, 0xff, 0x91, 0xb0, 0xda, 0x3a, 0x8f, 0xda, 0xe1, 0xb7, 0xd3, 0x9b, + 0x7f, 0xe0, 0x85, 0xf8, 0x0f, 0x97, 0x3d, 0x56, 0x08, 0xf1, 0x7f, 0x19, 0x14, 0x42, 0xa1, 0xa8, + 0xe6, 0x0d, 0x2f, 0x3c, 0x08, 0xb9, 0xfd, 0x46, 0xa5, 0x84, 0x2d, 0x3f, 0x0e, 0x16, 0xa0, 0xf7, + 0xef, 0xa1, 0x18, 0x4f, 0x98, 0x0a, 0xf7, 0xbb, 0x3c, 0xef, 0x45, 0x6a, 0x19, 0x58, 0x5e, 0x67, + 0x28, 0xb9, 0x5b, 0x24, 0x0a, 0xa5, 0x12, 0xab, 0x1b, 0x7b, 0xbf, 0x07, 0x17, 0x8b, 0x98, 0x04, + 0x57, 0x0d, 0xe4, 0xde, 0x46, 0x6d, 0x03, 0x80, 0x8c, 0x6d, 0xb7, 0x4f, 0x54, 0xfd, 0x9a, 0xe4, + 0x9f, 0x66, 0xb3, 0x3b, 0x1c, 0x0e, 0x1e, 0x90, 0x72, 0xba, 0x38, 0x3e, 0x6e, 0x38, 0x3f, 0x0b, + 0x8b, 0x66, 0xa3, 0x62, 0x7d, 0xa2, 0x10, 0x4b, 0x15, 0x9e, 0x57, 0xaa, 0x51, 0x35, 0x3f, 0x76, + 0xf6, 0x29, 0x8d, 0xf0, 0x5b, 0xaa, 0x2f, 0x28, 0xb9, 0x5b, 0x24, 0xcf, 0xe9, 0x02, 0xe7, 0x73, + 0xe8, 0xe7, 0xeb, 0xa0, 0x57, 0x40, 0xb0, 0x44, 0xb1, 0x3f, 0xe7, 0x56, 0xcb, 0xaa, 0xa5, 0x68, + 0x81, 0x2e, 0x98, 0xd9, 0x9b, 0x92, 0x44, 0xf2, 0x8f, 0x8a, 0xc5, 0xe2, 0x5d, 0xdb, 0xb7, 0x22, + 0x8f, 0x0c, 0x3e, 0xc8, 0x03, 0x1d, 0x1e, 0x54, 0x99, 0x2d, 0xcb, 0x17, 0xf5, 0xdf, 0xcc, 0x24, + 0x28, 0xe7, 0xd6, 0x0b, 0xb6, 0x43, 0x20, 0x7a, 0xba, 0x37, 0x48, 0x9b, 0x3c, 0xc1, 0x3a, 0x43, + 0xc9, 0xdd, 0x22, 0x89, 0xbe, 0x47, 0x47, 0xad, 0x64, 0xb4, 0x0f, 0x28, 0x48, 0x2b, 0x66, 0xaa, + 0x3b, 0x1f, 0xf4, 0x61, 0xba, 0x9d, 0xff, 0x46, 0x3f, 0x60, 0x59, 0xbb, 0x52, 0x63, 0xdf, 0x03, + 0x66, 0xc7, 0x96, 0x4d, 0x32, 0x19, 0xce, 0x9c, 0xe8, 0xf0, 0xcb, 0x67, 0xac, 0x9d, 0x9b, 0x50, + 0x25, 0x83, 0x11, 0xb0, 0xa8, 0x41, 0x6d, 0x16, 0x90, 0x21, 0x91, 0xcc, 0xeb, 0xf2, 0x1b, 0xf4, + 0x53, 0x9a, 0x14, 0x5a, 0x7b, 0x0f, 0xa9, 0x90, 0xf2, 0x1f, 0x02, 0x25, 0x77, 0xcb, 0x43, 0xae, + 0x50, 0x9e, 0xbe, 0x89, 0xee, 0xc9, 0x1c, 0x8b, 0x29, 0x6b, 0xec, 0x52, 0xbb, 0x2e, 0x45, 0x70, + 0x2d, 0x95, 0x61, 0x33, 0x4f, 0xa5, 0xd7, 0xb1, 0x70, 0x56, 0x4f, 0xbc, 0xda, 0x05, 0x7f, 0x22, + 0x91, 0x68, 0xd1, 0xbc, 0x39, 0xd0, 0x0c, 0xfa, 0x34, 0xab, 0xcf, 0xdb, 0xdb, 0x89, 0xb2, 0x10, + 0xf7, 0x81, 0xc2, 0x97, 0x2d, 0x50, 0x29, 0x7c, 0xfe, 0x8c, 0xde, 0xe4, 0xf3, 0x04, 0x97, 0x04, + 0xdf, 0x6a, 0x44, 0x46, 0xec, 0x80, 0xe6, 0x9e, 0x1e, 0x93, 0x08, 0xbf, 0xfa, 0x81, 0x50, 0x72, + 0xb7, 0x3c, 0x46, 0x24, 0xf2, 0x80, 0xcc, 0x5a, 0x94, 0xdc, 0xdd, 0x92, 0x2b, 0x51, 0xd9, 0xef, + 0x90, 0x8c, 0xa6, 0x03, 0x9f, 0x69, 0x85, 0x1a, 0x98, 0x8e, 0xd0, 0xd4, 0x66, 0xee, 0x5e, 0xb1, + 0x74, 0x09, 0x72, 0xcd, 0xc8, 0x72, 0x1b, 0x74, 0xdc, 0x1f, 0x24, 0x92, 0x0e, 0x56, 0xc7, 0xfe, + 0x3d, 0xa8, 0x06, 0x9e, 0x17, 0x11, 0xac, 0xc4, 0xfb, 0x35, 0x80, 0x88, 0xcd, 0xa7, 0xc3, 0xc1, + 0xe1, 0x0e, 0x8e, 0xad, 0x24, 0x2b, 0xbc, 0xfd, 0x52, 0x6d, 0x4e, 0x5e, 0x7d, 0x41, 0xc9, 0xdd, + 0xf2, 0x18, 0x16, 0xcb, 0x7c, 0xd3, 0xd1, 0x8b, 0x52, 0xbc, 0x52, 0xab, 0x08, 0x82, 0xe0, 0xf3, + 0x22, 0x83, 0x51, 0x72, 0x64, 0xed, 0xde, 0x4a, 0xd0, 0xdf, 0x00, 0x1c, 0x39, 0x64, 0xbf, 0xdc, + 0x66, 0xf1, 0x8a, 0x65, 0x4b, 0xfe, 0xfc, 0x7d, 0x2f, 0x71, 0x7d, 0xa4, 0x5d, 0x9d, 0x9d, 0x7f, + 0x3b, 0xa0, 0xca, 0xe7, 0x06, 0xf9, 0x2a, 0xd5, 0x67, 0xd1, 0x4c, 0x2c, 0x68, 0x81, 0xd3, 0xb3, + 0xbc, 0xef, 0x8c, 0x15, 0x66, 0x3e, 0xd5, 0x73, 0x26, 0x7b, 0x2c, 0x94, 0xdc, 0x2d, 0x0f, 0xfd, + 0xc8, 0xfd, 0x17, 0x20, 0x77, 0xfd, 0x4c, 0xe3, 0x53, 0x72, 0xa7, 0x30, 0x20, 0x46, 0x96, 0xbb, + 0x58, 0x2c, 0x96, 0x48, 0x88, 0xa6, 0xfa, 0xa5, 0x5d, 0x6c, 0x4a, 0xee, 0x14, 0x86, 0x02, 0x57, + 0xee, 0x9e, 0x7a, 0x95, 0x7b, 0x5c, 0x4c, 0x34, 0xe8, 0xb2, 0x3b, 0xda, 0xdb, 0x4e, 0xfe, 0xec, + 0x9f, 0x50, 0xa2, 0x6f, 0x57, 0xa7, 0x93, 0xea, 0x4e, 0xa6, 0xe4, 0x3e, 0x8a, 0x5c, 0x2e, 0xb7, + 0x79, 0xb7, 0x3b, 0xc4, 0x7a, 0xd2, 0xa7, 0xdd, 0x5d, 0xf8, 0x49, 0xe2, 0x29, 0x74, 0xc0, 0x68, + 0x72, 0x5f, 0xba, 0x68, 0x3e, 0x99, 0x2c, 0xf6, 0x94, 0xdc, 0x47, 0x39, 0xb8, 0x7f, 0x1f, 0x70, + 0xd9, 0xc3, 0xc2, 0x82, 0x29, 0x9f, 0xff, 0x6b, 0xe9, 0x57, 0x0b, 0xe4, 0x66, 0x13, 0xc4, 0xcb, + 0xd2, 0x31, 0x9a, 0xdc, 0x3d, 0xdd, 0x5c, 0xc8, 0xd4, 0x87, 0x92, 0xfb, 0xdb, 0xa4, 0x84, 0x78, + 0xe0, 0xaf, 0xe3, 0x47, 0x1c, 0xc1, 0xeb, 0x6b, 0x57, 0x2e, 0x83, 0xd7, 0x7b, 0x76, 0xef, 0xd2, + 0xa5, 0x82, 0x14, 0x18, 0x46, 0xc4, 0x72, 0xe3, 0xc8, 0x3d, 0x28, 0xc0, 0x8f, 0x4c, 0x7d, 0x3e, + 0x76, 0xb9, 0x57, 0xd1, 0x2a, 0x81, 0xb3, 0xbe, 0x5e, 0xb2, 0x08, 0xde, 0xe0, 0x77, 0xd8, 0xc1, + 0x0e, 0x1c, 0x09, 0xf4, 0xf3, 0xd5, 0xa5, 0x8e, 0x14, 0xaa, 0x50, 0x72, 0xd7, 0x19, 0xfd, 0xcb, + 0x5d, 0x24, 0x14, 0x7e, 0x39, 0x73, 0x1a, 0x18, 0xdf, 0xb4, 0xb6, 0x8c, 0x07, 0x5d, 0x50, 0x28, + 0x14, 0xcb, 0x16, 0x2f, 0x04, 0x1e, 0xbc, 0x79, 0x63, 0x3c, 0x29, 0xb1, 0xa0, 0xb7, 0x17, 0x9c, + 0x0c, 0xac, 0xfa, 0xcd, 0x6b, 0xf0, 0x2f, 0x74, 0x5a, 0x6d, 0x4d, 0x4d, 0x47, 0x87, 0xca, 0x6d, + 0x83, 0xd3, 0x68, 0x95, 0x15, 0x0c, 0x3a, 0x1d, 0xea, 0x0e, 0x49, 0x24, 0x12, 0x30, 0x12, 0x80, + 0x9f, 0x15, 0x0c, 0x0d, 0x0d, 0x71, 0x39, 0x1c, 0x92, 0x77, 0x31, 0x31, 0x30, 0x4b, 0xb9, 0xdb, + 0x7f, 0xa4, 0x72, 0x87, 0x83, 0xd2, 0xa8, 0xb3, 0x86, 0xfa, 0xb1, 0xd9, 0x8d, 0x3f, 0x7f, 0xff, + 0x0d, 0xfc, 0x02, 0xf4, 0xf6, 0x8e, 0x4e, 0x1d, 0xff, 0xb1, 0xe7, 0xd7, 0x99, 0xd6, 0x56, 0x2d, + 0x8c, 0xd1, 0xec, 0x4b, 0xe7, 0xa2, 0xa3, 0xd6, 0x7e, 0xbb, 0x0a, 0xbc, 0x78, 0x54, 0xf4, 0x70, + 0xfe, 0xec, 0x19, 0x40, 0xee, 0xe0, 0xf5, 0xc8, 0xc8, 0xc8, 0x9c, 0xe9, 0xd6, 0x99, 0xe9, 0xa3, + 0x69, 0x6c, 0xea, 0x6a, 0x6b, 0xa7, 0x4f, 0x9e, 0xf4, 0xf4, 0xc9, 0x63, 0xe7, 0x93, 0xc7, 0xb7, + 0x6d, 0xda, 0xf0, 0xb1, 0x8d, 0x0a, 0x8c, 0x20, 0x77, 0xad, 0xf8, 0x78, 0xe5, 0x1e, 0x1a, 0x1c, + 0x04, 0x04, 0xbd, 0x6f, 0xef, 0x1e, 0xdc, 0x77, 0xaf, 0x67, 0xa4, 0x83, 0x77, 0x81, 0x6a, 0x87, + 0x87, 0x47, 0xf7, 0x1f, 0x00, 0xb9, 0x87, 0x87, 0x9c, 0x81, 0xde, 0x8a, 0x8e, 0x8a, 0xd8, 0xfb, + 0xdf, 0x5f, 0xa0, 0xd7, 0xa5, 0xc5, 0xc5, 0x8b, 0xe6, 0xcd, 0x79, 0xfb, 0xae, 0xe5, 0x76, 0x38, + 0x78, 0x00, 0x28, 0x7b, 0xcd, 0xea, 0x95, 0xae, 0x4e, 0x27, 0x57, 0x2c, 0x5d, 0x92, 0x78, 0x3e, + 0x0e, 0x3a, 0x07, 0x34, 0xf0, 0xd6, 0x93, 0x3e, 0xfd, 0x75, 0xe7, 0x0e, 0x92, 0xf5, 0x9f, 0x48, + 0xe0, 0xca, 0xdd, 0x2b, 0x95, 0xd6, 0xad, 0x7e, 0x11, 0x01, 0x9e, 0xdc, 0xb7, 0x50, 0x72, 0x57, + 0x0b, 0x19, 0xb9, 0x97, 0x14, 0x3f, 0x02, 0x6a, 0x9e, 0x3b, 0x63, 0xaa, 0x58, 0xa4, 0xd6, 0xef, + 0x4e, 0x27, 0x8e, 0x81, 0x73, 0x7e, 0x5c, 0x33, 0x9a, 0x96, 0x0a, 0xc8, 0x3d, 0x32, 0x2c, 0x14, + 0x3a, 0x8e, 0x2b, 0xf7, 0xe3, 0x47, 0x0f, 0xef, 0xd8, 0xb2, 0x11, 0x6a, 0xbc, 0xc1, 0x57, 0x05, + 0x74, 0x90, 0x22, 0x42, 0x43, 0xc0, 0xeb, 0x8b, 0x17, 0x92, 0x6c, 0x16, 0xce, 0x1b, 0xe8, 0xef, + 0x2f, 0xb8, 0x9f, 0x3f, 0x6b, 0xda, 0x14, 0x64, 0xaf, 0xe9, 0x63, 0xc0, 0x32, 0xe4, 0x1e, 0x3c, + 0xd1, 0xe5, 0x6e, 0x08, 0x40, 0x6f, 0xbe, 0x93, 0xcd, 0x16, 0x8b, 0x4d, 0x10, 0x37, 0xc2, 0x6c, + 0xd1, 0x51, 0xee, 0xaa, 0xd1, 0xc1, 0x29, 0xb9, 0x13, 0x41, 0xcd, 0xaa, 0x9a, 0x0f, 0x3a, 0xc9, + 0xfd, 0x0c, 0x25, 0xf7, 0xb7, 0x94, 0xdc, 0x2d, 0x11, 0x4a, 0xee, 0x3a, 0x43, 0xc9, 0xdd, 0xf2, + 0x18, 0x91, 0x50, 0x72, 0xd7, 0x11, 0x4a, 0xee, 0x96, 0xc7, 0x44, 0x90, 0x7b, 0x21, 0x25, 0x77, + 0x0a, 0x72, 0x18, 0x47, 0xee, 0xed, 0x2c, 0x16, 0xfc, 0x1a, 0x39, 0xaf, 0x87, 0x85, 0x92, 0x3b, + 0x85, 0x01, 0x31, 0x9c, 0xdc, 0x07, 0xfa, 0xfb, 0x83, 0x02, 0xfc, 0x66, 0x58, 0x5b, 0x59, 0x7d, + 0xfa, 0x8f, 0xef, 0x56, 0xfd, 0x1b, 0x3e, 0x0e, 0xad, 0x7a, 0x02, 0xb6, 0x6d, 0xd3, 0x06, 0x6c, + 0x12, 0x52, 0x4a, 0xee, 0x14, 0x06, 0x44, 0xbf, 0x72, 0x57, 0x2a, 0x95, 0x39, 0x77, 0xef, 0xac, + 0xfa, 0x66, 0x19, 0x6a, 0xf2, 0x1b, 0x57, 0xee, 0xb0, 0x4d, 0xb3, 0xfa, 0xdc, 0xc3, 0xd5, 0x99, + 0xcf, 0x1f, 0x4d, 0x49, 0x2a, 0xed, 0x64, 0x77, 0x1d, 0x3f, 0x84, 0x99, 0x66, 0xf2, 0x21, 0x90, + 0x7b, 0x52, 0x61, 0x2b, 0x56, 0xee, 0xe9, 0x4f, 0x28, 0xb9, 0x53, 0x60, 0xd0, 0x8b, 0xdc, 0xe9, + 0x3f, 0x6f, 0x5a, 0xb6, 0xe0, 0x4b, 0x82, 0xb5, 0x1e, 0xb3, 0xa6, 0x4e, 0xfe, 0x75, 0xe7, 0x0e, + 0xc8, 0xbe, 0x5b, 0xb9, 0x82, 0xe0, 0xcc, 0x19, 0x93, 0x27, 0x45, 0xfe, 0xbc, 0x05, 0x25, 0xf7, + 0x6e, 0xf7, 0x93, 0xf2, 0x21, 0xb5, 0x91, 0x11, 0xf2, 0x68, 0xdd, 0x0e, 0xa7, 0x55, 0x42, 0xcd, + 0xd8, 0x7a, 0xe4, 0x85, 0xdd, 0x31, 0x78, 0x2c, 0x31, 0x4a, 0xee, 0x96, 0x07, 0xae, 0xdc, 0x3d, + 0xb5, 0x6d, 0xdd, 0x77, 0x6d, 0x11, 0xd5, 0x8f, 0xb6, 0xee, 0x7d, 0x02, 0x81, 0xbf, 0x8f, 0xf7, + 0x8c, 0x29, 0x5f, 0x68, 0xd5, 0xba, 0x03, 0xdb, 0xf8, 0xe3, 0x3a, 0x48, 0x12, 0x4a, 0xa9, 0x84, + 0x17, 0x81, 0x9e, 0xb5, 0x6d, 0xdf, 0xbb, 0x93, 0x20, 0x7f, 0x73, 0xa7, 0x40, 0xe4, 0x7b, 0x05, + 0x1d, 0x13, 0xf8, 0x54, 0x42, 0x79, 0x1b, 0xcf, 0xb0, 0xe1, 0x51, 0x29, 0xb9, 0x5b, 0x1e, 0xfa, + 0x95, 0x3b, 0x92, 0x2a, 0x1a, 0x6d, 0xd7, 0xf6, 0xad, 0x04, 0x72, 0x5f, 0x30, 0x67, 0x56, 0x52, + 0x42, 0x3c, 0x76, 0x92, 0xbb, 0x37, 0x25, 0x91, 0xbe, 0x70, 0x16, 0xb2, 0x7c, 0x86, 0xcd, 0xbc, + 0xde, 0xa4, 0x58, 0x82, 0xbb, 0xb8, 0x50, 0xc4, 0xb4, 0xc3, 0xc4, 0x2e, 0x76, 0x4d, 0x7a, 0x45, + 0x57, 0x1f, 0x2d, 0xe7, 0xc3, 0xa1, 0xe4, 0x6e, 0x79, 0x18, 0x4e, 0xee, 0x30, 0x32, 0x99, 0xac, + 0xb0, 0xe0, 0x01, 0xfc, 0xdf, 0xda, 0x9a, 0x6a, 0xe4, 0x83, 0x1a, 0x2c, 0x92, 0xb6, 0xd6, 0xae, + 0x93, 0x87, 0x51, 0x0d, 0x3c, 0x18, 0xbf, 0x12, 0xa7, 0x3f, 0xb8, 0x00, 0x7a, 0xf0, 0xbe, 0xe8, + 0x5c, 0x4e, 0x76, 0x3e, 0xf9, 0xd1, 0xb9, 0x64, 0x23, 0xd3, 0x6b, 0x0b, 0x25, 0x77, 0xcb, 0xc3, + 0x08, 0x72, 0xd7, 0x81, 0xde, 0xe4, 0xf3, 0xf4, 0x79, 0xd3, 0x51, 0x8a, 0xe7, 0x85, 0x07, 0x11, + 0x5f, 0x75, 0xa5, 0x94, 0x75, 0xf8, 0x34, 0x26, 0x6b, 0xda, 0xbb, 0x00, 0x63, 0x7e, 0x19, 0xb5, + 0xf5, 0x1d, 0xfd, 0x7a, 0xac, 0xe1, 0x5b, 0x82, 0xdc, 0x4c, 0x25, 0x8f, 0x8a, 0x90, 0xe6, 0x60, + 0xfb, 0x57, 0xfc, 0xb9, 0x18, 0xe4, 0x11, 0x5a, 0x65, 0xe5, 0x87, 0xff, 0x79, 0x89, 0x4c, 0x91, + 0x52, 0xc2, 0x4a, 0x2c, 0x68, 0xc5, 0x58, 0x0b, 0xca, 0x12, 0x80, 0x3d, 0xd0, 0x6c, 0xe0, 0xda, + 0xe4, 0x22, 0xe6, 0xe5, 0xd2, 0xb6, 0xac, 0x67, 0x1d, 0xf7, 0x5f, 0x73, 0xca, 0x5b, 0xfa, 0x9a, + 0xbb, 0x87, 0xf9, 0x83, 0x12, 0x99, 0xdc, 0x48, 0x11, 0xc4, 0x8d, 0x80, 0x11, 0xe4, 0xde, 0xdc, + 0xd4, 0x68, 0x7b, 0x60, 0xdf, 0xec, 0x69, 0xd6, 0xa0, 0x03, 0x33, 0xd3, 0xda, 0xea, 0xa7, 0xb5, + 0xdf, 0x85, 0x87, 0x9c, 0x21, 0x58, 0xe8, 0x0a, 0x50, 0xca, 0x64, 0xdc, 0x20, 0x5f, 0x94, 0xdc, + 0x41, 0x0f, 0xa7, 0xf7, 0x42, 0xbc, 0xc6, 0x3b, 0xf2, 0xba, 0x86, 0xbe, 0x1d, 0xa4, 0x39, 0x06, + 0x17, 0x79, 0xa5, 0x55, 0xf9, 0x66, 0xd6, 0x9d, 0xb9, 0xd5, 0x10, 0x99, 0xd3, 0x14, 0x95, 0xd3, + 0xac, 0xd6, 0x72, 0x9b, 0xcf, 0xa2, 0x8d, 0x0e, 0x59, 0xcc, 0x3d, 0x3a, 0x9d, 0x33, 0xac, 0xb6, + 0x75, 0xa7, 0x37, 0x37, 0x4d, 0xfd, 0xe2, 0x33, 0x75, 0x83, 0xf1, 0xff, 0xfc, 0xfb, 0x1b, 0x32, + 0x7b, 0xd4, 0x35, 0x22, 0x93, 0x2b, 0xfd, 0x32, 0xeb, 0x08, 0x6e, 0x55, 0x9f, 0xe6, 0x9e, 0x6b, + 0xef, 0xf7, 0xe0, 0x68, 0xe4, 0x63, 0xcf, 0xd4, 0x2a, 0xff, 0xac, 0xfa, 0x94, 0x47, 0xcc, 0x6a, + 0xd6, 0x00, 0x41, 0xc8, 0x5c, 0xb3, 0xc5, 0xd0, 0x72, 0x8f, 0x0a, 0x0f, 0x03, 0x1f, 0x31, 0x68, + 0xdd, 0xe0, 0x23, 0x9c, 0xee, 0xee, 0x05, 0x73, 0x67, 0x4d, 0xf9, 0xfc, 0x5f, 0xc4, 0x1b, 0xc7, + 0x64, 0x82, 0xde, 0xae, 0x53, 0x47, 0xd0, 0x8a, 0x9f, 0x3b, 0x95, 0x1f, 0x17, 0xa5, 0xf1, 0xa6, + 0x3a, 0x7a, 0x84, 0xfe, 0x99, 0xb5, 0xb8, 0x59, 0x9c, 0xf4, 0x62, 0xa7, 0x62, 0x9f, 0xd7, 0xb3, + 0x07, 0x89, 0x3a, 0x33, 0x3c, 0x2e, 0x77, 0xce, 0x74, 0x6b, 0xac, 0xd6, 0x77, 0x6c, 0xd9, 0xa4, + 0xb1, 0xf6, 0x24, 0x31, 0xaa, 0xdc, 0xd5, 0x98, 0x9d, 0x77, 0xbe, 0x4b, 0xfc, 0x8b, 0xc0, 0x1b, + 0xf5, 0x99, 0x65, 0x1d, 0x5d, 0x02, 0xa2, 0x06, 0xcc, 0x4c, 0x50, 0x27, 0x77, 0x82, 0x18, 0x91, + 0x5a, 0xc9, 0xfd, 0xf6, 0xcd, 0x1b, 0xe0, 0x53, 0x06, 0xe2, 0x3e, 0xea, 0xe8, 0x70, 0x37, 0xfb, + 0xf6, 0xc0, 0xc0, 0x00, 0xf9, 0xba, 0x49, 0xbb, 0x3b, 0xbb, 0x30, 0xfb, 0x56, 0x9b, 0x67, 0x4d, + 0xe6, 0x06, 0x93, 0xdd, 0xa6, 0x9c, 0x4f, 0xeb, 0x0e, 0xc8, 0xa8, 0xb1, 0xf7, 0x21, 0x95, 0x98, + 0x5b, 0x9f, 0x72, 0x7f, 0xfb, 0x2e, 0x93, 0xb0, 0xcd, 0xa2, 0xf9, 0x48, 0xad, 0xdb, 0x1f, 0x3c, + 0x40, 0xfe, 0xe6, 0x35, 0x02, 0xfa, 0x18, 0xa3, 0xb9, 0x67, 0x03, 0x0a, 0xf5, 0x68, 0xf6, 0xfe, + 0x05, 0xa0, 0x15, 0xb7, 0xf5, 0xce, 0x47, 0x85, 0x84, 0x26, 0x69, 0x87, 0x4e, 0x17, 0xfa, 0x5f, + 0x79, 0x9d, 0x58, 0xd8, 0xca, 0x55, 0xbf, 0x17, 0xce, 0xb4, 0xe8, 0x49, 0xee, 0x9b, 0x89, 0xfb, + 0xee, 0x0c, 0x3a, 0xfd, 0x5c, 0x74, 0xd4, 0xc6, 0x1f, 0xd7, 0x41, 0x61, 0x95, 0x80, 0x81, 0x2e, + 0x4d, 0x57, 0xa7, 0xe6, 0xa8, 0xa5, 0xd2, 0xce, 0x0e, 0x6c, 0x1b, 0x0f, 0x8c, 0x6d, 0xfb, 0x3f, + 0x61, 0x55, 0x05, 0xf9, 0xdb, 0x04, 0xb7, 0x53, 0xce, 0x10, 0x80, 0x4e, 0x88, 0x5f, 0x66, 0xad, + 0x6f, 0x1a, 0xcd, 0xed, 0xfc, 0xf3, 0xa3, 0x61, 0x25, 0x87, 0x82, 0x8b, 0xc6, 0x2c, 0x48, 0x3b, + 0x3b, 0x95, 0x50, 0xae, 0x59, 0xee, 0x6f, 0xdf, 0x45, 0x47, 0x5a, 0xf3, 0xed, 0x58, 0x9e, 0x78, + 0x1f, 0x4f, 0x77, 0xf2, 0xd5, 0x35, 0x13, 0xa4, 0x72, 0x05, 0x77, 0x40, 0xfc, 0xba, 0xad, 0xff, + 0x7a, 0x59, 0x07, 0xe8, 0xde, 0x81, 0x5f, 0x4c, 0xe7, 0xf8, 0xe7, 0xe0, 0x2b, 0x71, 0xd0, 0x43, + 0xf3, 0x97, 0xe1, 0x48, 0xd0, 0xc3, 0xe0, 0x9b, 0x0d, 0x65, 0x4d, 0xbd, 0xa6, 0xbe, 0x09, 0x15, + 0x0c, 0x2d, 0x77, 0x2f, 0x77, 0xd7, 0x9f, 0xb7, 0x6e, 0xea, 0x13, 0xa8, 0x64, 0xa8, 0xcc, 0xb9, + 0x93, 0x0d, 0x04, 0x00, 0x8e, 0x93, 0xac, 0x24, 0x3f, 0x2a, 0x04, 0x34, 0xea, 0x58, 0xd1, 0x77, + 0x39, 0xff, 0x4d, 0x32, 0x75, 0x99, 0x21, 0x20, 0xfb, 0x64, 0x66, 0xb9, 0xcd, 0x57, 0xce, 0x27, + 0x8f, 0x1b, 0xb4, 0x2a, 0xc6, 0x07, 0x74, 0x5d, 0x2e, 0x16, 0x31, 0x03, 0x33, 0x6b, 0x8f, 0x86, + 0x97, 0x12, 0xf7, 0x1a, 0x8f, 0x84, 0x16, 0x47, 0xdd, 0x69, 0xe4, 0x9b, 0x22, 0x19, 0x2f, 0x16, + 0x7c, 0xb9, 0xa7, 0xe8, 0x4d, 0xee, 0x35, 0xd5, 0xd5, 0x33, 0xa7, 0x4e, 0x86, 0xf6, 0x1c, 0x27, + 0xc4, 0xc7, 0x5d, 0x48, 0x3c, 0x0f, 0x86, 0xad, 0xa0, 0x8d, 0xff, 0x72, 0xe6, 0x34, 0x68, 0xef, + 0x3c, 0x49, 0x06, 0xef, 0xe7, 0xb2, 0xb6, 0x6f, 0xc0, 0x2a, 0x7e, 0xb4, 0xa5, 0x3f, 0xf8, 0xc7, + 0x50, 0xf1, 0x43, 0xad, 0xef, 0xfc, 0x83, 0xf9, 0xa4, 0xa3, 0xbd, 0xbd, 0xac, 0xec, 0xa9, 0x46, + 0x03, 0xdd, 0xb8, 0x6b, 0x57, 0xaf, 0x90, 0x39, 0xd3, 0xe4, 0xd6, 0xd2, 0xa2, 0xc5, 0x47, 0x82, + 0x24, 0xbf, 0x8a, 0x13, 0x74, 0xb3, 0xfe, 0xef, 0xc8, 0xc7, 0x04, 0xba, 0xf7, 0x48, 0x2a, 0xcf, + 0x7d, 0x65, 0xe2, 0x00, 0x80, 0x40, 0xee, 0x7e, 0x86, 0x94, 0x3b, 0x4c, 0x4f, 0x0f, 0xbf, 0xfa, + 0xcd, 0x6b, 0x5a, 0x65, 0x05, 0x78, 0xa1, 0x73, 0x6d, 0x7b, 0x12, 0x62, 0x18, 0x8b, 0xe7, 0xe2, + 0xb6, 0xf4, 0x2d, 0xab, 0x96, 0x72, 0xfc, 0x3d, 0x45, 0xf5, 0x35, 0x3a, 0x17, 0xae, 0x2d, 0xa4, + 0x5a, 0xf7, 0xac, 0xcc, 0x0c, 0xf0, 0x5d, 0x9f, 0x3d, 0x6d, 0xca, 0x87, 0xdc, 0xb6, 0x05, 0xc1, + 0x1f, 0x94, 0x24, 0x16, 0xb4, 0xb8, 0x24, 0xbd, 0x52, 0x27, 0x7a, 0xc7, 0x80, 0xc2, 0xe8, 0x3c, + 0xba, 0xa9, 0xaa, 0x27, 0x34, 0x70, 0xeb, 0x6e, 0x08, 0xfa, 0xd2, 0xd3, 0x88, 0x53, 0x5b, 0xb2, + 0xed, 0xf6, 0xf1, 0xe3, 0xa2, 0x44, 0x8d, 0xf5, 0x06, 0xad, 0x86, 0x66, 0xb9, 0xc7, 0x46, 0x9f, + 0x45, 0xae, 0x83, 0xd3, 0xb9, 0xed, 0xc4, 0x45, 0x29, 0x95, 0x4a, 0x39, 0xdd, 0xba, 0x99, 0x8c, + 0xcb, 0xc1, 0xb7, 0x1e, 0xbe, 0x7c, 0xa0, 0x5f, 0xa1, 0x8f, 0xdd, 0xdc, 0x35, 0xed, 0x03, 0xa1, + 0x77, 0x9a, 0x8e, 0x60, 0xe7, 0x41, 0xa0, 0x47, 0x3a, 0x9e, 0x79, 0xe7, 0xf3, 0xe9, 0x46, 0xc8, + 0x28, 0x84, 0xc2, 0x12, 0xe5, 0x0e, 0x31, 0x54, 0x52, 0xc4, 0xf1, 0x72, 0x46, 0xe5, 0x11, 0x41, + 0xdb, 0x8c, 0x2f, 0x5a, 0x56, 0x2c, 0xee, 0xf8, 0xfd, 0x17, 0xd0, 0xf0, 0xf3, 0xa3, 0xc3, 0xfa, + 0x6f, 0x64, 0x8c, 0xbc, 0x7a, 0x21, 0x61, 0xb6, 0x80, 0x4f, 0x5c, 0xde, 0x27, 0x00, 0x9f, 0xac, + 0xce, 0xa6, 0x10, 0x09, 0x35, 0xc8, 0x1d, 0x8c, 0x5a, 0xb0, 0x0f, 0x22, 0xb1, 0x2b, 0x9e, 0x75, + 0x46, 0x21, 0x1c, 0x01, 0x77, 0x45, 0x74, 0xf3, 0x1f, 0x68, 0x73, 0xa7, 0xb6, 0x7c, 0xbd, 0x90, + 0xb5, 0x63, 0x63, 0xb7, 0xfb, 0x29, 0x5e, 0x78, 0x50, 0x5f, 0xe6, 0x55, 0x09, 0xab, 0x4d, 0x87, + 0x7a, 0x36, 0xb3, 0x07, 0x7d, 0x2e, 0x55, 0xe0, 0x8a, 0x1e, 0x74, 0x7e, 0xf2, 0x5e, 0x19, 0x3c, + 0xcb, 0x0a, 0x12, 0xcb, 0x95, 0x3b, 0xcc, 0xe0, 0x83, 0x3c, 0x8e, 0xb7, 0x2b, 0x63, 0xc9, 0x97, + 0x06, 0xfc, 0xe8, 0x55, 0xad, 0xfd, 0xb7, 0x9d, 0xe2, 0xa6, 0x06, 0x22, 0xb9, 0xdb, 0xfd, 0xb5, + 0x5f, 0xdd, 0x34, 0x53, 0x5e, 0xce, 0x5d, 0xbd, 0xdc, 0xb6, 0xc1, 0xe5, 0xae, 0xc6, 0x98, 0xeb, + 0x56, 0x77, 0x7b, 0xb9, 0xf4, 0x5e, 0x4a, 0x54, 0x8c, 0x68, 0xb1, 0x04, 0x8f, 0x23, 0x10, 0x62, + 0xd7, 0xf1, 0x41, 0xe6, 0x76, 0x89, 0xc6, 0xe9, 0x33, 0x52, 0x33, 0x3f, 0x01, 0xe4, 0x0e, 0x23, + 0xed, 0xee, 0x04, 0x9d, 0xfb, 0x6e, 0x97, 0x63, 0x8c, 0xa5, 0xf3, 0x4d, 0x26, 0x77, 0xa5, 0x52, + 0xb9, 0x7d, 0xf3, 0x46, 0xe2, 0xd8, 0x77, 0x97, 0x92, 0x35, 0x27, 0x05, 0xd7, 0x88, 0xa9, 0xe4, + 0x8e, 0x34, 0x86, 0xcd, 0x3c, 0x5e, 0x48, 0xc0, 0xf0, 0x93, 0x12, 0x38, 0x80, 0x2b, 0x31, 0xbc, + 0x7e, 0x91, 0x5b, 0x4a, 0x15, 0x56, 0xf1, 0xb6, 0x5e, 0xf7, 0x8c, 0xd3, 0xa1, 0x57, 0x2b, 0x77, + 0xf5, 0x73, 0x64, 0x66, 0x2b, 0x77, 0x24, 0x40, 0x0c, 0x22, 0x7a, 0x93, 0xe0, 0x6a, 0x0a, 0xff, + 0x6c, 0x08, 0xc7, 0xc7, 0xad, 0x7d, 0xf7, 0x36, 0xc6, 0xb2, 0x05, 0xa3, 0x0b, 0x2d, 0x55, 0x6b, + 0xae, 0x7f, 0xb9, 0x63, 0xf1, 0xf5, 0xf6, 0xac, 0xac, 0x50, 0x9b, 0xa8, 0x4d, 0x67, 0x94, 0x0a, + 0x05, 0xe8, 0x64, 0x2b, 0x24, 0x08, 0x13, 0x13, 0x98, 0x48, 0xb3, 0x89, 0x44, 0xc0, 0x6b, 0xa0, + 0x93, 0x27, 0x65, 0xb7, 0x8f, 0xd0, 0x2a, 0x06, 0x72, 0x6f, 0xf3, 0x63, 0xc2, 0xb9, 0x81, 0xde, + 0x9d, 0x0e, 0xfb, 0x5b, 0x96, 0x7f, 0xa5, 0xc1, 0x29, 0x33, 0xad, 0x78, 0x11, 0xc1, 0x72, 0x31, + 0xa9, 0x89, 0xd5, 0x7b, 0x15, 0x5d, 0x47, 0x23, 0x4a, 0xb1, 0xa2, 0xf7, 0xbc, 0xf2, 0x66, 0x44, + 0x62, 0xd8, 0x85, 0x09, 0x13, 0x55, 0xee, 0x04, 0x28, 0xe5, 0x72, 0x85, 0x50, 0x28, 0x1f, 0x1c, + 0x30, 0x6c, 0xdf, 0x1d, 0xc6, 0x40, 0x72, 0x37, 0x32, 0xc0, 0x65, 0xa2, 0xc6, 0x3a, 0xa0, 0x69, + 0xb6, 0xdd, 0x3e, 0xdc, 0x47, 0x63, 0x63, 0x2d, 0xc1, 0xde, 0x5d, 0x04, 0x49, 0x18, 0x61, 0x7a, + 0x06, 0xc5, 0xbe, 0x57, 0xd1, 0xe9, 0x70, 0x81, 0x39, 0x9d, 0x7d, 0x52, 0x58, 0xcd, 0x35, 0xdc, + 0x5d, 0xe8, 0x4f, 0xee, 0xc6, 0x7b, 0x02, 0x68, 0x26, 0x7c, 0x5c, 0x72, 0x47, 0x31, 0x98, 0x9f, + 0xdb, 0xe5, 0x72, 0x0c, 0x57, 0xf1, 0xe0, 0x37, 0x94, 0x6d, 0xff, 0x27, 0x19, 0xd1, 0xc7, 0xdf, + 0xa3, 0xdb, 0xfb, 0x3d, 0x40, 0x4f, 0x4b, 0x9d, 0x79, 0x74, 0xbb, 0xdc, 0x50, 0xe3, 0x57, 0x4a, + 0xee, 0x3a, 0xf3, 0x51, 0xcb, 0x1d, 0x02, 0x8c, 0x56, 0x7b, 0x12, 0x63, 0x59, 0x3b, 0x37, 0xe3, + 0xea, 0x9e, 0xe3, 0xeb, 0x2e, 0x61, 0x31, 0x89, 0x4b, 0xb8, 0xf1, 0x94, 0x75, 0xe8, 0xcc, 0x23, + 0x94, 0xfe, 0x8e, 0x86, 0x97, 0x16, 0xd7, 0x18, 0xa4, 0x8d, 0x1f, 0x95, 0x7b, 0x06, 0x3a, 0x8d, + 0x30, 0x25, 0x77, 0x32, 0x50, 0x72, 0x1f, 0xa7, 0x27, 0x29, 0x96, 0xf9, 0xc3, 0xb7, 0x58, 0xc5, + 0xb3, 0xb6, 0xad, 0x1f, 0x7c, 0x90, 0x47, 0x7c, 0x6d, 0x41, 0x55, 0xf7, 0xe1, 0x30, 0x74, 0x57, + 0xfe, 0x64, 0x4c, 0xd9, 0x2b, 0x86, 0x80, 0xf8, 0x42, 0x1d, 0x10, 0x51, 0x72, 0xd7, 0x15, 0x4a, + 0xee, 0x68, 0x40, 0x4b, 0xdf, 0xba, 0x7a, 0x19, 0x5a, 0xf4, 0x73, 0xac, 0xf9, 0xd1, 0x61, 0xc4, + 0x17, 0xa6, 0x16, 0xb7, 0x61, 0x7b, 0x35, 0xde, 0xa9, 0x34, 0x96, 0xbe, 0xe7, 0xa1, 0x28, 0xb9, + 0xeb, 0x0c, 0x25, 0x77, 0x1c, 0xa4, 0x7c, 0x2e, 0x76, 0xe7, 0x25, 0x30, 0x5e, 0x88, 0xbf, 0x42, + 0x42, 0x34, 0x59, 0x1b, 0x75, 0xa7, 0x11, 0xbd, 0xf9, 0xd2, 0x3d, 0x37, 0xf8, 0x56, 0x83, 0x7e, + 0xab, 0x47, 0xc9, 0x5d, 0x67, 0x28, 0xb9, 0xe3, 0xa3, 0x94, 0xcb, 0xb9, 0x81, 0xde, 0x68, 0xc5, + 0xcf, 0xf8, 0x82, 0x17, 0x41, 0xb4, 0xf9, 0x52, 0x26, 0x57, 0x62, 0xf7, 0xa1, 0x1d, 0x0a, 0x2c, + 0xd4, 0xef, 0xb0, 0x95, 0x92, 0xbb, 0xce, 0x50, 0x72, 0x57, 0x0b, 0xae, 0xe2, 0x19, 0x8b, 0xe7, + 0x12, 0x6f, 0xbe, 0x7c, 0x52, 0xcf, 0x77, 0xc4, 0x74, 0xe2, 0xfd, 0x32, 0xeb, 0xa4, 0xfa, 0xdb, + 0x2c, 0xab, 0x4e, 0xee, 0x9d, 0x94, 0xdc, 0x35, 0x41, 0xc9, 0x9d, 0x08, 0x79, 0x9f, 0xa0, 0xdb, + 0xf5, 0x38, 0x7a, 0xe4, 0xfa, 0xeb, 0x36, 0x61, 0xf5, 0x6b, 0x82, 0xab, 0xbc, 0xd3, 0xd1, 0x5a, + 0x3c, 0x12, 0x56, 0x52, 0xd6, 0xd8, 0xa3, 0xaf, 0x5a, 0x51, 0x72, 0xd7, 0x19, 0xc3, 0xca, 0xdd, + 0xdd, 0xc5, 0x09, 0x5a, 0x71, 0x90, 0x7a, 0xe9, 0xa2, 0xb6, 0xd7, 0x9a, 0x09, 0x03, 0x79, 0xd9, + 0xad, 0xab, 0xbf, 0x46, 0x77, 0xe2, 0xc3, 0x4e, 0x13, 0xac, 0x38, 0xc8, 0x7c, 0xda, 0xee, 0x10, + 0xa8, 0x12, 0x11, 0xee, 0xa0, 0x5e, 0x23, 0xc2, 0xe9, 0x47, 0xee, 0x3b, 0x37, 0x51, 0x72, 0x57, + 0x8b, 0x0e, 0x72, 0x87, 0x76, 0xf8, 0x06, 0x07, 0xfa, 0xef, 0xdc, 0xb6, 0x65, 0x74, 0x1d, 0xe5, + 0x4b, 0xbd, 0xad, 0xa3, 0x34, 0x26, 0x40, 0xd6, 0xdc, 0x20, 0x1f, 0xf4, 0xb4, 0xeb, 0x9e, 0x9f, + 0x45, 0x35, 0x6f, 0xd4, 0x5d, 0xd2, 0xdd, 0x27, 0x72, 0x4b, 0xae, 0x44, 0xf7, 0x67, 0xd2, 0x6b, + 0x86, 0xc5, 0x32, 0xbd, 0x54, 0x89, 0x92, 0xbb, 0xce, 0x18, 0x4a, 0xee, 0xed, 0xed, 0xac, 0xc9, + 0x9f, 0xfd, 0x73, 0xfe, 0xec, 0x19, 0x32, 0x99, 0xac, 0xa7, 0x87, 0x6f, 0x3d, 0xe9, 0xd3, 0xe9, + 0x93, 0x27, 0x41, 0x29, 0x54, 0x2d, 0x8e, 0xd1, 0xad, 0x09, 0x2b, 0x16, 0xab, 0xf4, 0xe0, 0x97, + 0x2d, 0x10, 0x5c, 0x49, 0x51, 0x77, 0x3e, 0xee, 0x80, 0xd5, 0x35, 0xb1, 0x9c, 0xc1, 0xd1, 0x4f, + 0x00, 0x44, 0x20, 0x77, 0x3f, 0x4a, 0xee, 0x3a, 0x61, 0x10, 0xb9, 0x83, 0x16, 0x71, 0xb9, 0xcd, + 0x57, 0xa0, 0x45, 0xa7, 0x55, 0x8e, 0xed, 0x3c, 0xbf, 0x97, 0x97, 0x0b, 0xfe, 0xbb, 0x72, 0xf9, + 0x32, 0x92, 0xab, 0x0e, 0x61, 0xb6, 0x6c, 0xf8, 0xa9, 0xb6, 0xc6, 0xc4, 0x9f, 0xca, 0x48, 0x45, + 0x79, 0xdb, 0x96, 0x1f, 0x50, 0x8b, 0xc9, 0xf8, 0xe7, 0x22, 0x08, 0x2e, 0xf1, 0xbf, 0x5e, 0x8f, + 0x92, 0xe3, 0xb1, 0xc8, 0x27, 0x15, 0xad, 0x7d, 0x7a, 0xa9, 0x0f, 0x25, 0x77, 0x9d, 0x31, 0x88, + 0xdc, 0x1d, 0xed, 0x6d, 0x81, 0xb8, 0xe3, 0x62, 0xa2, 0x91, 0x07, 0xbd, 0x3d, 0xdc, 0xc0, 0xc1, + 0x03, 0xfb, 0xfe, 0x80, 0x8f, 0x08, 0x85, 0x23, 0x82, 0xde, 0x5e, 0xf0, 0x05, 0x68, 0x6c, 0xa8, + 0xe7, 0x74, 0x8f, 0x05, 0x13, 0xec, 0xe8, 0x68, 0xaf, 0xa9, 0xae, 0x56, 0x28, 0xc6, 0x9e, 0x63, + 0x80, 0xe3, 0x50, 0xde, 0x08, 0x2e, 0x87, 0x23, 0x97, 0xcb, 0x07, 0xfa, 0xfb, 0xab, 0x68, 0x95, + 0x43, 0xea, 0x23, 0x29, 0x1b, 0x02, 0x31, 0xbd, 0xa9, 0xfd, 0xf7, 0x5d, 0xe8, 0xee, 0x3b, 0x61, + 0x38, 0xb8, 0x90, 0xec, 0x46, 0xd4, 0x5e, 0x6f, 0xc7, 0x90, 0xe2, 0x27, 0x0d, 0xfa, 0xf9, 0x71, + 0xfb, 0x7f, 0x7b, 0x67, 0xfe, 0xd4, 0xc4, 0x19, 0xc6, 0xf1, 0xfe, 0x21, 0x1d, 0x1c, 0x5a, 0x0a, + 0x85, 0xaa, 0x48, 0x6d, 0x47, 0x66, 0xac, 0x62, 0xc7, 0xe3, 0x17, 0xb5, 0xc7, 0x54, 0x64, 0x14, + 0xe8, 0x28, 0xc3, 0x08, 0x72, 0xd4, 0x82, 0x65, 0x38, 0x0c, 0x10, 0xae, 0x10, 0x2e, 0x47, 0xa0, + 0x2d, 0x08, 0x16, 0x19, 0x3a, 0x9c, 0xc2, 0xa0, 0x23, 0x11, 0x4a, 0xa0, 0xc4, 0x58, 0x20, 0x81, + 0x8e, 0x22, 0x02, 0x53, 0xc9, 0x01, 0xe4, 0x30, 0x27, 0x77, 0x0e, 0x48, 0x08, 0xf4, 0x8d, 0xb1, + 0x14, 0xde, 0xdd, 0x6c, 0xb2, 0x99, 0x25, 0x10, 0xd8, 0xef, 0xbc, 0x3f, 0x2c, 0x9b, 0x3d, 0x9e, + 0x30, 0x9f, 0x7d, 0xf3, 0xbc, 0xef, 0x3e, 0xef, 0xf3, 0x90, 0xb8, 0x3b, 0x2c, 0xe2, 0x71, 0xaf, + 0xae, 0xfa, 0x0d, 0x3b, 0x50, 0xde, 0x52, 0x0a, 0x78, 0xed, 0xbf, 0x25, 0xb0, 0xdd, 0x5d, 0x4c, + 0xb0, 0xdd, 0x50, 0x57, 0x0b, 0xb6, 0xef, 0x55, 0x94, 0x83, 0x6d, 0x01, 0x9f, 0x0f, 0xb6, 0xf9, + 0x3c, 0x73, 0xec, 0xb8, 0xd7, 0x07, 0xfb, 0x06, 0xb8, 0x1c, 0xb0, 0x71, 0xd0, 0xc7, 0xeb, 0x6a, + 0x68, 0x30, 0x70, 0x8d, 0xc0, 0x76, 0x5c, 0x6c, 0xcc, 0xe5, 0x8b, 0x17, 0xf0, 0x7f, 0x59, 0x07, + 0x05, 0x70, 0x17, 0x5f, 0xb9, 0x84, 0xc0, 0x9d, 0x86, 0x71, 0x4a, 0x01, 0x1a, 0xee, 0xec, 0x31, + 0x62, 0x56, 0xfa, 0x92, 0xb8, 0x3b, 0x2c, 0x82, 0x71, 0x07, 0x8e, 0x87, 0x39, 0xbf, 0xbd, 0xbb, + 0x1b, 0x6a, 0xea, 0x29, 0xd0, 0x97, 0x7b, 0xec, 0x7b, 0x1f, 0x1c, 0xd0, 0xfe, 0xc4, 0x1c, 0x82, + 0x02, 0x70, 0x3f, 0x15, 0x70, 0xcc, 0xf2, 0x11, 0x8f, 0x37, 0x0e, 0xf6, 0x83, 0xfe, 0xde, 0xf2, + 0xe7, 0xc7, 0x1e, 0xee, 0x5c, 0x4e, 0xff, 0xda, 0x66, 0xdc, 0xd9, 0x2c, 0x96, 0xe5, 0xd3, 0x96, + 0xe6, 0x07, 0x27, 0x03, 0xbe, 0x40, 0x5e, 0x7f, 0x8b, 0xb4, 0xc4, 0x1f, 0xc7, 0x8b, 0x7b, 0xe1, + 0xc3, 0xd7, 0x24, 0xee, 0x3b, 0x50, 0x44, 0xe2, 0x0e, 0xbc, 0x0e, 0x00, 0x25, 0xa0, 0x96, 0xf9, + 0x47, 0x87, 0xb5, 0x63, 0x7a, 0xfe, 0xec, 0x06, 0x07, 0x80, 0x51, 0xec, 0x84, 0x50, 0x08, 0x70, + 0x3f, 0xfd, 0x65, 0x80, 0x65, 0xff, 0x4e, 0xc7, 0xfd, 0xea, 0x65, 0xe4, 0x5c, 0x24, 0xc6, 0x29, + 0x28, 0xb8, 0xe7, 0xf5, 0x3c, 0xdd, 0x62, 0xdc, 0x25, 0x24, 0xee, 0xb6, 0x44, 0xbe, 0x66, 0xb2, + 0x2d, 0x47, 0x70, 0x7f, 0x04, 0xe3, 0x1e, 0x0d, 0x70, 0x1f, 0x25, 0x71, 0xdf, 0x66, 0x91, 0xb8, + 0xdb, 0xd6, 0x92, 0x80, 0x27, 0x0e, 0x0b, 0xc6, 0x8f, 0x3b, 0x03, 0xc2, 0x9d, 0x45, 0xe2, 0xbe, + 0xdd, 0x22, 0x71, 0xb7, 0x2d, 0xe2, 0x70, 0x57, 0x12, 0x62, 0x0f, 0x61, 0xb8, 0x8f, 0x92, 0xb8, + 0x5b, 0xd1, 0x5e, 0xc6, 0x7d, 0x79, 0x42, 0x20, 0x8d, 0x0e, 0x87, 0x70, 0x57, 0xe4, 0x65, 0x62, + 0xbc, 0x43, 0x28, 0x66, 0xf0, 0xa0, 0xfc, 0xc3, 0xd1, 0xf4, 0xee, 0xd6, 0x7e, 0x31, 0x21, 0xf6, + 0x2c, 0xe8, 0x8d, 0xa9, 0x35, 0x70, 0xfe, 0x0f, 0xea, 0xef, 0x43, 0x72, 0xeb, 0x29, 0x8b, 0xc1, + 0xc3, 0x09, 0xbf, 0x18, 0x0e, 0x09, 0x24, 0xaa, 0x6a, 0xb6, 0x0b, 0x89, 0xc4, 0xdd, 0xb6, 0x8c, + 0x6a, 0x95, 0x39, 0xf7, 0xd5, 0x66, 0x5c, 0xe4, 0xe9, 0x49, 0x18, 0x39, 0x6a, 0x1e, 0x0d, 0x4a, + 0xa1, 0xb0, 0x99, 0x08, 0x0a, 0x83, 0xa8, 0xc0, 0x77, 0x30, 0xe4, 0x8d, 0xc9, 0xef, 0x81, 0x70, + 0xcf, 0x7e, 0x30, 0x66, 0x5c, 0x41, 0x7f, 0xfc, 0x4c, 0x7a, 0x9d, 0x82, 0x06, 0x87, 0x76, 0x4a, + 0xe3, 0xa3, 0x0d, 0x12, 0x62, 0x1e, 0x3f, 0x17, 0x12, 0x89, 0xbb, 0x6d, 0x81, 0x5e, 0x5c, 0x59, + 0x98, 0x03, 0xc7, 0x45, 0x7e, 0x77, 0x4e, 0xf7, 0xf2, 0x85, 0xb5, 0x53, 0x40, 0x07, 0x9c, 0x51, + 0x3f, 0x0c, 0x11, 0x09, 0x1e, 0x80, 0x5a, 0x96, 0x23, 0x39, 0xcc, 0x36, 0x6a, 0x46, 0x63, 0x48, + 0xb9, 0xff, 0x1c, 0xbe, 0x72, 0x76, 0x67, 0x53, 0xbf, 0xc4, 0xda, 0x29, 0x60, 0xa8, 0x2d, 0xfa, + 0x3e, 0x08, 0xfe, 0x75, 0xa2, 0x67, 0x10, 0x92, 0x57, 0xd0, 0xb5, 0x44, 0xe2, 0x6e, 0x97, 0xe6, + 0x9a, 0xeb, 0xf9, 0x9f, 0x1f, 0x80, 0xe3, 0x08, 0x30, 0x97, 0xf3, 0xf5, 0xbd, 0x9e, 0x8e, 0xbf, + 0xf3, 0x0c, 0xe6, 0x92, 0xd6, 0x75, 0x9f, 0xe9, 0x78, 0x92, 0xcd, 0x57, 0x93, 0xb3, 0x89, 0x77, + 0xb9, 0xd0, 0x35, 0xaf, 0xdd, 0x6a, 0xcb, 0x6b, 0xb5, 0x5a, 0xc1, 0x74, 0xcd, 0x5c, 0x24, 0xac, + 0x8c, 0x7f, 0xc4, 0x77, 0xa3, 0xf1, 0x3c, 0x5f, 0xaf, 0xe9, 0xea, 0x4a, 0x87, 0xcd, 0x70, 0x5d, + 0x91, 0xb8, 0xdb, 0x25, 0xe0, 0x0f, 0xc8, 0xa9, 0xc9, 0x70, 0x07, 0x1f, 0xf4, 0xb5, 0x6e, 0x08, + 0xab, 0x1e, 0x9b, 0x40, 0xb6, 0x70, 0xb3, 0x1c, 0x41, 0x67, 0xca, 0xe3, 0x84, 0x72, 0x2e, 0x77, + 0x0c, 0x5f, 0x92, 0x82, 0xb9, 0xc5, 0x25, 0x5a, 0xdd, 0x50, 0x44, 0x1a, 0x5c, 0x88, 0xf4, 0x7a, + 0x76, 0xe7, 0x2f, 0x6d, 0x58, 0xa1, 0xc5, 0xfa, 0x91, 0x61, 0x51, 0xf0, 0x05, 0xd8, 0x93, 0xf9, + 0x31, 0x6a, 0x59, 0x48, 0x64, 0x6a, 0x5b, 0x57, 0x11, 0x89, 0xbb, 0xbd, 0x5a, 0xe8, 0xea, 0x10, + 0x9e, 0x39, 0x0e, 0x7b, 0xf0, 0x59, 0x94, 0xd5, 0x15, 0xac, 0x9c, 0x61, 0xa8, 0xa5, 0x07, 0xde, + 0x79, 0xf3, 0xa9, 0x8c, 0xb4, 0x9a, 0xa1, 0x8a, 0x4e, 0xc1, 0x94, 0x4a, 0x8b, 0x7a, 0x2e, 0x18, + 0x7a, 0x3e, 0x19, 0x94, 0x66, 0x36, 0x8c, 0xc4, 0x95, 0xf6, 0xa1, 0x5e, 0x21, 0x36, 0xb7, 0xab, + 0xf1, 0x2f, 0x11, 0xb6, 0xd9, 0x0a, 0x1a, 0x1c, 0xbd, 0x0c, 0xba, 0xf6, 0x99, 0x5a, 0xab, 0xe1, + 0x9c, 0xbb, 0x5b, 0x24, 0xee, 0x38, 0x04, 0xbc, 0x17, 0x08, 0x9d, 0xb7, 0xc4, 0xa7, 0x9a, 0x74, + 0xe8, 0xbc, 0xae, 0xab, 0xaa, 0x67, 0x92, 0x72, 0x87, 0x85, 0x8a, 0xec, 0xff, 0x8d, 0xc2, 0xb8, + 0x9e, 0xd5, 0x69, 0xae, 0x2d, 0x95, 0xdd, 0x19, 0x49, 0xed, 0xb0, 0xd4, 0x16, 0xb5, 0xd6, 0xa2, + 0x72, 0x98, 0x65, 0x98, 0x9d, 0xfa, 0xda, 0xdb, 0x84, 0x84, 0x8a, 0xbc, 0x2c, 0xa4, 0xc1, 0xca, + 0xa2, 0xdc, 0x3d, 0xe8, 0xb5, 0x5b, 0x44, 0xe2, 0x8e, 0x4f, 0xc8, 0x19, 0x3d, 0x33, 0xf1, 0xd4, + 0x14, 0x7b, 0x00, 0xaa, 0x61, 0x8b, 0xf2, 0xea, 0x51, 0x92, 0xec, 0xe1, 0x6a, 0x49, 0x25, 0xec, + 0x4a, 0xa6, 0xd0, 0xe6, 0xbd, 0x8c, 0x0a, 0xb9, 0x3c, 0x1d, 0xf6, 0xbe, 0xcc, 0xac, 0xe7, 0x65, + 0xad, 0x12, 0x51, 0x21, 0xd4, 0x45, 0x45, 0xe2, 0x8e, 0x4f, 0x26, 0xbd, 0x1e, 0x95, 0x78, 0x51, + 0x48, 0xa0, 0x96, 0xdb, 0x67, 0xe7, 0x45, 0x58, 0xa3, 0x2a, 0x7a, 0xeb, 0x3f, 0x94, 0xbb, 0x5c, + 0x64, 0x5e, 0x1a, 0xb4, 0x5e, 0xbf, 0x2d, 0xae, 0xa4, 0x37, 0xa3, 0x71, 0xb4, 0xb9, 0x5f, 0xa2, + 0x5e, 0x5c, 0xb6, 0xe7, 0xfa, 0x9a, 0x5e, 0xf6, 0x54, 0xd0, 0x37, 0x48, 0x23, 0x15, 0x39, 0x69, + 0x2b, 0x73, 0xc4, 0xc4, 0xdc, 0xbb, 0xa8, 0x48, 0xdc, 0x1d, 0x91, 0xba, 0xe2, 0x67, 0xde, 0x41, + 0x4f, 0xd4, 0x6e, 0xde, 0x88, 0x73, 0xc5, 0x96, 0x66, 0xc9, 0x38, 0x2e, 0x5b, 0xe4, 0xf0, 0x66, + 0x5a, 0x38, 0xd2, 0x1a, 0xd6, 0x24, 0x70, 0xe5, 0xab, 0xba, 0x27, 0x9a, 0xfa, 0xc4, 0xec, 0x31, + 0xd5, 0xa4, 0x4a, 0xeb, 0x40, 0xb1, 0x6f, 0x73, 0xe9, 0x6a, 0xb4, 0xf2, 0x18, 0xaa, 0xd2, 0xdb, + 0xab, 0xa6, 0xdd, 0x53, 0x3a, 0xdc, 0x31, 0xa1, 0xe3, 0x6e, 0x30, 0x18, 0x5e, 0x0e, 0xbd, 0xd8, + 0xd8, 0x62, 0xa3, 0x22, 0x1b, 0xeb, 0xeb, 0x36, 0xee, 0xe1, 0xf3, 0xb7, 0xad, 0x38, 0xd1, 0x4e, + 0x90, 0x7e, 0x64, 0x58, 0x1c, 0x1e, 0x8a, 0xa4, 0x6a, 0xf2, 0xdc, 0x29, 0x25, 0x8d, 0x6a, 0x72, + 0xba, 0xc3, 0x60, 0x7e, 0x39, 0x50, 0x94, 0x3b, 0xee, 0xed, 0x8e, 0x34, 0x49, 0x78, 0xfc, 0xc8, + 0x6c, 0x4b, 0x83, 0x93, 0xed, 0xd9, 0x99, 0xb2, 0xda, 0xbb, 0xff, 0x3d, 0x38, 0xb0, 0x5e, 0x3f, + 0x16, 0xd9, 0x02, 0x8e, 0xfa, 0x13, 0x52, 0x24, 0xde, 0xd5, 0xf5, 0xae, 0xac, 0x1c, 0x82, 0x30, + 0xf3, 0x64, 0x5f, 0x5c, 0x94, 0xa6, 0xef, 0x99, 0x13, 0x6c, 0xd0, 0x3e, 0x1f, 0x94, 0x53, 0x93, + 0xcd, 0x69, 0xff, 0x91, 0x66, 0x78, 0xba, 0xa9, 0x8a, 0x0b, 0x9c, 0x60, 0x83, 0xab, 0x08, 0xcb, + 0x99, 0x11, 0x08, 0xf8, 0x96, 0xd5, 0x18, 0x50, 0xfb, 0xf6, 0xfc, 0x59, 0xbc, 0x4b, 0x4e, 0x77, + 0xb1, 0x80, 0x87, 0xa0, 0xcc, 0x47, 0x99, 0x00, 0x59, 0x6f, 0xb2, 0xe4, 0x9b, 0xb3, 0x4d, 0xb5, + 0x26, 0x0d, 0x91, 0x0b, 0x0e, 0xc1, 0x70, 0x73, 0xbe, 0xa3, 0x0d, 0xf8, 0x4e, 0x7c, 0x3f, 0x1f, + 0x6b, 0xf7, 0x55, 0x80, 0x1f, 0x99, 0x65, 0xbb, 0x7c, 0xfd, 0xbd, 0x23, 0x1b, 0xbe, 0xbb, 0x5a, + 0xad, 0xda, 0xef, 0xe5, 0xb1, 0x91, 0xf5, 0x88, 0xf0, 0x30, 0xe7, 0x58, 0xe6, 0x5a, 0x5a, 0x99, + 0x9d, 0x51, 0x97, 0x15, 0xf3, 0x7c, 0x3e, 0xc4, 0xe0, 0x5e, 0x70, 0xc2, 0x5f, 0x9e, 0x71, 0x6b, + 0xfa, 0xde, 0xaf, 0x4b, 0xbc, 0x71, 0xbc, 0xfd, 0x85, 0x49, 0xa7, 0x5b, 0xe2, 0x8f, 0x4f, 0x57, + 0x57, 0xca, 0xb3, 0x28, 0xc2, 0xd3, 0xc7, 0x30, 0xee, 0xc2, 0x3b, 0xfc, 0x89, 0xaa, 0xa4, 0x70, + 0x0f, 0xc6, 0xc3, 0xd8, 0x23, 0xdb, 0x43, 0x55, 0xbd, 0x4e, 0xe7, 0x7f, 0xd8, 0xcf, 0xc2, 0x7a, + 0x6a, 0x4a, 0x92, 0x13, 0x6c, 0x72, 0x69, 0x69, 0x07, 0x38, 0xb2, 0x54, 0x38, 0x9e, 0x0c, 0x9d, + 0xcb, 0x03, 0x1f, 0x09, 0x8e, 0x7e, 0x36, 0x75, 0xf1, 0xab, 0x37, 0x09, 0x37, 0xc0, 0x63, 0xa0, + 0xc8, 0xa5, 0x2a, 0xf3, 0xb3, 0x81, 0xff, 0xad, 0x2c, 0xc8, 0x51, 0xd0, 0x33, 0x15, 0xd9, 0x69, + 0xb2, 0xe4, 0x78, 0xf1, 0x95, 0x4b, 0xc2, 0x13, 0xfe, 0xf6, 0x54, 0xa8, 0xe3, 0xf9, 0x79, 0xcb, + 0x12, 0xe3, 0xe6, 0xdb, 0x1f, 0x6f, 0xf7, 0x3f, 0x60, 0x47, 0xcb, 0xae, 0x99, 0x19, 0x93, 0xc9, + 0x74, 0x68, 0xbf, 0x77, 0x4a, 0x62, 0xc2, 0x56, 0x5b, 0xb3, 0x9b, 0xa4, 0x61, 0xf7, 0x28, 0x0b, + 0x73, 0x26, 0xce, 0x9e, 0xb4, 0x07, 0x7d, 0x87, 0xdb, 0xe4, 0xf9, 0x33, 0x0a, 0x7a, 0xc6, 0x02, + 0xb3, 0x7d, 0xbb, 0xbf, 0xae, 0x6b, 0xe8, 0x3d, 0x83, 0x61, 0x59, 0x43, 0x6a, 0x2b, 0x35, 0x2f, + 0x95, 0xc8, 0x3b, 0x18, 0xa2, 0xd2, 0x22, 0x41, 0x52, 0xfc, 0x48, 0x80, 0xff, 0xab, 0x43, 0xde, + 0x38, 0x9a, 0xef, 0xe6, 0x01, 0xa8, 0xa7, 0x1b, 0xff, 0x53, 0x1f, 0x69, 0xcc, 0x35, 0xe5, 0x6d, + 0xba, 0xa6, 0xf7, 0x29, 0x39, 0xb1, 0x88, 0x57, 0xf6, 0xce, 0xbb, 0x93, 0x22, 0x50, 0x26, 0xf0, + 0x10, 0x70, 0x7a, 0x67, 0xea, 0x6b, 0x54, 0xa5, 0x45, 0x60, 0x98, 0x0b, 0x3c, 0x19, 0xe0, 0xb7, + 0x48, 0x6f, 0x44, 0x4a, 0xc2, 0x43, 0x45, 0x21, 0x81, 0xe2, 0xb0, 0x60, 0x69, 0x74, 0xf8, 0x9b, + 0x9f, 0x7e, 0x90, 0xa7, 0x27, 0x81, 0x4f, 0xc1, 0x31, 0x33, 0xb5, 0xd5, 0xda, 0x81, 0x7e, 0x83, + 0xcc, 0xa9, 0xc5, 0x8a, 0x77, 0xa5, 0xfe, 0x05, 0x34, 0x91, 0x5b, 0x5a, 0x71, 0xf0, 0xe8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE tune_diff_pair_length_legend_xpm[1] = {{ png, sizeof( png ), "tune_diff_pair_length_legend_xpm" }}; + +//EOF diff --git a/bitmaps_png/cpp_other/tune_diff_pair_skew_legend.cpp b/bitmaps_png/cpp_other/tune_diff_pair_skew_legend.cpp new file mode 100644 index 0000000000..212be2465f --- /dev/null +++ b/bitmaps_png/cpp_other/tune_diff_pair_skew_legend.cpp @@ -0,0 +1,362 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x62, 0x08, 0x02, 0x00, 0x00, 0x01, 0x81, 0xbc, 0x69, + 0xd2, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x1e, 0xc2, 0x00, 0x00, 0x1e, + 0xc2, 0x01, 0x6e, 0xd0, 0x75, 0x3e, 0x00, 0x00, 0x00, 0x09, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x89, 0x2a, 0x8d, 0x06, 0x00, 0x00, 0x15, 0x65, 0x49, + 0x44, 0x41, 0x54, 0x78, 0x9c, 0xed, 0x5d, 0x89, 0x5f, 0x13, 0xd7, 0x1a, 0xed, 0x7f, 0xf1, 0x6c, + 0x5d, 0x00, 0x37, 0xac, 0x56, 0xad, 0x6d, 0xad, 0xb6, 0xd5, 0xda, 0x45, 0x5f, 0xad, 0x4f, 0xfb, + 0xac, 0xaf, 0x8b, 0xad, 0xd6, 0xf7, 0x6c, 0x1b, 0x54, 0x04, 0x91, 0x55, 0x14, 0x10, 0x41, 0x14, + 0x14, 0x14, 0x5b, 0x15, 0x5c, 0xd9, 0x41, 0x01, 0xc1, 0x8d, 0x4d, 0x76, 0x64, 0x47, 0x05, 0x64, + 0x0b, 0x88, 0x0a, 0x84, 0x7d, 0xdf, 0x03, 0x01, 0x02, 0xef, 0x9b, 0xdc, 0x38, 0x0c, 0x59, 0x27, + 0xc9, 0x4c, 0x26, 0x81, 0x39, 0xbf, 0x43, 0x7e, 0x93, 0xc9, 0xcc, 0xbd, 0x5f, 0x0e, 0x27, 0x77, + 0xee, 0xdc, 0xb9, 0xcb, 0x5b, 0x13, 0xe4, 0x30, 0x32, 0x32, 0x42, 0xf2, 0x48, 0x09, 0xbc, 0x45, + 0xe6, 0x20, 0xb3, 0x03, 0xfb, 0x0c, 0xde, 0xf9, 0x07, 0xf5, 0x19, 0x38, 0x1e, 0x3d, 0x22, 0xbd, + 0x73, 0xfe, 0xdc, 0x77, 0x28, 0xcb, 0x80, 0x12, 0xbc, 0x55, 0x59, 0x59, 0xf9, 0xe0, 0xfe, 0x7d, + 0x99, 0x0c, 0x0c, 0x8b, 0xf4, 0x38, 0x77, 0x83, 0x48, 0xc7, 0xd3, 0x21, 0x4e, 0x7e, 0x4f, 0x55, + 0xcb, 0x00, 0xfe, 0xc8, 0xeb, 0xab, 0xc6, 0x7f, 0x82, 0xac, 0x44, 0x5b, 0x37, 0x6f, 0x1a, 0x1c, + 0x1c, 0x44, 0xdb, 0x39, 0xd5, 0x5d, 0x6a, 0x66, 0x10, 0x17, 0x1b, 0x03, 0xaf, 0xa1, 0xc1, 0x81, + 0x03, 0x03, 0x03, 0x12, 0xc7, 0xf5, 0xf7, 0xf7, 0xe3, 0xdb, 0xdc, 0xa6, 0xfe, 0x09, 0xd2, 0x90, + 0x2b, 0x11, 0xc7, 0x25, 0x41, 0xde, 0x39, 0x26, 0xc7, 0x1e, 0xaa, 0x96, 0x01, 0x11, 0x76, 0xa1, + 0xa5, 0x1c, 0x87, 0x18, 0xce, 0xf1, 0x38, 0xc5, 0xa7, 0x71, 0x5c, 0x13, 0x39, 0x4e, 0xb1, 0x4a, + 0x29, 0x23, 0x03, 0xca, 0xa1, 0xf5, 0x0c, 0x5a, 0x4f, 0x1f, 0xaf, 0xfb, 0xe5, 0x7b, 0xaa, 0x48, + 0xf6, 0x1b, 0x50, 0x5f, 0x10, 0xb5, 0xb5, 0xb5, 0xa9, 0x97, 0x22, 0xa9, 0xd4, 0x73, 0x72, 0xb2, + 0xa5, 0x43, 0x56, 0xf5, 0x4b, 0x28, 0x4a, 0x5d, 0xa5, 0x84, 0x64, 0xa7, 0x2e, 0xaf, 0x74, 0x0b, + 0x0e, 0x0a, 0x94, 0x28, 0xdd, 0x80, 0x2a, 0xfd, 0xac, 0x26, 0x14, 0xfc, 0x74, 0x9d, 0x3d, 0x82, + 0xa4, 0x77, 0xc2, 0x91, 0xea, 0xfc, 0x6e, 0x3d, 0x4e, 0xbb, 0x49, 0x7c, 0x20, 0x91, 0x0a, 0xaf, + 0xae, 0xf6, 0xc2, 0x79, 0x2f, 0xe9, 0xfd, 0xa4, 0x52, 0x07, 0x58, 0x9a, 0x9b, 0x75, 0x76, 0x76, + 0x18, 0xce, 0x9e, 0x95, 0x9d, 0x95, 0xa9, 0x20, 0x15, 0x95, 0x53, 0x97, 0xa9, 0x8c, 0xf9, 0xc5, + 0x1c, 0x99, 0x27, 0x5c, 0x4f, 0x7e, 0xad, 0x5a, 0xea, 0x44, 0x60, 0xc5, 0x99, 0x43, 0x8c, 0x53, + 0x64, 0x85, 0x82, 0x73, 0x62, 0x8a, 0x5a, 0xc8, 0x14, 0x67, 0x40, 0x7a, 0x4b, 0x1b, 0xed, 0xa6, + 0x4e, 0x61, 0x29, 0x06, 0xa4, 0x2c, 0x76, 0xdf, 0x4b, 0x17, 0xd1, 0xa5, 0x54, 0x9b, 0x50, 0x27, + 0x7a, 0x79, 0x85, 0x50, 0x51, 0xe1, 0x33, 0xcd, 0x82, 0x51, 0x19, 0x6a, 0x46, 0xbf, 0x72, 0xe9, + 0x12, 0x32, 0x25, 0x32, 0x1c, 0xa3, 0xf6, 0xd5, 0x87, 0x0c, 0xa8, 0xd4, 0x5e, 0xfb, 0x10, 0x47, + 0xbf, 0x7a, 0xd5, 0x0a, 0x1b, 0x4b, 0x0b, 0x92, 0xe7, 0x5c, 0x08, 0x8a, 0x81, 0x12, 0xf3, 0x55, + 0xab, 0x64, 0x3d, 0x51, 0x26, 0xe0, 0x48, 0xb3, 0x93, 0x8f, 0xd4, 0x8c, 0x4e, 0x19, 0x64, 0x68, + 0x6f, 0x34, 0xe7, 0x6d, 0xc5, 0xe7, 0xc8, 0xbc, 0x80, 0x49, 0x63, 0xef, 0x6f, 0xbb, 0x90, 0x6d, + 0xe0, 0x0b, 0xa4, 0x71, 0x3b, 0xd4, 0x08, 0x4e, 0x29, 0x24, 0xa3, 0x87, 0xfc, 0x76, 0xfd, 0xfc, + 0x23, 0x5c, 0xad, 0xa0, 0x00, 0x59, 0xba, 0x68, 0xfe, 0xaa, 0xe5, 0x4b, 0x53, 0x53, 0x92, 0x17, + 0x1a, 0xcc, 0x31, 0x5e, 0x60, 0x08, 0xdb, 0xe8, 0x18, 0x05, 0x97, 0xaa, 0xd6, 0xd6, 0x56, 0x78, + 0x5d, 0xb7, 0x66, 0xf5, 0xba, 0xb5, 0xab, 0xf1, 0x9d, 0x70, 0x7c, 0x61, 0x5d, 0x0f, 0x0d, 0xc1, + 0x8b, 0xa2, 0x0f, 0xbf, 0x15, 0xf6, 0xd9, 0x9a, 0x8f, 0x80, 0x8d, 0x8d, 0x0d, 0x64, 0xce, 0x51, + 0xb5, 0x16, 0x02, 0xc7, 0x07, 0x3f, 0xae, 0x53, 0x27, 0x3a, 0x65, 0x10, 0x6b, 0xdf, 0x26, 0xd2, + 0x4c, 0x1a, 0x91, 0xf9, 0x8d, 0x01, 0x19, 0x75, 0x88, 0x17, 0x62, 0xab, 0x21, 0x0e, 0x55, 0x43, + 0x47, 0x80, 0x5b, 0x11, 0x38, 0xd1, 0x29, 0xbc, 0xdc, 0x37, 0xf1, 0x15, 0x55, 0x9c, 0xd0, 0xc2, + 0x5d, 0x03, 0xad, 0x98, 0xd6, 0xd1, 0xb7, 0x5d, 0x38, 0xd3, 0xe2, 0xe6, 0xa8, 0x9b, 0x54, 0x1e, + 0x3d, 0x79, 0xec, 0xf8, 0x6e, 0xeb, 0xc6, 0x0d, 0xeb, 0xa9, 0x4a, 0x8d, 0x0c, 0x28, 0x0b, 0x7d, + 0xc1, 0xbc, 0xd9, 0x54, 0x25, 0x45, 0x12, 0x94, 0x85, 0x3e, 0x36, 0x36, 0x26, 0xef, 0x23, 0x3e, + 0x9f, 0x4f, 0x55, 0x2e, 0x44, 0xa8, 0x1f, 0xba, 0x74, 0xeb, 0x66, 0x77, 0xd7, 0x94, 0xd6, 0x29, + 0xbc, 0xa5, 0xa9, 0xa7, 0xa7, 0x5b, 0xed, 0x5c, 0x14, 0x40, 0xcd, 0xd0, 0xc9, 0x54, 0x18, 0x8b, + 0x8b, 0x0a, 0xe1, 0x98, 0xe6, 0xa6, 0x26, 0x1d, 0x0a, 0x7d, 0x78, 0x78, 0x98, 0x7c, 0xe8, 0x3e, + 0x17, 0xff, 0xd6, 0xa1, 0xd0, 0x11, 0xb2, 0x32, 0x1f, 0x2b, 0x3e, 0x80, 0x57, 0x27, 0xae, 0x0b, + 0xe8, 0x5c, 0xe8, 0xe4, 0x41, 0x57, 0xe8, 0xa8, 0xed, 0xe7, 0x82, 0xf7, 0x39, 0x79, 0xcd, 0x42, + 0x32, 0xe9, 0xe1, 0xed, 0x27, 0xdd, 0x56, 0x24, 0xdd, 0x74, 0x04, 0x1c, 0x14, 0x8c, 0xd1, 0xab, + 0xba, 0x4a, 0x37, 0x69, 0x10, 0x90, 0xa9, 0x67, 0x3a, 0x99, 0x23, 0x2d, 0xfc, 0x0a, 0xe0, 0x60, + 0x1a, 0x43, 0x9f, 0x3f, 0xf7, 0x1d, 0x08, 0xfd, 0x9b, 0xaf, 0xbf, 0x20, 0x73, 0xc2, 0xa9, 0x7b, + 0x55, 0x64, 0x2a, 0x92, 0x28, 0x4d, 0xa0, 0xed, 0xcd, 0x67, 0x07, 0x4f, 0xdc, 0xd7, 0x34, 0x4c, + 0x59, 0x10, 0xab, 0xfe, 0xfb, 0x9e, 0xdd, 0x68, 0x03, 0x6e, 0x38, 0x14, 0x9f, 0xc0, 0x71, 0x4b, + 0xb2, 0x0f, 0x2b, 0x25, 0x93, 0xf4, 0xc7, 0x1f, 0xac, 0x74, 0x77, 0x73, 0x1d, 0x1e, 0x15, 0x9a, + 0x39, 0x45, 0x6b, 0x12, 0xa2, 0x3c, 0x48, 0x86, 0xee, 0x74, 0xcc, 0x5e, 0xb1, 0x79, 0x38, 0xae, + 0x89, 0x4e, 0x11, 0xe5, 0x32, 0x3f, 0x32, 0x9e, 0x6f, 0x80, 0xaa, 0x03, 0xb6, 0x56, 0x87, 0xf1, + 0x9d, 0x23, 0x63, 0x42, 0xf5, 0xaa, 0xfb, 0x4a, 0x31, 0x25, 0xf4, 0x8e, 0x8e, 0x76, 0xb8, 0xdf, + 0xdb, 0xbc, 0xf1, 0xab, 0x09, 0x51, 0x75, 0xca, 0xf1, 0xe8, 0x11, 0x81, 0x40, 0xf0, 0xcd, 0xd7, + 0x5f, 0x92, 0x0f, 0x1d, 0x6d, 0x10, 0xbf, 0x3c, 0xfc, 0x4c, 0xb5, 0x11, 0x3a, 0x9e, 0xe5, 0xf7, + 0xdb, 0xfe, 0x05, 0xaf, 0xf5, 0xf5, 0xbc, 0x07, 0xf7, 0xee, 0xa2, 0x3d, 0x47, 0xed, 0x6c, 0xd1, + 0x06, 0x99, 0xd0, 0x89, 0xe8, 0x1e, 0x1c, 0xa1, 0x31, 0x74, 0xb8, 0x49, 0x85, 0x5c, 0xe1, 0x95, + 0xcc, 0x09, 0x9c, 0x63, 0x0f, 0xaf, 0xa7, 0xd6, 0xa8, 0x94, 0x07, 0x8d, 0xa1, 0x67, 0x67, 0x66, + 0x82, 0xde, 0xf8, 0x33, 0x3e, 0xca, 0xe3, 0x80, 0x53, 0xae, 0xa6, 0xd4, 0xa8, 0x7a, 0x96, 0x52, + 0x28, 0x29, 0xd7, 0xf1, 0xfb, 0x6b, 0xa0, 0xd5, 0x8d, 0xa7, 0x10, 0x04, 0x14, 0x8e, 0x6a, 0x64, + 0x03, 0x27, 0xee, 0x77, 0x4f, 0xf1, 0xa1, 0xee, 0xfe, 0xfa, 0x66, 0x7a, 0xad, 0x92, 0x8a, 0x00, + 0x6a, 0x94, 0xc7, 0xe8, 0x1c, 0x7f, 0xf0, 0x42, 0x96, 0x1a, 0x41, 0xe3, 0x70, 0x08, 0x2c, 0x24, + 0xd9, 0x76, 0x4f, 0x86, 0xb6, 0x21, 0xa5, 0x7a, 0x7c, 0x73, 0xcd, 0x86, 0xce, 0x04, 0xa6, 0x6f, + 0xe8, 0x8c, 0xb7, 0xb7, 0xc8, 0x63, 0xbb, 0x8f, 0xb7, 0x92, 0xd0, 0xa9, 0x7d, 0xb8, 0x45, 0x21, + 0xdb, 0xbc, 0xdd, 0x75, 0xce, 0x30, 0x56, 0x16, 0xe6, 0xb1, 0x0f, 0x1f, 0xc0, 0x46, 0x5d, 0x6d, + 0xad, 0xcc, 0x9a, 0xc5, 0xf4, 0x80, 0xce, 0xe9, 0x0e, 0xe0, 0x56, 0x94, 0x7f, 0xbb, 0xe9, 0x2b, + 0xb8, 0xc4, 0x7f, 0xb7, 0x65, 0x33, 0xd3, 0xb1, 0xd0, 0x05, 0x2d, 0xe9, 0x4e, 0xfe, 0x99, 0x60, + 0x7a, 0x5a, 0xaa, 0xe1, 0xec, 0x59, 0xd7, 0xaf, 0xfa, 0xee, 0xd9, 0xb5, 0xf3, 0xc3, 0x15, 0xef, + 0x91, 0xcf, 0x82, 0xa6, 0xbb, 0x60, 0x9a, 0xc0, 0x80, 0xee, 0x70, 0x17, 0xa6, 0xf9, 0xb3, 0x62, + 0xd4, 0xc0, 0x96, 0x93, 0x35, 0x59, 0x2f, 0x64, 0x75, 0x97, 0x81, 0xbd, 0x7b, 0x76, 0xa3, 0x36, + 0x0e, 0x9c, 0xfb, 0x39, 0x7f, 0x68, 0xc2, 0x9d, 0x3f, 0xfe, 0x87, 0x98, 0xda, 0x22, 0xc3, 0xb9, + 0xac, 0xee, 0x32, 0x80, 0xfb, 0xbd, 0xa1, 0xa1, 0xde, 0x68, 0xce, 0xdb, 0x54, 0xf9, 0xfd, 0xd0, + 0xc1, 0x03, 0xf8, 0x1e, 0x56, 0x77, 0x19, 0xd0, 0x42, 0x9f, 0x0f, 0x56, 0x77, 0x19, 0x20, 0xea, + 0x1e, 0xf5, 0xa4, 0xc9, 0x36, 0xb4, 0xd4, 0xca, 0xbf, 0xd0, 0xfa, 0xfa, 0x13, 0xab, 0x6b, 0x18, + 0x2d, 0xaf, 0x16, 0x58, 0x5d, 0x2d, 0xb0, 0xbc, 0x92, 0x0f, 0x3c, 0xec, 0x0b, 0xcc, 0xb3, 0xf0, + 0xc9, 0xb3, 0xb8, 0x9c, 0x7b, 0x08, 0x78, 0x09, 0x98, 0x63, 0x7e, 0x31, 0xc7, 0xec, 0xef, 0x6c, + 0xb3, 0xbf, 0xb2, 0xe0, 0x46, 0xff, 0xa0, 0x77, 0x26, 0xd0, 0xf6, 0xe6, 0x53, 0xc7, 0x88, 0x72, + 0x62, 0x47, 0x68, 0x3d, 0xd6, 0x7d, 0x6c, 0x6c, 0x8c, 0xa6, 0xde, 0x51, 0xa0, 0xbb, 0xeb, 0x9d, + 0x0a, 0xf4, 0xf0, 0x00, 0xc8, 0xf1, 0x48, 0xb5, 0xbc, 0xf6, 0xc4, 0xda, 0xaf, 0xd0, 0xc6, 0x5f, + 0xc4, 0x00, 0x78, 0x2d, 0xb2, 0x09, 0x10, 0x31, 0xb0, 0x18, 0x68, 0x8b, 0x18, 0x04, 0x7c, 0x2e, + 0x66, 0x30, 0x62, 0x09, 0xa2, 0xe5, 0x8d, 0x67, 0x1c, 0x87, 0x18, 0x94, 0xda, 0x3e, 0x87, 0x87, + 0xe3, 0xe3, 0xe3, 0xfa, 0xaa, 0x7b, 0x6b, 0x4b, 0x0b, 0x7e, 0x99, 0xa2, 0x3c, 0x1b, 0x67, 0x8f, + 0x20, 0xec, 0xe9, 0xc8, 0xa5, 0xbc, 0xae, 0x01, 0x35, 0x47, 0x97, 0x10, 0x11, 0x1a, 0x1c, 0xb4, + 0xd0, 0x60, 0x0e, 0x0a, 0x35, 0x30, 0x55, 0xdc, 0x11, 0xe6, 0x54, 0x84, 0xb6, 0xbb, 0x08, 0x6a, + 0x02, 0xb1, 0xee, 0x99, 0x8f, 0x33, 0x24, 0xea, 0x1b, 0x78, 0x8b, 0x2a, 0xdc, 0x37, 0x86, 0x04, + 0x05, 0x6a, 0x92, 0x87, 0xa5, 0x6f, 0x3e, 0xe8, 0x7e, 0xc8, 0x27, 0x4f, 0xd3, 0x60, 0xdf, 0x60, + 0xe9, 0xa2, 0xf9, 0x28, 0x48, 0x34, 0xb0, 0xe6, 0xe9, 0xeb, 0x6e, 0xac, 0x23, 0x9d, 0x53, 0x74, + 0x47, 0xbf, 0x80, 0xaa, 0x2c, 0xe8, 0x86, 0x64, 0xf9, 0x8e, 0x3f, 0xaf, 0xc1, 0x01, 0x5f, 0xef, + 0x56, 0x68, 0xc8, 0xbe, 0x3f, 0xf7, 0xaa, 0x9d, 0x07, 0xc7, 0x35, 0x11, 0x74, 0x97, 0xf7, 0x98, + 0x41, 0x31, 0x5a, 0xa5, 0xfa, 0x7e, 0xd9, 0xdb, 0x5a, 0xa3, 0x8d, 0xcd, 0x1b, 0xbf, 0x44, 0x9d, + 0x13, 0x84, 0xc2, 0x71, 0xa4, 0xfb, 0xe3, 0x4a, 0x5a, 0xba, 0x00, 0xd2, 0x01, 0x25, 0xba, 0xe3, + 0x65, 0xce, 0x47, 0x2b, 0x97, 0x27, 0xc4, 0x29, 0x19, 0x29, 0x23, 0x0f, 0xa0, 0x3b, 0xd6, 0xaf, + 0x4d, 0x45, 0xdd, 0x8d, 0xe7, 0x1b, 0xf8, 0xdf, 0xbc, 0x81, 0xbf, 0x7d, 0x7f, 0x19, 0xd6, 0x81, + 0xf7, 0x83, 0xe5, 0xcb, 0xa4, 0x8f, 0x1c, 0x1e, 0x11, 0xa2, 0xa2, 0xa6, 0xe0, 0xb5, 0xde, 0x14, + 0xf1, 0x8a, 0x74, 0xdf, 0xb8, 0x61, 0xfd, 0xed, 0xb0, 0xd0, 0xe1, 0x37, 0x80, 0xaf, 0x0d, 0xaf, + 0x71, 0xb1, 0x31, 0x42, 0xa1, 0x10, 0x6c, 0x58, 0xc9, 0xc5, 0xba, 0xf7, 0x3f, 0xb8, 0x77, 0x17, + 0xae, 0x69, 0x2d, 0xcd, 0xcd, 0xd6, 0x87, 0x0f, 0xc1, 0xdb, 0x9e, 0xee, 0xee, 0xf7, 0xdf, 0x7b, + 0x57, 0x22, 0x4d, 0xb5, 0x75, 0x27, 0x8e, 0xe1, 0x9a, 0x10, 0xd5, 0xd9, 0x65, 0x1e, 0xd9, 0x37, + 0x34, 0x8a, 0x74, 0x2f, 0xad, 0xef, 0x53, 0x29, 0x0b, 0x06, 0x21, 0x57, 0xf7, 0xc8, 0xf0, 0xdb, + 0xd2, 0xcd, 0x23, 0x20, 0x3d, 0xea, 0x9f, 0xdf, 0xd9, 0xd9, 0x51, 0x5f, 0xcf, 0x9b, 0x10, 0xe9, + 0x6e, 0x67, 0x6d, 0xf9, 0xc5, 0xba, 0x4f, 0xc2, 0x42, 0x82, 0x79, 0xbc, 0xba, 0xed, 0x5b, 0xb7, + 0xc0, 0x4e, 0xb8, 0x7b, 0x24, 0x9e, 0x85, 0x74, 0x3f, 0x71, 0x47, 0xd1, 0x30, 0x0c, 0x69, 0x48, + 0xeb, 0x2e, 0x0f, 0x50, 0xac, 0x23, 0xdd, 0x5f, 0xb4, 0x90, 0xea, 0x55, 0xad, 0x0b, 0x98, 0xd4, + 0x9d, 0x78, 0x51, 0x5d, 0xb6, 0x78, 0x01, 0x55, 0x19, 0x84, 0x65, 0xf1, 0x90, 0x28, 0x94, 0xd4, + 0x64, 0xe4, 0x61, 0xbf, 0x67, 0x3a, 0x64, 0x71, 0xd0, 0x27, 0x9f, 0xbe, 0x2c, 0xa8, 0xc5, 0x14, + 0xbf, 0xa3, 0x9e, 0x22, 0x70, 0xbd, 0xa2, 0x24, 0xe9, 0x86, 0xae, 0x21, 0x53, 0xd7, 0x04, 0x24, + 0x7a, 0x43, 0x27, 0x2d, 0xfd, 0x09, 0x89, 0x30, 0x75, 0x4f, 0x41, 0x79, 0xf9, 0x88, 0x7a, 0x35, + 0xeb, 0x38, 0x26, 0x75, 0x2f, 0x17, 0x61, 0xef, 0x9e, 0xdd, 0xe5, 0xa4, 0xe1, 0xea, 0x97, 0x68, + 0xef, 0x16, 0x26, 0xcd, 0xc9, 0xfb, 0x23, 0x87, 0x98, 0xf8, 0xa2, 0x66, 0x6d, 0x7e, 0x1f, 0x6b, + 0xcf, 0x54, 0x3c, 0x77, 0xdd, 0xa4, 0x6d, 0x48, 0xe9, 0x84, 0x6e, 0x3e, 0xf7, 0x98, 0x09, 0x60, + 0x75, 0x67, 0x06, 0xac, 0xee, 0xcc, 0x80, 0xd5, 0x9d, 0x19, 0xb0, 0xba, 0x33, 0x03, 0x8d, 0x74, + 0x6f, 0x71, 0x73, 0x7c, 0xb1, 0x68, 0x2e, 0x4b, 0x95, 0xd8, 0x76, 0xfe, 0xb4, 0xa6, 0xba, 0xd3, + 0x81, 0xbc, 0xdc, 0x1c, 0xc3, 0xd9, 0xb3, 0x16, 0xcc, 0x9b, 0x7d, 0xde, 0xf3, 0x2c, 0xd3, 0xb1, + 0xd0, 0x08, 0x9d, 0xd3, 0x1d, 0x6f, 0x89, 0x5b, 0xb2, 0xd0, 0xa8, 0xaf, 0x4f, 0x6f, 0xda, 0x5b, + 0x54, 0x85, 0x2e, 0xea, 0x6e, 0x63, 0x69, 0x41, 0xb2, 0x65, 0x46, 0x7f, 0xa1, 0x73, 0xba, 0x03, + 0xbc, 0xbd, 0x3c, 0x51, 0x33, 0x51, 0x7b, 0x3b, 0x05, 0xd3, 0xf7, 0xe8, 0x26, 0xb4, 0xa1, 0x3b, + 0xc9, 0xc1, 0x00, 0x80, 0xe1, 0xa1, 0xa1, 0x1f, 0xb6, 0x6f, 0x43, 0xdb, 0x81, 0xfe, 0x7e, 0x7f, + 0xee, 0xfd, 0x2f, 0xc9, 0x13, 0x2b, 0xb9, 0x5c, 0x35, 0x02, 0x63, 0x10, 0xda, 0xd0, 0xbd, 0xb1, + 0x81, 0xd4, 0x3c, 0x07, 0x08, 0x46, 0x73, 0xde, 0xde, 0xff, 0xe7, 0xef, 0xbe, 0x97, 0x2f, 0x81, + 0xdf, 0x5b, 0x9a, 0xc9, 0xb6, 0xed, 0x24, 0x25, 0x25, 0xaa, 0x15, 0x1a, 0x63, 0x60, 0x46, 0x77, + 0xc5, 0xc5, 0x77, 0x6b, 0x4b, 0x4b, 0xe1, 0x33, 0x25, 0xf3, 0xf9, 0x0d, 0x0f, 0x0f, 0x13, 0xdf, + 0xb2, 0xba, 0xcb, 0x00, 0x51, 0xf7, 0x27, 0x05, 0xf9, 0x60, 0xe4, 0x5c, 0x8d, 0xbb, 0x31, 0x41, + 0x22, 0xdb, 0xbe, 0xfd, 0x06, 0x7f, 0xcb, 0xea, 0x2e, 0x03, 0x48, 0xf7, 0xb6, 0xb6, 0x36, 0xa8, + 0x95, 0xa3, 0x0b, 0x26, 0x25, 0xba, 0x8b, 0x07, 0x95, 0x8a, 0x86, 0x3a, 0xb2, 0xba, 0xcb, 0x00, + 0xe8, 0xbe, 0xd8, 0x68, 0x1e, 0xf1, 0x79, 0xd6, 0x4f, 0x3b, 0xb6, 0x6b, 0xd8, 0x2f, 0x55, 0xa2, + 0xd7, 0xc9, 0xcf, 0x3f, 0xec, 0xd0, 0xc2, 0x17, 0xa1, 0x10, 0xda, 0xf3, 0xfb, 0xf5, 0xab, 0xbe, + 0xb8, 0x4c, 0x14, 0xfa, 0x7d, 0xf9, 0xbb, 0x8b, 0x87, 0xf8, 0x7c, 0xd6, 0xef, 0x32, 0x40, 0x2c, + 0xdf, 0xad, 0x0e, 0x99, 0x53, 0xa5, 0xbb, 0xe1, 0xec, 0x59, 0xf8, 0x5c, 0x0d, 0xac, 0xee, 0x32, + 0x20, 0x5d, 0x9f, 0x79, 0xf5, 0xea, 0xa5, 0x86, 0x69, 0xa6, 0x26, 0x27, 0x11, 0xdf, 0xb2, 0xba, + 0xcb, 0x80, 0x4a, 0xf5, 0x77, 0xf5, 0xc0, 0xea, 0x2e, 0x03, 0xac, 0xee, 0xd2, 0xd0, 0xb6, 0xee, + 0x1d, 0xfd, 0x82, 0x33, 0x0f, 0xaa, 0x8e, 0x04, 0x3f, 0xc7, 0x3a, 0xbf, 0xbf, 0xe9, 0xff, 0x8e, + 0x75, 0x7e, 0x17, 0xf5, 0x7f, 0x3f, 0x2c, 0xea, 0xff, 0x8e, 0x75, 0x7e, 0xf7, 0xc9, 0x3b, 0x24, + 0xea, 0xff, 0x6e, 0x7e, 0x29, 0xe7, 0x10, 0xea, 0xfc, 0xfe, 0x77, 0x36, 0xd6, 0xf9, 0xfd, 0x42, + 0x96, 0xa9, 0x77, 0xa6, 0xe5, 0xe5, 0x5c, 0xfb, 0xa0, 0xe2, 0x9b, 0xe9, 0x93, 0x93, 0xf2, 0xe9, + 0x9f, 0xee, 0xc4, 0x09, 0x70, 0xee, 0x46, 0x47, 0xa9, 0x34, 0x61, 0x0e, 0x49, 0x46, 0x46, 0x84, + 0xa3, 0x0d, 0xaf, 0x2b, 0x21, 0x4a, 0x27, 0xd8, 0x21, 0xc3, 0xc9, 0x7e, 0x22, 0xc7, 0x1e, 0xde, + 0xca, 0xc2, 0xba, 0xad, 0xe9, 0x9f, 0xee, 0xc4, 0x37, 0x5f, 0x6f, 0x58, 0xef, 0x75, 0xc6, 0x83, + 0xf2, 0x3c, 0xc0, 0xef, 0x7d, 0x43, 0xa3, 0xfb, 0x9c, 0xc4, 0xc3, 0x04, 0x38, 0x8e, 0xb1, 0x66, + 0x17, 0x73, 0xb0, 0x41, 0x07, 0xc4, 0x71, 0x07, 0x01, 0x93, 0xe3, 0x0e, 0xf0, 0x41, 0x07, 0x36, + 0xf8, 0xb8, 0x83, 0x60, 0xf4, 0xfa, 0x66, 0xd0, 0x41, 0x40, 0xf1, 0x01, 0xcf, 0x0c, 0x5c, 0xfd, + 0x63, 0xb7, 0xcb, 0xf4, 0x58, 0xf7, 0x81, 0x81, 0x01, 0x9a, 0x06, 0x1d, 0x80, 0xee, 0x07, 0x5d, + 0xc4, 0x1d, 0xc7, 0x9c, 0x23, 0xd5, 0xe9, 0x8d, 0x2d, 0x13, 0x05, 0x2f, 0x5a, 0x38, 0xc7, 0xc5, + 0xd2, 0x5f, 0x0c, 0x8c, 0xa2, 0x2a, 0x59, 0xed, 0x60, 0x52, 0x77, 0xbc, 0x33, 0xff, 0xee, 0x9d, + 0x3f, 0x51, 0x9b, 0x87, 0x6b, 0x48, 0x36, 0x52, 0x27, 0x8b, 0x4b, 0x41, 0x7b, 0x7a, 0x7b, 0x7b, + 0xfb, 0x6f, 0xbf, 0xfc, 0x8c, 0x42, 0x35, 0x5e, 0xb2, 0xd4, 0xdc, 0x2b, 0x0d, 0x52, 0x76, 0xf3, + 0xf2, 0xd7, 0x3c, 0x65, 0x6d, 0x42, 0xac, 0x7b, 0x25, 0x97, 0x4b, 0xbc, 0xed, 0x56, 0x30, 0x73, + 0xa2, 0x1a, 0xb0, 0x74, 0xbd, 0x03, 0xd2, 0x58, 0x04, 0x14, 0x6b, 0x9e, 0x54, 0xf8, 0xad, 0x30, + 0x62, 0x9c, 0xae, 0xce, 0xc7, 0x79, 0x9d, 0x7c, 0x4c, 0x77, 0x4f, 0xff, 0xf3, 0xf1, 0x9a, 0xde, + 0x13, 0x68, 0x13, 0x93, 0xf3, 0xfd, 0x10, 0xf9, 0xf1, 0x07, 0x2b, 0xf1, 0x23, 0x42, 0x82, 0x02, + 0x7b, 0x7b, 0xd4, 0x9f, 0x8c, 0x98, 0xdb, 0xd4, 0x6f, 0x79, 0x22, 0x12, 0xa4, 0x51, 0x69, 0x11, + 0x12, 0x05, 0x20, 0x0e, 0x09, 0xea, 0xed, 0xed, 0x85, 0x3d, 0x50, 0xc3, 0x01, 0xdd, 0xad, 0xfd, + 0x65, 0xf7, 0x8e, 0xd7, 0x4d, 0x4c, 0xb9, 0xae, 0x76, 0x74, 0xb4, 0x4b, 0x7c, 0xcc, 0xe7, 0x0f, + 0xae, 0x5f, 0xfb, 0xf1, 0x86, 0xcf, 0xd6, 0xaa, 0x9d, 0xc1, 0x9d, 0x82, 0x46, 0xd0, 0x9d, 0x43, + 0xe9, 0x04, 0x57, 0x05, 0xf9, 0x79, 0xc4, 0x4b, 0x91, 0x75, 0xd0, 0x73, 0xd0, 0xdd, 0x54, 0xb3, + 0xb9, 0x94, 0xb4, 0x0c, 0x25, 0xba, 0xff, 0xf0, 0xfd, 0x77, 0xf9, 0x79, 0xb9, 0x9a, 0x5c, 0x6c, + 0xfd, 0xd3, 0x6b, 0x31, 0xdd, 0x1d, 0xd4, 0x5c, 0x4d, 0x40, 0xba, 0xc4, 0xab, 0x28, 0xc7, 0xae, + 0xcc, 0x6d, 0xad, 0xad, 0x0b, 0x0d, 0xe6, 0xa0, 0x3d, 0xb6, 0x21, 0x25, 0xa0, 0x3b, 0xc7, 0x23, + 0x4d, 0xed, 0x20, 0xb5, 0x0f, 0x25, 0xba, 0x23, 0xc5, 0xf7, 0xfd, 0xb9, 0xf7, 0xc2, 0x39, 0x2f, + 0xf5, 0x32, 0x00, 0xdd, 0x51, 0x87, 0x6c, 0x55, 0x4f, 0xfc, 0xf4, 0xe3, 0x0f, 0x51, 0xe3, 0x17, + 0x7a, 0x9b, 0x98, 0x90, 0x10, 0xf3, 0xe0, 0xbe, 0x44, 0xb3, 0x0c, 0x02, 0xd4, 0x2c, 0xb1, 0x51, + 0xac, 0x9e, 0x19, 0xea, 0x45, 0xc8, 0x08, 0x14, 0xe9, 0x9e, 0x9c, 0x94, 0xb8, 0xe9, 0x8b, 0xcf, + 0x27, 0x34, 0x1b, 0x4f, 0xac, 0xb6, 0xee, 0xc4, 0x1c, 0x6b, 0x6b, 0x6a, 0x50, 0xc1, 0x72, 0x37, + 0xea, 0x8e, 0xf4, 0x91, 0x36, 0x41, 0xcf, 0xb1, 0xc1, 0xb1, 0xe7, 0x95, 0x4c, 0x34, 0xab, 0x53, + 0x50, 0xa4, 0xfb, 0x82, 0x79, 0xb3, 0xb7, 0x7d, 0xfb, 0xcd, 0xae, 0x9f, 0x7f, 0x04, 0xc2, 0x77, + 0xae, 0x79, 0x8d, 0x2d, 0x35, 0xb3, 0x79, 0xe3, 0x57, 0xff, 0xfc, 0xea, 0x0b, 0x07, 0x7b, 0x3b, + 0xf8, 0x97, 0xa0, 0xe9, 0x24, 0x57, 0x2d, 0x5f, 0xba, 0x7d, 0xeb, 0x96, 0xb0, 0x90, 0xa0, 0x3b, + 0x11, 0xe1, 0x20, 0x50, 0x70, 0x60, 0x40, 0x6b, 0x4b, 0x0b, 0x9e, 0x08, 0x25, 0xba, 0x37, 0x36, + 0x36, 0x20, 0xdd, 0x4b, 0x4b, 0x9e, 0x4b, 0x1f, 0x89, 0x74, 0xd7, 0x70, 0xae, 0x3c, 0x2d, 0x43, + 0x91, 0xee, 0x68, 0x00, 0x1f, 0x42, 0xa0, 0xbf, 0xdf, 0xca, 0x65, 0x4b, 0x60, 0x27, 0x2a, 0x55, + 0x91, 0x28, 0xeb, 0xd6, 0x60, 0x6b, 0x30, 0x8c, 0x8f, 0x8f, 0xc3, 0x2f, 0x03, 0x2d, 0xde, 0x00, + 0x15, 0xa1, 0x53, 0xae, 0x2e, 0xc4, 0x44, 0xfc, 0xa8, 0xd0, 0x7d, 0x42, 0x34, 0xd1, 0x36, 0xd4, + 0xdc, 0x65, 0x1e, 0x09, 0xb7, 0xb8, 0x90, 0x85, 0xbc, 0x95, 0x89, 0x74, 0x13, 0x72, 0x75, 0xb7, + 0xb7, 0xb5, 0x36, 0x37, 0xdd, 0x4f, 0xfc, 0x14, 0x09, 0xb1, 0xf6, 0xa3, 0x55, 0xf8, 0x36, 0xd4, + 0x73, 0x9a, 0x1a, 0x1b, 0xa1, 0xd8, 0x85, 0xed, 0xe5, 0x4b, 0x16, 0xc1, 0xab, 0xf1, 0x02, 0xc3, + 0xf5, 0x9f, 0xac, 0x21, 0x9e, 0x45, 0x95, 0xee, 0x0a, 0x60, 0x13, 0x50, 0x84, 0xdd, 0x1f, 0x5c, + 0xce, 0x55, 0x35, 0x0b, 0x06, 0x21, 0x57, 0x77, 0xe9, 0xbb, 0x27, 0x10, 0xf4, 0xa8, 0x9d, 0xad, + 0x84, 0xee, 0x13, 0xa2, 0x1e, 0x2f, 0x9f, 0x7f, 0xba, 0x06, 0x2e, 0x80, 0x07, 0xf7, 0x9b, 0x08, + 0x85, 0xc2, 0x17, 0x55, 0x95, 0xc4, 0x11, 0xc6, 0xd7, 0x53, 0x6a, 0xe8, 0xd6, 0xdd, 0x36, 0x10, + 0xd3, 0xdd, 0xec, 0x92, 0x1e, 0xea, 0x8e, 0x16, 0x52, 0xf9, 0x64, 0xf5, 0x07, 0x68, 0x43, 0x93, + 0x0a, 0xbb, 0x04, 0x8e, 0x84, 0x95, 0x61, 0xba, 0xcb, 0x5f, 0x73, 0x51, 0x73, 0xd8, 0x85, 0x96, + 0x62, 0x59, 0xb8, 0xc9, 0xa8, 0xea, 0xe8, 0x2c, 0xc4, 0xba, 0xc3, 0x25, 0x91, 0x78, 0xbf, 0x2a, + 0x6f, 0x45, 0x18, 0x35, 0x60, 0x72, 0x2a, 0x09, 0x1b, 0xc4, 0x16, 0x48, 0x41, 0x23, 0x81, 0x3c, + 0xa4, 0x57, 0xb4, 0x6b, 0x6d, 0xb4, 0x26, 0x55, 0x90, 0x31, 0x6e, 0x98, 0xd8, 0x48, 0xa0, 0x21, + 0xce, 0x3f, 0xac, 0x42, 0x8a, 0x94, 0xd4, 0xf5, 0x52, 0x95, 0xa6, 0x4c, 0x70, 0x4e, 0x62, 0x43, + 0xc2, 0xcd, 0x4e, 0x27, 0x0f, 0x0c, 0x8f, 0xd2, 0x9a, 0x11, 0x55, 0x98, 0xd4, 0xbd, 0xaa, 0xb2, + 0x92, 0xda, 0x46, 0x31, 0xa7, 0x88, 0x0a, 0x8e, 0x63, 0x2c, 0xc8, 0x71, 0xf8, 0xda, 0x13, 0x4a, + 0x12, 0x54, 0x80, 0xd0, 0x37, 0xa3, 0xc2, 0x0f, 0x5f, 0xc8, 0xec, 0x1b, 0xd2, 0x03, 0xe9, 0xa7, + 0x5c, 0x57, 0xdf, 0x5d, 0x68, 0xb4, 0xe7, 0xd7, 0x9d, 0xe4, 0x4f, 0x26, 0xce, 0x92, 0x8d, 0xd3, + 0x3f, 0xbd, 0xe6, 0xe8, 0xad, 0x32, 0x93, 0x37, 0x0d, 0xee, 0x26, 0xbe, 0xb4, 0x8b, 0x8e, 0x60, + 0x1f, 0x56, 0x8a, 0x3f, 0x09, 0xb1, 0xf2, 0x2f, 0x3c, 0x7b, 0xbf, 0x8a, 0xc2, 0x65, 0xad, 0x28, + 0xa4, 0x50, 0x38, 0x3e, 0x81, 0xeb, 0x5e, 0x55, 0x55, 0x59, 0x5e, 0x5e, 0x9e, 0x95, 0xf9, 0x98, + 0xfc, 0x60, 0x6d, 0x80, 0x9c, 0xc1, 0xda, 0x0f, 0xf0, 0xef, 0x7f, 0xe4, 0x2a, 0x65, 0x73, 0xfd, + 0x90, 0xc1, 0xe9, 0x7b, 0x55, 0x8c, 0x0f, 0xc7, 0x56, 0x4a, 0xb8, 0xdd, 0x99, 0xd0, 0xf0, 0xb9, + 0x36, 0x36, 0x8f, 0x39, 0x3e, 0xbb, 0x3a, 0x4e, 0xc7, 0x18, 0x8e, 0xcb, 0x23, 0xdb, 0xa0, 0xe2, + 0xac, 0x2a, 0x06, 0x66, 0xe1, 0x19, 0x1d, 0x13, 0x1e, 0x8f, 0xac, 0xd8, 0x7f, 0x26, 0x95, 0x73, + 0x3c, 0x8e, 0xe3, 0x1c, 0x47, 0xe1, 0x74, 0xed, 0x54, 0x91, 0x02, 0xdd, 0x59, 0xa8, 0x0d, 0x56, + 0x77, 0x66, 0xc0, 0xea, 0xce, 0x0c, 0x58, 0xdd, 0x99, 0x01, 0xab, 0x3b, 0x33, 0x60, 0x75, 0x67, + 0x06, 0xac, 0xee, 0xcc, 0x80, 0xd5, 0x9d, 0x19, 0xb0, 0xba, 0x33, 0x03, 0xf5, 0x75, 0x17, 0x0a, + 0x04, 0x8c, 0xaf, 0x25, 0xa4, 0x8f, 0xd4, 0x54, 0xf7, 0x71, 0x81, 0x80, 0xf1, 0xb9, 0x5c, 0xf4, + 0x8f, 0xc6, 0x06, 0x14, 0xf8, 0x9d, 0xf1, 0x05, 0xa8, 0xf4, 0x8e, 0xbc, 0x5f, 0x77, 0x68, 0xaa, + 0xfb, 0x0c, 0x41, 0x51, 0xe1, 0x33, 0xe3, 0xf9, 0x06, 0x8b, 0x8d, 0xe6, 0x99, 0x1d, 0xd8, 0x77, + 0xc6, 0xfd, 0x94, 0xbd, 0xad, 0xf5, 0xf2, 0x25, 0x8b, 0x0c, 0xde, 0xf9, 0x07, 0x3e, 0x2d, 0x37, + 0x0b, 0x3d, 0x02, 0x6b, 0x77, 0x25, 0xd8, 0xb5, 0xf3, 0x27, 0x30, 0x37, 0xea, 0xaa, 0x82, 0xe3, + 0x51, 0x7c, 0x5c, 0x6a, 0x4a, 0x32, 0x9a, 0x7c, 0x9e, 0x85, 0x1e, 0x81, 0xb5, 0xbb, 0x12, 0x0c, + 0xf1, 0xf9, 0x7b, 0x7f, 0xdb, 0x85, 0x3f, 0xf2, 0x5f, 0x68, 0x30, 0x67, 0xcf, 0xae, 0x9d, 0x21, + 0x41, 0x81, 0x12, 0xd3, 0xb0, 0xb0, 0xd0, 0x0b, 0xb0, 0x76, 0x57, 0x0d, 0xbc, 0xba, 0x5a, 0xdf, + 0xcb, 0x97, 0x56, 0x2d, 0x5f, 0x0a, 0xd6, 0x97, 0xe8, 0x06, 0xca, 0x42, 0xf7, 0x31, 0x7d, 0xec, + 0xde, 0xd8, 0xd0, 0x40, 0xc7, 0x4a, 0x74, 0x5b, 0x37, 0x6f, 0x02, 0x67, 0x07, 0x07, 0x06, 0x10, + 0x77, 0xda, 0x58, 0x5a, 0xc0, 0x4e, 0x8f, 0x53, 0x27, 0x29, 0xcf, 0x2e, 0x29, 0x29, 0x51, 0xbf, + 0x16, 0xff, 0xd2, 0x2f, 0xb0, 0x76, 0x57, 0x8e, 0x84, 0xb8, 0xb8, 0xaf, 0x37, 0xac, 0xc3, 0xeb, + 0x33, 0x46, 0x73, 0xde, 0x86, 0xfa, 0xcc, 0xeb, 0x57, 0xb4, 0x2c, 0x64, 0xc1, 0xda, 0x9d, 0x56, + 0xcc, 0x08, 0xbb, 0x8f, 0x8f, 0x8f, 0xc7, 0xc7, 0xc6, 0x7c, 0xb9, 0xee, 0x53, 0xcd, 0x67, 0x78, + 0xd2, 0x10, 0xc5, 0x45, 0x85, 0xeb, 0xd6, 0xae, 0xbe, 0x17, 0x1d, 0x85, 0xfa, 0x2d, 0x49, 0x83, + 0xb5, 0x3b, 0xad, 0x98, 0xce, 0x76, 0x7f, 0xf9, 0xb2, 0x7a, 0xdf, 0x9f, 0xbf, 0x13, 0x3b, 0xf4, + 0xeb, 0x82, 0xdd, 0x89, 0xf1, 0xfc, 0xef, 0xb7, 0x5f, 0x25, 0xa6, 0xd6, 0x65, 0xed, 0x4e, 0x2b, + 0xa6, 0x9b, 0xdd, 0x07, 0x07, 0x07, 0x2f, 0x9c, 0xf7, 0x5a, 0xb6, 0x78, 0x81, 0xc4, 0x4c, 0x0b, + 0xba, 0x69, 0x77, 0x9c, 0x4b, 0x16, 0x18, 0x9e, 0xf5, 0x38, 0xdd, 0xd7, 0xd7, 0xc7, 0xda, 0x9d, + 0x56, 0x4c, 0x2b, 0xbb, 0xc7, 0xc5, 0xc5, 0x06, 0xdc, 0xbc, 0xf1, 0xc9, 0x47, 0x1f, 0xc8, 0xb4, + 0x14, 0x30, 0xf6, 0xe1, 0x83, 0xa6, 0xc6, 0x46, 0x06, 0x99, 0x9c, 0x98, 0x28, 0x2f, 0xb6, 0xd5, + 0xef, 0xaf, 0xb8, 0xea, 0xeb, 0x73, 0x27, 0x32, 0x82, 0xb5, 0x3b, 0x7d, 0x98, 0x56, 0x76, 0x27, + 0x56, 0x66, 0x7a, 0x7b, 0x7a, 0xdc, 0xdd, 0x5c, 0x17, 0xcf, 0x37, 0xd0, 0xe5, 0xd2, 0x7d, 0xb1, + 0xd1, 0xbc, 0x93, 0x27, 0x9c, 0xbb, 0xba, 0x3a, 0xf1, 0x03, 0xd8, 0xd2, 0x9d, 0x56, 0x4c, 0x5b, + 0xbb, 0x13, 0x51, 0x5e, 0x56, 0xb6, 0x67, 0xd7, 0x2f, 0xba, 0x63, 0xf7, 0x5f, 0x7e, 0xfc, 0x4f, + 0xc9, 0x73, 0xd9, 0xa3, 0x78, 0x59, 0xbb, 0xd3, 0x8a, 0x19, 0x61, 0x77, 0x3d, 0x02, 0x6b, 0x77, + 0x5a, 0xc1, 0xda, 0x5d, 0xb7, 0xc0, 0xda, 0x9d, 0x56, 0xcc, 0x2c, 0xbb, 0xf7, 0x0d, 0x8d, 0x96, + 0x35, 0xf4, 0xf2, 0x3a, 0xf9, 0x75, 0x1d, 0x18, 0x6b, 0x81, 0xed, 0xfc, 0x9a, 0xf6, 0x41, 0xe0, + 0xeb, 0xb6, 0xc1, 0x57, 0x22, 0xbe, 0x6c, 0x1d, 0x00, 0x56, 0xb7, 0x0c, 0xbc, 0x10, 0xb1, 0xaa, + 0x19, 0xb1, 0xbf, 0xb2, 0xb9, 0x9f, 0xdb, 0x24, 0x66, 0x45, 0x23, 0xc6, 0xf2, 0xc6, 0xbe, 0xb2, + 0x06, 0x8c, 0xa5, 0xc0, 0xfa, 0xbe, 0x92, 0xfa, 0xde, 0xe7, 0x3c, 0x8c, 0xc5, 0xc0, 0xba, 0xde, + 0xa2, 0xba, 0xde, 0xc2, 0xba, 0x9e, 0xc2, 0xda, 0x9e, 0x67, 0xc0, 0x9a, 0x9e, 0xa7, 0x35, 0xd8, + 0x36, 0xa4, 0xdf, 0xd6, 0x27, 0x50, 0x10, 0x21, 0x6b, 0x77, 0x5a, 0xf1, 0xd6, 0xd8, 0xd8, 0x98, + 0x60, 0x5a, 0xa0, 0xb6, 0xb6, 0x36, 0x2b, 0xf3, 0x31, 0x71, 0x4f, 0xdf, 0xe0, 0x90, 0xef, 0xa3, + 0x2a, 0xfb, 0xc0, 0xa7, 0xe6, 0x67, 0x92, 0xf6, 0x39, 0xc7, 0xec, 0x77, 0xb8, 0xcf, 0x20, 0xf7, + 0x11, 0xe6, 0x69, 0xe0, 0x38, 0x3c, 0x34, 0x71, 0x49, 0xb0, 0xbb, 0xf1, 0xe4, 0x44, 0x14, 0xb7, + 0xe0, 0xf5, 0x14, 0x73, 0xb3, 0x76, 0xa7, 0x15, 0x8a, 0x4a, 0xf7, 0xfb, 0xf7, 0xa2, 0x17, 0x19, + 0xce, 0xd5, 0x97, 0x25, 0x38, 0x89, 0xa5, 0xbb, 0x4b, 0x54, 0xa5, 0xa9, 0x77, 0x16, 0xe3, 0x53, + 0x6d, 0x90, 0xe4, 0xc1, 0x33, 0xa9, 0xae, 0xd1, 0x95, 0x28, 0x72, 0xd6, 0xee, 0xb4, 0x42, 0xae, + 0xdd, 0x6f, 0x85, 0x86, 0xe0, 0x4f, 0x40, 0x9a, 0x9b, 0x9a, 0xb4, 0x19, 0x93, 0x7a, 0x40, 0x76, + 0x3f, 0x19, 0xc5, 0x35, 0x39, 0x1e, 0x47, 0x34, 0x13, 0xc7, 0x31, 0xc6, 0xe2, 0x4a, 0x81, 0x73, + 0x44, 0x79, 0x5c, 0x61, 0x33, 0xd4, 0x43, 0x1a, 0xba, 0x86, 0x5a, 0x7a, 0x87, 0x5b, 0x7b, 0x87, + 0xa1, 0x52, 0x01, 0x6c, 0x07, 0xf6, 0x0b, 0x3a, 0xde, 0xb0, 0x73, 0x40, 0xd0, 0x35, 0x30, 0x02, + 0xec, 0x1e, 0x14, 0xb3, 0x87, 0x3f, 0xd2, 0x2b, 0xe6, 0x28, 0xd4, 0x85, 0x10, 0xfb, 0x87, 0x46, + 0x07, 0x86, 0x81, 0x63, 0xc0, 0x41, 0x81, 0x98, 0x7c, 0xc1, 0xd8, 0xd0, 0x88, 0x98, 0xc3, 0x23, + 0x42, 0x8c, 0xa3, 0x18, 0x05, 0xa3, 0xc2, 0x91, 0x31, 0x8c, 0x70, 0x22, 0xe4, 0x9b, 0x56, 0xd1, + 0xee, 0x12, 0xc5, 0xb5, 0xbb, 0xfe, 0x44, 0xd2, 0xf7, 0xce, 0xf1, 0x67, 0xee, 0x57, 0xb1, 0x76, + 0xa7, 0x15, 0xb2, 0xed, 0xee, 0x7d, 0xce, 0x93, 0xd8, 0x3c, 0x6c, 0x34, 0xe7, 0xed, 0xf2, 0xb2, + 0x32, 0x2d, 0x47, 0xa6, 0x2a, 0x4a, 0xab, 0x6a, 0x4e, 0xfa, 0x46, 0x11, 0x0d, 0x64, 0xfd, 0x57, + 0x66, 0x74, 0x7e, 0x3d, 0xd3, 0x71, 0x4d, 0x41, 0x57, 0x57, 0x67, 0xf8, 0xad, 0xb0, 0xdf, 0xf7, + 0xec, 0x46, 0xcb, 0x13, 0x6e, 0x3d, 0x74, 0xde, 0xcc, 0x2b, 0x95, 0x18, 0xb3, 0x87, 0xef, 0xed, + 0x6a, 0x1e, 0x65, 0x53, 0x17, 0xb2, 0x90, 0x80, 0x0c, 0xbb, 0x1f, 0xb1, 0xb1, 0x92, 0xf9, 0xd8, + 0x2f, 0x3d, 0x35, 0x45, 0xfb, 0xf1, 0x91, 0x44, 0x72, 0x79, 0xfb, 0xd1, 0xbf, 0xe2, 0x9d, 0x3d, + 0x82, 0xc4, 0x25, 0xba, 0x5b, 0xb2, 0x7f, 0xca, 0x6b, 0xa6, 0x83, 0xc2, 0x26, 0x0d, 0xf7, 0xbf, + 0x79, 0xe3, 0xa7, 0x1d, 0xdb, 0xe7, 0xcf, 0x7d, 0x47, 0xa6, 0xa4, 0xdf, 0x6e, 0xfa, 0x7a, 0x02, + 0xab, 0x7a, 0x71, 0x39, 0x0e, 0xe2, 0x95, 0x7d, 0xdc, 0x3c, 0xfd, 0x8f, 0xf8, 0x66, 0xc0, 0xed, + 0x2f, 0xd3, 0xb1, 0x4f, 0x4f, 0x48, 0xda, 0xfd, 0xbf, 0xbb, 0x7f, 0x95, 0xf7, 0x94, 0x1b, 0x78, + 0x3b, 0x2c, 0x94, 0x91, 0x28, 0x15, 0x63, 0x4c, 0x38, 0x6e, 0x13, 0x5c, 0x62, 0x79, 0x22, 0x12, + 0xd9, 0x9d, 0x73, 0x3a, 0x25, 0x32, 0x97, 0xc7, 0x74, 0x50, 0x93, 0x88, 0x8b, 0x79, 0x28, 0xb1, + 0xd4, 0x2c, 0xce, 0xa4, 0xc4, 0x47, 0xe8, 0x98, 0xb3, 0x0f, 0xaa, 0x38, 0xa2, 0x3a, 0x18, 0xd8, + 0xdd, 0xcc, 0x29, 0xda, 0x2e, 0xac, 0x94, 0xd9, 0x98, 0xa7, 0x2b, 0xa6, 0xd8, 0xfd, 0xe5, 0xcb, + 0xea, 0xdc, 0xec, 0x6c, 0x9c, 0x09, 0xf1, 0x71, 0xdf, 0x6f, 0xdb, 0x42, 0xdc, 0x03, 0xec, 0xee, + 0xea, 0x92, 0x48, 0x02, 0xbf, 0x1a, 0x5c, 0xbb, 0xe2, 0xa3, 0xc5, 0xc8, 0x27, 0xf1, 0xa8, 0xb4, + 0xcd, 0xf4, 0x6c, 0x1a, 0x6e, 0x77, 0xdb, 0xe0, 0xe7, 0x50, 0x93, 0x66, 0x24, 0x12, 0x05, 0x28, + 0x2a, 0x2c, 0xc4, 0x57, 0xb4, 0x46, 0xfc, 0x6e, 0xcb, 0xe4, 0x52, 0xe0, 0x70, 0x63, 0x60, 0xe5, + 0x57, 0x88, 0xdb, 0xdd, 0xca, 0x37, 0xbf, 0xa2, 0x51, 0x3f, 0x5a, 0x08, 0xf4, 0x0b, 0x8a, 0x5a, + 0x66, 0x3a, 0x3a, 0xda, 0xa1, 0x96, 0xa9, 0xf8, 0xfc, 0xc8, 0xf0, 0xdb, 0xf0, 0x9f, 0x3b, 0xe3, + 0x7e, 0x0a, 0xad, 0x3a, 0x91, 0x99, 0x91, 0x4e, 0x69, 0x78, 0xa4, 0x10, 0x90, 0x51, 0xcb, 0x71, + 0x4d, 0x44, 0x76, 0x87, 0x5a, 0x81, 0x53, 0x04, 0x65, 0x8b, 0xce, 0x51, 0x05, 0xb3, 0x03, 0xfb, + 0xd0, 0x22, 0xc8, 0xf5, 0x3c, 0xde, 0x86, 0xcf, 0xd6, 0x4a, 0x57, 0x0e, 0x85, 0xe3, 0xe3, 0xb6, + 0x21, 0x25, 0xb8, 0xdd, 0x39, 0xee, 0xa9, 0x69, 0x15, 0x0c, 0xcc, 0x6f, 0x3a, 0xed, 0xa1, 0x91, + 0xdd, 0x5f, 0xbf, 0x7a, 0x65, 0x38, 0x7b, 0xd6, 0x8a, 0xa5, 0xc6, 0xa3, 0xa3, 0xa3, 0x3d, 0xdd, + 0xdd, 0x0b, 0x0d, 0xe6, 0x40, 0x25, 0xb5, 0xa5, 0xb9, 0x99, 0xea, 0x20, 0x95, 0xc0, 0x3f, 0x1d, + 0xb3, 0x3b, 0xb8, 0xc4, 0xc6, 0x35, 0x5c, 0x0b, 0x76, 0xff, 0xfc, 0xd3, 0x35, 0x3f, 0x7e, 0xff, + 0x6f, 0x1e, 0xaf, 0x0e, 0x19, 0x17, 0xbe, 0x7e, 0x56, 0xa6, 0xdc, 0x35, 0x73, 0xdc, 0x5c, 0x4e, + 0x14, 0xe4, 0x4b, 0xce, 0x86, 0xdc, 0xdb, 0xd3, 0x73, 0xfa, 0xe4, 0x94, 0x71, 0xae, 0xa3, 0x50, + 0x1f, 0x13, 0xd9, 0x1d, 0x7e, 0xb4, 0xfb, 0x1d, 0xee, 0x9b, 0x9c, 0x49, 0xcb, 0x7a, 0xd1, 0x39, + 0xc1, 0x82, 0x6a, 0xa8, 0x6f, 0x77, 0xb8, 0x0f, 0x5b, 0xfd, 0xfe, 0x72, 0xf8, 0x7f, 0x3f, 0x2f, + 0x2e, 0x42, 0x7b, 0x92, 0x13, 0x1f, 0xa1, 0xe9, 0xf9, 0xa9, 0x5d, 0x3e, 0x51, 0x29, 0x90, 0xdd, + 0xdf, 0x3c, 0xc1, 0xa1, 0xdd, 0xee, 0xc6, 0xa2, 0x5e, 0x96, 0x2b, 0x97, 0x2d, 0x29, 0x2a, 0x7c, + 0x26, 0x10, 0x08, 0x0a, 0x9f, 0x3d, 0x25, 0x7e, 0x7a, 0xcd, 0xd7, 0xe7, 0x3d, 0xe3, 0x85, 0x68, + 0xce, 0x82, 0x1f, 0xb6, 0x7f, 0x47, 0x32, 0x4d, 0xc1, 0xa8, 0xd0, 0x46, 0xb4, 0xca, 0x17, 0xe2, + 0x01, 0xaf, 0x8c, 0xbc, 0x97, 0x6c, 0x73, 0x24, 0xf5, 0x50, 0xdf, 0xee, 0x26, 0x7f, 0xec, 0x85, + 0x7f, 0xea, 0x15, 0x9f, 0xcb, 0xc4, 0x9d, 0x50, 0x98, 0xc1, 0xce, 0x5d, 0x84, 0x85, 0x5a, 0x87, + 0xf8, 0xfc, 0xb6, 0xb6, 0xb6, 0xf1, 0xf1, 0xf1, 0xf2, 0xb2, 0xb2, 0x97, 0xd5, 0xd5, 0x68, 0x27, + 0xaf, 0xae, 0xf6, 0x49, 0x7e, 0x3e, 0x78, 0x85, 0x78, 0x58, 0x69, 0x49, 0x49, 0x7e, 0x5e, 0x2e, + 0xbe, 0x74, 0x4e, 0x6b, 0x6b, 0x2b, 0x9c, 0x88, 0x1f, 0x50, 0x5f, 0xcf, 0x93, 0xf7, 0xc0, 0x8b, + 0x11, 0xbb, 0x43, 0xfc, 0xd2, 0x1f, 0xdd, 0x89, 0x08, 0xc7, 0x57, 0xab, 0x05, 0xc2, 0xb5, 0xee, + 0xc6, 0xb5, 0x2b, 0x64, 0xd2, 0x1c, 0x1a, 0x19, 0x43, 0x8b, 0x7c, 0x89, 0x1f, 0x3c, 0x79, 0x67, + 0x3e, 0xad, 0x51, 0x7f, 0xa9, 0x4f, 0x16, 0xf2, 0xa0, 0xa6, 0xdd, 0xe1, 0xae, 0x54, 0x41, 0x03, + 0x0e, 0x10, 0x7c, 0x8f, 0x8e, 0x8c, 0x8b, 0x8d, 0xc1, 0xa7, 0x25, 0x4a, 0x4c, 0x88, 0x87, 0x6d, + 0xb4, 0x50, 0x77, 0x57, 0x67, 0xe7, 0x62, 0xa3, 0x79, 0xa8, 0x1a, 0xf0, 0xe5, 0xfa, 0x4f, 0x71, + 0x5b, 0x78, 0x7a, 0xb8, 0x43, 0x6d, 0x01, 0x6d, 0x1f, 0xe0, 0xfc, 0xf1, 0xd3, 0x8e, 0xed, 0x2f, + 0xaa, 0x2a, 0x8d, 0x17, 0x18, 0xa6, 0xa6, 0x24, 0xcb, 0x8b, 0x93, 0x11, 0xbb, 0xcb, 0xfb, 0xed, + 0xf1, 0xf9, 0x83, 0x6b, 0x3f, 0x5c, 0xb5, 0x7a, 0xd5, 0x8a, 0x8f, 0x56, 0x2e, 0x47, 0x8b, 0xc0, + 0x92, 0xc1, 0xc0, 0xf0, 0x28, 0x5a, 0x5b, 0x0d, 0xd1, 0xec, 0xef, 0xec, 0x62, 0x1e, 0xbd, 0xeb, + 0xcd, 0xcc, 0x4c, 0xa8, 0x63, 0x77, 0xb8, 0x7c, 0xc3, 0xff, 0x1b, 0x2c, 0x28, 0x73, 0x1a, 0xad, + 0xbe, 0xbe, 0x3e, 0xf8, 0x08, 0x0e, 0x88, 0x8a, 0x8c, 0x98, 0x10, 0xd9, 0x1d, 0x5f, 0x7d, 0xa8, + 0xb3, 0xb3, 0x03, 0xf6, 0x43, 0x39, 0x8d, 0xde, 0x42, 0xdd, 0xf7, 0xc1, 0xbd, 0xbb, 0xb0, 0x11, + 0x71, 0xfb, 0xd6, 0x87, 0x2b, 0xde, 0x7b, 0xff, 0xbd, 0x77, 0xff, 0xf8, 0xdf, 0x1e, 0x73, 0xd3, + 0xfd, 0xb0, 0x81, 0xa7, 0x86, 0x9a, 0x7d, 0xf2, 0x72, 0x15, 0x2d, 0xb0, 0xe7, 0xa7, 0x4b, 0x76, + 0x57, 0x0f, 0xbd, 0xfc, 0x11, 0xb4, 0xa4, 0x1d, 0xe2, 0xa1, 0x4b, 0xb9, 0x6c, 0xd3, 0x3b, 0x1d, + 0x50, 0xd9, 0xee, 0x50, 0x7a, 0xa1, 0x91, 0xa0, 0x69, 0xf2, 0x9f, 0x3a, 0xe5, 0xe6, 0x64, 0xa3, + 0x32, 0x1e, 0x2a, 0x30, 0x60, 0x77, 0xb4, 0x6c, 0xdf, 0x84, 0x1c, 0xbb, 0xc3, 0x31, 0x58, 0xdd, + 0xa0, 0xa0, 0x60, 0x02, 0x5b, 0x0f, 0xbe, 0x6d, 0xd3, 0x17, 0x9f, 0xa3, 0x65, 0x2d, 0xe1, 0x7e, + 0x0e, 0x4e, 0xbc, 0xea, 0xeb, 0x23, 0x14, 0x0a, 0xb7, 0x7d, 0xfb, 0x8d, 0xad, 0xd5, 0x61, 0x79, + 0xd9, 0x4d, 0xb1, 0xbb, 0x73, 0xfc, 0x91, 0x30, 0x7a, 0x1f, 0x00, 0xd3, 0x61, 0xf7, 0xce, 0x01, + 0x81, 0x8d, 0xff, 0xa4, 0xdd, 0x0f, 0xfb, 0xe6, 0x55, 0x36, 0xb3, 0x0d, 0x91, 0xd4, 0x43, 0xd3, + 0x86, 0x48, 0xaa, 0x00, 0xe6, 0x6e, 0x55, 0x77, 0xdd, 0x3f, 0xec, 0x19, 0x8d, 0x73, 0xbc, 0xd8, + 0xee, 0x4e, 0xb1, 0xee, 0xf7, 0xab, 0xa8, 0x8d, 0x4d, 0x0b, 0xe8, 0x1b, 0x1a, 0xb5, 0x0b, 0x9e, + 0xac, 0xbb, 0x9b, 0x9c, 0x4d, 0xcf, 0xac, 0x64, 0x1b, 0x22, 0xa9, 0xc7, 0x14, 0xbb, 0xc3, 0xfd, + 0xa2, 0x82, 0x71, 0xcd, 0xc0, 0x63, 0x47, 0x6c, 0x99, 0x0a, 0x54, 0x1e, 0x06, 0x86, 0xc7, 0x2c, + 0x6e, 0x16, 0xe2, 0x46, 0x31, 0xf5, 0xca, 0xc8, 0xe0, 0xea, 0xa5, 0x51, 0xf0, 0x07, 0xab, 0xe8, + 0x47, 0xeb, 0x74, 0x5b, 0xd7, 0x3b, 0x29, 0xe9, 0x23, 0x24, 0x4b, 0x77, 0xa8, 0x39, 0x6c, 0xf9, + 0xe7, 0x46, 0x99, 0x5e, 0xff, 0xcb, 0xfb, 0x1c, 0x23, 0x21, 0x2a, 0x40, 0x7b, 0x9f, 0xc0, 0x8e, + 0x50, 0xe5, 0xe5, 0x38, 0xc5, 0x9d, 0x8a, 0xe6, 0x2a, 0x3f, 0x4d, 0x27, 0x31, 0x3c, 0x2a, 0xb4, + 0x0a, 0x28, 0x26, 0x7e, 0x97, 0x93, 0x51, 0xfa, 0xfa, 0x5d, 0x74, 0x16, 0xb2, 0x2b, 0x33, 0xc4, + 0x39, 0x6f, 0x11, 0xe1, 0x6e, 0x52, 0xcb, 0x91, 0x29, 0x85, 0x73, 0x54, 0xa5, 0xa9, 0x4b, 0x3c, + 0xa1, 0xa3, 0x6f, 0xac, 0xcb, 0x9d, 0x0a, 0xa6, 0x83, 0xd2, 0x08, 0x43, 0x23, 0x63, 0x16, 0x7e, + 0x93, 0xbf, 0x5e, 0xac, 0x53, 0xe7, 0xe5, 0xdc, 0xb8, 0x62, 0x6d, 0x3f, 0xb6, 0x9b, 0xc6, 0x90, + 0x5b, 0x77, 0x3f, 0x6a, 0x67, 0x83, 0x7b, 0x3d, 0x23, 0x3d, 0x4d, 0x8b, 0x21, 0x29, 0x42, 0x53, + 0xf7, 0xd0, 0xd5, 0x94, 0x1a, 0x7b, 0x28, 0xd1, 0x1d, 0xa6, 0x74, 0x16, 0xb7, 0xf0, 0x4a, 0xbf, + 0x9d, 0xa3, 0x5b, 0x7d, 0x7d, 0xd5, 0xc3, 0xc8, 0x98, 0xd0, 0x31, 0xbc, 0x5c, 0xa2, 0x2b, 0xfc, + 0x81, 0x93, 0x89, 0x0e, 0xb7, 0x4a, 0xa3, 0x9f, 0x34, 0xf5, 0xf2, 0x47, 0x98, 0x0e, 0x50, 0xbf, + 0xa1, 0xe8, 0x56, 0xd5, 0xdd, 0xcd, 0xd5, 0x70, 0xf6, 0x2c, 0x6e, 0x05, 0x2d, 0x45, 0xa6, 0x5d, + 0x68, 0x29, 0xc7, 0x2d, 0x89, 0xe3, 0x9a, 0x48, 0x8a, 0x27, 0x13, 0x39, 0x27, 0x12, 0x38, 0x53, + 0x2d, 0x8e, 0x68, 0xee, 0xfd, 0x38, 0xe1, 0x69, 0xe3, 0xeb, 0xb6, 0x41, 0x3a, 0x82, 0x64, 0x0a, + 0x1d, 0xfd, 0x02, 0x97, 0x88, 0x72, 0x93, 0xe3, 0xb1, 0xd2, 0xdf, 0x17, 0x5b, 0x34, 0xf8, 0x78, + 0x3c, 0xc7, 0xe5, 0x11, 0xc7, 0x25, 0x81, 0x25, 0x19, 0xda, 0x86, 0x94, 0xe2, 0x33, 0x72, 0x4a, + 0xda, 0x1d, 0xad, 0xa2, 0xad, 0x05, 0xb8, 0xfa, 0x25, 0xca, 0x5c, 0x7b, 0x5b, 0xe9, 0x82, 0xdc, + 0xd8, 0xbf, 0xfc, 0xd8, 0xc3, 0xc3, 0x7f, 0x67, 0x1f, 0x8f, 0xac, 0xc8, 0xa9, 0x96, 0xec, 0x9e, + 0x39, 0xcd, 0x70, 0x2d, 0xad, 0xd6, 0x2e, 0xa4, 0xc4, 0xf4, 0x6c, 0x1a, 0xde, 0x21, 0x9e, 0xa5, + 0xaa, 0x54, 0x64, 0x77, 0xad, 0xc1, 0x2b, 0xb6, 0x1a, 0xfc, 0x4a, 0x92, 0x2e, 0x51, 0x5c, 0xaf, + 0x98, 0xea, 0x9b, 0xe9, 0xb5, 0xcf, 0x6a, 0xba, 0xfb, 0x87, 0x46, 0x99, 0x8a, 0x99, 0x59, 0x54, + 0xb7, 0x0c, 0x3c, 0x2a, 0x69, 0x0b, 0xce, 0xe4, 0x9d, 0x8b, 0xad, 0x3e, 0x75, 0xb7, 0xd2, 0xe5, + 0x0e, 0xd7, 0x2d, 0xba, 0x92, 0xa5, 0x52, 0x7a, 0xc6, 0x56, 0x0b, 0x85, 0x4c, 0xdb, 0x9d, 0x05, + 0x0b, 0xed, 0x83, 0xb5, 0x3b, 0x8b, 0x19, 0x04, 0xd6, 0xee, 0x2c, 0x66, 0x10, 0x58, 0xbb, 0xb3, + 0x98, 0x41, 0x60, 0xed, 0xce, 0x62, 0x06, 0x81, 0xb5, 0x3b, 0x8b, 0x19, 0x04, 0xd6, 0xee, 0x2c, + 0x66, 0x10, 0x58, 0xbb, 0xb3, 0x98, 0x41, 0x60, 0xc6, 0xee, 0xe3, 0x00, 0x81, 0x80, 0x25, 0x4b, + 0x2d, 0x50, 0xc8, 0xe7, 0x33, 0x6d, 0x77, 0x81, 0xa0, 0xc5, 0xcd, 0xf1, 0xc5, 0xa2, 0xb9, 0x2c, + 0x59, 0xd2, 0x4b, 0x63, 0x83, 0xb6, 0xf3, 0xa7, 0x59, 0xbb, 0xb3, 0x9c, 0x19, 0xd4, 0x09, 0xbb, + 0x8f, 0x8c, 0x0c, 0x71, 0xcb, 0x87, 0x2a, 0x59, 0xb2, 0xa4, 0x99, 0xdc, 0xf2, 0xe1, 0xaa, 0xc9, + 0x51, 0x32, 0xff, 0x07, 0xaa, 0x57, 0x6f, 0xac, 0x69, 0xfa, 0xc1, 0x9f, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE tune_diff_pair_skew_legend_xpm[1] = {{ png, sizeof( png ), "tune_diff_pair_skew_legend_xpm" }}; + +//EOF diff --git a/bitmaps_png/cpp_other/tune_single_track_length_legend.cpp b/bitmaps_png/cpp_other/tune_single_track_length_legend.cpp new file mode 100644 index 0000000000..4c6cf7a2e5 --- /dev/null +++ b/bitmaps_png/cpp_other/tune_single_track_length_legend.cpp @@ -0,0 +1,655 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xa2, 0x08, 0x02, 0x00, 0x00, 0x01, 0x90, 0x54, 0x77, + 0x66, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x1e, 0xc2, 0x00, 0x00, 0x1e, + 0xc2, 0x01, 0x6e, 0xd0, 0x75, 0x3e, 0x00, 0x00, 0x00, 0x09, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x89, 0x2a, 0x8d, 0x06, 0x00, 0x00, 0x20, 0x00, 0x49, + 0x44, 0x41, 0x54, 0x78, 0x9c, 0xec, 0x5d, 0x87, 0x5f, 0x14, 0x47, 0xfb, 0xf7, 0xbf, 0xf8, 0x25, + 0xaf, 0x89, 0xd1, 0x18, 0x45, 0x45, 0x40, 0x63, 0x09, 0x56, 0x14, 0x03, 0xaf, 0xbd, 0xc5, 0x82, + 0x05, 0x15, 0x45, 0x2c, 0x09, 0x28, 0x16, 0x90, 0x2e, 0x28, 0x0a, 0x8a, 0xd1, 0x68, 0x6c, 0x49, + 0x34, 0x18, 0xa3, 0xd1, 0xe4, 0x35, 0x89, 0x46, 0xd4, 0x20, 0xbd, 0x88, 0x20, 0x20, 0xed, 0x80, + 0xa3, 0x2b, 0x45, 0x3d, 0x3a, 0x07, 0x47, 0xbb, 0xf2, 0x9b, 0xbb, 0x81, 0x61, 0xbd, 0xdd, 0xdb, + 0x9d, 0xbd, 0x9b, 0xbb, 0x83, 0x83, 0xef, 0x67, 0x3e, 0x7c, 0xf6, 0x86, 0xd9, 0x67, 0x9e, 0xfd, + 0xee, 0xec, 0xd4, 0x67, 0x9e, 0x19, 0xa6, 0xd0, 0x0d, 0x0d, 0x09, 0x71, 0xec, 0x09, 0x86, 0xe1, + 0xcb, 0xfa, 0xe4, 0x3f, 0xff, 0x47, 0xfd, 0x19, 0x39, 0x6e, 0x6c, 0xa1, 0xcf, 0x11, 0x70, 0x51, + 0x1c, 0xe8, 0x07, 0xae, 0x09, 0x64, 0xa0, 0x26, 0x1d, 0x5e, 0x4c, 0x34, 0xfb, 0x0c, 0xe6, 0xad, + 0x29, 0x0f, 0x2d, 0x33, 0xc8, 0x72, 0x76, 0x42, 0xd7, 0x36, 0xb3, 0xac, 0xc1, 0xdf, 0xca, 0xf0, + 0x6b, 0xc4, 0x32, 0x68, 0x2d, 0x2c, 0x40, 0xd7, 0x40, 0xf7, 0xd6, 0x5e, 0x14, 0x1d, 0xf5, 0x25, + 0x93, 0x81, 0x38, 0x2f, 0x87, 0x31, 0x5e, 0xe8, 0xef, 0x83, 0x9b, 0x81, 0xd9, 0xa7, 0x9f, 0x68, + 0x95, 0x81, 0x37, 0x6e, 0x06, 0x00, 0xe6, 0x63, 0x47, 0x8b, 0xc5, 0xe2, 0xe5, 0x8b, 0x17, 0x82, + 0x0b, 0x5a, 0x06, 0xb9, 0x8c, 0xb7, 0x14, 0xfa, 0x7a, 0xf1, 0xc8, 0x80, 0x05, 0x30, 0x03, 0x7b, + 0xdb, 0x79, 0x28, 0x66, 0xc7, 0xb6, 0x2d, 0xe0, 0xaf, 0xd0, 0xf7, 0x08, 0xa1, 0x0c, 0x04, 0xca, + 0x0c, 0xa8, 0x9f, 0xc5, 0x26, 0x87, 0x75, 0xe0, 0xaf, 0xc0, 0xdd, 0x8d, 0x54, 0x06, 0x79, 0x8c, + 0xf1, 0x8c, 0x9f, 0x02, 0xc9, 0x0c, 0xb2, 0x76, 0x6c, 0xad, 0xb9, 0x7b, 0x5b, 0x3d, 0x83, 0xd6, + 0x02, 0x41, 0xd5, 0xcd, 0x1b, 0x3d, 0x21, 0xfc, 0x1a, 0x0c, 0xb5, 0x4f, 0x1e, 0xd1, 0xef, 0x5f, + 0x64, 0x67, 0xdb, 0x93, 0x41, 0x3e, 0x73, 0x06, 0x00, 0x99, 0x9b, 0x37, 0x80, 0xe7, 0x00, 0x1f, + 0x44, 0x49, 0x70, 0x20, 0x0c, 0x1a, 0x9f, 0x20, 0xc9, 0xd6, 0x46, 0xd3, 0xd7, 0xcf, 0x92, 0x01, + 0x1d, 0xec, 0x14, 0xc9, 0xa3, 0x27, 0x59, 0x30, 0x65, 0x20, 0x20, 0x95, 0x01, 0xf3, 0x7b, 0x23, + 0x99, 0x41, 0xea, 0xca, 0x65, 0xf4, 0x48, 0xf0, 0xda, 0xd4, 0x62, 0xe4, 0x72, 0xb9, 0x96, 0x19, + 0xbc, 0xf9, 0xe3, 0x2e, 0x3d, 0x32, 0xc3, 0x71, 0x23, 0xf5, 0xa7, 0xeb, 0xde, 0xdd, 0xcd, 0xcd, + 0xcd, 0xbb, 0x9d, 0xb7, 0x6b, 0x93, 0x81, 0x82, 0x89, 0x25, 0x96, 0xe6, 0x45, 0x9b, 0x0c, 0xa8, + 0x12, 0x9b, 0xd2, 0x9e, 0xab, 0x49, 0x6f, 0x6b, 0x6b, 0x23, 0x90, 0x01, 0x40, 0xf5, 0x6f, 0xbf, + 0x66, 0x38, 0x6e, 0xe8, 0xa8, 0xae, 0xc2, 0x4c, 0xcf, 0x3b, 0x03, 0x46, 0x74, 0xd5, 0xd5, 0x4a, + 0xca, 0x4a, 0xc8, 0x48, 0x17, 0xe4, 0xf5, 0x7d, 0x5c, 0x25, 0x27, 0x82, 0x00, 0x51, 0x5d, 0x0d, + 0xf5, 0xf2, 0xee, 0x6e, 0x70, 0x91, 0xe7, 0xb6, 0x57, 0x57, 0xe9, 0x54, 0xc4, 0xcf, 0xb4, 0x46, + 0xd7, 0xa0, 0x42, 0x15, 0xfa, 0x79, 0xd5, 0xc7, 0xc5, 0x90, 0x91, 0xce, 0x58, 0x78, 0x88, 0xd5, + 0xa0, 0x54, 0x41, 0xa8, 0x95, 0xd7, 0x8b, 0x74, 0xf6, 0x48, 0x66, 0xe9, 0x4d, 0x8d, 0x8d, 0xfa, + 0x92, 0x5e, 0x5d, 0xa5, 0x2c, 0xd7, 0x09, 0x71, 0xca, 0xfe, 0x25, 0xec, 0xa3, 0x91, 0x94, 0xce, + 0x09, 0xbd, 0x4b, 0x6f, 0x97, 0x48, 0xa8, 0x31, 0xe0, 0x53, 0x20, 0xaf, 0x7b, 0x4d, 0x75, 0x35, + 0xea, 0x3d, 0x64, 0xbb, 0x38, 0x13, 0x96, 0xce, 0x19, 0x3f, 0x8c, 0xde, 0x9a, 0x83, 0x20, 0x97, + 0x76, 0x6b, 0x21, 0x1d, 0xfe, 0x2b, 0xd9, 0xce, 0x96, 0xa3, 0x35, 0x6f, 0xce, 0x78, 0xc1, 0x2e, + 0x02, 0x5e, 0xa8, 0x8d, 0x35, 0xe8, 0x60, 0x63, 0x86, 0x93, 0x81, 0xc2, 0x82, 0x7c, 0xed, 0xa5, + 0x03, 0xa4, 0x3b, 0xac, 0xd5, 0xa3, 0x74, 0xf6, 0xa2, 0x6d, 0x20, 0xe9, 0x57, 0x2e, 0x5d, 0x9c, + 0xc6, 0xd4, 0x1d, 0xe2, 0x90, 0x9e, 0xb3, 0xc7, 0xa5, 0xab, 0x56, 0x44, 0x8d, 0xc9, 0xd8, 0xe4, + 0x20, 0x97, 0x4a, 0xd5, 0xa4, 0x6b, 0x02, 0x56, 0xe7, 0x20, 0x7b, 0x97, 0x33, 0xba, 0xce, 0xdb, + 0xef, 0x8a, 0xfe, 0xa5, 0x53, 0x99, 0xa1, 0x42, 0xb0, 0xdf, 0x95, 0x71, 0x9c, 0x46, 0x46, 0x3a, + 0x27, 0xae, 0x5c, 0xbe, 0x84, 0x1b, 0x2e, 0x5d, 0x8c, 0xb3, 0x9e, 0x06, 0x68, 0x50, 0x0b, 0x6d, + 0xc5, 0x42, 0xa3, 0x69, 0x4f, 0x05, 0x78, 0x23, 0x8c, 0x2f, 0xa5, 0x34, 0x34, 0x18, 0x68, 0x99, + 0x68, 0x33, 0x87, 0xf1, 0xae, 0xec, 0xc3, 0xee, 0x4f, 0xcc, 0xc6, 0xe4, 0xec, 0x62, 0xa8, 0x0f, + 0x35, 0x41, 0x2f, 0xda, 0x33, 0xe2, 0xd9, 0x22, 0x7b, 0x96, 0xfa, 0xcb, 0x65, 0x87, 0x13, 0x7c, + 0x60, 0xf8, 0x1e, 0x30, 0x65, 0x1a, 0x4e, 0x7b, 0xa0, 0x53, 0x4b, 0x4e, 0x36, 0x3d, 0x7e, 0xd2, + 0xc4, 0xf1, 0xf4, 0x94, 0x15, 0x17, 0xce, 0xe2, 0xc8, 0x34, 0x90, 0xf6, 0xe2, 0xdc, 0x1c, 0x7c, + 0x46, 0x41, 0x4a, 0xc6, 0x89, 0x10, 0x3a, 0xf8, 0x69, 0xcf, 0x59, 0xc5, 0x68, 0x82, 0x38, 0x8f, + 0xaf, 0xf6, 0x0c, 0xb3, 0x2c, 0x74, 0xf0, 0xd0, 0xde, 0xd9, 0x69, 0x6b, 0x60, 0x80, 0x5f, 0x80, + 0xaf, 0xf7, 0xf7, 0xe7, 0xcf, 0x65, 0x67, 0xbd, 0x84, 0x91, 0x0d, 0xf5, 0xf5, 0x8d, 0x0d, 0x0d, + 0x9c, 0xf7, 0x8a, 0xf3, 0x72, 0xf9, 0x69, 0xef, 0xc7, 0x30, 0x85, 0x43, 0x87, 0xa1, 0x4a, 0xce, + 0xc0, 0xd6, 0x5e, 0xa0, 0xd4, 0xfe, 0xc7, 0xab, 0x97, 0x61, 0x65, 0xfa, 0xe7, 0xbd, 0x3f, 0xe8, + 0xc1, 0x6e, 0xbe, 0x0d, 0xf8, 0xd7, 0x8e, 0x6d, 0x5b, 0x94, 0xda, 0xfb, 0x30, 0xcc, 0x6e, 0xd1, + 0x61, 0x50, 0xed, 0xd5, 0x22, 0x85, 0xaa, 0xc9, 0xd1, 0x92, 0xe2, 0x62, 0xb5, 0x78, 0x90, 0x32, + 0x66, 0xb2, 0x25, 0x8e, 0x58, 0x83, 0x69, 0xcf, 0xdc, 0xe9, 0x2f, 0x60, 0x9e, 0xb2, 0x91, 0x83, + 0xc4, 0x05, 0x1e, 0x07, 0x39, 0xc5, 0x1a, 0xa8, 0xad, 0xd5, 0xa4, 0x3d, 0x0b, 0xa2, 0x2c, 0xcc, + 0x39, 0x3f, 0x80, 0x61, 0xf5, 0x71, 0xd1, 0xf4, 0x2e, 0x07, 0x7b, 0x28, 0xf0, 0xf1, 0xcc, 0xcd, + 0xc9, 0xe1, 0x15, 0x04, 0x8f, 0x23, 0xc0, 0x8d, 0x6a, 0x91, 0x59, 0x59, 0x2f, 0xb9, 0xef, 0xcd, + 0xcc, 0x48, 0x5d, 0xb9, 0x8c, 0x51, 0x8d, 0xf4, 0xf5, 0x6b, 0xb4, 0xe1, 0xbe, 0xab, 0xae, 0x96, + 0x57, 0x7b, 0xae, 0x50, 0x4d, 0x4b, 0xaa, 0xa5, 0x8f, 0x8f, 0x8d, 0xd5, 0xd4, 0x1d, 0xc2, 0x87, + 0xf6, 0x25, 0xa7, 0x2c, 0x2c, 0x04, 0x28, 0xd4, 0xf1, 0xa6, 0x06, 0x27, 0xb1, 0x38, 0x5f, 0x40, + 0x7f, 0x5a, 0xce, 0xce, 0x3b, 0x27, 0x74, 0x2a, 0xf7, 0x51, 0x96, 0x13, 0x31, 0xdf, 0x40, 0x6b, + 0x01, 0x87, 0xf6, 0x23, 0x87, 0x7f, 0xa0, 0x85, 0x02, 0x3a, 0x69, 0x2f, 0x70, 0x77, 0xc3, 0xd4, + 0xfe, 0xcd, 0xdd, 0xdb, 0x2c, 0xda, 0x7f, 0x69, 0x33, 0x07, 0x96, 0x22, 0xd0, 0xf5, 0x37, 0x1f, + 0x3b, 0x7a, 0xfa, 0xe7, 0x56, 0x20, 0xf2, 0xcc, 0xa9, 0x50, 0x4e, 0xb1, 0xba, 0xd6, 0x39, 0x40, + 0xa7, 0x2c, 0xa7, 0x2d, 0x38, 0xc9, 0x9e, 0x2d, 0xb4, 0x53, 0x8b, 0x34, 0x72, 0xc9, 0x81, 0x80, + 0x5f, 0x70, 0x57, 0x7d, 0x1d, 0xe3, 0x7f, 0x0b, 0xbd, 0x3c, 0x94, 0x8b, 0x11, 0xb4, 0x3e, 0xe3, + 0xb9, 0x6f, 0xc3, 0x8c, 0xf9, 0xd5, 0xaa, 0x21, 0x7b, 0xe7, 0x76, 0xc6, 0x7a, 0xed, 0xdd, 0xfd, + 0xbf, 0x34, 0xdd, 0x52, 0x53, 0x5d, 0xad, 0x63, 0xa6, 0x86, 0x1b, 0x9d, 0xd0, 0xd1, 0x92, 0x95, + 0xd9, 0xf4, 0x22, 0x55, 0xeb, 0xdb, 0xc9, 0xa8, 0xae, 0x36, 0xe8, 0x3e, 0x77, 0xf6, 0x5b, 0x96, + 0x21, 0x79, 0xbe, 0xbf, 0x37, 0x43, 0xf3, 0xe7, 0xed, 0x61, 0x1c, 0xd5, 0xd5, 0xc0, 0x52, 0x8e, + 0xa1, 0xa2, 0xd1, 0x96, 0x13, 0xe1, 0x0a, 0x4f, 0x57, 0x57, 0x17, 0x20, 0x3e, 0xd9, 0x7e, 0x01, + 0x8c, 0x97, 0x75, 0x74, 0xe0, 0xe7, 0x42, 0x5e, 0x75, 0xf8, 0x09, 0xde, 0xb9, 0x7d, 0x8b, 0xfe, + 0x2f, 0xa8, 0x5f, 0x5d, 0x4c, 0x14, 0x8a, 0xe9, 0xee, 0xea, 0x82, 0x17, 0xb2, 0x8e, 0x76, 0x87, + 0x4f, 0x3e, 0x22, 0xbf, 0x76, 0xc4, 0x17, 0x97, 0x2f, 0x7e, 0x4f, 0x8f, 0x7c, 0xb1, 0x7e, 0x8d, + 0xb2, 0x3d, 0xae, 0xe9, 0xfb, 0x3a, 0x51, 0x3d, 0x93, 0xfa, 0x3c, 0x05, 0x5c, 0x80, 0x21, 0xb9, + 0xf1, 0x27, 0x11, 0x18, 0x55, 0x8f, 0x54, 0x1a, 0x7e, 0x78, 0x52, 0x63, 0x80, 0xba, 0x52, 0xd5, + 0x1c, 0x1f, 0xf8, 0x5b, 0x57, 0x57, 0x0b, 0x2e, 0x44, 0x11, 0x0f, 0x40, 0x32, 0xd1, 0xe3, 0x08, + 0x9c, 0x5c, 0x0c, 0xa4, 0x7a, 0xd1, 0x51, 0x5f, 0x3a, 0x9d, 0x40, 0x75, 0x99, 0x4c, 0xa6, 0x16, + 0x09, 0x92, 0xc5, 0x4e, 0x9d, 0x8c, 0x93, 0x8b, 0x81, 0x54, 0x8f, 0xb6, 0x62, 0xee, 0xfc, 0xd0, + 0x17, 0x72, 0xd3, 0x56, 0xaf, 0xc0, 0x2c, 0x33, 0x06, 0x52, 0x5d, 0x59, 0xab, 0x30, 0x4d, 0x24, + 0x53, 0xad, 0x62, 0x7a, 0x6c, 0x63, 0x02, 0x7c, 0xfa, 0x9d, 0xea, 0x98, 0xc5, 0x80, 0xb1, 0x68, + 0x31, 0x62, 0x70, 0xa8, 0x8e, 0xdf, 0x61, 0xd2, 0x45, 0x75, 0x76, 0xc3, 0x33, 0x2a, 0xf8, 0xb1, + 0x3e, 0xc5, 0x72, 0x22, 0x4e, 0x32, 0x66, 0xd5, 0xa7, 0xe0, 0xa9, 0x4e, 0x9c, 0xf5, 0xa3, 0x7e, + 0x3e, 0x81, 0x01, 0x7e, 0x0e, 0x6b, 0x56, 0x2b, 0x54, 0x6b, 0xb3, 0x4f, 0xff, 0x7d, 0x92, 0x18, + 0x1f, 0x07, 0x62, 0x4e, 0x1e, 0x0f, 0xa2, 0x27, 0xd6, 0x4d, 0x75, 0xd2, 0xac, 0xa3, 0xd9, 0xe7, + 0xec, 0xac, 0x97, 0xa0, 0xe3, 0x91, 0xf2, 0x2c, 0xd9, 0x62, 0xdc, 0x98, 0x75, 0xab, 0x57, 0x32, + 0x26, 0xee, 0x5f, 0xac, 0xf3, 0x02, 0xa3, 0xea, 0x31, 0x03, 0x43, 0xf5, 0xef, 0x2f, 0xa8, 0xc5, + 0xe0, 0xa8, 0xde, 0xd2, 0xd2, 0xa2, 0x30, 0x7e, 0xe5, 0xa8, 0x41, 0x75, 0x96, 0xf9, 0x54, 0x10, + 0xe0, 0x7f, 0xfb, 0xa9, 0xea, 0x0a, 0x95, 0x61, 0xa5, 0xa6, 0xbb, 0x60, 0xe5, 0x5b, 0x7c, 0x2c, + 0xa0, 0x1f, 0xaa, 0x3e, 0x49, 0x2d, 0x72, 0xdb, 0xe6, 0x8d, 0x0a, 0x1a, 0x1a, 0x53, 0x92, 0x95, + 0xaa, 0x6b, 0x36, 0x52, 0x43, 0x30, 0xa6, 0xea, 0x55, 0x55, 0x95, 0x12, 0x09, 0x83, 0xa9, 0x98, + 0x72, 0x02, 0x95, 0xc9, 0xc2, 0x54, 0x0d, 0xc6, 0x54, 0x5d, 0x13, 0x12, 0xe7, 0xcf, 0xc5, 0x29, + 0x33, 0xfd, 0x51, 0x75, 0x98, 0xfe, 0xe9, 0x84, 0x71, 0xec, 0x69, 0xc8, 0xab, 0x0e, 0x3e, 0x44, + 0xf0, 0xc1, 0xa9, 0x95, 0x04, 0xa5, 0xea, 0x9f, 0xf3, 0x50, 0xbd, 0x2e, 0x2a, 0x12, 0x0e, 0xf6, + 0xa4, 0xaa, 0x1a, 0x93, 0x11, 0x14, 0x6b, 0x1d, 0x6a, 0xa0, 0x58, 0xee, 0xf4, 0x84, 0x9b, 0xe1, + 0xf8, 0x46, 0xb1, 0xf4, 0x8e, 0x1a, 0x5f, 0xd5, 0x01, 0x24, 0xe5, 0x65, 0x7d, 0x53, 0x1d, 0x1e, + 0x07, 0x41, 0xcd, 0x83, 0x8c, 0x80, 0x7a, 0x4c, 0x81, 0xf8, 0xae, 0x17, 0x80, 0xf7, 0x98, 0x97, + 0x98, 0xc0, 0x77, 0xc9, 0x00, 0xaa, 0xce, 0xf7, 0x2e, 0x10, 0x84, 0x41, 0xfe, 0x9a, 0x34, 0xe1, + 0x57, 0x60, 0x8a, 0x7a, 0x05, 0x15, 0x7a, 0x7b, 0x72, 0xa7, 0xa6, 0x80, 0xce, 0xfa, 0x9b, 0x9a, + 0x1a, 0x23, 0x4c, 0x3b, 0xe6, 0xec, 0x71, 0xe1, 0xab, 0x3d, 0x5d, 0x75, 0xd8, 0x76, 0x72, 0xda, + 0xd1, 0xb2, 0x40, 0xcb, 0xcf, 0x14, 0x6a, 0xdf, 0xf9, 0xee, 0x2d, 0x66, 0x7a, 0x2d, 0xca, 0x3a, + 0x27, 0xb4, 0xaf, 0x61, 0x70, 0xea, 0x2f, 0x6a, 0xe2, 0x7e, 0xa4, 0x3a, 0x2c, 0xf7, 0x98, 0x89, + 0x55, 0xaa, 0x5b, 0x69, 0x9d, 0x17, 0x23, 0x74, 0xaa, 0xd7, 0x81, 0x42, 0x2d, 0xb9, 0x0c, 0x56, + 0x31, 0x8c, 0x29, 0xd9, 0x55, 0x7f, 0x91, 0x96, 0xc6, 0x37, 0x77, 0x5d, 0x55, 0xaf, 0xfc, 0xf9, + 0x27, 0xcc, 0x94, 0x2c, 0xaa, 0x83, 0xef, 0x75, 0xf4, 0x88, 0xe1, 0x7c, 0x73, 0xd7, 0x55, 0x75, + 0x30, 0x80, 0xc7, 0x4c, 0x99, 0xdc, 0xbb, 0xa1, 0x84, 0x0e, 0x34, 0x6f, 0xea, 0xb2, 0xc3, 0x29, + 0x38, 0x28, 0x30, 0x23, 0xfd, 0xc5, 0x02, 0x9b, 0xd9, 0xa0, 0x07, 0xcf, 0x2e, 0x53, 0x57, 0xd5, + 0xe3, 0xac, 0xa7, 0x61, 0xa6, 0x2c, 0x0e, 0xf2, 0x67, 0xfc, 0xd7, 0x1f, 0x77, 0xef, 0xc0, 0x0b, + 0xb4, 0x1c, 0x09, 0x54, 0xf7, 0xf3, 0x3e, 0xa2, 0x69, 0xe3, 0x04, 0x82, 0x4e, 0xaa, 0xe7, 0x1f, + 0xdc, 0x8f, 0xf3, 0xa5, 0xe2, 0x0f, 0x37, 0x79, 0x81, 0xc0, 0x2a, 0x64, 0x9c, 0xf5, 0x74, 0xd6, + 0x24, 0x4a, 0x23, 0x0c, 0xd0, 0x8f, 0xd5, 0x31, 0x23, 0x3a, 0x74, 0x55, 0xbd, 0x34, 0xf4, 0x04, + 0xd0, 0x2c, 0xd3, 0x91, 0x61, 0xbc, 0xa3, 0x84, 0x4c, 0xaa, 0x69, 0xb6, 0x1f, 0x96, 0x6f, 0x5d, + 0xd6, 0x50, 0x09, 0x74, 0x7a, 0x61, 0x79, 0x50, 0x2e, 0x35, 0xfe, 0x73, 0x9f, 0x1a, 0x0f, 0x7a, + 0x0a, 0x30, 0x5e, 0xde, 0xcd, 0x60, 0x35, 0x1e, 0x1b, 0x13, 0xdd, 0x2f, 0x96, 0x4e, 0xa1, 0xf5, + 0x1d, 0x3d, 0x3c, 0x5f, 0xb2, 0x88, 0x88, 0x7c, 0x46, 0x90, 0x1c, 0x6a, 0x80, 0x61, 0x01, 0xa8, + 0x2b, 0x33, 0xb7, 0x6e, 0xce, 0x75, 0xdd, 0xf3, 0xea, 0x92, 0xfa, 0x40, 0x89, 0x38, 0x8c, 0xb9, + 0xe4, 0x6b, 0x68, 0xc8, 0x64, 0x95, 0xd7, 0x7f, 0xcc, 0x3f, 0xec, 0x1e, 0x3f, 0xd3, 0x5a, 0xad, + 0x74, 0x44, 0x5b, 0x4e, 0xcc, 0xd8, 0xe4, 0x00, 0x46, 0x33, 0x5a, 0x6c, 0xe1, 0xd2, 0x0e, 0x03, + 0x86, 0x77, 0x50, 0x1b, 0xb1, 0xef, 0x1e, 0xd6, 0x84, 0xda, 0xc7, 0x11, 0xcf, 0x16, 0xda, 0xd1, + 0xbf, 0xc4, 0xb8, 0x2f, 0xa6, 0x25, 0xce, 0x9b, 0x0b, 0xde, 0xc1, 0xd3, 0xf1, 0x66, 0xea, 0x63, + 0x41, 0xf3, 0xf1, 0xe0, 0x1d, 0x10, 0x7e, 0x80, 0xf7, 0x31, 0x30, 0x78, 0xff, 0xdf, 0xef, 0x77, + 0x61, 0x5b, 0xc0, 0xab, 0x4e, 0x2d, 0x7e, 0x7f, 0x6c, 0x0b, 0xaa, 0x91, 0xee, 0xa6, 0x26, 0xb5, + 0x34, 0x5d, 0xbd, 0x4b, 0xe6, 0x10, 0xaf, 0xaf, 0x5c, 0x4c, 0x98, 0x33, 0x0b, 0xdd, 0x92, 0xbe, + 0x61, 0x9d, 0xac, 0x5d, 0xa2, 0xd0, 0x03, 0x06, 0x00, 0xef, 0x8d, 0x0d, 0x0d, 0x39, 0xd9, 0x59, + 0x20, 0x04, 0xf8, 0x7a, 0xc3, 0x0b, 0xee, 0x5b, 0x52, 0x92, 0xc1, 0x38, 0xae, 0xa7, 0x85, 0x59, + 0xb6, 0xb8, 0x53, 0xf4, 0x4e, 0x53, 0xca, 0x6e, 0x4a, 0xa3, 0x29, 0x16, 0x8b, 0x77, 0x39, 0x3b, + 0xa1, 0x17, 0x4c, 0x7d, 0x61, 0x64, 0x9e, 0x84, 0x82, 0x01, 0xc0, 0x3b, 0x02, 0xa3, 0x09, 0x02, + 0x1d, 0xe5, 0x67, 0xc3, 0x10, 0x65, 0x6f, 0xef, 0xfd, 0xae, 0x29, 0x19, 0x9c, 0x97, 0x90, 0xf6, + 0x6e, 0x48, 0x82, 0x10, 0xe4, 0xe5, 0x41, 0xd2, 0x7f, 0x09, 0xff, 0x59, 0x2e, 0x95, 0xa6, 0x2e, + 0x5f, 0x02, 0xe5, 0x68, 0xec, 0xac, 0x69, 0x0b, 0x53, 0xe3, 0xbd, 0x3e, 0xb6, 0xcf, 0x12, 0xb8, + 0xad, 0x88, 0x6d, 0xcf, 0x11, 0xac, 0xb2, 0xa8, 0x96, 0x16, 0xab, 0x96, 0x2d, 0x81, 0x17, 0xd4, + 0x8d, 0x96, 0xd9, 0x2e, 0x3b, 0x7a, 0xa6, 0x11, 0xbd, 0x0e, 0x6b, 0xa9, 0x37, 0x13, 0x4c, 0x8d, + 0xf7, 0xe7, 0xcb, 0x7a, 0x4a, 0x68, 0xe9, 0xa9, 0x13, 0x38, 0x32, 0x21, 0xef, 0x8c, 0xfb, 0xf9, + 0x20, 0xe4, 0x9d, 0x9d, 0x51, 0xe6, 0x3d, 0xf6, 0x46, 0x4d, 0xa9, 0x29, 0xf8, 0xda, 0xb2, 0xc3, + 0xa4, 0x78, 0x07, 0xbd, 0x40, 0x54, 0xd8, 0x71, 0xec, 0xe1, 0x30, 0x1b, 0xea, 0xc2, 0x23, 0x1e, + 0xc4, 0x2b, 0x7a, 0x93, 0xe2, 0x1d, 0x6e, 0xfc, 0x8a, 0xc4, 0xb6, 0x0a, 0x01, 0x60, 0x5c, 0x97, + 0x53, 0x43, 0x91, 0xca, 0x78, 0x24, 0x92, 0xc9, 0xe8, 0x57, 0x6b, 0x98, 0x16, 0xef, 0x79, 0xbc, + 0x79, 0xc7, 0x41, 0x51, 0xef, 0x4c, 0x02, 0xe6, 0x06, 0x3d, 0x1c, 0xe8, 0x8b, 0xf7, 0x33, 0xa7, + 0x42, 0xff, 0xfe, 0xf3, 0x1e, 0x59, 0x99, 0xc6, 0xe7, 0xdd, 0x0f, 0x6b, 0x6b, 0x21, 0x0e, 0xf4, + 0xc2, 0x7b, 0x52, 0x62, 0x82, 0xb7, 0xa7, 0xc7, 0x8a, 0x25, 0x8b, 0x2a, 0x2b, 0x5f, 0x13, 0x14, + 0xcb, 0x83, 0x77, 0x3c, 0x8b, 0x1c, 0x4c, 0x14, 0xf7, 0x95, 0xf7, 0xfe, 0xcd, 0xfb, 0xc8, 0xe1, + 0x1f, 0x48, 0x55, 0x40, 0xad, 0x96, 0x48, 0x24, 0xba, 0xfd, 0xeb, 0x2f, 0xe0, 0xef, 0xdd, 0xdf, + 0x94, 0xbe, 0xa0, 0x8a, 0x8b, 0x8b, 0x7e, 0xfa, 0xe1, 0x0a, 0xb8, 0x80, 0x93, 0xeb, 0x7f, 0xdd, + 0xfb, 0x1f, 0x8e, 0x58, 0xe3, 0xf3, 0x8e, 0xb7, 0x9d, 0x13, 0x07, 0x7a, 0xb1, 0x6c, 0xa7, 0xff, + 0x4c, 0x88, 0x8b, 0x03, 0x23, 0x72, 0x30, 0x20, 0x4c, 0x79, 0x96, 0x0c, 0x7e, 0x6e, 0xdf, 0xb2, + 0xb9, 0xb5, 0xb5, 0xd5, 0xe7, 0x88, 0x72, 0x0f, 0x01, 0xd5, 0x9b, 0x19, 0x3b, 0x30, 0x78, 0xcf, + 0xd5, 0x0b, 0xef, 0xbd, 0xed, 0x6a, 0xbf, 0xe6, 0x5d, 0x7f, 0x30, 0x1a, 0xef, 0x03, 0xa2, 0xbc, + 0xeb, 0x0f, 0x74, 0xfb, 0x21, 0x35, 0x20, 0xde, 0x31, 0x2d, 0x2e, 0x31, 0xd1, 0xc7, 0x3b, 0x93, + 0xdf, 0x42, 0xed, 0x60, 0x5a, 0xbc, 0x0b, 0xd4, 0x79, 0xdf, 0xef, 0xfa, 0xb5, 0x16, 0x9b, 0xea, + 0x36, 0x39, 0xac, 0x03, 0xb7, 0xcc, 0x99, 0xf1, 0x05, 0xfc, 0xd9, 0xd7, 0x9f, 0xc1, 0x30, 0xb6, + 0xc3, 0x84, 0x29, 0xf3, 0x0e, 0x5a, 0x0e, 0x2d, 0x66, 0x8f, 0x15, 0xbd, 0xbc, 0x83, 0x30, 0x76, + 0xd4, 0x88, 0x96, 0x96, 0x16, 0xc4, 0x3b, 0x5f, 0x13, 0x27, 0x16, 0x98, 0x32, 0xef, 0x08, 0x65, + 0xa5, 0xa5, 0xf4, 0xc4, 0x80, 0xd6, 0xbd, 0x2e, 0xce, 0xa3, 0x3e, 0xfa, 0x90, 0xea, 0x6a, 0x0f, + 0xe2, 0x65, 0x66, 0x06, 0xf5, 0x67, 0xc5, 0x85, 0x73, 0x50, 0x6c, 0xda, 0xea, 0x15, 0x5a, 0x29, + 0xce, 0x00, 0x53, 0xe5, 0x9d, 0xdb, 0x2e, 0x0a, 0x3a, 0xea, 0xb1, 0x1c, 0x3f, 0xb6, 0x45, 0xb3, + 0xc1, 0x25, 0x84, 0x5c, 0xda, 0x0d, 0xbd, 0x22, 0x80, 0x50, 0x17, 0x15, 0xc9, 0x43, 0x63, 0xcd, + 0x30, 0x31, 0xde, 0xf3, 0x30, 0x79, 0x6f, 0x97, 0x48, 0xc0, 0x20, 0x23, 0x3a, 0xea, 0xa9, 0x97, + 0xc7, 0xa1, 0xcd, 0x1b, 0xd6, 0x73, 0x66, 0xfd, 0xea, 0xca, 0xc5, 0xc8, 0x5e, 0x73, 0x50, 0x22, + 0x6b, 0xb0, 0x83, 0x94, 0x77, 0x2d, 0x50, 0x76, 0x26, 0x14, 0x4d, 0x76, 0x96, 0x9f, 0x3d, 0xad, + 0xa3, 0xb4, 0x01, 0xc0, 0x3b, 0xa8, 0x9d, 0x51, 0xf3, 0x08, 0xc2, 0xf8, 0xcf, 0x3e, 0xd5, 0x94, + 0xb2, 0x8f, 0x77, 0xd2, 0xf6, 0x97, 0x10, 0x1d, 0x35, 0xd5, 0xb1, 0xd3, 0xa6, 0xa0, 0x82, 0xff, + 0xea, 0xe2, 0x79, 0xad, 0x45, 0x0d, 0x00, 0xde, 0x01, 0x3a, 0xda, 0xdb, 0x21, 0xe9, 0xec, 0x83, + 0x5b, 0x7d, 0xf3, 0x0e, 0x51, 0x1b, 0xf9, 0x04, 0xb4, 0xdb, 0xa8, 0xec, 0xc7, 0x4c, 0xb6, 0x2a, + 0x0a, 0xf4, 0x6b, 0x2d, 0xe4, 0x67, 0x7c, 0xa5, 0x8d, 0x8b, 0x1c, 0xcc, 0x00, 0x4a, 0x44, 0xfa, + 0x86, 0x75, 0xc5, 0xc7, 0x8f, 0x76, 0xbe, 0x7d, 0x43, 0xe4, 0x81, 0x0f, 0xb8, 0x7d, 0xc3, 0x9e, + 0x00, 0xba, 0xb2, 0xd1, 0x37, 0xef, 0x08, 0x25, 0xc1, 0x41, 0xb1, 0x53, 0x3f, 0xd7, 0x82, 0x19, + 0xa5, 0x73, 0x1f, 0xc0, 0x7b, 0xea, 0xca, 0x65, 0x58, 0x61, 0xd5, 0x72, 0xac, 0xb0, 0x62, 0x69, + 0xb2, 0xfd, 0x82, 0x68, 0x95, 0xef, 0x19, 0x6a, 0x88, 0x9e, 0x64, 0x51, 0x1a, 0x8a, 0xb5, 0xf6, + 0xa6, 0x35, 0xf0, 0x79, 0xff, 0xdc, 0xc2, 0xdc, 0xdd, 0x95, 0xe3, 0x2d, 0xf2, 0x02, 0x68, 0x6c, + 0x5f, 0xff, 0x74, 0xb5, 0x38, 0xd0, 0x0f, 0x8c, 0x69, 0xf3, 0x0f, 0xee, 0x17, 0xb8, 0xbb, 0xb1, + 0x84, 0x42, 0x6f, 0x0f, 0x43, 0xd4, 0x33, 0x65, 0x61, 0xa1, 0xb1, 0xd3, 0xa7, 0xa0, 0x17, 0x20, + 0x70, 0x77, 0xe5, 0xbe, 0x47, 0x2b, 0x60, 0xf2, 0x0e, 0x7a, 0x32, 0xb0, 0xd6, 0xb2, 0x9b, 0x47, + 0xde, 0xd8, 0x14, 0x13, 0x86, 0xac, 0xdf, 0xe5, 0xd0, 0x45, 0x0e, 0x0c, 0xa5, 0xa1, 0xc1, 0xc4, + 0x33, 0x80, 0x4e, 0xa7, 0x70, 0xca, 0xbb, 0xee, 0xfe, 0x84, 0x74, 0x84, 0xa1, 0xdb, 0xd5, 0xae, + 0xba, 0x5a, 0x54, 0x27, 0x12, 0x9c, 0xde, 0x83, 0x18, 0xe2, 0x9d, 0x1d, 0xf2, 0xa4, 0xf9, 0x36, + 0x90, 0x20, 0xd0, 0xea, 0x12, 0x94, 0x4b, 0xe1, 0x9d, 0x63, 0x33, 0xcc, 0xe0, 0xe4, 0x5d, 0xd1, + 0x94, 0x9e, 0x86, 0xfa, 0x3c, 0xf2, 0xae, 0x4e, 0x52, 0x62, 0x87, 0x78, 0xe7, 0x46, 0xb2, 0xfd, + 0x97, 0x90, 0x23, 0x16, 0xcf, 0x62, 0x7c, 0x01, 0xdd, 0xf2, 0x69, 0xcd, 0x7b, 0xc8, 0x89, 0xe3, + 0x19, 0xe9, 0x2f, 0xc6, 0x8c, 0xfc, 0x98, 0x94, 0x3e, 0x2c, 0x30, 0x1a, 0xef, 0x19, 0x8e, 0x1b, + 0x7b, 0xc7, 0xdc, 0x61, 0xa4, 0x64, 0xa2, 0xcf, 0x28, 0x6e, 0x06, 0xfb, 0xce, 0x19, 0x06, 0xde, + 0x6b, 0x6b, 0x45, 0xb0, 0x7b, 0xf3, 0xe4, 0xd1, 0x23, 0x67, 0xa7, 0xad, 0xf4, 0x5b, 0xde, 0xbd, + 0xc5, 0xdd, 0x6b, 0x89, 0x03, 0xa3, 0xf1, 0x8e, 0x16, 0x71, 0x04, 0x07, 0x18, 0x8e, 0x6e, 0xd2, + 0x52, 0x66, 0xaf, 0xe1, 0x75, 0xce, 0x5e, 0x17, 0xf6, 0x94, 0x74, 0xde, 0xa9, 0x73, 0xf4, 0x4b, + 0x17, 0xda, 0x47, 0x3c, 0xfc, 0xe7, 0xca, 0xa5, 0x8b, 0xe0, 0x1a, 0xee, 0x0d, 0x9b, 0x36, 0xc9, + 0x02, 0x7c, 0x0a, 0xe0, 0xc2, 0xcb, 0xe3, 0x30, 0x8a, 0xd4, 0x05, 0x46, 0xe3, 0x5d, 0xda, 0xd6, + 0x86, 0xec, 0x0e, 0x19, 0x8f, 0x16, 0xe2, 0x0b, 0xc9, 0xab, 0x0a, 0xd4, 0x49, 0x6d, 0x7c, 0x96, + 0xc4, 0x9e, 0x78, 0xf0, 0xd6, 0xef, 0x00, 0x75, 0x4f, 0xff, 0x45, 0x4c, 0x55, 0x5e, 0xfb, 0x41, + 0x17, 0x51, 0x60, 0xc4, 0x84, 0x0c, 0xde, 0x4b, 0x43, 0x8e, 0x73, 0xa6, 0x1f, 0xd4, 0xbc, 0x2b, + 0x54, 0x2d, 0xe1, 0xd3, 0xde, 0x52, 0x9f, 0xb9, 0xd9, 0x41, 0xbb, 0xbe, 0x0d, 0xda, 0x79, 0x07, + 0x42, 0xd5, 0xf5, 0x1f, 0xb1, 0x6e, 0x19, 0xe4, 0xbc, 0x43, 0xa0, 0xba, 0x1e, 0x84, 0xf8, 0x99, + 0xd6, 0xd5, 0xbf, 0xde, 0xc0, 0xb9, 0xab, 0x31, 0x25, 0x39, 0x6d, 0xed, 0x6a, 0x74, 0x63, 0x9e, + 0xdb, 0xd7, 0x98, 0xd9, 0xa1, 0x29, 0xe5, 0x71, 0x9f, 0x8d, 0xd2, 0x41, 0x6b, 0x9d, 0xd0, 0x2f, + 0x78, 0x87, 0x28, 0x0d, 0x09, 0x46, 0x75, 0x05, 0x0c, 0x09, 0xb3, 0x67, 0x40, 0xa7, 0x21, 0xe5, + 0xe7, 0xce, 0x94, 0x7f, 0x77, 0xb6, 0xe4, 0xf8, 0x51, 0xa1, 0xef, 0x91, 0x64, 0xbb, 0x05, 0x6a, + 0x33, 0x6e, 0xc5, 0x47, 0x79, 0x9b, 0x47, 0x8f, 0x1c, 0xfe, 0xc1, 0x02, 0x9b, 0xd9, 0xfa, 0x78, + 0x0a, 0x4c, 0xf4, 0x23, 0xde, 0x11, 0x44, 0x0f, 0xef, 0xe7, 0xed, 0xfb, 0x46, 0xed, 0x1d, 0xa8, + 0x85, 0x4c, 0xc7, 0x8d, 0x65, 0xdf, 0x7f, 0xd7, 0xa2, 0x4f, 0xe8, 0xf5, 0x19, 0xfb, 0x23, 0xef, + 0x7a, 0x87, 0x4c, 0xd6, 0x2a, 0x2c, 0x68, 0x4c, 0x4d, 0x69, 0x48, 0x4a, 0x68, 0xc9, 0xc9, 0xea, + 0x6e, 0x69, 0x36, 0xbc, 0x0a, 0x83, 0x88, 0xf7, 0xd6, 0xc2, 0xfc, 0xa2, 0x40, 0xbf, 0xb4, 0xd5, + 0x2b, 0x51, 0x4b, 0x8e, 0x42, 0xe2, 0xbc, 0x39, 0x42, 0x1f, 0xcf, 0xb7, 0x7f, 0x13, 0x36, 0x1c, + 0x67, 0x41, 0x3f, 0xe2, 0x3d, 0x23, 0x23, 0x83, 0xc5, 0x3b, 0xf6, 0xe9, 0x53, 0xa1, 0x3c, 0x4e, + 0xb7, 0x7a, 0x3f, 0x3c, 0x3a, 0x1d, 0x9a, 0xb9, 0xcb, 0x19, 0x67, 0x25, 0x28, 0xf9, 0xcb, 0xf9, + 0x65, 0x67, 0xb8, 0x5d, 0xe2, 0xeb, 0x8e, 0x7e, 0xc4, 0x3b, 0x3b, 0x46, 0x7d, 0xf4, 0xa1, 0x36, + 0x8e, 0xe0, 0xe5, 0xf2, 0x22, 0x4a, 0x67, 0x09, 0x85, 0xa8, 0x89, 0x13, 0x40, 0xa3, 0x9d, 0x68, + 0x33, 0x87, 0x71, 0xa1, 0x2e, 0x7d, 0xe3, 0x7a, 0x4d, 0x67, 0x47, 0x93, 0xc2, 0xc0, 0xe0, 0x3d, + 0x29, 0x21, 0x1e, 0x74, 0xfb, 0xf8, 0x1e, 0x36, 0xd1, 0x59, 0x2b, 0x4a, 0x77, 0x58, 0x4b, 0x25, + 0x34, 0x6d, 0xd5, 0xf2, 0x92, 0x13, 0x41, 0xca, 0xc3, 0xa6, 0x55, 0x1e, 0x11, 0x7f, 0xb8, 0xaa, + 0xb4, 0xc1, 0xef, 0x7c, 0xfb, 0x46, 0xe9, 0xb7, 0xe0, 0xc0, 0x7e, 0xea, 0x86, 0xf9, 0x28, 0x4b, + 0xf3, 0x57, 0x97, 0xf5, 0xe8, 0x1d, 0x62, 0x60, 0xf0, 0x8e, 0x7a, 0xdc, 0xb1, 0x31, 0xd1, 0x98, + 0xb7, 0x74, 0x54, 0x57, 0x25, 0xd9, 0xce, 0x43, 0x3c, 0x3e, 0x5f, 0xb6, 0xf8, 0x2d, 0xcd, 0x81, + 0x10, 0xe4, 0x1d, 0xa1, 0xfd, 0xf5, 0xab, 0x42, 0x1f, 0x4f, 0xea, 0x7b, 0x2a, 0x0b, 0x0b, 0x21, + 0xf3, 0x00, 0x34, 0x0c, 0x00, 0xde, 0x2f, 0x9e, 0xff, 0x8e, 0x6a, 0x3f, 0x83, 0x73, 0x8b, 0xac, + 0xbd, 0x1d, 0x6d, 0xb5, 0x8e, 0x54, 0x9d, 0x04, 0x0e, 0x6a, 0x1c, 0x7a, 0xb2, 0xab, 0x57, 0x2e, + 0xd3, 0x23, 0xab, 0xc2, 0xaf, 0x9f, 0x31, 0x1f, 0x3f, 0x65, 0xf8, 0x87, 0x3d, 0x03, 0x60, 0x3c, + 0x1f, 0x58, 0x7c, 0x31, 0x00, 0x78, 0x87, 0x3e, 0x09, 0xf6, 0x7d, 0xb3, 0x17, 0x5e, 0x88, 0xc5, + 0x62, 0xce, 0x5b, 0x0a, 0x3c, 0x0f, 0x23, 0xd2, 0x4b, 0x82, 0x19, 0xbc, 0x43, 0x43, 0xa8, 0xf1, + 0xfe, 0xf7, 0x9f, 0xf7, 0xcc, 0x46, 0x8f, 0x84, 0x6f, 0xd7, 0xbb, 0xf7, 0x6c, 0x82, 0xd8, 0x29, + 0x93, 0xdb, 0xab, 0x2a, 0x09, 0x3c, 0xc6, 0xfb, 0x18, 0x00, 0xbc, 0x43, 0x1c, 0x3b, 0x1a, 0x80, + 0x99, 0x12, 0x54, 0xd6, 0x7d, 0x43, 0x59, 0x0d, 0x1e, 0xc1, 0x44, 0x22, 0xd1, 0x9e, 0x9d, 0x3b, + 0xd4, 0x4e, 0xba, 0x98, 0x68, 0xf6, 0x19, 0xfa, 0xaa, 0x9a, 0xd2, 0x52, 0xa3, 0x7a, 0x4d, 0x51, + 0xf2, 0xf6, 0x93, 0xb4, 0xf8, 0x80, 0x30, 0x41, 0xde, 0x93, 0xed, 0x6c, 0x7b, 0x66, 0xe1, 0x77, + 0x6b, 0x9c, 0x85, 0x07, 0xbc, 0x8f, 0x19, 0xf9, 0x31, 0xe8, 0x62, 0xaa, 0xc5, 0x8f, 0x1d, 0x35, + 0x02, 0xd5, 0x66, 0xe5, 0xe7, 0x28, 0xfe, 0x25, 0x48, 0x77, 0xed, 0x4d, 0x8d, 0xf7, 0x8a, 0x4b, + 0x17, 0x7a, 0x56, 0x6e, 0xc7, 0x9b, 0x89, 0x99, 0xcf, 0xca, 0x53, 0x02, 0xf0, 0xee, 0xb8, 0x71, + 0xbd, 0x1a, 0xef, 0xbb, 0x9c, 0x9d, 0xa4, 0x52, 0xe9, 0x62, 0xfb, 0x05, 0x53, 0xad, 0x7a, 0xdc, + 0x15, 0x64, 0xef, 0xda, 0x09, 0xa5, 0xbd, 0xdc, 0xe6, 0xa8, 0x83, 0xee, 0x0c, 0x30, 0x35, 0xde, + 0xb3, 0xb6, 0x6f, 0x81, 0x4c, 0xb1, 0xef, 0xcd, 0x00, 0xbc, 0x3b, 0x39, 0x6e, 0xba, 0xac, 0x5a, + 0x51, 0x82, 0x08, 0xf4, 0xf7, 0x45, 0xee, 0x5a, 0xb3, 0x5e, 0x66, 0xc2, 0x0b, 0xea, 0x0a, 0x41, + 0x5b, 0x49, 0x91, 0x96, 0xaa, 0x33, 0xc1, 0xa4, 0x78, 0xef, 0x6e, 0x6e, 0x42, 0x34, 0x35, 0x24, + 0xc4, 0xb1, 0xa4, 0x54, 0xe3, 0xfd, 0xda, 0x8f, 0x57, 0x6b, 0xdf, 0x3f, 0x2b, 0x1b, 0x21, 0x65, + 0xe9, 0xa2, 0x48, 0x3d, 0x98, 0x9c, 0x98, 0x14, 0xef, 0x35, 0x77, 0x6e, 0xf5, 0x4c, 0xe2, 0xcf, + 0x9a, 0x81, 0x23, 0xf3, 0x92, 0x6a, 0x63, 0xe6, 0xa3, 0x88, 0x87, 0xf4, 0x83, 0x47, 0x11, 0xd0, + 0xe6, 0xd5, 0x6c, 0x97, 0x1d, 0xd8, 0xca, 0x72, 0xc3, 0xa4, 0x78, 0x47, 0x1c, 0xe5, 0xed, 0xc3, + 0x5a, 0x03, 0x09, 0x3e, 0x16, 0xf4, 0x22, 0x2d, 0x2d, 0x2d, 0xf5, 0x39, 0x4b, 0x1a, 0x78, 0x5c, + 0x14, 0x9c, 0x3b, 0xc3, 0x54, 0x15, 0x07, 0x26, 0xc5, 0xbb, 0xd0, 0xf7, 0x48, 0xef, 0x4a, 0x08, + 0xf7, 0xd9, 0xf4, 0x33, 0xa7, 0x4f, 0x81, 0x67, 0x88, 0xb1, 0x27, 0x93, 0x54, 0x94, 0xa1, 0x86, + 0x9a, 0x71, 0xf0, 0xa5, 0x1d, 0x4c, 0x8a, 0x77, 0xc1, 0x81, 0x7d, 0xec, 0xdd, 0x76, 0x2a, 0x66, + 0x4d, 0x9f, 0x0a, 0x78, 0xb7, 0x9e, 0xfa, 0x79, 0x2b, 0x2b, 0x9a, 0xab, 0xab, 0x51, 0x9b, 0xd1, + 0xdd, 0x4c, 0x6c, 0xa6, 0xde, 0xa4, 0x78, 0xcf, 0x73, 0xdb, 0xdb, 0x33, 0x46, 0x3d, 0xa1, 0x71, + 0x8c, 0x4a, 0x05, 0x8e, 0xbb, 0x10, 0x69, 0x5b, 0x1b, 0xe2, 0x5d, 0xd3, 0x51, 0xa0, 0x5a, 0x60, + 0x50, 0xf3, 0x8e, 0x03, 0x99, 0x64, 0x88, 0x77, 0x2e, 0xe4, 0xb9, 0x7d, 0xad, 0x07, 0xde, 0x25, + 0x7d, 0xbc, 0xab, 0x0e, 0x66, 0x24, 0x82, 0x21, 0xde, 0x39, 0x20, 0x6b, 0x1f, 0x50, 0xbc, 0xef, + 0x77, 0xc5, 0xb5, 0x66, 0xc1, 0x84, 0xf1, 0x78, 0x6f, 0xef, 0xe3, 0x5d, 0xc3, 0xd8, 0x4a, 0x0b, + 0xe8, 0x85, 0xf7, 0x73, 0xdf, 0x86, 0x1d, 0xd8, 0xe7, 0x1a, 0x1d, 0xf5, 0x94, 0xa0, 0x4c, 0xa3, + 0xf1, 0xde, 0xd1, 0xc7, 0x7b, 0x67, 0x3f, 0xe7, 0x1d, 0xce, 0xe7, 0x8d, 0xfa, 0xe8, 0x43, 0x82, + 0x32, 0xf9, 0xf1, 0xae, 0x79, 0xce, 0x9d, 0x2f, 0x64, 0x1d, 0x1d, 0x7d, 0xbc, 0x6b, 0xf6, 0x33, + 0xcc, 0x17, 0xe4, 0x79, 0x6f, 0x6e, 0x6a, 0x9a, 0x3b, 0x53, 0xe9, 0xb9, 0x65, 0xcc, 0xc8, 0x8f, + 0xd5, 0xbc, 0x4d, 0xeb, 0x02, 0xa3, 0xf1, 0xde, 0x39, 0x40, 0x78, 0x5f, 0xba, 0xd0, 0xee, 0xf5, + 0xab, 0x0a, 0xa9, 0x54, 0x1a, 0xf5, 0x34, 0x12, 0x9e, 0xa8, 0x19, 0x13, 0x1d, 0xf5, 0xe7, 0xbd, + 0x3f, 0xea, 0xeb, 0xeb, 0x6e, 0xde, 0x08, 0xef, 0xec, 0x54, 0x5a, 0x9e, 0xfe, 0xfa, 0xcb, 0x8d, + 0xf4, 0x17, 0x2f, 0x5a, 0x5a, 0x5a, 0x9a, 0x9b, 0x9b, 0x3b, 0xda, 0xdb, 0x9b, 0x1a, 0x1b, 0x39, + 0xc5, 0x1a, 0x8b, 0x77, 0x79, 0x67, 0x67, 0x1f, 0xef, 0xd8, 0xc7, 0x3c, 0x71, 0x42, 0x2f, 0x7e, + 0xdd, 0x02, 0x03, 0xfc, 0x60, 0x80, 0x15, 0xce, 0x62, 0xfb, 0x05, 0x8a, 0xde, 0xca, 0x47, 0x69, + 0xd2, 0xff, 0xcf, 0x03, 0x70, 0xe1, 0xeb, 0xa5, 0x9c, 0xa7, 0x9d, 0x3e, 0xd9, 0xd2, 0x1a, 0xcf, + 0xd7, 0x23, 0x16, 0xef, 0xfb, 0xf4, 0xc0, 0x7b, 0xd7, 0x40, 0xe0, 0xfd, 0xe1, 0x83, 0xfb, 0x7b, + 0x5d, 0x9c, 0xd1, 0xcf, 0x35, 0xab, 0x96, 0xdf, 0xb9, 0x7d, 0x8b, 0xca, 0xbb, 0xbd, 0xed, 0x3c, + 0x90, 0x20, 0x21, 0x4e, 0x79, 0x2e, 0xae, 0x42, 0xe5, 0x58, 0x6f, 0x93, 0xc3, 0x3a, 0x1c, 0xc9, + 0xc6, 0xe3, 0xbd, 0x6b, 0x00, 0xf0, 0x3e, 0x72, 0xf8, 0x07, 0x54, 0x4f, 0xf6, 0x12, 0x49, 0x1b, + 0xa0, 0x5b, 0x8d, 0xf7, 0x1b, 0x3f, 0x5f, 0xff, 0x6a, 0xc5, 0xd2, 0xa3, 0x7e, 0x3e, 0xa5, 0x25, + 0x25, 0xb9, 0x39, 0x39, 0x05, 0xf9, 0x02, 0x61, 0x61, 0x01, 0xa7, 0x64, 0xa3, 0xf1, 0xde, 0x4d, + 0xe1, 0x9d, 0x90, 0xa3, 0x05, 0x85, 0xa9, 0x8d, 0x9b, 0xf4, 0xc2, 0x7b, 0xf7, 0x10, 0xef, 0x1c, + 0xd0, 0x0b, 0xef, 0xd2, 0x3e, 0xde, 0x3b, 0xde, 0xd4, 0x90, 0x12, 0x3b, 0xc4, 0x3b, 0x07, 0xe4, + 0x52, 0xe9, 0x10, 0xef, 0x1c, 0x60, 0xe4, 0x1d, 0xf4, 0x62, 0xf3, 0x05, 0x1a, 0x0d, 0x0b, 0x18, + 0x71, 0x2a, 0x84, 0xe2, 0xaf, 0x45, 0x46, 0xe1, 0xbd, 0x86, 0xbf, 0x61, 0xac, 0x06, 0x98, 0x18, + 0xef, 0xdf, 0x50, 0x79, 0xaf, 0xaa, 0xaa, 0x84, 0x2e, 0x4f, 0xf8, 0xf2, 0x0e, 0x4d, 0x68, 0x40, + 0xdf, 0x4c, 0xf9, 0x43, 0x26, 0x1b, 0xe2, 0x9d, 0x03, 0x14, 0xde, 0x03, 0x2d, 0x27, 0x98, 0x21, + 0xe3, 0xaf, 0xef, 0xcf, 0x9f, 0xd3, 0x74, 0x58, 0x3d, 0xcb, 0x09, 0xf6, 0x30, 0x14, 0x0b, 0x0b, + 0xfb, 0x78, 0x27, 0x77, 0x9a, 0x99, 0xa9, 0xf2, 0x1e, 0x04, 0xaa, 0x17, 0xc4, 0x9d, 0x76, 0xe5, + 0x1d, 0x84, 0x90, 0x13, 0xc7, 0x15, 0x72, 0x39, 0xe2, 0x9d, 0xa0, 0xa1, 0xa4, 0xc9, 0xf2, 0x0e, + 0x63, 0x40, 0x49, 0xd7, 0x8e, 0x77, 0xaa, 0x53, 0xc9, 0x98, 0xc9, 0x96, 0x50, 0x6c, 0x2b, 0xb9, + 0x5d, 0xaf, 0xa6, 0xca, 0x7b, 0x20, 0x35, 0xbe, 0x9e, 0xb6, 0x3e, 0xe7, 0xe7, 0x7d, 0x64, 0x81, + 0xcd, 0xec, 0x0d, 0x6b, 0xbf, 0xb2, 0x32, 0x67, 0x38, 0x9e, 0x5a, 0x2e, 0x7f, 0xcf, 0x6e, 0x20, + 0x71, 0xde, 0x5c, 0x28, 0x56, 0xf4, 0xf0, 0x01, 0x6f, 0xbd, 0x35, 0x60, 0x50, 0xf0, 0x4e, 0x07, + 0x28, 0xd1, 0x70, 0x5c, 0xfd, 0xf4, 0xdf, 0x27, 0x9c, 0x62, 0xb3, 0x9c, 0xb6, 0xe0, 0x9b, 0x29, + 0x60, 0x62, 0x90, 0xf2, 0x5e, 0x5b, 0xab, 0xb4, 0x07, 0x06, 0xec, 0x7b, 0x1e, 0x3a, 0xc0, 0x29, + 0x16, 0x99, 0x43, 0x65, 0x6e, 0xdd, 0x8c, 0xa9, 0x2d, 0x27, 0x06, 0x29, 0xef, 0x11, 0x0f, 0xff, + 0x81, 0x17, 0x8b, 0xed, 0x17, 0x54, 0x94, 0x97, 0xb3, 0x27, 0x6e, 0x48, 0x4e, 0x44, 0x4d, 0xab, + 0xa4, 0xa2, 0x0c, 0x4b, 0x5d, 0x2e, 0x0c, 0x18, 0xde, 0x8f, 0x07, 0x72, 0x9b, 0x85, 0xe2, 0xf3, + 0x3e, 0x7f, 0xce, 0x4c, 0x7f, 0x1f, 0xaf, 0xa8, 0xa7, 0x91, 0x98, 0x1b, 0x77, 0x9e, 0xfd, 0xb7, + 0xc7, 0x3b, 0x54, 0x51, 0x00, 0x99, 0x23, 0x9c, 0x06, 0x29, 0xef, 0x0a, 0xd5, 0xba, 0x18, 0x67, + 0x49, 0x47, 0x28, 0x3b, 0x75, 0x02, 0x15, 0xf9, 0xe6, 0xf4, 0x34, 0xcc, 0xbb, 0x58, 0x30, 0x78, + 0x79, 0xe7, 0x07, 0xb9, 0x0c, 0x39, 0x44, 0x4b, 0x5d, 0xbe, 0x44, 0xde, 0xad, 0xeb, 0xfa, 0xa5, + 0x69, 0xf1, 0xbe, 0xbf, 0x97, 0x77, 0xa2, 0xb6, 0xea, 0x10, 0x35, 0x77, 0x6e, 0xa3, 0x22, 0x2f, + 0x70, 0x77, 0x53, 0xc8, 0x75, 0xb2, 0x51, 0x1d, 0x00, 0xbc, 0x8f, 0x1e, 0x31, 0x9c, 0x3a, 0x70, + 0x17, 0x89, 0x34, 0x1a, 0x53, 0xe8, 0x95, 0x77, 0x05, 0xa5, 0x63, 0x03, 0x42, 0xee, 0xd7, 0xbb, + 0xa4, 0x62, 0xed, 0x7d, 0x76, 0x0c, 0x00, 0xde, 0xc1, 0xa8, 0x07, 0x91, 0x3e, 0x7d, 0xb2, 0x25, + 0x4b, 0x4a, 0x7d, 0xf3, 0xae, 0xa0, 0x9c, 0x09, 0x0a, 0x42, 0xc2, 0xdc, 0xd9, 0xd5, 0xbf, 0x84, + 0x6b, 0x27, 0x67, 0x00, 0xf0, 0x0e, 0x60, 0x3d, 0x75, 0x32, 0xe4, 0x9d, 0x7a, 0x3c, 0x30, 0x1d, + 0x02, 0xfd, 0xf3, 0x0e, 0x20, 0xf4, 0x39, 0x82, 0xa8, 0x87, 0x9d, 0xfa, 0xca, 0xf0, 0x6b, 0x0a, + 0x99, 0x94, 0xfb, 0x4e, 0x0a, 0x86, 0x55, 0xdd, 0xbc, 0x81, 0x15, 0x6e, 0x5c, 0xaf, 0x0a, 0xbf, + 0x86, 0x13, 0xde, 0xfe, 0xef, 0xf7, 0x86, 0xc4, 0x78, 0x82, 0xa6, 0x26, 0x0a, 0xd5, 0xc0, 0x1d, + 0x90, 0xbe, 0x69, 0xfd, 0x5a, 0xf6, 0x64, 0x86, 0xe1, 0x5d, 0xa1, 0xf2, 0xfc, 0x4e, 0x75, 0x66, + 0x00, 0x42, 0xb4, 0xd5, 0xc4, 0x97, 0xdb, 0x1c, 0x41, 0x2f, 0x13, 0xd4, 0x45, 0xa0, 0x55, 0x67, + 0x0f, 0x8d, 0x29, 0xc9, 0xc3, 0x5e, 0xac, 0xfb, 0x8a, 0x7a, 0x3f, 0xc1, 0x90, 0x68, 0x33, 0xbb, + 0xd0, 0xdb, 0x53, 0xf4, 0xf0, 0x3e, 0x91, 0x47, 0xdd, 0xba, 0x69, 0x03, 0x67, 0x1a, 0x83, 0xf1, + 0xae, 0x50, 0x9d, 0x9d, 0x93, 0xb3, 0x1b, 0xcb, 0xb7, 0x0a, 0x3d, 0xd4, 0xc7, 0xc5, 0xe8, 0x91, + 0x77, 0x14, 0x52, 0x96, 0x2c, 0x2c, 0x0c, 0x3b, 0x95, 0x9b, 0x93, 0xa3, 0xef, 0x90, 0xd3, 0x6b, + 0xff, 0x2e, 0x0c, 0xf2, 0x67, 0x4f, 0x79, 0xe6, 0x74, 0x28, 0x91, 0x1c, 0x0b, 0x7e, 0xff, 0x2d, + 0xdf, 0xeb, 0x70, 0xcc, 0x64, 0x2b, 0xde, 0xbc, 0xe7, 0xec, 0xd9, 0x49, 0xf2, 0xb0, 0x83, 0x55, + 0xcb, 0x01, 0xcb, 0x09, 0xb3, 0x67, 0xd0, 0x33, 0x03, 0xa5, 0x83, 0xe0, 0x7a, 0x3c, 0x23, 0x30, + 0xcb, 0xbb, 0x58, 0x2c, 0xe6, 0x7b, 0x82, 0x19, 0x07, 0xe4, 0x72, 0x50, 0xb5, 0x96, 0x84, 0x04, + 0x17, 0xf9, 0xfb, 0x14, 0x1e, 0x39, 0xc4, 0x7e, 0xd8, 0x01, 0x08, 0x4a, 0xde, 0x49, 0x66, 0x4f, + 0x41, 0x57, 0x43, 0x7d, 0x65, 0xf8, 0x75, 0x34, 0x90, 0x81, 0x21, 0x76, 0xda, 0x14, 0x51, 0x04, + 0xb1, 0xa9, 0x54, 0x3a, 0x30, 0x79, 0x87, 0xce, 0x1f, 0x36, 0xac, 0xfd, 0x4a, 0x7f, 0x9a, 0x70, + 0x42, 0xef, 0xfd, 0x99, 0x56, 0x61, 0x81, 0xe0, 0x80, 0x1b, 0xa2, 0x3e, 0xca, 0x7c, 0x7c, 0xcd, + 0x6f, 0xb7, 0xb8, 0x6f, 0xd3, 0x0a, 0x38, 0xbc, 0xe7, 0x0b, 0x04, 0xa8, 0x57, 0xca, 0xde, 0x3b, + 0xd2, 0x2b, 0x0c, 0xd4, 0x8f, 0x2c, 0xff, 0xf6, 0x34, 0xf2, 0xa6, 0x16, 0x6d, 0x39, 0xb1, 0x29, + 0x35, 0x45, 0x1f, 0xb9, 0x08, 0xf6, 0xbb, 0x72, 0xf2, 0x4e, 0x1d, 0x82, 0x4d, 0x9b, 0x64, 0xa1, + 0x0f, 0x35, 0x70, 0x60, 0xb8, 0xfe, 0xfb, 0xbb, 0x07, 0x7f, 0x21, 0xcf, 0x22, 0x29, 0x0b, 0xed, + 0xe4, 0x9d, 0xc4, 0xdc, 0xed, 0x23, 0xe0, 0xf0, 0x0e, 0x61, 0x3b, 0x77, 0x16, 0xf1, 0xdc, 0x79, + 0xc1, 0xa0, 0xe3, 0xa6, 0xea, 0x5b, 0xbf, 0xa0, 0x0a, 0x07, 0x67, 0x6b, 0x2f, 0x5f, 0x0c, 0xf1, + 0xae, 0x11, 0xc8, 0x45, 0x3b, 0x18, 0x77, 0x90, 0x5a, 0x43, 0x40, 0x18, 0xe2, 0x5d, 0x33, 0x64, + 0xb2, 0xa4, 0x2f, 0xe7, 0x43, 0x76, 0x48, 0xad, 0x21, 0x20, 0x0c, 0xf1, 0xce, 0x86, 0xb2, 0xb0, + 0x10, 0xd4, 0xad, 0x54, 0x10, 0xed, 0x51, 0x0c, 0xf1, 0xce, 0x06, 0x59, 0x47, 0x7b, 0xb4, 0x55, + 0x4f, 0x03, 0x2b, 0x7a, 0x1c, 0x41, 0x50, 0xf2, 0x10, 0xef, 0x1c, 0xc8, 0xd9, 0xe3, 0xa2, 0x8f, + 0xd6, 0x55, 0xe0, 0x3e, 0xc4, 0x3b, 0x2b, 0xf8, 0x3a, 0x8a, 0xc1, 0x84, 0x8e, 0xbc, 0xaf, 0x59, + 0xb5, 0x9c, 0xf0, 0xfc, 0x81, 0x66, 0x18, 0x87, 0xf7, 0xca, 0x6b, 0x3d, 0xae, 0x06, 0x53, 0x57, + 0x2c, 0x23, 0x28, 0x56, 0x47, 0xde, 0x01, 0xe9, 0x2b, 0x97, 0x2e, 0x6e, 0xa8, 0xaf, 0x27, 0xa8, + 0x92, 0x26, 0x18, 0x87, 0xf7, 0x77, 0x0f, 0xfe, 0x86, 0x04, 0x25, 0xdb, 0x7f, 0x49, 0x50, 0xac, + 0x2e, 0xbc, 0x47, 0x45, 0xfe, 0xeb, 0xeb, 0xe5, 0x59, 0x5b, 0x2b, 0x9a, 0x33, 0xe3, 0x0b, 0x82, + 0x2a, 0x69, 0x82, 0x71, 0x78, 0x47, 0x96, 0x40, 0x09, 0x73, 0x48, 0xd6, 0xb3, 0xd9, 0x2e, 0x3b, + 0xa0, 0xd8, 0xd2, 0xd3, 0x27, 0xd9, 0x53, 0xd2, 0x79, 0x1f, 0x3d, 0x62, 0x38, 0xbc, 0xd0, 0x54, + 0xd5, 0x50, 0x3d, 0x7c, 0xea, 0x0e, 0xe3, 0xf0, 0x8e, 0x0e, 0xd2, 0x8b, 0x9a, 0x38, 0x41, 0xc7, + 0x85, 0x79, 0x2a, 0x9e, 0x2f, 0x5b, 0x0c, 0xc5, 0xbe, 0xfe, 0xf1, 0x0a, 0x7b, 0x4a, 0x3a, 0xef, + 0xd4, 0x79, 0x9b, 0x33, 0xa7, 0x94, 0x2e, 0xe0, 0x6d, 0x66, 0x59, 0x07, 0x06, 0xf8, 0xed, 0xd9, + 0xb9, 0x63, 0xdf, 0x37, 0x7b, 0xbb, 0xbb, 0xbb, 0xe7, 0xce, 0xfc, 0xe2, 0xdc, 0x99, 0x30, 0x27, + 0xc7, 0x4d, 0x96, 0x13, 0xcc, 0x74, 0x57, 0xd5, 0x38, 0xbc, 0xcb, 0xa5, 0xdd, 0xe8, 0xf8, 0x0e, + 0x62, 0x9e, 0xd6, 0x65, 0x32, 0xd4, 0x3d, 0x6d, 0xce, 0x4c, 0x67, 0x4f, 0xab, 0xc6, 0xbb, 0xe7, + 0xa1, 0x03, 0x7f, 0xdc, 0xbd, 0x83, 0x7e, 0xc2, 0x22, 0xef, 0xba, 0x77, 0xb7, 0x42, 0xb5, 0xb5, + 0x1c, 0xfc, 0x2d, 0x12, 0x16, 0x42, 0xde, 0xc1, 0xf5, 0xad, 0x9b, 0x58, 0xc7, 0xed, 0xb0, 0xc3, + 0x68, 0xeb, 0xda, 0xa9, 0x2b, 0x97, 0xf5, 0xd4, 0xc5, 0x27, 0x8e, 0x11, 0x11, 0x58, 0x17, 0x15, + 0xd9, 0x33, 0xdf, 0x69, 0x65, 0xc1, 0x39, 0x1c, 0x53, 0xe3, 0x5d, 0xad, 0x6e, 0x31, 0xfb, 0xf4, + 0x93, 0xd2, 0x92, 0x92, 0x43, 0xee, 0xfb, 0x14, 0xbd, 0x2c, 0x9b, 0x0e, 0xef, 0xe8, 0x18, 0x82, + 0x17, 0x5f, 0xad, 0x24, 0x22, 0x50, 0xe8, 0xeb, 0x05, 0x05, 0x66, 0x53, 0x36, 0x8c, 0x6b, 0xc2, + 0x20, 0xed, 0xbf, 0x03, 0xb4, 0xe4, 0x66, 0xa3, 0xb9, 0x49, 0x51, 0xc4, 0x3f, 0x3a, 0x4a, 0xeb, + 0x7c, 0xf7, 0x16, 0xcd, 0xef, 0xbf, 0xba, 0x72, 0x91, 0x33, 0xfd, 0xe0, 0xe5, 0x1d, 0x20, 0x7b, + 0xe7, 0xf6, 0x9e, 0x22, 0xbf, 0x4e, 0xd7, 0x25, 0x37, 0x74, 0x54, 0x9c, 0xb2, 0x83, 0x24, 0xe7, + 0x9e, 0xf3, 0x19, 0xd4, 0xbc, 0xd7, 0x3e, 0x8e, 0xe8, 0x9b, 0x8e, 0xd7, 0x61, 0x2b, 0xc5, 0xeb, + 0x1f, 0x2e, 0x23, 0x39, 0xa5, 0x27, 0xb9, 0x0f, 0x45, 0x54, 0x0c, 0x72, 0xde, 0x15, 0x4a, 0xdb, + 0xab, 0xbe, 0xf3, 0x34, 0x2a, 0xce, 0x9f, 0xd5, 0x42, 0x42, 0x7d, 0x42, 0x1c, 0x5a, 0xc6, 0x4a, + 0xc7, 0x73, 0xed, 0xa1, 0x18, 0xe2, 0x5d, 0xde, 0xd9, 0x99, 0xb2, 0xc8, 0x1e, 0x51, 0x5f, 0x76, + 0x9a, 0xdf, 0x39, 0x26, 0x6f, 0xff, 0xba, 0x87, 0x36, 0xdb, 0x81, 0x6e, 0x4c, 0x5b, 0x91, 0x90, + 0xf3, 0x16, 0x6a, 0x3f, 0x9d, 0xac, 0x27, 0x2e, 0x5e, 0x30, 0xbe, 0x7d, 0x64, 0x5b, 0x71, 0x51, + 0xdc, 0xf4, 0xa9, 0x88, 0x7a, 0xd0, 0x2d, 0x91, 0x62, 0x9c, 0xe0, 0x01, 0x46, 0x5b, 0xc5, 0xc7, + 0x02, 0xd0, 0x5d, 0x4f, 0xc7, 0x9b, 0xd5, 0xdc, 0xfe, 0x15, 0x27, 0x3b, 0x30, 0xec, 0x44, 0xbc, + 0x93, 0x3d, 0xba, 0x99, 0x17, 0x8c, 0xcf, 0x3b, 0x40, 0x73, 0x66, 0x46, 0xfc, 0x2c, 0x6b, 0xaa, + 0x99, 0x0d, 0xe0, 0x54, 0x93, 0x91, 0x93, 0xac, 0x5d, 0x52, 0x76, 0x26, 0x14, 0x1d, 0x26, 0x01, + 0x6d, 0x43, 0x2a, 0xaf, 0xf3, 0x38, 0x75, 0x06, 0x92, 0x8e, 0x0e, 0x35, 0x30, 0x0a, 0xfa, 0x05, + 0xef, 0x00, 0x1d, 0x35, 0xd5, 0x68, 0xbb, 0x22, 0x0a, 0xa0, 0x9f, 0x03, 0xba, 0xf9, 0xa5, 0xa7, + 0x43, 0x2a, 0x2e, 0x5d, 0x28, 0x0b, 0x0b, 0x29, 0x3e, 0xea, 0x9b, 0xb5, 0x63, 0x5b, 0x94, 0xa5, + 0x39, 0x35, 0x4d, 0x92, 0xad, 0x4d, 0x7d, 0x7c, 0x2c, 0xaf, 0xbc, 0x04, 0x79, 0x79, 0x80, 0x77, + 0xa9, 0x94, 0x9f, 0x05, 0x2f, 0x59, 0xf4, 0x17, 0xde, 0x21, 0x4a, 0x82, 0x03, 0xa3, 0x27, 0x59, + 0xaa, 0xb1, 0xaf, 0x29, 0x80, 0xba, 0x05, 0xbc, 0x09, 0x59, 0x47, 0x87, 0x16, 0x19, 0xe1, 0x58, + 0xb9, 0xea, 0x15, 0xfd, 0x8b, 0x77, 0x00, 0x50, 0xb9, 0x17, 0x07, 0xfa, 0x51, 0xab, 0x11, 0x7a, + 0x88, 0xb3, 0x9e, 0x2e, 0x0c, 0xf0, 0xa9, 0xcb, 0x17, 0xe8, 0xef, 0xf0, 0x55, 0x89, 0x44, 0xa2, + 0xd7, 0xc7, 0xec, 0x77, 0xbc, 0x23, 0xb4, 0x64, 0xbf, 0x2c, 0x3d, 0x79, 0x4c, 0xe8, 0xe7, 0x95, + 0xbd, 0xdb, 0x39, 0xdd, 0x61, 0x6d, 0xd6, 0xce, 0xed, 0x85, 0x5e, 0x87, 0x41, 0xbd, 0xaf, 0x3c, + 0xb8, 0x83, 0xdc, 0x14, 0xa6, 0xb1, 0xd0, 0x7f, 0x79, 0x1f, 0x82, 0xee, 0x00, 0x95, 0x70, 0xab, + 0xb0, 0xe0, 0xdd, 0xfd, 0xbf, 0x4a, 0x4f, 0x9f, 0x04, 0xb5, 0x88, 0xd0, 0xdf, 0x27, 0xff, 0xe0, + 0xbe, 0xdc, 0xbd, 0xbb, 0x40, 0x33, 0x99, 0xb1, 0xd1, 0x21, 0xc3, 0x71, 0x03, 0x28, 0xcd, 0xb9, + 0xae, 0x7b, 0x0a, 0x3c, 0x0f, 0x81, 0x1a, 0x1b, 0x0c, 0x5d, 0x2b, 0x2e, 0x9c, 0xad, 0x8f, 0x89, + 0x6a, 0x7f, 0xfd, 0x8a, 0xac, 0x9d, 0x47, 0xff, 0xc1, 0x50, 0x71, 0x67, 0x86, 0x5c, 0x2e, 0x97, + 0x0e, 0x40, 0x74, 0xb7, 0x4b, 0xea, 0xe2, 0x63, 0x41, 0xef, 0x24, 0xdf, 0xe3, 0x40, 0xca, 0xe2, + 0x85, 0x51, 0x16, 0xe6, 0x98, 0x7d, 0x14, 0xb5, 0x10, 0x33, 0x65, 0x52, 0xfa, 0xfa, 0x35, 0x42, + 0x7f, 0xef, 0xf2, 0xb3, 0xa7, 0x5b, 0x0b, 0x89, 0x39, 0x7f, 0x31, 0x3a, 0x86, 0x8a, 0x3b, 0x79, + 0x78, 0x1c, 0x74, 0x0f, 0x3d, 0x19, 0x6c, 0xc8, 0x1c, 0xdb, 0x2b, 0x5f, 0x83, 0x91, 0xa4, 0xc0, + 0xdd, 0x2d, 0xee, 0x8b, 0xa9, 0xda, 0x95, 0x6f, 0xf6, 0x90, 0xb4, 0x60, 0x3e, 0xe8, 0xde, 0x54, + 0x85, 0x5f, 0x27, 0x78, 0xae, 0x8c, 0x51, 0x30, 0x54, 0xdc, 0x09, 0x63, 0xcb, 0x26, 0x07, 0x38, + 0x09, 0x71, 0x60, 0x9f, 0xab, 0xbe, 0xf3, 0x02, 0x03, 0x9d, 0x57, 0x97, 0x2e, 0x08, 0xdc, 0x5d, + 0xa3, 0xad, 0x2c, 0x58, 0xc7, 0x3a, 0xd3, 0x52, 0x57, 0x2c, 0x15, 0x1c, 0x70, 0xeb, 0xd9, 0xe3, + 0x19, 0x72, 0xbc, 0xec, 0xdb, 0x53, 0x15, 0x17, 0xbf, 0xcb, 0x50, 0x4d, 0x04, 0x94, 0x9c, 0x08, + 0x52, 0xf6, 0x73, 0x40, 0x67, 0x7d, 0xd7, 0xce, 0x67, 0x0b, 0xed, 0xd8, 0x07, 0xad, 0xf1, 0x33, + 0xad, 0x41, 0x4a, 0xd0, 0x3b, 0xe2, 0xbb, 0x59, 0xb7, 0x9f, 0x60, 0xa8, 0xb8, 0x13, 0x03, 0xe8, + 0xff, 0x2c, 0xb2, 0xb3, 0xa5, 0xce, 0x31, 0x73, 0x6e, 0x72, 0xd6, 0x1a, 0xed, 0x55, 0x95, 0x60, + 0xf8, 0x98, 0xb2, 0xe8, 0xbf, 0x8c, 0x85, 0x12, 0x94, 0xfe, 0x8c, 0x8d, 0xeb, 0x41, 0x21, 0xae, + 0xb9, 0x7d, 0x93, 0x65, 0x83, 0xc1, 0x0f, 0x57, 0xaf, 0xd0, 0x0f, 0x45, 0x90, 0x77, 0x77, 0xb5, + 0xe4, 0x64, 0x55, 0x5c, 0x3c, 0x5f, 0xe4, 0xef, 0xfd, 0x7c, 0xd9, 0x62, 0x64, 0xf2, 0xa1, 0x36, + 0x3b, 0x93, 0xb5, 0x7d, 0x6b, 0xf9, 0xb9, 0x30, 0x69, 0x6b, 0xab, 0x9e, 0x1e, 0x50, 0x4f, 0x18, + 0x2a, 0xee, 0x64, 0xd0, 0xd1, 0xde, 0x8e, 0xfc, 0x27, 0x50, 0x83, 0xbd, 0xed, 0x3c, 0xb2, 0x1b, + 0x06, 0xbb, 0x1a, 0xea, 0x8b, 0x8f, 0x1f, 0x4d, 0xee, 0xdd, 0xb6, 0xf1, 0x5e, 0x87, 0x7b, 0xb2, + 0x55, 0xfe, 0xa1, 0xfd, 0x15, 0xe7, 0xcf, 0x62, 0xba, 0xa8, 0xbf, 0x7a, 0xe5, 0x72, 0x37, 0xd7, + 0x19, 0x20, 0xe2, 0xfc, 0xbc, 0x92, 0xe0, 0xa0, 0x2c, 0xa7, 0x2d, 0xa8, 0xdc, 0x3f, 0x36, 0x1b, + 0xf3, 0xc4, 0x6c, 0x0c, 0xbc, 0xce, 0x74, 0xdc, 0x58, 0x71, 0xe1, 0x9c, 0xee, 0x8e, 0x78, 0x0c, + 0x86, 0xa1, 0xe2, 0x4e, 0x00, 0xf5, 0xf5, 0x75, 0xe6, 0x63, 0x47, 0xd3, 0xcb, 0x3a, 0xf2, 0xda, + 0x22, 0x91, 0xb4, 0x11, 0xc9, 0xa8, 0x2a, 0xfc, 0xda, 0xcb, 0x6d, 0x8e, 0xf4, 0x82, 0x9e, 0xb2, + 0xd0, 0x1e, 0x54, 0xf6, 0x92, 0xb2, 0x52, 0x5e, 0xd2, 0x94, 0xc5, 0x9d, 0x72, 0x3c, 0x02, 0x23, + 0xda, 0x25, 0x92, 0xc7, 0x8f, 0x22, 0x5c, 0xf7, 0xee, 0x1e, 0xff, 0xe9, 0x27, 0xf0, 0x71, 0x56, + 0x8c, 0x18, 0xfe, 0x5e, 0x4d, 0x3f, 0x61, 0x5c, 0x81, 0xe7, 0xc1, 0x86, 0xc4, 0x78, 0x1d, 0x1e, + 0xcb, 0x70, 0x18, 0x2a, 0xee, 0xe4, 0x71, 0xec, 0x68, 0xc0, 0x65, 0xd5, 0x11, 0xc6, 0x04, 0xd1, + 0xdd, 0xd8, 0x50, 0x1c, 0xe4, 0x1f, 0x3b, 0x6d, 0x8a, 0x5a, 0x41, 0x4f, 0xb6, 0xb3, 0x2d, 0x39, + 0x1e, 0x08, 0xaa, 0x7c, 0x4c, 0x39, 0x62, 0xb1, 0xd8, 0xdb, 0xd3, 0x23, 0xf2, 0xc9, 0xe3, 0xa4, + 0x84, 0xf8, 0xb9, 0x33, 0xad, 0x37, 0xae, 0x5f, 0x9b, 0x18, 0xaf, 0x7e, 0x12, 0x76, 0x79, 0x59, + 0x99, 0xc5, 0xb8, 0x31, 0x9a, 0xbe, 0xde, 0x27, 0xae, 0x7b, 0x18, 0xd4, 0xb0, 0x5f, 0xa0, 0x34, + 0x46, 0xe8, 0xf7, 0x1d, 0xfa, 0xa1, 0xe2, 0x4e, 0x1e, 0xc4, 0x8b, 0x7b, 0x5b, 0x71, 0x91, 0xd0, + 0xf7, 0x88, 0x5a, 0x37, 0x3a, 0xca, 0xc2, 0x5c, 0xe8, 0xeb, 0x25, 0xce, 0xcb, 0xe5, 0x25, 0x4a, + 0x24, 0x12, 0xc1, 0x52, 0xeb, 0xf6, 0xf5, 0x9e, 0x53, 0x21, 0x27, 0x9f, 0x25, 0x27, 0xfd, 0x7f, + 0x7b, 0x67, 0xe2, 0xd5, 0xc4, 0xb5, 0x06, 0xf0, 0xf7, 0x5f, 0xb4, 0xa7, 0x9e, 0x67, 0x6b, 0x6d, + 0xd5, 0x08, 0x22, 0x28, 0x22, 0x22, 0x8a, 0x88, 0x28, 0x22, 0xd5, 0xd7, 0xba, 0x80, 0xa2, 0xd6, + 0xdd, 0xa2, 0x80, 0x82, 0x82, 0x10, 0x08, 0x61, 0x71, 0x17, 0xc5, 0x15, 0x50, 0x5a, 0xdc, 0xb1, + 0xd6, 0x56, 0xc5, 0xad, 0x56, 0xb6, 0x10, 0x90, 0xdd, 0x05, 0x42, 0x02, 0x24, 0x10, 0x56, 0x45, + 0x40, 0x90, 0x35, 0x10, 0x48, 0xe0, 0x7d, 0x71, 0x34, 0x0d, 0x93, 0x41, 0x43, 0xe6, 0x4e, 0x12, + 0xc8, 0xfc, 0xce, 0x3d, 0x9e, 0x6c, 0xde, 0xf9, 0xa2, 0xbf, 0xb9, 0xb9, 0x77, 0xe6, 0xde, 0xfb, + 0x75, 0x0e, 0xd1, 0xff, 0xe6, 0x15, 0x15, 0x5a, 0x4c, 0x99, 0x8c, 0x73, 0xdd, 0x63, 0xab, 0x22, + 0xb9, 0x38, 0x0c, 0x55, 0x8b, 0xbd, 0x76, 0xe0, 0x8c, 0x4f, 0xb3, 0xb4, 0x80, 0x13, 0xd2, 0xc0, + 0x2f, 0xdd, 0xd0, 0xba, 0xa3, 0x07, 0xad, 0xee, 0x5d, 0xe5, 0x22, 0x65, 0xba, 0x70, 0x65, 0xc9, + 0xb0, 0xb3, 0x2d, 0x3f, 0x18, 0xd1, 0x3f, 0xfc, 0xec, 0x7b, 0x98, 0xee, 0xd8, 0x2c, 0xb9, 0xd8, + 0x98, 0x68, 0xd9, 0xd0, 0x73, 0x29, 0xa4, 0x52, 0xe9, 0xda, 0xd5, 0xae, 0xe0, 0xf7, 0x3c, 0xdb, + 0x59, 0x4a, 0xdd, 0x85, 0x1f, 0x27, 0x84, 0x49, 0x1b, 0x1b, 0x44, 0xa1, 0xc1, 0xb8, 0xcb, 0x38, + 0xf0, 0x14, 0xc6, 0xc7, 0x1a, 0xcd, 0x7a, 0xd2, 0x13, 0xb4, 0xee, 0xe8, 0x41, 0xa8, 0x3b, 0xf4, + 0x61, 0x84, 0xa1, 0xc1, 0xb8, 0x76, 0x3d, 0x6b, 0xd1, 0x02, 0x18, 0x20, 0x6a, 0x57, 0x21, 0xa6, + 0xfb, 0xc6, 0x75, 0xee, 0xf0, 0x38, 0x26, 0xfa, 0xdc, 0x50, 0xc3, 0x68, 0xdf, 0x5d, 0x5e, 0xee, + 0xae, 0x2b, 0xa5, 0x1f, 0x77, 0x4a, 0x69, 0x6f, 0x6f, 0x5f, 0xf1, 0xe3, 0x52, 0x4f, 0x8f, 0xed, + 0xaa, 0x9f, 0x91, 0x4b, 0x7b, 0x60, 0xc0, 0x80, 0xeb, 0xd8, 0x70, 0x66, 0x5a, 0x2a, 0xb6, 0xa8, + 0x35, 0xd4, 0xe9, 0x06, 0xb4, 0xee, 0xe8, 0x41, 0xa8, 0xbb, 0xf8, 0xe8, 0x41, 0xae, 0xad, 0x8d, + 0xaa, 0x4f, 0xdc, 0x39, 0xb3, 0xe1, 0x45, 0xad, 0x2b, 0xfc, 0xac, 0xee, 0x51, 0x91, 0xc7, 0x16, + 0x39, 0xd8, 0xb7, 0xb5, 0xb6, 0x6a, 0x52, 0x9b, 0x5c, 0x22, 0x11, 0x86, 0xb2, 0x70, 0xd3, 0xf6, + 0x72, 0x97, 0xb9, 0xbc, 0x4a, 0xb8, 0xaa, 0x75, 0x84, 0x94, 0x42, 0xeb, 0x8e, 0x1e, 0x54, 0xba, + 0xb7, 0x17, 0xbe, 0xe0, 0x79, 0x0f, 0xea, 0x22, 0x27, 0x31, 0x26, 0x82, 0x5e, 0x7d, 0x6d, 0x1a, + 0xb9, 0x48, 0xc8, 0x27, 0x74, 0xbf, 0x79, 0x23, 0xc1, 0xda, 0xd2, 0xa2, 0xb6, 0xb6, 0x66, 0x58, + 0x15, 0x76, 0x96, 0x0a, 0x4a, 0xfc, 0x7c, 0x71, 0x7d, 0x2d, 0x61, 0x08, 0x13, 0xed, 0x66, 0xad, + 0xa8, 0xa0, 0x75, 0x47, 0x0f, 0x2a, 0xdd, 0xc5, 0x91, 0x47, 0x70, 0x9b, 0x6f, 0xe6, 0x2f, 0xff, + 0xdf, 0x9b, 0xbb, 0xb7, 0xc9, 0xd7, 0x8c, 0x11, 0x7d, 0xee, 0x2c, 0x96, 0xac, 0x87, 0xcb, 0xe1, + 0x4c, 0x9b, 0x62, 0x52, 0xcc, 0x1b, 0xde, 0xa8, 0x57, 0x49, 0xe5, 0xa9, 0x13, 0xca, 0x2c, 0x3f, + 0x58, 0xc9, 0xf9, 0x61, 0x49, 0xfd, 0xad, 0x9b, 0xa8, 0xe2, 0x44, 0x08, 0xad, 0x3b, 0x7a, 0x90, + 0xe8, 0x2e, 0x6b, 0x6f, 0x57, 0x2e, 0xea, 0x55, 0x96, 0xd2, 0xe0, 0x00, 0x24, 0x59, 0x01, 0xa1, + 0x51, 0x9f, 0x35, 0x63, 0x9a, 0xe9, 0xc4, 0xef, 0x2c, 0xa7, 0x4e, 0x31, 0x37, 0x65, 0xa4, 0xa5, + 0xa6, 0x90, 0xa9, 0xad, 0xed, 0x59, 0x7e, 0xe1, 0xc7, 0x2d, 0x84, 0x94, 0x37, 0xbc, 0x44, 0xd4, + 0x6f, 0xca, 0xac, 0x05, 0xb4, 0xee, 0xe8, 0x41, 0xa2, 0xbb, 0xa4, 0xb2, 0xa2, 0x64, 0x9f, 0xdf, + 0xa0, 0x9e, 0x8c, 0x09, 0x43, 0x14, 0xc6, 0x22, 0x1f, 0x5e, 0x4d, 0x75, 0x95, 0xea, 0x0d, 0x60, + 0x90, 0x3e, 0xf2, 0xd8, 0x91, 0x4b, 0x17, 0xe3, 0xb5, 0x2e, 0x7f, 0x9c, 0x8f, 0xe1, 0x07, 0xf8, + 0xe1, 0xce, 0x4c, 0xad, 0xd7, 0x80, 0x50, 0x0a, 0xad, 0x3b, 0x7a, 0x90, 0xe8, 0xde, 0x51, 0x5c, + 0x54, 0xec, 0xbd, 0x73, 0xd0, 0x85, 0xed, 0xe9, 0x16, 0xa8, 0xf2, 0x26, 0x43, 0x1f, 0x66, 0xb6, + 0x95, 0x25, 0x48, 0x0f, 0x7f, 0x92, 0x9f, 0xe3, 0x20, 0xeb, 0xea, 0x14, 0xaa, 0x64, 0x3a, 0xf9, + 0xd0, 0x7d, 0x67, 0x07, 0x69, 0x7e, 0xf3, 0x4b, 0x67, 0xd0, 0xba, 0xa3, 0x07, 0x8d, 0xee, 0x3c, + 0xd0, 0x7d, 0xd0, 0x38, 0x35, 0xcd, 0xd2, 0xa2, 0xfc, 0x20, 0xb2, 0x34, 0xe1, 0x08, 0x91, 0x4b, + 0xba, 0xd4, 0xfb, 0x5d, 0x30, 0x5a, 0xed, 0x55, 0xcb, 0xfa, 0xa9, 0x77, 0x68, 0xdd, 0xd1, 0x83, + 0x4e, 0xf7, 0x9d, 0x23, 0x44, 0x77, 0x89, 0xba, 0xee, 0x65, 0x21, 0x41, 0xb4, 0xee, 0x46, 0x01, + 0x55, 0x9d, 0x19, 0x83, 0xd5, 0xbd, 0x7b, 0x08, 0xdd, 0xdf, 0x36, 0xe9, 0x3b, 0x34, 0x3c, 0xb4, + 0xee, 0xe8, 0x31, 0x3e, 0xdd, 0xbb, 0x09, 0x74, 0x67, 0xd1, 0xba, 0x93, 0xe3, 0x55, 0x5d, 0xdd, + 0x37, 0x63, 0xbe, 0x84, 0x02, 0x03, 0xac, 0x7e, 0x43, 0xbd, 0x4d, 0x3d, 0x60, 0x84, 0xba, 0xf7, + 0x74, 0xab, 0x0f, 0x55, 0xcb, 0x58, 0xcc, 0xde, 0xa6, 0x21, 0x93, 0x84, 0xea, 0x8b, 0x11, 0xa3, + 0x3b, 0xf8, 0x6d, 0x3b, 0x73, 0x06, 0x88, 0x5e, 0x53, 0x53, 0x0d, 0xc6, 0x6f, 0x5a, 0x8f, 0x2c, + 0x89, 0x3b, 0x72, 0x28, 0xd4, 0x1d, 0xd1, 0x95, 0x19, 0xb4, 0xc8, 0x7b, 0x7a, 0x44, 0x04, 0x9d, + 0x19, 0xa6, 0x94, 0xd6, 0x5d, 0x6b, 0xbc, 0x77, 0x7a, 0x7c, 0xfd, 0xd5, 0x17, 0xf9, 0xb9, 0xb9, + 0xf0, 0xf8, 0xf2, 0xc5, 0x78, 0x78, 0x7c, 0xf4, 0xb0, 0xf6, 0x53, 0x47, 0x28, 0xc5, 0xf0, 0x2f, + 0x44, 0xa2, 0x45, 0x31, 0x57, 0x8c, 0x50, 0x77, 0xc3, 0x9b, 0x47, 0x30, 0x32, 0x74, 0xbf, 0x72, + 0xe9, 0x22, 0xf8, 0x7d, 0xfa, 0xe4, 0x09, 0xe5, 0x2b, 0x3b, 0xb6, 0x6f, 0x85, 0x57, 0xee, 0x27, + 0xde, 0xd5, 0x63, 0x54, 0x43, 0x61, 0x6c, 0xba, 0xf7, 0x4b, 0xa5, 0x04, 0xba, 0xb3, 0x68, 0xdd, + 0xb5, 0x42, 0x35, 0x51, 0x24, 0xae, 0x40, 0xaf, 0x46, 0x39, 0x03, 0xdb, 0x79, 0xa1, 0xc3, 0x62, + 0xc7, 0xf9, 0x87, 0x0f, 0x44, 0xb8, 0xbb, 0xae, 0x74, 0x71, 0x5a, 0xb8, 0x6e, 0x8d, 0xeb, 0x99, + 0x53, 0x51, 0x0b, 0xe7, 0xcf, 0x0b, 0x0b, 0x51, 0x64, 0xc6, 0x49, 0x49, 0x4e, 0xb2, 0xb6, 0xb4, + 0x88, 0x8a, 0x3c, 0x76, 0x20, 0x3c, 0xcc, 0x8c, 0x31, 0xe1, 0xd1, 0xc3, 0x07, 0xf0, 0xe2, 0xb6, + 0xcd, 0x1b, 0xf7, 0xec, 0xf6, 0x86, 0x07, 0x21, 0x41, 0x81, 0x9b, 0x7f, 0x5e, 0x87, 0x2a, 0x60, + 0x64, 0xba, 0xef, 0x1a, 0xd1, 0xba, 0x07, 0x6a, 0xb8, 0x64, 0x56, 0x97, 0x18, 0xba, 0xee, 0x52, + 0xa9, 0x74, 0xaa, 0xc9, 0xa4, 0x09, 0xe3, 0xc6, 0xb6, 0xb7, 0xb7, 0xe3, 0xde, 0x12, 0x57, 0x54, + 0x80, 0xee, 0x53, 0x26, 0x7d, 0x8f, 0xad, 0x04, 0x05, 0xdd, 0x93, 0xfe, 0x79, 0x0c, 0x0f, 0x7a, + 0x7b, 0x7b, 0xe1, 0x4c, 0x68, 0x7b, 0xbf, 0xac, 0x26, 0x3b, 0xeb, 0x29, 0x18, 0x0f, 0x0f, 0xe0, + 0x33, 0xf1, 0x71, 0x17, 0xe0, 0x37, 0xc1, 0x71, 0xde, 0x5c, 0xa8, 0x90, 0xc5, 0x0c, 0xc0, 0x2a, + 0x81, 0xdf, 0x07, 0xf8, 0x30, 0x66, 0x3f, 0x2a, 0x10, 0xe9, 0xce, 0x1b, 0x31, 0xba, 0xf7, 0x4a, + 0x45, 0x04, 0x43, 0x55, 0x5a, 0xf7, 0xe1, 0xb3, 0x76, 0xb5, 0x2b, 0xe8, 0x98, 0xfc, 0xe4, 0x1f, + 0xc2, 0x77, 0xff, 0xfc, 0xe3, 0x26, 0xbc, 0xeb, 0xe2, 0xe4, 0x38, 0xf0, 0x5e, 0x77, 0x2e, 0x47, + 0xb1, 0xec, 0x12, 0xd3, 0xbd, 0xe3, 0xfd, 0x9a, 0x1a, 0xa5, 0xee, 0xb3, 0x66, 0x4c, 0x7b, 0x78, + 0x5f, 0x91, 0x47, 0xba, 0xf5, 0xdd, 0xbb, 0x79, 0xb6, 0xb3, 0xb0, 0x46, 0xfd, 0xea, 0xe5, 0x4b, + 0x8b, 0x1c, 0xec, 0xbb, 0x25, 0x12, 0xa8, 0xe1, 0xb7, 0xb8, 0xf3, 0xa8, 0x62, 0x36, 0x3e, 0xdd, + 0x7b, 0x89, 0x74, 0x67, 0x52, 0x9d, 0x33, 0x5d, 0x0b, 0x0c, 0x5d, 0x77, 0x84, 0x40, 0x03, 0xdf, + 0xa4, 0x93, 0x6b, 0x05, 0x46, 0xa7, 0x7b, 0x5f, 0x2f, 0x71, 0x67, 0x86, 0xd6, 0xdd, 0x18, 0x30, + 0x3e, 0xdd, 0xfb, 0x08, 0x75, 0xef, 0xa9, 0x7f, 0xad, 0xef, 0xd0, 0xf0, 0xd0, 0xba, 0xa3, 0x47, + 0xa1, 0xfb, 0xd9, 0x33, 0x24, 0x2b, 0x51, 0xd7, 0x3d, 0xd5, 0x60, 0x75, 0x97, 0x11, 0xe9, 0x1e, + 0x4c, 0xeb, 0x6e, 0x1c, 0xe8, 0x45, 0x77, 0xb9, 0x5c, 0xae, 0x5c, 0x49, 0x4d, 0x05, 0xdd, 0x43, + 0xef, 0x78, 0xdf, 0x2f, 0x93, 0x11, 0xe9, 0x8e, 0x66, 0x25, 0x0a, 0x5a, 0x68, 0xdd, 0xd1, 0x83, + 0x4e, 0x77, 0xcf, 0xcf, 0xea, 0xce, 0x2b, 0x2a, 0x5a, 0xb3, 0x6a, 0x05, 0x76, 0x59, 0x56, 0xc0, + 0xe7, 0x93, 0x3c, 0xe8, 0x27, 0x70, 0x77, 0x5b, 0x05, 0x87, 0x30, 0x37, 0x65, 0xc4, 0xff, 0x1a, + 0x87, 0xdf, 0x59, 0x52, 0x2e, 0x23, 0x98, 0x33, 0x43, 0xeb, 0x6e, 0x24, 0xa0, 0xd1, 0x9d, 0x3f, + 0xa4, 0xee, 0x6d, 0xad, 0xad, 0x87, 0xf6, 0x87, 0x4f, 0x18, 0x37, 0x16, 0x77, 0x17, 0x42, 0x07, + 0xba, 0xab, 0x96, 0xa5, 0xce, 0x4e, 0x39, 0xd9, 0x59, 0x8a, 0xf7, 0xe4, 0x72, 0x02, 0xdd, 0x83, + 0x02, 0x7a, 0x5e, 0xd5, 0x51, 0x17, 0x8f, 0x76, 0xd0, 0xba, 0xa3, 0x87, 0x3a, 0xdd, 0x85, 0x11, + 0x6c, 0x75, 0xed, 0x94, 0x65, 0xbd, 0xbb, 0x9b, 0xa7, 0xc7, 0x76, 0x8a, 0xca, 0x74, 0x33, 0x53, + 0xc2, 0x83, 0x42, 0x7b, 0x7f, 0xf5, 0x62, 0x3c, 0xad, 0xbb, 0xf1, 0x42, 0x75, 0xeb, 0x3e, 0xf0, + 0xbe, 0xa7, 0x7e, 0xf3, 0x46, 0xc2, 0xcc, 0xe9, 0xe6, 0x7a, 0x69, 0xdd, 0xbf, 0x19, 0xf3, 0xa5, + 0xaf, 0xb7, 0xe7, 0xab, 0xba, 0x7f, 0x6d, 0x16, 0x85, 0xb1, 0x92, 0x4d, 0x27, 0xab, 0x46, 0x5b, + 0xe2, 0xbf, 0x57, 0x22, 0x2e, 0xa7, 0x2e, 0x1e, 0xed, 0xa0, 0x75, 0x47, 0x0f, 0x95, 0xba, 0x87, + 0xa9, 0x7f, 0xb2, 0xb9, 0xf9, 0x6d, 0x28, 0x2b, 0x68, 0xfc, 0xd8, 0x31, 0x9a, 0xe8, 0x0e, 0xe7, + 0xc9, 0xed, 0xbf, 0x6e, 0x6d, 0xdf, 0xb2, 0x71, 0xc9, 0xa2, 0x05, 0x9b, 0xd6, 0xaf, 0x3d, 0x7c, + 0x20, 0x42, 0xc3, 0x84, 0x9b, 0xa0, 0xbb, 0xd3, 0x82, 0xf9, 0xd8, 0x8d, 0x3c, 0x75, 0xca, 0x0f, + 0x86, 0xe3, 0xb6, 0x13, 0x2b, 0xdc, 0xbe, 0xa5, 0xb5, 0x20, 0x4f, 0x93, 0x9a, 0x75, 0x09, 0xad, + 0x3b, 0x7a, 0x28, 0xd3, 0xdd, 0x9c, 0x50, 0x77, 0xcd, 0x69, 0x6a, 0x6a, 0x84, 0x86, 0x79, 0xdb, + 0xe6, 0x8d, 0xca, 0x57, 0x92, 0x93, 0x9e, 0x40, 0x6b, 0xed, 0xe7, 0xbb, 0x9b, 0x64, 0xb4, 0xf5, + 0xb7, 0x6e, 0xe6, 0x2c, 0x5d, 0xa2, 0x1a, 0x6d, 0xe6, 0x02, 0x7b, 0x4d, 0x72, 0xc7, 0xeb, 0x18, + 0x5a, 0x77, 0xf4, 0x18, 0xac, 0xee, 0x80, 0xfd, 0x1c, 0x1b, 0xf0, 0xdb, 0x76, 0xe6, 0x8c, 0x90, + 0xa0, 0x40, 0x68, 0xaa, 0x3f, 0xbb, 0xbf, 0xbb, 0x86, 0x48, 0x2a, 0xc5, 0xa5, 0x4c, 0x7f, 0x82, + 0x4b, 0xef, 0x06, 0x76, 0x71, 0x86, 0xd6, 0x1d, 0x3d, 0x86, 0xac, 0xfb, 0x87, 0xca, 0x3b, 0x3a, + 0xee, 0xdd, 0xbd, 0xb3, 0x63, 0xdb, 0x16, 0xc6, 0x77, 0xdf, 0x82, 0xfd, 0x8b, 0x1d, 0xe7, 0x93, + 0xaf, 0xb3, 0x2a, 0xfa, 0x4c, 0xa6, 0xc3, 0x3c, 0xd5, 0x80, 0xd3, 0x6d, 0xac, 0x2b, 0x0e, 0xed, + 0x27, 0x5f, 0x33, 0x42, 0x68, 0xdd, 0xd1, 0xb3, 0x3f, 0x2c, 0x14, 0x85, 0xee, 0xc5, 0xc8, 0x75, + 0x7f, 0x70, 0x2f, 0x11, 0xe4, 0x0e, 0x63, 0x0f, 0xda, 0x9b, 0x69, 0xdd, 0x1a, 0xc5, 0x24, 0x3c, + 0x71, 0xc5, 0xf0, 0x32, 0x7f, 0xa8, 0x23, 0xeb, 0x68, 0x17, 0xc2, 0x80, 0xd5, 0x84, 0xa1, 0x1a, + 0x73, 0xce, 0x0f, 0xce, 0xd5, 0x17, 0x62, 0x48, 0xd6, 0x8c, 0x10, 0x5a, 0x77, 0xf4, 0x18, 0xac, + 0xee, 0x03, 0xef, 0xb7, 0x10, 0xdb, 0xb0, 0x76, 0x0d, 0xf4, 0xe0, 0xb1, 0x6b, 0x2c, 0x26, 0x13, + 0xc6, 0xfb, 0xee, 0xf2, 0x6a, 0x43, 0x94, 0x83, 0xa0, 0x43, 0x50, 0x5c, 0x12, 0xb0, 0x17, 0xd7, + 0xa5, 0xc9, 0x71, 0x59, 0x5c, 0x75, 0x3a, 0x6a, 0x60, 0xc0, 0x20, 0xd6, 0x16, 0xd3, 0xba, 0xa3, + 0xc7, 0x90, 0x75, 0xa7, 0x9a, 0xf6, 0x97, 0xcf, 0x05, 0x7e, 0x3e, 0x38, 0xe3, 0x39, 0xd6, 0x56, + 0xa2, 0xf0, 0x10, 0x43, 0xd8, 0x76, 0x86, 0xd6, 0x1d, 0x3d, 0xc8, 0x74, 0xdf, 0x3d, 0xf2, 0x74, + 0x07, 0xba, 0x6b, 0xaa, 0x85, 0xec, 0x20, 0xf5, 0x84, 0xdd, 0x45, 0xbf, 0x6c, 0xad, 0x8d, 0x8f, + 0x1b, 0xe8, 0xd7, 0x67, 0xfa, 0x79, 0x5a, 0x77, 0x04, 0xe0, 0x32, 0xca, 0x87, 0xb3, 0x43, 0xce, + 0x9d, 0x3e, 0xa5, 0xfa, 0x8a, 0x16, 0x75, 0x12, 0xe8, 0x3e, 0xcd, 0xbc, 0xdc, 0x20, 0xb7, 0xd5, + 0x55, 0x47, 0xde, 0xd3, 0x53, 0x71, 0xf8, 0xc0, 0xd3, 0x45, 0x0b, 0x70, 0xc6, 0x27, 0x9b, 0x4e, + 0xe6, 0xef, 0xd9, 0x55, 0x77, 0x39, 0xbe, 0x5f, 0x86, 0xe6, 0x8a, 0xd0, 0x70, 0xa1, 0x75, 0x47, + 0x03, 0x8b, 0x19, 0x30, 0xd4, 0xbd, 0xfd, 0x93, 0x27, 0x22, 0xb5, 0xa8, 0x70, 0x44, 0xeb, 0x8e, + 0xd1, 0x9a, 0x9f, 0x57, 0xc6, 0x0a, 0x24, 0x4c, 0xc3, 0x9d, 0xef, 0xba, 0x1c, 0xbe, 0x4b, 0x97, + 0xa8, 0x4c, 0xc7, 0x21, 0xd1, 0xba, 0x23, 0x03, 0x5a, 0x74, 0x75, 0xd7, 0x7f, 0x4f, 0xb8, 0xae, + 0x5d, 0x6d, 0xa0, 0x3b, 0x7f, 0x84, 0xeb, 0x8e, 0xf1, 0xe6, 0xee, 0xed, 0x92, 0x7d, 0x7b, 0x53, + 0xcc, 0x09, 0xd2, 0xd8, 0x27, 0x99, 0x30, 0x0a, 0xdc, 0x56, 0x8a, 0xc2, 0x58, 0x0d, 0x89, 0x77, + 0x74, 0x93, 0xb2, 0x8f, 0xd6, 0x1d, 0x25, 0xd8, 0xda, 0x59, 0x65, 0x21, 0x93, 0x26, 0xa0, 0x43, + 0x30, 0x4a, 0x74, 0xcc, 0x4c, 0x34, 0x61, 0x00, 0x00, 0x07, 0xa9, 0x49, 0x44, 0x41, 0x54, 0xc7, + 0x78, 0x97, 0x95, 0x29, 0x0a, 0x0d, 0xce, 0xb4, 0xb7, 0x53, 0x97, 0xfe, 0x43, 0x3f, 0xc7, 0xcc, + 0x24, 0xdb, 0x65, 0x71, 0xc9, 0xbe, 0x3d, 0x30, 0xa8, 0xad, 0x8e, 0x3d, 0xd7, 0x92, 0x91, 0xde, + 0x55, 0x2e, 0xec, 0x7b, 0xd7, 0x82, 0x36, 0xab, 0xd9, 0x7f, 0x9a, 0x39, 0x29, 0x2f, 0xb7, 0x6e, + 0xcc, 0x72, 0x72, 0x34, 0xdc, 0xb2, 0x78, 0x61, 0xce, 0xd2, 0x25, 0x79, 0x3f, 0x2d, 0x7b, 0xe6, + 0xee, 0xc6, 0xf7, 0xf1, 0x2a, 0x63, 0x31, 0xe1, 0x1f, 0xae, 0x32, 0x2a, 0xb2, 0xe9, 0xc9, 0x63, + 0x45, 0x9e, 0x68, 0xbd, 0x0e, 0x7d, 0xd4, 0xc9, 0xe4, 0xa6, 0x63, 0x93, 0xa8, 0xa0, 0x33, 0x42, + 0xa6, 0x9e, 0x51, 0xa6, 0x3b, 0x86, 0xac, 0xab, 0xb3, 0x36, 0x2e, 0xb6, 0x94, 0xe9, 0xcf, 0x9d, + 0x33, 0x7b, 0x28, 0xef, 0x09, 0xce, 0x84, 0x29, 0x93, 0x53, 0x2d, 0xcc, 0x60, 0xa4, 0x4e, 0xa6, + 0x3c, 0x5f, 0xbb, 0xba, 0x99, 0x93, 0xaa, 0xd0, 0x3d, 0x7f, 0xd5, 0x72, 0xcd, 0x8f, 0x6d, 0x68, + 0x25, 0xcd, 0x6a, 0x7a, 0xc1, 0xea, 0x55, 0xa5, 0xc1, 0x81, 0xc5, 0x47, 0x0f, 0xe5, 0xde, 0xbf, + 0x9b, 0xc1, 0xe5, 0xea, 0xbd, 0x24, 0x5c, 0xbf, 0x76, 0xf7, 0xce, 0x6d, 0x92, 0x95, 0xe4, 0xfd, + 0xf9, 0x47, 0xa1, 0xa7, 0x07, 0x4e, 0xf7, 0x92, 0x30, 0x96, 0xde, 0xbf, 0x1d, 0x92, 0x92, 0x99, + 0x9a, 0xfa, 0x22, 0x3e, 0xae, 0x94, 0x1d, 0xfc, 0x72, 0xdb, 0x66, 0xce, 0x2c, 0x2b, 0x1d, 0x78, + 0x52, 0xe0, 0xba, 0x62, 0x34, 0xe8, 0xae, 0x5a, 0x92, 0x18, 0x13, 0x73, 0x97, 0x2e, 0x81, 0xb6, + 0xbf, 0xe1, 0x7e, 0x62, 0x3f, 0x95, 0x2b, 0xd9, 0x74, 0x00, 0x15, 0xad, 0xfb, 0x95, 0x4b, 0x17, + 0x27, 0x8c, 0x1b, 0xab, 0x3a, 0x71, 0xd7, 0x30, 0xe8, 0x97, 0x88, 0xcb, 0x5f, 0xdf, 0xb8, 0x26, + 0x8a, 0x60, 0xc3, 0xd0, 0xf6, 0xc5, 0x86, 0x75, 0x4f, 0x17, 0x3a, 0x70, 0xac, 0xad, 0x92, 0x06, + 0xdf, 0xa0, 0x45, 0xa3, 0x3b, 0xf4, 0x90, 0x1a, 0xee, 0xdd, 0x45, 0x58, 0x1a, 0x1f, 0x24, 0x36, + 0x3e, 0xbc, 0x8f, 0xb0, 0x34, 0x24, 0xde, 0x7e, 0x95, 0x70, 0xad, 0x36, 0x3e, 0x4e, 0x7c, 0xfc, + 0x28, 0x74, 0xec, 0x84, 0xec, 0xa0, 0xa2, 0x1d, 0xdb, 0xb2, 0x9d, 0x17, 0xa5, 0x4c, 0x35, 0xfd, + 0xc4, 0xd7, 0xcb, 0x76, 0x5a, 0x08, 0x63, 0xa0, 0xd6, 0xfc, 0x5c, 0x7d, 0xff, 0x5f, 0x6a, 0x09, + 0x72, 0xdd, 0x8f, 0x1d, 0x3e, 0xa4, 0x9c, 0xad, 0xce, 0x2b, 0x2a, 0x42, 0x18, 0x2a, 0x55, 0xc8, + 0xe5, 0xbd, 0x2d, 0xcd, 0x3d, 0xf5, 0xaf, 0x25, 0x55, 0xe2, 0xce, 0xb2, 0xd2, 0xce, 0x52, 0x01, + 0xc9, 0xd2, 0xfb, 0xb6, 0x69, 0x04, 0x0f, 0x55, 0xe5, 0xd2, 0x9e, 0xb6, 0x17, 0xcf, 0xc4, 0xc7, + 0x8f, 0x40, 0x5f, 0x30, 0x73, 0x81, 0x3d, 0xa1, 0xf4, 0x70, 0x4a, 0x94, 0xf8, 0xfb, 0xbe, 0xbe, + 0x71, 0x7d, 0x80, 0x74, 0x06, 0x22, 0x1d, 0x83, 0x56, 0xf7, 0x3d, 0xbb, 0xbd, 0x71, 0x97, 0x8c, + 0x52, 0x92, 0x93, 0xd0, 0x06, 0x3c, 0x22, 0x18, 0xc1, 0xba, 0x0f, 0x42, 0x2e, 0x6f, 0xe1, 0x72, + 0xa0, 0xed, 0xcf, 0x5d, 0xea, 0xa2, 0x2e, 0x7d, 0xb2, 0x09, 0x03, 0xa4, 0x6f, 0x7c, 0x70, 0x4f, + 0xdf, 0x51, 0x0e, 0x03, 0x84, 0xba, 0x63, 0x3b, 0xb1, 0xa9, 0x97, 0x84, 0x6b, 0x06, 0x9a, 0xdb, + 0x9a, 0x3a, 0x46, 0x8b, 0xee, 0x1f, 0xe9, 0xef, 0xeb, 0x7b, 0x75, 0xfd, 0x8a, 0x60, 0xaf, 0x0f, + 0x2e, 0x73, 0x39, 0x94, 0xb4, 0x19, 0xd3, 0xa1, 0x7b, 0x63, 0x80, 0x9b, 0x9f, 0x10, 0xd2, 0x21, + 0xe0, 0xf3, 0x77, 0x7b, 0x91, 0xd7, 0x7d, 0xf3, 0x86, 0xf5, 0xb3, 0xad, 0x2c, 0xb1, 0x62, 0x65, + 0x61, 0x36, 0x7e, 0xec, 0x18, 0xe5, 0x53, 0x28, 0xd7, 0xae, 0x5c, 0xa6, 0x22, 0x78, 0x83, 0x65, + 0xb4, 0xe9, 0xae, 0x04, 0x7a, 0xfc, 0x02, 0x3f, 0x1f, 0xf5, 0xe1, 0x4e, 0xd1, 0x2f, 0x5b, 0x1a, + 0xee, 0x1b, 0xe2, 0x36, 0xd9, 0x38, 0x50, 0xe9, 0xae, 0x4a, 0x69, 0x89, 0x60, 0xfe, 0xdc, 0xd9, + 0xa8, 0x22, 0x1c, 0x89, 0x8c, 0x5a, 0xdd, 0x07, 0xde, 0xb7, 0xf4, 0x95, 0xc7, 0x8f, 0xe5, 0x0e, + 0x5e, 0x54, 0x06, 0x05, 0x5e, 0xa9, 0x3a, 0x77, 0x5a, 0xdf, 0xd1, 0x7d, 0x06, 0x5a, 0x77, 0x2a, + 0x18, 0xcd, 0xba, 0x63, 0xb4, 0x64, 0xa4, 0xf3, 0x7d, 0xbc, 0x70, 0xc6, 0x67, 0x3b, 0x3b, 0x55, + 0xc7, 0x92, 0xdd, 0xc6, 0x91, 0x52, 0x68, 0xdd, 0xa9, 0x60, 0xf4, 0xeb, 0x0e, 0x74, 0x96, 0xf0, + 0x4b, 0x02, 0xf1, 0x59, 0xcc, 0x0b, 0xdc, 0x56, 0x35, 0xdc, 0xbb, 0xa3, 0xef, 0xd0, 0x86, 0x84, + 0xd6, 0x9d, 0x0a, 0x8c, 0x42, 0x77, 0xa0, 0xad, 0x20, 0x8f, 0xe7, 0xb5, 0x13, 0x67, 0x3c, 0x9c, + 0x03, 0x5d, 0x15, 0x22, 0x7d, 0x87, 0x46, 0x0c, 0x91, 0xee, 0x53, 0x69, 0xdd, 0x49, 0x62, 0x2c, + 0xba, 0x03, 0xb5, 0x17, 0x7f, 0xcd, 0x5a, 0xbc, 0x50, 0x55, 0x20, 0xee, 0x5c, 0x5b, 0xf1, 0x89, + 0xa3, 0xfa, 0x8e, 0x8b, 0x18, 0x5a, 0x77, 0x2a, 0x30, 0x22, 0xdd, 0x7b, 0x9b, 0xdf, 0x0a, 0xd9, + 0x41, 0xb8, 0x06, 0x5e, 0xb1, 0x0d, 0xb9, 0x81, 0x6d, 0x0e, 0x81, 0x01, 0x1d, 0x30, 0xdc, 0x90, + 0x43, 0xc7, 0xba, 0xa7, 0xa5, 0xa6, 0xec, 0xf6, 0xda, 0xc9, 0x0e, 0x66, 0x92, 0x39, 0xa2, 0xa1, + 0x61, 0x44, 0xba, 0x03, 0xe2, 0xc8, 0xc3, 0xe9, 0xb3, 0x67, 0x0d, 0xee, 0xc1, 0xaf, 0x6c, 0x4e, + 0xd3, 0x7e, 0x9a, 0x2e, 0x75, 0xe8, 0x57, 0x77, 0x2c, 0xef, 0x95, 0xaf, 0xb7, 0x27, 0xfc, 0x09, + 0xd2, 0x93, 0x39, 0xa8, 0x41, 0x61, 0x5c, 0xba, 0xbf, 0xfe, 0x3d, 0x21, 0x7b, 0x89, 0xd3, 0xa0, + 0x4b, 0x34, 0x4b, 0x9c, 0x14, 0x53, 0x0c, 0x0c, 0x0f, 0x3d, 0xea, 0x2e, 0x93, 0xc9, 0x66, 0x98, + 0x4f, 0x99, 0x6a, 0x32, 0x49, 0x2e, 0x97, 0xc7, 0xc7, 0x5d, 0xf8, 0xfa, 0xab, 0x2f, 0x62, 0xa3, + 0x0d, 0x6e, 0x3f, 0x30, 0xed, 0x30, 0x2e, 0xdd, 0xdf, 0xa6, 0x24, 0xe5, 0xaf, 0xf8, 0x51, 0xd5, + 0xa1, 0xcc, 0xf9, 0x76, 0xd5, 0x31, 0x64, 0x97, 0x51, 0x53, 0x41, 0x7b, 0x51, 0x61, 0xb1, 0xd7, + 0x8e, 0x41, 0x77, 0x85, 0x2d, 0xa7, 0x91, 0x4c, 0x12, 0xaf, 0xa1, 0xee, 0x5b, 0x37, 0x6d, 0x00, + 0xc5, 0x0b, 0x5f, 0xbe, 0xc0, 0x9e, 0x6e, 0xde, 0xb0, 0x1e, 0x9e, 0xa6, 0xa7, 0xa5, 0x69, 0x7e, + 0x14, 0x8f, 0xad, 0x9b, 0xb5, 0x0e, 0x92, 0x52, 0x8c, 0x4b, 0xf7, 0xd6, 0x82, 0xbc, 0xc2, 0x6d, + 0x9b, 0x54, 0x1d, 0xe2, 0x58, 0x5b, 0x95, 0x1f, 0x39, 0xa0, 0xef, 0xb8, 0x08, 0x68, 0xc9, 0xe4, + 0x3e, 0x73, 0x77, 0x1b, 0x34, 0xb0, 0x9e, 0x63, 0x53, 0x19, 0xa5, 0xcd, 0xb2, 0x57, 0x25, 0x9a, + 0xe8, 0x7e, 0x3e, 0x26, 0x1a, 0xe4, 0x8e, 0x39, 0x7b, 0x46, 0xb9, 0xae, 0xbc, 0x5b, 0x22, 0x31, + 0x37, 0x65, 0x7c, 0xfb, 0xdf, 0xaf, 0xea, 0x5f, 0x7f, 0x98, 0x7f, 0x21, 0x14, 0x96, 0x95, 0x08, + 0xf8, 0x79, 0xb9, 0x39, 0xd0, 0xf6, 0x17, 0xf3, 0x78, 0xfd, 0xfd, 0xfd, 0x8f, 0x1e, 0x3e, 0x88, + 0x3b, 0x1f, 0x53, 0x5b, 0x5b, 0xa3, 0x88, 0xbc, 0xb9, 0xf9, 0xc9, 0xe3, 0xbf, 0xe1, 0x01, 0x7c, + 0x46, 0x58, 0x56, 0x9a, 0x93, 0x9d, 0x75, 0x21, 0x36, 0x3a, 0xeb, 0x69, 0x26, 0x99, 0xc8, 0x51, + 0x61, 0x5c, 0xba, 0x4b, 0x1b, 0xde, 0x94, 0xb1, 0x98, 0xb8, 0xd1, 0x2a, 0x8c, 0x5f, 0x7b, 0x5b, + 0x9a, 0xf5, 0x1d, 0x1a, 0x9e, 0xca, 0x53, 0x27, 0x32, 0xec, 0x6c, 0x55, 0xe3, 0x84, 0xdf, 0xa5, + 0xc6, 0x47, 0x0f, 0xc9, 0xd4, 0xf9, 0x59, 0xdd, 0x9f, 0x15, 0xe4, 0x83, 0xeb, 0x0e, 0x76, 0xb6, + 0x61, 0x6c, 0x96, 0x6a, 0x09, 0xf4, 0xf7, 0x83, 0xd7, 0xad, 0x2c, 0xcc, 0xb0, 0x3d, 0x25, 0xa1, + 0x6f, 0xb3, 0xc8, 0xc1, 0x1e, 0xfb, 0x2b, 0xcb, 0x97, 0xb9, 0x44, 0x84, 0xb2, 0xb1, 0xc7, 0x93, + 0xbe, 0x1b, 0x57, 0x29, 0x16, 0x43, 0x25, 0x73, 0x6d, 0x66, 0xc2, 0xd3, 0x93, 0xc7, 0x23, 0x57, + 0xfc, 0xb8, 0x14, 0x7b, 0xeb, 0x54, 0xd4, 0xf1, 0x5d, 0x9e, 0x3b, 0xc8, 0x04, 0x8f, 0x04, 0xe3, + 0xd2, 0x1d, 0xa8, 0x38, 0xbc, 0x1f, 0xb7, 0x7c, 0x26, 0x77, 0x99, 0x4b, 0xfd, 0xad, 0x9b, 0xfa, + 0x8e, 0x6b, 0x10, 0xdd, 0x35, 0xd5, 0xa5, 0x41, 0xfb, 0x70, 0xa7, 0x65, 0x69, 0x70, 0x40, 0x77, + 0x5d, 0x2d, 0x99, 0x6a, 0x3f, 0xad, 0xbb, 0x44, 0xd2, 0x35, 0xf9, 0xfb, 0x6f, 0x27, 0x8e, 0xff, + 0xa6, 0xb3, 0xb3, 0x53, 0xfd, 0xdd, 0xab, 0x97, 0x2f, 0x81, 0xf1, 0x6b, 0x56, 0xad, 0x18, 0x78, + 0xaf, 0xfb, 0x5e, 0x9f, 0x5d, 0xd8, 0xeb, 0xbf, 0x6c, 0xd9, 0x74, 0xfd, 0xea, 0x87, 0x79, 0x66, + 0xd0, 0xe3, 0x87, 0xe6, 0x5c, 0x55, 0x77, 0x65, 0xba, 0x66, 0xf8, 0x0c, 0x7c, 0x92, 0x4c, 0xf0, + 0x48, 0x30, 0x3a, 0xdd, 0x3b, 0x8a, 0x79, 0x7c, 0x5f, 0x6f, 0xfc, 0xe5, 0xc8, 0xe0, 0xc0, 0xee, + 0xea, 0x2a, 0x7d, 0x87, 0xf6, 0x2f, 0x70, 0x4e, 0xa6, 0xdb, 0x58, 0xab, 0x46, 0xf8, 0xd4, 0xd1, + 0xa1, 0xe6, 0x7c, 0x34, 0xc9, 0x6a, 0x3f, 0xad, 0x3b, 0xb4, 0xc4, 0x20, 0x74, 0x6a, 0x4a, 0xf2, + 0x50, 0x1f, 0x80, 0xe6, 0x19, 0x3e, 0x10, 0xce, 0x0e, 0xa1, 0x75, 0x1f, 0x49, 0x54, 0xc7, 0x9e, + 0xcb, 0x72, 0x72, 0x54, 0x95, 0x29, 0xc5, 0xdc, 0x4c, 0x14, 0xc6, 0x92, 0x75, 0x76, 0xe8, 0x3b, + 0x34, 0x05, 0xb5, 0xf1, 0x71, 0xf0, 0x83, 0xa3, 0x1a, 0x5e, 0xb2, 0x09, 0x43, 0x08, 0xe1, 0x75, + 0xb4, 0x93, 0xac, 0x99, 0xbe, 0xcd, 0x64, 0x8c, 0xba, 0x2b, 0xb2, 0x3c, 0x47, 0xb0, 0x71, 0xd9, + 0x26, 0x52, 0xa7, 0x99, 0x8b, 0xc2, 0x43, 0x14, 0x3b, 0x3d, 0xe8, 0x95, 0x9a, 0xb8, 0xd8, 0xbc, + 0x9f, 0x96, 0xe1, 0x7e, 0x7c, 0x04, 0xfe, 0xbe, 0x1d, 0xc5, 0x08, 0x96, 0xdb, 0xd1, 0xba, 0x1b, + 0xa3, 0xee, 0x80, 0x5c, 0x22, 0x01, 0xb9, 0x41, 0x71, 0x55, 0xab, 0x92, 0x26, 0x4f, 0x2a, 0x65, + 0xee, 0x6b, 0x7f, 0xf9, 0x5c, 0x2f, 0x21, 0xc9, 0x3a, 0x3b, 0xcb, 0xf7, 0x87, 0x72, 0xe7, 0xe2, + 0x77, 0xa4, 0xe0, 0xfb, 0x78, 0xb7, 0x21, 0xca, 0xfa, 0x42, 0xeb, 0x6e, 0xa4, 0xba, 0x03, 0xfd, + 0xbd, 0xd2, 0xf2, 0x83, 0xe1, 0x19, 0x76, 0x73, 0x70, 0x7a, 0xe5, 0xb8, 0x38, 0x57, 0x1c, 0x3d, + 0x28, 0xeb, 0xea, 0xd2, 0x65, 0x30, 0x8d, 0x0f, 0x12, 0x05, 0x7b, 0x77, 0x27, 0x31, 0x26, 0xe2, + 0x4e, 0xbf, 0xb2, 0xe0, 0x80, 0xce, 0xb2, 0x52, 0x54, 0x47, 0xa1, 0x75, 0x37, 0x5e, 0xdd, 0x31, + 0x5e, 0x5d, 0xbb, 0x5c, 0xb8, 0x75, 0x13, 0xce, 0x78, 0x28, 0x2f, 0xb7, 0x6c, 0xa8, 0x3a, 0x1d, + 0x25, 0x97, 0x50, 0x2e, 0xfd, 0xdb, 0x94, 0x24, 0x70, 0x3a, 0x75, 0xda, 0x54, 0x5c, 0x00, 0xdc, + 0x39, 0xb3, 0x45, 0xfb, 0x43, 0xfb, 0xda, 0xc9, 0x6e, 0x25, 0x17, 0x1f, 0x77, 0xe1, 0xf4, 0xc9, + 0x13, 0x58, 0x09, 0x09, 0x0a, 0x34, 0x37, 0x65, 0x28, 0x9f, 0x42, 0xd1, 0xfc, 0xe6, 0xd1, 0xe8, + 0xc0, 0xd8, 0x75, 0x07, 0xa4, 0x6f, 0xea, 0x45, 0x61, 0x21, 0x5c, 0x5b, 0x1b, 0x75, 0xe9, 0x61, + 0xc8, 0x08, 0x7d, 0x9e, 0x0e, 0x3e, 0x0f, 0xf9, 0x41, 0xfb, 0xda, 0x5a, 0xe1, 0x74, 0x2a, 0xde, + 0xb5, 0x53, 0x7d, 0x4d, 0x6d, 0x92, 0x09, 0x43, 0xe0, 0xe7, 0xdb, 0xf8, 0x37, 0xa9, 0x4b, 0xec, + 0x4a, 0x24, 0x92, 0x2e, 0x2b, 0x0b, 0x33, 0xc2, 0xa5, 0xd9, 0xfe, 0x7b, 0x7c, 0x90, 0x1c, 0x62, + 0x04, 0x41, 0xeb, 0xfe, 0x81, 0xb6, 0xe7, 0xcf, 0x84, 0x21, 0x4c, 0x8e, 0x95, 0xa5, 0xba, 0xf4, + 0xe0, 0xdf, 0xf3, 0xb5, 0x6e, 0x30, 0xba, 0x85, 0x96, 0x58, 0xde, 0xd3, 0x4d, 0xe6, 0x28, 0x9d, + 0x65, 0x25, 0x95, 0x27, 0x8e, 0x09, 0xf6, 0xfa, 0x70, 0x66, 0x12, 0x1d, 0x68, 0xf2, 0xa4, 0x22, + 0x8f, 0x6d, 0x35, 0x17, 0x62, 0x06, 0xe4, 0xda, 0xec, 0x91, 0x3d, 0x14, 0x72, 0xb9, 0xdc, 0xd1, + 0xde, 0x0e, 0xe7, 0xfa, 0xf1, 0xa3, 0x47, 0x10, 0x1e, 0x62, 0xa4, 0x40, 0xeb, 0x3e, 0x08, 0xe8, + 0x28, 0x8b, 0xc2, 0x58, 0x59, 0x6a, 0x1b, 0x93, 0xff, 0x3b, 0x71, 0x65, 0xba, 0x45, 0x81, 0xdb, + 0x2a, 0x38, 0x31, 0xc4, 0xc7, 0x0e, 0x35, 0x3e, 0xbc, 0xd7, 0x25, 0x2a, 0x1b, 0xea, 0xf2, 0x65, + 0xbf, 0x54, 0xda, 0x5d, 0x57, 0xdb, 0x92, 0xc9, 0xad, 0x8e, 0x39, 0x2b, 0x0c, 0x0f, 0xe1, 0x79, + 0x79, 0x64, 0xd8, 0xcf, 0x1d, 0xaa, 0xda, 0xd4, 0xe9, 0xe6, 0x25, 0xfb, 0xf6, 0xbc, 0xfe, 0xfd, + 0x3a, 0x75, 0x5b, 0x5e, 0xba, 0xbb, 0xae, 0x54, 0xba, 0xae, 0xbc, 0x52, 0x6e, 0x6c, 0xd0, 0xba, + 0x13, 0x20, 0xef, 0xe9, 0xa9, 0xbb, 0x14, 0x5f, 0x1a, 0xb8, 0x8f, 0x3b, 0x87, 0xa0, 0x87, 0xa3, + 0x5e, 0x52, 0xa6, 0x9a, 0x72, 0x66, 0x59, 0x41, 0x6f, 0x1b, 0x84, 0xce, 0xb0, 0xb3, 0x4d, 0xb7, + 0xb1, 0x06, 0x7d, 0x35, 0xfc, 0x8b, 0x30, 0x72, 0x28, 0x3f, 0x18, 0xd1, 0x5d, 0x53, 0xad, 0x83, + 0xef, 0xe5, 0xeb, 0xed, 0x09, 0xae, 0x27, 0x3f, 0xf9, 0x47, 0x07, 0xc7, 0x32, 0x4c, 0x68, 0xdd, + 0x3f, 0x85, 0x5c, 0xda, 0xd3, 0x70, 0x3f, 0x11, 0xda, 0xfb, 0x17, 0x1b, 0xd6, 0xa5, 0x5a, 0xe0, + 0x47, 0x93, 0xda, 0x95, 0x24, 0xc6, 0xc4, 0x6c, 0x67, 0x27, 0x01, 0xd3, 0xff, 0x79, 0x04, 0xfb, + 0xaf, 0x0b, 0xe7, 0xaf, 0x5c, 0xbe, 0xa4, 0xcb, 0xf2, 0xdb, 0xaf, 0x71, 0x24, 0x6b, 0x48, 0x25, + 0xb1, 0x8b, 0xb7, 0xde, 0xa1, 0x75, 0xd7, 0x14, 0xc5, 0x1e, 0x7d, 0xcf, 0x0b, 0x2a, 0x4f, 0x47, + 0x09, 0x43, 0x59, 0x3c, 0xef, 0x1d, 0x39, 0x2e, 0xce, 0xd0, 0xff, 0xc6, 0x5d, 0x3a, 0x24, 0x68, + 0xbf, 0xcd, 0xcd, 0x9e, 0x2e, 0xb0, 0x7f, 0xb9, 0x71, 0x7d, 0x59, 0x30, 0xb3, 0xfc, 0x40, 0x58, + 0xd3, 0xe3, 0x47, 0xbd, 0x6f, 0x9b, 0xf4, 0xfd, 0x55, 0x8c, 0x17, 0x5a, 0x77, 0x52, 0xf4, 0xf7, + 0xf5, 0x49, 0xdf, 0xd4, 0x77, 0x08, 0xf8, 0x6d, 0x2f, 0x9e, 0xbd, 0xcb, 0xc9, 0x6a, 0xe6, 0x72, + 0x5a, 0x9e, 0x66, 0xb4, 0x16, 0xe4, 0x75, 0xf0, 0x8a, 0x24, 0xd5, 0x55, 0x3a, 0xb8, 0x8e, 0x49, + 0x33, 0x2c, 0xfe, 0x0f, 0x35, 0xf0, 0x0d, 0xe1, 0x1b, 0xb4, 0xe3, 0xaf, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE tune_single_track_length_legend_xpm[1] = {{ png, sizeof( png ), "tune_single_track_length_legend_xpm" }}; + +//EOF diff --git a/bitmaps_png/sources/tune_diff_pair_length_legend.png b/bitmaps_png/sources/tune_diff_pair_length_legend.png new file mode 100644 index 0000000000..8eee166a7c Binary files /dev/null and b/bitmaps_png/sources/tune_diff_pair_length_legend.png differ diff --git a/bitmaps_png/sources/tune_diff_pair_skew_legend.png b/bitmaps_png/sources/tune_diff_pair_skew_legend.png new file mode 100644 index 0000000000..0ff1de2da5 Binary files /dev/null and b/bitmaps_png/sources/tune_diff_pair_skew_legend.png differ diff --git a/bitmaps_png/sources/tune_single_track_length_legend.png b/bitmaps_png/sources/tune_single_track_length_legend.png new file mode 100644 index 0000000000..7530620c0c Binary files /dev/null and b/bitmaps_png/sources/tune_single_track_length_legend.png differ diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 1b6a86d83d..509d99ba14 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -204,7 +204,8 @@ set( COMMON_SRCS wildcards_and_files_ext.cpp worksheet.cpp wxwineda.cpp - wxunittext.cpp + wx_unit_binder.cpp + wx_status_popup.cpp xnode.cpp zoom.cpp ) diff --git a/common/geometry/seg.cpp b/common/geometry/seg.cpp index daff746f98..ceb279d489 100644 --- a/common/geometry/seg.cpp +++ b/common/geometry/seg.cpp @@ -35,6 +35,7 @@ bool SEG::PointCloserThan( const VECTOR2I& aP, int aDist ) const { VECTOR2I d = B - A; ecoord dist_sq = (ecoord) aDist * aDist; + ecoord dist_sq_thr = (ecoord) ( aDist + 1 ) * ( aDist + 1 ); SEG::ecoord l_squared = d.Dot( d ); SEG::ecoord t = d.Dot( aP - A ); @@ -60,6 +61,7 @@ bool SEG::PointCloserThan( const VECTOR2I& aP, int aDist ) const if( num > ( dist_sq + 100 ) ) return false; + else if( num < ( dist_sq - 100 ) ) return true; } diff --git a/common/view/view.cpp b/common/view/view.cpp index 981c8b0d31..2186c87264 100644 --- a/common/view/view.cpp +++ b/common/view/view.cpp @@ -564,7 +564,7 @@ struct VIEW::drawItem bool operator()( VIEW_ITEM* aItem ) { // Conditions that have te be fulfilled for an item to be drawn - bool drawCondition = aItem->ViewIsVisible() && + bool drawCondition = aItem->isRenderable() && aItem->ViewGetLOD( layer ) < view->m_scale; if( !drawCondition ) return true; diff --git a/common/view/view_item.cpp b/common/view/view_item.cpp index 900e030e61..f1713f3f0f 100644 --- a/common/view/view_item.cpp +++ b/common/view/view_item.cpp @@ -29,17 +29,6 @@ using namespace KIGFX; -void VIEW_ITEM::ViewSetVisible( bool aIsVisible ) -{ - // update only if the visibility has really changed - if( m_visible != aIsVisible ) - { - m_visible = aIsVisible; - ViewUpdate( APPEARANCE ); - } -} - - void VIEW_ITEM::ViewRelease() { if( m_view && m_view->IsDynamic() ) diff --git a/common/wx_status_popup.cpp b/common/wx_status_popup.cpp new file mode 100644 index 0000000000..9679b3e0da --- /dev/null +++ b/common/wx_status_popup.cpp @@ -0,0 +1,65 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * Transient mouse following popup window implementation. + */ + +#include +#include + +WX_STATUS_POPUP::WX_STATUS_POPUP( PCB_EDIT_FRAME* aParent ) : + wxPopupWindow( aParent ) +{ + m_panel = new wxPanel( this, wxID_ANY ); + m_panel->SetBackgroundColour( *wxLIGHT_GREY ); + + m_topSizer = new wxBoxSizer( wxVERTICAL ); + m_panel->SetSizer( m_topSizer ); +} + + +void WX_STATUS_POPUP::updateSize() +{ + m_topSizer->Fit( m_panel ); + SetClientSize( m_panel->GetSize() ); +} + + +WX_STATUS_POPUP::~WX_STATUS_POPUP() +{ +} + + +void WX_STATUS_POPUP::Popup( wxWindow* ) +{ + Show( true ); + Raise(); +} + + +void WX_STATUS_POPUP::Move( const wxPoint& aWhere ) +{ + SetPosition ( aWhere ); +} diff --git a/common/wx_unit_binder.cpp b/common/wx_unit_binder.cpp new file mode 100644 index 0000000000..90a66ae12e --- /dev/null +++ b/common/wx_unit_binder.cpp @@ -0,0 +1,76 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#if wxCHECK_VERSION( 2, 9, 0 ) +#include +#endif + +#include + +#include "wx_unit_binder.h" + +WX_UNIT_BINDER::WX_UNIT_BINDER( wxWindow* aParent, wxTextCtrl* aTextInput, wxStaticText* aUnitLabel, wxSpinButton* aSpinButton ) +{ + // Use the currently selected units + m_units = g_UserUnit; + m_textCtrl = aTextInput; + m_textCtrl->SetValue( wxT( "0" ) ); + m_unitLabel = aUnitLabel; + m_unitLabel->SetLabel( GetAbbreviatedUnitsLabel( m_units ) ); +} + + +WX_UNIT_BINDER::~WX_UNIT_BINDER() +{ +} + + +void WX_UNIT_BINDER::SetValue( int aValue ) +{ + wxString s = StringFromValue( m_units, aValue, false ); + + m_textCtrl->SetValue( s ); + + m_unitLabel->SetLabel( GetAbbreviatedUnitsLabel( m_units ) ); +} + + +int WX_UNIT_BINDER::GetValue() const +{ + wxString s = m_textCtrl->GetValue(); + + return ValueFromString( m_units, s ); +} + + +void WX_UNIT_BINDER::Enable( bool aEnable ) +{ + m_textCtrl->Enable( aEnable ); + m_unitLabel->Enable( aEnable ); +} \ No newline at end of file diff --git a/eeschema/class_libentry.cpp b/eeschema/class_libentry.cpp index 9ffe3f3e83..5edc00b425 100644 --- a/eeschema/class_libentry.cpp +++ b/eeschema/class_libentry.cpp @@ -1697,11 +1697,11 @@ LIB_ALIAS* LIB_PART::RemoveAlias( LIB_ALIAS* aAlias ) bool rename = aAlias->IsRoot(); wxLogTrace( traceSchLibMem, - wxT( "%s: part:'%s', alias:'%s', alias count %llu, reference count %ld." ), + wxT( "%s: part:'%s', alias:'%s', alias count %u, reference count %d." ), GetChars( wxString::FromAscii( __WXFUNCTION__ ) ), GetChars( m_name ), GetChars( aAlias->GetName() ), - (long long unsigned) m_aliases.size(), + m_aliases.size(), m_me.use_count() ); it = m_aliases.erase( it ); diff --git a/include/base_struct.h b/include/base_struct.h index 18283dbac4..647f77f792 100644 --- a/include/base_struct.h +++ b/include/base_struct.h @@ -146,6 +146,9 @@ public: #define HIGHLIGHTED (1 << 25) ///< item is drawn in normal colors, when the rest is darkened #define BRIGHTENED (1 << 26) ///< item is drawn with a bright contour +#define DP_COUPLED (1 << 27) ///< item is coupled with another item making a differential pair + ///< (applies to segments only) + #define EDA_ITEM_ALL_FLAGS -1 typedef unsigned STATUS_FLAGS; diff --git a/include/bitmaps.h b/include/bitmaps.h index bb77f1cf27..d1ffd402c3 100644 --- a/include/bitmaps.h +++ b/include/bitmaps.h @@ -546,5 +546,8 @@ EXTERN_BITMAP( zoom_out_xpm ) EXTERN_BITMAP( zoom_page_xpm ) EXTERN_BITMAP( zoom_selection_xpm ) EXTERN_BITMAP( zoom_xpm ) +EXTERN_BITMAP( tune_diff_pair_length_legend_xpm ) +EXTERN_BITMAP( tune_diff_pair_skew_legend_xpm ) +EXTERN_BITMAP( tune_single_track_length_legend_xpm ) #endif // BITMAPS_H_ diff --git a/include/class_collector.h b/include/class_collector.h index 294fa0b3d9..04f7010e9d 100644 --- a/include/class_collector.h +++ b/include/class_collector.h @@ -129,6 +129,23 @@ public: m_List.erase( m_List.begin() + aIndex ); } + /** + * Function Remove + * removes the item aItem (if exists in the collector). + * @param aItem the item to be removed. + */ + void Remove( const EDA_ITEM* aItem ) + { + for( size_t i = 0; i < m_List.size(); i++ ) + { + if( m_List[i] == aItem ) + { + m_List.erase( m_List.begin() + i); + return; + } + } + } + /** * Function operator[int] * is used for read only access and returns the object at \a aIndex. @@ -223,6 +240,22 @@ public: else return false; } + /** + * Function CountType + * counts the number of items matching aType + * @param aType type we are interested in + * @return number of occurences + */ + int CountType( KICAD_T aType ) + { + int cnt = 0; + for( size_t i = 0; i < m_List.size(); i++ ) + { + if( m_List[i]->Type() == aType ) + cnt++; + } + return cnt; + } /** * Function Collect diff --git a/include/gal/opengl/vertex_item.h b/include/gal/opengl/vertex_item.h index 3b56e35a05..c5bf962172 100644 --- a/include/gal/opengl/vertex_item.h +++ b/include/gal/opengl/vertex_item.h @@ -69,7 +69,7 @@ public: /** * Function GetVertices() - * areturn a pointer to the data used by the VERTEX_ITEM. + * Returns pointer to the data used by the VERTEX_ITEM. */ VERTEX* GetVertices() const; diff --git a/include/geometry/seg.h b/include/geometry/seg.h index 43ceec3b41..06adfef52d 100644 --- a/include/geometry/seg.h +++ b/include/geometry/seg.h @@ -206,6 +206,13 @@ public: return sqrt( SquaredDistance( aP ) ); } + void CanonicalCoefs( ecoord& qA, ecoord& qB, ecoord& qC ) const + { + qA = A.y - B.y; + qB = B.x - A.x; + qC = -qA * A.x - qB * A.y; + } + /** * Function Collinear() * @@ -215,9 +222,8 @@ public: */ bool Collinear( const SEG& aSeg ) const { - ecoord qa = A.y - B.y; - ecoord qb = B.x - A.x; - ecoord qc = -qa * A.x - qb * A.y; + ecoord qa, qb, qc; + CanonicalCoefs( qa, qb, qc ); ecoord d1 = std::abs( aSeg.A.x * qa + aSeg.A.y * qb + qc ); ecoord d2 = std::abs( aSeg.B.x * qa + aSeg.B.y * qb + qc ); @@ -225,6 +231,29 @@ public: return ( d1 <= 1 && d2 <= 1 ); } + bool ApproxCollinear( const SEG& aSeg ) const + { + ecoord p, q, r; + CanonicalCoefs( p, q, r ); + + ecoord dist1 = ( p * aSeg.A.x + q * aSeg.A.y + r ) / sqrt( p * p + q * q ); + ecoord dist2 = ( p * aSeg.B.x + q * aSeg.B.y + r ) / sqrt( p * p + q * q ); + + return std::abs( dist1 ) <= 1 && std::abs( dist2 ) <= 1; + } + + bool ApproxParallel ( const SEG& aSeg ) const + { + ecoord p, q, r; + CanonicalCoefs( p, q, r ); + + ecoord dist1 = ( p * aSeg.A.x + q * aSeg.A.y + r ) / sqrt( p * p + q * q ); + ecoord dist2 = ( p * aSeg.B.x + q * aSeg.B.y + r ) / sqrt( p * p + q * q ); + + return std::abs( dist1 - dist2 ) <= 1; + } + + bool Overlaps( const SEG& aSeg ) const { if( aSeg.A == aSeg.B ) // single point corner case @@ -262,6 +291,8 @@ public: return ( A - B ).SquaredEuclideanNorm(); } + ecoord TCoef( const VECTOR2I& aP ) const; + /** * Function Index() * @@ -277,7 +308,11 @@ public: bool PointCloserThan( const VECTOR2I& aP, int aDist ) const; -// friend std::ostream& operator<<( std::ostream& stream, const SEG& aSeg ); + void Reverse() + { + std::swap( A, B ); + } + private: bool ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I &aC ) const; @@ -285,14 +320,21 @@ private: int m_index; }; - inline VECTOR2I SEG::LineProject( const VECTOR2I& aP ) const { - // fixme: numerical errors for large integers - assert( false ); - return VECTOR2I( 0, 0 ); -} + VECTOR2I d = B - A; + ecoord l_squared = d.Dot( d ); + if( l_squared == 0 ) + return A; + + ecoord t = d.Dot( aP - A ); + + int xp = rescale( t, (ecoord)d.x, l_squared ); + int yp = rescale( t, (ecoord)d.y, l_squared ); + + return A + VECTOR2I( xp, yp ); +} inline int SEG::LineDistance( const VECTOR2I& aP, bool aDetermineSide ) const { @@ -305,6 +347,11 @@ inline int SEG::LineDistance( const VECTOR2I& aP, bool aDetermineSide ) const return aDetermineSide ? dist : abs( dist ); } +inline SEG::ecoord SEG::TCoef( const VECTOR2I& aP ) const +{ + VECTOR2I d = B - A; + return d.Dot( aP - A); +} inline const VECTOR2I SEG::NearestPoint( const VECTOR2I& aP ) const { @@ -327,7 +374,6 @@ inline const VECTOR2I SEG::NearestPoint( const VECTOR2I& aP ) const return A + VECTOR2I( xp, yp ); } - inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg ) { aStream << "[ " << aSeg.A << " - " << aSeg.B << " ]"; diff --git a/include/math/box2.h b/include/math/box2.h index 467558e676..5bf6ddd03c 100644 --- a/include/math/box2.h +++ b/include/math/box2.h @@ -30,6 +30,7 @@ #include #include +#include /** * Class BOX2 @@ -467,6 +468,8 @@ public: typedef BOX2 BOX2I; typedef BOX2 BOX2D; +typedef boost::optional OPT_BOX2I; + // FIXME should be removed to avoid multiple typedefs for the same type typedef BOX2D DBOX; diff --git a/include/view/view_item.h b/include/view/view_item.h index b2fc558f94..4e6f97572d 100644 --- a/include/view/view_item.h +++ b/include/view/view_item.h @@ -76,7 +76,16 @@ public: ALL = 0xff }; - VIEW_ITEM() : m_view( NULL ), m_visible( true ), m_requiredUpdate( ALL ), + /** + * Enum VIEW_VISIBILITY_FLAGS. + * Defines the visibility of the item (temporarily hidden, invisible, etc). + */ + enum VIEW_VISIBILITY_FLAGS { + VISIBLE = 0x01, /// Item is visible (in general) + HIDDEN = 0x02 /// Item is temporarily hidden (e.g. being used by a tool). Overrides VISIBLE flag. + }; + + VIEW_ITEM() : m_view( NULL ), m_flags( VISIBLE ), m_requiredUpdate( ALL ), m_groups( NULL ), m_groupsSize( 0 ) {} /** @@ -128,7 +137,39 @@ public: * * @param aIsVisible: whether the item is visible (on all layers), or not. */ - void ViewSetVisible( bool aIsVisible = true ); + void ViewSetVisible( bool aIsVisible = true ) + { + bool cur_visible = m_flags & VISIBLE; + + if( cur_visible != aIsVisible ) + { + if( aIsVisible ) + m_flags |= VISIBLE; + else + m_flags &= ~VISIBLE; + + ViewUpdate( APPEARANCE | COLOR ); + } + } + + /** + * Function ViewHide() + * Temporarily hides the item in the view (e.g. for overlaying) + * + * @param aHide: whether the item is hidden (on all layers), or not. + */ + void ViewHide( bool aHide = true ) + { + if( !( m_flags & VISIBLE ) ) + return; + + if( aHide ) + m_flags |= HIDDEN; + else + m_flags &= ~HIDDEN; + + ViewUpdate( APPEARANCE ); + } /** * Function ViewIsVisible() @@ -139,7 +180,7 @@ public: */ bool ViewIsVisible() const { - return m_visible; + return m_flags & VISIBLE; } /** @@ -201,7 +242,7 @@ protected: } VIEW* m_view; ///< Current dynamic view the item is assigned to. - bool m_visible; ///< Are we visible in the current dynamic VIEW. + int m_flags; ///< Visibility flags int m_requiredUpdate; ///< Flag required for updating ///* Helper for storing cached items group ids @@ -295,6 +336,15 @@ protected: { m_requiredUpdate = NONE; } + + /** + * Function isRenderable() + * Returns if the item should be drawn or not. + */ + bool isRenderable() const + { + return m_flags == VISIBLE; + } }; } // namespace KIGFX diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index 0c54a0b02e..793496888a 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -658,6 +658,8 @@ public: */ void SetFastGrid2(); + void ClearSelection(); + DECLARE_EVENT_TABLE() }; diff --git a/include/wx_status_popup.h b/include/wx_status_popup.h new file mode 100644 index 0000000000..93e8e7f9aa --- /dev/null +++ b/include/wx_status_popup.h @@ -0,0 +1,58 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WX_STATUS_POPUP_H_ +#define __WX_STATUS_POPUP_H_ + + +#include +#include + +class PCB_EDIT_FRAME; + +/** + * Class WX_STATUS_POPUP + * + * A tiny, headerless popup window used to display useful status (e.g. line length + * tuning info) next to the mouse cursor. + */ + +class WX_STATUS_POPUP: public wxPopupWindow +{ +public: + WX_STATUS_POPUP( PCB_EDIT_FRAME* aParent ); + virtual ~WX_STATUS_POPUP(); + + virtual void Popup(wxWindow* aFocus = NULL); + virtual void Move( const wxPoint &aWhere ); + +protected: + + void updateSize(); + + wxPanel* m_panel; + wxBoxSizer* m_topSizer; +}; + +#endif /* __WX_STATUS_POPUP_H_*/ diff --git a/include/wx_unit_binder.h b/include/wx_unit_binder.h new file mode 100644 index 0000000000..be4e39d39d --- /dev/null +++ b/include/wx_unit_binder.h @@ -0,0 +1,91 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WX_UNIT_BINDER_H_ +#define __WX_UNIT_BINDER_H_ + +#include +#include + +class wxTextCtrl; +class wxSpinButton; +class wxStaticText; + +class WX_UNIT_BINDER +{ +public: + + /** + * Constructor. + * @param aParent is the parent window. + * @param aTextInput is the text input widget used to edit the given value. + * @param aUnitLabel is the units label displayed next to the text field. + * @param aSpinButton is an optional spin button (for adjusting the input value) + */ + WX_UNIT_BINDER( wxWindow* aParent, wxTextCtrl* aTextInput, wxStaticText* aUnitLabel, wxSpinButton* aSpinButton = NULL ); + + virtual ~WX_UNIT_BINDER(); + + /** + * Function SetValue + * Sets new value (in Internal Units) for the text field, taking care of units conversion. + * @param aValue is the new value. + */ + virtual void SetValue( int aValue ); + + /** + * Function GetValue + * Returns the current value in Internal Units. + */ + virtual int GetValue() const; + + /** + * Function Enable + * Enables/diasables the binded widgets + */ + void Enable( bool aEnable ); + +protected: + + void onTextChanged( wxEvent& aEvent ); + + ///> Text input control. + wxTextCtrl* m_textCtrl; + + ///> Label showing currently used units. + wxStaticText* m_unitLabel; + + ///> Currently used units. + EDA_UNITS_T m_units; + + ///> Step size (added/subtracted difference if spin buttons are used). + int m_step; + int m_min; + int m_max; + + ///> Default value (or non-specified) + static const wxString DEFAULT_VALUE; +}; + +#endif /* __WX_UNIT_BINDER_H_ */ diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 8c021667cc..62bb9d1c1a 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -108,6 +108,10 @@ set( PCBNEW_DIALOGS dialogs/dialog_pns_settings_base.cpp dialogs/dialog_move_exact.cpp dialogs/dialog_move_exact_base.cpp + dialogs/dialog_pns_diff_pair_dimensions.cpp + dialogs/dialog_pns_diff_pair_dimensions_base.cpp + dialogs/dialog_pns_length_tuning_settings.cpp + dialogs/dialog_pns_length_tuning_settings_base.cpp dialogs/dialog_non_copper_zones_properties.cpp dialogs/dialog_non_copper_zones_properties_base.cpp dialogs/dialog_pad_properties.cpp @@ -279,6 +283,8 @@ set( PCBNEW_CLASS_SRCS tools/module_tools.cpp tools/placement_tool.cpp tools/common_actions.cpp + tools/grid_helper.cpp + tools/tools_common.cpp ) set( PCBNEW_SRCS ${PCBNEW_AUTOROUTER_SRCS} ${PCBNEW_CLASS_SRCS} ${PCBNEW_DIALOGS} ) diff --git a/pcbnew/board_undo_redo.cpp b/pcbnew/board_undo_redo.cpp index 6bef956fc1..c999b37da7 100644 --- a/pcbnew/board_undo_redo.cpp +++ b/pcbnew/board_undo_redo.cpp @@ -387,7 +387,7 @@ void PCB_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsList, BOARD_ITEM* item = (BOARD_ITEM*) commandToUndo->GetPickedItem( ii ); // For texts belonging to modules, we need to save state of the parent module - if( item->Type() == PCB_MODULE_TEXT_T ) + if( item->Type() == PCB_MODULE_TEXT_T || item->Type() == PCB_PAD_T ) { item = item->GetParent(); wxASSERT( item->Type() == PCB_MODULE_T ); diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index 10920dd1a6..23d8bcff8d 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -69,6 +69,11 @@ public: /// skip the linked list stuff, and parent const DRAWSEGMENT& operator = ( const DRAWSEGMENT& rhs ); + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && PCB_LINE_T == aItem->Type(); + } + void SetWidth( int aWidth ) { m_Width = aWidth; } int GetWidth() const { return m_Width; } diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 5e8fa65d14..58a1d5dddd 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -57,7 +57,7 @@ MODULE::MODULE( BOARD* parent ) : m_Attributs = MOD_DEFAULT; m_Layer = F_Cu; m_Orient = 0; - m_ModuleStatus = 0; + m_ModuleStatus = MODULE_PADS_LOCKED; flag = 0; m_CntRot90 = m_CntRot180 = 0; m_Surface = 0.0; @@ -1241,3 +1241,20 @@ bool MODULE::IncrementReference( bool aFillSequenceGaps ) return success; } + + +double MODULE::PadCoverageRatio() const +{ + double padArea = 0.0; + double moduleArea = GetFootprintRect().GetArea(); + + for( D_PAD* pad = m_Pads; pad; pad = pad->Next() ) + padArea += pad->GetBoundingBox().GetArea(); + + if( moduleArea == 0.0 ) + return 1.0; + + double ratio = padArea / moduleArea; + + return std::min( ratio, 1.0 ); +} diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index d9df61a52e..8503ec5fdc 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -232,6 +232,7 @@ public: #define MODULE_is_LOCKED 0x01 ///< module LOCKED: no autoplace allowed #define MODULE_is_PLACED 0x02 ///< In autoplace: module automatically placed #define MODULE_to_PLACE 0x04 ///< In autoplace: module waiting for autoplace +#define MODULE_PADS_LOCKED 0x08 ///< In autoplace: module waiting for autoplace bool IsLocked() const @@ -270,6 +271,16 @@ public: m_ModuleStatus &= ~MODULE_to_PLACE; } + bool PadsLocked() const { return ( m_ModuleStatus & MODULE_PADS_LOCKED ); } + + void SetPadsLocked( bool aPadsLocked ) + { + if( aPadsLocked ) + m_ModuleStatus |= MODULE_PADS_LOCKED; + else + m_ModuleStatus &= ~MODULE_PADS_LOCKED; + } + void SetLastEditTime( time_t aTime ) { m_LastEditTime = aTime; } void SetLastEditTime( ) { m_LastEditTime = time( NULL ); } time_t GetLastEditTime() const { return m_LastEditTime; } @@ -606,6 +617,14 @@ public: m_initial_comments = aInitialComments; } + /** + * Function PadCoverageRatio + * Calculates the ratio of total area of the footprint pads to the area of the + * footprint. Used by selection tool heuristics. + * @return the ratio + */ + double PadCoverageRatio() const; + /// Return the initial comments block or NULL if none, without transfer of ownership. const wxArrayString* GetInitialComments() const { return m_initial_comments; } diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 0916e9b6ab..9480804bdb 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -177,7 +177,6 @@ const EDA_RECT D_PAD::GetBoundingBox() const area.SetOrigin( m_Pos.x-dx, m_Pos.y-dy ); area.SetSize( 2*dx, 2*dy ); break; - break; case PAD_RECT: //Use two corners and track their rotation diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index c80fce4746..b5a9d0c26b 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -94,6 +94,11 @@ public: ///< used for edge board connectors static LSET UnplatedHoleMask(); ///< layer set for a mechanical unplated through hole pad + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && PCB_PAD_T == aItem->Type(); + } + void Copy( D_PAD* source ); D_PAD* Next() const { return static_cast( Pnext ); } diff --git a/pcbnew/class_pcb_text.h b/pcbnew/class_pcb_text.h index 7a6d853704..e1aeee7e4d 100644 --- a/pcbnew/class_pcb_text.h +++ b/pcbnew/class_pcb_text.h @@ -49,6 +49,11 @@ public: ~TEXTE_PCB(); + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && PCB_TEXT_T == aItem->Type(); + } + virtual const wxPoint& GetPosition() const { return m_Pos; diff --git a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp index f7430f96fb..cf9f15161f 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp +++ b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp @@ -313,12 +313,19 @@ void DIALOG_MODULE_BOARD_EDITOR::InitModeditProperties() break; } - m_AutoPlaceCtrl->SetSelection( (m_CurrentModule->IsLocked()) ? 1 : 0 ); + if( m_CurrentModule->IsLocked() ) + m_AutoPlaceCtrl->SetSelection( 2 ); + else if( m_CurrentModule->PadsLocked() ) + m_AutoPlaceCtrl->SetSelection( 1 ); + else + m_AutoPlaceCtrl->SetSelection( 0 ); m_AutoPlaceCtrl->SetItemToolTip( 0, - _( "Enable hotkey move commands and Auto Placement" ) ); + _( "Component can be freely moved and auto placed. User can arbitrarily select and edit component's pads." ) ); m_AutoPlaceCtrl->SetItemToolTip( 1, - _( "Disable hotkey move commands and Auto Placement" ) ); + _( "Component can be freely moved and auto placed, but its pads cannot be selected or edited." ) ); + m_AutoPlaceCtrl->SetItemToolTip( 2, + _( "Component is locked: it cannot be freely moved or auto placed." ) ); m_CostRot90Ctrl->SetValue( m_CurrentModule->GetPlacementCost90() ); @@ -583,7 +590,8 @@ void DIALOG_MODULE_BOARD_EDITOR::OnOkClick( wxCommandEvent& event ) modpos.x = ValueFromTextCtrl( *m_ModPositionX ); modpos.y = ValueFromTextCtrl( *m_ModPositionY ); m_CurrentModule->SetPosition( modpos ); - m_CurrentModule->SetLocked( m_AutoPlaceCtrl->GetSelection() == 1 ); + m_CurrentModule->SetLocked( m_AutoPlaceCtrl->GetSelection() == 2 ); + m_CurrentModule->SetPadsLocked( m_AutoPlaceCtrl->GetSelection() == 1 ); switch( m_AttributsCtrl->GetSelection() ) { diff --git a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.cpp b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.cpp index d47946603b..f47642935a 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.cpp +++ b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.cpp @@ -143,10 +143,10 @@ DIALOG_MODULE_BOARD_EDITOR_BASE::DIALOG_MODULE_BOARD_EDITOR_BASE( wxWindow* pare m_AttributsCtrl->SetSelection( 0 ); bSizerAttrib->Add( m_AttributsCtrl, 1, wxALL|wxEXPAND, 5 ); - wxString m_AutoPlaceCtrlChoices[] = { _("Free"), _("Locked") }; + wxString m_AutoPlaceCtrlChoices[] = { _("Free"), _("Lock pads"), _("Lock module") }; int m_AutoPlaceCtrlNChoices = sizeof( m_AutoPlaceCtrlChoices ) / sizeof( wxString ); m_AutoPlaceCtrl = new wxRadioBox( m_PanelProperties, wxID_ANY, _("Move and Place"), wxDefaultPosition, wxDefaultSize, m_AutoPlaceCtrlNChoices, m_AutoPlaceCtrlChoices, 1, wxRA_SPECIFY_COLS ); - m_AutoPlaceCtrl->SetSelection( 0 ); + m_AutoPlaceCtrl->SetSelection( 1 ); bSizerAttrib->Add( m_AutoPlaceCtrl, 1, wxALL|wxEXPAND, 5 ); diff --git a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.fbp b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.fbp index 80a6083222..86a87cfef9 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.fbp +++ b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor_base.fbp @@ -2260,7 +2260,7 @@ 1 0 - "Free" "Locked" + "Free" "Lock pads" "Lock module" 1 1 @@ -2292,7 +2292,7 @@ 1 Resizable - 0 + 1 1 wxRA_SPECIFY_COLS diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.cpp b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.cpp new file mode 100644 index 0000000000..6438c6600d --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.cpp @@ -0,0 +1,92 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Push and Shove diff pair dimensions (gap) settings dialog. + */ + +#include "dialog_pns_diff_pair_dimensions.h" +#include + +DIALOG_PNS_DIFF_PAIR_DIMENSIONS::DIALOG_PNS_DIFF_PAIR_DIMENSIONS( wxWindow* aParent, PNS_SIZES_SETTINGS& aSizes ) : + DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE( aParent ), + m_traceWidth( this, m_traceWidthText, m_traceWidthUnit ), + m_traceGap( this, m_traceGapText, m_traceGapUnit ), + m_viaGap( this, m_viaGapText, m_viaGapUnit ), + m_sizes( aSizes ) +{ + m_traceWidth.SetValue( aSizes.DiffPairWidth() ); + m_traceGap.SetValue( aSizes.DiffPairGap() ); + m_viaGap.SetValue( aSizes.DiffPairViaGap() ); + m_viaTraceGapEqual->SetValue( m_sizes.DiffPairViaGapSameAsTraceGap() ); + + updateCheckbox(); +} + + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::updateCheckbox() +{ + if( m_viaTraceGapEqual->GetValue() ) + { + m_sizes.SetDiffPairViaGapSameAsTraceGap( true ); + m_viaGapText->Disable(); + m_viaGapLabel->Disable(); + m_viaGapUnit->Disable(); + } else { + m_sizes.SetDiffPairViaGapSameAsTraceGap( false ); + m_viaGapText->Enable(); + m_viaGapLabel->Enable(); + m_viaGapUnit->Enable(); + } +} + + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnClose( wxCloseEvent& aEvent ) +{ + // Do nothing, it is result of ESC pressing + EndModal( 0 ); +} + + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnOkClick( wxCommandEvent& aEvent ) +{ + // Save widgets' values to settings + m_sizes.SetDiffPairGap ( m_traceGap.GetValue() ); + m_sizes.SetDiffPairViaGap ( m_viaGap.GetValue() ); + m_sizes.SetDiffPairWidth ( m_traceWidth.GetValue() ); + + // todo: verify against design rules + EndModal( 1 ); +} + + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnCancelClick( wxCommandEvent& aEvent ) +{ + // Do nothing + EndModal( 0 ); +} + + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnViaTraceGapEqualCheck( wxCommandEvent& event ) +{ + event.Skip(); + updateCheckbox(); +} + diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.h b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.h new file mode 100644 index 0000000000..c40d9db1e6 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.h @@ -0,0 +1,54 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Push and Shove diff pair dimensions (gap) settings dialog. + */ + +#ifndef __dialog_diff_pair_dimensions_settings__ +#define __dialog_diff_pair_dimensions_settings__ + +#include + +#include "dialog_pns_diff_pair_dimensions_base.h" + +class PNS_SIZES_SETTINGS; + +class DIALOG_PNS_DIFF_PAIR_DIMENSIONS : public DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE +{ +public: + DIALOG_PNS_DIFF_PAIR_DIMENSIONS( wxWindow* aParent, PNS_SIZES_SETTINGS& aSizes ); + + virtual void OnClose( wxCloseEvent& aEvent ); + virtual void OnOkClick( wxCommandEvent& aEvent ); + virtual void OnCancelClick( wxCommandEvent& aEvent ); + virtual void OnViaTraceGapEqualCheck( wxCommandEvent& event ); + +private: + void updateCheckbox(); + + WX_UNIT_BINDER m_traceWidth; + WX_UNIT_BINDER m_traceGap; + WX_UNIT_BINDER m_viaGap; + + PNS_SIZES_SETTINGS& m_sizes; +}; + +#endif // __dialog_pns_settings__ diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.cpp b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.cpp new file mode 100644 index 0000000000..a74a8b0b1b --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.cpp @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_pns_diff_pair_dimensions_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( 400,-1 ), wxDefaultSize ); + + wxBoxSizer* bSizer7; + bSizer7 = new wxBoxSizer( wxVERTICAL ); + + wxFlexGridSizer* fgSizer1; + fgSizer1 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer1->AddGrowableCol( 1 ); + fgSizer1->SetFlexibleDirection( wxBOTH ); + fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_traceWidthLabel = new wxStaticText( this, wxID_ANY, _("Width:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceWidthLabel->Wrap( -1 ); + fgSizer1->Add( m_traceWidthLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_traceWidthText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_traceWidthText, 0, wxALL|wxEXPAND, 5 ); + + m_traceWidthUnit = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceWidthUnit->Wrap( -1 ); + fgSizer1->Add( m_traceWidthUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_traceGapLabel = new wxStaticText( this, wxID_ANY, _("Trace gap:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceGapLabel->Wrap( -1 ); + fgSizer1->Add( m_traceGapLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_traceGapText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_traceGapText, 0, wxALL|wxEXPAND, 5 ); + + m_traceGapUnit = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceGapUnit->Wrap( -1 ); + m_traceGapUnit->SetMaxSize( wxSize( 40,-1 ) ); + + fgSizer1->Add( m_traceGapUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaGapLabel = new wxStaticText( this, wxID_ANY, _("Via gap:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaGapLabel->Wrap( -1 ); + m_viaGapLabel->Enable( false ); + + fgSizer1->Add( m_viaGapLabel, 0, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaGapText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_viaGapText->Enable( false ); + + fgSizer1->Add( m_viaGapText, 0, wxALL|wxEXPAND, 5 ); + + m_viaGapUnit = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaGapUnit->Wrap( -1 ); + m_viaGapUnit->Enable( false ); + m_viaGapUnit->SetMaxSize( wxSize( 40,-1 ) ); + + fgSizer1->Add( m_viaGapUnit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer7->Add( fgSizer1, 0, wxEXPAND, 5 ); + + m_viaTraceGapEqual = new wxCheckBox( this, wxID_ANY, _("Via gap same as trace gap"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaTraceGapEqual->SetValue(true); + bSizer7->Add( m_viaTraceGapEqual, 0, wxALL|wxEXPAND, 5 ); + + m_stdButtons = new wxStdDialogButtonSizer(); + m_stdButtonsOK = new wxButton( this, wxID_OK ); + m_stdButtons->AddButton( m_stdButtonsOK ); + m_stdButtonsCancel = new wxButton( this, wxID_CANCEL ); + m_stdButtons->AddButton( m_stdButtonsCancel ); + m_stdButtons->Realize(); + + bSizer7->Add( m_stdButtons, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer7 ); + this->Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnClose ) ); + m_viaTraceGapEqual->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::onViaTraceGapEqualCheck ), NULL, this ); + m_stdButtonsCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnOkClick ), NULL, this ); +} + +DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::~DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnClose ) ); + m_viaTraceGapEqual->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::onViaTraceGapEqualCheck ), NULL, this ); + m_stdButtonsCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnOkClick ), NULL, this ); + +} diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.fbp b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.fbp new file mode 100644 index 0000000000..d87bb97fa9 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.fbp @@ -0,0 +1,1002 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_pns_diff_pair_dimensions_base + 1000 + none + 1 + DIALOG_PNS_SETTINGS_BASE + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + 400,-1 + DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE + + 400,-1 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Differential Pair Dimensions + + + + + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer7 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 3 + wxBOTH + 1 + + 0 + + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Width: + + 0 + + + 0 + + 1 + m_traceWidthLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_traceWidthText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + + + 0 + + 1 + m_traceWidthUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Trace gap: + + 0 + + + 0 + + 1 + m_traceGapLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_traceGapText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + 40,-1 + + 0 + + 1 + m_traceGapUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + Via gap: + + 0 + + + 0 + + 1 + m_viaGapLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_viaGapText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + 40,-1 + + 0 + + 1 + m_viaGapUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via gap same as trace gap + + 0 + + + 0 + + 1 + m_viaTraceGapEqual + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + OnViaTraceGapEqualCheck + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_stdButtons + protected + + OnCancelClick + + + + OnOkClick + + + + + + + + diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.h b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.h new file mode 100644 index 0000000000..4300d7f697 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE_H__ +#define __DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE_H__ + +#include +#include +#include +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_traceWidthLabel; + wxTextCtrl* m_traceWidthText; + wxStaticText* m_traceWidthUnit; + wxStaticText* m_traceGapLabel; + wxTextCtrl* m_traceGapText; + wxStaticText* m_traceGapUnit; + wxStaticText* m_viaGapLabel; + wxTextCtrl* m_viaGapText; + wxStaticText* m_viaGapUnit; + wxCheckBox* m_viaTraceGapEqual; + wxStdDialogButtonSizer* m_stdButtons; + wxButton* m_stdButtonsOK; + wxButton* m_stdButtonsCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void onViaTraceGapEqualCheck( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Differential Pair Dimensions"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 400,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE(); + +}; + +#endif //__DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE_H__ diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings.cpp b/pcbnew/dialogs/dialog_pns_length_tuning_settings.cpp new file mode 100644 index 0000000000..b817a0b1a4 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings.cpp @@ -0,0 +1,114 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Length tuner settings dialog. + */ + +#include "dialog_pns_length_tuning_settings.h" +#include + +DIALOG_PNS_LENGTH_TUNING_SETTINGS::DIALOG_PNS_LENGTH_TUNING_SETTINGS( wxWindow* aParent, PNS_MEANDER_SETTINGS& aSettings, PNS_ROUTER_MODE aMode ) : + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE( aParent ), + m_minAmpl( this, m_minAmplText, m_minAmplUnit ), + m_maxAmpl( this, m_maxAmplText, m_maxAmplUnit ), + m_spacing( this, m_spacingText, m_spacingUnit ), + m_targetLength( this, m_targetLengthText, m_targetLengthUnit ), + m_settings( aSettings ), + m_mode( aMode ) +{ + m_miterStyle->Enable( false ); + m_radiusText->Enable( aMode != PNS_MODE_TUNE_DIFF_PAIR ); + //m_minAmpl.Enable ( aMode != PNS_MODE_TUNE_DIFF_PAIR_SKEW ); + + m_minAmpl.SetValue( m_settings.m_minAmplitude ); + m_maxAmpl.SetValue( m_settings.m_maxAmplitude ); + + m_spacing.SetValue( m_settings.m_spacing ); + m_radiusText->SetValue( wxString::Format( wxT( "%i" ), m_settings.m_cornerRadiusPercentage ) ); + + m_miterStyle->SetSelection( m_settings.m_cornerType == PNS_MEANDER_SETTINGS::ROUND ? 1 : 0 ); + + switch( aMode ) + { + case PNS_MODE_TUNE_SINGLE: + SetTitle( _( "Single track length tuning" ) ); + m_legend->SetBitmap( KiBitmap( tune_single_track_length_legend_xpm ) ); + m_targetLength.SetValue( m_settings.m_targetLength ); + break; + + case PNS_MODE_TUNE_DIFF_PAIR: + SetTitle( _( "Differential pair length tuning" ) ); + m_legend->SetBitmap( KiBitmap( tune_diff_pair_length_legend_xpm ) ); + m_targetLength.SetValue( m_settings.m_targetLength ); + break; + + case PNS_MODE_TUNE_DIFF_PAIR_SKEW: + SetTitle( _( "Differential pair skew tuning" ) ); + m_legend->SetBitmap( KiBitmap( tune_diff_pair_skew_legend_xpm ) ); + m_targetLengthLabel->SetLabel( _( "Target skew: " ) ); + m_targetLength.SetValue ( m_settings.m_targetSkew ); + break; + + default: + break; + } + + m_stdButtonsOK->SetDefault(); + m_targetLengthText->SetSelection( -1, -1 ); + m_targetLengthText->SetFocus(); +} + + +void DIALOG_PNS_LENGTH_TUNING_SETTINGS::OnClose( wxCloseEvent& aEvent ) +{ + // Do nothing, it is result of ESC pressing + EndModal( 0 ); +} + + +void DIALOG_PNS_LENGTH_TUNING_SETTINGS::OnOkClick( wxCommandEvent& aEvent ) +{ + // fixme: use validators and TransferDataFromWindow + m_settings.m_minAmplitude = m_minAmpl.GetValue(); + m_settings.m_maxAmplitude = m_maxAmpl.GetValue(); + m_settings.m_spacing = m_spacing.GetValue(); + + m_settings.m_cornerRadiusPercentage = wxAtoi( m_radiusText->GetValue() ); + + if( m_mode == PNS_MODE_TUNE_DIFF_PAIR_SKEW ) + m_settings.m_targetSkew = m_targetLength.GetValue(); + else + m_settings.m_targetLength = m_targetLength.GetValue(); + + if( m_settings.m_maxAmplitude < m_settings.m_minAmplitude ) + m_settings.m_maxAmplitude = m_settings.m_maxAmplitude; + + m_settings.m_cornerType = m_miterStyle->GetSelection() ? PNS_MEANDER_SETTINGS::CHAMFER : PNS_MEANDER_SETTINGS::ROUND; + + EndModal( 1 ); +} + + +void DIALOG_PNS_LENGTH_TUNING_SETTINGS::OnCancelClick( wxCommandEvent& aEvent ) +{ + // Do nothing + EndModal( 0 ); +} diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings.h b/pcbnew/dialogs/dialog_pns_length_tuning_settings.h new file mode 100644 index 0000000000..f65d9503bf --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings.h @@ -0,0 +1,55 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014 CERN + * Author: Maciej Suminski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Push and Shove router settings dialog. + */ + +#ifndef __dialog_pns_length_tuning_settings__ +#define __dialog_pns_length_tuning_settings__ + +#include "dialog_pns_length_tuning_settings_base.h" + +#include + +#include + +class PNS_MEANDER_SETTINGS; + +class DIALOG_PNS_LENGTH_TUNING_SETTINGS : public DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE +{ +public: + DIALOG_PNS_LENGTH_TUNING_SETTINGS( wxWindow* aParent, PNS_MEANDER_SETTINGS& aSettings, PNS_ROUTER_MODE aMode ); + + virtual void OnClose( wxCloseEvent& aEvent ); + virtual void OnOkClick( wxCommandEvent& aEvent ); + virtual void OnCancelClick( wxCommandEvent& aEvent ); + +private: + WX_UNIT_BINDER m_minAmpl; + WX_UNIT_BINDER m_maxAmpl; + WX_UNIT_BINDER m_spacing; + WX_UNIT_BINDER m_targetLength; + + PNS_MEANDER_SETTINGS& m_settings; + PNS_ROUTER_MODE m_mode; +}; + +#endif // __dialog_pns_settings__ diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.cpp b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.cpp new file mode 100644 index 0000000000..5cb8aa03da --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.cpp @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_pns_length_tuning_settings_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( 345,668 ), wxDefaultSize ); + + wxBoxSizer* bMainSizer; + bMainSizer = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sbSizer1; + sbSizer1 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Length/skew") ), wxVERTICAL ); + + wxFlexGridSizer* fgSizer4; + fgSizer4 = new wxFlexGridSizer( 0, 2, 0, 0 ); + fgSizer4->AddGrowableCol( 1 ); + fgSizer4->SetFlexibleDirection( wxBOTH ); + fgSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText4 = new wxStaticText( this, wxID_ANY, _("Tune from:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText4->Wrap( -1 ); + fgSizer4->Add( m_staticText4, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxArrayString m_choicePathFromChoices; + m_choicePathFrom = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choicePathFromChoices, 0 ); + m_choicePathFrom->SetSelection( 0 ); + fgSizer4->Add( m_choicePathFrom, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText15 = new wxStaticText( this, wxID_ANY, _("Tune to:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText15->Wrap( -1 ); + fgSizer4->Add( m_staticText15, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxArrayString m_choice4Choices; + m_choice4 = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choice4Choices, 0 ); + m_choice4->SetSelection( 0 ); + fgSizer4->Add( m_choice4, 0, wxALL, 5 ); + + m_staticText3 = new wxStaticText( this, wxID_ANY, _("Constraint:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText3->Wrap( -1 ); + fgSizer4->Add( m_staticText3, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxString m_constraintSourceChoices[] = { _("from Design Rules"), _("manual") }; + int m_constraintSourceNChoices = sizeof( m_constraintSourceChoices ) / sizeof( wxString ); + m_constraintSource = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_constraintSourceNChoices, m_constraintSourceChoices, 0 ); + m_constraintSource->SetSelection( 1 ); + m_constraintSource->Enable( false ); + + fgSizer4->Add( m_constraintSource, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_targetLengthLabel = new wxStaticText( this, wxID_ANY, _("Target length:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_targetLengthLabel->Wrap( -1 ); + fgSizer4->Add( m_targetLengthLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxGridSizer* gSizer2; + gSizer2 = new wxGridSizer( 0, 2, 0, 0 ); + + m_targetLengthText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + gSizer2->Add( m_targetLengthText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_targetLengthUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_targetLengthUnit->Wrap( -1 ); + gSizer2->Add( m_targetLengthUnit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + fgSizer4->Add( gSizer2, 1, wxEXPAND, 5 ); + + + sbSizer1->Add( fgSizer4, 1, wxEXPAND, 5 ); + + + bMainSizer->Add( sbSizer1, 0, wxEXPAND|wxALL, 5 ); + + wxStaticBoxSizer* sbSizer2; + sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Meandering") ), wxVERTICAL ); + + m_legend = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + sbSizer2->Add( m_legend, 1, wxALL|wxEXPAND, 5 ); + + wxFlexGridSizer* fgSizer3; + fgSizer3 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer3->AddGrowableCol( 2 ); + fgSizer3->SetFlexibleDirection( wxBOTH ); + fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText9 = new wxStaticText( this, wxID_ANY, _("Min amplitude (Amin):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText9->Wrap( -1 ); + fgSizer3->Add( m_staticText9, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_minAmplText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_minAmplText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_minAmplUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_minAmplUnit->Wrap( -1 ); + fgSizer3->Add( m_minAmplUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText91 = new wxStaticText( this, wxID_ANY, _("Max amplitude (Amax):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText91->Wrap( -1 ); + fgSizer3->Add( m_staticText91, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_maxAmplText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_maxAmplText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_maxAmplUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_maxAmplUnit->Wrap( -1 ); + fgSizer3->Add( m_maxAmplUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText11 = new wxStaticText( this, wxID_ANY, _("Spacing (s):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText11->Wrap( -1 ); + fgSizer3->Add( m_staticText11, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_spacingText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_spacingText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_spacingUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_spacingUnit->Wrap( -1 ); + fgSizer3->Add( m_spacingUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText13 = new wxStaticText( this, wxID_ANY, _("Miter radius (r):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText13->Wrap( -1 ); + fgSizer3->Add( m_staticText13, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_radiusText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_radiusText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_radiusUnit = new wxStaticText( this, wxID_ANY, _("%"), wxDefaultPosition, wxDefaultSize, 0 ); + m_radiusUnit->Wrap( -1 ); + fgSizer3->Add( m_radiusUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText14 = new wxStaticText( this, wxID_ANY, _("Miter style:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText14->Wrap( -1 ); + m_staticText14->Enable( false ); + + fgSizer3->Add( m_staticText14, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxString m_miterStyleChoices[] = { _("45 degree"), _("arc") }; + int m_miterStyleNChoices = sizeof( m_miterStyleChoices ) / sizeof( wxString ); + m_miterStyle = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_miterStyleNChoices, m_miterStyleChoices, 0 ); + m_miterStyle->SetSelection( 0 ); + m_miterStyle->Enable( false ); + + fgSizer3->Add( m_miterStyle, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + sbSizer2->Add( fgSizer3, 1, wxEXPAND, 5 ); + + + bMainSizer->Add( sbSizer2, 1, wxALL|wxEXPAND, 5 ); + + m_stdButtons = new wxStdDialogButtonSizer(); + m_stdButtonsOK = new wxButton( this, wxID_OK ); + m_stdButtons->AddButton( m_stdButtonsOK ); + m_stdButtonsCancel = new wxButton( this, wxID_CANCEL ); + m_stdButtons->AddButton( m_stdButtonsCancel ); + m_stdButtons->Realize(); + + bMainSizer->Add( m_stdButtons, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 ); + + + this->SetSizer( bMainSizer ); + this->Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnClose ) ); + m_stdButtonsCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnOkClick ), NULL, this ); +} + +DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::~DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnClose ) ); + m_stdButtonsCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnOkClick ), NULL, this ); + +} diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.fbp b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.fbp new file mode 100644 index 0000000000..2727188e68 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.fbp @@ -0,0 +1,2253 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + /home/twl/Kicad-dev/kicad-git/bitmaps_png/cpp_other + UTF-8 + connect + dialog_pns_length_tuning_settings_base + 1000 + none + 1 + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + 345,668 + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE + + 345,668 + wxDEFAULT_DIALOG_STYLE + DIALOG_SHIM; dialog_shim.h + Trace length tuning + + + + + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bMainSizer + wxVERTICAL + none + + 5 + wxEXPAND|wxALL + 0 + + wxID_ANY + Length/skew + + sbSizer1 + wxVERTICAL + none + + + 5 + wxEXPAND + 1 + + 2 + wxBOTH + 1 + + 0 + + fgSizer4 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Tune from: + + 0 + + + 0 + + 1 + m_staticText4 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_choicePathFrom + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Tune to: + + 0 + + + 0 + + 1 + m_staticText15 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_choice4 + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Constraint: + + 0 + + + 0 + + 1 + m_staticText3 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "from Design Rules" "manual" + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_constraintSource + 1 + + + protected + 1 + + Resizable + 1 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Target length: + + 0 + + + 0 + + 1 + m_targetLengthLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 2 + 0 + + gSizer2 + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_targetLengthText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_targetLengthUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + wxID_ANY + Meandering + + sbSizer2 + wxVERTICAL + none + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + Load From Resource; + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_legend + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 3 + wxBOTH + 2 + + 0 + + fgSizer3 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Min amplitude (Amin): + + 0 + + + 0 + + 1 + m_staticText9 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_minAmplText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_minAmplUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Max amplitude (Amax): + + 0 + + + 0 + + 1 + m_staticText91 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_maxAmplText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_maxAmplUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Spacing (s): + + 0 + + + 0 + + 1 + m_staticText11 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_spacingText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_spacingUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Miter radius (r): + + 0 + + + 0 + + 1 + m_staticText13 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_radiusText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + % + + 0 + + + 0 + + 1 + m_radiusUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + Miter style: + + 0 + + + 0 + + 1 + m_staticText14 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "45 degree" "arc" + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_miterStyle + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxTOP|wxBOTTOM + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_stdButtons + protected + + OnCancelClick + + + + OnOkClick + + + + + + + + diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.h b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.h new file mode 100644 index 0000000000..f0e8217ac7 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.h @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE_H__ +#define __DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE_H__ + +#include +#include +#include +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_staticText4; + wxChoice* m_choicePathFrom; + wxStaticText* m_staticText15; + wxChoice* m_choice4; + wxStaticText* m_staticText3; + wxChoice* m_constraintSource; + wxStaticText* m_targetLengthLabel; + wxTextCtrl* m_targetLengthText; + wxStaticText* m_targetLengthUnit; + wxStaticBitmap* m_legend; + wxStaticText* m_staticText9; + wxTextCtrl* m_minAmplText; + wxStaticText* m_minAmplUnit; + wxStaticText* m_staticText91; + wxTextCtrl* m_maxAmplText; + wxStaticText* m_maxAmplUnit; + wxStaticText* m_staticText11; + wxTextCtrl* m_spacingText; + wxStaticText* m_spacingUnit; + wxStaticText* m_staticText13; + wxTextCtrl* m_radiusText; + wxStaticText* m_radiusUnit; + wxStaticText* m_staticText14; + wxChoice* m_miterStyle; + wxStdDialogButtonSizer* m_stdButtons; + wxButton* m_stdButtonsOK; + wxButton* m_stdButtonsCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Trace length tuning"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 345,668 ), long style = wxDEFAULT_DIALOG_STYLE ); + ~DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE(); + +}; + +#endif //__DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE_H__ diff --git a/pcbnew/dialogs/dialog_track_via_size.cpp b/pcbnew/dialogs/dialog_track_via_size.cpp index ab9e1cac37..d8f025bad0 100644 --- a/pcbnew/dialogs/dialog_track_via_size.cpp +++ b/pcbnew/dialogs/dialog_track_via_size.cpp @@ -31,14 +31,19 @@ DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings ) : DIALOG_TRACK_VIA_SIZE_BASE( aParent ), + m_trackWidth( aParent, m_trackWidthText, m_trackWidthLabel ), + m_viaDiameter( aParent, m_viaDiameterText, m_viaDiameterLabel ), + m_viaDrill( aParent, m_viaDrillText, m_viaDrillLabel ), m_settings( aSettings ) { // Load router settings to dialog fields - m_trackWidth->SetValue( To_User_Unit( m_trackWidth->GetUnits(), m_settings.GetCustomTrackWidth() ) ); - m_viaDiameter->SetValue( To_User_Unit( m_viaDiameter->GetUnits(), m_settings.GetCustomViaSize() ) ); - m_viaDrill->SetValue( To_User_Unit( m_viaDrill->GetUnits(), m_settings.GetCustomViaDrill() ) ); + m_trackWidth.SetValue( m_settings.GetCustomTrackWidth() ); + m_viaDiameter.SetValue( m_settings.GetCustomViaSize() ); + m_viaDrill.SetValue( m_settings.GetCustomViaDrill() ); - m_trackWidth->SetFocus(); + m_trackWidthText->SetFocus(); + m_trackWidthText->SetSelection( -1, -1 ); + m_stdButtonsOK->SetDefault(); // Pressing ENTER when any of the text input fields is active applies changes #if wxCHECK_VERSION( 3, 0, 0 ) @@ -46,7 +51,7 @@ DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SE #else Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE::onOkClick ), NULL, this ); #endif - + Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_TRACK_VIA_SIZE::onClose ) ); } @@ -54,11 +59,11 @@ DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SE bool DIALOG_TRACK_VIA_SIZE::check() { // Wrong input - if( !m_trackWidth->GetValue() || !m_viaDiameter->GetValue() || !m_viaDrill->GetValue() ) + if( m_trackWidth.GetValue() < 0 || m_viaDiameter.GetValue() < 0 || m_viaDrill.GetValue() < 0 ) return false; // Via drill should be smaller than via diameter - if( m_viaDrill->GetValue() >= m_viaDiameter->GetValue() ) + if( m_viaDrill.GetValue() >= m_viaDiameter.GetValue() ) return false; return true; @@ -76,15 +81,15 @@ void DIALOG_TRACK_VIA_SIZE::onOkClick( wxCommandEvent& aEvent ) if( check() ) { // Store dialog values to the router settings - m_settings.SetCustomTrackWidth( From_User_Unit( m_trackWidth->GetUnits(), *m_trackWidth->GetValue() ) ); - m_settings.SetCustomViaSize( From_User_Unit( m_viaDiameter->GetUnits(), *m_viaDiameter->GetValue() ) ); - m_settings.SetCustomViaDrill( From_User_Unit( m_viaDrill->GetUnits(), *m_viaDrill->GetValue() ) ); + m_settings.SetCustomTrackWidth( m_trackWidth.GetValue() ); + m_settings.SetCustomViaSize( m_viaDiameter.GetValue() ); + m_settings.SetCustomViaDrill( m_viaDrill.GetValue() ); EndModal( 1 ); } else { DisplayError( GetParent(), _( "Settings are incorrect" ) ); - m_trackWidth->SetFocus(); + m_trackWidthText->SetFocus(); } } diff --git a/pcbnew/dialogs/dialog_track_via_size.h b/pcbnew/dialogs/dialog_track_via_size.h index 5db003d7de..b84d77f109 100644 --- a/pcbnew/dialogs/dialog_track_via_size.h +++ b/pcbnew/dialogs/dialog_track_via_size.h @@ -25,6 +25,8 @@ #ifndef __dialog_track_via_size__ #define __dialog_track_via_size__ +#include + #include "dialog_track_via_size_base.h" class BOARD_DESIGN_SETTINGS; @@ -32,21 +34,25 @@ class BOARD_DESIGN_SETTINGS; /** Implementing DIALOG_TRACK_VIA_SIZE_BASE */ class DIALOG_TRACK_VIA_SIZE : public DIALOG_TRACK_VIA_SIZE_BASE { - public: - /** Constructor */ - DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings ); +public: + /** Constructor */ + DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings ); - protected: - // Routings settings that are modified by the dialog. - BOARD_DESIGN_SETTINGS& m_settings; +protected: + WX_UNIT_BINDER m_trackWidth; + WX_UNIT_BINDER m_viaDiameter; + WX_UNIT_BINDER m_viaDrill; - ///> Checks if values given in the dialog are sensible. - bool check(); + // Routings settings that are modified by the dialog. + BOARD_DESIGN_SETTINGS& m_settings; - // Handlers for DIALOG_TRACK_VIA_SIZE_BASE events. - void onClose( wxCloseEvent& aEvent ); - void onOkClick( wxCommandEvent& aEvent ); - void onCancelClick( wxCommandEvent& aEvent ); + ///> Checks if values given in the dialog are sensible. + bool check(); + + // Handlers for DIALOG_TRACK_VIA_SIZE_BASE events. + void onClose( wxCloseEvent& aEvent ); + void onOkClick( wxCommandEvent& aEvent ); + void onCancelClick( wxCommandEvent& aEvent ); }; #endif // __dialog_track_via_size__ diff --git a/pcbnew/dialogs/dialog_track_via_size_base.cpp b/pcbnew/dialogs/dialog_track_via_size_base.cpp index a1bfad0ce4..1c963f6bae 100644 --- a/pcbnew/dialogs/dialog_track_via_size_base.cpp +++ b/pcbnew/dialogs/dialog_track_via_size_base.cpp @@ -16,14 +16,46 @@ DIALOG_TRACK_VIA_SIZE_BASE::DIALOG_TRACK_VIA_SIZE_BASE( wxWindow* parent, wxWind wxBoxSizer* bSizes; bSizes = new wxBoxSizer( wxVERTICAL ); - m_trackWidth = new WX_UNIT_TEXT( this, _("Track width:") ); - bSizes->Add( m_trackWidth, 0, wxALL|wxEXPAND, 5 ); + wxFlexGridSizer* fgSizer1; + fgSizer1 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer1->SetFlexibleDirection( wxBOTH ); + fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - m_viaDiameter = new WX_UNIT_TEXT( this, _("Via diameter:") ); - bSizes->Add( m_viaDiameter, 0, wxALL|wxEXPAND, 5 ); + m_staticText3 = new wxStaticText( this, wxID_ANY, _("Track width:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText3->Wrap( -1 ); + fgSizer1->Add( m_staticText3, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - m_viaDrill = new WX_UNIT_TEXT( this, _("Via drill:") ); - bSizes->Add( m_viaDrill, 0, wxALL|wxEXPAND, 5 ); + m_trackWidthText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_trackWidthText, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_trackWidthLabel = new wxStaticText( this, wxID_ANY, _("inch"), wxDefaultPosition, wxDefaultSize, 0 ); + m_trackWidthLabel->Wrap( -1 ); + fgSizer1->Add( m_trackWidthLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText5 = new wxStaticText( this, wxID_ANY, _("Via diameter:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText5->Wrap( -1 ); + fgSizer1->Add( m_staticText5, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaDiameterText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_viaDiameterText, 1, wxALL|wxEXPAND, 5 ); + + m_viaDiameterLabel = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaDiameterLabel->Wrap( -1 ); + fgSizer1->Add( m_viaDiameterLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText7 = new wxStaticText( this, wxID_ANY, _("Via drill:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText7->Wrap( -1 ); + fgSizer1->Add( m_staticText7, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaDrillText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_viaDrillText, 1, wxALL|wxEXPAND, 5 ); + + m_viaDrillLabel = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaDrillLabel->Wrap( -1 ); + fgSizer1->Add( m_viaDrillLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizes->Add( fgSizer1, 1, wxEXPAND|wxALL, 5 ); m_stdButtons = new wxStdDialogButtonSizer(); m_stdButtonsOK = new wxButton( this, wxID_OK ); diff --git a/pcbnew/dialogs/dialog_track_via_size_base.fbp b/pcbnew/dialogs/dialog_track_via_size_base.fbp index c2e3325af7..6f660994ad 100644 --- a/pcbnew/dialogs/dialog_track_via_size_base.fbp +++ b/pcbnew/dialogs/dialog_track_via_size_base.fbp @@ -95,257 +95,791 @@ none 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - WX_UNIT_TEXT - 1 - m_trackWidth = new WX_UNIT_TEXT( this, _("Track width:") ); - - 1 - WX_UNIT_TEXT* m_trackWidth; - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - #include <wxunittext.h> - - 0 - - - 0 + wxEXPAND|wxALL + 1 + + 3 + wxBOTH + + + 0 - 1 - m_trackWidth - 1 - - - protected - 1 - - Resizable - - 1 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - WX_UNIT_TEXT - 1 - m_viaDiameter = new WX_UNIT_TEXT( this, _("Via diameter:") ); - - 1 - WX_UNIT_TEXT* m_viaDiameter; - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - #include <wxunittext.h> - - 0 - - - 0 - - 1 - m_viaDiameter - 1 - - - protected - 1 - - Resizable - - 1 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - WX_UNIT_TEXT - 1 - m_viaDrill = new WX_UNIT_TEXT( this, _("Via drill:") ); - - 1 - WX_UNIT_TEXT* m_viaDrill; - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - #include <wxunittext.h> - - 0 - - - 0 - - 1 - m_viaDrill - 1 - - - protected - 1 - - Resizable - - 1 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Track width: + + 0 + + + 0 + + 1 + m_staticText3 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_trackWidthText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + inch + + 0 + + + 0 + + 1 + m_trackWidthLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via diameter: + + 0 + + + 0 + + 1 + m_staticText5 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_viaDiameterText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + + + 0 + + 1 + m_viaDiameterLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via drill: + + 0 + + + 0 + + 1 + m_staticText7 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_viaDrillText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + + + 0 + + 1 + m_viaDrillLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pcbnew/dialogs/dialog_track_via_size_base.h b/pcbnew/dialogs/dialog_track_via_size_base.h index 3f62c01588..8cff008abf 100644 --- a/pcbnew/dialogs/dialog_track_via_size_base.h +++ b/pcbnew/dialogs/dialog_track_via_size_base.h @@ -11,12 +11,13 @@ #include #include #include -#include +#include +#include #include #include #include #include -#include +#include #include #include #include @@ -32,9 +33,15 @@ class DIALOG_TRACK_VIA_SIZE_BASE : public wxDialog private: protected: - WX_UNIT_TEXT* m_trackWidth; - WX_UNIT_TEXT* m_viaDiameter; - WX_UNIT_TEXT* m_viaDrill; + wxStaticText* m_staticText3; + wxTextCtrl* m_trackWidthText; + wxStaticText* m_trackWidthLabel; + wxStaticText* m_staticText5; + wxTextCtrl* m_viaDiameterText; + wxStaticText* m_viaDiameterLabel; + wxStaticText* m_staticText7; + wxTextCtrl* m_viaDrillText; + wxStaticText* m_viaDrillLabel; wxStdDialogButtonSizer* m_stdButtons; wxButton* m_stdButtonsOK; wxButton* m_stdButtonsCancel; diff --git a/pcbnew/loadcmp.cpp b/pcbnew/loadcmp.cpp index 25c9f8ab03..d042b72db2 100644 --- a/pcbnew/loadcmp.cpp +++ b/pcbnew/loadcmp.cpp @@ -28,6 +28,8 @@ * @brief Footprints selection and loading functions. */ +#include + #include #include #include @@ -61,6 +63,10 @@ static void DisplayCmpDoc( wxString& aName, void* aData ); static FOOTPRINT_LIST MList; +static void clearModuleItemFlags( BOARD_ITEM* aItem ) +{ + aItem->ClearFlags(); +} bool FOOTPRINT_EDIT_FRAME::Load_Module_From_BOARD( MODULE* aModule ) { @@ -92,9 +98,10 @@ bool FOOTPRINT_EDIT_FRAME::Load_Module_From_BOARD( MODULE* aModule ) aModule = newModule; - GetBoard()->Add( newModule ); - newModule->ClearFlags(); + newModule->RunOnChildren( boost::bind( &clearModuleItemFlags, _1 ) ); + + GetBoard()->Add( newModule ); // Clear references to any net info, because the footprint editor // does know any thing about nets handled by the current edited board. diff --git a/pcbnew/menubar_pcbframe.cpp b/pcbnew/menubar_pcbframe.cpp index 92008ca165..0062d6ea48 100644 --- a/pcbnew/menubar_pcbframe.cpp +++ b/pcbnew/menubar_pcbframe.cpp @@ -433,6 +433,49 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Set the origin point for the grid" ), KiBitmap( grid_select_axis_xpm ) ); + wxMenu* routeMenu = new wxMenu; + + AddMenuItem( routeMenu, ID_TRACK_BUTT, + _( "Single Track" ), + _( "Interactively route a single track" ), + KiBitmap( add_tracks_xpm ) ); + + AddMenuItem( routeMenu, ID_DIFF_PAIR_BUTT, + _( "Differential Pair" ), + _( "Interactively route a differential pair" ), + KiBitmap( add_tracks_xpm ) ); + + routeMenu->AppendSeparator(); + + AddMenuItem( routeMenu, ID_TUNE_SINGLE_TRACK_LEN_BUTT, + _( "Tune Track Length" ), + _( "Tune length of a single track" ), + KiBitmap( add_tracks_xpm ) ); + + AddMenuItem( routeMenu, ID_TUNE_DIFF_PAIR_LEN_BUTT, + _( "Tune Differential Pair Length" ), + _( "Tune length of a differential pair" ), + KiBitmap( add_tracks_xpm ) ); + + AddMenuItem( routeMenu, ID_TUNE_DIFF_PAIR_SKEW_BUTT, + _( "Tune Differential Pair Skew/Phase" ), + _( "Tune skew/phase of a differential pair" ), + KiBitmap( add_tracks_xpm ) ); + +/* Fixme: add icons & missing menu entries! + routeMenu->AppendSeparator(); + + AddMenuItem( routeMenu, ID_MENU_MITER_TRACES, + _( "Miter traces..." ), + _( "Miter trace corners with arcs" ), + KiBitmap( grid_select_axis_xpm ) ); + + AddMenuItem( routeMenu, ID_MENU_ADD_TEARDROPS, + _( "Teardrops..." ), + _( "Add teardrops to pads/vias" ), + KiBitmap( grid_select_axis_xpm ) ); +*/ + //----- Preferences and configuration menu------------------------------------ wxMenu* configmenu = new wxMenu; @@ -467,6 +510,11 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Select how items (pads, tracks texts ... ) are displayed" ), KiBitmap( display_options_xpm ) ); + AddMenuItem( configmenu, ID_MENU_INTERACTIVE_ROUTER_SETTINGS, + _( "Interactive Routing" ), + _( "Configure Interactive Routing." ), + KiBitmap( add_tracks_xpm ) ); // fixme: icon + //--- dimensions submenu ------------------------------------------------------ wxMenu* dimensionsMenu = new wxMenu; @@ -488,6 +536,11 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Adjust the global clearance between pads and the solder resist mask" ), KiBitmap( pads_mask_layers_xpm ) ); + AddMenuItem( dimensionsMenu, ID_MENU_DIFF_PAIR_DIMENSIONS, + _( "Differential Pairs" ), + _( "Define the global gap/width for differential pairs." ), + KiBitmap( add_tracks_xpm ) ); // fixme: icon + dimensionsMenu->AppendSeparator(); AddMenuItem( dimensionsMenu, ID_CONFIG_SAVE, _( "&Save" ), _( "Save dimension preferences" ), @@ -592,6 +645,7 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() menuBar->Append( editMenu, _( "&Edit" ) ); menuBar->Append( viewMenu, _( "&View" ) ); menuBar->Append( placeMenu, _( "&Place" ) ); + menuBar->Append( routeMenu, _( "&Route" ) ); menuBar->Append( configmenu, _( "P&references" ) ); menuBar->Append( dimensionsMenu, _( "D&imensions" ) ); menuBar->Append( toolsMenu, _( "&Tools" ) ); diff --git a/pcbnew/modedit.cpp b/pcbnew/modedit.cpp index f685c3f858..306cf872ff 100644 --- a/pcbnew/modedit.cpp +++ b/pcbnew/modedit.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -433,6 +434,8 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) break; } + m_toolManager->RunAction( COMMON_ACTIONS::selectionClear, true ); + // Create the "new" module MODULE* newmodule = new MODULE( *module_in_edit ); newmodule->SetParent( mainpcb ); @@ -485,6 +488,7 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) RN_DATA* ratsnest = pcbframe->GetBoard()->GetRatsnest(); ratsnest->Update( newmodule ); ratsnest->Recalculate(); + GetGalCanvas()->ForceRefresh(); } } break; diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 1ccc128d66..c16e125501 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -68,15 +68,6 @@ #include #include - -#include -#include -#include -#include -#include -#include -#include -#include #include #include @@ -545,14 +536,8 @@ void PCB_EDIT_FRAME::setupTools() m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager ); // Register tools - m_toolManager->RegisterTool( new SELECTION_TOOL ); - m_toolManager->RegisterTool( new ROUTER_TOOL ); - m_toolManager->RegisterTool( new EDIT_TOOL ); - m_toolManager->RegisterTool( new DRAWING_TOOL ); - m_toolManager->RegisterTool( new POINT_EDITOR ); - m_toolManager->RegisterTool( new PCBNEW_CONTROL ); - m_toolManager->RegisterTool( new PCB_EDITOR_CONTROL ); - m_toolManager->RegisterTool( new PLACEMENT_TOOL ); + registerAllTools( m_toolManager ); + m_toolManager->ResetTools( TOOL_BASE::RUN ); // Run the selection tool, it is supposed to be always active diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h index 6740e42484..334bfaa8f6 100644 --- a/pcbnew/pcbnew_id.h +++ b/pcbnew/pcbnew_id.h @@ -39,6 +39,15 @@ enum pcbnew_ids ID_PCB_DELETE_ITEM_BUTT, ID_PCB_PLACE_OFFSET_COORD_BUTT, ID_PCB_PLACE_GRID_COORD_BUTT, + ID_DIFF_PAIR_BUTT, + ID_TUNE_SINGLE_TRACK_LEN_BUTT, + ID_TUNE_DIFF_PAIR_LEN_BUTT, + ID_TUNE_DIFF_PAIR_SKEW_BUTT, + ID_MENU_REMOVE_MEANDERS, + ID_MENU_MITER_TRACES, + ID_MENU_ADD_TEARDROPS, + ID_MENU_DIFF_PAIR_DIMENSIONS, + ID_MENU_INTERACTIVE_ROUTER_SETTINGS, ID_PCB_MASK_CLEARANCE, ID_PCB_LAYERS_SETUP, diff --git a/pcbnew/router/CMakeLists.txt b/pcbnew/router/CMakeLists.txt index a69b09118f..2f324def01 100644 --- a/pcbnew/router/CMakeLists.txt +++ b/pcbnew/router/CMakeLists.txt @@ -13,12 +13,19 @@ set( PCBNEW_PNS_SRCS time_limit.cpp pns_algo_base.cpp + pns_diff_pair.cpp + pns_diff_pair_placer.cpp + pns_dp_meander_placer.cpp pns_dragger.cpp pns_item.cpp pns_itemset.cpp pns_line.cpp pns_line_placer.cpp pns_logger.cpp + pns_meander.cpp + pns_meander_placer.cpp + pns_meander_placer_base.cpp + pns_meander_skew_placer.cpp pns_node.cpp pns_optimizer.cpp pns_router.cpp @@ -26,11 +33,15 @@ set( PCBNEW_PNS_SRCS pns_shove.cpp pns_sizes_settings.cpp pns_solid.cpp + pns_tool_base.cpp + pns_topology.cpp + pns_tune_status_popup.cpp pns_utils.cpp pns_via.cpp pns_walkaround.cpp router_preview_item.cpp router_tool.cpp + length_tuner_tool.cpp ) add_library( pnsrouter STATIC ${PCBNEW_PNS_SRCS} ) diff --git a/pcbnew/router/direction.h b/pcbnew/router/direction.h index eb37a14988..402d4c991e 100644 --- a/pcbnew/router/direction.h +++ b/pcbnew/router/direction.h @@ -1,7 +1,7 @@ /* * KiRouter - a push-and-(sometimes-)shove PCB router * - * Copyright (C) 2013-2014 CERN + * Copyright (C) 2013-2015 CERN * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it @@ -73,7 +73,7 @@ public: */ DIRECTION_45( const VECTOR2I& aVec ) { - construct( aVec ); + construct_( aVec ); } /** @@ -82,7 +82,7 @@ public: */ DIRECTION_45( const SEG& aSeg ) { - construct( aSeg.B - aSeg.A ); + construct_( aSeg.B - aSeg.A ); } /** @@ -180,7 +180,7 @@ public: return ( m_dir % 2 ) == 1; } - bool IsDefined() const + bool IsDefined() const { return m_dir != UNDEFINED; } @@ -282,7 +282,7 @@ public: l.m_dir = NW; else l.m_dir = static_cast( m_dir - 1 ); - + return l; } @@ -303,10 +303,15 @@ public: case NW: return VECTOR2I( -1, 1 ); case SE: return VECTOR2I( 1, -1 ); case SW: return VECTOR2I( -1, -1 ); - + default: return VECTOR2I( 0, 0 ); - } + } + } + + int Mask() const + { + return 1 << ( (int) m_dir ); } private: @@ -315,8 +320,9 @@ private: * Function construct() * Calculates the direction from a vector. If the vector's angle is not a multiple of 45 * degrees, the direction is rounded to the nearest octant. - * @param aVec our vector */ - void construct( const VECTOR2I& aVec ) + * @param aVec our vector + */ + void construct_( const VECTOR2I& aVec ) { m_dir = UNDEFINED; @@ -338,40 +344,14 @@ private: if( dir < 0 ) dir = dir + 8; - + m_dir = (Directions) dir; return; - - if( aVec.y < 0 ) - { - if( aVec.x > 0 ) - m_dir = NE; - else if( aVec.x < 0 ) - m_dir = NW; - else - m_dir = N; - } - else if( aVec.y == 0 ) - { - if( aVec.x > 0 ) - m_dir = E; - else - m_dir = W; - } - else // aVec.y>0 - { - if( aVec.x > 0 ) - m_dir = SE; - else if( aVec.x < 0 ) - m_dir = SW; - else - m_dir = S; - } } - + ///> our actual direction - Directions m_dir; + Directions m_dir; }; #endif // __DIRECTION_H diff --git a/pcbnew/router/length_tuner_tool.cpp b/pcbnew/router/length_tuner_tool.cpp new file mode 100644 index 0000000000..79df24cfa5 --- /dev/null +++ b/pcbnew/router/length_tuner_tool.cpp @@ -0,0 +1,330 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +#include "class_draw_panel_gal.h" +#include "class_board.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pns_segment.h" +#include "pns_router.h" +#include "pns_meander_placer.h" // fixme: move settings to separate header +#include "pns_tune_status_popup.h" + +#include "length_tuner_tool.h" + +#include "trace.h" + +using namespace KIGFX; +using boost::optional; + +static TOOL_ACTION ACT_StartTuning( "pcbnew.LengthTuner.StartTuning", + AS_CONTEXT, 'X', + "New Track", "Starts laying a new track."); +static TOOL_ACTION ACT_EndTuning( "pcbnew.LengthTuner.EndTuning", + AS_CONTEXT, WXK_END, + "End Track", "Stops laying the current meander."); + +static TOOL_ACTION ACT_Settings( "pcbnew.LengthTuner.Settings", + AS_CONTEXT, 'L', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_SpacingIncrease( "pcbnew.LengthTuner.SpacingIncrease", + AS_CONTEXT, '1', + "Increase spacing", "Increase meander spacing by one step."); + +static TOOL_ACTION ACT_SpacingDecrease( "pcbnew.LengthTuner.SpacingDecrease", + AS_CONTEXT, '2', + "Decrease spacing ", "Decrease meander spacing by one step."); + +static TOOL_ACTION ACT_AmplIncrease( "pcbnew.LengthTuner.AmplIncrease", + AS_CONTEXT, '3', + "Increase amplitude", "Increase meander amplitude by one step."); + +static TOOL_ACTION ACT_AmplDecrease( "pcbnew.LengthTuner.AmplDecrease", + AS_CONTEXT, '4', + "Decrease amplitude", "Decrease meander amplitude by one step."); + + +LENGTH_TUNER_TOOL::LENGTH_TUNER_TOOL() : + PNS_TOOL_BASE( "pcbnew.LengthTuner" ) +{ +} + + +class TUNER_TOOL_MENU: public CONTEXT_MENU +{ +public: + TUNER_TOOL_MENU( BOARD* aBoard ) + { + SetTitle( wxT( "Length Tuner" ) ); + + //Add( ACT_StartTuning ); + //Add( ACT_EndTuning ); + + //AppendSeparator(); + + Add( ACT_SpacingIncrease ); + Add( ACT_SpacingDecrease ); + Add( ACT_AmplIncrease ); + Add( ACT_AmplDecrease ); + Add( ACT_Settings ); + } +}; + + +LENGTH_TUNER_TOOL::~LENGTH_TUNER_TOOL() +{ + delete m_router; +} + + +void LENGTH_TUNER_TOOL::Reset( RESET_REASON aReason ) +{ + PNS_TOOL_BASE::Reset( aReason ); + + Go( &LENGTH_TUNER_TOOL::TuneSingleTrace, COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent() ); + Go( &LENGTH_TUNER_TOOL::TuneDiffPair, COMMON_ACTIONS::routerActivateTuneDiffPair.MakeEvent() ); + Go( &LENGTH_TUNER_TOOL::TuneDiffPairSkew, COMMON_ACTIONS::routerActivateTuneDiffPairSkew.MakeEvent() ); +} + + +void LENGTH_TUNER_TOOL::handleCommonEvents( const TOOL_EVENT& aEvent ) +{ + if( aEvent.IsAction( &ACT_RouterOptions ) ) + { + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); + + if( settingsDlg.ShowModal() ) + { + // FIXME: do we need an explicit update? + } + } + + PNS_MEANDER_PLACER_BASE* placer = static_cast( m_router->Placer() ); + + if( !placer ) + return; + + if( aEvent.IsAction( &ACT_Settings ) ) + { + PNS_MEANDER_SETTINGS settings = placer->MeanderSettings(); + DIALOG_PNS_LENGTH_TUNING_SETTINGS settingsDlg( m_frame, settings, m_router->Mode() ); + + if( settingsDlg.ShowModal() ) + { + placer->UpdateSettings ( settings ); + } + + m_savedMeanderSettings = placer->MeanderSettings(); + } +} + + +void LENGTH_TUNER_TOOL::performTuning() +{ + bool saveUndoBuffer = true; + + if( m_startItem ) + { + m_frame->SetActiveLayer( ToLAYER_ID ( m_startItem->Layers().Start() ) ); + + if( m_startItem->Net() >= 0 ) + highlightNet( true, m_startItem->Net() ); + } + + m_ctls->ForceCursorPosition( false ); + m_ctls->SetAutoPan( true ); + + if( !m_router->StartRouting( m_startSnapPoint, m_startItem, 0 ) ) + { + wxMessageBox( m_router->FailureReason(), _( "Error" ) ); + highlightNet( false ); + return; + } + + PNS_TUNE_STATUS_POPUP statusPopup( m_frame ); + statusPopup.Popup(); + + PNS_MEANDER_PLACER* placer = static_cast( m_router->Placer() ); + VECTOR2I end; + + placer->UpdateSettings( m_savedMeanderSettings ); + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() || evt->IsActivate() ) + break; + else if( evt->Action() == TA_UNDO_REDO ) + { + saveUndoBuffer = false; + break; + } + else if( evt->IsMotion() ) + { + end = evt->Position(); + m_router->Move( end, NULL ); + + wxPoint p = wxGetMousePosition(); + + p.x += 20; + p.y += 20; + + statusPopup.Update( m_router ); + statusPopup.Move( p ); + } + else if( evt->IsClick( BUT_LEFT ) ) + { + if( m_router->FixRoute( evt->Position(), NULL ) ) + break; + } + else if( evt->IsAction( &ACT_EndTuning ) ) + { + if( m_router->FixRoute( end, NULL ) ) + break; + } + else if( evt->IsAction( &ACT_AmplDecrease ) ) + { + placer->AmplitudeStep( -1 ); + m_router->Move( end, NULL ); + } + else if( evt->IsAction( &ACT_AmplIncrease ) ) + { + placer->AmplitudeStep( 1 ); + m_router->Move( end, NULL ); + } + else if(evt->IsAction( &ACT_SpacingDecrease ) ) + { + placer->SpacingStep( -1 ); + m_router->Move( end, NULL ); + } + else if( evt->IsAction( &ACT_SpacingIncrease ) ) + { + placer->SpacingStep( 1 ); + m_router->Move( end, NULL ); + } + + handleCommonEvents( *evt ); + } + + m_router->StopRouting(); + + if( saveUndoBuffer ) + { + // Save the recent changes in the undo buffer + m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + m_frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + m_ctls->SetAutoPan( false ); + m_ctls->ForceCursorPosition( false ); + highlightNet( false ); +} + + +int LENGTH_TUNER_TOOL::TuneSingleTrace( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Trace Length" ) ); + return mainLoop( PNS_MODE_TUNE_SINGLE ); +} + + +int LENGTH_TUNER_TOOL::TuneDiffPair( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Diff Pair Length" ) ); + return mainLoop( PNS_MODE_TUNE_DIFF_PAIR ); +} + + +int LENGTH_TUNER_TOOL::TuneDiffPairSkew( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Diff Pair Skew" ) ); + return mainLoop( PNS_MODE_TUNE_DIFF_PAIR_SKEW ); +} + + +int LENGTH_TUNER_TOOL::mainLoop( PNS_ROUTER_MODE aMode ) +{ + // Deselect all items + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + Activate(); + + m_router->SetMode( aMode ); + + m_ctls->SetSnapping( true ); + m_ctls->ShowCursor( true ); + + std::auto_ptr ctxMenu( new TUNER_TOOL_MENU( m_board ) ); + SetContextMenu( ctxMenu.get() ); + + // Main loop: keep receiving events + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( m_needsSync ) + { + m_router->SyncWorld(); + m_router->SetView( getView() ); + m_needsSync = false; + } + + if( evt->IsCancel() || evt->IsActivate() ) + break; // Finish + else if( evt->Action() == TA_UNDO_REDO ) + m_needsSync = true; + else if( evt->IsMotion() ) + updateStartItem( *evt ); + else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_StartTuning ) ) + { + updateStartItem( *evt ); + performTuning( ); + } + + handleCommonEvents( *evt ); + } + + // Restore the default settings + m_ctls->SetAutoPan( false ); + m_ctls->ShowCursor( false ); + m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); + + // Store routing settings till the next invocation + m_savedSettings = m_router->Settings(); + m_savedSizes = m_router->Sizes(); + + return 0; +} diff --git a/pcbnew/router/length_tuner_tool.h b/pcbnew/router/length_tuner_tool.h new file mode 100644 index 0000000000..ed6ebf8fc1 --- /dev/null +++ b/pcbnew/router/length_tuner_tool.h @@ -0,0 +1,51 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * Author: Maciej Suminski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __LENGTH_TUNER_TOOL_H +#define __LENGTH_TUNER_TOOL_H + +#include "pns_tool_base.h" +#include "pns_meander.h" + +class PNS_TUNE_STATUS_POPUP; + +class APIEXPORT LENGTH_TUNER_TOOL : public PNS_TOOL_BASE +{ +public: + LENGTH_TUNER_TOOL(); + ~LENGTH_TUNER_TOOL(); + + void Reset( RESET_REASON aReason ); + + int TuneSingleTrace( const TOOL_EVENT& aEvent ); + int TuneDiffPair( const TOOL_EVENT& aEvent ); + int TuneDiffPairSkew( const TOOL_EVENT& aEvent ); + int ClearMeanders( const TOOL_EVENT& aEvent ); + +private: + void performTuning( ); + int mainLoop( PNS_ROUTER_MODE aMode ); + void handleCommonEvents( const TOOL_EVENT& aEvent ); + + PNS_MEANDER_SETTINGS m_savedMeanderSettings; +}; + +#endif diff --git a/pcbnew/router/pns_algo_base.cpp b/pcbnew/router/pns_algo_base.cpp index 7008665428..b72243b223 100644 --- a/pcbnew/router/pns_algo_base.cpp +++ b/pcbnew/router/pns_algo_base.cpp @@ -23,10 +23,10 @@ PNS_ROUTING_SETTINGS& PNS_ALGO_BASE::Settings() const { - return m_router->Settings(); + return m_router->Settings(); } -PNS_LOGGER *PNS_ALGO_BASE::Logger() +PNS_LOGGER *PNS_ALGO_BASE::Logger() { - return NULL; -} \ No newline at end of file + return NULL; +} diff --git a/pcbnew/router/pns_algo_base.h b/pcbnew/router/pns_algo_base.h index 1aa94333fc..a6429ea602 100644 --- a/pcbnew/router/pns_algo_base.h +++ b/pcbnew/router/pns_algo_base.h @@ -21,6 +21,8 @@ #ifndef __PNS_ALGO_BASE_H #define __PNS_ALGO_BASE_H +#include // for wxString + #include "pns_routing_settings.h" class PNS_ROUTER; @@ -31,31 +33,30 @@ class PNS_LOGGER; * * Base class for all P&S algorithms (shoving, walkaround, line placement, dragging, etc.) * Holds a bunch of objects commonly used by all algorithms (P&S settings, parent router instance, logging) - **/ - + */ class PNS_ALGO_BASE { public: - PNS_ALGO_BASE( PNS_ROUTER *aRouter ) : - m_router ( aRouter ) - {} + PNS_ALGO_BASE( PNS_ROUTER* aRouter ) : + m_router( aRouter ) + {} - virtual ~PNS_ALGO_BASE() {} + virtual ~PNS_ALGO_BASE() {} - ///> Returns the instance of our router - PNS_ROUTER* Router() const - { - return m_router; - } + ///> Returns the instance of our router + PNS_ROUTER* Router() const + { + return m_router; + } - ///> Returns current router settings - PNS_ROUTING_SETTINGS& Settings() const; + ///> Returns current router settings + PNS_ROUTING_SETTINGS& Settings() const; + + ///> Returns the logger object, allowing to dump geometry to a file. + virtual PNS_LOGGER* Logger(); - ///> Returns the logger object, allowing to dump geometry to a file. - virtual PNS_LOGGER* Logger(); - private: - PNS_ROUTER* m_router; + PNS_ROUTER* m_router; }; #endif diff --git a/pcbnew/router/pns_diff_pair.cpp b/pcbnew/router/pns_diff_pair.cpp new file mode 100644 index 0000000000..271a8f05de --- /dev/null +++ b/pcbnew/router/pns_diff_pair.cpp @@ -0,0 +1,850 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "direction.h" + +#include "pns_diff_pair.h" +#include "pns_router.h" +#include "pns_solid.h" +#include "pns_utils.h" + + +class PNS_LINE; + +PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR( PNS_ITEM* aPrimP, PNS_ITEM* aPrimN ) +{ + m_primP = aPrimP->Clone(); + m_primN = aPrimN->Clone(); + + m_anchorP = m_primP->Anchor( 0 ); + m_anchorN = m_primN->Anchor( 0 ); +} + + +void PNS_DP_PRIMITIVE_PAIR::SetAnchors( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ) +{ + m_anchorP = aAnchorP; + m_anchorN = aAnchorN; +} + + +PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ) +{ + m_anchorP = aAnchorP; + m_anchorN = aAnchorN; + m_primP = m_primN = NULL; +} + + +PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR( const PNS_DP_PRIMITIVE_PAIR& aOther ) +{ + if( aOther.m_primP ) + m_primP = aOther.m_primP->Clone(); + if( aOther.m_primN ) + m_primN = aOther.m_primN->Clone(); + + m_anchorP = aOther.m_anchorP; + m_anchorN = aOther.m_anchorN; +} + + +PNS_DP_PRIMITIVE_PAIR& PNS_DP_PRIMITIVE_PAIR::operator=( const PNS_DP_PRIMITIVE_PAIR& aOther ) +{ + if( aOther.m_primP ) + m_primP = aOther.m_primP->Clone(); + if( aOther.m_primN ) + m_primN = aOther.m_primN->Clone(); + + m_anchorP = aOther.m_anchorP; + m_anchorN = aOther.m_anchorN; + + return *this; +} + + +PNS_DP_PRIMITIVE_PAIR::~PNS_DP_PRIMITIVE_PAIR() +{ + delete m_primP; + delete m_primN; +} + + +bool PNS_DP_PRIMITIVE_PAIR::Directional() const +{ + if( !m_primP ) + return false; + + return m_primP->OfKind( PNS_ITEM::SEGMENT ); +} + + +DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::anchorDirection( PNS_ITEM* aItem, const VECTOR2I& aP ) const +{ + if( !aItem->OfKind ( PNS_ITEM::SEGMENT ) ) + return DIRECTION_45(); + + PNS_SEGMENT* s = static_cast( aItem ); + + if( s->Seg().A == aP ) + return DIRECTION_45( s->Seg().A - s->Seg().B ); + else + return DIRECTION_45( s->Seg().B - s->Seg().A ); +} + + +DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::DirP() const +{ + return anchorDirection( m_primP, m_anchorP ); +} + + +DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::DirN() const +{ + return anchorDirection( m_primN, m_anchorN ); +} + + +static void drawGw( VECTOR2I p, int color ) +{ + SHAPE_LINE_CHAIN l; + + l.Append( p - VECTOR2I( -50000, -50000 ) ); + l.Append( p + VECTOR2I( -50000, -50000 ) ); + + l.Clear(); + l.Append( p - VECTOR2I( 50000, -50000 ) ); + l.Append( p + VECTOR2I( 50000, -50000 ) ); +} + + +static DIRECTION_45::AngleType angle( const VECTOR2I &a, const VECTOR2I &b ) +{ + DIRECTION_45 dir_a( a ); + DIRECTION_45 dir_b( b ); + + return dir_a.Angle( dir_b ); +} + + +static bool checkGap( const SHAPE_LINE_CHAIN &p, const SHAPE_LINE_CHAIN &n, int gap ) +{ + int i, j; + + for( i = 0; i < p.SegmentCount(); i++ ) + { + for( j = 0; j < n.SegmentCount() ; j++ ) + { + int dist = p.CSegment( i ).Distance( n.CSegment( j ) ); + + if( dist < gap - 100 ) + return false; + } + } + + return true; +} + + +void PNS_DP_GATEWAY::Reverse() +{ + m_entryN = m_entryN.Reverse(); + m_entryP = m_entryP.Reverse(); +} + + +bool PNS_DIFF_PAIR::BuildInitial( PNS_DP_GATEWAY& aEntry, PNS_DP_GATEWAY &aTarget, bool aPrefDiagonal ) +{ + SHAPE_LINE_CHAIN p = DIRECTION_45().BuildInitialTrace ( aEntry.AnchorP(), aTarget.AnchorP(), aPrefDiagonal ); + SHAPE_LINE_CHAIN n = DIRECTION_45().BuildInitialTrace ( aEntry.AnchorN(), aTarget.AnchorN(), aPrefDiagonal ); + + int mask = aEntry.AllowedAngles() | DIRECTION_45::ANG_STRAIGHT | DIRECTION_45::ANG_OBTUSE; + + SHAPE_LINE_CHAIN sum_n, sum_p; + m_p = p; + m_n = n; + + if( aEntry.HasEntryLines() ) + { + if( !aEntry.Entry().CheckConnectionAngle( *this, mask ) ) + return false; + + sum_p = aEntry.Entry().CP(); + sum_n = aEntry.Entry().CN(); + sum_p.Append( p ); + sum_n.Append( n ); + } + else + { + sum_p = p; + sum_n = n; + } + + mask = aTarget.AllowedAngles() | DIRECTION_45::ANG_STRAIGHT | DIRECTION_45::ANG_OBTUSE; + + m_p = sum_p; + m_n = sum_n; + + if( aTarget.HasEntryLines() ) + { + PNS_DP_GATEWAY t(aTarget) ; + t.Reverse(); + + if( !CheckConnectionAngle( t.Entry(), mask ) ) + return false; + + sum_p.Append( t.Entry().CP() ); + sum_n.Append( t.Entry().CN() ); + } + + m_p = sum_p; + m_n = sum_n; + + if( !checkGap ( p, n, m_gapConstraint ) ) + return false; + + if( p.SelfIntersecting() || n.SelfIntersecting() ) + return false; + + if( p.Intersects( n ) ) + return false; + + return true; +} + + +bool PNS_DIFF_PAIR::CheckConnectionAngle( const PNS_DIFF_PAIR& aOther, int aAllowedAngles ) const +{ + bool checkP, checkN; + + if( m_p.SegmentCount() == 0 || aOther.m_p.SegmentCount() == 0 ) + checkP = true; + else + { + DIRECTION_45 p0( m_p.CSegment( -1 ) ); + DIRECTION_45 p1( aOther.m_p.CSegment( 0 ) ); + + checkP = ( p0.Angle( p1 ) & aAllowedAngles ) != 0; + } + + if( m_n.SegmentCount() == 0 || aOther.m_n.SegmentCount() == 0 ) + checkN = true; + else + { + DIRECTION_45 n0( m_n.CSegment( -1 ) ); + DIRECTION_45 n1( aOther.m_n.CSegment( 0 ) ); + + checkN = ( n0.Angle( n1 ) & aAllowedAngles ) != 0; + } + + return checkP && checkN; +} + + +const PNS_DIFF_PAIR PNS_DP_GATEWAY::Entry() const +{ + return PNS_DIFF_PAIR( m_entryP, m_entryN, 0 ); +} + + +void PNS_DP_GATEWAYS::BuildOrthoProjections( PNS_DP_GATEWAYS& aEntries, + const VECTOR2I& aCursorPos, int aOrthoScore ) +{ + BOOST_FOREACH( PNS_DP_GATEWAY g, aEntries.Gateways() ) + { + VECTOR2I dir = ( g.AnchorP() - g.AnchorN() ).Perpendicular(); + VECTOR2I midpoint( ( g.AnchorP() + g.AnchorN() ) / 2 ); + SEG guide( midpoint, midpoint + dir ); + VECTOR2I proj = guide.LineProject( aCursorPos ); + + PNS_DP_GATEWAYS targets( m_gap ); + + targets.m_viaGap = m_viaGap; + targets.m_viaDiameter = m_viaDiameter; + targets.m_fitVias = m_fitVias; + + targets.BuildForCursor( proj ); + + BOOST_FOREACH( PNS_DP_GATEWAY t, targets.Gateways() ) + { + t.SetPriority( aOrthoScore ); + m_gateways.push_back( t ); + } + } +} + + +bool PNS_DP_GATEWAYS::FitGateways( PNS_DP_GATEWAYS& aEntry, PNS_DP_GATEWAYS& aTarget, + bool aPrefDiagonal, PNS_DIFF_PAIR& aDp ) +{ + std::vector candidates; + + BOOST_FOREACH( PNS_DP_GATEWAY g_entry, aEntry.Gateways() ) + { + BOOST_FOREACH( PNS_DP_GATEWAY g_target, aTarget.Gateways() ) + { + for( int attempt = 0; attempt < 2; attempt++ ) + { + PNS_DIFF_PAIR l( m_gap ); + + if( l.BuildInitial( g_entry, g_target, aPrefDiagonal ^ ( attempt ? true : false ) ) ) + { + int score = ( attempt == 1 ? -3 : 0 ); + score +=g_entry.Priority(); + score +=g_target.Priority(); + + DP_CANDIDATE c; + c.score = score; + c.p = l.CP(); + c.n = l.CN(); + candidates.push_back( c ); + } + } + } + } + + int bestScore = -1000; + DP_CANDIDATE best; + bool found; + + BOOST_FOREACH( DP_CANDIDATE c, candidates ) + { + if( c.score > bestScore ) + { + bestScore = c.score; + best = c; + found = true; + } + } + + if( found ) + { + aDp.SetGap( m_gap ); + aDp.SetShape( best.p, best.n ); + return true; + } + + return false; +} + + +bool PNS_DP_GATEWAYS::checkDiagonalAlignment( const VECTOR2I& a, const VECTOR2I& b ) const +{ + VECTOR2I dir ( std::abs (a.x - b.x), std::abs ( a.y - b.y )); + + return (dir.x == 0 && dir.y != 0) || (dir.x == dir.y) || (dir.y == 0 && dir.x != 0); +} + + +void PNS_DP_GATEWAYS::BuildFromPrimitivePair( PNS_DP_PRIMITIVE_PAIR aPair, bool aPreferDiagonal ) +{ + VECTOR2I majorDirection; + VECTOR2I p0_p, p0_n; + int orthoFanDistance; + int diagFanDistance; + const SHAPE* shP = NULL; + + if( aPair.PrimP() == NULL ) + { + BuildGeneric( aPair.AnchorP(), aPair.AnchorN(), true ); + return; + } + + const int pvMask = PNS_ITEM::SOLID | PNS_ITEM::VIA; + + if( aPair.PrimP()->OfKind( pvMask ) && aPair.PrimN()->OfKind( pvMask ) ) + { + p0_p = aPair.AnchorP(); + p0_n = aPair.AnchorN(); + + shP = aPair.PrimP()->Shape(); + + } + else if( aPair.PrimP()->OfKind( PNS_ITEM::SEGMENT ) && aPair.PrimN()->OfKind( PNS_ITEM::SEGMENT ) ) + { + buildDpContinuation( aPair, aPreferDiagonal ); + + return; + } + + majorDirection = ( p0_p - p0_n ).Perpendicular(); + + switch( shP->Type() ) + { + case SH_RECT: + { + int w = static_cast( shP )->GetWidth(); + int h = static_cast( shP )->GetHeight(); + + if( w < h ) + std::swap( w, h ); + + orthoFanDistance = w * 3/4; + diagFanDistance = ( w - h ) / 2; + break; + } + + case SH_SEGMENT: + { + int w = static_cast( shP )->GetWidth(); + SEG s = static_cast( shP )->GetSeg(); + + orthoFanDistance = w + ( s.B - s.A ).EuclideanNorm() / 2; + diagFanDistance = ( s.B - s.A ).EuclideanNorm() / 2; + break; + } + + default: + BuildGeneric ( p0_p, p0_n, true ); + return; + } + + if( checkDiagonalAlignment( p0_p, p0_n ) ) + { + int padDist = ( p0_p - p0_n ).EuclideanNorm(); + + for( int k = 0; k < 2; k++ ) + { + VECTOR2I dir, dp, dv; + + if( k == 0 ) + { + dir = majorDirection.Resize( orthoFanDistance ); + int d = ( padDist - m_gap ) / 2; + + dp = dir.Resize( d ); + dv = ( p0_n - p0_p ).Resize( d ); + } + else + { + dir = majorDirection.Resize( diagFanDistance ); + int d = ( padDist - m_gap ) / 2; + dp = dir.Resize( d ); + dv = ( p0_n - p0_p ).Resize( d ); + } + + for( int i = 0; i < 2; i++ ) + { + int sign = i ? -1 : 1; + + VECTOR2I gw_p( p0_p + sign * ( dir + dp ) + dv ); + VECTOR2I gw_n( p0_n + sign * ( dir + dp ) - dv ); + + SHAPE_LINE_CHAIN entryP( p0_p, p0_p + sign * dir, gw_p ); + SHAPE_LINE_CHAIN entryN( p0_n, p0_n + sign * dir, gw_n ); + + PNS_DP_GATEWAY gw( gw_p, gw_n, false ); + + gw.SetEntryLines( entryP, entryN ); + gw.SetPriority( 100 - k ); + m_gateways.push_back( gw ); + } + } + } + + BuildGeneric( p0_p, p0_n, true ); +} + + +void PNS_DP_GATEWAYS::BuildForCursor( const VECTOR2I& aCursorPos ) +{ + int gap = m_fitVias ? m_viaGap + m_viaDiameter : m_gap; + + for( int attempt = 0; attempt < 2; attempt++ ) + { + for( int i = 0; i < 4; i++ ) + { + VECTOR2I dir; + + if( !attempt ) + { + dir = VECTOR2I( gap, gap ).Resize( gap / 2 ); + + if( i % 2 == 0 ) + dir.x = -dir.x; + + if( i / 2 == 0 ) + dir.y = -dir.y; + } + else + { + if( i /2 == 0 ) + dir = VECTOR2I( gap / 2 * ( ( i % 2 ) ? -1 : 1 ), 0 ); + else + dir = VECTOR2I( 0, gap / 2 * ( ( i % 2 ) ? -1 : 1) ); + } + + if( m_fitVias ) + BuildGeneric( aCursorPos + dir, aCursorPos - dir, true, true ); + else + m_gateways.push_back( PNS_DP_GATEWAY( aCursorPos + dir, + aCursorPos - dir, attempt ? true : false ) ); + + drawGw ( aCursorPos + dir, 2 ); + drawGw ( aCursorPos - dir, 3 ); + } + } +} + + +void PNS_DP_GATEWAYS::buildEntries( const VECTOR2I& p0_p, const VECTOR2I& p0_n ) +{ + BOOST_FOREACH( PNS_DP_GATEWAY &g, m_gateways ) + { + if( !g.HasEntryLines() ) + { + SHAPE_LINE_CHAIN lead_p = DIRECTION_45().BuildInitialTrace ( g.AnchorP(), p0_p, g.IsDiagonal() ).Reverse(); + SHAPE_LINE_CHAIN lead_n = DIRECTION_45().BuildInitialTrace ( g.AnchorN(), p0_n, g.IsDiagonal() ).Reverse(); + g.SetEntryLines( lead_p, lead_n ); + } + } +} + + +void PNS_DP_GATEWAYS::buildDpContinuation( PNS_DP_PRIMITIVE_PAIR aPair, bool aIsDiagonal ) +{ + PNS_DP_GATEWAY gw( aPair.AnchorP(), aPair.AnchorN(), aIsDiagonal ); + gw.SetPriority( 100 ); + m_gateways.push_back( gw ); + + if( !aPair.Directional() ) + return; + + DIRECTION_45 dP = aPair.DirP(); + DIRECTION_45 dN = aPair.DirN(); + + int gap = ( aPair.AnchorP() - aPair.AnchorN() ).EuclideanNorm(); + + VECTOR2I vdP = aPair.AnchorP() + dP.Left().ToVector(); + VECTOR2I vdN = aPair.AnchorN() + dN.Left().ToVector(); + + PNS_SEGMENT* sP = static_cast( aPair.PrimP() ); + + VECTOR2I t1, t2; + + if( sP->Seg().Side( vdP ) == sP->Seg().Side( vdN ) ) + { + t1 = aPair.AnchorP() + dP.Left().ToVector().Resize( gap ); + t2 = aPair.AnchorN() + dP.Right().ToVector().Resize( gap ); + } + else + { + t1 = aPair.AnchorP() + dP.Right().ToVector().Resize( gap ); + t2 = aPair.AnchorN() + dP.Left().ToVector().Resize( gap ); + } + + PNS_DP_GATEWAY gwL( t2, aPair.AnchorN(), !aIsDiagonal ); + SHAPE_LINE_CHAIN ep = dP.BuildInitialTrace( aPair.AnchorP(), t2, !aIsDiagonal ); + + gwL.SetPriority( 10 ); + gwL.SetEntryLines( ep , SHAPE_LINE_CHAIN() ); + + m_gateways.push_back( gwL ); + + PNS_DP_GATEWAY gwR( aPair.AnchorP(), t1, !aIsDiagonal ); + SHAPE_LINE_CHAIN en = dP.BuildInitialTrace( aPair.AnchorN(), t1, !aIsDiagonal ); + gwR.SetPriority( 10) ; + gwR.SetEntryLines( SHAPE_LINE_CHAIN(), en ); + + m_gateways.push_back( gwR ); +} + + +void PNS_DP_GATEWAYS::BuildGeneric( const VECTOR2I& p0_p, const VECTOR2I& p0_n, bool aBuildEntries, bool aViaMode ) +{ + SEG st_p[2], st_n[2]; + SEG d_n[2], d_p[2]; + + const int padToGapThreshold = 3; + int padDist = ( p0_p - p0_p ).EuclideanNorm(); + + st_p[0] = SEG(p0_p + VECTOR2I( -100, 0 ), p0_p + VECTOR2I( 100, 0 ) ); + st_n[0] = SEG(p0_n + VECTOR2I( -100, 0 ), p0_n + VECTOR2I( 100, 0 ) ); + st_p[1] = SEG(p0_p + VECTOR2I( 0, -100 ), p0_p + VECTOR2I( 0, 100 ) ); + st_n[1] = SEG(p0_n + VECTOR2I( 0, -100 ), p0_n + VECTOR2I( 0, 100 ) ); + d_p[0] = SEG( p0_p + VECTOR2I( -100, -100 ), p0_p + VECTOR2I( 100, 100 ) ); + d_p[1] = SEG( p0_p + VECTOR2I( 100, -100 ), p0_p + VECTOR2I( -100, 100 ) ); + d_n[0] = SEG( p0_n + VECTOR2I( -100, -100 ), p0_n + VECTOR2I( 100, 100 ) ); + d_n[1] = SEG( p0_n + VECTOR2I( 100, -100 ), p0_n + VECTOR2I( -100, 100 ) ); + + // midpoint exit & side-by exits + for( int i = 0; i < 2; i++ ) + { + bool straightColl = st_p[i].Collinear( st_n[i] ); + bool diagColl = d_p[i].Collinear( d_n[i] ); + + if( straightColl || diagColl ) + { + VECTOR2I dir = ( p0_n - p0_p ).Resize( m_gap / 2 ); + VECTOR2I m = ( p0_p + p0_n ) / 2; + int prio = ( padDist > padToGapThreshold * m_gap ? 2 : 1); + + if( !aViaMode ) + { + m_gateways.push_back( PNS_DP_GATEWAY( m - dir, m + dir, diagColl, DIRECTION_45::ANG_RIGHT, prio ) ); + + dir = ( p0_n - p0_p ).Resize( m_gap ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_p - dir, p0_p - dir + dir.Perpendicular(), diagColl ) ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_p - dir, p0_p - dir - dir.Perpendicular(), diagColl ) ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_n + dir + dir.Perpendicular(), p0_n + dir, diagColl ) ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_n + dir - dir.Perpendicular(), p0_n + dir, diagColl ) ); + } + } + } + + for( int i = 0; i < 2; i++ ) + { + for( int j = 0; j < 2; j++ ) + { + OPT_VECTOR2I ips[2], m; + + ips[0] = d_n[i].IntersectLines( d_p[j] ); + ips[1] = st_p[i].IntersectLines( st_n[j] ); + + if( d_n[i].Collinear( d_p[j] ) ) + ips [0] = OPT_VECTOR2I(); + if( st_p[i].Collinear( st_p[j] ) ) + ips [1] = OPT_VECTOR2I(); + + // diagonal-diagonal and straight-straight cases - the most typical case if the pads + // are on the same straight/diagonal line + for( int k = 0; k < 2; k++ ) + { + m = ips[k]; + if( m && *m != p0_p && *m != p0_n ) + { + int prio = ( padDist > padToGapThreshold * m_gap ? 10 : 20 ); + VECTOR2I g_p( ( p0_p - *m ).Resize( (double) m_gap * M_SQRT1_2 ) ); + VECTOR2I g_n( ( p0_n - *m ).Resize( (double) m_gap * M_SQRT1_2 ) ); + + m_gateways.push_back( PNS_DP_GATEWAY( *m + g_p, *m + g_n, k == 0 ? true : false, DIRECTION_45::ANG_OBTUSE, prio ) ); + } + } + + ips[0] = st_n[i].IntersectLines( d_p[j] ); + ips[1] = st_p[i].IntersectLines( d_n[j] ); + + // diagonal-straight cases: 8 possibilities of "weirder" exists + for( int k = 0; k < 2; k++ ) + { + m = ips[k]; + + if( !aViaMode && m && *m != p0_p && *m != p0_n ) + { + VECTOR2I g_p, g_n; + + g_p = ( p0_p - *m ).Resize( (double) m_gap * M_SQRT2 ); + g_n = ( p0_n - *m ).Resize( (double) m_gap ); + + if( angle( g_p, g_n ) != DIRECTION_45::ANG_ACUTE ) + m_gateways.push_back( PNS_DP_GATEWAY( *m + g_p, *m + g_n, true ) ); + + g_p = ( p0_p - *m ).Resize( m_gap ); + g_n = ( p0_n - *m ).Resize( (double) m_gap * M_SQRT2 ); + + if( angle( g_p, g_n ) != DIRECTION_45::ANG_ACUTE ) + m_gateways.push_back( PNS_DP_GATEWAY( *m + g_p, *m + g_n, true ) ); + } + } + } + } + + if( aBuildEntries ) + buildEntries( p0_p, p0_n ); +} + + +PNS_DP_PRIMITIVE_PAIR PNS_DIFF_PAIR::EndingPrimitives() +{ + if( m_hasVias ) + return PNS_DP_PRIMITIVE_PAIR( &m_via_p, &m_via_n ); + else + { + const PNS_LINE lP( PLine() ); + const PNS_LINE lN( NLine() ); + + PNS_SEGMENT sP( lP, lP.CSegment( -1 ) ); + PNS_SEGMENT sN( lN, lN.CSegment( -1 ) ); + + PNS_DP_PRIMITIVE_PAIR dpair( &sP, &sN ); + dpair.SetAnchors( sP.Seg().B, sN.Seg().B ); + + return dpair; + } +} + + +bool commonParallelProjection( SEG n, SEG p, SEG &pClip, SEG& nClip ) +{ + SEG n_proj_p( p.LineProject( n.A ), p.LineProject( n.B ) ); + + int64_t t_a = 0; + int64_t t_b = p.TCoef( p.B ); + + int64_t tproj_a = p.TCoef( n_proj_p.A ); + int64_t tproj_b = p.TCoef( n_proj_p.B ); + + if( t_b < t_a ) + std::swap( t_b, t_a ); + + if( tproj_b < tproj_a ) + std::swap( tproj_b, tproj_a ); + + if( t_b <= tproj_a ) + return false; + + if( t_a >= tproj_b ) + return false; + + int64_t t[4] = { 0, p.TCoef( p.B ), p.TCoef( n_proj_p.A ), p.TCoef( n_proj_p.B ) }; + std::vector tv( t, t + 4 ); + std::sort( tv.begin(), tv.end() ); // fixme: awful and disgusting way of finding 2 midpoints + + int64_t pLenSq = p.SquaredLength(); + + VECTOR2I dp = p.B - p.A; + pClip.A.x = p.A.x + rescale( (int64_t)dp.x, tv[1], pLenSq ); + pClip.A.y = p.A.y + rescale( (int64_t)dp.y, tv[1], pLenSq ); + + pClip.B.x = p.A.x + rescale( (int64_t)dp.x, tv[2], pLenSq ); + pClip.B.y = p.A.y + rescale( (int64_t)dp.y, tv[2], pLenSq ); + + nClip.A = n.LineProject( pClip.A ); + nClip.B = n.LineProject( pClip.B ); + + return true; +} + + +double PNS_DIFF_PAIR::Skew() const +{ + return m_p.Length() - m_n.Length(); +} + + +void PNS_DIFF_PAIR::CoupledSegmentPairs( COUPLED_SEGMENTS_VEC& aPairs ) const +{ + SHAPE_LINE_CHAIN p( m_p ); + SHAPE_LINE_CHAIN n( m_n ); + + p.Simplify(); + n.Simplify(); + + for( int i = 0; i < p.SegmentCount(); i++ ) + { + for( int j = 0; j < n.SegmentCount(); j++ ) + { + SEG sp = p.CSegment( i ); + SEG sn = n.CSegment( j ); + + SEG p_clip, n_clip; + + int64_t dist = std::abs( sp.Distance( sn ) - m_width ); + + if( sp.ApproxParallel( sn ) && m_gapConstraint.Matches( dist ) && commonParallelProjection( sp, sn, p_clip, n_clip ) ) + { + const COUPLED_SEGMENTS spair( p_clip, sp, i, n_clip, sn, j ); + aPairs.push_back( spair ); + } + } + } +} + + +int64_t PNS_DIFF_PAIR::CoupledLength( const SHAPE_LINE_CHAIN& aP, const SHAPE_LINE_CHAIN& aN ) const +{ + int64_t total = 0; + + for( int i = 0; i < aP.SegmentCount(); i++ ) + { + for( int j = 0; j < aN.SegmentCount(); j++ ) + { + SEG sp = aP.CSegment( i ); + SEG sn = aN.CSegment( j ); + + SEG p_clip, n_clip; + + int64_t dist = std::abs( sp.Distance(sn) - m_width ); + + if( sp.ApproxParallel( sn ) && m_gapConstraint.Matches( dist ) && + commonParallelProjection( sp, sn, p_clip, n_clip ) ) + total += p_clip.Length(); + } + } + + return total; +} + + +double PNS_DIFF_PAIR::CoupledLength() const +{ + COUPLED_SEGMENTS_VEC pairs; + + CoupledSegmentPairs( pairs ); + + double l = 0.0; + for( unsigned int i = 0; i < pairs.size(); i++ ) + l += pairs[i].coupledP.Length(); + + return l; +} + + +double PNS_DIFF_PAIR::CoupledLengthFactor() const +{ + double t = TotalLength(); + + if( t == 0.0 ) + return 0.0; + + return CoupledLength() / t; +} + + +double PNS_DIFF_PAIR::TotalLength() const +{ + double lenP = m_p.Length(); + double lenN = m_n.Length(); + + return (lenN + lenP ) / 2.0; +} + + +int PNS_DIFF_PAIR::CoupledLength ( const SEG& aP, const SEG& aN ) const +{ + SEG p_clip, n_clip; + int64_t dist = std::abs( aP.Distance( aN ) - m_width ); + + if( aP.ApproxParallel( aN ) && m_gapConstraint.Matches( dist ) && + commonParallelProjection ( aP, aN, p_clip, n_clip ) ) + return p_clip.Length(); + + return 0; +} diff --git a/pcbnew/router/pns_diff_pair.h b/pcbnew/router/pns_diff_pair.h new file mode 100644 index 0000000000..3b6a591839 --- /dev/null +++ b/pcbnew/router/pns_diff_pair.h @@ -0,0 +1,454 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + + +#ifndef __PNS_DIFF_PAIR_H +#define __PNS_DIFF_PAIR_H + +#include + +#include +#include + +#include "pns_line.h" +#include "pns_via.h" + +#include "ranged_num.h" + +class PNS_DIFF_PAIR; + +/** + * Class PNS_DP_GATEWAY + * + * Defines a "gateway" for routing a differential pair - e.g. a pair of points (anchors) with certain + * orientation, spacing and (optionally) predefined entry paths. The routing algorithm connects such + * gateways with parallel lines, thus creating a difrerential pair. + **/ +class PNS_DP_GATEWAY { +public: + PNS_DP_GATEWAY( const VECTOR2I& aAnchorP, + const VECTOR2I& aAnchorN, + bool aIsDiagonal, + int aAllowedEntryAngles = DIRECTION_45::ANG_OBTUSE, + int aPriority = 0 ) + : m_anchorP( aAnchorP ), + m_anchorN( aAnchorN ), + m_isDiagonal( aIsDiagonal ), + m_allowedEntryAngles( aAllowedEntryAngles ), + m_priority( aPriority ) + { + m_hasEntryLines = false; + } + + ~PNS_DP_GATEWAY() + { + } + + /** + * Function IsDiagonal() + * + * @return true, if the gateway anchors lie on a diagonal line + */ + bool IsDiagonal() const + { + return m_isDiagonal; + } + + const VECTOR2I& AnchorP() const { return m_anchorP; } + + const VECTOR2I& AnchorN() const { return m_anchorN; } + + /** + * Function AllowedAngles() + * + * @return a mask of 45-degree entry directoins allowed for the + * gateway. + */ + int AllowedAngles () const { return m_allowedEntryAngles; } + + /** + * Function Priority() + * + * @return priority/score value for gateway matching + */ + int Priority() const + { + return m_priority; + } + + void SetPriority(int aPriority) + { + m_priority = aPriority; + } + + void SetEntryLines( const SHAPE_LINE_CHAIN& aEntryP, const SHAPE_LINE_CHAIN& aEntryN ) + { + m_entryP = aEntryP; + m_entryN = aEntryN; + m_hasEntryLines = true; + } + + const SHAPE_LINE_CHAIN& EntryP() const { return m_entryP; } + const SHAPE_LINE_CHAIN& EntryN() const { return m_entryN; } + const PNS_DIFF_PAIR Entry() const ; + + void Reverse(); + + bool HasEntryLines () const + { + return m_hasEntryLines; + } + +private: + SHAPE_LINE_CHAIN m_entryP, m_entryN; + bool m_hasEntryLines; + VECTOR2I m_anchorP, m_anchorN; + bool m_isDiagonal; + int m_allowedEntryAngles; + int m_priority; +}; + +/** + * Class PNS_DP_PRIMITIVE_PAIR + * + * Stores staring/ending primitives (pads, vias or segments) for a differential pair. + **/ +class PNS_DP_PRIMITIVE_PAIR +{ +public: + PNS_DP_PRIMITIVE_PAIR(): + m_primP( NULL ), m_primN( NULL ) {}; + + PNS_DP_PRIMITIVE_PAIR( const PNS_DP_PRIMITIVE_PAIR& aOther ); + PNS_DP_PRIMITIVE_PAIR( PNS_ITEM* aPrimP, PNS_ITEM* aPrimN ); + PNS_DP_PRIMITIVE_PAIR( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ); + + ~PNS_DP_PRIMITIVE_PAIR(); + + void SetAnchors( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ); + + const VECTOR2I& AnchorP() const { return m_anchorP; } + const VECTOR2I& AnchorN() const { return m_anchorN; } + + PNS_DP_PRIMITIVE_PAIR& operator=( const PNS_DP_PRIMITIVE_PAIR& aOther ); + + PNS_ITEM* PrimP() const { return m_primP; } + PNS_ITEM* PrimN() const { return m_primN; } + + bool Directional() const; + + DIRECTION_45 DirP() const; + DIRECTION_45 DirN() const; + +private: + DIRECTION_45 anchorDirection( PNS_ITEM* aItem, const VECTOR2I& aP ) const; + + PNS_ITEM* m_primP; + PNS_ITEM* m_primN; + VECTOR2I m_anchorP, m_anchorN; +}; + +/** + * Class PNS_GATEWAYS + * + * A set of gateways calculated for the cursor or starting/ending primitive pair. + **/ + +class PNS_DP_GATEWAYS +{ + + public: + PNS_DP_GATEWAYS ( int aGap ): + m_gap(aGap), m_viaGap( aGap ) {}; + + void SetGap ( int aGap ) { + m_gap = aGap; + m_viaGap = aGap; + } + + void Clear() + { + m_gateways.clear(); + } + + void SetFitVias ( bool aEnable, int aDiameter = 0, int aViaGap = -1 ) + { + m_fitVias = aEnable; + m_viaDiameter = aDiameter; + if(aViaGap < 0) + m_viaGap = m_gap; + else + m_viaGap = aViaGap; + } + + + void BuildForCursor ( const VECTOR2I& aCursorPos ); + void BuildOrthoProjections ( PNS_DP_GATEWAYS &aEntries, const VECTOR2I& aCursorPos, int aOrthoScore ); + void BuildGeneric ( const VECTOR2I& p0_p, const VECTOR2I& p0_n, bool aBuildEntries = false, bool aViaMode = false ); + void BuildFromPrimitivePair( PNS_DP_PRIMITIVE_PAIR aPair, bool aPreferDiagonal ); + + bool FitGateways ( PNS_DP_GATEWAYS& aEntry, PNS_DP_GATEWAYS& aTarget, bool aPrefDiagonal, PNS_DIFF_PAIR& aDp ); + + std::vector& Gateways() + { + return m_gateways; + } + + private: + + struct DP_CANDIDATE + { + SHAPE_LINE_CHAIN p, n; + VECTOR2I gw_p, gw_n; + int score; + }; + + bool checkDiagonalAlignment ( const VECTOR2I& a, const VECTOR2I& b) const; + void buildDpContinuation ( PNS_DP_PRIMITIVE_PAIR aPair, bool aIsDiagonal ); + void buildEntries ( const VECTOR2I& p0_p, const VECTOR2I& p0_n ); + + int m_gap; + int m_viaGap; + int m_viaDiameter; + bool m_fitVias; + + std::vector m_gateways; +}; + + +/** + * Class PNS_DIFF_PAIR + * + * Basic class for a differential pair. Stores two PNS_LINEs (for positive and negative nets, respectively), + * the gap and coupling constraints. + **/ +class PNS_DIFF_PAIR : public PNS_ITEM { + +public: + struct COUPLED_SEGMENTS { + COUPLED_SEGMENTS ( const SEG& aCoupledP, const SEG& aParentP, int aIndexP, + const SEG& aCoupledN, const SEG& aParentN, int aIndexN ) : + coupledP ( aCoupledP ), + coupledN ( aCoupledN ), + parentP ( aParentP ), + parentN ( aParentN ), + indexP ( aIndexP ), + indexN ( aIndexN ) + {} + + SEG coupledP; + SEG coupledN; + SEG parentP; + SEG parentN; + int indexP; + int indexN; + }; + + typedef std::vector COUPLED_SEGMENTS_VEC; + + PNS_DIFF_PAIR ( ) : PNS_ITEM ( DIFF_PAIR ), m_hasVias (false) {} + + PNS_DIFF_PAIR ( int aGap ) : + PNS_ITEM ( DIFF_PAIR ), + m_hasVias (false) + { + m_gapConstraint = aGap; + } + + PNS_DIFF_PAIR ( const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN& aN, int aGap = 0 ): + PNS_ITEM ( DIFF_PAIR ), + m_n (aN), + m_p (aP), + m_hasVias (false) + { + m_gapConstraint = aGap; + } + + PNS_DIFF_PAIR ( const PNS_LINE &aLineP, const PNS_LINE &aLineN, int aGap = 0 ): + PNS_ITEM ( DIFF_PAIR ), + m_line_p ( aLineP ), + m_line_n ( aLineN ), + m_hasVias (false) + { + m_gapConstraint = aGap; + m_net_p = aLineP.Net(); + m_net_n = aLineN.Net(); + m_p = aLineP.CLine(); + m_n = aLineN.CLine(); + } + + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && DIFF_PAIR == aItem->Kind(); + } + + PNS_DIFF_PAIR * Clone() const { assert(false); return NULL; } + + static PNS_DIFF_PAIR* AssembleDp ( PNS_LINE *aLine ); + + void SetShape ( const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN& aN, bool aSwapLanes = false) + { + if (aSwapLanes) + { + m_p = aN; + m_n = aP; + } else { + m_p = aP; + m_n = aN; + } + } + + void SetShape ( const PNS_DIFF_PAIR& aPair ) + { + m_p = aPair.m_p; + m_n = aPair.m_n; + } + + void SetNets ( int aP, int aN ) + { + m_net_p = aP; + m_net_n = aN; + } + + void SetWidth ( int aWidth ) + { + m_width = aWidth; + } + + int Width() const { return m_width; } + + void SetGap ( int aGap) + { + m_gap = aGap; + m_gapConstraint = RANGED_NUM ( m_gap, 10000, 10000 ); + } + + int Gap() const { + return m_gap; + } + + void AppendVias ( const PNS_VIA &aViaP, const PNS_VIA& aViaN ) + { + m_hasVias = true; + m_via_p = aViaP; + m_via_n = aViaN; + } + + void RemoveVias () + { + m_hasVias = false; + } + + bool EndsWithVias() const + { + return m_hasVias; + } + + int NetP() const + { + return m_net_p; + } + + int NetN() const + { + return m_net_n; + } + + PNS_LINE& PLine() + { + if ( !m_line_p.IsLinked ( ) ) + updateLine(m_line_p, m_p, m_net_p, m_via_p ); + return m_line_p; + } + + PNS_LINE& NLine() + { + if ( !m_line_n.IsLinked ( ) ) + updateLine(m_line_n, m_n, m_net_n, m_via_n ); + return m_line_n; + } + + PNS_DP_PRIMITIVE_PAIR EndingPrimitives(); + + double CoupledLength() const; + double TotalLength() const; + double CoupledLengthFactor () const; + double Skew () const; + + void CoupledSegmentPairs ( COUPLED_SEGMENTS_VEC& aPairs ) const; + + void Clear() + { + m_n.Clear(); + m_p.Clear(); + } + + void Append (const PNS_DIFF_PAIR& aOther ) + { + m_n.Append ( aOther.m_n ); + m_p.Append ( aOther.m_p ); + } + + bool Empty() const + { + return (m_n.SegmentCount() == 0) || (m_p.SegmentCount() == 0); + } + const SHAPE_LINE_CHAIN& CP() const { return m_p; } + const SHAPE_LINE_CHAIN& CN() const { return m_n; } + + bool BuildInitial ( PNS_DP_GATEWAY& aEntry, PNS_DP_GATEWAY& aTarget, bool aPrefDiagonal ); + bool CheckConnectionAngle ( const PNS_DIFF_PAIR &aOther, int allowedAngles ) const; + int CoupledLength ( const SEG& aP, const SEG& aN ) const; + + int64_t CoupledLength ( const SHAPE_LINE_CHAIN& aP, const SHAPE_LINE_CHAIN& aN ) const; + + const RANGED_NUM GapConstraint() const { + return m_gapConstraint; + } + +private: + + void updateLine( PNS_LINE &aLine, const SHAPE_LINE_CHAIN& aShape, int aNet, PNS_VIA& aVia ) + { + aLine.SetShape( aShape ); + aLine.SetWidth( m_width ); + aLine.SetNet(aNet); + aLine.SetLayer (Layers().Start()); + + if(m_hasVias) + aLine.AppendVia ( aVia ); + } + + SHAPE_LINE_CHAIN m_n, m_p; + PNS_LINE m_line_p, m_line_n; + PNS_VIA m_via_p, m_via_n; + + bool m_hasVias; + int m_net_p, m_net_n; + int m_width; + int m_gap; + int m_viaGap; + int m_maxUncoupledLength; + int m_chamferLimit; + RANGED_NUM m_gapConstraint; +}; + + +#endif diff --git a/pcbnew/router/pns_diff_pair_placer.cpp b/pcbnew/router/pns_diff_pair_placer.cpp new file mode 100644 index 0000000000..e8a3d4f27f --- /dev/null +++ b/pcbnew/router/pns_diff_pair_placer.cpp @@ -0,0 +1,797 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_walkaround.h" +#include "pns_shove.h" +#include "pns_utils.h" +#include "pns_router.h" +#include "pns_diff_pair_placer.h" +#include "pns_solid.h" +#include "pns_topology.h" + +using boost::optional; + +PNS_DIFF_PAIR_PLACER::PNS_DIFF_PAIR_PLACER( PNS_ROUTER* aRouter ) : + PNS_PLACEMENT_ALGO ( aRouter ) +{ + m_initialDiagonal = false; + m_startDiagonal = false; + m_world = NULL; + m_shove = NULL; + m_currentNode = NULL; + m_idle = true; +} + +PNS_DIFF_PAIR_PLACER::~PNS_DIFF_PAIR_PLACER() +{ + if( m_shove ) + delete m_shove; +} + + +void PNS_DIFF_PAIR_PLACER::setWorld( PNS_NODE* aWorld ) +{ + m_world = aWorld; +} + + +const PNS_VIA PNS_DIFF_PAIR_PLACER::makeVia( const VECTOR2I& aP, int aNet ) +{ + const PNS_LAYERSET layers( m_sizes.GetLayerTop(), m_sizes.GetLayerBottom() ); + + PNS_VIA v( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), -1, m_sizes.ViaType() ); + v.SetNet( aNet ); + + return v; +} + + +void PNS_DIFF_PAIR_PLACER::SetOrthoMode ( bool aOrthoMode ) +{ + m_orthoMode = aOrthoMode; + + if( !m_idle ) + Move( m_currentEnd, NULL ); +} + + +bool PNS_DIFF_PAIR_PLACER::ToggleVia( bool aEnabled ) +{ + m_placingVia = aEnabled; + + if( !m_idle ) + Move( m_currentEnd, NULL ); + + return true; +} + + +bool PNS_DIFF_PAIR_PLACER::rhMarkObstacles( const VECTOR2I& aP ) +{ + if( !routeHead( aP ) ) + return false; + + bool collP = m_currentNode->CheckColliding( &m_currentTrace.PLine() ); + bool collN = m_currentNode->CheckColliding( &m_currentTrace.NLine() ); + + m_fitOk = !( collP || collN ) ; + return m_fitOk; +} + + +bool PNS_DIFF_PAIR_PLACER::propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNewP ) +{ + PNS_VIA virtHead = makeVia( aP, -1 ); + + if( m_placingVia ) + virtHead.SetDiameter( viaGap() + 2 * virtHead.Diameter() ); + else + { + virtHead.SetLayer( m_currentLayer ); + virtHead.SetDiameter( m_sizes.DiffPairGap() + 2 * m_sizes.TrackWidth() ); + } + + VECTOR2I lead( 0, 0 );// = aP - m_currentStart ; + VECTOR2I force; + bool solidsOnly = true; + + if( m_currentMode == RM_MarkObstacles ) + { + aNewP = aP; + return true; + } + else if( m_currentMode == RM_Walkaround ) + { + solidsOnly = false; + } + + // fixme: I'm too lazy to do it well. Circular approximaton will do for the moment. + if( virtHead.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) ) + { + aNewP = aP + force; + return true; + } + + return false; +} + + +bool PNS_DIFF_PAIR_PLACER::attemptWalk ( PNS_NODE* aNode, PNS_DIFF_PAIR* aCurrent, PNS_DIFF_PAIR& aWalk, bool aPFirst, bool aWindCw, bool aSolidsOnly ) +{ + PNS_WALKAROUND walkaround( aNode, Router() ); + PNS_WALKAROUND::WALKAROUND_STATUS wf1; + + Router()->GetClearanceFunc()->OverrideClearance( true, aCurrent->NetP(), aCurrent->NetN(), aCurrent->Gap() ); + + walkaround.SetSolidsOnly( aSolidsOnly ); + walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() ); + + PNS_SHOVE shove( aNode, Router() ); + PNS_LINE walkP, walkN; + + aWalk = *aCurrent; + + int iter = 0; + + PNS_DIFF_PAIR cur( *aCurrent ); + + bool currentIsP = aPFirst; + + int mask = aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY; + + do + { + PNS_LINE preWalk = ( currentIsP ? cur.PLine() : cur.NLine() ); + PNS_LINE preShove = ( currentIsP ? cur.NLine() : cur.PLine() ); + PNS_LINE postWalk; + + if( !aNode->CheckColliding ( &preWalk, mask ) ) + { + currentIsP = !currentIsP; + + if( !aNode->CheckColliding( &preShove, mask ) ) + break; + else + continue; + } + + wf1 = walkaround.Route( preWalk, postWalk, false ); + + if( wf1 != PNS_WALKAROUND::DONE ) + return false; + + PNS_LINE postShove( preShove ); + + shove.ForceClearance( true, cur.Gap() - 2 * PNS_HULL_MARGIN ); + + PNS_SHOVE::SHOVE_STATUS sh1; + + sh1 = shove.ProcessSingleLine( &postWalk, &preShove, &postShove ); + + if( sh1 != PNS_SHOVE::SH_OK ) + return false; + + postWalk.Line().Simplify(); + postShove.Line().Simplify(); + + cur.SetShape( postWalk.CLine(), postShove.CLine(), !currentIsP ); + + currentIsP = !currentIsP; + + if( !aNode->CheckColliding( &postShove, mask ) ) + break; + + iter++; + } + while( iter < 3 ); + + if( iter == 3 ) + return false; + + aWalk.SetShape( cur.CP(), cur.CN() ); + Router()->GetClearanceFunc()->OverrideClearance( false ); + + return true; +} + + +bool PNS_DIFF_PAIR_PLACER::tryWalkDp( PNS_NODE* aNode, PNS_DIFF_PAIR &aPair, bool aSolidsOnly ) +{ + PNS_DIFF_PAIR best; + double bestScore = 100000000000000.0; + + for( int attempt = 0; attempt <= 1; attempt++ ) + { + PNS_DIFF_PAIR p; + PNS_NODE *tmp = m_currentNode->Branch(); + + bool pfirst = attempt % 2 ? true : false; + bool wind_cw = attempt / 2 ? true : false; + + if( attemptWalk ( tmp, &aPair, p, pfirst, wind_cw, aSolidsOnly ) ) + { + // double len = p.TotalLength(); + double cl = p.CoupledLength(); + double skew = p.Skew(); + + double score = cl + fabs(skew) * 3.0; + + if( score < bestScore ) + { + bestScore = score; + best = p; + } + } + + delete tmp; + } + + if( bestScore > 0.0 ) + { + PNS_OPTIMIZER optimizer( m_currentNode ); + + aPair.SetShape( best ); + optimizer.Optimize( &aPair ); + + return true; + } + + return false; +} + + +bool PNS_DIFF_PAIR_PLACER::rhWalkOnly( const VECTOR2I& aP ) +{ + if( !routeHead ( aP ) ) + return false; + + m_fitOk = tryWalkDp( m_currentNode, m_currentTrace, false ); + + return m_fitOk; +} + + +bool PNS_DIFF_PAIR_PLACER::route( const VECTOR2I& aP ) +{ + switch( m_currentMode ) + { + case RM_MarkObstacles: + return rhMarkObstacles( aP ); + case RM_Walkaround: + return rhWalkOnly ( aP ); + case RM_Shove: + return rhShoveOnly ( aP ); + default: + break; + } + + return false; +} + + +bool PNS_DIFF_PAIR_PLACER::rhShoveOnly( const VECTOR2I& aP ) +{ + m_currentNode = m_shove->CurrentNode(); + + bool ok = routeHead ( aP ); + + m_fitOk = false; + + if( !ok ) + return false; + + if( !tryWalkDp( m_currentNode, m_currentTrace, true ) ) + return false; + + PNS_LINE pLine( m_currentTrace.PLine() ); + PNS_LINE nLine( m_currentTrace.NLine() ); + PNS_ITEMSET head; + + head.Add( &pLine ); + head.Add( &nLine ); + + PNS_SHOVE::SHOVE_STATUS status = m_shove->ShoveMultiLines( head ); + + m_currentNode = m_shove->CurrentNode(); + + if( status == PNS_SHOVE::SH_OK ) + { + m_currentNode = m_shove->CurrentNode(); + + if( !m_currentNode->CheckColliding( &m_currentTrace.PLine() ) && + !m_currentNode->CheckColliding( &m_currentTrace.NLine() ) ) + { + m_fitOk = true; + } + } + + return m_fitOk; +} + + +const PNS_ITEMSET PNS_DIFF_PAIR_PLACER::Traces() +{ + PNS_ITEMSET t; + + t.Add( const_cast( &m_currentTrace.PLine() ) ); + t.Add( const_cast( &m_currentTrace.NLine() ) ); + + return t; +} + + +void PNS_DIFF_PAIR_PLACER::FlipPosture() +{ + m_startDiagonal = !m_startDiagonal; + + if( !m_idle ) + Move( m_currentEnd, NULL ); +} + + +PNS_NODE* PNS_DIFF_PAIR_PLACER::CurrentNode( bool aLoopsRemoved ) const +{ + if( m_lastNode ) + return m_lastNode; + + return m_currentNode; +} + + +bool PNS_DIFF_PAIR_PLACER::SetLayer( int aLayer ) +{ + if( m_idle ) + { + m_currentLayer = aLayer; + return true; + } else if( m_chainedPlacement ) + return false; + else if( !m_prevPair ) + return false; + else if( m_prevPair->PrimP() || ( m_prevPair->PrimP()->OfKind( PNS_ITEM::VIA ) && + m_prevPair->PrimP()->Layers().Overlaps( aLayer ) ) ) + { + m_currentLayer = aLayer; + m_start = *m_prevPair; + initPlacement( false ); + Move( m_currentEnd, NULL ); + return true; + } + + return false; +} + + +int PNS_DIFF_PAIR_PLACER::matchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ) +{ + int rv = 0; + + if( aNetName.EndsWith( "+" ) ) + { + aComplementNet = "-"; + rv = 1; + } + else if( aNetName.EndsWith( "_P" ) ) + { + aComplementNet = "_N"; + rv = 1; + } + else if( aNetName.EndsWith( "-" ) ) + { + aComplementNet = "+"; + rv = -1; + } + else if( aNetName.EndsWith( "_N" ) ) + { + aComplementNet = "_P"; + rv = -1; + } + + if( rv != 0 ) + { + aBaseDpName = aNetName.Left( aNetName.Length() - aComplementNet.Length() ); + } + + return rv; +} + + +OPT_VECTOR2I PNS_DIFF_PAIR_PLACER::getDanglingAnchor( PNS_NODE* aNode, PNS_ITEM* aItem ) +{ + switch( aItem->Kind() ) + { + case PNS_ITEM::VIA: + case PNS_ITEM::SOLID: + return aItem->Anchor( 0 ); + + case PNS_ITEM::SEGMENT: + { + PNS_SEGMENT* s =static_cast( aItem ); + + PNS_JOINT* jA = aNode->FindJoint( s->Seg().A, s ); + PNS_JOINT* jB = aNode->FindJoint( s->Seg().B, s ); + + if( jA->LinkCount() == 1 ) + return s->Seg().A; + else if( jB->LinkCount() == 1 ) + return s->Seg().B; + else + return OPT_VECTOR2I(); + } + + default: + return OPT_VECTOR2I(); + break; + } +} + + +bool PNS_DIFF_PAIR_PLACER::findDpPrimitivePair( const VECTOR2I& aP, PNS_ITEM* aItem, PNS_DP_PRIMITIVE_PAIR& aPair ) +{ + if( !aItem || !aItem->Parent() || !aItem->Parent()->GetNet() ) + return false; + + wxString netNameP = aItem->Parent()->GetNet()->GetNetname(); + wxString netNameN, netNameBase; + + BOARD* brd = Router()->GetBoard(); + PNS_ITEM *primRef = NULL, *primP = NULL, *primN = NULL; + + int refNet; + + wxString suffix; + + int r = matchDpSuffix ( netNameP, suffix, netNameBase ); + + if( r == 0 ) + return false; + else if( r == 1 ) + { + primRef = primP = static_cast( aItem ); + netNameN = netNameBase + suffix; + } + else + { + primRef = primN = static_cast( aItem ); + netNameN = netNameP; + netNameP = netNameBase + suffix; + } + + int netP = brd->FindNet( netNameP )->GetNet(); + int netN = brd->FindNet( netNameN )->GetNet(); + + if( primP ) + refNet = netN; + else + refNet = netP; + + + std::set items; + + OPT_VECTOR2I refAnchor = getDanglingAnchor( m_currentNode, primRef ); + + if( !refAnchor ) + return false; + + m_currentNode->AllItemsInNet( refNet, items ); + double bestDist = std::numeric_limits::max(); + bool found = false; + + BOOST_FOREACH(PNS_ITEM* item, items ) + { + if( item->Kind() == aItem->Kind() ) + { + OPT_VECTOR2I anchor = getDanglingAnchor( m_currentNode, item ); + if( !anchor ) + continue; + + double dist = ( *anchor - *refAnchor ).EuclideanNorm(); + + if( dist < bestDist ) + { + found = true; + bestDist = dist; + + if( refNet == netP ) + { + aPair = PNS_DP_PRIMITIVE_PAIR ( item, primRef ); + aPair.SetAnchors( *anchor, *refAnchor ); + } + else + { + aPair = PNS_DP_PRIMITIVE_PAIR( primRef, item ); + aPair.SetAnchors( *refAnchor, *anchor ); + } + } + } + } + + return found; +} + + +int PNS_DIFF_PAIR_PLACER::viaGap() const +{ + return m_sizes.DiffPairViaGap(); +} + + +int PNS_DIFF_PAIR_PLACER::gap() const +{ + return m_sizes.DiffPairGap() + m_sizes.DiffPairWidth(); +} + + +bool PNS_DIFF_PAIR_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p( aP ); + + bool split; + + if( Router()->SnappingEnabled() ) + p = Router()->SnapToItem( aStartItem, aP, split ); + + if( !aStartItem ) + { + Router()->SetFailureReason( _( "Can't start a differential pair " + " in the middle of nowhere." ) ); + return false; + } + + PNS_DP_PRIMITIVE_PAIR start; + + m_currentNode = Router()->GetWorld(); + + if( !findDpPrimitivePair( aP, aStartItem, m_start ) ) + { + Router()->SetFailureReason( _( "Unable to find complementary differential pair " + "net. Make sure the names of the nets belonging " + "to a differential pair end with either _N/_P or +/-." ) ); + return false; + } + + m_netP = m_start.PrimP()->Net(); + m_netN = m_start.PrimN()->Net(); + + m_currentStart = p; + m_currentEnd = p; + m_placingVia = false; + m_chainedPlacement = false; + + initPlacement( false ); + return true; +} + + +void PNS_DIFF_PAIR_PLACER::initPlacement( bool aSplitSeg ) +{ + m_idle = false; + m_orthoMode = false; + m_currentEndItem = NULL; + m_startDiagonal = m_initialDiagonal; + + PNS_NODE* world = Router()->GetWorld(); + + world->KillChildren(); + PNS_NODE* rootNode = world->Branch(); + + setWorld( rootNode ); + + m_lastNode = NULL; + m_currentNode = rootNode; + m_currentMode = Settings().Mode(); + + if( m_shove ) + delete m_shove; + + m_shove = NULL; + + if( m_currentMode == RM_Shove || m_currentMode == RM_Smart ) + { + m_shove = new PNS_SHOVE( m_currentNode, Router() ); + } +} + +bool PNS_DIFF_PAIR_PLACER::routeHead( const VECTOR2I& aP ) +{ + m_fitOk = false; + + PNS_DP_GATEWAYS gwsEntry( gap() ); + PNS_DP_GATEWAYS gwsTarget( gap() ); + + if( !m_prevPair ) + m_prevPair = m_start; + + gwsEntry.BuildFromPrimitivePair( *m_prevPair, m_startDiagonal ); + + PNS_DP_PRIMITIVE_PAIR target; + + if( findDpPrimitivePair ( aP, m_currentEndItem, target ) ) + { + gwsTarget.BuildFromPrimitivePair( target, m_startDiagonal ); + m_snapOnTarget = true; + } else { + VECTOR2I fp; + + if( !propagateDpHeadForces( aP, fp ) ) + return false; + + gwsTarget.SetFitVias( m_placingVia, m_sizes.ViaDiameter(), viaGap() ); + gwsTarget.BuildForCursor( fp ); + gwsTarget.BuildOrthoProjections( gwsEntry, fp, m_orthoMode ? 200 : -200 ); + m_snapOnTarget = false; + } + + m_currentTrace = PNS_DIFF_PAIR(); + m_currentTrace.SetGap( gap() ); + m_currentTrace.SetLayer( m_currentLayer ); + + if ( gwsEntry.FitGateways( gwsEntry, gwsTarget, m_startDiagonal, m_currentTrace ) ) + { + m_currentTrace.SetNets( m_netP, m_netN ); + m_currentTrace.SetWidth( m_sizes.DiffPairWidth() ); + m_currentTrace.SetGap( m_sizes.DiffPairGap() ); + + if( m_placingVia ) + { + m_currentTrace.AppendVias ( makeVia ( m_currentTrace.CP().CPoint(-1), m_netP ), + makeVia ( m_currentTrace.CN().CPoint(-1), m_netN ) ); + } + + return true; + } + + return false; +} + + +bool PNS_DIFF_PAIR_PLACER::Move( const VECTOR2I& aP , PNS_ITEM* aEndItem ) +{ + m_currentEndItem = aEndItem; + m_fitOk = false; + + delete m_lastNode; + m_lastNode = NULL; + + if( !route( aP ) ) + return false; + + PNS_NODE* latestNode = m_currentNode; + m_lastNode = latestNode->Branch(); + + assert( m_lastNode != NULL ); + m_currentEnd = aP; + + updateLeadingRatLine(); + + return true; +} + + +void PNS_DIFF_PAIR_PLACER::UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ) +{ + m_sizes = aSizes; + + if( !m_idle ) + { + initPlacement(); + Move( m_currentEnd, NULL ); + } +} + + +bool PNS_DIFF_PAIR_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + if( !m_fitOk ) + return false; + + if( m_currentTrace.CP().SegmentCount() < 1 || + m_currentTrace.CN().SegmentCount() < 1 ) + return false; + + if( m_currentTrace.CP().SegmentCount() > 1 ) + m_initialDiagonal = !DIRECTION_45( m_currentTrace.CP().CSegment( -2 ) ).IsDiagonal(); + + PNS_TOPOLOGY topo( m_lastNode ); + + if( !m_snapOnTarget && !m_currentTrace.EndsWithVias() ) + { + SHAPE_LINE_CHAIN newP ( m_currentTrace.CP() ); + SHAPE_LINE_CHAIN newN ( m_currentTrace.CN() ); + + if( newP.SegmentCount() > 1 && newN.SegmentCount() > 1 ) + { + newP.Remove( -1, -1 ); + newN.Remove( -1, -1 ); + } + + m_currentTrace.SetShape( newP, newN ); + } + + if( m_currentTrace.EndsWithVias() ) + { + m_lastNode->Add( m_currentTrace.PLine().Via().Clone() ); + m_lastNode->Add( m_currentTrace.NLine().Via().Clone() ); + m_chainedPlacement = false; + } else + m_chainedPlacement = !m_snapOnTarget; + + PNS_LINE lineP( m_currentTrace.PLine() ); + PNS_LINE lineN( m_currentTrace.NLine() ); + + m_lastNode->Add( &lineP ); + m_lastNode->Add( &lineN ); + + topo.SimplifyLine( &lineP ); + topo.SimplifyLine( &lineN ); + + m_prevPair = m_currentTrace.EndingPrimitives(); + + Router()->CommitRouting( m_lastNode ); + + m_lastNode = NULL; + m_placingVia = false; + + if( m_snapOnTarget ) + { + m_idle = true; + return true; + } + else + { + initPlacement(); + return false; + } +} + + +void PNS_DIFF_PAIR_PLACER::GetModifiedNets( std::vector &aNets ) const +{ + aNets.push_back( m_netP ); + aNets.push_back( m_netN ); +} + + +void PNS_DIFF_PAIR_PLACER::updateLeadingRatLine() +{ + SHAPE_LINE_CHAIN ratLineN, ratLineP; + PNS_TOPOLOGY topo( m_lastNode ); + + if( topo.LeadingRatLine( &m_currentTrace.PLine(), ratLineP ) ) + { + Router()->DisplayDebugLine( ratLineP, 1, 10000 ); + } + + if( topo.LeadingRatLine ( &m_currentTrace.NLine(), ratLineN ) ) + { + Router()->DisplayDebugLine( ratLineN, 3, 10000 ); + } +} diff --git a/pcbnew/router/pns_diff_pair_placer.h b/pcbnew/router/pns_diff_pair_placer.h new file mode 100644 index 0000000000..19a43ccb6a --- /dev/null +++ b/pcbnew/router/pns_diff_pair_placer.h @@ -0,0 +1,302 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_DIFF_PLACER_H +#define __PNS_DIFF_PLACER_H + +#include + +#include +#include + +#include "pns_sizes_settings.h" +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_algo_base.h" +#include "pns_diff_pair.h" + +#include "pns_placement_algo.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; +class PNS_VIA; +class PNS_SIZES_SETTINGS; + + +/** + * Class PNS_LINE_PLACER + * + * Single track placement algorithm. Interactively routes a track. + * Applies shove and walkaround algorithms when needed. + */ + +class PNS_DIFF_PAIR_PLACER : public PNS_PLACEMENT_ALGO +{ +public: + PNS_DIFF_PAIR_PLACER( PNS_ROUTER* aRouter ); + ~PNS_DIFF_PAIR_PLACER(); + + /** + * Function Start() + * + * Starts routing a single track at point aP, taking item aStartItem as anchor + * (unless NULL). + */ + bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /** + * Function Move() + * + * Moves the end of the currently routed trace to the point aP, taking + * aEndItem as anchor (if not NULL). + * (unless NULL). + */ + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /** + * Function FixRoute() + * + * Commits the currently routed track to the parent node, taking + * aP as the final end point and aEndItem as the final anchor (if provided). + * @return true, if route has been commited. May return false if the routing + * result is violating design rules - in such case, the track is only committed + * if Settings.CanViolateDRC() is on. + */ + bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /** + * Function ToggleVia() + * + * Enables/disables a via at the end of currently routed trace. + */ + bool ToggleVia( bool aEnabled ); + + /** + * Function SetLayer() + * + * Sets the current routing layer. + */ + bool SetLayer( int aLayer ); + + /** + * Function Traces() + * + * Returns the complete routed line, as a single-member PNS_ITEMSET. + */ + const PNS_ITEMSET Traces(); + + /** + * Function CurrentEnd() + * + * Returns the current end of the line being placed. It may not be equal + * to the cursor position due to collisions. + */ + const VECTOR2I& CurrentEnd() const + { + return m_currentEnd; + } + + /** + * Function CurrentNet() + * + * Returns the net code of currently routed track. + */ + int CurrentNet() const + { + return m_currentNet; + } + + /** + * Function CurrentLayer() + * + * Returns the layer of currently routed track. + */ + int CurrentLayer() const + { + return m_currentLayer; + } + + /** + * Function CurrentNode() + * + * Returns the most recent world state. + */ + PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const; + + /** + * Function FlipPosture() + * + * Toggles the current posture (straight/diagonal) of the trace head. + */ + void FlipPosture(); + + /** + * Function UpdateSizes() + * + * Performs on-the-fly update of the width, via diameter & drill size from + * a settings class. Used to dynamically change these parameters as + * the track is routed. + */ + void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ); + + bool IsPlacingVia() const { return m_placingVia; } + + void SetOrthoMode( bool aOrthoMode ); + + void GetModifiedNets( std::vector& aNets ) const; + +private: + int viaGap() const; + int gap() const; + + /** + * Function route() + * + * Re-routes the current track to point aP. Returns true, when routing has + * completed successfully (i.e. the trace end has reached point aP), and false + * if the trace was stuck somewhere on the way. May call routeStep() + * repetitively due to mouse smoothing. + * @param aP ending point of current route. + * @return true, if the routing is complete. + */ + bool route( const VECTOR2I& aP ); + + /** + * Function updateLeadingRatLine() + * + * Draws the "leading" ratsnest line, which connects the end of currently + * routed track and the nearest yet unrouted item. If the routing for + * current net is complete, draws nothing. + */ + void updateLeadingRatLine(); + + /** + * Function setWorld() + * + * Sets the board to route. + */ + void setWorld( PNS_NODE* aWorld ); + + /** + * Function startPlacement() + * + * Initializes placement of a new line with given parameters. + */ + void initPlacement( bool aSplitSeg = false ); + + /** + * Function setInitialDirection() + * + * Sets preferred direction of the very first track segment to be laid. + * Used by posture switching mechanism. + */ + void setInitialDirection( const DIRECTION_45& aDirection ); + + + bool routeHead( const VECTOR2I& aP ); + bool tryWalkDp( PNS_NODE* aNode, PNS_DIFF_PAIR& aPair, bool aSolidsOnly ); + + ///> route step, walkaround mode + bool rhWalkOnly( const VECTOR2I& aP ); + + ///> route step, shove mode + bool rhShoveOnly ( const VECTOR2I& aP ); + + ///> route step, mark obstacles mode + bool rhMarkObstacles( const VECTOR2I& aP ); + + const PNS_VIA makeVia ( const VECTOR2I& aP, int aNet ); + + bool findDpPrimitivePair( const VECTOR2I& aP, PNS_ITEM* aItem, PNS_DP_PRIMITIVE_PAIR& aPair ); + OPT_VECTOR2I getDanglingAnchor( PNS_NODE* aNode, PNS_ITEM* aItem ); + int matchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ); + bool attemptWalk( PNS_NODE* aNode, PNS_DIFF_PAIR* aCurrent, PNS_DIFF_PAIR& aWalk, bool aPFirst, bool aWindCw, bool aSolidsOnly ); + bool propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNewP ); + + enum State { + RT_START = 0, + RT_ROUTE = 1, + RT_FINISH = 2 + }; + + State m_state; + + bool m_chainedPlacement; + bool m_initialDiagonal; + bool m_startDiagonal; + bool m_fitOk; + + int m_netP, m_netN; + + PNS_DP_PRIMITIVE_PAIR m_start; + boost::optional m_prevPair; + + ///> current algorithm iteration + int m_iteration; + + ///> pointer to world to search colliding items + PNS_NODE* m_world; + + ///> current routing start point (end of tail, beginning of head) + VECTOR2I m_p_start; + + ///> The shove engine + PNS_SHOVE* m_shove; + + ///> Current world state + PNS_NODE* m_currentNode; + + ///> Postprocessed world state (including marked collisions & removed loops) + PNS_NODE* m_lastNode; + + PNS_SIZES_SETTINGS m_sizes; + + ///> Are we placing a via? + bool m_placingVia; + + ///> current via diameter + int m_viaDiameter; + + ///> current via drill + int m_viaDrill; + + ///> current track width + int m_currentWidth; + + int m_currentNet; + int m_currentLayer; + + bool m_startsOnVia; + bool m_orthoMode; + bool m_snapOnTarget; + + VECTOR2I m_currentEnd, m_currentStart; + PNS_DIFF_PAIR m_currentTrace; + + PNS_ITEM* m_currentEndItem; + PNS_MODE m_currentMode; + + bool m_idle; +}; + +#endif // __PNS_LINE_PLACER_H diff --git a/pcbnew/router/pns_dp_meander_placer.cpp b/pcbnew/router/pns_dp_meander_placer.cpp new file mode 100644 index 0000000000..d92821a46c --- /dev/null +++ b/pcbnew/router/pns_dp_meander_placer.cpp @@ -0,0 +1,459 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_dp_meander_placer.h" +#include "pns_diff_pair.h" +#include "pns_router.h" +#include "pns_utils.h" + +using boost::optional; + +PNS_DP_MEANDER_PLACER::PNS_DP_MEANDER_PLACER( PNS_ROUTER* aRouter ) : + PNS_MEANDER_PLACER_BASE( aRouter ) +{ + m_world = NULL; + m_currentNode = NULL; +} + + +PNS_DP_MEANDER_PLACER::~PNS_DP_MEANDER_PLACER() +{ +} + + +const PNS_LINE PNS_DP_MEANDER_PLACER::Trace() const +{ + return m_currentTraceP; +} + + +PNS_NODE* PNS_DP_MEANDER_PLACER::CurrentNode( bool aLoopsRemoved ) const +{ + if( !m_currentNode ) + return m_world; + + return m_currentNode; +} + + +bool PNS_DP_MEANDER_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p; + + if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) ) + { + Router()->SetFailureReason( _( "Please select a track whose length you want to tune." ) ); + return false; + } + + m_initialSegment = static_cast( aStartItem ); + + p = m_initialSegment->Seg().NearestPoint( aP ); + + m_currentNode=NULL; + m_currentStart = p; + + m_world = Router()->GetWorld()->Branch(); + + PNS_TOPOLOGY topo( m_world ); + + if( !topo.AssembleDiffPair( m_initialSegment, m_originPair ) ) + { + Router()->SetFailureReason( _( "Unable to find complementary differential pair " + "net for length tuning. Make sure the names of the nets belonging " + "to a differential pair end with either _N/_P or +/-." ) ); + return false; + } + + m_originPair.SetGap( Router()->Sizes().DiffPairGap() ); + + if( !m_originPair.PLine().SegmentCount() || + !m_originPair.NLine().SegmentCount() ) + return false; + + m_tunedPathP = topo.AssembleTrivialPath( m_originPair.PLine().GetLink( 0 ) ); + m_tunedPathN = topo.AssembleTrivialPath( m_originPair.NLine().GetLink( 0 ) ); + + m_world->Remove( m_originPair.PLine() ); + m_world->Remove( m_originPair.NLine() ); + + m_currentWidth = m_originPair.Width(); + + return true; +} + + +void PNS_DP_MEANDER_PLACER::release() +{ + #if 0 + BOOST_FOREACH(PNS_MEANDER *m, m_meanders) + { + delete m; + } + + m_meanders.clear(); + #endif +} + + +int PNS_DP_MEANDER_PLACER::origPathLength() const +{ + int totalP = 0; + int totalN = 0; + + BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathP.CItems() ) + { + if( const PNS_LINE* l = dyn_cast( item ) ) + totalP += l->CLine().Length(); + + } + + BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathN.CItems() ) + { + if( const PNS_LINE* l = dyn_cast( item ) ) + totalN += l->CLine().Length(); + } + + return std::max( totalP, totalN ); +} + + +const SEG PNS_DP_MEANDER_PLACER::baselineSegment( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aCoupledSegs ) +{ + const VECTOR2I a( ( aCoupledSegs.coupledP.A + aCoupledSegs.coupledN.A ) / 2 ); + const VECTOR2I b( ( aCoupledSegs.coupledP.B + aCoupledSegs.coupledN.B ) / 2 ); + + return SEG( a, b ); +} + + +#if 0 +PNS_MEANDER_PLACER_BASE::TUNING_STATUS PNS_DP_MEANDER_PLACER::tuneLineLength ( PNS_MEANDERED_LINE& aTuned, int aElongation ) +{ + int remaining = aElongation; + bool finished = false; + + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + + if(m->Type() != MT_CORNER ) + { + + if(remaining >= 0) + remaining -= m->MaxTunableLength() - m->BaselineLength(); + + if(remaining < 0) + { + if(!finished) + { + PNS_MEANDER_TYPE newType; + + if ( m->Type() == MT_START || m->Type() == MT_SINGLE) + newType = MT_SINGLE; + else + newType = MT_FINISH; + + m->SetType ( newType ); + m->Recalculate( ); + + finished = true; + } else { + m->MakeEmpty(); + } + } + } + } + + remaining = aElongation; + int meanderCount = 0; + + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + if( m->Type() != MT_CORNER && m->Type() != MT_EMPTY ) + { + if(remaining >= 0) + { + remaining -= m->MaxTunableLength() - m->BaselineLength(); + meanderCount ++; + } + } + } + + int balance = 0; + + + if( meanderCount ) + balance = -remaining / meanderCount; + + if (balance >= 0) + { + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + if(m->Type() != MT_CORNER && m->Type() != MT_EMPTY) + { +// int pre = m->MaxTunableLength(); + m->Resize ( std::max( m->Amplitude() - balance / 2, m_settings.m_minAmplitude ) ); + } + } + + } + return TUNED; +} +#endif + + +bool pairOrientation( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aPair ) +{ + VECTOR2I midp = ( aPair.coupledP.A + aPair.coupledN.A ) / 2; + + //DrawDebugPoint (midp, 6); + + return aPair.coupledP.Side( midp ) > 0; +} + + +bool PNS_DP_MEANDER_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ +// return false; + + if( m_currentNode ) + delete m_currentNode; + + m_currentNode = m_world->Branch(); + + SHAPE_LINE_CHAIN preP, tunedP, postP; + SHAPE_LINE_CHAIN preN, tunedN, postN; + + cutTunedLine( m_originPair.CP(), m_currentStart, aP, preP, tunedP, postP ); + cutTunedLine( m_originPair.CN(), m_currentStart, aP, preN, tunedN, postN ); + + PNS_DIFF_PAIR tuned ( m_originPair ); + + tuned.SetShape( tunedP, tunedN ); + + m_coupledSegments.clear(); + + tuned.CoupledSegmentPairs( m_coupledSegments ); + + if( m_coupledSegments.size() == 0 ) + return false; + + //Router()->DisplayDebugLine ( tuned.CP(), 5, 20000 ); + //Router()->DisplayDebugLine ( tuned.CN(), 4, 20000 ); + + //Router()->DisplayDebugLine ( m_originPair.CP(), 5, 20000 ); + //Router()->DisplayDebugLine ( m_originPair.CN(), 4, 20000 ); + + m_result = PNS_MEANDERED_LINE( this, true ); + m_result.SetWidth( tuned.Width() ); + + int offset = ( tuned.Gap() + tuned.Width() ) / 2; + + if( !pairOrientation( m_coupledSegments[0] ) ) + offset *= -1; + + m_result.SetBaselineOffset( offset ); + + BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathP.CItems() ) + { + if( const PNS_LINE* l = dyn_cast( item ) ) + Router()->DisplayDebugLine( l->CLine(), 5, 10000 ); + } + + BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathN.CItems() ) + { + if( const PNS_LINE* l = dyn_cast( item ) ) + Router()->DisplayDebugLine( l->CLine(), 5, 10000 ); + } + + BOOST_FOREACH( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& sp, m_coupledSegments ) + { + SEG base = baselineSegment( sp ); + + // DrawDebugSeg ( base, 3 ); + + m_result.AddCorner( sp.parentP.A, sp.parentN.A ); + m_result.MeanderSegment( base ); + m_result.AddCorner( sp.parentP.B, sp.parentN.B ); + } + + int dpLen = origPathLength(); + + m_lastStatus = TUNED; + + if( dpLen - m_settings.m_targetLength > m_settings.m_lengthTolerance ) + { + m_lastStatus = TOO_LONG; + m_lastLength = dpLen; + } + else + { + m_lastLength = dpLen - std::max( tunedP.Length(), tunedN.Length() ); + tuneLineLength( m_result, m_settings.m_targetLength - dpLen ); + } + + if( m_lastStatus != TOO_LONG ) + { + tunedP.Clear(); + tunedN.Clear(); + + BOOST_FOREACH( PNS_MEANDER_SHAPE* m, m_result.Meanders() ) + { + if( m->Type() != MT_EMPTY ) + { + tunedP.Append ( m->CLine( 0 ) ); + tunedN.Append ( m->CLine( 1 ) ); + } + } + + m_lastLength += std::max( tunedP.Length(), tunedN.Length() ); + + int comp = compareWithTolerance( m_lastLength - m_settings.m_targetLength, 0, m_settings.m_lengthTolerance ); + + if( comp > 0 ) + m_lastStatus = TOO_LONG; + else if( comp < 0 ) + m_lastStatus = TOO_SHORT; + else + m_lastStatus = TUNED; + } + + m_finalShapeP.Clear(); + m_finalShapeP.Append( preP ); + m_finalShapeP.Append( tunedP ); + m_finalShapeP.Append( postP ); + m_finalShapeP.Simplify(); + + m_finalShapeN.Clear(); + m_finalShapeN.Append( preN ); + m_finalShapeN.Append( tunedN ); + m_finalShapeN.Append( postN ); + m_finalShapeN.Simplify(); + + return true; +} + + +bool PNS_DP_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + PNS_LINE lP( m_originPair.PLine(), m_finalShapeP ); + PNS_LINE lN( m_originPair.NLine(), m_finalShapeN ); + + m_currentNode->Add( &lP ); + m_currentNode->Add( &lN ); + + Router()->CommitRouting( m_currentNode ); + + return true; +} + + +bool PNS_DP_MEANDER_PLACER::CheckFit( PNS_MEANDER_SHAPE* aShape ) +{ + PNS_LINE l1( m_originPair.PLine(), aShape->CLine( 0 ) ); + PNS_LINE l2( m_originPair.NLine(), aShape->CLine( 1 ) ); + + if( m_currentNode->CheckColliding( &l1 ) ) + return false; + + if( m_currentNode->CheckColliding( &l2 ) ) + return false; + + int w = aShape->Width(); + int clearance = w + m_settings.m_spacing; + + return m_result.CheckSelfIntersections( aShape, clearance ); +} + + +const PNS_ITEMSET PNS_DP_MEANDER_PLACER::Traces() +{ + m_currentTraceP = PNS_LINE( m_originPair.PLine(), m_finalShapeP ); + m_currentTraceN = PNS_LINE( m_originPair.NLine(), m_finalShapeN ); + + PNS_ITEMSET traces; + + traces.Add( &m_currentTraceP ); + traces.Add( &m_currentTraceN ); + + return traces; +} + + +const VECTOR2I& PNS_DP_MEANDER_PLACER::CurrentEnd() const +{ + return m_currentEnd; +} + + +int PNS_DP_MEANDER_PLACER::CurrentNet() const +{ + return m_initialSegment->Net(); +} + + +int PNS_DP_MEANDER_PLACER::CurrentLayer() const +{ + return m_initialSegment->Layers().Start(); +} + + +const wxString PNS_DP_MEANDER_PLACER::TuningInfo() const +{ + wxString status; + + switch( m_lastStatus ) + { + case TOO_LONG: + status = _( "Too long: " ); + break; + case TOO_SHORT: + status = _("Too short: " ); + break; + case TUNED: + status = _( "Tuned: " ); + break; + default: + return _( "?" ); + } + + status += LengthDoubleToString( (double) m_lastLength, false ); + status += "/"; + status += LengthDoubleToString( (double) m_settings.m_targetLength, false ); + + return status; +} + + +PNS_DP_MEANDER_PLACER::TUNING_STATUS PNS_DP_MEANDER_PLACER::TuningStatus() const +{ + return m_lastStatus; +} diff --git a/pcbnew/router/pns_dp_meander_placer.h b/pcbnew/router/pns_dp_meander_placer.h new file mode 100644 index 0000000000..029c93ae8e --- /dev/null +++ b/pcbnew/router/pns_dp_meander_placer.h @@ -0,0 +1,144 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_DP_MEANDER_PLACER_H +#define __PNS_DP_MEANDER_PLACER_H + +#include + +#include +#include + +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_placement_algo.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" +#include "pns_diff_pair.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_DP_MEANDER_PLACER + * + * Differential Pair length-matching/meandering tool. + */ + +class PNS_DP_MEANDER_PLACER : public PNS_MEANDER_PLACER_BASE +{ +public: + PNS_DP_MEANDER_PLACER( PNS_ROUTER* aRouter ); + ~PNS_DP_MEANDER_PLACER(); + + /** + * Function Start() + * + * Starts routing a single track at point aP, taking item aStartItem as anchor + * (unless NULL). + */ + bool Start ( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /** + * Function Move() + * + * Moves the end of the currently routed trace to the point aP, taking + * aEndItem as anchor (if not NULL). + * (unless NULL). + */ + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /** + * Function FixRoute() + * + * Commits the currently routed track to the parent node, taking + * aP as the final end point and aEndItem as the final anchor (if provided). + * @return true, if route has been commited. May return false if the routing + * result is violating design rules - in such case, the track is only committed + * if Settings.CanViolateDRC() is on. + */ + bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + const PNS_LINE Trace() const; + + /** + * Function CurrentNode() + * + * Returns the most recent world state. + */ + PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const; + + const PNS_ITEMSET Traces(); + + const VECTOR2I& CurrentEnd() const; + + int CurrentNet() const; + int CurrentLayer() const; + + int totalLength(); + + const wxString TuningInfo() const; + TUNING_STATUS TuningStatus() const; + + bool CheckFit( PNS_MEANDER_SHAPE* aShape ); + + +private: + friend class PNS_MEANDER_SHAPE; + + void meanderSegment ( const SEG& aBase ); + +// void addMeander ( PNS_MEANDER *aM ); +// void addCorner ( const VECTOR2I& aP ); + + const SEG baselineSegment( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aCoupledSegs ); + + void setWorld( PNS_NODE* aWorld ); + void release(); + + int origPathLength() const; + + ///> pointer to world to search colliding items + PNS_NODE* m_world; + + ///> current routing start point (end of tail, beginning of head) + VECTOR2I m_currentStart; + + ///> Current world state + PNS_NODE* m_currentNode; + + PNS_DIFF_PAIR m_originPair; + PNS_DIFF_PAIR::COUPLED_SEGMENTS_VEC m_coupledSegments; + + PNS_LINE m_currentTraceN, m_currentTraceP; + PNS_ITEMSET m_tunedPath, m_tunedPathP, m_tunedPathN; + + SHAPE_LINE_CHAIN m_finalShapeP, m_finalShapeN; + PNS_MEANDERED_LINE m_result; + PNS_SEGMENT* m_initialSegment; + + int m_lastLength; + TUNING_STATUS m_lastStatus; +}; + +#endif // __PNS_DP_MEANDER_PLACER_H diff --git a/pcbnew/router/pns_dragger.h b/pcbnew/router/pns_dragger.h index 171b8ac79e..ced82083d9 100644 --- a/pcbnew/router/pns_dragger.h +++ b/pcbnew/router/pns_dragger.h @@ -42,7 +42,7 @@ class PNS_ROUTER_BASE; class PNS_DRAGGER : public PNS_ALGO_BASE { public: - PNS_DRAGGER( PNS_ROUTER* aRouter ); + PNS_DRAGGER( PNS_ROUTER* aRouter ); ~PNS_DRAGGER(); /** @@ -54,7 +54,7 @@ public: /** * Function Start() - * + * * Starts routing a single track at point aP, taking item aStartItem as anchor * (unless NULL). Returns true if a dragging operation has started. */ @@ -67,24 +67,24 @@ public: * @return true, if dragging finished with success. */ bool Drag( const VECTOR2I& aP ); - + /** * Function FixRoute() * - * Checks if the result of current dragging operation is correct + * Checks if the result of current dragging operation is correct * and eventually commits it to the world. * @return true, if dragging finished with success. */ bool FixRoute(); - - /** + + /** * Function CurrentNode() * * Returns the most recent world state, including all * items changed due to dragging operation. */ PNS_NODE* CurrentNode() const; - + /** * Function Traces() * @@ -94,30 +94,30 @@ public: /// @copydoc PNS_ALGO_BASE::Logger() virtual PNS_LOGGER* Logger(); - + private: - typedef std::pair LinePair; + typedef std::pair LinePair; typedef std::vector LinePairVec; - enum DragMode { - CORNER = 0, - SEGMENT, - VIA - }; + enum DragMode { + CORNER = 0, + SEGMENT, + VIA + }; bool dragMarkObstacles( const VECTOR2I& aP ); bool dragShove(const VECTOR2I& aP ); - bool startDragSegment( const VECTOR2D& aP, PNS_SEGMENT* aSeg ); - bool startDragVia( const VECTOR2D& aP, PNS_VIA* aVia ); - void dumbDragVia( PNS_VIA* aVia, PNS_NODE* aNode, const VECTOR2I& aP ); + bool startDragSegment( const VECTOR2D& aP, PNS_SEGMENT* aSeg ); + bool startDragVia( const VECTOR2D& aP, PNS_VIA* aVia ); + void dumbDragVia( PNS_VIA* aVia, PNS_NODE* aNode, const VECTOR2I& aP ); - PNS_NODE* m_world; - PNS_NODE* m_lastNode; - DragMode m_mode; - PNS_LINE* m_draggedLine; - PNS_VIA* m_draggedVia; - PNS_LINE m_lastValidDraggedLine; - PNS_SHOVE* m_shove; + PNS_NODE* m_world; + PNS_NODE* m_lastNode; + DragMode m_mode; + PNS_LINE* m_draggedLine; + PNS_VIA* m_draggedVia; + PNS_LINE m_lastValidDraggedLine; + PNS_SHOVE* m_shove; int m_draggedSegmentIndex; bool m_dragStatus; PNS_MODE m_currentMode; diff --git a/pcbnew/router/pns_index.h b/pcbnew/router/pns_index.h index 9e1ef37db0..6c895dc8a0 100644 --- a/pcbnew/router/pns_index.h +++ b/pcbnew/router/pns_index.h @@ -160,7 +160,6 @@ PNS_INDEX::PNS_INDEX() memset( m_subIndices, 0, sizeof( m_subIndices ) ); } - PNS_INDEX::ITEM_SHAPE_INDEX* PNS_INDEX::getSubindex( const PNS_ITEM* aItem ) { int idx_n = -1; @@ -201,7 +200,6 @@ PNS_INDEX::ITEM_SHAPE_INDEX* PNS_INDEX::getSubindex( const PNS_ITEM* aItem ) return m_subIndices[idx_n]; } - void PNS_INDEX::Add( PNS_ITEM* aItem ) { ITEM_SHAPE_INDEX* idx = getSubindex( aItem ); @@ -216,7 +214,6 @@ void PNS_INDEX::Add( PNS_ITEM* aItem ) } } - void PNS_INDEX::Remove( PNS_ITEM* aItem ) { ITEM_SHAPE_INDEX* idx = getSubindex( aItem ); @@ -230,14 +227,12 @@ void PNS_INDEX::Remove( PNS_ITEM* aItem ) m_netMap[net].remove( aItem ); } - void PNS_INDEX::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem ) { Remove( aOldItem ); Add( aNewItem ); } - template int PNS_INDEX::querySingle( int index, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor ) { @@ -247,7 +242,6 @@ int PNS_INDEX::querySingle( int index, const SHAPE* aShape, int aMinDistance, Vi return m_subIndices[index]->Query( aShape, aMinDistance, aVisitor, false ); } - template int PNS_INDEX::Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& aVisitor ) { @@ -281,7 +275,6 @@ int PNS_INDEX::Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& aVisitor return total; } - template int PNS_INDEX::Query( const SHAPE* aShape, int aMinDistance, Visitor& aVisitor ) { @@ -293,7 +286,6 @@ int PNS_INDEX::Query( const SHAPE* aShape, int aMinDistance, Visitor& aVisitor ) return total; } - void PNS_INDEX::Clear() { for( int i = 0; i < MaxSubIndices; ++i ) @@ -307,13 +299,11 @@ void PNS_INDEX::Clear() } } - PNS_INDEX::~PNS_INDEX() { Clear(); } - PNS_INDEX::NET_ITEMS_LIST* PNS_INDEX::GetItemsForNet( int aNet ) { if( m_netMap.find( aNet ) == m_netMap.end() ) diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h index 7e4333052e..cd53b76b61 100644 --- a/pcbnew/router/pns_item.h +++ b/pcbnew/router/pns_item.h @@ -36,7 +36,8 @@ class PNS_NODE; enum LineMarker { MK_HEAD = ( 1 << 0 ), MK_VIOLATION = ( 1 << 3 ), - MK_LOCKED = ( 1 << 4 ) + MK_LOCKED = ( 1 << 4 ), + MK_DP_COUPLED = ( 1 << 5 ) }; /** @@ -58,6 +59,7 @@ public: JOINT = 4, SEGMENT = 8, VIA = 16, + DIFF_PAIR = 32, ANY = 0xff }; diff --git a/pcbnew/router/pns_itemset.cpp b/pcbnew/router/pns_itemset.cpp index 837f5e4ee5..a21c645c57 100644 --- a/pcbnew/router/pns_itemset.cpp +++ b/pcbnew/router/pns_itemset.cpp @@ -22,12 +22,24 @@ #include "pns_itemset.h" -PNS_ITEMSET::PNS_ITEMSET( PNS_ITEM* aInitialItem ) +PNS_ITEMSET::PNS_ITEMSET( PNS_ITEM* aInitialItem ) : + m_owner( false ) { if( aInitialItem ) m_items.push_back( aInitialItem ); } + +PNS_ITEMSET::~PNS_ITEMSET() +{ + if( m_owner ) + { + BOOST_FOREACH( PNS_ITEM* item, m_items ) + delete item; + } +} + + PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd, bool aInvert ) { ITEMS newItems; @@ -65,6 +77,22 @@ PNS_ITEMSET& PNS_ITEMSET::FilterKinds( int aKindMask, bool aInvert ) } +PNS_ITEMSET& PNS_ITEMSET::FilterMarker( int aMarker, bool aInvert ) +{ + ITEMS newItems; + + BOOST_FOREACH( PNS_ITEM* item, m_items ) + { + if( item->Marker() & aMarker ) + newItems.push_back( item ); + } + + m_items = newItems; + + return *this; +} + + PNS_ITEMSET& PNS_ITEMSET::FilterNet( int aNet, bool aInvert ) { ITEMS newItems; @@ -80,6 +108,7 @@ PNS_ITEMSET& PNS_ITEMSET::FilterNet( int aNet, bool aInvert ) return *this; } + PNS_ITEMSET& PNS_ITEMSET::ExcludeItem( const PNS_ITEM* aItem ) { ITEMS newItems; diff --git a/pcbnew/router/pns_itemset.h b/pcbnew/router/pns_itemset.h index d229fc017c..3f25f3d35d 100644 --- a/pcbnew/router/pns_itemset.h +++ b/pcbnew/router/pns_itemset.h @@ -21,7 +21,7 @@ #ifndef __PNS_ITEMSET_H #define __PNS_ITEMSET_H -#include +#include #include #include "pns_item.h" @@ -36,15 +36,23 @@ class PNS_ITEMSET { public: - typedef std::vector ITEMS; + typedef std::deque ITEMS; PNS_ITEMSET( PNS_ITEM* aInitialItem = NULL ); - PNS_ITEMSET( const PNS_ITEMSET& aOther ) + PNS_ITEMSET( const PNS_ITEMSET& aOther ): + m_owner( false ) { m_items = aOther.m_items; } + ~PNS_ITEMSET(); + + void MakeOwner() + { + m_owner = true; + } + const PNS_ITEMSET& operator=( const PNS_ITEMSET& aOther ) { m_items = aOther.m_items; @@ -68,6 +76,7 @@ public: PNS_ITEMSET& FilterLayers( int aStart, int aEnd = -1, bool aInvert = false ); PNS_ITEMSET& FilterKinds( int aKindMask, bool aInvert = false ); PNS_ITEMSET& FilterNet( int aNet, bool aInvert = false ); + PNS_ITEMSET& FilterMarker( int aMarker, bool aInvert = false ); PNS_ITEMSET& ExcludeLayers( int aStart, int aEnd = -1 ) { @@ -96,6 +105,11 @@ public: m_items.push_back( aItem ); } + void Prepend( PNS_ITEM* aItem ) + { + m_items.push_front( aItem ); + } + PNS_ITEM* Get( int index ) const { return m_items[index]; @@ -126,6 +140,7 @@ public: private: ITEMS m_items; + bool m_owner; }; #endif diff --git a/pcbnew/router/pns_joint.h b/pcbnew/router/pns_joint.h index 43ca70daf0..78cb36e4d2 100644 --- a/pcbnew/router/pns_joint.h +++ b/pcbnew/router/pns_joint.h @@ -41,7 +41,7 @@ class PNS_JOINT : public PNS_ITEM { public: - typedef std::vector LINKED_ITEMS; + typedef std::deque LINKED_ITEMS; ///> Joints are hashed by their position, layers and net. /// Linked items are, obviously, not hashed @@ -95,6 +95,22 @@ public: return seg1->Width() == seg2->Width(); } + bool IsNonFanoutVia() const + { + if( m_linkedItems.Size() != 3 ) + return false; + + int vias = 0, segs = 0; + + for( int i = 0; i < 3; i++ ) + { + vias += m_linkedItems[i]->Kind() == VIA ? 1 : 0; + segs += m_linkedItems[i]->Kind() == SEGMENT ? 1 : 0; + } + + return ( vias == 1 && segs == 2 ); + } + ///> Links the joint to a given board item (when it's added to the PNS_NODE) void Link( PNS_ITEM* aItem ) { @@ -193,15 +209,11 @@ private: PNS_ITEMSET m_linkedItems; }; - -// hash function & comparison operator for boost::unordered_map<> -inline bool operator==( PNS_JOINT::HASH_TAG const& aP1, - PNS_JOINT::HASH_TAG const& aP2 ) +inline bool operator==( PNS_JOINT::HASH_TAG const& aP1, PNS_JOINT::HASH_TAG const& aP2 ) { return aP1.pos == aP2.pos && aP1.net == aP2.net; } - inline std::size_t hash_value( PNS_JOINT::HASH_TAG const& aP ) { std::size_t seed = 0; diff --git a/pcbnew/router/pns_line.cpp b/pcbnew/router/pns_line.cpp index c391f31a9e..dd346e4cae 100644 --- a/pcbnew/router/pns_line.cpp +++ b/pcbnew/router/pns_line.cpp @@ -140,7 +140,7 @@ PNS_SEGMENT* PNS_SEGMENT::Clone( ) const { PNS_SEGMENT* s = new PNS_SEGMENT; - s->m_seg = m_seg; + s->m_seg = m_seg; s->m_net = m_net; s->m_layers = m_layers; s->m_marker = m_marker; @@ -348,7 +348,6 @@ SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECT { optional picked; int i; - int d = 2; if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter? @@ -701,6 +700,9 @@ void PNS_LINE::Reverse() void PNS_LINE::AppendVia( const PNS_VIA& aVia ) { + if( m_line.PointCount() == 0 ) + return; + if( aVia.Pos() == m_line.CPoint( 0 ) ) { Reverse(); @@ -781,3 +783,93 @@ void PNS_LINE::ClearSegmentLinks() m_segmentRefs = NULL; } + + +static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP ) +{ + if( aDefined ) + aBox.Merge ( aP ); + else { + aBox = BOX2I( aP, VECTOR2I( 0, 0 ) ); + aDefined = true; + } +} + + +OPT_BOX2I PNS_LINE::ChangedArea( const PNS_LINE* aOther ) const +{ + BOX2I area; + bool areaDefined = false; + + int i_start = -1; + int i_end_self = -1, i_end_other = -1; + + SHAPE_LINE_CHAIN self( m_line ); + self.Simplify(); + SHAPE_LINE_CHAIN other( aOther->m_line ); + other.Simplify(); + + int np_self = self.PointCount(); + int np_other = other.PointCount(); + + int n = std::min( np_self, np_other ); + + for( int i = 0; i < n; i++ ) + { + const VECTOR2I p1 = self.CPoint( i ); + const VECTOR2I p2 = other.CPoint( i ); + + if( p1 != p2 ) + { + if( i != n - 1 ) + { + SEG s = self.CSegment( i ); + + if( !s.Contains( p2 ) ) + { + i_start = i; + break; + } + } else { + i_start = i; + break; + } + } + } + + for( int i = 0; i < n; i++ ) + { + const VECTOR2I p1 = self.CPoint( np_self - 1 - i ); + const VECTOR2I p2 = other.CPoint( np_other - 1 - i ); + + if( p1 != p2 ) + { + i_end_self = np_self - 1 - i; + i_end_other = np_other - 1 - i; + break; + } + } + + if( i_start < 0 ) + i_start = n; + + if( i_end_self < 0 ) + i_end_self = np_self - 1; + + if( i_end_other < 0 ) + i_end_other = np_other - 1; + + for( int i = i_start; i <= i_end_self; i++ ) + extendBox( area, areaDefined, self.CPoint( i ) ); + + for( int i = i_start; i <= i_end_other; i++ ) + extendBox( area, areaDefined, other.CPoint( i ) ); + + if( areaDefined ) + { + area.Inflate( std::max( Width(), aOther->Width() ) ); + return area; + } + + return OPT_BOX2I(); +} diff --git a/pcbnew/router/pns_line.h b/pcbnew/router/pns_line.h index d77c37ec96..cf75e0ff1f 100644 --- a/pcbnew/router/pns_line.h +++ b/pcbnew/router/pns_line.h @@ -70,7 +70,7 @@ public: } PNS_LINE( const PNS_LINE& aOther ) ; - + /** * Constructor * Copies properties (net, layers, etc.) from a base line and replaces the shape @@ -89,6 +89,11 @@ public: ~PNS_LINE(); + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && LINE == aItem->Kind(); + } + /// @copydoc PNS_ITEM::Clone() virtual PNS_LINE* Clone() const; @@ -96,62 +101,62 @@ public: ///> Assigns a shape to the line (a polyline/line chain) void SetShape( const SHAPE_LINE_CHAIN& aLine ) - { + { m_line = aLine; } ///> Returns the shape of the line - const SHAPE* Shape() const - { - return &m_line; + const SHAPE* Shape() const + { + return &m_line; } ///> Modifiable accessor to the underlying shape - SHAPE_LINE_CHAIN& Line() - { - return m_line; + SHAPE_LINE_CHAIN& Line() + { + return m_line; } ///> Const accessor to the underlying shape - const SHAPE_LINE_CHAIN& CLine() const - { + const SHAPE_LINE_CHAIN& CLine() const + { return m_line; } ///> Returns the number of segments in the line - int SegmentCount() const + int SegmentCount() const { - return m_line.SegmentCount(); + return m_line.SegmentCount(); } ///> Returns the number of points in the line - int PointCount() const - { - return m_line.PointCount(); + int PointCount() const + { + return m_line.PointCount(); } ///> Returns the aIdx-th point of the line - const VECTOR2I& CPoint( int aIdx ) const - { + const VECTOR2I& CPoint( int aIdx ) const + { return m_line.CPoint( aIdx ); } ///> Returns the aIdx-th segment of the line const SEG CSegment( int aIdx ) const - { + { return m_line.CSegment( aIdx ); } ///> Sets line width - void SetWidth( int aWidth ) - { - m_width = aWidth; + void SetWidth( int aWidth ) + { + m_width = aWidth; } - + ///> Returns line width - int Width() const - { - return m_width; + int Width() const + { + return m_width; } ///> Returns true if the line is geometrically identical as line aOther @@ -163,7 +168,7 @@ public: /* Linking functions */ - ///> Adds a reference to a segment registered in a PNS_NODE that is a part of this line. + ///> Adds a reference to a segment registered in a PNS_NODE that is a part of this line. void LinkSegment( PNS_SEGMENT* aSeg ) { if( !m_segmentRefs ) @@ -179,6 +184,11 @@ public: return m_segmentRefs; } + bool IsLinked() const + { + return m_segmentRefs != NULL; + } + ///> Checks if the segment aSeg is a part of the line. bool ContainsSegment( PNS_SEGMENT* aSeg ) const { @@ -189,9 +199,14 @@ public: aSeg ) != m_segmentRefs->end(); } + PNS_SEGMENT* GetLink( int aIndex ) const + { + return (*m_segmentRefs) [ aIndex ]; + } + ///> Erases the linking information. Used to detach the line from the owning node. void ClearSegmentLinks(); - + ///> Returns the number of segments that were assembled together to form this line. int LinkCount() const { @@ -205,9 +220,9 @@ public: ///> Returns the clipped line. const PNS_LINE ClipToNearestObstacle( PNS_NODE* aNode ) const; - ///> Clips the line to a given range of vertices. + ///> Clips the line to a given range of vertices. void ClipVertexRange ( int aStart, int aEnd ); - + ///> Returns the number of corners of angles specified by mask aAngles. int CountCorners( int aAngles ); @@ -238,19 +253,21 @@ public: void RemoveVia() { m_hasVia = false; } const PNS_VIA& Via() const { return m_via; } - + virtual void Mark( int aMarker ); virtual void Unmark (); virtual int Marker() const; - + void DragSegment( const VECTOR2I& aP, int aIndex, int aSnappingThreshold = 0 ); void DragCorner( const VECTOR2I& aP, int aIndex, int aSnappingThreshold = 0 ); void SetRank( int aRank ); int Rank() const; - + bool HasLoops() const; + OPT_BOX2I ChangedArea( const PNS_LINE* aOther ) const; + private: VECTOR2I snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, int aIndex, int aThreshold) const; @@ -260,7 +277,7 @@ private: ///> Copies m_segmentRefs from the line aParent. void copyLinks( const PNS_LINE* aParent ) ; - + ///> List of segments in the owning PNS_NODE (PNS_ITEM::m_owner) that constitute this line, or NULL ///> if the line is not a part of any node. SEGMENT_REFS* m_segmentRefs; diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp index 093fbe6218..9a9ff9b1c1 100644 --- a/pcbnew/router/pns_line_placer.cpp +++ b/pcbnew/router/pns_line_placer.cpp @@ -31,13 +31,14 @@ #include "pns_shove.h" #include "pns_utils.h" #include "pns_router.h" +#include "pns_topology.h" #include using boost::optional; PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_ROUTER* aRouter ) : - PNS_ALGO_BASE ( aRouter ) + PNS_PLACEMENT_ALGO( aRouter ) { m_initial_direction = DIRECTION_45::N; m_world = NULL; @@ -67,11 +68,13 @@ const PNS_VIA PNS_LINE_PLACER::makeVia ( const VECTOR2I& aP ) } -void PNS_LINE_PLACER::ToggleVia( bool aEnabled ) +bool PNS_LINE_PLACER::ToggleVia( bool aEnabled ) { m_placingVia = aEnabled; - if(!m_idle) - Move ( m_currentEnd, NULL ); + if( !m_idle ) + Move( m_currentEnd, NULL ); + + return true; } @@ -225,8 +228,6 @@ bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd ) VECTOR2I new_start; int reduce_index = -1; - DIRECTION_45 head_dir( head.CSegment( 0 ) ); - for( int i = tail.SegmentCount() - 1; i >= 0; i-- ) { const SEG s = tail.CSegment( i ); @@ -376,7 +377,7 @@ bool PNS_LINE_PLACER::handleViaPlacement( PNS_LINE& aHead ) bool PNS_LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, PNS_LINE& aNewHead ) { - SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP ); + SHAPE_LINE_CHAIN line = buildInitialLine( aP ); PNS_LINE initTrack( m_head, line ), walkFull; int effort = 0; bool viaOk = handleViaPlacement( initTrack ); @@ -430,7 +431,7 @@ bool PNS_LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, PNS_LINE& aNewHead ) bool PNS_LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead ) { - m_head.SetShape( m_direction.BuildInitialTrace( m_p_start, aP ) ); + m_head.SetShape( buildInitialLine( aP ) ); if( m_placingVia ) { @@ -445,7 +446,7 @@ bool PNS_LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead ) bool PNS_LINE_PLACER::rhShoveOnly ( const VECTOR2I& aP, PNS_LINE& aNewHead ) { - SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP ); + SHAPE_LINE_CHAIN line = buildInitialLine( aP ); PNS_LINE initTrack( m_head, line ); PNS_LINE walkSolids, l2; @@ -530,9 +531,9 @@ bool PNS_LINE_PLACER::routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead ) case RM_MarkObstacles: return rhMarkObstacles( aP, aNewHead ); case RM_Walkaround: - return rhWalkOnly ( aP, aNewHead ); + return rhWalkOnly( aP, aNewHead ); case RM_Shove: - return rhShoveOnly ( aP, aNewHead ); + return rhShoveOnly( aP, aNewHead ); default: break; } @@ -731,9 +732,12 @@ bool PNS_LINE_PLACER::SetLayer( int aLayer ) { m_currentLayer = aLayer; return true; - } else if( m_chainedPlacement ) { + } + else if( m_chainedPlacement ) + { return false; - } else if( !m_startItem || ( m_startItem->OfKind( PNS_ITEM::VIA ) && m_startItem->Layers().Overlaps( aLayer ) ) ) { + } + else if( !m_startItem || ( m_startItem->OfKind( PNS_ITEM::VIA ) && m_startItem->Layers().Overlaps( aLayer ) ) ) { m_currentLayer = aLayer; m_splitSeg = false; initPlacement ( m_splitSeg ); @@ -744,7 +748,8 @@ bool PNS_LINE_PLACER::SetLayer( int aLayer ) return false; } -void PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) + +bool PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) { VECTOR2I p( aP ); @@ -772,6 +777,7 @@ void PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) setInitialDirection( Settings().InitialDirection() ); initPlacement( m_splitSeg ); + return true; } void PNS_LINE_PLACER::initPlacement( bool aSplitSeg ) @@ -819,7 +825,7 @@ void PNS_LINE_PLACER::initPlacement( bool aSplitSeg ) } -void PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +bool PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) { PNS_LINE current; VECTOR2I p = aP; @@ -856,6 +862,7 @@ void PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) } updateLeadingRatLine(); + return true; } @@ -962,11 +969,6 @@ void PNS_LINE_PLACER::removeLoops( PNS_NODE* aNode, PNS_LINE* aLatest ) if( !( line->ContainsSegment( seg ) ) && line->SegmentCount() ) { - Router()->DisplayDebugLine ( line->CLine(), -1, 10000 ); - - for( int i = 0; i < line->PointCount(); i++ ) - Router()->DisplayDebugPoint( line->CPoint( i ), -1 ); - aNode->Remove( line ); removedCount ++; } @@ -1012,27 +1014,42 @@ void PNS_LINE_PLACER::UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ) void PNS_LINE_PLACER::updateLeadingRatLine() { PNS_LINE current = Trace(); + SHAPE_LINE_CHAIN ratLine; + PNS_TOPOLOGY topo ( m_lastNode ); - if( !current.PointCount() ) - return; - - std::auto_ptr tmpNode ( m_lastNode->Branch() ); - tmpNode->Add( ¤t ); - - PNS_JOINT* jt = tmpNode->FindJoint( current.CPoint( -1 ), - current.Layers().Start(), current.Net() ); - - if( !jt ) - return; - - int anchor; - PNS_ITEM* it = tmpNode->NearestUnconnectedItem( jt, &anchor ); - - if( it ) - { - SHAPE_LINE_CHAIN lc; - lc.Append ( current.CPoint( -1 ) ); - lc.Append ( it->Anchor( anchor ) ); - Router()->DisplayDebugLine( lc, 5, 10000 ); - } + if( topo.LeadingRatLine( ¤t, ratLine ) ) + Router()->DisplayDebugLine( ratLine, 5, 10000 ); +} + + +void PNS_LINE_PLACER::SetOrthoMode( bool aOrthoMode ) +{ + m_orthoMode = aOrthoMode; + + if( !m_idle ) + Move( m_currentEnd, NULL ); +} + +const SHAPE_LINE_CHAIN PNS_LINE_PLACER::buildInitialLine( const VECTOR2I& aP ) +{ + SHAPE_LINE_CHAIN l( m_direction.BuildInitialTrace( m_p_start, aP ) ); + + if( l.SegmentCount() <= 1 ) + return l; + + if( m_orthoMode ) + { + VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CPoint( -1 ) ); + + l.Remove( -1, -1 ); + l.Point( 1 ) = newLast; + } + + return l; +} + + +void PNS_LINE_PLACER::GetModifiedNets( std::vector& aNets ) const +{ + aNets.push_back( m_currentNet ); } diff --git a/pcbnew/router/pns_line_placer.h b/pcbnew/router/pns_line_placer.h index 1d603bd9b6..cfa6228f66 100644 --- a/pcbnew/router/pns_line_placer.h +++ b/pcbnew/router/pns_line_placer.h @@ -30,7 +30,7 @@ #include "pns_node.h" #include "pns_via.h" #include "pns_line.h" -#include "pns_algo_base.h" +#include "pns_placement_algo.h" class PNS_ROUTER; class PNS_SHOVE; @@ -47,7 +47,7 @@ class PNS_SIZES_SETTINGS; * Applies shove and walkaround algorithms when needed. */ -class PNS_LINE_PLACER : public PNS_ALGO_BASE +class PNS_LINE_PLACER : public PNS_PLACEMENT_ALGO { public: PNS_LINE_PLACER( PNS_ROUTER* aRouter ); @@ -59,7 +59,7 @@ public: * Starts routing a single track at point aP, taking item aStartItem as anchor * (unless NULL). */ - void Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); /** * Function Move() @@ -68,7 +68,7 @@ public: * aEndItem as anchor (if not NULL). * (unless NULL). */ - void Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); /** * Function FixRoute() @@ -86,7 +86,7 @@ public: * * Enables/disables a via at the end of currently routed trace. */ - void ToggleVia( bool aEnabled ); + bool ToggleVia( bool aEnabled ); /** * Function SetLayer() @@ -95,7 +95,6 @@ public: */ bool SetLayer( int aLayer ); - /** * Function Head() * @@ -180,7 +179,11 @@ public: */ void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ); + void SetOrthoMode( bool aOrthoMode ); + bool IsPlacingVia() const { return m_placingVia; } + + void GetModifiedNets( std::vector& aNets ) const; private: /** * Function route() @@ -344,6 +347,8 @@ private: const PNS_VIA makeVia ( const VECTOR2I& aP ); + const SHAPE_LINE_CHAIN buildInitialLine( const VECTOR2I& aP ); + ///> current routing direction DIRECTION_45 m_direction; @@ -403,6 +408,7 @@ private: bool m_idle; bool m_chainedPlacement; bool m_splitSeg; + bool m_orthoMode; }; #endif // __PNS_LINE_PLACER_H diff --git a/pcbnew/router/pns_logger.cpp b/pcbnew/router/pns_logger.cpp index b09578137c..e0f8d6ac4b 100644 --- a/pcbnew/router/pns_logger.cpp +++ b/pcbnew/router/pns_logger.cpp @@ -32,7 +32,7 @@ PNS_LOGGER::PNS_LOGGER( ) { - m_groupOpened = false; + m_groupOpened = false; } @@ -43,89 +43,89 @@ PNS_LOGGER::~PNS_LOGGER() void PNS_LOGGER::Clear() { - m_theLog.str( std::string() ); - m_groupOpened = false; + m_theLog.str( std::string() ); + m_groupOpened = false; } void PNS_LOGGER::NewGroup( const std::string& aName, int aIter ) { - if( m_groupOpened ) - m_theLog << "endgroup" << std::endl; + if( m_groupOpened ) + m_theLog << "endgroup" << std::endl; - m_theLog << "group " << aName << " " << aIter << std::endl; - m_groupOpened = true; + m_theLog << "group " << aName << " " << aIter << std::endl; + m_groupOpened = true; } void PNS_LOGGER::EndGroup() { - if( !m_groupOpened ) - return; + if( !m_groupOpened ) + return; - m_groupOpened = false; - m_theLog << "endgroup" << std::endl; + m_groupOpened = false; + m_theLog << "endgroup" << std::endl; } void PNS_LOGGER::Log ( const PNS_ITEM* aItem, int aKind, const std::string aName ) { - m_theLog << "aItem " << aKind << " " << aName << " "; - m_theLog << aItem->Net() << " " << aItem->Layers().Start() << " " << - aItem->Layers().End() << " " << aItem->Marker() << " " << aItem->Rank(); + m_theLog << "item " << aKind << " " << aName << " "; + m_theLog << aItem->Net() << " " << aItem->Layers().Start() << " " << + aItem->Layers().End() << " " << aItem->Marker() << " " << aItem->Rank(); - switch( aItem->Kind() ) - { - case PNS_ITEM::LINE: - { - PNS_LINE* l = (PNS_LINE*) aItem; - m_theLog << " line "; - m_theLog << l->Width() << " " << ( l->EndsWithVia() ? 1 : 0 ) << " "; - dumpShape ( l->Shape() ); - m_theLog << std::endl; - break; - } + switch( aItem->Kind() ) + { + case PNS_ITEM::LINE: + { + PNS_LINE* l = (PNS_LINE*) aItem; + m_theLog << " line "; + m_theLog << l->Width() << " " << ( l->EndsWithVia() ? 1 : 0 ) << " "; + dumpShape ( l->Shape() ); + m_theLog << std::endl; + break; + } - case PNS_ITEM::VIA: - { - m_theLog << " via 0 0 "; - dumpShape ( aItem->Shape() ); - m_theLog << std::endl; - break; - } + case PNS_ITEM::VIA: + { + m_theLog << " via 0 0 "; + dumpShape ( aItem->Shape() ); + m_theLog << std::endl; + break; + } - case PNS_ITEM::SEGMENT: - { - PNS_SEGMENT* s =(PNS_SEGMENT*) aItem; - m_theLog << " line "; - m_theLog << s->Width() << " 0 linechain 2 0 " << s->Seg().A.x << " " << - s->Seg().A.y << " " << s->Seg().B.x << " " <Seg().B.y << std::endl; - break; - } + case PNS_ITEM::SEGMENT: + { + PNS_SEGMENT* s =(PNS_SEGMENT*) aItem; + m_theLog << " line "; + m_theLog << s->Width() << " 0 linechain 2 0 " << s->Seg().A.x << " " << + s->Seg().A.y << " " << s->Seg().B.x << " " <Seg().B.y << std::endl; + break; + } - case PNS_ITEM::SOLID: - { - PNS_SOLID* s = (PNS_SOLID*) aItem; - m_theLog << " solid 0 0 "; - dumpShape( s->Shape() ); - m_theLog << std::endl; - break; - } + case PNS_ITEM::SOLID: + { + PNS_SOLID* s = (PNS_SOLID*) aItem; + m_theLog << " solid 0 0 "; + dumpShape( s->Shape() ); + m_theLog << std::endl; + break; + } - default: - break; - } + default: + break; + } } void PNS_LOGGER::Log( const SHAPE_LINE_CHAIN *aL, int aKind, const std::string aName ) { - m_theLog << "item " << aKind << " " << aName << " "; - m_theLog << 0 << " " << 0 << " " << 0 << " " << 0 << " " << 0; - m_theLog << " line "; - m_theLog << 0 << " " << 0 << " "; - dumpShape( aL ); - m_theLog << std::endl; + m_theLog << "item " << aKind << " " << aName << " "; + m_theLog << 0 << " " << 0 << " " << 0 << " " << 0 << " " << 0; + m_theLog << " line "; + m_theLog << 0 << " " << 0 << " "; + dumpShape( aL ); + m_theLog << std::endl; } @@ -137,54 +137,55 @@ void PNS_LOGGER::Log( const VECTOR2I& aStart, const VECTOR2I& aEnd, void PNS_LOGGER::dumpShape( const SHAPE* aSh ) { - switch( aSh->Type() ) - { - case SH_LINE_CHAIN: - { - const SHAPE_LINE_CHAIN* lc = (const SHAPE_LINE_CHAIN*) aSh; - m_theLog << "linechain " << lc->PointCount() << " " << ( lc->IsClosed() ? 1 : 0 ) << " "; + switch( aSh->Type() ) + { + case SH_LINE_CHAIN: + { + const SHAPE_LINE_CHAIN* lc = (const SHAPE_LINE_CHAIN*) aSh; + m_theLog << "linechain " << lc->PointCount() << " " << ( lc->IsClosed() ? 1 : 0 ) << " "; - for( int i = 0; i < lc->PointCount(); i++ ) - m_theLog << lc->CPoint( i ).x << " " << lc->CPoint( i ).y << " "; + for( int i = 0; i < lc->PointCount(); i++ ) + m_theLog << lc->CPoint( i ).x << " " << lc->CPoint( i ).y << " "; - break; - } + break; + } - case SH_CIRCLE: - { - const SHAPE_CIRCLE *c = (const SHAPE_CIRCLE*) aSh; - m_theLog << "circle " << c->GetCenter().x << " " << c->GetCenter().y << " " << c->GetRadius(); - break; - } + case SH_CIRCLE: + { + const SHAPE_CIRCLE *c = (const SHAPE_CIRCLE*) aSh; + m_theLog << "circle " << c->GetCenter().x << " " << c->GetCenter().y << " " << c->GetRadius(); + break; + } - case SH_RECT: - { - const SHAPE_RECT* r = (const SHAPE_RECT*) aSh; - m_theLog << "rect " << r->GetPosition().x << " " << r->GetPosition().y << " " << - r->GetSize().x << " " <GetSize().y; - break; - } + case SH_RECT: + { + const SHAPE_RECT* r = (const SHAPE_RECT*) aSh; + m_theLog << "rect " << r->GetPosition().x << " " << r->GetPosition().y << " " << + r->GetSize().x << " " <GetSize().y; + break; + } - case SH_SEGMENT: - { - const SHAPE_SEGMENT* s = (const SHAPE_SEGMENT*) aSh; - m_theLog << "linechain 2 0 " << s->GetSeg().A.x << " " << s->GetSeg().A.y << " " << - s->GetSeg().B.x << " " << s->GetSeg().B.y; - break; - } + case SH_SEGMENT: + { + const SHAPE_SEGMENT* s = (const SHAPE_SEGMENT*) aSh; + m_theLog << "linechain 2 0 " << s->GetSeg().A.x << " " << s->GetSeg().A.y << " " << + s->GetSeg().B.x << " " << s->GetSeg().B.y; + break; + } - default: - break; - } + default: + break; + } } + void PNS_LOGGER::Save( const std::string& aFilename ) { - EndGroup(); + EndGroup(); - FILE* f = fopen( aFilename.c_str(), "wb" ); - printf( "Saving to '%s' [%p]\n", aFilename.c_str(), f ); - const std::string s = m_theLog.str(); - fwrite( s.c_str(), 1, s.length(), f ); - fclose( f ); + FILE* f = fopen( aFilename.c_str(), "wb" ); + printf( "Saving to '%s' [%p]\n", aFilename.c_str(), f ); + const std::string s = m_theLog.str(); + fwrite( s.c_str(), 1, s.length(), f ); + fclose( f ); } diff --git a/pcbnew/router/pns_logger.h b/pcbnew/router/pns_logger.h index 9120a43557..b38fd427c8 100644 --- a/pcbnew/router/pns_logger.h +++ b/pcbnew/router/pns_logger.h @@ -35,25 +35,25 @@ class SHAPE; class PNS_LOGGER { public: - PNS_LOGGER(); - ~PNS_LOGGER(); + PNS_LOGGER(); + ~PNS_LOGGER(); - void Save( const std::string& aFilename ); - void Clear(); + void Save( const std::string& aFilename ); + void Clear(); - void NewGroup( const std::string& aName, int aIter = 0 ); - void EndGroup(); + void NewGroup( const std::string& aName, int aIter = 0 ); + void EndGroup(); - void Log( const PNS_ITEM* aItem, int aKind = 0, const std::string aName = std::string() ); - void Log( const SHAPE_LINE_CHAIN *aL, int aKind = 0, const std::string aName = std::string() ); - void Log( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aKind = 0, - const std::string aName = std::string() ); + void Log( const PNS_ITEM* aItem, int aKind = 0, const std::string aName = std::string() ); + void Log( const SHAPE_LINE_CHAIN *aL, int aKind = 0, const std::string aName = std::string() ); + void Log( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aKind = 0, + const std::string aName = std::string() ); private: - void dumpShape ( const SHAPE* aSh ); + void dumpShape( const SHAPE* aSh ); - bool m_groupOpened; - std::stringstream m_theLog; + bool m_groupOpened; + std::stringstream m_theLog; }; #endif diff --git a/pcbnew/router/pns_meander.cpp b/pcbnew/router/pns_meander.cpp new file mode 100644 index 0000000000..0577aad404 --- /dev/null +++ b/pcbnew/router/pns_meander.cpp @@ -0,0 +1,594 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" +#include "pns_router.h" + +const PNS_MEANDER_SETTINGS& PNS_MEANDER_SHAPE::Settings() const +{ + return m_placer->MeanderSettings(); +} + +const PNS_MEANDER_SETTINGS& PNS_MEANDERED_LINE::Settings() const +{ + return m_placer->MeanderSettings(); +} + +void PNS_MEANDERED_LINE::MeanderSegment( const SEG& aBase, int aBaseIndex ) +{ + double base_len = aBase.Length(); + + SHAPE_LINE_CHAIN lc; + + bool side = true; + VECTOR2D dir( aBase.B - aBase.A ); + + if( !m_dual ) + AddCorner( aBase.A ); + + bool turning = false; + bool started = false; + + m_last = aBase.A; + + do + { + PNS_MEANDER_SHAPE* m = new PNS_MEANDER_SHAPE( m_placer, m_width, m_dual ); + m->SetBaselineOffset( m_baselineOffset ); + m->SetBaseIndex( aBaseIndex ); + + double thr = (double) m->spacing(); + + bool fail = false; + double remaining = base_len - ( m_last - aBase.A ).EuclideanNorm(); + + if( remaining < Settings( ).m_step ) + break; + + if( remaining > 3.0 * thr ) + { + if( !turning ) + { + for( int i = 0; i < 2; i++ ) + { + if ( m->Fit( MT_CHECK_START, aBase, m_last, i ) ) + { + turning = true; + AddMeander( m ); + side = !i; + started = true; + break; + } + } + + if( !turning ) + { + fail = true; + + for( int i = 0; i < 2; i++ ) + { + if ( m->Fit ( MT_SINGLE, aBase, m_last, i ) ) + { + AddMeander( m ); + fail = false; + started = false; + side = !i; + break; + } + } + } + } else { + bool rv = m->Fit( MT_CHECK_FINISH, aBase, m_last, side ); + + if( rv ) + { + m->Fit( MT_TURN, aBase, m_last, side ); + AddMeander( m ); + started = true; + } else { + m->Fit( MT_FINISH, aBase, m_last, side ); + started = false; + AddMeander( m ); + turning = false; + } + + side = !side; + } + } else if( started ) + { + bool rv = m->Fit( MT_FINISH, aBase, m_last, side ); + if( rv ) + AddMeander( m ); + + break; + + } else { + fail = true; + } + + remaining = base_len - ( m_last - aBase.A ).EuclideanNorm( ); + + if( remaining < Settings( ).m_step ) + break; + + if( fail ) + { + PNS_MEANDER_SHAPE tmp( m_placer, m_width, m_dual ); + tmp.SetBaselineOffset( m_baselineOffset ); + tmp.SetBaseIndex( aBaseIndex ); + + int nextP = tmp.spacing() - 2 * tmp.cornerRadius() + Settings().m_step; + VECTOR2I pn = m_last + dir.Resize( nextP ); + + if( aBase.Contains( pn ) && !m_dual ) + { + AddCorner( pn ); + } else + break; + } + + + } while( true ); + + if( !m_dual ) + AddCorner( aBase.B ); +} + + +int PNS_MEANDER_SHAPE::cornerRadius() const +{ + int cr = (int64_t) spacing() * Settings().m_cornerRadiusPercentage / 200; + + return cr; +} + + +int PNS_MEANDER_SHAPE::spacing( ) const +{ + if ( !m_dual ) + return std::max( 2 * m_width, Settings().m_spacing ); + else + { + int sp = 2 * ( m_width + std::abs( m_baselineOffset ) ); + return std::max ( sp, Settings().m_spacing ); + } +} + + +SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide ) +{ + SHAPE_LINE_CHAIN lc; + + if( aDir.EuclideanNorm( ) == 0.0f ) + { + lc.Append( aP ); + return lc; + } + + VECTOR2D dir_u( aDir ); + VECTOR2D dir_v( aDir.Perpendicular( ) ); + + const int ArcSegments = Settings().m_cornerArcSegments; + + for( int i = ArcSegments - 1; i >= 0; i-- ) + { + VECTOR2D p; + double alpha = (double) i / (double) ( ArcSegments - 1 ) * M_PI / 2.0; + p = aP + dir_u * cos( alpha ) + dir_v * ( aSide ? -1.0 : 1.0 ) * ( 1.0 - sin( alpha ) ); + lc.Append( ( int ) p.x, ( int ) p.y ); + } + + return lc; +} + + +VECTOR2I PNS_MEANDER_SHAPE::reflect( VECTOR2I p, const SEG& line ) +{ + typedef int64_t ecoord; + VECTOR2I d = line.B - line.A; + ecoord l_squared = d.Dot( d ); + ecoord t = d.Dot( p - line.A ); + VECTOR2I c, rv; + + if( !l_squared ) + c = p; + else { + c.x = line.A.x + rescale( t, (ecoord) d.x, l_squared ); + c.y = line.A.y + rescale( t, (ecoord) d.y, l_squared ); + } + + return 2 * c - p; +} + + +void PNS_MEANDER_SHAPE::start( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir ) +{ + m_currentTarget = aTarget; + m_currentTarget->Clear(); + m_currentTarget->Append( aWhere ); + m_currentDir = aDir; + m_currentPos = aWhere; +} + + +void PNS_MEANDER_SHAPE::forward( int aLength ) +{ + m_currentPos += m_currentDir.Resize( aLength ); + m_currentTarget->Append( m_currentPos ); +} + + +void PNS_MEANDER_SHAPE::turn( int aAngle ) +{ + m_currentDir = m_currentDir.Rotate( (double) aAngle * M_PI / 180.0 ); +} + + +void PNS_MEANDER_SHAPE::arc( int aRadius, bool aSide ) +{ + if( aRadius <= 0 ) + { + turn( aSide ? -90 : 90 ); + return; + } + + VECTOR2D dir = m_currentDir.Resize( (double) aRadius ); + SHAPE_LINE_CHAIN arc = circleQuad( m_currentPos, dir, aSide ); + m_currentPos = arc.CPoint( -1 ); + m_currentDir = dir.Rotate( aSide ? -M_PI / 2.0 : M_PI / 2.0 ); + + m_currentTarget->Append ( arc ); +} + + +void PNS_MEANDER_SHAPE::uShape( int aSides, int aCorner, int aTop ) +{ + forward( aSides ); + arc( aCorner, true ); + forward( aTop ); + arc( aCorner, true ); + forward( aSides ); +} + + +SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::genMeanderShape( VECTOR2D aP, VECTOR2D aDir, + bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset ) +{ + const PNS_MEANDER_SETTINGS& st = Settings(); + int cr = cornerRadius(); + int offset = aBaselineOffset; + int spc = spacing(); + + if( aSide ) + offset *= -1; + + VECTOR2D dir_u_b( aDir.Resize( offset ) ); + VECTOR2D dir_v_b( dir_u_b.Perpendicular() ); + + if( 2 * cr > aAmpl ) + { + cr = aAmpl / 2; + } + + if( 2 * cr > spc ) + { + cr = spc / 2; + } + + SHAPE_LINE_CHAIN lc; + + start( &lc, aP + dir_v_b, aDir ); + + switch( aType ) + { + case MT_EMPTY: + { + lc.Append( aP + dir_v_b + aDir ); + break; + } + case MT_START: + { + arc( cr - offset, false ); + uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); + forward( std::min( cr - offset, cr + offset ) ); + forward( std::abs( offset ) ); + + break; + } + + case MT_FINISH: + { + start( &lc, aP - dir_u_b, aDir ); + turn ( 90 ); + forward( std::min( cr - offset, cr + offset ) ); + forward( std::abs( offset ) ); + uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); + arc( cr - offset, false ); + break; + } + + case MT_TURN: + { + start( &lc, aP - dir_u_b, aDir ); + turn( 90 ); + forward( std::abs( offset ) ); + uShape ( aAmpl - cr, cr + offset, spc - 2 * cr ); + forward( std::abs( offset ) ); + break; + } + + case MT_SINGLE: + { + arc( cr - offset, false ); + uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); + arc( cr - offset, false ); + lc.Append( aP + dir_v_b + aDir.Resize ( 2 * st.m_spacing ) ); + break; + } + + default: + break; + } + + if( aSide ) + { + SEG axis ( aP, aP + aDir ); + + for( int i = 0; i < lc.PointCount(); i++ ) + lc.Point( i ) = reflect( lc.CPoint( i ), axis ); + } + + return lc; +} + + +bool PNS_MEANDERED_LINE::CheckSelfIntersections( PNS_MEANDER_SHAPE* aShape, int aClearance ) +{ + for( int i = m_meanders.size() - 1; i >= 0; i-- ) + { + PNS_MEANDER_SHAPE* m = m_meanders[i]; + + if( m->Type() == MT_EMPTY || m->Type() == MT_CORNER ) + continue; + + const SEG& b1 = aShape->BaseSegment(); + const SEG& b2 = m->BaseSegment(); + + if( b1.ApproxParallel( b2 ) ) + continue; + + int n = m->CLine( 0 ).SegmentCount(); + + for( int j = n - 1; j >= 0; j-- ) + if( aShape->CLine( 0 ).Collide ( m->CLine( 0 ) .CSegment( j ), aClearance ) ) + return false; + } + + return true; +} + + +bool PNS_MEANDER_SHAPE::Fit( PNS_MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide ) +{ + const PNS_MEANDER_SETTINGS& st = Settings(); + + bool checkMode = false; + PNS_MEANDER_TYPE prim1, prim2; + + if( aType == MT_CHECK_START ) + { + prim1 = MT_START; + prim2 = MT_TURN; + checkMode = true; + } + else if( aType == MT_CHECK_FINISH ) + { + prim1 = MT_TURN; + prim2 = MT_FINISH; + checkMode = true; + } + + if( checkMode ) + { + PNS_MEANDER_SHAPE m1( m_placer, m_width, m_dual ); + PNS_MEANDER_SHAPE m2( m_placer, m_width, m_dual ); + + m1.SetBaselineOffset( m_baselineOffset ); + m2.SetBaselineOffset( m_baselineOffset ); + + bool c1 = m1.Fit( prim1, aSeg, aP, aSide ); + bool c2 = false; + + if( c1 ) + c2 = m2.Fit( prim2, aSeg, m1.End(), !aSide ); + + if( c1 && c2 ) + { + m_type = prim1; + m_shapes[0] = m1.m_shapes[0]; + m_shapes[1] = m1.m_shapes[1]; + m_baseSeg =aSeg; + m_p0 = aP; + m_side = aSide; + m_amplitude = m1.Amplitude(); + m_dual = m1.m_dual; + m_baseSeg = m1.m_baseSeg; + m_baseIndex = m1.m_baseIndex; + updateBaseSegment(); + m_baselineOffset = m1.m_baselineOffset; + return true; + } else + return false; + } + + int minAmpl = st.m_minAmplitude; + int maxAmpl = st.m_maxAmplitude; + + if( m_dual ) + { + minAmpl = std::max( minAmpl, 2 * std::abs( m_baselineOffset ) ); + maxAmpl = std::max( maxAmpl, 2 * std::abs( m_baselineOffset ) ); + } + + for( int ampl = maxAmpl; ampl >= minAmpl; ampl -= st.m_step ) + { + if( m_dual ) + { + m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, m_baselineOffset ); + m_shapes[1] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, -m_baselineOffset ); + } + else + { + m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, 0 ); + } + + m_type = aType; + m_baseSeg = aSeg; + m_p0 = aP; + m_side = aSide; + m_amplitude = ampl; + + updateBaseSegment(); + + if( m_placer->CheckFit( this ) ) + return true; + } + + return false; +} + + +void PNS_MEANDER_SHAPE::Recalculate() +{ + m_shapes[0] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude, m_dual ? m_baselineOffset : 0 ); + + if( m_dual ) + m_shapes[1] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude, -m_baselineOffset ); + + updateBaseSegment(); +} + + +void PNS_MEANDER_SHAPE::Resize( int aAmpl ) +{ + if( aAmpl < 0 ) + return; + + m_amplitude = aAmpl; + + Recalculate(); +} + + +void PNS_MEANDER_SHAPE::MakeEmpty() +{ + updateBaseSegment(); + + VECTOR2I dir = m_clippedBaseSeg.B - m_clippedBaseSeg.A; + + m_type = MT_EMPTY; + + m_shapes[0] = genMeanderShape ( m_p0, dir, m_side, m_type, 0, m_dual ? m_baselineOffset : 0 ); + + if( m_dual ) + m_shapes[1] = genMeanderShape( m_p0, dir, m_side, m_type, 0, -m_baselineOffset ); +} + + +void PNS_MEANDERED_LINE::AddCorner( const VECTOR2I& aA, const VECTOR2I& aB ) +{ + PNS_MEANDER_SHAPE* m = new PNS_MEANDER_SHAPE( m_placer, m_width, m_dual ); + + m->MakeCorner( aA, aB ); + m_last = aA; + + m_meanders.push_back( m ); +} + + +void PNS_MEANDER_SHAPE::MakeCorner( VECTOR2I aP1, VECTOR2I aP2 ) +{ + SetType( MT_CORNER ); + m_shapes[0].Clear(); + m_shapes[1].Clear(); + m_shapes[0].Append( aP1 ); + m_shapes[1].Append( aP2 ); + m_clippedBaseSeg.A = aP1; + m_clippedBaseSeg.B = aP1; +} + + +void PNS_MEANDERED_LINE::AddMeander( PNS_MEANDER_SHAPE* aShape ) +{ + m_last = aShape->BaseSegment().B; + m_meanders.push_back( aShape ); +} + + +void PNS_MEANDERED_LINE::Clear() +{ + BOOST_FOREACH( PNS_MEANDER_SHAPE* m, m_meanders ) + { + delete m; + } + + m_meanders.clear( ); +} + + +int PNS_MEANDER_SHAPE::BaselineLength() const +{ + return m_clippedBaseSeg.Length(); +} + + +int PNS_MEANDER_SHAPE::MaxTunableLength() const +{ + return CLine( 0 ).Length(); +} + + +void PNS_MEANDER_SHAPE::updateBaseSegment( ) +{ + if( m_dual ) + { + VECTOR2I midpA = ( CLine( 0 ).CPoint( 0 ) + CLine( 1 ).CPoint( 0 ) ) / 2; + VECTOR2I midpB = ( CLine( 0 ).CPoint( -1 ) + CLine( 1 ).CPoint( -1 ) ) / 2; + + m_clippedBaseSeg.A = m_baseSeg.LineProject( midpA ); + m_clippedBaseSeg.B = m_baseSeg.LineProject( midpB ); + } + else + { + m_clippedBaseSeg.A = m_baseSeg.LineProject( CLine( 0 ).CPoint( 0 ) ); + m_clippedBaseSeg.B = m_baseSeg.LineProject( CLine( 0 ).CPoint( -1 ) ); + } +} diff --git a/pcbnew/router/pns_meander.h b/pcbnew/router/pns_meander.h new file mode 100644 index 0000000000..de08329ed7 --- /dev/null +++ b/pcbnew/router/pns_meander.h @@ -0,0 +1,492 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_H +#define __PNS_MEANDER_H + +#include + +#include +#include + +class PNS_MEANDER_PLACER_BASE; + +///< Shapes of available meanders +enum PNS_MEANDER_TYPE { + MT_SINGLE, // _|^|_, single-sided + MT_START, // _|^| + MT_FINISH, // |^|_ + MT_TURN, // |^| or |_| + MT_CHECK_START, // try fitting a start type, but don't produce a line + MT_CHECK_FINISH, // try fitting a finish type, but don't produce a line + MT_CORNER, // line corner + MT_EMPTY // no meander (straight line) +}; + +/** + * Class PNS_MEANDER_SETTINGS + * + * Holds dimensions for the meandering algorithm. + */ +class PNS_MEANDER_SETTINGS +{ +public: + + ///> meander corner shape + enum CornerType { + ROUND = 1, // rounded (90 degree arc) + CHAMFER // chamfered (45 degree segment) + }; + + PNS_MEANDER_SETTINGS() + { + m_minAmplitude = 100000; + m_maxAmplitude = 1000000; + m_step = 50000; + m_spacing = 600000; + m_targetLength = 100000000; + m_targetSkew = 0; + m_cornerType = ROUND; + m_cornerRadiusPercentage = 100; + m_lengthTolerance = 100000; + m_cornerArcSegments = 8; + } + + ///> minimum meandering amplitude + int m_minAmplitude; + ///> maximum meandering amplitude + int m_maxAmplitude; + ///> meandering period/spacing (see dialog picture for explanation) + int m_spacing; + ///> amplitude/spacing adjustment step + int m_step; + ///> desired length of the tuned line/diff pair + int m_targetLength; + ///> type of corners for the meandered line + CornerType m_cornerType; + ///> rounding percentage (0 - 100) + int m_cornerRadiusPercentage; + ///> allowable tuning error + int m_lengthTolerance; + ///> number of line segments for arc approximation + int m_cornerArcSegments; + ///> target skew value for diff pair de-skewing + int m_targetSkew; +}; + +class PNS_MEANDERED_LINE; + +/** + * Class PNS_MEANDER_SETTINGS + * + * Holds the geometry of a single meander. + */ +class PNS_MEANDER_SHAPE +{ +public: + /** + * Constructor + * + * @param aPlacer the meander placer instance + * @param aWidth width of the meandered line + * @param aIsDual when true, the shape contains two meandered + * lines at a given offset (diff pairs) + */ + PNS_MEANDER_SHAPE( PNS_MEANDER_PLACER_BASE *aPlacer, int aWidth, bool aIsDual = false ) : + m_placer( aPlacer ), + m_dual( aIsDual ), + m_width( aWidth ), + m_baselineOffset( 0 ) + { + } + + /** + * Function SetType() + * + * Sets the type of the meander. + */ + void SetType( PNS_MEANDER_TYPE aType ) + { + m_type = aType; + } + + /** + * Function Type() + * + * @return the type of the meander. + */ + PNS_MEANDER_TYPE Type() const + { + return m_type; + } + + /** + * Function SetBaseIndex() + * + * Sets an auxillary index of the segment being meandered in its original PNS_LINE. + */ + void SetBaseIndex( int aIndex ) + { + m_baseIndex = aIndex; + } + + /** + * Function BaseIndex() + * + * @return auxillary index of the segment being meandered in its original PNS_LINE. + */ + int BaseIndex() const + { + return m_baseIndex; + } + + /** + * Function Amplitude() + * + * @return the amplitude of the meander shape. + */ + int Amplitude() const + { + return m_amplitude; + } + + /** + * Function MakeCorner() + * + * Creates a dummy meander shape representing a line corner. Used to define + * the starts/ends of meandered segments. + * @param aP1 corner point of the 1st line + * @param aP2 corner point of the 2nd line (if m_dual == true) + */ + void MakeCorner( VECTOR2I aP1, VECTOR2I aP2 = VECTOR2I( 0, 0 ) ); + + /** + * Function Resize() + * + * Changes the amplitude of the meander shape to aAmpl and recalculates + * the resulting line chain. + * @param aAmpl new amplitude. + */ + void Resize( int aAmpl ); + + /** + * Function Recalculate() + * + * Recalculates the line chain representing the meanders's shape. + */ + void Recalculate(); + + /** + * Function IsDual() + * + * @return true if the shape represents 2 parallel lines (diff pair). + */ + bool IsDual() const + { + return m_dual; + } + + /** + * Function Side() + * + * @return true if the meander is to the right of its base segment. + */ + bool Side() const + { + return m_side; + } + + /** + * Function End() + * + * @return end vertex of the base segment of the meander shape. + */ + VECTOR2I End() const + { + return m_clippedBaseSeg.B; + } + + /** + * Function CLine() + * + * @return the line chain representing the shape of the meander. + */ + const SHAPE_LINE_CHAIN& CLine( int aShape ) const + { + return m_shapes[aShape]; + } + + /** + * Function MakeEmpty() + * + * Replaces the meander with straight bypass line(s), effectively + * clearing it. + */ + void MakeEmpty(); + + /** + * Function Fit() + * + * Attempts to fit a meander of a given type onto a segment, avoiding + * collisions with other board features. + * @param aType type of meander shape + * @param aSeg base segment for meandering + * @param aP start point of the meander + * @param aSide side of aSeg to put the meander on (true = right) + * @return true on success. + */ + bool Fit( PNS_MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide ); + + /** + * Function BaseSegment() + * + * Returns the base segment the meadner was fitted to. + * @return the base segment. + */ + const SEG& BaseSegment() const + { + return m_clippedBaseSeg; + } + + /** + * Function BaselineLength() + * + * @return length of the base segment for the meander (i.e. + * the minimum tuned length. + */ + int BaselineLength() const; + + /** + * Function MaxTunableLength() + * + * @return the length of the fitted line chain. + */ + int MaxTunableLength() const; + + /** + * Function Settings() + * + * @return the current meandering settings. + */ + const PNS_MEANDER_SETTINGS& Settings() const; + + /** + * Function Width() + * + * @return width of the meandered line. + */ + int Width() const + { + return m_width; + } + + /** + * Function SetBaselineOffset() + * + * Sets the parallel offset between the base segment and the meandered + * line. Used for dual menaders (diff pair) only. + * @param aOffset the offset + */ + void SetBaselineOffset( int aOffset ) + { + m_baselineOffset = aOffset; + } + +private: + friend class PNS_MEANDERED_LINE; + + ///> starts turtle drawing + void start( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir ); + ///> moves turtle forward by aLength + void forward( int aLength ); + ///> turns the turtle by aAngle + void turn( int aAngle ); + ///> tells the turtle to draw an arc of given radius and turn direction + void arc( int aRadius, bool aSide ); + ///> tells the turtle to draw an U-like shape + void uShape( int aSides, int aCorner, int aTop ); + + ///> generates a 90-degree circular arc + SHAPE_LINE_CHAIN circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide ); + + ///> reflects a point onto other side of a given segment + VECTOR2I reflect( VECTOR2I aP, const SEG& aLine ); + + ///> produces a meander shape of given type + SHAPE_LINE_CHAIN genMeanderShape( VECTOR2D aP, VECTOR2D aDir, bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset = 0 ); + + ///> recalculates the clipped baseline after the parameters of + ///> the meander have been changed. + void updateBaseSegment(); + + ///> returns sanitized corner radius value + int cornerRadius() const; + + ///> returns sanitized spacing value + int spacing() const; + + ///> the type + PNS_MEANDER_TYPE m_type; + ///> the placer that placed this meander + PNS_MEANDER_PLACER_BASE* m_placer; + ///> dual or single line + bool m_dual; + ///> width of the line + int m_width; + ///> amplitude of the meander + int m_amplitude; + ///> offset wrs the base segment (dual only) + int m_baselineOffset; + ///> first point of the meandered line + VECTOR2I m_p0; + ///> base segment (unclipped) + SEG m_baseSeg; + ///> base segment (clipped) + SEG m_clippedBaseSeg; + ///> side (true = right) + bool m_side; + ///> the actual shapes (0 used for single, both for dual) + SHAPE_LINE_CHAIN m_shapes[2]; + ///> index of the meandered segment in the base line + int m_baseIndex; + ///> current turtle direction + VECTOR2D m_currentDir; + ///> current turtle position + VECTOR2D m_currentPos; + ///> the line the turtle is drawing on + SHAPE_LINE_CHAIN* m_currentTarget; +}; + + +/** + * Class PNS_MEANDERED_LINE + * + * Represents a set of meanders fitted over a single or two lines. + */ +class PNS_MEANDERED_LINE +{ +public: + PNS_MEANDERED_LINE() + { + } + + /** + * Constructor + * + * @param aPlacer the meander placer instance + * @param aIsDual when true, the meanders are generated for two coupled lines + */ + PNS_MEANDERED_LINE( PNS_MEANDER_PLACER_BASE* aPlacer, bool aIsDual = false ) : + m_placer( aPlacer ), + m_dual( aIsDual ) + { + } + + /** + * Function AddCorner() + * + * Creates a dummy meander shape representing a line corner. Used to define + * the starts/ends of meandered segments. + * @param aA corner point of the 1st line + * @param aB corner point of the 2nd line (if m_dual == true) + */ + void AddCorner( const VECTOR2I& aA, const VECTOR2I& aB = VECTOR2I( 0, 0 ) ); + + /** + * Function AddMeander() + * + * Adds a new meander shape the the meandered line. + * @param aShape the meander shape to add + */ + void AddMeander( PNS_MEANDER_SHAPE* aShape ); + + /** + * Function Clear() + * + * Clears the line geometry, removing all corners and meanders. + */ + void Clear(); + + /** + * Function SetWidth() + * + * Sets the line width. + */ + void SetWidth( int aWidth ) + { + m_width = aWidth; + } + + /** + * Function MeanderSegment() + * + * Fits maximum amplitude meanders on a given segment and adds to the + * current line. + * @param aSeg the base segment to meander + * @param aBaseIndex index of the base segment in the original line + */ + void MeanderSegment( const SEG& aSeg, int aBaseIndex = 0 ); + + /// @copydoc PNS_MEANDER_SHAPE::SetBaselineOffset() + void SetBaselineOffset( int aOffset ) + { + m_baselineOffset = aOffset; + } + + /** + * Function Meanders() + * + * @return set of meander shapes for this line + */ + std::vector& Meanders() + { + return m_meanders; + } + + /** + * Function CheckSelfIntersections() + * + * Checks if the given shape is intersecting with any other meander in + * the current line. + * @param aShape the shape to check + * @param aClearance clearance value + * @return true, if the meander shape is not colliding + */ + bool CheckSelfIntersections ( PNS_MEANDER_SHAPE* aShape, int aClearance ); + + /** + * Function Settings() + * + * @return the current meandering settings. + */ + const PNS_MEANDER_SETTINGS& Settings() const; + +private: + VECTOR2I m_last; + + PNS_MEANDER_PLACER_BASE* m_placer; + std::vector m_meanders; + + bool m_dual; + int m_width; + int m_baselineOffset; +}; + +#endif // __PNS_MEANDER_H diff --git a/pcbnew/router/pns_meander_placer.cpp b/pcbnew/router/pns_meander_placer.cpp new file mode 100644 index 0000000000..6b421d2d65 --- /dev/null +++ b/pcbnew/router/pns_meander_placer.cpp @@ -0,0 +1,269 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_meander_placer.h" +#include "pns_router.h" + + +PNS_MEANDER_PLACER::PNS_MEANDER_PLACER( PNS_ROUTER* aRouter ) : + PNS_MEANDER_PLACER_BASE( aRouter ) +{ + m_world = NULL; + m_currentNode = NULL; + m_originLine = NULL; +} + + +PNS_MEANDER_PLACER::~PNS_MEANDER_PLACER() +{ +} + + +PNS_NODE* PNS_MEANDER_PLACER::CurrentNode( bool aLoopsRemoved ) const +{ + if( !m_currentNode ) + return m_world; + + return m_currentNode; +} + + +bool PNS_MEANDER_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p; + + if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) ) + { + Router()->SetFailureReason( _( "Please select a track whose length you want to tune." ) ); + return false; + } + + m_initialSegment = static_cast( aStartItem ); + + p = m_initialSegment->Seg().NearestPoint( aP ); + + m_originLine = NULL; + m_currentNode = NULL; + m_currentStart = p; + + m_world = Router()->GetWorld()->Branch(); + m_originLine = m_world->AssembleLine( m_initialSegment ); + + PNS_TOPOLOGY topo( m_world ); + m_tunedPath = topo.AssembleTrivialPath( m_initialSegment ); + + m_world->Remove( m_originLine ); + + m_currentWidth = m_originLine->Width(); + m_currentEnd = VECTOR2I( 0, 0 ); + + return true; +} + + +int PNS_MEANDER_PLACER::origPathLength() const +{ + int total = 0; + BOOST_FOREACH( const PNS_ITEM* item, m_tunedPath.CItems() ) + { + if( const PNS_LINE* l = dyn_cast( item ) ) + { + total += l->CLine().Length(); + } + } + + return total; +} + + +bool PNS_MEANDER_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + return doMove( aP, aEndItem, m_settings.m_targetLength ); +} + + +bool PNS_MEANDER_PLACER::doMove( const VECTOR2I& aP, PNS_ITEM* aEndItem, int aTargetLength ) +{ + SHAPE_LINE_CHAIN pre, tuned, post; + + if( m_currentNode ) + delete m_currentNode; + + m_currentNode = m_world->Branch(); + + cutTunedLine( m_originLine->CLine(), m_currentStart, aP, pre, tuned, post ); + + m_result = PNS_MEANDERED_LINE( this, false ); + m_result.SetWidth( m_originLine->Width() ); + m_result.SetBaselineOffset( 0 ); + + for( int i = 0; i < tuned.SegmentCount(); i++ ) + { + const SEG s = tuned.CSegment( i ); + m_result.AddCorner( s.A ); + m_result.MeanderSegment( s ); + m_result.AddCorner( s.B ); + } + + int lineLen = origPathLength(); + + m_lastLength = lineLen; + m_lastStatus = TUNED; + + if( compareWithTolerance( lineLen, aTargetLength, m_settings.m_lengthTolerance ) > 0 ) + { + m_lastStatus = TOO_LONG; + } else { + m_lastLength = lineLen - tuned.Length(); + tuneLineLength( m_result, aTargetLength - lineLen ); + } + + BOOST_FOREACH ( const PNS_ITEM* item, m_tunedPath.CItems() ) + { + if( const PNS_LINE* l = dyn_cast( item ) ) + { + Router()->DisplayDebugLine( l->CLine(), 5, 10000 ); + } + } + + if( m_lastStatus != TOO_LONG ) + { + tuned.Clear(); + + BOOST_FOREACH( PNS_MEANDER_SHAPE* m, m_result.Meanders() ) + { + if( m->Type() != MT_EMPTY ) + { + tuned.Append ( m->CLine( 0 ) ); + } + } + + m_lastLength += tuned.Length(); + + int comp = compareWithTolerance( m_lastLength - aTargetLength, 0, m_settings.m_lengthTolerance ); + + if( comp > 0 ) + m_lastStatus = TOO_LONG; + else if( comp < 0 ) + m_lastStatus = TOO_SHORT; + else + m_lastStatus = TUNED; + } + + m_finalShape.Clear(); + m_finalShape.Append( pre ); + m_finalShape.Append( tuned ); + m_finalShape.Append( post ); + m_finalShape.Simplify(); + + return true; +} + + +bool PNS_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + m_currentTrace = PNS_LINE( *m_originLine, m_finalShape ); + m_currentNode->Add( &m_currentTrace ); + + Router()->CommitRouting( m_currentNode ); + return true; +} + + +bool PNS_MEANDER_PLACER::CheckFit( PNS_MEANDER_SHAPE* aShape ) +{ + PNS_LINE l( *m_originLine, aShape->CLine( 0 ) ); + + if( m_currentNode->CheckColliding( &l ) ) + return false; + + int w = aShape->Width(); + int clearance = w + m_settings.m_spacing; + + return m_result.CheckSelfIntersections( aShape, clearance ); +} + + +const PNS_ITEMSET PNS_MEANDER_PLACER::Traces() +{ + m_currentTrace = PNS_LINE( *m_originLine, m_finalShape ); + return PNS_ITEMSET( &m_currentTrace ); +} + + +const VECTOR2I& PNS_MEANDER_PLACER::CurrentEnd() const +{ + return m_currentEnd; +} + + +int PNS_MEANDER_PLACER::CurrentNet() const +{ + return m_initialSegment->Net(); +} + + +int PNS_MEANDER_PLACER::CurrentLayer() const +{ + return m_initialSegment->Layers().Start(); +} + + +const wxString PNS_MEANDER_PLACER::TuningInfo() const +{ + wxString status; + + switch ( m_lastStatus ) + { + case TOO_LONG: + status = _( "Too long: " ); + break; + case TOO_SHORT: + status = _( "Too short: " ); + break; + case TUNED: + status = _( "Tuned: " ); + break; + default: + return _( "?" ); + } + + status += LengthDoubleToString( (double) m_lastLength, false ); + status += "/"; + status += LengthDoubleToString( (double) m_settings.m_targetLength, false ); + + return status; +} + + +PNS_MEANDER_PLACER::TUNING_STATUS PNS_MEANDER_PLACER::TuningStatus() const +{ + return m_lastStatus; +} diff --git a/pcbnew/router/pns_meander_placer.h b/pcbnew/router/pns_meander_placer.h new file mode 100644 index 0000000000..227a0ce291 --- /dev/null +++ b/pcbnew/router/pns_meander_placer.h @@ -0,0 +1,115 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_PLACER_H +#define __PNS_MEANDER_PLACER_H + +#include + +#include +#include + +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_placement_algo.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_MEANDER_PLACER + * + * Single track length matching/meandering tool. + */ +class PNS_MEANDER_PLACER : public PNS_MEANDER_PLACER_BASE +{ +public: + + PNS_MEANDER_PLACER( PNS_ROUTER* aRouter ); + virtual ~PNS_MEANDER_PLACER(); + + /// @copydoc PNS_PLACEMENT_ALGO::Start() + virtual bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::Move() + virtual bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::FixRoute() + virtual bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentNode() + PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const; + + /// @copydoc PNS_PLACEMENT_ALGO::Traces() + const PNS_ITEMSET Traces(); + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentEnd() + const VECTOR2I& CurrentEnd() const; + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentNet() + int CurrentNet() const; + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentLayer() + int CurrentLayer() const; + + /// @copydoc PNS_MEANDER_PLACER_BASE::TuningInfo() + virtual const wxString TuningInfo() const; + + /// @copydoc PNS_MEANDER_PLACER_BASE::TuningStatus() + virtual TUNING_STATUS TuningStatus() const; + + /// @copydoc PNS_MEANDER_PLACER_BASE::CheckFit() + bool CheckFit ( PNS_MEANDER_SHAPE* aShape ); + +protected: + + bool doMove( const VECTOR2I& aP, PNS_ITEM* aEndItem, int aTargetLength ); + + void setWorld( PNS_NODE* aWorld ); + + virtual int origPathLength() const; + + ///> pointer to world to search colliding items + PNS_NODE* m_world; + + ///> current routing start point (end of tail, beginning of head) + VECTOR2I m_currentStart; + + ///> Current world state + PNS_NODE* m_currentNode; + + PNS_LINE* m_originLine; + PNS_LINE m_currentTrace; + PNS_ITEMSET m_tunedPath; + + SHAPE_LINE_CHAIN m_finalShape; + PNS_MEANDERED_LINE m_result; + PNS_SEGMENT* m_initialSegment; + + int m_lastLength; + TUNING_STATUS m_lastStatus; +}; + +#endif // __PNS_MEANDER_PLACER_H diff --git a/pcbnew/router/pns_meander_placer_base.cpp b/pcbnew/router/pns_meander_placer_base.cpp new file mode 100644 index 0000000000..291a33a763 --- /dev/null +++ b/pcbnew/router/pns_meander_placer_base.cpp @@ -0,0 +1,172 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pns_router.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" + +PNS_MEANDER_PLACER_BASE::PNS_MEANDER_PLACER_BASE( PNS_ROUTER* aRouter ) : + PNS_PLACEMENT_ALGO( aRouter ) +{ +} + + +PNS_MEANDER_PLACER_BASE::~PNS_MEANDER_PLACER_BASE() +{ +} + + +void PNS_MEANDER_PLACER_BASE::AmplitudeStep( int aSign ) +{ + int a = m_settings.m_maxAmplitude + aSign * m_settings.m_step; + a = std::max( a, m_settings.m_minAmplitude ); + + m_settings.m_maxAmplitude = a; +} + + +void PNS_MEANDER_PLACER_BASE::SpacingStep( int aSign ) +{ + int s = m_settings.m_spacing + aSign * m_settings.m_step; + s = std::max( s, 2 * m_currentWidth ); + + m_settings.m_spacing = s; +} + + +void PNS_MEANDER_PLACER_BASE::UpdateSettings( const PNS_MEANDER_SETTINGS& aSettings ) +{ + m_settings = aSettings; +} + + +void PNS_MEANDER_PLACER_BASE::cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin, + const VECTOR2I& aTuneStart, + const VECTOR2I& aCursorPos, + SHAPE_LINE_CHAIN& aPre, + SHAPE_LINE_CHAIN& aTuned, + SHAPE_LINE_CHAIN& aPost ) +{ + VECTOR2I n = aOrigin.NearestPoint( aCursorPos ); + VECTOR2I m = aOrigin.NearestPoint( aTuneStart ); + + SHAPE_LINE_CHAIN l( aOrigin ); + l.Split( n ); + l.Split( m ); + + int i_start = l.Find( m ); + int i_end = l.Find( n ); + + if( i_start > i_end ) + { + l = l.Reverse(); + i_start = l.Find( m ); + i_end = l.Find( n ); + } + + aPre = l.Slice( 0, i_start ); + aPost = l.Slice( i_end, -1 ); + aTuned = l.Slice( i_start, i_end ); + aTuned.Simplify(); +} + + +void PNS_MEANDER_PLACER_BASE::tuneLineLength( PNS_MEANDERED_LINE& aTuned, int aElongation ) +{ + int remaining = aElongation; + bool finished = false; + + BOOST_FOREACH( PNS_MEANDER_SHAPE* m, aTuned.Meanders() ) + { + if( m->Type() != MT_CORNER ) + { + if( remaining >= 0 ) + remaining -= m->MaxTunableLength() - m->BaselineLength(); + + if( remaining < 0 ) + { + if( !finished ) + { + PNS_MEANDER_TYPE newType; + + if( m->Type() == MT_START || m->Type() == MT_SINGLE ) + newType = MT_SINGLE; + else + newType = MT_FINISH; + + m->SetType( newType ); + m->Recalculate(); + + finished = true; + } else { + m->MakeEmpty(); + } + } + } + } + + remaining = aElongation; + int meanderCount = 0; + + BOOST_FOREACH(PNS_MEANDER_SHAPE* m, aTuned.Meanders()) + { + if( m->Type() != MT_CORNER && m->Type() != MT_EMPTY ) + { + if(remaining >= 0) + { + remaining -= m->MaxTunableLength() - m->BaselineLength(); + meanderCount ++; + } + } + } + + int balance = 0; + + if( meanderCount ) + balance = -remaining / meanderCount; + + if( balance >= 0 ) + { + BOOST_FOREACH( PNS_MEANDER_SHAPE* m, aTuned.Meanders() ) + { + if( m->Type() != MT_CORNER && m->Type() != MT_EMPTY ) + { + m->Resize( std::max( m->Amplitude() - balance / 2, m_settings.m_minAmplitude ) ); + } + } + } +} + + +const PNS_MEANDER_SETTINGS& PNS_MEANDER_PLACER_BASE::MeanderSettings() const +{ + return m_settings; +} + + +int PNS_MEANDER_PLACER_BASE::compareWithTolerance( int aValue, int aExpected, int aTolerance ) const +{ + if( aValue < aExpected - aTolerance ) + return -1; + else if( aValue > aExpected + aTolerance ) + return 1; + else + return 0; +} diff --git a/pcbnew/router/pns_meander_placer_base.h b/pcbnew/router/pns_meander_placer_base.h new file mode 100644 index 0000000000..070e322d3f --- /dev/null +++ b/pcbnew/router/pns_meander_placer_base.h @@ -0,0 +1,165 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_PLACER_BASE_H +#define __PNS_MEANDER_PLACER_BASE_H + +#include + +#include +#include + +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_placement_algo.h" +#include "pns_meander.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_MEANDER_PLACER_BASE + * + * Base class for Single trace & Differenial pair meandering tools, as + * both of them share a lot of code. + */ +class PNS_MEANDER_PLACER_BASE : public PNS_PLACEMENT_ALGO +{ +public: + ///> Result of the length tuning operation + enum TUNING_STATUS { + TOO_SHORT = 0, + TOO_LONG, + TUNED + }; + + PNS_MEANDER_PLACER_BASE( PNS_ROUTER* aRouter ); + virtual ~PNS_MEANDER_PLACER_BASE(); + + /** + * Function TuningInfo() + * + * Returns a string describing the status and length of the + * tuned traces. + */ + virtual const wxString TuningInfo() const = 0; + + /** + * Function TuningStatus() + * + * Returns the tuning status (too short, too long, etc.) + * of the trace(s) being tuned. + */ + virtual TUNING_STATUS TuningStatus() const = 0; + + /** + * Function AmplitudeStep() + * + * Increases/decreases the current meandering amplitude by one step. + * @param aSign direction (negative = decrease, positive = increase). + */ + virtual void AmplitudeStep( int aSign ); + + /** + * Function SpacingStep() + * + * Increases/decreases the current meandering spcing by one step. + * @param aSign direction (negative = decrease, positive = increase). + */ + virtual void SpacingStep( int aSign ); + + /** + * Function MeanderSettings() + * + * Returns the current meandering configuration. + * @return the settings + */ + virtual const PNS_MEANDER_SETTINGS& MeanderSettings() const; + + /* + * Function UpdateSettings() + * + * Sets the current meandering configuration. + * @param aSettings the settings + */ + virtual void UpdateSettings( const PNS_MEANDER_SETTINGS& aSettings); + + /** + * Function CheckFit() + * + * Checks if it's ok to place the shape aShape (i.e. + * if it doesn't cause DRC violations or collide with + * other meanders). + * @param aShape the shape to check + * @return true if the shape fits + */ + virtual bool CheckFit( PNS_MEANDER_SHAPE* aShape ) + { + return false; + } + +protected: + + /** + * Function cutTunedLine() + * + * Extracts the part of a track to be meandered, depending on the + * starting point and the cursor position. + * @param aOrigin the original line + * @param aTuneStart point where we start meandering (start click coorinates) + * @param aCursorPos current cursor position + * @param aPre part before the beginning of meanders + * @param aTuned part to be meandered + * @param aPost part after the end of meanders + */ + void cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin, + const VECTOR2I& aTuneStart, + const VECTOR2I& aCursorPos, + SHAPE_LINE_CHAIN& aPre, + SHAPE_LINE_CHAIN& aTuned, + SHAPE_LINE_CHAIN& aPost ); + + /** + * Function tuneLineLength() + * + * Takes a set of meanders in aTuned and tunes their length to + * extend the original line length by aElongation. + */ + void tuneLineLength( PNS_MEANDERED_LINE& aTuned, int aElongation ); + + /** + * Function compareWithTolerance() + * + * Compares aValue against aExpected with given tolerance. + */ + int compareWithTolerance ( int aValue, int aExpected, int aTolerance = 0 ) const; + + ///> width of the meandered trace(s) + int m_currentWidth; + ///> meandering settings + PNS_MEANDER_SETTINGS m_settings; + ///> current end point + VECTOR2I m_currentEnd; +}; + +#endif // __PNS_MEANDER_PLACER_BASE_H diff --git a/pcbnew/router/pns_meander_skew_placer.cpp b/pcbnew/router/pns_meander_skew_placer.cpp new file mode 100644 index 0000000000..2b670a5db9 --- /dev/null +++ b/pcbnew/router/pns_meander_skew_placer.cpp @@ -0,0 +1,160 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_meander_skew_placer.h" + +#include "pns_router.h" + + +PNS_MEANDER_SKEW_PLACER::PNS_MEANDER_SKEW_PLACER ( PNS_ROUTER* aRouter ) : + PNS_MEANDER_PLACER ( aRouter ) +{ +} + + +PNS_MEANDER_SKEW_PLACER::~PNS_MEANDER_SKEW_PLACER( ) +{ +} + + +bool PNS_MEANDER_SKEW_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p; + + if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) ) + { + Router()->SetFailureReason( _( "Please select a differential pair trace you want to tune." ) ); + return false; + } + + m_initialSegment = static_cast( aStartItem ); + + p = m_initialSegment->Seg().NearestPoint( aP ); + + m_originLine = NULL; + m_currentNode = NULL; + m_currentStart = p; + + m_world = Router()->GetWorld( )->Branch(); + m_originLine = m_world->AssembleLine( m_initialSegment ); + + PNS_TOPOLOGY topo( m_world ); + m_tunedPath = topo.AssembleTrivialPath( m_initialSegment ); + + if( !topo.AssembleDiffPair ( m_initialSegment, m_originPair ) ) + { + Router()->SetFailureReason( _( "Unable to find complementary differential pair " + "net for skew tuning. Make sure the names of the nets belonging " + "to a differential pair end with either _N/_P or +/-." ) ); + return false; + } + + m_originPair.SetGap ( Router()->Sizes().DiffPairGap() ); + + if( !m_originPair.PLine().SegmentCount() || + !m_originPair.NLine().SegmentCount() ) + return false; + + m_tunedPathP = topo.AssembleTrivialPath ( m_originPair.PLine().GetLink( 0 ) ); + m_tunedPathN = topo.AssembleTrivialPath ( m_originPair.NLine().GetLink( 0 ) ); + + m_world->Remove( m_originLine ); + + m_currentWidth = m_originLine->Width( ); + m_currentEnd = VECTOR2I( 0, 0 ); + + if ( m_originPair.PLine().Net () == m_originLine->Net() ) + m_coupledLength = itemsetLength ( m_tunedPathN ); + else + m_coupledLength = itemsetLength ( m_tunedPathP ); + + return true; +} + + +int PNS_MEANDER_SKEW_PLACER::origPathLength( ) const +{ + return itemsetLength ( m_tunedPath ); +} + + +int PNS_MEANDER_SKEW_PLACER::itemsetLength( const PNS_ITEMSET& aSet ) const +{ + int total = 0; + BOOST_FOREACH( const PNS_ITEM* item, aSet.CItems() ) + { + if( const PNS_LINE* l = dyn_cast( item ) ) + { + total += l->CLine().Length(); + } + } + + return total; +} + + +int PNS_MEANDER_SKEW_PLACER::currentSkew() const +{ + return m_lastLength - m_coupledLength; +} + + +bool PNS_MEANDER_SKEW_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + return doMove( aP, aEndItem, m_coupledLength + m_settings.m_targetSkew ); +} + + +const wxString PNS_MEANDER_SKEW_PLACER::TuningInfo() const +{ + wxString status; + + switch( m_lastStatus ) + { + case TOO_LONG: + status = _( "Too long: skew " ); + break; + case TOO_SHORT: + status = _( "Too short: skew " ); + break; + case TUNED: + status = _( "Tuned: skew " ); + break; + default: + return _( "?" ); + } + + status += LengthDoubleToString( (double) m_lastLength - m_coupledLength, false ); + status += "/"; + status += LengthDoubleToString( (double) m_settings.m_targetSkew, false ); + + return status; +} + diff --git a/pcbnew/router/pns_meander_skew_placer.h b/pcbnew/router/pns_meander_skew_placer.h new file mode 100644 index 0000000000..7cb303c15d --- /dev/null +++ b/pcbnew/router/pns_meander_skew_placer.h @@ -0,0 +1,65 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_SKEW_PLACER_H +#define __PNS_MEANDER_SKEW_PLACER_H + +#include "pns_meander_placer.h" +#include "pns_diff_pair.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_MEANDER_SKEW_PLACER + * + * Differential pair skew adjustment algorithm. + */ +class PNS_MEANDER_SKEW_PLACER : public PNS_MEANDER_PLACER +{ +public: + PNS_MEANDER_SKEW_PLACER( PNS_ROUTER* aRouter ); + ~PNS_MEANDER_SKEW_PLACER(); + + /// @copydoc PNS_PLACEMENT_ALGO::Start() + bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::Move() + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /// @copydoc PNS_MEANDER_PLACER_BASE::TuningInfo() + const wxString TuningInfo() const; + +private: + + int currentSkew( ) const; + int itemsetLength( const PNS_ITEMSET& aSet ) const; + + int origPathLength () const; + + PNS_DIFF_PAIR m_originPair; + PNS_ITEMSET m_tunedPath, m_tunedPathP, m_tunedPathN; + + int m_coupledLength; +}; + +#endif // __PNS_MEANDER_SKEW_PLACER_H diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp index 8ee7feeb3d..08e7d75a1c 100644 --- a/pcbnew/router/pns_node.cpp +++ b/pcbnew/router/pns_node.cpp @@ -53,6 +53,7 @@ PNS_NODE::PNS_NODE() m_parent = NULL; m_maxClearance = 800000; // fixme: depends on how thick traces are. m_index = new PNS_INDEX; + m_collisionFilter = NULL; #ifdef DEBUG allocNodes.insert( this ); @@ -63,7 +64,7 @@ PNS_NODE::PNS_NODE() PNS_NODE::~PNS_NODE() { TRACE( 0, "PNS_NODE::delete %p", this ); - + if( !m_children.empty() ) { TRACEn( 0, "attempting to free a node that has kids.\n" ); @@ -102,13 +103,14 @@ PNS_NODE* PNS_NODE::Branch() PNS_NODE* child = new PNS_NODE; TRACE( 0, "PNS_NODE::branch %p (parent %p)", child % this ); - + m_children.push_back( child ); child->m_depth = m_depth + 1; child->m_parent = this; child->m_clearanceFunctor = m_clearanceFunctor; child->m_root = isRoot() ? this : m_root; + child->m_collisionFilter = m_collisionFilter; // immmediate offspring of the root branch needs not copy anything. // For the rest, deep-copy joints, overridden item map and pointers @@ -173,8 +175,8 @@ struct PNS_NODE::OBSTACLE_VISITOR ///> number of items found so far int m_matchCount; - ///> additional clearance - int m_extraClearance; + ///> additional clearance + int m_extraClearance; OBSTACLE_VISITOR( PNS_NODE::OBSTACLES& aTab, const PNS_ITEM* aItem, int aKindMask ) : m_tab( aTab ), @@ -182,11 +184,11 @@ struct PNS_NODE::OBSTACLE_VISITOR m_kindMask( aKindMask ), m_limitCount( -1 ), m_matchCount( 0 ), - m_extraClearance( 0 ) + m_extraClearance( 0 ) { - if( aItem->Kind() == PNS_ITEM::LINE ) + if( aItem->Kind() == PNS_ITEM::LINE ) m_extraClearance += static_cast( aItem )->Width() / 2; - } + } void SetCountLimit( int aLimit ) { @@ -211,6 +213,9 @@ struct PNS_NODE::OBSTACLE_VISITOR int clearance = m_extraClearance + m_node->GetClearance( aItem, m_item ); + if( m_node->m_collisionFilter && (*m_node->m_collisionFilter)(aItem, m_item)) + return true; + if( aItem->Kind() == PNS_ITEM::LINE ) clearance += static_cast(aItem)->Width() / 2; @@ -220,6 +225,7 @@ struct PNS_NODE::OBSTACLE_VISITOR PNS_OBSTACLE obs; obs.m_item = aItem; + obs.m_head = m_item; m_tab.push_back( obs ); m_matchCount++; @@ -414,12 +420,16 @@ PNS_NODE::OPT_OBSTACLE PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKi } -bool PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB, int aKindMask ) +bool PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB, int aKindMask, int aForceClearance ) { assert( aItemB ); - int clearance = GetClearance( aItemA, aItemB ); + int clearance; + if( aForceClearance >= 0 ) + clearance = aForceClearance; + else + clearance = GetClearance( aItemA, aItemB ); - // fixme: refactor + // fixme: refactor if( aItemA->Kind() == PNS_ITEM::LINE ) clearance += static_cast( aItemA )->Width() / 2; if( aItemB->Kind() == PNS_ITEM::LINE ) @@ -523,7 +533,7 @@ void PNS_NODE::addLine( PNS_LINE* aLine, bool aAllowRedundant ) m_index->Add( pseg ); } - } + } } } @@ -538,7 +548,7 @@ void PNS_NODE::addSegment( PNS_SEGMENT* aSeg, bool aAllowRedundant ) if( !aAllowRedundant && findRedundantSegment ( aSeg ) ) return; - + aSeg->SetOwner( this ); linkJoint( aSeg->Seg().A, aSeg->Layers(), aSeg->Net(), aSeg ); @@ -609,13 +619,13 @@ void PNS_NODE::removeLine( PNS_LINE* aLine ) { std::vector* segRefs = aLine->LinkedSegments(); - if(! aLine->SegmentCount() ) + if( !aLine->SegmentCount() ) return; - assert (segRefs != NULL); - assert (aLine->Owner()); - - if ( (int) segRefs->size() != aLine->SegmentCount() ) + assert( segRefs != NULL ); + assert( aLine->Owner() ); + + if( (int) segRefs->size() != aLine->SegmentCount() ) { //printf("******weird deletion: segrefs %d segcount %d hasloops %d\n", segRefs->size(), aLine->SegmentCount(), aLine->HasLoops()); } @@ -647,7 +657,7 @@ void PNS_NODE::removeVia( PNS_VIA* aVia ) tag.net = net; tag.pos = p; - bool split; + bool split; do { split = false; @@ -668,7 +678,7 @@ void PNS_NODE::removeVia( PNS_VIA* aVia ) } } } while( split ); - + // and re-link them, using the former via's link list BOOST_FOREACH(PNS_ITEM* item, links) { @@ -676,7 +686,7 @@ void PNS_NODE::removeVia( PNS_VIA* aVia ) linkJoint ( p, item->Layers(), net, item ); } - doRemove( aVia ); + doRemove( aVia ); } @@ -714,6 +724,12 @@ void PNS_NODE::Remove( PNS_ITEM* aItem ) } +void PNS_NODE::Remove( PNS_LINE& aLine ) +{ + removeLine( &aLine ); +} + + void PNS_NODE::followLine( PNS_SEGMENT* aCurrent, bool aScanDirection, int& aPos, int aLimit, VECTOR2I* aCorners, PNS_SEGMENT** aSegments, bool& aGuardHit ) { @@ -739,7 +755,7 @@ void PNS_NODE::followLine( PNS_SEGMENT* aCurrent, bool aScanDirection, int& aPos } aSegments[aPos] = aCurrent; - + aPos += ( aScanDirection ? 1 : -1 ); if( !jt->IsLineCorner() || aPos < 0 || aPos == aLimit ) @@ -748,14 +764,14 @@ void PNS_NODE::followLine( PNS_SEGMENT* aCurrent, bool aScanDirection, int& aPos aCurrent = jt->NextSegment( aCurrent ); prevReversed = - ( jt->Pos() == (aScanDirection ? aCurrent->Seg().B : aCurrent->Seg().A ) ); + ( jt->Pos() == ( aScanDirection ? aCurrent->Seg().B : aCurrent->Seg().A ) ); } } PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex) { - const int MaxVerts = 1024; + const int MaxVerts = 1024 * 16; VECTOR2I corners[MaxVerts + 1]; PNS_SEGMENT* segs[MaxVerts + 1]; @@ -771,10 +787,10 @@ PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex) pl->SetOwner( this ); followLine( aSeg, false, i_start, MaxVerts, corners, segs, guardHit ); - + if( !guardHit ) followLine( aSeg, true, i_end, MaxVerts, corners, segs, guardHit ); - + int n = 0; PNS_SEGMENT* prev_seg = NULL; @@ -784,7 +800,7 @@ PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex) const VECTOR2I& p = corners[i]; pl->Line().Append( p ); - + if( segs[i] && prev_seg != segs[i] ) { pl->LinkSegment( segs[i] ); @@ -800,7 +816,7 @@ PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex) assert( pl->SegmentCount() != 0 ); assert( pl->SegmentCount() == (int) pl->LinkedSegments()->size() ); - + return pl; } @@ -812,6 +828,7 @@ void PNS_NODE::FindLineEnds( PNS_LINE* aLine, PNS_JOINT& aA, PNS_JOINT& aB ) } +#if 0 void PNS_NODE::MapConnectivity ( PNS_JOINT* aStart, std::vector& aFoundJoints ) { std::deque searchQueue; @@ -839,58 +856,14 @@ void PNS_NODE::MapConnectivity ( PNS_JOINT* aStart, std::vector& aFo processed.insert( next ); searchQueue.push_back( next ); } - } + } } } BOOST_FOREACH(PNS_JOINT* jt, processed) aFoundJoints.push_back( jt ); } - - -PNS_ITEM* PNS_NODE::NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor, int aKindMask ) -{ - std::set disconnected; - std::vector joints; - - AllItemsInNet( aStart->Net(), disconnected ); - MapConnectivity ( aStart, joints ); - - BOOST_FOREACH( PNS_JOINT *jt, joints ) - { - BOOST_FOREACH( PNS_ITEM* link, jt->LinkList() ) - { - if( disconnected.find( link ) != disconnected.end() ) - disconnected.erase( link ); - } - } - - int best_dist = INT_MAX; - PNS_ITEM* best = NULL; - - BOOST_FOREACH( PNS_ITEM* item, disconnected ) - { - if( item->OfKind( aKindMask ) ) - { - for(int i = 0; i < item->AnchorCount(); i++) - { - VECTOR2I p = item->Anchor( i ); - int d = ( p - aStart->Pos() ).EuclideanNorm(); - - if( d < best_dist ) - { - best_dist = d; - best = item; - - if( aAnchor ) - *aAnchor = i; - } - } - } - } - - return best; -} +#endif int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& aA, PNS_JOINT& aB, std::vector& aLines ) @@ -903,7 +876,7 @@ int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& aA, PNS_JOINT& aB, std::vector< PNS_LINE* line = AssembleLine( seg ); PNS_JOINT j_start, j_end; - + FindLineEnds( line, j_start, j_end ); int id_start = line->CLine().Find( aA.Pos() ); @@ -1046,13 +1019,13 @@ void PNS_NODE::Dump( bool aLong ) } if( !isRoot() ) - { + { for( i = m_root->m_items.begin(); i != m_root->m_items.end(); i++ ) { if( (*i)->GetKind() == PNS_ITEM::SEGMENT && !overrides( *i ) ) all_segs.insert( static_cast(*i) ); } - } + } JOINT_MAP::iterator j; @@ -1179,7 +1152,7 @@ void PNS_NODE::AllItemsInNet( int aNet, std::set& aItems ) BOOST_FOREACH( PNS_ITEM*item, *l_cur ) aItems.insert( item ); } - + if( !isRoot() ) { PNS_INDEX::NET_ITEMS_LIST* l_root = m_root->m_index->GetItemsForNet( aNet ); @@ -1192,13 +1165,13 @@ void PNS_NODE::AllItemsInNet( int aNet, std::set& aItems ) } -void PNS_NODE::ClearRanks() +void PNS_NODE::ClearRanks( int aMarkerMask ) { - for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i ) - { - (*i)->SetRank( -1 ); - (*i)->Mark( 0 ); - } + for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i ) + { + (*i)->SetRank( -1 ); + (*i)->Mark( (*i)->Marker() & (~aMarkerMask) ); + } } @@ -1227,7 +1200,7 @@ int PNS_NODE::RemoveByMarker( int aMarker ) } for( std::list::const_iterator i = garbage.begin(), end = garbage.end(); i != end; ++i ) - { + { Remove( *i ); } @@ -1235,7 +1208,7 @@ int PNS_NODE::RemoveByMarker( int aMarker ) } -PNS_SEGMENT* PNS_NODE::findRedundantSegment ( PNS_SEGMENT *aSeg ) +PNS_SEGMENT* PNS_NODE::findRedundantSegment( PNS_SEGMENT* aSeg ) { PNS_JOINT* jtStart = FindJoint ( aSeg->Seg().A, aSeg ); @@ -1247,18 +1220,23 @@ PNS_SEGMENT* PNS_NODE::findRedundantSegment ( PNS_SEGMENT *aSeg ) if( item->OfKind( PNS_ITEM::SEGMENT ) ) { PNS_SEGMENT* seg2 = (PNS_SEGMENT*) item; - + const VECTOR2I a1( aSeg->Seg().A ); const VECTOR2I b1( aSeg->Seg().B ); - + const VECTOR2I a2( seg2->Seg().A ); const VECTOR2I b2( seg2->Seg().B ); - - if( seg2->Layers().Start() == aSeg->Layers().Start() && + + if( seg2->Layers().Start() == aSeg->Layers().Start() && ( ( a1 == a2 && b1 == b2 ) || ( a1 == b2 && a2 == b1 ) ) ) return seg2; } } - + return NULL; } + +void PNS_NODE::SetCollisionFilter( PNS_COLLISION_FILTER* aFilter ) +{ + m_collisionFilter = aFilter; +} diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h index 13e1672f6a..915d0b044d 100644 --- a/pcbnew/router/pns_node.h +++ b/pcbnew/router/pns_node.h @@ -42,7 +42,7 @@ class PNS_SOLID; class PNS_VIA; class PNS_RATSNEST; class PNS_INDEX; - +class PNS_ROUTER; /** * Class PNS_CLEARANCE_FUNC @@ -52,8 +52,34 @@ class PNS_INDEX; class PNS_CLEARANCE_FUNC { public: - virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) = 0; virtual ~PNS_CLEARANCE_FUNC() {} + virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) = 0; + virtual void OverrideClearance (bool aEnable, int aNetA = 0, int aNetB = 0, int aClearance = 0) = 0; +}; + +class PNS_PCBNEW_CLEARANCE_FUNC : public PNS_CLEARANCE_FUNC +{ +public: + PNS_PCBNEW_CLEARANCE_FUNC( PNS_ROUTER *aRouter ); + virtual ~PNS_PCBNEW_CLEARANCE_FUNC(); + + virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ); + virtual void OverrideClearance (bool aEnable, int aNetA = 0, int aNetB = 0, int aClearance = 0); + +private: + struct CLEARANCE_ENT { + int coupledNet; + int clearance; + }; + + PNS_ROUTER *m_router; + + int localPadClearance( const PNS_ITEM* aItem ) const; + std::vector m_clearanceCache; + int m_defaultClearance; + bool m_overrideEnabled; + int m_overrideNetA, m_overrideNetB; + int m_overrideClearance; }; /** @@ -65,7 +91,7 @@ public: struct PNS_OBSTACLE { ///> Item we search collisions with - PNS_ITEM* m_head; + const PNS_ITEM* m_head; ///> Item found to be colliding with m_head PNS_ITEM* m_item; @@ -81,6 +107,15 @@ struct PNS_OBSTACLE int m_distFirst, m_distLast; }; +/** + * Struct PNS_COLLISION_FILTER + * Used to override the decision of the collision search algorithm whether two + * items collide. + **/ +struct PNS_COLLISION_FILTER { + virtual bool operator()( const PNS_ITEM *aItemA, const PNS_ITEM *aItemB ) const = 0; +}; + /** * Class PNS_NODE * @@ -201,7 +236,8 @@ public: */ bool CheckColliding( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB, - int aKindMask = PNS_ITEM::ANY ); + int aKindMask = PNS_ITEM::ANY, + int aForceClearance = -1 ); /** * Function HitTest() @@ -230,6 +266,15 @@ public: */ void Remove( PNS_ITEM* aItem ); + /** + * Function Remove() + * + * Just as the name says, removes a line from this branch. + * @param aItem item to remove + */ + void Remove( PNS_LINE& aLine ); + + /** * Function Replace() * @@ -258,7 +303,7 @@ public: * @param aOriginSegmentIndex index of aSeg in the resulting line * @return the line */ - PNS_LINE* AssembleLine( PNS_SEGMENT* aSeg, int *aOriginSegmentIndex = NULL ); + PNS_LINE* AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex = NULL ); ///> Prints the contents and joints structure void Dump( bool aLong = false ); @@ -296,16 +341,18 @@ public: * Searches for a joint at a given position, linked to given item. * @return the joint, if found, otherwise empty */ - PNS_JOINT* FindJoint( const VECTOR2I& aPos, PNS_ITEM* aItem ) + PNS_JOINT* FindJoint( const VECTOR2I& aPos, const PNS_ITEM* aItem ) { return FindJoint( aPos, aItem->Layers().Start(), aItem->Net() ); } +#if 0 void MapConnectivity( PNS_JOINT* aStart, std::vector & aFoundJoints ); PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int *aAnchor = NULL, int aKindMask = PNS_ITEM::ANY); +#endif ///> finds all lines between a pair of joints. Used by the loop removal procedure. int FindLinesBetweenJoints( PNS_JOINT& aA, @@ -320,10 +367,11 @@ public: void AllItemsInNet( int aNet, std::set& aItems ); - void ClearRanks(); + void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION ); int FindByMarker( int aMarker, PNS_ITEMSET& aItems ); int RemoveByMarker( int aMarker ); + void SetCollisionFilter( PNS_COLLISION_FILTER* aFilter ); private: struct OBSTACLE_VISITOR; @@ -411,6 +459,9 @@ private: ///> depth of the node (number of parent nodes in the inheritance chain) int m_depth; + + ///> optional collision filtering object + PNS_COLLISION_FILTER *m_collisionFilter; }; #endif diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp index 6451bca600..0cebcd86ab 100644 --- a/pcbnew/router/pns_optimizer.cpp +++ b/pcbnew/router/pns_optimizer.cpp @@ -24,6 +24,7 @@ #include #include "pns_line.h" +#include "pns_diff_pair.h" #include "pns_node.h" #include "pns_optimizer.h" #include "pns_utils.h" @@ -100,14 +101,14 @@ void PNS_COST_ESTIMATOR::Replace( PNS_LINE& aOldLine, PNS_LINE& aNewLine ) bool PNS_COST_ESTIMATOR::IsBetter( PNS_COST_ESTIMATOR& aOther, - double aLengthTollerance, - double aCornerTollerance ) const + double aLengthTolerance, + double aCornerTolerance ) const { if( aOther.m_cornerCost < m_cornerCost && aOther.m_lengthCost < m_lengthCost ) return true; - else if( aOther.m_cornerCost < m_cornerCost * aCornerTollerance && - aOther.m_lengthCost < m_lengthCost * aLengthTollerance ) + else if( aOther.m_cornerCost < m_cornerCost * aCornerTolerance && + aOther.m_lengthCost < m_lengthCost * aLengthTolerance ) return true; return false; @@ -121,6 +122,7 @@ PNS_OPTIMIZER::PNS_OPTIMIZER( PNS_NODE* aWorld ) : m_world( aWorld ), m_collisionKindMask( PNS_ITEM::ANY ), m_effortLevel( MERGE_SEGMENTS ) { // m_cache = new SHAPE_INDEX_LIST(); + m_restrictAreaActive = false; } @@ -223,6 +225,158 @@ void PNS_OPTIMIZER::ClearCache( bool aStaticOnly ) } +class LINE_RESTRICTIONS +{ + public: + LINE_RESTRICTIONS() {}; + ~LINE_RESTRICTIONS() {}; + + void Build( PNS_NODE* aWorld, PNS_LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable ); + bool Check ( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement ); + void Dump(); + + private: + int allowedAngles( PNS_NODE* aWorld, const PNS_LINE* aLine, const VECTOR2I& aP, bool aFirst ); + + struct RVERTEX + { + RVERTEX ( bool aRestricted, int aAllowedAngles ) : + restricted( aRestricted ), + allowedAngles( aAllowedAngles ) + { + } + + bool restricted; + int allowedAngles; + }; + + std::vector m_rs; +}; + + +// fixme: use later +int LINE_RESTRICTIONS::allowedAngles( PNS_NODE* aWorld, const PNS_LINE* aLine, const VECTOR2I& aP, bool aFirst ) +{ + PNS_JOINT* jt = aWorld->FindJoint( aP , aLine ); + + if( !jt ) + return 0xff; + + DIRECTION_45 dirs [8]; + + int n_dirs = 0; + + BOOST_FOREACH( const PNS_ITEM* item, jt->Links().CItems() ) + { + if( item->OfKind( PNS_ITEM::VIA ) || item->OfKind( PNS_ITEM::SOLID ) ) + return 0xff; + else if( const PNS_SEGMENT* seg = dyn_cast( item ) ) + { + SEG s = seg->Seg(); + if( s.A != aP ) + s.Reverse(); + + if( n_dirs < 8 ) + dirs[n_dirs++] = aFirst ? DIRECTION_45( s ) : DIRECTION_45( s ).Opposite(); + } + } + + const int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_STRAIGHT; + int outputMask = 0xff; + + for( int d = 0; d < 8; d++ ) + { + DIRECTION_45 refDir( ( DIRECTION_45::Directions ) d ); + + for( int i = 0; i < n_dirs; i++ ) + { + if( !(refDir.Angle( dirs[i] ) & angleMask ) ) + outputMask &= ~refDir.Mask(); + } + } + + DrawDebugDirs( aP, outputMask, 3 ); + return 0xff; +} + + +void LINE_RESTRICTIONS::Build( PNS_NODE* aWorld, PNS_LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable ) +{ + const SHAPE_LINE_CHAIN& l = aLine; + VECTOR2I v_prev; + int n = l.PointCount( ); + + m_rs.reserve( n ); + + for( int i = 0; i < n; i++ ) + { + const VECTOR2I &v = l.CPoint( i ), v_next; + RVERTEX r( false, 0xff ); + + if( aRestrictedAreaEnable ) + { + bool exiting = ( i > 0 && aRestrictedArea.Contains( v_prev ) && !aRestrictedArea.Contains( v ) ); + bool entering = false; + + if( i != l.PointCount() - 1 ) + { + const VECTOR2I& v_next = l.CPoint( i + 1 ); + entering = ( !aRestrictedArea.Contains( v ) && aRestrictedArea.Contains( v_next ) ); + } + + if( entering ) + { + const SEG& sp = l.CSegment( i ); + r.allowedAngles = DIRECTION_45( sp ).Mask(); + } + else if( exiting ) + { + const SEG& sp = l.CSegment( i - 1 ); + r.allowedAngles = DIRECTION_45( sp ).Mask(); + } + else + { + r.allowedAngles = ( !aRestrictedArea.Contains( v ) ) ? 0 : 0xff; + r.restricted = r.allowedAngles ? false : true; + } + } + + v_prev = v; + m_rs.push_back( r ); + } +} + + +void LINE_RESTRICTIONS::Dump() +{ +} + + +bool LINE_RESTRICTIONS::Check( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement ) +{ + if( m_rs.empty( ) ) + return true; + + for( int i = aVertex1; i <= aVertex2; i++ ) + if ( m_rs[i].restricted ) + return false; + + const RVERTEX& v1 = m_rs[ aVertex1 ]; + const RVERTEX& v2 = m_rs[ aVertex2 ]; + + int m1 = DIRECTION_45( aReplacement.CSegment( 0 ) ).Mask(); + int m2; + + if( aReplacement.SegmentCount() == 1 ) + m2 = m1; + else + m2 = DIRECTION_45( aReplacement.CSegment( 1 ) ).Mask(); + + return ( ( v1.allowedAngles & m1 ) != 0 ) && + ( ( v2.allowedAngles & m2 ) != 0 ); +} + + bool PNS_OPTIMIZER::checkColliding( PNS_ITEM* aItem, bool aUpdateCache ) { CACHE_VISITOR v( aItem, m_world, m_collisionKindMask ); @@ -314,7 +468,6 @@ bool PNS_OPTIMIZER::mergeObtuse( PNS_LINE* aLine ) s2opt = SEG( ip, s2.B ); } - if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) ) { SHAPE_LINE_CHAIN opt_path; @@ -391,7 +544,7 @@ bool PNS_OPTIMIZER::mergeFull( PNS_LINE* aLine ) } -bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, PNS_LINE* aResult )//, int aStartVertex, int aEndVertex ) +bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, PNS_LINE* aResult ) { if( !aResult ) aResult = aLine; @@ -425,12 +578,16 @@ bool PNS_OPTIMIZER::mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int cost_orig = PNS_COST_ESTIMATOR::CornerCost( aCurrentPath ); + LINE_RESTRICTIONS restr; + if( aLine->SegmentCount() < 4 ) return false; DIRECTION_45 orig_start( aLine->CSegment( 0 ) ); DIRECTION_45 orig_end( aLine->CSegment( -1 ) ); + restr.Build( m_world, aLine, aCurrentPath, m_restrictArea, m_restrictAreaActive ); + while( n < n_segs - step ) { const SEG s1 = aCurrentPath.CSegment( n ); @@ -446,13 +603,14 @@ bool PNS_OPTIMIZER::mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i ); cost[i] = INT_MAX; + bool restrictionsOK = restr.Check ( n, n + step + 1, bypass ); if( n == 0 && orig_start != DIRECTION_45( bypass.CSegment( 0 ) ) ) postureMatch = false; else if( n == n_segs - step && orig_end != DIRECTION_45( bypass.CSegment( -1 ) ) ) postureMatch = false; - if( (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) ) + if( restrictionsOK && (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) ) { path[i] = aCurrentPath; path[i].Replace( s1.Index(), s2.Index(), bypass ); @@ -647,7 +805,7 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, if(!connect.SegmentCount()) continue; - + int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) ); int ang2 = 0; @@ -717,7 +875,7 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, bool PNS_OPTIMIZER::runSmartPads( PNS_LINE* aLine ) { SHAPE_LINE_CHAIN& line = aLine->Line(); - + if( line.PointCount() < 3 ) return false; @@ -761,28 +919,29 @@ bool PNS_OPTIMIZER::fanoutCleanup( PNS_LINE* aLine ) PNS_ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start ); PNS_ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end ); - int thr = aLine->Width() * 10; + int thr = aLine->Width() * 10; int len = aLine->CLine().Length(); if( !startPad ) return false; bool startMatch = startPad->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ); - bool endMatch = false; + bool endMatch = false; if(endPad) { endMatch = endPad->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ); - } else { + } + else + { endMatch = aLine->EndsWithVia(); } - + if( startMatch && endMatch && len < thr ) { - for(int i = 0; i < 2; i++ ) + for( int i = 0; i < 2; i++ ) { SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i ); - PNS_ROUTER::GetInstance()->DisplayDebugLine( l2, 4, 10000 ); PNS_LINE repl; repl = PNS_LINE( *aLine, l2 ); @@ -796,3 +955,215 @@ bool PNS_OPTIMIZER::fanoutCleanup( PNS_LINE* aLine ) return false; } + + +int findCoupledVertices( const VECTOR2I& aVertex, const SEG& aOrigSeg, const SHAPE_LINE_CHAIN& aCoupled, PNS_DIFF_PAIR* aPair, int* aIndices ) +{ + int count = 0; + for ( int i = 0; i < aCoupled.SegmentCount(); i++ ) + { + SEG s = aCoupled.CSegment( i ); + VECTOR2I projOverCoupled = s.LineProject ( aVertex ); + + if( s.ApproxParallel ( aOrigSeg ) ) + { + int64_t dist = ( projOverCoupled - aVertex ).EuclideanNorm() - aPair->Width(); + + if( aPair->GapConstraint().Matches( dist ) ) + { + *aIndices++ = i; + count++; + } + } + } + + return count; +} + + +bool verifyDpBypass( PNS_NODE* aNode, PNS_DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef, const SHAPE_LINE_CHAIN& aNewCoupled ) +{ + PNS_LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef ); + PNS_LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled ); + + if( aNode->CheckColliding( &refLine, &coupledLine, PNS_ITEM::ANY, aPair->Gap() - 10 ) ) + return false; + + if( aNode->CheckColliding ( &refLine ) ) + return false; + + if( aNode->CheckColliding ( &coupledLine ) ) + return false; + + return true; +} + + +bool coupledBypass( PNS_NODE* aNode, PNS_DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef, const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled, SHAPE_LINE_CHAIN& aNewCoupled ) +{ + int vStartIdx[1024]; // fixme: possible overflow + + int nStarts = findCoupledVertices( aRefBypass.CPoint( 0 ), aRefBypass.CSegment( 0 ), aCoupled, aPair, vStartIdx ); + DIRECTION_45 dir( aRefBypass.CSegment( 0 ) ); + + int64_t bestLength = -1; + bool found = false; + SHAPE_LINE_CHAIN bestBypass; + int si, ei; + + for( int i=0; i< nStarts; i++ ) + { + for( int j = 1; j < aCoupled.PointCount() - 1; j++ ) + { + int delta = std::abs ( vStartIdx[i] - j ); + + if( delta > 1 ) + { + const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] ); + SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j), dir.IsDiagonal() ); + + int64_t coupledLength = aPair->CoupledLength( aRef, bypass ); + + SHAPE_LINE_CHAIN newCoupled = aCoupled; + + si = vStartIdx[i]; + ei = j; + + if(si < ei) + newCoupled.Replace( si, ei, bypass ); + else + newCoupled.Replace( ei, si, bypass.Reverse() ); + + if(coupledLength > bestLength && verifyDpBypass( aNode, aPair, aRefIsP, aRef, newCoupled) ) + { + bestBypass = newCoupled; + bestLength = coupledLength; + found = true; + } + } + } + } + + + if( found ) + aNewCoupled = bestBypass; + + return found; +} + + +bool checkDpColliding( PNS_NODE* aNode, PNS_DIFF_PAIR* aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath ) +{ + PNS_LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath ); + + return aNode->CheckColliding( &tmp ); +} + + +bool PNS_OPTIMIZER::mergeDpStep( PNS_DIFF_PAIR* aPair, bool aTryP, int step ) +{ + int n = 1; + + SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN(); + SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP(); + + int n_segs = currentPath.SegmentCount() - 1; + + int64_t clenPre = aPair->CoupledLength( currentPath, coupledPath ); + int64_t budget = clenPre / 10; // fixme: come up with somethig more intelligent here... + + while( n < n_segs - step ) + { + const SEG s1 = currentPath.CSegment( n ); + const SEG s2 = currentPath.CSegment( n + step ); + + DIRECTION_45 dir1( s1 ); + DIRECTION_45 dir2( s2 ); + + if( dir1.IsObtuse( dir2 ) ) + { + SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, dir1.IsDiagonal() ); + SHAPE_LINE_CHAIN newRef; + SHAPE_LINE_CHAIN newCoup; + int64_t deltaCoupled = -1, deltaUni = -1; + + newRef = currentPath; + newRef.Replace( s1.Index(), s2.Index(), bypass ); + + deltaUni = aPair->CoupledLength ( newRef, coupledPath ) - clenPre + budget; + + if ( coupledBypass( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) ) + { + deltaCoupled = aPair->CoupledLength( newRef, newCoup ) - clenPre + budget; + + if( deltaCoupled >= 0 ) + { + newRef.Simplify(); + newCoup.Simplify(); + + aPair->SetShape( newRef, newCoup, !aTryP ); + return true; + } + } + else if( deltaUni >= 0 && verifyDpBypass ( m_world, aPair, aTryP, newRef, coupledPath ) ) + { + newRef.Simplify(); + coupledPath.Simplify(); + + aPair->SetShape( newRef, coupledPath, !aTryP ); + return true; + } + } + + n++; + } + + return false; +} + + +bool PNS_OPTIMIZER::mergeDpSegments( PNS_DIFF_PAIR* aPair ) +{ + int step_p = aPair->CP().SegmentCount() - 2; + int step_n = aPair->CN().SegmentCount() - 2; + + while( 1 ) + { + int n_segs_p = aPair->CP().SegmentCount(); + int n_segs_n = aPair->CN().SegmentCount(); + + int max_step_p = n_segs_p - 2; + int max_step_n = n_segs_n - 2; + + if( step_p > max_step_p ) + step_p = max_step_p; + + if( step_n > max_step_n ) + step_n = max_step_n; + + if( step_p < 1 && step_n < 1) + break; + + bool found_anything_p = false; + bool found_anything_n = false; + + if( step_p > 1 ) + found_anything_p = mergeDpStep( aPair, true, step_p ); + + if( step_n > 1 ) + found_anything_n = mergeDpStep( aPair, false, step_n ); + + if( !found_anything_n && !found_anything_p ) + { + step_n--; + step_p--; + } + } + return true; +} + + +bool PNS_OPTIMIZER::Optimize( PNS_DIFF_PAIR* aPair ) +{ + return mergeDpSegments( aPair ); +} diff --git a/pcbnew/router/pns_optimizer.h b/pcbnew/router/pns_optimizer.h index fb75b41a53..84072693cb 100644 --- a/pcbnew/router/pns_optimizer.h +++ b/pcbnew/router/pns_optimizer.h @@ -30,8 +30,9 @@ #include "range.h" class PNS_NODE; -class PNS_LINE; class PNS_ROUTER; +class PNS_LINE; +class PNS_DIFF_PAIR; /** * Class PNS_COST_ESTIMATOR @@ -61,7 +62,7 @@ public: void Remove( PNS_LINE& aLine ); void Replace( PNS_LINE& aOldLine, PNS_LINE& aNewLine ); - bool IsBetter( PNS_COST_ESTIMATOR& aOther, double aLengthTollerance, + bool IsBetter( PNS_COST_ESTIMATOR& aOther, double aLengthTolerance, double aCornerTollerace ) const; double GetLengthCost() const { return m_lengthCost; } @@ -101,6 +102,8 @@ public: static bool Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld); bool Optimize( PNS_LINE* aLine, PNS_LINE* aResult = NULL ); + bool Optimize( PNS_DIFF_PAIR* aPair ); + void SetWorld( PNS_NODE* aNode ) { m_world = aNode; } void CacheStaticItem( PNS_ITEM* aItem ); @@ -117,6 +120,13 @@ public: m_effortLevel = aEffort; } + + void SetRestrictArea( const BOX2I& aArea ) + { + m_restrictArea = aArea; + m_restrictAreaActive = true; + } + private: static const int MaxCachedItems = 256; @@ -136,6 +146,8 @@ private: bool runSmartPads( PNS_LINE* aLine ); bool mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentLine, int step ); bool fanoutCleanup( PNS_LINE * aLine ); + bool mergeDpSegments( PNS_DIFF_PAIR *aPair ); + bool mergeDpStep( PNS_DIFF_PAIR *aPair, bool aTryP, int step ); bool checkColliding( PNS_ITEM* aItem, bool aUpdateCache = true ); bool checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath ); @@ -160,6 +172,9 @@ private: int m_collisionKindMask; int m_effortLevel; bool m_keepPostures; + + BOX2I m_restrictArea; + bool m_restrictAreaActive; }; #endif diff --git a/pcbnew/router/pns_placement_algo.h b/pcbnew/router/pns_placement_algo.h new file mode 100644 index 0000000000..315ddc3813 --- /dev/null +++ b/pcbnew/router/pns_placement_algo.h @@ -0,0 +1,185 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_PLACEMENT_ALGO_H +#define __PNS_PLACEMENT_ALGO_H + +#include + +#include "pns_algo_base.h" +#include "pns_sizes_settings.h" +#include "pns_itemset.h" + +class PNS_ROUTER; +class PNS_ITEM; +class PNS_NODE; + +/** + * Class PNS_PLACEMENT_ALGO + * + * Abstract class for a P&S placement/dragging algorithm. + * All subtools (drag, single/diff pair routing and meandering) + * are derived from it. + */ + +class PNS_PLACEMENT_ALGO : public PNS_ALGO_BASE +{ +public: + PNS_PLACEMENT_ALGO( PNS_ROUTER* aRouter ) : + PNS_ALGO_BASE( aRouter ) {}; + + virtual ~PNS_PLACEMENT_ALGO () {}; + + /** + * Function Start() + * + * Starts placement/drag operation at point aP, taking item aStartItem as anchor + * (unless NULL). + */ + virtual bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) = 0; + + /** + * Function Move() + * + * Moves the end of the currently routed primtive(s) to the point aP, taking + * aEndItem as the anchor (if not NULL). + * (unless NULL). + */ + virtual bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) = 0; + + /** + * Function FixRoute() + * + * Commits the currently routed items to the parent node, taking + * aP as the final end point and aEndItem as the final anchor (if provided). + * @return true, if route has been commited. May return false if the routing + * result is violating design rules - in such case, the track is only committed + * if Settings.CanViolateDRC() is on. + */ + virtual bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) = 0; + + /** + * Function ToggleVia() + * + * Enables/disables a via at the end of currently routed trace. + */ + virtual bool ToggleVia( bool aEnabled ) + { + return false; + } + + /** + * Function IsPlacingVia() + * + * Returns true if the placer is placing a via (or more vias). + */ + virtual bool IsPlacingVia() const + { + return false; + } + + /** + * Function SetLayer() + * + * Sets the current routing layer. + */ + virtual bool SetLayer( int aLayer ) + { + return false; + } + + /** + * Function Traces() + * + * Returns all routed/tuned traces. + */ + virtual const PNS_ITEMSET Traces() = 0; + + /** + * Function CurrentEnd() + * + * Returns the current end of the line being placed/tuned. It may not be equal + * to the cursor position due to collisions. + */ + virtual const VECTOR2I& CurrentEnd() const = 0; + + /** + * Function CurrentNet() + * + * Returns the net code of currently routed track. + */ + virtual int CurrentNet() const = 0; + + /** + * Function CurrentLayer() + * + * Returns the layer of currently routed track. + */ + virtual int CurrentLayer() const = 0; + + /** + * Function CurrentNode() + * + * Returns the most recent board state. + */ + virtual PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const = 0; + + /** + * Function FlipPosture() + * + * Toggles the current posture (straight/diagonal) of the trace head. + */ + virtual void FlipPosture() + { + } + + /** + * Function UpdateSizes() + * + * Performs on-the-fly update of the width, via diameter & drill size from + * a settings class. Used to dynamically change these parameters as + * the track is routed. + */ + virtual void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ) + { + } + + /** + * Function SetOrthoMode() + * + * Forces the router to place a straight 90/45 degree trace (with the end + * as near to the cursor as possible) instead of a standard 135 degree + * two-segment bend. + */ + virtual void SetOrthoMode ( bool aOrthoMode ) + { + } + + /** + * Function GetModifiedNets + * + * Returns the net codes of all currently routed trace(s) + */ + virtual void GetModifiedNets( std::vector &aNets ) const + { + } +}; + +#endif diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index d8c9f23678..d35973c878 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -44,6 +44,11 @@ #include "pns_router.h" #include "pns_shove.h" #include "pns_dragger.h" +#include "pns_topology.h" +#include "pns_diff_pair_placer.h" +#include "pns_meander_placer.h" +#include "pns_meander_skew_placer.h" +#include "pns_dp_meander_placer.h" #include @@ -58,66 +63,98 @@ // To be fixed sometime in the future. static PNS_ROUTER* theRouter; -class PCBNEW_CLEARANCE_FUNC : public PNS_CLEARANCE_FUNC + +PNS_PCBNEW_CLEARANCE_FUNC::PNS_PCBNEW_CLEARANCE_FUNC( PNS_ROUTER *aRouter ) : + m_router( aRouter ) { -public: - PCBNEW_CLEARANCE_FUNC( BOARD* aBoard ) + BOARD *brd = m_router->GetBoard(); + PNS_NODE *world = m_router->GetWorld(); + + PNS_TOPOLOGY topo( world ); + m_clearanceCache.resize( brd->GetNetCount() ); + + for( unsigned int i = 0; i < brd->GetNetCount(); i++ ) { - m_clearanceCache.resize( aBoard->GetNetCount() ); + NETINFO_ITEM* ni = brd->FindNet( i ); + if( ni == NULL ) + continue; - for( unsigned int i = 0; i < aBoard->GetNetCount(); i++ ) - { - NETINFO_ITEM* ni = aBoard->FindNet( i ); - if( ni == NULL ) - continue; + CLEARANCE_ENT ent; + ent.coupledNet = topo.DpCoupledNet( i ); - wxString netClassName = ni->GetClassName(); - NETCLASSPTR nc = aBoard->GetDesignSettings().m_NetClasses.Find( netClassName ); - int clearance = nc->GetClearance(); - m_clearanceCache[i] = clearance; - TRACE( 1, "Add net %d netclass %s clearance %d", i % netClassName.mb_str() % - clearance ); - } - - m_defaultClearance = 254000; // aBoard->m_NetClasses.Find ("Default clearance")->GetClearance(); + wxString netClassName = ni->GetClassName(); + NETCLASSPTR nc = brd->GetDesignSettings().m_NetClasses.Find( netClassName ); + + int clearance = nc->GetClearance(); + ent.clearance = clearance; + m_clearanceCache[i] = ent; + + TRACE( 1, "Add net %d netclass %s clearance %d", i % netClassName.mb_str() % + clearance ); } - int localPadClearance( const PNS_ITEM* aItem ) const + m_overrideEnabled = false; + m_defaultClearance = 254000; // aBoard->m_NetClasses.Find ("Default clearance")->GetClearance(); +} + + +PNS_PCBNEW_CLEARANCE_FUNC::~PNS_PCBNEW_CLEARANCE_FUNC() +{ +} + + +int PNS_PCBNEW_CLEARANCE_FUNC::localPadClearance( const PNS_ITEM* aItem ) const +{ + if( !aItem->Parent() || aItem->Parent()->Type() != PCB_PAD_T ) + return 0; + + const D_PAD* pad = static_cast( aItem->Parent() ); + return pad->GetLocalClearance(); +} + + +int PNS_PCBNEW_CLEARANCE_FUNC::operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) +{ + int net_a = aA->Net(); + int cl_a = ( net_a >= 0 ? m_clearanceCache[net_a].clearance : m_defaultClearance ); + int net_b = aB->Net(); + int cl_b = ( net_b >= 0 ? m_clearanceCache[net_b].clearance : m_defaultClearance ); + + bool linesOnly = aA->OfKind( PNS_ITEM::SEGMENT | PNS_ITEM::LINE ) && aB->OfKind( PNS_ITEM::SEGMENT | PNS_ITEM::LINE ); + + if( linesOnly && net_a >= 0 && net_b >= 0 && m_clearanceCache[net_a].coupledNet == net_b ) { - if( !aItem->Parent() || aItem->Parent()->Type() != PCB_PAD_T ) - return 0; - - const D_PAD* pad = static_cast( aItem->Parent() ); - - return pad->GetLocalClearance(); + cl_a = cl_b = m_router->Sizes().DiffPairGap() - 2 * PNS_HULL_MARGIN; } - int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) - { - int net_a = aA->Net(); - int cl_a = ( net_a >= 0 ? m_clearanceCache[net_a] : m_defaultClearance ); - int net_b = aB->Net(); - int cl_b = ( net_b >= 0 ? m_clearanceCache[net_b] : m_defaultClearance ); + int pad_a = localPadClearance( aA ); + int pad_b = localPadClearance( aB ); - int pad_a = localPadClearance( aA ); - int pad_b = localPadClearance( aB ); + cl_a = std::max( cl_a, pad_a ); + cl_b = std::max( cl_b, pad_b ); - cl_a = std::max( cl_a, pad_a ); - cl_b = std::max( cl_b, pad_b ); + return std::max( cl_a, cl_b ); +} - return std::max( cl_a, cl_b ); - } -private: - std::vector m_clearanceCache; - int m_defaultClearance; -}; +// fixme: ugly hack to make the optimizer respect gap width for currently routed differential pair. +void PNS_PCBNEW_CLEARANCE_FUNC::OverrideClearance( bool aEnable, int aNetA, int aNetB , int aClearance ) +{ + m_overrideEnabled = aEnable; + m_overrideNetA = aNetA; + m_overrideNetB = aNetB; + m_overrideClearance = aClearance; +} PNS_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad ) { PNS_LAYERSET layers( 0, MAX_CU_LAYERS - 1 ); + // ignore non-copper pads + if ( (aPad->GetLayerSet() & LSET::AllCuMask()).none() ) + return NULL; + switch( aPad->GetAttribute() ) { case PAD_STANDARD: @@ -253,6 +290,7 @@ void PNS_ROUTER::SetBoard( BOARD* aBoard ) TRACE( 1, "m_board = %p\n", m_board ); } + void PNS_ROUTER::SyncWorld() { if( !m_board ) @@ -263,12 +301,7 @@ void PNS_ROUTER::SyncWorld() ClearWorld(); - int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); - - m_clearanceFunc = new PCBNEW_CLEARANCE_FUNC( m_board ); m_world = new PNS_NODE(); - m_world->SetClearanceFunctor( m_clearanceFunc ); - m_world->SetMaxClearance( 4 * worstClearance ); for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) { @@ -294,7 +327,13 @@ void PNS_ROUTER::SyncWorld() if( item ) m_world->Add( item ); } + + int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); + m_clearanceFunc = new PNS_PCBNEW_CLEARANCE_FUNC( this ); + m_world->SetClearanceFunctor( m_clearanceFunc ); + m_world->SetMaxClearance( 4 * worstClearance ); } + PNS_ROUTER::PNS_ROUTER() @@ -309,6 +348,7 @@ PNS_ROUTER::PNS_ROUTER() m_previewItems = NULL; m_board = NULL; m_dragger = NULL; + m_mode = PNS_MODE_ROUTE_SINGLE; } @@ -460,19 +500,49 @@ bool PNS_ROUTER::StartDragging( const VECTOR2I& aP, PNS_ITEM* aStartItem ) bool PNS_ROUTER::StartRouting( const VECTOR2I& aP, PNS_ITEM* aStartItem, int aLayer ) { - m_placer = new PNS_LINE_PLACER( this ); + switch( m_mode ) + { + case PNS_MODE_ROUTE_SINGLE: + m_placer = new PNS_LINE_PLACER( this ); + break; + case PNS_MODE_ROUTE_DIFF_PAIR: + m_placer = new PNS_DIFF_PAIR_PLACER( this ); + break; + case PNS_MODE_TUNE_SINGLE: + m_placer = new PNS_MEANDER_PLACER( this ); + break; + case PNS_MODE_TUNE_DIFF_PAIR: + m_placer = new PNS_DP_MEANDER_PLACER( this ); + break; + case PNS_MODE_TUNE_DIFF_PAIR_SKEW: + m_placer = new PNS_MEANDER_SKEW_PLACER( this ); + break; + + default: + return false; + } m_placer->UpdateSizes ( m_sizes ); m_placer->SetLayer( aLayer ); - m_placer->Start( aP, aStartItem ); + + bool rv = m_placer->Start( aP, aStartItem ); + + if( !rv ) + return false; m_currentEnd = aP; m_currentEndItem = NULL; m_state = ROUTE_TRACK; - - return true; + return rv; } + +BOARD* PNS_ROUTER::GetBoard() +{ + return m_board; +} + + void PNS_ROUTER::eraseView() { BOOST_FOREACH( BOARD_ITEM* item, m_hiddenItems ) @@ -652,15 +722,23 @@ void PNS_ROUTER::movePlacing( const VECTOR2I& aP, PNS_ITEM* aEndItem ) eraseView(); m_placer->Move( aP, aEndItem ); - PNS_LINE current = m_placer->Trace(); + PNS_ITEMSET current = m_placer->Traces(); - DisplayItem( ¤t ); + BOOST_FOREACH( const PNS_ITEM* item, current.CItems() ) + { + if( !item->OfKind( PNS_ITEM::LINE ) ) + continue; - if( current.EndsWithVia() ) - DisplayItem( ¤t.Via() ); + const PNS_LINE* l = static_cast (item); + DisplayItem( l ); - PNS_ITEMSET tmp( ¤t ); - updateView( m_placer->CurrentNode( true ), tmp ); + if( l->EndsWithVia() ) + DisplayItem( &l->Via() ); + } + + //PNS_ITEMSET tmp( ¤t ); + + updateView( m_placer->CurrentNode( true ), current ); } @@ -765,18 +843,19 @@ bool PNS_ROUTER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) void PNS_ROUTER::StopRouting() { // Update the ratsnest with new changes + if( m_placer ) { - int n = m_placer->CurrentNet(); + std::vector nets; + m_placer->GetModifiedNets( nets ); - if( n >= 0) + BOOST_FOREACH ( int n, nets ) { // Update the ratsnest with new changes m_board->GetRatsnest()->Recalculate( n ); } } - if( !RoutingInProgress() ) return; @@ -822,9 +901,9 @@ void PNS_ROUTER::SwitchLayer( int aLayer ) void PNS_ROUTER::ToggleViaPlacement() { - if( m_state == ROUTE_TRACK ) + if( m_state == ROUTE_TRACK ) { - bool toggle = !m_placer->IsPlacingVia(); + bool toggle = !m_placer->IsPlacingVia(); m_placer->ToggleVia( toggle ); } } @@ -864,10 +943,26 @@ void PNS_ROUTER::DumpLog() logger->Save( "/tmp/shove.log" ); } + bool PNS_ROUTER::IsPlacingVia() const { - if(!m_placer) + if( !m_placer ) return NULL; + return m_placer->IsPlacingVia(); } + +void PNS_ROUTER::SetOrthoMode( bool aEnable ) +{ + if( !m_placer ) + return; + + m_placer->SetOrthoMode( aEnable ); +} + + +void PNS_ROUTER::SetMode( PNS_ROUTER_MODE aMode ) +{ + m_mode = aMode; +} diff --git a/pcbnew/router/pns_router.h b/pcbnew/router/pns_router.h index 0969bade68..433372ebab 100644 --- a/pcbnew/router/pns_router.h +++ b/pcbnew/router/pns_router.h @@ -41,6 +41,8 @@ class D_PAD; class TRACK; class VIA; class PNS_NODE; +class PNS_DIFF_PAIR_PLACER; +class PNS_PLACEMENT_ALGO; class PNS_LINE_PLACER; class PNS_ITEM; class PNS_LINE; @@ -59,6 +61,14 @@ namespace KIGFX }; +enum PNS_ROUTER_MODE { + PNS_MODE_ROUTE_SINGLE = 1, + PNS_MODE_ROUTE_DIFF_PAIR, + PNS_MODE_TUNE_SINGLE, + PNS_MODE_TUNE_DIFF_PAIR, + PNS_MODE_TUNE_DIFF_PAIR_SKEW +}; + /** * Class PNS_ROUTER * @@ -78,6 +88,9 @@ public: PNS_ROUTER(); ~PNS_ROUTER(); + void SetMode ( PNS_ROUTER_MODE aMode ); + PNS_ROUTER_MODE Mode() const { return m_mode; } + static PNS_ROUTER* GetInstance(); void ClearWorld(); @@ -113,6 +126,7 @@ public: void SwitchLayer( int layer ); void ToggleViaPlacement(); + void SetOrthoMode ( bool aEnable ); int GetCurrentLayer() const; int GetCurrentNet() const; @@ -193,6 +207,15 @@ public: return m_sizes; } + PNS_ITEM *QueryItemByParent ( const BOARD_ITEM *aItem ) const; + + BOARD *GetBoard(); + + void SetFailureReason ( const wxString& aReason ) { m_failureReason = aReason; } + const wxString& FailureReason() const { return m_failureReason; } + + PNS_PLACEMENT_ALGO *Placer() { return m_placer; } + private: void movePlacing( const VECTOR2I& aP, PNS_ITEM* aItem ); void moveDragging( const VECTOR2I& aP, PNS_ITEM* aItem ); @@ -225,7 +248,7 @@ private: BOARD* m_board; PNS_NODE* m_world; PNS_NODE* m_lastNode; - PNS_LINE_PLACER* m_placer; + PNS_PLACEMENT_ALGO * m_placer; PNS_DRAGGER* m_dragger; PNS_SHOVE* m_shove; int m_iterLimit; @@ -250,6 +273,10 @@ private: ///> Stores list of modified items in the current operation PICKED_ITEMS_LIST m_undoBuffer; PNS_SIZES_SETTINGS m_sizes; + PNS_ROUTER_MODE m_mode; + + wxString m_toolStatusbarName; + wxString m_failureReason; }; #endif diff --git a/pcbnew/router/pns_routing_settings.cpp b/pcbnew/router/pns_routing_settings.cpp index bb67840fbe..81ae5f7b5f 100644 --- a/pcbnew/router/pns_routing_settings.cpp +++ b/pcbnew/router/pns_routing_settings.cpp @@ -24,7 +24,7 @@ PNS_ROUTING_SETTINGS::PNS_ROUTING_SETTINGS() { m_routingMode = RM_Walkaround; - m_optimizerEffort = OE_FULL; + m_optimizerEffort = OE_MEDIUM; m_removeLoops = true; m_smartPads = true; m_shoveVias = true; diff --git a/pcbnew/router/pns_routing_settings.h b/pcbnew/router/pns_routing_settings.h index 1e4e82bdff..7acd5d1ade 100644 --- a/pcbnew/router/pns_routing_settings.h +++ b/pcbnew/router/pns_routing_settings.h @@ -137,7 +137,6 @@ private: PNS_MODE m_routingMode; PNS_OPTIMIZATION_EFFORT m_optimizerEffort; - int m_walkaroundIterationLimit; int m_shoveIterationLimit; TIME_LIMIT m_shoveTimeLimit; diff --git a/pcbnew/router/pns_segment.h b/pcbnew/router/pns_segment.h index 2550526c2e..ecd24bdd08 100644 --- a/pcbnew/router/pns_segment.h +++ b/pcbnew/router/pns_segment.h @@ -46,7 +46,7 @@ public: } PNS_SEGMENT( const PNS_LINE& aParentLine, const SEG& aSeg ) : - PNS_ITEM( SEGMENT ), + PNS_ITEM( SEGMENT ), m_seg( aSeg, aParentLine.Width() ) { m_net = aParentLine.Net(); @@ -55,6 +55,11 @@ public: m_rank = aParentLine.Rank(); }; + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && SEGMENT == aItem->Kind(); + } + PNS_SEGMENT* Clone( ) const; const SHAPE* Shape() const @@ -95,7 +100,7 @@ public: void SetEnds( const VECTOR2I& a, const VECTOR2I& b ) { m_seg.SetSeg( SEG ( a, b ) ); - } + } void SwapEnds() { @@ -105,7 +110,7 @@ public: const SHAPE_LINE_CHAIN Hull( int aClearance, int aWalkaroundThickness ) const; - virtual VECTOR2I Anchor(int n) const + virtual VECTOR2I Anchor(int n) const { if( n == 0 ) return m_seg.GetSeg().A; @@ -113,9 +118,9 @@ public: return m_seg.GetSeg().B; } - virtual int AnchorCount() const + virtual int AnchorCount() const { - return 2; + return 2; } private: diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp index f761e4e3ba..a2df254d74 100644 --- a/pcbnew/router/pns_shove.cpp +++ b/pcbnew/router/pns_shove.cpp @@ -18,6 +18,8 @@ * with this program. If not, see . */ +#define PNS_DEBUG + #include #include @@ -36,11 +38,36 @@ #include "pns_utils.h" #include "pns_router.h" #include "pns_shove.h" +#include "pns_utils.h" #include "time_limit.h" #include +void PNS_SHOVE::replaceItems( PNS_ITEM* aOld, PNS_ITEM* aNew ) +{ + OPT_BOX2I changed_area = ChangedArea( aOld, aNew ); + + if( changed_area ) + { + assert( !changed_area->Contains( VECTOR2I( 0, 0 ) ) ); + + m_affectedAreaSum = m_affectedAreaSum ? m_affectedAreaSum->Merge ( *changed_area ) : *changed_area; + } + + m_currentNode->Replace( aOld, aNew ); +} + + +int PNS_SHOVE::getClearance( PNS_ITEM *aA, PNS_ITEM *aB ) const +{ + if( m_forceClearance >= 0 ) + return m_forceClearance; + + return m_currentNode->GetClearance( aA, aB ); +} + + static void sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew ) { assert( aOld->CPoint( 0 ) == aNew->CPoint( 0 ) ); @@ -51,6 +78,7 @@ static void sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew ) PNS_SHOVE::PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER* aRouter ) : PNS_ALGO_BASE ( aRouter ) { + m_forceClearance = -1; m_root = aWorld; } @@ -59,7 +87,7 @@ PNS_SHOVE::~PNS_SHOVE() { // free all the stuff we've created during routing/dragging operation. BOOST_FOREACH( PNS_ITEM* item, m_gcItems ) - delete item; + delete item; } @@ -74,16 +102,6 @@ PNS_LINE* PNS_SHOVE::assembleLine( const PNS_SEGMENT* aSeg, int* aIndex ) } -// garbage-collected line cloning -PNS_LINE* PNS_SHOVE::cloneLine ( const PNS_LINE* aLine ) -{ - PNS_LINE* l = aLine->Clone(); - - m_gcItems.push_back( l ); - return l; -} - - // A dumb function that checks if the shoved line is shoved the right way, e.g. // visually "outwards" of the line/via applying pressure on it. Unfortunately there's no // mathematical concept of orientation of an open curve, so we use some primitive heuristics: @@ -92,7 +110,7 @@ bool PNS_SHOVE::checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) cons { const SEG ss = aCurrent->CSegment( 0 ); - int dist = m_currentNode->GetClearance( aCurrent, aShoved ) + PNS_HULL_MARGIN; + int dist = getClearance( aCurrent, aShoved ) + PNS_HULL_MARGIN; dist += aCurrent->Width() / 2; dist += aShoved->Width() / 2; @@ -106,7 +124,7 @@ bool PNS_SHOVE::checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) cons PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::walkaroundLoneVia( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ) { - int clearance = m_currentNode->GetClearance( aCurrent, aObstacle ); + int clearance = getClearance( aCurrent, aObstacle ); const SHAPE_LINE_CHAIN hull = aCurrent->Via().Hull( clearance, aObstacle->Width() ); SHAPE_LINE_CHAIN path_cw, path_ccw; @@ -204,7 +222,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE* aCurrent, PNS_LINE* continue; } - bool colliding = m_currentNode->CheckColliding( &l, aCurrent ); + bool colliding = m_currentNode->CheckColliding( &l, aCurrent, PNS_ITEM::ANY, m_forceClearance ); if( ( aCurrent->Marker() & MK_HEAD ) && !colliding ) { @@ -232,7 +250,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE* aCurrent, PNS_LINE* } -PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, +PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ProcessSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ) { aShoved->ClearSegmentLinks(); @@ -262,7 +280,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processSingleLine( PNS_LINE* aCurrent, PNS_LI { int w = aObstacle->Width(); int n_segs = aCurrent->SegmentCount(); - int clearance = m_currentNode->GetClearance( aCurrent, aObstacle ); + + int clearance = getClearance( aCurrent, aObstacle ); HULL_SET hulls; @@ -291,19 +310,24 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE* aCurrent, PNS_S { int segIndex; PNS_LINE* obstacleLine = assembleLine( aObstacleSeg, &segIndex ); - PNS_LINE* shovedLine = cloneLine( obstacleLine ); + PNS_LINE* shovedLine = clone( obstacleLine ); - SHOVE_STATUS rv = processSingleLine( aCurrent, obstacleLine, shovedLine ); + SHOVE_STATUS rv = ProcessSingleLine( aCurrent, obstacleLine, shovedLine ); - assert ( obstacleLine->LayersOverlap( shovedLine ) ); + assert( obstacleLine->LayersOverlap( shovedLine ) ); if( rv == SH_OK ) { if( shovedLine->Marker() & MK_HEAD ) + { + if( m_multiLineMode ) + return SH_INCOMPLETE; + m_newHead = *shovedLine; + } sanityCheck( obstacleLine, shovedLine ); - m_currentNode->Replace( obstacleLine, shovedLine ); + replaceItems ( obstacleLine, shovedLine ); sanityCheck( obstacleLine, shovedLine ); int rank = aCurrent->Rank(); @@ -326,17 +350,22 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE* aCurrent, PNS_S PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle ) { - PNS_LINE* shovedLine = cloneLine( aObstacle ); + PNS_LINE* shovedLine = clone( aObstacle ); - SHOVE_STATUS rv = processSingleLine( aCurrent, aObstacle, shovedLine ); + SHOVE_STATUS rv = ProcessSingleLine( aCurrent, aObstacle, shovedLine ); if( rv == SH_OK ) { if( shovedLine->Marker() & MK_HEAD ) + { + if( m_multiLineMode ) + return SH_INCOMPLETE; + m_newHead = *shovedLine; + } sanityCheck( aObstacle, shovedLine ); - m_currentNode->Replace( aObstacle, shovedLine ); + replaceItems( aObstacle, shovedLine ); sanityCheck( aObstacle, shovedLine ); int rank = aObstacle->Rank(); @@ -359,7 +388,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE* aCurrent, PNS_LINE PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE* aCurrent, PNS_SOLID* aObstacleSolid ) { PNS_WALKAROUND walkaround( m_currentNode, Router() ); - PNS_LINE* walkaroundLine = cloneLine( aCurrent ); + PNS_LINE* walkaroundLine = clone( aCurrent ); if( aCurrent->EndsWithVia() ) { @@ -413,10 +442,14 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE* aCurrent, PNS_SOL if( aCurrent->Marker() & MK_HEAD ) { walkaroundLine->Mark( MK_HEAD ); + + if(m_multiLineMode) + return SH_INCOMPLETE; + m_newHead = *walkaroundLine; } - m_currentNode->Replace( aCurrent, walkaroundLine ); + replaceItems ( aCurrent, walkaroundLine ); walkaroundLine->SetRank( nextRank ); #ifdef DEBUG @@ -457,32 +490,58 @@ bool PNS_SHOVE::reduceSpringback( const PNS_ITEMSET& aHeadSet ) bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems, - const PNS_COST_ESTIMATOR& aCost ) + const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea ) { SPRINGBACK_TAG st; + OPT_BOX2I prev_area; + + if( !m_nodeStack.empty() ) + prev_area = m_nodeStack.back().m_affectedArea; st.m_node = aNode; st.m_cost = aCost; st.m_headItems = aHeadItems; + + if( aAffectedArea ) + { + if( prev_area ) + st.m_affectedArea = prev_area->Merge ( *aAffectedArea ); + else + st.m_affectedArea = aAffectedArea; + } else + st.m_affectedArea = prev_area; + m_nodeStack.push_back( st ); return true; } -PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank ) +PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun ) { LINE_PAIR_VEC draggedLines; - VECTOR2I p0 ( aVia->Pos() ); - PNS_JOINT* jt = m_currentNode->FindJoint( p0, 1, aVia->Net() ); - PNS_VIA* pushedVia = aVia -> Clone(); + VECTOR2I p0( aVia->Pos() ); + PNS_JOINT* jt = m_currentNode->FindJoint( p0, aVia ); + VECTOR2I p0_pushed( p0 + aForce ); - pushedVia->SetPos( p0 + aForce ); + while( aForce.x != 0 || aForce.y != 0 ) + { + PNS_JOINT* jt_next = m_currentNode->FindJoint( p0_pushed, aVia ); + + if( !jt_next ) + break; + + p0_pushed += aForce.Resize( 2 ); // make sure pushed via does not overlap with any existing joint + } + + PNS_VIA* pushedVia = aVia->Clone(); + pushedVia->SetPos( p0_pushed ); pushedVia->Mark( aVia->Marker() ); if( aVia->Marker() & MK_HEAD ) { m_draggedVia = pushedVia; + m_draggedViaHeadSet.Clear(); } if( !jt ) @@ -493,9 +552,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() ) { - if( item->OfKind( PNS_ITEM::SEGMENT ) ) + if( PNS_SEGMENT* seg = dyn_cast( item ) ) { - PNS_SEGMENT* seg = (PNS_SEGMENT*) item; LINE_PAIR lp; int segIndex; @@ -506,16 +564,23 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc if( segIndex == 0 ) lp.first->Reverse(); - lp.second = cloneLine( lp.first ); + lp.second = clone( lp.first ); lp.second->ClearSegmentLinks(); - lp.second->DragCorner( p0 + aForce, lp.second->CLine().Find( p0 ) ); - lp.second->AppendVia ( *pushedVia ); + lp.second->DragCorner( p0_pushed, lp.second->CLine().Find( p0 ) ); + lp.second->AppendVia( *pushedVia ); draggedLines.push_back( lp ); + + if( aVia->Marker() & MK_HEAD ) + m_draggedViaHeadSet.Add( clone ( lp.second ) ); } } - m_currentNode->Remove( aVia ); - m_currentNode->Add ( pushedVia ); + m_draggedViaHeadSet.Add ( pushedVia ); + + if ( aDryRun ) + return SH_OK; + + replaceItems ( aVia, pushedVia ); #ifdef DEBUG m_logger.Log( aVia, 0, "obstacle-via" ); @@ -535,6 +600,10 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc if( lp.first->Marker() & MK_HEAD ) { lp.second->Mark( MK_HEAD ); + + if ( m_multiLineMode ) + return SH_INCOMPLETE; + m_newHead = *lp.second; } @@ -542,7 +611,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc if( lp.second->SegmentCount() ) { - m_currentNode->Replace( lp.first, lp.second ); + replaceItems( lp.first, lp.second ); lp.second->SetRank( aCurrentRank - 1 ); pushLine( lp.second ); } @@ -561,7 +630,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia ) { - int clearance = m_currentNode->GetClearance( aCurrent, aObstacleVia ) ; + int clearance = getClearance( aCurrent, aObstacleVia ) ; LINE_PAIR_VEC draggedLines; bool colLine = false, colVia = false; PNS_LINE* currentLine = NULL; @@ -612,11 +681,11 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN { std::vector steps; int n = 0; - PNS_LINE* cur = cloneLine( aCurrent ); + PNS_LINE* cur = clone( aCurrent ); cur->ClearSegmentLinks(); PNS_JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia ); - PNS_LINE* shoved = cloneLine( aCurrent ); + PNS_LINE* shoved = clone( aCurrent ); shoved->ClearSegmentLinks(); cur->RemoveVia(); @@ -631,7 +700,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN head->AppendVia( *aObstacleVia ); - SHOVE_STATUS st = processSingleLine( head, cur, shoved ); + SHOVE_STATUS st = ProcessSingleLine( head, cur, shoved ); if( st != SH_OK ) { @@ -663,7 +732,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN head.AppendVia( *aObstacleVia ); head.ClearSegmentLinks(); - SHOVE_STATUS st = processSingleLine( &head, aCurrent, shoved ); + SHOVE_STATUS st = ProcessSingleLine( &head, aCurrent, shoved ); if( st != SH_OK ) return st; @@ -681,7 +750,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN m_logger.Log( shoved, 3, "shoved-line" ); #endif int currentRank = aCurrent->Rank(); - m_currentNode->Replace( aCurrent, shoved ); + replaceItems( aCurrent, shoved ); pushLine( shoved ); shoved->SetRank( currentRank ); @@ -730,7 +799,15 @@ void PNS_SHOVE::unwindStack( PNS_ITEM* aItem ) void PNS_SHOVE::pushLine( PNS_LINE* aL ) { if( aL->LinkCount() >= 0 && ( aL->LinkCount() != aL->SegmentCount() ) ) + { + printf("LC: %d SC %d\n", aL->LinkCount(), aL->SegmentCount() ); + for(int i=0;iSegmentCount();i++) + { + SEG s = aL->CLine().CSegment(i); + printf("s %d: %d %d %d %d\n", i, s.A.x, s.A.y, s.B.x, s.B.y ); + } assert( false ); + } m_lineStack.push_back( aL ); m_optimizerQueue.push_back( aL ); @@ -850,6 +927,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop() { SHOVE_STATUS st = SH_OK; + m_affectedAreaSum = OPT_BOX2I(); + TRACE( 1, "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount() % m_currentNode->JointCount() ); @@ -877,15 +956,34 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop() } +OPT_BOX2I PNS_SHOVE::totalAffectedArea() const +{ + OPT_BOX2I area; + if( !m_nodeStack.empty() ) + area = m_nodeStack.back().m_affectedArea; + + if( area ) + { + if ( m_affectedAreaSum ) + area->Merge ( *m_affectedAreaSum ); + } else + area = m_affectedAreaSum; + + return area; +} + + PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) { SHOVE_STATUS st = SH_OK; + m_multiLineMode = false; + // empty head? nothing to shove... if( !aCurrentHead.SegmentCount() ) return SH_INCOMPLETE; - PNS_LINE* head = cloneLine( &aCurrentHead ); + PNS_LINE* head = clone( &aCurrentHead ); head->ClearSegmentLinks(); m_lineStack.clear(); @@ -893,7 +991,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) m_newHead = OPT_LINE(); m_logger.Clear(); - PNS_ITEMSET headSet( cloneLine( &aCurrentHead ) ); + PNS_ITEMSET headSet( clone( &aCurrentHead ) ); reduceSpringback( headSet ); @@ -927,7 +1025,6 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) if( m_newHead && st == SH_OK ) { st = SH_HEAD_MODIFIED; - //Router()->DisplayDebugLine( m_newHead->CLine(), 3, 20000 ); } m_currentNode->RemoveByMarker( MK_HEAD ); @@ -937,7 +1034,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) if( st == SH_OK || st == SH_HEAD_MODIFIED ) { - pushSpringback( m_currentNode, headSet, PNS_COST_ESTIMATOR() ); + pushSpringback( m_currentNode, headSet, PNS_COST_ESTIMATOR(), m_affectedAreaSum); } else { @@ -951,6 +1048,86 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) } +PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveMultiLines( const PNS_ITEMSET& aHeadSet ) +{ + SHOVE_STATUS st = SH_OK; + + m_multiLineMode = true; + + PNS_ITEMSET headSet; + + BOOST_FOREACH ( const PNS_ITEM* item, aHeadSet.CItems() ) + { + const PNS_LINE* headOrig = static_cast( item ); + + // empty head? nothing to shove... + if( !headOrig->SegmentCount() ) + return SH_INCOMPLETE; + + headSet.Add( clone( headOrig ) ); + } + + m_lineStack.clear(); + m_optimizerQueue.clear(); + m_logger.Clear(); + + reduceSpringback( headSet ); + + PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; + + m_currentNode = parent->Branch(); + m_currentNode->ClearRanks(); + int n = 0; + BOOST_FOREACH ( const PNS_ITEM* item, aHeadSet.CItems() ) + { + const PNS_LINE* headOrig = static_cast( item ); + PNS_LINE* head = clone( headOrig ); + head->ClearSegmentLinks(); + + m_currentNode->Add( head ); + + head->Mark( MK_HEAD ); + head->SetRank( 100000 ); + n++; + pushLine( head ); + + PNS_VIA* headVia = NULL; + + if( head->EndsWithVia() ) + { + headVia = head->Via().Clone(); // fixme: leak + m_currentNode->Add( headVia ); + headVia->Mark( MK_HEAD ); + headVia->SetRank( 100000 ); + m_logger.Log( headVia, 0, "head-via" ); + } + } + + m_logger.NewGroup( "initial", 0 ); + //m_logger.Log( head, 0, "head" ); + + st = shoveMainLoop(); + runOptimizer( m_currentNode, NULL ); + + m_currentNode->RemoveByMarker( MK_HEAD ); + + TRACE( 1, "Shove status : %s after %d iterations", + ( st == SH_OK ? "OK" : "FAILURE") % m_iter ); + + if( st == SH_OK ) + { + pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR(), m_affectedAreaSum ); + } + else + { + delete m_currentNode; + m_currentNode = parent; + } + + return st; +} + + PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR2I& aWhere, PNS_VIA** aNewVia ) { @@ -960,10 +1137,16 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR m_optimizerQueue.clear(); m_newHead = OPT_LINE(); m_draggedVia = NULL; - - //reduceSpringback( aCurrentHead ); + m_draggedViaHeadSet.Clear(); PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; + + m_currentNode = parent; + //st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0, true ); + //reduceSpringback( m_draggedViaHeadSet ); + + parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; + m_currentNode = parent->Branch(); m_currentNode->ClearRanks(); @@ -972,10 +1155,11 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0 ); st = shoveMainLoop(); runOptimizer( m_currentNode, NULL ); + //m_currentNode->RemoveByMarker( MK_HEAD ); if( st == SH_OK || st == SH_HEAD_MODIFIED ) { - pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR() ); + pushSpringback( m_currentNode, m_draggedViaHeadSet, PNS_COST_ESTIMATOR(), m_affectedAreaSum ); } else { @@ -984,8 +1168,9 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR } if( aNewVia ) + { *aNewVia = m_draggedVia; - + } return st; } @@ -993,22 +1178,39 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead ) { PNS_OPTIMIZER optimizer( aNode ); - int optFlags = 0, n_passes = 0, extend = 0; + int optFlags = 0, n_passes = 0; PNS_OPTIMIZATION_EFFORT effort = Settings().OptimizerEffort(); + OPT_BOX2I area = totalAffectedArea(); + + int maxWidth = 0; + + for( std::vector::iterator i = m_optimizerQueue.begin(); + i != m_optimizerQueue.end(); ++i ) + { + maxWidth = std::max ( (*i)->Width(), maxWidth ); + } + + if( area ) + { + area->Inflate( 10 * maxWidth ); + } + switch( effort ) { case OE_LOW: optFlags = PNS_OPTIMIZER::MERGE_OBTUSE; n_passes = 1; - extend = 0; break; case OE_MEDIUM: - optFlags = PNS_OPTIMIZER::MERGE_OBTUSE; + optFlags = PNS_OPTIMIZER::MERGE_SEGMENTS; + + if( area ) + optimizer.SetRestrictArea( *area ); + n_passes = 2; - extend = 1; break; case OE_FULL: @@ -1037,18 +1239,6 @@ void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead ) if( !( line -> Marker() & MK_HEAD ) ) { - if( effort == OE_MEDIUM || effort == OE_LOW ) - { - RANGE r = findShovedVertexRange( line ); - - if( r.Defined() ) - { - int start_v = std::max( 0, r.MinV() - extend ); - int end_v = std::min( line->PointCount() - 1 , r.MaxV() + extend ); - line->ClipVertexRange( start_v, end_v ); - } - } - PNS_LINE optimized; if( optimizer.Optimize( line, &optimized ) ) @@ -1063,47 +1253,6 @@ void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead ) } -const RANGE PNS_SHOVE::findShovedVertexRange( PNS_LINE* aL ) -{ - RANGE r; - - for( int i = 0; i < aL->SegmentCount(); i++ ) - { - PNS_SEGMENT* s = (*aL->LinkedSegments())[i]; - PNS_JOINT* jt = m_root->FindJoint( s->Seg().A, s->Layer(), s->Net() ); - bool found = false; - - if( jt ) - { - BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() ) - { - if( item->OfKind( PNS_ITEM::SEGMENT ) ) - { - PNS_SEGMENT* s_old = (PNS_SEGMENT*) item; - - if( s_old->Net() == s->Net() && - s_old->Layer() == s->Layer() && - s_old->Seg().A == s->Seg().A && - s_old->Seg().B == s->Seg().B ) - { - found = true; - break; - } - } - } - } - - if( !found ) - { - r.Grow( i ); - r.Grow( i + 1 ); - } - } - - return r; -} - - PNS_NODE* PNS_SHOVE::CurrentNode() { return m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; diff --git a/pcbnew/router/pns_shove.h b/pcbnew/router/pns_shove.h index 2b4f32729f..cf8ae3f383 100644 --- a/pcbnew/router/pns_shove.h +++ b/pcbnew/router/pns_shove.h @@ -61,7 +61,19 @@ public: } SHOVE_STATUS ShoveLines( const PNS_LINE& aCurrentHead ); + SHOVE_STATUS ShoveMultiLines( const PNS_ITEMSET& aHeadSet ); + SHOVE_STATUS ShoveDraggingVia( PNS_VIA*aVia, const VECTOR2I& aWhere, PNS_VIA** aNewVia ); + SHOVE_STATUS ProcessSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, + PNS_LINE* aShoved ); + + void ForceClearance ( bool aEnabled, int aClearance ) + { + if( aEnabled ) + m_forceClearance = aClearance; + else + m_forceClearance = -1; + } PNS_NODE* CurrentNode(); @@ -83,15 +95,15 @@ private: PNS_NODE* m_node; PNS_ITEMSET m_headItems; PNS_COST_ESTIMATOR m_cost; + OPT_BOX2I m_affectedArea; }; - SHOVE_STATUS processSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ); SHOVE_STATUS processHullSet( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved, const HULL_SET& hulls ); bool reduceSpringback( const PNS_ITEMSET& aHeadItems ); - bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET &aHeadItems, - const PNS_COST_ESTIMATOR& aCost ); + bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems, + const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea ); SHOVE_STATUS walkaroundLoneVia( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ); bool checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) const; @@ -101,7 +113,9 @@ private: SHOVE_STATUS onCollidingSolid( PNS_LINE* aCurrent, PNS_SOLID* aObstacleSolid ); SHOVE_STATUS onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia ); SHOVE_STATUS onReverseCollidingVia( PNS_LINE* aCurrent, PNS_VIA* aObstacleVia ); - SHOVE_STATUS pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank ); + SHOVE_STATUS pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun = false ); + + OPT_BOX2I totalAffectedArea() const; void unwindStack( PNS_SEGMENT* aSeg ); void unwindStack( PNS_ITEM* aItem ); @@ -111,14 +125,25 @@ private: void pushLine( PNS_LINE* aL ); void popLine(); - const RANGE findShovedVertexRange( PNS_LINE* aL ); - PNS_LINE* assembleLine( const PNS_SEGMENT* aSeg, int* aIndex = NULL ); - PNS_LINE* cloneLine( const PNS_LINE* aLine ); + + void replaceItems( PNS_ITEM *aOld, PNS_ITEM *aNew ); + + template T* clone ( const T* aItem ) + { + T *cloned = aItem->Clone(); + + m_gcItems.push_back( cloned ); + return cloned; + } + + OPT_BOX2I m_affectedAreaSum; SHOVE_STATUS shoveIteration( int aIter ); SHOVE_STATUS shoveMainLoop(); + int getClearance( PNS_ITEM *aA, PNS_ITEM *aB ) const; + std::vector m_nodeStack; std::vector m_lineStack; std::vector m_optimizerQueue; @@ -131,8 +156,12 @@ private: PNS_LOGGER m_logger; PNS_VIA* m_draggedVia; + PNS_ITEMSET m_draggedViaHeadSet; int m_iter; + int m_forceClearance; + bool m_multiLineMode; + bool m_headModified; }; #endif // __PNS_SHOVE_H diff --git a/pcbnew/router/pns_sizes_settings.cpp b/pcbnew/router/pns_sizes_settings.cpp index f2ab01a3be..94f12ac04a 100644 --- a/pcbnew/router/pns_sizes_settings.cpp +++ b/pcbnew/router/pns_sizes_settings.cpp @@ -66,6 +66,7 @@ int PNS_SIZES_SETTINGS::inheritTrackWidth( PNS_ITEM* aItem ) return ( mval == INT_MAX ? 0 : mval ); } + void PNS_SIZES_SETTINGS::Init( BOARD* aBoard, PNS_ITEM* aStartItem, int aNet ) { BOARD_DESIGN_SETTINGS &bds = aBoard->GetDesignSettings(); @@ -121,11 +122,13 @@ void PNS_SIZES_SETTINGS::Init( BOARD* aBoard, PNS_ITEM* aStartItem, int aNet ) m_layerPairs.clear(); } + void PNS_SIZES_SETTINGS::ClearLayerPairs() { m_layerPairs.clear(); } + void PNS_SIZES_SETTINGS::AddLayerPair( int aL1, int aL2 ) { int top = std::min( aL1, aL2 ); @@ -135,6 +138,7 @@ void PNS_SIZES_SETTINGS::AddLayerPair( int aL1, int aL2 ) m_layerPairs[top] = bottom; } + void PNS_SIZES_SETTINGS::ImportCurrent( BOARD_DESIGN_SETTINGS& aSettings ) { m_trackWidth = aSettings.GetCurrentTrackWidth(); @@ -142,6 +146,7 @@ void PNS_SIZES_SETTINGS::ImportCurrent( BOARD_DESIGN_SETTINGS& aSettings ) m_viaDrill = aSettings.GetCurrentViaDrill(); } + int PNS_SIZES_SETTINGS::GetLayerTop() const { if( m_layerPairs.empty() ) @@ -150,6 +155,7 @@ int PNS_SIZES_SETTINGS::GetLayerTop() const return m_layerPairs.begin()->first; } + int PNS_SIZES_SETTINGS::GetLayerBottom() const { if( m_layerPairs.empty() ) diff --git a/pcbnew/router/pns_sizes_settings.h b/pcbnew/router/pns_sizes_settings.h index 2cd0dae9a1..9e335cbbdf 100644 --- a/pcbnew/router/pns_sizes_settings.h +++ b/pcbnew/router/pns_sizes_settings.h @@ -34,18 +34,20 @@ class PNS_SIZES_SETTINGS { public: PNS_SIZES_SETTINGS() : - m_trackWidth( 100000 ), - m_diffPairWidth( 100000 ), - m_diffPairGap( 125000 ), - m_viaDiameter( 500000 ), - m_viaDrill( 200000 ), + m_trackWidth( 155000 ), + m_diffPairWidth( 125000 ), + m_diffPairGap( 180000 ), + m_diffPairViaGap ( 180000 ), + m_viaDiameter( 600000 ), + m_viaDrill( 250000 ), + m_diffPairViaGapSameAsTraceGap ( true ), m_viaType( VIA_THROUGH ) {}; ~PNS_SIZES_SETTINGS() {}; void Init( BOARD* aBoard, PNS_ITEM* aStartItem = NULL, int aNet = -1 ); - void ImportCurrent ( BOARD_DESIGN_SETTINGS& aSettings ); + void ImportCurrent( BOARD_DESIGN_SETTINGS& aSettings ); void ClearLayerPairs(); void AddLayerPair( int aL1, int aL2 ); @@ -54,9 +56,22 @@ public: void SetTrackWidth( int aWidth ) { m_trackWidth = aWidth; } int DiffPairWidth() const { return m_diffPairWidth; } - int DiffPairGap() const { return m_diffPairGap; } + int DiffPairViaGap() const { + if(m_diffPairViaGapSameAsTraceGap) + return m_diffPairGap; + else + return m_diffPairViaGap; + } + + bool DiffPairViaGapSameAsTraceGap() const { return m_diffPairViaGapSameAsTraceGap; } + + void SetDiffPairWidth( int aWidth ) { m_diffPairWidth = aWidth; } + void SetDiffPairGap( int aGap ) { m_diffPairGap = aGap; } + void SetDiffPairViaGapSameAsTraceGap ( bool aEnable ) { m_diffPairViaGapSameAsTraceGap = aEnable; } + void SetDiffPairViaGap( int aGap ) { m_diffPairViaGap = aGap; } + int ViaDiameter() const { return m_viaDiameter; } void SetViaDiameter( int aDiameter) { m_viaDiameter = aDiameter; } @@ -68,7 +83,7 @@ public: if( m_layerPairs.find(aLayerId) == m_layerPairs.end() ) return boost::optional(); - return m_layerPairs [ aLayerId ]; + return m_layerPairs[aLayerId]; } int GetLayerTop() const; @@ -84,9 +99,12 @@ private: int m_trackWidth; int m_diffPairWidth; int m_diffPairGap; + int m_diffPairViaGap; int m_viaDiameter; int m_viaDrill; + bool m_diffPairViaGapSameAsTraceGap; + VIATYPE_T m_viaType; std::map m_layerPairs; diff --git a/pcbnew/router/pns_solid.cpp b/pcbnew/router/pns_solid.cpp index 6305ceabc4..9b0a048dab 100644 --- a/pcbnew/router/pns_solid.cpp +++ b/pcbnew/router/pns_solid.cpp @@ -62,7 +62,7 @@ const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness } -PNS_ITEM* PNS_SOLID::Clone ( ) const +PNS_ITEM* PNS_SOLID::Clone() const { PNS_ITEM* solid = new PNS_SOLID( *this ); return solid; diff --git a/pcbnew/router/pns_solid.h b/pcbnew/router/pns_solid.h index 88f8431d24..e598409849 100644 --- a/pcbnew/router/pns_solid.h +++ b/pcbnew/router/pns_solid.h @@ -43,12 +43,17 @@ public: } PNS_SOLID( const PNS_SOLID& aSolid ) : - PNS_ITEM ( aSolid ) + PNS_ITEM( aSolid ) { m_shape = aSolid.m_shape->Clone(); m_pos = aSolid.m_pos; } - + + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && SOLID == aItem->Kind(); + } + PNS_ITEM* Clone() const; const SHAPE* Shape() const { return m_shape; } @@ -78,7 +83,7 @@ public: return m_pos; } - virtual int AnchorCount() const + virtual int AnchorCount() const { return 1; } diff --git a/pcbnew/router/pns_tool_base.cpp b/pcbnew/router/pns_tool_base.cpp new file mode 100644 index 0000000000..9491d81a2c --- /dev/null +++ b/pcbnew/router/pns_tool_base.cpp @@ -0,0 +1,276 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include +#include +#include + +#include "class_draw_panel_gal.h" +#include "class_board.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "pns_tool_base.h" +#include "pns_segment.h" +#include "pns_router.h" +#include "pns_meander_placer.h" // fixme: move settings to separate header +#include "pns_tune_status_popup.h" +#include "trace.h" + +using namespace KIGFX; +using boost::optional; + +TOOL_ACTION PNS_TOOL_BASE::ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions", + AS_CONTEXT, 'E', + "Routing Options...", "Shows a dialog containing router options."); + + +PNS_TOOL_BASE::PNS_TOOL_BASE( const std::string& aToolName ) : + TOOL_INTERACTIVE( aToolName ) +{ + m_router = NULL; +} + + +PNS_TOOL_BASE::~PNS_TOOL_BASE() +{ + delete m_router; +} + + +void PNS_TOOL_BASE::Reset( RESET_REASON aReason ) +{ + if( m_router ) + delete m_router; + + m_frame = getEditFrame(); + m_ctls = getViewControls(); + m_board = getModel(); + + m_router = new PNS_ROUTER; + + m_router->ClearWorld(); + m_router->SetBoard( m_board ); + m_router->SyncWorld(); + m_router->LoadSettings( m_savedSettings ); + m_router->UpdateSizes( m_savedSizes ); + m_needsSync = false; + + if( getView() ) + m_router->SetView( getView() ); +} + + +PNS_ITEM* PNS_TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer ) +{ + int tl = getView()->GetTopLayer(); + + if( aLayer > 0 ) + tl = aLayer; + + PNS_ITEM* prioritized[4]; + + for( int i = 0; i < 4; i++ ) + prioritized[i] = 0; + + PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere ); + + BOOST_FOREACH( PNS_ITEM* item, candidates.Items() ) + { + if( !IsCopperLayer( item->Layers().Start() ) ) + continue; + + // fixme: this causes flicker with live loop removal... + //if( item->Parent() && !item->Parent()->ViewIsVisible() ) + // continue; + + if( aNet < 0 || item->Net() == aNet ) + { + if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) ) + { + if( !prioritized[2] ) + prioritized[2] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[0] = item; + } + else + { + if( !prioritized[3] ) + prioritized[3] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[1] = item; + } + } + } + + PNS_ITEM* rv = NULL; + PCB_EDIT_FRAME* frame = getEditFrame(); + DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)frame->GetDisplayOptions(); + + for( int i = 0; i < 4; i++ ) + { + PNS_ITEM* item = prioritized[i]; + + if( displ_opts->m_ContrastModeDisplay ) + if( item && !item->Layers().Overlaps( tl ) ) + item = NULL; + + if( item ) + { + rv = item; + break; + } + } + + if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) ) + rv = NULL; + + if( rv ) + TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl ); + + return rv; +} + + +void PNS_TOOL_BASE::highlightNet( bool aEnabled, int aNetcode ) +{ + RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings(); + + if( aNetcode >= 0 && aEnabled ) + rs->SetHighlight( true, aNetcode ); + else + rs->SetHighlight( false ); + + getView()->UpdateAllLayersColor(); +} + + +void PNS_TOOL_BASE::updateStartItem( TOOL_EVENT& aEvent ) +{ + int tl = getView()->GetTopLayer(); + VECTOR2I cp = m_ctls->GetCursorPosition(); + PNS_ITEM* startItem = NULL; + + if( aEvent.IsMotion() || aEvent.IsClick() ) + { + bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); + + VECTOR2I p( aEvent.Position() ); + startItem = pickSingleItem( p ); + m_router->EnableSnapping ( snapEnabled ); + + if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) ) + startItem = NULL; + + if( startItem && startItem->Net() >= 0 ) + { + bool dummy; + VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy ); + + if( snapEnabled ) + { + m_startSnapPoint = psnap; + m_ctls->ForceCursorPosition( true, psnap ); + } + else + { + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + +// if( startItem->Layers().IsMultilayer() ) +// m_startLayer = tl; +// else +// m_startLayer = startItem->Layers().Start(); + + m_startItem = startItem; + } + else + { + m_startItem = NULL; + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + } +} + + +void PNS_TOOL_BASE::updateEndItem( TOOL_EVENT& aEvent ) +{ + VECTOR2I mp = m_ctls->GetMousePosition(); + VECTOR2I p = getView()->ToWorld( mp ); + VECTOR2I cp = m_ctls->GetCursorPosition(); + int layer; + bool snapEnabled = !aEvent.Modifier( MD_CTRL ); + + m_router->EnableSnapping( snapEnabled ); + + if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem ) + { + m_endItem = NULL; + m_endSnapPoint = cp; + return; + } + + bool dummy; + + if( m_router->IsPlacingVia() ) + layer = -1; + else + layer = m_router->GetCurrentLayer(); + + PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer ); + + if( endItem ) + { + VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy ); + m_ctls->ForceCursorPosition( true, cursorPos ); + m_endItem = endItem; + m_endSnapPoint = cursorPos; + } + else + { + m_endItem = NULL; + m_endSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + + if( m_endItem ) + TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() ); +} + diff --git a/pcbnew/router/pns_tool_base.h b/pcbnew/router/pns_tool_base.h new file mode 100644 index 0000000000..db8998561a --- /dev/null +++ b/pcbnew/router/pns_tool_base.h @@ -0,0 +1,74 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * Author: Maciej Suminski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_TOOL_BASE_H +#define __PNS_TOOL_BASE_H + +#include + +#include +#include + +#include + +#include "pns_router.h" + +class PNS_TUNE_STATUS_POPUP; + +class APIEXPORT PNS_TOOL_BASE : public TOOL_INTERACTIVE +{ +public: + static TOOL_ACTION ACT_RouterOptions; + + PNS_TOOL_BASE( const std::string& aToolName ); + virtual ~PNS_TOOL_BASE(); + + virtual void Reset( RESET_REASON aReason ); + +protected: + + virtual PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 ); + virtual void highlightNet( bool aEnabled, int aNetcode = -1 ); + virtual void updateStartItem( TOOL_EVENT& aEvent ); + virtual void updateEndItem( TOOL_EVENT& aEvent ); + + MSG_PANEL_ITEMS m_panelItems; + + PNS_ROUTER* m_router; + PNS_ROUTING_SETTINGS m_savedSettings; ///< Stores routing settings between router invocations + PNS_SIZES_SETTINGS m_savedSizes; ///< Stores sizes settings between router invocations + PNS_ITEM* m_startItem; + int m_startLayer; + VECTOR2I m_startSnapPoint; + + PNS_ITEM* m_endItem; + VECTOR2I m_endSnapPoint; + + ///> Flag marking that the router's world needs syncing. + bool m_needsSync; + + PCB_EDIT_FRAME* m_frame; + KIGFX::VIEW_CONTROLS* m_ctls; + BOARD* m_board; + +}; + +#endif diff --git a/pcbnew/router/pns_topology.cpp b/pcbnew/router/pns_topology.cpp new file mode 100644 index 0000000000..f59a0fdb15 --- /dev/null +++ b/pcbnew/router/pns_topology.cpp @@ -0,0 +1,375 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pns_line.h" +#include "pns_segment.h" +#include "pns_node.h" +#include "pns_joint.h" +#include "pns_solid.h" +#include "pns_router.h" + +#include "pns_diff_pair.h" +#include "pns_topology.h" + +#include + +bool PNS_TOPOLOGY::SimplifyLine( PNS_LINE* aLine ) +{ + if( !aLine->LinkedSegments() || !aLine->SegmentCount() ) + return false; + + PNS_SEGMENT* root = ( *aLine->LinkedSegments() )[0]; + std::auto_ptr l( m_world->AssembleLine( root ) ); + SHAPE_LINE_CHAIN simplified( l->CLine() ); + + simplified.Simplify(); + + if( simplified.PointCount() != l->PointCount() ) + { + std::auto_ptr lnew( l->Clone() ); + m_world->Remove( l.get() ); + lnew->SetShape( simplified ); + m_world->Add( lnew.get() ); + return true; + } + + return false; +} + + +const PNS_TOPOLOGY::JOINT_SET PNS_TOPOLOGY::ConnectedJoints( PNS_JOINT* aStart ) +{ + std::deque searchQueue; + JOINT_SET processed; + + searchQueue.push_back( aStart ); + processed.insert( aStart ); + + while( !searchQueue.empty() ) + { + PNS_JOINT* current = searchQueue.front(); + searchQueue.pop_front(); + + BOOST_FOREACH( PNS_ITEM* item, current->LinkList() ) + { + if( item->OfKind( PNS_ITEM::SEGMENT ) ) + { + PNS_SEGMENT* seg = static_cast( item ); + PNS_JOINT* a = m_world->FindJoint( seg->Seg().A, seg ); + PNS_JOINT* b = m_world->FindJoint( seg->Seg().B, seg ); + PNS_JOINT* next = ( *a == *current ) ? b : a; + + if( processed.find( next ) == processed.end() ) + { + processed.insert( next ); + searchQueue.push_back( next ); + } + } + } + } + + return processed; +} + + +bool PNS_TOPOLOGY::LeadingRatLine( const PNS_LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine ) +{ + PNS_LINE track( *aTrack ); + VECTOR2I end; + + if( !track.PointCount() ) + return false; + + std::auto_ptr tmpNode( m_world->Branch() ); + tmpNode->Add( &track ); + + PNS_JOINT* jt = tmpNode->FindJoint( track.CPoint( -1 ), &track ); + + if( !jt ) + return false; + + if( ( !track.EndsWithVia() && jt->LinkCount() >= 2 ) || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected + { + end = jt->Pos(); + } + else + { + int anchor; + + PNS_TOPOLOGY topo( tmpNode.get() ); + PNS_ITEM* it = topo.NearestUnconnectedItem( jt, &anchor ); + + if( !it ) + return false; + + end = it->Anchor( anchor ); + } + + aRatLine.Clear(); + aRatLine.Append( track.CPoint( -1 ) ); + aRatLine.Append( end ); + return true; +} + +PNS_ITEM* PNS_TOPOLOGY::NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor, int aKindMask ) +{ + std::set disconnected; + + m_world->AllItemsInNet( aStart->Net(), disconnected ); + + BOOST_FOREACH( const PNS_JOINT* jt, ConnectedJoints( aStart ) ) + { + BOOST_FOREACH( PNS_ITEM* link, jt->LinkList() ) + { + if( disconnected.find( link ) != disconnected.end() ) + disconnected.erase( link ); + } + } + + int best_dist = INT_MAX; + PNS_ITEM* best = NULL; + + BOOST_FOREACH( PNS_ITEM* item, disconnected ) + { + if( item->OfKind( aKindMask ) ) + { + for(int i = 0; i < item->AnchorCount(); i++) + { + VECTOR2I p = item->Anchor( i ); + int d = ( p - aStart->Pos() ).EuclideanNorm(); + + if( d < best_dist ) + { + best_dist = d; + best = item; + + if( aAnchor ) + *aAnchor = i; + } + } + } + } + + return best; +} + + +bool PNS_TOPOLOGY::followTrivialPath( PNS_LINE* aLine, bool aLeft, PNS_ITEMSET& aSet, std::set& aVisited ) +{ + VECTOR2I anchor = aLeft ? aLine->CPoint( 0 ) : aLine->CPoint( -1 ); + PNS_SEGMENT* last = aLeft ? aLine->LinkedSegments()->front() : aLine->LinkedSegments()->back(); + PNS_JOINT* jt = m_world->FindJoint( anchor, aLine ); + + assert( jt != NULL ); + + aVisited.insert( last ); + + if( jt->IsNonFanoutVia() ) + { + PNS_ITEM* via = NULL; + PNS_SEGMENT* next_seg = NULL; + + BOOST_FOREACH( PNS_ITEM* link, jt->Links().Items() ) + { + if( link->OfKind( PNS_ITEM::VIA ) ) + via = link; + else if( aVisited.find( link ) == aVisited.end() ) + next_seg = static_cast( link ); + } + + if( !next_seg ) + return false; + + PNS_LINE* l = m_world->AssembleLine( next_seg ); + + VECTOR2I nextAnchor = ( aLeft ? l->CLine().CPoint( -1 ) : l->CLine().CPoint( 0 ) ); + + if( nextAnchor != anchor ) + { + l->Reverse(); + } + + if( aLeft ) + { + aSet.Prepend( via ); + aSet.Prepend( l ); + } + else + { + aSet.Add( via ); + aSet.Add( l ); + } + + return followTrivialPath( l, aLeft, aSet, aVisited ); + } + + return false; +} + + +const PNS_ITEMSET PNS_TOPOLOGY::AssembleTrivialPath( PNS_SEGMENT* aStart ) +{ + PNS_ITEMSET path; + std::set visited; + + PNS_LINE* l = m_world->AssembleLine( aStart ); + + path.Add( l ); + + followTrivialPath( l, false, path, visited ); + followTrivialPath( l, true, path, visited ); + + return path; +} + + +const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_JOINT* aStart, int aKindMask ) +{ + return PNS_ITEMSET(); +} + + +const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_ITEM* aStart, int aKindMask ) +{ + return PNS_ITEMSET(); +} + + +int PNS_TOPOLOGY::MatchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ) +{ + int rv = 0; + + if( aNetName.EndsWith( "+" ) ) + { + aComplementNet = "-"; + rv = 1; + } + else if( aNetName.EndsWith( "_P" ) ) + { + aComplementNet = "_N"; + rv = 1; + } + else if( aNetName.EndsWith( "-" ) ) + { + aComplementNet = "+"; + rv = -1; + } + else if( aNetName.EndsWith( "_N" ) ) + { + aComplementNet = "_P"; + rv = -1; + } + + if( rv != 0 ) + { + aBaseDpName = aNetName.Left( aNetName.Length() - aComplementNet.Length() ); + aComplementNet = aBaseDpName + aComplementNet; + } + + return rv; +} + + +int PNS_TOPOLOGY::DpCoupledNet( int aNet ) +{ + BOARD* brd = PNS_ROUTER::GetInstance()->GetBoard(); + + wxString refName = brd->FindNet( aNet )->GetNetname(); + wxString dummy, coupledNetName; + + if( MatchDpSuffix( refName, coupledNetName, dummy ) ) + { + NETINFO_ITEM* net = brd->FindNet( coupledNetName ); + + if( !net ) + return -1; + + return net->GetNet(); + + } + + return -1; +} + + +int PNS_TOPOLOGY::DpNetPolarity( int aNet ) +{ + BOARD* brd = PNS_ROUTER::GetInstance()->GetBoard(); + + wxString refName = brd->FindNet( aNet )->GetNetname(); + wxString dummy1, dummy2; + + return MatchDpSuffix( refName, dummy1, dummy2 ); +} + + +bool PNS_TOPOLOGY::AssembleDiffPair( PNS_ITEM* aStart, PNS_DIFF_PAIR& aPair ) +{ + int refNet = aStart->Net(); + int coupledNet = DpCoupledNet( refNet ); + + if( coupledNet < 0 ) + return false; + + std::set coupledItems; + + m_world->AllItemsInNet( coupledNet, coupledItems ); + + PNS_SEGMENT *coupledSeg = NULL, *refSeg; + int minDist = std::numeric_limits::max(); + + if( ( refSeg = dyn_cast( aStart ) ) != NULL ) + { + BOOST_FOREACH( PNS_ITEM* item, coupledItems ) + { + if( PNS_SEGMENT* s = dyn_cast( item ) ) + { + if( s->Layers().Start() == refSeg->Layers().Start() && s->Width() == refSeg->Width() ) + { + int dist = s->Seg().Distance( refSeg->Seg() ); + + if( dist < minDist ) + { + minDist = dist; + coupledSeg = s; + } + } + } + } + } else + return false; + + if( !coupledSeg ) + return false; + + std::auto_ptr lp ( m_world->AssembleLine( refSeg ) ); + std::auto_ptr ln ( m_world->AssembleLine( coupledSeg ) ); + + if( DpNetPolarity( refNet ) < 0 ) + { + std::swap( lp, ln ); + } + + aPair = PNS_DIFF_PAIR( *lp, *ln ); + aPair.SetWidth( lp->Width() ); + aPair.SetLayers( lp->Layers() ); + + return true; +} diff --git a/pcbnew/router/pns_topology.h b/pcbnew/router/pns_topology.h new file mode 100644 index 0000000000..ebc4894b74 --- /dev/null +++ b/pcbnew/router/pns_topology.h @@ -0,0 +1,70 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_TOPOLOGY_H +#define __PNS_TOPOLOGY_H + +#include +#include + +#include "pns_itemset.h" + +class PNS_NODE; +class PNS_SEGMENT; +class PNS_JOINT; +class PNS_ITEM; +class PNS_SOLID; +class PNS_DIFF_PAIR; + +class PNS_TOPOLOGY +{ +public: + typedef std::set JOINT_SET; + + PNS_TOPOLOGY( PNS_NODE* aNode ): + m_world( aNode ) {}; + + ~PNS_TOPOLOGY() {}; + + bool SimplifyLine( PNS_LINE *aLine ); + PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor = NULL, int aKindMask = PNS_ITEM::ANY ); + bool LeadingRatLine( const PNS_LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine ); + + const JOINT_SET ConnectedJoints( PNS_JOINT* aStart ); + const PNS_ITEMSET ConnectedItems( PNS_JOINT* aStart, int aKindMask = PNS_ITEM::ANY ); + const PNS_ITEMSET ConnectedItems( PNS_ITEM* aStart, int aKindMask = PNS_ITEM::ANY ); + int64_t ShortestConnectionLength( PNS_ITEM* aFrom, PNS_ITEM* aTo ); + + const PNS_ITEMSET AssembleTrivialPath( PNS_SEGMENT* aStart ); + const PNS_DIFF_PAIR AssembleDiffPair( PNS_SEGMENT* aStart ); + + int MatchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ); + int DpCoupledNet( int aNet ); + int DpNetPolarity( int aNet ); + const PNS_LINE DpCoupledLine( PNS_LINE* aLine ); + bool AssembleDiffPair( PNS_ITEM* aStart, PNS_DIFF_PAIR& aPair ); + +private: + bool followTrivialPath( PNS_LINE* aLine, bool aLeft, PNS_ITEMSET& aSet, std::set& aVisited ); + + PNS_NODE *m_world; +}; + +#endif diff --git a/pcbnew/router/pns_tune_status_popup.cpp b/pcbnew/router/pns_tune_status_popup.cpp new file mode 100644 index 0000000000..3250b2c56d --- /dev/null +++ b/pcbnew/router/pns_tune_status_popup.cpp @@ -0,0 +1,69 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pns_tune_status_popup.h" +#include "pns_router.h" +#include "pns_meander_placer.h" + +PNS_TUNE_STATUS_POPUP::PNS_TUNE_STATUS_POPUP( PCB_EDIT_FRAME* aParent ) : + WX_STATUS_POPUP( aParent ) +{ + m_panel->SetBackgroundColour( wxColour( 64, 64, 64 ) ); + m_statusLine = new wxStaticText( m_panel, wxID_ANY, wxT( "Status text 1\n" ) ) ; + m_topSizer->Add( m_statusLine, 1, wxALL | wxEXPAND, 5 ); + + updateSize(); +} + + +PNS_TUNE_STATUS_POPUP::~PNS_TUNE_STATUS_POPUP() +{ +} + + +void PNS_TUNE_STATUS_POPUP::Update( PNS_ROUTER* aRouter ) +{ + PNS_MEANDER_PLACER_BASE* placer = dynamic_cast( aRouter->Placer() ); + + if( !placer ) + return; + + m_statusLine->SetLabel( placer->TuningInfo() ); + + wxColour color; + + switch( placer->TuningStatus() ) + { + case PNS_MEANDER_PLACER::TUNED: + color = wxColour( 0, 255, 0 ); + break; + case PNS_MEANDER_PLACER::TOO_SHORT: + color = wxColour( 255, 128, 128 ); + break; + case PNS_MEANDER_PLACER::TOO_LONG: + color = wxColour( 128, 128, 255 ); + break; + } + + m_statusLine->SetForegroundColour( color ); + + updateSize(); +} + diff --git a/pcbnew/router/pns_tune_status_popup.h b/pcbnew/router/pns_tune_status_popup.h new file mode 100644 index 0000000000..3776882ad0 --- /dev/null +++ b/pcbnew/router/pns_tune_status_popup.h @@ -0,0 +1,44 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __PNS_TUNE_STATUS_POPUP_H_ +#define __PNS_TUNE_STATUS_POPUP_H_ + +#include + +class PNS_ROUTER; + +class PNS_TUNE_STATUS_POPUP : public WX_STATUS_POPUP +{ +public: + PNS_TUNE_STATUS_POPUP( PCB_EDIT_FRAME* aParent ); + ~PNS_TUNE_STATUS_POPUP(); + + void Update( PNS_ROUTER* aRouter ); + +private: + wxStaticText* m_statusLine; +}; + +#endif /* __PNS_TUNE_STATUS_POPUP_H_*/ diff --git a/pcbnew/router/pns_utils.cpp b/pcbnew/router/pns_utils.cpp index 5a69e673d8..6046fa222d 100644 --- a/pcbnew/router/pns_utils.cpp +++ b/pcbnew/router/pns_utils.cpp @@ -20,6 +20,7 @@ #include "pns_utils.h" #include "pns_line.h" +#include "pns_via.h" #include "pns_router.h" #include @@ -91,3 +92,85 @@ SHAPE_RECT ApproximateSegmentAsRect( const SHAPE_SEGMENT& aSeg ) return SHAPE_RECT( std::min( p0.x, p1.x ), std::min( p0.y, p1.y ), std::abs( p1.x - p0.x ), std::abs( p1.y - p0.y ) ); } + + +void DrawDebugPoint( VECTOR2I aP, int aColor ) +{ + SHAPE_LINE_CHAIN l; + + l.Append( aP - VECTOR2I( -50000, -50000 ) ); + l.Append( aP + VECTOR2I( -50000, -50000 ) ); + + PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, aColor, 10000 ); + + l.Clear(); + l.Append( aP - VECTOR2I( 50000, -50000 ) ); + l.Append( aP + VECTOR2I( 50000, -50000 ) ); + + PNS_ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 ); +} + + +void DrawDebugBox( BOX2I aB, int aColor ) +{ + SHAPE_LINE_CHAIN l; + + VECTOR2I o = aB.GetOrigin(); + VECTOR2I s = aB.GetSize(); + + l.Append( o ); + l.Append( o.x + s.x, o.y ); + l.Append( o.x + s.x, o.y + s.y ); + l.Append( o.x, o.y + s.y ); + l.Append( o ); + + PNS_ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 ); +} + + +void DrawDebugSeg( SEG aS, int aColor ) +{ + SHAPE_LINE_CHAIN l; + + l.Append( aS.A ); + l.Append( aS.B ); + + PNS_ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 ); +} + + +void DrawDebugDirs( VECTOR2D aP, int aMask, int aColor ) +{ + BOX2I b( aP - VECTOR2I( 10000, 10000 ), VECTOR2I( 20000, 20000 ) ); + + DrawDebugBox( b, aColor ); + for( int i = 0; i < 8; i++ ) + { + if( ( 1 << i ) & aMask ) + { + VECTOR2I v = DIRECTION_45( ( DIRECTION_45::Directions ) i ).ToVector() * 100000; + DrawDebugSeg( SEG( aP, aP + v ), aColor ); + } + } +} + + +OPT_BOX2I ChangedArea( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB ) +{ + if( aItemA->OfKind( PNS_ITEM::VIA ) && aItemB->OfKind( PNS_ITEM::VIA ) ) + { + const PNS_VIA* va = static_cast( aItemA ); + const PNS_VIA* vb = static_cast( aItemB ); + + return va->ChangedArea( vb ); + } + else if( aItemA->OfKind( PNS_ITEM::LINE ) && aItemB->OfKind( PNS_ITEM::LINE ) ) + { + const PNS_LINE* la = static_cast ( aItemA ); + const PNS_LINE* lb = static_cast ( aItemB ); + + return la->ChangedArea( lb ); + } + + return OPT_BOX2I(); +} diff --git a/pcbnew/router/pns_utils.h b/pcbnew/router/pns_utils.h index 5bf6d9313b..bb02d9dbf6 100644 --- a/pcbnew/router/pns_utils.h +++ b/pcbnew/router/pns_utils.h @@ -22,12 +22,15 @@ #define __PNS_UTILS_H #include +#include #include #include #include #define HULL_MARGIN 10 +class PNS_ITEM; + /** Various utility functions */ const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize, @@ -38,4 +41,12 @@ const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg, int aClearance, SHAPE_RECT ApproximateSegmentAsRect( const SHAPE_SEGMENT& aSeg ); +void DrawDebugPoint( VECTOR2I aP, int aColor ); +void DrawDebugBox( BOX2I aB, int aColor ); +void DrawDebugSeg( SEG aS, int aColor ); +void DrawDebugDirs( VECTOR2D aP, int aMask, int aColor ); + +OPT_BOX2I ChangedArea( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB ); + + #endif // __PNS_UTILS_H diff --git a/pcbnew/router/pns_via.cpp b/pcbnew/router/pns_via.cpp index 1b5c369bf2..b038167d3d 100644 --- a/pcbnew/router/pns_via.cpp +++ b/pcbnew/router/pns_via.cpp @@ -95,3 +95,16 @@ PNS_VIA* PNS_VIA::Clone ( ) const return v; } + + +OPT_BOX2I PNS_VIA::ChangedArea ( const PNS_VIA* aOther ) const +{ + if ( aOther->Pos() != Pos() ) + { + BOX2I tmp = Shape()->BBox(); + tmp.Merge ( aOther->Shape()->BBox() ); + return tmp; + } + + return OPT_BOX2I(); +} diff --git a/pcbnew/router/pns_via.h b/pcbnew/router/pns_via.h index b7637e9f13..1417e59c21 100644 --- a/pcbnew/router/pns_via.h +++ b/pcbnew/router/pns_via.h @@ -73,6 +73,12 @@ public: m_viaType = aB.m_viaType; } + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && VIA == aItem->Kind(); + } + + const VECTOR2I& Pos() const { return m_pos; @@ -140,6 +146,8 @@ public: return 1; } + OPT_BOX2I ChangedArea( const PNS_VIA* aOther ) const; + private: int m_diameter; int m_drill; diff --git a/pcbnew/router/pns_walkaround.cpp b/pcbnew/router/pns_walkaround.cpp index bbe8d2bca3..da965343ed 100644 --- a/pcbnew/router/pns_walkaround.cpp +++ b/pcbnew/router/pns_walkaround.cpp @@ -99,7 +99,7 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::singleStep( PNS_LINE& aPath, pnew.Append( path_walk[1] ); pnew.Append( path_post[1] ); - if(!path_post[1].PointCount() || !path_walk[1].PointCount()) + if( !path_post[1].PointCount() || !path_walk[1].PointCount() ) current_obs = nearestObstacle( PNS_LINE( aPath, path_pre[1] ) ); else current_obs = nearestObstacle( PNS_LINE( aPath, path_post[1] ) ); @@ -111,7 +111,7 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::singleStep( PNS_LINE& aPath, pnew.Append( path_walk[0] ); pnew.Append( path_post[0] ); - if(!path_post[0].PointCount() || !path_walk[0].PointCount()) + if( !path_post[0].PointCount() || !path_walk[0].PointCount() ) current_obs = nearestObstacle( PNS_LINE( aPath, path_pre[0] ) ); else current_obs = nearestObstacle( PNS_LINE( aPath, path_walk[0] ) ); @@ -136,7 +136,7 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia PNS_LINE& aWalkPath, bool aOptimize ) { PNS_LINE path_cw( aInitialPath ), path_ccw( aInitialPath ); - WALKAROUND_STATUS s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS; + WALKAROUND_STATUS s_cw, s_ccw; // = IN_PROGRESS, s_ccw = IN_PROGRESS; SHAPE_LINE_CHAIN best_path; start( aInitialPath ); @@ -146,6 +146,15 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia aWalkPath = aInitialPath; + if( m_forceWinding ) + { + s_cw = m_forceCw ? IN_PROGRESS : STUCK; + s_ccw = m_forceCw ? STUCK : IN_PROGRESS; + m_forceSingleDirection = true; + } else { + m_forceSingleDirection = false; + } + while( m_iteration < m_iterationLimit ) { if( s_cw != STUCK ) @@ -160,9 +169,9 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia int len_ccw = path_ccw.CLine().Length(); if( m_forceLongerPath ) - aWalkPath = (len_cw > len_ccw ? path_cw : path_ccw); + aWalkPath = ( len_cw > len_ccw ? path_cw : path_ccw ); else - aWalkPath = (len_cw < len_ccw ? path_cw : path_ccw); + aWalkPath = ( len_cw < len_ccw ? path_cw : path_ccw ); break; } @@ -229,17 +238,18 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia if( aWalkPath.SegmentCount() < 1 ) return STUCK; - if( aWalkPath.CPoint( -1 ) != aInitialPath.CPoint( -1 ) ) return STUCK; - if( aWalkPath.CPoint( 0 ) != aInitialPath.CPoint( 0 ) ) return STUCK; WALKAROUND_STATUS st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK; - if( aOptimize && st == DONE ) - PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world ); + if( st == DONE ) + { + if( aOptimize ) + PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world ); + } return st; } diff --git a/pcbnew/router/pns_walkaround.h b/pcbnew/router/pns_walkaround.h index 95c2079b61..939ba426d0 100644 --- a/pcbnew/router/pns_walkaround.h +++ b/pcbnew/router/pns_walkaround.h @@ -39,6 +39,7 @@ public: { m_forceSingleDirection = false; m_forceLongerPath = false; + m_forceWinding = false; m_cursorApproachMode = false; m_itemMask = PNS_ITEM::ANY; } @@ -79,7 +80,11 @@ public: { m_forceSingleDirection = aForceSingleDirection; m_forceLongerPath = aForceSingleDirection; - //printf("FSD %d FPD %d\n", m_forceSingleDirection?1:0, m_forceLongerPath ? 1: 0); + } + + void SetSingleDirection2( bool aForceSingleDirection ) + { + m_forceSingleDirection = aForceSingleDirection; } void SetApproachCursor( bool aEnabled, const VECTOR2I& aPos ) @@ -88,6 +93,12 @@ public: m_cursorApproachMode = aEnabled; } + void SetForceWinding ( bool aEnabled, bool aCw ) + { + m_forceCw = aCw; + m_forceWinding = aEnabled; + } + WALKAROUND_STATUS Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath, bool aOptimize = true ); @@ -110,6 +121,8 @@ private: int m_itemMask; bool m_forceSingleDirection, m_forceLongerPath; bool m_cursorApproachMode; + bool m_forceWinding; + bool m_forceCw; VECTOR2I m_cursorPos; PNS_NODE::OPT_OBSTACLE m_currentObstacle[2]; bool m_recursiveCollision[2]; diff --git a/pcbnew/router/range.h b/pcbnew/router/range.h index 0778cf70e7..5b47c74f1d 100644 --- a/pcbnew/router/range.h +++ b/pcbnew/router/range.h @@ -24,70 +24,70 @@ template class RANGE { - public: - RANGE( T aMin, T aMax ) : - m_min( aMin ), - m_max( aMax ), - m_defined( true ) {} +public: + RANGE( T aMin, T aMax ) : + m_min( aMin ), + m_max( aMax ), + m_defined( true ) {} - RANGE(): - m_defined( false ) {} + RANGE(): + m_defined( false ) {} - T MinV() const - { - return m_min; - } + T MinV() const + { + return m_min; + } - T MaxV() const - { - return m_max; - } + T MaxV() const + { + return m_max; + } - void Set( T aMin, T aMax ) const - { - m_max = aMax; - m_min = aMin; - } + void Set( T aMin, T aMax ) const + { + m_max = aMax; + m_min = aMin; + } - void Grow( T aValue ) - { - if( !m_defined ) - { - m_min = aValue; - m_max = aValue; - m_defined = true; - } - else - { - m_min = std::min( m_min, aValue ); - m_max = std::max( m_max, aValue ); - } - } + void Grow( T aValue ) + { + if( !m_defined ) + { + m_min = aValue; + m_max = aValue; + m_defined = true; + } + else + { + m_min = std::min( m_min, aValue ); + m_max = std::max( m_max, aValue ); + } + } - bool Inside( const T& aValue ) const - { - if( !m_defined ) - return true; + bool Inside( const T& aValue ) const + { + if( !m_defined ) + return true; - return aValue >= m_min && aValue <= m_max; - } + return aValue >= m_min && aValue <= m_max; + } - bool Overlaps ( const RANGE& aOther ) const - { - if( !m_defined || !aOther.m_defined ) - return true; + bool Overlaps ( const RANGE& aOther ) const + { + if( !m_defined || !aOther.m_defined ) + return true; - return m_max >= aOther.m_min && m_min <= aOther.m_max; - } + return m_max >= aOther.m_min && m_min <= aOther.m_max; + } - bool Defined() const - { - return m_defined; - } + bool Defined() const + { + return m_defined; + } - private: - T m_min, m_max; - bool m_defined; +private: + T m_min, m_max; + bool m_defined; }; #endif diff --git a/pcbnew/router/ranged_num.h b/pcbnew/router/ranged_num.h new file mode 100644 index 0000000000..5eadaf6879 --- /dev/null +++ b/pcbnew/router/ranged_num.h @@ -0,0 +1,52 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __RANGED_NUM_H +#define __RANGED_NUM_H + +template class RANGED_NUM { + public: + RANGED_NUM( T aValue = 0, T aTolerancePlus = 0, T aToleranceMinus = 0 ) : + m_value( aValue ), + m_tolerancePlus( aTolerancePlus ), + m_toleranceMinus( aToleranceMinus ) + {} + + operator T() + { + return m_value; + } + + RANGED_NUM& operator=( const T aValue ) + { + m_value = aValue; + return *this; + } + + bool Matches( const T& aOther ) const + { + return ( aOther >= m_value - m_toleranceMinus && aOther <= m_value + m_tolerancePlus ); + } + + private: + T m_value, m_tolerancePlus, m_toleranceMinus; +}; + +#endif diff --git a/pcbnew/router/router_menus.h b/pcbnew/router/router_menus.h new file mode 100644 index 0000000000..c52c5dab04 --- /dev/null +++ b/pcbnew/router/router_menus.h @@ -0,0 +1,987 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include +#include +#include + +#include "class_draw_panel_gal.h" +#include "class_board.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "router_tool.h" +#include "pns_segment.h" +#include "pns_router.h" +#include "pns_meander_placer.h" // fixme: move settings to separate header +#include "pns_tune_status_popup.h" +#include "trace.h" + +using namespace KIGFX; +using boost::optional; + +static TOOL_ACTION ACT_NewTrack( "pcbnew.InteractiveRouter.NewTrack", + AS_CONTEXT, 'X', + "New Track", "Starts laying a new track."); +static TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack", + AS_CONTEXT, WXK_END, + "End Track", "Stops laying the current track."); +static TOOL_ACTION ACT_AutoEndRoute( "pcbnew.InteractiveRouter.AutoEndRoute", + AS_CONTEXT, 'F', + "Auto-end Track", "Automagically finishes currently routed track." ); +static TOOL_ACTION ACT_Drag( "pcbnew.InteractiveRouter.Drag", + AS_CONTEXT, 'G', + "Drag Track/Via", "Drags a track or a via." ); +static TOOL_ACTION ACT_PlaceThroughVia( "pcbnew.InteractiveRouter.PlaceVia", + AS_CONTEXT, 'V', + "Place Through Via", "Adds a through-hole via at the end of currently routed track." ); +static TOOL_ACTION ACT_PlaceBlindVia( "pcbnew.InteractiveRouter.PlaceBlindVia", + AS_CONTEXT, 'Z', + "Place Blind/Buried Via", "Adds a blind or buried via at the end of currently routed track." ); +static TOOL_ACTION ACT_PlaceMicroVia( "pcbnew.InteractiveRouter.PlaceMicroVia", + AS_CONTEXT, 'Q', + "Place Microvia", "Adds a microvia at the end of currently routed track." ); +static TOOL_ACTION ACT_CustomTrackWidth( "pcbnew.InteractiveRouter.CustomTrackWidth", + AS_CONTEXT, 'W', + "Custom Track Width", "Shows a dialog for changing the track width and via size."); +static TOOL_ACTION ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions", + AS_CONTEXT, 'E', + "Routing Options...", "Shows a dialog containing router options."); +static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture", + AS_CONTEXT, '/', + "Switch Track Posture", "Switches posture of the currenly routed track."); + +static TOOL_ACTION ACT_SetDpDimensions( "pcbnew.InteractiveRouter.SetDpDimensions", + AS_CONTEXT, 'D', + "Differential Pair Dimensions...", "Sets the width and gap of the currently routed differential pair."); + +static TOOL_ACTION ACT_SetLengthTune( "pcbnew.InteractiveRouter.LengthTunerSettings", + AS_CONTEXT, 'L', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_LengthTuneSpacingIncrease( "pcbnew.InteractiveRouter.LengthTunerSpacingIncrease", + AS_CONTEXT, '1', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_LengthTuneSpacingDecrease( "pcbnew.InteractiveRouter.LengthTunerSpacingDecrease", + AS_CONTEXT, '2', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_SetLengthTuneAmplIncrease( "pcbnew.InteractiveRouter.LengthTunerAmplIncrease", + AS_CONTEXT, '3', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_SetLengthTuneAmplDecrease( "pcbnew.InteractiveRouter.LengthTunerAmplDecrease", + AS_CONTEXT, '4', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + + +ROUTER_TOOL::ROUTER_TOOL() : + TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" ) +{ + m_router = NULL; +} + + +class CONTEXT_TRACK_WIDTH_MENU: public CONTEXT_MENU +{ +public: + CONTEXT_TRACK_WIDTH_MENU() + { + setCustomEventHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::handleCustomEvent, + this, _1 ) ); + } + + void SetBoard( BOARD* aBoard ) + { + BOARD_DESIGN_SETTINGS& bds = aBoard->GetDesignSettings(); + + wxString msg; + m_board = aBoard; + + Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Custom size" ), + wxEmptyString, wxITEM_CHECK ); + + Append( ID_POPUP_PCB_SELECT_AUTO_WIDTH, _( "Use the starting track width" ), + _( "Route using the width of the starting track." ), wxITEM_CHECK ); + + Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES, _( "Use netclass values" ), + _( "Use track and via sizes from the net class" ), wxITEM_CHECK ); + + for( unsigned i = 0; i < bds.m_TrackWidthList.size(); i++ ) + { + msg = _( "Track "); + msg << StringFromValue( g_UserUnit, bds.m_TrackWidthList[i], true ); + + if( i == 0 ) + msg << _( " (from netclass)" ); + + Append( ID_POPUP_PCB_SELECT_WIDTH1 + i, msg, wxEmptyString, wxITEM_CHECK ); + } + + AppendSeparator(); + + for( unsigned i = 0; i < bds.m_ViasDimensionsList.size(); i++ ) + { + msg = _("Via "); + msg << StringFromValue( g_UserUnit, bds.m_ViasDimensionsList[i].m_Diameter, true ); + wxString drill = StringFromValue( g_UserUnit, + bds.m_ViasDimensionsList[i].m_Drill, + true ); + + if( bds.m_ViasDimensionsList[i].m_Drill <= 0 ) + { + msg << _(", drill: default"); + } + else + { + msg << _(", drill: ") << drill; + } + + if( i == 0 ) + msg << _( " (from netclass)" ); + + Append( ID_POPUP_PCB_SELECT_VIASIZE1 + i, msg, wxEmptyString, wxITEM_CHECK ); + } + } + +protected: + OPT_TOOL_EVENT handleCustomEvent( const wxEvent& aEvent ) + { +#if ID_POPUP_PCB_SELECT_VIASIZE1 < ID_POPUP_PCB_SELECT_WIDTH1 +#error You have changed event ids order, it breaks code. Check the source code for more details. +// Recognising type of event (track width/via size) is based on comparison if the event id is +// within a specific range. If ranges of event ids changes, then the following is not valid anymore. +#endif + BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings(); + + int id = aEvent.GetId(); + + // Initial settings, to be modified below + bds.m_UseConnectedTrackWidth = false; + bds.UseCustomTrackViaSize( false ); + + if( id == ID_POPUP_PCB_SELECT_CUSTOM_WIDTH ) + { + bds.UseCustomTrackViaSize( true ); + } + + else if( id == ID_POPUP_PCB_SELECT_AUTO_WIDTH ) + { + bds.m_UseConnectedTrackWidth = true; + } + + else if( id == ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES ) + { + bds.SetViaSizeIndex( 0 ); + bds.SetTrackWidthIndex( 0 ); + } + + else if( id > ID_POPUP_PCB_SELECT_VIASIZE1 ) // via size has changed + { + assert( id < ID_POPUP_PCB_SELECT_WIDTH_END_RANGE ); + + bds.SetViaSizeIndex( id - ID_POPUP_PCB_SELECT_VIASIZE1 ); + } + + else // track width has changed + { + assert( id >= ID_POPUP_PCB_SELECT_WIDTH1 ); + assert( id < ID_POPUP_PCB_SELECT_VIASIZE ); + + bds.SetTrackWidthIndex( id - ID_POPUP_PCB_SELECT_WIDTH1 ); + } + + return OPT_TOOL_EVENT( COMMON_ACTIONS::trackViaSizeChanged.MakeEvent() ); + } + + BOARD* m_board; +}; + + +class ROUTER_TOOL_MENU: public CONTEXT_MENU +{ +public: + ROUTER_TOOL_MENU( BOARD* aBoard, PNS_ROUTER_MODE aMode ) + { + SetTitle( wxT( "Interactive Router" ) ); + Add( ACT_NewTrack ); + Add( ACT_EndTrack ); +// Add( ACT_AutoEndRoute ); // fixme: not implemented yet. Sorry. + Add( ACT_Drag ); + Add( ACT_PlaceThroughVia ); + Add( ACT_PlaceBlindVia ); + Add( ACT_PlaceMicroVia ); + Add( ACT_SwitchPosture ); + + AppendSeparator(); + + CONTEXT_TRACK_WIDTH_MENU* trackMenu = new CONTEXT_TRACK_WIDTH_MENU; + trackMenu->SetBoard( aBoard ); + AppendSubMenu( trackMenu, wxT( "Select Track Width" ) ); + + Add( ACT_CustomTrackWidth ); + + if ( aMode == PNS_MODE_ROUTE_DIFF_PAIR ) + Add( ACT_SetDpDimensions ); + + AppendSeparator(); + Add( ACT_RouterOptions ); + } +}; + + +ROUTER_TOOL::~ROUTER_TOOL() +{ + delete m_router; +} + + +void ROUTER_TOOL::Reset( RESET_REASON aReason ) +{ + //printf("RESET\n"); + if( m_router ) + delete m_router; + + m_frame = getEditFrame(); + m_ctls = getViewControls(); + m_board = getModel(); + + m_router = new PNS_ROUTER; + + m_router->ClearWorld(); + m_router->SetBoard( m_board ); + m_router->SyncWorld(); + m_router->LoadSettings( m_savedSettings ); + m_router->UpdateSizes( m_savedSizes ); + m_needsSync = false; + + if( getView() ) + m_router->SetView( getView() ); + + Go( &ROUTER_TOOL::RouteSingleTrace, COMMON_ACTIONS::routerActivateSingle.MakeEvent() ); + Go( &ROUTER_TOOL::RouteDiffPair, COMMON_ACTIONS::routerActivateDiffPair.MakeEvent() ); + Go( &ROUTER_TOOL::TuneSingleTrace, COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent() ); +} + + +int ROUTER_TOOL::getDefaultWidth( int aNetCode ) +{ + int w, d1, d2; + + getNetclassDimensions( aNetCode, w, d1, d2 ); + + return w; +} + + +void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, + int& aViaDiameter, int& aViaDrill ) +{ + BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings(); + + NETCLASSPTR netClass; + NETINFO_ITEM* ni = m_board->FindNet( aNetCode ); + + if( ni ) + { + wxString netClassName = ni->GetClassName(); + netClass = bds.m_NetClasses.Find( netClassName ); + } + + if( !netClass ) + netClass = bds.GetDefault(); + + aWidth = netClass->GetTrackWidth(); + aViaDiameter = netClass->GetViaDiameter(); + aViaDrill = netClass->GetViaDrill(); +} + + +PNS_ITEM* ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer ) +{ + int tl = getView()->GetTopLayer(); + + if( aLayer > 0 ) + tl = aLayer; + + PNS_ITEM* prioritized[4]; + + for( int i = 0; i < 4; i++ ) + prioritized[i] = 0; + + PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere ); + + BOOST_FOREACH( PNS_ITEM* item, candidates.Items() ) + { + if( !IsCopperLayer( item->Layers().Start() ) ) + continue; + + // fixme: this causes flicker with live loop removal... + //if( item->Parent() && !item->Parent()->ViewIsVisible() ) + // continue; + + if( aNet < 0 || item->Net() == aNet ) + { + if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) ) + { + if( !prioritized[2] ) + prioritized[2] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[0] = item; + } + else + { + if( !prioritized[3] ) + prioritized[3] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[1] = item; + } + } + } + + PNS_ITEM* rv = NULL; + PCB_EDIT_FRAME* frame = getEditFrame (); + DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)frame->GetDisplayOptions(); + + for( int i = 0; i < 4; i++ ) + { + PNS_ITEM* item = prioritized[i]; + + if( displ_opts->m_ContrastModeDisplay ) + if( item && !item->Layers().Overlaps( tl ) ) + item = NULL; + + if( item ) + { + rv = item; + break; + } + } + + if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) ) + rv = NULL; + + if( rv ) + TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl ); + + return rv; +} + + +void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode ) +{ + RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings(); + + if( aNetcode >= 0 && aEnabled ) + rs->SetHighlight( true, aNetcode ); + else + rs->SetHighlight( false ); + + getView()->UpdateAllLayersColor(); +} + + +void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) +{ +#ifdef DEBUG + if( aEvent.IsKeyPressed() ) + { + switch( aEvent.KeyCode() ) + { + case 'S': + TRACEn( 2, "saving drag/route log...\n" ); + m_router->DumpLog(); + break; + } + } + else +#endif + if( aEvent.IsAction( &ACT_RouterOptions ) ) + { + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); + + if( settingsDlg.ShowModal() ) + { + // FIXME: do we need an explicit update? + } + } + else if( aEvent.IsAction( &ACT_SetDpDimensions ) ) + { + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes ); + + if( settingsDlg.ShowModal() ) + { + m_router->UpdateSizes( sizes ); + } + } + else if( aEvent.IsAction( &ACT_SetLengthTune ) ) + { + PNS_MEANDER_PLACER *placer = dynamic_cast ( m_router->Placer() ); + + if( !placer ) + return; + + PNS_MEANDER_SETTINGS settings = placer->Settings(); + DIALOG_PNS_LENGTH_TUNING_SETTINGS settingsDlg( m_frame, settings, m_router->Mode() ); + + if( settingsDlg.ShowModal() ) + { + placer->UpdateSettings ( settings ); + } + } + else if( aEvent.IsAction( &ACT_CustomTrackWidth ) ) + { + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + DIALOG_TRACK_VIA_SIZE sizeDlg( m_frame, bds ); + + if( sizeDlg.ShowModal() ) + { + bds.UseCustomTrackViaSize( true ); + m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged ); + } + } + + else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) ) + { + PNS_SIZES_SETTINGS sizes( m_savedSizes ); + sizes.ImportCurrent( m_board->GetDesignSettings() ); + m_router->UpdateSizes( sizes ); + } +} + + +void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent ) +{ + int tl = getView()->GetTopLayer(); + VECTOR2I cp = m_ctls->GetCursorPosition(); + PNS_ITEM* startItem = NULL; + + if( aEvent.IsMotion() || aEvent.IsClick() ) + { + bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); + + VECTOR2I p( aEvent.Position() ); + startItem = pickSingleItem( p ); + m_router->EnableSnapping ( snapEnabled ); + + if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) ) + startItem = NULL; + + if( startItem && startItem->Net() >= 0 ) + { + bool dummy; + VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy ); + + if( snapEnabled ) + { + m_startSnapPoint = psnap; + m_ctls->ForceCursorPosition( true, psnap ); + } + else + { + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + +// if( startItem->Layers().IsMultilayer() ) +// m_startLayer = tl; +// else +// m_startLayer = startItem->Layers().Start(); + + m_startItem = startItem; + } + else + { + m_startItem = NULL; + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + } +} + + +void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent ) +{ + VECTOR2I mp = m_ctls->GetMousePosition(); + VECTOR2I p = getView()->ToWorld( mp ); + VECTOR2I cp = m_ctls->GetCursorPosition(); + int layer; + bool snapEnabled = !aEvent.Modifier( MD_CTRL ); + + m_router->EnableSnapping( snapEnabled ); + + if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem ) + { + m_endItem = NULL; + m_endSnapPoint = cp; + return; + } + + bool dummy; + + if( m_router->IsPlacingVia() ) + layer = -1; + else + layer = m_router->GetCurrentLayer(); + + PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer ); + + if( endItem ) + { + VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy ); + m_ctls->ForceCursorPosition( true, cursorPos ); + m_endItem = endItem; + m_endSnapPoint = cursorPos; + } + else + { + m_endItem = NULL; + m_endSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + + if( m_endItem ) + TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() ); +} + + +int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem ) +{ + int tl = getView()->GetTopLayer(); + + if( m_startItem ) + { + const PNS_LAYERSET& ls = m_startItem->Layers(); + + if( ls.Overlaps( tl ) ) + return tl; + else + return ls.Start(); + } + + return tl; +} + + +void ROUTER_TOOL::switchLayerOnViaPlacement() +{ + int al = m_frame->GetActiveLayer(); + int cl = m_router->GetCurrentLayer(); + + if( cl != al ) + { + m_router->SwitchLayer( al ); + } + + optional newLayer = m_router->Sizes().PairedLayer( cl ); + + if( newLayer ) + { + m_router->SwitchLayer ( *newLayer ); + m_frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) ); + } +} + + +bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType ) +{ + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + + const int layerCount = bds.GetCopperLayerCount(); + int currentLayer = m_router->GetCurrentLayer(); + + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + + // fixme: P&S supports more than one fixed layer pair. Update the dialog? + sizes.ClearLayerPairs(); + sizes.AddLayerPair( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); + + if( !m_router->IsPlacingVia() ) + { + // Cannot place microvias or blind vias if not allowed (obvious) + if( ( aType == VIA_BLIND_BURIED ) && ( !bds.m_BlindBuriedViaAllowed ) ) + return false; + if( ( aType == VIA_MICROVIA ) && ( !bds.m_MicroViasAllowed ) ) + return false; + + //Can only place through vias on 2-layer boards + if( ( aType != VIA_THROUGH ) && ( layerCount <= 2 ) ) + return false; + + //Can only place microvias if we're on an outer layer, or directly adjacent to one + if( ( aType == VIA_MICROVIA ) && ( currentLayer > In1_Cu ) && ( currentLayer < layerCount-2 ) ) + return false; + + //Cannot place blind vias with front/back as the layer pair, this doesn't make sense + if( ( aType == VIA_BLIND_BURIED ) && ( sizes.GetLayerTop() == F_Cu ) && ( sizes.GetLayerBottom() == B_Cu ) ) + return false; + } + + sizes.SetViaType ( aType ); + m_router->ToggleViaPlacement( ); + m_router->UpdateSizes( sizes ); + + m_router->Move( m_endSnapPoint, m_endItem ); // refresh + + return false; +} + +//void ROUTER_TOOL::prepareRouting() +//{} + +void ROUTER_TOOL::performRouting() +{ + bool saveUndoBuffer = true; + + int routingLayer = getStartLayer( m_startItem ); + m_frame->SetActiveLayer( ToLAYER_ID( routingLayer ) ); + // fixme: switch on invisible layer + + if( m_startItem && m_startItem->Net() >= 0 ) + { + highlightNet( true, m_startItem->Net() ); + // Update track width and via size shown in main toolbar comboboxes + m_frame->SetCurrentNetClass( m_startItem->Parent()->GetNetClass()->GetName() ); + } + else + m_frame->SetCurrentNetClass( NETCLASS::Default ); + + m_ctls->ForceCursorPosition( false ); + m_ctls->SetAutoPan( true ); + + PNS_SIZES_SETTINGS sizes ( m_savedSizes ); + sizes.Init ( m_board, m_startItem ); + sizes.AddLayerPair( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); + m_router->UpdateSizes( sizes ); + + if ( !m_router->StartRouting( m_startSnapPoint, m_startItem, routingLayer ) ) + { + wxMessageBox( m_router->FailureReason(), _( "Error" ) ); + highlightNet( false ); + return; + } + + m_tuneStatusPopup = new PNS_TUNE_STATUS_POPUP( m_frame ); + m_tuneStatusPopup->Popup(); + + m_endItem = NULL; + m_endSnapPoint = m_startSnapPoint; + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() || evt->IsActivate() ) + break; + else if( evt->Action() == TA_UNDO_REDO ) + { + saveUndoBuffer = false; + break; + } + else if( evt->IsMotion() ) + { + wxPoint p = wxGetMousePosition(); + p.x += 20; + p.y += 20; + m_tuneStatusPopup->Update( m_router ); + m_tuneStatusPopup->Move( p ); + + updateEndItem( *evt ); + m_router->SetOrthoMode( evt->Modifier ( MD_CTRL ) ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsClick( BUT_LEFT ) ) + { + updateEndItem( *evt ); + bool needLayerSwitch = m_router->IsPlacingVia(); + + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + + if( needLayerSwitch ) + switchLayerOnViaPlacement(); + + // Synchronize the indicated layer + m_frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsAction( &ACT_PlaceThroughVia ) ) + { + onViaCommand ( VIA_THROUGH ); + } + else if( evt->IsAction( &ACT_PlaceBlindVia ) ) + { + onViaCommand ( VIA_BLIND_BURIED ); + } + else if( evt->IsAction( &ACT_PlaceMicroVia ) ) + { + onViaCommand ( VIA_MICROVIA ); + } + else if( evt->IsAction( &ACT_SwitchPosture ) ) + { + m_router->FlipPosture(); + m_router->Move( m_endSnapPoint, m_endItem ); // refresh + } + else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) ) + { + updateEndItem( *evt ); + m_router->SwitchLayer( m_frame->GetActiveLayer() ); + m_router->Move( m_endSnapPoint, m_endItem ); // refresh + } + else if( evt->IsAction( &ACT_EndTrack ) ) + { + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + } + + handleCommonEvents( *evt ); + } + + m_router->StopRouting(); + + if( saveUndoBuffer ) + { + // Save the recent changes in the undo buffer + m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + m_frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + delete m_tuneStatusPopup; + + m_ctls->SetAutoPan( false ); + m_ctls->ForceCursorPosition( false ); + highlightNet( false ); +} + + +int ROUTER_TOOL::RouteSingleTrace( TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Route Track" ) ); + return mainLoop( PNS_MODE_ROUTE_SINGLE ); +} + + +int ROUTER_TOOL::RouteDiffPair( TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Router Differential Pair" ) ); + return mainLoop( PNS_MODE_ROUTE_DIFF_PAIR ); +} + + +int ROUTER_TOOL::TuneSingleTrace( TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Track Length" ) ); + return mainLoop( PNS_MODE_TUNE_SINGLE ); +} + + +int ROUTER_TOOL::mainLoop( PNS_ROUTER_MODE aMode ) +{ + PCB_EDIT_FRAME* frame = getEditFrame(); + BOARD* board = getModel(); + + // Deselect all items + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + Activate(); + + m_router->SetMode ( aMode ); + + m_ctls->SetSnapping( true ); + m_ctls->ShowCursor( true ); + + std::auto_ptr ctxMenu ( new ROUTER_TOOL_MENU( board, aMode ) ); + SetContextMenu ( ctxMenu.get() ); + + // Main loop: keep receiving events + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( m_needsSync ) + { + m_router->SyncWorld(); + m_router->SetView( getView() ); + m_needsSync = false; + } + + if( evt->IsCancel() || evt->IsActivate() ) + break; // Finish + else if( evt->Action() == TA_UNDO_REDO ) + m_needsSync = true; + else if( evt->IsMotion() ) + updateStartItem( *evt ); + else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_NewTrack ) ) + { + updateStartItem( *evt ); + + if( evt->Modifier( MD_CTRL ) ) + performDragging(); + else + performRouting(); + } + else if( evt->IsAction( &ACT_Drag ) ) + performDragging(); + + handleCommonEvents( *evt ); + } + + // Restore the default settings + m_ctls->SetAutoPan( false ); + m_ctls->ShowCursor( false ); + frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); + + // Store routing settings till the next invocation + m_savedSettings = m_router->Settings(); + m_savedSizes = m_router->Sizes(); + + return 0; +} + + +int ROUTER_TOOL::InlineDrag( TOOL_EVENT& aEvent ) +{ + return 0; + + #if 0 + VIEW_CONTROLS* ctls = getViewControls(); + PCB_EDIT_FRAME* frame = getEditFrame(); + BOARD* board = getModel(); + + printf("RouterTool::InlineDrag!\n"); + Reset(); + + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + ctls->SetSnapping( true ); + ctls->ShowCursor( true ); + + m_startItem = m_router->QueryItemByParent ( ) + + bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem ); + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() ) + break; + else if( evt->IsMotion() ) + { + updateEndItem( *evt ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsUp( BUT_LEFT ) ) + { + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + } + } + + if( m_router->RoutingInProgress() ) + m_router->StopRouting(); + + return 0; + #endif + +} + + +void ROUTER_TOOL::performDragging() +{ + PCB_EDIT_FRAME* frame = getEditFrame(); + bool saveUndoBuffer = true; + VIEW_CONTROLS* ctls = getViewControls(); + + bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem ); + + if( !dragStarted ) + return; + + if( m_startItem && m_startItem->Net() >= 0 ) + highlightNet( true, m_startItem->Net() ); + + ctls->ForceCursorPosition( false ); + ctls->SetAutoPan( true ); + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() || evt->IsActivate() ) + break; + else if( evt->Action() == TA_UNDO_REDO ) + { + saveUndoBuffer = false; + break; + } + else if( evt->IsMotion() ) + { + updateEndItem( *evt ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsClick( BUT_LEFT ) ) + { + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + } + + handleCommonEvents( *evt ); + } + + if( m_router->RoutingInProgress() ) + m_router->StopRouting(); + + if( saveUndoBuffer ) + { + // Save the recent changes in the undo buffer + frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + ctls->SetAutoPan( false ); + ctls->ForceCursorPosition( false ); + highlightNet( false ); +} diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp index c51c91c18c..da9a0f0820 100644 --- a/pcbnew/router/router_preview_item.cpp +++ b/pcbnew/router/router_preview_item.cpp @@ -57,6 +57,13 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS_ITEM* aItem ) { m_originLayer = aItem->Layers().Start(); + if( aItem->OfKind ( PNS_ITEM::LINE ) ) + { + const PNS_LINE* l=static_cast( aItem ); + if( !l->SegmentCount() ) + return; + } + assert( m_originLayer >= 0 ); m_layer = m_originLayer; @@ -131,7 +138,7 @@ const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const } -void ROUTER_PREVIEW_ITEM::drawLineChain( const SHAPE_LINE_CHAIN &aL, KIGFX::GAL* aGal ) const +void ROUTER_PREVIEW_ITEM::drawLineChain( const SHAPE_LINE_CHAIN& aL, KIGFX::GAL* aGal ) const { for( int s = 0; s < aL.SegmentCount(); s++ ) aGal->DrawLine( aL.CSegment( s ).A, aL.CSegment( s ).B ); @@ -159,65 +166,65 @@ void ROUTER_PREVIEW_ITEM::ViewDraw( int aLayer, KIGFX::GAL* aGal ) const switch( m_shape->Type() ) { - case SH_LINE_CHAIN: - { - const SHAPE_LINE_CHAIN* l = (const SHAPE_LINE_CHAIN*) m_shape; - drawLineChain( *l, aGal ); - break; - } - - case SH_SEGMENT: - { - const SHAPE_SEGMENT* s = (const SHAPE_SEGMENT*) m_shape; - aGal->DrawLine( s->GetSeg().A, s->GetSeg().B ); - - if( m_clearance > 0 ) + case SH_LINE_CHAIN: { - aGal->SetLayerDepth( ClearanceOverlayDepth ); - aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); - aGal->SetLineWidth( m_width + 2 * m_clearance ); + const SHAPE_LINE_CHAIN* l = (const SHAPE_LINE_CHAIN*) m_shape; + drawLineChain( *l, aGal ); + break; + } + + case SH_SEGMENT: + { + const SHAPE_SEGMENT* s = (const SHAPE_SEGMENT*) m_shape; aGal->DrawLine( s->GetSeg().A, s->GetSeg().B ); + + if( m_clearance > 0 ) + { + aGal->SetLayerDepth( ClearanceOverlayDepth ); + aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); + aGal->SetLineWidth( m_width + 2 * m_clearance ); + aGal->DrawLine( s->GetSeg().A, s->GetSeg().B ); + } + + break; } - break; - } - - case SH_CIRCLE: - { - const SHAPE_CIRCLE* c = (const SHAPE_CIRCLE*) m_shape; - aGal->DrawCircle( c->GetCenter(), c->GetRadius() ); - - if( m_clearance > 0 ) + case SH_CIRCLE: { - aGal->SetLayerDepth( ClearanceOverlayDepth ); - aGal->SetFillColor( COLOR4D( DARKDARKGRAY ) ); - aGal->SetIsStroke( false ); - aGal->DrawCircle( c->GetCenter(), c->GetRadius() + m_clearance ); + const SHAPE_CIRCLE* c = (const SHAPE_CIRCLE*) m_shape; + aGal->DrawCircle( c->GetCenter(), c->GetRadius() ); + + if( m_clearance > 0 ) + { + aGal->SetLayerDepth( ClearanceOverlayDepth ); + aGal->SetFillColor( COLOR4D( DARKDARKGRAY ) ); + aGal->SetIsStroke( false ); + aGal->DrawCircle( c->GetCenter(), c->GetRadius() + m_clearance ); + } + + break; } - break; - } - - case SH_RECT: - { - const SHAPE_RECT* r = (const SHAPE_RECT*) m_shape; - aGal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() ); - - if( m_clearance > 0 ) + case SH_RECT: { - aGal->SetLayerDepth( ClearanceOverlayDepth ); - VECTOR2I p0( r->GetPosition() ), s( r->GetSize() ); - aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); - aGal->SetIsStroke( true ); - aGal->SetLineWidth( 2 * m_clearance ); - aGal->DrawLine( p0, VECTOR2I( p0.x + s.x, p0.y ) ); - aGal->DrawLine( p0, VECTOR2I( p0.x, p0.y + s.y ) ); - aGal->DrawLine( p0 + s , VECTOR2I( p0.x + s.x, p0.y ) ); - aGal->DrawLine( p0 + s, VECTOR2I( p0.x, p0.y + s.y ) ); - } + const SHAPE_RECT* r = (const SHAPE_RECT*) m_shape; + aGal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() ); - break; - } + if( m_clearance > 0 ) + { + aGal->SetLayerDepth( ClearanceOverlayDepth ); + VECTOR2I p0( r->GetPosition() ), s( r->GetSize() ); + aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); + aGal->SetIsStroke( true ); + aGal->SetLineWidth( 2 * m_clearance ); + aGal->DrawLine( p0, VECTOR2I( p0.x + s.x, p0.y ) ); + aGal->DrawLine( p0, VECTOR2I( p0.x, p0.y + s.y ) ); + aGal->DrawLine( p0 + s , VECTOR2I( p0.x + s.x, p0.y ) ); + aGal->DrawLine( p0 + s, VECTOR2I( p0.x, p0.y + s.y ) ); + } + + break; + } case SH_CONVEX: case SH_POLYGON: diff --git a/pcbnew/router/router_preview_item.h b/pcbnew/router/router_preview_item.h index aeb5492979..a5b6759755 100644 --- a/pcbnew/router/router_preview_item.h +++ b/pcbnew/router/router_preview_item.h @@ -50,19 +50,19 @@ public: PR_POINT, PR_SHAPE }; - + ROUTER_PREVIEW_ITEM( const PNS_ITEM* aItem = NULL, KIGFX::VIEW_GROUP* aParent = NULL ); ~ROUTER_PREVIEW_ITEM(); void Update( const PNS_ITEM* aItem ); void StuckMarker( VECTOR2I& aPosition ); - + void Line( const SHAPE_LINE_CHAIN& aLine, int aWidth = 0, int aStyle = 0 ); void Box( const BOX2I& aBox, int aStyle = 0 ); void Point ( const VECTOR2I& aPos, int aStyle = 0); - void SetColor( const KIGFX::COLOR4D& aColor ) + void SetColor( const KIGFX::COLOR4D& aColor ) { m_color = aColor; } @@ -96,7 +96,7 @@ private: SHAPE* m_shape; ITEM_TYPE m_type; - + int m_style; int m_width; int m_layer; diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index a36bc83da4..088695d8a9 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -75,17 +76,18 @@ static TOOL_ACTION ACT_PlaceMicroVia( "pcbnew.InteractiveRouter.PlaceMicroVia", static TOOL_ACTION ACT_CustomTrackWidth( "pcbnew.InteractiveRouter.CustomTrackWidth", AS_CONTEXT, 'W', "Custom Track Width", "Shows a dialog for changing the track width and via size."); -static TOOL_ACTION ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions", - AS_CONTEXT, 'E', - "Routing Options...", "Shows a dialog containing router options."); static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture", AS_CONTEXT, '/', "Switch Track Posture", "Switches posture of the currenly routed track."); +static TOOL_ACTION ACT_SetDpDimensions( "pcbnew.InteractiveRouter.SetDpDimensions", + AS_CONTEXT, 'D', + "Differential Pair Dimensions...", "Sets the width and gap of the currently routed differential pair."); + + ROUTER_TOOL::ROUTER_TOOL() : - TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" ) + PNS_TOOL_BASE( "pcbnew.InteractiveRouter" ) { - m_router = NULL; } @@ -208,7 +210,7 @@ protected: class ROUTER_TOOL_MENU: public CONTEXT_MENU { public: - ROUTER_TOOL_MENU( BOARD* aBoard ) + ROUTER_TOOL_MENU( BOARD* aBoard, PNS_ROUTER_MODE aMode ) { SetTitle( wxT( "Interactive Router" ) ); Add( ACT_NewTrack ); @@ -228,39 +230,32 @@ public: Add( ACT_CustomTrackWidth ); + if ( aMode == PNS_MODE_ROUTE_DIFF_PAIR ) + Add( ACT_SetDpDimensions ); + AppendSeparator(); - Add( ACT_RouterOptions ); + Add( PNS_TOOL_BASE::ACT_RouterOptions ); } }; ROUTER_TOOL::~ROUTER_TOOL() { - delete m_router; + } void ROUTER_TOOL::Reset( RESET_REASON aReason ) { - if( m_router ) - delete m_router; + PNS_TOOL_BASE::Reset( aReason ); - m_router = new PNS_ROUTER; + Go( &ROUTER_TOOL::RouteSingleTrace, COMMON_ACTIONS::routerActivateSingle.MakeEvent() ); + Go( &ROUTER_TOOL::RouteDiffPair, COMMON_ACTIONS::routerActivateDiffPair.MakeEvent() ); + Go( &ROUTER_TOOL::DpDimensionsDialog, COMMON_ACTIONS::routerActivateDpDimensionsDialog.MakeEvent() ); + Go( &ROUTER_TOOL::SettingsDialog, COMMON_ACTIONS::routerActivateSettingsDialog.MakeEvent() ); - TRACEn( 0, "Reset" ); - m_router->ClearWorld(); - m_router->SetBoard( getModel() ); - m_router->SyncWorld(); - m_router->LoadSettings( m_settings ); - m_needsSync = false; - - if( getView() ) - m_router->SetView( getView() ); - - Go( &ROUTER_TOOL::Main, COMMON_ACTIONS::routerActivate.MakeEvent() ); } - int ROUTER_TOOL::getDefaultWidth( int aNetCode ) { int w, d1, d2; @@ -274,11 +269,10 @@ int ROUTER_TOOL::getDefaultWidth( int aNetCode ) void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, int& aViaDiameter, int& aViaDrill ) { - BOARD* board = getModel(); - BOARD_DESIGN_SETTINGS &bds = board->GetDesignSettings(); + BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings(); NETCLASSPTR netClass; - NETINFO_ITEM* ni = board->FindNet( aNetCode ); + NETINFO_ITEM* ni = m_board->FindNet( aNetCode ); if( ni ) { @@ -294,96 +288,8 @@ void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, aViaDrill = netClass->GetViaDrill(); } - -PNS_ITEM* ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer ) +void ROUTER_TOOL::handleCommonEvents( const TOOL_EVENT& aEvent ) { - int tl = getView()->GetTopLayer(); - - if( aLayer > 0 ) - tl = aLayer; - - PNS_ITEM* prioritized[4]; - - for( int i = 0; i < 4; i++ ) - prioritized[i] = 0; - - PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere ); - - BOOST_FOREACH( PNS_ITEM* item, candidates.Items() ) - { - if( !IsCopperLayer( item->Layers().Start() ) ) - continue; - - // fixme: this causes flicker with live loop removal... - //if( item->Parent() && !item->Parent()->ViewIsVisible() ) - // continue; - - if( aNet < 0 || item->Net() == aNet ) - { - if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) ) - { - if( !prioritized[2] ) - prioritized[2] = item; - if( item->Layers().Overlaps( tl ) ) - prioritized[0] = item; - } - else - { - if( !prioritized[3] ) - prioritized[3] = item; - if( item->Layers().Overlaps( tl ) ) - prioritized[1] = item; - } - } - } - - PNS_ITEM* rv = NULL; - PCB_EDIT_FRAME* frame = getEditFrame (); - DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)frame->GetDisplayOptions(); - - for( int i = 0; i < 4; i++ ) - { - PNS_ITEM* item = prioritized[i]; - - if( displ_opts->m_ContrastModeDisplay ) - if( item && !item->Layers().Overlaps( tl ) ) - item = NULL; - - if( item ) - { - rv = item; - break; - } - } - - if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) ) - rv = NULL; - - if( rv ) - TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl ); - - return rv; -} - - -void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode ) -{ - RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings(); - - if( aNetcode >= 0 && aEnabled ) - rs->SetHighlight( true, aNetcode ); - else - rs->SetHighlight( false ); - - getView()->UpdateAllLayersColor(); -} - - -void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) -{ - PCB_EDIT_FRAME* frame = getEditFrame (); - BOARD* board = getModel (); - #ifdef DEBUG if( aEvent.IsKeyPressed() ) { @@ -399,18 +305,27 @@ void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) #endif if( aEvent.IsAction( &ACT_RouterOptions ) ) { - DIALOG_PNS_SETTINGS settingsDlg( frame, m_router->Settings() ); + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); if( settingsDlg.ShowModal() ) { // FIXME: do we need an explicit update? } } + else if( aEvent.IsAction( &ACT_SetDpDimensions ) ) + { + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes ); + if( settingsDlg.ShowModal() ) + { + m_router->UpdateSizes( sizes ); + } + } else if( aEvent.IsAction( &ACT_CustomTrackWidth ) ) { - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds ); + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + DIALOG_TRACK_VIA_SIZE sizeDlg( m_frame, bds ); if( sizeDlg.ShowModal() ) { @@ -422,107 +337,12 @@ void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) ) { - PNS_SIZES_SETTINGS sizes; - sizes.ImportCurrent ( board->GetDesignSettings() ); + PNS_SIZES_SETTINGS sizes ( m_router->Sizes() ); + sizes.ImportCurrent ( m_board->GetDesignSettings() ); m_router->UpdateSizes ( sizes ); } } - -void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent ) -{ - VIEW_CONTROLS* ctls = getViewControls(); - int tl = getView()->GetTopLayer(); - VECTOR2I cp = ctls->GetCursorPosition(); - PNS_ITEM* startItem = NULL; - - if( aEvent.IsMotion() || aEvent.IsClick() ) - { - VECTOR2I p = aEvent.Position(); - startItem = pickSingleItem( p ); - bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); - m_router->EnableSnapping ( snapEnabled ); - - if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) ) - startItem = NULL; - - if( startItem && startItem->Net() >= 0 ) - { - bool dummy; - VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy ); - - if( snapEnabled ) - { - m_startSnapPoint = psnap; - ctls->ForceCursorPosition( true, psnap ); - } - else - { - m_startSnapPoint = cp; - ctls->ForceCursorPosition( false ); - } - -// if( startItem->Layers().IsMultilayer() ) -// m_startLayer = tl; -// else -// m_startLayer = startItem->Layers().Start(); - - m_startItem = startItem; - } - else - { - m_startItem = NULL; - m_startSnapPoint = cp; - ctls->ForceCursorPosition( false ); - } - } -} - - -void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent ) -{ - VIEW_CONTROLS* ctls = getViewControls(); - VECTOR2I p = getView()->ToWorld( ctls->GetMousePosition() ); - VECTOR2I cp = ctls->GetCursorPosition(); - int layer; - bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); - - m_router->EnableSnapping( snapEnabled ); - - if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem ) - { - m_endItem = NULL; - m_endSnapPoint = cp; - return; - } - - bool dummy; - - if( m_router->IsPlacingVia() ) - layer = -1; - else - layer = m_router->GetCurrentLayer(); - - PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer ); - - if( endItem ) - { - VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy ); - ctls->ForceCursorPosition( true, cursorPos ); - m_endItem = endItem; - m_endSnapPoint = cursorPos; - } - else - { - m_endItem = NULL; - m_endSnapPoint = cp; - ctls->ForceCursorPosition( false ); - } - - if( m_endItem ) - TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() ); -} - int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem ) { int tl = getView()->GetTopLayer(); @@ -541,9 +361,7 @@ int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem ) } void ROUTER_TOOL::switchLayerOnViaPlacement() { - PCB_EDIT_FRAME* frame = getEditFrame(); - - int al = frame->GetActiveLayer(); + int al = m_frame->GetActiveLayer(); int cl = m_router->GetCurrentLayer(); if( cl != al ) @@ -556,24 +374,23 @@ void ROUTER_TOOL::switchLayerOnViaPlacement() if( newLayer ) { m_router->SwitchLayer ( *newLayer ); - frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) ); + m_frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) ); } } bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType ) { - BOARD* board = getModel (); - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - PCB_EDIT_FRAME* frame = getEditFrame(); + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); const int layerCount = bds.GetCopperLayerCount(); int currentLayer = m_router->GetCurrentLayer(); PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + // fixme: P&S supports more than one fixed layer pair. Update the dialog? sizes.ClearLayerPairs(); - sizes.AddLayerPair( frame->GetScreen()->m_Route_Layer_TOP, - frame->GetScreen()->m_Route_Layer_BOTTOM ); + sizes.AddLayerPair( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); if( !m_router->IsPlacingVia() ) { @@ -606,40 +423,77 @@ bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType ) return false; } -void ROUTER_TOOL::performRouting() -{ - PCB_EDIT_FRAME* frame = getEditFrame(); - bool saveUndoBuffer = true; - VIEW_CONTROLS* ctls = getViewControls(); - BOARD* board = getModel(); - int routingLayer = getStartLayer ( m_startItem ); - frame->SetActiveLayer( ToLAYER_ID ( routingLayer ) ); +bool ROUTER_TOOL::prepareInteractive() +{ + int routingLayer = getStartLayer ( m_startItem ); + m_frame->SetActiveLayer( ToLAYER_ID ( routingLayer ) ); + // fixme: switch on invisible layer if( m_startItem && m_startItem->Net() >= 0 ) { highlightNet( true, m_startItem->Net() ); // Update track width and via size shown in main toolbar comboboxes - frame->SetCurrentNetClass( m_startItem->Parent()->GetNetClass()->GetName() ); + m_frame->SetCurrentNetClass( m_startItem->Parent()->GetNetClass()->GetName() ); } else - frame->SetCurrentNetClass( NETCLASS::Default ); + m_frame->SetCurrentNetClass( NETCLASS::Default ); - ctls->ForceCursorPosition( false ); - ctls->SetAutoPan( true ); + m_ctls->ForceCursorPosition( false ); + m_ctls->SetAutoPan( true ); - PNS_SIZES_SETTINGS sizes; - sizes.Init ( board, m_startItem ); - sizes.AddLayerPair ( frame->GetScreen()->m_Route_Layer_TOP, - frame->GetScreen()->m_Route_Layer_BOTTOM ); + PNS_SIZES_SETTINGS sizes ( m_router->Sizes() ); + + sizes.Init ( m_board, m_startItem ); + sizes.AddLayerPair ( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); m_router->UpdateSizes( sizes ); - m_router->StartRouting( m_startSnapPoint, m_startItem, routingLayer ); + if ( !m_router->StartRouting( m_startSnapPoint, m_startItem, routingLayer ) ) + { + wxMessageBox ( m_router->FailureReason(), _("Error") ); + highlightNet ( false ); + return false; + } m_endItem = NULL; m_endSnapPoint = m_startSnapPoint; + return true; +} + +bool ROUTER_TOOL::finishInteractive( bool aSaveUndoBuffer ) +{ + m_router->StopRouting(); + + if( aSaveUndoBuffer ) + { + // Save the recent changes in the undo buffer + m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + m_frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + m_ctls->SetAutoPan( false ); + m_ctls->ForceCursorPosition( false ); + highlightNet( false ); + + return true; +} + +void ROUTER_TOOL::performRouting() +{ + bool saveUndoBuffer = true; + + if ( !prepareInteractive( ) ) + return; + while( OPT_TOOL_EVENT evt = Wait() ) { if( evt->IsCancel() || evt->IsActivate() ) @@ -652,6 +506,7 @@ void ROUTER_TOOL::performRouting() else if( evt->IsMotion() ) { updateEndItem( *evt ); + m_router->SetOrthoMode ( evt->Modifier ( MD_CTRL ) ); m_router->Move( m_endSnapPoint, m_endItem ); } else if( evt->IsClick( BUT_LEFT ) ) @@ -663,13 +518,10 @@ void ROUTER_TOOL::performRouting() break; if( needLayerSwitch ) - { switchLayerOnViaPlacement(); - } // Synchronize the indicated layer - frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) ); - + m_frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) ); m_router->Move( m_endSnapPoint, m_endItem ); } else if( evt->IsAction( &ACT_PlaceThroughVia ) ) @@ -692,7 +544,7 @@ void ROUTER_TOOL::performRouting() else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) ) { updateEndItem( *evt ); - m_router->SwitchLayer( frame->GetActiveLayer() ); + m_router->SwitchLayer( m_frame->GetActiveLayer() ); m_router->Move( m_endSnapPoint, m_endItem ); // refresh } else if( evt->IsAction( &ACT_EndTrack ) ) @@ -704,42 +556,67 @@ void ROUTER_TOOL::performRouting() handleCommonEvents( *evt ); } - m_router->StopRouting(); + finishInteractive ( saveUndoBuffer ); +} - if( saveUndoBuffer ) +int ROUTER_TOOL::DpDimensionsDialog( const TOOL_EVENT& aEvent ) +{ + Activate(); + + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes ); + + if( settingsDlg.ShowModal() ) { - // Save the recent changes in the undo buffer - frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); - m_router->ClearUndoBuffer(); - frame->OnModify(); - } - else - { - // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now - m_needsSync = true; + m_router->UpdateSizes( sizes ); + m_savedSizes = sizes; } - ctls->SetAutoPan( false ); - ctls->ForceCursorPosition( false ); - highlightNet( false ); + return 0; +} + +int ROUTER_TOOL::SettingsDialog( const TOOL_EVENT& aEvent ) +{ + Activate(); + + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); + + if( settingsDlg.ShowModal() ) + { + m_savedSettings = m_router->Settings(); + } + return 0; } -int ROUTER_TOOL::Main( const TOOL_EVENT& aEvent ) +int ROUTER_TOOL::RouteSingleTrace( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Route Track" ) ); + return mainLoop( PNS_MODE_ROUTE_SINGLE ); +} + +int ROUTER_TOOL::RouteDiffPair( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Router Differential Pair" ) ); + return mainLoop( PNS_MODE_ROUTE_DIFF_PAIR ); +} + +int ROUTER_TOOL::mainLoop( PNS_ROUTER_MODE aMode ) { - VIEW_CONTROLS* ctls = getViewControls(); PCB_EDIT_FRAME* frame = getEditFrame(); BOARD* board = getModel(); // Deselect all items m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); - frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Interactive Router" ) ); + Activate(); - ctls->SetSnapping( true ); - ctls->ShowCursor( true ); + m_router->SetMode ( aMode ); - std::auto_ptr ctxMenu ( new ROUTER_TOOL_MENU( board ) ); + m_ctls->SetSnapping( true ); + m_ctls->ShowCursor( true ); + + std::auto_ptr ctxMenu ( new ROUTER_TOOL_MENU( board, aMode ) ); SetContextMenu ( ctxMenu.get() ); // Main loop: keep receiving events @@ -774,17 +651,17 @@ int ROUTER_TOOL::Main( const TOOL_EVENT& aEvent ) } // Restore the default settings - ctls->SetAutoPan( false ); - ctls->ShowCursor( false ); + m_ctls->SetAutoPan( false ); + m_ctls->ShowCursor( false ); frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); // Store routing settings till the next invocation - m_settings = m_router->Settings(); + m_savedSettings = m_router->Settings(); + m_savedSizes = m_router->Sizes(); return 0; } - void ROUTER_TOOL::performDragging() { PCB_EDIT_FRAME* frame = getEditFrame(); @@ -845,3 +722,9 @@ void ROUTER_TOOL::performDragging() ctls->ForceCursorPosition( false ); highlightNet( false ); } + +int ROUTER_TOOL::InlineDrag ( const TOOL_EVENT& aEvent ) +{ + return 0; +} + diff --git a/pcbnew/router/router_tool.h b/pcbnew/router/router_tool.h index 314e844ae5..73c4ea917f 100644 --- a/pcbnew/router/router_tool.h +++ b/pcbnew/router/router_tool.h @@ -22,61 +22,41 @@ #ifndef __ROUTER_TOOL_H #define __ROUTER_TOOL_H -#include +#include "pns_tool_base.h" -#include -#include - -#include - -#include "pns_routing_settings.h" - -class PNS_ROUTER; -class PNS_ITEM; - -class APIEXPORT ROUTER_TOOL : public TOOL_INTERACTIVE +class APIEXPORT ROUTER_TOOL : public PNS_TOOL_BASE { public: ROUTER_TOOL(); ~ROUTER_TOOL(); void Reset( RESET_REASON aReason ); - int Main( const TOOL_EVENT& aEvent ); + + int RouteSingleTrace ( const TOOL_EVENT& aEvent ); + int RouteDiffPair ( const TOOL_EVENT& aEvent ); + int InlineDrag ( const TOOL_EVENT& aEvent ); + + int DpDimensionsDialog ( const TOOL_EVENT& aEvent ); + int SettingsDialog ( const TOOL_EVENT& aEvent ); private: - PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 ); + + int mainLoop( PNS_ROUTER_MODE aMode ); int getDefaultWidth( int aNetCode ); void performRouting(); void performDragging(); - void highlightNet( bool aEnabled, int aNetcode = -1 ); - - void updateStartItem( TOOL_EVENT& aEvent ); - void updateEndItem( TOOL_EVENT& aEvent ); - void getNetclassDimensions( int aNetCode, int& aWidth, int& aViaDiameter, int& aViaDrill ); - void handleCommonEvents( TOOL_EVENT& evt ); + void handleCommonEvents( const TOOL_EVENT& evt ); int getStartLayer( const PNS_ITEM* aItem ); void switchLayerOnViaPlacement(); bool onViaCommand( VIATYPE_T aType ); - MSG_PANEL_ITEMS m_panelItems; - - PNS_ROUTER* m_router; - PNS_ROUTING_SETTINGS m_settings; ///< Stores routing settings between router invocations - - PNS_ITEM* m_startItem; - int m_startLayer; - VECTOR2I m_startSnapPoint; - - PNS_ITEM* m_endItem; - VECTOR2I m_endSnapPoint; - - ///> Flag marking that the router's world needs syncing. - bool m_needsSync; + bool prepareInteractive( ); + bool finishInteractive( bool aSaveUndoBuffer ); }; #endif diff --git a/pcbnew/router/time_limit.h b/pcbnew/router/time_limit.h index 8c7a07db5e..233fd96a1d 100644 --- a/pcbnew/router/time_limit.h +++ b/pcbnew/router/time_limit.h @@ -26,17 +26,17 @@ class TIME_LIMIT { public: - TIME_LIMIT( int aMilliseconds = 0 ); - ~TIME_LIMIT(); + TIME_LIMIT( int aMilliseconds = 0 ); + ~TIME_LIMIT(); - bool Expired() const; - void Restart(); + bool Expired() const; + void Restart(); - void Set( int aMilliseconds ); + void Set( int aMilliseconds ); private: - int m_limitMs; - int64_t m_startTics; + int m_limitMs; + int64_t m_startTics; }; #endif diff --git a/pcbnew/tools/common_actions.cpp b/pcbnew/tools/common_actions.cpp index 1d511a0558..10038316ca 100644 --- a/pcbnew/tools/common_actions.cpp +++ b/pcbnew/tools/common_actions.cpp @@ -58,8 +58,27 @@ TOOL_ACTION COMMON_ACTIONS::findDummy( "pcbnew.Find.Dummy", // only block the ho TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove", AS_GLOBAL, 'T' ); - // Edit tool actions +TOOL_ACTION COMMON_ACTIONS::editFootprintInFpEditor( "pcbnew.InteractiveEdit.editFootprintInFpEditor", + AS_CONTEXT, MD_CTRL + 'E', + "Open in Footprint Editor", + "Opens the selected footprint in the Footprint Editor" ); + +TOOL_ACTION COMMON_ACTIONS::copyPadToSettings ( "pcbnew.InteractiveEdit.copyPadToSettings", + AS_CONTEXT, 0, + "Copy pad settings to Current Settings", + "Copies the properties of selected pad to the current template pad settings." ); + +TOOL_ACTION COMMON_ACTIONS::copySettingsToPads ( "pcbnew.InteractiveEdit.copySettingsToPads", + AS_CONTEXT, 0, + "Copy Current Settings to pads", + "Copies the current template pad settings to the selected pad(s)." ); + +TOOL_ACTION COMMON_ACTIONS::globalEditPads ( "pcbnew.InteractiveEdit.globalPadEdit", + AS_CONTEXT, 0, + "Global Pad Edition", + "Changes pad properties globally." ); + TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit", AS_GLOBAL, 'M', "Move", "Moves the selected item(s)", AF_ACTIVATE ); @@ -387,11 +406,41 @@ TOOL_ACTION COMMON_ACTIONS::toBeDone( "pcbnew.Control.toBeDone", AS_GLOBAL, 0, // dialog saying it is not implemented yet "", "" ); // so users are aware of that -TOOL_ACTION COMMON_ACTIONS::routerActivate( "pcbnew.InteractiveRouter", +TOOL_ACTION COMMON_ACTIONS::routerActivateSingle( "pcbnew.InteractiveRouter.SingleTrack", AS_GLOBAL, 'X', - "Run push & shove router", "Run push & shove router", AF_ACTIVATE ); + "Run push & shove router (single tracks)", "Run push & shove router (single tracks)", AF_ACTIVATE ); +TOOL_ACTION COMMON_ACTIONS::routerActivateDiffPair( "pcbnew.InteractiveRouter.DiffPair", + AS_GLOBAL, '6', + "Run push & shove router (differential pairs)", "Run push & shove router (differential pairs)", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateSettingsDialog( "pcbnew.InteractiveRouter.SettingsDialog", + AS_GLOBAL, 0, + "Open Interactive Router settings", "Open Interactive Router settings", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateDpDimensionsDialog( "pcbnew.InteractiveRouter.DpDimensionsDialog", + AS_GLOBAL, 0, + "Open Differential Pair Dimension settings", "Open Differential Pair Dimension settings", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneSingleTrace( "pcbnew.LengthTuner.TuneSingleTrack", + AS_GLOBAL, '7', + "Tune length of a single track", "", AF_ACTIVATE ); + + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPair( "pcbnew.LengthTuner.TuneDiffPair", + AS_GLOBAL, '8', + "Tune length of a differential pair", "", AF_ACTIVATE ); + + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPairSkew( "pcbnew.LengthTuner.TuneDiffPairSkew", + AS_GLOBAL, '9', + "Tune skew of a differential pair", "", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerInlineDrag( "pcbnew.InteractiveRouter.InlineDrag", + AS_GLOBAL, 0, + "", "" ); + // Point editor TOOL_ACTION COMMON_ACTIONS::pointEditorUpdate( "pcbnew.PointEditor.update", AS_GLOBAL, 0, @@ -401,7 +450,6 @@ TOOL_ACTION COMMON_ACTIONS::pointEditorBreakOutline( "pcbnew.PointEditor.breakOu AS_GLOBAL, 0, "Create corner", "Create corner" ); - // Placement tool TOOL_ACTION COMMON_ACTIONS::alignTop( "pcbnew.Place.alignTop", AS_GLOBAL, 0, @@ -442,7 +490,25 @@ boost::optional COMMON_ACTIONS::TranslateLegacyId( int aId ) return COMMON_ACTIONS::placeModule.MakeEvent(); case ID_TRACK_BUTT: - return COMMON_ACTIONS::routerActivate.MakeEvent(); + return COMMON_ACTIONS::routerActivateSingle.MakeEvent(); + + case ID_DIFF_PAIR_BUTT: + return COMMON_ACTIONS::routerActivateDiffPair.MakeEvent(); + + case ID_TUNE_SINGLE_TRACK_LEN_BUTT: + return COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent(); + + case ID_TUNE_DIFF_PAIR_LEN_BUTT: + return COMMON_ACTIONS::routerActivateTuneDiffPair.MakeEvent(); + + case ID_TUNE_DIFF_PAIR_SKEW_BUTT: + return COMMON_ACTIONS::routerActivateTuneDiffPairSkew.MakeEvent(); + + case ID_MENU_INTERACTIVE_ROUTER_SETTINGS: + return COMMON_ACTIONS::routerActivateSettingsDialog.MakeEvent(); + + case ID_MENU_DIFF_PAIR_DIMENSIONS: + return COMMON_ACTIONS::routerActivateDpDimensionsDialog.MakeEvent(); case ID_PCB_ZONES_BUTT: return COMMON_ACTIONS::drawZone.MakeEvent(); diff --git a/pcbnew/tools/common_actions.h b/pcbnew/tools/common_actions.h index b05e4d3c73..6d07eaeac0 100644 --- a/pcbnew/tools/common_actions.h +++ b/pcbnew/tools/common_actions.h @@ -22,10 +22,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#ifndef __COMMON_ACTIONS_H +#define __COMMON_ACTIONS_H + #include #include class TOOL_EVENT; +class TOOL_MANAGER; /** * Class COMMON_ACTIONS @@ -121,8 +125,29 @@ public: static TOOL_ACTION arcPosture; // Push and Shove Router Tool + /// Activation of the Push and Shove router - static TOOL_ACTION routerActivate; + static TOOL_ACTION routerActivateSingle; + + /// Activation of the Push and Shove router (differential pair mode) + static TOOL_ACTION routerActivateDiffPair; + + /// Activation of the Push and Shove router (tune single line mode) + static TOOL_ACTION routerActivateTuneSingleTrace; + + /// Activation of the Push and Shove router (diff pair tuning mode) + static TOOL_ACTION routerActivateTuneDiffPair; + + /// Activation of the Push and Shove router (skew tuning mode) + static TOOL_ACTION routerActivateTuneDiffPairSkew; + + /// Activation of the Push and Shove settings dialogs + static TOOL_ACTION routerActivateSettingsDialog; + static TOOL_ACTION routerActivateDpDimensionsDialog; + + + /// Activation of the Push and Shove router (inline dragging mode) + static TOOL_ACTION routerInlineDrag; // Point Editor /// Update edit points @@ -244,6 +269,13 @@ public: /// Blocks CTRL+F, it is handled by wxWidgets static TOOL_ACTION findDummy; + + static TOOL_ACTION editFootprintInFpEditor; + static TOOL_ACTION copyPadToSettings; + static TOOL_ACTION copySettingsToPads; + static TOOL_ACTION globalEditPads; + + /** * Function TranslateLegacyId() * Translates legacy tool ids to the corresponding TOOL_ACTION name. @@ -253,3 +285,7 @@ public: */ static boost::optional TranslateLegacyId( int aId ); }; + +void registerAllTools ( TOOL_MANAGER *aToolManager ); + +#endif diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index bbaf2b0e6b..7f3c615eab 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -22,13 +22,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include + #include #include #include #include +#include +#include #include #include -#include #include #include @@ -43,6 +46,7 @@ #include "common_actions.h" #include "selection_tool.h" #include "edit_tool.h" +#include "grid_helper.h" #include #include @@ -83,6 +87,11 @@ bool EDIT_TOOL::Init() m_selectionTool->AddMenuItem( COMMON_ACTIONS::duplicate, SELECTION_CONDITIONS::NotEmpty ); m_selectionTool->AddMenuItem( COMMON_ACTIONS::createArray, SELECTION_CONDITIONS::NotEmpty ); + // Footprint actions + m_selectionTool->AddMenuItem( COMMON_ACTIONS::editFootprintInFpEditor, + SELECTION_CONDITIONS::OnlyType ( PCB_MODULE_T ) && + SELECTION_CONDITIONS::Count ( 1 ) ); + m_offset.x = 0; m_offset.y = 0; @@ -92,6 +101,22 @@ bool EDIT_TOOL::Init() } +bool EDIT_TOOL::invokeInlineRouter() +{ + TRACK* track = uniqueSelected(); + VIA* via = uniqueSelected(); + + if( track || via ) + { + //printf("Calling interactive drag\n"); + m_toolMgr->RunAction( COMMON_ACTIONS::routerInlineDrag, true ); + return true; + } + + return false; +} + + int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) { const SELECTION& selection = m_selectionTool->GetSelection(); @@ -99,11 +124,11 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) // Shall the selection be cleared at the end? bool unselect = selection.Empty(); - // Be sure that there is at least one item that we can modify - if( !makeSelection( selection ) ) + // Be sure that there is at least one item that we can modify. If nothing was selected before, + // try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection) + if( !hoverSelection( selection ) ) { setTransitions(); - return 0; } @@ -111,6 +136,8 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) m_dragging = false; // Are selected items being dragged? bool restore = false; // Should items' state be restored when finishing the tool? + bool lockOverride = false; + bool isDragAndDrop = false; // By default, modified items need to update their geometry m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY; @@ -118,12 +145,14 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) KIGFX::VIEW_CONTROLS* controls = getViewControls(); PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); controls->ShowCursor( true ); - controls->SetSnapping( true ); + //controls->SetSnapping( true ); controls->ForceCursorPosition( false ); // cumulative translation wxPoint totalMovement( 0, 0 ); + GRID_HELPER grid ( editFrame ); + // Main loop: keep receiving events while( OPT_TOOL_EVENT evt = Wait() ) { @@ -195,10 +224,16 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) else if( evt->IsAction( &COMMON_ACTIONS::editActivate ) || evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) { - m_cursor = controls->GetCursorPosition(); + VECTOR2I mousePos = evt->Position(); + + m_cursor = grid.Align( evt->Position() ); + isDragAndDrop = evt->IsDrag( BUT_LEFT ); if( m_dragging ) { + m_cursor = grid.BestSnapAnchor( evt->Position(), selection.Item( 0 ) ); + getViewControls()->ForceCursorPosition ( true, m_cursor ); + wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) - selection.Item( 0 )->GetPosition(); @@ -212,9 +247,19 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) } else // Prepare to start dragging { - if( m_selectionTool->CheckLock() || selection.Empty() ) + m_selectionTool->SanitizeSelection(); + + if( selection.Empty() ) break; + // deal with locked items (override lock or abort the operation) + SELECTION_LOCK_FLAGS lockFlags = m_selectionTool->CheckLock(); + + if( lockFlags == SELECTION_LOCKED ) + break; + else if( lockFlags == SELECTION_LOCK_OVERRIDE ) + lockOverride = true; + // Save items, so changes can be undone if( !isUndoInhibited() ) { @@ -222,26 +267,32 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED ); } + VECTOR2I origin; + + if( evt->IsDrag( BUT_LEFT ) ) + mousePos = evt->DragOrigin(); + + // origin = grid.Align ( evt->DragOrigin() ); + //else + origin = grid.Align( mousePos ); + if( selection.Size() == 1 ) { // Set the current cursor position to the first dragged item origin, so the // movement vector could be computed later - m_cursor = VECTOR2I( selection.Item( 0 )->GetPosition() ); - m_offset.x = 0; - m_offset.y = 0; + m_cursor = grid.BestDragOrigin( mousePos, selection.Item( 0 ) ); + getViewControls()->ForceCursorPosition( true, m_cursor ); + grid.SetAuxAxes( true, m_cursor ); + + VECTOR2I o = VECTOR2I( selection.Item( 0 )->GetPosition() ); + m_offset.x = o.x - m_cursor.x; + m_offset.y = o.y - m_cursor.y; } else { - VECTOR2D origin; - - if( evt->IsDrag( BUT_LEFT ) ) - origin = getView()->GetGAL()->GetGridPoint( evt->DragOrigin() ); - else - origin = getViewControls()->GetCursorPosition(); - - // Update dragging offset (distance between cursor and the first dragged item) m_offset = static_cast( selection.items.GetPickedItem( 0 ) )->GetPosition() - wxPoint( origin.x, origin.y ); + getViewControls()->ForceCursorPosition( true, origin ); } controls->SetAutoPan( true ); @@ -254,7 +305,12 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) } else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) - break; // Finish + { + if( !isDragAndDrop || !lockOverride ) + break; // Finish + + lockOverride = false; + } } if( m_dragging ) @@ -284,7 +340,7 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) ratsnest->Recalculate(); controls->ShowCursor( false ); - controls->SetSnapping( false ); + //controls->SetSnapping( false ); controls->SetAutoPan( false ); setTransitions(); @@ -298,7 +354,7 @@ int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent ) const SELECTION& selection = m_selectionTool->GetSelection(); PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); - if( !makeSelection( selection ) ) + if( !hoverSelection( selection, false ) ) { setTransitions(); @@ -311,22 +367,6 @@ int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent ) // Display properties dialog BOARD_ITEM* item = selection.Item( 0 ); - // Check if user wants to edit pad or module properties - if( item->Type() == PCB_MODULE_T ) - { - VECTOR2D cursor = getViewControls()->GetCursorPosition(); - - for( D_PAD* pad = static_cast( item )->Pads(); pad; pad = pad->Next() ) - { - if( pad->ViewBBox().Contains( cursor ) ) - { - // Turns out that user wants to edit a pad properties - item = pad; - break; - } - } - } - std::vector& undoList = editFrame->GetScreen()->m_UndoList.m_CommandsList; // Some of properties dialogs alter pointers, so we should deselect them @@ -370,7 +410,7 @@ int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent ) // Shall the selection be cleared at the end? bool unselect = selection.Empty(); - if( !makeSelection( selection ) || m_selectionTool->CheckLock() ) + if( !hoverSelection( selection ) ) { setTransitions(); @@ -425,7 +465,7 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent ) // Shall the selection be cleared at the end? bool unselect = selection.Empty(); - if( !makeSelection( selection ) || m_selectionTool->CheckLock() ) + if( !hoverSelection( selection ) ) { setTransitions(); @@ -475,7 +515,7 @@ int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent ) { const SELECTION& selection = m_selectionTool->GetSelection(); - if( !makeSelection( selection ) || m_selectionTool->CheckLock() ) + if( !hoverSelection( selection ) ) { setTransitions(); @@ -531,40 +571,58 @@ void EDIT_TOOL::remove( BOARD_ITEM* aItem ) // Default removal procedure case PCB_MODULE_TEXT_T: { - if( m_editModules ) - { - TEXTE_MODULE* text = static_cast( aItem ); + TEXTE_MODULE* text = static_cast( aItem ); - switch( text->GetType() ) - { + switch( text->GetType() ) + { case TEXTE_MODULE::TEXT_is_REFERENCE: - DisplayError( getEditFrame(), _( "Cannot delete REFERENCE!" ) ); + DisplayError( getEditFrame(), _( "Cannot delete component reference." ) ); return; case TEXTE_MODULE::TEXT_is_VALUE: - DisplayError( getEditFrame(), _( "Cannot delete VALUE!" ) ); + DisplayError( getEditFrame(), _( "Cannot delete component value." ) ); return; case TEXTE_MODULE::TEXT_is_DIVERS: // suppress warnings break; - } } - } - /* no break */ - case PCB_PAD_T: - case PCB_MODULE_EDGE_T: if( m_editModules ) { MODULE* module = static_cast( aItem->GetParent() ); module->SetLastEditTime(); - board->m_Status_Pcb = 0; // it is done in the legacy view aItem->DeleteStructure(); } return; - break; + } + + case PCB_PAD_T: + case PCB_MODULE_EDGE_T: + { + MODULE* module = static_cast( aItem->GetParent() ); + module->SetLastEditTime(); + + board->m_Status_Pcb = 0; // it is done in the legacy view + + + if( !m_editModules ) + { + if( aItem->Type() == PCB_PAD_T && module->GetPadCount() == 1 ) + { + DisplayError( getEditFrame(), _( "Cannot delete the only remaining pad of the module (modules on PCB must have at least one pad)." ) ); + return; + } + + getView()->Remove( aItem ); + board->Remove( aItem ); + } + + aItem->DeleteStructure(); + + return; + } case PCB_LINE_T: // a segment not on copper layers case PCB_TEXT_T: // a text on a layer @@ -580,7 +638,6 @@ void EDIT_TOOL::remove( BOARD_ITEM* aItem ) default: // other types do not need to (or should not) be handled assert( false ); return; - break; } getView()->Remove( aItem ); @@ -595,7 +652,7 @@ int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent ) // Shall the selection be cleared at the end? bool unselect = selection.Empty(); - if( !makeSelection( selection ) || m_selectionTool->CheckLock() ) + if( !hoverSelection( selection ) ) { setTransitions(); @@ -661,7 +718,7 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent ) const SELECTION& selection = selTool->GetSelection(); // Be sure that there is at least one item that we can modify - if( !makeSelection( selection ) || selTool->CheckLock() ) + if( !hoverSelection( selection ) ) { setTransitions(); return 0; @@ -745,7 +802,7 @@ int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent ) const SELECTION& selection = selTool->GetSelection(); // Be sure that there is at least one item that we can modify - if( !makeSelection( selection ) || selTool->CheckLock() ) + if( !hoverSelection( selection ) ) { setTransitions(); return 0; @@ -913,6 +970,7 @@ void EDIT_TOOL::setTransitions() Go( &EDIT_TOOL::Duplicate, COMMON_ACTIONS::duplicate.MakeEvent() ); Go( &EDIT_TOOL::Duplicate, COMMON_ACTIONS::duplicateIncrement.MakeEvent() ); Go( &EDIT_TOOL::CreateArray,COMMON_ACTIONS::createArray.MakeEvent() ); + Go( &EDIT_TOOL::editFootprintInFpEditor, COMMON_ACTIONS::editFootprintInFpEditor.MakeEvent() ); } @@ -922,6 +980,7 @@ void EDIT_TOOL::updateRatsnest( bool aRedraw ) RN_DATA* ratsnest = getModel()->GetRatsnest(); ratsnest->ClearSimple(); + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) { BOARD_ITEM* item = selection.Item( i ); @@ -952,11 +1011,25 @@ wxPoint EDIT_TOOL::getModificationPoint( const SELECTION& aSelection ) } -bool EDIT_TOOL::makeSelection( const SELECTION& aSelection ) +bool EDIT_TOOL::hoverSelection( const SELECTION& aSelection, bool aSanitize ) { if( aSelection.Empty() ) // Try to find an item that could be modified + { m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true ); + if( m_selectionTool->CheckLock() == SELECTION_LOCKED ) + { + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + return false; + } + } + + if( aSanitize ) + m_selectionTool->SanitizeSelection(); + + if( aSelection.Empty() ) + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + return !aSelection.Empty(); } @@ -998,3 +1071,33 @@ void EDIT_TOOL::processChanges( const PICKED_ITEMS_LIST* aList ) } } } + + +int EDIT_TOOL::editFootprintInFpEditor( const TOOL_EVENT& aEvent ) +{ + MODULE* mod = uniqueSelected(); + + if( !mod ) + return 0; + + PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); + + editFrame-> SetCurItem( mod ); + + if( editFrame->GetCurItem()->GetTimeStamp() == 0 ) // Module Editor needs a non null timestamp + { + editFrame->GetCurItem()->SetTimeStamp( GetNewTimeStamp() ); + editFrame->OnModify(); + } + + FOOTPRINT_EDIT_FRAME* editor = (FOOTPRINT_EDIT_FRAME*) editFrame->Kiway().Player( FRAME_PCB_MODULE_EDITOR, true ); + + editor->Load_Module_From_BOARD( (MODULE*) editFrame->GetCurItem() ); + editFrame->SetCurItem( NULL ); // the current module could be deleted by + + editor->Show( true ); + editor->Raise(); // Iconize( false ); + + setTransitions(); + return 0; +} diff --git a/pcbnew/tools/edit_tool.cpp.orig b/pcbnew/tools/edit_tool.cpp.orig new file mode 100644 index 0000000000..40163ea7d0 --- /dev/null +++ b/pcbnew/tools/edit_tool.cpp.orig @@ -0,0 +1,1109 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common_actions.h" +#include "selection_tool.h" +#include "edit_tool.h" +#include "grid_helper.h" + +#include +#include + +EDIT_TOOL::EDIT_TOOL() : + TOOL_INTERACTIVE( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ), + m_dragging( false ), m_editModules( false ), m_undoInhibit( 0 ), + m_updateFlag( KIGFX::VIEW_ITEM::NONE ) +{ +} + + +void EDIT_TOOL::Reset( RESET_REASON aReason ) +{ + m_dragging = false; + m_updateFlag = KIGFX::VIEW_ITEM::NONE; +} + + +bool EDIT_TOOL::Init() +{ + // Find the selection tool, so they can cooperate + m_selectionTool = static_cast( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) ); + + if( !m_selectionTool ) + { + DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) ); + return false; + } + + // Add context menu entries that are displayed when selection tool is active + m_selectionTool->AddMenuItem( COMMON_ACTIONS::editActivate, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::rotate, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::flip, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::remove, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::properties, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::duplicate, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::createArray, SELECTION_CONDITIONS::NotEmpty ); + + // Footprint actions + m_selectionTool->AddMenuItem( COMMON_ACTIONS::editFootprintInFpEditor, + SELECTION_CONDITIONS::OnlyType ( PCB_MODULE_T ) && + SELECTION_CONDITIONS::Count ( 1 ) ); + + m_offset.x = 0; + m_offset.y = 0; + + setTransitions(); + + return true; +} + +bool EDIT_TOOL::invokeInlineRouter() +{ + TRACK *track = uniqueSelected(); + VIA *via = uniqueSelected(); + + if( track || via ) + { + //printf("Calling interactive drag\n"); + m_toolMgr->RunAction( COMMON_ACTIONS::routerInlineDrag, true ); + return true; + } + + return false; +} + +int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + + // Shall the selection be cleared at the end? + bool unselect = selection.Empty(); + + // Be sure that there is at least one item that we can modify. If nothing was selected before, + // try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection) + if( !hoverSelection( selection ) ) + { + setTransitions(); + return 0; + } + + Activate(); + + m_dragging = false; // Are selected items being dragged? + bool restore = false; // Should items' state be restored when finishing the tool? + bool lockOverride = false; + bool isDragAndDrop = false; + + // By default, modified items need to update their geometry + m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY; + + KIGFX::VIEW_CONTROLS* controls = getViewControls(); + PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); + controls->ShowCursor( true ); + //controls->SetSnapping( true ); + controls->ForceCursorPosition( false ); + +<<<<<<< HEAD + // cumulative translation + wxPoint totalMovement( 0, 0 ); +======= + GRID_HELPER grid ( editFrame ); +>>>>>>> tom-dp-master + + // Main loop: keep receiving events + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() ) + { + restore = true; // Cancelling the tool means that items have to be restored + break; // Finish + } + + else if( evt->Action() == TA_UNDO_REDO ) + { + unselect = true; + break; + } + + // Dispatch TOOL_ACTIONs + else if( evt->Category() == TC_COMMAND ) + { + if( evt->IsAction( &COMMON_ACTIONS::rotate ) ) + { + Rotate( aEvent ); + } + else if( evt->IsAction( &COMMON_ACTIONS::flip ) ) + { + Flip( aEvent ); + + // Flip causes change of layers + enableUpdateFlag( KIGFX::VIEW_ITEM::LAYERS ); + } + else if( evt->IsAction( &COMMON_ACTIONS::remove ) ) + { + Remove( aEvent ); + + break; // exit the loop, as there is no further processing for removed items + } + else if( evt->IsAction( &COMMON_ACTIONS::duplicate ) ) + { + // On duplicate, stop moving this item + // The duplicate tool should then select the new item and start + // a new move procedure + break; + } + else if( evt->IsAction( &COMMON_ACTIONS::moveExact ) ) + { + // Can't do this, because the selection will then contain + // stale pointers and it will all go horribly wrong... + //editFrame->RestoreCopyFromUndoList( dummy ); + // + // So, instead, reset the position manually + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + item->SetPosition( item->GetPosition() - totalMovement ); + + // And what about flipping and rotation? + // for now, they won't be undone, but maybe that is how + // it should be, so you can flip and move exact in the + // same action? + } + + // This causes a double event, so we will get the dialogue + // correctly, somehow - why does Rotate not? + //MoveExact( aEvent ); + break; // exit the loop - we move exactly, so we have + // finished moving + } + } + + else if( evt->IsAction( &COMMON_ACTIONS::editActivate ) + || evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) + { + //if ( invokeInlineRouter ( ) ) + // break; + + VECTOR2I mousePos = evt->Position(); + + m_cursor = grid.Align( evt->Position() ); + isDragAndDrop = evt->IsDrag( BUT_LEFT ); + + if( m_dragging ) + { + m_cursor = grid.BestSnapAnchor( evt->Position(), selection.Item( 0 ) ); + getViewControls()->ForceCursorPosition ( true, m_cursor ); + + wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) - + selection.Item( 0 )->GetPosition(); + + totalMovement += movement; + + // Drag items to the current cursor position + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + selection.Item( i )->Move( movement + m_offset ); + + updateRatsnest( true ); + } + else // Prepare to start dragging + { + m_selectionTool->SanitizeSelection(); + + if( selection.Empty() ) + break; + + // deal with locked items (override lock or abort the operation) + SELECTION_LOCK_FLAGS lockFlags = m_selectionTool->CheckLock(); + + if( lockFlags == SELECTION_LOCKED ) + break; + else if( lockFlags == SELECTION_LOCK_OVERRIDE ) + lockOverride = true; + + // Save items, so changes can be undone + if( !isUndoInhibited() ) + { + editFrame->OnModify(); + editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED ); + } + + VECTOR2I origin; + + if( evt->IsDrag( BUT_LEFT ) ) + mousePos = evt->DragOrigin(); + + // origin = grid.Align ( evt->DragOrigin() ); + //else + origin = grid.Align( mousePos ); + + if( selection.Size() == 1 ) + { + // Set the current cursor position to the first dragged item origin, so the + // movement vector could be computed later + m_cursor = grid.BestDragOrigin( mousePos, selection.Item( 0 ) ); + getViewControls()->ForceCursorPosition ( true, m_cursor ); + grid.SetAuxAxes( true, m_cursor ); + + VECTOR2I o = VECTOR2I( selection.Item( 0 )->GetPosition() ); + m_offset.x = o.x - m_cursor.x; + m_offset.y = o.y - m_cursor.y; + } + else + { + m_offset = static_cast( selection.items.GetPickedItem( 0 ) )->GetPosition() - + wxPoint( origin.x, origin.y ); + getViewControls()->ForceCursorPosition( true, origin ); + } + + controls->SetAutoPan( true ); + m_dragging = true; + incUndoInhibit(); + } + + selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true ); + } + + else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) + { + if( !isDragAndDrop || !lockOverride ) + break; // Finish + + lockOverride = false; + } + } + + if( m_dragging ) + decUndoInhibit(); + + m_dragging = false; + m_offset.x = 0; + m_offset.y = 0; + + if( restore ) + { + // Modifications have to be rollbacked, so restore the previous state of items + wxCommandEvent dummy; + editFrame->RestoreCopyFromUndoList( dummy ); + } + else + { + // Changes are applied, so update the items + selection.group->ItemsViewUpdate( m_updateFlag ); + } + + if( unselect ) + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + RN_DATA* ratsnest = getModel()->GetRatsnest(); + ratsnest->ClearSimple(); + ratsnest->Recalculate(); + + controls->ShowCursor( false ); + //controls->SetSnapping( false ); + controls->SetAutoPan( false ); + + setTransitions(); + + return 0; +} + + +int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); + + if( !hoverSelection( selection, false ) ) + { + setTransitions(); + + return 0; + } + + // Properties are displayed when there is only one item selected + if( selection.Size() == 1 ) + { + // Display properties dialog + BOARD_ITEM* item = selection.Item( 0 ); + + std::vector& undoList = editFrame->GetScreen()->m_UndoList.m_CommandsList; + + // Some of properties dialogs alter pointers, so we should deselect them + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + STATUS_FLAGS flags = item->GetFlags(); + item->ClearFlags(); + + // It is necessary to determine if anything has changed + PICKED_ITEMS_LIST* lastChange = undoList.empty() ? NULL : undoList.back(); + + // Display properties dialog + editFrame->OnEditItemRequest( NULL, item ); + + PICKED_ITEMS_LIST* currentChange = undoList.empty() ? NULL : undoList.back(); + + if( lastChange != currentChange ) // Something has changed + { + processChanges( currentChange ); + + updateRatsnest( true ); + getModel()->GetRatsnest()->Recalculate(); + item->ViewUpdate(); + + m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true ); + } + + item->SetFlags( flags ); + } + + setTransitions(); + + return 0; +} + + +int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); + + // Shall the selection be cleared at the end? + bool unselect = selection.Empty(); + + if( !hoverSelection( selection ) ) + { + setTransitions(); + + return 0; + } + + wxPoint rotatePoint = getModificationPoint( selection ); + + // If it is being dragged, then it is already saved with UR_CHANGED flag + if( !isUndoInhibited() ) + { + editFrame->OnModify(); + editFrame->SaveCopyInUndoList( selection.items, UR_ROTATED, rotatePoint ); + } + + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + item->Rotate( rotatePoint, editFrame->GetRotationAngle() ); + + if( !m_dragging ) + item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + } + + updateRatsnest( m_dragging ); + + // Update dragging offset (distance between cursor and the first dragged item) + m_offset = static_cast( selection.items.GetPickedItem( 0 ) )->GetPosition() - + rotatePoint; + + if( m_dragging ) + selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + else + getModel()->GetRatsnest()->Recalculate(); + + if( unselect ) + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true ); + setTransitions(); + + return 0; +} + + +int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + PCB_BASE_FRAME* editFrame = getEditFrame(); + + // Shall the selection be cleared at the end? + bool unselect = selection.Empty(); + + if( !hoverSelection( selection ) ) + { + setTransitions(); + + return 0; + } + + wxPoint flipPoint = getModificationPoint( selection ); + + if( !isUndoInhibited() ) // If it is being dragged, then it is already saved with UR_CHANGED flag + { + editFrame->OnModify(); + editFrame->SaveCopyInUndoList( selection.items, UR_FLIPPED, flipPoint ); + } + + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + item->Flip( flipPoint ); + + if( !m_dragging ) + item->ViewUpdate( KIGFX::VIEW_ITEM::LAYERS ); + } + + updateRatsnest( m_dragging ); + + // Update dragging offset (distance between cursor and the first dragged item) + m_offset = static_cast( selection.items.GetPickedItem( 0 ) )->GetPosition() - + flipPoint; + + if( m_dragging ) + selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + else + getModel()->GetRatsnest()->Recalculate(); + + if( unselect ) + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true ); + setTransitions(); + + return 0; +} + + +int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + + if( !hoverSelection( selection ) ) + { + setTransitions(); + + return 0; + } + + // Get a copy of the selected items set + PICKED_ITEMS_LIST selectedItems = selection.items; + PCB_BASE_FRAME* editFrame = getEditFrame(); + + // As we are about to remove items, they have to be removed from the selection first + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + // Save them + for( unsigned int i = 0; i < selectedItems.GetCount(); ++i ) + selectedItems.SetPickedItemStatus( UR_DELETED, i ); + + editFrame->OnModify(); + editFrame->SaveCopyInUndoList( selectedItems, UR_DELETED ); + + // And now remove + for( unsigned int i = 0; i < selectedItems.GetCount(); ++i ) + remove( static_cast( selectedItems.GetPickedItem( i ) ) ); + + getModel()->GetRatsnest()->Recalculate(); + + setTransitions(); + + return 0; +} + + +void EDIT_TOOL::remove( BOARD_ITEM* aItem ) +{ + BOARD* board = getModel(); + + switch( aItem->Type() ) + { + case PCB_MODULE_T: + { + MODULE* module = static_cast( aItem ); + module->ClearFlags(); + module->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, getView(), _1 ) ); + + // Module itself is deleted after the switch scope is finished + // list of pads is rebuild by BOARD::BuildListOfNets() + + // Clear flags to indicate, that the ratsnest, list of nets & pads are not valid anymore + board->m_Status_Pcb = 0; + } + break; + + // Default removal procedure + case PCB_MODULE_TEXT_T: + { + TEXTE_MODULE* text = static_cast( aItem ); + + switch( text->GetType() ) + { + case TEXTE_MODULE::TEXT_is_REFERENCE: + DisplayError( getEditFrame(), _( "Cannot delete component reference." ) ); + return; + + case TEXTE_MODULE::TEXT_is_VALUE: + DisplayError( getEditFrame(), _( "Cannot delete component value." ) ); + return; + + case TEXTE_MODULE::TEXT_is_DIVERS: // suppress warnings + break; + } + + if( m_editModules ) + { + MODULE* module = static_cast( aItem->GetParent() ); + module->SetLastEditTime(); + board->m_Status_Pcb = 0; // it is done in the legacy view + aItem->DeleteStructure(); + } + + return; + } + + case PCB_PAD_T: + case PCB_MODULE_EDGE_T: + { + MODULE* module = static_cast( aItem->GetParent() ); + module->SetLastEditTime(); + + board->m_Status_Pcb = 0; // it is done in the legacy view + + + if( !m_editModules ) + { + if( aItem->Type() == PCB_PAD_T && module->GetPadCount() == 1 ) + { + DisplayError( getEditFrame(), _( "Cannot delete the only remaining pad of the module (modules on PCB must have at least one pad)." ) ); + return; + } + + getView()->Remove( aItem ); + board->Remove( aItem ); + } + + aItem->DeleteStructure(); + + return; + } + + case PCB_LINE_T: // a segment not on copper layers + case PCB_TEXT_T: // a text on a layer + case PCB_TRACE_T: // a track segment (segment on a copper layer) + case PCB_VIA_T: // a via (like track segment on a copper layer) + case PCB_DIMENSION_T: // a dimension (graphic item) + case PCB_TARGET_T: // a target (graphic item) + case PCB_MARKER_T: // a marker used to show something + case PCB_ZONE_T: // SEG_ZONE items are now deprecated + case PCB_ZONE_AREA_T: + break; + + default: // other types do not need to (or should not) be handled + assert( false ); + return; + } + + getView()->Remove( aItem ); + board->Remove( aItem ); +} + + +int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + + // Shall the selection be cleared at the end? + bool unselect = selection.Empty(); + + if( !makeSelection( selection ) || m_selectionTool->CheckLock() ) + { + setTransitions(); + + return 0; + } + + wxPoint translation; + double rotation = 0; + + PCB_BASE_FRAME* editFrame = getEditFrame(); + + DIALOG_MOVE_EXACT dialog( editFrame, translation, rotation ); + int ret = dialog.ShowModal(); + + if( ret == wxID_OK ) + { + if( !isUndoInhibited() ) + { + editFrame->OnModify(); + // Record an action of move and rotate + editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED ); + } + + VECTOR2I rp = selection.GetCenter(); + wxPoint rotPoint( rp.x, rp.y ); + + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + item->Move( translation ); + item->Rotate( rotPoint, rotation ); + + if( !m_dragging ) + item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + } + + updateRatsnest( m_dragging ); + + if( m_dragging ) + selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + else + getModel()->GetRatsnest()->Recalculate(); + + if( unselect ) + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true ); + } + + setTransitions(); + + return 0; +} + + +int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent ) +{ + bool increment = aEvent.IsAction( &COMMON_ACTIONS::duplicateIncrement ); + + // first, check if we have a selection, or try to get one + SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + const SELECTION& selection = selTool->GetSelection(); + + // Be sure that there is at least one item that we can modify + if( !makeSelection( selection ) || selTool->CheckLock() ) + { + setTransitions(); + return 0; + } + + // we have a selection to work on now, so start the tool process + + PCB_BASE_FRAME* editFrame = getEditFrame(); + editFrame->OnModify(); + + // prevent other tools making undo points while the duplicate is going on + // so that if you cancel, you don't get a duplicate object hiding over + // the original + incUndoInhibit(); + + std::vector old_items; + + for( int i = 0; i < selection.Size(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + if( item ) + old_items.push_back( item ); + } + + for( unsigned i = 0; i < old_items.size(); ++i ) + { + BOARD_ITEM* item = old_items[i]; + + // Unselect the item, so we won't pick it up again + // Do this first, so a single-item duplicate will correctly call + // SetCurItem and show the item properties + m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, item ); + + BOARD_ITEM* new_item = NULL; + + if( m_editModules ) + new_item = editFrame->GetBoard()->m_Modules->DuplicateAndAddItem( item, increment ); + else + new_item = editFrame->GetBoard()->DuplicateAndAddItem( item, increment ); + + if( new_item ) + { + if( new_item->Type() == PCB_MODULE_T ) + { + static_cast( new_item )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, + getView(), _1 ) ); + } + + editFrame->GetGalCanvas()->GetView()->Add( new_item ); + + // Select the new item, so we can pick it up + m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, new_item ); + } + } + + // record the new items as added + editFrame->SaveCopyInUndoList( selection.items, UR_NEW ); + + editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ), + (int) old_items.size() ) ); + + // pick up the selected item(s) and start moving + // this works well for "dropping" copies around + TOOL_EVENT evt = COMMON_ACTIONS::editActivate.MakeEvent(); + Main( evt ); + + // and re-enable undos + decUndoInhibit(); + + setTransitions(); + + return 0; +} + + +int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent ) +{ + // first, check if we have a selection, or try to get one + SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + const SELECTION& selection = selTool->GetSelection(); + + // Be sure that there is at least one item that we can modify + if( !makeSelection( selection ) || selTool->CheckLock() ) + { + setTransitions(); + return 0; + } + + bool originalItemsModified = false; + + // we have a selection to work on now, so start the tool process + + PCB_BASE_FRAME* editFrame = getEditFrame(); + editFrame->OnModify(); + + if( m_editModules ) + { + // Module editors do their undo point upfront for the whole module + editFrame->SaveCopyInUndoList( editFrame->GetBoard()->m_Modules, UR_MODEDIT ); + } + else + { + // We may also change the original item + editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED ); + } + + DIALOG_CREATE_ARRAY::ARRAY_OPTIONS* array_opts = NULL; + + VECTOR2I rp = selection.GetCenter(); + const wxPoint rotPoint( rp.x, rp.y ); + + DIALOG_CREATE_ARRAY dialog( editFrame, rotPoint, &array_opts ); + int ret = dialog.ShowModal(); + + if( ret == DIALOG_CREATE_ARRAY::CREATE_ARRAY_OK && array_opts != NULL ) + { + PICKED_ITEMS_LIST newItemList; + + for( int i = 0; i < selection.Size(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + if( !item ) + continue; + + wxString cachedString; + + if( item->Type() == PCB_MODULE_T ) + { + cachedString = static_cast( item )->GetReferencePrefix(); + } + else if( EDA_TEXT* text = dynamic_cast( item ) ) + { + // Copy the text (not just take a reference + cachedString = text->GetText(); + } + + // iterate across the array, laying out the item at the + // correct position + const unsigned nPoints = array_opts->GetArraySize(); + + for( unsigned ptN = 0; ptN < nPoints; ++ptN ) + { + BOARD_ITEM* newItem = NULL; + + if( ptN == 0 ) + { + newItem = item; + } + else + { + // if renumbering, no need to increment + const bool increment = !array_opts->ShouldRenumberItems(); + + if( m_editModules ) + newItem = editFrame->GetBoard()->m_Modules->DuplicateAndAddItem( item, increment ); + else + newItem = editFrame->GetBoard()->DuplicateAndAddItem( item, increment ); + + if( newItem ) + { + array_opts->TransformItem( ptN, newItem, rotPoint ); + + m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, newItem ); + + newItemList.PushItem( newItem ); + + if( newItem->Type() == PCB_MODULE_T) + { + static_cast( newItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, + getView(), _1 ) ); + } + + editFrame->GetGalCanvas()->GetView()->Add( newItem ); + getModel()->GetRatsnest()->Update( newItem ); + } + } + + // set the number if needed: + if( array_opts->ShouldRenumberItems() ) + { + switch( newItem->Type() ) + { + case PCB_PAD_T: + { + const wxString padName = array_opts->GetItemNumber( ptN ); + static_cast( newItem )->SetPadName( padName ); + + originalItemsModified = true; + break; + } + case PCB_MODULE_T: + { + const wxString moduleName = array_opts->GetItemNumber( ptN ); + MODULE* module = static_cast( newItem ); + module->SetReference( cachedString + moduleName ); + + originalItemsModified = true; + break; + } + case PCB_MODULE_TEXT_T: + case PCB_TEXT_T: + { + EDA_TEXT* text = dynamic_cast( newItem ); + text->SetText( array_opts->InterpolateNumberIntoString( ptN, cachedString ) ); + + originalItemsModified = true; + break; + } + default: + // no renumbering of other items + break; + } + } + } + } + + if( !m_editModules ) + { + if( originalItemsModified ) + { + // Update the appearance of the original items + selection.group->ItemsViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + } + + // Add all items as a single undo point for PCB editors + // TODO: Can this be merged into the previous undo point (where + // we saved the original items) + editFrame->SaveCopyInUndoList( newItemList, UR_NEW ); + } + } + + getModel()->GetRatsnest()->Recalculate(); + setTransitions(); + + return 0; +} + + +void EDIT_TOOL::setTransitions() +{ + Go( &EDIT_TOOL::Main, COMMON_ACTIONS::editActivate.MakeEvent() ); + Go( &EDIT_TOOL::Rotate, COMMON_ACTIONS::rotate.MakeEvent() ); + Go( &EDIT_TOOL::Flip, COMMON_ACTIONS::flip.MakeEvent() ); + Go( &EDIT_TOOL::Remove, COMMON_ACTIONS::remove.MakeEvent() ); + Go( &EDIT_TOOL::Properties, COMMON_ACTIONS::properties.MakeEvent() ); +<<<<<<< HEAD + Go( &EDIT_TOOL::MoveExact, COMMON_ACTIONS::moveExact.MakeEvent() ); + Go( &EDIT_TOOL::Duplicate, COMMON_ACTIONS::duplicate.MakeEvent() ); + Go( &EDIT_TOOL::Duplicate, COMMON_ACTIONS::duplicateIncrement.MakeEvent() ); + Go( &EDIT_TOOL::CreateArray,COMMON_ACTIONS::createArray.MakeEvent() ); +======= + + Go( &EDIT_TOOL::editFootprintInFpEditor, COMMON_ACTIONS::editFootprintInFpEditor.MakeEvent() ); +>>>>>>> tom-dp-master +} + + +void EDIT_TOOL::updateRatsnest( bool aRedraw ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + RN_DATA* ratsnest = getModel()->GetRatsnest(); + + ratsnest->ClearSimple(); + + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + ratsnest->Update( item ); + + if( aRedraw ) + ratsnest->AddSimple( item ); + } +} + + +wxPoint EDIT_TOOL::getModificationPoint( const SELECTION& aSelection ) +{ + if( aSelection.Size() == 1 ) + { + return aSelection.Item( 0 )->GetPosition() - m_offset; + } + else + { + // If EDIT_TOOL is not currently active then it means that the cursor position is not + // updated, so we have to fetch the latest value + if( m_toolMgr->GetCurrentToolId() != m_toolId ) + m_cursor = getViewControls()->GetCursorPosition(); + + return wxPoint( m_cursor.x, m_cursor.y ); + } +} + +bool EDIT_TOOL::hoverSelection( const SELECTION& aSelection, bool aSanitize ) +{ + if( aSelection.Empty() ) // Try to find an item that could be modified + { + m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true ); + + if( m_selectionTool->CheckLock() == SELECTION_LOCKED ) + { + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + return false; + } + } + + if( aSanitize ) + m_selectionTool->SanitizeSelection(); + + if( aSelection.Empty() ) + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + return !aSelection.Empty(); +} + +void EDIT_TOOL::processChanges( const PICKED_ITEMS_LIST* aList ) +{ + for( unsigned int i = 0; i < aList->GetCount(); ++i ) + { + UNDO_REDO_T operation = aList->GetPickedItemStatus( i ); + EDA_ITEM* updItem = aList->GetPickedItem( i ); + + switch( operation ) + { + case UR_CHANGED: + case UR_MODEDIT: + updItem->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + break; + + case UR_DELETED: + if( updItem->Type() == PCB_MODULE_T ) + static_cast( updItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, + getView(), _1 ) ); + + getView()->Remove( updItem ); + break; + + case UR_NEW: + if( updItem->Type() == PCB_MODULE_T ) + static_cast( updItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, + getView(), _1 ) ); + + getView()->Add( updItem ); + updItem->ViewUpdate(); + break; + + default: + assert( false ); // Not handled + break; + } + } +} + + +int EDIT_TOOL::editFootprintInFpEditor( const TOOL_EVENT& aEvent ) +{ + MODULE *mod = uniqueSelected(); + + if( !mod ) + return 0; + + PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); + + editFrame-> SetCurItem( mod ); + + if( editFrame->GetCurItem()->GetTimeStamp() == 0 ) // Module Editor needs a non null timestamp + { + editFrame->GetCurItem()->SetTimeStamp( GetNewTimeStamp() ); + editFrame->OnModify(); + } + + FOOTPRINT_EDIT_FRAME* editor = (FOOTPRINT_EDIT_FRAME*) editFrame->Kiway().Player( FRAME_PCB_MODULE_EDITOR, true ); + + editor->Load_Module_From_BOARD( (MODULE*) editFrame->GetCurItem() ); + editFrame->SetCurItem( NULL ); // the current module could be deleted by + + editor->Show( true ); + editor->Raise(); // Iconize( false ); + + setTransitions(); + return 0; +} diff --git a/pcbnew/tools/edit_tool.h b/pcbnew/tools/edit_tool.h index ce2e9d957f..049632bda8 100644 --- a/pcbnew/tools/edit_tool.h +++ b/pcbnew/tools/edit_tool.h @@ -172,7 +172,7 @@ private: ///> If there are no items currently selected, it tries to choose the item that is under ///> the cursor or displays a disambiguation menu if there are multpile items. - bool makeSelection( const SELECTION& aSelection ); + bool hoverSelection( const SELECTION& aSelection, bool aSanitize = true ); ///> Updates view with the changes in the list. void processChanges( const PICKED_ITEMS_LIST* aList ); @@ -210,6 +210,21 @@ private: { return m_undoInhibit > 0; } + + int editFootprintInFpEditor( const TOOL_EVENT& aEvent ); + + bool invokeInlineRouter(); + + template T* uniqueSelected() + { + const SELECTION& selection = m_selectionTool->GetSelection(); + + if( selection.items.GetCount() > 1 ) + return NULL; + + BOARD_ITEM* item = selection.Item( 0 ); + return dyn_cast( item ); + } }; #endif diff --git a/pcbnew/tools/edit_tool.h.orig b/pcbnew/tools/edit_tool.h.orig new file mode 100644 index 0000000000..7c0fdf33e2 --- /dev/null +++ b/pcbnew/tools/edit_tool.h.orig @@ -0,0 +1,231 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __EDIT_TOOL_H +#define __EDIT_TOOL_H + +#include +#include +#include + +class BOARD_ITEM; +class SELECTION_TOOL; + +namespace KIGFX +{ +class VIEW_GROUP; +} + +/** + * Class EDIT_TOOL + * + * The interactive edit tool. Allows to move, rotate, flip and change properties of items selected + * using the pcbnew.InteractiveSelection tool. + */ + +class EDIT_TOOL : public TOOL_INTERACTIVE +{ +public: + EDIT_TOOL(); + + /// @copydoc TOOL_INTERACTIVE::Reset() + void Reset( RESET_REASON aReason ); + + /// @copydoc TOOL_INTERACTIVE::Init() + bool Init(); + + /** + * Function Main() + * + * Main loop in which events are handled. + * @param aEvent is the handled event. + */ + int Main( const TOOL_EVENT& aEvent ); + + /** + * Function Edit() + * + * Displays properties window for the selected object. + */ + int Properties( const TOOL_EVENT& aEvent ); + + /** + * Function Rotate() + * + * Rotates currently selected items. + */ + int Rotate( const TOOL_EVENT& aEvent ); + + /** + * Function Flip() + * + * Rotates currently selected items. The rotation point is the current cursor position. + */ + int Flip( const TOOL_EVENT& aEvent ); + + /** + * Function Remove() + * + * Deletes currently selected items. The rotation point is the current cursor position. + */ + int Remove( const TOOL_EVENT& aEvent ); + + /** + * Function Duplicate() + * + * Duplicates a selection and starts a move action + */ + int Duplicate( const TOOL_EVENT& aEvent ); + + /** + * Function MoveExact() + * + * Invokes a dialog box to allow moving of the item by an exact amount. + */ + int MoveExact( const TOOL_EVENT& aEvent ); + + /** + * Function CreateArray() + * + * Creates an array of the selected items, invoking the array editor dialog + * to set the array options + */ + int CreateArray( const TOOL_EVENT& aEvent ); + + /** + * Function EditModules() + * + * Toggles edit module mode. When enabled, one may select parts of modules individually + * (graphics, pads, etc.), so they can be modified. + * @param aEnabled decides if the mode should be enabled. + */ + void EditModules( bool aEnabled ) + { + m_editModules = aEnabled; + } + +private: + ///> Selection tool used for obtaining selected items + SELECTION_TOOL* m_selectionTool; + + ///> Flag determining if anything is being dragged right now + bool m_dragging; + + ///> Offset from the dragged item's center (anchor) + wxPoint m_offset; + + ///> Last cursor position (needed for getModificationPoint() to avoid changes + ///> of edit reference point). + VECTOR2I m_cursor; + + /// Edit module mode flag + bool m_editModules; + + /// Counter of undo inhibitions. When zero, undo is not inhibited. + int m_undoInhibit; + + ///> Removes and frees a single BOARD_ITEM. + void remove( BOARD_ITEM* aItem ); + + ///> Sets up handlers for various events. + void setTransitions(); + + ///> The required update flag for modified items + KIGFX::VIEW_ITEM::VIEW_UPDATE_FLAGS m_updateFlag; + + ///> Enables higher order update flag + void enableUpdateFlag( KIGFX::VIEW_ITEM::VIEW_UPDATE_FLAGS aFlag ) + { + if( m_updateFlag < aFlag ) + m_updateFlag = aFlag; + } + + ///> Updates ratsnest for selected items. + ///> @param aRedraw says if selected items should be drawn using the simple mode (e.g. one line + ///> per item). + void updateRatsnest( bool aRedraw ); + + ///> Returns the right modification point (e.g. for rotation), depending on the number of + ///> selected items. + wxPoint getModificationPoint( const SELECTION& aSelection ); + + ///> If there are no items currently selected, it tries to choose the item that is under + ///> the cursor or displays a disambiguation menu if there are multpile items. + bool hoverSelection( const SELECTION& aSelection, bool aSanitize = true ); + + ///> Updates view with the changes in the list. + void processChanges( const PICKED_ITEMS_LIST* aList ); + +<<<<<<< HEAD + /** + * Increments the undo inhibit counter. This will indicate that tools + * should not create an undo point, as another tool is doing it already, + * and considers that its operation is atomic, even if it calls another one + * (for example a duplicate calls a move). + */ + inline void incUndoInhibit() + { + m_undoInhibit++; + } + + /** + * Decrements the inhibit counter. An assert is raised if the counter drops + * below zero. + */ + inline void decUndoInhibit() + { + m_undoInhibit--; + + wxASSERT_MSG( m_undoInhibit >= 0, wxT( "Undo inhibit count decremented past zero" ) ); + } + + /** + * Report if the tool manager has been told at least once that undo + * points should not be created. This can be ignored if the undo point + * is still required. + * + * @return true if undo are inhibited + */ + inline bool isUndoInhibited() const + { + return m_undoInhibit > 0; +======= + int editFootprintInFpEditor( const TOOL_EVENT& aEvent ); + + bool invokeInlineRouter(); + + template T* uniqueSelected() + { + const SELECTION& selection = m_selectionTool->GetSelection(); + + if( selection.items.GetCount() > 1 ) + return NULL; + + BOARD_ITEM* item = selection.Item( 0 ); + return dyn_cast( item ); +>>>>>>> tom-dp-master + } +}; + +#endif diff --git a/pcbnew/tools/grid_helper.cpp b/pcbnew/tools/grid_helper.cpp new file mode 100644 index 0000000000..c141d16aaa --- /dev/null +++ b/pcbnew/tools/grid_helper.cpp @@ -0,0 +1,339 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "grid_helper.h" + +GRID_HELPER::GRID_HELPER( PCB_BASE_FRAME* aFrame ) : + m_frame( aFrame ) +{ +} + + +GRID_HELPER::~GRID_HELPER() +{ +} + + +void GRID_HELPER::SetGrid( int aSize ) +{ + assert( false ); +} + + +void GRID_HELPER::SetOrigin( const VECTOR2I& aOrigin ) +{ +} + + +VECTOR2I GRID_HELPER::GetGrid() +{ + PCB_SCREEN* screen = m_frame->GetScreen(); + + const wxRealPoint& size = screen->GetGridSize(); + + return VECTOR2I ( KiROUND( size.x ), KiROUND( size.y ) ); +} + + +VECTOR2I GRID_HELPER::GetOrigin() +{ + return VECTOR2I( 0, 0 ); +} + + +void GRID_HELPER::SetAuxAxes( bool aEnable, const VECTOR2I aOrigin, bool aEnableDiagonal ) +{ + if( aEnable ) + m_auxAxis = aOrigin; + else + m_auxAxis = boost::optional(); + + m_diagonalAuxAxesEnable = aEnable; +} + + +VECTOR2I GRID_HELPER::Align( const VECTOR2I& aPoint ) +{ + const VECTOR2D gridOffset( GetOrigin () ); + const VECTOR2D gridSize( GetGrid() ); + + VECTOR2I nearest( round( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x, + round( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y ); + + if( !m_auxAxis ) + return nearest; + + if( std::abs( m_auxAxis->x - aPoint.x) < std::abs( nearest.x - aPoint.x ) ) + nearest.x = m_auxAxis->x; + + if( std::abs( m_auxAxis->y - aPoint.y) < std::abs( nearest.y - aPoint.y ) ) + nearest.y = m_auxAxis->y; + + return nearest; +} + + +VECTOR2I GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos, BOARD_ITEM* aItem ) +{ + clearAnchors(); + computeAnchors( aItem, aMousePos ); + + double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale(); + double lineSnapMinCornerDistance = 50.0 / worldScale; + + ANCHOR* nearestOutline = nearestAnchor( aMousePos, OUTLINE, LSET::AllLayersMask() ); + ANCHOR* nearestCorner = nearestAnchor( aMousePos, CORNER, LSET::AllLayersMask() ); + ANCHOR* nearestOrigin = nearestAnchor( aMousePos, ORIGIN, LSET::AllLayersMask() ); + ANCHOR* best = NULL; + double minDist = std::numeric_limits::max(); + + if( nearestOrigin ) + { + minDist = nearestOrigin->Distance( aMousePos ); + best = nearestOrigin; + } + + if( nearestCorner ) + { + double dist = nearestCorner->Distance( aMousePos ); + if( dist < minDist ) + { + minDist = dist; + best = nearestCorner; + } + } + + if( nearestOutline ) + { + double dist = nearestOutline->Distance( aMousePos ); + if( minDist > lineSnapMinCornerDistance && dist < minDist ) + best = nearestOutline; + } + + return best ? best->pos : aMousePos; +} + + +std::set GRID_HELPER::queryVisible( const BOX2I& aArea ) +{ + std::set items; + + std::vector selectedItems; + std::vector::iterator it, it_end; + + m_frame->GetGalCanvas()->GetView()->Query( aArea, selectedItems ); // Get the list of selected items + + for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it ) + { + BOARD_ITEM* item = static_cast( it->first ); + if( item->ViewIsVisible() ) + items.insert ( item ); + } + + return items; +} + + +VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I &aOrigin, BOARD_ITEM* aDraggedItem ) +{ + double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale(); + int snapRange = (int) ( 100.0 / worldScale ); + + BOX2I bb( VECTOR2I( aOrigin.x - snapRange / 2, aOrigin.y - snapRange/2 ), VECTOR2I( snapRange, snapRange ) ); + + clearAnchors(); + + BOOST_FOREACH( BOARD_ITEM* item, queryVisible( bb ) ) + { + computeAnchors( item, aOrigin ); + } + + LSET layers( aDraggedItem->GetLayer() ); + ANCHOR* nearest = nearestAnchor( aOrigin, CORNER | SNAPPABLE, layers ); + + VECTOR2I nearestGrid = Align( aOrigin ); + double gridDist = ( nearestGrid - aOrigin ).EuclideanNorm(); + if( nearest ) + { + double snapDist = nearest->Distance( aOrigin ); + + if( nearest && snapDist < gridDist ) + return nearest->pos; + } + + return nearestGrid; +} + + +void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos ) +{ + VECTOR2I origin; + + switch( aItem->Type() ) + { + case PCB_MODULE_T: + { + MODULE* mod = static_cast( aItem ); + addAnchor( mod->GetPosition(), ORIGIN | SNAPPABLE, mod ); + + for( D_PAD* pad = mod->Pads(); pad; pad = pad->Next() ) + addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad ); + + break; + } + + case PCB_MODULE_EDGE_T: + case PCB_LINE_T: + { + DRAWSEGMENT* dseg = static_cast( aItem ); + VECTOR2I start = dseg->GetStart(); + VECTOR2I end = dseg->GetEnd(); + //LAYER_ID layer = dseg->GetLayer(); + + switch( dseg->GetShape() ) + { + case S_CIRCLE: + { + int r = ( start - end ).EuclideanNorm(); + + addAnchor( start, ORIGIN | SNAPPABLE, dseg ); + addAnchor( start + VECTOR2I ( -r, 0 ), OUTLINE | SNAPPABLE, dseg ); + addAnchor( start + VECTOR2I ( r, 0 ), OUTLINE | SNAPPABLE, dseg ); + addAnchor( start + VECTOR2I ( 0, -r ), OUTLINE | SNAPPABLE, dseg); + addAnchor( start + VECTOR2I ( 0, r ), OUTLINE | SNAPPABLE, dseg ); + break; + } + + case S_ARC: + { + origin = dseg->GetCenter(); + addAnchor( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg ); + addAnchor( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg ); + addAnchor( origin, ORIGIN | SNAPPABLE, dseg ); + break; + } + + case S_SEGMENT: + { + origin.x = start.x + ( start.x - end.x ) / 2; + origin.y = start.y + ( start.y - end.y ) / 2; + addAnchor( start, CORNER | SNAPPABLE, dseg ); + addAnchor( end, CORNER | SNAPPABLE, dseg ); + addAnchor( origin, ORIGIN, dseg ); + break; + } + + default: + { + origin = dseg->GetStart(); + addAnchor( origin, ORIGIN | SNAPPABLE, dseg ); + break; + } + } + break; + } + + case PCB_TRACE_T: + { + TRACK* track = static_cast( aItem ); + VECTOR2I start = track->GetStart(); + VECTOR2I end = track->GetEnd(); + origin.x = start.x + ( start.x - end.x ) / 2; + origin.y = start.y + ( start.y - end.y ) / 2; + addAnchor( start, CORNER | SNAPPABLE, track ); + addAnchor( end, CORNER | SNAPPABLE, track ); + addAnchor( origin, ORIGIN, track); + break; + } + + case PCB_ZONE_AREA_T: + { + const CPolyLine* outline = static_cast( aItem )->Outline(); + int cornersCount = outline->GetCornersCount(); + + SHAPE_LINE_CHAIN lc; + lc.SetClosed( true ); + + for( int i = 0; i < cornersCount; ++i ) + { + const VECTOR2I p ( outline->GetPos( i ) ); + addAnchor( p, CORNER, aItem ); + lc.Append( p ); + } + + addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem ); + + break; + } + + case PCB_MODULE_TEXT_T: + case PCB_TEXT_T: + addAnchor( aItem->GetPosition(), ORIGIN, aItem ); + default: + + break; + } +} + + +GRID_HELPER::ANCHOR* GRID_HELPER::nearestAnchor( VECTOR2I aPos, int aFlags, LSET aMatchLayers ) +{ + double minDist = std::numeric_limits::max(); + ANCHOR* best = NULL; + + BOOST_FOREACH( ANCHOR& a, m_anchors ) + { + if( !aMatchLayers[a.item->GetLayer()] ) + continue; + + if( ( aFlags & a.flags ) != aFlags ) + continue; + + double dist = a.Distance( aPos ); + + if( dist < minDist ) + { + minDist = dist; + best = &a; + } + } + + return best; +} diff --git a/pcbnew/tools/grid_helper.h b/pcbnew/tools/grid_helper.h new file mode 100644 index 0000000000..59435b5d1a --- /dev/null +++ b/pcbnew/tools/grid_helper.h @@ -0,0 +1,104 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __GRID_HELPER_H +#define __GRID_HELPER_H + +#include + +#include +#include + +#include + +class PCB_BASE_FRAME; + +class GRID_HELPER { +public: + + GRID_HELPER( PCB_BASE_FRAME* aFrame ); + ~GRID_HELPER(); + + void SetGrid( int aSize ); + void SetOrigin( const VECTOR2I& aOrigin ); + + VECTOR2I GetGrid(); + VECTOR2I GetOrigin(); + + void SetAuxAxes( bool aEnable, const VECTOR2I aOrigin = VECTOR2I( 0, 0 ), bool aEnableDiagonal = false ); + + VECTOR2I Align( const VECTOR2I& aPoint ); + + VECTOR2I BestDragOrigin ( const VECTOR2I &aMousePos, BOARD_ITEM* aItem ); + VECTOR2I BestSnapAnchor ( const VECTOR2I &aOrigin, BOARD_ITEM* aDraggedItem ); + +private: + enum ANCHOR_FLAGS { + CORNER = 0x1, + OUTLINE = 0x2, + SNAPPABLE = 0x4, + ORIGIN = 0x8 + }; + + struct ANCHOR + { + ANCHOR( VECTOR2I aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM* aItem = NULL ): + pos( aPos ), flags( aFlags ), item( aItem ) {} ; + + VECTOR2I pos; + int flags; + BOARD_ITEM* item; + + double Distance( const VECTOR2I& aP ) + { + return ( aP - pos ).EuclideanNorm(); + } + + bool CanSnapItem( const BOARD_ITEM* aItem ); + }; + + std::vector m_anchors; + + std::set queryVisible( const BOX2I& aArea ); + + void addAnchor( VECTOR2I aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM* aItem = NULL ) + { + m_anchors.push_back( ANCHOR( aPos, aFlags, aItem ) ); + } + + ANCHOR* nearestAnchor( VECTOR2I aPos, int aFlags, LSET aMatchLayers ); + + void computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos ); + + void clearAnchors () + { + m_anchors.clear(); + } + + PCB_BASE_FRAME* m_frame; + boost::optional m_auxAxis; + bool m_diagonalAuxAxesEnable; +}; + +#endif diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index dc9832a17d..c400d521bf 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2013 CERN + * Copyright (C) 2013-2015 CERN * @author Tomasz Wlostowski * @author Maciej Suminski * @@ -22,14 +22,19 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #include +#include + #include #include #include #include +#include +#include #include #include @@ -69,9 +74,12 @@ SELECTION_TOOL::~SELECTION_TOOL() void SELECTION_TOOL::Reset( RESET_REASON aReason ) { if( aReason == TOOL_BASE::MODEL_RELOAD ) + { // Remove pointers to the selected items from containers // without changing their properties (as they are already deleted) + m_selection.group->Clear(); m_selection.clear(); + } else // Restore previous properties of selected items and remove them from containers clearSelection(); @@ -250,16 +258,12 @@ void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem ) } -bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambiguation ) +bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aOnDrag ) { BOARD_ITEM* item; GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide(); GENERAL_COLLECTOR collector; - // Preferred types (they have the priority when if they are covered by a bigger item) - const KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, PCB_LINE_T, - PCB_MODULE_EDGE_T, PCB_MODULE_TEXT_T, EOT }; - if( m_editModules ) collector.Collect( getModel(), GENERAL_COLLECTOR::ModuleItems, wxPoint( aWhere.x, aWhere.y ), guide ); @@ -267,10 +271,19 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua collector.Collect( getModel(), GENERAL_COLLECTOR::AllBoardItems, wxPoint( aWhere.x, aWhere.y ), guide ); + bool anyCollected = collector.GetCount() != 0; + + // Remove unselectable items + for( int i = collector.GetCount() - 1; i >= 0; --i ) + { + if( !selectable( collector[i] ) || ( aOnDrag && collector[i]->IsLocked() ) ) + collector.Remove( i ); + } + switch( collector.GetCount() ) { case 0: - if( !m_additive ) + if( !m_additive && anyCollected ) clearSelection(); return false; @@ -280,22 +293,9 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua return true; - default: - // Remove unselectable items - for( int i = collector.GetCount() - 1; i >= 0; --i ) - { - if( !selectable( collector[i] ) ) - collector.Remove( i ); - } - - // Check if among the selection candidates there is only one instance of preferred type - item = prefer( collector, types ); - if( item ) - { - toggleSelection( item ); - - return true; - } + default: + // Apply some ugly heuristics to avoid disambiguation menus whenever possible + guessSelectionCandidates( collector ); // Let's see if there is still disambiguation in selection.. if( collector.GetCount() == 1 ) @@ -304,9 +304,11 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua return true; } - - else if( aAllowDisambiguation && collector.GetCount() > 1 ) + else if( collector.GetCount() > 1 ) { + if( aOnDrag ) + Wait ( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) ); + item = disambiguationMenu( &collector ); if( item ) @@ -354,6 +356,9 @@ bool SELECTION_TOOL::selectMultiple() if( evt->IsMouseUp( BUT_LEFT ) ) { + // End drawing the selection box + m_selArea->ViewSetVisible( false ); + // Mark items within the selection box as selected std::vector selectedItems; BOX2I selectionBox = m_selArea->ViewBBox(); @@ -408,10 +413,10 @@ void SELECTION_TOOL::setTransitions() } -bool SELECTION_TOOL::CheckLock() +SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock() { if( !m_locked || m_editModules ) - return false; + return SELECTION_UNLOCKED; bool containsLocked = false; @@ -438,18 +443,22 @@ bool SELECTION_TOOL::CheckLock() } } - if( containsLocked && - !IsOK( m_frame, _( "Selection contains locked items. Do you want to continue?" ) ) ) + if( containsLocked ) { - return true; + if ( IsOK( m_frame, _( "Selection contains locked items. Do you want to continue?" ) ) ) + { + m_locked = false; + return SELECTION_LOCK_OVERRIDE; + } + else + return SELECTION_LOCKED; } - + m_locked = false; - return false; + return SELECTION_UNLOCKED; } - int SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent ) { selectCursor( getView()->ToWorld( getViewControls()->GetMousePosition() ) ); @@ -562,8 +571,9 @@ void SELECTION_TOOL::clearSelection() { BOARD_ITEM* item = static_cast( *it ); - item->ViewSetVisible( true ); + item->ViewHide( false ); item->ClearSelected(); + item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ) ; } m_selection.clear(); @@ -732,12 +742,22 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const case PCB_MODULE_TEXT_T: if( m_multiple && !m_editModules ) return false; - break; + + return aItem->ViewIsVisible() && board->IsLayerVisible( aItem->GetLayer() ); // These are not selectable case PCB_MODULE_EDGE_T: case PCB_PAD_T: - return m_editModules; + { + if( m_multiple && !m_editModules ) + return false; + + MODULE* mod = static_cast( aItem )->GetParent(); + if( mod && mod->IsLocked() ) + return false; + + break; + } case NOT_USED: case TYPE_NOT_INIT: @@ -762,6 +782,14 @@ void SELECTION_TOOL::select( BOARD_ITEM* aItem ) module->RunOnChildren( boost::bind( &SELECTION_TOOL::selectVisually, this, _1 ) ); } + if( aItem->Type() == PCB_PAD_T ) + { + MODULE* module = static_cast( aItem->GetParent() ); + + if( m_selection.items.FindItem( module ) >= 0 ) + return; + } + selectVisually( aItem ); ITEM_PICKER picker( aItem ); m_selection.items.PushItem( picker ); @@ -811,7 +839,7 @@ void SELECTION_TOOL::selectVisually( BOARD_ITEM* aItem ) const m_selection.group->Add( aItem ); // Hide the original item, so it is shown only on overlay - aItem->ViewSetVisible( false ); + aItem->ViewHide( true ); aItem->SetSelected(); } @@ -821,8 +849,9 @@ void SELECTION_TOOL::unselectVisually( BOARD_ITEM* aItem ) const m_selection.group->Remove( aItem ); // Restore original item visibility - aItem->ViewSetVisible( true ); + aItem->ViewHide( false ); aItem->ClearSelected(); + aItem->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); } @@ -870,35 +899,315 @@ void SELECTION_TOOL::highlightNet( const VECTOR2I& aPoint ) } -BOARD_ITEM* SELECTION_TOOL::prefer( GENERAL_COLLECTOR& aCollector, const KICAD_T aTypes[] ) const +static double calcArea( BOARD_ITEM* aItem ) { - BOARD_ITEM* preferred = NULL; - - int typesNr = 0; - while( aTypes[typesNr++] != EOT ); // count number of types, excluding the sentinel (EOT) - - for( int i = 0; i < aCollector.GetCount(); ++i ) + switch( aItem -> Type() ) { - KICAD_T type = aCollector[i]->Type(); + case PCB_MODULE_T: + return static_cast ( aItem )->GetFootprintRect().GetArea(); - for( int j = 0; j < typesNr - 1; ++j ) // Check if the item's type is in our list + case PCB_TRACE_T: { - if( aTypes[j] == type ) + TRACK* t = static_cast( aItem ); + return ( t->GetWidth() + t->GetLength() ) * t->GetWidth(); + } + + default: + return aItem->GetBoundingBox().GetArea(); + } +} + + +static double calcMinArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType ) +{ + double best = std::numeric_limits::max(); + + if( !aCollector.GetCount() ) + return 0.0; + + for( int i = 0; i < aCollector.GetCount(); i++ ) + { + BOARD_ITEM* item = aCollector[i]; + if( item->Type() == aType ) + best = std::min( best, calcArea( item ) ); + + } + + return best; +} + + +static double calcMaxArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType ) +{ + double best = 0.0; + + for( int i = 0; i < aCollector.GetCount(); i++ ) + { + BOARD_ITEM* item = aCollector[i]; + if( item->Type() == aType ) + best = std::max(best, calcArea( item ) ); + + } + + return best; +} + + +double calcRatio( double a, double b ) +{ + if ( a == 0.0 && b == 0.0 ) + return 1.0; + if ( b == 0.0 ) + return 10000000.0; // something arbitrarily big for the moment + + return a / b; +} + + +// todo: explain the selection heuristics +void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const +{ + std::set rejected; + + const double footprintAreaRatio = 0.2; + const double modulePadMinCoverRatio = 0.45; + const double padViaAreaRatio = 0.5; + const double trackViaLengthRatio = 2.0; + const double trackTrackLengthRatio = 0.3; + const double textToFeatureMinRatio = 0.2; + const double textToFootprintMinRatio = 0.4; + + LAYER_ID actLayer = m_frame->GetActiveLayer(); + + LSET silkLayers( 2, B_SilkS, F_SilkS ); + + if( silkLayers[actLayer] ) + { + std::set preferred; + + for( int i = 0; i < aCollector.GetCount(); ++i ) + { + BOARD_ITEM* item = aCollector[i]; + + if ( item->Type() == PCB_MODULE_TEXT_T || item->Type() == PCB_TEXT_T || item->Type() == PCB_LINE_T ) + if ( silkLayers[item->GetLayer()] ) + preferred.insert ( item ); + } + + if( preferred.size() != 0 ) + { + aCollector.Empty(); + + BOOST_FOREACH( BOARD_ITEM* item, preferred ) + aCollector.Append( item ); + return; + } + } + + if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 ) + { + for( int i = 0; i < aCollector.GetCount(); ++i ) + if( TEXTE_MODULE* txt = dyn_cast( aCollector[i] ) ) { - if( preferred == NULL ) + double textArea = calcArea( txt ); + + for( int j = 0; j < aCollector.GetCount(); ++j ) { - preferred = aCollector[i]; // save the first matching item - break; + BOARD_ITEM* item = aCollector[j]; + double areaRatio = calcRatio( textArea, calcArea( item ) ); + + if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio ) + { + //printf("rejectModuleN\n"); + + rejected.insert( item ); + } + + switch( item->Type() ) + { + case PCB_TRACE_T: + case PCB_PAD_T: + case PCB_LINE_T: + case PCB_VIA_T: + case PCB_MODULE_T: + if( areaRatio > textToFeatureMinRatio ) + { + //printf("t after moduleRejected\n"); + rejected.insert( txt ); + } + break; + default: + break; + } } - else + } + } + + if( aCollector.CountType( PCB_MODULE_T ) > 0 ) + { + double minArea = calcMinArea( aCollector, PCB_MODULE_T ); + double maxArea = calcMaxArea( aCollector, PCB_MODULE_T ); + + if( calcRatio( minArea, maxArea ) <= footprintAreaRatio ) + { + for( int i = 0; i < aCollector.GetCount(); ++i ) + if( MODULE* mod = dyn_cast( aCollector[i] ) ) { - return NULL; // there is more than one preferred item, so there is no clear choice + double normalizedArea = calcRatio( calcArea(mod), maxArea ); + + if( normalizedArea > footprintAreaRatio ) + { + //printf("rejectModule1\n"); + + rejected.insert( mod ); + } + } + } + } + + if( aCollector.CountType ( PCB_PAD_T ) > 0 ) + { + for( int i = 0; i < aCollector.GetCount(); ++i ) + { + if ( D_PAD* pad = dyn_cast( aCollector[i] ) ) + { + double ratio = pad->GetParent()->PadCoverageRatio(); + + if( ratio < modulePadMinCoverRatio ) + rejected.insert( pad->GetParent() ); + } + } + } + + if( aCollector.CountType( PCB_VIA_T ) > 0 ) + { + for( int i = 0; i < aCollector.GetCount(); ++i ) + { + if( VIA* via = dyn_cast( aCollector[i] ) ) + { + double viaArea = calcArea( via ); + + for( int j = 0; j < aCollector.GetCount(); ++j ) + { + BOARD_ITEM* item = aCollector[j]; + double areaRatio = calcRatio ( viaArea, calcArea( item ) ); + + if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio ) + rejected.insert( item ); + + if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio ) + rejected.insert( item ); + + if( TRACK* track = dyn_cast( item ) ) + { + if( track->GetNetCode() != via->GetNetCode() ) + continue; + + double lenRatio = (double) ( track->GetLength() + track->GetWidth() ) / (double) via->GetWidth(); + + if( lenRatio > trackViaLengthRatio ) + rejected.insert( track ); + } } } } } - return preferred; + int nTracks = aCollector.CountType ( PCB_TRACE_T ); + + if( nTracks > 0 ) + { + double maxLength = 0.0; + double minLength = std::numeric_limits::max(); + double maxArea = 0.0; + + for( int i = 0; i < aCollector.GetCount(); ++i ) + if ( TRACK *track = dyn_cast ( aCollector[i] ) ) + { + maxLength = std::max( track->GetLength(), maxLength ); + maxLength = std::max( (double)track->GetWidth(), maxLength ); + + minLength = std::min( std::max ( track->GetLength(), (double)track->GetWidth() ), minLength ); + + double area = ( track->GetLength() + track->GetWidth() * track->GetWidth() ); + maxArea = std::max(area, maxArea); + } + + if( maxLength > 0.0 && minLength/maxLength < trackTrackLengthRatio && nTracks > 1 ) + { + for( int i = 0; i < aCollector.GetCount(); ++i ) + { + if( TRACK* track = dyn_cast( aCollector[i] ) ) + { + double ratio = std::max( (double) track->GetWidth(), track->GetLength()) / maxLength; + + if( ratio > trackTrackLengthRatio ) + rejected.insert( track) ; + } + } + } + + for( int j = 0; j < aCollector.GetCount(); ++j ) + { + if( MODULE* mod = dyn_cast( aCollector[j] ) ) + { + double ratio = maxArea / mod->GetFootprintRect().GetArea(); + + if( ratio < modulePadMinCoverRatio ) + { + //printf("rejectModule\n"); + rejected.insert( mod ); + } + } + } + } + + BOOST_FOREACH( BOARD_ITEM* item, rejected ) + { + aCollector.Remove( item ); + } + + //printf("Post-selection: %d\n", aCollector.GetCount() ); +} + + +bool SELECTION_TOOL::SanitizeSelection() +{ + std::set rejected; + + if( !m_editModules ) + { + for( unsigned int i = 0; i < m_selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = m_selection.Item( i ); + + if( item->Type() == PCB_PAD_T ) + { + MODULE* mod = static_cast ( item->GetParent() ); + + // case 1: module (or its pads) are locked + if( mod && ( mod->PadsLocked() || mod->IsLocked() ) ) + rejected.insert( item ); + + // case 2: multi-item selection contains both the module and its pads - remove the pads + if( mod && m_selection.items.FindItem( mod ) >= 0 ) + rejected.insert( item ); + } + } + } + + while( !rejected.empty () ) + { + BOARD_ITEM* item = *rejected.begin(); + int itemIdx = m_selection.items.FindItem( item ); + + if( itemIdx >= 0 ) + m_selection.items.RemovePicker( itemIdx ); + + rejected.erase( item ); + } + + return true; } diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h index ad6e03e6c3..149262c1ee 100644 --- a/pcbnew/tools/selection_tool.h +++ b/pcbnew/tools/selection_tool.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2013 CERN + * Copyright (C) 2013-2015 CERN * @author Tomasz Wlostowski * @author Maciej Suminski * @@ -80,6 +80,13 @@ private: friend class SELECTION_TOOL; }; +enum SELECTION_LOCK_FLAGS +{ + SELECTION_UNLOCKED = 0, + SELECTION_LOCK_OVERRIDE = 1, + SELECTION_LOCKED = 2 +}; + /** * Class SELECTION_TOOL * @@ -151,7 +158,7 @@ public: } ///> Checks if the user has agreed to modify locked items for the given selection. - bool CheckLock(); + SELECTION_LOCK_FLAGS CheckLock(); ///> Select a single item under cursor event handler. int CursorSelection( const TOOL_EVENT& aEvent ); @@ -159,6 +166,10 @@ public: ///> Clear current selection event handler. int ClearSelection( const TOOL_EVENT& aEvent ); + ///> Makes sure a group selection does not contain items that would cause + ///> conflicts when moving/rotating together (e.g. a footprint and one of the same footprint's pads) + bool SanitizeSelection(); + ///> Item selection event handler. int SelectItem( const TOOL_EVENT& aEvent ); @@ -185,7 +196,7 @@ private: * a menu is shown, otherise function finishes without selecting anything. * @return True if an item was selected, false otherwise. */ - bool selectCursor( const VECTOR2I& aWhere, bool aAllowDisambiguation = true ); + bool selectCursor( const VECTOR2I& aWhere, bool aOnDrag = false ); /** * Function selectMultiple() @@ -293,14 +304,12 @@ private: void highlightNet( const VECTOR2I& aPoint ); /** - * Function prefer() - * Checks if collector's list contains only single entry of asked types. If so, it returns it. + * Function guessSelectionCandidates() + * Tries to guess best selection candidates in case multiple items are clicked, by + * doing some braindead heuristics. * @param aCollector is the collector that has a list of items to be queried. - * @param aTypes is the list of searched/preferred types. - * @return Pointer to the preferred item, if there is only one entry of given type or NULL - * if there are more entries or no entries at all. */ - BOARD_ITEM* prefer( GENERAL_COLLECTOR& aCollector, const KICAD_T aTypes[] ) const; + void guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const; /** * Function generateMenu() diff --git a/pcbnew/tools/tools_common.cpp b/pcbnew/tools/tools_common.cpp new file mode 100644 index 0000000000..e9af1bab65 --- /dev/null +++ b/pcbnew/tools/tools_common.cpp @@ -0,0 +1,52 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void registerAllTools( TOOL_MANAGER *aToolManager ) +{ + aToolManager->RegisterTool( new SELECTION_TOOL ); + aToolManager->RegisterTool( new ROUTER_TOOL ); + aToolManager->RegisterTool( new LENGTH_TUNER_TOOL ); + aToolManager->RegisterTool( new EDIT_TOOL ); + aToolManager->RegisterTool( new DRAWING_TOOL ); + aToolManager->RegisterTool( new POINT_EDITOR ); + aToolManager->RegisterTool( new PCBNEW_CONTROL ); + aToolManager->RegisterTool( new PCB_EDITOR_CONTROL ); + aToolManager->RegisterTool( new PLACEMENT_TOOL ); +} \ No newline at end of file diff --git a/pcbnew/xchgmod.cpp b/pcbnew/xchgmod.cpp index dcaea360dd..9835d424fa 100644 --- a/pcbnew/xchgmod.cpp +++ b/pcbnew/xchgmod.cpp @@ -45,6 +45,8 @@ #include #include +#include +#include static bool RecreateCmpFile( BOARD * aBrd, const wxString& aFullCmpFileName ); @@ -481,6 +483,9 @@ void PCB_EDIT_FRAME::Exchange_Module( MODULE* aOldModule, aNewModule->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) ); view->Add( aNewModule ); + + m_toolManager->RunAction( COMMON_ACTIONS::selectionClear, true ); + GetGalCanvas()->ForceRefresh(); } } else