From 265ee8bf53db88d272ff5c10730e1a39a476922d Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Tue, 6 May 2025 16:54:38 +0100 Subject: [PATCH 01/38] frag cluster, cut with considering gap extesions --- .gitignore | 2 +- .gitmodules | 6 + CMakeLists.txt | 110 +++++-- PretextView.cpp | 472 +++++++++++++++++++++------- include/stb_image_resize.h | 2 +- include/stb_image_write.h | 2 +- install.cmake.sh | 1 + python/frag_cluster/kmeans_utils.py | 134 ++++++++ src/auto_curation_state.h | 81 ++--- src/copy_texture.cpp | 428 +++++++++++++------------ src/copy_texture.h | 299 +----------------- src/frag_cut_calculation.h | 2 +- src/frag_for_compress.h | 199 ++++++++++++ src/frag_sort.cpp | 9 +- src/frag_sort.h | 59 +++- src/frags_order.h | 117 +++---- src/genomeData.h | 46 ++- src/hic_figure.h | 1 + src/kmeans_pybind.h | 124 ++++++++ src/likelihood_table.h | 39 ++- src/self_matrix.h | 168 ++++++++++ src/showWindowData.h | 2 +- src/spectral_cluster.h | 141 +++++++++ src/utilsPretextView.h | 18 +- subprojects/eigen | 1 + subprojects/pybind11 | 1 + 26 files changed, 1704 insertions(+), 760 deletions(-) create mode 100644 python/frag_cluster/kmeans_utils.py create mode 100644 src/frag_for_compress.h create mode 100644 src/kmeans_pybind.h create mode 100644 src/self_matrix.h create mode 100644 src/spectral_cluster.h create mode 160000 subprojects/eigen create mode 160000 subprojects/pybind11 diff --git a/.gitignore b/.gitignore index 1570b61..d25b304 100644 --- a/.gitignore +++ b/.gitignore @@ -35,8 +35,8 @@ python/classic_cut/show_hic_density.py python/colormap_colorblind/* python/greedy_topo_sort_test/* python/linear_weight_test/* +python/frag_cluster/kmeans_main_test.cpp -subprojects/eigen subprojects/glfw subprojects/glm subprojects/imgui diff --git a/.gitmodules b/.gitmodules index cfbf012..eee42f1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,9 @@ [submodule "subprojects/fmt"] path = subprojects/fmt url = https://github.com/fmtlib/fmt.git +[submodule "subprojects/pybind11"] + path = subprojects/pybind11 + url = https://github.com/pybind/pybind11.git +[submodule "subprojects/eigen"] + path = subprojects/eigen + url = https://gitlab.com/libeigen/eigen.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e800b11..9be0fda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.17) -project(PretextView VERSION "1.0.3") +project(PretextView VERSION "1.0.4") option(FORCE_MAC_X86 "Force macOS x86_64 build" OFF) @@ -7,6 +7,8 @@ option(BUILD_UNIVERSAL "Build macOS universal binary" OFF) option(WITH_TORCH "Find and link torch lib" OFF) option(DEBUG_OUTPUT_LIKELIHOOD_TABLE "Output likelihood table to file" OFF) option(DEBUG_OUTPUT_PIXEL_CUT_FILE "Output the pixel cut files" OFF) +option(PYTHON_SCOPED_INTERPRETER "Use Python scoped interpreter" ON) +option(AI_ERROR_PIC_DETECTION "Enable AI error pattern detection " OFF) set(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 17) @@ -64,11 +66,11 @@ message(STATUS "Install path: ${CMAKE_INSTALL_PREFIX}") if (CMAKE_BUILD_TYPE STREQUAL "Debug") - message(STATUS "Debug version") + message(STATUS "Build version: Debug ") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0") else() - message(STATUS "Release version") + message(STATUS "Build version: Release ") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") endif() @@ -76,11 +78,11 @@ endif() include_directories( include src - subprojects/imgui subprojects/glfw/include subprojects/libdeflate subprojects/glm subprojects/fmt/include + subprojects/eigen ) add_subdirectory(subprojects/glfw) @@ -145,16 +147,7 @@ endif() add_library(glad STATIC src/glad.c) add_library(utilsPretextView STATIC src/utilsPretextView.cpp) add_library(copy_texture STATIC src/copy_texture.cpp) -# target_link_libraries(copy_texture PRIVATE Eigen3::Eigen) -add_library(imgui STATIC - subprojects/imgui/imgui_demo.cpp - subprojects/imgui/imgui_draw.cpp - subprojects/imgui/imgui_tables.cpp - subprojects/imgui/imgui_widgets.cpp - subprojects/imgui/imgui.cpp - subprojects/imgui/backends/imgui_impl_glfw.cpp - subprojects/imgui/backends/imgui_impl_opengl3.cpp - ) + if(NOT APPLE) add_executable( ${target_name} PretextView.cpp ) @@ -170,15 +163,21 @@ target_compile_definitions(${target_name} PRIVATE PV=${PROJECT_VERSION}) if (CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(${target_name} PRIVATE DEBUG) target_compile_definitions(copy_texture PRIVATE DEBUG) - message(STATUS "Debug version: flag (${CMAKE_BUILD_TYPE}) with DEBUG") -else() - message(STATUS "Release version: flag (${CMAKE_BUILD_TYPE}) without DEBUG") endif() if (DEBUG_OUTPUT_LIKELIHOOD_TABLE) - message(STATUS "Debug version: flag (${CMAKE_BUILD_TYPE}) with DEBUG_OUTPUT_LIKELIHOOD_TABLE") + message(STATUS "DEBUG_OUTPUT_LIKELIHOOD_TABLE: defined") target_compile_definitions(${target_name} PRIVATE DEBUG_OUTPUT_LIKELIHOOD_TABLE) +else() + message(STATUS "DEBUG_OUTPUT_LIKELIHOOD_TABLE: not defined") +endif() +if (AI_ERROR_PIC_DETECTION) + message(STATUS "AI_ERROR_PIC_DETECTION: defined") + target_compile_definitions(${target_name} PRIVATE AI_ERROR_PIC_DETECTION) +else() + message(STATUS "AI_ERROR_PIC_DETECTION: not defined") endif() + target_link_libraries( ${target_name} PRIVATE ${lib_deflate_static} @@ -188,11 +187,27 @@ target_link_libraries( glad utilsPretextView copy_texture - imgui glfw lib_yahs ) +# link libraries only for debug version +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + include_directories( + subprojects/imgui + ) + add_library(imgui STATIC + subprojects/imgui/imgui_demo.cpp + subprojects/imgui/imgui_draw.cpp + subprojects/imgui/imgui_tables.cpp + subprojects/imgui/imgui_widgets.cpp + subprojects/imgui/imgui.cpp + subprojects/imgui/backends/imgui_impl_glfw.cpp + subprojects/imgui/backends/imgui_impl_opengl3.cpp + ) + target_link_libraries(${target_name} PRIVATE imgui) +endif() + if (APPLE) set(library_need Cocoa OpenGL IOKit CoreVideo) find_library(METAL_LIB Metal) @@ -229,6 +244,37 @@ foreach (library ${library_need}) endforeach() + +# ============ Kmeans for Clustering ============== +# find_package(Python REQUIRED COMPONENTS Development) + +# add_executable(kmeans_test python/frag_cluster/kmeans_main.cpp) +# target_include_directories(kmeans_test PRIVATE ${Python_INCLUDE_DIRS}) +# target_link_libraries(kmeans_test PRIVATE ${Python_LIBRARIES}) +# 查找 Python 开发环境 + +find_package(Python COMPONENTS Development Interpreter) +if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) + message(STATUS "Python found: ${Python_VERSION} PYTHON_lib: ${Python_LIBRARIES}") + add_subdirectory(subprojects/pybind11) + + # 链接 pybind11 和 Python 库 + target_link_libraries(${target_name} PRIVATE + pybind11::embed + ${Python_LIBRARIES} + ) + + # 将 Python 脚本复制到构建目录 + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py + ${CMAKE_CURRENT_BINARY_DIR}/kmeans_utils.py + COPYONLY ) + + message(STATUS "Using Python scoped interpreter for Kmeans") + target_compile_definitions(${target_name} PRIVATE PYTHON_SCOPED_INTERPRETER) +endif() + + # ============ CMAKE INSTALLATION ============== file(GLOB aimodel_files "${CMAKE_CURRENT_SOURCE_DIR}/ai_model/*") file(GLOB torch_copy_libs "${TORCH_INSTALL_PREFIX}/lib/*") @@ -273,6 +319,14 @@ if(APPLE) install(FILES ${aimodel_files} DESTINATION "${target_name}.app/Contents/Resources/ai_model" ) endif() + if (PYTHON_SCOPED_INTERPRETER) + install( + FILES ${Python_LIBRARIES} + DESTINATION "${target_name}.app/Contents/Frameworks" ) + install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py + DESTINATION "${target_name}.app/Contents/Resources" ) + endif () elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") message(STATUS "[Cmake Install]: Linux") set_target_properties( @@ -291,6 +345,14 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") FILES ${aimodel_files} DESTINATION "ai_model") endif() + if (PYTHON_SCOPED_INTERPRETER) + install( + FILES ${Python_LIBRARIES} + DESTINATION "lib" ) + install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py + DESTINATION "." ) + endif () else () # windows message(STATUS "[Cmake Install]: Windows") target_sources(${target_name} PRIVATE "${CMAKE_SOURCE_DIR}/PretextView.rc") # add logo to win version @@ -306,6 +368,14 @@ else () # windows FILES ${aimodel_files} DESTINATION "ai_model") endif() + if (PYTHON_SCOPED_INTERPRETER) + install( + FILES ${Python_LIBRARIES} + DESTINATION . ) + install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py + DESTINATION . ) + endif () endif() @@ -314,7 +384,7 @@ include(InstallRequiredSystemLibraries) set(CPACK_PACKAGE_NAME ${target_name}) set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}) -set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/guanshaoheng/PretextView") +set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/sanger-tol/PretextView") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}-${ARCH_TYPE}") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENCE.txt") diff --git a/PretextView.cpp b/PretextView.cpp index ea20743..79d4a0a 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -30,6 +30,10 @@ SOFTWARE. #include // place this before add Header.h to avoid macro conflict + +#ifdef PYTHON_SCOPED_INTERPRETER + #include "kmeans_pybind.h" +#endif // PYTHON_SCOPED_INTERPRETER #include #ifdef DEBUG @@ -84,7 +88,7 @@ SOFTWARE. #define STBI_ONLY_PNG #ifndef DEBUG -#define STBI_ASSERT(x) + #define STBI_ASSERT(x) #endif // debug #ifndef STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION @@ -573,6 +577,7 @@ meta_mode_data * Extension_Mode_Data; + /* 用于显示在input sequences中点击后选中的片段的名字, 显示label持续 5 秒钟 @@ -639,7 +644,7 @@ Global_Mode = mode_normal; #define Scaff_Edit_Mode (Global_Mode == mode_scaff_edit) #define MetaData_Edit_Mode (Global_Mode == mode_meta_edit) #define Extension_Mode (Global_Mode == mode_extension) -#define Select_Sort_Area_Mode (Global_Mode == mode_selectExclude_sort_area) +#define Select_Sort_Area_Mode (Global_Mode == mode_select_sort_area) global_variable s32 @@ -679,7 +684,7 @@ auto_sort_state = 0; global_variable u32 -auto_cut_state = 0; +auto_cut_state = 0; // 0: not auto cut, 1: cut, 2: glue the cutted contigs global_variable s32 auto_cut_button = 0; global_variable s32 auto_sort_button = 0; @@ -813,6 +818,11 @@ Map_State; FragSortTool* frag_sort_method = new FragSortTool(); global_variable auto auto_curation_state = AutoCurationState(); +#ifdef PYTHON_SCOPED_INTERPRETER + global_variable KmeansClusters* kmeans_cluster = nullptr; +#endif // PYTHON_SCOPED_INTERPRETER + +#include "spectral_cluster.h" global_function void @@ -820,7 +830,7 @@ UpdateContigsFromMapState() // reading 从map的状态更新contigs { u32 lastScaffID = Map_State->scaffIds[0]; // 第一个scaff的编号 u32 scaffId = lastScaffID ? 1 : 0; // - u32 lastId = Map_State->originalContigIds[0]; // 第一个像素点对应的id + u32 lastId_original_contig = Map_State->originalContigIds[0]; // 第一个像素点对应的id u32 lastCoord = Map_State->contigRelCoords[0]; // 第一个像素点的局部坐标 u32 contigPtr = 0; u32 length = 0; @@ -833,26 +843,26 @@ UpdateContigsFromMapState() // reading 从map的状态更新contigs ForLoop(Number_of_Pixels_1D - 1) // 遍历每一个像素点 更新 Original_Contigs, Contigs // 遍历完之后,contigPtr为214,但是Number_of_Original_Contigs = 218 { - if (contigPtr == Max_Number_of_Contigs) break; // 确保 contigPtr 不超出最大contig的数值 + if (contigPtr >= Max_Number_of_Contigs) break; // 确保 contigPtr 不超出最大contig的数值 ++length; // current fragment length pixelIdx = index + 1; // 像素点编号, 加一因为第一个已经用来初始化了 - u32 id = Map_State->originalContigIds[pixelIdx]; // 像素点的 original contig id, 这里 不使用 % Max_Number_of_Contigs的值是为了区分后面cut之后的片段 + u32 original_contig_id = Map_State->originalContigIds[pixelIdx]; // 像素点的 original contig id, 这里 不使用 % Max_Number_of_Contigs的值是为了区分后面cut之后的片段 u32 coord = Map_State->contigRelCoords[pixelIdx]; // 像素点的局部坐标 if ( - id != lastId || + original_contig_id != lastId_original_contig || (inverted && coord != (lastCoord - 1)) || (!inverted && coord != (lastCoord + 1)) - ) // 如果不是一个连续片段 + ) // not a continuous fragment { - Original_Contigs[lastId % Max_Number_of_Contigs].contigMapPixels[Original_Contigs[lastId%Max_Number_of_Contigs].nContigs] = pixelIdx - 1 - (length >> 1); // update Original_Contigs: contigMapPixels - Original_Contigs[lastId % Max_Number_of_Contigs].nContigs++; // update Original_Contigs: nContigs, contigMapPixels + Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].contigMapPixels[Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].nContigs] = pixelIdx - 1 - (length >> 1); // update Original_Contigs: contigMapPixels + Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].nContigs++; // update Original_Contigs: nContigs, contigMapPixels contig *last_cont = Contigs->contigs_arr + contigPtr; // 获取上一个contig的指针, 并且给contigPtr + 1 contigPtr++; - last_cont->originalContigId = lastId; // 更新这个片段的id + last_cont->originalContigId = lastId_original_contig; // 更新这个片段的id last_cont->length = length; // 更新长度 last_cont->startCoord = startCoord; // 更新开头为当前片段在该contig上的局部坐标 endCoord = startCoord + length - 1 last_cont->metaDataFlags = Map_State->metaDataFlags + pixelIdx - 1; // Finished (shaoheng): memory problem: assign the pointer to the cont->metaDataFlags, the original is nullptr, the let this ptr point to the last pixel of the contig @@ -879,18 +889,18 @@ UpdateContigsFromMapState() // reading 从map的状态更新contigs } // 更新上一个id和局部坐标 Map_State->contigIds[pixelIdx] = (u32)contigPtr; // 像素点对应的 片段id 修改为当前的统计得到片段id - lastId = id; // 更新上一个像素点的id + lastId_original_contig = original_contig_id; // 更新上一个像素点的id lastCoord = coord; // 更新上一个像素点的局部坐标 } if (contigPtr < Max_Number_of_Contigs) // contigptr 小于 Number_of_Original_Contigs // 更新最后一个contig的最后一个片段信息 { - (Original_Contigs + lastId % Max_Number_of_Contigs)->contigMapPixels[(Original_Contigs + lastId % Max_Number_of_Contigs)->nContigs++] = pixelIdx - 1 - (length >> 1); + (Original_Contigs + lastId_original_contig % Max_Number_of_Contigs)->contigMapPixels[(Original_Contigs + lastId_original_contig % Max_Number_of_Contigs)->nContigs++] = pixelIdx - 1 - (length >> 1); ++length; contig *cont = Contigs->contigs_arr + contigPtr++; - cont->originalContigId = lastId; + cont->originalContigId = lastId_original_contig; cont->length = length; cont->startCoord = startCoord; cont->metaDataFlags = Map_State->metaDataFlags + pixelIdx - 1; @@ -3550,9 +3560,6 @@ Render() { f32 start_fraction = (f32)auto_curation_state.get_start()/(f32)Number_of_Pixels_1D; f32 end_fraction = (f32)auto_curation_state.get_end() /(f32)Number_of_Pixels_1D; - // selected frags into a string - SelectArea select_area; - auto_curation_state.get_selected_fragments(select_area, Map_State, Number_of_Pixels_1D, Contigs); { // draw the help text in the bottom right corner fonsSetFont(FontStash_Context, Font_Bold); @@ -3568,10 +3575,11 @@ Render() { "Q/W: quit / redo edit", "Space: Pixel sort", "Left Click: un/select the fragment", - fmt::format("Up/Down: inc/dec Cut threshold: {:.4f}", auto_curation_state.auto_cut_threshold).c_str(), + fmt::format("Up/Down: inc/dec Cut threshold: {:.4f}", auto_curation_state.auto_cut_threshold), fmt::format("Left/Right: change Sort Mode ({})", auto_curation_state.sort_mode_names[auto_curation_state.sort_mode]), - // fmt::format("Selected fragments number: {}", select_area.selected_frag_ids.size()), - // fmt::format("Select pixel range: {} - {}", auto_curation_state.get_start(), auto_curation_state.get_end()) + fmt::format("Left/Right Shift: Number of Clusters: {}", auto_curation_state.num_clusters), + fmt::format("H: Hap_Name_Clustering: {}", auto_curation_state.hap_cluster_flag ? "ON" : "OFF"), + fmt::format("K: Cut with gap: {}", auto_curation_state.auto_cut_with_extension? "ON" : "OFF"), }; f32 textBoxHeight = (f32)helpTexts.size() * (lh + 1.0f) - 1.0f; @@ -3658,11 +3666,21 @@ Render() { glUseProgram(UI_Shader->shaderProgram); fonsSetColor(FontStash_Context, colour); + // selected frags into a string + s32 selected_fragment_size = 1, last_contig = Map_State->contigIds[auto_curation_state.get_start()]; + for (s32 i = auto_curation_state.get_start()+1; i < auto_curation_state.get_end(); i++) + { + if (last_contig!=Map_State->contigIds[i]) + { + selected_fragment_size ++ ; + last_contig = Map_State->contigIds[i]; + } + } std::string buff = fmt::format( "{}: ({}) pixels, ({}) fragments", auto_curation_state.selected_or_exclude==0?"Selected" :"Excluded", std::abs(auto_curation_state.get_end() - auto_curation_state.get_start()), - select_area.selected_frag_ids.size()); + selected_fragment_size); f32 lh = 0.0f; f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, buff.c_str(), 0, NULL); @@ -5000,7 +5018,6 @@ LoadFile(const char *filePath, memory_arena *arena, char **fileName, u64 *header // contig name赋值 ForLoop2(16) { - // ?? 一个contig 的name为什么是一个长度为u32的数组 Original_Contigs[index].name[index2] = name[index2]; // 将 u32 name[16] 给到每一个contig 的name } @@ -5490,7 +5507,7 @@ LoadFile(const char *filePath, memory_arena *arena, char **fileName, u64 *header prepare_before_copy(original_color_control_points); textures_array_ptr->copy_buffer_to_textures( Contact_Matrix, - false + false // show_flag ); restore_settings_after_copy(original_color_control_points); @@ -6198,7 +6215,7 @@ RearrangeMap( // NOTE: VERY IMPORTANT { fmt::print( stderr, - "RearrangeMap: Invalid parameters: delta = {}, pixelFrom = {}, pixelTo = {}, nPixels = {}, file: {}, line: {}\n", + "RearrangeMap: Invalid parameters: delta = {}, pixelFrom = {}, pixelTo = {}, nPixels = {}, file {} line {}\n", delta, pixelFrom, pixelTo, nPixels, __FILE__, __LINE__ ); @@ -6384,14 +6401,20 @@ RearrangeMap( // NOTE: VERY IMPORTANT } - +/* + BreakMap: cut the contig at loc, and update the original contig ids + loc: the location to cut + ignore_len: the length of the contig to be ignored + extension_flag: if true, consider the extension signal + +*/ global_function void BreakMap( const u32& loc, - const u32 ignore_len // cut点到开头或者结尾的长度不足ignore_len的contig不会被切断 + const u32& ignore_len // cut点到开头或者结尾的长度不足ignore_len的contig不会被切断 ) -{ +{ if (loc >= Number_of_Pixels_1D) { char buff[512]; @@ -6417,7 +6440,7 @@ BreakMap( if ((loc - ptr_left) < ignore_len || (ptr_right - loc) < ignore_len ) { fmt::print( - "[Pixel Cut] Warning: original_contig_id {} current_contig_id {}, pixel range: [{}, cut({}), {}], left({}), right({}), smaller than ignore_len ({}), cut at loc: ({}) is ignored\n", + "[Pixel Cut::warning] original_contig_id {} current_contig_id {}, pixel range: [{}, cut({}), {}], left({}), right({}), smaller than ignore_len ({}), cut at loc: ({}) is ignored\n", original_contig_id%Max_Number_of_Contigs, contig_id, ptr_left, @@ -6436,7 +6459,7 @@ BreakMap( { if (Map_State->originalContigIds[tmp] > (std::numeric_limits::max() - Max_Number_of_Contigs)) { - fmt::print("[Pixel Cut] originalContigIds[{}] + {} = {} is greater than the max number of contigs ({}), file: {}, line: {}\n", tmp, + fmt::print("[Pixel Cut] originalContigIds[{}] + {} = {} is greater than the max number of contigs ({}), file {}, line {}\n", tmp, Max_Number_of_Contigs, (u64)Map_State->originalContigIds[tmp] + Max_Number_of_Contigs, std::numeric_limits::max(), @@ -6515,11 +6538,45 @@ void run_ai_detection() } void cut_frags(const std::vector& problem_locs) -{ - for (auto & i : problem_locs) - { +{ + const u32* gap_data_ptr = auto_curation_state.auto_cut_with_extension ? Extensions.get_graph_data_ptr("gap"):nullptr; + + for (auto & loc_orig : problem_locs) + { + u32 loc = loc_orig; + if (gap_data_ptr) // correct loc with considering the gap extension + { + u32 distance_tmp = 1; + while (distance_tmp < auto_curation_state.auto_cut_gap_loc_threshold) + { + if (loc_orig + distance_tmp < Number_of_Pixels_1D && gap_data_ptr[loc_orig + distance_tmp] > 0 ) + { + fmt::println( + "[Pixel Cut] gap[{}+{}] = {}, correct cut from ({}) to ({})", + loc_orig, distance_tmp, gap_data_ptr[loc_orig + distance_tmp], loc_orig, loc_orig + distance_tmp + ); + loc = loc_orig + distance_tmp; + break; + } + else if (loc_orig-distance_tmp >= 0 && gap_data_ptr[loc_orig - distance_tmp] > 0) + { + fmt::println( + "[Pixel Cut] gap[{}-{}] = {}, correct cut from ({}) to ({})", + loc_orig, distance_tmp, gap_data_ptr[loc_orig - distance_tmp], loc_orig, loc_orig - distance_tmp + ); + loc = loc_orig - distance_tmp; + break; + } + else + { + distance_tmp++; + } + } + } // cut the fragment - BreakMap(i, auto_curation_state.auto_cut_smallest_frag_size_in_pixel); + BreakMap( + loc, // cut loc + auto_curation_state.auto_cut_smallest_frag_size_in_pixel); // ignore length UpdateContigsFromMapState(); } Redisplay = 1; @@ -6530,15 +6587,15 @@ void AutoCurationFromFragsOrder( const FragsOrder* frags_order_, contigs* contigs_, map_state* map_state_, - SelectArea* select_area=nullptr) + SelectArea* select_area) { u08 using_select_area = (select_area && select_area->select_flag)? 1 : 0; u32 num_frags = contigs_->numberOfContigs; - if (!using_select_area ) + if (!using_select_area ) { if ( num_frags != frags_order_->get_num_frags()) { - fprintf(stderr, "Number of contigs(%d) and fragsOrder.num_frags(%d) do not match.\n", num_frags, frags_order_->get_num_frags()); + fprintf(stderr, "[AutoCurationFromFragsOrder::error]: Number of contigs(%d) and fragsOrder.num_frags(%d) does not match.\n", num_frags, frags_order_->get_num_frags()); assert(0); } } @@ -6548,7 +6605,7 @@ void AutoCurationFromFragsOrder( { fmt::print( stderr, - "num_to_sort_contigs({}) != fragsOrder.num_frags({}), file:{}, line:{}\n", select_area->get_to_sort_frags_num(), + "num_to_sort_contigs({}) != fragsOrder.num_frags({}), file {}, line {}\n", select_area->get_to_sort_frags_num(), frags_order_->get_num_frags(), __FILE__, __LINE__); assert(0); @@ -6565,9 +6622,9 @@ void AutoCurationFromFragsOrder( { std::vector full_predicted_order(num_frags); std::iota(full_predicted_order.begin(), full_predicted_order.end(), 1); - auto frags_id_to_sort = select_area->get_to_sort_frags_id(Contigs); - for (u32 i=0; i< select_area->get_to_sort_frags_num(); i++) - full_predicted_order[frags_id_to_sort[i]] = (predicted_order[i]>0?1:-1) * (select_area->get_first_frag_id() + std::abs(predicted_order[i])); + std::vector frags_id_to_sort = select_area->get_to_sort_frags_id(Contigs); + for (u32 i=0; i < frags_id_to_sort.size(); i++) + full_predicted_order[frags_id_to_sort[i]] = (predicted_order[i]>0?1:-1) * (frags_id_to_sort.front() + std::abs(predicted_order[i])); predicted_order = full_predicted_order; } for (s32 i = 0; i < num_frags; ++i) current_order[i] = {i+1, contigs_->contigs_arr[i].length}; // start from 1 @@ -6726,6 +6783,7 @@ auto_cut_func( cut_frags(cut_locs_pixel); } + // AI cutting // generate figures if (0) { @@ -6742,16 +6800,55 @@ auto_cut_func( // restore the error info via reading the error json HIC_Problems hic_problems("/auto_curation_tmp/auto_cut_output/infer_result/infer_result.json", (f32) Total_Genome_Length / (f32)Number_of_Pixels_1D ); std::vector> problem_loc = hic_problems.get_problem_loci(); - } - - // cut the contigs - // cut_frags(); + // cut the contigs + // cut_frags(); + } return ; } +global_function +std::unordered_map> collect_hap_cluster( + AutoCurationState& auto_curation_state, + const SelectArea& selected_area +) +{ + std::unordered_map> hap_cluster; + if (!auto_curation_state.hap_cluster_flag) return hap_cluster; + + for (const auto& i : selected_area.selected_frag_ids) + { + std::string original_contig_name = std::string((char*)(Original_Contigs+((Contigs->contigs_arr+i)->get_original_contig_id()))->name); + std::transform(original_contig_name.begin(), original_contig_name.end(), original_contig_name.begin(), + [](unsigned char c) { return std::tolower(c); }); + if (original_contig_name.find("hap") != std::string::npos) + { + s32 hap_loc = original_contig_name.find("hap"); + s32 hap_id = std::stoi( + original_contig_name.substr( + hap_loc + 3, original_contig_name.find_first_of("_", hap_loc + 3) - hap_loc - 3)); + if (hap_cluster.find(hap_id) == hap_cluster.end()) + { + hap_cluster[hap_id] = std::vector(); + } + hap_cluster[hap_id].push_back(i - selected_area.selected_frag_ids[0]); + }else + { + fmt::print( + stdout, + "[Pixel Sort::Warning]: haplotig cluster is selected but the original contig name does not contain 'hap'.\n"); + auto_curation_state.hap_cluster_flag = false; + break; + } + } + return hap_cluster; +} + + + + global_function void auto_sort_func(char* currFileName) @@ -6763,28 +6860,52 @@ auto_sort_func(char* currFileName) fprintf(stdout, "[Pixel Sort] smallest_frag_size_in_pixel: %d\n", auto_curation_state.smallest_frag_size_in_pixel); fprintf(stdout, "[Pixel Sort] link_score_threshold: %.3f\n", auto_curation_state.link_score_threshold); fprintf(stdout, "[Pixel Sort] Sort mode: %s\n", auto_curation_state.get_sort_mode_name().c_str()); + fmt::print(stdout, "[Pixel Sort] num_clusters: {}\n", auto_curation_state.num_clusters); // compress the HiC data // prepare before reading textures f32 original_color_control_points[3]; prepare_before_copy(original_color_control_points); - textures_array_ptr->copy_buffer_to_textures_dynamic(Contact_Matrix, false); + textures_array_ptr->copy_buffer_to_textures_dynamic( + Contact_Matrix, + false // show_flag + ); restore_settings_after_copy(original_color_control_points); + // check if select the area for sorting SelectArea selected_area; - auto_curation_state.get_selected_fragments( + auto_curation_state.get_selected_fragments( // consider wheather include the sink and source while calculating the selected area selected_area, Map_State, Number_of_Pixels_1D, Contigs ); + + std::unordered_map> hap_cluster = collect_hap_cluster(auto_curation_state, selected_area); + + bool hap_cluster_flag = (hap_cluster.size() > 1 && auto_curation_state.hap_cluster_flag); + // NOTE: if hap_cluster is selected, then the kmeans cluster is not needed + if (hap_cluster_flag) auto_curation_state.num_clusters = 1; + else auto_curation_state.hap_cluster_flag = false; + bool kmeans_cluster_flag = selected_area.selected_frag_ids.size() > std::max(auto_curation_state.min_frag_num_for_cluster, (s32)auto_curation_state.num_clusters) + && auto_curation_state.num_clusters > 1 + && !hap_cluster_flag; + + // correct the selected_area, wheather use or not use the source and sink + if ( hap_cluster_flag || kmeans_cluster_flag ) + { + selected_area.source_frag_id = -1; + selected_area.sink_frag_id = -1; + } + if (auto_curation_state.get_start() >=0 && auto_curation_state.get_end() >= 0 && selected_area.selected_frag_ids.size() > 0) { - fprintf(stdout, "[Pixel Sort] Using selected area for sorting:\n"); + fprintf(stdout, "[Pixel Sort] local sort within selected area\n"); fprintf(stdout, "[Pixel Sort] Selected pixel range: %d ~ %d\n", auto_curation_state.get_start(), auto_curation_state.get_end()); std::stringstream ss; ss << selected_area.selected_frag_ids[0]; - for (u32 i = 1; i < selected_area.selected_frag_ids.size(); ++i) ss << ", " << selected_area.selected_frag_ids[i]; + for (u32 i = 1; i < selected_area.selected_frag_ids.size(); ++i) + ss << ", " << selected_area.selected_frag_ids[i]; fmt::print(stdout, "[Pixel Sort] Selected fragments ({}): {}\n\n", selected_area.selected_frag_ids.size(), ss.str().c_str()); selected_area.select_flag = true; selected_area.start_pixel = auto_curation_state.get_start(); @@ -6794,80 +6915,174 @@ auto_sort_func(char* currFileName) textures_array_ptr->cal_compressed_hic( Contigs, Extensions, - false, - false, + false, // is_extension_required + false, // is_mass_center_required &selected_area ); - FragsOrder frags_order(textures_array_ptr->get_num_frags()); // intilize the frags_order with the number of fragments including the filtered out ones - // exclude the fragments with first two tags during the auto curation std::vector exclude_tags = {"haplotig", "unloc"}; std::vector exclude_meta_tag_idx = get_exclude_metaData_idx(exclude_tags); - LikelihoodTable likelihood_table( - textures_array_ptr->get_frags(), - textures_array_ptr->get_compressed_hic(), - (f32)auto_curation_state.smallest_frag_size_in_pixel / ((f32)Number_of_Pixels_1D + 1.f), - exclude_meta_tag_idx, - Number_of_Pixels_1D); - - #ifdef DEBUG_OUTPUT_LIKELIHOOD_TABLE - likelihood_table.output_fragsInfo_likelihoodTable("frags_info_likelihood_table.txt", textures_array_ptr->get_compressed_hic()); - #endif // DEBUG_OUTPUT_LIKELIHOOD_TABLE - - // clear mem for textures and compressed hic - textures_array_ptr->clear_textures(); - textures_array_ptr->clear_compressed_hic(); - // use the compressed_hic to calculate the frags_order directly - if (auto_curation_state.sort_mode == 0 || !selected_area.select_flag) // sort with union find if sorting the whole genome - { - frag_sort_method->sort_according_likelihood_unionFind( - likelihood_table, - frags_order, - selected_area, - auto_curation_state.link_score_threshold, - textures_array_ptr->get_frags()); - } - else if (auto_curation_state.sort_mode == 1) - { - frag_sort_method->sort_according_likelihood_unionFind_doFuse( - likelihood_table, - frags_order, - selected_area, - auto_curation_state.link_score_threshold, - textures_array_ptr->get_frags(), true, true); - } - else if (auto_curation_state.sort_mode == 2) - { - frag_sort_method->sort_according_likelihood_unionFind_doFuse( - likelihood_table, - frags_order, - selected_area, - auto_curation_state.link_score_threshold, - textures_array_ptr->get_frags(), false, true); + // cluster and sort + // 1. clustering + if (hap_cluster_flag || kmeans_cluster_flag ) + { + std::vector> clusters; + if (kmeans_cluster_flag) // kmeans cluster + { + fmt::print(stdout, + "[Pixel Sort] kmeans: num of clusters: {}, num of selected fragments: {}\n", auto_curation_state.num_clusters, + selected_area.selected_frag_ids.size()); + #ifdef PYTHON_SCOPED_INTERPRETER + if (kmeans_cluster && kmeans_cluster->is_init) + { + fmt::print(stdout, "[Pixel Sort] cluster with Python module.\n"); + clusters = kmeans_cluster->kmeans_func( + auto_curation_state.num_clusters, + textures_array_ptr->get_compressed_hic()); + } else + { + fmt::print(stdout, "[Pixel Sort::warning] Python clustering module initializing failed. Clustering with Eigen-based algorithm.\n"); + clusters = spectral_clustering(*(textures_array_ptr->get_compressed_hic()), auto_curation_state.num_clusters); + } + #else + fmt::print(stdout, "[Pixel Sort::warning] Python clustering module initializing failed. Clustering with Eigen-based algorithm.\n"); + clusters = spectral_clustering(*(textures_array_ptr->get_compressed_hic()), auto_curation_state.num_clusters); + #endif // PYTHON_SCOPED_INTERPRETER + } else // hap cluster + { + fmt::print(stdout, + "[Pixel Sort] haplotig cluster: num of haps: {}, num of selected fragments: {}\n", + hap_cluster.size(), selected_area.selected_frag_ids.size()); + clusters.clear(); + for (const auto& i : hap_cluster) + { + clusters.push_back(i.second); + } + } + + // create the frag4compress object for sorting within the clusters + std::vector clusters_frags(clusters.size()); + for (u32 i = 0; i < clusters.size(); ++i) + { + std::sort(clusters[i].begin(), clusters[i].end()); + clusters_frags[i].re_allocate_mem( + Contigs, + clusters[i]); + } + + #ifdef DEBUG + fmt::print(stdout, "[Pixel Sort]: Kmeans clusters size: {} \n", clusters.size()); + for (u32 i = 0; i < clusters.size(); ++i) + { + auto& tmp = clusters[i]; + fmt::print(stdout, "\t[{} ({})]: ", i, clusters[i].size()); + if (tmp.size() > 0) + { + for (auto& tmp1 : tmp) fmt::print(stdout, "{}, ", tmp1); + } + fmt::print(stdout, "\n"); + } + fmt::print(stdout, "\n"); + #endif // DEBUG + + // 3. sort within the clusters + std::vector frags_order_list; + for (u32 i = 0; i < clusters.size(); ++i) + { + frags_order_list.push_back(FragsOrder(clusters[i].size())); + } + for (u32 i = 0; i < clusters.size(); ++i) + { + // every cluster forms a likelihood table + if (clusters[i].size() < 2) + continue; + auto tmp_likelihood_table = LikelihoodTable( + &clusters_frags[i], + textures_array_ptr->get_compressed_hic(), + (f32)auto_curation_state.smallest_frag_size_in_pixel / ((f32)Number_of_Pixels_1D + 1.f), + exclude_meta_tag_idx, + Number_of_Pixels_1D, + &clusters[i] + ); + frag_sort_method->sort_method_mask( + tmp_likelihood_table, + frags_order_list[i], + selected_area, + auto_curation_state, + &clusters_frags[i], + true + ); + } + // merge all clusters + FragsOrder merged_frags_order(frags_order_list, clusters); + + #ifdef DEBUG + merged_frags_order.print_order() ; + #endif // DEBUG + + AutoCurationFromFragsOrder( + &merged_frags_order, + Contigs, + Map_State, + &selected_area); + + // evaluate the compressed hic between all of the clusters + + // 4. sort the clusters } - else if (auto_curation_state.sort_mode == 3) // not finished yet - { - frag_sort_method->sort_according_yahs( + else // sort without clustering + { + if (auto_curation_state.num_clusters == 1) + { + fmt::print(stdout, "[Pixel Sort] sort without clustering.\n"); + } else // (selected_area.selected_frag_ids.size() <= std::max(auto_curation_state.min_frag_num_for_cluster, (s32)auto_curation_state.num_clusters) ) + { + fmt::print( + stdout, + "[Pixel Sort] num_cluster({})>1 but selected fragments({}) <= max(min_num_frags_for_cluster {}, num_cluster {})\n", + auto_curation_state.num_clusters, + selected_area.selected_frag_ids.size(), + auto_curation_state.min_frag_num_for_cluster, + auto_curation_state.num_clusters); + } + + FragsOrder frags_order(textures_array_ptr->get_num_frags()); // intilize the frags_order with the number of fragments including the filtered out ones + // exclude the fragments with first two tags during the auto curation + LikelihoodTable likelihood_table( + textures_array_ptr->get_frags(), + textures_array_ptr->get_compressed_hic(), + (f32)auto_curation_state.smallest_frag_size_in_pixel / ((f32)Number_of_Pixels_1D + 1.f), + exclude_meta_tag_idx, + Number_of_Pixels_1D); + + #ifdef DEBUG_OUTPUT_LIKELIHOOD_TABLE + likelihood_table.output_fragsInfo_likelihoodTable("frags_info_likelihood_table.txt", textures_array_ptr->get_compressed_hic()); + #endif // DEBUG_OUTPUT_LIKELIHOOD_TABLE + + // use the compressed_hic to calculate the frags_order directly + frag_sort_method->sort_method_mask( likelihood_table, frags_order, selected_area, - auto_curation_state.link_score_threshold, - textures_array_ptr->get_frags() + auto_curation_state, + textures_array_ptr->get_frags(), + true // sort chromosomes according length + ); + + AutoCurationFromFragsOrder( + &frags_order, + Contigs, + Map_State, + &selected_area ); } - else - { - fprintf(stderr, "[Pixel Sort] Error: Unknown sort mode (%d)\n", auto_curation_state.sort_mode); - assert(0); - } - AutoCurationFromFragsOrder( - &frags_order, - Contigs, - Map_State, - &selected_area); + std::cout << std::endl; auto_sort_state = 0; + // clear mem for textures and compressed hic + textures_array_ptr->clear_textures(); + textures_array_ptr->clear_compressed_hic(); return; } @@ -7216,7 +7431,7 @@ ToggleSelectSortAreaMode(GLFWwindow* window) } else if (Normal_Mode || (Edit_Mode && !Edit_Pixels.editing) || MetaData_Edit_Mode) { - Global_Mode = mode_selectExclude_sort_area; + Global_Mode = mode_select_sort_area; f64 mousex, mousey; glfwGetCursorPos(window, &mousex, &mousey); MouseMove(window, mousex, mousey); @@ -7500,6 +7715,11 @@ KeyBoard(GLFWwindow* window, s32 key, s32 scancode, s32 action, s32 mods) break; case GLFW_KEY_H: + if (Select_Sort_Area_Mode) + { + auto_curation_state.hap_cluster_flag = !auto_curation_state.hap_cluster_flag; + } + else keyPressed = 0; break; case GLFW_KEY_I: @@ -7511,6 +7731,9 @@ KeyBoard(GLFWwindow* window, s32 key, s32 scancode, s32 action, s32 mods) break; case GLFW_KEY_K: + if (Select_Sort_Area_Mode) + auto_curation_state.auto_cut_with_extension = !auto_curation_state.auto_cut_with_extension; + else keyPressed = 0; break; case GLFW_KEY_L: @@ -7724,6 +7947,10 @@ KeyBoard(GLFWwindow* window, s32 key, s32 scancode, s32 action, s32 mods) else Scaff_FF_Flag &= ~2; } else if (Edit_Mode) Edit_Pixels.scaffSelecting = action != GLFW_RELEASE; + else if (Select_Sort_Area_Mode && action == GLFW_PRESS) // num_cluster descrease + { + auto_curation_state.num_clusters = auto_curation_state.num_clusters >= 2 ? auto_curation_state.num_clusters - 1 : 1; + } else keyPressed = 0; break; @@ -7749,6 +7976,14 @@ KeyBoard(GLFWwindow* window, s32 key, s32 scancode, s32 action, s32 mods) else AdjustColorMap(-1); break; + case GLFW_KEY_RIGHT_SHIFT: // num_cluster increase + if (Select_Sort_Area_Mode) + { + auto_curation_state.num_clusters = my_Min(auto_curation_state.num_clusters + 1, 200) ; + } + else keyPressed = 0; + break; + case GLFW_KEY_RIGHT: if (MetaData_Edit_Mode) { @@ -10915,7 +11150,14 @@ void SortMapByMetaTags(u64 tagMask) MainArgs -{ +{ + + #ifdef PYTHON_SCOPED_INTERPRETER + // START Python interpreter + py::scoped_interpreter guard{}; + kmeans_cluster = new KmeansClusters(); // import the kmeans module + #endif // PYTHON_SCOPED_INTERPRETER + u32 initWithFile = 0; // initialization with .map file or not u08 currFile[256]; // save the name of file inputed u08 *currFileName = 0; @@ -11585,8 +11827,8 @@ MainArgs nk_contextual_end(NK_Context); } - if ((nk_option_label(NK_Context, "Select sort area", Global_Mode == mode_selectExclude_sort_area) ? 1 : 0) != (Global_Mode == mode_selectExclude_sort_area ? 1 : 0)) - Global_Mode = (Global_Mode == mode_selectExclude_sort_area ? mode_normal : mode_selectExclude_sort_area); + if ((nk_option_label(NK_Context, "Select sort area", Global_Mode == mode_select_sort_area) ? 1 : 0) != (Global_Mode == mode_select_sort_area ? 1 : 0)) + Global_Mode = (Global_Mode == mode_select_sort_area ? mode_normal : mode_select_sort_area); nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 2); Waypoints_Always_Visible = nk_check_label(NK_Context, "Waypoints Always Visible", (s32)Waypoints_Always_Visible) ? 1 : 0; @@ -12445,6 +12687,12 @@ MainArgs { delete frag_sort_method; frag_sort_method = nullptr; } + #ifdef PYTHON_SCOPED_INTERPRETER + if (kmeans_cluster) + { + delete kmeans_cluster; kmeans_cluster = nullptr; + } + #endif // PYTHON_SCOPED_INTERPRETER ResetMemoryArenaP(Loading_Arena); fprintf(stdout, "Memory freed for the arena.\n"); diff --git a/include/stb_image_resize.h b/include/stb_image_resize.h index bcca92c..a828fc1 100644 --- a/include/stb_image_resize.h +++ b/include/stb_image_resize.h @@ -385,7 +385,7 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int #ifdef STBIR_DEBUG #define STBIR__DEBUG_ASSERT STBIR_ASSERT #else -#define STBIR__DEBUG_ASSERT +#define STBIR__DEBUG_ASSERT(x) #endif // If you hit this it means I haven't done it yet. diff --git a/include/stb_image_write.h b/include/stb_image_write.h index a19b548..6504811 100644 --- a/include/stb_image_write.h +++ b/include/stb_image_write.h @@ -740,7 +740,7 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f #ifdef __STDC_WANT_SECURE_LIB__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else - len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + len = std::snprintf(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif s->func(s->context, buffer, len); diff --git a/install.cmake.sh b/install.cmake.sh index 05efad3..ac10a7d 100755 --- a/install.cmake.sh +++ b/install.cmake.sh @@ -90,6 +90,7 @@ CMAKE_OPTIONS=( -DCMAKE_INSTALL_PREFIX="$install_path" -DCMAKE_PREFIX_PATH="$cmake_prefix_path_tmp" -DCMAKE_OSX_ARCHITECTURES=${ARCH} + -DPYTHON_SCOPED_INTERPRETER=ON ) if [[ "$OS" == "Darwin" ]]; then if [[ "$FORCE_MAC_X86" == true ]]; then diff --git a/python/frag_cluster/kmeans_utils.py b/python/frag_cluster/kmeans_utils.py new file mode 100644 index 0000000..6602a5a --- /dev/null +++ b/python/frag_cluster/kmeans_utils.py @@ -0,0 +1,134 @@ + +import re +from scipy.linalg import eigh +from sklearn.cluster import KMeans, SpectralClustering +from sklearn.metrics import silhouette_score +import numpy as np + +""" + genome片段聚类分析: + 1. 读取基因组片段数据 + 2. 计算拉普拉斯矩阵 + 3. 谱聚类 + +""" + + +class Frags: + def __init__(self): + self.n = 0 + self.frags_infos = np.zeros((self.n, 4)) + self.adj_matrix = np.zeros((self.n, self.n, 5)) + + def ini_size(self, n:int): + self.n = n + self.frags_infos = np.zeros((self.n, 4)) + self.adj_matrix = np.zeros((self.n, self.n, 5)) # 邻接矩阵 + + def read_frags_adj_matrix(self, frags_file): + """ + 读取基因组片段数据 + :param frags_file: 基因组片段文件 + :return: None + """ + with open(frags_file, 'r') as f: + while (line := f.readline()): + if re.match(r'# frags num:', line): + n = int(line.split(':')[1].strip()) + self.ini_size(n) + elif re.match(r'# frags_index', line): + for i in range(self.n): + line_tmp = f.readline().strip().split() + self.frags_infos[i] = [int(j) for j in line_tmp] + elif re.match(r'# Matrix shape:', line): + shape = [int(j) for j in line.split(':')[1].strip().split()] + if shape[0] != self.n or shape[1] != self.n or shape[2] != 5: + raise ValueError("Matrix shape is not correct") + for layer_tmp in range(5): + line = f.readline().strip() + if not re.match(r'# layer:', line): + raise ValueError(f"Matrix shape is not correct, line: {line}") + for row_tmp in range(self.n): + line_tmp = f.readline().strip().split() + self.adj_matrix[row_tmp, :, layer_tmp] = [float(j) for j in line_tmp] + elif line == '\n': continue + else: + print(f"[Frags file reading :: Warning]: {line}") + + print("[Frags file reading :: Status]: Read file successfully") + + + def kmeans_cluster(self, k, matrix:np.ndarray = None): + """ + 计算特征值和特征向量 + :return: None + """ + if matrix is None: + raise ValueError("Matrix is None, please provide a matrix") + processed_adj_matrix = np.max(matrix[..., :4], axis=-1) + print(f"[KMeans]: original matrix shape = {matrix.shape} processed matrix shape = {processed_adj_matrix.shape}") + # 谱聚类 + model = SpectralClustering( + n_clusters=k, + affinity='precomputed', # 相似度计算方式(高斯核或K近邻) + assign_labels='kmeans' # 低维空间用K-Means聚类 + ) + clusters = model.fit_predict(processed_adj_matrix) + return clusters + + def auto_kmeans_cluster(self, k_range=range(2, 20), matrix: np.ndarray = None): + """ + 自动选择最佳聚类数并聚类(基于轮廓系数) + """ + if matrix is None: + matrix = self.adj_matrix + processed_adj_matrix = np.max(matrix[..., :4], axis=-1) + degrees = np.sum(processed_adj_matrix, axis=1) + D = np.diag(degrees) + lap_matrix = D - processed_adj_matrix + + eigenvalues, eigenvectors = eigh(lap_matrix) + + best_k = None + best_score = -1 + best_labels = None + + for k in k_range: + if k >= matrix.shape[0]: break + kmeans = KMeans(n_clusters=k, n_init=10, random_state=0) + labels = kmeans.fit_predict(eigenvectors[:, :k]) + score = silhouette_score(eigenvectors[:, :k], labels) + if score > best_score: + best_k = k + best_score = score + best_labels = labels + + print(f"[Auto-KMeans]: Best K = {best_k}, silhouette score = {best_score:.4f}") + return best_labels + + + +def run_kmeans(k = 6, matrix:np.ndarray = None): + """ + 运行kmeans聚类 + :return: None + """ + frags = Frags() + if matrix is None: + raise ValueError("Matrix is None, please provide a matrix") + frags.read_frags_adj_matrix("/Users/sg35/PretextView/build_cmake/PretextViewAI.app/Contents/Resources/frags_info_likelihood_table.txt") + + return frags.kmeans_cluster(k).astype(np.int32) + else: + return frags.kmeans_cluster(k, matrix).astype(np.int32) + + + + + +if __name__ == "__main__": + frags = Frags() + frags.read_frags_adj_matrix("/Users/sg35/PretextView/build_cmake/PretextViewAI.app/Contents/Resources/frags_info_likelihood_table.txt") + lables = frags.kmeans_cluster(10) + print(f"labels = {lables}") + diff --git a/src/auto_curation_state.h b/src/auto_curation_state.h index e1b8454..d01e8f4 100644 --- a/src/auto_curation_state.h +++ b/src/auto_curation_state.h @@ -15,7 +15,7 @@ struct SelectArea u32 end_pixel = 0; s32 source_frag_id = -1; s32 sink_frag_id = -1; - std::vector selected_frag_ids; + std::vector selected_frag_ids; void clearup() @@ -28,21 +28,16 @@ struct SelectArea this->selected_frag_ids.clear(); } - /* - first frag id, including the source fragment - */ - u32 get_first_frag_id() - { - return this->source_frag_id >=0? this->source_frag_id : this->selected_frag_ids[0]; - } - /* len in pixel, including the source and sink fragments */ - u32 get_selected_len(const contigs* Contigs) const + u32 get_selected_len( + const contigs* Contigs, + const bool used_for_cluster_flag=false + ) const { u32 len = 0; - if (this->source_frag_id>=0) + if (this->source_frag_id>=0 && !used_for_cluster_flag) { len += Contigs->contigs_arr[this->source_frag_id].length; } @@ -50,7 +45,7 @@ struct SelectArea { len += Contigs->contigs_arr[frag_id].length; } - if (this->sink_frag_id>=0) + if (this->sink_frag_id>=0 && !used_for_cluster_flag) { len += Contigs->contigs_arr[this->sink_frag_id].length; } @@ -62,7 +57,7 @@ struct SelectArea */ u32 get_to_sort_frags_num() { - return this->selected_frag_ids.size() + (this->source_frag_id>=0) + (this->sink_frag_id>=0); + return (this->source_frag_id>=0) + this->selected_frag_ids.size() + (this->sink_frag_id>=0); } /* @@ -71,7 +66,7 @@ struct SelectArea std::vector get_to_sort_frags_id(const contigs* Contigs) { std::vector to_sort_frags; - if (this->source_frag_id>=0) + if (this->source_frag_id>=0 ) { to_sort_frags.push_back(this->source_frag_id); } @@ -79,7 +74,7 @@ struct SelectArea { to_sort_frags.push_back(frag_id); } - if (this->sink_frag_id>=0) + if (this->sink_frag_id>=0 ) { to_sort_frags.push_back(this->sink_frag_id); } @@ -108,6 +103,13 @@ struct AutoCurationState u32 auto_cut_diag_window_for_pixel_mean= 8; u32 auto_cut_smallest_frag_size_in_pixel = 8; + // cluster according to the hap name + u08 hap_cluster_flag = 0; + + // kmeans cluster + u32 num_clusters = 1; + const s32 min_frag_num_for_cluster = 4; + // Variables for the editing UI state bool show_autoSort_erase_confirm_popup = false; bool show_autoSort_redo_confirm_popup = false; @@ -115,14 +117,16 @@ struct AutoCurationState u32 sort_mode = 1; // 0: union find, 1: fuse union find, 2 deep fuse, 3 yahs std::vector sort_mode_names = {"Union Find", "Fuse", "Deep Fuse"}; - // auto sort + // pixel sort char frag_size_buf[16]; char score_threshold_buf[16]; - // auto cut + // pixel cut char auto_cut_threshold_buf[16]; char auto_cut_diag_window_for_pixel_mean_buf[16]; char auto_cut_smallest_frag_size_in_pixel_buf[16]; + bool auto_cut_with_extension = true; // considering the gap extension while cutting + s32 auto_cut_gap_loc_threshold = 3; // if the distance between the calculated cut loc and the gap is less than this value, the cut will be made at the gap AutoCurationState() { @@ -282,13 +286,14 @@ struct AutoCurationState map_state* Map_State, u32 number_of_pixels_1D, contigs* Contigs, - u08 exclude_flag=false) + u08 exclude_flag=false + ) // return cluster_flag { if (this->start_pixel > number_of_pixels_1D || this->end_pixel > number_of_pixels_1D || this->start_pixel < 0 || this->end_pixel < 0) { - // char buff[128]; - // snprintf((char*)buff, 128, "start_pixel(%d) and end_pixel(%d) should be within [0, Number_of_Pixels_1D(%d)-1]", this->start_pixel, this->end_pixel, number_of_pixels_1D); - // MY_CHECK((const char*) buff); + // fmt::println(stderr, + // "[AutoCurationState::get_selected_fragments]: start_pixel({}) should smaller than end_pixel({}), and they should be within [0, Number_of_Pixels_1D({})-1], file:{}, line:{}\n", + // this->start_pixel, this->end_pixel, number_of_pixels_1D, __FILE__, __LINE__); return ; } if (this->start_pixel > this->end_pixel) @@ -300,22 +305,7 @@ struct AutoCurationState { select_area.clearup(); } - - // define source frag - if (this->start_pixel>0) - { - u32 frag_id = Map_State->contigIds[this->start_pixel-1]; - if (Contigs->contigs_arr[frag_id].length >= this->smallest_frag_size_in_pixel) - { - select_area.source_frag_id = frag_id; - } - else - { - select_area.source_frag_id = -1; - fmt::print("The source_frag_len ({}) < smallest_length ({}), not set the source frag.\n", Contigs->contigs_arr[frag_id].length, this->smallest_frag_size_in_pixel); - } - } - + // push the selected frag id into select_area select_area.start_pixel = this->start_pixel; select_area.end_pixel = this->end_pixel; @@ -331,6 +321,21 @@ struct AutoCurationState } } + // define source frag + if (this->start_pixel>0 ) // if selected frags <= 5, still sort them together without clustering first. + { + u32 frag_id = Map_State->contigIds[this->start_pixel-1]; + if (Contigs->contigs_arr[frag_id].length >= this->smallest_frag_size_in_pixel) + { + select_area.source_frag_id = frag_id; + } + else + { + select_area.source_frag_id = -1; + fmt::print("The source_frag_len ({}) < smallest_length ({}), not set the source frag.\n", Contigs->contigs_arr[frag_id].length, this->smallest_frag_size_in_pixel); + } + } + // define sink frag if (this->end_pixel + 1 <= number_of_pixels_1D - 1) { @@ -347,6 +352,8 @@ struct AutoCurationState } // set the select_area to valid if (!select_area.selected_frag_ids.empty()) select_area.select_flag = 1; + + return; } void update_sort_area( diff --git a/src/copy_texture.cpp b/src/copy_texture.cpp index cab6001..89dc344 100644 --- a/src/copy_texture.cpp +++ b/src/copy_texture.cpp @@ -60,71 +60,71 @@ void get_linear_mask(std::vector& linear_array) } +#ifdef DEBUG + // Callback function for mouse scroll + void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) + { + // pass the scroll event to the ImGui + ImGuiIO& io = ImGui::GetIO(); + io.MouseWheelH += (f32)xoffset; + io.MouseWheel += (f32)yoffset; -// Callback function for mouse scroll -void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) -{ - // pass the scroll event to the ImGui - ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (f32)xoffset; - io.MouseWheel += (f32)yoffset; - - if (io.WantCaptureMouse) return ; - auto* show_state = reinterpret_cast(glfwGetWindowUserPointer(window)); - if (show_state->show_menu_window) return; - f32* zoomlevel = &show_state->zoomlevel; - // Adjust the zoom level based on the scroll input - *zoomlevel *= (1.0 - yoffset * 0.05f); // Change 0.1f to adjust sensitivity - - // Clamp the zoom level to prevent excessive zooming - if (*zoomlevel < 1.0 / 32. / 4.) *zoomlevel = 1.0 / 32. / 4.; - if (*zoomlevel > 1.0f) *zoomlevel = 1.f; -} + if (io.WantCaptureMouse) return ; + auto* show_state = reinterpret_cast(glfwGetWindowUserPointer(window)); + if (show_state->show_menu_window) return; + f32* zoomlevel = &show_state->zoomlevel; + // Adjust the zoom level based on the scroll input + *zoomlevel *= (1.0 - yoffset * 0.05f); // Change 0.1f to adjust sensitivity + // Clamp the zoom level to prevent excessive zooming + if (*zoomlevel < 1.0 / 32. / 4.) *zoomlevel = 1.0 / 32. / 4.; + if (*zoomlevel > 1.0f) *zoomlevel = 1.f; + } -void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) -{ - // pass the mouse button event to the ImGui - ImGuiIO& io = ImGui::GetIO(); - if (button >= 0 && button < IM_ARRAYSIZE(io.MouseDown)) { - io.MouseDown[button] = (action == GLFW_PRESS); - } - - if (io.WantCaptureMouse) return ; - auto* show_state = reinterpret_cast(glfwGetWindowUserPointer(window)); - if (show_state->show_menu_window) return; - if (button == GLFW_MOUSE_BUTTON_LEFT) { - if (action == GLFW_PRESS) { - show_state->isDragging = true; - glfwGetCursorPos(window, &show_state->lastMouseX, &show_state->lastMouseY); // Initial cursor position - } else if (action == GLFW_RELEASE) { - show_state->isDragging = false; + void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) + { + // pass the mouse button event to the ImGui + ImGuiIO& io = ImGui::GetIO(); + if (button >= 0 && button < IM_ARRAYSIZE(io.MouseDown)) { + io.MouseDown[button] = (action == GLFW_PRESS); + } + + if (io.WantCaptureMouse) return ; + auto* show_state = reinterpret_cast(glfwGetWindowUserPointer(window)); + if (show_state->show_menu_window) return; + if (button == GLFW_MOUSE_BUTTON_LEFT) { + if (action == GLFW_PRESS) { + show_state->isDragging = true; + glfwGetCursorPos(window, &show_state->lastMouseX, &show_state->lastMouseY); // Initial cursor position + } else if (action == GLFW_RELEASE) { + show_state->isDragging = false; + } } } -} -void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) -{ - // pass the cursor position to the ImGui - ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ImVec2((f32)xpos, (f32)ypos); + void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) + { + // pass the cursor position to the ImGui + ImGuiIO& io = ImGui::GetIO(); + io.MousePos = ImVec2((f32)xpos, (f32)ypos); - if (io.WantCaptureMouse) return ; - auto* show_state = reinterpret_cast(glfwGetWindowUserPointer(window)); - if (show_state->show_menu_window) return; + if (io.WantCaptureMouse) return ; + auto* show_state = reinterpret_cast(glfwGetWindowUserPointer(window)); + if (show_state->show_menu_window) return; - if (show_state->isDragging) { - double dx = xpos - show_state->lastMouseX; - double dy = ypos - show_state->lastMouseY; + if (show_state->isDragging) { + double dx = xpos - show_state->lastMouseX; + double dy = ypos - show_state->lastMouseY; - // Adjust the translation offset based on mouse movement (scale as needed) - show_state->translationOffset.x += static_cast(dx) * 0.00175f * show_state->zoomlevel; // Adjust sensitivity with 0.01f - show_state->translationOffset.y -= static_cast(dy) * 0.00175f * show_state->zoomlevel; + // Adjust the translation offset based on mouse movement (scale as needed) + show_state->translationOffset.x += static_cast(dx) * 0.00175f * show_state->zoomlevel; // Adjust sensitivity with 0.01f + show_state->translationOffset.y -= static_cast(dy) * 0.00175f * show_state->zoomlevel; - show_state->lastMouseX = xpos; - show_state->lastMouseY = ypos; + show_state->lastMouseX = xpos; + show_state->lastMouseY = ypos; + } } -} +#endif // DEBUG TexturesArray4AI::TexturesArray4AI( @@ -410,12 +410,10 @@ void TexturesArray4AI::copy_buffer_to_textures( glBindTexture(GL_TEXTURE_2D, 0); glDeleteTextures(1, &temptexture); - if (show_flag) - { - // show_collected_texture(); - show_collected_textures(); - } - + #ifdef DEBUG + if (show_flag) show_collected_textures(); + #endif // DEBUG + // return back to the original viewport glViewport(orignal_viewport[0], orignal_viewport[1], orignal_viewport[2], orignal_viewport[3]); @@ -513,10 +511,9 @@ void TexturesArray4AI::copy_buffer_to_textures_dynamic( glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &framebuffer); - if (show_flag) - { - show_collected_textures(); - } + #ifdef DEBUG + if (show_flag) show_collected_textures(); + #endif // DEBUG glViewport(orignal_viewport[0], orignal_viewport[1], orignal_viewport[2], orignal_viewport[3]); return ; @@ -701,151 +698,154 @@ void TexturesArray4AI::show_collected_texture() return ; } - -void TexturesArray4AI::show_collected_textures() -{ - GLuint tmp_texture2d_array; - this->prepare_tmp_texture2d_array(tmp_texture2d_array); - - GLFWwindow* window = glfwGetCurrentContext(); - if(!window) assert(0); - glfwSwapInterval(1); - glfwMakeContextCurrent(window); - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) assert(0); - glfwSetWindowShouldClose(window, false); - - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsLight(); - // Setup Platform/Renderer backends - ImGui_ImplGlfw_InitForOpenGL(window, true); - #ifdef __EMSCRIPTEN__ - ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); - #endif - ImGui_ImplOpenGL3_Init((const char*)"#version 330"); - // Our state - bool show_demo_window = false; - bool show_another_window = false; - glm::vec4 clear_color(0.45f, 0.55f, 0.60f, 1.00f); - bool m_pressed = false; - - int width = 1080, height = 1080; - glViewport(0, 0, width, height); - glDisable(GL_CULL_FACE); // can also see the back face - Show_State show_state; - - glfwSetWindowUserPointer(window, &show_state); - // register the scroll and mouse move callback function - glfwSetScrollCallback(window, scroll_callback); - glfwSetMouseButtonCallback(window, mouse_button_callback); - glfwSetCursorPosCallback(window, cursor_position_callback); - glUseProgram(shaderProgram); - GLcall(glUniform1i(glGetUniformLocation(shaderProgram, "texArray"), 0)); - glm::mat4 model_not_flipped = glm::scale( // 控制显示大小 - glm::mat4(1.0f), - glm::vec3( - 1.0f/(f32)num_textures_1d, - -1.0f/(f32)num_textures_1d, // flipped on y axis - 1.0f)); - glm::mat4 model_flipped = glm::scale( // 控制显示大小 - glm::rotate(model_not_flipped, glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)), - glm::vec3(1.0f, -1.0f, 1.0f)); - while (!glfwWindowShouldClose(window)) - { - int viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - f32 ratio_w_h = (f32)viewport[2] / (f32)viewport[3]; - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); - glClear(GL_COLOR_BUFFER_BIT); - glActiveTexture(GL_TEXTURE0); - glBindBuffer(GL_TEXTURE_2D_ARRAY, tmp_texture2d_array); - - glBindVertexArray(vao); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - glm::mat4 projection = glm::ortho( // 控制镜头远近 - -1.0f * ratio_w_h * show_state.zoomlevel, 1.0f * ratio_w_h * show_state.zoomlevel, - -1.0f * show_state.zoomlevel, 1.0f * show_state.zoomlevel, - -1.0f, 1.0f); +#ifdef DEBUG + void TexturesArray4AI::show_collected_textures() + { + GLuint tmp_texture2d_array; + this->prepare_tmp_texture2d_array(tmp_texture2d_array); - glm::mat4 model_tmp; - for (int row = 0; row < num_textures_1d; row++ )// 显示所有的 tile - { - for (int column = 0; column < num_textures_1d; column ++ ) - { - // if (row > column) continue; - u32 layer = texture_id_cal((u32)row, (u32)column, num_textures_1d); - GLcall(glUniform1i(glGetUniformLocation(shaderProgram, "layer"), (int)layer)); - model_tmp = glm::translate( // 控制显示位置 - glm::mat4(1.0f), - glm::vec3( - -1.0f + ((f32)column + 0.5f) * 2.0f / (f32)num_textures_1d + show_state.translationOffset.x, - 1.0f - ((f32)row + 0.5f) * 2.0f / (f32)num_textures_1d + show_state.translationOffset.y, - 0.0f) - ) * (row>column?model_flipped:model_not_flipped); - model_tmp = projection * model_tmp; - GLcall(glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model_tmp))); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + GLFWwindow* window = glfwGetCurrentContext(); + if(!window) assert(0); + glfwSwapInterval(1); + glfwMakeContextCurrent(window); + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) assert(0); + glfwSetWindowShouldClose(window, false); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForOpenGL(window, true); + #ifdef __EMSCRIPTEN__ + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); + #endif + ImGui_ImplOpenGL3_Init((const char*)"#version 330"); + // Our state + bool show_demo_window = false; + bool show_another_window = false; + glm::vec4 clear_color(0.45f, 0.55f, 0.60f, 1.00f); + bool m_pressed = false; + + int width = 1080, height = 1080; + glViewport(0, 0, width, height); + glDisable(GL_CULL_FACE); // can also see the back face + Show_State show_state; + + glfwSetWindowUserPointer(window, &show_state); + // register the scroll and mouse move callback function + glfwSetScrollCallback(window, scroll_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetCursorPosCallback(window, cursor_position_callback); + glUseProgram(shaderProgram); + GLcall(glUniform1i(glGetUniformLocation(shaderProgram, "texArray"), 0)); + glm::mat4 model_not_flipped = glm::scale( // 控制显示大小 + glm::mat4(1.0f), + glm::vec3( + 1.0f/(f32)num_textures_1d, + -1.0f/(f32)num_textures_1d, // flipped on y axis + 1.0f)); + glm::mat4 model_flipped = glm::scale( // 控制显示大小 + glm::rotate(model_not_flipped, glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)), + glm::vec3(1.0f, -1.0f, 1.0f)); + while (!glfwWindowShouldClose(window)) + { + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + f32 ratio_w_h = (f32)viewport[2] / (f32)viewport[3]; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); + glClear(GL_COLOR_BUFFER_BIT); + glActiveTexture(GL_TEXTURE0); + glBindBuffer(GL_TEXTURE_2D_ARRAY, tmp_texture2d_array); + + glBindVertexArray(vao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glm::mat4 projection = glm::ortho( // 控制镜头远近 + -1.0f * ratio_w_h * show_state.zoomlevel, 1.0f * ratio_w_h * show_state.zoomlevel, + -1.0f * show_state.zoomlevel, 1.0f * show_state.zoomlevel, + -1.0f, 1.0f); + + glm::mat4 model_tmp; + for (int row = 0; row < num_textures_1d; row++ )// 显示所有的 tile + { + for (int column = 0; column < num_textures_1d; column ++ ) + { + // if (row > column) continue; + u32 layer = texture_id_cal((u32)row, (u32)column, num_textures_1d); + GLcall(glUniform1i(glGetUniformLocation(shaderProgram, "layer"), (int)layer)); + model_tmp = glm::translate( // 控制显示位置 + glm::mat4(1.0f), + glm::vec3( + -1.0f + ((f32)column + 0.5f) * 2.0f / (f32)num_textures_1d + show_state.translationOffset.x, + 1.0f - ((f32)row + 0.5f) * 2.0f / (f32)num_textures_1d + show_state.translationOffset.y, + 0.0f) + ) * (row>column?model_flipped:model_not_flipped); + model_tmp = projection * model_tmp; + GLcall(glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model_tmp))); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + } } - } - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); - if (glfwGetKey(window, GLFW_KEY_U) == GLFW_PRESS) - { - if (!m_pressed) show_state.show_menu_window = !show_state.show_menu_window; - m_pressed = true; - } - if (glfwGetKey(window, GLFW_KEY_U) == GLFW_RELEASE) m_pressed = false; - - // Start the Dear ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). - if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); - // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. - if (show_state.show_menu_window ) - { - ImGui::Begin("Main menu"); // Create a window called "Hello, world!" and append into it. - ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state - - ImGui::ColorEdit3("clear color", (f32*)&clear_color[0]); // Edit 3 floats representing a color - ImGui::Text("Zoom level: %.4f", show_state.zoomlevel); - ImGui::Text("Translation Offset: (%.2f, %.2f)", show_state.translationOffset.x, show_state.translationOffset.y); - // ImGui::SameLine(); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); - ImGui::End(); - } + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); + if (glfwGetKey(window, GLFW_KEY_U) == GLFW_PRESS) + { + if (!m_pressed) show_state.show_menu_window = !show_state.show_menu_window; + m_pressed = true; + } + if (glfwGetKey(window, GLFW_KEY_U) == GLFW_RELEASE) m_pressed = false; + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + if (show_state.show_menu_window ) + { + ImGui::Begin("Main menu"); // Create a window called "Hello, world!" and append into it. + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + + ImGui::ColorEdit3("clear color", (f32*)&clear_color[0]); // Edit 3 floats representing a color + ImGui::Text("Zoom level: %.4f", show_state.zoomlevel); + ImGui::Text("Translation Offset: (%.2f, %.2f)", show_state.translationOffset.x, show_state.translationOffset.y); + // ImGui::SameLine(); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } - // 3. Show another simple window. - if (show_another_window) - { - ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) - ImGui::Text("Hello from another window!"); - if (ImGui::Button("Close Me")) - show_another_window = false; - ImGui::End(); - } + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } - // Rendering - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - glfwSwapBuffers(window); - glfwPollEvents(); - } + // Rendering + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glfwSwapBuffers(window); + glfwPollEvents(); + } - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - glDeleteTextures(1, &tmp_texture2d_array); - return ; -} + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + glDeleteTextures(1, &tmp_texture2d_array); + return ; + } +#else + void TexturesArray4AI::show_collected_textures(){return ;} +#endif // DEBUG @@ -885,27 +885,37 @@ void TexturesArray4AI::cal_compressed_hic( bool is_extension_required, bool is_massCenter_required, const SelectArea* select_area, + const AutoCurationState* auto_curation_state, f32 D_hic_ratio, u32 maximum_D, f32 min_hic_density) { check_copied_from_buffer(); - u08 using_select_area = (select_area != nullptr && select_area->select_flag)?1:0; - // clean the memory of compressed_hic_mx - frags->re_allocate_mem(Contigs, select_area); - if (frags->total_length != (using_select_area ? select_area->get_selected_len(Contigs) : num_pixels_1d)) + u08 using_select_area = (select_area && select_area->select_flag)?1:0; + + u08 cluster_flag = (auto_curation_state && auto_curation_state->num_clusters > 1)?1:0; + + // re-calculate the frags selected within this area + frags->re_allocate_mem( + Contigs, + select_area, + false, // used for cut + cluster_flag); + if (frags->total_length != (using_select_area ? select_area->get_selected_len(Contigs, cluster_flag) : num_pixels_1d)) { fmt::print( - "\n[Compress Hic] warning: frags->total_length({}) != num_pixels_1d ({}) ({}). file:{}, line:{}\n\n", + "\n[Compress Hic] warning: frags->total_length({}) != num_pixels_1d ({}) ({}) ({}). file:{}, line:{}\n\n", frags->total_length, - (using_select_area? (select_area->end_pixel - select_area->start_pixel + 1): num_pixels_1d), + using_select_area ? select_area->get_selected_len(Contigs, cluster_flag) : num_pixels_1d, (using_select_area?"selected area":"full area"), + cluster_flag?"clustering":"not clustering", __FILE__, __LINE__ ); assert(0); } + // clean the memory of compressed_hic_mx if (compressed_hic) { compressed_hic->re_allocate_mem( diff --git a/src/copy_texture.h b/src/copy_texture.h index a13f6e8..17784e8 100644 --- a/src/copy_texture.h +++ b/src/copy_texture.h @@ -30,142 +30,16 @@ SOFTWARE. #include "utilsPretextView.h" #include "genomeData.h" #include "auto_curation_state.h" +#include "frag_for_compress.h" #include +#include "self_matrix.h" -struct Frag4compress { - u32 num; - u32* frag_id = nullptr; - u32* startCoord = nullptr; // global start coordinate - u32* length = nullptr; - bool* inversed = nullptr; - u32 total_length; - u64* metaDataFlags = nullptr; - - Frag4compress(const contigs* Contigs) - : total_length(0) - { - re_allocate_mem(Contigs); - } - - ~Frag4compress() - { - cleanup(); - } - - void re_allocate_mem( - const contigs* Contigs, - const SelectArea* select_area=nullptr, - bool use_for_cut_flag=false) - { - cleanup(); - u08 using_select_area = (select_area != nullptr && select_area->select_flag)?1:0; - this->num = Contigs->numberOfContigs; - std::vector selected_frag_ids_tmp; - if (using_select_area ) - { - selected_frag_ids_tmp = select_area->selected_frag_ids; - this->num = select_area->selected_frag_ids.size(); - if (this->num < 2 && !use_for_cut_flag) - { - fmt::print( - stderr, - "The number_of_select_fragments_for_sorting({}) should not be less than 2, file:{}, line:{}\n", this->num, __FILE__, __LINE__); - assert(0); - } - if (select_area->source_frag_id >=0 && !use_for_cut_flag) - { - this->num ++; - selected_frag_ids_tmp.insert(selected_frag_ids_tmp.begin(), select_area->source_frag_id); - } - if (select_area->sink_frag_id >=0 && !use_for_cut_flag) - { - this->num ++; - selected_frag_ids_tmp.push_back(select_area->sink_frag_id); - } - } - else // global area - { - selected_frag_ids_tmp.resize(this->num); - std::iota(selected_frag_ids_tmp.begin(), selected_frag_ids_tmp.end(), 0); - } - - frag_id = new u32[num]; - startCoord = new u32[num]; - length = new u32[num]; - inversed = new bool[num]; - metaDataFlags = new u64[num]; - total_length = 0; - - // Initialize the startCoord, length, and inversed - frag_id[0] = selected_frag_ids_tmp[0]; - inversed[0] = false; - startCoord[0] = 0; - if (using_select_area) - { - s32 tmp = 0; - while (tmp < frag_id[0]) - { - startCoord[0] += Contigs->contigs_arr[tmp].length; - tmp ++; - } - if (tmp>=Contigs->numberOfContigs) - { - fmt::print( - stderr, - "The frag_id[0]({}) should be less than the number_of_contigs({}), file:{}, line:{}\n", - frag_id[0], Contigs->numberOfContigs, __FILE__, __LINE__); - assert(0); - } - } - length[0] = Contigs->contigs_arr[frag_id[0]].length; - metaDataFlags[0] = (Contigs->contigs_arr[frag_id[0]].metaDataFlags == nullptr)?0:*(Contigs->contigs_arr[frag_id[0]].metaDataFlags); - total_length = length[0]; - - for (u32 i = 1; i < num; i++) - { - u32 contig_id = selected_frag_ids_tmp[i]; - frag_id[i] = contig_id; - inversed[i] = false; // currently, this is not used - startCoord[i] = startCoord[i-1] + length[i-1]; - length[i] = Contigs->contigs_arr[contig_id].length; - metaDataFlags[i] = (Contigs->contigs_arr[contig_id].metaDataFlags == nullptr)?0:*(Contigs->contigs_arr[contig_id].metaDataFlags); - total_length += length[i]; - } - } - -private: - void cleanup() - { - if (frag_id) - { - delete[] frag_id; - frag_id = nullptr; - } - - if (startCoord) - { - delete[] startCoord; - startCoord = nullptr; - } - if (length) - { - delete[] length; - length = nullptr; - } - if (inversed) - { - delete[] inversed; - inversed = nullptr; - } - if (metaDataFlags) - { - delete[] metaDataFlags; - metaDataFlags = nullptr; - } - total_length = 0; - } -}; +#ifdef DEBUG + #include + #include + #include +#endif // DEBUG void scroll_callback(GLFWwindow* window, f64 xoffset, f64 yoffset); @@ -205,164 +79,6 @@ struct Show_State void get_linear_mask(f32* linear_array, u32 length); -template -class Matrix2D -{ -private: - T* data = nullptr; -public: - u32 row_num, col_num, length; - u32 shape[2]; - Matrix2D(u32 row_num_, u32 col_num_) - : row_num(row_num_), col_num(col_num_) - { - shape[0] = row_num_; - shape[1] = col_num_; - if (row_num_ <= 0 || col_num_ <= 0) - { - fprintf(stderr, "The row_num(%d), col_num(%d) should be larger than 0\n", row_num_, col_num_); - assert(0); - } - length = row_num * col_num; - data = new T[length]; - memset(data, 0, length * sizeof(T)); - } - - ~Matrix2D() - { - if (data) - { - delete[] data; - data = nullptr; - } - } - - void check_indexing(const u32& row, const u32& col) const - { - if (row >= row_num || col>= col_num) - { - fprintf(stderr, "Index [%d, %d] is out of the maximum [%d, %d]\n", row, col, row_num-1, col_num-1); - assert(0); - } - return ; - } - - // Access operator - T& operator()(const u32& row, const u32& col) - { - check_indexing(row, col); - return data[row * col_num + col]; - } - - const T& operator()(const u32& row, const u32& col) const - { - check_indexing(row, col); - return data[row * col_num + col]; - } - - T* get_data_ptr() const - { - return data; - } - -}; - - - -template -class Matrix3D -{ -private: - T* data = nullptr; -public: - u32 row_num, col_num, layer_num, length; - u32 shape[3]; - Matrix3D( - u32 row_num_, - u32 col_num_, - u32 layer_) - { - MY_CHECK(0); - re_allocate_mem(row_num_, col_num_, layer_); - } - - ~Matrix3D() - { - cleanup(); - } - - void cleanup() - { - - if (data) - { - delete[] data; - data = nullptr; - } - } - - void re_allocate_mem(u32 row_num_, u32 col_num_, u32 layer_) - { - cleanup(); - row_num = row_num_; - col_num = col_num_; - layer_num = layer_; - shape[0] = row_num_; - shape[1] = col_num_; - shape[2] = layer_; - length = row_num * col_num * layer_num; - - data = new T[length]; - - memset(data, 0, length * sizeof(T)); - } - - void check_indexing(const u32& row, const u32& col, const u32& layer) const - { - if (row >= row_num || col>= col_num || layer >= layer_num) - { - fprintf(stderr, "Index [%d, %d, %d] is out of the maximum [%d, %d, %d]\n", row, col, layer, row_num-1, col_num-1, layer_num-1); - assert(0); - } - return ; - } - - // Access operator - T& operator()(const u32& row, const u32& col, const u32& layer) { - check_indexing(row, col, layer); - return data[row * col_num * layer_num + col * layer_num + layer]; - } - - const T& operator()(const u32& row, const u32& col, const u32& layer) const { - check_indexing(row, col, layer); - return data[row * col_num * layer_num + col * layer_num + layer]; - } - - void set_one(const u32& row, const u32& col, const u32& layer, const T& value) - { - check_indexing(row, col, layer); - data[row * col_num * layer_num + col* layer_num + layer] = value; - } - - void output_to_file(FILE* fp) const - { - fmt::print(fp, "# Matrix shape: {} {} {}\n", row_num, col_num, layer_num); - for (u32 l = 0; l < layer_num; l++) - { - fmt::print(fp, "# layer: {}\n", l); - for (u32 i = 0; i < row_num; i++) - { - for (u32 j = 0; j < col_num; j++) - { - fmt::print(fp, "{:.4f} ", data[i * col_num * layer_num + j * layer_num + l]); - } - fmt::print(fp, "\n"); - } - } - } -}; - - struct CompressedExtensions { u32 num; // number of extensions: 2 : coverage, repeat_density @@ -698,6 +414,7 @@ class TexturesArray4AI bool is_extension_required=true, bool is_massCenter_required=true, const SelectArea* select_area=nullptr, + const AutoCurationState* auto_curation_state=nullptr, f32 D_hic_ratio=0.05f, u32 maximum_D=5000, f32 min_hic_density = 30.f); diff --git a/src/frag_cut_calculation.h b/src/frag_cut_calculation.h index 80023b6..fa5906c 100644 --- a/src/frag_cut_calculation.h +++ b/src/frag_cut_calculation.h @@ -211,7 +211,6 @@ class FragCutCal #ifdef DEBUG_OUTPUT_PIXEL_CUT_FILE // output the hic_pixel_density to text file std::string filename = fmt::format("{}/current_id_{}.txt", this->file_save_dir, frag_id); - fmt::print("[Pixel Cut]: output hic_pixel_density to {}\n", filename); std::ofstream out(filename); if (!out) { @@ -223,6 +222,7 @@ class FragCutCal out << hic_pixel_density_tmp[i] << std::endl; } out.close(); + fmt::print("[Pixel Cut]: output hic_pixel_density to {}\n", filename); #endif // DEBUG_OUTPUT_PIXEL_CUT_FILE std::vector break_points(0); diff --git a/src/frag_for_compress.h b/src/frag_for_compress.h new file mode 100644 index 0000000..96903b9 --- /dev/null +++ b/src/frag_for_compress.h @@ -0,0 +1,199 @@ + +#ifndef FRAG_FOR_COMPRESS_H +#define FRAG_FOR_COMPRESS_H + +#include +#include "utilsPretextView.h" +#include "genomeData.h" +#include "auto_curation_state.h" +#include + + +struct Frag4compress { + u32 num; // number of fragments + u32* frag_id = nullptr; + u32* startCoord = nullptr; // global start coordinate + u32* length = nullptr; + bool* inversed = nullptr; + u32 total_length; + u64* metaDataFlags = nullptr; + + Frag4compress(const contigs* Contigs) + : total_length(0) + { + re_allocate_mem(Contigs); + } + + Frag4compress() + : total_length(0){} + + ~Frag4compress() + { + cleanup(); + } + + void re_allocate_mem( // re-initialize frags within the selected area + const contigs* Contigs, + const SelectArea* select_area=nullptr, + bool use_for_cut_flag=false, + bool cluster_flag=false + ) + { + cleanup(); + u08 using_select_area = (select_area != nullptr && select_area->select_flag) ? 1:0; + + std::vector selected_frag_ids_tmp; + if (using_select_area ) + { + selected_frag_ids_tmp = select_area->selected_frag_ids; + this->num = select_area->selected_frag_ids.size(); + if (this->num < 2 && !use_for_cut_flag) + { + fmt::print( + stderr, + "The number_of_select_fragments_for_sorting({}) should not be less than 2, file:{}, line:{}\n", this->num, __FILE__, __LINE__); + assert(0); + } + + // add the source and sink fragments to the the selected fragments + if (select_area->source_frag_id >=0 && !use_for_cut_flag && !cluster_flag) + { + this->num ++; + selected_frag_ids_tmp.insert(selected_frag_ids_tmp.begin(), select_area->source_frag_id); + } + if (select_area->sink_frag_id >=0 && !use_for_cut_flag && !cluster_flag) + { + this->num ++; + selected_frag_ids_tmp.push_back(select_area->sink_frag_id); + } + } + else // global sort or cut + { + this->num = Contigs->numberOfContigs; + selected_frag_ids_tmp.resize(this->num); + std::iota(selected_frag_ids_tmp.begin(), selected_frag_ids_tmp.end(), 0); + } + + this->frag_id = new u32[num]; + this->startCoord = new u32[num]; + this->length = new u32[num]; + this->inversed = new bool[num]; + this->metaDataFlags = new u64[num]; + this->total_length = 0; + + // Initialize the startCoord, length, and inversed + frag_id[0] = selected_frag_ids_tmp[0]; + inversed[0] = false; + startCoord[0] = 0; + if (using_select_area) + { + s32 tmp = 0; + while (tmp < frag_id[0]) + { + if (tmp >= Contigs->numberOfContigs) + { + fmt::print( + stderr, + "The frag_id[0]({}) should be less than the number_of_contigs({}), file:{}, line:{}\n", + frag_id[0], Contigs->numberOfContigs, __FILE__, __LINE__); + assert(0); + } + startCoord[0] += Contigs->contigs_arr[tmp++].length; + } + } + length[0] = Contigs->contigs_arr[frag_id[0]].length; + metaDataFlags[0] = (Contigs->contigs_arr[frag_id[0]].metaDataFlags == nullptr)?0:*(Contigs->contigs_arr[frag_id[0]].metaDataFlags); + total_length = length[0]; + + for (u32 i = 1; i < num; i++) + { + frag_id[i] = selected_frag_ids_tmp[i]; + inversed[i] = false; // currently, this is not used + startCoord[i] = startCoord[i-1] + length[i-1]; + length[i] = Contigs->contigs_arr[frag_id[i]].length; + metaDataFlags[i] = (Contigs->contigs_arr[frag_id[i]].metaDataFlags == nullptr)?0:*(Contigs->contigs_arr[frag_id[i]].metaDataFlags); + total_length += length[i]; + } + } + + void re_allocate_mem( // re-initialize frags within selected fragments + const contigs* Contigs, + const std::vector& selected_frag_ids_tmp + ) + { + cleanup(); + this->num = selected_frag_ids_tmp.size(); + if (this->num < 2) + { + fmt::print( + stderr, + "[Frag4compress::re_allocate_mem::warning]: number_of_select_fragments_for_sorting({}) should not < 2, file:{}, line:{}\n", this->num, __FILE__, __LINE__); + } + + this->frag_id = new u32[num]; + this->startCoord = new u32[num]; + this->length = new u32[num]; + this->inversed = new bool[num]; + this->metaDataFlags = new u64[num]; + this->total_length = 0; + + // Initialize the startCoord, length, and inversed + s32 global_frag_index = 0; + for (s32 i = 0; i < num; i++) + { + frag_id[i] = selected_frag_ids_tmp[i]; + inversed[i] = false; + length[i] = Contigs->contigs_arr[frag_id[i]].length; + total_length += length[i]; + metaDataFlags[i] = (Contigs->contigs_arr[frag_id[i]].metaDataFlags == nullptr)?0:*(Contigs->contigs_arr[frag_id[i]].metaDataFlags); + while (global_frag_index < frag_id[i]) + { + if (global_frag_index >= Contigs->numberOfContigs) + { + fmt::print( + stderr, + "The frag_id[0]({}) should be less than the number_of_contigs({}), file:{}, line:{}\n", + frag_id[0], Contigs->numberOfContigs, __FILE__, __LINE__); + assert(0); + } + startCoord[i] += Contigs->contigs_arr[global_frag_index++].length; + } + } + } + + +private: + void cleanup() + { + if (frag_id) + { + delete[] frag_id; + frag_id = nullptr; + } + + if (startCoord) + { + delete[] startCoord; + startCoord = nullptr; + } + if (length) + { + delete[] length; + length = nullptr; + } + if (inversed) + { + delete[] inversed; + inversed = nullptr; + } + if (metaDataFlags) + { + delete[] metaDataFlags; + metaDataFlags = nullptr; + } + total_length = 0; + num = 0; + } +}; + +#endif // FRAG4COMPRESS_H \ No newline at end of file diff --git a/src/frag_sort.cpp b/src/frag_sort.cpp index b158388..8c385e5 100644 --- a/src/frag_sort.cpp +++ b/src/frag_sort.cpp @@ -436,7 +436,8 @@ void FragSortTool::sort_according_likelihood_unionFind( SelectArea& select_area, const f32 threshold, const Frag4compress* frags, - bool sort_according_len_flag) const + const bool sort_according_len_flag +) const { // this is used to fix the source and sink during local sort s32 source_frag_id = -1, source_chain_id = -1, @@ -672,6 +673,12 @@ void FragSortTool::sort_according_likelihood_unionFind_doFuse( // 计算的分位数 f32 threshold_val = percentile_cal(likelihood_table.data, likelihood_table.size, this->threshold_ratio); + if (threshold_val < 0) + { + fmt::print(stderr, "[Pixel Sort::error]: threshold at {}: {}, fixed_threshold: {}, using: {}, table.size={} \n", this->threshold_ratio, threshold_val, threshold, threshold_val, likelihood_table.size); + assert(0); + threshold_val = 0; + } f32 threshold_using = std::min(threshold_val, threshold); fmt::print(stderr, "[Pixel Sort]: link score threshold at {}: {}, fixed_threshold: {}, using: {}\n", this->threshold_ratio, threshold_val, threshold, threshold_using); diff --git a/src/frag_sort.h b/src/frag_sort.h index 9400d79..969f5b2 100644 --- a/src/frag_sort.h +++ b/src/frag_sort.h @@ -74,7 +74,7 @@ class FragSortTool SelectArea& select_area, const f32 threshold=-0.001, const Frag4compress* frags=nullptr, - bool sort_according_len_flag=true) const; + const bool sort_according_len_flag=true) const; void sort_according_likelihood_unionFind_doFuse( const LikelihoodTable& likelihood_table, @@ -84,7 +84,7 @@ class FragSortTool const Frag4compress* frags=nullptr, const bool doStageOne=true, const bool doStageTwo=false, - bool sort_according_len_flag=true) const; + const bool sort_according_len_flag=true) const; void sort_according_yahs( const LikelihoodTable& likelihood_table, @@ -92,6 +92,61 @@ class FragSortTool SelectArea& select_area, const f32 threshold=-0.001, const Frag4compress* frags=nullptr) const; + + + void sort_method_mask( + const LikelihoodTable& likelihood_table, + FragsOrder& frags_order, + SelectArea& selected_area, + const AutoCurationState& auto_curation_state, + const Frag4compress* frags=nullptr, + const bool sort_according_len_flag=true + ) const + { + if (auto_curation_state.sort_mode == 0 || !selected_area.select_flag) // sort with union find if sorting the whole genome + { + this->sort_according_likelihood_unionFind( + likelihood_table, + frags_order, + selected_area, + auto_curation_state.link_score_threshold, + frags); + } + else if (auto_curation_state.sort_mode == 1) + { + this->sort_according_likelihood_unionFind_doFuse( + likelihood_table, + frags_order, + selected_area, + auto_curation_state.link_score_threshold, + frags, true, true); + } + else if (auto_curation_state.sort_mode == 2) + { + this->sort_according_likelihood_unionFind_doFuse( + likelihood_table, + frags_order, + selected_area, + auto_curation_state.link_score_threshold, + frags, false, true); + } + else if (auto_curation_state.sort_mode == 3) // not finished yet + { + this->sort_according_yahs( + likelihood_table, + frags_order, + selected_area, + auto_curation_state.link_score_threshold, + frags + ); + } + else + { + fprintf(stderr, "[Pixel Sort] Error: Unknown sort mode (%d)\n", auto_curation_state.sort_mode); + assert(0); + } + } + }; diff --git a/src/frags_order.h b/src/frags_order.h index ceaf93e..8aa3b0f 100644 --- a/src/frags_order.h +++ b/src/frags_order.h @@ -8,37 +8,29 @@ struct FragsOrder { private: u32 num_frags; // number of all the fragments - u32 num_chromosomes; - u32* num_frags_in_chromosomes = nullptr; // [num_chromosomes] - s32** order = nullptr; // [num_chromosomes, num_frags_in_chromosomes], + - represents the direction of the fragment, start from 1 + std::vector> order; // [num_chromosomes, num_frags_in_chromosomes], + - represents the direction of the fragment, start from 1 void cleanup() { - if (order) - { - for (u32 i = 0; i < num_chromosomes; i++) + num_frags = 0; + if (order.size() > 0) + { + for (u32 i = 0; i < order.size(); i++) { - if (order[i]) - { - delete[] order[i]; - order[i] = nullptr; - } + if (order[i].size() > 0) order[i].clear(); } - delete[] order; - order = nullptr; + order.clear(); } - - if (num_frags_in_chromosomes) - { - delete[] num_frags_in_chromosomes; - num_frags_in_chromosomes = nullptr; - } - num_chromosomes = 0; } public: FragsOrder(u32 num_frags_) - :num_frags(num_frags_), num_chromosomes(0), num_frags_in_chromosomes(nullptr), order(nullptr) - {} + { + cleanup(); + num_frags = num_frags_; + order.push_back(std::vector(num_frags)); + for (s32 i = 0; i < num_frags; i++) order[0][i] = i + 1; + return ; + } ~FragsOrder() { @@ -47,71 +39,64 @@ struct FragsOrder u32 get_num_frags() const {return num_frags;} - void set_order(const std::vector> chromosomes) + void set_order(const std::vector>& chromosomes) { cleanup(); // make sure the memory is released - u32 tmp = 0; for (auto chromosome : chromosomes) { - if (chromosome.size()!=0) tmp += 1; + if (chromosome.size()==0) continue; + num_frags += chromosome.size(); + order.emplace_back(chromosome.begin(), chromosome.end()); } - num_chromosomes = tmp; - num_frags_in_chromosomes = new u32[num_chromosomes]; - order = new s32*[num_chromosomes]; - u32 cnt= 0; - for (u32 i = 0; i < chromosomes.size(); i++) - { - if (chromosomes[i].size() == 0) continue; - num_frags_in_chromosomes[cnt] = chromosomes[i].size(); - order[cnt] = new s32[num_frags_in_chromosomes[cnt]]; - for (u32 j = 0; j < num_frags_in_chromosomes[cnt]; j++) - { - order[cnt][j] = chromosomes[i][j]; - if (order[cnt][j] == 0) - { - std::cerr << "Error: the order should not contain ("<< order[cnt][j] << ")." << std::endl; - assert(0); - } - } - cnt++ ; - } - if (cnt != num_chromosomes) + } + + std::vector get_order_without_chromosomeInfor() const + { + if (num_frags == 0) { - fprintf(stderr, "The cnt(%d) != num_chromosomes(%d)\n", cnt, num_chromosomes); + std::cerr << "Error: the order is not set yet" << std::endl; assert(0); } + std::vector output; + for (auto& chromorsome:order) for (auto& frag: chromorsome) output.push_back(frag); + return output; } - std::vector get_order_without_chromosomeInfor() const + void print_order() const { - if (num_chromosomes == 0) + if (num_frags == 0) { std::cerr << "Error: the order is not set yet" << std::endl; assert(0); } - std::vector output; - for (u32 i = 0; i < num_chromosomes; i++) + std::cout << "[Pixel Sort::print_order] Sorting results \n"; + for (u32 i = 0; i < order.size(); i++) { - for (u32 j = 0; j < num_frags_in_chromosomes[i]; j++) + std::cout << "\tChr [" << i+1 << "]: "; + for (u32 j = 0; j < order[i].size(); j++) { - output.push_back(order[i][j]); + std::cout << order[i][j] << " "; } + std::cout << std::endl; } + std::cout << std::endl; + return ; + } - #ifdef DEBUG // echo the results - std::cout << "\n\nSorting results \n"; - for (u32 i = 0; i < num_chromosomes; i++) - { - std::cout << "Chr [" << i+1 << "]: "; - for (u32 j = 0; j < num_frags_in_chromosomes[i]; j++) - { - std::cout << order[i][j] << " "; - } - std::cout << std::endl; - } - #endif //DEBUG - return output; + FragsOrder( + const std::vector& frags_order_list, + const std::vector>& clusters) + { + cleanup(); + for (u32 i = 0; i < frags_order_list.size(); i++) // 遍历所有的cluster + { + this->num_frags += clusters[i].size(); + auto tmp_order = frags_order_list[i].get_order_without_chromosomeInfor(); + for (auto& tmp:tmp_order) + tmp = (clusters[i][std::abs(tmp)-1] + 1) * (tmp>0?1:-1); + this->order.push_back(std::move(tmp_order)); + } } }; diff --git a/src/genomeData.h b/src/genomeData.h index 7453bd4..8bfb0a1 100644 --- a/src/genomeData.h +++ b/src/genomeData.h @@ -28,8 +28,8 @@ SOFTWARE. #include "utilsPretextView.h" #include "showWindowData.h" - -#define Max_Number_of_Contigs 4096 +// NOTE: if the number exceed this value, the contig counted as N % 16384, for example, contig 16384 will be counted as 0, 16385 1, 16386 as 2 +#define Max_Number_of_Contigs 16384 // 16384 // originally 4096 struct file_atlas_entry @@ -229,6 +229,48 @@ extension_sentinel } return false; } + + /* + get the id of extension whose name including the tmp name + return: -1 if not found + id of the extension if found (return the first found id if multiple found) + */ + s32 get_graph_id(const std::string name) + { + s32 id = 0; + TraverseLinkedList(this->head, extension_node) + { + if (node->type == extension_graph) + { + if (std::string((char*)((graph*)node->extension)->name).find(name) != std::string::npos) + { + fmt::println("[extension_sentinel] Found the Extension name: {}, id: {}", (char*)((graph*)node->extension)->name, id); + return id; + } + } + id++; + } + fmt::println("[extension_sentinel] Not found the Extension name: {}", name); + return -1; + } + + const u32* get_graph_data_ptr(const std::string name) + { + s32 id = get_graph_id(name); + if (id < 0 ) return nullptr; + extension_node* node = this->head; + while ( id-- > 0 ) + { + if (node && node->next) node = node->next; + else + { + fmt::println("[Pixel Cut::error]: The graph id is out of range"); + assert(0); + } + } + return ((graph*)(node->extension))->data; + } + }; diff --git a/src/hic_figure.h b/src/hic_figure.h index de1dd68..fa303a3 100644 --- a/src/hic_figure.h +++ b/src/hic_figure.h @@ -20,6 +20,7 @@ #include "utilsPretextView.h" #include "copy_texture.h" + #ifndef STB_IMAGE_RESIZE_IMPLEMENTATION #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize.h" diff --git a/src/kmeans_pybind.h b/src/kmeans_pybind.h new file mode 100644 index 0000000..3a03523 --- /dev/null +++ b/src/kmeans_pybind.h @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include "fmt/core.h" +#include "likelihood_table.h" + +namespace py = pybind11; + + +template +py::array_t create_tensor(const int n, const int channel, const T* data_ptr) { + // 总元素个数 + size_t total_size = n * n * channel; + + // 指定 shape 和 strides + std::vector shape = {n, n, channel}; + std::vector strides = { + static_cast(n * channel * sizeof(T)), // 跨 n 行 + static_cast(channel * sizeof(T)), // 跨 n 列 + static_cast(sizeof(T)) // 跨特征维 + }; + + return py::array_t(shape, strides, data_ptr); +} + + + +class KmeansClusters +{ + public: + bool is_init = false; + py::module kmeans_utils; + KmeansClusters() + { + try + { + // 将当前目录加入 Python 模块搜索路径 + py::module sys = py::module::import("sys"); + py::module os = py::module::import("os"); + #ifdef DEBUG + sys.attr("path").attr("append")("./build_cmake"); + #else // NDEBUG + #ifdef __APPLE__ + sys.attr("path").attr("append")("./PretextViewAI.app/Contents/Resources"); + #elif def __linux__ + sys.attr("path").attr("append")("."); + #else // _WIN32 + sys.attr("path").attr("append")("."); + #endif + #endif // DEBUG + std::string current_dir = os.attr("getcwd")().cast(); + fmt::print("Current dir: {}\n", current_dir); + + fmt::print("PythonPaths: "); + auto python_paths = sys.attr("path"); + for (auto& path : python_paths) + { + fmt::print("\t{}\n", path.cast()); + } + + // 导入 Python 模块 + kmeans_utils = py::module::import("kmeans_utils"); + fmt::print("Python kmeans module loaded successfully\n"); + is_init = true; + } + catch(const std::exception& e) + { + std::cerr << e.what() << '\n'; + } + + } + + + void print_clusters(const std::vector>& clusters) const + { + for (int i = 0; i < clusters.size(); ++i) + { + if (clusters[i].empty()) continue; + fmt::print("cluster id: {}, size: {}, frags: \t", i, clusters[i].size()); + for (auto& frag_id : clusters[i]) + { + fmt::print("{}, ", frag_id); + } + fmt::print("\n"); + } + } + + + std::vector> kmeans_func(const int k, const Matrix3D* mat3d=nullptr) + { + if (!is_init) + { + std::cerr << "Error: KmeansClusters not initialized" << std::endl; + assert(0); + } + py::array_t labels ; + + // cal py function to get cluster labels + py::array_t adj_matrix = create_tensor(mat3d->row_num, mat3d->layer_num, mat3d->get_data_ptr()); + labels = kmeans_utils.attr("run_kmeans")(k, adj_matrix); + + // 解析 NumPy 数组结果 + py::buffer_info buf = labels.request(); + int* cluster_ids = static_cast(buf.ptr); + std::vector> clusters(k); + for (int i = 0; i < buf.size; ++i) + { + if (cluster_ids[i] < 0 || cluster_ids[i] >= k) + { + fmt::println(stderr, "Error: cluster id out of range"); + assert(0); + } + clusters[cluster_ids[i]].push_back(i); + } + #ifdef DEBUG + this->print_clusters(clusters); + #endif // DEBUG + return clusters; + } + + +}; \ No newline at end of file diff --git a/src/likelihood_table.h b/src/likelihood_table.h index 20d1713..fc9a9f8 100644 --- a/src/likelihood_table.h +++ b/src/likelihood_table.h @@ -19,11 +19,20 @@ struct LikelihoodTable const Matrix3D* compressed_hic, const f32 threshold=10.f/32769.f, const std::vector& exclude_tag_idx=std::vector(), - const u32 num_pixels_1d=32768) - :num_frags(frags->num), frags(frags), data(nullptr) + const u32 num_pixels_1d=32768, + const std::vector* selected_frags_id=nullptr) + :num_frags(frags->num), frags(frags) { + frags = frags; + num_frags = frags->num; + if (selected_frags_id && frags->num != selected_frags_id->size()) + { + fmt::print(stderr, "Error: frags->num({}) != selected_frags_id.size({})\n", frags->num, selected_frags_id->size()); + assert(0); + } + auto is_exclude_tag = [&](u32 idx) -> bool - { + { if ((f32)frags->length[idx]/(f32)num_pixels_1d <= threshold) { this->excluded_fragment_idx.insert(idx); @@ -44,18 +53,29 @@ struct LikelihoodTable size = num_frags * num_frags * 4; data = new f32[size]; for (u32 i = 0; i frags->num / 2 ) + { + fmt::print(stderr, "[LikelihoodTable::warning]: too many excluded fragments: {} / {}\n", excluded_fragment_idx.size(), frags->num); + } + + return ; } ~LikelihoodTable() @@ -104,12 +124,11 @@ struct LikelihoodTable { fmt::print( ofs, - "{}\t{}\t{}\t{}\t{}\n", + "{}\t{}\t{}\t{}\n", i, frags->length[i], 0, - 0, - 0); + 0 ); } fmt::print(ofs, "\n"); diff --git a/src/self_matrix.h b/src/self_matrix.h new file mode 100644 index 0000000..0b0db1c --- /dev/null +++ b/src/self_matrix.h @@ -0,0 +1,168 @@ +#ifndef SELF_MATRIX_H +#define SELF_MATRIX_H + + +template +class Matrix2D +{ +private: + T* data = nullptr; +public: + u32 row_num, col_num, length; + u32 shape[2]; + Matrix2D(u32 row_num_, u32 col_num_) + : row_num(row_num_), col_num(col_num_) + { + shape[0] = row_num_; + shape[1] = col_num_; + if (row_num_ <= 0 || col_num_ <= 0) + { + fprintf(stderr, "The row_num(%d), col_num(%d) should be larger than 0\n", row_num_, col_num_); + assert(0); + } + length = row_num * col_num; + data = new T[length]; + memset(data, 0, length * sizeof(T)); + } + + ~Matrix2D() + { + if (data) + { + delete[] data; + data = nullptr; + } + } + + void check_indexing(const u32& row, const u32& col) const + { + if (row >= row_num || col>= col_num) + { + fprintf(stderr, "Index [%d, %d] is out of the maximum [%d, %d]\n", row, col, row_num-1, col_num-1); + assert(0); + } + return ; + } + + // Access operator + T& operator()(const u32& row, const u32& col) + { + check_indexing(row, col); + return data[row * col_num + col]; + } + + const T& operator()(const u32& row, const u32& col) const + { + check_indexing(row, col); + return data[row * col_num + col]; + } + + T* get_data_ptr() const + { + return data; + } + +}; + + + +template +class Matrix3D +{ +private: + T* data = nullptr; +public: + u32 row_num, col_num, layer_num, length; + u32 shape[3]; + Matrix3D( + u32 row_num_, + u32 col_num_, + u32 layer_) + { + MY_CHECK(0); + re_allocate_mem(row_num_, col_num_, layer_); + } + + ~Matrix3D() + { + cleanup(); + } + + void cleanup() + { + + if (data) + { + delete[] data; + data = nullptr; + } + } + + void re_allocate_mem(u32 row_num_, u32 col_num_, u32 layer_) + { + cleanup(); + row_num = row_num_; + col_num = col_num_; + layer_num = layer_; + shape[0] = row_num_; + shape[1] = col_num_; + shape[2] = layer_; + length = row_num * col_num * layer_num; + + data = new T[length]; + + memset(data, 0, length * sizeof(T)); + } + + void check_indexing(const u32& row, const u32& col, const u32& layer) const + { + if (row >= row_num || col>= col_num || layer >= layer_num) + { + fprintf(stderr, "Index [%d, %d, %d] is out of the maximum [%d, %d, %d]\n", row, col, layer, row_num-1, col_num-1, layer_num-1); + assert(0); + } + return ; + } + + // Access operator + T& operator()(const u32& row, const u32& col, const u32& layer) { + check_indexing(row, col, layer); + return data[row * col_num * layer_num + col * layer_num + layer]; + } + + const T& operator()(const u32& row, const u32& col, const u32& layer) const { + check_indexing(row, col, layer); + return data[row * col_num * layer_num + col * layer_num + layer]; + } + + void set_one(const u32& row, const u32& col, const u32& layer, const T& value) + { + check_indexing(row, col, layer); + data[row * col_num * layer_num + col* layer_num + layer] = value; + } + + void output_to_file(FILE* fp) const + { + fmt::print(fp, "# Matrix shape: {} {} {}\n", row_num, col_num, layer_num); + for (u32 l = 0; l < layer_num; l++) + { + fmt::print(fp, "# layer: {}\n", l); + for (u32 i = 0; i < row_num; i++) + { + for (u32 j = 0; j < col_num; j++) + { + fmt::print(fp, "{:.4f} ", data[i * col_num * layer_num + j * layer_num + l]); + } + fmt::print(fp, "\n"); + } + } + } + + const T* get_data_ptr() const + { + return data; + } +}; + + +#endif // SELF_MATRIX_H \ No newline at end of file diff --git a/src/showWindowData.h b/src/showWindowData.h index 4132b4e..c8c72d4 100644 --- a/src/showWindowData.h +++ b/src/showWindowData.h @@ -225,7 +225,7 @@ enum global_mode mode_scaff_edit = 3, mode_meta_edit = 4, mode_extension = 5, - mode_selectExclude_sort_area = 6 + mode_select_sort_area = 6 }; diff --git a/src/spectral_cluster.h b/src/spectral_cluster.h new file mode 100644 index 0000000..366924d --- /dev/null +++ b/src/spectral_cluster.h @@ -0,0 +1,141 @@ + +#ifndef SPECTRAL_CLUSTER_H +#define SPECTRAL_CLUSTER_H + + +#include +#include +#include +#include +#include +#include +#include +#include "self_matrix.h" + + +// Gaussian kernel +double gaussian_kernel(const Eigen::VectorXd& xi, const Eigen::VectorXd& xj, double sigma) { + return exp(-(xi - xj).squaredNorm() / (2 * sigma * sigma)); +} + +// Similarity matrix +Eigen::MatrixXd buildSimilarityMatrix(const Eigen::MatrixXd& X, double sigma) { + int n = X.rows(); + Eigen::MatrixXd W(n, n); + for (int i = 0; i < n; ++i) + for (int j = 0; j < n; ++j) + W(i, j) = gaussian_kernel(X.row(i), X.row(j), sigma); + return W; +} + +// Degree matrix +Eigen::MatrixXd buildDegreeMatrix(const Eigen::MatrixXd& W) { + int n = W.rows(); + Eigen::MatrixXd D = Eigen::MatrixXd::Zero(n, n); + for (int i = 0; i < n; ++i) + D(i, i) = W.row(i).sum(); + return D; +} + +// Basic KMeans clustering +Eigen::VectorXi kmeans(const Eigen::MatrixXd& data, int k, int max_iters = 100) { + int n = data.rows(); + int d = data.cols(); + Eigen::VectorXi labels(n); + Eigen::MatrixXd centroids = Eigen::MatrixXd::Zero(k, d); + + // Randomly initialize centroids + std::srand(static_cast(time(nullptr))); + for (int i = 0; i < k; ++i) + centroids.row(i) = data.row(std::rand() % n); + + for (int iter = 0; iter < max_iters; ++iter) { + // Step 1: Assign labels + for (int i = 0; i < n; ++i) { + double min_dist = std::numeric_limits::max(); + int best_cluster = 0; + for (int j = 0; j < k; ++j) { + double dist = (data.row(i) - centroids.row(j)).squaredNorm(); + if (dist < min_dist) { + min_dist = dist; + best_cluster = j; + } + } + labels[i] = best_cluster; + } + + // Step 2: Update centroids + Eigen::MatrixXd new_centroids = Eigen::MatrixXd::Zero(k, d); + Eigen::VectorXi count = Eigen::VectorXi::Zero(k); + for (int i = 0; i < n; ++i) { + new_centroids.row(labels[i]) += data.row(i); + count[labels[i]]++; + } + for (int j = 0; j < k; ++j) + if (count[j] > 0) + new_centroids.row(j) /= count[j]; + + // Check convergence + if ((centroids - new_centroids).norm() < 1e-4) + break; + centroids = new_centroids; + } + + return labels; +} + + +Eigen::MatrixXd selfmatrix_to_eigenMatrix(const Matrix3D& adj_mat) { + Eigen::MatrixXd norm_mat(adj_mat.row_num, adj_mat.col_num); + float max_tmp = 0; + for (int i = 0; i < norm_mat.rows(); ++i) { + for (int j = 0; j < norm_mat.cols(); ++j) { + max_tmp = -1.; + for (int k = 0; k < 4; ++k) { + max_tmp = std::max(max_tmp, adj_mat(i, j, k)); + } + norm_mat(i, j) = max_tmp; + } + } + return norm_mat; +} + + +std::vector> labels_to_clusters(const Eigen::VectorXi& labels, int k) { + std::vector> clusters(k); + for (int i = 0; i < labels.size(); ++i) { + clusters[labels[i]].push_back(i); + } + return clusters; +} + + +// Main spectral clustering function +std::vector> spectral_clustering(const Matrix3D& adj_mat, int k) { + + // Step 1: Similarity matrix + Eigen::MatrixXd W = selfmatrix_to_eigenMatrix(adj_mat); + + // Step 2: Degree matrix + Eigen::MatrixXd D = buildDegreeMatrix(W); + // Step 3: Laplacian + Eigen::MatrixXd L = D - W; + // Step 4: Eigen decomposition + Eigen::SelfAdjointEigenSolver eigensolver(L); + if (eigensolver.info() != Eigen::Success) + throw std::runtime_error("Eigen decomposition failed"); + + // Step 5: Use first k smallest eigenvectors + Eigen::MatrixXd eigvecs = eigensolver.eigenvectors().leftCols(k); + + // Optional: Row normalization + for (int i = 0; i < eigvecs.rows(); ++i) + eigvecs.row(i).normalize(); + + // Step 6: KMeans on rows of eigvecs + Eigen::VectorXi labels = kmeans(eigvecs, k); + + return labels_to_clusters(labels, k); +} + +#endif // SPECTRAL_CLUSTER_H \ No newline at end of file diff --git a/src/utilsPretextView.h b/src/utilsPretextView.h index 13247e9..84848b2 100644 --- a/src/utilsPretextView.h +++ b/src/utilsPretextView.h @@ -55,11 +55,6 @@ SOFTWARE. -#include -#include -#include - - /* original version not commented here */ @@ -238,6 +233,7 @@ template T percentile_cal(T* data, u32 size, f32 percentile=0.95) { u32 heap_size = (u32)((f32)size * (1-percentile)); + if (heap_size <=0) return 1.; std::priority_queue, std::greater> min_heap; for (auto i = 0; i < size; i++) { @@ -251,6 +247,18 @@ T percentile_cal(T* data, u32 size, f32 percentile=0.95) min_heap.push(data[i]); } } + + if (min_heap.top() < 0 ) + { + s32 cnt_neg_1 = 0; + for (int i = 0; i< size; i++) if (data[i] < 0) cnt_neg_1++; + fmt::print( + stderr, + "[percentile_cal::error] Number of negative value: {} / {}, file:{}, line:{}\n", + cnt_neg_1, size, __FILE__, __LINE__); + assert(0); + } + if (min_heap.size() > 0) return min_heap.top(); else return 0; diff --git a/subprojects/eigen b/subprojects/eigen new file mode 160000 index 0000000..d6b23a2 --- /dev/null +++ b/subprojects/eigen @@ -0,0 +1 @@ +Subproject commit d6b23a2256b0c5d77bdc9988b8a82a9e79b5c5af diff --git a/subprojects/pybind11 b/subprojects/pybind11 new file mode 160000 index 0000000..a28ea8c --- /dev/null +++ b/subprojects/pybind11 @@ -0,0 +1 @@ +Subproject commit a28ea8ccc5b98af73a2306ca0e43bcf0c82d39d8 From d3b82acab36273a4faceaa1302bbf78cfc989d38 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Tue, 6 May 2025 17:06:50 +0100 Subject: [PATCH 02/38] info --- Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Info.plist b/Info.plist index d9961f5..a0bd507 100644 --- a/Info.plist +++ b/Info.plist @@ -13,7 +13,7 @@ CFBundleIconFile icon.icns CFBundleShortVersionString - 1.0.3 + 1.0.4 CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType From 3eed77c7464f92a13215a9ae10af7b6374a35572 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Tue, 6 May 2025 17:31:32 +0100 Subject: [PATCH 03/38] correct for mac_x86_64 building --- .github/workflows/build.yml | 9 ++++++++- CMakeLists.txt | 12 ++++++------ src/kmeans_pybind.h | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 71ac201..9214004 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,8 +18,15 @@ jobs: target_arch: arm64 - os: ubuntu-latest target_arch: arm64 + include: + - os: macos-latest + target_arch: x86_64 + runs-on: macos-12 + - os: macos-latest + target_arch: arm64 + runs-on: macos-latest - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.runs-on || matrix.os }} steps: - name: Check runner OS run: echo "Runner OS is ${{ runner.os }}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 9be0fda..3fe0468 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ option(WITH_TORCH "Find and link torch lib" OFF) option(DEBUG_OUTPUT_LIKELIHOOD_TABLE "Output likelihood table to file" OFF) option(DEBUG_OUTPUT_PIXEL_CUT_FILE "Output the pixel cut files" OFF) option(PYTHON_SCOPED_INTERPRETER "Use Python scoped interpreter" ON) -option(AI_ERROR_PIC_DETECTION "Enable AI error pattern detection " OFF) +option(AI_PIC_DETECTION "Enable AI error pattern detection " OFF) set(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 17) @@ -170,11 +170,11 @@ if (DEBUG_OUTPUT_LIKELIHOOD_TABLE) else() message(STATUS "DEBUG_OUTPUT_LIKELIHOOD_TABLE: not defined") endif() -if (AI_ERROR_PIC_DETECTION) - message(STATUS "AI_ERROR_PIC_DETECTION: defined") - target_compile_definitions(${target_name} PRIVATE AI_ERROR_PIC_DETECTION) +if (AI_PIC_DETECTION) + message(STATUS "AI_PIC_DETECTION: defined") + target_compile_definitions(${target_name} PRIVATE AI_PIC_DETECTION) else() - message(STATUS "AI_ERROR_PIC_DETECTION: not defined") + message(STATUS "AI_PIC_DETECTION: not defined") endif() @@ -255,7 +255,7 @@ endforeach() find_package(Python COMPONENTS Development Interpreter) if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) - message(STATUS "Python found: ${Python_VERSION} PYTHON_lib: ${Python_LIBRARIES}") + message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${Python_LIBRARIES}") add_subdirectory(subprojects/pybind11) # 链接 pybind11 和 Python 库 diff --git a/src/kmeans_pybind.h b/src/kmeans_pybind.h index 3a03523..b3e6139 100644 --- a/src/kmeans_pybind.h +++ b/src/kmeans_pybind.h @@ -44,9 +44,9 @@ class KmeansClusters #else // NDEBUG #ifdef __APPLE__ sys.attr("path").attr("append")("./PretextViewAI.app/Contents/Resources"); - #elif def __linux__ + #elif defined(__linux__) sys.attr("path").attr("append")("."); - #else // _WIN32 + #else // _WIN32 _WIN64 sys.attr("path").attr("append")("."); #endif #endif // DEBUG From abb531587e7d579804d4e737f2aaefb3d85f2f48 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Tue, 6 May 2025 17:42:06 +0100 Subject: [PATCH 04/38] change ssize_t to s32 --- src/kmeans_pybind.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/kmeans_pybind.h b/src/kmeans_pybind.h index b3e6139..243249e 100644 --- a/src/kmeans_pybind.h +++ b/src/kmeans_pybind.h @@ -12,14 +12,14 @@ namespace py = pybind11; template py::array_t create_tensor(const int n, const int channel, const T* data_ptr) { // 总元素个数 - size_t total_size = n * n * channel; + s32 total_size = n * n * channel; // 指定 shape 和 strides - std::vector shape = {n, n, channel}; - std::vector strides = { - static_cast(n * channel * sizeof(T)), // 跨 n 行 - static_cast(channel * sizeof(T)), // 跨 n 列 - static_cast(sizeof(T)) // 跨特征维 + std::vector shape = {n, n, channel}; + std::vector strides = { + static_cast(n * channel * sizeof(T)), // 跨 n 行 + static_cast(channel * sizeof(T)), // 跨 n 列 + static_cast(sizeof(T)) // 跨特征维 }; return py::array_t(shape, strides, data_ptr); From 0e1fb972594c80e30fdf28347cdf2c2943b6e1a7 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 09:55:25 +0100 Subject: [PATCH 05/38] update mac x86_64 version --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9214004..a987f19 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: include: - os: macos-latest target_arch: x86_64 - runs-on: macos-12 + runs-on: macos-13 - os: macos-latest target_arch: arm64 runs-on: macos-latest From 7ff7c834031a240ca59e30ab77a281f92d6d7eef Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 10:33:03 +0100 Subject: [PATCH 06/38] rpath for python lib --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fe0468..5eb3aa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,10 +305,13 @@ if(APPLE) set_target_properties( ${target_name} PROPERTIES - INSTALL_RPATH "@executable_path/../Frameworks") + INSTALL_RPATH "@executable_path/../Frameworks" + BUILD_WITH_INSTALL_RPATH TRUE + MACOSX_RPATH TRUE + ) install(TARGETS ${target_name} BUNDLE DESTINATION . - COMPONENT Runtime) # generate the ${target_name}.app package under ${cmake_install_prefix} + COMPONENT Runtime) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" DESTINATION "${target_name}.app/Contents") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icon.icns" From c5e2969915536c0c8a6ce7541646eeb2f6539882 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 10:54:19 +0100 Subject: [PATCH 07/38] mac dynamic lib path and info.plisy registery --- CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5eb3aa4..a5eb234 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,12 +308,14 @@ if(APPLE) INSTALL_RPATH "@executable_path/../Frameworks" BUILD_WITH_INSTALL_RPATH TRUE MACOSX_RPATH TRUE + BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" ) install(TARGETS ${target_name} BUNDLE DESTINATION . - COMPONENT Runtime) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" - DESTINATION "${target_name}.app/Contents") + COMPONENT Runtime) + # install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" + # DESTINATION "${target_name}.app/Contents") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icon.icns" DESTINATION "${target_name}.app/Contents/Resources" ) if (WITH_TORCH) From fa83062ae29fcdc4a09f333baa9c4ae7ec1e183e Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 11:18:12 +0100 Subject: [PATCH 08/38] python lib path --- CMakeLists.txt | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5eb234..47a5b5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,30 +246,26 @@ endforeach() # ============ Kmeans for Clustering ============== -# find_package(Python REQUIRED COMPONENTS Development) - -# add_executable(kmeans_test python/frag_cluster/kmeans_main.cpp) -# target_include_directories(kmeans_test PRIVATE ${Python_INCLUDE_DIRS}) -# target_link_libraries(kmeans_test PRIVATE ${Python_LIBRARIES}) -# 查找 Python 开发环境 find_package(Python COMPONENTS Development Interpreter) if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${Python_LIBRARIES}") add_subdirectory(subprojects/pybind11) - # 链接 pybind11 和 Python 库 - target_link_libraries(${target_name} PRIVATE - pybind11::embed - ${Python_LIBRARIES} - ) - # 将 Python 脚本复制到构建目录 configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py ${CMAKE_CURRENT_BINARY_DIR}/kmeans_utils.py COPYONLY ) + set(PYTHON_LIB_PATH "${Python_LIBRARIES}") + + # 链接 pybind11 和 Python 库 + target_link_libraries(${target_name} PRIVATE + pybind11::embed + ${PYTHON_LIB_PATH} + ) + message(STATUS "Using Python scoped interpreter for Kmeans") target_compile_definitions(${target_name} PRIVATE PYTHON_SCOPED_INTERPRETER) endif() @@ -326,7 +322,7 @@ if(APPLE) endif() if (PYTHON_SCOPED_INTERPRETER) install( - FILES ${Python_LIBRARIES} + FILES ${PYTHON_LIB_PATH} DESTINATION "${target_name}.app/Contents/Frameworks" ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py @@ -352,7 +348,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() if (PYTHON_SCOPED_INTERPRETER) install( - FILES ${Python_LIBRARIES} + FILES ${PYTHON_LIB_PATH} DESTINATION "lib" ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py @@ -375,7 +371,7 @@ else () # windows endif() if (PYTHON_SCOPED_INTERPRETER) install( - FILES ${Python_LIBRARIES} + FILES ${PYTHON_LIB_PATH} DESTINATION . ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py From 795fd3d52414f91c4b9359404e6bad9ba0825118 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 11:28:44 +0100 Subject: [PATCH 09/38] lib python path --- CMakeLists.txt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47a5b5c..dfa533f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,13 +252,26 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${Python_LIBRARIES}") add_subdirectory(subprojects/pybind11) + # 拷贝python lib文件到编译目录下 + file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + # 获取拷贝后文件的路径 + if (APPLE) + set(PYTHON_LIB_PATH "${Python_LIBRARIES}/libpython${Python_VERSION_MAJOR}${Python_VERSION_MINOR}.dylib") + elseif (UNIX AND NOT APPLE) + set(PYTHON_LIB_PATH "${Python_LIBRARIES}/libpython${Python_VERSION_MAJOR}${Python_VERSION_MINOR}.so") + elseif (WIN32) + set(PYTHON_LIB_PATH "${Python_LIBRARIES}/python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}.dll") + else () + message(FATAL_ERROR "Unsupported platform") + endif() + + # 将 Python 脚本复制到构建目录 configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py ${CMAKE_CURRENT_BINARY_DIR}/kmeans_utils.py COPYONLY ) - set(PYTHON_LIB_PATH "${Python_LIBRARIES}") # 链接 pybind11 和 Python 库 target_link_libraries(${target_name} PRIVATE From e6c4dc1f605e9a7a62994a6811affa98503eda6f Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 11:34:34 +0100 Subject: [PATCH 10/38] lib python path --- CMakeLists.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dfa533f..e5a808f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,23 +256,21 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) # 获取拷贝后文件的路径 if (APPLE) - set(PYTHON_LIB_PATH "${Python_LIBRARIES}/libpython${Python_VERSION_MAJOR}${Python_VERSION_MINOR}.dylib") + set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dylib") elseif (UNIX AND NOT APPLE) - set(PYTHON_LIB_PATH "${Python_LIBRARIES}/libpython${Python_VERSION_MAJOR}${Python_VERSION_MINOR}.so") + set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") elseif (WIN32) - set(PYTHON_LIB_PATH "${Python_LIBRARIES}/python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}.dll") + set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dll") else () message(FATAL_ERROR "Unsupported platform") endif() - # 将 Python 脚本复制到构建目录 configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py ${CMAKE_CURRENT_BINARY_DIR}/kmeans_utils.py COPYONLY ) - # 链接 pybind11 和 Python 库 target_link_libraries(${target_name} PRIVATE pybind11::embed From 6a7c4441973a2f2a6fd0891114fd0a0be9eb0297 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 12:05:25 +0100 Subject: [PATCH 11/38] lib python path --- CMakeLists.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e5a808f..4a97ba3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,22 +249,27 @@ endforeach() find_package(Python COMPONENTS Development Interpreter) if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) - message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${Python_LIBRARIES}") - add_subdirectory(subprojects/pybind11) # 拷贝python lib文件到编译目录下 file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + message(STATUS "Python lib (originally): ${Python_LIBRARIES}") # 获取拷贝后文件的路径 if (APPLE) set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dylib") + message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") elseif (UNIX AND NOT APPLE) set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") + message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") elseif (WIN32) set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dll") + message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") else () message(FATAL_ERROR "Unsupported platform") endif() + message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${PYTHON_LIB_PATH}") + add_subdirectory(subprojects/pybind11) + # 将 Python 脚本复制到构建目录 configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py @@ -313,10 +318,9 @@ if(APPLE) ${target_name} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks" - BUILD_WITH_INSTALL_RPATH TRUE - MACOSX_RPATH TRUE - BUNDLE TRUE - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" + # BUILD_WITH_INSTALL_RPATH TRUE + # MACOSX_RPATH TRUE + # BUNDLE TRUE ) install(TARGETS ${target_name} BUNDLE DESTINATION . From 81e47946667589d3206e3448ca2906715794c8e1 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 12:19:13 +0100 Subject: [PATCH 12/38] lib python path --- CMakeLists.txt | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a97ba3..3a7259a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,18 +254,8 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) message(STATUS "Python lib (originally): ${Python_LIBRARIES}") # 获取拷贝后文件的路径 - if (APPLE) - set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dylib") - message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") - elseif (UNIX AND NOT APPLE) - set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") - message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") - elseif (WIN32) - set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dll") - message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") - else () - message(FATAL_ERROR "Unsupported platform") - endif() + find_library(PYTHON_LIB_PATH NAMES python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR} PATHS ${CMAKE_CURRENT_BINARY_DIR} REQUIRED) + message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${PYTHON_LIB_PATH}") add_subdirectory(subprojects/pybind11) From 2cfe11f3d9b075eb672b97a2008f9c8e7cc2ac4b Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 12:24:24 +0100 Subject: [PATCH 13/38] lib python path --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a7259a..00d0a4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,7 +254,10 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) message(STATUS "Python lib (originally): ${Python_LIBRARIES}") # 获取拷贝后文件的路径 - find_library(PYTHON_LIB_PATH NAMES python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR} PATHS ${CMAKE_CURRENT_BINARY_DIR} REQUIRED) + find_library(PYTHON_LIB_PATH + NAMES "python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" "libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" + PATHS ${CMAKE_CURRENT_BINARY_DIR} + REQUIRED) message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${PYTHON_LIB_PATH}") From eb4cfbdfe1ce8c26db5033e99aef5f7f488c5fb5 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 13:27:24 +0100 Subject: [PATCH 14/38] lib python path --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00d0a4b..fa1e9f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,6 +251,7 @@ find_package(Python COMPONENTS Development Interpreter) if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) # 拷贝python lib文件到编译目录下 + get_filename_component(Python_LIBRARIES "${Python_LIBRARIES}" REALPATH) file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) message(STATUS "Python lib (originally): ${Python_LIBRARIES}") # 获取拷贝后文件的路径 From 6cd99fcfb5ada1fef3dc9c8de8ff05ab9c94e893 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 13:44:19 +0100 Subject: [PATCH 15/38] lib python path --- CMakeLists.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa1e9f9..079dfb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,10 +249,24 @@ endforeach() find_package(Python COMPONENTS Development Interpreter) if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) - # 拷贝python lib文件到编译目录下 get_filename_component(Python_LIBRARIES "${Python_LIBRARIES}" REALPATH) + get_filename_component(Python_lib_name "${Python_LIBRARIES}" NAME) + message(STATUS "Python lib realpath: ${Python_LIBRARIES}, name: ${Python_lib_name}") file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + if (APPLE) + set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dylib") + elseif (UNIX NOT APPLE) + set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") + elseif (WIN32) + set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dll") + else() + message(FATAL_ERROR "Unsupported platform") + endif() + file( + RENAME + ${CMAKE_CURRENT_BINARY_DIR}/${Python_lib_name} + ${new_python_lib_name}) message(STATUS "Python lib (originally): ${Python_LIBRARIES}") # 获取拷贝后文件的路径 find_library(PYTHON_LIB_PATH From a6930913b2fe48fb39477fef42ea025540a4a3eb Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 13:48:27 +0100 Subject: [PATCH 16/38] UNIX AND NOT APPLE --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 079dfb8..606e2d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,8 +256,8 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) if (APPLE) set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dylib") - elseif (UNIX NOT APPLE) - set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") + elseif (UNIX AND NOT APPLE) + set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") elseif (WIN32) set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dll") else() From 6ec4ff1ccd53eeb5133ce250b54f21f34c937d74 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 14:03:48 +0100 Subject: [PATCH 17/38] avoid pybind11 subdirectory change python settings --- CMakeLists.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 606e2d1..b42ef58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,7 @@ endforeach() find_package(Python COMPONENTS Development Interpreter) if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) + add_subdirectory(subprojects/pybind11) # 拷贝python lib文件到编译目录下 get_filename_component(Python_LIBRARIES "${Python_LIBRARIES}" REALPATH) get_filename_component(Python_lib_name "${Python_LIBRARIES}" NAME) @@ -259,7 +260,7 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) elseif (UNIX AND NOT APPLE) set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") elseif (WIN32) - set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dll") + set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.lib") else() message(FATAL_ERROR "Unsupported platform") endif() @@ -270,13 +271,13 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) message(STATUS "Python lib (originally): ${Python_LIBRARIES}") # 获取拷贝后文件的路径 find_library(PYTHON_LIB_PATH - NAMES "python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" "libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" - PATHS ${CMAKE_CURRENT_BINARY_DIR} - REQUIRED) + NAMES "python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" + "libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" + PATHS ${CMAKE_CURRENT_BINARY_DIR} + REQUIRED) message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${PYTHON_LIB_PATH}") - add_subdirectory(subprojects/pybind11) # 将 Python 脚本复制到构建目录 configure_file( @@ -326,9 +327,9 @@ if(APPLE) ${target_name} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks" - # BUILD_WITH_INSTALL_RPATH TRUE - # MACOSX_RPATH TRUE - # BUNDLE TRUE + BUILD_WITH_INSTALL_RPATH TRUE + MACOSX_RPATH TRUE + BUNDLE TRUE ) install(TARGETS ${target_name} BUNDLE DESTINATION . @@ -351,7 +352,7 @@ if(APPLE) FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/frag_cluster/kmeans_utils.py DESTINATION "${target_name}.app/Contents/Resources" ) endif () -elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +elseif(UNIX AND NOT APPLE) # Linux message(STATUS "[Cmake Install]: Linux") set_target_properties( ${target_name} From f12eab6243cf17d0c2cbc7d51ecc77b770409b38 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 14:41:26 +0100 Subject: [PATCH 18/38] lib python path --- CMakeLists.txt | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b42ef58..b9f728c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,30 +253,13 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) # 拷贝python lib文件到编译目录下 get_filename_component(Python_LIBRARIES "${Python_LIBRARIES}" REALPATH) get_filename_component(Python_lib_name "${Python_LIBRARIES}" NAME) - message(STATUS "Python lib realpath: ${Python_LIBRARIES}, name: ${Python_lib_name}") file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) - if (APPLE) - set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.dylib") - elseif (UNIX AND NOT APPLE) - set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.so") - elseif (WIN32) - set(new_python_lib_name "${CMAKE_CURRENT_BINARY_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.lib") - else() - message(FATAL_ERROR "Unsupported platform") - endif() - file( - RENAME - ${CMAKE_CURRENT_BINARY_DIR}/${Python_lib_name} - ${new_python_lib_name}) - message(STATUS "Python lib (originally): ${Python_LIBRARIES}") + message(STATUS "Python lib realpath: ${Python_LIBRARIES}, name: ${Python_lib_name}") # 获取拷贝后文件的路径 find_library(PYTHON_LIB_PATH - NAMES "python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" - "libpython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" + NAMES ${Python_lib_name} # "python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" "python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}" PATHS ${CMAKE_CURRENT_BINARY_DIR} REQUIRED) - message(STATUS "Python lib (after copy): ${PYTHON_LIB_PATH}") - message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${PYTHON_LIB_PATH}") # 将 Python 脚本复制到构建目录 From 882e47d0e2b2665d228be906f11bc8196269e497 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 15:59:30 +0100 Subject: [PATCH 19/38] lib python path --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9f728c..836d118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,10 +257,10 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) message(STATUS "Python lib realpath: ${Python_LIBRARIES}, name: ${Python_lib_name}") # 获取拷贝后文件的路径 find_library(PYTHON_LIB_PATH - NAMES ${Python_lib_name} # "python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}" "python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}" + NAMES ${Python_lib_name} PATHS ${CMAKE_CURRENT_BINARY_DIR} REQUIRED) - message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python version: ${Python_VERSION} Python_lib: ${PYTHON_LIB_PATH}") + message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python search path: ${CMAKE_CURRENT_BINARY_DIR}, Python version: ${Python_VERSION}, Python_lib: ${PYTHON_LIB_PATH}") # 将 Python 脚本复制到构建目录 configure_file( From 6affefcee04b51c3fe5086b216daa27464e7f6e3 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 16:04:25 +0100 Subject: [PATCH 20/38] lib python path --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 836d118..f69a341 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,10 +256,11 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) file(COPY ${Python_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) message(STATUS "Python lib realpath: ${Python_LIBRARIES}, name: ${Python_lib_name}") # 获取拷贝后文件的路径 - find_library(PYTHON_LIB_PATH - NAMES ${Python_lib_name} - PATHS ${CMAKE_CURRENT_BINARY_DIR} - REQUIRED) + # find_library(PYTHON_LIB_PATH + # NAMES ${Python_lib_name} + # PATHS ${CMAKE_CURRENT_BINARY_DIR} + # REQUIRED) + set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${Python_lib_name}") message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python search path: ${CMAKE_CURRENT_BINARY_DIR}, Python version: ${Python_VERSION}, Python_lib: ${PYTHON_LIB_PATH}") # 将 Python 脚本复制到构建目录 From 49ca605c917c7322a2bf786acafb64e8775c326b Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 16:29:38 +0100 Subject: [PATCH 21/38] lib python path --- CMakeLists.txt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f69a341..6d47554 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,7 @@ else() message(STATUS "lib_deflate_static found: ${lib_deflate_static}") endif() -add_library(lib_yahs STATIC +add_library(yahs STATIC src/yahs_sort.cpp ) @@ -136,11 +136,11 @@ if (WITH_TORCH) sortlib PUBLIC ${TORCH_LIBRARIES} ${torchscatter} - lib_yahs) + yahs) else() message(STATUS "Libtorch: Build without libtorch") add_library(sortlib STATIC src/frag_sort.cpp ) - target_link_libraries(sortlib PRIVATE lib_yahs) + target_link_libraries(sortlib PRIVATE yahs) endif() @@ -188,7 +188,7 @@ target_link_libraries( utilsPretextView copy_texture glfw - lib_yahs + yahs ) # link libraries only for debug version @@ -271,12 +271,18 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) # 链接 pybind11 和 Python 库 target_link_libraries(${target_name} PRIVATE - pybind11::embed ${PYTHON_LIB_PATH} + pybind11::embed # 链接pybind11的时候把python的库弄坏了? ) message(STATUS "Using Python scoped interpreter for Kmeans") target_compile_definitions(${target_name} PRIVATE PYTHON_SCOPED_INTERPRETER) + # 修改 install_name 和 rpath,确保在构建后正确链接 + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND install_name_tool -id @rpath/${Python_lib_name} ${PYTHON_LIB_PATH} + COMMAND install_name_tool -change "${Python_LIBRARIES}" @rpath/${Python_lib_name} $ + COMMENT "Fixing install_name and rpath for Python dylib" + ) endif() From 7a7f0a6df1d5202d283b164e7e8753bdfaf43736 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 7 May 2025 16:35:57 +0100 Subject: [PATCH 22/38] lib python path --- CMakeLists.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d47554..b0b254f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,11 +278,14 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) message(STATUS "Using Python scoped interpreter for Kmeans") target_compile_definitions(${target_name} PRIVATE PYTHON_SCOPED_INTERPRETER) # 修改 install_name 和 rpath,确保在构建后正确链接 - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND install_name_tool -id @rpath/${Python_lib_name} ${PYTHON_LIB_PATH} - COMMAND install_name_tool -change "${Python_LIBRARIES}" @rpath/${Python_lib_name} $ - COMMENT "Fixing install_name and rpath for Python dylib" - ) + if (APPLE) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND install_name_tool -id @rpath/${Python_lib_name} ${PYTHON_LIB_PATH} + COMMAND install_name_tool -change "${Python_LIBRARIES}" @rpath/${Python_lib_name} $ + COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks" $ + COMMENT "Fixing install_name and rpath for Python dylib" + ) + endif() # APPLE endif() From 183c72728121662d95cb4f47d0e65018a87f9cc2 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Thu, 8 May 2025 10:07:10 +0100 Subject: [PATCH 23/38] lib python path --- CMakeLists.txt | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b0b254f..1510f6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,19 +272,18 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) # 链接 pybind11 和 Python 库 target_link_libraries(${target_name} PRIVATE ${PYTHON_LIB_PATH} - pybind11::embed # 链接pybind11的时候把python的库弄坏了? + pybind11::embed ) - message(STATUS "Using Python scoped interpreter for Kmeans") target_compile_definitions(${target_name} PRIVATE PYTHON_SCOPED_INTERPRETER) # 修改 install_name 和 rpath,确保在构建后正确链接 if (APPLE) - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND install_name_tool -id @rpath/${Python_lib_name} ${PYTHON_LIB_PATH} - COMMAND install_name_tool -change "${Python_LIBRARIES}" @rpath/${Python_lib_name} $ - COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks" $ - COMMENT "Fixing install_name and rpath for Python dylib" - ) + # add_custom_command(TARGET ${target_name} POST_BUILD + # COMMAND install_name_tool -id @rpath/${Python_lib_name} ${PYTHON_LIB_PATH} + # COMMAND install_name_tool -change "${Python_LIBRARIES}" @rpath/${Python_lib_name} $ + # COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks" $ + # COMMENT "Fixing install_name and rpath for Python dylib" + # ) endif() # APPLE endif() @@ -331,6 +330,9 @@ if(APPLE) # DESTINATION "${target_name}.app/Contents") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icon.icns" DESTINATION "${target_name}.app/Contents/Resources" ) + install( + FILES ${lib_deflate_static} + DESTINATION "${target_name}.app/Contents/Frameworks") if (WITH_TORCH) install(FILES ${torch_copy_libs} DESTINATION "${target_name}.app/Contents/Frameworks" ) @@ -355,6 +357,9 @@ elseif(UNIX AND NOT APPLE) # Linux TARGETS ${target_name} RUNTIME DESTINATION "bin" COMPONENT Runtime) + install( + FILES ${lib_deflate_static} + DESTINATION "lib") if (WITH_TORCH) install( FILES ${torch_copy_libs} @@ -378,6 +383,9 @@ else () # windows TARGETS ${target_name} RUNTIME DESTINATION . COMPONENT Runtime) + install( + FILES ${lib_deflate_static} + DESTINATION .) if (WITH_TORCH) install( FILES ${torch_copy_libs} From a831bf65f5516227bc76823bb191eeb58a504334 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Thu, 8 May 2025 10:38:07 +0100 Subject: [PATCH 24/38] lib python path --- CMakeLists.txt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1510f6f..e6bc84a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,6 +261,9 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) # PATHS ${CMAKE_CURRENT_BINARY_DIR} # REQUIRED) set(PYTHON_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${Python_lib_name}") + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND install_name_tool -id @rpath/${Python_lib_name} ${Python_LIBRARIES} + COMMENT "Fix install_name of python lib") message(STATUS "Build with PYTHON_SCOPED_INTERPRETER. Python search path: ${CMAKE_CURRENT_BINARY_DIR}, Python version: ${Python_VERSION}, Python_lib: ${PYTHON_LIB_PATH}") # 将 Python 脚本复制到构建目录 @@ -276,15 +279,6 @@ if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) ) message(STATUS "Using Python scoped interpreter for Kmeans") target_compile_definitions(${target_name} PRIVATE PYTHON_SCOPED_INTERPRETER) - # 修改 install_name 和 rpath,确保在构建后正确链接 - if (APPLE) - # add_custom_command(TARGET ${target_name} POST_BUILD - # COMMAND install_name_tool -id @rpath/${Python_lib_name} ${PYTHON_LIB_PATH} - # COMMAND install_name_tool -change "${Python_LIBRARIES}" @rpath/${Python_lib_name} $ - # COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks" $ - # COMMENT "Fixing install_name and rpath for Python dylib" - # ) - endif() # APPLE endif() From e0db72b7240d4c3d65c5866aeeda1c9fb452454b Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Thu, 8 May 2025 10:38:37 +0100 Subject: [PATCH 25/38] pixel cut position correct --- PretextView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PretextView.cpp b/PretextView.cpp index 79d4a0a..ce029ea 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -6547,7 +6547,7 @@ void cut_frags(const std::vector& problem_locs) if (gap_data_ptr) // correct loc with considering the gap extension { u32 distance_tmp = 1; - while (distance_tmp < auto_curation_state.auto_cut_gap_loc_threshold) + while (distance_tmp <= auto_curation_state.auto_cut_gap_loc_threshold) { if (loc_orig + distance_tmp < Number_of_Pixels_1D && gap_data_ptr[loc_orig + distance_tmp] > 0 ) { From 4ffaca9b184097d6016a1bd8889ef1f2ef5979d4 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Thu, 8 May 2025 11:19:15 +0100 Subject: [PATCH 26/38] remove PYTHON_SCOPED_INTERPRETER --- CMakeLists.txt | 10 ++++++++-- install.cmake.sh | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6bc84a..8f2a616 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ option(BUILD_UNIVERSAL "Build macOS universal binary" OFF) option(WITH_TORCH "Find and link torch lib" OFF) option(DEBUG_OUTPUT_LIKELIHOOD_TABLE "Output likelihood table to file" OFF) option(DEBUG_OUTPUT_PIXEL_CUT_FILE "Output the pixel cut files" OFF) -option(PYTHON_SCOPED_INTERPRETER "Use Python scoped interpreter" ON) +option(PYTHON_SCOPED_INTERPRETER "Use Python scoped interpreter" OFF) option(AI_PIC_DETECTION "Enable AI error pattern detection " OFF) set(CMAKE_C_STANDARD 17) @@ -247,8 +247,14 @@ endforeach() # ============ Kmeans for Clustering ============== -find_package(Python COMPONENTS Development Interpreter) +if (PYTHON_SCOPED_INTERPRETER) + find_package(Python COMPONENTS Development Interpreter) +else() + message(STATUS "PYTHON_SCOPED_INTERPRETER defined, but Python not found, build without PYTHON_SCOPED_INTERPRETER") +endif() + if (PYTHON_SCOPED_INTERPRETER AND Python_FOUND) + message(STATUS "PYTHON_SCOPED_INTERPRETER defined, build with PYTHON_SCOPED_INTERPRETER") add_subdirectory(subprojects/pybind11) # 拷贝python lib文件到编译目录下 get_filename_component(Python_LIBRARIES "${Python_LIBRARIES}" REALPATH) diff --git a/install.cmake.sh b/install.cmake.sh index ac10a7d..38e524b 100755 --- a/install.cmake.sh +++ b/install.cmake.sh @@ -90,7 +90,7 @@ CMAKE_OPTIONS=( -DCMAKE_INSTALL_PREFIX="$install_path" -DCMAKE_PREFIX_PATH="$cmake_prefix_path_tmp" -DCMAKE_OSX_ARCHITECTURES=${ARCH} - -DPYTHON_SCOPED_INTERPRETER=ON + -DPYTHON_SCOPED_INTERPRETER=OFF ) if [[ "$OS" == "Darwin" ]]; then if [[ "$FORCE_MAC_X86" == true ]]; then From fff7ab9abce641f5180dba57199b3c84c8b43af2 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Thu, 8 May 2025 11:20:02 +0100 Subject: [PATCH 27/38] show max number of input sequences --- PretextView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PretextView.cpp b/PretextView.cpp index ce029ea..2fac749 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -12340,7 +12340,7 @@ MainArgs // Input Sequences { - std::string input_sequence_name = fmt::format("Input Sequences ({})", Number_of_Original_Contigs); + std::string input_sequence_name = fmt::format("Input Sequences ({} / Max:{})", Number_of_Original_Contigs, Max_Number_of_Contigs); nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 1); if (nk_tree_push(NK_Context, NK_TREE_TAB, input_sequence_name.c_str(), NK_MINIMIZED)) { From 2c6ec3ee946243a3c87da0168731b17f061b4e89 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Tue, 3 Jun 2025 14:32:56 +0100 Subject: [PATCH 28/38] load from agp 1.0.4 --- .gitignore | 1 + CMakeLists.txt | 10 + PretextView.cpp | 13734 ++++++++++++++++++----------------- src/Resources.cpp | 1 + src/aisort.cpp | 2 +- src/auto_curation_state.h | 2 +- src/frag_cut_calculation.h | 12 +- src/frags_order.h | 40 +- src/genomeData.h | 46 +- src/grey_out_settings.h | 16 + src/parse_agp.h | 377 + src/utilsPretextView.h | 2 +- 12 files changed, 7500 insertions(+), 6743 deletions(-) create mode 100644 src/parse_agp.h diff --git a/.gitignore b/.gitignore index d25b304..cc14256 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ include/GLFW/glfw3native.h include/libdeflate.h src/aisort_cz.cpp src/aisort_cz.h +src/parse_agp_test.cpp PretextViewAI.zip test_data/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f2a616..7ff137c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(target_name PretextViewAI) + + if (APPLE) if(FORCE_MAC_X86) message(STATUS "Forcing x86_64 architecture build") @@ -103,6 +105,14 @@ else() endif() +# test the parse_agp +add_executable(parse_agp_test src/parse_agp_test.cpp) +target_link_libraries(parse_agp_test PRIVATE ${lib_fmt}) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(parse_agp_test PRIVATE DEBUG) +endif() + + find_library( lib_deflate_static NAMES deflate deflatestatic diff --git a/PretextView.cpp b/PretextView.cpp index 2fac749..28127fb 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -112,6 +112,7 @@ SOFTWARE. #include "frag_cut_calculation.h" #include "grey_out_settings.h" #include "user_profile_settings.h" +#include "parse_agp.h" #include "shaderSource.h" /* @@ -399,6 +400,29 @@ s32 useCustomOrder = 0; +#define text_box_font_size_default 20.0f +struct TextBoxSize +{ + f32 font_size = text_box_font_size_default; + + TextBoxSize(): font_size(text_box_font_size_default) {} + + void increase_size(){ + font_size += 2.0f; + font_size = my_Min(font_size, 36.0f); + } + + void decrease_size(){ + font_size -= 2.0f; + font_size = my_Max(font_size, 8.0f); + } + + void defult_size(){ + font_size = text_box_font_size_default; + } +}; +global_variable TextBoxSize text_box_size; + global_variable quad_data * Grid_Data; @@ -804,10 +828,15 @@ Default_Tags[] = "B2", "B3", "FalseDuplicate", - "Primary" + "Primary", + "vertPaint", // paint vertically + "horzPaint", // paint horizontally }; +char searchbuf[256] = {0}; +s32 caseSensitive_search_sequences = 0; + global_variable map_state * Map_State; @@ -824,3038 +853,2884 @@ global_variable auto auto_curation_state = AutoCurationState(); #include "spectral_cluster.h" -global_function -void -UpdateContigsFromMapState() // reading 从map的状态更新contigs -{ - u32 lastScaffID = Map_State->scaffIds[0]; // 第一个scaff的编号 - u32 scaffId = lastScaffID ? 1 : 0; // - u32 lastId_original_contig = Map_State->originalContigIds[0]; // 第一个像素点对应的id - u32 lastCoord = Map_State->contigRelCoords[0]; // 第一个像素点的局部坐标 - u32 contigPtr = 0; - u32 length = 0; - u32 startCoord = lastCoord; - u08 inverted = Map_State->contigRelCoords[1] < lastCoord; // 判断是不是反转的 - Map_State->contigIds[0] = 0; - - u32 pixelIdx = 0; - ForLoop(Number_of_Original_Contigs) (Original_Contigs + index)->nContigs = 0; // 将每一个contig的 片段数目 置为零 - ForLoop(Number_of_Pixels_1D - 1) // 遍历每一个像素点 更新 Original_Contigs, Contigs - // 遍历完之后,contigPtr为214,但是Number_of_Original_Contigs = 218 - { - if (contigPtr >= Max_Number_of_Contigs) break; // 确保 contigPtr 不超出最大contig的数值 - - ++length; // current fragment length - pixelIdx = index + 1; // 像素点编号, 加一因为第一个已经用来初始化了 - u32 original_contig_id = Map_State->originalContigIds[pixelIdx]; // 像素点的 original contig id, 这里 不使用 % Max_Number_of_Contigs的值是为了区分后面cut之后的片段 - u32 coord = Map_State->contigRelCoords[pixelIdx]; // 像素点的局部坐标 - - if ( - original_contig_id != lastId_original_contig || - (inverted && coord != (lastCoord - 1)) || - (!inverted && coord != (lastCoord + 1)) - ) // not a continuous fragment - { - Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].contigMapPixels[Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].nContigs] = pixelIdx - 1 - (length >> 1); // update Original_Contigs: contigMapPixels - Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].nContigs++; // update Original_Contigs: nContigs, contigMapPixels +global_function void +UserSaveState(const char *headerHash = "userprofile", u08 overwrite = 1, char *path = 0); - contig *last_cont = Contigs->contigs_arr + contigPtr; // 获取上一个contig的指针, 并且给contigPtr + 1 - contigPtr++; - last_cont->originalContigId = lastId_original_contig; // 更新这个片段的id - last_cont->length = length; // 更新长度 - last_cont->startCoord = startCoord; // 更新开头为当前片段在该contig上的局部坐标 endCoord = startCoord + length - 1 - last_cont->metaDataFlags = Map_State->metaDataFlags + pixelIdx - 1; // Finished (shaoheng): memory problem: assign the pointer to the cont->metaDataFlags, the original is nullptr, the let this ptr point to the last pixel of the contig - u32 thisScaffID = Map_State->scaffIds[pixelIdx - 1]; // 上一个像素点对应的 scaffid - last_cont->scaffId = thisScaffID ? ((thisScaffID == lastScaffID) ? (scaffId) : (++scaffId)) : 0; // 如果存在scaffid则(判断是不是同一个scaff,如果是则继续用scaffid,否则++scaffid),否则为0 - lastScaffID = thisScaffID; // 更新 +global_variable +u08 +Grey_Haplotigs = 1; +global_variable +GreyOutSettings* Grey_Out_Settings = nullptr; - // 余数表示8数的第几位,如果未反向则对应位为0,若反向则对应位为1 - if (IsContigInverted(contigPtr - 1)) // 判断上一个contig是否反向 - { // 位操作更新contigflag - // 每8个片段的正反采用一个u08表示,每一个bit表示一个正反,余数表示这八个中的第几个,如果没有反向则对应位为0 - if (!inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] &= ~(1 << ((contigPtr - 1) & 7)); - } - else - { // 如果反向则额对应位的bit为1 - if (inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] |= (1 << ((contigPtr - 1) & 7)); // 如果反向 - } +global_variable +UserProfileSettings* user_profile_settings_ptr = nullptr; - startCoord = coord; // 当前片段开始的坐标 - length = 0; // 当前片段长度清零0 - if (pixelIdx < (Number_of_Pixels_1D - 1)) inverted = Map_State->contigRelCoords[pixelIdx + 1] < coord; // 更新inverted - } - // 更新上一个id和局部坐标 - Map_State->contigIds[pixelIdx] = (u32)contigPtr; // 像素点对应的 片段id 修改为当前的统计得到片段id - lastId_original_contig = original_contig_id; // 更新上一个像素点的id - lastCoord = coord; // 更新上一个像素点的局部坐标 - } +global_variable +u08 +Deferred_Close_UI = 0; - if (contigPtr < Max_Number_of_Contigs) // contigptr 小于 Number_of_Original_Contigs - // 更新最后一个contig的最后一个片段信息 - { - (Original_Contigs + lastId_original_contig % Max_Number_of_Contigs)->contigMapPixels[(Original_Contigs + lastId_original_contig % Max_Number_of_Contigs)->nContigs++] = pixelIdx - 1 - (length >> 1); - ++length; - contig *cont = Contigs->contigs_arr + contigPtr++; - cont->originalContigId = lastId_original_contig; - cont->length = length; - cont->startCoord = startCoord; - cont->metaDataFlags = Map_State->metaDataFlags + pixelIdx - 1; - - u32 thisScaffID = Map_State->scaffIds[pixelIdx]; - cont->scaffId = thisScaffID ? ((thisScaffID == lastScaffID) ? (scaffId) : (++scaffId)) : 0; - - if (IsContigInverted(contigPtr - 1)) - { - if (!inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] &= ~(1 << ((contigPtr - 1) & 7)); - } - else - { - if (inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] |= (1 << ((contigPtr - 1) & 7)); - } - } +// File Browser +// from nuklear file browser example +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +struct +icons +{ + struct nk_image home; + struct nk_image computer; + struct nk_image directory; - Contigs->numberOfContigs = contigPtr; -} + struct nk_image default_file; + struct nk_image img_file; +}; -global_function -void -AddMapEdit(s32 delta, pointui finalPixels, u32 invert); +enum +file_groups +{ + FILE_GROUP_DEFAULT, + FILE_GROUP_PRETEXT, + FILE_GROUP_MAX +}; -global_function -void -RebuildContig(u32 pixel) +enum +file_types { - for (;;) - { - u32 contigId = Map_State->contigIds[pixel]; - u32 origContigId = Map_State->get_original_contig_id(pixel); + FILE_DEFAULT, + FILE_PRETEXT, + FILE_PSTM, + FILE_MAX +}; - u32 top = (u32)pixel; - while (top && (Map_State->contigIds[top - 1] == contigId)) --top; +struct +file_group +{ + enum file_groups group; + u32 pad; + const char *name; + struct nk_image *icon; +}; - u32 bottom = pixel; - while ((bottom < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[bottom + 1] == contigId)) ++bottom; +struct +file +{ + enum file_types type; + enum file_groups group; + const char *suffix; +}; - if (IsContigInverted(contigId)) - { - InvertMap(top, bottom); - AddMapEdit(0, {top, bottom}, 1); - continue; - } +struct +media +{ + int font; + int icon_sheet; + struct icons icons; + struct file_group group[FILE_GROUP_MAX]; + struct file files[FILE_MAX]; +}; - u08 fragmented = 0; - ForLoop(Number_of_Pixels_1D) - { - if ((Map_State->contigIds[index] != contigId) && (Map_State->get_original_contig_id(index) == origContigId)) - { - fragmented = 1; - break; - } - } +#define MAX_PATH_LEN 512 +struct +file_browser +{ + /* path */ + char file[MAX_PATH_LEN]; + char home[MAX_PATH_LEN]; + char directory[MAX_PATH_LEN]; - if (fragmented) - { - u32 contigTopCoord = Map_State->contigRelCoords[top]; - if (contigTopCoord) - { - u32 otherPixel = 0; - ForLoop(Number_of_Pixels_1D) - { - if ((Map_State->get_original_contig_id(index) == origContigId) && (Map_State->contigRelCoords[index] == (contigTopCoord - 1))) - { - otherPixel = index; - break; - } - } + /* directory content */ + char **files; + char **directories; + size_t file_count; + size_t dir_count; + struct media *media; +}; - u08 invert = !otherPixel || (Map_State->contigIds[otherPixel - 1] != Map_State->contigIds[otherPixel]); - u32 otherPixel2 = otherPixel; - if (invert) - { - while ((otherPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[otherPixel + 1] == Map_State->contigIds[otherPixel2])) ++otherPixel; - } - else - { - while (otherPixel2 && (Map_State->contigIds[otherPixel2 - 1] == Map_State->contigIds[otherPixel])) --otherPixel2; - } +// file browser +struct file_browser browser; +struct file_browser saveBrowser; +struct file_browser loadBrowser; +struct file_browser saveAGPBrowser; +struct file_browser loadAGPBrowser; - s32 delta = (s32)top - (s32)otherPixel; - if (delta > 0) --delta; - else delta = (s32)top - (s32)otherPixel2; - pointui finalPixels = {(u32)((s32)otherPixel2 + delta), (u32)((s32)otherPixel + delta)}; - RearrangeMap(otherPixel2, otherPixel, delta); - if (invert) InvertMap(finalPixels.x, finalPixels.y); - AddMapEdit(delta, finalPixels, invert); - } - else - { - u32 contigBottomCoord = Map_State->contigRelCoords[bottom]; - u32 otherPixel = 0; - ForLoop(Number_of_Pixels_1D) - { - if ((Map_State->get_original_contig_id(index) == origContigId) && (Map_State->contigRelCoords[index] == (contigBottomCoord + 1))) - { - otherPixel = index; - break; - } - } +#if defined __unix__ || defined __APPLE__ +#include +#include +#endif - u08 invert = (otherPixel == (Number_of_Pixels_1D - 1)) || (Map_State->contigIds[otherPixel + 1] != Map_State->contigIds[otherPixel]); - u32 otherPixel2 = otherPixel; - - if (!invert) - { - while ((otherPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[otherPixel + 1] == Map_State->contigIds[otherPixel2])) ++otherPixel; - } - else - { - while (otherPixel2 && (Map_State->contigIds[otherPixel2 - 1] == Map_State->contigIds[otherPixel])) --otherPixel2; - } - - s32 delta = (s32)bottom - (s32)otherPixel2; - if (delta < 0) ++delta; - else delta = (s32)bottom - (s32)otherPixel; - pointui finalPixels = {(u32)((s32)otherPixel2 + delta), (u32)((s32)otherPixel + delta)}; - RearrangeMap(otherPixel2, otherPixel, delta); - if (invert) InvertMap(finalPixels.x, finalPixels.y); - AddMapEdit(delta, finalPixels, invert); - } - - continue; - } - else break; - } -} - -struct -map_edit -{ - u32 finalPix1; - u32 finalPix2; - s32 delta; -}; - -struct -waypoint; - -struct -waypoint_quadtree_node -{ - waypoint *wayp; - waypoint_quadtree_node *next; - waypoint_quadtree_node *prev; -}; - -struct -waypoint -{ - point2f coords; - f32 z; - u32 index; - waypoint *prev; - waypoint *next; - waypoint_quadtree_node *node; - // u16 long_waypoint_mode; -}; - -#define Edits_Stack_Size 32768 -#define Waypoints_Stack_Size 128 - -struct -map_editor -{ - map_edit *edits; - u32 nEdits; - u32 editStackPtr; - u32 nUndone; - u32 pad; -}; - -global_variable -map_editor * -Map_Editor; - -#define Waypoints_Quadtree_Levels 5 - -struct -waypoint_quadtree_level -{ -#ifdef Internal - u32 show; -#else - u32 pad; +#ifndef _WIN32 +#include #endif - f32 size; - point2f lowerBound; - waypoint_quadtree_level *children[4]; - waypoint_quadtree_node headNode; -}; - -struct -waypoint_editor -{ - waypoint freeWaypoints; - waypoint activeWaypoints; - waypoint_quadtree_level *quadtree; - waypoint_quadtree_node freeNodes; - u32 nWaypointsActive; - u32 pad; -}; - -global_variable -waypoint_editor * -Waypoint_Editor; - -global_variable u16 Long_Waypoints_Mode = 0; // mode 0: vertical, mode 1: horizontal, mode 2: both - -#define Waypoint_Select_Distance 8.0f -global_variable -waypoint * -Selected_Waypoint = 0; global_function -waypoint_quadtree_level * -PushQuadTree(memory_arena *arena, u32 level = 0, point2f lowerBound = {-0.5f, -0.5f}, f32 size = 1.0f) +char* +StrDuplicate(const char *src) { - waypoint_quadtree_level *quadtreeLevel = 0; - - if (level < Waypoints_Quadtree_Levels) - { - quadtreeLevel = PushStructP(arena, waypoint_quadtree_level); - quadtreeLevel->size = size; - quadtreeLevel->lowerBound = lowerBound; - quadtreeLevel->headNode = {}; - -#ifdef Internal - quadtreeLevel->show = 0; -#endif - - f32 halfSize = size * 0.5f; - quadtreeLevel->children[0] = PushQuadTree(arena, level + 1, {lowerBound.x, lowerBound.y}, halfSize); - quadtreeLevel->children[1] = PushQuadTree(arena, level + 1, {lowerBound.x, lowerBound.y + halfSize}, halfSize); - quadtreeLevel->children[2] = PushQuadTree(arena, level + 1, {lowerBound.x + halfSize, lowerBound.y}, halfSize); - quadtreeLevel->children[3] = PushQuadTree(arena, level + 1, {lowerBound.x + halfSize, lowerBound.y + halfSize}, halfSize); - } - - return(quadtreeLevel); + char *ret; + size_t len = strlen(src); + if (!len) return 0; + ret = (char*)malloc(len+1); + if (!ret) return 0; + memcpy(ret, src, len); + ret[len] = '\0'; + return ret; } global_function void -#ifdef Internal -GetWaypointsWithinRectange(point2f lowerBound, point2f size, waypoint ***bufferPtr, u32 reset = 1); -#else -GetWaypointsWithinRectange(point2f lowerBound, point2f size, waypoint ***bufferPtr); -#endif - -global_function -void -UpdateWayPoint(waypoint *wayp, point2f coords); - -global_function -void -MoveWayPoints(map_edit *edit, u32 undo = 0); +DirFreeList(char **list, size_t size) +{ + size_t i; + for (i = 0; i < size; ++i) + free(list[i]); + free(list); +} global_function -void -AddMapEdit(s32 delta, pointui finalPixels, u32 invert) +u32 +StringIsLexBigger(char *string, char *toCompareTo) { - ++Map_Editor->nEdits; - Map_Editor->nUndone = 0; - - map_edit *edit = Map_Editor->edits + Map_Editor->editStackPtr++; + u32 result; + u32 equal; - if (Map_Editor->editStackPtr == Edits_Stack_Size) + do { - Map_Editor->editStackPtr = 0; - } - - u32 pix1 = (u32)(invert ? my_Max(finalPixels.x, finalPixels.y) : my_Min(finalPixels.x, finalPixels.y)); - u32 pix2 = (u32)(invert ? my_Min(finalPixels.x, finalPixels.y) : my_Max(finalPixels.x, finalPixels.y)); - - edit->delta = (s32)delta; - edit->finalPix1 = pix1; - edit->finalPix2 = pix2; -} + equal = *string == *toCompareTo; + result = *string > *(toCompareTo++); + } while (equal && (*(string++) != '\0')); -global_function -void -UpdateScaffolds() -{ - ForLoop(Number_of_Pixels_1D) Map_State->scaffIds[index] = (Contigs->contigs_arr + Map_State->contigIds[index])->scaffId; + return(result); } global_function void -UndoMapEdit() +CharArrayBubbleSort(char **list, u32 size) { - if (Map_Editor->nEdits && !Edit_Pixels.editing) + while (size > 1) { - --Map_Editor->nEdits; - ++Map_Editor->nUndone; - - if (!Map_Editor->editStackPtr) - { - Map_Editor->editStackPtr = Edits_Stack_Size + 1; - } - - map_edit *edit = Map_Editor->edits + (--Map_Editor->editStackPtr); - - if (edit->finalPix1 > edit->finalPix2) - { - InvertMap((u32)edit->finalPix1, (u32)edit->finalPix2); + u32 newSize = 0; + ForLoop(size - 1) + { + if (StringIsLexBigger(list[index], list[index + 1])) + { + char *tmp = list[index]; + list[index] = list[index + 1]; + list[index + 1] = tmp; + newSize = index + 1; + } } - - u32 start = my_Min(edit->finalPix1, edit->finalPix2); - u32 end = my_Max(edit->finalPix1, edit->finalPix2); - - RearrangeMap(start, end, -edit->delta); - - UpdateScaffolds(); + size = newSize; } } global_function -void -RedoMapEdit() +char** +DirList(const char *dir, u32 return_subdirs, size_t *count) { - if (Map_Editor->nUndone && !Edit_Pixels.editing) - { - ++Map_Editor->nEdits; - --Map_Editor->nUndone; - - map_edit *edit = Map_Editor->edits + Map_Editor->editStackPtr++; + size_t n = 0; + char buffer[MAX_PATH_LEN]; + char **results = NULL; +#ifndef _WIN32 + const DIR *none = NULL; + DIR *z; +#else + WIN32_FIND_DATA ffd; + HANDLE hFind = INVALID_HANDLE_VALUE; + char dirBuff[MAX_PATH_LEN]; +#endif + size_t capacity = 32; + size_t size; - if (Map_Editor->editStackPtr == Edits_Stack_Size) - { - Map_Editor->editStackPtr = 0; - } - - u32 start = my_Min(edit->finalPix1, edit->finalPix2); - u32 end = my_Max(edit->finalPix1, edit->finalPix2); + Assert(dir); + Assert(count); + strncpy(buffer, dir, MAX_PATH_LEN); + n = strlen(buffer); - RearrangeMap((u32)((s32)start - edit->delta), (u32)((s32)end - edit->delta), edit->delta); - - if (edit->finalPix1 > edit->finalPix2) - { - InvertMap((u32)edit->finalPix1, (u32)edit->finalPix2); - } - - UpdateScaffolds(); - } -} +#ifndef _WIN32 + if (n > 0 && (buffer[n-1] != '/')) + buffer[n++] = '/'; +#else + if (n > 0 && (buffer[n-1] != '\\')) + buffer[n++] = '\\'; +#endif + size = 0; -global_function -void -MoveWayPoints(map_edit *edit, u32 undo) -{ - pointui tmp; - s32 delta; - pointui finalPixels; - if (undo) - { - tmp = {(u32)edit->finalPix1, (u32)edit->finalPix2}; - delta = -(s32)edit->delta; - finalPixels = {(u32)((s32)tmp.x + delta), (u32)((s32)tmp.y + delta)}; - } - else +#ifndef _WIN32 + z = opendir(dir); +#else + strncpy(dirBuff, buffer, MAX_PATH_LEN); + dirBuff[n] = '*'; + hFind = FindFirstFile(dirBuff, &ffd); +#endif + +#ifndef _WIN32 + if (z != none) +#else + if (hFind != INVALID_HANDLE_VALUE) +#endif { - tmp = {(u32)((s32)edit->finalPix1 - (s32)edit->delta), (u32)((s32)edit->finalPix2 - (s32)edit->delta)}; - delta = (s32)edit->delta; - finalPixels = {(u32)edit->finalPix1, (u32)edit->finalPix2}; - } +#ifndef _WIN32 + u32 nonempty = 1; + struct dirent *data = readdir(z); + nonempty = (data != NULL); + if (!nonempty) return NULL; +#endif + do + { +#ifndef _WIN32 + DIR *y; +#endif + char *p; + u32 is_subdir; +#ifndef _WIN32 + if (data->d_name[0] == '.') +#else + if (ffd.cFileName[0] == '.') +#endif + continue; - pointui startPixels = {my_Min(tmp.x, tmp.y), my_Max(tmp.x, tmp.y)}; - u32 lengthMove = startPixels.y - startPixels.x + 1; - pointui editRange; +#ifndef _WIN32 + strncpy(buffer + n, data->d_name, MAX_PATH_LEN-n); + y = opendir(buffer); + is_subdir = (y != NULL); + if (y != NULL) closedir(y); +#else + strncpy(buffer + n, ffd.cFileName, MAX_PATH_LEN-n); + is_subdir = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; +#endif - if (delta > 0) - { - editRange.x = startPixels.x; - editRange.y = my_Max(finalPixels.x, finalPixels.y); - } - else - { - editRange.x = my_Min(finalPixels.x, finalPixels.y); - editRange.y = startPixels.y; + if ((return_subdirs && is_subdir) || (!is_subdir && !return_subdirs)) + { + if (!size) + { + results = (char**)calloc(sizeof(char*), capacity); + } + else if (size >= capacity) + { + void *old = results; + capacity = capacity * 2; + results = (char**)realloc(results, capacity * sizeof(char*)); + Assert(results); + if (!results) free(old); + } +#ifndef _WIN32 + p = StrDuplicate(data->d_name); +#else + p = StrDuplicate(ffd.cFileName); +#endif + results[size++] = p; + } +#ifndef _WIN32 + } while ((data = readdir(z)) != NULL); +#else + } while (FindNextFile(hFind, &ffd) != 0); +#endif } - f32 ooNPixels = (f32)(1.0 / (f64)Number_of_Pixels_1D); - point2f editRangeModel = {((f32)editRange.x * ooNPixels) - 0.5f, ((f32)editRange.y * ooNPixels) - 0.5f}; - f32 dRange = editRangeModel.y - editRangeModel.x; +#ifndef _WIN32 + if (z) closedir(z); +#else + FindClose(hFind); +#endif + *count = size; + + CharArrayBubbleSort(results, (u32)size); + + return results; +} - waypoint **searchBuffer = PushArray(Working_Set, waypoint*, Waypoints_Stack_Size); - waypoint **bufferEnd = searchBuffer; +global_function +struct file_group +FILE_GROUP(enum file_groups group, const char *name, struct nk_image *icon) +{ + struct file_group fg; + fg.group = group; + fg.name = name; + fg.icon = icon; + return fg; +} - GetWaypointsWithinRectange({editRangeModel.x, -0.5f}, {dRange, 0.999f}, &bufferEnd); - GetWaypointsWithinRectange({-0.5f, editRangeModel.x}, {0.999f, dRange}, &bufferEnd -#ifdef Internal -, 0 -#endif - ); +global_function +struct file +FILE_DEF(enum file_types type, const char *suffix, enum file_groups group) +{ + struct file fd; + fd.type = type; + fd.suffix = suffix; + fd.group = group; + return fd; +} - u32 nWayp = (u32)((my_Max((size_t)bufferEnd, (size_t)searchBuffer) - my_Min((size_t)bufferEnd, (size_t)searchBuffer)) / sizeof(searchBuffer)); - waypoint **seen = PushArray(Working_Set, waypoint*, nWayp); - u32 seenIdx = 0; +global_function +struct nk_image* +MediaIconForFile(struct media *media, const char *file) +{ + u32 i = 0; + const char *s = file; + char suffix[16]; + u32 found = 0; + memset(suffix, 0, sizeof(suffix)); - for ( waypoint **waypPtr = searchBuffer; - waypPtr != bufferEnd; - ++waypPtr ) + /* extract suffix .xxx from file */ + while (*s++ != '\0') { - waypoint *wayp = *waypPtr; - u32 newWayP = 1; + if (found && i < (sizeof(suffix)-1)) + suffix[i++] = *s; - ForLoop(seenIdx) + if (*s == '.') { - if (wayp == seen[index]) + if (found) { - newWayP = 0; + found = 0; break; } + found = 1; } + } - if (newWayP) + /* check for all file definition of all groups for fitting suffix*/ + for ( i = 0; + i < FILE_MAX && found; + ++i) + { + struct file *d = &media->files[i]; { - point2f normCoords = {my_Min(wayp->coords.x, wayp->coords.y), my_Max(wayp->coords.x, wayp->coords.y)}; - - u32 inXRange = normCoords.x >= editRangeModel.x && normCoords.x < editRangeModel.y; - u32 inYRange = normCoords.y >= editRangeModel.x && normCoords.y < editRangeModel.y; - u32 inRange = inXRange || inYRange; - - if (inRange) + const char *f = d->suffix; + s = suffix; + while (f && *f && *s && *s == *f) { - u32 upperTri = wayp->coords.x > wayp->coords.y; - - pointui pix = {(u32)((0.5f + wayp->coords.x) / ooNPixels), (u32)((0.5f + wayp->coords.y) / ooNPixels)}; + s++; f++; + } - if (pix.x >= startPixels.x && pix.x < startPixels.y) - { - pix.x = (u32)((s32)pix.x + delta); + /* found correct file definition so */ + if (f && *s == '\0' && *f == '\0') + return media->group[d->group].icon; + } + } + return &media->icons.default_file; +} - if (edit->finalPix1 > edit->finalPix2) - { - pix.x = my_Max(finalPixels.x, finalPixels.y) - pix.x + my_Min(finalPixels.x, finalPixels.y); - } - } - else if (pix.x >= editRange.x && pix.x < editRange.y) - { - pix.x = delta > 0 ? pix.x - lengthMove : pix.x + lengthMove; - } +global_function +void +MediaInit(struct media *media) +{ + /* file groups */ + struct icons *icons = &media->icons; + media->group[FILE_GROUP_DEFAULT] = FILE_GROUP(FILE_GROUP_DEFAULT,"default",&icons->default_file); + media->group[FILE_GROUP_PRETEXT] = FILE_GROUP(FILE_GROUP_PRETEXT, "pretext", &icons->img_file); - if (pix.y >= startPixels.x && pix.y < startPixels.y) - { - pix.y = (u32)((s32)pix.y + delta); + /* files */ + media->files[FILE_DEFAULT] = FILE_DEF(FILE_DEFAULT, NULL, FILE_GROUP_DEFAULT); + media->files[FILE_PRETEXT] = FILE_DEF(FILE_PRETEXT, "pretext", FILE_GROUP_PRETEXT); + media->files[FILE_PSTM] = FILE_DEF(FILE_PSTM, "pstm", FILE_GROUP_PRETEXT); +} - if (edit->finalPix1 > edit->finalPix2) - { - pix.y = my_Max(finalPixels.x, finalPixels.y) - pix.y + my_Min(finalPixels.x, finalPixels.y); - } - } - else if (pix.y >= editRange.x && pix.y < editRange.y) - { - pix.y = delta > 0 ? pix.y - lengthMove : pix.y + lengthMove; - } - - point2f newCoords = {((f32)pix.x * ooNPixels) - 0.5f, ((f32)pix.y * ooNPixels) - 0.5f}; - - u32 upperTriNew = newCoords.x > newCoords.y; - if (upperTriNew != upperTri) - { - newCoords = {newCoords.y, newCoords.x}; - } - - UpdateWayPoint(wayp, newCoords); - } - - seen[seenIdx++] = wayp; - } +global_function +void +FileBrowserReloadDirectoryContent(struct file_browser *browser, const char *path) +{ + // check if the path is valid, yes: run the load, no: set the path to the home directory + if (path && std::filesystem::exists(path) && std::filesystem::is_directory(path)) + strncpy(browser->directory, path, MAX_PATH_LEN); // copy the path to the browser directory + else{ + fmt::println("[FileBrowserReloadDirectoryContent::Warning]: Invalid path: {}, setting to home directory {}.", path, browser->home); + strncpy(browser->directory, browser->home, MAX_PATH_LEN); // if not valid, set the path to the home directory } - - FreeLastPush(Working_Set); // searchBuffer - FreeLastPush(Working_Set); // seen + DirFreeList(browser->files, browser->file_count); + DirFreeList(browser->directories, browser->dir_count); + browser->files = DirList(path, 0, &browser->file_count); + browser->directories = DirList(path, 1, &browser->dir_count); + UserSaveState(); // save the current directory to the user profile cache } global_function void -GetWaypointsWithinRectange(waypoint_quadtree_level *level, point2f lowerBound, point2f size, waypoint ***bufferPtr) +FileBrowserInit(struct file_browser *browser, struct media *media) { - if (level->children[0]) + memset(browser, 0, sizeof(*browser)); + browser->media = media; { - u08 toSearch[4] = {1, 1, 1, 1}; -#define epsilon 0.001f - ForLoop(4) - { - // bottom left - waypoint_quadtree_level *child = level->children[index]; - point2f insertSize = {lowerBound.x - child->lowerBound.x, lowerBound.y - child->lowerBound.y}; - if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) - { - point2f recSize = {my_Min(size.x, child->size - insertSize.x - epsilon), my_Min(size.y, child->size - insertSize.y - epsilon)}; - GetWaypointsWithinRectange(child, lowerBound, recSize, bufferPtr); - toSearch[index] = 0; - break; - } - } - ForLoop(4) - { - if (toSearch[index]) - { - // bottom right - waypoint_quadtree_level *child = level->children[index]; - point2f insertSize = {lowerBound.x + size.x - child->lowerBound.x, lowerBound.y - child->lowerBound.y}; - if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) - { - point2f recSize = {my_Min(size.x, insertSize.x), my_Min(size.y, child->size - insertSize.y - epsilon)}; - GetWaypointsWithinRectange(child, {child->lowerBound.x + insertSize.x - recSize.x, lowerBound.y}, recSize, bufferPtr); - toSearch[index] = 0; - break; - } - } - } - ForLoop(4) - { - if (toSearch[index]) - { - // top left - waypoint_quadtree_level *child = level->children[index]; - point2f insertSize = {lowerBound.x - child->lowerBound.x, lowerBound.y + size.y - child->lowerBound.y}; - if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) - { - point2f recSize = {my_Min(size.x, child->size - insertSize.x - epsilon), my_Min(size.y, insertSize.y)}; - GetWaypointsWithinRectange(child, {lowerBound.x, child->lowerBound.y + insertSize.y - recSize.y}, recSize, bufferPtr); - toSearch[index] = 0; - break; - } - } - } - ForLoop(4) - { - if (toSearch[index]) - { - // top right - waypoint_quadtree_level *child = level->children[index]; - point2f insertSize = {lowerBound.x + size.x - child->lowerBound.x, lowerBound.y + size.y - child->lowerBound.y}; - if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) - { - point2f recSize = {my_Min(size.x, insertSize.x), my_Min(size.y, insertSize.y)}; - GetWaypointsWithinRectange(child, {child->lowerBound.x + insertSize.x - recSize.x, child->lowerBound.y + insertSize.y - recSize.y}, recSize, bufferPtr); -#ifdef Internal - toSearch[index] = 0; + /* load files and sub-directory list */ + const char *home = getenv("HOME"); +#ifdef _WIN32 + if (!home) home = getenv("USERPROFILE"); +#else + if (!home) home = getpwuid(getuid())->pw_dir; #endif - break; - } - } - } - -#ifdef Internal - ForLoop(4) { - if (!toSearch[index]) level->children[index]->show = 1; - } + size_t l; + strncpy(browser->home, home, MAX_PATH_LEN); + l = strlen(browser->home); +#ifdef _WIN32 + char *sep = (char *)"\\"; +#else + char *sep = (char *)"/"; #endif - - } - else - { - TraverseLinkedList(level->headNode.next, waypoint_quadtree_node) - { - **bufferPtr = node->wayp; - ++(*bufferPtr); + strcpy(browser->home + l, sep); + strcpy(browser->directory, browser->home); } + + browser->files = DirList(browser->directory, 0, &browser->file_count); + browser->directories = DirList(browser->directory, 1, &browser->dir_count); } } -#ifdef Internal +global_variable +char +Save_State_Name_Buffer[1024] = {0}; + +global_variable +char +AGP_Name_Buffer[1024] = {0}; + global_function void -TurnOffDrawingForQuadTreeLevel(waypoint_quadtree_level *level = 0) +SetSaveStateNameBuffer(char *name) { - if (!level) level = Waypoint_Editor->quadtree; - level->show = 0; - ForLoop(4) + u32 ptr = 0; + while (*name) { - if (level->children[index]) TurnOffDrawingForQuadTreeLevel(level->children[index]); + AGP_Name_Buffer[ptr] = *name; + Save_State_Name_Buffer[ptr++] = *name++; } -} -#endif + + u32 ptr1 = ptr; + name = (char *)".savestate_1"; + while (*name) Save_State_Name_Buffer[ptr++] = *name++; + Save_State_Name_Buffer[ptr] = 0; -global_function -void -GetWaypointsWithinSquare(point2f lowerBound, f32 size, waypoint ***bufferPtr -#ifdef Internal - , u32 reset = 1) -#else - ) -#endif -{ -#ifdef Internal - if (reset) TurnOffDrawingForQuadTreeLevel(); -#endif - GetWaypointsWithinRectange(Waypoint_Editor->quadtree, lowerBound, {size, size}, bufferPtr); + ptr = ptr1; + name = (char *)".agp_1"; + while (*name) AGP_Name_Buffer[ptr++] = *name++; + AGP_Name_Buffer[ptr] = 0; } -global_function -void -GetWaypointsWithinRectange(point2f lowerBound, point2f size, waypoint ***bufferPtr -#ifdef Internal - , u32 reset) -#else - ) -#endif -{ -#ifdef Internal - if (reset) TurnOffDrawingForQuadTreeLevel(); -#endif - GetWaypointsWithinRectange(Waypoint_Editor->quadtree, lowerBound, size, bufferPtr); -} +/* +@params: + - save 0 file open + 1 save state + 2 agp state +*/ global_function -waypoint_quadtree_level * -GetWaypointQuadTreeLevel(point2f coords) +u08 +FileBrowserRun(const char *name, struct file_browser *browser, struct nk_context *ctx, u32 show, u08 save = 0) { - coords = {my_Max(my_Min(coords.x, 0.499f), -0.5f), my_Max(my_Min(coords.y, 0.499f), -0.5f)}; - - waypoint_quadtree_level *level = Waypoint_Editor->quadtree; - - while (level->children[0]) - { -#ifdef DEBUG - u32 found = 0; -#endif - ForLoop(4) - { - point2f insertSize = {coords.x - level->children[index]->lowerBound.x, coords.y - level->children[index]->lowerBound.y}; - f32 size = level->children[index]->size; - if (insertSize.x >= 0 && insertSize.x < size && insertSize.y >= 0 && insertSize.y < size) - { - level = level->children[index]; - -#ifdef DEBUG - found = 1; -#endif - - break; - } - } - -#ifdef DEBUG - if (!found) - { - Assert(found); - } +#ifndef _WIN32 + char pathSep = '/'; +#else + char pathSep = '\\'; #endif + + struct nk_window *window = nk_window_find(ctx, name); + u32 doesExist = window != 0; + if (!show && !doesExist) + { + return(0); } - return(level); -} + if (show && doesExist && (window->flags & NK_WINDOW_HIDDEN)) + { + window->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; + FileBrowserReloadDirectoryContent(browser, browser->directory); + } -global_function -void -AddWayPoint(point2f coords) -{ - u32 nFree = Waypoints_Stack_Size - Waypoint_Editor->nWaypointsActive; + u08 ret = 0; + struct media *media = browser->media; + struct nk_rect total_space; - if (nFree) + if (nk_begin(ctx, name, nk_rect(Screen_Scale.x * 50, Screen_Scale.y * 50, Screen_Scale.x * 800, Screen_Scale.y * 700), + NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE|NK_WINDOW_CLOSABLE)) { - waypoint *wayp = Waypoint_Editor->freeWaypoints.next; - Waypoint_Editor->freeWaypoints.next = wayp->next; - if (Waypoint_Editor->freeWaypoints.next) Waypoint_Editor->freeWaypoints.next->prev = &Waypoint_Editor->freeWaypoints; - - wayp->next = Waypoint_Editor->activeWaypoints.next; - if (Waypoint_Editor->activeWaypoints.next) Waypoint_Editor->activeWaypoints.next->prev = wayp; - Waypoint_Editor->activeWaypoints.next = wayp; - wayp->prev = &Waypoint_Editor->activeWaypoints; - wayp->index = wayp->next ? wayp->next->index + 1 : 0; + static f32 ratio[] = {0.25f, NK_UNDEFINED}; + f32 spacing_x = ctx->style.window.spacing.x; + nk_style_set_font(ctx, &NK_Font_Browser->handle); - wayp->coords = coords; - wayp->z = Camera_Position.z; - ++Waypoint_Editor->nWaypointsActive; + /* output path directory selector in the menubar */ + ctx->style.window.spacing.x = 0; + nk_menubar_begin(ctx); + { + char *d = browser->directory; + char *begin = d + 1; + nk_layout_row_dynamic(ctx, Screen_Scale.y * 25.0f, 6); + while (*d++) + { + if (*d == pathSep) + { + *d = '\0'; + if (nk_button_label(ctx, begin)) + { + *d++ = pathSep; *d = '\0'; + FileBrowserReloadDirectoryContent(browser, browser->directory); + break; + } + *d = pathSep; + begin = d + 1; + } + } + } + nk_menubar_end(ctx); + ctx->style.window.spacing.x = spacing_x; - waypoint_quadtree_level *level = GetWaypointQuadTreeLevel(wayp->coords); - - waypoint_quadtree_node *node = Waypoint_Editor->freeNodes.next; - if (node->next) node->next->prev = &Waypoint_Editor->freeNodes; - Waypoint_Editor->freeNodes.next = node->next; + /* window layout */ + f32 endSpace = save ? 100.0f : 0; // Resolved: end space is clipped as the space is not enough + total_space = nk_window_get_content_region(ctx); + nk_layout_row(ctx, NK_DYNAMIC, total_space.h - endSpace, 2, ratio); + nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR); + { + struct nk_image home = media->icons.home; + struct nk_image computer = media->icons.computer; - if (level->headNode.next) level->headNode.next->prev = node; - node->next = level->headNode.next; - node->prev = &level->headNode; - level->headNode.next = node; + nk_layout_row_dynamic(ctx, Screen_Scale.y * 40.0f, 1); + if (nk_button_image_label(ctx, home, "home", NK_TEXT_CENTERED)) + FileBrowserReloadDirectoryContent(browser, browser->home); + if (nk_button_image_label(ctx,computer,"computer",NK_TEXT_CENTERED)) +#ifndef _WIN32 + FileBrowserReloadDirectoryContent(browser, "/"); +#else + FileBrowserReloadDirectoryContent(browser, "C:\\"); +#endif + nk_group_end(ctx); + } - wayp->node = node; - node->wayp = wayp; - } -} + /* output directory content window */ + nk_group_begin(ctx, "Content", 0); + { + /* define a string here for filtering */ + std::string searchbuf_str=""; + if (!save){ // only need this while loading something + nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 25.0f, 3); + nk_label(NK_Context, "Search: ", NK_TEXT_LEFT); + nk_edit_string_zero_terminated(NK_Context, NK_EDIT_FIELD, searchbuf, sizeof(searchbuf) - 1, nk_filter_default); + caseSensitive_search_sequences = nk_check_label(NK_Context, "Case Sensitive", caseSensitive_search_sequences) ? 1 : 0; + searchbuf_str = std::string(searchbuf); + if (!caseSensitive_search_sequences) std::transform(searchbuf_str.begin(), searchbuf_str.end(), searchbuf_str.begin(), ::tolower); + } + + s32 index = -1; + size_t i = 0, j = 0;//, k = 0; + size_t rows = 0, cols = 0; + size_t count = browser->dir_count + browser->file_count; + f32 iconRatio[] = {0.05f, NK_UNDEFINED}; -global_function -void -RemoveWayPoint(waypoint *wayp) -{ - switch (Waypoint_Editor->nWaypointsActive) - { - case 0: - wayp = 0; - break; + cols = 1; + rows = count / cols; + for ( i = 0; + i <= rows; + i += 1) + { + size_t n = j + cols; + // nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 25.0f, 2, iconRatio); + for ( ; + j < count && j < n; + ++j) + { + /* draw one row of icons */ + if (j < browser->dir_count) + { + if (!searchbuf_str.empty()){ + std::string dir_str = browser->directories[j]; + if (!caseSensitive_search_sequences) std::transform(dir_str.begin(), dir_str.end(), dir_str.begin(), ::tolower); + if (dir_str.find(searchbuf_str) == std::string::npos) continue; // not found + } + /* draw and execute directory buttons */ + if (j == n - cols) nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 25.0f, 2, iconRatio); + if (nk_button_image(ctx,media->icons.directory)) index = (s32)j; + nk_label(ctx, browser->directories[j], NK_TEXT_LEFT); + } + else + { + /* draw and execute files buttons */ + struct nk_image *icon; + size_t fileIndex = ((size_t)j - browser->dir_count); + if (!searchbuf_str.empty()){ + std::string dir_str = browser->files[fileIndex]; + if (!caseSensitive_search_sequences) std::transform(dir_str.begin(), dir_str.end(), dir_str.begin(), ::tolower); + if (dir_str.find(searchbuf_str) == std::string::npos) continue; // not found + } + if (j == n - cols) nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 25.0f, 2, iconRatio); + icon = MediaIconForFile(media,browser->files[fileIndex]); + if (nk_button_image(ctx, *icon)) + { + if (save) + { + strncpy(save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer, browser->files[fileIndex], sizeof(save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer)); + } + else + { + strncpy(browser->file, browser->directory, MAX_PATH_LEN); + n = strlen(browser->file); + strncpy(browser->file + n, browser->files[fileIndex], MAX_PATH_LEN - n); + ret = 1; + } + } + nk_label(ctx,browser->files[fileIndex],NK_TEXT_LEFT); + } + } + } - case 1: - Waypoint_Editor->activeWaypoints.next = 0; - Waypoint_Editor->nWaypointsActive = 0; - break; + if (index != -1) + { + size_t n = strlen(browser->directory); + strncpy(browser->directory + n, browser->directories[index], MAX_PATH_LEN - n); + n = strlen(browser->directory); + if (n < MAX_PATH_LEN - 1) + { + browser->directory[n] = pathSep; + browser->directory[n+1] = '\0'; + } + FileBrowserReloadDirectoryContent(browser, browser->directory); + } + + nk_group_end(ctx); + } - default: - if (wayp->next) wayp->next->prev = wayp->prev; - if (wayp->prev) wayp->prev->next = wayp->next; - --Waypoint_Editor->nWaypointsActive; - } + if (save) + { + Deferred_Close_UI = 0; - if (wayp) - { - wayp->next = Waypoint_Editor->freeWaypoints.next; - if (Waypoint_Editor->freeWaypoints.next) Waypoint_Editor->freeWaypoints.next->prev = wayp; - Waypoint_Editor->freeWaypoints.next = wayp; - wayp->prev = &Waypoint_Editor->freeWaypoints; + nk_layout_row(ctx, NK_DYNAMIC, endSpace - 5.0f, 1, ratio + 1); + nk_group_begin(ctx, "File", NK_WINDOW_NO_SCROLLBAR); + { + f32 fileRatio[] = {0.8f, 0.1f, NK_UNDEFINED}; + f32 fileRatio2[] = {0.45f, 0.1f, 0.18f, 0.17f, NK_UNDEFINED}; + nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 35.0f, save == 2 ? 5 : 3, save == 2 ? fileRatio2 : fileRatio); - waypoint_quadtree_node *node = wayp->node; + u08 saveViaEnter = (nk_edit_string_zero_terminated(ctx, NK_EDIT_FIELD | NK_EDIT_SIG_ENTER, save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer, sizeof(save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer), 0) & NK_EDIT_COMMITED) ? 1 : 0; + + static u08 overwrite = 0; + overwrite = (u08)nk_check_label(ctx, "Override", overwrite); + + static u08 singletons = 0; + if (save == 2) singletons = (u08)nk_check_label(ctx, "Format Singletons", singletons); + static u08 preserveOrder = 0; + if (save == 2) preserveOrder = (u08)nk_check_label(ctx, "Preserve Order", preserveOrder); - if (node->next) node->next->prev = node->prev; - if (node->prev) node->prev->next = node->next; - node->prev = 0; + if (nk_button_label(ctx, "Save") || saveViaEnter) + { + strncpy(browser->file, browser->directory, MAX_PATH_LEN); + size_t n = strlen(browser->file); + strncpy(browser->file + n, save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer, MAX_PATH_LEN - n); + ret = 1 | (overwrite ? 2 : 0) | (singletons ? 4 : 0) | (preserveOrder ? 8 : 0); + } - if (Waypoint_Editor->freeNodes.next) Waypoint_Editor->freeNodes.next->prev = node; - node->next = Waypoint_Editor->freeNodes.next; - Waypoint_Editor->freeNodes.next = node; - node->prev = &Waypoint_Editor->freeNodes; + nk_group_end(ctx); + } + } + + nk_style_set_font(ctx, &NK_Font->handle); } + nk_end(ctx); + + return(ret); } global_function void -UpdateWayPoint(waypoint *wayp, point2f coords) +MetaTagsEditorRun(struct nk_context *ctx, u08 show) { - waypoint_quadtree_node *node = wayp->node; + const char *name = "Meta Data Tag Editor"; + struct nk_window *window = nk_window_find(ctx, name); - if (node->next) node->next->prev = node->prev; - if (node->prev) node->prev->next = node->next; - node->prev = 0; + u08 doesExist = window != 0; + if (!show && !doesExist) return; + if (show && doesExist && (window->flags & NK_WINDOW_HIDDEN)) window->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; - wayp->coords = coords; + if (nk_begin(ctx, name, nk_rect(Screen_Scale.x * 50, Screen_Scale.y * 50, Screen_Scale.x * 800, Screen_Scale.y * 600), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE|NK_WINDOW_NO_SCROLLBAR)) + { + Deferred_Close_UI = 0; - waypoint_quadtree_level *level = GetWaypointQuadTreeLevel(wayp->coords); + struct nk_rect total_space = nk_window_get_content_region(ctx); + f32 ratio[] = {0.05f, NK_UNDEFINED}; + nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 1, ratio + 1); - if (level->headNode.next) level->headNode.next->prev = node; - node->next = level->headNode.next; - node->prev = &level->headNode; - level->headNode.next = node; + nk_group_begin(ctx, "Content", 0); + { + nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 35.0f, 2, ratio); - wayp->node = node; - node->wayp = wayp; + ForLoop(ArrayCount(Meta_Data->tags)) + { + char buff[4]; + stbsp_snprintf(buff, sizeof(buff), "%u:", index + 1); + nk_label(ctx, buff, NK_TEXT_LEFT); + if (nk_edit_string_zero_terminated(ctx, NK_EDIT_FIELD, (char *)Meta_Data->tags[index], sizeof(Meta_Data->tags[index]), 0) & NK_EDIT_ACTIVE) + { + if (!strlen((const char *)Meta_Data->tags[index])) + { + ForLoop2(Number_of_Pixels_1D) Map_State->metaDataFlags[index2] &= ~(1 << index); + u32 nextActive = index; + ForLoop2((ArrayCount(Meta_Data->tags) - 1)) + { + if (++nextActive == ArrayCount(Meta_Data->tags)) nextActive = 0; + if (strlen((const char *)Meta_Data->tags[nextActive])) + { + MetaData_Active_Tag = nextActive; + break; + } + } + } + else if (!strlen((const char *)Meta_Data->tags[MetaData_Active_Tag])) MetaData_Active_Tag = index; + } + } + + nk_group_end(ctx); + } + } + nk_end(ctx); } global_function void -MouseMove(GLFWwindow* window, f64 x, f64 y) +AboutWindowRun(struct nk_context *ctx, u32 show) { - if (Loading || auto_sort_state || auto_cut_state) + struct nk_window *window = nk_window_find(ctx, "About"); + u32 doesExist = window != 0; + + if (!show && !doesExist) { return; } - (void)window; - - u32 redisplay = 0; - - if (UI_On) + if (show && doesExist && (window->flags & NK_WINDOW_HIDDEN)) { + window->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; } - else // ui_on == false - { - if (Edit_Mode) - { - static s32 netDelta = 0; - - s32 w, h; - glfwGetWindowSize(window, &w, &h); - f32 height = (f32)h; - f32 width = (f32)w; - f32 factor1 = 1.0f / (2.0f * Camera_Position.z); - f32 factor2 = 2.0f / height; - f32 factor3 = width * 0.5f; + enum windowMode {showAcknowledgements, showLicence, showThirdParty}; - f32 wx = (factor1 * factor2 * ((f32)x - factor3)) + Camera_Position.x; - f32 wy = (-factor1 * (1.0f - (factor2 * (f32)y))) - Camera_Position.y; + static windowMode mode = showAcknowledgements; - wx = my_Max(-0.5f, my_Min(0.5f, wx)); - wy = my_Max(-0.5f, my_Min(0.5f, wy)); + if (nk_begin_titled(ctx, "About", PretextView_Version, nk_rect(Screen_Scale.x * 50, Screen_Scale.y * 50, Screen_Scale.x * 870, Screen_Scale.y * 610), + NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE|NK_WINDOW_CLOSABLE)) + { + nk_menubar_begin(ctx); + { + nk_layout_row_dynamic(ctx, Screen_Scale.y * 35.0f, 3); + if (nk_button_label(ctx, "Acknowledgements")) + { + mode = showAcknowledgements; + } + if (nk_button_label(ctx, "Licence")) + { + mode = showLicence; + } + if (nk_button_label(ctx, "Third Party Software")) + { + mode = showThirdParty; + } + } + nk_menubar_end(ctx); - u32 nPixels = Number_of_Pixels_1D; + struct nk_rect total_space = nk_window_get_content_region(ctx); + f32 one = NK_UNDEFINED; + nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 1, &one); - u32 pixel1 = (u32)((f64)nPixels * (0.5 + (f64)wx)); - u32 pixel2 = (u32)((f64)nPixels * (0.5 + (f64)wy)); - - // 防止在尾部溢出 - pixel1 = my_Max(0, my_Min(nPixels - 1, pixel1)); - pixel2 = my_Max(0, my_Min(nPixels - 1, pixel2)); + nk_group_begin(ctx, "About_Content", 0); + { + if (mode == showThirdParty) + { + u08 text[] = R"text(PretextView was made possible thanks to the following third party libraries and +resources, click each entry to view its licence.)text"; - u32 contig = Map_State->contigIds[pixel1]; + nk_layout_row_static(ctx, Screen_Scale.y * 60, (s32)(Screen_Scale.x * 820), 1); + s32 len = sizeof(text); + nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)text, &len, len, 0); - if (!Edit_Pixels.editing && !Edit_Pixels.selecting && Map_State->contigIds[pixel2] != contig) - { - u32 testPixel = pixel1; - u32 testContig = contig; - while (testContig == contig) // move testContig to the 1 before the first or 1 after the last pixel of the contig + ForLoop(Number_of_ThirdParties) { + u32 nameIndex = 2 * index; + u32 licenceIndex = nameIndex + 1; + s32 *sizes = ThirdParty_Licence_Sizes[index]; - if (pixel1 < pixel2) - { - testPixel ++ ; - } - else + if (nk_tree_push_id(NK_Context, NK_TREE_TAB, (const char *)ThirdParty[nameIndex], NK_MINIMIZED, (s32)index)) { - testPixel -- ; + nk_layout_row_static(ctx, Screen_Scale.y * (f32)sizes[0], (s32)(Screen_Scale.x * (f32)sizes[1]), 1); + len = (s32)StringLength(ThirdParty[licenceIndex]); + nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)ThirdParty[licenceIndex], &len, len, 0); + nk_tree_pop(NK_Context); } - - testContig = Map_State->contigIds[testPixel]; - - if (testPixel == 0 || testPixel >= (nPixels - 1)) break; - - // testContig = pixel1 < pixel2 ? Map_State->contigIds[++testPixel] : Map_State->contigIds[--testPixel]; - // if (testPixel == 0 || testPixel >= (nPixels - 1)) break; - } - if (Map_State->contigIds[testPixel] == contig) - { - pixel2 = testPixel; - } - else - { - pixel2 = pixel1 < pixel2 ? testPixel - 1 : testPixel + 1; } } - - - if (Edit_Pixels.selecting) // select the contig + else if (mode == showAcknowledgements) { - Edit_Pixels.selectPixels.x = my_Max(pixel1, my_Max(Edit_Pixels.selectPixels.x, Edit_Pixels.selectPixels.y)); // used to select multiple contigs with pressing space and dragging the mouse - // Edit_Pixels.selectPixels.x = pixel1; - while( Edit_Pixels.selectPixels.x < (Number_of_Pixels_1D - 1) && - ((Edit_Pixels.scaffSelecting && Map_State->scaffIds[Edit_Pixels.selectPixels.x] && Map_State->scaffIds[Edit_Pixels.selectPixels.x] == Map_State->scaffIds[1 + Edit_Pixels.selectPixels.x] ) || - Map_State->contigIds[Edit_Pixels.selectPixels.x] == Map_State->contigIds[1 + Edit_Pixels.selectPixels.x])) - { - ++Edit_Pixels.selectPixels.x; - } + nk_layout_row_static(ctx, Screen_Scale.y * 500, (s32)(Screen_Scale.x * 820), 1); + s32 len = sizeof(Acknowledgements); + nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)Acknowledgements, &len, len, 0); + } + else + { + nk_layout_row_static(ctx, Screen_Scale.y * 500, (s32)(Screen_Scale.x * 820), 1); + s32 len = sizeof(Licence); + nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)Licence, &len, len, 0); + } + + nk_group_end(ctx); + } + } - Edit_Pixels.selectPixels.y = my_Min(pixel1, my_Min(Edit_Pixels.selectPixels.x, Edit_Pixels.selectPixels.y)); - // Edit_Pixels.selectPixels.y = pixel1; - while( Edit_Pixels.selectPixels.y > 0 && - ((Edit_Pixels.scaffSelecting && Map_State->scaffIds[Edit_Pixels.selectPixels.y] && Map_State->scaffIds[Edit_Pixels.selectPixels.y] == Map_State->scaffIds[Edit_Pixels.selectPixels.y - 1]) || Map_State->contigIds[Edit_Pixels.selectPixels.y] == Map_State->contigIds[Edit_Pixels.selectPixels.y - 1])) - { - --Edit_Pixels.selectPixels.y; - } + nk_end(ctx); +} - pixel1 = Edit_Pixels.selectPixels.x; - pixel2 = Edit_Pixels.selectPixels.y; - } - if (Edit_Pixels.editing) - { - u32 midPixel = (pixel1 + pixel2) >> 1; - u32 oldMidPixel = (Edit_Pixels.pixels.x + Edit_Pixels.pixels.y) >> 1; - s32 diff = (s32)midPixel - (s32)oldMidPixel; +global_function +void +UpdateContigsFromMapState() // reading 从map的状态更新contigs +{ + u32 lastScaffID = Map_State->scaffIds[0]; // 第一个scaff的编号 + u32 scaffId = lastScaffID ? 1 : 0; // + u32 lastId_original_contig = Map_State->originalContigIds[0]; // 第一个像素点对应的id + u32 lastCoord = Map_State->contigRelCoords[0]; // 第一个像素点的局部坐标 + u32 contigPtr = 0; + u32 length = 0; + u32 startCoord = lastCoord; + u08 inverted = Map_State->contigRelCoords[1] < lastCoord; // 判断是不是反转的 + Map_State->contigIds[0] = 0; + + u32 pixelIdx = 0; + ForLoop(Number_of_Original_Contigs) (Original_Contigs + index)->nContigs = 0; // 将每一个contig的 片段数目 置为零 + ForLoop(Number_of_Pixels_1D - 1) // 遍历每一个像素点 更新 Original_Contigs, Contigs + // 遍历完之后,contigPtr为214,但是Number_of_Original_Contigs = 218 + { + if (contigPtr >= Max_Number_of_Contigs) break; // 确保 contigPtr 不超出最大contig的数值 - u32 forward = diff > 0; - - s32 newX = (s32)Edit_Pixels.pixels.x + diff; - newX = my_Max(0, my_Min((s32)nPixels - 1, newX)); - s32 newY = (s32)Edit_Pixels.pixels.y + diff; - newY = my_Max(0, my_Min((s32)nPixels - 1, newY)); + ++length; // current fragment length - s32 diffx = newX - (s32)Edit_Pixels.pixels.x; - s32 diffy = newY - (s32)Edit_Pixels.pixels.y; + pixelIdx = index + 1; // 像素点编号, 加一因为第一个已经用来初始化了 + u32 original_contig_id = Map_State->originalContigIds[pixelIdx]; // 像素点的 original contig id, 这里 不使用 % Max_Number_of_Contigs的值是为了区分后面cut之后的片段 + u32 coord = Map_State->contigRelCoords[pixelIdx]; // 像素点的局部坐标 + + if ( + original_contig_id != lastId_original_contig || + (inverted && coord != (lastCoord - 1)) || + (!inverted && coord != (lastCoord + 1)) + ) // not a continuous fragment + { + Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].contigMapPixels[Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].nContigs] = pixelIdx - 1 - (length >> 1); // update Original_Contigs: contigMapPixels + Original_Contigs[lastId_original_contig % Max_Number_of_Contigs].nContigs++; // update Original_Contigs: nContigs, contigMapPixels - diff = forward ? my_Min(diffx, diffy) : my_Max(diffx, diffy); - - //newX = (s32)Edit_Pixels.pixels.x + diff; - //newY = (s32)Edit_Pixels.pixels.y + diff; - - diff = RearrangeMap(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y, diff, Edit_Pixels.snap); - netDelta += diff; + contig *last_cont = Contigs->contigs_arr + contigPtr; // 获取上一个contig的指针, 并且给contigPtr + 1 + contigPtr++; + last_cont->originalContigId = lastId_original_contig; // 更新这个片段的id + last_cont->length = length; // 更新长度 + last_cont->startCoord = startCoord; // 更新开头为当前片段在该contig上的局部坐标 endCoord = startCoord + length - 1 + last_cont->metaDataFlags = Map_State->metaDataFlags + pixelIdx - 1; // Finished (shaoheng): memory problem: assign the pointer to the cont->metaDataFlags, the original is nullptr, the let this ptr point to the last pixel of the contig - newX = (s32)Edit_Pixels.pixels.x + diff; - newY = (s32)Edit_Pixels.pixels.y + diff; - - Edit_Pixels.pixels.x = (u32)newX; - Edit_Pixels.pixels.y = (u32)newY; + u32 thisScaffID = Map_State->scaffIds[pixelIdx - 1]; // 上一个像素点对应的 scaffid + last_cont->scaffId = thisScaffID ? ((thisScaffID == lastScaffID) ? (scaffId) : (++scaffId)) : 0; // 如果存在scaffid则(判断是不是同一个scaff,如果是则继续用scaffid,否则++scaffid),否则为0 + lastScaffID = thisScaffID; // 更新 - Edit_Pixels.worldCoords.x = (f32)(((f64)((2 * Edit_Pixels.pixels.x) + 1)) / ((f64)(2 * nPixels))) - 0.5f; - Edit_Pixels.worldCoords.y = (f32)(((f64)((2 * Edit_Pixels.pixels.y) + 1)) / ((f64)(2 * nPixels))) - 0.5f; - } - else // edit_pixels.editing == 0 - { - if (netDelta || Global_Edit_Invert_Flag) - { - AddMapEdit(netDelta, Edit_Pixels.pixels, Global_Edit_Invert_Flag); - netDelta = 0; - } - - wx = (f32)(((f64)((2 * pixel1) + 1)) / ((f64)(2 * nPixels))) - 0.5f; - wy = (f32)(((f64)((2 * pixel2) + 1)) / ((f64)(2 * nPixels))) - 0.5f; - Edit_Pixels.pixels.x = pixel1; - Edit_Pixels.pixels.y = pixel2; - Edit_Pixels.worldCoords.x = wx; - Edit_Pixels.worldCoords.y = wy; - - Global_Edit_Invert_Flag = 0; + // 余数表示8数的第几位,如果未反向则对应位为0,若反向则对应位为1 + if (IsContigInverted(contigPtr - 1)) // 判断上一个contig是否反向 + { // 位操作更新contigflag + // 每8个片段的正反采用一个u08表示,每一个bit表示一个正反,余数表示这八个中的第几个,如果没有反向则对应位为0 + if (!inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] &= ~(1 << ((contigPtr - 1) & 7)); + } + else + { // 如果反向则额对应位的bit为1 + if (inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] |= (1 << ((contigPtr - 1) & 7)); // 如果反向 } - redisplay = 1; + startCoord = coord; // 当前片段开始的坐标 + length = 0; // 当前片段长度清零0 + if (pixelIdx < (Number_of_Pixels_1D - 1)) inverted = Map_State->contigRelCoords[pixelIdx + 1] < coord; // 更新inverted } - else if ( - Tool_Tip->on || - Waypoint_Edit_Mode || - Scaff_Edit_Mode || - MetaData_Edit_Mode || - Select_Sort_Area_Mode) - { - s32 w, h; - glfwGetWindowSize(window, &w, &h); - f32 height = (f32)h; - f32 width = (f32)w; - - f32 factor1 = 1.0f / (2.0f * Camera_Position.z); - f32 factor2 = 2.0f / height; - f32 factor3 = width * 0.5f; - - f32 wx = (factor1 * factor2 * ((f32)x - factor3)) + Camera_Position.x; - f32 wy = (-factor1 * (1.0f - (factor2 * (f32)y))) - Camera_Position.y; + // 更新上一个id和局部坐标 + Map_State->contigIds[pixelIdx] = (u32)contigPtr; // 像素点对应的 片段id 修改为当前的统计得到片段id + lastId_original_contig = original_contig_id; // 更新上一个像素点的id + lastCoord = coord; // 更新上一个像素点的局部坐标 + } - wx = my_Max(-0.5f, my_Min(0.5f, wx)); - wy = my_Max(-0.5f, my_Min(0.5f, wy)); + if (contigPtr < Max_Number_of_Contigs) // contigptr 小于 Number_of_Original_Contigs + // 更新最后一个contig的最后一个片段信息 + { + (Original_Contigs + lastId_original_contig % Max_Number_of_Contigs)->contigMapPixels[(Original_Contigs + lastId_original_contig % Max_Number_of_Contigs)->nContigs++] = pixelIdx - 1 - (length >> 1); - u32 nPixels = Number_of_Textures_1D * Texture_Resolution; + ++length; + contig *cont = Contigs->contigs_arr + contigPtr++; + cont->originalContigId = lastId_original_contig; + cont->length = length; + cont->startCoord = startCoord; + cont->metaDataFlags = Map_State->metaDataFlags + pixelIdx - 1; + + u32 thisScaffID = Map_State->scaffIds[pixelIdx]; + cont->scaffId = thisScaffID ? ((thisScaffID == lastScaffID) ? (scaffId) : (++scaffId)) : 0; + + if (IsContigInverted(contigPtr - 1)) + { + if (!inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] &= ~(1 << ((contigPtr - 1) & 7)); + } + else + { + if (inverted) Contigs->contigInvertFlags[(contigPtr - 1) >> 3] |= (1 << ((contigPtr - 1) & 7)); + } + } - u32 pixel1 = my_Min((u32)((f64)nPixels * (0.5 + (f64)wx)), (nPixels - 1)); - u32 pixel2 = my_Min((u32)((f64)nPixels * (0.5 + (f64)wy)), (nPixels - 1)); + Contigs->numberOfContigs = contigPtr; +} - Tool_Tip_Move.pixels.x = pixel1; - Tool_Tip_Move.pixels.y = pixel2; - Tool_Tip_Move.worldCoords.x = wx; - Tool_Tip_Move.worldCoords.y = wy; +global_function +void +AddMapEdit(s32 delta, pointui finalPixels, u32 invert); - if (Waypoint_Edit_Mode) - { - Selected_Waypoint = 0; - f32 selectDis = Waypoint_Select_Distance / (height * Camera_Position.z); - f32 closestDistanceSq = selectDis * selectDis; +global_function +void +RebuildContig(u32 pixel) +{ + for (;;) + { + u32 contigId = Map_State->contigIds[pixel]; + u32 origContigId = Map_State->get_original_contig_id(pixel); - waypoint **searchBuffer = PushArray(Working_Set, waypoint*, Waypoints_Stack_Size); - waypoint **bufferEnd = searchBuffer; - GetWaypointsWithinSquare( // add waypoints within the square to the buffer - {Tool_Tip_Move.worldCoords.x - selectDis, Tool_Tip_Move.worldCoords.y - selectDis}, - 2.0f * selectDis, - &bufferEnd); - for ( waypoint **waypPtr = searchBuffer; - waypPtr != bufferEnd; - ++waypPtr ) { // make sure select the closest waypoint to the mouse - waypoint *wayp = *waypPtr; + u32 top = (u32)pixel; + while (top && (Map_State->contigIds[top - 1] == contigId)) --top; - f32 dx = Tool_Tip_Move.worldCoords.x - wayp->coords.x; - f32 dy = Tool_Tip_Move.worldCoords.y - wayp->coords.y; - f32 disSq = (dx * dx) + (dy * dy); + u32 bottom = pixel; + while ((bottom < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[bottom + 1] == contigId)) ++bottom; - if (disSq < closestDistanceSq) - { - closestDistanceSq = disSq; - Selected_Waypoint = wayp; - } - } + if (IsContigInverted(contigId)) + { + InvertMap(top, bottom); + AddMapEdit(0, {top, bottom}, 1); + continue; + } - FreeLastPush(Working_Set); // searchBuffer + u08 fragmented = 0; + ForLoop(Number_of_Pixels_1D) + { + if ((Map_State->contigIds[index] != contigId) && (Map_State->get_original_contig_id(index) == origContigId)) + { + fragmented = 1; + break; } - else if (Scaff_Edit_Mode && Scaff_Painting_Flag) + } + + if (fragmented) + { + u32 contigTopCoord = Map_State->contigRelCoords[top]; + if (contigTopCoord) { - if (Scaff_Painting_Flag == 1) + u32 otherPixel = 0; + ForLoop(Number_of_Pixels_1D) { - if (!Scaff_Painting_Id) + if ((Map_State->get_original_contig_id(index) == origContigId) && (Map_State->contigRelCoords[index] == (contigTopCoord - 1))) { - if (Map_State->scaffIds[Tool_Tip_Move.pixels.x]) - { - Scaff_Painting_Id = Map_State->scaffIds[Tool_Tip_Move.pixels.x]; - } - else - { - u32 max = 0; - ForLoop(Number_of_Pixels_1D) max = my_Max(max, Map_State->scaffIds[index]); - Scaff_Painting_Id = max + 1; - } + otherPixel = index; + break; } } - else Scaff_Painting_Id = 0; - if (Map_State->scaffIds[Tool_Tip_Move.pixels.x] != Scaff_Painting_Id || (Scaff_FF_Flag & 1)) - { - u32 pixel = Tool_Tip_Move.pixels.x; + u08 invert = !otherPixel || (Map_State->contigIds[otherPixel - 1] != Map_State->contigIds[otherPixel]); + u32 otherPixel2 = otherPixel; - u32 currScaffId = Map_State->scaffIds[pixel]; - u32 contigId = Map_State->contigIds[pixel]; - Map_State->scaffIds[pixel] = Scaff_Painting_Id; + if (invert) + { + while ((otherPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[otherPixel + 1] == Map_State->contigIds[otherPixel2])) ++otherPixel; + } + else + { + while (otherPixel2 && (Map_State->contigIds[otherPixel2 - 1] == Map_State->contigIds[otherPixel])) --otherPixel2; + } - // set the pixels with same contig_id into the same scaff - u32 testPixel = pixel; - while (testPixel && (Map_State->contigIds[testPixel - 1] == contigId)) Map_State->scaffIds[--testPixel] = Scaff_Painting_Id; - testPixel = pixel; - while ((testPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[testPixel + 1] == contigId)) Map_State->scaffIds[++testPixel] = Scaff_Painting_Id; + s32 delta = (s32)top - (s32)otherPixel; + if (delta > 0) --delta; + else delta = (s32)top - (s32)otherPixel2; + pointui finalPixels = {(u32)((s32)otherPixel2 + delta), (u32)((s32)otherPixel + delta)}; + RearrangeMap(otherPixel2, otherPixel, delta); + if (invert) InvertMap(finalPixels.x, finalPixels.y); + AddMapEdit(delta, finalPixels, invert); + } + else + { + u32 contigBottomCoord = Map_State->contigRelCoords[bottom]; - if (Scaff_FF_Flag & 1) + u32 otherPixel = 0; + ForLoop(Number_of_Pixels_1D) + { + if ((Map_State->get_original_contig_id(index) == origContigId) && (Map_State->contigRelCoords[index] == (contigBottomCoord + 1))) { - ++testPixel; - while ((testPixel < Number_of_Pixels_1D) && ((Scaff_FF_Flag & 2) || (Map_State->scaffIds[testPixel] == (Scaff_Painting_Flag == 1 ? 0 : currScaffId)))) Map_State->scaffIds[testPixel++] = Scaff_Painting_Id; + otherPixel = index; + break; } } - UpdateContigsFromMapState(); - } - else if (Select_Sort_Area_Mode) - { - auto_curation_state.update_sort_area(Tool_Tip_Move.pixels.x, Map_State, Number_of_Pixels_1D); - } - else if ( - MetaData_Edit_Mode && - MetaData_Edit_State && - strlen((const char *)Meta_Data->tags[MetaData_Active_Tag]) - ) - { - u32 pixel = Tool_Tip_Move.pixels.x; - u32 contigId = Map_State->contigIds[pixel]; + u08 invert = (otherPixel == (Number_of_Pixels_1D - 1)) || (Map_State->contigIds[otherPixel + 1] != Map_State->contigIds[otherPixel]); + u32 otherPixel2 = otherPixel; - if (MetaData_Edit_State == 1) - Map_State->metaDataFlags[pixel] |= (1 << MetaData_Active_Tag); // set the active tag - else - Map_State->metaDataFlags[pixel] &= ~(1 << MetaData_Active_Tag); // clear the active tag - - u32 testPixel = pixel; - while (testPixel && (Map_State->contigIds[testPixel - 1] == contigId)) + if (!invert) { - if (MetaData_Edit_State == 1) - Map_State->metaDataFlags[--testPixel] |= (1 << MetaData_Active_Tag); - else - Map_State->metaDataFlags[--testPixel] &= ~(1 << MetaData_Active_Tag); + while ((otherPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[otherPixel + 1] == Map_State->contigIds[otherPixel2])) ++otherPixel; } - - testPixel = pixel; - while ((testPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[testPixel + 1] == contigId)) + else { - if (MetaData_Edit_State == 1) - Map_State->metaDataFlags[++testPixel] |= (1 << MetaData_Active_Tag); - else - Map_State->metaDataFlags[++testPixel] &= ~(1 << MetaData_Active_Tag); + while (otherPixel2 && (Map_State->contigIds[otherPixel2 - 1] == Map_State->contigIds[otherPixel])) --otherPixel2; } - UpdateContigsFromMapState(); + s32 delta = (s32)bottom - (s32)otherPixel2; + if (delta < 0) ++delta; + else delta = (s32)bottom - (s32)otherPixel; + pointui finalPixels = {(u32)((s32)otherPixel2 + delta), (u32)((s32)otherPixel + delta)}; + RearrangeMap(otherPixel2, otherPixel, delta); + if (invert) InvertMap(finalPixels.x, finalPixels.y); + AddMapEdit(delta, finalPixels, invert); } - redisplay = 1; - } - - if (Mouse_Move.x >= 0.0) - { - s32 w, h; - glfwGetWindowSize(window, &w, &h); - f32 height = (f32)h; - - f32 factor = 1.0f / (height * Camera_Position.z); - f32 dx = (f32)(Mouse_Move.x - x) * factor; - f32 dy = (f32)(y - Mouse_Move.y) * factor; - - Camera_Position.x += dx; - Camera_Position.y += dy; - ClampCamera(); - - Mouse_Move.x = x; - Mouse_Move.y = y; - - redisplay = 1; + continue; } - } - - if (redisplay) - { - Redisplay = 1; + else break; } } +struct +map_edit +{ + u32 finalPix1; + u32 finalPix2; + s32 delta; -global_variable -u08 -Grey_Haplotigs = 1; - -global_variable -GreyOutSettings* Grey_Out_Settings = nullptr; + map_edit() {finalPix1 = 0; finalPix2 = 0; delta = 0;} + map_edit(u32 fp1, u32 fp2, s32 d) + { + finalPix1 = fp1; + finalPix2 = fp2; + delta = d; + } +}; -global_variable -UserProfileSettings* user_profile_settings_ptr = nullptr; +struct +waypoint; -global_variable -u08 -Deferred_Close_UI = 0; +struct +waypoint_quadtree_node +{ + waypoint *wayp; + waypoint_quadtree_node *next; + waypoint_quadtree_node *prev; +}; -global_function -void -Mouse(GLFWwindow* window, s32 button, s32 action, s32 mods) +struct +waypoint { - s32 primaryMouse = user_profile_settings_ptr->invert_mouse ? GLFW_MOUSE_BUTTON_RIGHT : GLFW_MOUSE_BUTTON_LEFT; - s32 secondaryMouse = user_profile_settings_ptr->invert_mouse ? GLFW_MOUSE_BUTTON_LEFT : GLFW_MOUSE_BUTTON_RIGHT; - - if (Loading || auto_sort_state) - { - return; - } - - (void)mods; - - f64 x, y; - glfwGetCursorPos(window, &x, &y); + point2f coords; + f32 z; + u32 index; + waypoint *prev; + waypoint *next; + waypoint_quadtree_node *node; + // u16 long_waypoint_mode; +}; - if (UI_On) - { - if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) - { - Deferred_Close_UI = 1; - } - } - else // UI not on - { - if (button == primaryMouse && Edit_Mode && action == GLFW_PRESS) - { - Edit_Pixels.editing = !Edit_Pixels.editing; - MouseMove(window, x, y); - if (!Edit_Pixels.editing) UpdateScaffolds(); - } - else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Edit_Mode && action == GLFW_RELEASE && !Edit_Pixels.editing) - { - Edit_Pixels.editing = 1; - Edit_Pixels.selecting = 0; - MouseMove(window, x, y); - } - else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Edit_Mode && action == GLFW_PRESS && !Edit_Pixels.editing) - { - Edit_Pixels.selecting = 1; - Edit_Pixels.selectPixels = Edit_Pixels.pixels; - MouseMove(window, x, y); - } - else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Edit_Mode && Edit_Pixels.editing && action == GLFW_PRESS) - { - InvertMap(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); - Global_Edit_Invert_Flag = !Global_Edit_Invert_Flag; - UpdateContigsFromMapState(); +#define Edits_Stack_Size 32768 +#define Waypoints_Stack_Size 128 - Redisplay = 1; - } - else if (button == primaryMouse && Waypoint_Edit_Mode && action == GLFW_PRESS) - { - AddWayPoint(Tool_Tip_Move.worldCoords); - MouseMove(window, x, y); - } - else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Waypoint_Edit_Mode && action == GLFW_PRESS) - { - if (Selected_Waypoint) - { - RemoveWayPoint(Selected_Waypoint); - MouseMove(window, x, y); - } - } - else if (button == primaryMouse && Scaff_Edit_Mode && action == GLFW_PRESS) - { - Scaff_Painting_Flag = 1; - MouseMove(window, x, y); - } - else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Scaff_Edit_Mode && action == GLFW_PRESS) - { - Scaff_Painting_Flag = 2; - MouseMove(window, x, y); - } - else if ((button == GLFW_MOUSE_BUTTON_MIDDLE || button == primaryMouse) && Scaff_Edit_Mode && action == GLFW_RELEASE) - { - Scaff_Painting_Flag = 0; - Scaff_Painting_Id = 0; - MouseMove(window, x, y); - UpdateScaffolds(); - } - else if (button == primaryMouse && Select_Sort_Area_Mode && action == GLFW_PRESS) - { - auto_curation_state.select_mode = 1; - MouseMove(window, x, y); - } - else if (button == primaryMouse && Select_Sort_Area_Mode && action == GLFW_RELEASE) - { - auto_curation_state.select_mode = 0; - MouseMove(window, x, y); - } - else if (button == primaryMouse && MetaData_Edit_Mode && action == GLFW_PRESS) - { - MetaData_Edit_State = 1; - MouseMove(window, x, y); - } - else if (button == GLFW_MOUSE_BUTTON_MIDDLE && MetaData_Edit_Mode && action == GLFW_PRESS) - { - MetaData_Edit_State = 2; - MouseMove(window, x, y); - } - else if ((button == GLFW_MOUSE_BUTTON_MIDDLE || button == primaryMouse) && MetaData_Edit_Mode && action == GLFW_RELEASE) - { - MetaData_Edit_State = 0; - MouseMove(window, x, y); - } - else if (button == secondaryMouse) - { - if (action == GLFW_PRESS) - { - Mouse_Move.x = x; - Mouse_Move.y = y; - } - else - { - Mouse_Move.x = Mouse_Move.y = -1.0; - } - } - else if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) - { - UI_On = !UI_On; - Mouse_Move.x = Mouse_Move.y = -1; - Redisplay = 1; - ++NK_Device->lastContextMemory[0]; - } - } -} +struct +map_editor +{ + map_edit *edits; + u32 nEdits; + u32 editStackPtr; + u32 nUndone; + u32 pad; +}; global_variable -struct nk_vec2 -NK_Scroll; +map_editor * +Map_Editor; -global_function -void -Scroll(GLFWwindow* window, f64 x, f64 y) +#define Waypoints_Quadtree_Levels 5 + +struct +waypoint_quadtree_level { - if (Loading || auto_sort_state) - { - return; - } - - if (UI_On) - { - NK_Scroll.y = (f32)y; - NK_Scroll.x = (f32)x; - } - else - { - if (y != 0.0) - { - ZoomCamera(my_Max(my_Min((f32)y, 0.01f), -0.01f)); - Redisplay = 1; - } +#ifdef Internal + u32 show; +#else + u32 pad; +#endif + f32 size; + point2f lowerBound; + waypoint_quadtree_level *children[4]; + waypoint_quadtree_node headNode; +}; - if (Edit_Mode || Tool_Tip->on || Waypoint_Edit_Mode) - { - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); - } - } -} +struct +waypoint_editor +{ + waypoint freeWaypoints; + waypoint activeWaypoints; + waypoint_quadtree_level *quadtree; + waypoint_quadtree_node freeNodes; + u32 nWaypointsActive; + u32 pad; +}; global_variable -u64 -Total_Genome_Length; +waypoint_editor * +Waypoint_Editor; -global_function -void -Setup(); +global_variable u16 Long_Waypoints_Mode = 0; // mode 0: vertical, mode 1: horizontal, mode 2: both + +#define Waypoint_Select_Distance 8.0f +global_variable +waypoint * +Selected_Waypoint = 0; global_function -u32 -SubDivideScaleBar(f32 left, f32 right, f32 middle, f32 bpPerPixel, f32 offset) +waypoint_quadtree_level * +PushQuadTree(memory_arena *arena, u32 level = 0, point2f lowerBound = {-0.5f, -0.5f}, f32 size = 1.0f) { - u32 result = 0; + waypoint_quadtree_level *quadtreeLevel = 0; - if (left < right) + if (level < Waypoints_Quadtree_Levels) { - f32 length = right - left; - f32 half = length * 0.5f; - char buff[16]; - stbsp_snprintf(buff, 16, "%$.2f", (f64)(offset + (half * bpPerPixel))); - f32 width = fonsTextBounds(FontStash_Context, middle, 0.0, buff, 0, NULL); - f32 halfWidth = 0.5f * width; + quadtreeLevel = PushStructP(arena, waypoint_quadtree_level); + quadtreeLevel->size = size; + quadtreeLevel->lowerBound = lowerBound; + quadtreeLevel->headNode = {}; - if ((middle + halfWidth) < right && (middle - halfWidth) > left) - { - u32 leftResult = SubDivideScaleBar(left, middle - halfWidth, (left + middle) * 0.5f, bpPerPixel, offset); - u32 rightResult = SubDivideScaleBar(middle + halfWidth, right, (right + middle) * 0.5f, bpPerPixel, offset); - result = 1 + my_Min(leftResult, rightResult); - } +#ifdef Internal + quadtreeLevel->show = 0; +#endif + + f32 halfSize = size * 0.5f; + quadtreeLevel->children[0] = PushQuadTree(arena, level + 1, {lowerBound.x, lowerBound.y}, halfSize); + quadtreeLevel->children[1] = PushQuadTree(arena, level + 1, {lowerBound.x, lowerBound.y + halfSize}, halfSize); + quadtreeLevel->children[2] = PushQuadTree(arena, level + 1, {lowerBound.x + halfSize, lowerBound.y}, halfSize); + quadtreeLevel->children[3] = PushQuadTree(arena, level + 1, {lowerBound.x + halfSize, lowerBound.y + halfSize}, halfSize); } - return(result); + return(quadtreeLevel); } -global_variable -u32 -File_Loaded = 0; - -global_variable -nk_color -Theme_Colour; - -#ifdef Internal global_function void -DrawQuadTreeLevel (u32 *ptr, waypoint_quadtree_level *level, vertex *vert, f32 lineWidth, u32 n = 0) -{ - f32 colour[5][4] = {{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 1.0f, 1.0f}}; - f32 *col = (f32 *)colour[n%5]; - - glUniform4fv(Flat_Shader->colorLocation, 1, col); - - if (level->show) - { - vert[0].x = level->lowerBound.x; - vert[0].y = -level->lowerBound.y; - vert[1].x = level->lowerBound.x; - vert[1].y = -level->lowerBound.y - level->size; - vert[2].x = level->lowerBound.x + lineWidth; - vert[2].y = -level->lowerBound.y - level->size; - vert[3].x = level->lowerBound.x + lineWidth; - vert[3].y = -level->lowerBound.y; - - glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - - vert[0].x = level->lowerBound.x; - vert[0].y = -level->lowerBound.y - level->size; - vert[1].x = level->lowerBound.x + level->size; - vert[1].y = -level->lowerBound.y - level->size; - vert[2].x = level->lowerBound.x + level->size; - vert[2].y = -level->lowerBound.y - level->size + lineWidth; - vert[3].x = level->lowerBound.x; - vert[3].y = -level->lowerBound.y - level->size + lineWidth; - - glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); +#ifdef Internal +GetWaypointsWithinRectange(point2f lowerBound, point2f size, waypoint ***bufferPtr, u32 reset = 1); +#else +GetWaypointsWithinRectange(point2f lowerBound, point2f size, waypoint ***bufferPtr); +#endif - vert[0].x = level->lowerBound.x + level->size - lineWidth; - vert[0].y = -level->lowerBound.y; - vert[1].x = level->lowerBound.x + level->size - lineWidth; - vert[1].y = -level->lowerBound.y - level->size; - vert[2].x = level->lowerBound.x + level->size; - vert[2].y = -level->lowerBound.y - level->size; - vert[3].x = level->lowerBound.x + level->size; - vert[3].y = -level->lowerBound.y; +global_function +void +UpdateWayPoint(waypoint *wayp, point2f coords); - glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); +global_function +void +MoveWayPoints(map_edit *edit, u32 undo = 0); - vert[0].x = level->lowerBound.x; - vert[0].y = -level->lowerBound.y; - vert[1].x = level->lowerBound.x; - vert[1].y = -level->lowerBound.y - lineWidth; - vert[2].x = level->lowerBound.x + level->size; - vert[2].y = -level->lowerBound.y - lineWidth; - vert[3].x = level->lowerBound.x + level->size; - vert[3].y = -level->lowerBound.y; +global_function +void +AddMapEdit(s32 delta, pointui finalPixels, u32 invert) +{ + ++Map_Editor->nEdits; + Map_Editor->nUndone = 0; - glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } + map_edit *edit = Map_Editor->edits + Map_Editor->editStackPtr++; - if (level->children[0]) + if (Map_Editor->editStackPtr == Edits_Stack_Size) { - ForLoop(4) - { - DrawQuadTreeLevel(ptr, level->children[index], vert, lineWidth, n+1); - } + Map_Editor->editStackPtr = 0; } -} -#endif -global_function -u32 -FourFloatColorToU32(nk_colorf colour) -{ - return(glfonsRGBA((u08)(colour.r * 255.0f), (u08)(colour.g * 255.0f), - (u08)(colour.b * 255.0f), (u08)(colour.a * 255.0f))); + u32 pix1 = (u32)(invert ? my_Max(finalPixels.x, finalPixels.y) : my_Min(finalPixels.x, finalPixels.y)); + u32 pix2 = (u32)(invert ? my_Min(finalPixels.x, finalPixels.y) : my_Max(finalPixels.x, finalPixels.y)); + + edit->delta = (s32)delta; + edit->finalPix1 = pix1; + edit->finalPix2 = pix2; } global_function -u32 -ThreeFloatColorToU32(nk_colorf colour) +void +UpdateScaffolds() { - return(glfonsRGBA((u08)(colour.r * 255.0f), (u08)(colour.g * 255.0f), - (u08)(colour.b * 255.0f), 255)); + ForLoop(Number_of_Pixels_1D) Map_State->scaffIds[index] = (Contigs->contigs_arr + Map_State->contigIds[index])->scaffId; } global_function void -ColourGenerator(u32 index, f32 *rgb) +UndoMapEdit() { -// #define RedFreq 1.666f -// #define GreenFreq 2.666f -// #define BlueFreq 3.666f + if (Map_Editor->nEdits && !Edit_Pixels.editing) + { + --Map_Editor->nEdits; + ++Map_Editor->nUndone; -// rgb[0] = 0.5f * (sinf((f32)index * RedFreq) + 1.0f); -// rgb[1] = 0.5f * (sinf((f32)index * GreenFreq) + 1.0f); -// rgb[2] = 0.5f * (sinf((f32)index * BlueFreq) + 1.0f); + if (!Map_Editor->editStackPtr) + { + Map_Editor->editStackPtr = Edits_Stack_Size + 1; + } - f32 RedFreq = meta_dataColors[meta_data_curcolorProfile][0]; - f32 GreenFreq = meta_dataColors[meta_data_curcolorProfile][1]; - f32 BlueFreq = meta_dataColors[meta_data_curcolorProfile][2]; + map_edit *edit = Map_Editor->edits + (--Map_Editor->editStackPtr); - rgb[0] = 0.5f * (sinf((f32)index * RedFreq) + 1.0f); - rgb[1] = 0.5f * (sinf((f32)index * GreenFreq) + 1.0f); - rgb[2] = 0.5f * (sinf((f32)index * BlueFreq) + 1.0f); -} + if (edit->finalPix1 > edit->finalPix2) + { + InvertMap((u32)edit->finalPix1, (u32)edit->finalPix2); + } + u32 start = my_Min(edit->finalPix1, edit->finalPix2); + u32 end = my_Max(edit->finalPix1, edit->finalPix2); -void DrawOutlinedText( - FONScontext *FontStash_Context, - nk_colorf *colour, - const char *text, - f32 x, - f32 y, - f32 offset = 1.0f, - bool outline_on = false) -{ + RearrangeMap(start, end, -edit->delta); - nk_colorf outlineColor; - if (meta_outline->on == 1) - { - outlineColor = {0.0f, 0.0f, 0.0f, 1.0f}; // black - } - else - { - outlineColor = {1.0f, 1.0f, 1.0f, 1.0f}; // white + UpdateScaffolds(); } +} - if (outline_on) - outlineColor = {0.0f, 0.0f, 0.0f, 1.0f}; // black - - unsigned int outlineColorU32 = FourFloatColorToU32(outlineColor); - unsigned int textColorU32 = FourFloatColorToU32(*colour); - - if (meta_outline->on > 0 || outline_on) +global_function +void +RedoMapEdit() +{ + if (Map_Editor->nUndone && !Edit_Pixels.editing) { - fonsSetColor(FontStash_Context, outlineColorU32); - fonsDrawText(FontStash_Context, x - offset, y - offset, text, 0); - fonsDrawText(FontStash_Context, x + offset, y - offset, text, 0); - fonsDrawText(FontStash_Context, x - offset, y + offset, text, 0); - fonsDrawText(FontStash_Context, x + offset, y + offset, text, 0); - } - - // // Draw the original text on top - fonsSetColor(FontStash_Context, textColorU32); - fonsDrawText(FontStash_Context, x, y, text, 0); -} + ++Map_Editor->nEdits; + --Map_Editor->nUndone; + map_edit *edit = Map_Editor->edits + Map_Editor->editStackPtr++; -global_variable -char -Extension_Magic_Bytes[][4] = -{ - {'p', 's', 'g', 'h'} -}; + if (Map_Editor->editStackPtr == Edits_Stack_Size) + { + Map_Editor->editStackPtr = 0; + } + u32 start = my_Min(edit->finalPix1, edit->finalPix2); + u32 end = my_Max(edit->finalPix1, edit->finalPix2); + RearrangeMap((u32)((s32)start - edit->delta), (u32)((s32)end - edit->delta), edit->delta); + + if (edit->finalPix1 > edit->finalPix2) + { + InvertMap((u32)edit->finalPix1, (u32)edit->finalPix2); + } + + UpdateScaffolds(); + } +} -global_variable -extension_sentinel -Extensions = {}; global_function void -AddExtension(extension_node *node) +MoveWayPoints(map_edit *edit, u32 undo) { - node->next = 0; - - if (!Extensions.head) + pointui tmp; + s32 delta; + pointui finalPixels; + if (undo) { - Extensions.head = node; - Extensions.tail = node; + tmp = {(u32)edit->finalPix1, (u32)edit->finalPix2}; + delta = -(s32)edit->delta; + finalPixels = {(u32)((s32)tmp.x + delta), (u32)((s32)tmp.y + delta)}; } else { - Extensions.tail->next = node; // append the last to the end - Extensions.tail = node; // set the last to the new node just appended + tmp = {(u32)((s32)edit->finalPix1 - (s32)edit->delta), (u32)((s32)edit->finalPix2 - (s32)edit->delta)}; + delta = (s32)edit->delta; + finalPixels = {(u32)edit->finalPix1, (u32)edit->finalPix2}; } -} -global_function -void -Render() { - // Projection Matrix - f32 width; - f32 height; + pointui startPixels = {my_Min(tmp.x, tmp.y), my_Max(tmp.x, tmp.y)}; + u32 lengthMove = startPixels.y - startPixels.x + 1; + pointui editRange; + + if (delta > 0) { - glClear(GL_COLOR_BUFFER_BIT); - // glClearColor(0.2f, 0.6f, 0.4f, 1.0f); // classic background color - glClearColor(bgcolor[active_bgcolor][0], bgcolor[active_bgcolor][1], bgcolor[active_bgcolor][2], bgcolor[active_bgcolor][3]); + editRange.x = startPixels.x; + editRange.y = my_Max(finalPixels.x, finalPixels.y); + } + else + { + editRange.x = my_Min(finalPixels.x, finalPixels.y); + editRange.y = startPixels.y; + } - s32 viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); - width = (f32)viewport[2]; - height = (f32)viewport[3]; + f32 ooNPixels = (f32)(1.0 / (f64)Number_of_Pixels_1D); + point2f editRangeModel = {((f32)editRange.x * ooNPixels) - 0.5f, ((f32)editRange.y * ooNPixels) - 0.5f}; + f32 dRange = editRangeModel.y - editRangeModel.x; - f32 mat[16]; - memset(mat, 0, sizeof(mat)); - mat[0] = 2.0f * Camera_Position.z * height / width; - mat[5] = 2.0f * Camera_Position.z; - mat[10] = -2.0f; - mat[12] = -2.0f * height * Camera_Position.x * Camera_Position.z / width; - mat[13] = -2.0f * Camera_Position.y * Camera_Position.z; - mat[15] = 1.0f; + waypoint **searchBuffer = PushArray(Working_Set, waypoint*, Waypoints_Stack_Size); + waypoint **bufferEnd = searchBuffer; - /* - template - GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH_NO(T left, T right, T bottom, T top, T zNear, T zFar) - { - mat<4, 4, T, defaultp> Result(1); - Result[0][0] = static_cast(2) / (right - left); // 0 - Result[1][1] = static_cast(2) / (top - bottom); // 5 - Result[2][2] = - static_cast(2) / (zFar - zNear);// 10 - Result[3][0] = - (right + left) / (right - left); // 12 - Result[3][1] = - (top + bottom) / (top - bottom); // 13 - Result[3][2] = - (zFar + zNear) / (zFar - zNear); // 14 - return Result; - } - glm::mat4 projection_mat = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); - */ + GetWaypointsWithinRectange({editRangeModel.x, -0.5f}, {dRange, 0.999f}, &bufferEnd); + GetWaypointsWithinRectange({-0.5f, editRangeModel.x}, {0.999f, dRange}, &bufferEnd +#ifdef Internal +, 0 +#endif + ); - // apply the transformation - glUseProgram(Contact_Matrix->shaderProgram); - glUniformMatrix4fv(Contact_Matrix->matLocation, 1, GL_FALSE, mat); - glUseProgram(Flat_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, mat); + u32 nWayp = (u32)((my_Max((size_t)bufferEnd, (size_t)searchBuffer) - my_Min((size_t)bufferEnd, (size_t)searchBuffer)) / sizeof(searchBuffer)); + waypoint **seen = PushArray(Working_Set, waypoint*, nWayp); + u32 seenIdx = 0; - TraverseLinkedList(Extensions.head, extension_node) + for ( waypoint **waypPtr = searchBuffer; + waypPtr != bufferEnd; + ++waypPtr ) + { + waypoint *wayp = *waypPtr; + u32 newWayP = 1; + + ForLoop(seenIdx) { - switch (node->type) + if (wayp == seen[index]) { - case extension_graph: - { - graph *gph = (graph *)node->extension; - glUseProgram(gph->shader->shaderProgram); - glUniformMatrix4fv(gph->shader->matLocation, 1, GL_FALSE, mat); - } - break; + newWayP = 0; + break; } } - } - // Textures - if (File_Loaded) - { - glUseProgram(Contact_Matrix->shaderProgram); - glBindTexture(GL_TEXTURE_2D_ARRAY, Contact_Matrix->textures); - u32 ptr = 0; - while (ptr < Number_of_Textures_1D * Number_of_Textures_1D) + if (newWayP) { - glBindVertexArray(Contact_Matrix->vaos[ptr++]); // bind the vertices and then draw the quad - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } - } - - #ifdef Internal - if (File_Loaded && Tiles->on) - { - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Tiles->bg); - - u32 ptr = 0; - vertex vert[4]; - - f32 lineWidth = 0.001f / sqrtf(Camera_Position.z); - f32 position = 0.0f; - f32 spacing = 1.0f / (f32)Number_of_Textures_1D; - - vert[0].x = -0.5f; - vert[0].y = -0.5f; - vert[1].x = lineWidth - 0.5f; - vert[1].y = -0.5f; - vert[2].x = lineWidth - 0.5f; - vert[2].y = 0.5f; - vert[3].x = -0.5f; - vert[3].y = 0.5f; - - glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + point2f normCoords = {my_Min(wayp->coords.x, wayp->coords.y), my_Max(wayp->coords.x, wayp->coords.y)}; - f32 x = -0.5f; - ForLoop(Number_of_Textures_1D - 1) - { - position += spacing; - f32 px = x + lineWidth; - x = position - (0.5f * (lineWidth + 1.0f)); + u32 inXRange = normCoords.x >= editRangeModel.x && normCoords.x < editRangeModel.y; + u32 inYRange = normCoords.y >= editRangeModel.x && normCoords.y < editRangeModel.y; + u32 inRange = inXRange || inYRange; - if (x > px) + if (inRange) { - vert[0].x = x; - vert[0].y = -0.5f; - vert[1].x = x + lineWidth; - vert[1].y = -0.5f; - vert[2].x = x + lineWidth; - vert[2].y = 0.5f; - vert[3].x = x; - vert[3].y = 0.5f; - - glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } - } - - vert[0].x = 0.5f - lineWidth; - vert[0].y = -0.5f; - vert[1].x = 0.5f; - vert[1].y = -0.5f; - vert[2].x = 0.5f; - vert[2].y = 0.5f; - vert[3].x = 0.5f - lineWidth; - vert[3].y = 0.5f; + u32 upperTri = wayp->coords.x > wayp->coords.y; + + pointui pix = {(u32)((0.5f + wayp->coords.x) / ooNPixels), (u32)((0.5f + wayp->coords.y) / ooNPixels)}; - glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + if (pix.x >= startPixels.x && pix.x < startPixels.y) + { + pix.x = (u32)((s32)pix.x + delta); - position = 0.0f; + if (edit->finalPix1 > edit->finalPix2) + { + pix.x = my_Max(finalPixels.x, finalPixels.y) - pix.x + my_Min(finalPixels.x, finalPixels.y); + } + } + else if (pix.x >= editRange.x && pix.x < editRange.y) + { + pix.x = delta > 0 ? pix.x - lengthMove : pix.x + lengthMove; + } - vert[0].x = -0.5f; - vert[0].y = 0.5f - lineWidth; - vert[1].x = 0.5f; - vert[1].y = 0.5f - lineWidth; - vert[2].x = 0.5f; - vert[2].y = 0.5f; - vert[3].x = -0.5f; - vert[3].y = 0.5f; + if (pix.y >= startPixels.x && pix.y < startPixels.y) + { + pix.y = (u32)((s32)pix.y + delta); - glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + if (edit->finalPix1 > edit->finalPix2) + { + pix.y = my_Max(finalPixels.x, finalPixels.y) - pix.y + my_Min(finalPixels.x, finalPixels.y); + } + } + else if (pix.y >= editRange.x && pix.y < editRange.y) + { + pix.y = delta > 0 ? pix.y - lengthMove : pix.y + lengthMove; + } - f32 y = 0.5f; - ForLoop(Number_of_Textures_1D - 1) - { - position += spacing; - f32 py = y - lineWidth; - y = 1.0f - position + (0.5f * (lineWidth - 1.0f)); + point2f newCoords = {((f32)pix.x * ooNPixels) - 0.5f, ((f32)pix.y * ooNPixels) - 0.5f}; - if (y < py) - { - vert[0].x = -0.5f; - vert[0].y = y - lineWidth; - vert[1].x = 0.5f; - vert[1].y = y - lineWidth; - vert[2].x = 0.5f; - vert[2].y = y; - vert[3].x = -0.5f; - vert[3].y = y; + u32 upperTriNew = newCoords.x > newCoords.y; + if (upperTriNew != upperTri) + { + newCoords = {newCoords.y, newCoords.x}; + } - glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + UpdateWayPoint(wayp, newCoords); } - } - - vert[0].x = -0.5f; - vert[0].y = -0.5f; - vert[1].x = 0.5f; - vert[1].y = -0.5f; - vert[2].x = 0.5f; - vert[2].y = lineWidth - 0.5f; - vert[3].x = -0.5f; - vert[3].y = lineWidth - 0.5f; - glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + seen[seenIdx++] = wayp; + } } - // Quad Trees - //if (Waypoint_Edit_Mode) - { - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&QuadTrees->bg); - - u32 ptr = 0; - vertex vert[4]; - - f32 lineWidth = 0.001f / sqrtf(Camera_Position.z); + FreeLastPush(Working_Set); // searchBuffer + FreeLastPush(Working_Set); // seen +} - DrawQuadTreeLevel(&ptr, Waypoint_Editor->quadtree, vert, lineWidth); - } - #endif - - // Extensions - u08 graphNamesOn = 0; +global_function +void +GetWaypointsWithinRectange(waypoint_quadtree_level *level, point2f lowerBound, point2f size, waypoint ***bufferPtr) +{ + if (level->children[0]) { - TraverseLinkedList(Extensions.head, extension_node) + u08 toSearch[4] = {1, 1, 1, 1}; +#define epsilon 0.001f + ForLoop(4) { - switch (node->type) + // bottom left + waypoint_quadtree_level *child = level->children[index]; + point2f insertSize = {lowerBound.x - child->lowerBound.x, lowerBound.y - child->lowerBound.y}; + if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) { - case extension_graph: - { - graph *gph = (graph *)node->extension; - - if (gph->on) - { - if (gph->nameOn) graphNamesOn = 1; - - f32 factor1 = 1.0f / (2.0f * Camera_Position.z); - f32 factor2 = 2.0f / height; - - f32 wy = (factor1 * (1.0f - (factor2 * (height - gph->base)))) + Camera_Position.y; - - glUseProgram(gph->shader->shaderProgram); - glUniform4fv(gph->shader->colorLocation, 1, (GLfloat *)&gph->colour); - glUniform1f(gph->shader->yScaleLocation, gph->scale); - glUniform1f(gph->shader->yTopLocation, wy); - glUniform1f(gph->shader->lineSizeLocation, gph->lineSize / Camera_Position.z); - - glBindBuffer(GL_ARRAY_BUFFER, gph->vbo); - glBindVertexArray(gph->vao); - glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)Number_of_Pixels_1D); - } - } + point2f recSize = {my_Min(size.x, child->size - insertSize.x - epsilon), my_Min(size.y, child->size - insertSize.y - epsilon)}; + GetWaypointsWithinRectange(child, lowerBound, recSize, bufferPtr); + toSearch[index] = 0; + break; + } + } + ForLoop(4) + { + if (toSearch[index]) + { + // bottom right + waypoint_quadtree_level *child = level->children[index]; + point2f insertSize = {lowerBound.x + size.x - child->lowerBound.x, lowerBound.y - child->lowerBound.y}; + if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) + { + point2f recSize = {my_Min(size.x, insertSize.x), my_Min(size.y, child->size - insertSize.y - epsilon)}; + GetWaypointsWithinRectange(child, {child->lowerBound.x + insertSize.x - recSize.x, lowerBound.y}, recSize, bufferPtr); + toSearch[index] = 0; + break; + } + } + } + ForLoop(4) + { + if (toSearch[index]) + { + // top left + waypoint_quadtree_level *child = level->children[index]; + point2f insertSize = {lowerBound.x - child->lowerBound.x, lowerBound.y + size.y - child->lowerBound.y}; + if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) + { + point2f recSize = {my_Min(size.x, child->size - insertSize.x - epsilon), my_Min(size.y, insertSize.y)}; + GetWaypointsWithinRectange(child, {lowerBound.x, child->lowerBound.y + insertSize.y - recSize.y}, recSize, bufferPtr); + toSearch[index] = 0; + break; + } + } + } + ForLoop(4) + { + if (toSearch[index]) + { + // top right + waypoint_quadtree_level *child = level->children[index]; + point2f insertSize = {lowerBound.x + size.x - child->lowerBound.x, lowerBound.y + size.y - child->lowerBound.y}; + if (insertSize.x >= 0 && insertSize.x < child->size && insertSize.y >= 0 && insertSize.y < child->size) + { + point2f recSize = {my_Min(size.x, insertSize.x), my_Min(size.y, insertSize.y)}; + GetWaypointsWithinRectange(child, {child->lowerBound.x + insertSize.x - recSize.x, child->lowerBound.y + insertSize.y - recSize.y}, recSize, bufferPtr); +#ifdef Internal + toSearch[index] = 0; +#endif break; + } } } + +#ifdef Internal + ForLoop(4) + { + if (!toSearch[index]) level->children[index]->show = 1; + } +#endif + + } + else + { + TraverseLinkedList(level->headNode.next, waypoint_quadtree_node) + { + **bufferPtr = node->wayp; + ++(*bufferPtr); + } } +} - // Grid - if (File_Loaded && Grid->on) +#ifdef Internal +global_function +void +TurnOffDrawingForQuadTreeLevel(waypoint_quadtree_level *level = 0) +{ + if (!level) level = Waypoint_Editor->quadtree; + level->show = 0; + ForLoop(4) { - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Grid->bg); + if (level->children[index]) TurnOffDrawingForQuadTreeLevel(level->children[index]); + } +} +#endif - u32 ptr = 0; - vertex vert[4]; +global_function +void +GetWaypointsWithinSquare(point2f lowerBound, f32 size, waypoint ***bufferPtr +#ifdef Internal + , u32 reset = 1) +#else + ) +#endif +{ +#ifdef Internal + if (reset) TurnOffDrawingForQuadTreeLevel(); +#endif + GetWaypointsWithinRectange(Waypoint_Editor->quadtree, lowerBound, {size, size}, bufferPtr); +} - f32 lineWidth = Grid->size / sqrtf(Camera_Position.z); - f32 position = 0.0f; +global_function +void +GetWaypointsWithinRectange(point2f lowerBound, point2f size, waypoint ***bufferPtr +#ifdef Internal + , u32 reset) +#else + ) +#endif +{ +#ifdef Internal + if (reset) TurnOffDrawingForQuadTreeLevel(); +#endif + GetWaypointsWithinRectange(Waypoint_Editor->quadtree, lowerBound, size, bufferPtr); +} - // left border - vert[0].x = -0.5f; vert[0].y = -0.5f; - vert[1].x = lineWidth - 0.5f; vert[1].y = -0.5f; - vert[2].x = lineWidth - 0.5f; vert[2].y = 0.5f; - vert[3].x = -0.5f; vert[3].y = 0.5f; +global_function +waypoint_quadtree_level * +GetWaypointQuadTreeLevel(point2f coords) +{ + coords = {my_Max(my_Min(coords.x, 0.499f), -0.5f), my_Max(my_Min(coords.y, 0.499f), -0.5f)}; + + waypoint_quadtree_level *level = Waypoint_Editor->quadtree; - glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Grid_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + while (level->children[0]) + { +#ifdef DEBUG + u32 found = 0; +#endif + ForLoop(4) + { + point2f insertSize = {coords.x - level->children[index]->lowerBound.x, coords.y - level->children[index]->lowerBound.y}; + f32 size = level->children[index]->size; + if (insertSize.x >= 0 && insertSize.x < size && insertSize.y >= 0 && insertSize.y < size) + { + level = level->children[index]; + +#ifdef DEBUG + found = 1; +#endif + + break; + } + } - f32 x = -0.5f; - ForLoop(Contigs->numberOfContigs - 1) +#ifdef DEBUG + if (!found) { - contig *cont = Contigs->contigs_arr + index; + Assert(found); + } +#endif - position += ((f32)cont->length / (f32)Number_of_Pixels_1D); - f32 px = x + lineWidth; - x = position - (0.5f * (lineWidth + 1.0f)); + } - if (x > px) - { - // contig vertical line - vert[0].x = x; vert[0].y = -0.5f; - vert[1].x = x + lineWidth; vert[1].y = -0.5f; - vert[2].x = x + lineWidth; vert[2].y = 0.5f; - vert[3].x = x; vert[3].y = 0.5f; + return(level); +} - glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Grid_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } - } - // right border - vert[0].x = 0.5f - lineWidth; vert[0].y = -0.5f; - vert[1].x = 0.5f; vert[1].y = -0.5f; - vert[2].x = 0.5f; vert[2].y = 0.5f; - vert[3].x = 0.5f - lineWidth; vert[3].y = 0.5f; +global_function +void +AddWayPoint(point2f coords) +{ + u32 nFree = Waypoints_Stack_Size - Waypoint_Editor->nWaypointsActive; - glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Grid_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + if (nFree) + { + waypoint *wayp = Waypoint_Editor->freeWaypoints.next; + Waypoint_Editor->freeWaypoints.next = wayp->next; + if (Waypoint_Editor->freeWaypoints.next) Waypoint_Editor->freeWaypoints.next->prev = &Waypoint_Editor->freeWaypoints; - position = 0.0f; - // top border - vert[0].x = -0.5f; vert[0].y = 0.5f - lineWidth; - vert[1].x = 0.5f; vert[1].y = 0.5f - lineWidth; - vert[2].x = 0.5f; vert[2].y = 0.5f; - vert[3].x = -0.5f; vert[3].y = 0.5f; + wayp->next = Waypoint_Editor->activeWaypoints.next; + if (Waypoint_Editor->activeWaypoints.next) Waypoint_Editor->activeWaypoints.next->prev = wayp; + Waypoint_Editor->activeWaypoints.next = wayp; + wayp->prev = &Waypoint_Editor->activeWaypoints; + wayp->index = wayp->next ? wayp->next->index + 1 : 0; - glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Grid_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + wayp->coords = coords; + wayp->z = Camera_Position.z; + ++Waypoint_Editor->nWaypointsActive; - f32 y = 0.5f; - ForLoop(Contigs->numberOfContigs - 1) - { - contig *cont = Contigs->contigs_arr + index; + waypoint_quadtree_level *level = GetWaypointQuadTreeLevel(wayp->coords); + + waypoint_quadtree_node *node = Waypoint_Editor->freeNodes.next; + if (node->next) node->next->prev = &Waypoint_Editor->freeNodes; + Waypoint_Editor->freeNodes.next = node->next; - position += ((f32)cont->length / (f32)Number_of_Pixels_1D); - f32 py = y - lineWidth; - y = 1.0f - position + (0.5f * (lineWidth - 1.0f)); + if (level->headNode.next) level->headNode.next->prev = node; + node->next = level->headNode.next; + node->prev = &level->headNode; + level->headNode.next = node; - if (y < py) - { - // contig horizontal line - vert[0].x = -0.5f; vert[0].y = y - lineWidth; - vert[1].x = 0.5f; vert[1].y = y - lineWidth; - vert[2].x = 0.5f; vert[2].y = y; - vert[3].x = -0.5f; vert[3].y = y; + wayp->node = node; + node->wayp = wayp; + } +} - glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Grid_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } - } - // bottom border - vert[0].x = -0.5f; vert[0].y = -0.5f; - vert[1].x = 0.5f; vert[1].y = -0.5f; - vert[2].x = 0.5f; vert[2].y = lineWidth - 0.5f; - vert[3].x = -0.5f; vert[3].y = lineWidth - 0.5f; +global_function +void +RemoveWayPoint(waypoint *wayp) +{ + switch (Waypoint_Editor->nWaypointsActive) + { + case 0: + wayp = 0; + break; - glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Grid_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + case 1: + Waypoint_Editor->activeWaypoints.next = 0; + Waypoint_Editor->nWaypointsActive = 0; + break; + + default: + if (wayp->next) wayp->next->prev = wayp->prev; + if (wayp->prev) wayp->prev->next = wayp->next; + --Waypoint_Editor->nWaypointsActive; } - // Contig Id Bars - if (File_Loaded && Contig_Ids->on) + if (wayp) { - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Grid->bg); + wayp->next = Waypoint_Editor->freeWaypoints.next; + if (Waypoint_Editor->freeWaypoints.next) Waypoint_Editor->freeWaypoints.next->prev = wayp; + Waypoint_Editor->freeWaypoints.next = wayp; + wayp->prev = &Waypoint_Editor->freeWaypoints; - u32 ptr = 0; - vertex vert[4]; + waypoint_quadtree_node *node = wayp->node; - f32 barColour[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + if (node->next) node->next->prev = node->prev; + if (node->prev) node->prev->next = node->next; + node->prev = 0; - f32 lineWidth = Contig_Ids->size / sqrtf(Camera_Position.z); - f32 position = 0.0f; + if (Waypoint_Editor->freeNodes.next) Waypoint_Editor->freeNodes.next->prev = node; + node->next = Waypoint_Editor->freeNodes.next; + Waypoint_Editor->freeNodes.next = node; + node->prev = &Waypoint_Editor->freeNodes; + } +} - f32 y = 0.5f; - ForLoop(Contigs->numberOfContigs) - { - contig *cont = Contigs->contigs_arr + index; +global_function +void +UpdateWayPoint(waypoint *wayp, point2f coords) +{ + waypoint_quadtree_node *node = wayp->node; - position += ((f32)cont->length / (f32)Number_of_Pixels_1D); - f32 py = y - lineWidth; - y = 1.0f - position + (0.5f * (lineWidth - 1.0f)); + if (node->next) node->next->prev = node->prev; + if (node->prev) node->prev->next = node->next; + node->prev = 0; - if (y < py) - { - u32 invert = IsContigInverted(index); + wayp->coords = coords; - vert[0].x = -py; vert[0].y = invert ? y : (py + lineWidth); - vert[1].x = -py; vert[1].y = invert ? (y - lineWidth) : py; - vert[2].x = -y; vert[2].y = invert ? (y - lineWidth) : py; - vert[3].x = -y; vert[3].y = invert ? y : (py + lineWidth); + waypoint_quadtree_level *level = GetWaypointQuadTreeLevel(wayp->coords); - ColourGenerator((u32)cont->originalContigId, (f32 *)barColour); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); + if (level->headNode.next) level->headNode.next->prev = node; + node->next = level->headNode.next; + node->prev = &level->headNode; + level->headNode.next = node; - glBindBuffer(GL_ARRAY_BUFFER, Contig_ColourBar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Contig_ColourBar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } - } - } + wayp->node = node; + node->wayp = wayp; +} - // Text / UI Rendering - if (Contig_Name_Labels->on || Scale_Bars->on || Tool_Tip->on || UI_On || Loading || auto_sort_state || Edit_Mode || Waypoint_Edit_Mode || Waypoints_Always_Visible || Scaff_Edit_Mode || Scaffs_Always_Visible || MetaData_Edit_Mode || MetaData_Always_Visible || graphNamesOn) +global_function +void +MouseMove(GLFWwindow* window, f64 x, f64 y) +{ + if (Loading || auto_sort_state || auto_cut_state) { - f32 textNormalMat[16]; - f32 textRotMat[16]; + return; + } - f32 w2 = width * 0.5f; - f32 h2 = height * 0.5f; - f32 hz = height * Camera_Position.z; + (void)window; - // coverting model coords (world space) to screen coords - auto ModelXToScreen = [hz, w2](f32 xin)->f32 { - return (xin - Camera_Position.x) * hz + w2; }; - auto ModelYToScreen = [hz, h2](f32 yin)->f32 { - return h2 - (yin - Camera_Position.y) * hz; }; + u32 redisplay = 0; - // Text Projection Matrix + if (UI_On) + { + } + else // ui_on == false + { + if (Edit_Mode) { - memset(textNormalMat, 0, sizeof(textNormalMat)); - textNormalMat[0] = 2.0f / width; - textNormalMat[5] = -2.0f / height; - textNormalMat[10] = -1.0f; - textNormalMat[12] = -1.0f; - textNormalMat[13] = 1.0f; - textNormalMat[15] = 1.0f; + static s32 netDelta = 0; - memset(textRotMat, 0, sizeof(textRotMat)); - textRotMat[4] = 2.0f / width; - textRotMat[1] = 2.0f / height; - textRotMat[10] = -1.0f; - textRotMat[12] = -1.0f; - textRotMat[13] = 1.0f; - textRotMat[15] = 1.0f; - } + s32 w, h; + glfwGetWindowSize(window, &w, &h); + f32 height = (f32)h; + f32 width = (f32)w; - // Extension Labels - if (graphNamesOn) - { - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + f32 factor1 = 1.0f / (2.0f * Camera_Position.z); + f32 factor2 = 2.0f / height; + f32 factor3 = width * 0.5f; - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, 32.0f * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Bold); + f32 wx = (factor1 * factor2 * ((f32)x - factor3)) + Camera_Position.x; + f32 wy = (-factor1 * (1.0f - (factor2 * (f32)y))) - Camera_Position.y; - f32 x = ModelXToScreen(-0.5f); - x = my_Max(x, 0.0f) + 10.0f; + wx = my_Max(-0.5f, my_Min(0.5f, wx)); + wy = my_Max(-0.5f, my_Min(0.5f, wy)); - f32 h = height + 1.0f; + u32 nPixels = Number_of_Pixels_1D; - TraverseLinkedList(Extensions.head, extension_node) + u32 pixel1 = (u32)((f64)nPixels * (0.5 + (f64)wx)); + u32 pixel2 = (u32)((f64)nPixels * (0.5 + (f64)wy)); + + // 防止在尾部溢出 + pixel1 = my_Max(0, my_Min(nPixels - 1, pixel1)); + pixel2 = my_Max(0, my_Min(nPixels - 1, pixel2)); + + u32 contig = Map_State->contigIds[pixel1]; + + if (!Edit_Pixels.editing && !Edit_Pixels.selecting && Map_State->contigIds[pixel2] != contig) { - switch (node->type) + u32 testPixel = pixel1; + u32 testContig = contig; + while (testContig == contig) // move testContig to the 1 before the first or 1 after the last pixel of the contig { - case extension_graph: - { - graph *gph = (graph *)node->extension; - if (gph->on && gph->nameOn) - { - fonsSetColor(FontStash_Context, ThreeFloatColorToU32(gph->colour)); - fonsDrawText(FontStash_Context, x, h - gph->base, (const char *)gph->name, 0); - } - } - break; - } - } - } + if (pixel1 < pixel2) + { + testPixel ++ ; + } + else + { + testPixel -- ; + } - if (File_Loaded && Contig_Name_Labels->on) // Contig Labels - { - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Contig_Name_Labels->bg); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + testContig = Map_State->contigIds[testPixel]; - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + if (testPixel == 0 || testPixel >= (nPixels - 1)) break; - u32 ptr = 0; - vertex vert[4]; + // testContig = pixel1 < pixel2 ? Map_State->contigIds[++testPixel] : Map_State->contigIds[--testPixel]; + // if (testPixel == 0 || testPixel >= (nPixels - 1)) break; + } + if (Map_State->contigIds[testPixel] == contig) + { + pixel2 = testPixel; + } + else + { + pixel2 = pixel1 < pixel2 ? testPixel - 1 : testPixel + 1; + } + } - glViewport(0, 0, (s32)width, (s32)height); + + if (Edit_Pixels.selecting) // select the contig + { + Edit_Pixels.selectPixels.x = my_Max(pixel1, my_Max(Edit_Pixels.selectPixels.x, Edit_Pixels.selectPixels.y)); // used to select multiple contigs with pressing space and dragging the mouse + // Edit_Pixels.selectPixels.x = pixel1; + while( Edit_Pixels.selectPixels.x < (Number_of_Pixels_1D - 1) && + ((Edit_Pixels.scaffSelecting && Map_State->scaffIds[Edit_Pixels.selectPixels.x] && Map_State->scaffIds[Edit_Pixels.selectPixels.x] == Map_State->scaffIds[1 + Edit_Pixels.selectPixels.x] ) || + Map_State->contigIds[Edit_Pixels.selectPixels.x] == Map_State->contigIds[1 + Edit_Pixels.selectPixels.x])) + { + ++Edit_Pixels.selectPixels.x; + } - f32 lh = 0.0f; + Edit_Pixels.selectPixels.y = my_Min(pixel1, my_Min(Edit_Pixels.selectPixels.x, Edit_Pixels.selectPixels.y)); + // Edit_Pixels.selectPixels.y = pixel1; + while( Edit_Pixels.selectPixels.y > 0 && + ((Edit_Pixels.scaffSelecting && Map_State->scaffIds[Edit_Pixels.selectPixels.y] && Map_State->scaffIds[Edit_Pixels.selectPixels.y] == Map_State->scaffIds[Edit_Pixels.selectPixels.y - 1]) || Map_State->contigIds[Edit_Pixels.selectPixels.y] == Map_State->contigIds[Edit_Pixels.selectPixels.y - 1])) + { + --Edit_Pixels.selectPixels.y; + } - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, Contig_Name_Labels->size * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Bold); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Contig_Name_Labels->fg)); - - f32 leftPixel = ModelXToScreen(-0.5f); - f32 totalLength = 0.0f; - f32 wy0 = ModelYToScreen(0.5f); + pixel1 = Edit_Pixels.selectPixels.x; + pixel2 = Edit_Pixels.selectPixels.y; + } - ForLoop(Contigs->numberOfContigs) + if (Edit_Pixels.editing) { - contig *cont = Contigs->contigs_arr + index; + u32 midPixel = (pixel1 + pixel2) >> 1; + u32 oldMidPixel = (Edit_Pixels.pixels.x + Edit_Pixels.pixels.y) >> 1; + s32 diff = (s32)midPixel - (s32)oldMidPixel; + + u32 forward = diff > 0; - totalLength += (f32)((f64)cont->length / (f64)Number_of_Pixels_1D); + s32 newX = (s32)Edit_Pixels.pixels.x + diff; + newX = my_Max(0, my_Min((s32)nPixels - 1, newX)); + s32 newY = (s32)Edit_Pixels.pixels.y + diff; + newY = my_Max(0, my_Min((s32)nPixels - 1, newY)); - f32 rightPixel = ModelXToScreen(totalLength - 0.5f); + s32 diffx = newX - (s32)Edit_Pixels.pixels.x; + s32 diffy = newY - (s32)Edit_Pixels.pixels.y; - if (rightPixel > 0.0f && leftPixel < width) - { - const char *name = (const char *)(Original_Contigs + cont->get_original_contig_id())->name; - f32 x = (rightPixel + leftPixel) * 0.5f; - f32 y = my_Max(wy0, 0.0f) + 10.0f; - f32 textWidth = fonsTextBounds(FontStash_Context, x, y, name, 0, NULL); + diff = forward ? my_Min(diffx, diffy) : my_Max(diffx, diffy); + + //newX = (s32)Edit_Pixels.pixels.x + diff; + //newY = (s32)Edit_Pixels.pixels.y + diff; + + diff = RearrangeMap(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y, diff, Edit_Pixels.snap); + netDelta += diff; - if (textWidth < (rightPixel - leftPixel)) - { - f32 w2t = 0.5f * textWidth; + newX = (s32)Edit_Pixels.pixels.x + diff; + newY = (s32)Edit_Pixels.pixels.y + diff; + + Edit_Pixels.pixels.x = (u32)newX; + Edit_Pixels.pixels.y = (u32)newY; - glUseProgram(Flat_Shader->shaderProgram); + Edit_Pixels.worldCoords.x = (f32)(((f64)((2 * Edit_Pixels.pixels.x) + 1)) / ((f64)(2 * nPixels))) - 0.5f; + Edit_Pixels.worldCoords.y = (f32)(((f64)((2 * Edit_Pixels.pixels.y) + 1)) / ((f64)(2 * nPixels))) - 0.5f; + } + else // edit_pixels.editing == 0 + { + if (netDelta || Global_Edit_Invert_Flag) + { + AddMapEdit(netDelta, Edit_Pixels.pixels, Global_Edit_Invert_Flag); + netDelta = 0; + } + + wx = (f32)(((f64)((2 * pixel1) + 1)) / ((f64)(2 * nPixels))) - 0.5f; + wy = (f32)(((f64)((2 * pixel2) + 1)) / ((f64)(2 * nPixels))) - 0.5f; - vert[0].x = x - w2t; vert[0].y = y + lh; - vert[1].x = x + w2t; vert[1].y = y + lh; - vert[2].x = x + w2t; vert[2].y = y; - vert[3].x = x - w2t; vert[3].y = y; + Edit_Pixels.pixels.x = pixel1; + Edit_Pixels.pixels.y = pixel2; + Edit_Pixels.worldCoords.x = wx; + Edit_Pixels.worldCoords.y = wy; + + Global_Edit_Invert_Flag = 0; + } - glBindBuffer(GL_ARRAY_BUFFER, Label_Box_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Label_Box_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + redisplay = 1; + } + else if ( + Tool_Tip->on || + Waypoint_Edit_Mode || + Scaff_Edit_Mode || + MetaData_Edit_Mode || + Select_Sort_Area_Mode) + { + s32 w, h; + glfwGetWindowSize(window, &w, &h); + f32 height = (f32)h; + f32 width = (f32)w; - glUseProgram(UI_Shader->shaderProgram); - fonsDrawText(FontStash_Context, x, y, name, 0); - } - } + f32 factor1 = 1.0f / (2.0f * Camera_Position.z); + f32 factor2 = 2.0f / height; + f32 factor3 = width * 0.5f; - leftPixel = rightPixel; - } + f32 wx = (factor1 * factor2 * ((f32)x - factor3)) + Camera_Position.x; + f32 wy = (-factor1 * (1.0f - (factor2 * (f32)y))) - Camera_Position.y; - glUseProgram(Flat_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textRotMat); + wx = my_Max(-0.5f, my_Min(0.5f, wx)); + wy = my_Max(-0.5f, my_Min(0.5f, wy)); - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textRotMat); + u32 nPixels = Number_of_Textures_1D * Texture_Resolution; - f32 topPixel = ModelYToScreen(0.5f); - f32 wx0 = ModelXToScreen(-0.5f); - totalLength = 0.0f; + u32 pixel1 = my_Min((u32)((f64)nPixels * (0.5 + (f64)wx)), (nPixels - 1)); + u32 pixel2 = my_Min((u32)((f64)nPixels * (0.5 + (f64)wy)), (nPixels - 1)); - ForLoop(Contigs->numberOfContigs) + Tool_Tip_Move.pixels.x = pixel1; + Tool_Tip_Move.pixels.y = pixel2; + Tool_Tip_Move.worldCoords.x = wx; + Tool_Tip_Move.worldCoords.y = wy; + + if (Waypoint_Edit_Mode) { - contig *cont = Contigs->contigs_arr + index; - - totalLength += (f32)((f64)cont->length / (f64)Number_of_Pixels_1D); + Selected_Waypoint = 0; + f32 selectDis = Waypoint_Select_Distance / (height * Camera_Position.z); + f32 closestDistanceSq = selectDis * selectDis; - f32 bottomPixel = ModelYToScreen(0.5f - totalLength); + waypoint **searchBuffer = PushArray(Working_Set, waypoint*, Waypoints_Stack_Size); + waypoint **bufferEnd = searchBuffer; + GetWaypointsWithinSquare( // add waypoints within the square to the buffer + {Tool_Tip_Move.worldCoords.x - selectDis, Tool_Tip_Move.worldCoords.y - selectDis}, + 2.0f * selectDis, + &bufferEnd); + for ( waypoint **waypPtr = searchBuffer; + waypPtr != bufferEnd; + ++waypPtr ) { // make sure select the closest waypoint to the mouse + waypoint *wayp = *waypPtr; - if (topPixel < height && bottomPixel > 0.0f) - { - const char *name = (const char *)(Original_Contigs + cont->get_original_contig_id())->name; - f32 y = (topPixel + bottomPixel) * 0.5f; - f32 x = my_Max(wx0, 0.0f) + 10.0f; - f32 textWidth = fonsTextBounds(FontStash_Context, x, y, name, 0, NULL); + f32 dx = Tool_Tip_Move.worldCoords.x - wayp->coords.x; + f32 dy = Tool_Tip_Move.worldCoords.y - wayp->coords.y; + f32 disSq = (dx * dx) + (dy * dy); - if (textWidth < (bottomPixel - topPixel)) + if (disSq < closestDistanceSq) { - f32 tmp = x; - x = -y; - y = tmp; + closestDistanceSq = disSq; + Selected_Waypoint = wayp; + } + } - f32 w2t = 0.5f * textWidth; + FreeLastPush(Working_Set); // searchBuffer + } + else if (Scaff_Edit_Mode && Scaff_Painting_Flag) + { + if (Scaff_Painting_Flag == 1) + { + if (!Scaff_Painting_Id) + { + if (Map_State->scaffIds[Tool_Tip_Move.pixels.x]) + { + Scaff_Painting_Id = Map_State->scaffIds[Tool_Tip_Move.pixels.x]; + } + else + { + u32 max = 0; + ForLoop(Number_of_Pixels_1D) max = my_Max(max, Map_State->scaffIds[index]); + Scaff_Painting_Id = max + 1; + } + } + } + else Scaff_Painting_Id = 0; - glUseProgram(Flat_Shader->shaderProgram); + if (Map_State->scaffIds[Tool_Tip_Move.pixels.x] != Scaff_Painting_Id || (Scaff_FF_Flag & 1)) + { + u32 pixel = Tool_Tip_Move.pixels.x; - vert[0].x = x - w2t; vert[0].y = y + lh; - vert[1].x = x + w2t; vert[1].y = y + lh; - vert[2].x = x + w2t; vert[2].y = y; - vert[3].x = x - w2t; vert[3].y = y; + u32 currScaffId = Map_State->scaffIds[pixel]; + u32 contigId = Map_State->contigIds[pixel]; + Map_State->scaffIds[pixel] = Scaff_Painting_Id; - glBindBuffer(GL_ARRAY_BUFFER, Label_Box_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Label_Box_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + // set the pixels with same contig_id into the same scaff + u32 testPixel = pixel; + while (testPixel && (Map_State->contigIds[testPixel - 1] == contigId)) Map_State->scaffIds[--testPixel] = Scaff_Painting_Id; + testPixel = pixel; + while ((testPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[testPixel + 1] == contigId)) Map_State->scaffIds[++testPixel] = Scaff_Painting_Id; - glUseProgram(UI_Shader->shaderProgram); - fonsDrawText(FontStash_Context, x, y, name, 0); + if (Scaff_FF_Flag & 1) + { + ++testPixel; + while ((testPixel < Number_of_Pixels_1D) && ((Scaff_FF_Flag & 2) || (Map_State->scaffIds[testPixel] == (Scaff_Painting_Flag == 1 ? 0 : currScaffId)))) Map_State->scaffIds[testPixel++] = Scaff_Painting_Id; } } - topPixel = bottomPixel; + UpdateContigsFromMapState(); + } + else if (Select_Sort_Area_Mode) + { + auto_curation_state.update_sort_area(Tool_Tip_Move.pixels.x, Map_State, Number_of_Pixels_1D); } + else if ( + MetaData_Edit_Mode && + MetaData_Edit_State && + strlen((const char *)Meta_Data->tags[MetaData_Active_Tag]) + ) + { + u32 pixel = Tool_Tip_Move.pixels.x; + u32 contigId = Map_State->contigIds[pixel]; - ChangeSize((s32)width, (s32)height); - } + if (MetaData_Edit_State == 1) + Map_State->metaDataFlags[pixel] |= (1 << MetaData_Active_Tag); // set the active tag + else + Map_State->metaDataFlags[pixel] &= ~(1 << MetaData_Active_Tag); // clear the active tag + + u32 testPixel = pixel; + while (testPixel && (Map_State->contigIds[testPixel - 1] == contigId)) + { + if (MetaData_Edit_State == 1) + Map_State->metaDataFlags[--testPixel] |= (1 << MetaData_Active_Tag); + else + Map_State->metaDataFlags[--testPixel] &= ~(1 << MetaData_Active_Tag); + } - #define MaxTicksPerScaleBar 128 - if (File_Loaded && Scale_Bars->on) // Scale bars - { - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Scale_Bars->bg); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - - u32 ptr = 0; - vertex vert[4]; + testPixel = pixel; + while ((testPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[testPixel + 1] == contigId)) + { + if (MetaData_Edit_State == 1) + Map_State->metaDataFlags[++testPixel] |= (1 << MetaData_Active_Tag); + else + Map_State->metaDataFlags[++testPixel] &= ~(1 << MetaData_Active_Tag); + } - glViewport(0, 0, (s32)width, (s32)height); + UpdateContigsFromMapState(); + } - f32 lh = 0.0f; + redisplay = 1; + } - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, Scale_Bars->size * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Normal); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Scale_Bars->fg)); + if (Mouse_Move.x >= 0.0) + { + s32 w, h; + glfwGetWindowSize(window, &w, &h); + f32 height = (f32)h; - f32 leftPixel = ModelXToScreen(-0.5f); - f32 rightPixel = ModelXToScreen(0.5f); - f32 wy0 = ModelYToScreen(0.5); - f32 offset = 45.0f * Screen_Scale.x; - f32 y = my_Max(wy0, 0.0f) + offset; - f32 totalLength = 0.0f; + f32 factor = 1.0f / (height * Camera_Position.z); + f32 dx = (f32)(Mouse_Move.x - x) * factor; + f32 dy = (f32)(y - Mouse_Move.y) * factor; - f32 bpPerPixel = (f32)((f64)Total_Genome_Length / (f64)(rightPixel - leftPixel)); + Camera_Position.x += dx; + Camera_Position.y += dy; + ClampCamera(); - GLfloat *bg = (GLfloat *)&Scale_Bars->bg; - - f32 scaleBarWidth = Scale_Bars->size * 4.0f / 20.0f * Screen_Scale.x; - f32 tickLength = Scale_Bars->size * 3.0f / 20.0f * Screen_Scale.x; + Mouse_Move.x = x; + Mouse_Move.y = y; - ForLoop(Contigs->numberOfContigs) - { - contig *cont = Contigs->contigs_arr + index; - - totalLength += (f32)((f64)cont->length / (f64)Number_of_Pixels_1D); - rightPixel = ModelXToScreen(totalLength - 0.5f); + redisplay = 1; + } + } - f32 pixelLength = rightPixel - leftPixel; - f32 startCoord = (f32)((f64)(IsContigInverted(index) ? (cont->startCoord - cont->length) : cont->startCoord) * (f64)Total_Genome_Length / (f64)Number_of_Pixels_1D); + if (redisplay) + { + Redisplay = 1; + } +} - u32 labelLevels = SubDivideScaleBar(leftPixel, rightPixel, (leftPixel + rightPixel) * 0.5f, bpPerPixel, startCoord); - u32 labels = 0; - ForLoop2(labelLevels) - { - labels += (labels + 1); - } - labels = my_Min(labels, MaxTicksPerScaleBar); - if (rightPixel > 0.0f && leftPixel < width) - { - if (labels) - { - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, bg); - vert[0].x = leftPixel + 1.0f; vert[0].y = y + scaleBarWidth + tickLength + 1.0f + lh; - vert[1].x = rightPixel - 1.0f; vert[1].y = y + scaleBarWidth + tickLength + 1.0f + lh; - vert[2].x = rightPixel - 1.0f; vert[2].y = y; - vert[3].x = leftPixel + 1.0f; vert[3].y = y; +global_function +void +Mouse(GLFWwindow* window, s32 button, s32 action, s32 mods) +{ + s32 primaryMouse = user_profile_settings_ptr->invert_mouse ? GLFW_MOUSE_BUTTON_RIGHT : GLFW_MOUSE_BUTTON_LEFT; + s32 secondaryMouse = user_profile_settings_ptr->invert_mouse ? GLFW_MOUSE_BUTTON_LEFT : GLFW_MOUSE_BUTTON_RIGHT; + + if (Loading || auto_sort_state) + { + return; + } + + (void)mods; - glBindBuffer(GL_ARRAY_BUFFER, Scale_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scale_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + f64 x, y; + glfwGetCursorPos(window, &x, &y); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Scale_Bars->fg); + if (UI_On) + { + if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) + { + Deferred_Close_UI = 1; + } + } + else // UI not on + { + if (button == primaryMouse && Edit_Mode && action == GLFW_PRESS) + { + Edit_Pixels.editing = !Edit_Pixels.editing; + MouseMove(window, x, y); + if (!Edit_Pixels.editing) UpdateScaffolds(); + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Edit_Mode && action == GLFW_RELEASE && !Edit_Pixels.editing) + { + Edit_Pixels.editing = 1; + Edit_Pixels.selecting = 0; + MouseMove(window, x, y); + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Edit_Mode && action == GLFW_PRESS && !Edit_Pixels.editing) + { + Edit_Pixels.selecting = 1; + Edit_Pixels.selectPixels = Edit_Pixels.pixels; + MouseMove(window, x, y); + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Edit_Mode && Edit_Pixels.editing && action == GLFW_PRESS) + { + InvertMap(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); + Global_Edit_Invert_Flag = !Global_Edit_Invert_Flag; + UpdateContigsFromMapState(); - vert[0].x = leftPixel + 1.0f; vert[0].y = y + scaleBarWidth; - vert[1].x = rightPixel - 1.0f; vert[1].y = y + scaleBarWidth; - vert[2].x = rightPixel - 1.0f; vert[2].y = y; - vert[3].x = leftPixel + 1.0f; vert[3].y = y; + Redisplay = 1; + } + else if (button == primaryMouse && Waypoint_Edit_Mode && action == GLFW_PRESS) + { + AddWayPoint(Tool_Tip_Move.worldCoords); + MouseMove(window, x, y); + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Waypoint_Edit_Mode && action == GLFW_PRESS) + { + if (Selected_Waypoint) + { + RemoveWayPoint(Selected_Waypoint); + MouseMove(window, x, y); + } + } + else if (button == primaryMouse && Scaff_Edit_Mode && action == GLFW_PRESS) + { + Scaff_Painting_Flag = 1; + MouseMove(window, x, y); + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && Scaff_Edit_Mode && action == GLFW_PRESS) + { + Scaff_Painting_Flag = 2; + MouseMove(window, x, y); + } + else if ((button == GLFW_MOUSE_BUTTON_MIDDLE || button == primaryMouse) && Scaff_Edit_Mode && action == GLFW_RELEASE) + { + Scaff_Painting_Flag = 0; + Scaff_Painting_Id = 0; + MouseMove(window, x, y); + UpdateScaffolds(); + } + else if (button == primaryMouse && Select_Sort_Area_Mode && action == GLFW_PRESS) + { + auto_curation_state.select_mode = 1; + MouseMove(window, x, y); + } + else if (button == primaryMouse && Select_Sort_Area_Mode && action == GLFW_RELEASE) + { + auto_curation_state.select_mode = 0; + MouseMove(window, x, y); + } + else if (button == primaryMouse && MetaData_Edit_Mode && action == GLFW_PRESS) + { + MetaData_Edit_State = 1; + MouseMove(window, x, y); + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && MetaData_Edit_Mode && action == GLFW_PRESS) + { + MetaData_Edit_State = 2; + MouseMove(window, x, y); + } + else if ((button == GLFW_MOUSE_BUTTON_MIDDLE || button == primaryMouse) && MetaData_Edit_Mode && action == GLFW_RELEASE) + { + MetaData_Edit_State = 0; + MouseMove(window, x, y); + } + else if (button == secondaryMouse) + { + if (action == GLFW_PRESS) + { + Mouse_Move.x = x; + Mouse_Move.y = y; + } + else + { + Mouse_Move.x = Mouse_Move.y = -1.0; + } + } + else if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) + { + UI_On = !UI_On; + Mouse_Move.x = Mouse_Move.y = -1; + Redisplay = 1; + ++NK_Device->lastContextMemory[0]; + } + } +} - glBindBuffer(GL_ARRAY_BUFFER, Scale_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scale_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); +global_variable +struct nk_vec2 +NK_Scroll; - f32 fraction = 1.0f / (f32)(labels + 1); - f32 distance = 0.0f; - ForLoop2(labels) - { - distance += fraction; - f32 x = (pixelLength * distance) + (f32)leftPixel; +global_function +void +Scroll(GLFWwindow* window, f64 x, f64 y) +{ + if (Loading || auto_sort_state) + { + return; + } + + if (UI_On) + { + NK_Scroll.y = (f32)y; + NK_Scroll.x = (f32)x; + } + else + { + if (y != 0.0) + { + ZoomCamera(my_Max(my_Min((f32)y, 0.01f), -0.01f)); + Redisplay = 1; + } - glUseProgram(Flat_Shader->shaderProgram); + if (Edit_Mode || Tool_Tip->on || Waypoint_Edit_Mode) + { + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + } +} - vert[0].x = x - (0.5f * scaleBarWidth); vert[0].y = y + scaleBarWidth + tickLength; - vert[1].x = x + (0.5f * scaleBarWidth); vert[1].y = y + scaleBarWidth + tickLength; - vert[2].x = x + (0.5f * scaleBarWidth); vert[2].y = y + scaleBarWidth; - vert[3].x = x - (0.5f * scaleBarWidth); vert[3].y = y + scaleBarWidth; +global_variable +u64 +Total_Genome_Length; - glBindBuffer(GL_ARRAY_BUFFER, Scale_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scale_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); +global_function +void +Setup(); - char buff[16]; - stbsp_snprintf(buff, 16, "%$.2f", (f64)(startCoord + (pixelLength * (IsContigInverted(index) ? (1.0f - distance) : distance) * bpPerPixel))); - glUseProgram(UI_Shader->shaderProgram); - fonsDrawText(FontStash_Context, x, y + scaleBarWidth + tickLength + 1.0f, buff, 0); - } - } - } +global_function +u32 +SubDivideScaleBar(f32 left, f32 right, f32 middle, f32 bpPerPixel, f32 offset) +{ + u32 result = 0; - leftPixel = rightPixel; - } + if (left < right) + { + f32 length = right - left; + f32 half = length * 0.5f; + char buff[16]; + stbsp_snprintf(buff, 16, "%$.2f", (f64)(offset + (half * bpPerPixel))); + f32 width = fonsTextBounds(FontStash_Context, middle, 0.0, buff, 0, NULL); + f32 halfWidth = 0.5f * width; - ChangeSize((s32)width, (s32)height); - } - - // Finished - add the cross line to the waypoint - if (File_Loaded && (Waypoint_Edit_Mode || Waypoints_Always_Visible)) // Waypoint Edit Mode + if ((middle + halfWidth) < right && (middle - halfWidth) > left) { - u32 ptr = 0; - vertex vert[4]; + u32 leftResult = SubDivideScaleBar(left, middle - halfWidth, (left + middle) * 0.5f, bpPerPixel, offset); + u32 rightResult = SubDivideScaleBar(middle + halfWidth, right, (right + middle) * 0.5f, bpPerPixel, offset); + result = 1 + my_Min(leftResult, rightResult); + } + } - glUseProgram(Flat_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + return(result); +} - glViewport(0, 0, (s32)width, (s32)height); -#define DefaultWaypointSize 18.0f - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->base); +global_variable +u32 +File_Loaded = 0; - // define the vertical line - f32 lineWidth = Waypoint_Mode_Data->size / DefaultWaypointSize * 0.7f * Screen_Scale.x; - f32 lineHeight = Waypoint_Mode_Data->size / DefaultWaypointSize * 8.0f * Screen_Scale.x; +global_variable +nk_color +Theme_Colour; - f32 lh = 0.0f; +#ifdef Internal +global_function +void +DrawQuadTreeLevel (u32 *ptr, waypoint_quadtree_level *level, vertex *vert, f32 lineWidth, u32 n = 0) +{ + f32 colour[5][4] = {{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 1.0f, 1.0f}}; + f32 *col = (f32 *)colour[n%5]; - u32 baseColour = FourFloatColorToU32(Waypoint_Mode_Data->base); - u32 selectColour = FourFloatColorToU32(Waypoint_Mode_Data->selected); - - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, Waypoint_Mode_Data->size * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Bold); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, baseColour); + glUniform4fv(Flat_Shader->colorLocation, 1, col); + + if (level->show) + { + vert[0].x = level->lowerBound.x; + vert[0].y = -level->lowerBound.y; + vert[1].x = level->lowerBound.x; + vert[1].y = -level->lowerBound.y - level->size; + vert[2].x = level->lowerBound.x + lineWidth; + vert[2].y = -level->lowerBound.y - level->size; + vert[3].x = level->lowerBound.x + lineWidth; + vert[3].y = -level->lowerBound.y; - char buff[4]; - - // render the waypoints lines - TraverseLinkedList(Waypoint_Editor->activeWaypoints.next, waypoint) - { - point2f screen = {ModelXToScreen(node->coords.x), ModelYToScreen(-node->coords.y)}; - point2f screenYRange = {ModelYToScreen(0.5f), ModelYToScreen(-0.5f)}; - point2f screenXRange = {ModelXToScreen(-0.5f), ModelXToScreen(0.5f)}; + glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - glUseProgram(Flat_Shader->shaderProgram); - if (node == Selected_Waypoint) - { - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->selected); - } - - bool long_horizontal, long_vertical; - if (Long_Waypoints_Mode == 0) { // vertical line - long_horizontal = false; - long_vertical = true; - } else if (Long_Waypoints_Mode == 1) { // horizontal line - long_horizontal = true; - long_vertical = false; - } else { // cross line - long_horizontal = true; - long_vertical = true; - } + vert[0].x = level->lowerBound.x; + vert[0].y = -level->lowerBound.y - level->size; + vert[1].x = level->lowerBound.x + level->size; + vert[1].y = -level->lowerBound.y - level->size; + vert[2].x = level->lowerBound.x + level->size; + vert[2].y = -level->lowerBound.y - level->size + lineWidth; + vert[3].x = level->lowerBound.x; + vert[3].y = -level->lowerBound.y - level->size + lineWidth; - // draw the vertical line - vert[0].x = screen.x - lineWidth; vert[0].y = long_vertical ? screenYRange.x : screen.y - lineHeight; - vert[1].x = screen.x - lineWidth; vert[1].y = long_vertical ? screenYRange.y : screen.y + lineHeight; - vert[2].x = screen.x + lineWidth; vert[2].y = long_vertical ? screenYRange.y : screen.y + lineHeight; - vert[3].x = screen.x + lineWidth; vert[3].y = long_vertical ? screenYRange.x : screen.y - lineHeight; - glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Waypoint_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - // draw the horizontal line (originally: the left part, not sure why Ed draw them seperately, if problem arises we can returen here) maybe avoid drawing the cross part twice... - vert[0].x = long_horizontal ? screenXRange.x : screen.x - lineHeight; vert[0].y = screen.y - lineWidth; - vert[1].x = long_horizontal ? screenXRange.x : screen.x - lineHeight; vert[1].y = screen.y + lineWidth; - vert[2].x = long_horizontal ? screenXRange.y : screen.x + lineHeight; vert[2].y = screen.y + lineWidth;// vert[2].x = screen.x - lineWidth; - vert[3].x = long_horizontal ? screenXRange.y : screen.x + lineHeight; vert[3].y = screen.y - lineWidth;// vert[3].x = screen.x - lineWidth; - glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Waypoint_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + vert[0].x = level->lowerBound.x + level->size - lineWidth; + vert[0].y = -level->lowerBound.y; + vert[1].x = level->lowerBound.x + level->size - lineWidth; + vert[1].y = -level->lowerBound.y - level->size; + vert[2].x = level->lowerBound.x + level->size; + vert[2].y = -level->lowerBound.y - level->size; + vert[3].x = level->lowerBound.x + level->size; + vert[3].y = -level->lowerBound.y; - // draw the horizontal line (originally: the right part, not sure why Ed draw them seperately, if problem arises we can returen here) - // vert[0].x = screen.x + lineWidth; - // vert[0].y = screen.y - lineWidth; - // vert[1].x = screen.x + lineWidth; - // vert[1].y = screen.y + lineWidth; - // vert[2].x = screen.x + lineHeight; - // vert[2].y = screen.y + lineWidth; - // vert[3].x = screen.x + lineHeight; - // vert[3].y = screen.y - lineWidth; - // glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); - // glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - // glBindVertexArray(Waypoint_Data->vaos[ptr++]); - // glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - - if (node == Selected_Waypoint) - { - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->base); - } + glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - glUseProgram(UI_Shader->shaderProgram); - if (node == Selected_Waypoint) - { - fonsSetColor(FontStash_Context, selectColour); - } - - stbsp_snprintf(buff, sizeof(buff), "%d", node->index + 1); - fonsDrawText(FontStash_Context, screen.x + lineWidth + lineWidth, screen.y - lineWidth - lh, buff, 0); + vert[0].x = level->lowerBound.x; + vert[0].y = -level->lowerBound.y; + vert[1].x = level->lowerBound.x; + vert[1].y = -level->lowerBound.y - lineWidth; + vert[2].x = level->lowerBound.x + level->size; + vert[2].y = -level->lowerBound.y - lineWidth; + vert[3].x = level->lowerBound.x + level->size; + vert[3].y = -level->lowerBound.y; - if (node == Selected_Waypoint) - { - fonsSetColor(FontStash_Context, baseColour); - } - } + glBindBuffer(GL_ARRAY_BUFFER, QuadTree_Data->vbos[*ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(QuadTree_Data->vaos[(*ptr)++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } - if (Waypoint_Edit_Mode && !UI_On) - { - fonsSetSize(FontStash_Context, 24.0f * Screen_Scale.x); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Waypoint_Mode_Data->text)); + if (level->children[0]) + { + ForLoop(4) + { + DrawQuadTreeLevel(ptr, level->children[index], vert, lineWidth, n+1); + } + } +} +#endif - std::vector helpTexts = { - (char*)"Waypoint Edit Mode", - (char*)"W: exit", - (char*)"Left Click: place", - (char*)"Middle Click / Spacebar: delete", - }; - if (Long_Waypoints_Mode == 2) { - helpTexts.push_back((char*)"L: both directions"); - } else if (Long_Waypoints_Mode == 1) { - helpTexts.push_back((char*)"L: horizontal"); - } else { - helpTexts.push_back((char*)"L: vertical"); - } +global_function +u32 +FourFloatColorToU32(nk_colorf colour) +{ + return(glfonsRGBA((u08)(colour.r * 255.0f), (u08)(colour.g * 255.0f), + (u08)(colour.b * 255.0f), (u08)(colour.a * 255.0f))); +} - f32 textBoxHeight = lh; - textBoxHeight *= (f32)helpTexts.size(); - textBoxHeight += (f32)helpTexts.size() - 1.0f; - f32 spacing = 10.0f; // distance from the edge of the text box +global_function +u32 +ThreeFloatColorToU32(nk_colorf colour) +{ + return(glfonsRGBA((u08)(colour.r * 255.0f), (u08)(colour.g * 255.0f), + (u08)(colour.b * 255.0f), 255)); +} - // f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, helpText4, 0, NULL); - f32 textWidth = 0; - for (auto i : helpTexts){ - textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i, 0, NULL)) + 0.5f * spacing; - } +global_function +void +ColourGenerator(u32 index, f32 *rgb) +{ +// #define RedFreq 1.666f +// #define GreenFreq 2.666f +// #define BlueFreq 3.666f - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->bg); +// rgb[0] = 0.5f * (sinf((f32)index * RedFreq) + 1.0f); +// rgb[1] = 0.5f * (sinf((f32)index * GreenFreq) + 1.0f); +// rgb[2] = 0.5f * (sinf((f32)index * BlueFreq) + 1.0f); - vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; - vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; - vert[2].x = width - spacing; vert[2].y = height - spacing; - vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; + f32 RedFreq = meta_dataColors[meta_data_curcolorProfile][0]; + f32 GreenFreq = meta_dataColors[meta_data_curcolorProfile][1]; + f32 BlueFreq = meta_dataColors[meta_data_curcolorProfile][2]; - glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Waypoint_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + rgb[0] = 0.5f * (sinf((f32)index * RedFreq) + 1.0f); + rgb[1] = 0.5f * (sinf((f32)index * GreenFreq) + 1.0f); + rgb[2] = 0.5f * (sinf((f32)index * BlueFreq) + 1.0f); +} - glUseProgram(UI_Shader->shaderProgram); - for (u32 i=0; i< helpTexts.size() ; i++) { - fonsDrawText( - FontStash_Context, - width - spacing - textWidth, - height - spacing - textBoxHeight + (lh + 1.0f) * i, - helpTexts[i], - 0); - } - } - } - - // Extension Mode - if (Extension_Mode && !UI_On) - { - u32 ptr = 0; - vertex vert[4]; - f32 lh = 0.0f; +void DrawOutlinedText( + FONScontext *FontStash_Context, + nk_colorf *colour, + const char *text, + f32 x, + f32 y, + f32 offset = 1.0f, + bool outline_on = false) +{ - glUseProgram(Flat_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + nk_colorf outlineColor; + if (meta_outline->on == 1) + { + outlineColor = {0.0f, 0.0f, 0.0f, 1.0f}; // black + } + else + { + outlineColor = {1.0f, 1.0f, 1.0f, 1.0f}; // white + } - glViewport(0, 0, (s32)width, (s32)height); + if (outline_on) + outlineColor = {0.0f, 0.0f, 0.0f, 1.0f}; // black -#define DefaultExtensionSize 20.0f - // glUseProgram(Flat_Shader->shaderProgram); - // glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->base); + unsigned int outlineColorU32 = FourFloatColorToU32(outlineColor); + unsigned int textColorU32 = FourFloatColorToU32(*colour); - // f32 lineWidth = Waypoint_Mode_Data->size / DefaultWaypointSize * 0.7f * Screen_Scale.x; - // f32 lineHeight = Waypoint_Mode_Data->size / DefaultWaypointSize * 8.0f * Screen_Scale.x; + if (meta_outline->on > 0 || outline_on) + { + fonsSetColor(FontStash_Context, outlineColorU32); + fonsDrawText(FontStash_Context, x - offset, y - offset, text, 0); + fonsDrawText(FontStash_Context, x + offset, y - offset, text, 0); + fonsDrawText(FontStash_Context, x - offset, y + offset, text, 0); + fonsDrawText(FontStash_Context, x + offset, y + offset, text, 0); + } - fonsSetSize(FontStash_Context, 24.0f * Screen_Scale.x); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Extension_Mode_Data->text)); + // // Draw the original text on top + fonsSetColor(FontStash_Context, textColorU32); + fonsDrawText(FontStash_Context, x, y, text, 0); +} - f32 textBoxHeight = lh; - textBoxHeight *= 7.0f; - textBoxHeight += 6.0f; - f32 spacing = 10.0f; - // 6 lines in total - std::vector helpTexts = { - "Extensions:", - "X: exit" - }; - if (Extensions.head) - { - TraverseLinkedList(Extensions.head, extension_node) - { - switch (node->type) - { - case extension_graph: - { - graph *gph = (graph *)node->extension; +global_variable +char +Extension_Magic_Bytes[][4] = +{ + {'p', 's', 'g', 'h'} +}; - if (strstr((char*)gph->name, "coverage")) - { - helpTexts.push_back("C: Graph: coverage"); - } - else if (strstr((char*)gph->name, "gap")) - { - helpTexts.push_back("G: Graph: gap"); - } - else if (strstr((char*)gph->name, "repeat_density")) - { - helpTexts.push_back("R: Graph: repeat_density"); - } - else if (strstr((char*)gph->name, "telomere")) - { - helpTexts.push_back("T: Graph: telomere"); - } - } - break; - } - } - } - f32 textWidth = 0.; - for (auto i : helpTexts) - { - textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i.c_str(), 0, NULL)) ; - } - textWidth += 0.5f * spacing; - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Extension_Mode_Data->bg); +global_variable +extension_sentinel +Extensions = {}; - vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; - vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; - vert[2].x = width - spacing; vert[2].y = height - spacing; - vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; +global_function +void +AddExtension(extension_node *node) +{ + node->next = 0; - glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Waypoint_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + if (!Extensions.head) + { + Extensions.head = node; + Extensions.tail = node; + } + else + { + Extensions.tail->next = node; // append the last to the end + Extensions.tail = node; // set the last to the new node just appended + } +} - glUseProgram(UI_Shader->shaderProgram); - for (u32 i=0; i< helpTexts.size() ; i++) - { - fonsDrawText( - FontStash_Context, - width - spacing - textWidth, - height - spacing - textBoxHeight + (lh + 1.0f) * i, - helpTexts[i].c_str(), - 0); - } - } +global_function +void +Render() { + // Projection Matrix + f32 width; + f32 height; + { + glClear(GL_COLOR_BUFFER_BIT); + // glClearColor(0.2f, 0.6f, 0.4f, 1.0f); // classic background color + glClearColor(bgcolor[active_bgcolor][0], bgcolor[active_bgcolor][1], bgcolor[active_bgcolor][2], bgcolor[active_bgcolor][3]); - // label to show the selected sequence 从 input sequences 里面选中的片段 - if (Selected_Sequence_Cover_Countor.end_time > 0.) - { - f64 crt_time = GetTime(); - if (crt_time < Selected_Sequence_Cover_Countor.end_time) - { - char buff[128]; - f32 colour[4] = {1.0, 1.0, 1.0, 1.0}; - snprintf(buff, 128, "%s (%u)", (char *)((Original_Contigs+Selected_Sequence_Cover_Countor.original_contig_index)->name), Selected_Sequence_Cover_Countor.idx_within_original_contig+1); + s32 viewport[4]; + glGetIntegerv (GL_VIEWPORT, viewport); + width = (f32)viewport[2]; + height = (f32)viewport[3]; - f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, (char *)buff, 0, NULL); - ColourGenerator(65, colour); - // position of text - f32 textX = ModelXToScreen( - (f32)Selected_Sequence_Cover_Countor.map_loc / (f32)Number_of_Pixels_1D -0.5f) - - (0.5f * textWidth); - f32 textY = ModelYToScreen( 0.5f - (f32)Selected_Sequence_Cover_Countor.map_loc / (f32)Number_of_Pixels_1D); - - glUseProgram(UI_Shader->shaderProgram); - DrawOutlinedText( - FontStash_Context, - (nk_colorf *)colour, - (char *)buff, - textX, - textY, - 3.0f, true); - Selected_Sequence_Cover_Countor.plotted = true; - } - else - { - Selected_Sequence_Cover_Countor.clear(); - Redisplay = 1; - } - } + f32 mat[16]; + memset(mat, 0, sizeof(mat)); + mat[0] = 2.0f * Camera_Position.z * height / width; + mat[5] = 2.0f * Camera_Position.z; + mat[10] = -2.0f; + mat[12] = -2.0f * height * Camera_Position.x * Camera_Position.z / width; + mat[13] = -2.0f * Camera_Position.y * Camera_Position.z; + mat[15] = 1.0f; - // Scaff Bars - if (File_Loaded && (Scaff_Edit_Mode || Scaffs_Always_Visible)) + /* + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH_NO(T left, T right, T bottom, T top, T zNear, T zFar) { - glUseProgram(Flat_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); // 0 + Result[1][1] = static_cast(2) / (top - bottom); // 5 + Result[2][2] = - static_cast(2) / (zFar - zNear);// 10 + Result[3][0] = - (right + left) / (right - left); // 12 + Result[3][1] = - (top + bottom) / (top - bottom); // 13 + Result[3][2] = - (zFar + zNear) / (zFar - zNear); // 14 + return Result; + } + glm::mat4 projection_mat = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + */ - glViewport(0, 0, (s32)width, (s32)height); + // apply the transformation + glUseProgram(Contact_Matrix->shaderProgram); + glUniformMatrix4fv(Contact_Matrix->matLocation, 1, GL_FALSE, mat); + glUseProgram(Flat_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, mat); - u32 ptr = 0; - vertex vert[4]; - f32 barColour[4] = {1.0f, 1.0f, 1.0f, 0.5f}; - - f32 lh = 0.0f; - fonsClearState(FontStash_Context); -#define DefaultScaffSize 40.0f - fonsSetSize(FontStash_Context, Scaff_Mode_Data->size * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_MIDDLE | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Bold); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - - char buff[128]; - f32 position = 0.0f; - f32 start = 0.0f; - u32 scaffId = Contigs->contigs_arr->scaffId; - ForLoop(Contigs->numberOfContigs) + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) { - contig *cont = Contigs->contigs_arr + index; - - if (cont->scaffId != scaffId) - { - if (scaffId) + case extension_graph: { - vert[0].x = ModelXToScreen(start - 0.5f); vert[0].y = ModelYToScreen(0.5f - start); - vert[1].x = ModelXToScreen(start - 0.5f); vert[1].y = ModelYToScreen(0.5f - position); - vert[2].x = ModelXToScreen(position - 0.5f); vert[2].y = ModelYToScreen(0.5f - position); - vert[3].x = ModelXToScreen(position - 0.5f); vert[3].y = ModelYToScreen(0.5f - start); + graph *gph = (graph *)node->extension; + glUseProgram(gph->shader->shaderProgram); + glUniformMatrix4fv(gph->shader->matLocation, 1, GL_FALSE, mat); + } + break; + } + } + } - ColourGenerator((u32)scaffId, (f32 *)barColour); - u32 colour = ThreeFloatColorToU32(*((nk_colorf *)barColour)); + // Textures + if (File_Loaded) + { + glUseProgram(Contact_Matrix->shaderProgram); + glBindTexture(GL_TEXTURE_2D_ARRAY, Contact_Matrix->textures); + u32 ptr = 0; + while (ptr < Number_of_Textures_1D * Number_of_Textures_1D) + { + glBindVertexArray(Contact_Matrix->vaos[ptr++]); // bind the vertices and then draw the quad + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } + } - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); + #ifdef Internal + if (File_Loaded && Tiles->on) + { + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Tiles->bg); - glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + u32 ptr = 0; + vertex vert[4]; - glUseProgram(UI_Shader->shaderProgram); - fonsSetColor(FontStash_Context, colour); + f32 lineWidth = 0.001f / sqrtf(Camera_Position.z); + f32 position = 0.0f; + f32 spacing = 1.0f / (f32)Number_of_Textures_1D; - stbsp_snprintf(buff, sizeof(buff), "Scaffold %u", scaffId); - f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, buff, 0, NULL); - fonsDrawText(FontStash_Context, ModelXToScreen(0.5f * (position + start - 1.0f)) - (0.5f * textWidth), ModelYToScreen(0.5f - start) - lh, buff, 0); - } + vert[0].x = -0.5f; + vert[0].y = -0.5f; + vert[1].x = lineWidth - 0.5f; + vert[1].y = -0.5f; + vert[2].x = lineWidth - 0.5f; + vert[2].y = 0.5f; + vert[3].x = -0.5f; + vert[3].y = 0.5f; - start = position; - scaffId = cont->scaffId; - } + glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - position += ((f32)cont->length / (f32)Number_of_Pixels_1D); - } + f32 x = -0.5f; + ForLoop(Number_of_Textures_1D - 1) + { + position += spacing; + f32 px = x + lineWidth; + x = position - (0.5f * (lineWidth + 1.0f)); - if (scaffId) + if (x > px) { - vert[0].x = ModelXToScreen(start - 0.5f); vert[0].y = ModelYToScreen(0.5f - start); - vert[1].x = ModelXToScreen(start - 0.5f); vert[1].y = ModelYToScreen(0.5f - position); - vert[2].x = ModelXToScreen(position - 0.5f); vert[2].y = ModelYToScreen(0.5f - position); - vert[3].x = ModelXToScreen(position - 0.5f); vert[3].y = ModelYToScreen(0.5f - start); - - ColourGenerator((u32)scaffId, (f32 *)barColour); - u32 colour = FourFloatColorToU32(*((nk_colorf *)barColour)); - - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); + vert[0].x = x; + vert[0].y = -0.5f; + vert[1].x = x + lineWidth; + vert[1].y = -0.5f; + vert[2].x = x + lineWidth; + vert[2].y = 0.5f; + vert[3].x = x; + vert[3].y = 0.5f; - glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); + glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); + glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - - glUseProgram(UI_Shader->shaderProgram); - fonsSetColor(FontStash_Context, colour); - - stbsp_snprintf(buff, sizeof(buff), "Scaffold %u", scaffId); - f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, buff, 0, NULL); - fonsDrawText(FontStash_Context, ModelXToScreen(0.5f * (position + start - 1.0f)) - (0.5f * textWidth), ModelYToScreen(0.5f - start) - lh, buff, 0); } + } - if (Scaff_Edit_Mode && !UI_On) - { - fonsSetSize(FontStash_Context, 24.0f * Screen_Scale.x); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Scaff_Mode_Data->text)); + vert[0].x = 0.5f - lineWidth; + vert[0].y = -0.5f; + vert[1].x = 0.5f; + vert[1].y = -0.5f; + vert[2].x = 0.5f; + vert[2].y = 0.5f; + vert[3].x = 0.5f - lineWidth; + vert[3].y = 0.5f; - f32 textBoxHeight = lh; - textBoxHeight *= 7.0f; - textBoxHeight += 6.0f; - f32 spacing = 10.0f; + glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - std::vector helpTexts = { - "Scaffold Edit Mode", - "S: exit", - "Left Click: place", - "Middle Click / Spacebar: delete", - "Shift-D: delete all", - "A (Hold): flood fill", - "Shift-A (Hold): flood fill and override" - }; + position = 0.0f; - f32 textWidth =0.f; - for (const auto& tmp:helpTexts) - textWidth = std::max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, tmp.c_str(), 0, NULL)); + vert[0].x = -0.5f; + vert[0].y = 0.5f - lineWidth; + vert[1].x = 0.5f; + vert[1].y = 0.5f - lineWidth; + vert[2].x = 0.5f; + vert[2].y = 0.5f; + vert[3].x = -0.5f; + vert[3].y = 0.5f; - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Scaff_Mode_Data->bg); + glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; - vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; - vert[2].x = width - spacing; vert[2].y = height - spacing; - vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; + f32 y = 0.5f; + ForLoop(Number_of_Textures_1D - 1) + { + position += spacing; + f32 py = y - lineWidth; + y = 1.0f - position + (0.5f * (lineWidth - 1.0f)); - glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); + if (y < py) + { + vert[0].x = -0.5f; + vert[0].y = y - lineWidth; + vert[1].x = 0.5f; + vert[1].y = y - lineWidth; + vert[2].x = 0.5f; + vert[2].y = y; + vert[3].x = -0.5f; + vert[3].y = y; + + glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Waypoint_Data->vaos[ptr++]); + glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - - glUseProgram(UI_Shader->shaderProgram); - for (u32 i=0; i< helpTexts.size() ; i++) - { - fonsDrawText( - FontStash_Context, - width - spacing - textWidth, - height - spacing - textBoxHeight + (lh + 1.0f) * i, - helpTexts[i].c_str(), - 0); - } } - } // scaff_mode + } - // draw the screen for select fragments for sorting - if (File_Loaded && !UI_On && Select_Sort_Area_Mode) - { - f32 lh = 0.f; - f32 start_fraction = (f32)auto_curation_state.get_start()/(f32)Number_of_Pixels_1D; - f32 end_fraction = (f32)auto_curation_state.get_end() /(f32)Number_of_Pixels_1D; + vert[0].x = -0.5f; + vert[0].y = -0.5f; + vert[1].x = 0.5f; + vert[1].y = -0.5f; + vert[2].x = 0.5f; + vert[2].y = lineWidth - 0.5f; + vert[3].x = -0.5f; + vert[3].y = lineWidth - 0.5f; + glBindBuffer(GL_ARRAY_BUFFER, Texture_Tile_Grid->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Texture_Tile_Grid->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } - { // draw the help text in the bottom right corner - fonsSetFont(FontStash_Context, Font_Bold); - fonsSetSize(FontStash_Context, 24.0f * Screen_Scale.x); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Scaff_Mode_Data->text)); + // Quad Trees + //if (Waypoint_Edit_Mode) + { + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&QuadTrees->bg); - std::vector helpTexts = { - "Select/Exclude area for sorting", - "F: exit", - "S: clear select area", - "C/Z: cut / cancel cut", - "Q/W: quit / redo edit", - "Space: Pixel sort", - "Left Click: un/select the fragment", - fmt::format("Up/Down: inc/dec Cut threshold: {:.4f}", auto_curation_state.auto_cut_threshold), - fmt::format("Left/Right: change Sort Mode ({})", auto_curation_state.sort_mode_names[auto_curation_state.sort_mode]), - fmt::format("Left/Right Shift: Number of Clusters: {}", auto_curation_state.num_clusters), - fmt::format("H: Hap_Name_Clustering: {}", auto_curation_state.hap_cluster_flag ? "ON" : "OFF"), - fmt::format("K: Cut with gap: {}", auto_curation_state.auto_cut_with_extension? "ON" : "OFF"), - }; + u32 ptr = 0; + vertex vert[4]; - f32 textBoxHeight = (f32)helpTexts.size() * (lh + 1.0f) - 1.0f; - f32 spacing = 10.0f; - - f32 textWidth = 0.f; - for (const auto& i :helpTexts){ - textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i.c_str(), 0, NULL)); - } - textWidth = my_Min(textWidth, width - 2 * spacing); + f32 lineWidth = 0.001f / sqrtf(Camera_Position.z); - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Scaff_Mode_Data->bg); - point2f vert[4]; - vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; - vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; - vert[2].x = width - spacing; vert[2].y = height - spacing; - vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; - - u32 ptr = 0; - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + DrawQuadTreeLevel(&ptr, Waypoint_Editor->quadtree, vert, lineWidth); + } + #endif + + // Extensions + u08 graphNamesOn = 0; + { + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) + { + case extension_graph: + { + graph *gph = (graph *)node->extension; - glUseProgram(UI_Shader->shaderProgram); + if (gph->on) + { + if (gph->nameOn) graphNamesOn = 1; - for (int i = 0; i < helpTexts.size(); i++) - { - fonsDrawText( - FontStash_Context, - width - spacing - textWidth, - height - spacing - textBoxHeight + (i * (lh + 1.0f)), - helpTexts[i].c_str(), - 0); - } - } + f32 factor1 = 1.0f / (2.0f * Camera_Position.z); + f32 factor2 = 2.0f / height; - { // paint the selected area - if (start_fraction >= 0 && end_fraction >= 0 ) - { - u32 ptr = 0; - point2f vert[4]; + f32 wy = (factor1 * (1.0f - (factor2 * (height - gph->base)))) + Camera_Position.y; - // draw the start & end point - { - f32 line_width = 0.002f / Camera_Position.z; - f32 mask_color[4] = {1.0f, 0.f, 0.f, 0.8f}; // red - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&mask_color); - for (auto loc_fraction : {start_fraction, end_fraction}) - { - vert[0].x = ModelXToScreen(loc_fraction - 0.5f - line_width); vert[0].y = ModelYToScreen(0.5f - loc_fraction + line_width); - vert[1].x = ModelXToScreen(loc_fraction - 0.5f - line_width); vert[1].y = ModelYToScreen(0.5f - loc_fraction - line_width); - vert[2].x = ModelXToScreen(loc_fraction - 0.5f + line_width); vert[2].y = ModelYToScreen(0.5f - loc_fraction - line_width); - vert[3].x = ModelXToScreen(loc_fraction - 0.5f + line_width); vert[3].y = ModelYToScreen(0.5f - loc_fraction + line_width); + glUseProgram(gph->shader->shaderProgram); + glUniform4fv(gph->shader->colorLocation, 1, (GLfloat *)&gph->colour); + glUniform1f(gph->shader->yScaleLocation, gph->scale); + glUniform1f(gph->shader->yTopLocation, wy); + glUniform1f(gph->shader->lineSizeLocation, gph->lineSize / Camera_Position.z); - glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + glBindBuffer(GL_ARRAY_BUFFER, gph->vbo); + glBindVertexArray(gph->vao); + glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)Number_of_Pixels_1D); } } + break; + } + } + } - // draw the cover on selected area - { - // f32 mask_color[4] = {0.906f, 0.03921f, 0.949f, 0.5f}; - vert[0].x = ModelXToScreen(start_fraction - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_fraction); - vert[1].x = ModelXToScreen(start_fraction - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_fraction); - vert[2].x = ModelXToScreen(end_fraction - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_fraction); - vert[3].x = ModelXToScreen(end_fraction - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_fraction); - - f32 font_color[4] = {0.f, 0.f, 0.f, 1.f}; - u32 colour = FourFloatColorToU32(*((nk_colorf *)font_color)); + // Grid + if (File_Loaded && Grid->on) + { + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Grid->bg); - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&auto_curation_state.mask_color); + u32 ptr = 0; + vertex vert[4]; - glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + f32 lineWidth = Grid->size / sqrtf(Camera_Position.z); + f32 position = 0.0f; - glUseProgram(UI_Shader->shaderProgram); - fonsSetColor(FontStash_Context, colour); - - // selected frags into a string - s32 selected_fragment_size = 1, last_contig = Map_State->contigIds[auto_curation_state.get_start()]; - for (s32 i = auto_curation_state.get_start()+1; i < auto_curation_state.get_end(); i++) - { - if (last_contig!=Map_State->contigIds[i]) - { - selected_fragment_size ++ ; - last_contig = Map_State->contigIds[i]; - } - } - std::string buff = fmt::format( - "{}: ({}) pixels, ({}) fragments", - auto_curation_state.selected_or_exclude==0?"Selected" :"Excluded", - std::abs(auto_curation_state.get_end() - auto_curation_state.get_start()), - selected_fragment_size); + // left border + vert[0].x = -0.5f; vert[0].y = -0.5f; + vert[1].x = lineWidth - 0.5f; vert[1].y = -0.5f; + vert[2].x = lineWidth - 0.5f; vert[2].y = 0.5f; + vert[3].x = -0.5f; vert[3].y = 0.5f; - f32 lh = 0.0f; - f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, buff.c_str(), 0, NULL); - fonsDrawText( - FontStash_Context, - ModelXToScreen( 0.5f * (end_fraction + start_fraction ) - 0.5f) - (0.5f * textWidth), - ModelYToScreen(0.5f - 0.5f*(start_fraction + end_fraction)) - lh * 0.5f, buff.c_str(), 0); - } - } - } - } // select_sort_area + glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Grid_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - // Meta Tags - if (File_Loaded && (MetaData_Edit_Mode || MetaData_Always_Visible)) + f32 x = -0.5f; + ForLoop(Contigs->numberOfContigs - 1) { - glUseProgram(Flat_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - - glViewport(0, 0, (s32)width, (s32)height); + contig *cont = Contigs->contigs_arr + index; - f32 colour[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - vertex vert[4]; - u32 ptr = 0; - f32 barColour[4] = {1.0f, 1.0f, 1.0f, 0.5f}; + position += ((f32)cont->length / (f32)Number_of_Pixels_1D); + f32 px = x + lineWidth; + x = position - (0.5f * (lineWidth + 1.0f)); - f32 lh = 0.0f; - fonsClearState(FontStash_Context); -#define DefaultMetaDataSize 20.0f - fonsSetSize(FontStash_Context, MetaData_Mode_Data->size * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_MIDDLE | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Bold); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); + if (x > px) + { + // contig vertical line + vert[0].x = x; vert[0].y = -0.5f; + vert[1].x = x + lineWidth; vert[1].y = -0.5f; + vert[2].x = x + lineWidth; vert[2].y = 0.5f; + vert[3].x = x; vert[3].y = 0.5f; - f32 end_contig = 0.0f, start_contig = 0.0f; - u32 scaffId = Contigs->contigs_arr->scaffId; - ForLoop(Contigs->numberOfContigs) - { - contig *cont = Contigs->contigs_arr + index; - end_contig += ((f32)cont->length / (f32)Number_of_Pixels_1D); // end of the contig - if (*cont->metaDataFlags) - { - u32 tmp = 0; // used to count the number of tags drawn - bool haplotigTagged = false; - ForLoop2(ArrayCount(Meta_Data->tags)) - { - if (*cont->metaDataFlags & ((u64)1 << index2)) - { - f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, (char *)Meta_Data->tags[index2], 0, NULL); - ColourGenerator(index2 + 1, colour); - // fonsSetColor(FontStash_Context, FourFloatColorToU32(*((nk_colorf *)colour))); - // fonsDrawText(FontStash_Context, ModelXToScreen(0.5f * (end_contig + start_contig - 1.0f)) - (0.5f * textWidth), ModelYToScreen((0.5f * (1.0f - end_contig - start_contig))) - (lh * (f32)(++tmp)), (char *)Meta_Data->tags[index2], 0); - if (meta_outline->on) - { - // position of text - f32 textX = ModelXToScreen(0.5f * (end_contig + start_contig - 1.0f)) - (0.5f * textWidth); - f32 textY = ModelYToScreen((0.5f * (1.0f - end_contig - start_contig))) - (lh * (f32)(++tmp)); + glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Grid_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } + } + // right border + vert[0].x = 0.5f - lineWidth; vert[0].y = -0.5f; + vert[1].x = 0.5f; vert[1].y = -0.5f; + vert[2].x = 0.5f; vert[2].y = 0.5f; + vert[3].x = 0.5f - lineWidth; vert[3].y = 0.5f; - DrawOutlinedText(FontStash_Context, (nk_colorf *)colour, (char *)Meta_Data->tags[index2], textX, textY); - } - else - { - fonsSetColor(FontStash_Context, FourFloatColorToU32(*((nk_colorf *)colour))); - fonsDrawText( - FontStash_Context, - ModelXToScreen(0.5f * (end_contig + start_contig - 1.0f)) - (0.5f * textWidth), - ModelYToScreen((0.5f * (1.0f - end_contig - start_contig))) - (lh * (f32)(++tmp)), - (char *)Meta_Data->tags[index2], - 0); - } + glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Grid_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - // Check if the tag is "Haplotig" - if (strcmp((char *)Meta_Data->tags[index2], "Haplotig") == 0) - { - haplotigTagged = true; - } - } - } + position = 0.0f; + // top border + vert[0].x = -0.5f; vert[0].y = 0.5f - lineWidth; + vert[1].x = 0.5f; vert[1].y = 0.5f - lineWidth; + vert[2].x = 0.5f; vert[2].y = 0.5f; + vert[3].x = -0.5f; vert[3].y = 0.5f; - // draw the grey out mask - std::string grey_out_tag = Grey_Out_Settings->is_grey_out(cont->metaDataFlags, Meta_Data); - if (!grey_out_tag.empty()) - { - vert[0].x = ModelXToScreen(start_contig - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_contig); - vert[1].x = ModelXToScreen(start_contig - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_contig); - vert[2].x = ModelXToScreen(end_contig - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_contig); - vert[3].x = ModelXToScreen(end_contig - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_contig); + glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Grid_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - ColourGenerator((u32)scaffId, (f32 *)barColour); - u32 colour = FourFloatColorToU32(*((nk_colorf *)barColour)); + f32 y = 0.5f; + ForLoop(Contigs->numberOfContigs - 1) + { + contig *cont = Contigs->contigs_arr + index; - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); + position += ((f32)cont->length / (f32)Number_of_Pixels_1D); + f32 py = y - lineWidth; + y = 1.0f - position + (0.5f * (lineWidth - 1.0f)); - glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + if (y < py) + { + // contig horizontal line + vert[0].x = -0.5f; vert[0].y = y - lineWidth; + vert[1].x = 0.5f; vert[1].y = y - lineWidth; + vert[2].x = 0.5f; vert[2].y = y; + vert[3].x = -0.5f; vert[3].y = y; - glUseProgram(UI_Shader->shaderProgram); - fonsSetColor(FontStash_Context, colour); - } - } - start_contig = end_contig; - scaffId = cont->scaffId; + glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Grid_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); } + } + // bottom border + vert[0].x = -0.5f; vert[0].y = -0.5f; + vert[1].x = 0.5f; vert[1].y = -0.5f; + vert[2].x = 0.5f; vert[2].y = lineWidth - 0.5f; + vert[3].x = -0.5f; vert[3].y = lineWidth - 0.5f; - if (MetaData_Edit_Mode && !UI_On) - { - u32 ptr = 0; - vertex vert[4]; + glBindBuffer(GL_ARRAY_BUFFER, Grid_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Grid_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } - fonsSetSize(FontStash_Context, 24.0f * Screen_Scale.x); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(MetaData_Mode_Data->text)); + // Contig Id Bars + if (File_Loaded && Contig_Ids->on) + { + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Grid->bg); - f32 textBoxHeight = lh; - textBoxHeight *= 7.0f; - textBoxHeight += 6.0f; - f32 spacing = 10.0f; + u32 ptr = 0; + vertex vert[4]; - char helpText7[128]; - const char *activeTag = (const char *)Meta_Data->tags[MetaData_Active_Tag]; - stbsp_snprintf(helpText7, sizeof(helpText7), "Active Tag: %s", strlen(activeTag) ? activeTag : ""); + f32 barColour[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - std::vector helpTexts = { - "MetaData Tag Mode", - "M: exit", - "Left Click: place", - "Middle Click / Spacebar: delete", - "Shift-D: delete all", - "Arrow Keys: select active tag", - helpText7 - }; + f32 lineWidth = Contig_Ids->size / sqrtf(Camera_Position.z); + f32 position = 0.0f; - f32 textWidth = 0.f; - for (const auto& tmp:helpTexts) - textWidth = std::max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, tmp.c_str(), 0, NULL)); + f32 y = 0.5f; + ForLoop(Contigs->numberOfContigs) + { + contig *cont = Contigs->contigs_arr + index; - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&MetaData_Mode_Data->bg); + position += ((f32)cont->length / (f32)Number_of_Pixels_1D); + f32 py = y - lineWidth; + y = 1.0f - position + (0.5f * (lineWidth - 1.0f)); - vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; - vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; - vert[2].x = width - spacing; vert[2].y = height - spacing; - vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; + if (y < py) + { + u32 invert = IsContigInverted(index); - glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); + vert[0].x = -py; vert[0].y = invert ? y : (py + lineWidth); + vert[1].x = -py; vert[1].y = invert ? (y - lineWidth) : py; + vert[2].x = -y; vert[2].y = invert ? (y - lineWidth) : py; + vert[3].x = -y; vert[3].y = invert ? y : (py + lineWidth); + + ColourGenerator((u32)cont->originalContigId, (f32 *)barColour); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); + + glBindBuffer(GL_ARRAY_BUFFER, Contig_ColourBar_Data->vbos[ptr]); glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Waypoint_Data->vaos[ptr++]); + glBindVertexArray(Contig_ColourBar_Data->vaos[ptr++]); glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } + } + } - glUseProgram(UI_Shader->shaderProgram); - for (u32 i=0; i< helpTexts.size() ; i++) { - fonsDrawText( - FontStash_Context, - width - spacing - textWidth, - height - spacing - textBoxHeight + (lh + 1.0f) * i, - helpTexts[i].c_str(), - 0); + // Text / UI Rendering + if (Contig_Name_Labels->on || Scale_Bars->on || Tool_Tip->on || UI_On || Loading || auto_sort_state || Edit_Mode || Waypoint_Edit_Mode || Waypoints_Always_Visible || Scaff_Edit_Mode || Scaffs_Always_Visible || MetaData_Edit_Mode || MetaData_Always_Visible || graphNamesOn) + { + f32 textNormalMat[16]; + f32 textRotMat[16]; + + f32 w2 = width * 0.5f; + f32 h2 = height * 0.5f; + f32 hz = height * Camera_Position.z; + + // coverting model coords (world space) to screen coords + auto ModelXToScreen = [hz, w2](f32 xin)->f32 { + return (xin - Camera_Position.x) * hz + w2; }; + auto ModelYToScreen = [hz, h2](f32 yin)->f32 { + return h2 - (yin - Camera_Position.y) * hz; }; + + // Text Projection Matrix + { + memset(textNormalMat, 0, sizeof(textNormalMat)); + textNormalMat[0] = 2.0f / width; + textNormalMat[5] = -2.0f / height; + textNormalMat[10] = -1.0f; + textNormalMat[12] = -1.0f; + textNormalMat[13] = 1.0f; + textNormalMat[15] = 1.0f; + + memset(textRotMat, 0, sizeof(textRotMat)); + textRotMat[4] = 2.0f / width; + textRotMat[1] = 2.0f / height; + textRotMat[10] = -1.0f; + textRotMat[12] = -1.0f; + textRotMat[13] = 1.0f; + textRotMat[15] = 1.0f; + } + + // Extension Labels + if (graphNamesOn) + { + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, 32.0f * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Bold); + + f32 x = ModelXToScreen(-0.5f); + x = my_Max(x, 0.0f) + 10.0f; + + f32 h = height + 1.0f; + + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) + { + case extension_graph: + { + graph *gph = (graph *)node->extension; + + if (gph->on && gph->nameOn) + { + fonsSetColor(FontStash_Context, ThreeFloatColorToU32(gph->colour)); + fonsDrawText(FontStash_Context, x, h - gph->base, (const char *)gph->name, 0); + } + } + break; } } } - - // Tool Tip - if (File_Loaded && Tool_Tip->on && !Edit_Mode && !UI_On) + + if (File_Loaded && Contig_Name_Labels->on) // Contig Labels { glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Tool_Tip->bg); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Contig_Name_Labels->bg); glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); glUseProgram(UI_Shader->shaderProgram); glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + u32 ptr = 0; vertex vert[4]; glViewport(0, 0, (s32)width, (s32)height); @@ -3863,133 +3738,231 @@ Render() { f32 lh = 0.0f; fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, Tool_Tip->size * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Normal); + fonsSetSize(FontStash_Context, Contig_Name_Labels->size * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Bold); fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Tool_Tip->fg)); - - // Extension info, extra lines - u32 nExtra = 0; - f32 longestExtraLineLength = 0.0f; + fonsSetColor(FontStash_Context, FourFloatColorToU32(Contig_Name_Labels->fg)); + + f32 leftPixel = ModelXToScreen(-0.5f); + f32 totalLength = 0.0f; + f32 wy0 = ModelYToScreen(0.5f); + + ForLoop(Contigs->numberOfContigs) { - if (Extensions.head) + contig *cont = Contigs->contigs_arr + index; + + totalLength += (f32)((f64)cont->length / (f64)Number_of_Pixels_1D); + + f32 rightPixel = ModelXToScreen(totalLength - 0.5f); + + if (rightPixel > 0.0f && leftPixel < width) { - char buff[128]; - TraverseLinkedList(Extensions.head, extension_node) + const char *name = (const char *)(Original_Contigs + cont->get_original_contig_id())->name; + f32 x = (rightPixel + leftPixel) * 0.5f; + f32 y = my_Max(wy0, 0.0f) + 10.0f; + f32 textWidth = fonsTextBounds(FontStash_Context, x, y, name, 0, NULL); + + if (textWidth < (rightPixel - leftPixel)) { - switch (node->type) - { - case extension_graph: - { - graph *gph = (graph *)node->extension; - if (gph->on) - { - glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); - u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, Tool_Tip_Move.pixels.x * sizeof(u32), sizeof(u32), GL_MAP_READ_BIT); + f32 w2t = 0.5f * textWidth; - stbsp_snprintf(buff, sizeof(buff), "%s: %$d", (char *)gph->name, gph->data[*buffer]); - ++nExtra; - longestExtraLineLength = my_Max(longestExtraLineLength, fonsTextBounds(FontStash_Context, 0, 0, buff, 0, NULL)); + glUseProgram(Flat_Shader->shaderProgram); - glUnmapBuffer(GL_TEXTURE_BUFFER); - glBindBuffer(GL_TEXTURE_BUFFER, 0); - } - } - break; - } + vert[0].x = x - w2t; vert[0].y = y + lh; + vert[1].x = x + w2t; vert[1].y = y + lh; + vert[2].x = x + w2t; vert[2].y = y; + vert[3].x = x - w2t; vert[3].y = y; + + glBindBuffer(GL_ARRAY_BUFFER, Label_Box_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Label_Box_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + glUseProgram(UI_Shader->shaderProgram); + fonsDrawText(FontStash_Context, x, y, name, 0); } } - } - f32 textBoxHeight = lh; - textBoxHeight *= (3.0f + (f32)nExtra); - textBoxHeight += (2.0f + (f32)nExtra); + leftPixel = rightPixel; + } - u32 id1 = Map_State->get_original_contig_id(Tool_Tip_Move.pixels.x); - u32 id2 = Map_State->get_original_contig_id(Tool_Tip_Move.pixels.y); - u32 coord1 = Map_State->contigRelCoords[Tool_Tip_Move.pixels.x]; - u32 coord2 = Map_State->contigRelCoords[Tool_Tip_Move.pixels.y]; - - f64 bpPerPixel = (f64)Total_Genome_Length / (f64)Number_of_Pixels_1D; + glUseProgram(Flat_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textRotMat); - char line1[64]; - char *line2 = (char *)"vs"; - char line3[64]; + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textRotMat); - auto NicePrint = [bpPerPixel](u32 id, u32 coord, char *buffer) + f32 topPixel = ModelYToScreen(0.5f); + f32 wx0 = ModelXToScreen(-0.5f); + totalLength = 0.0f; + + ForLoop(Contigs->numberOfContigs) { - f64 pos = (f64)coord * bpPerPixel; - stbsp_snprintf(buffer, 64, "%s %'u %sbp", (Original_Contigs + id)->name, (u32)(pos / (pos > 1000.0 ? 1000.0 : 1.0)), pos > 1000.0 ? "K" : ""); - }; + contig *cont = Contigs->contigs_arr + index; + + totalLength += (f32)((f64)cont->length / (f64)Number_of_Pixels_1D); - NicePrint(id1, coord1, line1); - NicePrint(id2, coord2, line3); + f32 bottomPixel = ModelYToScreen(0.5f - totalLength); - f32 textWidth_1 = fonsTextBounds(FontStash_Context, 0, 0, line1, 0, NULL); - f32 textWidth_2 = fonsTextBounds(FontStash_Context, 0, 0, line2, 0, NULL); - f32 textWidth_3 = fonsTextBounds(FontStash_Context, 0, 0, line3, 0, NULL); - f32 textWidth = my_Max(textWidth_1, textWidth_2); - textWidth = my_Max(textWidth, textWidth_3); - textWidth = my_Max(textWidth, longestExtraLineLength); + if (topPixel < height && bottomPixel > 0.0f) + { + const char *name = (const char *)(Original_Contigs + cont->get_original_contig_id())->name; + f32 y = (topPixel + bottomPixel) * 0.5f; + f32 x = my_Max(wx0, 0.0f) + 10.0f; + f32 textWidth = fonsTextBounds(FontStash_Context, x, y, name, 0, NULL); - f32 spacing = 12.0f; + if (textWidth < (bottomPixel - topPixel)) + { + f32 tmp = x; + x = -y; + y = tmp; - glUseProgram(Flat_Shader->shaderProgram); + f32 w2t = 0.5f * textWidth; - vert[0].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing; vert[0].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing; - vert[1].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing; vert[1].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + textBoxHeight; - vert[2].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing + textWidth; vert[2].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + textBoxHeight; - vert[3].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing + textWidth; vert[3].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing; + glUseProgram(Flat_Shader->shaderProgram); - glBindBuffer(GL_ARRAY_BUFFER, Tool_Tip_Data->vbos[0]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Tool_Tip_Data->vaos[0]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + vert[0].x = x - w2t; vert[0].y = y + lh; + vert[1].x = x + w2t; vert[1].y = y + lh; + vert[2].x = x + w2t; vert[2].y = y; + vert[3].x = x - w2t; vert[3].y = y; + + glBindBuffer(GL_ARRAY_BUFFER, Label_Box_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Label_Box_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + glUseProgram(UI_Shader->shaderProgram); + fonsDrawText(FontStash_Context, x, y, name, 0); + } + } + + topPixel = bottomPixel; + } + + ChangeSize((s32)width, (s32)height); + } + #define MaxTicksPerScaleBar 128 + if (File_Loaded && Scale_Bars->on) // Scale bars + { + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Scale_Bars->bg); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glUseProgram(UI_Shader->shaderProgram); - fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, - ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing, line1, 0); - fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, - ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + lh + 1.0f, line2, 0); - fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, - ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + (2.0f * lh) + 2.0f, line3, 0); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + + u32 ptr = 0; + vertex vert[4]; + + glViewport(0, 0, (s32)width, (s32)height); + + f32 lh = 0.0f; + + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, Scale_Bars->size * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Normal); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(Scale_Bars->fg)); + + f32 leftPixel = ModelXToScreen(-0.5f); + f32 rightPixel = ModelXToScreen(0.5f); + f32 wy0 = ModelYToScreen(0.5); + f32 offset = 45.0f * Screen_Scale.x; + f32 y = my_Max(wy0, 0.0f) + offset; + f32 totalLength = 0.0f; + + f32 bpPerPixel = (f32)((f64)Total_Genome_Length / (f64)(rightPixel - leftPixel)); + + GLfloat *bg = (GLfloat *)&Scale_Bars->bg; + + f32 scaleBarWidth = Scale_Bars->size * 4.0f / 20.0f * Screen_Scale.x; + f32 tickLength = Scale_Bars->size * 3.0f / 20.0f * Screen_Scale.x; + ForLoop(Contigs->numberOfContigs) { - if (Extensions.head) + contig *cont = Contigs->contigs_arr + index; + + totalLength += (f32)((f64)cont->length / (f64)Number_of_Pixels_1D); + rightPixel = ModelXToScreen(totalLength - 0.5f); + + f32 pixelLength = rightPixel - leftPixel; + f32 startCoord = (f32)((f64)(IsContigInverted(index) ? (cont->startCoord - cont->length) : cont->startCoord) * (f64)Total_Genome_Length / (f64)Number_of_Pixels_1D); + + u32 labelLevels = SubDivideScaleBar(leftPixel, rightPixel, (leftPixel + rightPixel) * 0.5f, bpPerPixel, startCoord); + u32 labels = 0; + ForLoop2(labelLevels) { - u32 count = 0; - char buff[128]; - TraverseLinkedList(Extensions.head, extension_node) + labels += (labels + 1); + } + labels = my_Min(labels, MaxTicksPerScaleBar); + + if (rightPixel > 0.0f && leftPixel < width) + { + if (labels) { - switch (node->type) + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, bg); + + vert[0].x = leftPixel + 1.0f; vert[0].y = y + scaleBarWidth + tickLength + 1.0f + lh; + vert[1].x = rightPixel - 1.0f; vert[1].y = y + scaleBarWidth + tickLength + 1.0f + lh; + vert[2].x = rightPixel - 1.0f; vert[2].y = y; + vert[3].x = leftPixel + 1.0f; vert[3].y = y; + + glBindBuffer(GL_ARRAY_BUFFER, Scale_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scale_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Scale_Bars->fg); + + vert[0].x = leftPixel + 1.0f; vert[0].y = y + scaleBarWidth; + vert[1].x = rightPixel - 1.0f; vert[1].y = y + scaleBarWidth; + vert[2].x = rightPixel - 1.0f; vert[2].y = y; + vert[3].x = leftPixel + 1.0f; vert[3].y = y; + + glBindBuffer(GL_ARRAY_BUFFER, Scale_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scale_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + f32 fraction = 1.0f / (f32)(labels + 1); + f32 distance = 0.0f; + ForLoop2(labels) { - case extension_graph: - { - graph *gph = (graph *)node->extension; - if (gph->on) - { - glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); - u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, Tool_Tip_Move.pixels.x * sizeof(u32), sizeof(u32), GL_MAP_READ_BIT); + distance += fraction; + f32 x = (pixelLength * distance) + (f32)leftPixel; - stbsp_snprintf(buff, sizeof(buff), "%s: %$d", (char *)gph->name, gph->data[*buffer]); + glUseProgram(Flat_Shader->shaderProgram); - fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, - ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + ((2.0f + (f32)(++count)) * (lh + 1.0f)), buff, 0); + vert[0].x = x - (0.5f * scaleBarWidth); vert[0].y = y + scaleBarWidth + tickLength; + vert[1].x = x + (0.5f * scaleBarWidth); vert[1].y = y + scaleBarWidth + tickLength; + vert[2].x = x + (0.5f * scaleBarWidth); vert[2].y = y + scaleBarWidth; + vert[3].x = x - (0.5f * scaleBarWidth); vert[3].y = y + scaleBarWidth; - glUnmapBuffer(GL_TEXTURE_BUFFER); - glBindBuffer(GL_TEXTURE_BUFFER, 0); - } - } - break; + glBindBuffer(GL_ARRAY_BUFFER, Scale_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scale_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + char buff[16]; + stbsp_snprintf(buff, 16, "%$.2f", (f64)(startCoord + (pixelLength * (IsContigInverted(index) ? (1.0f - distance) : distance) * bpPerPixel))); + glUseProgram(UI_Shader->shaderProgram); + fonsDrawText(FontStash_Context, x, y + scaleBarWidth + tickLength + 1.0f, buff, 0); } } } + + leftPixel = rightPixel; } - } - // Edit Mode - if (File_Loaded && Edit_Mode && !UI_On) // Edit Mode + ChangeSize((s32)width, (s32)height); + } + + // Finished - add the cross line to the waypoint + if (File_Loaded && (Waypoint_Edit_Mode || Waypoints_Always_Visible)) // Waypoint Edit Mode { u32 ptr = 0; vertex vert[4]; @@ -3999,4738 +3972,4939 @@ Render() { glUseProgram(UI_Shader->shaderProgram); glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glUseProgram(Flat_Shader->shaderProgram); glViewport(0, 0, (s32)width, (s32)height); +#define DefaultWaypointSize 18.0f + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->base); - f32 color[4]; - f32* source; - if (Edit_Pixels.editing) // edit color - { - if (Global_Edit_Invert_Flag) source = (f32*)&Edit_Mode_Colours->invSelect; - else source = (f32*)&Edit_Mode_Colours->select; - } - else source = (f32*)&Edit_Mode_Colours->preSelect; // pre-edit color - ForLoop(4) - { - color[index] = source[index]; - } - f32 alpha = color[3]; - color[3] = 1.0f; + // define the vertical line + f32 lineWidth = Waypoint_Mode_Data->size / DefaultWaypointSize * 0.7f * Screen_Scale.x; + f32 lineHeight = Waypoint_Mode_Data->size / DefaultWaypointSize * 8.0f * Screen_Scale.x; - glUniform4fv(Flat_Shader->colorLocation, 1, color); + f32 lh = 0.0f; - f32 lineWidth = 0.005f / Camera_Position.z; + u32 baseColour = FourFloatColorToU32(Waypoint_Mode_Data->base); + u32 selectColour = FourFloatColorToU32(Waypoint_Mode_Data->selected); - { // draw the two squared dots at the beginning and end of the selected fragment - vert[0].x = ModelXToScreen(Edit_Pixels.worldCoords.x - lineWidth); - vert[0].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.x); - vert[1].x = ModelXToScreen(Edit_Pixels.worldCoords.x - lineWidth); - vert[1].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.x); - vert[2].x = ModelXToScreen(Edit_Pixels.worldCoords.x + lineWidth); - vert[2].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.x); - vert[3].x = ModelXToScreen(Edit_Pixels.worldCoords.x + lineWidth); - vert[3].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.x); - - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - - vert[0].x = ModelXToScreen(Edit_Pixels.worldCoords.y - lineWidth); - vert[0].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.y); - vert[1].x = ModelXToScreen(Edit_Pixels.worldCoords.y - lineWidth); - vert[1].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.y); - vert[2].x = ModelXToScreen(Edit_Pixels.worldCoords.y + lineWidth); - vert[2].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.y); - vert[3].x = ModelXToScreen(Edit_Pixels.worldCoords.y + lineWidth); - vert[3].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.y); + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, Waypoint_Mode_Data->size * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Bold); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, baseColour); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } + char buff[4]; - f32 min = my_Min(Edit_Pixels.worldCoords.x, Edit_Pixels.worldCoords.y); - f32 max = my_Max(Edit_Pixels.worldCoords.x, Edit_Pixels.worldCoords.y); - - if (Global_Edit_Invert_Flag) // draw the two arrows + // render the waypoints lines + TraverseLinkedList(Waypoint_Editor->activeWaypoints.next, waypoint) { - f32 spacing = 0.002f / Camera_Position.z; - f32 arrowWidth = 0.01f / Camera_Position.z; - f32 arrowHeight = arrowWidth * 0.65f; - f32 recHeight = arrowHeight * 0.65f; + point2f screen = {ModelXToScreen(node->coords.x), ModelYToScreen(-node->coords.y)}; + point2f screenYRange = {ModelYToScreen(0.5f), ModelYToScreen(-0.5f)}; + point2f screenXRange = {ModelXToScreen(-0.5f), ModelXToScreen(0.5f)}; - // draw the the triangle part - vert[0].x = ModelXToScreen(min + spacing); vert[0].y = ModelYToScreen(arrowHeight + spacing - min); - vert[1].x = ModelXToScreen(min + spacing + arrowWidth); vert[1].y = ModelYToScreen(spacing - min); - vert[2].x = ModelXToScreen(min + spacing + arrowWidth); vert[2].y = ModelYToScreen(arrowHeight + spacing - min); - vert[3].x = ModelXToScreen(min + spacing + arrowWidth); vert[3].y = ModelYToScreen((2.0f * arrowHeight) + spacing - min); + glUseProgram(Flat_Shader->shaderProgram); + if (node == Selected_Waypoint) + { + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->selected); + } - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + bool long_horizontal, long_vertical; + if (Long_Waypoints_Mode == 0) { // vertical line + long_horizontal = false; + long_vertical = true; + } else if (Long_Waypoints_Mode == 1) { // horizontal line + long_horizontal = true; + long_vertical = false; + } else { // cross line + long_horizontal = true; + long_vertical = true; + } + + // draw the vertical line + vert[0].x = screen.x - lineWidth; vert[0].y = long_vertical ? screenYRange.x : screen.y - lineHeight; + vert[1].x = screen.x - lineWidth; vert[1].y = long_vertical ? screenYRange.y : screen.y + lineHeight; + vert[2].x = screen.x + lineWidth; vert[2].y = long_vertical ? screenYRange.y : screen.y + lineHeight; + vert[3].x = screen.x + lineWidth; vert[3].y = long_vertical ? screenYRange.x : screen.y - lineHeight; + glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glBindVertexArray(Waypoint_Data->vaos[ptr++]); glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - // draw the rectangle part - vert[0].x = ModelXToScreen(min + spacing + arrowWidth); vert[0].y = ModelYToScreen((arrowHeight + 0.5f * recHeight) + spacing - min); - vert[1].x = ModelXToScreen(min + spacing + arrowWidth); vert[1].y = ModelYToScreen((arrowHeight - 0.5f * recHeight) + spacing - min); - vert[2].x = ModelXToScreen(max - spacing); vert[2].y = ModelYToScreen((arrowHeight - 0.5f * recHeight) + spacing - min); - vert[3].x = ModelXToScreen(max - spacing); vert[3].y = ModelYToScreen((arrowHeight + 0.5f * recHeight) + spacing - min); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + // draw the horizontal line (originally: the left part, not sure why Ed draw them seperately, if problem arises we can returen here) maybe avoid drawing the cross part twice... + vert[0].x = long_horizontal ? screenXRange.x : screen.x - lineHeight; vert[0].y = screen.y - lineWidth; + vert[1].x = long_horizontal ? screenXRange.x : screen.x - lineHeight; vert[1].y = screen.y + lineWidth; + vert[2].x = long_horizontal ? screenXRange.y : screen.x + lineHeight; vert[2].y = screen.y + lineWidth;// vert[2].x = screen.x - lineWidth; + vert[3].x = long_horizontal ? screenXRange.y : screen.x + lineHeight; vert[3].y = screen.y - lineWidth;// vert[3].x = screen.x - lineWidth; + glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glBindVertexArray(Waypoint_Data->vaos[ptr++]); glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } - { // draw the interacted area + // draw the horizontal line (originally: the right part, not sure why Ed draw them seperately, if problem arises we can returen here) + // vert[0].x = screen.x + lineWidth; + // vert[0].y = screen.y - lineWidth; + // vert[1].x = screen.x + lineWidth; + // vert[1].y = screen.y + lineWidth; + // vert[2].x = screen.x + lineHeight; + // vert[2].y = screen.y + lineWidth; + // vert[3].x = screen.x + lineHeight; + // vert[3].y = screen.y - lineWidth; + // glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); + // glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + // glBindVertexArray(Waypoint_Data->vaos[ptr++]); + // glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + if (node == Selected_Waypoint) + { + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->base); + } + + glUseProgram(UI_Shader->shaderProgram); + if (node == Selected_Waypoint) + { + fonsSetColor(FontStash_Context, selectColour); + } - color[3] = alpha; - glUniform4fv(Flat_Shader->colorLocation, 1, color); + stbsp_snprintf(buff, sizeof(buff), "%d", node->index + 1); + fonsDrawText(FontStash_Context, screen.x + lineWidth + lineWidth, screen.y - lineWidth - lh, buff, 0); - // upper vertical part - vert[0].x = ModelXToScreen(min); vert[0].y = ModelYToScreen(0.5f); - vert[1].x = ModelXToScreen(min); vert[1].y = ModelYToScreen(-min); - vert[2].x = ModelXToScreen(max); vert[2].y = ModelYToScreen(-min); - vert[3].x = ModelXToScreen(max); vert[3].y = ModelYToScreen(0.5f); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + if (node == Selected_Waypoint) + { + fonsSetColor(FontStash_Context, baseColour); + } + } - // left horizontal part - vert[0].x = ModelXToScreen(-0.5f); vert[0].y = ModelYToScreen(-min); - vert[1].x = ModelXToScreen(-0.5f); vert[1].y = ModelYToScreen(-max); - vert[2].x = ModelXToScreen(min); vert[2].y = ModelYToScreen(-max); - vert[3].x = ModelXToScreen(min); vert[3].y = ModelYToScreen(-min); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + if (Waypoint_Edit_Mode && !UI_On) + { + fonsSetSize(FontStash_Context, text_box_size.font_size * Screen_Scale.x); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(Waypoint_Mode_Data->text)); - // lower vertical part - vert[0].x = ModelXToScreen(min); vert[0].y = ModelYToScreen(-max); - vert[1].x = ModelXToScreen(min); vert[1].y = ModelYToScreen(-0.5f); - vert[2].x = ModelXToScreen(max); vert[2].y = ModelYToScreen(-0.5f); - vert[3].x = ModelXToScreen(max); vert[3].y = ModelYToScreen(-max); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + std::vector helpTexts = { + (char*)"Waypoint Edit Mode", + (char*)"W: exit", + (char*)"Left Click: place", + (char*)"Middle Click / Spacebar: delete", + }; + if (Long_Waypoints_Mode == 2) { + helpTexts.push_back((char*)"L: both directions"); + } else if (Long_Waypoints_Mode == 1) { + helpTexts.push_back((char*)"L: horizontal"); + } else { + helpTexts.push_back((char*)"L: vertical"); + } - // right horizontal part - vert[0].x = ModelXToScreen(max); vert[0].y = ModelYToScreen(-min); - vert[1].x = ModelXToScreen(max); vert[1].y = ModelYToScreen(-max); - vert[2].x = ModelXToScreen(0.5f); vert[2].y = ModelYToScreen(-max); - vert[3].x = ModelXToScreen(0.5f); vert[3].y = ModelYToScreen(-min); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + f32 textBoxHeight = lh; + textBoxHeight *= (f32)helpTexts.size(); + textBoxHeight += (f32)helpTexts.size() - 1.0f; + f32 spacing = 10.0f; // distance from the edge of the text box - // ceter part - vert[0].x = ModelXToScreen(min); vert[0].y = ModelYToScreen(-min); - vert[1].x = ModelXToScreen(min); vert[1].y = ModelYToScreen(-max); - vert[2].x = ModelXToScreen(max); vert[2].y = ModelYToScreen(-max); - vert[3].x = ModelXToScreen(max); vert[3].y = ModelYToScreen(-min); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + // f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, helpText4, 0, NULL); + f32 textWidth = 0; + for (auto i : helpTexts){ + textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i, 0, NULL)) + 0.5f * spacing; + } + + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->bg); + + vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; + vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; + vert[2].x = width - spacing; vert[2].y = height - spacing; + vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; + + glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glBindVertexArray(Waypoint_Data->vaos[ptr++]); glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - } - { // draw the text by the selected (pre-select) fragment - f32 lh = 0.0f; - - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, 18.0f * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); - fonsSetFont(FontStash_Context, Font_Normal); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); - fonsSetColor(FontStash_Context, FourFloatColorToU32(Edit_Mode_Colours->fg)); - - f32 textBoxHeight = lh; - textBoxHeight *= Edit_Pixels.editing ? 3.0f : 1.0f; - textBoxHeight += Edit_Pixels.editing ? 3.0f : 0.0f; + glUseProgram(UI_Shader->shaderProgram); - static char line1[64]; - static u32 line1Done = 0; - char line2[64]; + for (u32 i=0; i< helpTexts.size() ; i++) { + fonsDrawText( + FontStash_Context, + width - spacing - textWidth, + height - spacing - textBoxHeight + (lh + 1.0f) * i, + helpTexts[i], + 0); + } + } + } + + // Extension Mode + if (Extension_Mode && !UI_On) + { + u32 ptr = 0; + vertex vert[4]; + f32 lh = 0.0f; - char *midLineNoInv = (char *)"moved to"; - char *midLineInv = (char *)"inverted and moved to"; - char *midLine = Global_Edit_Invert_Flag ? midLineInv : midLineNoInv; + glUseProgram(Flat_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - u32 pix1 = my_Min(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); - u32 pix2 = my_Max(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); + glViewport(0, 0, (s32)width, (s32)height); - if (Edit_Pixels.editing && line1Done) - { - pix1 = pix1 ? pix1 - 1 : (pix2 < (Number_of_Pixels_1D - 1) ? pix2 + 1 : pix2); - } +#define DefaultExtensionSize 20.0f + // glUseProgram(Flat_Shader->shaderProgram); + // glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Waypoint_Mode_Data->base); - original_contig *cont = Original_Contigs + Map_State->get_original_contig_id(pix1); + // f32 lineWidth = Waypoint_Mode_Data->size / DefaultWaypointSize * 0.7f * Screen_Scale.x; + // f32 lineHeight = Waypoint_Mode_Data->size / DefaultWaypointSize * 8.0f * Screen_Scale.x; - u32 nPixels = Number_of_Pixels_1D; - f64 bpPerPixel = (f64)Total_Genome_Length / (f64)nPixels; + fonsSetSize(FontStash_Context, text_box_size.font_size * Screen_Scale.x); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(Extension_Mode_Data->text)); - f64 bpStart = bpPerPixel * (f64)Map_State->contigRelCoords[pix1]; - - if (Edit_Pixels.editing) - { - stbsp_snprintf(line2, 64, "%s[%$.2fbp]", cont->name, bpStart); - } - else if (line1Done) - { - line1Done = 0; - } - - if (!line1Done) + f32 textBoxHeight = lh; + textBoxHeight *= 7.0f; + textBoxHeight += 6.0f; + f32 spacing = 10.0f; + + // 6 lines in total + std::vector helpTexts = { + "Extensions:", + "X: exit" + }; + if (Extensions.head) + { + TraverseLinkedList(Extensions.head, extension_node) { - f64 bpEnd = bpPerPixel * (f64)Map_State->contigRelCoords[pix2]; - original_contig *cont2 = Original_Contigs + Map_State->get_original_contig_id(pix2); - stbsp_snprintf(line1, 64, "%s[%$.2fbp] to %s[%$.2fbp]", cont->name, bpStart, cont2->name, bpEnd); - if (Edit_Pixels.editing) + switch (node->type) { - line1Done = 1; + case extension_graph: + { + graph *gph = (graph *)node->extension; + + if (strstr((char*)gph->name, "coverage")) + { + helpTexts.push_back("C: Graph: coverage"); + } + else if (strstr((char*)gph->name, "gap")) + { + helpTexts.push_back("G: Graph: gap"); + } + else if (strstr((char*)gph->name, "repeat_density")) + { + helpTexts.push_back("R: Graph: repeat_density"); + } + else if (strstr((char*)gph->name, "telomere")) + { + helpTexts.push_back("T: Graph: telomere"); + } + } + break; } } + } - f32 textWidth_1 = fonsTextBounds(FontStash_Context, 0, 0, line1, 0, NULL); - f32 textWidth_2 = fonsTextBounds(FontStash_Context, 0, 0, line2, 0, NULL); - f32 textWidth_3 = fonsTextBounds(FontStash_Context, 0, 0, midLine, 0, NULL); - f32 textWidth = my_Max(textWidth_1, textWidth_2); - textWidth = my_Max(textWidth, textWidth_3); + f32 textWidth = 0.; + for (auto i : helpTexts) + { + textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i.c_str(), 0, NULL)) ; + } + textWidth += 0.5f * spacing; - f32 spacing = 3.0f; + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Extension_Mode_Data->bg); - glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Edit_Mode_Colours->bg); + vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; + vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; + vert[2].x = width - spacing; vert[2].y = height - spacing; + vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; - vert[0].x = ModelXToScreen(min) - spacing - textWidth; - vert[0].y = ModelYToScreen(-max) + spacing; - vert[1].x = ModelXToScreen(min) - spacing - textWidth; - vert[1].y = ModelYToScreen(-max) + spacing + textBoxHeight; - vert[2].x = ModelXToScreen(min) - spacing; - vert[2].y = ModelYToScreen(-max) + spacing + textBoxHeight; - vert[3].x = ModelXToScreen(min) - spacing; - vert[3].y = ModelYToScreen(-max) + spacing; + glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Waypoint_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + glUseProgram(UI_Shader->shaderProgram); + for (u32 i=0; i< helpTexts.size() ; i++) + { + fonsDrawText( + FontStash_Context, + width - spacing - textWidth, + height - spacing - textBoxHeight + (lh + 1.0f) * i, + helpTexts[i].c_str(), + 0); + } + } - glUseProgram(UI_Shader->shaderProgram); - fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-max) + spacing, line1, 0); + // label to show the selected sequence 从 input sequences 里面选中的片段 + if (Selected_Sequence_Cover_Countor.end_time > 0.) + { + f64 crt_time = GetTime(); + if (crt_time < Selected_Sequence_Cover_Countor.end_time) + { + char buff[128]; + f32 colour[4] = {1.0, 1.0, 1.0, 1.0}; + snprintf(buff, 128, "%s (%u)", (char *)((Original_Contigs+Selected_Sequence_Cover_Countor.original_contig_index)->name), Selected_Sequence_Cover_Countor.idx_within_original_contig+1); - if (Edit_Pixels.editing) - { - fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-max) + spacing + lh + 1.0f, midLine, 0); - fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-max) + spacing + (2.0f * (lh + 1.0f)), line2, 0); - } + f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, (char *)buff, 0, NULL); + ColourGenerator(65, colour); + // position of text + f32 textX = ModelXToScreen( + (f32)Selected_Sequence_Cover_Countor.map_loc / (f32)Number_of_Pixels_1D -0.5f) + - (0.5f * textWidth); + f32 textY = ModelYToScreen( 0.5f - (f32)Selected_Sequence_Cover_Countor.map_loc / (f32)Number_of_Pixels_1D); + + glUseProgram(UI_Shader->shaderProgram); + DrawOutlinedText( + FontStash_Context, + (nk_colorf *)colour, + (char *)buff, + textX, + textY, + 3.0f, true); + Selected_Sequence_Cover_Countor.plotted = true; + } + else + { + Selected_Sequence_Cover_Countor.clear(); + Redisplay = 1; + } + } - if (Edit_Pixels.snap) - { - char *text = (char *)"Snap Mode On"; - textWidth = fonsTextBounds(FontStash_Context, 0, 0, text, 0, NULL); - glUseProgram(Flat_Shader->shaderProgram); - vert[0].x = ModelXToScreen(min) - spacing - textWidth; vert[0].y = ModelYToScreen(-min) + spacing; - vert[1].x = ModelXToScreen(min) - spacing - textWidth; vert[1].y = ModelYToScreen(-min) + spacing + lh; - vert[2].x = ModelXToScreen(min) - spacing; vert[2].y = ModelYToScreen(-min) + spacing + lh; - vert[3].x = ModelXToScreen(min) - spacing; vert[3].y = ModelYToScreen(-min) + spacing; + // Scaff Bars + if (File_Loaded && (Scaff_Edit_Mode || Scaffs_Always_Visible)) + { + glUseProgram(Flat_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - - glUseProgram(UI_Shader->shaderProgram); - fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-min) + spacing, text, 0); - } + glViewport(0, 0, (s32)width, (s32)height); - { // draw the help text in the bottom right corner - fonsSetFont(FontStash_Context, Font_Bold); - fonsSetSize(FontStash_Context, 24.0f * Screen_Scale.x); - fonsVertMetrics(FontStash_Context, 0, 0, &lh); + u32 ptr = 0; + vertex vert[4]; + f32 barColour[4] = {1.0f, 1.0f, 1.0f, 0.5f}; - std::vector helpTexts = { - (char *)"Edit Mode", - (char *)"E: exit, Q: undo, W: redo", - (char *)"Left Click: pickup, place", - (char *)"S: toggle snap mode", - (char *)"Middle Click / Spacebar: pickup whole sequence or (hold Shift): scaffold", - (char *)"Middle Click / Spacebar (while editing): invert sequence" - }; + f32 lh = 0.0f; + fonsClearState(FontStash_Context); +#define DefaultScaffSize 40.0f + fonsSetSize(FontStash_Context, Scaff_Mode_Data->size * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_MIDDLE | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Bold); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); - textBoxHeight = (f32)helpTexts.size() * (lh + 1.0f) - 1.0f; - spacing = 10.0f; + char buff[128]; + f32 position = 0.0f; + f32 start = 0.0f; + u32 scaffId = Contigs->contigs_arr->scaffId; + ForLoop(Contigs->numberOfContigs) + { + contig *cont = Contigs->contigs_arr + index; - textWidth = 0.f; - for (auto* i :helpTexts){ - textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i, 0, NULL)); - } + if (cont->scaffId != scaffId) + { + if (scaffId) + { + vert[0].x = ModelXToScreen(start - 0.5f); vert[0].y = ModelYToScreen(0.5f - start); + vert[1].x = ModelXToScreen(start - 0.5f); vert[1].y = ModelYToScreen(0.5f - position); + vert[2].x = ModelXToScreen(position - 0.5f); vert[2].y = ModelYToScreen(0.5f - position); + vert[3].x = ModelXToScreen(position - 0.5f); vert[3].y = ModelYToScreen(0.5f - start); - glUseProgram(Flat_Shader->shaderProgram); + ColourGenerator((u32)scaffId, (f32 *)barColour); + u32 colour = ThreeFloatColorToU32(*((nk_colorf *)barColour)); - vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; - vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; - vert[2].x = width - spacing; vert[2].y = height - spacing; - vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); - glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - glUseProgram(UI_Shader->shaderProgram); + glUseProgram(UI_Shader->shaderProgram); + fonsSetColor(FontStash_Context, colour); - for (int i = 0; i < helpTexts.size(); i++) - { - fonsDrawText( - FontStash_Context, - width - spacing - textWidth, - height - spacing - textBoxHeight + (i * (lh + 1.0f)), - helpTexts[i], - 0); + stbsp_snprintf(buff, sizeof(buff), "Scaffold %u", scaffId); + f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, buff, 0, NULL); + fonsDrawText(FontStash_Context, ModelXToScreen(0.5f * (position + start - 1.0f)) - (0.5f * textWidth), ModelYToScreen(0.5f - start) - lh, buff, 0); } - } - } - } - // NK - if (UI_On) - { - glDisable(GL_CULL_FACE); - glEnable(GL_SCISSOR_TEST); + start = position; + scaffId = cont->scaffId; + } - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glViewport(0, 0, (s32)width, (s32)height); + position += ((f32)cont->length / (f32)Number_of_Pixels_1D); + } + if (scaffId) { - const struct nk_draw_command *cmd; - void *vertices, *elements; - nk_draw_index *offset = 0; + vert[0].x = ModelXToScreen(start - 0.5f); vert[0].y = ModelYToScreen(0.5f - start); + vert[1].x = ModelXToScreen(start - 0.5f); vert[1].y = ModelYToScreen(0.5f - position); + vert[2].x = ModelXToScreen(position - 0.5f); vert[2].y = ModelYToScreen(0.5f - position); + vert[3].x = ModelXToScreen(position - 0.5f); vert[3].y = ModelYToScreen(0.5f - start); - glBindVertexArray(NK_Device->vao); - glBindBuffer(GL_ARRAY_BUFFER, NK_Device->vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NK_Device->ebo); + ColourGenerator((u32)scaffId, (f32 *)barColour); + u32 colour = FourFloatColorToU32(*((nk_colorf *)barColour)); -#define MAX_VERTEX_MEMORY KiloByte(512) -#define MAX_ELEMENT_MEMORY KiloByte(128) - glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); - vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - { - nk_convert_config config; - static const nk_draw_vertex_layout_element vertex_layout[] = { - {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_glfw_vertex, position)}, - {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_glfw_vertex, uv)}, - {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(nk_glfw_vertex, col)}, - {NK_VERTEX_LAYOUT_END} - }; + glUseProgram(UI_Shader->shaderProgram); + fonsSetColor(FontStash_Context, colour); - NK_MEMSET(&config, 0, sizeof(config)); - config.vertex_layout = vertex_layout; - config.vertex_size = sizeof(nk_glfw_vertex); - config.vertex_alignment = NK_ALIGNOF(nk_glfw_vertex); - config.null = NK_Device->null; - config.circle_segment_count = 22; - config.curve_segment_count = 22; - config.arc_segment_count = 22; - config.global_alpha = 1.0f; - config.shape_AA = NK_ANTI_ALIASING_ON; - config.line_AA = NK_ANTI_ALIASING_ON; + stbsp_snprintf(buff, sizeof(buff), "Scaffold %u", scaffId); + f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, buff, 0, NULL); + fonsDrawText(FontStash_Context, ModelXToScreen(0.5f * (position + start - 1.0f)) - (0.5f * textWidth), ModelYToScreen(0.5f - start) - lh, buff, 0); + } - { - nk_buffer vbuf, ebuf; - nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); - nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); - nk_convert(NK_Context, &NK_Device->cmds, &vbuf, &ebuf, &config); - } - } - glUnmapBuffer(GL_ARRAY_BUFFER); - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + if (Scaff_Edit_Mode && !UI_On) + { + fonsSetSize(FontStash_Context, text_box_size.font_size * Screen_Scale.x); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(Scaff_Mode_Data->text)); - nk_draw_foreach(cmd, NK_Context, &NK_Device->cmds) - { - if (!cmd->elem_count) continue; - glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); - glScissor( - (GLint)(cmd->clip_rect.x), - (GLint)(height - cmd->clip_rect.y - cmd->clip_rect.h), - (GLint)(cmd->clip_rect.w), - (GLint)(cmd->clip_rect.h)); - glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); - offset += cmd->elem_count; - } - } + f32 textBoxHeight = lh; + textBoxHeight *= 7.0f; + textBoxHeight += 6.0f; + f32 spacing = 10.0f; - ChangeSize((s32)width, (s32)height); - glDisable(GL_SCISSOR_TEST); - glEnable(GL_CULL_FACE); + std::vector helpTexts = { + "Scaffold Edit Mode", + "S: exit", + "Left Click: place", + "Middle Click / Spacebar: delete", + "Shift-D: delete all", + "A (Hold): flood fill", + "Shift-A (Hold): flood fill and override" + }; - nk_clear(NK_Context); - } + f32 textWidth =0.f; + for (const auto& tmp:helpTexts) + textWidth = std::max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, tmp.c_str(), 0, NULL)); - if (Loading) - { - u32 colour = glfonsRGBA(Theme_Colour.r, Theme_Colour.g, Theme_Colour.b, Theme_Colour.a); + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Scaff_Mode_Data->bg); - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, 64.0f * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_MIDDLE); - fonsSetFont(FontStash_Context, Font_Bold); - fonsSetColor(FontStash_Context, colour); + vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; + vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; + vert[2].x = width - spacing; vert[2].y = height - spacing; + vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glViewport(0, 0, (s32)width, (s32)height); - fonsDrawText(FontStash_Context, width * 0.5f, height * 0.5f, "Loading...", 0); + glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Waypoint_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - ChangeSize((s32)width, (s32)height); - } + glUseProgram(UI_Shader->shaderProgram); + for (u32 i=0; i< helpTexts.size() ; i++) + { + fonsDrawText( + FontStash_Context, + width - spacing - textWidth, + height - spacing - textBoxHeight + (lh + 1.0f) * i, + helpTexts[i].c_str(), + 0); + } + } + } // scaff_mode + // draw the screen for select fragments for sorting + if (File_Loaded && !UI_On && Select_Sort_Area_Mode) + { + f32 lh = 0.f; + f32 start_fraction = (f32)auto_curation_state.get_start()/(f32)Number_of_Pixels_1D; + f32 end_fraction = (f32)auto_curation_state.get_end() /(f32)Number_of_Pixels_1D; - if (auto_sort_state) - { - u32 colour = glfonsRGBA(Theme_Colour.r, Theme_Colour.g, Theme_Colour.b, Theme_Colour.a); - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, 64.0f * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_MIDDLE); - fonsSetFont(FontStash_Context, Font_Bold); - fonsSetColor(FontStash_Context, colour); + { // draw the help text in the bottom right corner + fonsSetFont(FontStash_Context, Font_Bold); + fonsSetSize(FontStash_Context, text_box_size.font_size * Screen_Scale.x); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(Scaff_Mode_Data->text)); - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glViewport(0, 0, (s32)width, (s32)height); - fonsDrawText(FontStash_Context, width * 0.5f, height * 0.5f, "Pixel Sorting...", 0); + std::vector helpTexts = { + "Select/Exclude area for sorting", + "F: exit", + "S: clear select area", + "C/Z: cut / cancel cut", + "Q/W: quit / redo edit", + "Space: Pixel sort", + "Left Click: un/select the fragment", + fmt::format("Up/Down: inc/dec Cut threshold: {:.4f}", auto_curation_state.auto_cut_threshold), + fmt::format("Left/Right: change Sort Mode ({})", auto_curation_state.sort_mode_names[auto_curation_state.sort_mode]), + fmt::format("Left/Right Shift: Number of Clusters: {}", auto_curation_state.num_clusters), + fmt::format("H: Hap_Name_Clustering: {}", auto_curation_state.hap_cluster_flag ? "ON" : "OFF"), + fmt::format("O: Cut with gap: {}", auto_curation_state.auto_cut_with_extension? "ON" : "OFF"), + }; - ChangeSize((s32)width, (s32)height); - } + f32 textBoxHeight = (f32)helpTexts.size() * (lh + 1.0f) - 1.0f; + f32 spacing = 10.0f; - if (auto_cut_state) - { - u32 colour = glfonsRGBA(Theme_Colour.r, Theme_Colour.g, Theme_Colour.b, Theme_Colour.a); + f32 textWidth = 0.f; + for (const auto& i :helpTexts){ + textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i.c_str(), 0, NULL)); + } + textWidth = my_Min(textWidth, width - 2 * spacing); - fonsClearState(FontStash_Context); - fonsSetSize(FontStash_Context, 64.0f * Screen_Scale.x); - fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_MIDDLE); - fonsSetFont(FontStash_Context, Font_Bold); - fonsSetColor(FontStash_Context, colour); + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Scaff_Mode_Data->bg); + point2f vert[4]; + vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; + vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; + vert[2].x = width - spacing; vert[2].y = height - spacing; + vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; + + u32 ptr = 0; + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - glUseProgram(UI_Shader->shaderProgram); - glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glViewport(0, 0, (s32)width, (s32)height); - fonsDrawText(FontStash_Context, width * 0.5f, height * 0.5f, "Pixel cut...", 0); + glUseProgram(UI_Shader->shaderProgram); - ChangeSize((s32)width, (s32)height); - } - } -} + for (int i = 0; i < helpTexts.size(); i++) + { + fonsDrawText( + FontStash_Context, + width - spacing - textWidth, + height - spacing - textBoxHeight + (i * (lh + 1.0f)), + helpTexts[i].c_str(), + 0); + } + } + { // paint the selected area + if (start_fraction >= 0 && end_fraction >= 0 ) + { + u32 ptr = 0; + point2f vert[4]; -global_variable -file_atlas_entry * -File_Atlas; + // draw the start & end point + { + f32 line_width = 0.002f / Camera_Position.z; + f32 mask_color[4] = {1.0f, 0.f, 0.f, 0.8f}; // red + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&mask_color); + for (auto loc_fraction : {start_fraction, end_fraction}) + { + vert[0].x = ModelXToScreen(loc_fraction - 0.5f - line_width); vert[0].y = ModelYToScreen(0.5f - loc_fraction + line_width); + vert[1].x = ModelXToScreen(loc_fraction - 0.5f - line_width); vert[1].y = ModelYToScreen(0.5f - loc_fraction - line_width); + vert[2].x = ModelXToScreen(loc_fraction - 0.5f + line_width); vert[2].y = ModelYToScreen(0.5f - loc_fraction - line_width); + vert[3].x = ModelXToScreen(loc_fraction - 0.5f + line_width); vert[3].y = ModelYToScreen(0.5f - loc_fraction + line_width); -global_function -void -LoadTexture(void *in) -{ // - GLuint *textureHandle = (GLuint *)in; + glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } + } - texture_buffer *buffer = TakeTextureBufferFromQueue_Wait(Texture_Buffer_Queue); // take a buffer from the queue - // assign the texture handle to the buffer - buffer->x = (u16)(*textureHandle >> 16); // the former 16 bits represent the row number - buffer->y = (u16)(*textureHandle & ((1 << 16) - 1)); // the lower 16 bits represnet the column number + // draw the cover on selected area + { + // f32 mask_color[4] = {0.906f, 0.03921f, 0.949f, 0.5f}; + vert[0].x = ModelXToScreen(start_fraction - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_fraction); + vert[1].x = ModelXToScreen(start_fraction - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_fraction); + vert[2].x = ModelXToScreen(end_fraction - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_fraction); + vert[3].x = ModelXToScreen(end_fraction - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_fraction); - /* - rule for linear ordering - Ordering of the texture boxes - ======================== - 00 01 02 03 04 ... 30 31 - // 32 33 34 35 ... 61 62 - // // 63 64 65 ... 91 92 - // // // ... - ======================== - */ - // u32 linearIndex = (buffer->x * (Number_of_Textures_1D - 1)) - ((buffer->x * (buffer->x-1)) >> 1) + buffer->y; // get the index accoding to index in x and y diretion - u32 linearIndex = texture_id_cal((u32)buffer->x, (u32)buffer->y, (u32)Number_of_Textures_1D); - - file_atlas_entry *entry = File_Atlas + linearIndex; // set a tempory pointer - u32 nBytes = entry->nBytes; // base is the beginning, nBytes is the size - fseek(buffer->file, entry->base, SEEK_SET); // move from the begining to the start of this texture numbered as linearIndex + f32 font_color[4] = {0.f, 0.f, 0.f, 1.f}; + u32 colour = FourFloatColorToU32(*((nk_colorf *)font_color)); - fread(buffer->compressionBuffer, 1, nBytes, buffer->file); // read texture to the buffer + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&auto_curation_state.mask_color); - if ( - libdeflate_deflate_decompress( - buffer->decompressor, // compresser object - (const void *)buffer->compressionBuffer, // buffer data before decompressing - nBytes, // size before decompressing - (void *)buffer->texture, // place to store the data after decompressing - Bytes_Per_Texture, // size after decompressing - NULL) - ) - { // 解压压缩的texture到 buffer->texture - fprintf(stderr, "Could not decompress texture from disk\n"); - } + glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - while (true) - { - FenceIn(u32 texture_index_tmp = static_cast(Texture_Ptr)); - if (linearIndex == texture_index_tmp) break; - } + glUseProgram(UI_Shader->shaderProgram); + fonsSetColor(FontStash_Context, colour); + + // selected frags into a string + s32 selected_fragment_size = 1, last_contig = Map_State->contigIds[auto_curation_state.get_start()]; + for (s32 i = auto_curation_state.get_start()+1; i < auto_curation_state.get_end(); i++) + { + if (last_contig!=Map_State->contigIds[i]) + { + selected_fragment_size ++ ; + last_contig = Map_State->contigIds[i]; + } + } + std::string buff = fmt::format( + "{}: ({}) pixels, ({}) fragments", + auto_curation_state.selected_or_exclude==0?"Selected" :"Excluded", + std::abs(auto_curation_state.get_end() - auto_curation_state.get_start()), + selected_fragment_size); - FenceIn(Current_Loaded_Texture = buffer); // assign buffer to current_loaded_texture - -} + f32 lh = 0.0f; + f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, buff.c_str(), 0, NULL); + fonsDrawText( + FontStash_Context, + ModelXToScreen( 0.5f * (end_fraction + start_fraction ) - 0.5f) - (0.5f * textWidth), + ModelYToScreen(0.5f - 0.5f*(start_fraction + end_fraction)) - lh * 0.5f, buff.c_str(), 0); + } + } + } + } // select_sort_area -global_function -void -PopulateTextureLoadQueue(void *in) // 填充已经初始化过的 所有的queue,填充的任务为所有的528个texture的读取任务 -{ - u32 *packedTextureIndexes = (u32 *)in; // 将空指针转化为u32 - u32 ptr = 0; - ForLoop(Number_of_Textures_1D) // row number - { - for (u32 index2 = index; index2 < Number_of_Textures_1D; index2++ ) // column number + // Meta Tags + if (File_Loaded && (MetaData_Edit_Mode || MetaData_Always_Visible)) { - packedTextureIndexes[ptr] = (index << 16) | (index2); // first 16 bits is the raw number and the second 16 bits is the column number, there will be problem if number of texture in one dimension is larger than 2^16, but it is not possible ... just for a reminder - ThreadPoolAddTask(Thread_Pool, LoadTexture, (void *)(packedTextureIndexes + ptr++)); // 填充当前的任务队列,输入为对应的texture的行、列编号 - } - } -} + glUseProgram(Flat_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glViewport(0, 0, (s32)width, (s32)height); -/* -adjust the gamma thus adjust the contrast -*/ -global_function -void -AdjustColorMap(s32 dir) -{ - f32 unit = 0.05f; - f32 delta = unit * (dir > 0 ? 1.0f : -1.0f); + f32 colour[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + vertex vert[4]; + u32 ptr = 0; + f32 barColour[4] = {1.0f, 1.0f, 1.0f, 0.5f}; - Color_Maps->controlPoints[1] = my_Max( - my_Min(Color_Maps->controlPoints[1] + delta, Color_Maps->controlPoints[2]), - Color_Maps->controlPoints[0] - ); + f32 lh = 0.0f; + fonsClearState(FontStash_Context); +#define DefaultMetaDataSize 20.0f + fonsSetSize(FontStash_Context, MetaData_Mode_Data->size * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_MIDDLE | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Bold); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); - glUseProgram(Contact_Matrix->shaderProgram); - glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); -} + f32 end_contig = 0.0f, start_contig = 0.0f; + u32 scaffId = Contigs->contigs_arr->scaffId; + ForLoop(Contigs->numberOfContigs) + { + contig *cont = Contigs->contigs_arr + index; + end_contig += ((f32)cont->length / (f32)Number_of_Pixels_1D); // end of the contig + if (*cont->metaDataFlags) + { + u32 tmp = 0; // used to count the number of tags drawn + bool haplotigTagged = false; + ForLoop2(ArrayCount(Meta_Data->tags)) + { + if (*cont->metaDataFlags & ((u64)1 << index2)) + { + f32 textWidth = fonsTextBounds(FontStash_Context, 0, 0, (char *)Meta_Data->tags[index2], 0, NULL); + ColourGenerator(index2 + 1, colour); + // fonsSetColor(FontStash_Context, FourFloatColorToU32(*((nk_colorf *)colour))); + // fonsDrawText(FontStash_Context, ModelXToScreen(0.5f * (end_contig + start_contig - 1.0f)) - (0.5f * textWidth), ModelYToScreen((0.5f * (1.0f - end_contig - start_contig))) - (lh * (f32)(++tmp)), (char *)Meta_Data->tags[index2], 0); + if (meta_outline->on) + { + // position of text + f32 textX = ModelXToScreen(0.5f * (end_contig + start_contig - 1.0f)) - (0.5f * textWidth); + f32 textY = ModelYToScreen((0.5f * (1.0f - end_contig - start_contig))) - (lh * (f32)(++tmp)); + DrawOutlinedText(FontStash_Context, (nk_colorf *)colour, (char *)Meta_Data->tags[index2], textX, textY); + } + else + { + fonsSetColor(FontStash_Context, FourFloatColorToU32(*((nk_colorf *)colour))); + fonsDrawText( + FontStash_Context, + ModelXToScreen(0.5f * (end_contig + start_contig - 1.0f)) - (0.5f * textWidth), + ModelYToScreen((0.5f * (1.0f - end_contig - start_contig))) - (lh * (f32)(++tmp)), + (char *)Meta_Data->tags[index2], + 0); + } -/* -change to the last or next color map -*/ -global_function -void -NextColorMap(s32 dir) -{ - glActiveTexture(GL_TEXTURE1); - - if (useCustomOrder) - { - // Find the current index in the custom order - u32 currentIndex = 0; - for (u32 i = 0; i < userColourMapOrder.nMaps; i++) - { - if (userColourMapOrder.order[i] == Color_Maps->currMap) - { - currentIndex = i; - break; - } - } + // Check if the tag is "Haplotig" + if (strcmp((char *)Meta_Data->tags[index2], "Haplotig") == 0) + { + haplotigTagged = true; + } + } + } - // Calculate the next index in the custom order - u32 nextIndex = dir > 0 ? (currentIndex == (userColourMapOrder.nMaps - 1) ? 0 : currentIndex + 1) : (currentIndex == 0 ? userColourMapOrder.nMaps - 1 : currentIndex - 1); + // draw the grey out mask + std::string grey_out_tag = Grey_Out_Settings->is_grey_out(cont->metaDataFlags, Meta_Data); + if (!grey_out_tag.empty()) + { + vert[0].x = ModelXToScreen(start_contig - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_contig); + vert[1].x = ModelXToScreen(start_contig - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_contig); + vert[2].x = ModelXToScreen(end_contig - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_contig); + vert[3].x = ModelXToScreen(end_contig - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_contig); - Color_Maps->currMap = userColourMapOrder.order[nextIndex]; - } - else - { - Color_Maps->currMap = dir > 0 ? (Color_Maps->currMap == (Color_Maps->nMaps - 1) ? 0 : Color_Maps->currMap + 1) : (Color_Maps->currMap == 0 ? Color_Maps->nMaps - 1 : Color_Maps->currMap - 1); - } - - glBindTexture(GL_TEXTURE_BUFFER, Color_Maps->maps[Color_Maps->currMap]); - - glActiveTexture(GL_TEXTURE0); -} + ColourGenerator((u32)scaffId, (f32 *)barColour); + u32 colour = FourFloatColorToU32(*((nk_colorf *)barColour)); + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); -global_function -u32 -GetOrderedColorMapIndex(u32 displayIndex) -{ - if (displayIndex < userColourMapOrder.nMaps) - { - return userColourMapOrder.order[displayIndex]; - } - return displayIndex; -} + glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + glUseProgram(UI_Shader->shaderProgram); + fonsSetColor(FontStash_Context, colour); + } + else{ + int is_vert_horiz_grey_out = Grey_Out_Settings->is_vert_horiz_grey_out(cont->metaDataFlags, Meta_Data); + if (is_vert_horiz_grey_out == 0) continue; + // draw the vertical or horizontal grey out mask + vert[0].x = ModelXToScreen(start_contig - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_contig); + vert[1].x = ModelXToScreen(start_contig - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_contig); + vert[2].x = ModelXToScreen(end_contig - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_contig); + vert[3].x = ModelXToScreen(end_contig - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_contig); + + if (is_vert_horiz_grey_out == 1) { // vertical + vert[0].y = ModelYToScreen( 0.5f); + vert[1].y = ModelYToScreen(-0.5f); + vert[2].y = ModelYToScreen(-0.5f); + vert[3].y = ModelYToScreen( 0.5f); + } + else { // horizontal + vert[0].x = ModelXToScreen(-0.5f); + vert[1].x = ModelXToScreen(-0.5f); + vert[2].x = ModelXToScreen( 0.5f); + vert[3].x = ModelXToScreen( 0.5f); + } -global_function void -InitializeColorMapOrder() -{ - userColourMapOrder.nMaps = Color_Maps->nMaps; - for (u32 i = 0; i < userColourMapOrder.nMaps; i++) - { - userColourMapOrder.order[i] = i; - } -} + ColourGenerator((u32)scaffId, (f32 *)barColour); + u32 colour = FourFloatColorToU32(*((nk_colorf *)barColour)); + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); -global_variable -GLuint -Quad_EBO; + glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); -enum -load_file_result -{ - ok, - fileErr, - decompErr, -}; + glUseProgram(UI_Shader->shaderProgram); + fonsSetColor(FontStash_Context, colour); -global_variable -libdeflate_decompressor * -Decompressor; - -global_variable -libdeflate_compressor * -Compressor; + } -global_variable -u08 Magic[] = {'p', 's', 't', 'm'}; + } + start_contig = end_contig; + scaffId = cont->scaffId; + } -global_function -FILE * -TestFile(const char *fileName, u64 *fileSize = 0) -{ - FILE *file=0; - { - file = fopen(fileName, "rb"); - if (!file) - { -#ifdef DEBUG - fprintf(stderr, "The file is not available: \'%s\' [errno %d] \n", fileName, errno); - exit(errno); -#endif - } - else - { - if (fileSize) + if (MetaData_Edit_Mode && !UI_On) { - fseek(file, 0, SEEK_END); - *fileSize = (u64)ftell(file); - fseek(file, 0, SEEK_SET); - } + u32 ptr = 0; + vertex vert[4]; - u08 magicTest[sizeof(Magic)]; + fonsSetSize(FontStash_Context, text_box_size.font_size * Screen_Scale.x); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(MetaData_Mode_Data->text)); - u32 bytesRead = (u32)fread(magicTest, 1, sizeof(magicTest), file); - if (bytesRead == sizeof(magicTest)) - { - ForLoop(sizeof(Magic)) // #define ForLoop(n) for (u32 index=0; indextags[MetaData_Active_Tag]; + stbsp_snprintf(helpText7, sizeof(helpText7), "Active Tag: %s", strlen(activeTag) ? activeTag : ""); + std::vector helpTexts = { + "MetaData Tag Mode", + "M: exit", + "Left Click: place", + "Middle Click / Spacebar: delete", + "Shift-D: delete all", + "Arrow Keys: select active tag", + helpText7 + }; -global_function -void -add_graph_to_extensions( - memory_arena *arena, - const u32* graph_name, - const u32* graph_data) -{ - - graph *gph = PushStructP(arena, graph); // create the size to store the extension data - extension_node *node = PushStructP(arena, extension_node); - u08 *dataPlusName = PushArrayP(arena, u08, ((sizeof(u32) * Number_of_Pixels_1D) + sizeof(gph->name) )); - gph->data = (u32 *)(dataPlusName + sizeof(gph->name)); - u32 *namePtr = (u32 *)graph_name; - for (u32 i = 0; i < ArrayCount(gph->name); i++ ) // get the graph name - { - gph->name[i] = *(namePtr + i); - } - for (u32 i = 0; i < Number_of_Pixels_1D; i++) // get the graph data - { - gph->data[i] = *(graph_data + i); - } - node->type = extension_graph; // assign the gph to node - node->extension = gph; - AddExtension(node); // add node to extension - - return ; -} + f32 textWidth = 0.f; + for (const auto& tmp:helpTexts) + textWidth = std::max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, tmp.c_str(), 0, NULL)); + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&MetaData_Mode_Data->bg); + vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; + vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; + vert[2].x = width - spacing; vert[2].y = height - spacing; + vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; -global_function -void -push_extensions_to_opengl(memory_arena *arena, u32 added_index = 0, f32 scale=-1.f) -{ - u32 exIndex = 0; - // TraverseLinkedList(Extensions.head, extension_node) + glBindBuffer(GL_ARRAY_BUFFER, Waypoint_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Waypoint_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - extension_node* node = Extensions.head ; - while (added_index--) - { - node = node->next; // 移动到最后一个节点 - exIndex ++ ; - } - for ( ; node; node = node->next) { + glUseProgram(UI_Shader->shaderProgram); + for (u32 i=0; i< helpTexts.size() ; i++) { + fonsDrawText( + FontStash_Context, + width - spacing - textWidth, + height - spacing - textBoxHeight + (lh + 1.0f) * i, + helpTexts[i].c_str(), + 0); + } + } + } - switch (node->type) + // Tool Tip + if (File_Loaded && Tool_Tip->on && !Edit_Mode && !UI_On) { - case extension_graph: - { - graph *gph = (graph *)node->extension; // get the graph ptr and assigned to gph -#define DefaultGraphScale 0.2f -#define DefaultGraphBase 32.0f -#define DefaultGraphLineSize 1.0f -#define DefaultGraphColour {0.1f, 0.8f, 0.7f, 1.0f} - gph->scale = scale>0.f? scale : DefaultGraphScale; - gph->base = DefaultGraphBase; - gph->lineSize = DefaultGraphLineSize; - gph->colour = DefaultGraphColour; - gph->on = 0; + glUseProgram(Flat_Shader->shaderProgram); + glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&Tool_Tip->bg); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); - gph->shader = PushStructP(arena, editable_plot_shader); - gph->shader->shaderProgram = CreateShader(FragmentSource_EditablePlot.c_str(), VertexSource_EditablePlot.c_str(), GeometrySource_EditablePlot.c_str()); + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - glUseProgram(gph->shader->shaderProgram); - glBindFragDataLocation(gph->shader->shaderProgram, 0, "outColor"); - gph->shader->matLocation = glGetUniformLocation(gph->shader->shaderProgram, "matrix"); - gph->shader->colorLocation = glGetUniformLocation(gph->shader->shaderProgram, "color"); - gph->shader->yScaleLocation = glGetUniformLocation(gph->shader->shaderProgram, "yscale"); - gph->shader->yTopLocation = glGetUniformLocation(gph->shader->shaderProgram, "ytop"); - gph->shader->lineSizeLocation = glGetUniformLocation(gph->shader->shaderProgram, "linewidth"); + vertex vert[4]; - glUniform1i(glGetUniformLocation(gph->shader->shaderProgram, "pixrearrangelookup"), 3); - glUniform1i(glGetUniformLocation(gph->shader->shaderProgram, "yvalues"), 4 + (s32)exIndex); + glViewport(0, 0, (s32)width, (s32)height); - u32 nValues = Number_of_Pixels_1D; - auto* xValues = new f32[nValues]; // allocate memory for xValues, actually, x is the coordinate of the pixel - auto* yValues = new f32[nValues]; - - u32 max = 0; - ForLoop(Number_of_Pixels_1D) - { - max = my_Max(max, gph->data[index]); - } + f32 lh = 0.0f; - ForLoop(Number_of_Pixels_1D) + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, Tool_Tip->size * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Normal); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(Tool_Tip->fg)); + + // Extension info, extra lines + u32 nExtra = 0; + f32 longestExtraLineLength = 0.0f; + { + if (Extensions.head) + { + char buff[128]; + TraverseLinkedList(Extensions.head, extension_node) { - xValues[index] = (f32)index; - yValues[index] = (f32)gph->data[index] / (f32)max ; // normalise the data - } + switch (node->type) + { + case extension_graph: + { + graph *gph = (graph *)node->extension; + if (gph->on) + { + glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); + u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, Tool_Tip_Move.pixels.x * sizeof(u32), sizeof(u32), GL_MAP_READ_BIT); - glActiveTexture(GL_TEXTURE4 + exIndex++); + stbsp_snprintf(buff, sizeof(buff), "%s: %$d", (char *)gph->name, gph->data[*buffer]); + ++nExtra; + longestExtraLineLength = my_Max(longestExtraLineLength, fonsTextBounds(FontStash_Context, 0, 0, buff, 0, NULL)); - GLuint yVal, yValTex; + glUnmapBuffer(GL_TEXTURE_BUFFER); + glBindBuffer(GL_TEXTURE_BUFFER, 0); + } + } + break; + } + } + } + } - // generate a buffer object named yVal and save data to yVal - glGenBuffers(1, &yVal); - glBindBuffer(GL_TEXTURE_BUFFER, yVal); - glBufferData(GL_TEXTURE_BUFFER, sizeof(f32) * nValues, yValues, GL_STATIC_DRAW); + f32 textBoxHeight = lh; + textBoxHeight *= (3.0f + (f32)nExtra); + textBoxHeight += (2.0f + (f32)nExtra); - // add texture and link to the buffer object - glGenTextures(1, &yValTex); - glBindTexture(GL_TEXTURE_BUFFER, yValTex); - glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, yVal); + u32 id1 = Map_State->get_original_contig_id(Tool_Tip_Move.pixels.x); + u32 id2 = Map_State->get_original_contig_id(Tool_Tip_Move.pixels.y); + u32 coord1 = Map_State->contigRelCoords[Tool_Tip_Move.pixels.x]; + u32 coord2 = Map_State->contigRelCoords[Tool_Tip_Move.pixels.y]; + + f64 bpPerPixel = (f64)Total_Genome_Length / (f64)Number_of_Pixels_1D; - gph->shader->yValuesBuffer = yVal; - gph->shader->yValuesBufferTex = yValTex; - - // add the vertext data into buffer - glGenBuffers(1, &gph->vbo); - glBindBuffer(GL_ARRAY_BUFFER, gph->vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * nValues, xValues, GL_STATIC_DRAW); + char line1[64]; + char *line2 = (char *)"vs"; + char line3[64]; - // gen the vertex array object - glGenVertexArrays(1, &gph->vao); - glBindVertexArray(gph->vao); - - GLuint posAttrib = (GLuint)glGetAttribLocation(gph->shader->shaderProgram, "position"); - glEnableVertexAttribArray(posAttrib); - glVertexAttribPointer(posAttrib, 1, GL_FLOAT, GL_FALSE, 0, 0); - - delete[] xValues; - delete[] yValues; - - glActiveTexture(GL_TEXTURE0); - } - break; - } - } - - return ; -} - - - -global_function -u08 -LoadState(u64 headerHash, char *path = 0); - -global_function -load_file_result -LoadFile(const char *filePath, memory_arena *arena, char **fileName, u64 *headerHash) -{ - u64 fileSize = 0; + auto NicePrint = [bpPerPixel](u32 id, u32 coord, char *buffer) + { + f64 pos = (f64)coord * bpPerPixel; + stbsp_snprintf(buffer, 64, "%s %'u %sbp", (Original_Contigs + id)->name, (u32)(pos / (pos > 1000.0 ? 1000.0 : 1.0)), pos > 1000.0 ? "K" : ""); + }; - FILE *file = TestFile(filePath, &fileSize); // 检查前4个字节读取到的数据, 如果为 u08 Magic[] = {'p', 's', 't', 'm'} 则通过验证,否则将指针file设置为空指针 - if (!file) // 如果为空指针, 返回读取错误fileErr - { - return(fileErr); - } - - FenceIn(File_Loaded = 0); + NicePrint(id1, coord1, line1); + NicePrint(id2, coord2, line3); - static u32 reload = 0; - - if (!reload) - { - reload = 1; - } - else // clear all the memory, in gl and the arena - { - glDeleteTextures(1, &Contact_Matrix->textures); + f32 textWidth_1 = fonsTextBounds(FontStash_Context, 0, 0, line1, 0, NULL); + f32 textWidth_2 = fonsTextBounds(FontStash_Context, 0, 0, line2, 0, NULL); + f32 textWidth_3 = fonsTextBounds(FontStash_Context, 0, 0, line3, 0, NULL); + f32 textWidth = my_Max(textWidth_1, textWidth_2); + textWidth = my_Max(textWidth, textWidth_3); + textWidth = my_Max(textWidth, longestExtraLineLength); - glDeleteVertexArrays((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vaos); - glDeleteBuffers((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vbos); + f32 spacing = 12.0f; - glDeleteBuffers(1, &Contact_Matrix->pixelStartLookupBuffer); - glDeleteTextures(1, &Contact_Matrix->pixelStartLookupBufferTex); + glUseProgram(Flat_Shader->shaderProgram); - glDeleteBuffers(1, &Contact_Matrix->pixelRearrangmentLookupBuffer); - glDeleteTextures(1, &Contact_Matrix->pixelRearrangmentLookupBufferTex); + vert[0].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing; vert[0].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing; + vert[1].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing; vert[1].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + textBoxHeight; + vert[2].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing + textWidth; vert[2].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + textBoxHeight; + vert[3].x = ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing + textWidth; vert[3].y = ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing; - glDeleteVertexArrays((GLsizei)Grid_Data->nBuffers, Grid_Data->vaos); - glDeleteBuffers((GLsizei)Grid_Data->nBuffers, Grid_Data->vbos); + glBindBuffer(GL_ARRAY_BUFFER, Tool_Tip_Data->vbos[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Tool_Tip_Data->vaos[0]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - glDeleteVertexArrays((GLsizei)Label_Box_Data->nBuffers, Label_Box_Data->vaos); - glDeleteBuffers((GLsizei)Label_Box_Data->nBuffers, Label_Box_Data->vbos); + glUseProgram(UI_Shader->shaderProgram); + fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, + ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing, line1, 0); + fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, + ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + lh + 1.0f, line2, 0); + fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, + ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + (2.0f * lh) + 2.0f, line3, 0); - glDeleteVertexArrays((GLsizei)Scale_Bar_Data->nBuffers, Scale_Bar_Data->vaos); - glDeleteBuffers((GLsizei)Scale_Bar_Data->nBuffers, Scale_Bar_Data->vbos); + { + if (Extensions.head) + { + u32 count = 0; + char buff[128]; + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) + { + case extension_graph: + { + graph *gph = (graph *)node->extension; + if (gph->on) + { + glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); + u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, Tool_Tip_Move.pixels.x * sizeof(u32), sizeof(u32), GL_MAP_READ_BIT); - glDeleteVertexArrays((GLsizei)Contig_ColourBar_Data->nBuffers, Contig_ColourBar_Data->vaos); - glDeleteBuffers((GLsizei)Contig_ColourBar_Data->nBuffers, Contig_ColourBar_Data->vbos); + stbsp_snprintf(buff, sizeof(buff), "%s: %$d", (char *)gph->name, gph->data[*buffer]); - glDeleteVertexArrays((GLsizei)Scaff_Bar_Data->nBuffers, Scaff_Bar_Data->vaos); - glDeleteBuffers((GLsizei)Scaff_Bar_Data->nBuffers, Scaff_Bar_Data->vbos); + fonsDrawText(FontStash_Context, ModelXToScreen(Tool_Tip_Move.worldCoords.x) + spacing, + ModelYToScreen(-Tool_Tip_Move.worldCoords.y) + spacing + ((2.0f + (f32)(++count)) * (lh + 1.0f)), buff, 0); - TraverseLinkedList(Extensions.head, extension_node) - { - switch (node->type) - { - case extension_graph: - { - graph *gph = (graph *)node->extension; - glDeleteVertexArrays(1, &gph->vao); - glDeleteBuffers(1, &gph->vbo); - glDeleteBuffers(1, &gph->shader->yValuesBuffer); - glDeleteTextures(1, &gph->shader->yValuesBufferTex); + glUnmapBuffer(GL_TEXTURE_BUFFER); + glBindBuffer(GL_TEXTURE_BUFFER, 0); + } + } + break; + } } - break; + } } } - Current_Loaded_Texture = 0; - Texture_Ptr = 0; - - Mouse_Move.x = -1.0; - Mouse_Move.y = -1.0; + // Edit Mode + if (File_Loaded && Edit_Mode && !UI_On) // Edit Mode + { + u32 ptr = 0; + vertex vert[4]; - Camera_Position.x = 0.0f; - Camera_Position.y = 0.0f; - Camera_Position.z = 1.0f; - - Edit_Pixels.editing = 0; - Global_Mode = mode_normal; + glUseProgram(Flat_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); - Extensions = {}; + glUseProgram(Flat_Shader->shaderProgram); + glViewport(0, 0, (s32)width, (s32)height); - // delete the memory collected by new - if (Contact_Matrix->vaos) - { - delete[] Contact_Matrix->vaos; - Contact_Matrix->vaos = nullptr; - } - if (Contact_Matrix->vbos) - { - delete[] Contact_Matrix->vbos; - Contact_Matrix->vbos = nullptr; - } - if (pixel_density_extension) - { - delete pixel_density_extension; - pixel_density_extension = nullptr; - } - if (frag_cut_cal_ptr) - { - delete frag_cut_cal_ptr; - frag_cut_cal_ptr = nullptr; - } - ResetMemoryArenaP(arena); // release all the memory allocated, avoid memory leak - auto_curation_state.clear(); - } + f32 color[4]; + f32* source; + if (Edit_Pixels.editing) // edit color + { + if (Global_Edit_Invert_Flag) source = (f32*)&Edit_Mode_Colours->invSelect; + else source = (f32*)&Edit_Mode_Colours->select; + } + else source = (f32*)&Edit_Mode_Colours->preSelect; // pre-edit color + ForLoop(4) + { + color[index] = source[index]; + } + f32 alpha = color[3]; + color[3] = 1.0f; - // File Contents - { - char *tmp = (char *)filePath; -#ifdef _WIN32 - char sep = '\\'; -#else - char sep = '/'; -#endif + glUniform4fv(Flat_Shader->colorLocation, 1, color); - while (*++tmp) {} - while ((*--tmp != sep) && *tmp) {} + f32 lineWidth = 0.005f / Camera_Position.z; + + { // draw the two squared dots at the beginning and end of the selected fragment + vert[0].x = ModelXToScreen(Edit_Pixels.worldCoords.x - lineWidth); + vert[0].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.x); + vert[1].x = ModelXToScreen(Edit_Pixels.worldCoords.x - lineWidth); + vert[1].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.x); + vert[2].x = ModelXToScreen(Edit_Pixels.worldCoords.x + lineWidth); + vert[2].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.x); + vert[3].x = ModelXToScreen(Edit_Pixels.worldCoords.x + lineWidth); + vert[3].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.x); - *fileName = tmp + 1; + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - u32 intBuff[16]; - PushStringIntoIntArray(intBuff, ArrayCount(intBuff), (u08 *)(*fileName)); // 将字符穿转移到intbuff数组中 + vert[0].x = ModelXToScreen(Edit_Pixels.worldCoords.y - lineWidth); + vert[0].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.y); + vert[1].x = ModelXToScreen(Edit_Pixels.worldCoords.y - lineWidth); + vert[1].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.y); + vert[2].x = ModelXToScreen(Edit_Pixels.worldCoords.y + lineWidth); + vert[2].y = ModelYToScreen(-lineWidth - Edit_Pixels.worldCoords.y); + vert[3].x = ModelXToScreen(Edit_Pixels.worldCoords.y + lineWidth); + vert[3].y = ModelYToScreen(lineWidth - Edit_Pixels.worldCoords.y); - /* - 这段代码假设文件中的数据是以 4 字节整数的形式存储的,并且依次存储了 - nBytesHeaderComp 和 nBytesHeader 两个值。通常情况下, - 这种操作用于读取文件中存储的数据头部信息或者其他固定格式的数据。 - */ - u32 nBytesHeaderComp; // 压缩后数据大小 - u32 nBytesHeader; // 解压后数据大小 - fread(&nBytesHeaderComp, 1, 4, file); // 从文件中读取 4 个字节的数据,并将其存储在 nBytesHeaderComp 变量中 - fread(&nBytesHeader, 1, 4, file); + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } + + f32 min = my_Min(Edit_Pixels.worldCoords.x, Edit_Pixels.worldCoords.y); + f32 max = my_Max(Edit_Pixels.worldCoords.x, Edit_Pixels.worldCoords.y); - u08 *header = PushArrayP(arena, u08, nBytesHeader); // 从内存池中分配u08 数组,大小为 nBytesHeader - u08 *compressionBuffer = PushArrayP(arena, u08, nBytesHeaderComp); // 从内存池中分配u08 数组,大小为 nBytesHeaderComp + if (Global_Edit_Invert_Flag) // draw the two arrows + { + f32 spacing = 0.002f / Camera_Position.z; + f32 arrowWidth = 0.01f / Camera_Position.z; + f32 arrowHeight = arrowWidth * 0.65f; + f32 recHeight = arrowHeight * 0.65f; - fread(compressionBuffer, 1, nBytesHeaderComp, file); // nBytesHeaderComp个字节的压缩数据,存储到compressionBuffer - *headerHash = FastHash64( // head的地址采用fasthash加密,作为存储文件的文件名。 compressionBuffer所指向的值进行hash得到一个名字,作为存储文件的文件名 - compressionBuffer, - nBytesHeaderComp, - FastHash64(intBuff, sizeof(intBuff), 0xbafd06832de619c2) - ); - // fprintf(stdout, "The headerHash is calculated accordig to the compressed head, the hash number is (%llu) and the cache file name is (%s)\n", *headerHash, (u08*) headerHash); - if ( - libdeflate_deflate_decompress( - Decompressor, // 指向 解压缩器 - (const void *)compressionBuffer, // 指向 即将解压的数据,(const void *)强制转换为不可修改的内存块 - nBytesHeaderComp, // 解压缩的字节数 - (void *)header, // 指向 解压缩后存储的位置,转换为通用内存块 - nBytesHeader, // 解压后预计大小 - NULL) // 表示不传递其他参数给压缩函数 - ) - { - return(decompErr); // decompress err - } - FreeLastPushP(arena); // comp buffer 释放内存池(arena)中最近一次通过 PushArrayP 宏分配的内存空间 - // 遍历内存池链表,找到最后一个内存池,并释放其最近一次分配的内存空间。防止内存泄漏 + // draw the the triangle part + vert[0].x = ModelXToScreen(min + spacing); vert[0].y = ModelYToScreen(arrowHeight + spacing - min); + vert[1].x = ModelXToScreen(min + spacing + arrowWidth); vert[1].y = ModelYToScreen(spacing - min); + vert[2].x = ModelXToScreen(min + spacing + arrowWidth); vert[2].y = ModelYToScreen(arrowHeight + spacing - min); + vert[3].x = ModelXToScreen(min + spacing + arrowWidth); vert[3].y = ModelYToScreen((2.0f * arrowHeight) + spacing - min); + + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - /* - header的格式 - ========================== - nBytes Contents - -------------------------- - 8 Total_Genome_Length - 4 Number_of_Original_Contigs - ------------------- - Number_of_Original_Contigs 个 contigs 的存储规则 - ------------------- - 4 contig fracs - 64 name - ------------------ - 1 textureRes - 1 nTextRes - 1 mipMapLevels + // draw the rectangle part + vert[0].x = ModelXToScreen(min + spacing + arrowWidth); vert[0].y = ModelYToScreen((arrowHeight + 0.5f * recHeight) + spacing - min); + vert[1].x = ModelXToScreen(min + spacing + arrowWidth); vert[1].y = ModelYToScreen((arrowHeight - 0.5f * recHeight) + spacing - min); + vert[2].x = ModelXToScreen(max - spacing); vert[2].y = ModelYToScreen((arrowHeight - 0.5f * recHeight) + spacing - min); + vert[3].x = ModelXToScreen(max - spacing); vert[3].y = ModelYToScreen((arrowHeight + 0.5f * recHeight) + spacing - min); + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + } - */ + { // draw the interacted area + + color[3] = alpha; + glUniform4fv(Flat_Shader->colorLocation, 1, color); - u64 val64; - u08 *ptr = (u08 *)&val64; // 获取val64存储的 八位无符号整型(u08)的指针 - ForLoop(8) - { - *ptr++ = *header++; // 指针赋值给到val64的大小 -> 整个基因的长度 - } - Total_Genome_Length = val64; + // upper vertical part + vert[0].x = ModelXToScreen(min); vert[0].y = ModelYToScreen(0.5f); + vert[1].x = ModelXToScreen(min); vert[1].y = ModelYToScreen(-min); + vert[2].x = ModelXToScreen(max); vert[2].y = ModelYToScreen(-min); + vert[3].x = ModelXToScreen(max); vert[3].y = ModelYToScreen(0.5f); + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - u32 val32; - ptr = (u08 *)&val32; - ForLoop(4) - { - *ptr++ = *header++; - } - Number_of_Original_Contigs = val32; // 指针赋值给到val32的值 -> contigs的数目 + // left horizontal part + vert[0].x = ModelXToScreen(-0.5f); vert[0].y = ModelYToScreen(-min); + vert[1].x = ModelXToScreen(-0.5f); vert[1].y = ModelYToScreen(-max); + vert[2].x = ModelXToScreen(min); vert[2].y = ModelYToScreen(-max); + vert[3].x = ModelXToScreen(min); vert[3].y = ModelYToScreen(-min); + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - // 从内存池中分配存储原始 contig 的数组内存,类型为 original_contig,数组长度为 Number_of_Original_Contigs - Original_Contigs = PushArrayP(arena, original_contig, Number_of_Original_Contigs); - // 分配一个存储浮点数的数组 - f32 *contigFracs = PushArrayP(arena, f32, Number_of_Original_Contigs); - ForLoop(Number_of_Original_Contigs) // 读取 contigs fraction (f32) and name - { + // lower vertical part + vert[0].x = ModelXToScreen(min); vert[0].y = ModelYToScreen(-max); + vert[1].x = ModelXToScreen(min); vert[1].y = ModelYToScreen(-0.5f); + vert[2].x = ModelXToScreen(max); vert[2].y = ModelYToScreen(-0.5f); + vert[3].x = ModelXToScreen(max); vert[3].y = ModelYToScreen(-max); + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - f32 frac; - u32 name[16]; + // right horizontal part + vert[0].x = ModelXToScreen(max); vert[0].y = ModelYToScreen(-min); + vert[1].x = ModelXToScreen(max); vert[1].y = ModelYToScreen(-max); + vert[2].x = ModelXToScreen(0.5f); vert[2].y = ModelYToScreen(-max); + vert[3].x = ModelXToScreen(0.5f); vert[3].y = ModelYToScreen(-min); + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - // 读取每个contig 对应的一个f32 - ptr = (u08 *)&frac; - ForLoop2(4) // 从header中读取4个字节的数据存储到frac中 - { - *ptr++ = *header++; - } - contigFracs[index] = frac; // 将这个f32 存储到 contigFracs[index] 中 - - // 读取contig的名字 - ptr = (u08 *)name; - ForLoop2(64) - { - *ptr++ = *header++; - } - // contig name赋值 - ForLoop2(16) - { - Original_Contigs[index].name[index2] = name[index2]; // 将 u32 name[16] 给到每一个contig 的name + // ceter part + vert[0].x = ModelXToScreen(min); vert[0].y = ModelYToScreen(-min); + vert[1].x = ModelXToScreen(min); vert[1].y = ModelYToScreen(-max); + vert[2].x = ModelXToScreen(max); vert[2].y = ModelYToScreen(-max); + vert[3].x = ModelXToScreen(max); vert[3].y = ModelYToScreen(-min); + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); } - - (Original_Contigs + index)->contigMapPixels = PushArrayP(arena, u32, Number_of_Pixels_1D); // 为每个contig 的 mapPixels 变量 申请内存 - (Original_Contigs + index)->nContigs = 0; - } - - u08 textureRes = *header++; // 分辨率 - u08 nTextRes = *header++; // 纹理数目 - u08 mipMapLevels = *header; // ?? - Texture_Resolution = Pow2(textureRes); // 纹理分辨率 当前显示的像素点个数,1024 - Number_of_Textures_1D = Pow2(nTextRes); // 一维纹理数目 可以放大的次数,每一个像素点可以放大32次, - Number_of_MipMaps = mipMapLevels; + { // draw the text by the selected (pre-select) fragment + f32 lh = 0.0f; + + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, 18.0f * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + fonsSetFont(FontStash_Context, Font_Normal); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); + fonsSetColor(FontStash_Context, FourFloatColorToU32(Edit_Mode_Colours->fg)); + + f32 textBoxHeight = lh; + textBoxHeight *= Edit_Pixels.editing ? 3.0f : 1.0f; + textBoxHeight += Edit_Pixels.editing ? 3.0f : 0.0f; - Number_of_Pixels_1D = Number_of_Textures_1D * Texture_Resolution; // 更新一维数据的长度 + static char line1[64]; + static u32 line1Done = 0; + char line2[64]; - Map_State = PushStructP(arena, map_state); // 从内存池中分配一个包含 map_state 结构的内存块,并返回指向该结构的指针, 存储contigs map到图像中的数据 - Map_State->contigIds = PushArrayP(arena, u32, Number_of_Pixels_1D); // 从内存池中分配存储 contigIds 的数组,数组长度为 Number_of_Pixels_1D - Map_State->originalContigIds = PushArrayP(arena, u32, Number_of_Pixels_1D); // - Map_State->contigRelCoords = PushArrayP(arena, u32, Number_of_Pixels_1D); // 像素坐标 - Map_State->scaffIds = PushArrayP(arena, u32, Number_of_Pixels_1D); // scaffID - Map_State->metaDataFlags = PushArrayP(arena, u64, Number_of_Pixels_1D); // 标记 - memset(Map_State->scaffIds, 0, Number_of_Pixels_1D * sizeof(u32)); // 将scaffID和metaDataFlags初始化为0 - memset(Map_State->metaDataFlags, 0, Number_of_Pixels_1D * sizeof(u64)); - f32 total = 0.0f; // 所有contig的一个浮点数的累积, finally should approximately be 1.0 - u32 lastPixel = 0; - u32 relCoord = 0; + char *midLineNoInv = (char *)"moved to"; + char *midLineInv = (char *)"inverted and moved to"; + char *midLine = Global_Edit_Invert_Flag ? midLineInv : midLineNoInv; - ForLoop(Number_of_Original_Contigs) // 初始设定每个contig的每个像素点的id和局部坐标 - { - total += contigFracs[index]; // 当前所有contig对应的浮点数的累积,包括当前contig - u32 pixel = (u32)((f64)Number_of_Pixels_1D * (f64)total); // 每行像素点数 * 当前占比 - - relCoord = 0; -#ifdef RevCoords - u32 tmp = pixel - lastPixel - 1; -#endif - while (lastPixel < pixel) - { - Map_State->originalContigIds[lastPixel] = index; // 每一个像素点对应的都是当前contig的编号 - Map_State->contigRelCoords[lastPixel++] = // 每一个像素点对应的在当前contig中的局部坐标 -#ifdef RevCoords - tmp - relCoord++; -#else - relCoord++; -#endif - } - } - while (lastPixel < Number_of_Pixels_1D) // 处理数值计算导致的lastPixel小于Number_of_Pixels_1D的问题 - { - Map_State->originalContigIds[lastPixel] = (u32)(Number_of_Original_Contigs - 1); //假设其为最后一个contig的像素点 - Map_State->contigRelCoords[lastPixel++] = relCoord++; - } + u32 pix1 = my_Min(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); + u32 pix2 = my_Max(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); - Contigs = PushStructP(arena, contigs); // 声明一个存储contigs的内存块, 其返回Contigs作为这个块的指针,实际上此处为整个genome的信息 - Contigs->contigs_arr = PushArrayP(arena, contig, Max_Number_of_Contigs); // 每一个Contigs中会有contigs (片段),一共有Max_Number_of_Contigs多个片段,最多存放4096个contigs - Contigs->contigInvertFlags = PushArrayP(arena, u08, (Max_Number_of_Contigs + 7) >> 3); // (4096 + 7 ) >> 3 = 512, 声明512个u08的存储空间 + if (Edit_Pixels.editing && line1Done) + { + pix1 = pix1 ? pix1 - 1 : (pix2 < (Number_of_Pixels_1D - 1) ? pix2 + 1 : pix2); + } - UpdateContigsFromMapState(); // 根据mapstate 跟新当前的contigs, 并且更新original_contigs里面的每个contig所包含的片段的个数和片段的中点 + original_contig *cont = Original_Contigs + Map_State->get_original_contig_id(pix1); - u32 nBytesPerText = 0; // 程序将一整张图分成了32*32个小格子,每一个格子被称作texture - ForLoop(Number_of_MipMaps) - { - nBytesPerText += Pow2((2 * textureRes--)); // sum([2**(2*i) for i in range(10, 10-Number_of_MipMaps, -1)])/2 - } - nBytesPerText >>= 1; // 除以 2 因为数据是经过压缩的 - Bytes_Per_Texture = nBytesPerText; // 一个texture 对应的字节数目 + u32 nPixels = Number_of_Pixels_1D; + f64 bpPerPixel = (f64)Total_Genome_Length / (f64)nPixels; - File_Atlas = PushArrayP(arena, file_atlas_entry, (Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1)); // variable to store the data entry + f64 bpStart = bpPerPixel * (f64)Map_State->contigRelCoords[pix1]; + + if (Edit_Pixels.editing) + { + stbsp_snprintf(line2, 64, "%s[%$.2fbp]", cont->name, bpStart); + } + else if (line1Done) + { + line1Done = 0; + } + + if (!line1Done) + { + f64 bpEnd = bpPerPixel * (f64)Map_State->contigRelCoords[pix2]; + original_contig *cont2 = Original_Contigs + Map_State->get_original_contig_id(pix2); + stbsp_snprintf(line1, 64, "%s[%$.2fbp] to %s[%$.2fbp]", cont->name, bpStart, cont2->name, bpEnd); + if (Edit_Pixels.editing) + { + line1Done = 1; + } + } - u32 currLocation = sizeof(Magic) + 8 + nBytesHeaderComp; // current localtion of the pointer = magic_check + (u32 compressed head length) + (u32 decompressed head length) + compressed header length - - // locating the pointers to data of entries - ForLoop((Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1)) // loop through total number of the textures - { - /* - 数据结构: - - magic (4 bytes) - - 8 bytes - - headercomp (nBytesHeaderComp bytes) - - a block of entry: - - number of bytes in u32 - - data + f32 textWidth_1 = fonsTextBounds(FontStash_Context, 0, 0, line1, 0, NULL); + f32 textWidth_2 = fonsTextBounds(FontStash_Context, 0, 0, line2, 0, NULL); + f32 textWidth_3 = fonsTextBounds(FontStash_Context, 0, 0, midLine, 0, NULL); + f32 textWidth = my_Max(textWidth_1, textWidth_2); + textWidth = my_Max(textWidth, textWidth_3); - */ - file_atlas_entry *entry = File_Atlas + index; // get the temprory pointer of the entry - u32 nBytes; // define a u32 to save the data - fread(&nBytes, 1, 4, file); // 读取四个字节, 前四个字节存储一个u32表示大小,后面的数据存储 - fseek(file, nBytes, SEEK_CUR); // 文件指针会向前移动 nBytes 个字节,SEEK_CUR在c标准库中被定义为1, after the loop, pointer file is moved to the end of the reading file - currLocation += 4; // 每移动一次,currLocation 增加 4 + f32 spacing = 3.0f; - entry->base = currLocation; // asign the current location to File_Atlas + index - entry->nBytes = nBytes; // asign the size to File_Atlas + index + glUniform4fv(Flat_Shader->colorLocation, 1, (f32 *)&Edit_Mode_Colours->bg); - currLocation += nBytes; - } + vert[0].x = ModelXToScreen(min) - spacing - textWidth; + vert[0].y = ModelYToScreen(-max) + spacing; + vert[1].x = ModelXToScreen(min) - spacing - textWidth; + vert[1].y = ModelYToScreen(-max) + spacing + textBoxHeight; + vert[2].x = ModelXToScreen(min) - spacing; + vert[2].y = ModelYToScreen(-max) + spacing + textBoxHeight; + vert[3].x = ModelXToScreen(min) - spacing; + vert[3].y = ModelYToScreen(-max) + spacing; - // Extensions - { - u08 magicTest[sizeof(Extension_Magic_Bytes[0])]; // define a u08 array, to check the end of the file, use this to read the extensions + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - while ((u64)(currLocation + sizeof(magicTest)) < fileSize) - { - u32 bytesRead = (u32)fread(magicTest, 1, sizeof(magicTest), file); // reading 4 bytes from the file - currLocation += bytesRead; - if (bytesRead == sizeof(magicTest)) + glUseProgram(UI_Shader->shaderProgram); + fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-max) + spacing, line1, 0); + + if (Edit_Pixels.editing) { - ForLoop(ArrayCount(Extension_Magic_Bytes)) - { - u08 foundExtension = 1; - u08 *magic = (u08 *)Extension_Magic_Bytes[index]; - ForLoop2(sizeof(magicTest)) - { - if (magic[index2] != magicTest[index2]) // magicTest is from the file, magic is from the definition // if magic this isn't the same, means no extensions found - { - foundExtension = 0; - break; - } - } + fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-max) + spacing + lh + 1.0f, midLine, 0); + fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-max) + spacing + (2.0f * (lh + 1.0f)), line2, 0); + } - if (foundExtension) // if extension is found - { - extension_type type = (extension_type)index; // get the type of the extension - u32 extensionSize = 0; - switch (type) - { - case extension_graph: - { - u32 compSize; - fread(&compSize, 1, sizeof(u32), file); // get the size of the extension data - graph *gph = PushStructP(arena, graph); // create the size to store the extension data - extension_node *node = PushStructP(arena, extension_node); - u08 *dataPlusName = PushArrayP(arena, u08, ((sizeof(u32) * Number_of_Pixels_1D) + sizeof(gph->name) )); // there are 1024 * 32 u32 numbers. Every single one of them represents the data on a pixel. -#pragma clang diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" - gph->data = (u32 *)(dataPlusName + sizeof(gph->name)); // the first 16 u32 are the name of the extention, which can include 16*32/8=64 u08 (char). -#pragma clang diagnostic pop - extensionSize += (compSize + sizeof(u32)); - u08 *compBuffer = PushArrayP(arena, u08, compSize); - fread(compBuffer, 1, compSize, file); // read the extension data from the file pointer - if (libdeflate_deflate_decompress(Decompressor, (const void *)compBuffer, compSize, (void *)dataPlusName, (sizeof(u32) * Number_of_Pixels_1D) + sizeof(gph->name), NULL)) // decompress compBuffer to dataPlusName - /* code from the libdeflate.h - enum libdeflate_result { - // Decompression was successful. - LIBDEFLATE_SUCCESS = 0, + if (Edit_Pixels.snap) + { + char *text = (char *)"Snap Mode On"; + textWidth = fonsTextBounds(FontStash_Context, 0, 0, text, 0, NULL); + glUseProgram(Flat_Shader->shaderProgram); + vert[0].x = ModelXToScreen(min) - spacing - textWidth; vert[0].y = ModelYToScreen(-min) + spacing; + vert[1].x = ModelXToScreen(min) - spacing - textWidth; vert[1].y = ModelYToScreen(-min) + spacing + lh; + vert[2].x = ModelXToScreen(min) - spacing; vert[2].y = ModelYToScreen(-min) + spacing + lh; + vert[3].x = ModelXToScreen(min) - spacing; vert[3].y = ModelYToScreen(-min) + spacing; - // Decompression failed because the compressed data was invalid, - * corrupt, or otherwise unsupported. - LIBDEFLATE_BAD_DATA = 1, + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + glUseProgram(UI_Shader->shaderProgram); + fonsDrawText(FontStash_Context, ModelXToScreen(min) - spacing - textWidth, ModelYToScreen(-min) + spacing, text, 0); + } - // A NULL 'actual_out_nbytes_ret' was provided, but the data would have - * decompressed to fewer than 'out_nbytes_avail' bytes. - LIBDEFLATE_SHORT_OUTPUT = 2, + { // draw the help text in the bottom right corner + fonsSetFont(FontStash_Context, Font_Bold); + fonsSetSize(FontStash_Context, text_box_size.font_size * Screen_Scale.x); + fonsVertMetrics(FontStash_Context, 0, 0, &lh); - // The data would have decompressed to more than 'out_nbytes_avail' - * bytes. - LIBDEFLATE_INSUFFICIENT_SPACE = 3, - }; - */ - { // unsuccessful decompress - FreeLastPushP(arena); // data - FreeLastPushP(arena); // graph - FreeLastPushP(arena); // node - } - else - { // successful decompress -#pragma clang diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" - u32 *namePtr = (u32 *)dataPlusName; // get a temp pointer,将原来的u08指针切换为u32指针,所指向的位置相同,只是不同的解释方式 -#pragma clang diagnostic pop - ForLoop2(ArrayCount(gph->name)) // get the graph name - { - gph->name[index2] = *(namePtr + index2); - } + std::vector helpTexts = { + (char *)"Edit Mode", + (char *)"E: exit, Q: undo, W: redo", + (char *)"Left Click: pickup, place", + (char *)"S: toggle snap mode", + (char *)"Middle Click / Spacebar: pickup whole sequence or (hold Shift): scaffold", + (char *)"Middle Click / Spacebar (while editing): invert sequence" + }; - node->type = type; // assign the gph to node - node->extension = gph; - AddExtension(node); // add node to extension - } - FreeLastPushP(arena); // compBuffer - } - break; - } - currLocation += extensionSize; - } + textBoxHeight = (f32)helpTexts.size() * (lh + 1.0f) - 1.0f; + spacing = 10.0f; + + textWidth = 0.f; + for (auto* i :helpTexts){ + textWidth = my_Max(textWidth, fonsTextBounds(FontStash_Context, 0, 0, i, 0, NULL)); + } + + glUseProgram(Flat_Shader->shaderProgram); + + vert[0].x = width - spacing - textWidth; vert[0].y = height - spacing - textBoxHeight; + vert[1].x = width - spacing - textWidth; vert[1].y = height - spacing; + vert[2].x = width - spacing; vert[2].y = height - spacing; + vert[3].x = width - spacing; vert[3].y = height - spacing - textBoxHeight; + + glBindBuffer(GL_ARRAY_BUFFER, Edit_Mode_Data->vbos[ptr]); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBindVertexArray(Edit_Mode_Data->vaos[ptr++]); + glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); + + glUseProgram(UI_Shader->shaderProgram); + + for (int i = 0; i < helpTexts.size(); i++) + { + fonsDrawText( + FontStash_Context, + width - spacing - textWidth, + height - spacing - textBoxHeight + (i * (lh + 1.0f)), + helpTexts[i], + 0); } } - else - { - break; - } } } - - fclose(file); // the positions and file pointers will be saved to texture_buffer_queue, which can be read in multi-thread mode - } - // Load Textures: reading from file and pushing into glTexture with index Contact_Matrix->textures - { - InitialiseTextureBufferQueue(arena, Texture_Buffer_Queue, Bytes_Per_Texture, filePath); // 初始化所有的queue texture_buffer_queue, 一共有8个queue,每个queue有8个buffer - - u32 nTextures = (Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1); // number of textures (528) - // u32 *packedTextureIndexes = PushArrayP(arena, u32, nTextures); // using a pointer of sequences of u32 as the texture index - u32* packedTextureIndexes = new u32[nTextures]; // (u32*) malloc(sizeof(u32) * nTextures); // this is released so new is ok - ThreadPoolAddTask(Thread_Pool, PopulateTextureLoadQueue, packedTextureIndexes); // multi-thread for loading the texture entries + // NK + if (UI_On) + { + glDisable(GL_CULL_FACE); + glEnable(GL_SCISSOR_TEST); - glActiveTexture(GL_TEXTURE0); // 所有的子图加载在 texture 0 - glGenTextures(1, &Contact_Matrix->textures); // 获取一个texture 存到 - glBindTexture(GL_TEXTURE_2D_ARRAY, Contact_Matrix->textures); // 绑定到当前的texture - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, (GLint)Number_of_MipMaps - 1); + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(UI_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glViewport(0, 0, (s32)width, (s32)height); - u32 resolution = Texture_Resolution; - ForLoop(Number_of_MipMaps) // 初始一个texture的一个维度有1024个像素点,放大后一个texture的一个维度只有32(1024 / 2**5)个像素点 - { // 初始化所有层级mipmap level的 gl_texture_2d_array - glCompressedTexImage3D ( - GL_TEXTURE_2D_ARRAY, // 指定纹理目标 例如GL_TEXTURE_3D - (GLint)index, // 指定纹理层级 - GL_COMPRESSED_RED_RGTC1, // texture数据的压缩格式 Unsigned normalized 1-component only. - (GLsizei)resolution, (GLsizei)resolution, (GLsizei)nTextures, // 纹理宽度, 高度, 深度 - 0, // border - (GLsizei)((resolution >> 1) * resolution * nTextures), // 每一个texture 的数据大小 (bytes),注意此处初始化了全部的 nTextures 个texture - 0); // 指向数据的指针 - resolution >>= 1; - } - - u32 ptr = 0; - printf("Loading textures...\n"); - ForLoop(Number_of_Textures_1D) - { - ForLoop2(Number_of_Textures_1D - index) { - volatile texture_buffer *loadedTexture = 0; // 使用volatile锁放弃存取优化。如果优化(不使用volatile),该值存取会被优化,可能会存放在寄存器中,而不是每次访问该值都会从内存中读取 - while (!loadedTexture) - { - #ifndef _WIN32 // unix - __atomic_load(&Current_Loaded_Texture, &loadedTexture, __ATOMIC_SEQ_CST); - #else // windows - // loadedTexture = InterlockedCompareExchangePointer(&Current_Loaded_Texture, nullptr, nullptr); - loadedTexture = (texture_buffer*)InterlockedCompareExchangePointer( - (PVOID volatile*)&Current_Loaded_Texture, - NULL, - NULL); - #endif // __WIN32 - } - u08 *texture = loadedTexture->texture; // 获取loadedtexture的texture的指针 - - // push the texture data (number_of_mipmaps) to the gl_texture_2d_array - resolution = Texture_Resolution; - for ( GLint level = 0; - level < (GLint)Number_of_MipMaps; - ++level ) - { - GLsizei nBytes = (GLsizei)(resolution * (resolution >> 1)); // 为什么每一个mipmap存储的像素点个数是 resolution ** 2 / 2 ,不应该是resolution**2吗 - // 此处将texture的数据压入到gl中,存储为gl_texture_2d_array的对象 - glCompressedTexSubImage3D( - GL_TEXTURE_2D_ARRAY, // target texture. Must be GL_TEXTURE_3D or GL_TEXTURE_2D_ARRAY. - level, // level-of-detail number. Level 0 is the base image level. Level n is the nth mipmap reduction image. - 0, 0, (GLint)Texture_Ptr, // Texture_Ptr is the index of Current_loaded_texture - (GLsizei)resolution, (GLsizei)resolution, 1, // Specifies the width, height, depth of the texture subimage. - GL_COMPRESSED_RED_RGTC1, // 压缩数据的格式,这就是为什么只用了一半的 (nBytes) bytes 的数据表示了 resolution * resolution 的图片 - nBytes, // the number of unsigned bytes of image data starting at the address specified by data. - texture // a pointer to the compressed image data in memory. - ); // 是否此处将texture给到 GL_TEXTURE_2D_ARRAY, check the doc on https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glCompressedTexSubImage3D.xhtml - - resolution >>= 1; - texture += nBytes; - } - - printf("\r%3d/%3d (%1.2f%%) textures loaded from disk...", Texture_Ptr + 1, nTextures, 100.0 * (f64)((f32)(Texture_Ptr + 1) / (f32)((Number_of_Textures_1D >> 1) * (Number_of_Textures_1D + 1)))); // echo out 读取到了第Texture_Ptr个texture - fflush(stdout); + const struct nk_draw_command *cmd; + void *vertices, *elements; + nk_draw_index *offset = 0; - AddTextureBufferToQueue(Texture_Buffer_Queue, (texture_buffer *)loadedTexture); // texture_buffer_queue 是全部的读取任务队列,读取后的buffer重新添加到队列中,供下一次读取。解决了从队列中弹出任务后任务队列空了的疑问。读取文件的任务队列在别的地方还会被调用吗? - FenceIn(Current_Loaded_Texture = 0); // 将临时变量置空,重新读取到current_loaded_texture后会跳出上面的循环 - __atomic_fetch_add(&Texture_Ptr, 1, 0); // 更新全局的 texture_ptr - ptr ++ ; - } - } + glBindVertexArray(NK_Device->vao); + glBindBuffer(GL_ARRAY_BUFFER, NK_Device->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NK_Device->ebo); - printf("\n"); - CloseTextureBufferQueueFiles(Texture_Buffer_Queue); // 关闭所有的buffer_texture中的文件流指针file,并且释放解压器内存 - delete[] packedTextureIndexes; // packedTextureIndexes 返回 - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); // 给之前已经压入的GL_TEXTURE_2D_ARRAY解除绑定 - } +#define MAX_VERTEX_MEMORY KiloByte(512) +#define MAX_ELEMENT_MEMORY KiloByte(128) + glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); - - { // Define Contact Matrix Vertex Data (vao, vbo) - glUseProgram(Contact_Matrix->shaderProgram); + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); - // Contact_Matrix->vaos = PushArrayP(arena, GLuint, Number_of_Textures_1D * Number_of_Textures_1D); // - // Contact_Matrix->vbos = PushArrayP(arena, GLuint, Number_of_Textures_1D * Number_of_Textures_1D); // - Contact_Matrix->vaos = new GLuint[ Number_of_Textures_1D * Number_of_Textures_1D]; // (GLuint*) malloc(sizeof(GLuint) * Number_of_Textures_1D * Number_of_Textures_1D ); // TODO make sure the memory is freed before the re-allocation - Contact_Matrix->vbos = new GLuint[ Number_of_Textures_1D * Number_of_Textures_1D]; // (GLuint*) malloc(sizeof(GLuint) * Number_of_Textures_1D * Number_of_Textures_1D ); + { + nk_convert_config config; + static const nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; - setContactMatrixVertexArray(Contact_Matrix, false, false); // set the vertex data of the contact matrix + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(nk_glfw_vertex); + config.null = NK_Device->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = NK_ANTI_ALIASING_ON; + config.line_AA = NK_ANTI_ALIASING_ON; - } + { + nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(NK_Context, &NK_Device->cmds, &vbuf, &ebuf, &config); + } + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - // Texture Pixel Lookups texture像素查找 - { - GLuint pixStart, pixStartTex, pixRearrage, pixRearrageTex; - - u32 nTex = (Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1); // (32 + 1) * 32 / 2 = 528 - u32 nPix1D = Number_of_Textures_1D * Texture_Resolution; // 32 * 1024 + nk_draw_foreach(cmd, NK_Context, &NK_Device->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x), + (GLint)(height - cmd->clip_rect.y - cmd->clip_rect.h), + (GLint)(cmd->clip_rect.w), + (GLint)(cmd->clip_rect.h)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + } - glActiveTexture(GL_TEXTURE2); // 调用 glActiveTexture 函数,可以选择当前活动的纹理单元,并且后续的纹理操作都会影响到该纹理单元 + ChangeSize((s32)width, (s32)height); + glDisable(GL_SCISSOR_TEST); + glEnable(GL_CULL_FACE); - u32 *pixStartLookup = PushArrayP(arena, u32, 2 * nTex); // 申请空间 2 * 528 个 u32 - u32 ptr = 0; - for (u32 i = 0; i < Number_of_Textures_1D; i ++ ) // 遍历每一个texture - { - for (u32 j = i ; j < Number_of_Textures_1D; j ++ ) - { - pixStartLookup[ptr++] = (u32)(j * Texture_Resolution); // 列 * 1024 双数索引是列,单数是行 - pixStartLookup[ptr++] = (u32)(i * Texture_Resolution); // 行 * 1024 - } + nk_clear(NK_Context); } - glGenBuffers(1, &pixStart); // 生成一个缓冲区对象,并将其标识符存储到 pixStart 变量中。这样,pixStart 变量就可以用于引用这个生成的缓冲区对象 - glBindBuffer(GL_TEXTURE_BUFFER, pixStart); // 缓冲区对象 pixStart 就会被绑定到当前的纹理缓冲区上,后续的操作会影响这个缓冲区对象 - glBufferData(GL_TEXTURE_BUFFER, sizeof(u32) * 2 * nTex, pixStartLookup, GL_STATIC_DRAW); // 将数据从 pixStartLookup 指向的内存区域拷贝到绑定到 GL_TEXTURE_BUFFER 目标的缓冲区对象中,大小为 sizeof(u32) * 2 * nTex 字节,并且告诉 OpenGL 这些数据是静态的,不会频繁地变化 + if (Loading) + { + u32 colour = glfonsRGBA(Theme_Colour.r, Theme_Colour.g, Theme_Colour.b, Theme_Colour.a); - glGenTextures(1, &pixStartTex); // 生成一个纹理对象,并将其标识符存储到 pixStartTex 变量中 - glBindTexture(GL_TEXTURE_BUFFER, pixStartTex); // 纹理对象 pixStartTex 就会被绑定到当前的纹理缓冲区上,后续的纹理操作(比如使用 glTexBuffer 函数将其与纹理缓冲区对象关联)将会影响到这个纹理对象 - glTexBuffer( // 纹理缓冲区对象与缓冲区对象关联的函数 - GL_TEXTURE_BUFFER, // 要关联到缓冲区的纹理目标,这里是 GL_TEXTURE_BUFFER,表示纹理缓冲区。 - GL_RG32UI, // 纹理缓冲区数据的格式, GL_RG32UI表示每个像素由两个u32组成,一个红色分量和一个绿色分量 - pixStart); // 缓冲区对象的标识符,这个缓冲区会与纹理缓冲区关联 + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, 64.0f * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_MIDDLE); + fonsSetFont(FontStash_Context, Font_Bold); + fonsSetColor(FontStash_Context, colour); - Contact_Matrix->pixelStartLookupBuffer = pixStart; // 缓冲区对象标识符 - Contact_Matrix->pixelStartLookupBufferTex = pixStartTex; // 纹理对象标识符 + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glViewport(0, 0, (s32)width, (s32)height); + fonsDrawText(FontStash_Context, width * 0.5f, height * 0.5f, "Loading...", 0); - FreeLastPushP(arena); // pixStartLookup 释放存放像素点开始的空间 + ChangeSize((s32)width, (s32)height); + } - glActiveTexture(GL_TEXTURE3); // 激活第三个texture,以下代码会影响第三个texture - u32 *pixRearrageLookup = PushArrayP(arena, u32, nPix1D); // allocte 1024 * 32 u32 memory - for (u32 i = 0 ; i < nPix1D; i ++ ) + if (auto_sort_state) { - pixRearrageLookup[i] = (u32)i; // assign the index to the pixRearrageLookup - } + u32 colour = glfonsRGBA(Theme_Colour.r, Theme_Colour.g, Theme_Colour.b, Theme_Colour.a); - glGenBuffers(1, &pixRearrage); // generate a buffer object - glBindBuffer(GL_TEXTURE_BUFFER, pixRearrage); // bind as a texture buffer for the current buffer - glBufferData(GL_TEXTURE_BUFFER, sizeof(u32) * nPix1D, pixRearrageLookup, GL_DYNAMIC_DRAW); // copy the data from pixRearrageLookup to the buffer object, and tell OpenGL that the data is dynamic and will change frequently + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, 64.0f * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_MIDDLE); + fonsSetFont(FontStash_Context, Font_Bold); + fonsSetColor(FontStash_Context, colour); - glGenTextures(1, &pixRearrageTex); - glBindTexture(GL_TEXTURE_BUFFER, pixRearrageTex); - glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, pixRearrage); + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glViewport(0, 0, (s32)width, (s32)height); + fonsDrawText(FontStash_Context, width * 0.5f, height * 0.5f, "Pixel Sorting...", 0); - Contact_Matrix->pixelRearrangmentLookupBuffer = pixRearrage; // 32 * 1024 u32, this is a ascending order of 0 to 1024 * 32 - Contact_Matrix->pixelRearrangmentLookupBufferTex = pixRearrageTex; // texture 的索引 + ChangeSize((s32)width, (s32)height); + } - FreeLastPushP(arena); // free pixRearrageLookup as it has already been copied to the buffer object with index pixRearrage + if (auto_cut_state) + { + u32 colour = glfonsRGBA(Theme_Colour.r, Theme_Colour.g, Theme_Colour.b, Theme_Colour.a); - glUniform1ui(glGetUniformLocation(Contact_Matrix->shaderProgram, "pixpertex"), Texture_Resolution); // 将1024传递给 pixpertex,每个texture有多少个像素点 - glUniform1f(glGetUniformLocation(Contact_Matrix->shaderProgram, "oopixpertex"), 1.0f / (f32)Texture_Resolution); // 将 1 / 1024 传递给 oopixpertex - glUniform1ui(glGetUniformLocation(Contact_Matrix->shaderProgram, "ntex1dm1"), Number_of_Textures_1D - 1); // 将31传递给 ntex1dm1 + fonsClearState(FontStash_Context); + fonsSetSize(FontStash_Context, 64.0f * Screen_Scale.x); + fonsSetAlign(FontStash_Context, FONS_ALIGN_CENTER | FONS_ALIGN_MIDDLE); + fonsSetFont(FontStash_Context, Font_Bold); + fonsSetColor(FontStash_Context, colour); - glActiveTexture(GL_TEXTURE0); // 关闭激活的texture 3 - } + glUseProgram(UI_Shader->shaderProgram); + glUniformMatrix4fv(Flat_Shader->matLocation, 1, GL_FALSE, textNormalMat); + glViewport(0, 0, (s32)width, (s32)height); + fonsDrawText(FontStash_Context, width * 0.5f, height * 0.5f, "Pixel cut...", 0); - GLuint posAttribFlatShader = (GLuint)glGetAttribLocation(Flat_Shader->shaderProgram, "position"); - u32 pad = 0; - auto PushGenericBuffer = [posAttribFlatShader, pad, arena] (quad_data **quadData, u32 numberOfBuffers) - { - (void)pad; + ChangeSize((s32)width, (s32)height); + } + } +} - *quadData = PushStructP(arena, quad_data); - (*quadData)->vaos = PushArrayP(arena, GLuint, numberOfBuffers); - (*quadData)->vbos = PushArrayP(arena, GLuint, numberOfBuffers); - (*quadData)->nBuffers = numberOfBuffers; +global_variable +file_atlas_entry * +File_Atlas; - glUseProgram(Flat_Shader->shaderProgram); +global_function +void +LoadTexture(void *in) +{ // + GLuint *textureHandle = (GLuint *)in; - ForLoop(numberOfBuffers) - { - glGenVertexArrays(1, (*quadData)->vaos + index); - glBindVertexArray((*quadData)->vaos[index]); + texture_buffer *buffer = TakeTextureBufferFromQueue_Wait(Texture_Buffer_Queue); // take a buffer from the queue + // assign the texture handle to the buffer + buffer->x = (u16)(*textureHandle >> 16); // the former 16 bits represent the row number + buffer->y = (u16)(*textureHandle & ((1 << 16) - 1)); // the lower 16 bits represnet the column number - glGenBuffers(1, (*quadData)->vbos + index); - glBindBuffer(GL_ARRAY_BUFFER, (*quadData)->vbos[index]); - glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(vertex), NULL, GL_DYNAMIC_DRAW); + /* + rule for linear ordering + Ordering of the texture boxes + ======================== + 00 01 02 03 04 ... 30 31 + // 32 33 34 35 ... 61 62 + // // 63 64 65 ... 91 92 + // // // ... + ======================== + */ + // u32 linearIndex = (buffer->x * (Number_of_Textures_1D - 1)) - ((buffer->x * (buffer->x-1)) >> 1) + buffer->y; // get the index accoding to index in x and y diretion + u32 linearIndex = texture_id_cal((u32)buffer->x, (u32)buffer->y, (u32)Number_of_Textures_1D); - glEnableVertexAttribArray(posAttribFlatShader); - glVertexAttribPointer(posAttribFlatShader, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), 0); + file_atlas_entry *entry = File_Atlas + linearIndex; // set a tempory pointer + u32 nBytes = entry->nBytes; // base is the beginning, nBytes is the size + fseek(buffer->file, entry->base, SEEK_SET); // move from the begining to the start of this texture numbered as linearIndex - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); - } - }; + fread(buffer->compressionBuffer, 1, nBytes, buffer->file); // read texture to the buffer - // Grid Data - { - PushGenericBuffer(&Grid_Data, 2 * (Max_Number_of_Contigs + 1)); + if ( + libdeflate_deflate_decompress( + buffer->decompressor, // compresser object + (const void *)buffer->compressionBuffer, // buffer data before decompressing + nBytes, // size before decompressing + (void *)buffer->texture, // place to store the data after decompressing + Bytes_Per_Texture, // size after decompressing + NULL) + ) + { // 解压压缩的texture到 buffer->texture + fprintf(stderr, "Could not decompress texture from disk\n"); } - // Label Box Data + while (true) { - PushGenericBuffer(&Label_Box_Data, 2 * Max_Number_of_Contigs); + FenceIn(u32 texture_index_tmp = static_cast(Texture_Ptr)); + if (linearIndex == texture_index_tmp) break; } - //Scale Bar Data - { - PushGenericBuffer(&Scale_Bar_Data, 4 * (2 + MaxTicksPerScaleBar)); - } + FenceIn(Current_Loaded_Texture = buffer); // assign buffer to current_loaded_texture + +} - //Contig Colour Bars +global_function +void +PopulateTextureLoadQueue(void *in) // 填充已经初始化过的 所有的queue,填充的任务为所有的528个texture的读取任务 +{ + u32 *packedTextureIndexes = (u32 *)in; // 将空指针转化为u32 + u32 ptr = 0; + ForLoop(Number_of_Textures_1D) // row number { - PushGenericBuffer(&Contig_ColourBar_Data, Max_Number_of_Contigs); + for (u32 index2 = index; index2 < Number_of_Textures_1D; index2++ ) // column number + { + packedTextureIndexes[ptr] = (index << 16) | (index2); // first 16 bits is the raw number and the second 16 bits is the column number, there will be problem if number of texture in one dimension is larger than 2^16, but it is not possible ... just for a reminder + ThreadPoolAddTask(Thread_Pool, LoadTexture, (void *)(packedTextureIndexes + ptr++)); // 填充当前的任务队列,输入为对应的texture的行、列编号 + } } +} - //Scaff Bars - { - PushGenericBuffer(&Scaff_Bar_Data, Max_Number_of_Contigs); - } - // Extensions +/* +adjust the gamma thus adjust the contrast +*/ +global_function +void +AdjustColorMap(s32 dir) +{ + f32 unit = 0.05f; + f32 delta = unit * (dir > 0 ? 1.0f : -1.0f); + + Color_Maps->controlPoints[1] = my_Max( + my_Min(Color_Maps->controlPoints[1] + delta, Color_Maps->controlPoints[2]), + Color_Maps->controlPoints[0] + ); + + glUseProgram(Contact_Matrix->shaderProgram); + glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); +} + + +/* +change to the last or next color map +*/ +global_function +void +NextColorMap(s32 dir) +{ + glActiveTexture(GL_TEXTURE1); + + if (useCustomOrder) { - push_extensions_to_opengl(arena, 0); - } + // Find the current index in the custom order + u32 currentIndex = 0; + for (u32 i = 0; i < userColourMapOrder.nMaps; i++) + { + if (userColourMapOrder.order[i] == Color_Maps->currMap) + { + currentIndex = i; + break; + } + } -#ifdef Internal + // Calculate the next index in the custom order + u32 nextIndex = dir > 0 ? (currentIndex == (userColourMapOrder.nMaps - 1) ? 0 : currentIndex + 1) : (currentIndex == 0 ? userColourMapOrder.nMaps - 1 : currentIndex - 1); + + Color_Maps->currMap = userColourMapOrder.order[nextIndex]; + } + else { - PushGenericBuffer(&Texture_Tile_Grid, 2 * (Number_of_Textures_1D + 1)); - PushGenericBuffer(&QuadTree_Data, 1 << (2 * (Waypoints_Quadtree_Levels + 1))); + Color_Maps->currMap = dir > 0 ? (Color_Maps->currMap == (Color_Maps->nMaps - 1) ? 0 : Color_Maps->currMap + 1) : (Color_Maps->currMap == 0 ? Color_Maps->nMaps - 1 : Color_Maps->currMap - 1); } -#endif - // Map Editor + glBindTexture(GL_TEXTURE_BUFFER, Color_Maps->maps[Color_Maps->currMap]); + + glActiveTexture(GL_TEXTURE0); +} + + +global_function +u32 +GetOrderedColorMapIndex(u32 displayIndex) +{ + if (displayIndex < userColourMapOrder.nMaps) { - Map_Editor = PushStructP(arena, map_editor); - Map_Editor->nEdits = 0; - Map_Editor->editStackPtr = 0; - Map_Editor->nUndone = 0; - Map_Editor->edits = PushArrayP(arena, map_edit, Edits_Stack_Size); + return userColourMapOrder.order[displayIndex]; } + return displayIndex; +} - // Waypoint Editor + +global_function void +InitializeColorMapOrder() +{ + userColourMapOrder.nMaps = Color_Maps->nMaps; + for (u32 i = 0; i < userColourMapOrder.nMaps; i++) { - Waypoint_Editor = PushStructP(arena, waypoint_editor); - Waypoint_Editor->nWaypointsActive = 0; - Waypoint_Editor->freeWaypoints = {}; - Waypoint_Editor->activeWaypoints = {}; - Waypoint_Editor->freeWaypoints.next = PushArrayP(arena, waypoint, Waypoints_Stack_Size); + userColourMapOrder.order[i] = i; + } +} - Waypoint_Editor->quadtree = PushQuadTree(arena); - Waypoint_Editor->freeNodes = {}; - Waypoint_Editor->freeNodes.next = PushArrayP(arena, waypoint_quadtree_node, Waypoints_Stack_Size); - Waypoint_Editor->freeWaypoints.next->prev = &Waypoint_Editor->freeWaypoints; - Waypoint_Editor->freeNodes.next->prev = &Waypoint_Editor->freeNodes; - ForLoop(Waypoints_Stack_Size - 1) - { - waypoint *wayp = Waypoint_Editor->freeWaypoints.next + index; - wayp->next = (wayp + 1); - (wayp + 1)->prev = wayp; - (wayp + 1)->next = 0; +global_variable +GLuint +Quad_EBO; - waypoint_quadtree_node *node = Waypoint_Editor->freeNodes.next + index; - node->next = (node + 1); - (node + 1)->prev = node; - (node + 1)->next = 0; - } - } +enum +load_file_result +{ + ok, + fileErr, + decompErr, +}; - // 为添加的功能初始化指针 - { - // 给pixel_density_extension分配内存 - // pixel_density_extension = new Extension_Graph_Data(Number_of_Pixels_1D); - - // 为 copy textures to cpu 分配内存 - if (textures_array_ptr) delete textures_array_ptr; - textures_array_ptr = new TexturesArray4AI( - Number_of_Textures_1D, - Texture_Resolution, - *fileName, - Contigs - ); - // prepare before reading textures - f32 original_color_control_points[3]; - prepare_before_copy(original_color_control_points); - textures_array_ptr->copy_buffer_to_textures( - Contact_Matrix, - false // show_flag - ); - restore_settings_after_copy(original_color_control_points); +global_variable +libdeflate_decompressor * +Decompressor; - // 为 Pixel Cut 存储的数据分配空间,初始化,并且将数据push到extension中 - if (frag_cut_cal_ptr) delete frag_cut_cal_ptr; - frag_cut_cal_ptr = new FragCutCal( - textures_array_ptr, - Number_of_Pixels_1D, - auto_curation_state.auto_cut_diag_window_for_pixel_mean); +global_variable +libdeflate_compressor * +Compressor; - // clear mem for textures - textures_array_ptr->clear_textures(); - - // 添加数据到extensions - std::string graph_name = "pixel_discontinuity"; - bool is_pix_density_added = Extensions.is_graph_name_exist(graph_name); - if (!is_pix_density_added) // 未添加pixel density +global_variable +u08 Magic[] = {'p', 's', 't', 'm'}; + +global_function +FILE * +TestFile(const char *fileName, u64 *fileSize = 0) +{ + FILE *file=0; + { + file = fopen(fileName, "rb"); + if (!file) { - u32 added_num = Extensions.get_num_extensions(); - u32* graph_data = new u32[Number_of_Pixels_1D]; - f32 max_desity = 0.f; - for (auto& i : frag_cut_cal_ptr->hic_pixel_density_origin) max_desity = std::max(max_desity, i); - for (u32 i=0; i < Number_of_Pixels_1D; i ++) - { - graph_data[i] = (u32)(255 * (1.001f- frag_cut_cal_ptr->hic_pixel_density_origin[i] / max_desity)); - } - add_graph_to_extensions(arena, (u32*)graph_name.c_str(), graph_data); - delete[] graph_data; - - push_extensions_to_opengl(arena, added_num, 0.05f); // push hic_pixel_density to opengl buffer +#ifdef DEBUG + fprintf(stderr, "The file is not available: \'%s\' [errno %d] \n", fileName, errno); + exit(errno); +#endif } - else + else { - fmt::print("hic_pixel_density extension already exists\n"); - assert(false); - } - } + if (fileSize) + { + fseek(file, 0, SEEK_END); + *fileSize = (u64)ftell(file); + fseek(file, 0, SEEK_SET); + } - FenceIn(File_Loaded = 1); + u08 magicTest[sizeof(Magic)]; - if (LoadState(*headerHash)) LoadState(*headerHash + 1); - return(ok); + u32 bytesRead = (u32)fread(magicTest, 1, sizeof(magicTest), file); + if (bytesRead == sizeof(magicTest)) + { + ForLoop(sizeof(Magic)) // #define ForLoop(n) for (u32 index=0; indexname) )); + gph->data = (u32 *)(dataPlusName + sizeof(gph->name)); + u32 *namePtr = (u32 *)graph_name; + for (u32 i = 0; i < ArrayCount(gph->name); i++ ) // get the graph name { - case THEME_WHITE: - { - table[NK_COLOR_TEXT] = nk_rgba(70, 70, 70, 255); - table[NK_COLOR_WINDOW] = nk_rgba(175, 175, 175, 255); - table[NK_COLOR_HEADER] = nk_rgba(175, 175, 175, 255); - table[NK_COLOR_BORDER] = nk_rgba(0, 0, 0, 255); - table[NK_COLOR_BUTTON] = nk_rgba(185, 185, 185, 255); - table[NK_COLOR_BUTTON_HOVER] = nk_rgba(170, 170, 170, 255); - table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(160, 160, 160, 255); - table[NK_COLOR_TOGGLE] = nk_rgba(150, 150, 150, 255); - table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(120, 120, 120, 255); - table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(175, 175, 175, 255); - table[NK_COLOR_SELECT] = nk_rgba(190, 190, 190, 255); - table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(175, 175, 175, 255); - table[NK_COLOR_SLIDER] = nk_rgba(190, 190, 190, 255); - table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(80, 80, 80, 255); - table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(70, 70, 70, 255); - table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(60, 60, 60, 255); - table[NK_COLOR_PROPERTY] = nk_rgba(175, 175, 175, 255); - table[NK_COLOR_EDIT] = nk_rgba(150, 150, 150, 255); - table[NK_COLOR_EDIT_CURSOR] = nk_rgba(0, 0, 0, 255); - table[NK_COLOR_COMBO] = nk_rgba(175, 175, 175, 255); - table[NK_COLOR_CHART] = nk_rgba(160, 160, 160, 255); - table[NK_COLOR_CHART_COLOR] = nk_rgba(45, 45, 45, 255); - table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); - table[NK_COLOR_SCROLLBAR] = nk_rgba(180, 180, 180, 255); - table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(140, 140, 140, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(150, 150, 150, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(160, 160, 160, 255); - table[NK_COLOR_TAB_HEADER] = nk_rgba(180, 180, 180, 255); - } - break; + gph->name[i] = *(namePtr + i); + } + for (u32 i = 0; i < Number_of_Pixels_1D; i++) // get the graph data + { + gph->data[i] = *(graph_data + i); + } + node->type = extension_graph; // assign the gph to node + node->extension = gph; + AddExtension(node); // add node to extension + + return ; +} - case THEME_RED: - { - table[NK_COLOR_TEXT] = nk_rgba(190, 190, 190, 255); - table[NK_COLOR_WINDOW] = nk_rgba(30, 33, 40, 215); - table[NK_COLOR_HEADER] = nk_rgba(181, 45, 69, 220); - table[NK_COLOR_BORDER] = nk_rgba(51, 55, 67, 255); - table[NK_COLOR_BUTTON] = nk_rgba(181, 45, 69, 255); - table[NK_COLOR_BUTTON_HOVER] = nk_rgba(190, 50, 70, 255); - table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(195, 55, 75, 255); - table[NK_COLOR_TOGGLE] = nk_rgba(51, 55, 67, 255); - table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 60, 60, 255); - table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(181, 45, 69, 255); - table[NK_COLOR_SELECT] = nk_rgba(51, 55, 67, 255); - table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(181, 45, 69, 255); - table[NK_COLOR_SLIDER] = nk_rgba(51, 55, 67, 255); - table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(181, 45, 69, 255); - table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(186, 50, 74, 255); - table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(191, 55, 79, 255); - table[NK_COLOR_PROPERTY] = nk_rgba(51, 55, 67, 255); - table[NK_COLOR_EDIT] = nk_rgba(51, 55, 67, 225); - table[NK_COLOR_EDIT_CURSOR] = nk_rgba(190, 190, 190, 255); - table[NK_COLOR_COMBO] = nk_rgba(51, 55, 67, 255); - table[NK_COLOR_CHART] = nk_rgba(51, 55, 67, 255); - table[NK_COLOR_CHART_COLOR] = nk_rgba(170, 40, 60, 255); - table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); - table[NK_COLOR_SCROLLBAR] = nk_rgba(30, 33, 40, 255); - table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); - table[NK_COLOR_TAB_HEADER] = nk_rgba(181, 45, 69, 220); - } - break; - case THEME_BLUE: - { - table[NK_COLOR_TEXT] = nk_rgba(20, 20, 20, 255); - table[NK_COLOR_WINDOW] = nk_rgba(202, 212, 214, 215); - table[NK_COLOR_HEADER] = nk_rgba(137, 182, 224, 220); - table[NK_COLOR_BORDER] = nk_rgba(140, 159, 173, 255); - table[NK_COLOR_BUTTON] = nk_rgba(137, 182, 224, 255); - table[NK_COLOR_BUTTON_HOVER] = nk_rgba(142, 187, 229, 255); - table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(147, 192, 234, 255); - table[NK_COLOR_TOGGLE] = nk_rgba(177, 210, 210, 255); - table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(182, 215, 215, 255); - table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(137, 182, 224, 255); - table[NK_COLOR_SELECT] = nk_rgba(177, 210, 210, 255); - table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(137, 182, 224, 255); - table[NK_COLOR_SLIDER] = nk_rgba(177, 210, 210, 255); - table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(137, 182, 224, 245); - table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(142, 188, 229, 255); - table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(147, 193, 234, 255); - table[NK_COLOR_PROPERTY] = nk_rgba(210, 210, 210, 255); - table[NK_COLOR_EDIT] = nk_rgba(210, 210, 210, 225); - table[NK_COLOR_EDIT_CURSOR] = nk_rgba(20, 20, 20, 255); - table[NK_COLOR_COMBO] = nk_rgba(210, 210, 210, 255); - table[NK_COLOR_CHART] = nk_rgba(210, 210, 210, 255); - table[NK_COLOR_CHART_COLOR] = nk_rgba(137, 182, 224, 255); - table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); - table[NK_COLOR_SCROLLBAR] = nk_rgba(190, 200, 200, 255); - table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); - table[NK_COLOR_TAB_HEADER] = nk_rgba(156, 193, 220, 255); - } - break; - case THEME_DARK: - { - table[NK_COLOR_TEXT] = nk_rgba(210, 210, 210, 255); - table[NK_COLOR_WINDOW] = nk_rgba(57, 67, 71, 215); - table[NK_COLOR_HEADER] = nk_rgba(51, 51, 56, 220); - table[NK_COLOR_BORDER] = nk_rgba(46, 46, 46, 255); - table[NK_COLOR_BUTTON] = nk_rgba(48, 83, 111, 255); - table[NK_COLOR_BUTTON_HOVER] = nk_rgba(58, 93, 121, 255); - table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(63, 98, 126, 255); - table[NK_COLOR_TOGGLE] = nk_rgba(50, 58, 61, 255); - table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 53, 56, 255); - table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(48, 83, 111, 255); - table[NK_COLOR_SELECT] = nk_rgba(57, 67, 61, 255); - table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(48, 83, 111, 255); - table[NK_COLOR_SLIDER] = nk_rgba(50, 58, 61, 255); - table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(48, 83, 111, 245); - table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); - table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); - table[NK_COLOR_PROPERTY] = nk_rgba(50, 58, 61, 255); - table[NK_COLOR_EDIT] = nk_rgba(50, 58, 61, 225); - table[NK_COLOR_EDIT_CURSOR] = nk_rgba(210, 210, 210, 255); - table[NK_COLOR_COMBO] = nk_rgba(50, 58, 61, 255); - table[NK_COLOR_CHART] = nk_rgba(50, 58, 61, 255); - table[NK_COLOR_CHART_COLOR] = nk_rgba(48, 83, 111, 255); - table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba(255, 0, 0, 255); - table[NK_COLOR_SCROLLBAR] = nk_rgba(50, 58, 61, 255); - table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(48, 83, 111, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); - table[NK_COLOR_TAB_HEADER] = nk_rgba(48, 83, 111, 255); - } - break; +global_function +void +push_extensions_to_opengl(memory_arena *arena, u32 added_index = 0, f32 scale=-1.f) +{ + u32 exIndex = 0; + // TraverseLinkedList(Extensions.head, extension_node) - case THEME_BLACK: - case THEME_COUNT: - themeSet = 0; + extension_node* node = Extensions.head ; + while (added_index--) + { + node = node->next; // 移动到最后一个节点 + exIndex ++ ; } + for ( ; node; node = node->next) { + + switch (node->type) + { + case extension_graph: + { + graph *gph = (graph *)node->extension; // get the graph ptr and assigned to gph +#define DefaultGraphScale 0.2f +#define DefaultGraphBase 32.0f +#define DefaultGraphLineSize 1.0f +#define DefaultGraphColour {0.1f, 0.8f, 0.7f, 1.0f} + gph->scale = scale>0.f? scale : DefaultGraphScale; + gph->base = DefaultGraphBase; + gph->lineSize = DefaultGraphLineSize; + gph->colour = DefaultGraphColour; + gph->on = 0; - if (themeSet) nk_style_from_table(ctx, table, Screen_Scale.x, Screen_Scale.y); - else nk_style_default(ctx, Screen_Scale.x, Screen_Scale.y); + gph->shader = PushStructP(arena, editable_plot_shader); + gph->shader->shaderProgram = CreateShader(FragmentSource_EditablePlot.c_str(), VertexSource_EditablePlot.c_str(), GeometrySource_EditablePlot.c_str()); - Theme_Colour = table[NK_COLOR_BUTTON_ACTIVE]; - - NK_Context->style.slider.show_buttons = nk_true; + glUseProgram(gph->shader->shaderProgram); + glBindFragDataLocation(gph->shader->shaderProgram, 0, "outColor"); + gph->shader->matLocation = glGetUniformLocation(gph->shader->shaderProgram, "matrix"); + gph->shader->colorLocation = glGetUniformLocation(gph->shader->shaderProgram, "color"); + gph->shader->yScaleLocation = glGetUniformLocation(gph->shader->shaderProgram, "yscale"); + gph->shader->yTopLocation = glGetUniformLocation(gph->shader->shaderProgram, "ytop"); + gph->shader->lineSizeLocation = glGetUniformLocation(gph->shader->shaderProgram, "linewidth"); - Current_Theme = theme; -} + glUniform1i(glGetUniformLocation(gph->shader->shaderProgram, "pixrearrangelookup"), 3); + glUniform1i(glGetUniformLocation(gph->shader->shaderProgram, "yvalues"), 4 + (s32)exIndex); + u32 nValues = Number_of_Pixels_1D; + auto* xValues = new f32[nValues]; // allocate memory for xValues, actually, x is the coordinate of the pixel + auto* yValues = new f32[nValues]; + + u32 max = 0; + ForLoop(Number_of_Pixels_1D) + { + max = my_Max(max, gph->data[index]); + } + ForLoop(Number_of_Pixels_1D) + { + xValues[index] = (f32)index; + yValues[index] = (f32)gph->data[index] / (f32)max ; // normalise the data + } -global_function -void -Setup() -{ - Decompressor = libdeflate_alloc_decompressor(); - if (!Decompressor) - { - fprintf(stderr, "Could not allocate decompressor\n"); - exit(1); - } + glActiveTexture(GL_TEXTURE4 + exIndex++); - Compressor = libdeflate_alloc_compressor(12); - if (!Compressor) - { - fprintf(stderr, "Could not allocate compressor\n"); - exit(1); - } + GLuint yVal, yValTex; - Texture_Buffer_Queue = PushStruct(Working_Set, texture_buffer_queue); + // generate a buffer object named yVal and save data to yVal + glGenBuffers(1, &yVal); + glBindBuffer(GL_TEXTURE_BUFFER, yVal); + glBufferData(GL_TEXTURE_BUFFER, sizeof(f32) * nValues, yValues, GL_STATIC_DRAW); - Meta_Data = PushStruct(Working_Set, meta_data); - memset(Meta_Data, 0, sizeof(meta_data)); - MetaData_Active_Tag = 0; - ForLoop(ArrayCount(Default_Tags)) strcpy((char *)Meta_Data->tags[MetaData_Active_Tag + index], Default_Tags[index]); - if (Grey_Out_Settings) - { - delete Grey_Out_Settings; - Grey_Out_Settings = nullptr; - } - Grey_Out_Settings = new GreyOutSettings(Meta_Data); - if (user_profile_settings_ptr) - { - delete user_profile_settings_ptr; - user_profile_settings_ptr = nullptr; - } - user_profile_settings_ptr = new UserProfileSettings(); + // add texture and link to the buffer object + glGenTextures(1, &yValTex); + glBindTexture(GL_TEXTURE_BUFFER, yValTex); + glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, yVal); - // Contig Name Label UI - { - Contig_Name_Labels = PushStruct(Working_Set, ui_colour_element_bg); - Contig_Name_Labels->on = 0; - Contig_Name_Labels->fg = Yellow_Text_Float; - Contig_Name_Labels->bg = Grey_Background; -#define DefaultNameLabelTextSize 32.0f - Contig_Name_Labels->size = DefaultNameLabelTextSize; - } - - // Scale Bar UI - { - Scale_Bars = PushStruct(Working_Set, ui_colour_element_bg); - Scale_Bars->on = 0; - Scale_Bars->fg = Red_Text_Float; - Scale_Bars->bg = Grey_Background; -#define DefaultScaleBarSize 20.0f - Scale_Bars->size = DefaultScaleBarSize; - } - - // Grid UI - { - Grid = PushStruct(Working_Set, ui_colour_element); - Grid->on = 1; - Grid->bg = Grey_Background; -#define DefaultGridSize 0.00025f - Grid->size = DefaultGridSize; - } + gph->shader->yValuesBuffer = yVal; + gph->shader->yValuesBufferTex = yValTex; + + // add the vertext data into buffer + glGenBuffers(1, &gph->vbo); + glBindBuffer(GL_ARRAY_BUFFER, gph->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * nValues, xValues, GL_STATIC_DRAW); - // Contig Ids UI - { - Contig_Ids = PushStruct(Working_Set, ui_colour_element); - Contig_Ids->on = 1; -#define DefaultContigIdSize (DefaultGridSize * 3.0f) - Contig_Ids->size = DefaultContigIdSize; - } + // gen the vertex array object + glGenVertexArrays(1, &gph->vao); + glBindVertexArray(gph->vao); - // Tool Tip UI - { - Tool_Tip = PushStruct(Working_Set, ui_colour_element_bg); - Tool_Tip->on = 1; - Tool_Tip->fg = Yellow_Text_Float; - Tool_Tip->bg = Grey_Background; -#define DefaultToolTipTextSize 20.0f - Tool_Tip->size = DefaultToolTipTextSize; - } + GLuint posAttrib = (GLuint)glGetAttribLocation(gph->shader->shaderProgram, "position"); + glEnableVertexAttribArray(posAttrib); + glVertexAttribPointer(posAttrib, 1, GL_FLOAT, GL_FALSE, 0, 0); - // Edit Mode Colours - { - Edit_Mode_Colours = PushStruct(Working_Set, edit_mode_colours); - Edit_Mode_Colours->preSelect = Green_Float; - Edit_Mode_Colours->select = Blue_Float; - Edit_Mode_Colours->invSelect = Red_Float; - Edit_Mode_Colours->fg = Yellow_Text_Float; - Edit_Mode_Colours->bg = Grey_Background; - } + delete[] xValues; + delete[] yValues; - // Waypoint Mode Colours - { - Waypoint_Mode_Data = PushStruct(Working_Set, waypoint_mode_data); - Waypoint_Mode_Data->base = Red_Full; - Waypoint_Mode_Data->selected = Blue_Full; - Waypoint_Mode_Data->text = Yellow_Text_Float; - Waypoint_Mode_Data->bg = Grey_Background; - Waypoint_Mode_Data->size = DefaultWaypointSize; + glActiveTexture(GL_TEXTURE0); + } + break; + } } - // Scaff Mode Colours - { - Scaff_Mode_Data = PushStruct(Working_Set, meta_mode_data); - Scaff_Mode_Data->text = Yellow_Text_Float; - Scaff_Mode_Data->bg = Grey_Background; - Scaff_Mode_Data->size = DefaultScaffSize; - } + return ; +} - // Meta Mode Colours - { - MetaData_Mode_Data = PushStruct(Working_Set, meta_mode_data); - MetaData_Mode_Data->text = Yellow_Text_Float; - MetaData_Mode_Data->bg = Grey_Background; - MetaData_Mode_Data->size = DefaultMetaDataSize; - } - // Extension Mode Colours - { - Extension_Mode_Data = PushStruct(Working_Set, meta_mode_data); - Extension_Mode_Data->text = Yellow_Text_Float; - Extension_Mode_Data->bg = Grey_Background; - Extension_Mode_Data->size = DefaultExtensionSize; - } -#ifdef Internal - { - Tiles = PushStruct(Working_Set, ui_colour_element); - Tiles->on = 1; - Tiles->bg = {0.0f, 1.0f, 1.0f, 1.0f}; +global_function +u08 +LoadState(u64 headerHash, char *path = 0); - QuadTrees = PushStruct(Working_Set, ui_colour_element); - QuadTrees->on = 1; - QuadTrees->bg = {0.0f, 1.0f, 0.0f, 1.0f}; - } -#endif +global_function +load_file_result +LoadFile(const char *filePath, memory_arena *arena, char **fileName, u64 *headerHash) +{ + u64 fileSize = 0; - // Contact Matrix Shader + FILE *file = TestFile(filePath, &fileSize); // 检查前4个字节读取到的数据, 如果为 u08 Magic[] = {'p', 's', 't', 'm'} 则通过验证,否则将指针file设置为空指针 + if (!file) // 如果为空指针, 返回读取错误fileErr { - Contact_Matrix = PushStruct(Working_Set, contact_matrix); - Contact_Matrix->shaderProgram = CreateShader(FragmentSource_Texture.c_str(), VertexSource_Texture.c_str()); - - glUseProgram(Contact_Matrix->shaderProgram); - glBindFragDataLocation(Contact_Matrix->shaderProgram, 0, "outColor"); + return(fileErr); + } + + FenceIn(File_Loaded = 0); - Contact_Matrix->matLocation = glGetUniformLocation(Contact_Matrix->shaderProgram, "matrix"); - glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "tex"), 0); - glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "colormap"), 1); - glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "pixstartlookup"), 2); - glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "pixrearrangelookup"), 3); + static u32 reload = 0; + + if (!reload) + { + reload = 1; + } + else // clear all the memory, in gl and the arena + { + glDeleteTextures(1, &Contact_Matrix->textures); - glActiveTexture(GL_TEXTURE1); + glDeleteVertexArrays((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vaos); + glDeleteBuffers((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vbos); - Color_Maps = PushStruct(Working_Set, color_maps); - u32 nMaps = Number_of_Color_Maps; - Color_Maps->maps = PushArray(Working_Set, GLuint, nMaps); - Color_Maps->mapPreviews = PushArray(Working_Set, struct nk_image, nMaps); - Color_Maps->nMaps = nMaps; - Color_Maps->currMap = 1; + glDeleteBuffers(1, &Contact_Matrix->pixelStartLookupBuffer); + glDeleteTextures(1, &Contact_Matrix->pixelStartLookupBufferTex); - ForLoop(nMaps) - { - u32 mapPreviewImage[256]; + glDeleteBuffers(1, &Contact_Matrix->pixelRearrangmentLookupBuffer); + glDeleteTextures(1, &Contact_Matrix->pixelRearrangmentLookupBufferTex); - GLuint tbo, tboTex, texPreview; + glDeleteVertexArrays((GLsizei)Grid_Data->nBuffers, Grid_Data->vaos); + glDeleteBuffers((GLsizei)Grid_Data->nBuffers, Grid_Data->vbos); - glGenBuffers(1, &tbo); - glBindBuffer(GL_TEXTURE_BUFFER, tbo); - glBufferData(GL_TEXTURE_BUFFER, sizeof(Color_Map_Data[index]), Color_Map_Data[index], GL_STATIC_DRAW); + glDeleteVertexArrays((GLsizei)Label_Box_Data->nBuffers, Label_Box_Data->vaos); + glDeleteBuffers((GLsizei)Label_Box_Data->nBuffers, Label_Box_Data->vbos); - glGenTextures(1, &tboTex); - glBindTexture(GL_TEXTURE_BUFFER, tboTex); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, tbo); + glDeleteVertexArrays((GLsizei)Scale_Bar_Data->nBuffers, Scale_Bar_Data->vaos); + glDeleteBuffers((GLsizei)Scale_Bar_Data->nBuffers, Scale_Bar_Data->vbos); - Color_Maps->maps[index] = tboTex; + glDeleteVertexArrays((GLsizei)Contig_ColourBar_Data->nBuffers, Contig_ColourBar_Data->vaos); + glDeleteBuffers((GLsizei)Contig_ColourBar_Data->nBuffers, Contig_ColourBar_Data->vbos); - glActiveTexture(GL_TEXTURE0); + glDeleteVertexArrays((GLsizei)Scaff_Bar_Data->nBuffers, Scaff_Bar_Data->vaos); + glDeleteBuffers((GLsizei)Scaff_Bar_Data->nBuffers, Scaff_Bar_Data->vbos); - ForLoop2(256) //TODO SIMD + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) { - mapPreviewImage[index2] = ((u32)(Color_Map_Data[index][3 * index2] * 255.0f)) | - (((u32)(Color_Map_Data[index][(3 * index2) + 1] * 255.0f)) << 8) | - (((u32)(Color_Map_Data[index][(3 * index2) + 2] * 255.0f)) << 16) | - 0xff000000; + case extension_graph: + { + graph *gph = (graph *)node->extension; + glDeleteVertexArrays(1, &gph->vao); + glDeleteBuffers(1, &gph->vbo); + glDeleteBuffers(1, &gph->shader->yValuesBuffer); + glDeleteTextures(1, &gph->shader->yValuesBufferTex); + } + break; } - glGenTextures(1, &texPreview); - glBindTexture(GL_TEXTURE_2D, texPreview); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, mapPreviewImage); - glGenerateMipmap(GL_TEXTURE_2D); + } - Color_Maps->mapPreviews[index] = nk_image_id((s32)texPreview); + Current_Loaded_Texture = 0; + Texture_Ptr = 0; + + Mouse_Move.x = -1.0; + Mouse_Move.y = -1.0; - glActiveTexture(GL_TEXTURE1); - } - NextColorMap(-1); + Camera_Position.x = 0.0f; + Camera_Position.y = 0.0f; + Camera_Position.z = 1.0f; + + Edit_Pixels.editing = 0; + Global_Mode = mode_normal; - glActiveTexture(GL_TEXTURE0); + Extensions = {}; - Color_Maps->cpLocation = glGetUniformLocation(Contact_Matrix->shaderProgram, "controlpoints"); - Color_Maps->controlPoints[0] = 0.0f; - Color_Maps->controlPoints[1] = 0.5f; - Color_Maps->controlPoints[2] = 1.0f; - glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); + // delete the memory collected by new + if (Contact_Matrix->vaos) + { + delete[] Contact_Matrix->vaos; + Contact_Matrix->vaos = nullptr; + } + if (Contact_Matrix->vbos) + { + delete[] Contact_Matrix->vbos; + Contact_Matrix->vbos = nullptr; + } + if (pixel_density_extension) + { + delete pixel_density_extension; + pixel_density_extension = nullptr; + } + if (frag_cut_cal_ptr) + { + delete frag_cut_cal_ptr; + frag_cut_cal_ptr = nullptr; + } + ResetMemoryArenaP(arena); // release all the memory allocated, avoid memory leak + auto_curation_state.clear(); } - // Flat Color Shader + // File Contents { - Flat_Shader = PushStruct(Working_Set, flat_shader); - Flat_Shader->shaderProgram = CreateShader(FragmentSource_Flat.c_str(), VertexSource_Flat.c_str()); + char *tmp = (char *)filePath; +#ifdef _WIN32 + char sep = '\\'; +#else + char sep = '/'; +#endif - glUseProgram(Flat_Shader->shaderProgram); - glBindFragDataLocation(Flat_Shader->shaderProgram, 0, "outColor"); + while (*++tmp) {} + while ((*--tmp != sep) && *tmp) {} - Flat_Shader->matLocation = glGetUniformLocation(Flat_Shader->shaderProgram, "matrix"); - Flat_Shader->colorLocation = glGetUniformLocation(Flat_Shader->shaderProgram, "color"); - } - - // Fonts - { - UI_Shader = PushStruct(Working_Set, ui_shader); - UI_Shader->shaderProgram = CreateShader(FragmentSource_UI.c_str(), VertexSource_UI.c_str()); - glUseProgram(UI_Shader->shaderProgram); - glBindFragDataLocation(UI_Shader->shaderProgram, 0, "outColor"); - glUniform1i(glGetUniformLocation(UI_Shader->shaderProgram, "tex"), 0); - UI_Shader->matLocation = glGetUniformLocation(UI_Shader->shaderProgram, "matrix"); + *fileName = tmp + 1; - FontStash_Context = glfonsCreate(512, 512, FONS_ZERO_TOPLEFT); - Font_Normal = fonsAddFontMem(FontStash_Context, "Sans Regular", FontNormal, (s32)FontNormal_Size, 0); + u32 intBuff[16]; + PushStringIntoIntArray(intBuff, ArrayCount(intBuff), (u08 *)(*fileName)); // 将字符穿转移到intbuff数组中 - if (Font_Normal == FONS_INVALID) - { - fprintf(stderr, "Could not add font 'DroidSerif-Regular.ttf'\n"); - exit(1); - } - Font_Bold = fonsAddFontMem(FontStash_Context, "Sans Bold", FontBold, (s32)FontBold_Size, 0); - if (Font_Bold == FONS_INVALID) - { - fprintf(stderr, "Could not add font 'DroidSerif-Bold.ttf'\n"); - exit(1); - } - } + /* + 这段代码假设文件中的数据是以 4 字节整数的形式存储的,并且依次存储了 + nBytesHeaderComp 和 nBytesHeader 两个值。通常情况下, + 这种操作用于读取文件中存储的数据头部信息或者其他固定格式的数据。 + */ + u32 nBytesHeaderComp; // 压缩后数据大小 + u32 nBytesHeader; // 解压后数据大小 + fread(&nBytesHeaderComp, 1, 4, file); // 从文件中读取 4 个字节的数据,并将其存储在 nBytesHeaderComp 变量中 + fread(&nBytesHeader, 1, 4, file); - // Quad EBO - { - GLushort pIndexQuad[6]; - pIndexQuad[0] = 0; - pIndexQuad[1] = 1; - pIndexQuad[2] = 2; - pIndexQuad[3] = 2; - pIndexQuad[4] = 3; - pIndexQuad[5] = 0; + u08 *header = PushArrayP(arena, u08, nBytesHeader); // 从内存池中分配u08 数组,大小为 nBytesHeader + u08 *compressionBuffer = PushArrayP(arena, u08, nBytesHeaderComp); // 从内存池中分配u08 数组,大小为 nBytesHeaderComp - glGenBuffers(1, &Quad_EBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLushort), pIndexQuad, GL_STATIC_DRAW); - } + fread(compressionBuffer, 1, nBytesHeaderComp, file); // nBytesHeaderComp个字节的压缩数据,存储到compressionBuffer + *headerHash = FastHash64( // head的地址采用fasthash加密,作为存储文件的文件名。 compressionBuffer所指向的值进行hash得到一个名字,作为存储文件的文件名 + compressionBuffer, + nBytesHeaderComp, + FastHash64(intBuff, sizeof(intBuff), 0xbafd06832de619c2) + ); + // fprintf(stdout, "The headerHash is calculated accordig to the compressed head, the hash number is (%llu) and the cache file name is (%s)\n", *headerHash, (u08*) headerHash); + if ( + libdeflate_deflate_decompress( + Decompressor, // 指向 解压缩器 + (const void *)compressionBuffer, // 指向 即将解压的数据,(const void *)强制转换为不可修改的内存块 + nBytesHeaderComp, // 解压缩的字节数 + (void *)header, // 指向 解压缩后存储的位置,转换为通用内存块 + nBytesHeader, // 解压后预计大小 + NULL) // 表示不传递其他参数给压缩函数 + ) + { + return(decompErr); // decompress err + } + FreeLastPushP(arena); // comp buffer 释放内存池(arena)中最近一次通过 PushArrayP 宏分配的内存空间 + // 遍历内存池链表,找到最后一个内存池,并释放其最近一次分配的内存空间。防止内存泄漏 - GLuint posAttribFlatShader = (GLuint)glGetAttribLocation(Flat_Shader->shaderProgram, "position"); - auto PushGenericBuffer = [posAttribFlatShader] (quad_data **quadData, u32 numberOfBuffers) -> void - { - *quadData = PushStruct(Working_Set, quad_data); + /* + header的格式 + ========================== + nBytes Contents + -------------------------- + 8 Total_Genome_Length + 4 Number_of_Original_Contigs + ------------------- + Number_of_Original_Contigs 个 contigs 的存储规则 + ------------------- + 4 contig fracs + 64 name + ------------------ + 1 textureRes + 1 nTextRes + 1 mipMapLevels - (*quadData)->vaos = PushArray(Working_Set, GLuint, numberOfBuffers); - (*quadData)->vbos = PushArray(Working_Set, GLuint, numberOfBuffers); + */ - glUseProgram(Flat_Shader->shaderProgram); + u64 val64; + u08 *ptr = (u08 *)&val64; // 获取val64存储的 八位无符号整型(u08)的指针 + ForLoop(8) + { + *ptr++ = *header++; // 指针赋值给到val64的大小 -> 整个基因的长度 + } + Total_Genome_Length = val64; - ForLoop(numberOfBuffers) + u32 val32; + ptr = (u08 *)&val32; + ForLoop(4) { - glGenVertexArrays(1, (*quadData)->vaos + index); - glBindVertexArray((*quadData)->vaos[index]); + *ptr++ = *header++; + } + Number_of_Original_Contigs = val32; // 指针赋值给到val32的值 -> contigs的数目 - glGenBuffers(1, (*quadData)->vbos + index); - glBindBuffer(GL_ARRAY_BUFFER, (*quadData)->vbos[index]); - glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(vertex), NULL, GL_DYNAMIC_DRAW); + // 从内存池中分配存储原始 contig 的数组内存,类型为 original_contig,数组长度为 Number_of_Original_Contigs + Original_Contigs = PushArrayP(arena, original_contig, Number_of_Original_Contigs); + // 分配一个存储浮点数的数组 + f32 *contigFracs = PushArrayP(arena, f32, Number_of_Original_Contigs); + ForLoop(Number_of_Original_Contigs) // 读取 contigs fraction (f32) and name + { - glEnableVertexAttribArray(posAttribFlatShader); - glVertexAttribPointer(posAttribFlatShader, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), 0); + f32 frac; + u32 name[16]; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); + // 读取每个contig 对应的一个f32 + ptr = (u08 *)&frac; + ForLoop2(4) // 从header中读取4个字节的数据存储到frac中 + { + *ptr++ = *header++; + } + contigFracs[index] = frac; // 将这个f32 存储到 contigFracs[index] 中 + + // 读取contig的名字 + ptr = (u08 *)name; + ForLoop2(64) + { + *ptr++ = *header++; + } + // contig name赋值 + ForLoop2(16) + { + Original_Contigs[index].name[index2] = name[index2]; // 将 u32 name[16] 给到每一个contig 的name + } + + (Original_Contigs + index)->contigMapPixels = PushArrayP(arena, u32, Number_of_Pixels_1D); // 为每个contig 的 mapPixels 变量 申请内存 + (Original_Contigs + index)->nContigs = 0; } - }; - - // Edit Mode Data - { - PushGenericBuffer(&Edit_Mode_Data, 12); - } - // Tool Tip Data - { - PushGenericBuffer(&Tool_Tip_Data, 1); - } + u08 textureRes = *header++; // 分辨率 + u08 nTextRes = *header++; // 纹理数目 + u08 mipMapLevels = *header; // ?? - // Waypoint Data - { - PushGenericBuffer(&Waypoint_Data, (3 * Waypoints_Stack_Size) + 1); - } + Texture_Resolution = Pow2(textureRes); // 纹理分辨率 当前显示的像素点个数,1024 + Number_of_Textures_1D = Pow2(nTextRes); // 一维纹理数目 可以放大的次数,每一个像素点可以放大32次, + Number_of_MipMaps = mipMapLevels; - // Nuklear Setup - { -#define NK_Memory_Size MegaByte(32) - NK_Device = PushStruct(Working_Set, device); - u08 *nkCmdMemory = PushArray(Working_Set, u08, NK_Memory_Size); - nk_buffer_init_fixed(&NK_Device->cmds, (void *)nkCmdMemory, NK_Memory_Size); - NK_Device->lastContextMemory = PushArray(Working_Set, u08, NK_Memory_Size); - memset(NK_Device->lastContextMemory, 0, NK_Memory_Size); - NK_Device->prog = UI_Shader->shaderProgram; - NK_Device->uniform_proj = UI_Shader->matLocation; - NK_Device->attrib_pos = UI_SHADER_LOC_POSITION; - NK_Device->attrib_uv = UI_SHADER_LOC_TEXCOORD; - NK_Device->attrib_col = UI_SHADER_LOC_COLOR; + Number_of_Pixels_1D = Number_of_Textures_1D * Texture_Resolution; // 更新一维数据的长度 - GLsizei vs = sizeof(nk_glfw_vertex); - size_t vp = offsetof(nk_glfw_vertex, position); - size_t vt = offsetof(nk_glfw_vertex, uv); - size_t vc = offsetof(nk_glfw_vertex, col); + // update the pixel_cut consider range if high resolution + if (Number_of_Pixels_1D > 32768) auto_curation_state.auto_cut_diag_window_for_pixel_mean = 16; - glGenBuffers(1, &NK_Device->vbo); - glGenBuffers(1, &NK_Device->ebo); - glGenVertexArrays(1, &NK_Device->vao); + Map_State = PushStructP(arena, map_state); // 从内存池中分配一个包含 map_state 结构的内存块,并返回指向该结构的指针, 存储contigs map到图像中的数据 + Map_State->contigIds = PushArrayP(arena, u32, Number_of_Pixels_1D); // 从内存池中分配存储 contigIds 的数组,数组长度为 Number_of_Pixels_1D + Map_State->originalContigIds = PushArrayP(arena, u32, Number_of_Pixels_1D); // + Map_State->contigRelCoords = PushArrayP(arena, u32, Number_of_Pixels_1D); // 像素坐标 + Map_State->scaffIds = PushArrayP(arena, u32, Number_of_Pixels_1D); // scaffID + Map_State->metaDataFlags = PushArrayP(arena, u64, Number_of_Pixels_1D); // 标记 + memset(Map_State->scaffIds, 0, Number_of_Pixels_1D * sizeof(u32)); // 将scaffID和metaDataFlags初始化为0 + memset(Map_State->metaDataFlags, 0, Number_of_Pixels_1D * sizeof(u64)); + f32 total = 0.0f; // 所有contig的一个浮点数的累积, finally should approximately be 1.0 + u32 lastPixel = 0; + u32 relCoord = 0; - glBindVertexArray(NK_Device->vao); - glBindBuffer(GL_ARRAY_BUFFER, NK_Device->vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NK_Device->ebo); + ForLoop(Number_of_Original_Contigs) // 初始设定每个contig的每个像素点的id和局部坐标 + { + total += contigFracs[index]; // 当前所有contig对应的浮点数的累积,包括当前contig + u32 pixel = (u32)((f64)Number_of_Pixels_1D * (f64)total); // 每行像素点数 * 当前占比 + + relCoord = 0; +#ifdef RevCoords + u32 tmp = pixel - lastPixel - 1; +#endif + while (lastPixel < pixel) + { + Map_State->originalContigIds[lastPixel] = index; // 每一个像素点对应的都是当前contig的编号 + Map_State->contigRelCoords[lastPixel++] = // 每一个像素点对应的在当前contig中的局部坐标 +#ifdef RevCoords + tmp - relCoord++; +#else + relCoord++; +#endif + } + } + while (lastPixel < Number_of_Pixels_1D) // 处理数值计算导致的lastPixel小于Number_of_Pixels_1D的问题 + { + Map_State->originalContigIds[lastPixel] = (u32)(Number_of_Original_Contigs - 1); //假设其为最后一个contig的像素点 + Map_State->contigRelCoords[lastPixel++] = relCoord++; + } - glEnableVertexAttribArray((GLuint)NK_Device->attrib_pos); - glEnableVertexAttribArray((GLuint)NK_Device->attrib_uv); - glEnableVertexAttribArray((GLuint)NK_Device->attrib_col); + Contigs = PushStructP(arena, contigs); // 声明一个存储contigs的内存块, 其返回Contigs作为这个块的指针,实际上此处为整个genome的信息 + Contigs->contigs_arr = PushArrayP(arena, contig, Max_Number_of_Contigs); // 每一个Contigs中会有contigs (片段),一共有Max_Number_of_Contigs多个片段,最多存放4096个contigs + Contigs->contigInvertFlags = PushArrayP(arena, u08, (Max_Number_of_Contigs + 7) >> 3); // (4096 + 7 ) >> 3 = 512, 声明512个u08的存储空间 - glVertexAttribPointer((GLuint)NK_Device->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); - glVertexAttribPointer((GLuint)NK_Device->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); - glVertexAttribPointer((GLuint)NK_Device->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + UpdateContigsFromMapState(); // 根据mapstate 跟新当前的contigs, 并且更新original_contigs里面的每个contig所包含的片段的个数和片段的中点 - NK_Atlas = PushStruct(Working_Set, nk_font_atlas); - nk_font_atlas_init_default(NK_Atlas); - nk_font_atlas_begin(NK_Atlas); - struct nk_font_config cfg = nk_font_config(14); - cfg.oversample_h = 3; - cfg.oversample_v = 3; - NK_Font = nk_font_atlas_add_from_memory(NK_Atlas, FontBold, (nk_size)FontBold_Size, 22 * Screen_Scale.y, &cfg); - NK_Font_Browser = nk_font_atlas_add_from_memory(NK_Atlas, FontBold, (nk_size)FontBold_Size, 14 * Screen_Scale.y, &cfg); - - s32 w,h; - const void *image = nk_font_atlas_bake(NK_Atlas, &w, &h, NK_FONT_ATLAS_RGBA32); - - glGenTextures(1, &NK_Device->font_tex); - glBindTexture(GL_TEXTURE_2D, NK_Device->font_tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, - GL_RGBA, GL_UNSIGNED_BYTE, image); + u32 nBytesPerText = 0; // 程序将一整张图分成了32*32个小格子,每一个格子被称作texture + ForLoop(Number_of_MipMaps) + { + nBytesPerText += Pow2((2 * textureRes--)); // sum([2**(2*i) for i in range(10, 10-Number_of_MipMaps, -1)])/2 + } + nBytesPerText >>= 1; // 除以 2 因为数据是经过压缩的 + Bytes_Per_Texture = nBytesPerText; // 一个texture 对应的字节数目 - nk_font_atlas_end(NK_Atlas, nk_handle_id((s32)NK_Device->font_tex), &NK_Device->null); - u08 *nkContextMemory = PushArray(Working_Set, u08, NK_Memory_Size); - nk_init_fixed(NK_Context, (void *)nkContextMemory, NK_Memory_Size, &NK_Font->handle); + File_Atlas = PushArrayP(arena, file_atlas_entry, (Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1)); // variable to store the data entry - SetTheme(NK_Context, THEME_DARK); + u32 currLocation = sizeof(Magic) + 8 + nBytesHeaderComp; // current localtion of the pointer = magic_check + (u32 compressed head length) + (u32 decompressed head length) + compressed header length + + // locating the pointers to data of entries + ForLoop((Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1)) // loop through total number of the textures + { + /* + 数据结构: + - magic (4 bytes) + - 8 bytes + - headercomp (nBytesHeaderComp bytes) + - a block of entry: + - number of bytes in u32 + - data - Theme_Name[THEME_RED] = (u08 *)"Red"; - Theme_Name[THEME_BLUE] = (u08 *)"Blue"; - Theme_Name[THEME_WHITE] = (u08 *)"White"; - Theme_Name[THEME_BLACK] = (u08 *)"Black"; - Theme_Name[THEME_DARK] = (u08 *)"Dark"; - } + */ + file_atlas_entry *entry = File_Atlas + index; // get the temprory pointer of the entry + u32 nBytes; // define a u32 to save the data + fread(&nBytes, 1, 4, file); // 读取四个字节, 前四个字节存储一个u32表示大小,后面的数据存储 + fseek(file, nBytes, SEEK_CUR); // 文件指针会向前移动 nBytes 个字节,SEEK_CUR在c标准库中被定义为1, after the loop, pointer file is moved to the end of the reading file + currLocation += 4; // 每移动一次,currLocation 增加 4 - Loading_Arena = PushStruct(Working_Set, memory_arena); - CreateMemoryArena(*Loading_Arena, MegaByte(512)); - //Loading_Arena = PushSubArena(Working_Set, MegaByte(128)); -} + entry->base = currLocation; // asign the current location to File_Atlas + index + entry->nBytes = nBytes; // asign the size to File_Atlas + index + currLocation += nBytes; + } + // Extensions + { + u08 magicTest[sizeof(Extension_Magic_Bytes[0])]; // define a u08 array, to check the end of the file, use this to read the extensions -global_function -void -TakeScreenShot(int nchannels = 4) -{ - s32 viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); + while ((u64)(currLocation + sizeof(magicTest)) < fileSize) + { + u32 bytesRead = (u32)fread(magicTest, 1, sizeof(magicTest), file); // reading 4 bytes from the file + currLocation += bytesRead; + if (bytesRead == sizeof(magicTest)) + { + ForLoop(ArrayCount(Extension_Magic_Bytes)) + { + u08 foundExtension = 1; + u08 *magic = (u08 *)Extension_Magic_Bytes[index]; + ForLoop2(sizeof(magicTest)) + { + if (magic[index2] != magicTest[index2]) // magicTest is from the file, magic is from the definition // if magic this isn't the same, means no extensions found + { + foundExtension = 0; + break; + } + } - u08 *imageBuffer = PushArray(Working_Set, u08, (u32)(nchannels * viewport[2] * viewport[3])); - glReadPixels ( 0, 0, viewport[2], viewport[3], - nchannels==4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, - imageBuffer); + if (foundExtension) // if extension is found + { + extension_type type = (extension_type)index; // get the type of the extension + u32 extensionSize = 0; + switch (type) + { + case extension_graph: + { + u32 compSize; + fread(&compSize, 1, sizeof(u32), file); // get the size of the extension data + graph *gph = PushStructP(arena, graph); // create the size to store the extension data + extension_node *node = PushStructP(arena, extension_node); + u08 *dataPlusName = PushArrayP(arena, u08, ((sizeof(u32) * Number_of_Pixels_1D) + sizeof(gph->name) )); // there are 1024 * 32 u32 numbers. Every single one of them represents the data on a pixel. +#pragma clang diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + gph->data = (u32 *)(dataPlusName + sizeof(gph->name)); // the first 16 u32 are the name of the extention, which can include 16*32/8=64 u08 (char). +#pragma clang diagnostic pop + extensionSize += (compSize + sizeof(u32)); + u08 *compBuffer = PushArrayP(arena, u08, compSize); + fread(compBuffer, 1, compSize, file); // read the extension data from the file pointer + if (libdeflate_deflate_decompress(Decompressor, (const void *)compBuffer, compSize, (void *)dataPlusName, (sizeof(u32) * Number_of_Pixels_1D) + sizeof(gph->name), NULL)) // decompress compBuffer to dataPlusName + /* code from the libdeflate.h + enum libdeflate_result { + // Decompression was successful. + LIBDEFLATE_SUCCESS = 0, - stbi_flip_vertically_on_write(1); - stbi_write_png("PretextView_ScreenShot.png", viewport[2], viewport[3], nchannels, imageBuffer, nchannels * viewport[2]); //TODO change png compression to use libdeflate zlib impl - FreeLastPush(Working_Set); -} + // Decompression failed because the compressed data was invalid, + * corrupt, or otherwise unsupported. + LIBDEFLATE_BAD_DATA = 1, + // A NULL 'actual_out_nbytes_ret' was provided, but the data would have + * decompressed to fewer than 'out_nbytes_avail' bytes. + LIBDEFLATE_SHORT_OUTPUT = 2, + // The data would have decompressed to more than 'out_nbytes_avail' + * bytes. + LIBDEFLATE_INSUFFICIENT_SPACE = 3, + }; + */ + { // unsuccessful decompress + FreeLastPushP(arena); // data + FreeLastPushP(arena); // graph + FreeLastPushP(arena); // node + } + else + { // successful decompress +#pragma clang diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + u32 *namePtr = (u32 *)dataPlusName; // get a temp pointer,将原来的u08指针切换为u32指针,所指向的位置相同,只是不同的解释方式 +#pragma clang diagnostic pop + ForLoop2(ArrayCount(gph->name)) // get the graph name + { + gph->name[index2] = *(namePtr + index2); + } -global_function -void -InvertMap( - u32 pixelFrom, // from pixel - u32 pixelTo, // end pixel - bool update_contigs_flag - ) -{ - if (pixelFrom > pixelTo) - { - u32 tmp = pixelFrom; - pixelFrom = pixelTo; - pixelTo = tmp; + node->type = type; // assign the gph to node + node->extension = gph; + AddExtension(node); // add node to extension + } + FreeLastPushP(arena); // compBuffer + } + break; + } + currLocation += extensionSize; + } + } + } + else + { + break; + } + } + } + + fclose(file); // the positions and file pointers will be saved to texture_buffer_queue, which can be read in multi-thread mode } - u32 nPixels = Number_of_Pixels_1D; - - Assert(pixelFrom < nPixels); - Assert(pixelTo < nPixels); - - u32 copySize = (pixelTo - pixelFrom + 1) >> 1; - - u32 *tmpBuffer = new u32[copySize]; - u32 *tmpBuffer2 = new u32[copySize]; - u32 *tmpBuffer3 = new u32[copySize]; - u32 *tmpBuffer4 = new u32[copySize]; - u64 *tmpBuffer5 = new u64[copySize]; + // Load Textures: reading from file and pushing into glTexture with index Contact_Matrix->textures + { + InitialiseTextureBufferQueue(arena, Texture_Buffer_Queue, Bytes_Per_Texture, filePath); // 初始化所有的queue texture_buffer_queue, 一共有8个queue,每个queue有8个buffer - glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); - u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, 0, nPixels * sizeof(u32), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); // map buffer to read and write + u32 nTextures = (Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1); // number of textures (528) + // u32 *packedTextureIndexes = PushArrayP(arena, u32, nTextures); // using a pointer of sequences of u32 as the texture index + u32* packedTextureIndexes = new u32[nTextures]; // (u32*) malloc(sizeof(u32) * nTextures); // this is released so new is ok + ThreadPoolAddTask(Thread_Pool, PopulateTextureLoadQueue, packedTextureIndexes); // multi-thread for loading the texture entries - if (buffer) - { - ForLoop(copySize) // put the first half in the buffer - { - tmpBuffer[index] = buffer[pixelFrom + index]; - tmpBuffer2[index] = Map_State->contigRelCoords[pixelFrom + index]; - tmpBuffer3[index] = Map_State->originalContigIds[pixelFrom + index]; - tmpBuffer4[index] = Map_State->scaffIds[pixelFrom + index]; - tmpBuffer5[index] = Map_State->metaDataFlags[pixelFrom + index]; - } + glActiveTexture(GL_TEXTURE0); // 所有的子图加载在 texture 0 + glGenTextures(1, &Contact_Matrix->textures); // 获取一个texture 存到 + glBindTexture(GL_TEXTURE_2D_ARRAY, Contact_Matrix->textures); // 绑定到当前的texture + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, (GLint)Number_of_MipMaps - 1); - ForLoop(copySize) // set the first half to the second half - { - buffer[pixelFrom + index] = buffer[pixelTo - index]; - Map_State->contigRelCoords[pixelFrom + index] = Map_State->contigRelCoords[pixelTo - index]; - Map_State->originalContigIds[pixelFrom + index] = Map_State->originalContigIds[pixelTo - index]; - Map_State->scaffIds[pixelFrom + index] = Map_State->scaffIds[pixelTo - index]; - Map_State->metaDataFlags[pixelFrom + index] = Map_State->metaDataFlags[pixelTo - index]; + u32 resolution = Texture_Resolution; + ForLoop(Number_of_MipMaps) // 初始一个texture的一个维度有1024个像素点,放大后一个texture的一个维度只有32(1024 / 2**5)个像素点 + { // 初始化所有层级mipmap level的 gl_texture_2d_array + glCompressedTexImage3D ( + GL_TEXTURE_2D_ARRAY, // 指定纹理目标 例如GL_TEXTURE_3D + (GLint)index, // 指定纹理层级 + GL_COMPRESSED_RED_RGTC1, // texture数据的压缩格式 Unsigned normalized 1-component only. + (GLsizei)resolution, (GLsizei)resolution, (GLsizei)nTextures, // 纹理宽度, 高度, 深度 + 0, // border + (GLsizei)((resolution >> 1) * resolution * nTextures), // 每一个texture 的数据大小 (bytes),注意此处初始化了全部的 nTextures 个texture + 0); // 指向数据的指针 + resolution >>= 1; } - - ForLoop(copySize) // set the second half from the buffer + + u32 ptr = 0; + printf("Loading textures...\n"); + ForLoop(Number_of_Textures_1D) { - buffer[pixelTo - index] = tmpBuffer[index]; - Map_State->contigRelCoords[pixelTo - index] = tmpBuffer2[index]; - Map_State->originalContigIds[pixelTo - index] = tmpBuffer3[index]; - Map_State->scaffIds[pixelTo - index] = tmpBuffer4[index]; - Map_State->metaDataFlags[pixelTo - index] = tmpBuffer5[index]; - } - } - else - { - fprintf(stderr, "Could not map pixel rearrange buffer\n"); - } - - glUnmapBuffer(GL_TEXTURE_BUFFER); - glBindBuffer(GL_TEXTURE_BUFFER, 0); + ForLoop2(Number_of_Textures_1D - index) + { + volatile texture_buffer *loadedTexture = 0; // 使用volatile锁放弃存取优化。如果优化(不使用volatile),该值存取会被优化,可能会存放在寄存器中,而不是每次访问该值都会从内存中读取 + while (!loadedTexture) + { + #ifndef _WIN32 // unix + __atomic_load(&Current_Loaded_Texture, &loadedTexture, __ATOMIC_SEQ_CST); + #else // windows + // loadedTexture = InterlockedCompareExchangePointer(&Current_Loaded_Texture, nullptr, nullptr); + loadedTexture = (texture_buffer*)InterlockedCompareExchangePointer( + (PVOID volatile*)&Current_Loaded_Texture, + NULL, + NULL); + #endif // __WIN32 + } + u08 *texture = loadedTexture->texture; // 获取loadedtexture的texture的指针 + + // push the texture data (number_of_mipmaps) to the gl_texture_2d_array + resolution = Texture_Resolution; + for ( GLint level = 0; + level < (GLint)Number_of_MipMaps; + ++level ) + { + GLsizei nBytes = (GLsizei)(resolution * (resolution >> 1)); // 为什么每一个mipmap存储的像素点个数是 resolution ** 2 / 2 ,不应该是resolution**2吗 + // 此处将texture的数据压入到gl中,存储为gl_texture_2d_array的对象 + glCompressedTexSubImage3D( + GL_TEXTURE_2D_ARRAY, // target texture. Must be GL_TEXTURE_3D or GL_TEXTURE_2D_ARRAY. + level, // level-of-detail number. Level 0 is the base image level. Level n is the nth mipmap reduction image. + 0, 0, (GLint)Texture_Ptr, // Texture_Ptr is the index of Current_loaded_texture + (GLsizei)resolution, (GLsizei)resolution, 1, // Specifies the width, height, depth of the texture subimage. + GL_COMPRESSED_RED_RGTC1, // 压缩数据的格式,这就是为什么只用了一半的 (nBytes) bytes 的数据表示了 resolution * resolution 的图片 + nBytes, // the number of unsigned bytes of image data starting at the address specified by data. + texture // a pointer to the compressed image data in memory. + ); // 是否此处将texture给到 GL_TEXTURE_2D_ARRAY, check the doc on https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glCompressedTexSubImage3D.xhtml - delete[] tmpBuffer ; - delete[] tmpBuffer2; - delete[] tmpBuffer3; - delete[] tmpBuffer4; - delete[] tmpBuffer5; + resolution >>= 1; + texture += nBytes; + } - if (update_contigs_flag) UpdateContigsFromMapState(); // update the contigs from the buffer + printf("\r%3d/%3d (%1.2f%%) textures loaded from disk...", Texture_Ptr + 1, nTextures, 100.0 * (f64)((f32)(Texture_Ptr + 1) / (f32)((Number_of_Textures_1D >> 1) * (Number_of_Textures_1D + 1)))); // echo out 读取到了第Texture_Ptr个texture + fflush(stdout); - map_edit edit; - edit.finalPix1 = (u32)pixelTo; - edit.finalPix2 = (u32)pixelFrom; - edit.delta = 0; - MoveWayPoints(&edit); -} + AddTextureBufferToQueue(Texture_Buffer_Queue, (texture_buffer *)loadedTexture); // texture_buffer_queue 是全部的读取任务队列,读取后的buffer重新添加到队列中,供下一次读取。解决了从队列中弹出任务后任务队列空了的疑问。读取文件的任务队列在别的地方还会被调用吗? + FenceIn(Current_Loaded_Texture = 0); // 将临时变量置空,重新读取到current_loaded_texture后会跳出上面的循环 + __atomic_fetch_add(&Texture_Ptr, 1, 0); // 更新全局的 texture_ptr + ptr ++ ; + } + } + printf("\n"); + CloseTextureBufferQueueFiles(Texture_Buffer_Queue); // 关闭所有的buffer_texture中的文件流指针file,并且释放解压器内存 + delete[] packedTextureIndexes; // packedTextureIndexes 返回 + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); // 给之前已经压入的GL_TEXTURE_2D_ARRAY解除绑定 + } -global_function -s32 -RearrangeMap( // NOTE: VERY IMPORTANT - u32 pixelFrom, // start of the fragment - u32 pixelTo, // end of the fragment - s32 delta, // movement of the distance - u08 snap, // if true, the fragment will snap to the nearest contig - bool update_contigs_flag // if true, update the contigs from the map state - ) -{ + + { // Define Contact Matrix Vertex Data (vao, vbo) + glUseProgram(Contact_Matrix->shaderProgram); - u32 nPixels = Number_of_Pixels_1D; + // Contact_Matrix->vaos = PushArrayP(arena, GLuint, Number_of_Textures_1D * Number_of_Textures_1D); // + // Contact_Matrix->vbos = PushArrayP(arena, GLuint, Number_of_Textures_1D * Number_of_Textures_1D); // + Contact_Matrix->vaos = new GLuint[ Number_of_Textures_1D * Number_of_Textures_1D]; // (GLuint*) malloc(sizeof(GLuint) * Number_of_Textures_1D * Number_of_Textures_1D ); // TODO make sure the memory is freed before the re-allocation + Contact_Matrix->vbos = new GLuint[ Number_of_Textures_1D * Number_of_Textures_1D]; // (GLuint*) malloc(sizeof(GLuint) * Number_of_Textures_1D * Number_of_Textures_1D ); - if (std::abs(delta) >= nPixels || - pixelFrom >= nPixels || - pixelTo >= nPixels) - { - fmt::print( - stderr, - "RearrangeMap: Invalid parameters: delta = {}, pixelFrom = {}, pixelTo = {}, nPixels = {}, file {} line {}\n", - delta, pixelFrom, pixelTo, nPixels, - __FILE__, __LINE__ - ); - assert(0); - } + setContactMatrixVertexArray(Contact_Matrix, false, false); // set the vertex data of the contact matrix - if (pixelFrom > pixelTo) // Swap, make sure pixelFrom is less than pixelTo - { - std::swap(pixelFrom, pixelTo); } - u32 nPixelsInRange = pixelTo - pixelFrom + 1; - - pixelFrom += nPixels; - pixelTo += nPixels; - auto GetRealBufferLocation = [nPixels] (u32 index) - { - u32 result; + // Texture Pixel Lookups texture像素查找 + { + GLuint pixStart, pixStartTex, pixRearrage, pixRearrageTex; + + u32 nTex = (Number_of_Textures_1D + 1) * (Number_of_Textures_1D >> 1); // (32 + 1) * 32 / 2 = 528 + u32 nPix1D = Number_of_Textures_1D * Texture_Resolution; // 32 * 1024 - Assert(index >= 0 && index < 3 * nPixels); + glActiveTexture(GL_TEXTURE2); // 调用 glActiveTexture 函数,可以选择当前活动的纹理单元,并且后续的纹理操作都会影响到该纹理单元 - if (index >= (2 * nPixels)) - { - result = index - (2 * nPixels); - } - else if (index >= nPixels) - { - result = index - nPixels; - } - else + u32 *pixStartLookup = PushArrayP(arena, u32, 2 * nTex); // 申请空间 2 * 528 个 u32 + u32 ptr = 0; + for (u32 i = 0; i < Number_of_Textures_1D; i ++ ) // 遍历每一个texture { - result = index; + for (u32 j = i ; j < Number_of_Textures_1D; j ++ ) + { + pixStartLookup[ptr++] = (u32)(j * Texture_Resolution); // 列 * 1024 双数索引是列,单数是行 + pixStartLookup[ptr++] = (u32)(i * Texture_Resolution); // 行 * 1024 + } } - return(result); - }; + glGenBuffers(1, &pixStart); // 生成一个缓冲区对象,并将其标识符存储到 pixStart 变量中。这样,pixStart 变量就可以用于引用这个生成的缓冲区对象 + glBindBuffer(GL_TEXTURE_BUFFER, pixStart); // 缓冲区对象 pixStart 就会被绑定到当前的纹理缓冲区上,后续的操作会影响这个缓冲区对象 + glBufferData(GL_TEXTURE_BUFFER, sizeof(u32) * 2 * nTex, pixStartLookup, GL_STATIC_DRAW); // 将数据从 pixStartLookup 指向的内存区域拷贝到绑定到 GL_TEXTURE_BUFFER 目标的缓冲区对象中,大小为 sizeof(u32) * 2 * nTex 字节,并且告诉 OpenGL 这些数据是静态的,不会频繁地变化 - u32 forward = delta > 0; // move direction + glGenTextures(1, &pixStartTex); // 生成一个纹理对象,并将其标识符存储到 pixStartTex 变量中 + glBindTexture(GL_TEXTURE_BUFFER, pixStartTex); // 纹理对象 pixStartTex 就会被绑定到当前的纹理缓冲区上,后续的纹理操作(比如使用 glTexBuffer 函数将其与纹理缓冲区对象关联)将会影响到这个纹理对象 + glTexBuffer( // 纹理缓冲区对象与缓冲区对象关联的函数 + GL_TEXTURE_BUFFER, // 要关联到缓冲区的纹理目标,这里是 GL_TEXTURE_BUFFER,表示纹理缓冲区。 + GL_RG32UI, // 纹理缓冲区数据的格式, GL_RG32UI表示每个像素由两个u32组成,一个红色分量和一个绿色分量 + pixStart); // 缓冲区对象的标识符,这个缓冲区会与纹理缓冲区关联 - if (snap) // can not put the selected frag into one frag. - { - if (forward) // move to the end - { - u32 target = GetRealBufferLocation(pixelTo + (u32)delta); - u32 targetContigId = Map_State->contigIds[target] + (target == Number_of_Pixels_1D - 1 ? 1 : 0); // why is the last pixel +1? - if (targetContigId) // only if the target is not the selected contig - { - contig *targetContig = Contigs->contigs_arr + targetContigId - 1; - - u32 targetCoord = IsContigInverted(targetContigId - 1) ? (targetContig->startCoord - targetContig->length + 1) : (targetContig->startCoord + targetContig->length - 1); - while (delta > 0 && - (Map_State->contigIds[target] != targetContigId - 1 || - Map_State->contigRelCoords[target] != targetCoord)) - { - --target; - --delta; - } - } - else - { - delta = 0; - } - } - else // move to the start + Contact_Matrix->pixelStartLookupBuffer = pixStart; // 缓冲区对象标识符 + Contact_Matrix->pixelStartLookupBufferTex = pixStartTex; // 纹理对象标识符 + + FreeLastPushP(arena); // pixStartLookup 释放存放像素点开始的空间 + + glActiveTexture(GL_TEXTURE3); // 激活第三个texture,以下代码会影响第三个texture + + u32 *pixRearrageLookup = PushArrayP(arena, u32, nPix1D); // allocte 1024 * 32 u32 memory + for (u32 i = 0 ; i < nPix1D; i ++ ) { - u32 target = GetRealBufferLocation((u32)((s32)pixelFrom + delta)); - u32 targetContigId = Map_State->contigIds[target]; - if (targetContigId < (Contigs->numberOfContigs - 1)) // only if the target is not the selected contig - { - contig *targetContig = Contigs->contigs_arr + (target ? targetContigId + 1 : 0); - u32 targetCoord = targetContig->startCoord; - while (delta < 0 && (Map_State->contigIds[target] != (target ? targetContigId + 1 : 0) || Map_State->contigRelCoords[target] != targetCoord)) - { - ++target; - ++delta; - } - } - else delta = 0; + pixRearrageLookup[i] = (u32)i; // assign the index to the pixRearrageLookup } + + glGenBuffers(1, &pixRearrage); // generate a buffer object + glBindBuffer(GL_TEXTURE_BUFFER, pixRearrage); // bind as a texture buffer for the current buffer + glBufferData(GL_TEXTURE_BUFFER, sizeof(u32) * nPix1D, pixRearrageLookup, GL_DYNAMIC_DRAW); // copy the data from pixRearrageLookup to the buffer object, and tell OpenGL that the data is dynamic and will change frequently + + glGenTextures(1, &pixRearrageTex); + glBindTexture(GL_TEXTURE_BUFFER, pixRearrageTex); + glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, pixRearrage); + + Contact_Matrix->pixelRearrangmentLookupBuffer = pixRearrage; // 32 * 1024 u32, this is a ascending order of 0 to 1024 * 32 + Contact_Matrix->pixelRearrangmentLookupBufferTex = pixRearrageTex; // texture 的索引 + + FreeLastPushP(arena); // free pixRearrageLookup as it has already been copied to the buffer object with index pixRearrage + + glUniform1ui(glGetUniformLocation(Contact_Matrix->shaderProgram, "pixpertex"), Texture_Resolution); // 将1024传递给 pixpertex,每个texture有多少个像素点 + glUniform1f(glGetUniformLocation(Contact_Matrix->shaderProgram, "oopixpertex"), 1.0f / (f32)Texture_Resolution); // 将 1 / 1024 传递给 oopixpertex + glUniform1ui(glGetUniformLocation(Contact_Matrix->shaderProgram, "ntex1dm1"), Number_of_Textures_1D - 1); // 将31传递给 ntex1dm1 + + glActiveTexture(GL_TEXTURE0); // 关闭激活的texture 3 } - if (delta) + GLuint posAttribFlatShader = (GLuint)glGetAttribLocation(Flat_Shader->shaderProgram, "position"); + u32 pad = 0; + auto PushGenericBuffer = [posAttribFlatShader, pad, arena] (quad_data **quadData, u32 numberOfBuffers) { - u32 startCopyFromRange; // location start to copy - u32 startCopyToRange; // location start to put the copied data + (void)pad; - if (forward) // move to the end - { - startCopyFromRange = pixelTo + 1; - startCopyToRange = pixelFrom; - } - else // move to the start + *quadData = PushStructP(arena, quad_data); + + (*quadData)->vaos = PushArrayP(arena, GLuint, numberOfBuffers); + (*quadData)->vbos = PushArrayP(arena, GLuint, numberOfBuffers); + (*quadData)->nBuffers = numberOfBuffers; + + glUseProgram(Flat_Shader->shaderProgram); + + ForLoop(numberOfBuffers) { - startCopyFromRange = (u32)((s32)pixelFrom + delta); - startCopyToRange = (u32)((s32)pixelTo + delta) + 1; - } + glGenVertexArrays(1, (*quadData)->vaos + index); + glBindVertexArray((*quadData)->vaos[index]); - u32 copySize = std::abs(delta); + glGenBuffers(1, (*quadData)->vbos + index); + glBindBuffer(GL_ARRAY_BUFFER, (*quadData)->vbos[index]); + glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(vertex), NULL, GL_DYNAMIC_DRAW); - u32 *tmpBuffer = new u32[copySize]; - u32 *tmpBuffer2 = new u32[copySize]; // original contig ids - u32 *tmpBuffer3 = new u32[copySize]; // original contig coords - u32 *tmpBuffer4 = new u32[copySize]; // scaff ids - u64 *tmpBuffer5 = new u64[copySize]; // meta data flags + glEnableVertexAttribArray(posAttribFlatShader); + glVertexAttribPointer(posAttribFlatShader, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), 0); - glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); // pixel rearrange buffer - u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, 0, nPixels * sizeof(u32), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); // map the buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); + } + }; - if (buffer) - { - ForLoop(copySize) // copySize is abs(delta) - { - tmpBuffer[index] = buffer[GetRealBufferLocation(index + startCopyFromRange)]; // TODO understand how is the texture buffer data getting exchanged - tmpBuffer2[index] = Map_State->originalContigIds[GetRealBufferLocation(index + startCopyFromRange)]; - tmpBuffer3[index] = Map_State->contigRelCoords[GetRealBufferLocation(index + startCopyFromRange)]; - tmpBuffer4[index] = Map_State->scaffIds[GetRealBufferLocation(index + startCopyFromRange)]; - tmpBuffer5[index] = Map_State->metaDataFlags[GetRealBufferLocation(index + startCopyFromRange)]; - } - - // copy the selected fragment to the new location - if (forward) // move to the ends - { - ForLoop(nPixelsInRange) // (pixelTo - pixelFrom + 1) - { - buffer[GetRealBufferLocation(pixelTo + (u32)delta - index)] = buffer[GetRealBufferLocation(pixelTo - index)]; - Map_State->originalContigIds[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->originalContigIds[GetRealBufferLocation(pixelTo - index)]; - Map_State->contigRelCoords[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->contigRelCoords[GetRealBufferLocation(pixelTo - index)]; - Map_State->scaffIds[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->scaffIds[GetRealBufferLocation(pixelTo - index)]; - Map_State->metaDataFlags[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->metaDataFlags[GetRealBufferLocation(pixelTo - index)]; - } - } - else // move to the start - { - ForLoop(nPixelsInRange) - { - buffer[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = buffer[GetRealBufferLocation(pixelFrom + index)]; - Map_State->originalContigIds[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->originalContigIds[GetRealBufferLocation(pixelFrom + index)]; - Map_State->contigRelCoords[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->contigRelCoords[GetRealBufferLocation(pixelFrom + index)]; - Map_State->scaffIds[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->scaffIds[GetRealBufferLocation(pixelFrom + index)]; - Map_State->metaDataFlags[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->metaDataFlags[GetRealBufferLocation(pixelFrom + index)]; - } - } - - // copy the influenced fragment (delta) to the new location - ForLoop(copySize) - { - buffer[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer[index]; - Map_State->originalContigIds[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer2[index]; - Map_State->contigRelCoords[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer3[index]; - Map_State->scaffIds[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer4[index]; - Map_State->metaDataFlags[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer5[index]; - } - } - else - { - fprintf(stderr, "Could not map pixel rearrange buffer\n"); - } - - glUnmapBuffer(GL_TEXTURE_BUFFER); - glBindBuffer(GL_TEXTURE_BUFFER, 0); - delete[] tmpBuffer ; - delete[] tmpBuffer2; - delete[] tmpBuffer3; - delete[] tmpBuffer4; - delete[] tmpBuffer5; - // FreeLastPush(Working_Set); // tmpBuffer - // FreeLastPush(Working_Set); // tmpBuffer2 - // FreeLastPush(Working_Set); // tmpBuffer3 - // FreeLastPush(Working_Set); // tmpBuffer4 - // FreeLastPush(Working_Set); // tmpBuffer5 + // Grid Data + { + PushGenericBuffer(&Grid_Data, 2 * (Max_Number_of_Contigs + 1)); + } + // Label Box Data + { + PushGenericBuffer(&Label_Box_Data, 2 * Max_Number_of_Contigs); + } - if (update_contigs_flag) UpdateContigsFromMapState(); - - map_edit edit; - edit.finalPix1 = (u32)GetRealBufferLocation((u32)((s32)pixelFrom + delta)); - edit.finalPix2 = (u32)GetRealBufferLocation((u32)((s32)pixelTo + delta)); - edit.delta = (s32)delta; - MoveWayPoints(&edit); + //Scale Bar Data + { + PushGenericBuffer(&Scale_Bar_Data, 4 * (2 + MaxTicksPerScaleBar)); } - return(delta); -} + //Contig Colour Bars + { + PushGenericBuffer(&Contig_ColourBar_Data, Max_Number_of_Contigs); + } + //Scaff Bars + { + PushGenericBuffer(&Scaff_Bar_Data, Max_Number_of_Contigs); + } -/* - BreakMap: cut the contig at loc, and update the original contig ids - loc: the location to cut - ignore_len: the length of the contig to be ignored - extension_flag: if true, consider the extension signal - -*/ -global_function -void -BreakMap( - const u32& loc, - const u32& ignore_len // cut点到开头或者结尾的长度不足ignore_len的contig不会被切断 -) -{ - if (loc >= Number_of_Pixels_1D) + // Extensions { - char buff[512]; - snprintf(buff, sizeof(buff), "[Pixel Cut] Error: loc (%d) should be smaller than number of pixels (%d)", loc, Number_of_Pixels_1D); - MY_CHECK(buff); - assert(0); + push_extensions_to_opengl(arena, 0); } - u32 original_contig_id = Map_State->originalContigIds[loc]; - u32 contig_id = Map_State->contigIds[loc]; - u32 contig_rel_coord = Map_State->contigRelCoords[loc]; - s32 ptr_left = (s32)loc, ptr_right = (s32)loc; - u08 inversed = IsContigInverted(contig_id); - while ( // 从loc向左遍历,找到第一个不满足条件的像素点索引 - --ptr_left >= 0 && - Map_State->contigIds[ptr_left] == contig_id && - (Map_State->contigRelCoords[ptr_left] == Map_State->contigRelCoords[ptr_left+1]+ (inversed ? +1 : -1)) ) {}; - while ( // 从loc向右遍历,找到第一个不满足条件的像素点索引 - ++ptr_right < Number_of_Pixels_1D && - Map_State->contigIds[ptr_right] == contig_id && - (Map_State->contigRelCoords[ptr_right - 1] == Map_State->contigRelCoords[ptr_right]+ (inversed ? +1 : -1)) ) {}; +#ifdef Internal + { + PushGenericBuffer(&Texture_Tile_Grid, 2 * (Number_of_Textures_1D + 1)); + PushGenericBuffer(&QuadTree_Data, 1 << (2 * (Waypoints_Quadtree_Levels + 1))); + } +#endif - if ((loc - ptr_left) < ignore_len || (ptr_right - loc) < ignore_len ) - { - fmt::print( - "[Pixel Cut::warning] original_contig_id {} current_contig_id {}, pixel range: [{}, cut({}), {}], left({}), right({}), smaller than ignore_len ({}), cut at loc: ({}) is ignored\n", - original_contig_id%Max_Number_of_Contigs, - contig_id, - ptr_left, - loc, - ptr_right, - loc - ptr_left, - ptr_right - loc, - ignore_len, loc); - return; - } + // Map Editor + { + Map_Editor = PushStructP(arena, map_editor); + Map_Editor->nEdits = 0; + Map_Editor->editStackPtr = 0; + Map_Editor->nUndone = 0; + Map_Editor->edits = PushArrayP(arena, map_edit, Edits_Stack_Size); + } - ptr_left++; ptr_right--; + // Waypoint Editor + { + Waypoint_Editor = PushStructP(arena, waypoint_editor); + Waypoint_Editor->nWaypointsActive = 0; + Waypoint_Editor->freeWaypoints = {}; + Waypoint_Editor->activeWaypoints = {}; + Waypoint_Editor->freeWaypoints.next = PushArrayP(arena, waypoint, Waypoints_Stack_Size); - // cut the contig by amending the original Contig Ids. - for (u32 tmp = loc; tmp <= ptr_right; tmp++) // left side - { - if (Map_State->originalContigIds[tmp] > (std::numeric_limits::max() - Max_Number_of_Contigs)) + Waypoint_Editor->quadtree = PushQuadTree(arena); + Waypoint_Editor->freeNodes = {}; + Waypoint_Editor->freeNodes.next = PushArrayP(arena, waypoint_quadtree_node, Waypoints_Stack_Size); + + Waypoint_Editor->freeWaypoints.next->prev = &Waypoint_Editor->freeWaypoints; + Waypoint_Editor->freeNodes.next->prev = &Waypoint_Editor->freeNodes; + ForLoop(Waypoints_Stack_Size - 1) { - fmt::print("[Pixel Cut] originalContigIds[{}] + {} = {} is greater than the max number of contigs ({}), file {}, line {}\n", tmp, - Max_Number_of_Contigs, - (u64)Map_State->originalContigIds[tmp] + Max_Number_of_Contigs, - std::numeric_limits::max(), - __FILE__, __LINE__); - assert(0); + waypoint *wayp = Waypoint_Editor->freeWaypoints.next + index; + wayp->next = (wayp + 1); + (wayp + 1)->prev = wayp; + (wayp + 1)->next = 0; + + waypoint_quadtree_node *node = Waypoint_Editor->freeNodes.next + index; + node->next = (node + 1); + (node + 1)->prev = node; + (node + 1)->next = 0; } - Map_State->originalContigIds[tmp] += Max_Number_of_Contigs; // NOTE: the number can not exceed the max number 2**32 - 1 = 4294967295. However, it is not easy to exceed the max number of contigs, as 4294967295 / 4096 = 1048575.9997 } - fmt::print( - "[Pixel Cut] Original contig_id ({}), current_id ({}), pixel range: [{}, {}] {} inversed, cut at {}\n", - original_contig_id%Max_Number_of_Contigs, - contig_id, - ptr_left, - ptr_right, - inversed ? "(is)" : "(isn\'t)", - loc ); - - return ; - -} - + // 为添加的功能初始化指针 + { + // 给pixel_density_extension分配内存 + // pixel_density_extension = new Extension_Graph_Data(Number_of_Pixels_1D); + + // 为 copy textures to cpu 分配内存 + if (textures_array_ptr) delete textures_array_ptr; + textures_array_ptr = new TexturesArray4AI( + Number_of_Textures_1D, + Texture_Resolution, + *fileName, + Contigs + ); + // prepare before reading textures + f32 original_color_control_points[3]; + prepare_before_copy(original_color_control_points); + textures_array_ptr->copy_buffer_to_textures( + Contact_Matrix, + false // show_flag + ); + restore_settings_after_copy(original_color_control_points); -global_function -std::vector -get_exclude_metaData_idx(const std::vector& exclude_tags) -{ - if (exclude_tags.empty()) return std::vector(); + // 为 Pixel Cut 存储的数据分配空间,初始化,并且将数据push到extension中 + if (frag_cut_cal_ptr) delete frag_cut_cal_ptr; + frag_cut_cal_ptr = new FragCutCal( + textures_array_ptr, + Number_of_Pixels_1D, + auto_curation_state.auto_cut_diag_window_for_pixel_mean); - std::vector exclude_frag_idx((u32) exclude_tags.size(), -1); - for (u32 i=0; i < exclude_frag_idx.size(); i++) - { - for (u32 j=0; j < 64; j++) - { - if (!Meta_Data->tags[j]) break; - auto tmp = std::string((char*)Meta_Data->tags[j]); - std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); - if ( tmp == exclude_tags[i]) + // clear mem for textures + textures_array_ptr->clear_textures(); + + // 添加数据到extensions + std::string graph_name = "pixel_discontinuity"; + bool is_pix_density_added = Extensions.is_graph_name_exist(graph_name); + if (!is_pix_density_added) // 未添加pixel density + { + u32 added_num = Extensions.get_num_extensions(); + u32* graph_data = new u32[Number_of_Pixels_1D]; + f32 max_desity = 0.f; + for (auto& i : frag_cut_cal_ptr->hic_pixel_density_origin) max_desity = std::max(max_desity, i); + for (u32 i=0; i < Number_of_Pixels_1D; i ++) { - exclude_frag_idx[i] = j; - break; + graph_data[i] = (u32)(255 * (1.001f- frag_cut_cal_ptr->hic_pixel_density_origin[i] / max_desity)); } + add_graph_to_extensions(arena, (u32*)graph_name.c_str(), graph_data); + delete[] graph_data; + + push_extensions_to_opengl(arena, added_num, 0.05f); // push hic_pixel_density to opengl buffer + } + else + { + fmt::print("hic_pixel_density extension already exists\n"); + assert(false); } } - return exclude_frag_idx; -} - + FenceIn(File_Loaded = 1); -void RedoAllEdits(map_editor* map_editor_) -{ - while (map_editor_->nUndone) RedoMapEdit(); - return ; + if (LoadState(*headerHash)) LoadState(*headerHash + 1); + return(ok); } +global_variable +memory_arena * +Loading_Arena; -void EraseAllEdits(map_editor* map_editor_, u32 max_edit_recorded=Edits_Stack_Size) +global_function +void +SetTheme(struct nk_context *ctx, enum theme theme) { - u32 nEdits = my_Min(max_edit_recorded, map_editor_->nEdits); - ForLoop(nEdits) UndoMapEdit(); - return ; -} - + struct nk_color table[NK_COLOR_COUNT]; + u32 themeSet = 1; -void run_ai_detection() -{ - std::cout << "Running AI detection..." << std::endl; - int status = std::system("/Users/sg35/miniconda3/envs/auto_cut/bin/python /Users/sg35/PretextView/python/autoCut/auto_cut.py"); + switch (theme) + { + case THEME_WHITE: + { + table[NK_COLOR_TEXT] = nk_rgba(70, 70, 70, 255); + table[NK_COLOR_WINDOW] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_HEADER] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_BORDER] = nk_rgba(0, 0, 0, 255); + table[NK_COLOR_BUTTON] = nk_rgba(185, 185, 185, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(170, 170, 170, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(120, 120, 120, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_SELECT] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_SLIDER] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(80, 80, 80, 255); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(70, 70, 70, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(60, 60, 60, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_EDIT] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(0, 0, 0, 255); + table[NK_COLOR_COMBO] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_CHART] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(45, 45, 45, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(180, 180, 180, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(140, 140, 140, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(180, 180, 180, 255); + } + break; - // 检查命令执行状态 - if (status == 0) { - std::cout << "Command successful!" << std::endl; - } else { - std::cerr << "Command failed, code: " << status << std::endl; - } -} + case THEME_RED: + { + table[NK_COLOR_TEXT] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_WINDOW] = nk_rgba(30, 33, 40, 215); + table[NK_COLOR_HEADER] = nk_rgba(181, 45, 69, 220); + table[NK_COLOR_BORDER] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_BUTTON] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(190, 50, 70, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(195, 55, 75, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 60, 60, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SELECT] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SLIDER] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(186, 50, 74, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(191, 55, 79, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_EDIT] = nk_rgba(51, 55, 67, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_COMBO] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_CHART] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(170, 40, 60, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(30, 33, 40, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(181, 45, 69, 220); + } + break; -void cut_frags(const std::vector& problem_locs) -{ - const u32* gap_data_ptr = auto_curation_state.auto_cut_with_extension ? Extensions.get_graph_data_ptr("gap"):nullptr; + case THEME_BLUE: + { + table[NK_COLOR_TEXT] = nk_rgba(20, 20, 20, 255); + table[NK_COLOR_WINDOW] = nk_rgba(202, 212, 214, 215); + table[NK_COLOR_HEADER] = nk_rgba(137, 182, 224, 220); + table[NK_COLOR_BORDER] = nk_rgba(140, 159, 173, 255); + table[NK_COLOR_BUTTON] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(142, 187, 229, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(147, 192, 234, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(182, 215, 215, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_SELECT] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_SLIDER] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(137, 182, 224, 245); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(142, 188, 229, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(147, 193, 234, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_EDIT] = nk_rgba(210, 210, 210, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(20, 20, 20, 255); + table[NK_COLOR_COMBO] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_CHART] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(190, 200, 200, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(156, 193, 220, 255); + } + break; - for (auto & loc_orig : problem_locs) - { - u32 loc = loc_orig; - if (gap_data_ptr) // correct loc with considering the gap extension - { - u32 distance_tmp = 1; - while (distance_tmp <= auto_curation_state.auto_cut_gap_loc_threshold) + case THEME_DARK: { - if (loc_orig + distance_tmp < Number_of_Pixels_1D && gap_data_ptr[loc_orig + distance_tmp] > 0 ) - { - fmt::println( - "[Pixel Cut] gap[{}+{}] = {}, correct cut from ({}) to ({})", - loc_orig, distance_tmp, gap_data_ptr[loc_orig + distance_tmp], loc_orig, loc_orig + distance_tmp - ); - loc = loc_orig + distance_tmp; - break; - } - else if (loc_orig-distance_tmp >= 0 && gap_data_ptr[loc_orig - distance_tmp] > 0) - { - fmt::println( - "[Pixel Cut] gap[{}-{}] = {}, correct cut from ({}) to ({})", - loc_orig, distance_tmp, gap_data_ptr[loc_orig - distance_tmp], loc_orig, loc_orig - distance_tmp - ); - loc = loc_orig - distance_tmp; - break; - } - else - { - distance_tmp++; - } + table[NK_COLOR_TEXT] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_WINDOW] = nk_rgba(57, 67, 71, 215); + table[NK_COLOR_HEADER] = nk_rgba(51, 51, 56, 220); + table[NK_COLOR_BORDER] = nk_rgba(46, 46, 46, 255); + table[NK_COLOR_BUTTON] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(63, 98, 126, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 53, 56, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SELECT] = nk_rgba(57, 67, 61, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SLIDER] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(48, 83, 111, 245); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_EDIT] = nk_rgba(50, 58, 61, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_COMBO] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_CHART] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba(255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(48, 83, 111, 255); } - } - // cut the fragment - BreakMap( - loc, // cut loc - auto_curation_state.auto_cut_smallest_frag_size_in_pixel); // ignore length - UpdateContigsFromMapState(); + break; + + case THEME_BLACK: + case THEME_COUNT: + themeSet = 0; } - Redisplay = 1; + + if (themeSet) nk_style_from_table(ctx, table, Screen_Scale.x, Screen_Scale.y); + else nk_style_default(ctx, Screen_Scale.x, Screen_Scale.y); + + Theme_Colour = table[NK_COLOR_BUTTON_ACTIVE]; + + NK_Context->style.slider.show_buttons = nk_true; + + Current_Theme = theme; } -void AutoCurationFromFragsOrder( - const FragsOrder* frags_order_, - contigs* contigs_, - map_state* map_state_, - SelectArea* select_area) -{ - u08 using_select_area = (select_area && select_area->select_flag)? 1 : 0; - u32 num_frags = contigs_->numberOfContigs; - if (!using_select_area ) - { - if ( num_frags != frags_order_->get_num_frags()) - { - fprintf(stderr, "[AutoCurationFromFragsOrder::error]: Number of contigs(%d) and fragsOrder.num_frags(%d) does not match.\n", num_frags, frags_order_->get_num_frags()); - assert(0); - } + +global_function +void +Setup() +{ + Decompressor = libdeflate_alloc_decompressor(); + if (!Decompressor) + { + fprintf(stderr, "Could not allocate decompressor\n"); + exit(1); } - else + + Compressor = libdeflate_alloc_compressor(12); + if (!Compressor) { - if (select_area->get_to_sort_frags_num() != frags_order_->get_num_frags()) - { - fmt::print( - stderr, - "num_to_sort_contigs({}) != fragsOrder.num_frags({}), file {}, line {}\n", select_area->get_to_sort_frags_num(), - frags_order_->get_num_frags(), - __FILE__, __LINE__); - assert(0); - } + fprintf(stderr, "Could not allocate compressor\n"); + exit(1); } - u32 num_autoCurated_edits=0; + Texture_Buffer_Queue = PushStruct(Working_Set, texture_buffer_queue); - // check the difference between the contigs, order and the new frags order - u32 start_loc = 0; - std::vector> current_order(num_frags); // [contig_id, contig_length] - std::vector predicted_order = frags_order_->get_order_without_chromosomeInfor(); // start from 1 - if (using_select_area) + Meta_Data = PushStruct(Working_Set, meta_data); + memset(Meta_Data, 0, sizeof(meta_data)); + MetaData_Active_Tag = 0; + ForLoop(ArrayCount(Default_Tags)) strcpy((char *)Meta_Data->tags[MetaData_Active_Tag + index], Default_Tags[index]); + if (Grey_Out_Settings) { - std::vector full_predicted_order(num_frags); - std::iota(full_predicted_order.begin(), full_predicted_order.end(), 1); - std::vector frags_id_to_sort = select_area->get_to_sort_frags_id(Contigs); - for (u32 i=0; i < frags_id_to_sort.size(); i++) - full_predicted_order[frags_id_to_sort[i]] = (predicted_order[i]>0?1:-1) * (frags_id_to_sort.front() + std::abs(predicted_order[i])); - predicted_order = full_predicted_order; + delete Grey_Out_Settings; + Grey_Out_Settings = nullptr; } - for (s32 i = 0; i < num_frags; ++i) current_order[i] = {i+1, contigs_->contigs_arr[i].length}; // start from 1 - auto move_current_order_element = [¤t_order, &num_frags](u32 from, u32 to) + Grey_Out_Settings = new GreyOutSettings(Meta_Data); + if (user_profile_settings_ptr) { - if (from >= num_frags || to >= num_frags) - { - fprintf(stderr, "Invalid from(%d) or to(%d), index should betwee [0, %d].\n", from, to, num_frags-1); - return; - } - if (from == to) return; - auto tmp = current_order[from]; - if (from > to) - { - for (u32 i = from; i > to; --i) current_order[i] = current_order[i-1]; - } - else // to > from - { - for (u32 i = from; i < to; ++i) current_order[i] = current_order[i+1]; - } - current_order[to] = tmp; - }; + delete user_profile_settings_ptr; + user_profile_settings_ptr = nullptr; + } + user_profile_settings_ptr = new UserProfileSettings(); + + // Contig Name Label UI + { + Contig_Name_Labels = PushStruct(Working_Set, ui_colour_element_bg); + Contig_Name_Labels->on = 0; + Contig_Name_Labels->fg = Yellow_Text_Float; + Contig_Name_Labels->bg = Grey_Background; +#define DefaultNameLabelTextSize 32.0f + Contig_Name_Labels->size = DefaultNameLabelTextSize; + } - // update the map state based on the new order - for (u32 i = 0; i < num_frags; ++i) + // Scale Bar UI { - if (predicted_order[i] == current_order[i].first) // leave the contig unchanged - { - start_loc += current_order[i].second; - continue; - } - - // only invert - if (predicted_order[i] == -current_order[i].first) - { - u32 pixelFrom = start_loc, pixelEnd= start_loc + current_order[i].second - 1; - InvertMap(pixelFrom, pixelEnd); - AddMapEdit(0, {start_loc, start_loc + current_order[i].second - 1}, true); - current_order[i].first = -current_order[i].first; - start_loc += current_order[i].second; - printf("[Pixel Sort] (#%d) Invert contig %d.\n", ++num_autoCurated_edits, predicted_order[i]); - continue; - } - else if ( - predicted_order[i] != current_order[i].first && - predicted_order[i] != -current_order[i].first) // move the contig to the new position - { - - bool is_curated_inverted = predicted_order[i] < 0; - - // find the pixel range of the contig to move - u32 pixelFrom = 0, tmp_i = 0; // tmp_i is the index of the current contig, which is going to be processed - while (std::abs(predicted_order[i])!=std::abs(current_order[tmp_i].first)) - { - if (tmp_i >= num_frags) - { - char buff[256]; - snprintf(buff, sizeof(buff), "Error: contig %d not found in the current order.\n", current_order[i].first); - MY_CHECK(buff); - assert(0); - } - pixelFrom += current_order[tmp_i].second; // length updated based on the current order - if (pixelFrom>=Number_of_Pixels_1D) - { - char buff[256]; - snprintf(buff, sizeof(buff), "Error: pixelFrom(%d) >= Number_of_Pixels_1D(%d).\n", pixelFrom, Number_of_Pixels_1D); - MY_CHECK(buff); - assert(0); - } - ++tmp_i; - } - u32 pixelEnd = pixelFrom + current_order[tmp_i].second - 1; - if (is_curated_inverted) - { - InvertMap(pixelFrom, pixelEnd); - current_order[tmp_i].first = -current_order[tmp_i].first; - } - s32 delta = start_loc - pixelFrom; - RearrangeMap(pixelFrom, pixelEnd, delta); - AddMapEdit(delta, {(u32)((s32)pixelFrom + delta), (u32)((s32)pixelEnd + delta)}, is_curated_inverted); - - // update the current order, insert the contig to the new position - move_current_order_element(tmp_i, i); // move element from tmp_i to i - start_loc += current_order[i].second; // update start_loc after move fragments - if (is_curated_inverted) - { - printf("[Pixel Sort] (#%d) Invert and Move contig %d to %d, start_loc:%d.\n", ++num_autoCurated_edits, predicted_order[i], i, start_loc); - } - else printf("[Pixel Sort] (#%d) Move contig %d to %d, start_loc:%d.\n", ++num_autoCurated_edits, predicted_order[i], i, start_loc); - continue; - } - else - { - fprintf(stderr, "Unknown error, predicted_order[%d] = %d, current[%d] = %d.\n", i, predicted_order[i], i, current_order[i].first); - continue; - } + Scale_Bars = PushStruct(Working_Set, ui_colour_element_bg); + Scale_Bars->on = 0; + Scale_Bars->fg = Red_Text_Float; + Scale_Bars->bg = Grey_Background; +#define DefaultScaleBarSize 20.0f + Scale_Bars->size = DefaultScaleBarSize; + } + + // Grid UI + { + Grid = PushStruct(Working_Set, ui_colour_element); + Grid->on = 1; + Grid->bg = Grey_Background; +#define DefaultGridSize 0.00025f + Grid->size = DefaultGridSize; } - printf("[Pixel Sort] finished with %d edits\n", num_autoCurated_edits); - return ; -} + // Contig Ids UI + { + Contig_Ids = PushStruct(Working_Set, ui_colour_element); + Contig_Ids->on = 1; +#define DefaultContigIdSize (DefaultGridSize * 3.0f) + Contig_Ids->size = DefaultContigIdSize; + } + // Tool Tip UI + { + Tool_Tip = PushStruct(Working_Set, ui_colour_element_bg); + Tool_Tip->on = 1; + Tool_Tip->fg = Yellow_Text_Float; + Tool_Tip->bg = Grey_Background; +#define DefaultToolTipTextSize 20.0f + Tool_Tip->size = DefaultToolTipTextSize; + } -global_function -void -auto_cut_func( - char * currFileName, - memory_arena* arena = nullptr, - std::string file_save_name = std::string("/auto_curation_tmp") -) -{ - if (!currFileName || !auto_cut_state) return; + // Edit Mode Colours + { + Edit_Mode_Colours = PushStruct(Working_Set, edit_mode_colours); + Edit_Mode_Colours->preSelect = Green_Float; + Edit_Mode_Colours->select = Blue_Float; + Edit_Mode_Colours->invSelect = Red_Float; + Edit_Mode_Colours->fg = Yellow_Text_Float; + Edit_Mode_Colours->bg = Grey_Background; + } - if (auto_cut_state == 2) // restore the cutted frags within the selected area - { - u32 start = auto_curation_state.get_start(); - u32 end = auto_curation_state.get_end(); - if (start < 0 || end < 0) - { - fmt::print( - stdout, - "Error: no area selected. start({}) or end({}) is less than 0\n", - start, end); - return ; - } - fmt::print(stdout, "[Pixel cut] restoring the cutted frags within the selected area: [{}, {}]\n", start, end); - Map_State->restore_cutted_contigs( start, end ); - UpdateContigsFromMapState(); - Redisplay = 1; - return ; + // Waypoint Mode Colours + { + Waypoint_Mode_Data = PushStruct(Working_Set, waypoint_mode_data); + Waypoint_Mode_Data->base = Red_Full; + Waypoint_Mode_Data->selected = Blue_Full; + Waypoint_Mode_Data->text = Yellow_Text_Float; + Waypoint_Mode_Data->bg = Grey_Background; + Waypoint_Mode_Data->size = DefaultWaypointSize; } - // classic cut - { - SelectArea selected_area; - auto_curation_state.get_selected_fragments( - selected_area, - Map_State, - Number_of_Pixels_1D, - Contigs); - glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); - const u32 *pixel_rearrange_index = (const u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, 0, Number_of_Pixels_1D * sizeof(u32), GL_MAP_READ_BIT); - std::vector cut_locs_pixel = frag_cut_cal_ptr->get_cut_locs_pixel( - auto_curation_state, - pixel_rearrange_index, // pixel_rearrangement_buffer - Contigs, - &selected_area - ); - glUnmapBuffer(GL_TEXTURE_BUFFER); - - fmt::print(stdout, "[Pixel cut] cut locs in pixel: ["); - for (auto & i : cut_locs_pixel) fmt::print(stdout, "{} ", i); - fmt::print(stdout, "]\n"); - - // cut the contigs - cut_frags(cut_locs_pixel); + // Scaff Mode Colours + { + Scaff_Mode_Data = PushStruct(Working_Set, meta_mode_data); + Scaff_Mode_Data->text = Yellow_Text_Float; + Scaff_Mode_Data->bg = Grey_Background; + Scaff_Mode_Data->size = DefaultScaffSize; } - // AI cutting - // generate figures - if (0) + // Meta Mode Colours { - fprintf(stdout, "[Pixel cut] generating hic figures...\n"); - Hic_Figure hic_figure( - Total_Genome_Length, - file_save_name, - textures_array_ptr ); - hic_figure.generate_hic_figures(); + MetaData_Mode_Data = PushStruct(Working_Set, meta_mode_data); + MetaData_Mode_Data->text = Yellow_Text_Float; + MetaData_Mode_Data->bg = Grey_Background; + MetaData_Mode_Data->size = DefaultMetaDataSize; + } - // run ai model with python script - run_ai_detection(); + // Extension Mode Colours + { + Extension_Mode_Data = PushStruct(Working_Set, meta_mode_data); + Extension_Mode_Data->text = Yellow_Text_Float; + Extension_Mode_Data->bg = Grey_Background; + Extension_Mode_Data->size = DefaultExtensionSize; + } - // restore the error info via reading the error json - HIC_Problems hic_problems("/auto_curation_tmp/auto_cut_output/infer_result/infer_result.json", (f32) Total_Genome_Length / (f32)Number_of_Pixels_1D ); - std::vector> problem_loc = hic_problems.get_problem_loci(); +#ifdef Internal + { + Tiles = PushStruct(Working_Set, ui_colour_element); + Tiles->on = 1; + Tiles->bg = {0.0f, 1.0f, 1.0f, 1.0f}; - // cut the contigs - // cut_frags(); + QuadTrees = PushStruct(Working_Set, ui_colour_element); + QuadTrees->on = 1; + QuadTrees->bg = {0.0f, 1.0f, 0.0f, 1.0f}; } +#endif - return ; -} + // Contact Matrix Shader + { + Contact_Matrix = PushStruct(Working_Set, contact_matrix); + Contact_Matrix->shaderProgram = CreateShader(FragmentSource_Texture.c_str(), VertexSource_Texture.c_str()); + glUseProgram(Contact_Matrix->shaderProgram); + glBindFragDataLocation(Contact_Matrix->shaderProgram, 0, "outColor"); -global_function -std::unordered_map> collect_hap_cluster( - AutoCurationState& auto_curation_state, - const SelectArea& selected_area -) -{ - std::unordered_map> hap_cluster; - if (!auto_curation_state.hap_cluster_flag) return hap_cluster; + Contact_Matrix->matLocation = glGetUniformLocation(Contact_Matrix->shaderProgram, "matrix"); + glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "tex"), 0); + glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "colormap"), 1); + glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "pixstartlookup"), 2); + glUniform1i(glGetUniformLocation(Contact_Matrix->shaderProgram, "pixrearrangelookup"), 3); - for (const auto& i : selected_area.selected_frag_ids) - { - std::string original_contig_name = std::string((char*)(Original_Contigs+((Contigs->contigs_arr+i)->get_original_contig_id()))->name); - std::transform(original_contig_name.begin(), original_contig_name.end(), original_contig_name.begin(), - [](unsigned char c) { return std::tolower(c); }); - if (original_contig_name.find("hap") != std::string::npos) - { - s32 hap_loc = original_contig_name.find("hap"); - s32 hap_id = std::stoi( - original_contig_name.substr( - hap_loc + 3, original_contig_name.find_first_of("_", hap_loc + 3) - hap_loc - 3)); - if (hap_cluster.find(hap_id) == hap_cluster.end()) - { - hap_cluster[hap_id] = std::vector(); - } - hap_cluster[hap_id].push_back(i - selected_area.selected_frag_ids[0]); - }else + glActiveTexture(GL_TEXTURE1); + + Color_Maps = PushStruct(Working_Set, color_maps); + u32 nMaps = Number_of_Color_Maps; + Color_Maps->maps = PushArray(Working_Set, GLuint, nMaps); + Color_Maps->mapPreviews = PushArray(Working_Set, struct nk_image, nMaps); + Color_Maps->nMaps = nMaps; + Color_Maps->currMap = 1; + + ForLoop(nMaps) { - fmt::print( - stdout, - "[Pixel Sort::Warning]: haplotig cluster is selected but the original contig name does not contain 'hap'.\n"); - auto_curation_state.hap_cluster_flag = false; - break; - } - } - return hap_cluster; -} + u32 mapPreviewImage[256]; + GLuint tbo, tboTex, texPreview; + glGenBuffers(1, &tbo); + glBindBuffer(GL_TEXTURE_BUFFER, tbo); + glBufferData(GL_TEXTURE_BUFFER, sizeof(Color_Map_Data[index]), Color_Map_Data[index], GL_STATIC_DRAW); + glGenTextures(1, &tboTex); + glBindTexture(GL_TEXTURE_BUFFER, tboTex); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, tbo); -global_function -void -auto_sort_func(char* currFileName) -{ - if (!currFileName || !auto_sort_state) return; - - fprintf(stdout, "========================\n"); - fprintf(stdout, "[Pixel Sort] start...\n"); - fprintf(stdout, "[Pixel Sort] smallest_frag_size_in_pixel: %d\n", auto_curation_state.smallest_frag_size_in_pixel); - fprintf(stdout, "[Pixel Sort] link_score_threshold: %.3f\n", auto_curation_state.link_score_threshold); - fprintf(stdout, "[Pixel Sort] Sort mode: %s\n", auto_curation_state.get_sort_mode_name().c_str()); - fmt::print(stdout, "[Pixel Sort] num_clusters: {}\n", auto_curation_state.num_clusters); + Color_Maps->maps[index] = tboTex; - // compress the HiC data - // prepare before reading textures - f32 original_color_control_points[3]; - prepare_before_copy(original_color_control_points); - textures_array_ptr->copy_buffer_to_textures_dynamic( - Contact_Matrix, - false // show_flag - ); - restore_settings_after_copy(original_color_control_points); + glActiveTexture(GL_TEXTURE0); - // check if select the area for sorting - SelectArea selected_area; - auto_curation_state.get_selected_fragments( // consider wheather include the sink and source while calculating the selected area - selected_area, - Map_State, - Number_of_Pixels_1D, - Contigs - ); + ForLoop2(256) //TODO SIMD + { + mapPreviewImage[index2] = ((u32)(Color_Map_Data[index][3 * index2] * 255.0f)) | + (((u32)(Color_Map_Data[index][(3 * index2) + 1] * 255.0f)) << 8) | + (((u32)(Color_Map_Data[index][(3 * index2) + 2] * 255.0f)) << 16) | + 0xff000000; + } + glGenTextures(1, &texPreview); + glBindTexture(GL_TEXTURE_2D, texPreview); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, mapPreviewImage); + glGenerateMipmap(GL_TEXTURE_2D); - std::unordered_map> hap_cluster = collect_hap_cluster(auto_curation_state, selected_area); + Color_Maps->mapPreviews[index] = nk_image_id((s32)texPreview); - bool hap_cluster_flag = (hap_cluster.size() > 1 && auto_curation_state.hap_cluster_flag); - // NOTE: if hap_cluster is selected, then the kmeans cluster is not needed - if (hap_cluster_flag) auto_curation_state.num_clusters = 1; - else auto_curation_state.hap_cluster_flag = false; - bool kmeans_cluster_flag = selected_area.selected_frag_ids.size() > std::max(auto_curation_state.min_frag_num_for_cluster, (s32)auto_curation_state.num_clusters) - && auto_curation_state.num_clusters > 1 - && !hap_cluster_flag; + glActiveTexture(GL_TEXTURE1); + } + NextColorMap(-1); - // correct the selected_area, wheather use or not use the source and sink - if ( hap_cluster_flag || kmeans_cluster_flag ) - { - selected_area.source_frag_id = -1; - selected_area.sink_frag_id = -1; + glActiveTexture(GL_TEXTURE0); + + Color_Maps->cpLocation = glGetUniformLocation(Contact_Matrix->shaderProgram, "controlpoints"); + Color_Maps->controlPoints[0] = 0.0f; + Color_Maps->controlPoints[1] = 0.5f; + Color_Maps->controlPoints[2] = 1.0f; + glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); } - if (auto_curation_state.get_start() >=0 && auto_curation_state.get_end() >= 0 && selected_area.selected_frag_ids.size() > 0) - { - fprintf(stdout, "[Pixel Sort] local sort within selected area\n"); - fprintf(stdout, "[Pixel Sort] Selected pixel range: %d ~ %d\n", auto_curation_state.get_start(), auto_curation_state.get_end()); - std::stringstream ss; - ss << selected_area.selected_frag_ids[0]; - for (u32 i = 1; i < selected_area.selected_frag_ids.size(); ++i) - ss << ", " << selected_area.selected_frag_ids[i]; - fmt::print(stdout, "[Pixel Sort] Selected fragments ({}): {}\n\n", selected_area.selected_frag_ids.size(), ss.str().c_str()); - selected_area.select_flag = true; - selected_area.start_pixel = auto_curation_state.get_start(); - selected_area.end_pixel = auto_curation_state.get_end(); + // Flat Color Shader + { + Flat_Shader = PushStruct(Working_Set, flat_shader); + Flat_Shader->shaderProgram = CreateShader(FragmentSource_Flat.c_str(), VertexSource_Flat.c_str()); + + glUseProgram(Flat_Shader->shaderProgram); + glBindFragDataLocation(Flat_Shader->shaderProgram, 0, "outColor"); + + Flat_Shader->matLocation = glGetUniformLocation(Flat_Shader->shaderProgram, "matrix"); + Flat_Shader->colorLocation = glGetUniformLocation(Flat_Shader->shaderProgram, "color"); } + + // Fonts + { + UI_Shader = PushStruct(Working_Set, ui_shader); + UI_Shader->shaderProgram = CreateShader(FragmentSource_UI.c_str(), VertexSource_UI.c_str()); + glUseProgram(UI_Shader->shaderProgram); + glBindFragDataLocation(UI_Shader->shaderProgram, 0, "outColor"); + glUniform1i(glGetUniformLocation(UI_Shader->shaderProgram, "tex"), 0); + UI_Shader->matLocation = glGetUniformLocation(UI_Shader->shaderProgram, "matrix"); - textures_array_ptr->cal_compressed_hic( - Contigs, - Extensions, - false, // is_extension_required - false, // is_mass_center_required - &selected_area - ); - std::vector exclude_tags = {"haplotig", "unloc"}; - std::vector exclude_meta_tag_idx = get_exclude_metaData_idx(exclude_tags); + FontStash_Context = glfonsCreate(512, 512, FONS_ZERO_TOPLEFT); + Font_Normal = fonsAddFontMem(FontStash_Context, "Sans Regular", FontNormal, (s32)FontNormal_Size, 0); - // cluster and sort - // 1. clustering - if (hap_cluster_flag || kmeans_cluster_flag ) - { - std::vector> clusters; - if (kmeans_cluster_flag) // kmeans cluster + if (Font_Normal == FONS_INVALID) { - fmt::print(stdout, - "[Pixel Sort] kmeans: num of clusters: {}, num of selected fragments: {}\n", auto_curation_state.num_clusters, - selected_area.selected_frag_ids.size()); - #ifdef PYTHON_SCOPED_INTERPRETER - if (kmeans_cluster && kmeans_cluster->is_init) - { - fmt::print(stdout, "[Pixel Sort] cluster with Python module.\n"); - clusters = kmeans_cluster->kmeans_func( - auto_curation_state.num_clusters, - textures_array_ptr->get_compressed_hic()); - } else - { - fmt::print(stdout, "[Pixel Sort::warning] Python clustering module initializing failed. Clustering with Eigen-based algorithm.\n"); - clusters = spectral_clustering(*(textures_array_ptr->get_compressed_hic()), auto_curation_state.num_clusters); - } - #else - fmt::print(stdout, "[Pixel Sort::warning] Python clustering module initializing failed. Clustering with Eigen-based algorithm.\n"); - clusters = spectral_clustering(*(textures_array_ptr->get_compressed_hic()), auto_curation_state.num_clusters); - #endif // PYTHON_SCOPED_INTERPRETER - } else // hap cluster + fprintf(stderr, "Could not add font 'DroidSerif-Regular.ttf'\n"); + exit(1); + } + Font_Bold = fonsAddFontMem(FontStash_Context, "Sans Bold", FontBold, (s32)FontBold_Size, 0); + if (Font_Bold == FONS_INVALID) { - fmt::print(stdout, - "[Pixel Sort] haplotig cluster: num of haps: {}, num of selected fragments: {}\n", - hap_cluster.size(), selected_area.selected_frag_ids.size()); - clusters.clear(); - for (const auto& i : hap_cluster) - { - clusters.push_back(i.second); - } + fprintf(stderr, "Could not add font 'DroidSerif-Bold.ttf'\n"); + exit(1); } + } - // create the frag4compress object for sorting within the clusters - std::vector clusters_frags(clusters.size()); - for (u32 i = 0; i < clusters.size(); ++i) - { - std::sort(clusters[i].begin(), clusters[i].end()); - clusters_frags[i].re_allocate_mem( - Contigs, - clusters[i]); - } + // Quad EBO + { + GLushort pIndexQuad[6]; + pIndexQuad[0] = 0; + pIndexQuad[1] = 1; + pIndexQuad[2] = 2; + pIndexQuad[3] = 2; + pIndexQuad[4] = 3; + pIndexQuad[5] = 0; - #ifdef DEBUG - fmt::print(stdout, "[Pixel Sort]: Kmeans clusters size: {} \n", clusters.size()); - for (u32 i = 0; i < clusters.size(); ++i) - { - auto& tmp = clusters[i]; - fmt::print(stdout, "\t[{} ({})]: ", i, clusters[i].size()); - if (tmp.size() > 0) - { - for (auto& tmp1 : tmp) fmt::print(stdout, "{}, ", tmp1); - } - fmt::print(stdout, "\n"); - } - fmt::print(stdout, "\n"); - #endif // DEBUG + glGenBuffers(1, &Quad_EBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLushort), pIndexQuad, GL_STATIC_DRAW); + } - // 3. sort within the clusters - std::vector frags_order_list; - for (u32 i = 0; i < clusters.size(); ++i) + GLuint posAttribFlatShader = (GLuint)glGetAttribLocation(Flat_Shader->shaderProgram, "position"); + auto PushGenericBuffer = [posAttribFlatShader] (quad_data **quadData, u32 numberOfBuffers) -> void + { + *quadData = PushStruct(Working_Set, quad_data); + + (*quadData)->vaos = PushArray(Working_Set, GLuint, numberOfBuffers); + (*quadData)->vbos = PushArray(Working_Set, GLuint, numberOfBuffers); + + glUseProgram(Flat_Shader->shaderProgram); + + ForLoop(numberOfBuffers) { - frags_order_list.push_back(FragsOrder(clusters[i].size())); - } - for (u32 i = 0; i < clusters.size(); ++i) - { - // every cluster forms a likelihood table - if (clusters[i].size() < 2) - continue; - auto tmp_likelihood_table = LikelihoodTable( - &clusters_frags[i], - textures_array_ptr->get_compressed_hic(), - (f32)auto_curation_state.smallest_frag_size_in_pixel / ((f32)Number_of_Pixels_1D + 1.f), - exclude_meta_tag_idx, - Number_of_Pixels_1D, - &clusters[i] - ); - frag_sort_method->sort_method_mask( - tmp_likelihood_table, - frags_order_list[i], - selected_area, - auto_curation_state, - &clusters_frags[i], - true - ); - } - // merge all clusters - FragsOrder merged_frags_order(frags_order_list, clusters); + glGenVertexArrays(1, (*quadData)->vaos + index); + glBindVertexArray((*quadData)->vaos[index]); - #ifdef DEBUG - merged_frags_order.print_order() ; - #endif // DEBUG + glGenBuffers(1, (*quadData)->vbos + index); + glBindBuffer(GL_ARRAY_BUFFER, (*quadData)->vbos[index]); + glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(vertex), NULL, GL_DYNAMIC_DRAW); - AutoCurationFromFragsOrder( - &merged_frags_order, - Contigs, - Map_State, - &selected_area); + glEnableVertexAttribArray(posAttribFlatShader); + glVertexAttribPointer(posAttribFlatShader, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), 0); - // evaluate the compressed hic between all of the clusters + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); + } + }; - // 4. sort the clusters + // Edit Mode Data + { + PushGenericBuffer(&Edit_Mode_Data, 12); } - else // sort without clustering - { - if (auto_curation_state.num_clusters == 1) - { - fmt::print(stdout, "[Pixel Sort] sort without clustering.\n"); - } else // (selected_area.selected_frag_ids.size() <= std::max(auto_curation_state.min_frag_num_for_cluster, (s32)auto_curation_state.num_clusters) ) - { - fmt::print( - stdout, - "[Pixel Sort] num_cluster({})>1 but selected fragments({}) <= max(min_num_frags_for_cluster {}, num_cluster {})\n", - auto_curation_state.num_clusters, - selected_area.selected_frag_ids.size(), - auto_curation_state.min_frag_num_for_cluster, - auto_curation_state.num_clusters); - } - - FragsOrder frags_order(textures_array_ptr->get_num_frags()); // intilize the frags_order with the number of fragments including the filtered out ones - // exclude the fragments with first two tags during the auto curation - LikelihoodTable likelihood_table( - textures_array_ptr->get_frags(), - textures_array_ptr->get_compressed_hic(), - (f32)auto_curation_state.smallest_frag_size_in_pixel / ((f32)Number_of_Pixels_1D + 1.f), - exclude_meta_tag_idx, - Number_of_Pixels_1D); - - #ifdef DEBUG_OUTPUT_LIKELIHOOD_TABLE - likelihood_table.output_fragsInfo_likelihoodTable("frags_info_likelihood_table.txt", textures_array_ptr->get_compressed_hic()); - #endif // DEBUG_OUTPUT_LIKELIHOOD_TABLE - // use the compressed_hic to calculate the frags_order directly - frag_sort_method->sort_method_mask( - likelihood_table, - frags_order, - selected_area, - auto_curation_state, - textures_array_ptr->get_frags(), - true // sort chromosomes according length - ); + // Tool Tip Data + { + PushGenericBuffer(&Tool_Tip_Data, 1); + } - AutoCurationFromFragsOrder( - &frags_order, - Contigs, - Map_State, - &selected_area - ); + // Waypoint Data + { + PushGenericBuffer(&Waypoint_Data, (3 * Waypoints_Stack_Size) + 1); } - std::cout << std::endl; - auto_sort_state = 0; + // Nuklear Setup + { +#define NK_Memory_Size MegaByte(32) + NK_Device = PushStruct(Working_Set, device); + u08 *nkCmdMemory = PushArray(Working_Set, u08, NK_Memory_Size); + nk_buffer_init_fixed(&NK_Device->cmds, (void *)nkCmdMemory, NK_Memory_Size); + NK_Device->lastContextMemory = PushArray(Working_Set, u08, NK_Memory_Size); + memset(NK_Device->lastContextMemory, 0, NK_Memory_Size); + NK_Device->prog = UI_Shader->shaderProgram; + NK_Device->uniform_proj = UI_Shader->matLocation; + NK_Device->attrib_pos = UI_SHADER_LOC_POSITION; + NK_Device->attrib_uv = UI_SHADER_LOC_TEXCOORD; + NK_Device->attrib_col = UI_SHADER_LOC_COLOR; - // clear mem for textures and compressed hic - textures_array_ptr->clear_textures(); - textures_array_ptr->clear_compressed_hic(); - return; -} + GLsizei vs = sizeof(nk_glfw_vertex); + size_t vp = offsetof(nk_glfw_vertex, position); + size_t vt = offsetof(nk_glfw_vertex, uv); + size_t vc = offsetof(nk_glfw_vertex, col); + glGenBuffers(1, &NK_Device->vbo); + glGenBuffers(1, &NK_Device->ebo); + glGenVertexArrays(1, &NK_Device->vao); -// set the vertex array for the contact matrix -// copy_flag: if true, set vaos to [-1, 1, -1, 1] for copying tiles from opengl buffer to cpu memory -global_function -void setContactMatrixVertexArray( - contact_matrix* Contact_Matrix_, - bool copy_flag, - bool regenerate_flag) -{ - glUseProgram(Contact_Matrix_->shaderProgram); - GLuint posAttrib = (GLuint)glGetAttribLocation(Contact_Matrix_->shaderProgram, "position"); - GLuint texAttrib = (GLuint)glGetAttribLocation(Contact_Matrix_->shaderProgram, "texcoord"); - - /* - 从左上到右下开始遍历,见上述编号 - */ - f32 x = -0.5f; - f32 y = 0.5f; - f32 quadSize = 1.0f / (f32)Number_of_Textures_1D; // 1.0 / 32.f = 0.03125 - f32 allCornerCoords[2][2] = {{0.0f, 1.0f}, {1.0f, 0.0f}}; + glBindVertexArray(NK_Device->vao); + glBindBuffer(GL_ARRAY_BUFFER, NK_Device->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NK_Device->ebo); - if (regenerate_flag) // first delete the generated vertex array objects and buffers - { - glDeleteVertexArrays((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vaos); - glDeleteBuffers((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vbos); - } - - u32 ptr = 0; - ForLoop(Number_of_Textures_1D) - { - ForLoop2(Number_of_Textures_1D) - { - tex_vertex textureVertices[4]; + glEnableVertexAttribArray((GLuint)NK_Device->attrib_pos); + glEnableVertexAttribArray((GLuint)NK_Device->attrib_uv); + glEnableVertexAttribArray((GLuint)NK_Device->attrib_col); - glGenVertexArrays( // 生成对象的名称, - 1, // 生成的个数 - Contact_Matrix_->vaos + ptr); // 存储位置的指针 - glBindVertexArray(Contact_Matrix_->vaos[ptr]); // 绑定顶点名称 + glVertexAttribPointer((GLuint)NK_Device->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)NK_Device->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)NK_Device->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); - f32 *cornerCoords = allCornerCoords[index2 >= index ? 0 : 1]; // 包含对角线的上三角的时候取第一行{0, 1},否则取第二行{1, 0} 为了解决关于对角线对称 - - u32 min = my_Min(index, index2); - u32 max = my_Max(index, index2); - /* - 对称性编号 - [[ 0 1 2 3 4 5 6 7 8 9] - [ 1 10 11 12 13 14 15 16 17 18] - [ 2 11 19 20 21 22 23 24 25 26] - [ 3 12 20 27 28 29 30 31 32 33] - [ 4 13 21 28 34 35 36 37 38 39] - [ 5 14 22 29 35 40 41 42 43 44] - [ 6 15 23 30 36 41 45 46 47 48] - [ 7 16 24 31 37 42 46 49 50 51] - [ 8 17 25 32 38 43 47 50 52 53] - [ 9 18 26 33 39 44 48 51 53 54]] - */ - f32 texture_index_symmetric = (f32)((min * (Number_of_Textures_1D - 1)) - - (((min-1) * min) >> 1 ) - + max) ; - - /* - 3 2 - 0 1 - */ - f32 half_len = 1.0f; - textureVertices[0].x = !copy_flag? x : -half_len; // x -> [-0.5, 0.5] - textureVertices[0].y = !copy_flag? y - quadSize : -half_len; // y -> [-0.5, 0.5] - textureVertices[0].s = !copy_flag? cornerCoords[0] : 0.f; // s表示texture的水平分量 - textureVertices[0].t = !copy_flag? cornerCoords[1] : 0.f; // t表示texture的垂直分量 - textureVertices[0].u = texture_index_symmetric; + NK_Atlas = PushStruct(Working_Set, nk_font_atlas); + nk_font_atlas_init_default(NK_Atlas); + nk_font_atlas_begin(NK_Atlas); + struct nk_font_config cfg = nk_font_config(14); + cfg.oversample_h = 3; + cfg.oversample_v = 3; + NK_Font = nk_font_atlas_add_from_memory(NK_Atlas, FontBold, (nk_size)FontBold_Size, 22 * Screen_Scale.y, &cfg); + NK_Font_Browser = nk_font_atlas_add_from_memory(NK_Atlas, FontBold, (nk_size)FontBold_Size, 14 * Screen_Scale.y, &cfg); - textureVertices[1].x = !copy_flag? x + quadSize : half_len; - textureVertices[1].y = !copy_flag? y - quadSize : -half_len; - textureVertices[1].s = !copy_flag? 1.0f : 1.f; - textureVertices[1].t = !copy_flag? 1.0f : 0.f; - textureVertices[1].u = texture_index_symmetric; + s32 w,h; + const void *image = nk_font_atlas_bake(NK_Atlas, &w, &h, NK_FONT_ATLAS_RGBA32); - textureVertices[2].x = !copy_flag? x + quadSize : half_len; - textureVertices[2].y = !copy_flag? y : half_len; - textureVertices[2].s = !copy_flag? cornerCoords[1] : 1.f; - textureVertices[2].t = !copy_flag? cornerCoords[0] : 1.f; - textureVertices[2].u = texture_index_symmetric; + glGenTextures(1, &NK_Device->font_tex); + glBindTexture(GL_TEXTURE_2D, NK_Device->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); - textureVertices[3].x = !copy_flag? x : -half_len; - textureVertices[3].y = !copy_flag? y : half_len; - textureVertices[3].s = !copy_flag? 0.0f : 0.f; - textureVertices[3].t = !copy_flag? 0.0f : 1.f; - textureVertices[3].u = texture_index_symmetric; + nk_font_atlas_end(NK_Atlas, nk_handle_id((s32)NK_Device->font_tex), &NK_Device->null); + u08 *nkContextMemory = PushArray(Working_Set, u08, NK_Memory_Size); + nk_init_fixed(NK_Context, (void *)nkContextMemory, NK_Memory_Size, &NK_Font->handle); - glGenBuffers( // 生成buffer的名字 存储在contact_matrix->vbos+ptr 对应的地址上 - 1, // 个数 - Contact_Matrix_->vbos + ptr); // 指针,存储生成的名字 - glBindBuffer( // 绑定到一个已经命名的buffer对象上 - GL_ARRAY_BUFFER, // 绑定的对象,gl_array_buffer 一般是绑定顶点的信息 - Contact_Matrix_->vbos[ptr]); // 通过输入buffer的名字将其绑定到gl_array_buffer - glBufferData( // 创建、初始化存储数据的buffer - GL_ARRAY_BUFFER, // 绑定的对象,gl_array_buffer 一般是绑定顶点的信息 - 4 * sizeof(tex_vertex), // 对象的大小(bytes) - textureVertices, // 即将绑定到gl_array_buffer的数据的指针 - GL_STATIC_DRAW); // 绑定后的数据是用于干什么的 - - // tell GL how to interpret the data in the GL_ARRAY_BUFFER - glEnableVertexAttribArray(posAttrib); - glVertexAttribPointer( // 定义一个数组存储所有的通用顶点属性 - posAttrib, // 即将修改属性的索引 - 2, // 定义每个属性的参数的个数, 只能为1 2 3 4中的一个,初始值为4 - GL_FLOAT, // 定义该属性的数据类型,则数据为4个字节, 占用两个的话,则一共为8字节 - GL_FALSE, // normalized, 是否要在取用的时候规范化这些数 - sizeof(tex_vertex), // stride 定义从一个信息的指针下一个信息指针的bytes的间隔, 一个信息包到下一个信息包的长度 - 0); // const void* 指定当前与 GL_ARRAY_BUFFER 绑定的缓冲区数据头指针偏移多少个到 第一个通用顶点属性的第一个特征开始的位置。 初始值为 0。因为此处想要读取的信息为 x 和 y - glEnableVertexAttribArray(texAttrib); // - glVertexAttribPointer( - texAttrib, - 3, - GL_FLOAT, // 定义每个属性的数据类型 - GL_FALSE, // GL_FALSE, GL_TRUE - sizeof(tex_vertex), - (void *)(3 * sizeof(GLfloat))); // 偏移量为3,因为前三个为x, y and pad, 想要读取的数据为 s, t and u - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); - - x += quadSize; // next column - ++ptr; // ptr for current texture - } + SetTheme(NK_Context, THEME_DARK); - y -= quadSize; // next row - x = -0.5f; // from column 0 + Theme_Name[THEME_RED] = (u08 *)"Red"; + Theme_Name[THEME_BLUE] = (u08 *)"Blue"; + Theme_Name[THEME_WHITE] = (u08 *)"White"; + Theme_Name[THEME_BLACK] = (u08 *)"Black"; + Theme_Name[THEME_DARK] = (u08 *)"Dark"; } + + Loading_Arena = PushStruct(Working_Set, memory_arena); + CreateMemoryArena(*Loading_Arena, MegaByte(512)); + //Loading_Arena = PushSubArena(Working_Set, MegaByte(128)); } + global_function -void -prepare_before_copy(f32 *original_color_control_points) +void +TakeScreenShot(int nchannels = 4) { - // prepare before reading textures - glActiveTexture(GL_TEXTURE1); + s32 viewport[4]; + glGetIntegerv (GL_VIEWPORT, viewport); - glBindTexture(GL_TEXTURE_BUFFER, Color_Maps->maps[Color_Maps->nMaps-1]); // 绑定到颜色映射的最后一个颜色 - glActiveTexture(GL_TEXTURE0); - for (u32 i = 0; i < 3 ; i ++ ) - original_color_control_points[i] = Color_Maps->controlPoints[i]; - Color_Maps->controlPoints[0] = 0.0f; - Color_Maps->controlPoints[1] = 0.5f; - Color_Maps->controlPoints[2] = 1.0f; - glUseProgram(Contact_Matrix->shaderProgram); - glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); - setContactMatrixVertexArray(Contact_Matrix, true, true); // set the vertex for copying - return ; + u08 *imageBuffer = PushArray(Working_Set, u08, (u32)(nchannels * viewport[2] * viewport[3])); + glReadPixels ( 0, 0, viewport[2], viewport[3], + nchannels==4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, + imageBuffer); + + stbi_flip_vertically_on_write(1); + stbi_write_png("PretextView_ScreenShot.png", viewport[2], viewport[3], nchannels, imageBuffer, nchannels * viewport[2]); //TODO change png compression to use libdeflate zlib impl + FreeLastPush(Working_Set); } + global_function -void -restore_settings_after_copy(const f32 *original_color_control_points) +void +InvertMap( + u32 pixelFrom, // from pixel + u32 pixelTo, // end pixel + bool update_contigs_flag + ) { - // restore settings - setContactMatrixVertexArray(Contact_Matrix, false, true); // restore the vertex array for rendering - for (u32 i = 0 ; i < 3 ; i ++ ) - Color_Maps->controlPoints[i] = original_color_control_points[i]; - glUseProgram(Contact_Matrix->shaderProgram); - glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_BUFFER, Color_Maps->maps[Color_Maps->currMap]); - glActiveTexture(GL_TEXTURE0); -} - + if (pixelFrom > pixelTo) + { + u32 tmp = pixelFrom; + pixelFrom = pixelTo; + pixelTo = tmp; + } -global_function void -JumpToDiagonal(GLFWwindow *window) -{ - Camera_Position.x = -Camera_Position.y; - ClampCamera(); - Redisplay = 1; -} + u32 nPixels = Number_of_Pixels_1D; + Assert(pixelFrom < nPixels); + Assert(pixelTo < nPixels); + + u32 copySize = (pixelTo - pixelFrom + 1) >> 1; + + u32 *tmpBuffer = new u32[copySize]; + u32 *tmpBuffer2 = new u32[copySize]; + u32 *tmpBuffer3 = new u32[copySize]; + u32 *tmpBuffer4 = new u32[copySize]; + u64 *tmpBuffer5 = new u64[copySize]; -global_function -u32 -ToggleEditMode(GLFWwindow* window) -{ - u32 result = 1; + glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); + u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, 0, nPixels * sizeof(u32), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); // map buffer to read and write - if (Edit_Mode && !Edit_Pixels.editing) + if (buffer) { - Edit_Pixels.scaffSelecting = 0; - Global_Mode = mode_normal; - if (Tool_Tip->on) + ForLoop(copySize) // put the first half in the buffer { - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); + tmpBuffer[index] = buffer[pixelFrom + index]; + tmpBuffer2[index] = Map_State->contigRelCoords[pixelFrom + index]; + tmpBuffer3[index] = Map_State->originalContigIds[pixelFrom + index]; + tmpBuffer4[index] = Map_State->scaffIds[pixelFrom + index]; + tmpBuffer5[index] = Map_State->metaDataFlags[pixelFrom + index]; + } + + ForLoop(copySize) // set the first half to the second half + { + buffer[pixelFrom + index] = buffer[pixelTo - index]; + Map_State->contigRelCoords[pixelFrom + index] = Map_State->contigRelCoords[pixelTo - index]; + Map_State->originalContigIds[pixelFrom + index] = Map_State->originalContigIds[pixelTo - index]; + Map_State->scaffIds[pixelFrom + index] = Map_State->scaffIds[pixelTo - index]; + Map_State->metaDataFlags[pixelFrom + index] = Map_State->metaDataFlags[pixelTo - index]; + } + + ForLoop(copySize) // set the second half from the buffer + { + buffer[pixelTo - index] = tmpBuffer[index]; + Map_State->contigRelCoords[pixelTo - index] = tmpBuffer2[index]; + Map_State->originalContigIds[pixelTo - index] = tmpBuffer3[index]; + Map_State->scaffIds[pixelTo - index] = tmpBuffer4[index]; + Map_State->metaDataFlags[pixelTo - index] = tmpBuffer5[index]; } - } - else if (Normal_Mode || Select_Sort_Area_Mode || MetaData_Edit_Mode) - { - Global_Mode = mode_edit; } else { - result = 0; + fprintf(stderr, "Could not map pixel rearrange buffer\n"); } - return(result); + glUnmapBuffer(GL_TEXTURE_BUFFER); + glBindBuffer(GL_TEXTURE_BUFFER, 0); + + delete[] tmpBuffer ; + delete[] tmpBuffer2; + delete[] tmpBuffer3; + delete[] tmpBuffer4; + delete[] tmpBuffer5; + + if (update_contigs_flag) UpdateContigsFromMapState(); // update the contigs from the buffer + + map_edit edit; + edit.finalPix1 = (u32)pixelTo; + edit.finalPix2 = (u32)pixelFrom; + edit.delta = 0; + MoveWayPoints(&edit); } global_function - u32 - ToggleExtensionMode(GLFWwindow *window) +s32 +RearrangeMap( // NOTE: VERY IMPORTANT + u32 pixelFrom, // start of the fragment + u32 pixelTo, // end of the fragment + s32 delta, // movement of the distance + u08 snap, // if true, the fragment will snap to the nearest contig + bool update_contigs_flag // if true, update the contigs from the map state + ) { - u32 result = 1; - if (Extension_Mode) - { - Global_Mode = mode_normal; - } - else if (Normal_Mode) + u32 nPixels = Number_of_Pixels_1D; + + if (std::abs(delta) >= nPixels || + pixelFrom >= nPixels || + pixelTo >= nPixels) { - Global_Mode = mode_extension; + fmt::print( + stderr, + "RearrangeMap: Invalid parameters: delta = {}, pixelFrom = {}, pixelTo = {}, nPixels = {}, file {} line {}\n", + delta, pixelFrom, pixelTo, nPixels, + __FILE__, __LINE__ + ); + assert(0); } - else + + if (pixelFrom > pixelTo) // Swap, make sure pixelFrom is less than pixelTo { - result = 0; + std::swap(pixelFrom, pixelTo); } + u32 nPixelsInRange = pixelTo - pixelFrom + 1; - return result; -} + pixelFrom += nPixels; + pixelTo += nPixels; + auto GetRealBufferLocation = [nPixels] (u32 index) + { + u32 result; -global_function -u32 -ToggleWaypointMode(GLFWwindow* window) -{ - u32 result = 1; + Assert(index >= 0 && index < 3 * nPixels); - if (Waypoint_Edit_Mode) - { - Global_Mode = mode_normal; - if (Tool_Tip->on) + if (index >= (2 * nPixels)) { - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); + result = index - (2 * nPixels); + } + else if (index >= nPixels) + { + result = index - nPixels; + } + else + { + result = index; } - } - else if (Normal_Mode) - { - Global_Mode = mode_waypoint_edit; - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); - } - else - { - result = 0; - } - return(result); -} + return(result); + }; -global_function -u32 -ToggleScaffMode(GLFWwindow* window) -{ - u32 result = 1; + u32 forward = delta > 0; // move direction - if (Scaff_Edit_Mode && !Scaff_Painting_Flag) + if (snap) // can not put the selected frag into one frag. { - Global_Mode = mode_normal; - Scaff_Painting_Flag = 0; - Scaff_Painting_Id = 0; - Scaff_FF_Flag = 0; - if (Tool_Tip->on) + if (forward) // move to the end { - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); + u32 target = GetRealBufferLocation(pixelTo + (u32)delta); + u32 targetContigId = Map_State->contigIds[target] + (target == Number_of_Pixels_1D - 1 ? 1 : 0); // why is the last pixel +1? + if (targetContigId) // only if the target is not the selected contig + { + contig *targetContig = Contigs->contigs_arr + targetContigId - 1; + + u32 targetCoord = IsContigInverted(targetContigId - 1) ? (targetContig->startCoord - targetContig->length + 1) : (targetContig->startCoord + targetContig->length - 1); + while (delta > 0 && + (Map_State->contigIds[target] != targetContigId - 1 || + Map_State->contigRelCoords[target] != targetCoord)) + { + --target; + --delta; + } + } + else + { + delta = 0; + } + } + else // move to the start + { + u32 target = GetRealBufferLocation((u32)((s32)pixelFrom + delta)); + u32 targetContigId = Map_State->contigIds[target]; + if (targetContigId < (Contigs->numberOfContigs - 1)) // only if the target is not the selected contig + { + contig *targetContig = Contigs->contigs_arr + (target ? targetContigId + 1 : 0); + u32 targetCoord = targetContig->startCoord; + while (delta < 0 && (Map_State->contigIds[target] != (target ? targetContigId + 1 : 0) || Map_State->contigRelCoords[target] != targetCoord)) + { + ++target; + ++delta; + } + } + else delta = 0; } } - else if (Normal_Mode) - { - Global_Mode = mode_scaff_edit; - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); - } - else - { - result = 0; - } - - return(result); -} - -global_function -u32 -ToggleMetaDataMode(GLFWwindow* window) -{ - u32 result = 1; - if (MetaData_Edit_Mode) + if (delta) { - Global_Mode = mode_normal; - if (Tool_Tip->on) + u32 startCopyFromRange; // location start to copy + u32 startCopyToRange; // location start to put the copied data + + if (forward) // move to the end { - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); + startCopyFromRange = pixelTo + 1; + startCopyToRange = pixelFrom; + } + else // move to the start + { + startCopyFromRange = (u32)((s32)pixelFrom + delta); + startCopyToRange = (u32)((s32)pixelTo + delta) + 1; } - } - else if (Normal_Mode || (Edit_Mode && !Edit_Pixels.editing) || Select_Sort_Area_Mode) - { - Global_Mode = mode_meta_edit; - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); - } - else - { - result = 0; - } - return(result); -} + u32 copySize = std::abs(delta); + u32 *tmpBuffer = new u32[copySize]; + u32 *tmpBuffer2 = new u32[copySize]; // original contig ids + u32 *tmpBuffer3 = new u32[copySize]; // original contig coords + u32 *tmpBuffer4 = new u32[copySize]; // scaff ids + u64 *tmpBuffer5 = new u64[copySize]; // meta data flags -global_function -u32 -ToggleSelectSortAreaMode(GLFWwindow* window) -{ - u32 result = 1; + glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); // pixel rearrange buffer + u32 *buffer = (u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, 0, nPixels * sizeof(u32), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); // map the buffer - if (Select_Sort_Area_Mode) - { - Global_Mode = mode_normal; - if (Tool_Tip->on) + if (buffer) { - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); + ForLoop(copySize) // copySize is abs(delta) + { + tmpBuffer[index] = buffer[GetRealBufferLocation(index + startCopyFromRange)]; // TODO understand how is the texture buffer data getting exchanged + tmpBuffer2[index] = Map_State->originalContigIds[GetRealBufferLocation(index + startCopyFromRange)]; + tmpBuffer3[index] = Map_State->contigRelCoords[GetRealBufferLocation(index + startCopyFromRange)]; + tmpBuffer4[index] = Map_State->scaffIds[GetRealBufferLocation(index + startCopyFromRange)]; + tmpBuffer5[index] = Map_State->metaDataFlags[GetRealBufferLocation(index + startCopyFromRange)]; + } + + // copy the selected fragment to the new location + if (forward) // move to the ends + { + ForLoop(nPixelsInRange) // (pixelTo - pixelFrom + 1) + { + buffer[GetRealBufferLocation(pixelTo + (u32)delta - index)] = buffer[GetRealBufferLocation(pixelTo - index)]; + Map_State->originalContigIds[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->originalContigIds[GetRealBufferLocation(pixelTo - index)]; + Map_State->contigRelCoords[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->contigRelCoords[GetRealBufferLocation(pixelTo - index)]; + Map_State->scaffIds[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->scaffIds[GetRealBufferLocation(pixelTo - index)]; + Map_State->metaDataFlags[GetRealBufferLocation(pixelTo + (u32)delta - index)] = Map_State->metaDataFlags[GetRealBufferLocation(pixelTo - index)]; + } + } + else // move to the start + { + ForLoop(nPixelsInRange) + { + buffer[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = buffer[GetRealBufferLocation(pixelFrom + index)]; + Map_State->originalContigIds[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->originalContigIds[GetRealBufferLocation(pixelFrom + index)]; + Map_State->contigRelCoords[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->contigRelCoords[GetRealBufferLocation(pixelFrom + index)]; + Map_State->scaffIds[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->scaffIds[GetRealBufferLocation(pixelFrom + index)]; + Map_State->metaDataFlags[GetRealBufferLocation((u32)((s32)pixelFrom + delta) + index)] = Map_State->metaDataFlags[GetRealBufferLocation(pixelFrom + index)]; + } + } + + // copy the influenced fragment (delta) to the new location + ForLoop(copySize) + { + buffer[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer[index]; + Map_State->originalContigIds[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer2[index]; + Map_State->contigRelCoords[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer3[index]; + Map_State->scaffIds[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer4[index]; + Map_State->metaDataFlags[GetRealBufferLocation(index + startCopyToRange)] = tmpBuffer5[index]; + } + } + else + { + fprintf(stderr, "Could not map pixel rearrange buffer\n"); } - } - else if (Normal_Mode || (Edit_Mode && !Edit_Pixels.editing) || MetaData_Edit_Mode) - { - Global_Mode = mode_select_sort_area; - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); - } - else - { - result = 0; - } - return(result); + glUnmapBuffer(GL_TEXTURE_BUFFER); + glBindBuffer(GL_TEXTURE_BUFFER, 0); + delete[] tmpBuffer ; + delete[] tmpBuffer2; + delete[] tmpBuffer3; + delete[] tmpBuffer4; + delete[] tmpBuffer5; + // FreeLastPush(Working_Set); // tmpBuffer + // FreeLastPush(Working_Set); // tmpBuffer2 + // FreeLastPush(Working_Set); // tmpBuffer3 + // FreeLastPush(Working_Set); // tmpBuffer4 + // FreeLastPush(Working_Set); // tmpBuffer5 -} + if (update_contigs_flag) UpdateContigsFromMapState(); + + map_edit edit; + edit.finalPix1 = (u32)GetRealBufferLocation((u32)((s32)pixelFrom + delta)); + edit.finalPix2 = (u32)GetRealBufferLocation((u32)((s32)pixelTo + delta)); + edit.delta = (s32)delta; + MoveWayPoints(&edit); + } + + return(delta); +} +/* + BreakMap: cut the contig at loc, and update the original contig ids + loc: the location to cut + ignore_len: the length of the contig to be ignored + extension_flag: if true, consider the extension signal + +*/ global_function -void -ToggleToolTip(GLFWwindow* window) -{ - Tool_Tip->on = !Tool_Tip->on; - if (Tool_Tip->on) +void +BreakMap( + const int& loc, + const u32& ignore_len // cut点到开头或者结尾的长度不足ignore_len的contig不会被切断 +) +{ + if (loc >= Number_of_Pixels_1D) { - f64 mousex, mousey; - glfwGetCursorPos(window, &mousex, &mousey); - MouseMove(window, mousex, mousey); + char buff[512]; + snprintf(buff, sizeof(buff), "[Pixel Cut] Error: loc (%d) should be smaller than number of pixels (%d)", loc, Number_of_Pixels_1D); + MY_CHECK(buff); + assert(0); } -} - -#if 0 -enum -MouseButon -{ - left, - right, - middle, -}; - -struct -KeyBindings -{ - s32 ui; - s32 editMode; - s32 waypointMode; - s32 undoEdit; - s32 redoEdit; - s32 selectWholeSeq_key; - MouseButon selectWholeSeq_mouse; + u32 original_contig_id = Map_State->originalContigIds[loc]; + u32 contig_id = Map_State->contigIds[loc]; + u32 contig_rel_coord = Map_State->contigRelCoords[loc]; -}; -#endif - -global_variable -s32 -Windowed_Xpos, Windowed_Ypos, Windowed_Width, Windowed_Height; + s32 ptr_left = (s32)loc, ptr_right = (s32)loc; + u08 inversed = IsContigInverted(contig_id); + while ( // 从loc向左遍历,找到最后一个满足条件的像素点索引 + ptr_left > 0 && + Map_State->contigIds[ptr_left] == contig_id && + (Map_State->contigRelCoords[ptr_left-1] == Map_State->contigRelCoords[ptr_left]+ (inversed ? +1 : -1)) ) { --ptr_left; }; + while ( // 从loc向右遍历,找到最后一个满足条件的像素点索引 + ptr_right < Number_of_Pixels_1D-1 && + Map_State->contigIds[ptr_right] == contig_id && + (Map_State->contigRelCoords[ptr_right] == Map_State->contigRelCoords[ptr_right+1]+ (inversed ? +1 : -1)) ) {++ptr_right;}; + + int l_len = loc - ptr_left + 1, r_len = ptr_right - loc ; // treat the loc as the right side + if (l_len <= (ignore_len) || r_len <= (ignore_len-1) ) + { + fmt::print( + "[Pixel Cut::warning] original_contig_id {} current_contig_id {}, pixel range: [{}, cut({}), {}], left_len({})<=ignore_len({}) or right_len({})<=ignore_len({}-1), cut at loc: ({}) is ignored\n", + original_contig_id % Max_Number_of_Contigs, + contig_id, + ptr_left, + loc, + ptr_right, + l_len, ignore_len, + r_len, ignore_len, + loc); + return; + } -#if 0 -struct -GLFWKeyAndScancode -{ - s32 key; - s32 scancode; -}; + // cut the contig by amending the original Contig Ids. + for (u32 tmp = loc + 1; tmp <= ptr_right; tmp++) // cut_loc is included as left + { + if (Map_State->originalContigIds[tmp] > (std::numeric_limits::max() - Max_Number_of_Contigs)) + { + fmt::print("[Pixel Cut] originalContigIds[{}] + {} = {} is greater than the max number of contigs ({}), file {}, line {}\n", tmp, + Max_Number_of_Contigs, + (u64)Map_State->originalContigIds[tmp] + Max_Number_of_Contigs, + std::numeric_limits::max(), + __FILE__, __LINE__); + assert(0); + } + Map_State->originalContigIds[tmp] += Max_Number_of_Contigs; // NOTE: the number can not exceed the max number 2**32 - 1 = 4294967295. However, it is not easy to exceed the max number of contigs, as 4294967295 / 4096 = 1048575.9997 + } -global_variable -GLFWKeyAndScancode -LastPressedKey = {0, 0}; -#endif + fmt::print( + "[Pixel Cut] Original contig_id ({}), current_id ({}), pixel range: [{}, {}] {} inversed, cut at {}\n", + original_contig_id%Max_Number_of_Contigs, + contig_id, + ptr_left, + ptr_right, + inversed ? "(is)" : "(isn\'t)", + loc ); -global_variable -nk_keys -NK_Pressed_Keys[1024] = {}; + return ; -global_variable -u32 -NK_Pressed_Keys_Ptr = 0; +} -global_variable -u32 -GatheringTextInput = 0; global_function -void -KeyBoard(GLFWwindow* window, s32 key, s32 scancode, s32 action, s32 mods) -{ - if (!Loading && !auto_sort_state && !auto_cut_state && (action != GLFW_RELEASE || key == GLFW_KEY_SPACE || key == GLFW_KEY_A || key == GLFW_KEY_LEFT_SHIFT)) - { - if (UI_On) - { -#if 0 - LastPressedKey.key = key; - LastPressedKey.scancode = scancode; -#else - (void)scancode; -#endif +std::vector +get_exclude_metaData_idx(const std::vector& exclude_tags) +{ + if (exclude_tags.empty()) return std::vector(); - if (key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) - { - if (glfwGetWindowMonitor(window)) - { - glfwSetWindowMonitor(window, NULL, - Windowed_Xpos, Windowed_Ypos, - Windowed_Width, Windowed_Height, 0); - } - else - { - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); - if (monitor) - { - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - glfwGetWindowPos(window, &Windowed_Xpos, &Windowed_Ypos); - glfwGetWindowSize(window, &Windowed_Width, &Windowed_Height); - glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); - } - } - } -#ifdef Internal - else if (key == GLFW_KEY_ESCAPE && !mods) + std::vector exclude_frag_idx((u32) exclude_tags.size(), -1); + for (u32 i=0; i < exclude_frag_idx.size(); i++) + { + for (u32 j=0; j < 64; j++) + { + if (!Meta_Data->tags[j]) break; + auto tmp = std::string((char*)Meta_Data->tags[j]); + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); + if ( tmp == exclude_tags[i]) { - glfwSetWindowShouldClose(window, GLFW_TRUE); + exclude_frag_idx[i] = j; + break; } -#endif - /*nk_input_key(NK_Context, NK_KEY_DEL, glfwGetKey(window, GLFW_KEY_DELETE) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_ENTER, glfwGetKey(window, GLFW_KEY_ENTER) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_TAB, glfwGetKey(window, GLFW_KEY_TAB) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_BACKSPACE, glfwGetKey(window, GLFW_KEY_BACKSPACE) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_LEFT, glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_RIGHT, glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_UP, glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_DOWN, glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS); + } + } + return exclude_frag_idx; +} - if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) - { - nk_input_key(NK_Context, NK_KEY_COPY, glfwGetKey(window, GLFW_KEY_C) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_PASTE, glfwGetKey(window, GLFW_KEY_P) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_CUT, glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_CUT, glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS); - nk_input_key(NK_Context, NK_KEY_SHIFT, 1); - } - else - { - nk_input_key(NK_Context, NK_KEY_COPY, 0); - nk_input_key(NK_Context, NK_KEY_PASTE, 0); - nk_input_key(NK_Context, NK_KEY_CUT, 0); - nk_input_key(NK_Context, NK_KEY_SHIFT, 0); - }*/ - else if (key == GLFW_KEY_U) - { - Deferred_Close_UI = 1; - } - else if (action != GLFW_RELEASE && !GatheringTextInput) - { - nk_keys addKey = NK_KEY_NONE; - switch (key) - { - case GLFW_KEY_DELETE: - addKey = NK_KEY_DEL; - break; +void RedoAllEdits(map_editor* map_editor_) +{ + while (map_editor_->nUndone) RedoMapEdit(); + return ; +} - case GLFW_KEY_ENTER: - addKey = NK_KEY_ENTER; - break; - case GLFW_KEY_TAB: - addKey = NK_KEY_TAB; - break; +void EraseAllEdits(map_editor* map_editor_, u32 max_edit_recorded=Edits_Stack_Size) +{ + u32 nEdits = my_Min(max_edit_recorded, map_editor_->nEdits); + ForLoop(nEdits) UndoMapEdit(); + return ; +} - case GLFW_KEY_BACKSPACE: - addKey = NK_KEY_BACKSPACE; - break; - case GLFW_KEY_LEFT: - addKey = NK_KEY_LEFT; - break; +void run_ai_detection() +{ + std::cout << "Running AI detection..." << std::endl; + int status = std::system("/Users/sg35/miniconda3/envs/auto_cut/bin/python /Users/sg35/PretextView/python/autoCut/auto_cut.py"); - case GLFW_KEY_RIGHT: - addKey = NK_KEY_RIGHT; - break; + // 检查命令执行状态 + if (status == 0) { + std::cout << "Command successful!" << std::endl; + } else { + std::cerr << "Command failed, code: " << status << std::endl; + } +} - case GLFW_KEY_UP: - addKey = NK_KEY_UP; - break; +void cut_frags(const std::vector& problem_locs, bool consider_gap_extension_flag=true, bool consider_min_len_flag=true) +{ + const u32* gap_data_ptr = (auto_curation_state.auto_cut_with_extension && consider_gap_extension_flag) ? Extensions.get_graph_data_ptr("gap"):nullptr; - case GLFW_KEY_DOWN: - addKey = NK_KEY_DOWN; - break; + for (auto & loc_orig : problem_locs) + { + int loc = loc_orig; + if (gap_data_ptr) // correct loc with considering the gap extension + { + int distance_tmp = 0; + while (distance_tmp <= auto_curation_state.auto_cut_gap_loc_threshold) + { + if (loc_orig + distance_tmp < Number_of_Pixels_1D && gap_data_ptr[loc_orig + distance_tmp] > 0 ) + { + fmt::println( + "[Pixel Cut] gap[{}+{}] = {}, correct cut from ({}) to ({})", + loc_orig, distance_tmp, gap_data_ptr[loc_orig + distance_tmp], loc_orig, loc_orig + distance_tmp + ); + loc = loc_orig + distance_tmp; + break; } - - if (addKey != NK_KEY_NONE) + else if (loc_orig-distance_tmp >= 0 && gap_data_ptr[loc_orig - distance_tmp] > 0) + { + fmt::println( + "[Pixel Cut] gap[{}-{}] = {}, correct cut from ({}) to ({})", + loc_orig, distance_tmp, gap_data_ptr[loc_orig - distance_tmp], loc_orig, loc_orig - distance_tmp + ); + loc = loc_orig - distance_tmp; + break; + } + else { - NK_Pressed_Keys[NK_Pressed_Keys_Ptr++] = addKey; - if (NK_Pressed_Keys_Ptr == ArrayCount(NK_Pressed_Keys)) NK_Pressed_Keys_Ptr = 0; + distance_tmp++; } } } - else // not UI_On - { - u32 keyPressed = 1; - - switch (key) - { - - case GLFW_KEY_A: - if (Scaff_Edit_Mode) - { - if (action != GLFW_RELEASE) Scaff_FF_Flag |= 1; - else Scaff_FF_Flag &= ~1; - } - else keyPressed = 0; - break; + // cut the fragment + BreakMap( + loc, // cut loc + consider_min_len_flag ? auto_curation_state.auto_cut_smallest_frag_size_in_pixel:1); // ignore length + UpdateContigsFromMapState(); + } + Redisplay = 1; +} - case GLFW_KEY_B: - Scale_Bars->on = !Scale_Bars->on; - break; - case GLFW_KEY_C: - if (Select_Sort_Area_Mode && auto_curation_state.get_selected_fragments_num(Map_State, Number_of_Pixels_1D) > 0) // local area break - { - auto_cut_state = 1; - } - else // open "coverage" extension - { - if (!Extensions.head) break; - TraverseLinkedList(Extensions.head, extension_node) - { - switch (node->type) - { - case extension_graph: - { - - graph *gph = (graph *)node->extension; - if (strcmp((char *)gph->name, "coverage") == 0) - { - gph->on = !gph->on; - break; - } - } - } - } - } - - break; - - case GLFW_KEY_D: - if (Scaff_Edit_Mode && (mods & GLFW_MOD_SHIFT)) - { - ForLoop(Contigs->numberOfContigs) (Contigs->contigs_arr + index)->scaffId = 0; - UpdateScaffolds(); - } - else if (MetaData_Edit_Mode && (mods & GLFW_MOD_SHIFT)) memset(Map_State->metaDataFlags, 0, Number_of_Pixels_1D * sizeof(u64)); - else keyPressed = 0; - break; - - case GLFW_KEY_E: - keyPressed = ToggleEditMode(window); - break; +void AutoCurationFromFragsOrder( + const FragsOrder* frags_order_, + contigs* contigs_, + map_state* map_state_, + SelectArea* select_area) +{ + u08 using_select_area = (select_area && select_area->select_flag)? 1 : 0; + u32 num_frags = contigs_->numberOfContigs; + if (!using_select_area ) + { + if ( num_frags != frags_order_->get_num_frags()) + { + fprintf(stderr, "[AutoCurationFromFragsOrder::error]: Number of contigs(%d) and fragsOrder.num_frags(%d) does not match.\n", num_frags, frags_order_->get_num_frags()); + assert(0); + } + } + else + { + if (select_area->get_to_sort_frags_num() != frags_order_->get_num_frags()) + { + fmt::print( + stderr, + "num_to_sort_contigs({}) != fragsOrder.num_frags({}), file {}, line {}\n", select_area->get_to_sort_frags_num(), + frags_order_->get_num_frags(), + __FILE__, __LINE__); + assert(0); + } + } - case GLFW_KEY_F: - keyPressed = ToggleSelectSortAreaMode(window); - break; + u32 num_autoCurated_edits=0; - case GLFW_KEY_G: - if (!Extensions.head) break; - TraverseLinkedList(Extensions.head, extension_node) - { - switch (node->type) - { - case extension_graph: - { + // check the difference between the contigs, order and the new frags order + u32 start_loc = 0; + std::vector> current_order(num_frags); // [contig_id, contig_length] + std::vector predicted_order = frags_order_->get_order_without_chromosomeInfor(); // start from 1 + if (using_select_area) + { + std::vector full_predicted_order(num_frags); + std::iota(full_predicted_order.begin(), full_predicted_order.end(), 1); + std::vector frags_id_to_sort = select_area->get_to_sort_frags_id(Contigs); + for (u32 i=0; i < frags_id_to_sort.size(); i++) + full_predicted_order[frags_id_to_sort[i]] = (predicted_order[i]>0?1:-1) * (frags_id_to_sort.front() + std::abs(predicted_order[i])); + predicted_order = full_predicted_order; + } + for (s32 i = 0; i < num_frags; ++i) current_order[i] = {i+1, contigs_->contigs_arr[i].length}; // start from 1 + auto move_current_order_element = [¤t_order, &num_frags](u32 from, u32 to) + { + if (from >= num_frags || to >= num_frags) + { + fprintf(stderr, "Invalid from(%d) or to(%d), index should betwee [0, %d].\n", from, to, num_frags-1); + return; + } + if (from == to) return; + auto tmp = current_order[from]; + if (from > to) + { + for (u32 i = from; i > to; --i) current_order[i] = current_order[i-1]; + } + else // to > from + { + for (u32 i = from; i < to; ++i) current_order[i] = current_order[i+1]; + } + current_order[to] = tmp; + }; + + // update the map state based on the new order + for (u32 i = 0; i < num_frags; ++i) + { + if (predicted_order[i] == current_order[i].first) // leave the contig unchanged + { + start_loc += current_order[i].second; + continue; + } - graph *gph = (graph *)node->extension; - if (strcmp((char *)gph->name, "gap") == 0) - { - gph->on = !gph->on; - break; - } - } - } - } - break; + // only invert + if (predicted_order[i] == -current_order[i].first) + { + u32 pixelFrom = start_loc, pixelEnd= start_loc + current_order[i].second - 1; + InvertMap(pixelFrom, pixelEnd); + AddMapEdit(0, {start_loc, start_loc + current_order[i].second - 1}, true); + current_order[i].first = -current_order[i].first; + start_loc += current_order[i].second; + printf("[Pixel Sort] (#%d) Invert contig %d.\n", ++num_autoCurated_edits, predicted_order[i]); + continue; + } + else if ( + predicted_order[i] != current_order[i].first && + predicted_order[i] != -current_order[i].first) // move the contig to the new position + { - case GLFW_KEY_H: - if (Select_Sort_Area_Mode) - { - auto_curation_state.hap_cluster_flag = !auto_curation_state.hap_cluster_flag; - } - else keyPressed = 0; - break; + bool is_curated_inverted = predicted_order[i] < 0; - case GLFW_KEY_I: - Contig_Ids->on = !Contig_Ids->on; - break; + // find the pixel range of the contig to move + u32 pixelFrom = 0, tmp_i = 0; // tmp_i is the index of the current contig, which is going to be processed + while (std::abs(predicted_order[i])!=std::abs(current_order[tmp_i].first)) + { + if (tmp_i >= num_frags) + { + char buff[256]; + snprintf(buff, sizeof(buff), "Error: contig %d not found in the current order.\n", current_order[i].first); + MY_CHECK(buff); + assert(0); + } + pixelFrom += current_order[tmp_i].second; // length updated based on the current order + if (pixelFrom>=Number_of_Pixels_1D) + { + char buff[256]; + snprintf(buff, sizeof(buff), "Error: pixelFrom(%d) >= Number_of_Pixels_1D(%d).\n", pixelFrom, Number_of_Pixels_1D); + MY_CHECK(buff); + assert(0); + } + ++tmp_i; + } + u32 pixelEnd = pixelFrom + current_order[tmp_i].second - 1; + if (is_curated_inverted) + { + InvertMap(pixelFrom, pixelEnd); + current_order[tmp_i].first = -current_order[tmp_i].first; + } + s32 delta = start_loc - pixelFrom; + RearrangeMap(pixelFrom, pixelEnd, delta); + AddMapEdit(delta, {(u32)((s32)pixelFrom + delta), (u32)((s32)pixelEnd + delta)}, is_curated_inverted); - case GLFW_KEY_J: - JumpToDiagonal(window); - break; + // update the current order, insert the contig to the new position + move_current_order_element(tmp_i, i); // move element from tmp_i to i + start_loc += current_order[i].second; // update start_loc after move fragments + if (is_curated_inverted) + { + printf("[Pixel Sort] (#%d) Invert and Move contig %d to %d, start_loc:%d.\n", ++num_autoCurated_edits, predicted_order[i], i, start_loc); + } + else printf("[Pixel Sort] (#%d) Move contig %d to %d, start_loc:%d.\n", ++num_autoCurated_edits, predicted_order[i], i, start_loc); + continue; + } + else + { + fprintf(stderr, "Unknown error, predicted_order[%d] = %d, current[%d] = %d.\n", i, predicted_order[i], i, current_order[i].first); + continue; + } + } + printf("[Pixel Sort] finished with %d edits\n", num_autoCurated_edits); - case GLFW_KEY_K: - if (Select_Sort_Area_Mode) - auto_curation_state.auto_cut_with_extension = !auto_curation_state.auto_cut_with_extension; - else keyPressed = 0; - break; + return ; +} - case GLFW_KEY_L: - if (Waypoint_Edit_Mode) - { - Long_Waypoints_Mode = (Long_Waypoints_Mode +1) % 3; - } - else - { - Grid->on = !Grid->on; - } - break; - case GLFW_KEY_M: - keyPressed = ToggleMetaDataMode(window); - break; +global_function +void +auto_cut_func( + char * currFileName, + memory_arena* arena = nullptr, + std::string file_save_name = std::string("/auto_curation_tmp") +) +{ + if (!currFileName || !auto_cut_state) return; - case GLFW_KEY_N: - Contig_Name_Labels->on = !Contig_Name_Labels->on; - break; + if (auto_cut_state == 2) // restore the cutted frags within the selected area + { + u32 start = auto_curation_state.get_start(); + u32 end = auto_curation_state.get_end(); + if (start < 0 || end < 0) + { + fmt::print( + stdout, + "Error: no area selected. start({}) or end({}) is less than 0\n", + start, end); + return ; + } + fmt::print(stdout, "[Pixel cut] restoring the cutted frags within the selected area: [{}, {}]\n", start, end); + Map_State->restore_cutted_contigs( start, end ); + UpdateContigsFromMapState(); + Redisplay = 1; + return ; + } - case GLFW_KEY_O: - break; - - case GLFW_KEY_P: - break; + // classic cut + { + SelectArea selected_area; + auto_curation_state.get_selected_fragments( + selected_area, + Map_State, + Number_of_Pixels_1D, + Contigs); + glBindBuffer(GL_TEXTURE_BUFFER, Contact_Matrix->pixelRearrangmentLookupBuffer); + const u32 *pixel_rearrange_index = (const u32 *)glMapBufferRange(GL_TEXTURE_BUFFER, 0, Number_of_Pixels_1D * sizeof(u32), GL_MAP_READ_BIT); + std::vector cut_locs_pixel = frag_cut_cal_ptr->get_cut_locs_pixel( + auto_curation_state, + pixel_rearrange_index, // pixel_rearrangement_buffer + Contigs, + &selected_area + ); + glUnmapBuffer(GL_TEXTURE_BUFFER); + + fmt::print(stdout, "[Pixel cut] cut locs in pixel: ["); + for (auto & i : cut_locs_pixel) fmt::print(stdout, "{} ", i); + fmt::print(stdout, "]\n"); + + // cut the contigs + cut_frags(cut_locs_pixel); + } - case GLFW_KEY_Q: - if (Edit_Mode || Select_Sort_Area_Mode) - { - UndoMapEdit(); - } - else - { - keyPressed = 0; - } - break; + // AI cutting + // generate figures + if (0) + { + fprintf(stdout, "[Pixel cut] generating hic figures...\n"); + Hic_Figure hic_figure( + Total_Genome_Length, + file_save_name, + textures_array_ptr ); + hic_figure.generate_hic_figures(); - case GLFW_KEY_R: - if (mods == GLFW_MOD_CONTROL) - { - Loading = 1; - } - else if (Extension_Mode && Extensions.head) - { - TraverseLinkedList(Extensions.head, extension_node) - { - switch (node->type) - { - case extension_graph: - { - graph *gph = (graph *)node->extension; - if (strcmp((char *)gph->name, "repeat_density") == 0) - { - gph->on = !gph->on; - break; - } - } - } - } - break; - } - else - { - keyPressed = 0; - } - break; + // run ai model with python script + run_ai_detection(); - case GLFW_KEY_S: - if (Edit_Mode) - { - Edit_Pixels.snap = !Edit_Pixels.snap; - } - else if (mods & GLFW_MOD_SHIFT) - { - Scaffs_Always_Visible = Scaffs_Always_Visible ? 0 : 1; - } - else if (Select_Sort_Area_Mode) - { - auto_curation_state.clear(); // clear the selected area - } - else - { - keyPressed = ToggleScaffMode(window); - } - break; + // restore the error info via reading the error json + HIC_Problems hic_problems("/auto_curation_tmp/auto_cut_output/infer_result/infer_result.json", (f32) Total_Genome_Length / (f32)Number_of_Pixels_1D ); + std::vector> problem_loc = hic_problems.get_problem_loci(); - case GLFW_KEY_T: - if (Extension_Mode && Extensions.head) - { - TraverseLinkedList(Extensions.head, extension_node) - { - switch (node->type) - { - case extension_graph: - { + // cut the contigs + // cut_frags(); + } - graph *gph = (graph *)node->extension; - if (strcmp((char *)gph->name, "telomere") == 0) - { - gph->on = !gph->on; - break; - } - } - } - } - break; - } - else - { - ToggleToolTip(window); - } - break; + return ; +} - case GLFW_KEY_U: - UI_On = !UI_On; - ++NK_Device->lastContextMemory[0]; - Mouse_Move.x = Mouse_Move.y = -1; - break; - case GLFW_KEY_V: - break; +global_function +std::unordered_map> collect_hap_cluster( + AutoCurationState& auto_curation_state, + const SelectArea& selected_area +) +{ + std::unordered_map> hap_cluster; + if (!auto_curation_state.hap_cluster_flag) return hap_cluster; - case GLFW_KEY_W: - if (Edit_Mode || Select_Sort_Area_Mode) - { - RedoMapEdit(); - } - else - { - keyPressed = ToggleWaypointMode(window); - } - break; + for (const auto& i : selected_area.selected_frag_ids) + { + std::string original_contig_name = std::string((char*)(Original_Contigs+((Contigs->contigs_arr+i)->get_original_contig_id()))->name); + std::transform(original_contig_name.begin(), original_contig_name.end(), original_contig_name.begin(), + [](unsigned char c) { return std::tolower(c); }); + if (original_contig_name.find("hap") != std::string::npos) + { + s32 hap_loc = original_contig_name.find("hap"); + s32 hap_id = std::stoi( + original_contig_name.substr( + hap_loc + 3, original_contig_name.find_first_of("_", hap_loc + 3) - hap_loc - 3)); + if (hap_cluster.find(hap_id) == hap_cluster.end()) + { + hap_cluster[hap_id] = std::vector(); + } + hap_cluster[hap_id].push_back(i - selected_area.selected_frag_ids[0]); + }else + { + fmt::print( + stdout, + "[Pixel Sort::Warning]: haplotig cluster is selected but the original contig name does not contain 'hap'.\n"); + auto_curation_state.hap_cluster_flag = false; + break; + } + } + return hap_cluster; +} - case GLFW_KEY_X: - keyPressed = ToggleExtensionMode(window); - break; - case GLFW_KEY_Y: - break; - case GLFW_KEY_Z: - if (Select_Sort_Area_Mode) - { - auto_cut_state = 2; // restore the cutted frags within the selected area - } - break; - case GLFW_KEY_SPACE: - if (Edit_Mode && !Edit_Pixels.editing && action == GLFW_PRESS) - { - Edit_Pixels.selecting = 1; - Edit_Pixels.selectPixels = Edit_Pixels.pixels; - f64 x, y; - glfwGetCursorPos(window, &x, &y); - MouseMove(window, x, y); - } - else if (Edit_Mode && !Edit_Pixels.editing && action == GLFW_RELEASE) - { - Edit_Pixels.editing = 1; - Edit_Pixels.selecting = 0; - f64 x, y; - glfwGetCursorPos(window, &x, &y); - MouseMove(window, x, y); - } - else if (Edit_Mode && Edit_Pixels.editing && action == GLFW_PRESS) - { - InvertMap(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); - Global_Edit_Invert_Flag = !Global_Edit_Invert_Flag; - } - else if (Waypoint_Edit_Mode && Selected_Waypoint && action == GLFW_PRESS) - { - f64 x, y; - glfwGetCursorPos(window, &x, &y); - RemoveWayPoint(Selected_Waypoint); - MouseMove(window, x, y); - } - else if (Scaff_Edit_Mode && (action == GLFW_PRESS || action == GLFW_RELEASE)) - { - f64 x, y; - glfwGetCursorPos(window, &x, &y); - Scaff_Painting_Flag = action == GLFW_PRESS ? 2 : 0; - MouseMove(window, x, y); - if (action == GLFW_RELEASE) UpdateScaffolds(); - } - else if (MetaData_Edit_Mode && (action == GLFW_PRESS || action == GLFW_RELEASE)) - { - f64 x, y; - glfwGetCursorPos(window, &x, &y); - MetaData_Edit_State = action == GLFW_PRESS ? 2 : 0; - MouseMove(window, x, y); - } - else if (Select_Sort_Area_Mode && action == GLFW_PRESS) - { - u32 selected_frag_num = auto_curation_state.get_selected_fragments_num(Map_State, Number_of_Pixels_1D); - if (selected_frag_num >= 2) - { - auto_sort_state = 1; - } - } - else - { - keyPressed = 0; - } - break; - - case GLFW_KEY_LEFT_SHIFT: - if (Scaff_Edit_Mode) - { - if (action != GLFW_RELEASE) Scaff_FF_Flag |= 2; - else Scaff_FF_Flag &= ~2; - } - else if (Edit_Mode) Edit_Pixels.scaffSelecting = action != GLFW_RELEASE; - else if (Select_Sort_Area_Mode && action == GLFW_PRESS) // num_cluster descrease - { - auto_curation_state.num_clusters = auto_curation_state.num_clusters >= 2 ? auto_curation_state.num_clusters - 1 : 1; - } - else keyPressed = 0; - break; +global_function +void +auto_sort_func(char* currFileName) +{ + if (!currFileName || !auto_sort_state) return; + + fprintf(stdout, "========================\n"); + fprintf(stdout, "[Pixel Sort] start...\n"); + fprintf(stdout, "[Pixel Sort] smallest_frag_size_in_pixel: %d\n", auto_curation_state.smallest_frag_size_in_pixel); + fprintf(stdout, "[Pixel Sort] link_score_threshold: %.3f\n", auto_curation_state.link_score_threshold); + fprintf(stdout, "[Pixel Sort] Sort mode: %s\n", auto_curation_state.get_sort_mode_name().c_str()); + fmt::print(stdout, "[Pixel Sort] num_clusters: {}\n", auto_curation_state.num_clusters); - case GLFW_KEY_LEFT: - if (MetaData_Edit_Mode) - { - u32 nextActive = MetaData_Active_Tag; - ForLoop(ArrayCount(Meta_Data->tags)) - { - if (--nextActive > (ArrayCount(Meta_Data->tags) - 1)) nextActive = ArrayCount(Meta_Data->tags) - 1; - if (strlen((const char *)Meta_Data->tags[nextActive])) - { - MetaData_Active_Tag = nextActive; - break; - } - } + // compress the HiC data + // prepare before reading textures + f32 original_color_control_points[3]; + prepare_before_copy(original_color_control_points); + textures_array_ptr->copy_buffer_to_textures_dynamic( + Contact_Matrix, + false // show_flag + ); + restore_settings_after_copy(original_color_control_points); - } - else if (Select_Sort_Area_Mode) - { - auto_curation_state.change_sort_mode(-1); - } - else AdjustColorMap(-1); - break; + // check if select the area for sorting + SelectArea selected_area; + auto_curation_state.get_selected_fragments( // consider wheather include the sink and source while calculating the selected area + selected_area, + Map_State, + Number_of_Pixels_1D, + Contigs + ); + + std::unordered_map> hap_cluster = collect_hap_cluster(auto_curation_state, selected_area); + + bool hap_cluster_flag = (hap_cluster.size() > 1 && auto_curation_state.hap_cluster_flag); + // NOTE: if hap_cluster is selected, then the kmeans cluster is not needed + if (hap_cluster_flag) auto_curation_state.num_clusters = 1; + else auto_curation_state.hap_cluster_flag = false; + bool kmeans_cluster_flag = selected_area.selected_frag_ids.size() > std::max(auto_curation_state.min_frag_num_for_cluster, (s32)auto_curation_state.num_clusters) + && auto_curation_state.num_clusters > 1 + && !hap_cluster_flag; + + // correct the selected_area, wheather use or not use the source and sink + if ( hap_cluster_flag || kmeans_cluster_flag ) + { + selected_area.source_frag_id = -1; + selected_area.sink_frag_id = -1; + } + + if (auto_curation_state.get_start() >=0 && auto_curation_state.get_end() >= 0 && selected_area.selected_frag_ids.size() > 0) + { + fprintf(stdout, "[Pixel Sort] local sort within selected area\n"); + fprintf(stdout, "[Pixel Sort] Selected pixel range: %d ~ %d\n", auto_curation_state.get_start(), auto_curation_state.get_end()); + std::stringstream ss; + ss << selected_area.selected_frag_ids[0]; + for (u32 i = 1; i < selected_area.selected_frag_ids.size(); ++i) + ss << ", " << selected_area.selected_frag_ids[i]; + fmt::print(stdout, "[Pixel Sort] Selected fragments ({}): {}\n\n", selected_area.selected_frag_ids.size(), ss.str().c_str()); + selected_area.select_flag = true; + selected_area.start_pixel = auto_curation_state.get_start(); + selected_area.end_pixel = auto_curation_state.get_end(); + } + + textures_array_ptr->cal_compressed_hic( + Contigs, + Extensions, + false, // is_extension_required + false, // is_mass_center_required + &selected_area + ); + std::vector exclude_tags = {"haplotig", "unloc"}; + std::vector exclude_meta_tag_idx = get_exclude_metaData_idx(exclude_tags); + + // cluster and sort + // 1. clustering + if (hap_cluster_flag || kmeans_cluster_flag ) + { + std::vector> clusters; + if (kmeans_cluster_flag) // kmeans cluster + { + fmt::print(stdout, + "[Pixel Sort] kmeans: num of clusters: {}, num of selected fragments: {}\n", auto_curation_state.num_clusters, + selected_area.selected_frag_ids.size()); + #ifdef PYTHON_SCOPED_INTERPRETER + if (kmeans_cluster && kmeans_cluster->is_init) + { + fmt::print(stdout, "[Pixel Sort] cluster with Python module.\n"); + clusters = kmeans_cluster->kmeans_func( + auto_curation_state.num_clusters, + textures_array_ptr->get_compressed_hic()); + } else + { + fmt::print(stdout, "[Pixel Sort::warning] Python clustering module initializing failed. Clustering with Eigen-based algorithm.\n"); + clusters = spectral_clustering(*(textures_array_ptr->get_compressed_hic()), auto_curation_state.num_clusters); + } + #else + fmt::print(stdout, "[Pixel Sort::warning] Python clustering module initializing failed. Clustering with Eigen-based algorithm.\n"); + clusters = spectral_clustering(*(textures_array_ptr->get_compressed_hic()), auto_curation_state.num_clusters); + #endif // PYTHON_SCOPED_INTERPRETER + } else // hap cluster + { + fmt::print(stdout, + "[Pixel Sort] haplotig cluster: num of haps: {}, num of selected fragments: {}\n", + hap_cluster.size(), selected_area.selected_frag_ids.size()); + clusters.clear(); + for (const auto& i : hap_cluster) + { + clusters.push_back(i.second); + } + } + + // create the frag4compress object for sorting within the clusters + std::vector clusters_frags(clusters.size()); + for (u32 i = 0; i < clusters.size(); ++i) + { + std::sort(clusters[i].begin(), clusters[i].end()); + clusters_frags[i].re_allocate_mem( + Contigs, + clusters[i]); + } + + #ifdef DEBUG + fmt::print(stdout, "[Pixel Sort]: Kmeans clusters size: {} \n", clusters.size()); + for (u32 i = 0; i < clusters.size(); ++i) + { + auto& tmp = clusters[i]; + fmt::print(stdout, "\t[{} ({})]: ", i, clusters[i].size()); + if (tmp.size() > 0) + { + for (auto& tmp1 : tmp) fmt::print(stdout, "{}, ", tmp1); + } + fmt::print(stdout, "\n"); + } + fmt::print(stdout, "\n"); + #endif // DEBUG + + // 3. sort within the clusters + std::vector frags_order_list; + for (u32 i = 0; i < clusters.size(); ++i) + { + frags_order_list.push_back(FragsOrder(clusters[i].size())); + } + for (u32 i = 0; i < clusters.size(); ++i) + { + // every cluster forms a likelihood table + if (clusters[i].size() < 2) + continue; + auto tmp_likelihood_table = LikelihoodTable( + &clusters_frags[i], + textures_array_ptr->get_compressed_hic(), + (f32)auto_curation_state.smallest_frag_size_in_pixel / ((f32)Number_of_Pixels_1D + 1.f), + exclude_meta_tag_idx, + Number_of_Pixels_1D, + &clusters[i] + ); + frag_sort_method->sort_method_mask( + tmp_likelihood_table, + frags_order_list[i], + selected_area, + auto_curation_state, + &clusters_frags[i], + true + ); + } + // merge all clusters + FragsOrder merged_frags_order(frags_order_list, clusters); + + #ifdef DEBUG + merged_frags_order.print_order() ; + #endif // DEBUG + + AutoCurationFromFragsOrder( + &merged_frags_order, + Contigs, + Map_State, + &selected_area); + + // evaluate the compressed hic between all of the clusters + + // 4. sort the clusters + } + else // sort without clustering + { + if (auto_curation_state.num_clusters == 1) + { + fmt::print(stdout, "[Pixel Sort] sort without clustering.\n"); + } else // (selected_area.selected_frag_ids.size() <= std::max(auto_curation_state.min_frag_num_for_cluster, (s32)auto_curation_state.num_clusters) ) + { + fmt::print( + stdout, + "[Pixel Sort] num_cluster({})>1 but selected fragments({}) <= max(min_num_frags_for_cluster {}, num_cluster {})\n", + auto_curation_state.num_clusters, + selected_area.selected_frag_ids.size(), + auto_curation_state.min_frag_num_for_cluster, + auto_curation_state.num_clusters); + } + + FragsOrder frags_order(textures_array_ptr->get_num_frags()); // intilize the frags_order with the number of fragments including the filtered out ones + // exclude the fragments with first two tags during the auto curation + LikelihoodTable likelihood_table( + textures_array_ptr->get_frags(), + textures_array_ptr->get_compressed_hic(), + (f32)auto_curation_state.smallest_frag_size_in_pixel / ((f32)Number_of_Pixels_1D + 1.f), + exclude_meta_tag_idx, + Number_of_Pixels_1D); + + #ifdef DEBUG_OUTPUT_LIKELIHOOD_TABLE + likelihood_table.output_fragsInfo_likelihoodTable("frags_info_likelihood_table.txt", textures_array_ptr->get_compressed_hic()); + #endif // DEBUG_OUTPUT_LIKELIHOOD_TABLE + + // use the compressed_hic to calculate the frags_order directly + frag_sort_method->sort_method_mask( + likelihood_table, + frags_order, + selected_area, + auto_curation_state, + textures_array_ptr->get_frags(), + true // sort chromosomes according length + ); + + AutoCurationFromFragsOrder( + &frags_order, + Contigs, + Map_State, + &selected_area + ); + } + + std::cout << std::endl; + auto_sort_state = 0; + + // clear mem for textures and compressed hic + textures_array_ptr->clear_textures(); + textures_array_ptr->clear_compressed_hic(); + return; +} + + +// set the vertex array for the contact matrix +// copy_flag: if true, set vaos to [-1, 1, -1, 1] for copying tiles from opengl buffer to cpu memory +global_function +void setContactMatrixVertexArray( + contact_matrix* Contact_Matrix_, + bool copy_flag, + bool regenerate_flag) +{ + glUseProgram(Contact_Matrix_->shaderProgram); + GLuint posAttrib = (GLuint)glGetAttribLocation(Contact_Matrix_->shaderProgram, "position"); + GLuint texAttrib = (GLuint)glGetAttribLocation(Contact_Matrix_->shaderProgram, "texcoord"); + + /* + 从左上到右下开始遍历,见上述编号 + */ + f32 x = -0.5f; + f32 y = 0.5f; + f32 quadSize = 1.0f / (f32)Number_of_Textures_1D; // 1.0 / 32.f = 0.03125 + f32 allCornerCoords[2][2] = {{0.0f, 1.0f}, {1.0f, 0.0f}}; + + if (regenerate_flag) // first delete the generated vertex array objects and buffers + { + glDeleteVertexArrays((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vaos); + glDeleteBuffers((GLsizei)(Number_of_Textures_1D * Number_of_Textures_1D), Contact_Matrix->vbos); + } + + u32 ptr = 0; + ForLoop(Number_of_Textures_1D) + { + ForLoop2(Number_of_Textures_1D) + { + tex_vertex textureVertices[4]; + + glGenVertexArrays( // 生成对象的名称, + 1, // 生成的个数 + Contact_Matrix_->vaos + ptr); // 存储位置的指针 + glBindVertexArray(Contact_Matrix_->vaos[ptr]); // 绑定顶点名称 + + f32 *cornerCoords = allCornerCoords[index2 >= index ? 0 : 1]; // 包含对角线的上三角的时候取第一行{0, 1},否则取第二行{1, 0} 为了解决关于对角线对称 + + u32 min = my_Min(index, index2); + u32 max = my_Max(index, index2); + /* + 对称性编号 + [[ 0 1 2 3 4 5 6 7 8 9] + [ 1 10 11 12 13 14 15 16 17 18] + [ 2 11 19 20 21 22 23 24 25 26] + [ 3 12 20 27 28 29 30 31 32 33] + [ 4 13 21 28 34 35 36 37 38 39] + [ 5 14 22 29 35 40 41 42 43 44] + [ 6 15 23 30 36 41 45 46 47 48] + [ 7 16 24 31 37 42 46 49 50 51] + [ 8 17 25 32 38 43 47 50 52 53] + [ 9 18 26 33 39 44 48 51 53 54]] + */ + f32 texture_index_symmetric = (f32)((min * (Number_of_Textures_1D - 1)) + - (((min-1) * min) >> 1 ) + + max) ; + + /* + 3 2 + 0 1 + */ + f32 half_len = 1.0f; + textureVertices[0].x = !copy_flag? x : -half_len; // x -> [-0.5, 0.5] + textureVertices[0].y = !copy_flag? y - quadSize : -half_len; // y -> [-0.5, 0.5] + textureVertices[0].s = !copy_flag? cornerCoords[0] : 0.f; // s表示texture的水平分量 + textureVertices[0].t = !copy_flag? cornerCoords[1] : 0.f; // t表示texture的垂直分量 + textureVertices[0].u = texture_index_symmetric; - case GLFW_KEY_RIGHT_SHIFT: // num_cluster increase - if (Select_Sort_Area_Mode) - { - auto_curation_state.num_clusters = my_Min(auto_curation_state.num_clusters + 1, 200) ; - } - else keyPressed = 0; - break; + textureVertices[1].x = !copy_flag? x + quadSize : half_len; + textureVertices[1].y = !copy_flag? y - quadSize : -half_len; + textureVertices[1].s = !copy_flag? 1.0f : 1.f; + textureVertices[1].t = !copy_flag? 1.0f : 0.f; + textureVertices[1].u = texture_index_symmetric; - case GLFW_KEY_RIGHT: - if (MetaData_Edit_Mode) - { - u32 nextActive = MetaData_Active_Tag; - ForLoop(ArrayCount(Meta_Data->tags)) - { - if (++nextActive == ArrayCount(Meta_Data->tags)) nextActive = 0; - if (strlen((const char *)Meta_Data->tags[nextActive])) - { - MetaData_Active_Tag = nextActive; - break; - } - } - } - else if (Select_Sort_Area_Mode) - { - auto_curation_state.change_sort_mode(1); - } - else AdjustColorMap(1); - break; + textureVertices[2].x = !copy_flag? x + quadSize : half_len; + textureVertices[2].y = !copy_flag? y : half_len; + textureVertices[2].s = !copy_flag? cornerCoords[1] : 1.f; + textureVertices[2].t = !copy_flag? cornerCoords[0] : 1.f; + textureVertices[2].u = texture_index_symmetric; - case GLFW_KEY_UP: - if (Select_Sort_Area_Mode) - { - auto_curation_state.adjust_cut_threshold(1); - } - else NextColorMap(1); - break; + textureVertices[3].x = !copy_flag? x : -half_len; + textureVertices[3].y = !copy_flag? y : half_len; + textureVertices[3].s = !copy_flag? 0.0f : 0.f; + textureVertices[3].t = !copy_flag? 0.0f : 1.f; + textureVertices[3].u = texture_index_symmetric; - case GLFW_KEY_DOWN: - if (Select_Sort_Area_Mode) - { - auto_curation_state.adjust_cut_threshold(-1); - } - else - NextColorMap(-1); - break; -#ifdef Internal - case GLFW_KEY_ESCAPE: - if (!mods) - { - glfwSetWindowShouldClose(window, GLFW_TRUE); - } - break; -#endif - case GLFW_KEY_ENTER: - if (mods == GLFW_MOD_ALT) - { - if (glfwGetWindowMonitor(window)) - { - glfwSetWindowMonitor(window, NULL, - Windowed_Xpos, Windowed_Ypos, - Windowed_Width, Windowed_Height, 0); - } - else - { - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); - if (monitor) - { - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - glfwGetWindowPos(window, &Windowed_Xpos, &Windowed_Ypos); - glfwGetWindowSize(window, &Windowed_Width, &Windowed_Height); - glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); - } - } - } - else - { - keyPressed = 0; - } - break; + glGenBuffers( // 生成buffer的名字 存储在contact_matrix->vbos+ptr 对应的地址上 + 1, // 个数 + Contact_Matrix_->vbos + ptr); // 指针,存储生成的名字 + glBindBuffer( // 绑定到一个已经命名的buffer对象上 + GL_ARRAY_BUFFER, // 绑定的对象,gl_array_buffer 一般是绑定顶点的信息 + Contact_Matrix_->vbos[ptr]); // 通过输入buffer的名字将其绑定到gl_array_buffer + glBufferData( // 创建、初始化存储数据的buffer + GL_ARRAY_BUFFER, // 绑定的对象,gl_array_buffer 一般是绑定顶点的信息 + 4 * sizeof(tex_vertex), // 对象的大小(bytes) + textureVertices, // 即将绑定到gl_array_buffer的数据的指针 + GL_STATIC_DRAW); // 绑定后的数据是用于干什么的 + + // tell GL how to interpret the data in the GL_ARRAY_BUFFER + glEnableVertexAttribArray(posAttrib); + glVertexAttribPointer( // 定义一个数组存储所有的通用顶点属性 + posAttrib, // 即将修改属性的索引 + 2, // 定义每个属性的参数的个数, 只能为1 2 3 4中的一个,初始值为4 + GL_FLOAT, // 定义该属性的数据类型,则数据为4个字节, 占用两个的话,则一共为8字节 + GL_FALSE, // normalized, 是否要在取用的时候规范化这些数 + sizeof(tex_vertex), // stride 定义从一个信息的指针下一个信息指针的bytes的间隔, 一个信息包到下一个信息包的长度 + 0); // const void* 指定当前与 GL_ARRAY_BUFFER 绑定的缓冲区数据头指针偏移多少个到 第一个通用顶点属性的第一个特征开始的位置。 初始值为 0。因为此处想要读取的信息为 x 和 y + glEnableVertexAttribArray(texAttrib); // + glVertexAttribPointer( + texAttrib, + 3, + GL_FLOAT, // 定义每个属性的数据类型 + GL_FALSE, // GL_FALSE, GL_TRUE + sizeof(tex_vertex), + (void *)(3 * sizeof(GLfloat))); // 偏移量为3,因为前三个为x, y and pad, 想要读取的数据为 s, t and u - default: - keyPressed = 0; - } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Quad_EBO); - if (keyPressed) - { - Redisplay = 1; - } + x += quadSize; // next column + ++ptr; // ptr for current texture } + + y -= quadSize; // next row + x = -0.5f; // from column 0 } } + global_function -void -ErrorCallback(s32 error, const char *desc) +void +prepare_before_copy(f32 *original_color_control_points) { - (void)error; - fprintf(stderr, "Error: %s\n", desc); + // prepare before reading textures + glActiveTexture(GL_TEXTURE1); + + glBindTexture(GL_TEXTURE_BUFFER, Color_Maps->maps[Color_Maps->nMaps-1]); // 绑定到颜色映射的最后一个颜色 + glActiveTexture(GL_TEXTURE0); + for (u32 i = 0; i < 3 ; i ++ ) + original_color_control_points[i] = Color_Maps->controlPoints[i]; + Color_Maps->controlPoints[0] = 0.0f; + Color_Maps->controlPoints[1] = 0.5f; + Color_Maps->controlPoints[2] = 1.0f; + glUseProgram(Contact_Matrix->shaderProgram); + glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); + setContactMatrixVertexArray(Contact_Matrix, true, true); // set the vertex for copying + return ; } -// File Browser -// from nuklear file browser example -/* =============================================================== - * - * GUI - * - * ===============================================================*/ -struct -icons + +global_function +void +restore_settings_after_copy(const f32 *original_color_control_points) { - struct nk_image home; - struct nk_image computer; - struct nk_image directory; + // restore settings + setContactMatrixVertexArray(Contact_Matrix, false, true); // restore the vertex array for rendering + for (u32 i = 0 ; i < 3 ; i ++ ) + Color_Maps->controlPoints[i] = original_color_control_points[i]; + glUseProgram(Contact_Matrix->shaderProgram); + glUniform3fv( Color_Maps->cpLocation, 1, Color_Maps->controlPoints); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_BUFFER, Color_Maps->maps[Color_Maps->currMap]); + glActiveTexture(GL_TEXTURE0); +} - struct nk_image default_file; - struct nk_image img_file; -}; -enum -file_groups -{ - FILE_GROUP_DEFAULT, - FILE_GROUP_PRETEXT, - FILE_GROUP_MAX -}; +static f32 camera_pos_before_jump[] = {-1000.f, -1000.f}; -enum -file_types -{ - FILE_DEFAULT, - FILE_PRETEXT, - FILE_PSTM, - FILE_MAX -}; -struct -file_group +global_function void +JumpToDiagonal(GLFWwindow *window) +{ + // save the current camera position before jumping + camera_pos_before_jump[0] = Camera_Position.x; + camera_pos_before_jump[1] = Camera_Position.y; + // jump to the diagonal position + Camera_Position.x = -Camera_Position.y; + ClampCamera(); + Redisplay = 1; +} + +global_function void +jump_back_pos_before_jump(GLFWwindow *window) { - enum file_groups group; - u32 pad; - const char *name; - struct nk_image *icon; -}; + if (camera_pos_before_jump[0] > -999.f && camera_pos_before_jump[1] > -999.f) { + Camera_Position.x = camera_pos_before_jump[0]; + Camera_Position.y = camera_pos_before_jump[1]; + ClampCamera(); + Redisplay = 1; + } + else { + fmt::print(stderr, "[Jump::warning] No saved position to jump back.\n"); + } +} -struct -file + +global_function +u32 +ToggleEditMode(GLFWwindow* window) { - enum file_types type; - enum file_groups group; - const char *suffix; -}; + u32 result = 1; + + if (Edit_Mode && !Edit_Pixels.editing) + { + Edit_Pixels.scaffSelecting = 0; + Global_Mode = mode_normal; + if (Tool_Tip->on) + { + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + } + else if (Normal_Mode || Select_Sort_Area_Mode || MetaData_Edit_Mode) + { + Global_Mode = mode_edit; + } + else + { + result = 0; + } + + return(result); +} -struct -media -{ - int font; - int icon_sheet; - struct icons icons; - struct file_group group[FILE_GROUP_MAX]; - struct file files[FILE_MAX]; -}; -#define MAX_PATH_LEN 512 -struct -file_browser +global_function + u32 + ToggleExtensionMode(GLFWwindow *window) { - /* path */ - char file[MAX_PATH_LEN]; - char home[MAX_PATH_LEN]; - char directory[MAX_PATH_LEN]; + u32 result = 1; - /* directory content */ - char **files; - char **directories; - size_t file_count; - size_t dir_count; - struct media *media; -}; + if (Extension_Mode) + { + Global_Mode = mode_normal; + } + else if (Normal_Mode) + { + Global_Mode = mode_extension; + } + else + { + result = 0; + } -#if defined __unix__ || defined __APPLE__ -#include -#include -#endif + return result; +} -#ifndef _WIN32 -#include -#endif global_function -char* -StrDuplicate(const char *src) +u32 +ToggleWaypointMode(GLFWwindow* window) { - char *ret; - size_t len = strlen(src); - if (!len) return 0; - ret = (char*)malloc(len+1); - if (!ret) return 0; - memcpy(ret, src, len); - ret[len] = '\0'; - return ret; + u32 result = 1; + + if (Waypoint_Edit_Mode) + { + Global_Mode = mode_normal; + if (Tool_Tip->on) + { + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + } + else if (Normal_Mode) + { + Global_Mode = mode_waypoint_edit; + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + else + { + result = 0; + } + + return(result); } global_function -void -DirFreeList(char **list, size_t size) +u32 +ToggleScaffMode(GLFWwindow* window) { - size_t i; - for (i = 0; i < size; ++i) - free(list[i]); - free(list); + u32 result = 1; + + if (Scaff_Edit_Mode && !Scaff_Painting_Flag) + { + Global_Mode = mode_normal; + Scaff_Painting_Flag = 0; + Scaff_Painting_Id = 0; + Scaff_FF_Flag = 0; + if (Tool_Tip->on) + { + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + } + else if (Normal_Mode) + { + Global_Mode = mode_scaff_edit; + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + else + { + result = 0; + } + + return(result); } global_function u32 -StringIsLexBigger(char *string, char *toCompareTo) +ToggleMetaDataMode(GLFWwindow* window) { - u32 result; - u32 equal; + u32 result = 1; - do + if (MetaData_Edit_Mode) { - equal = *string == *toCompareTo; - result = *string > *(toCompareTo++); - } while (equal && (*(string++) != '\0')); + Global_Mode = mode_normal; + if (Tool_Tip->on) + { + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + } + else if (Normal_Mode || (Edit_Mode && !Edit_Pixels.editing) || Select_Sort_Area_Mode) + { + Global_Mode = mode_meta_edit; + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + else + { + result = 0; + } return(result); } + global_function -void -CharArrayBubbleSort(char **list, u32 size) +u32 +ToggleSelectSortAreaMode(GLFWwindow* window) { - while (size > 1) + u32 result = 1; + + if (Select_Sort_Area_Mode) { - u32 newSize = 0; - ForLoop(size - 1) - { - if (StringIsLexBigger(list[index], list[index + 1])) - { - char *tmp = list[index]; - list[index] = list[index + 1]; - list[index + 1] = tmp; - newSize = index + 1; - } + Global_Mode = mode_normal; + if (Tool_Tip->on) + { + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); } - size = newSize; } + else if (Normal_Mode || (Edit_Mode && !Edit_Pixels.editing) || MetaData_Edit_Mode) + { + Global_Mode = mode_select_sort_area; + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } + else + { + result = 0; + } + + return(result); + } + + global_function -char** -DirList(const char *dir, u32 return_subdirs, size_t *count) +void +ToggleToolTip(GLFWwindow* window) { - size_t n = 0; - char buffer[MAX_PATH_LEN]; - char **results = NULL; -#ifndef _WIN32 - const DIR *none = NULL; - DIR *z; -#else - WIN32_FIND_DATA ffd; - HANDLE hFind = INVALID_HANDLE_VALUE; - char dirBuff[MAX_PATH_LEN]; + Tool_Tip->on = !Tool_Tip->on; + if (Tool_Tip->on) + { + f64 mousex, mousey; + glfwGetCursorPos(window, &mousex, &mousey); + MouseMove(window, mousex, mousey); + } +} + +#if 0 +enum +MouseButon +{ + left, + right, + middle, +}; + +struct +KeyBindings +{ + s32 ui; + s32 editMode; + s32 waypointMode; + s32 undoEdit; + s32 redoEdit; + s32 selectWholeSeq_key; + MouseButon selectWholeSeq_mouse; + +}; #endif - size_t capacity = 32; - size_t size; - Assert(dir); - Assert(count); - strncpy(buffer, dir, MAX_PATH_LEN); - n = strlen(buffer); +global_variable +s32 +Windowed_Xpos, Windowed_Ypos, Windowed_Width, Windowed_Height; -#ifndef _WIN32 - if (n > 0 && (buffer[n-1] != '/')) - buffer[n++] = '/'; -#else - if (n > 0 && (buffer[n-1] != '\\')) - buffer[n++] = '\\'; +#if 0 +struct +GLFWKeyAndScancode +{ + s32 key; + s32 scancode; +}; + +global_variable +GLFWKeyAndScancode +LastPressedKey = {0, 0}; #endif - size = 0; +global_variable +nk_keys +NK_Pressed_Keys[1024] = {}; -#ifndef _WIN32 - z = opendir(dir); -#else - strncpy(dirBuff, buffer, MAX_PATH_LEN); - dirBuff[n] = '*'; - hFind = FindFirstFile(dirBuff, &ffd); -#endif - -#ifndef _WIN32 - if (z != none) -#else - if (hFind != INVALID_HANDLE_VALUE) -#endif +global_variable +u32 +NK_Pressed_Keys_Ptr = 0; + +global_variable +u32 +GatheringTextInput = 0; + +global_function +void +KeyBoard(GLFWwindow* window, s32 key, s32 scancode, s32 action, s32 mods) +{ + if (!Loading && !auto_sort_state && !auto_cut_state && (action != GLFW_RELEASE || key == GLFW_KEY_SPACE || key == GLFW_KEY_A || key == GLFW_KEY_LEFT_SHIFT)) { -#ifndef _WIN32 - u32 nonempty = 1; - struct dirent *data = readdir(z); - nonempty = (data != NULL); - if (!nonempty) return NULL; -#endif - do + if (UI_On) { -#ifndef _WIN32 - DIR *y; -#endif - char *p; - u32 is_subdir; -#ifndef _WIN32 - if (data->d_name[0] == '.') -#else - if (ffd.cFileName[0] == '.') -#endif - continue; - -#ifndef _WIN32 - strncpy(buffer + n, data->d_name, MAX_PATH_LEN-n); - y = opendir(buffer); - is_subdir = (y != NULL); - if (y != NULL) closedir(y); +#if 0 + LastPressedKey.key = key; + LastPressedKey.scancode = scancode; #else - strncpy(buffer + n, ffd.cFileName, MAX_PATH_LEN-n); - is_subdir = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + (void)scancode; #endif - if ((return_subdirs && is_subdir) || (!is_subdir && !return_subdirs)) + if (key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) { - if (!size) + if (glfwGetWindowMonitor(window)) { - results = (char**)calloc(sizeof(char*), capacity); - } - else if (size >= capacity) + glfwSetWindowMonitor(window, NULL, + Windowed_Xpos, Windowed_Ypos, + Windowed_Width, Windowed_Height, 0); + } + else { - void *old = results; - capacity = capacity * 2; - results = (char**)realloc(results, capacity * sizeof(char*)); - Assert(results); - if (!results) free(old); + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwGetWindowPos(window, &Windowed_Xpos, &Windowed_Ypos); + glfwGetWindowSize(window, &Windowed_Width, &Windowed_Height); + glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); + } } -#ifndef _WIN32 - p = StrDuplicate(data->d_name); -#else - p = StrDuplicate(ffd.cFileName); -#endif - results[size++] = p; } -#ifndef _WIN32 - } while ((data = readdir(z)) != NULL); -#else - } while (FindNextFile(hFind, &ffd) != 0); +#ifdef Internal + else if (key == GLFW_KEY_ESCAPE && !mods) + { + glfwSetWindowShouldClose(window, GLFW_TRUE); + } #endif - } + /*nk_input_key(NK_Context, NK_KEY_DEL, glfwGetKey(window, GLFW_KEY_DELETE) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_ENTER, glfwGetKey(window, GLFW_KEY_ENTER) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_TAB, glfwGetKey(window, GLFW_KEY_TAB) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_BACKSPACE, glfwGetKey(window, GLFW_KEY_BACKSPACE) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_LEFT, glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_RIGHT, glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_UP, glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_DOWN, glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS); -#ifndef _WIN32 - if (z) closedir(z); -#else - FindClose(hFind); -#endif - *count = size; - - CharArrayBubbleSort(results, (u32)size); - - return results; -} + if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) + { + nk_input_key(NK_Context, NK_KEY_COPY, glfwGetKey(window, GLFW_KEY_C) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_PASTE, glfwGetKey(window, GLFW_KEY_P) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_CUT, glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_CUT, glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(NK_Context, NK_KEY_SHIFT, 1); + } + else + { + nk_input_key(NK_Context, NK_KEY_COPY, 0); + nk_input_key(NK_Context, NK_KEY_PASTE, 0); + nk_input_key(NK_Context, NK_KEY_CUT, 0); + nk_input_key(NK_Context, NK_KEY_SHIFT, 0); + }*/ -global_function -struct file_group -FILE_GROUP(enum file_groups group, const char *name, struct nk_image *icon) -{ - struct file_group fg; - fg.group = group; - fg.name = name; - fg.icon = icon; - return fg; -} -global_function -struct file -FILE_DEF(enum file_types type, const char *suffix, enum file_groups group) -{ - struct file fd; - fd.type = type; - fd.suffix = suffix; - fd.group = group; - return fd; -} + else if (key == GLFW_KEY_U) + { + Deferred_Close_UI = 1; + } + else if (action != GLFW_RELEASE && !GatheringTextInput) + { + nk_keys addKey = NK_KEY_NONE; + switch (key) + { + case GLFW_KEY_DELETE: + addKey = NK_KEY_DEL; + break; -global_function -struct nk_image* -MediaIconForFile(struct media *media, const char *file) -{ - u32 i = 0; - const char *s = file; - char suffix[16]; - u32 found = 0; - memset(suffix, 0, sizeof(suffix)); + case GLFW_KEY_ENTER: + addKey = NK_KEY_ENTER; + break; - /* extract suffix .xxx from file */ - while (*s++ != '\0') - { - if (found && i < (sizeof(suffix)-1)) - suffix[i++] = *s; + case GLFW_KEY_TAB: + addKey = NK_KEY_TAB; + break; - if (*s == '.') - { - if (found) - { - found = 0; - break; - } - found = 1; - } - } + case GLFW_KEY_BACKSPACE: + addKey = NK_KEY_BACKSPACE; + break; - /* check for all file definition of all groups for fitting suffix*/ - for ( i = 0; - i < FILE_MAX && found; - ++i) - { - struct file *d = &media->files[i]; - { - const char *f = d->suffix; - s = suffix; - while (f && *f && *s && *s == *f) - { - s++; f++; - } + case GLFW_KEY_LEFT: + addKey = NK_KEY_LEFT; + break; - /* found correct file definition so */ - if (f && *s == '\0' && *f == '\0') - return media->group[d->group].icon; - } - } - return &media->icons.default_file; -} + case GLFW_KEY_RIGHT: + addKey = NK_KEY_RIGHT; + break; -global_function -void -MediaInit(struct media *media) -{ - /* file groups */ - struct icons *icons = &media->icons; - media->group[FILE_GROUP_DEFAULT] = FILE_GROUP(FILE_GROUP_DEFAULT,"default",&icons->default_file); - media->group[FILE_GROUP_PRETEXT] = FILE_GROUP(FILE_GROUP_PRETEXT, "pretext", &icons->img_file); + case GLFW_KEY_UP: + addKey = NK_KEY_UP; + break; + + case GLFW_KEY_DOWN: + addKey = NK_KEY_DOWN; + break; + } + + if (addKey != NK_KEY_NONE) + { + NK_Pressed_Keys[NK_Pressed_Keys_Ptr++] = addKey; + if (NK_Pressed_Keys_Ptr == ArrayCount(NK_Pressed_Keys)) NK_Pressed_Keys_Ptr = 0; + } + } + } + else // not UI_On + { + u32 keyPressed = 1; - /* files */ - media->files[FILE_DEFAULT] = FILE_DEF(FILE_DEFAULT, NULL, FILE_GROUP_DEFAULT); - media->files[FILE_PRETEXT] = FILE_DEF(FILE_PRETEXT, "pretext", FILE_GROUP_PRETEXT); - media->files[FILE_PSTM] = FILE_DEF(FILE_PSTM, "pstm", FILE_GROUP_PRETEXT); -} + switch (key) + { + + case GLFW_KEY_A: + if (Scaff_Edit_Mode) + { + if (action != GLFW_RELEASE) Scaff_FF_Flag |= 1; + else Scaff_FF_Flag &= ~1; + } + else keyPressed = 0; + break; -global_function -void -FileBrowserReloadDirectoryContent(struct file_browser *browser, const char *path) -{ - strncpy(browser->directory, path, MAX_PATH_LEN); - DirFreeList(browser->files, browser->file_count); - DirFreeList(browser->directories, browser->dir_count); - browser->files = DirList(path, 0, &browser->file_count); - browser->directories = DirList(path, 1, &browser->dir_count); -} + case GLFW_KEY_B: + Scale_Bars->on = !Scale_Bars->on; + break; -global_function -void -FileBrowserInit(struct file_browser *browser, struct media *media) -{ - memset(browser, 0, sizeof(*browser)); - browser->media = media; - { - /* load files and sub-directory list */ - const char *home = getenv("HOME"); -#ifdef _WIN32 - if (!home) home = getenv("USERPROFILE"); -#else - if (!home) home = getpwuid(getuid())->pw_dir; -#endif - { - size_t l; - strncpy(browser->home, home, MAX_PATH_LEN); - l = strlen(browser->home); -#ifdef _WIN32 - char *sep = (char *)"\\"; -#else - char *sep = (char *)"/"; -#endif - strcpy(browser->home + l, sep); - strcpy(browser->directory, browser->home); - } - - browser->files = DirList(browser->directory, 0, &browser->file_count); - browser->directories = DirList(browser->directory, 1, &browser->dir_count); - } -} + case GLFW_KEY_C: + if (Select_Sort_Area_Mode && auto_curation_state.get_selected_fragments_num(Map_State, Number_of_Pixels_1D) > 0) // local area break + { + auto_cut_state = 1; + } + else // open "coverage" extension + { + if (!Extensions.head) break; + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) + { + case extension_graph: + { -global_variable -char -Save_State_Name_Buffer[1024] = {0}; + graph *gph = (graph *)node->extension; + if (strcmp((char *)gph->name, "coverage") == 0) + { + gph->on = !gph->on; + break; + } + } + } + } + } + + break; -global_variable -char -AGP_Name_Buffer[1024] = {0}; + case GLFW_KEY_D: + if (Scaff_Edit_Mode && (mods & GLFW_MOD_SHIFT)) + { + ForLoop(Contigs->numberOfContigs) (Contigs->contigs_arr + index)->scaffId = 0; + UpdateScaffolds(); + } + else if (MetaData_Edit_Mode && (mods & GLFW_MOD_SHIFT)) memset(Map_State->metaDataFlags, 0, Number_of_Pixels_1D * sizeof(u64)); + else keyPressed = 0; + break; -global_function -void -SetSaveStateNameBuffer(char *name) -{ - u32 ptr = 0; - while (*name) - { - AGP_Name_Buffer[ptr] = *name; - Save_State_Name_Buffer[ptr++] = *name++; - } - - u32 ptr1 = ptr; - name = (char *)".savestate_1"; - while (*name) Save_State_Name_Buffer[ptr++] = *name++; - Save_State_Name_Buffer[ptr] = 0; + case GLFW_KEY_E: + keyPressed = ToggleEditMode(window); + break; - ptr = ptr1; - name = (char *)".agp_1"; - while (*name) AGP_Name_Buffer[ptr++] = *name++; - AGP_Name_Buffer[ptr] = 0; -} + case GLFW_KEY_F: + keyPressed = ToggleSelectSortAreaMode(window); + break; -global_function -u08 -FileBrowserRun(const char *name, struct file_browser *browser, struct nk_context *ctx, u32 show, u08 save = 0) -{ -#ifndef _WIN32 - char pathSep = '/'; -#else - char pathSep = '\\'; -#endif - - struct nk_window *window = nk_window_find(ctx, name); - u32 doesExist = window != 0; + case GLFW_KEY_G: + if (!Extensions.head) break; + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) + { + case extension_graph: + { - if (!show && !doesExist) - { - return(0); - } + graph *gph = (graph *)node->extension; + if (strcmp((char *)gph->name, "gap") == 0) + { + gph->on = !gph->on; + break; + } + } + } + } + break; - if (show && doesExist && (window->flags & NK_WINDOW_HIDDEN)) - { - window->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; - FileBrowserReloadDirectoryContent(browser, browser->directory); - } + case GLFW_KEY_H: + if (Select_Sort_Area_Mode) + { + auto_curation_state.hap_cluster_flag = !auto_curation_state.hap_cluster_flag; + } + else keyPressed = 0; + break; - u08 ret = 0; - struct media *media = browser->media; - struct nk_rect total_space; + case GLFW_KEY_I: + Contig_Ids->on = !Contig_Ids->on; + break; - if (nk_begin(ctx, name, nk_rect(Screen_Scale.x * 50, Screen_Scale.y * 50, Screen_Scale.x * 800, Screen_Scale.y * 700), - NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE|NK_WINDOW_CLOSABLE)) - { - static f32 ratio[] = {0.25f, NK_UNDEFINED}; - f32 spacing_x = ctx->style.window.spacing.x; - nk_style_set_font(ctx, &NK_Font_Browser->handle); + case GLFW_KEY_J: + JumpToDiagonal(window); + break; - /* output path directory selector in the menubar */ - ctx->style.window.spacing.x = 0; - nk_menubar_begin(ctx); - { - char *d = browser->directory; - char *begin = d + 1; - nk_layout_row_dynamic(ctx, Screen_Scale.y * 25.0f, 6); - while (*d++) - { - if (*d == pathSep) - { - *d = '\0'; - if (nk_button_label(ctx, begin)) + case GLFW_KEY_K: + jump_back_pos_before_jump(window); + break; + + case GLFW_KEY_L: + if (Waypoint_Edit_Mode) { - *d++ = pathSep; *d = '\0'; - FileBrowserReloadDirectoryContent(browser, browser->directory); - break; + Long_Waypoints_Mode = (Long_Waypoints_Mode +1) % 3; } - *d = pathSep; - begin = d + 1; - } - } - } - nk_menubar_end(ctx); - ctx->style.window.spacing.x = spacing_x; + else + { + Grid->on = !Grid->on; + } + break; - /* window layout */ - f32 endSpace = save ? 100.0f : 0; // Resolved: end space is clipped as the space is not enough - total_space = nk_window_get_content_region(ctx); - nk_layout_row(ctx, NK_DYNAMIC, total_space.h - endSpace, 2, ratio); - nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR); - { - struct nk_image home = media->icons.home; - struct nk_image computer = media->icons.computer; + case GLFW_KEY_M: + keyPressed = ToggleMetaDataMode(window); + break; - nk_layout_row_dynamic(ctx, Screen_Scale.y * 40.0f, 1); - if (nk_button_image_label(ctx, home, "home", NK_TEXT_CENTERED)) - FileBrowserReloadDirectoryContent(browser, browser->home); - if (nk_button_image_label(ctx,computer,"computer",NK_TEXT_CENTERED)) -#ifndef _WIN32 - FileBrowserReloadDirectoryContent(browser, "/"); -#else - FileBrowserReloadDirectoryContent(browser, "C:\\"); -#endif - nk_group_end(ctx); - } + case GLFW_KEY_N: + Contig_Name_Labels->on = !Contig_Name_Labels->on; + break; + + case GLFW_KEY_O: + if (Select_Sort_Area_Mode) + auto_curation_state.auto_cut_with_extension = !auto_curation_state.auto_cut_with_extension; + else keyPressed = 0; + break; + + case GLFW_KEY_P: + break; - /* output directory content window */ - nk_group_begin(ctx, "Content", 0); - { - s32 index = -1; - size_t i = 0, j = 0;//, k = 0; - size_t rows = 0, cols = 0; - size_t count = browser->dir_count + browser->file_count; - f32 iconRatio[] = {0.05f, NK_UNDEFINED}; + case GLFW_KEY_Q: + if (Edit_Mode || Select_Sort_Area_Mode) + { + UndoMapEdit(); + } + else + { + keyPressed = 0; + } + break; - cols = 1; - rows = count / cols; - for ( i = 0; - i <= rows; - i += 1) - { - { - size_t n = j + cols; - nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 25.0f, 2, iconRatio); - for ( ; - j < count && j < n; - ++j) + case GLFW_KEY_R: + if (mods == GLFW_MOD_CONTROL) { - /* draw one row of icons */ - if (j < browser->dir_count) - { - /* draw and execute directory buttons */ - if (nk_button_image(ctx,media->icons.directory)) - index = (s32)j; - nk_label(ctx, browser->directories[j], NK_TEXT_LEFT); - } - else + Loading = 1; + } + else if (Extension_Mode && Extensions.head) + { + TraverseLinkedList(Extensions.head, extension_node) { - /* draw and execute files buttons */ - struct nk_image *icon; - size_t fileIndex = ((size_t)j - browser->dir_count); - icon = MediaIconForFile(media,browser->files[fileIndex]); - if (nk_button_image(ctx, *icon)) + switch (node->type) + { + case extension_graph: { - if (save) + graph *gph = (graph *)node->extension; + if (strcmp((char *)gph->name, "repeat_density") == 0) { - strncpy(save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer, browser->files[fileIndex], sizeof(save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer)); + gph->on = !gph->on; + break; } - else + } + } + } + break; + } + else + { + keyPressed = 0; + } + break; + + case GLFW_KEY_S: + if (Edit_Mode) + { + Edit_Pixels.snap = !Edit_Pixels.snap; + } + else if (mods & GLFW_MOD_SHIFT) + { + Scaffs_Always_Visible = Scaffs_Always_Visible ? 0 : 1; + } + else if (Select_Sort_Area_Mode) + { + auto_curation_state.clear(); // clear the selected area + } + else + { + keyPressed = ToggleScaffMode(window); + } + break; + + case GLFW_KEY_T: + if (Extension_Mode && Extensions.head) + { + TraverseLinkedList(Extensions.head, extension_node) + { + switch (node->type) + { + case extension_graph: + { + + graph *gph = (graph *)node->extension; + if (strcmp((char *)gph->name, "telomere") == 0) { - strncpy(browser->file, browser->directory, MAX_PATH_LEN); - n = strlen(browser->file); - strncpy(browser->file + n, browser->files[fileIndex], MAX_PATH_LEN - n); - ret = 1; + gph->on = !gph->on; + break; } } - nk_label(ctx,browser->files[fileIndex],NK_TEXT_LEFT); + } } + break; } - } - } - - if (index != -1) - { - size_t n = strlen(browser->directory); - strncpy(browser->directory + n, browser->directories[index], MAX_PATH_LEN - n); - n = strlen(browser->directory); - if (n < MAX_PATH_LEN - 1) - { - browser->directory[n] = pathSep; - browser->directory[n+1] = '\0'; - } - FileBrowserReloadDirectoryContent(browser, browser->directory); - } - - nk_group_end(ctx); - } + else + { + ToggleToolTip(window); + } + break; - if (save) - { - Deferred_Close_UI = 0; + case GLFW_KEY_U: + UI_On = !UI_On; + ++NK_Device->lastContextMemory[0]; + Mouse_Move.x = Mouse_Move.y = -1; + break; - nk_layout_row(ctx, NK_DYNAMIC, endSpace - 5.0f, 1, ratio + 1); - nk_group_begin(ctx, "File", NK_WINDOW_NO_SCROLLBAR); - { - f32 fileRatio[] = {0.8f, 0.1f, NK_UNDEFINED}; - f32 fileRatio2[] = {0.45f, 0.1f, 0.18f, 0.17f, NK_UNDEFINED}; - nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 35.0f, save == 2 ? 5 : 3, save == 2 ? fileRatio2 : fileRatio); + case GLFW_KEY_V: + break; - u08 saveViaEnter = (nk_edit_string_zero_terminated(ctx, NK_EDIT_FIELD | NK_EDIT_SIG_ENTER, save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer, sizeof(save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer), 0) & NK_EDIT_COMMITED) ? 1 : 0; - - static u08 overwrite = 0; - overwrite = (u08)nk_check_label(ctx, "Override", overwrite); - - static u08 singletons = 0; - if (save == 2) singletons = (u08)nk_check_label(ctx, "Format Singletons", singletons); - static u08 preserveOrder = 0; - if (save == 2) preserveOrder = (u08)nk_check_label(ctx, "Preserve Order", preserveOrder); + case GLFW_KEY_W: + if (Edit_Mode || Select_Sort_Area_Mode) + { + RedoMapEdit(); + } + else + { + keyPressed = ToggleWaypointMode(window); + } + break; - if (nk_button_label(ctx, "Save") || saveViaEnter) - { - strncpy(browser->file, browser->directory, MAX_PATH_LEN); - size_t n = strlen(browser->file); - strncpy(browser->file + n, save == 2 ? AGP_Name_Buffer : Save_State_Name_Buffer, MAX_PATH_LEN - n); - ret = 1 | (overwrite ? 2 : 0) | (singletons ? 4 : 0) | (preserveOrder ? 8 : 0); - } + case GLFW_KEY_X: + keyPressed = ToggleExtensionMode(window); + break; - nk_group_end(ctx); - } - } - - nk_style_set_font(ctx, &NK_Font->handle); - } - nk_end(ctx); - - return(ret); -} + case GLFW_KEY_Y: + break; -global_function -void -MetaTagsEditorRun(struct nk_context *ctx, u08 show) -{ - const char *name = "Meta Data Tag Editor"; - struct nk_window *window = nk_window_find(ctx, name); + case GLFW_KEY_Z: + if (Select_Sort_Area_Mode) + { + auto_cut_state = 2; // restore the cutted frags within the selected area + } + break; - u08 doesExist = window != 0; - if (!show && !doesExist) return; - if (show && doesExist && (window->flags & NK_WINDOW_HIDDEN)) window->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; + case GLFW_KEY_SPACE: + if (Edit_Mode && !Edit_Pixels.editing && action == GLFW_PRESS) + { + Edit_Pixels.selecting = 1; + Edit_Pixels.selectPixels = Edit_Pixels.pixels; + f64 x, y; + glfwGetCursorPos(window, &x, &y); + MouseMove(window, x, y); + } + else if (Edit_Mode && !Edit_Pixels.editing && action == GLFW_RELEASE) + { + Edit_Pixels.editing = 1; + Edit_Pixels.selecting = 0; + f64 x, y; + glfwGetCursorPos(window, &x, &y); + MouseMove(window, x, y); + } + else if (Edit_Mode && Edit_Pixels.editing && action == GLFW_PRESS) + { + InvertMap(Edit_Pixels.pixels.x, Edit_Pixels.pixels.y); + Global_Edit_Invert_Flag = !Global_Edit_Invert_Flag; + } + else if (Waypoint_Edit_Mode && Selected_Waypoint && action == GLFW_PRESS) + { + f64 x, y; + glfwGetCursorPos(window, &x, &y); + RemoveWayPoint(Selected_Waypoint); + MouseMove(window, x, y); + } + else if (Scaff_Edit_Mode && (action == GLFW_PRESS || action == GLFW_RELEASE)) + { + f64 x, y; + glfwGetCursorPos(window, &x, &y); + Scaff_Painting_Flag = action == GLFW_PRESS ? 2 : 0; + MouseMove(window, x, y); + if (action == GLFW_RELEASE) UpdateScaffolds(); + } + else if (MetaData_Edit_Mode && (action == GLFW_PRESS || action == GLFW_RELEASE)) + { + f64 x, y; + glfwGetCursorPos(window, &x, &y); + MetaData_Edit_State = action == GLFW_PRESS ? 2 : 0; + MouseMove(window, x, y); + } + else if (Select_Sort_Area_Mode && action == GLFW_PRESS) + { + u32 selected_frag_num = auto_curation_state.get_selected_fragments_num(Map_State, Number_of_Pixels_1D); + if (selected_frag_num >= 2) + { + auto_sort_state = 1; + } + } + else + { + keyPressed = 0; + } + break; + + case GLFW_KEY_LEFT_SHIFT: + if (Scaff_Edit_Mode) + { + if (action != GLFW_RELEASE) Scaff_FF_Flag |= 2; + else Scaff_FF_Flag &= ~2; + } + else if (Edit_Mode) Edit_Pixels.scaffSelecting = action != GLFW_RELEASE; + else if (Select_Sort_Area_Mode && action == GLFW_PRESS) // num_cluster descrease + { + auto_curation_state.num_clusters = auto_curation_state.num_clusters >= 2 ? auto_curation_state.num_clusters - 1 : 1; + } + else keyPressed = 0; + break; - if (nk_begin(ctx, name, nk_rect(Screen_Scale.x * 50, Screen_Scale.y * 50, Screen_Scale.x * 800, Screen_Scale.y * 600), - NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE|NK_WINDOW_NO_SCROLLBAR)) - { - Deferred_Close_UI = 0; + case GLFW_KEY_LEFT: + if (MetaData_Edit_Mode) + { + u32 nextActive = MetaData_Active_Tag; + ForLoop(ArrayCount(Meta_Data->tags)) + { + if (--nextActive > (ArrayCount(Meta_Data->tags) - 1)) nextActive = ArrayCount(Meta_Data->tags) - 1; + if (strlen((const char *)Meta_Data->tags[nextActive])) + { + MetaData_Active_Tag = nextActive; + break; + } + } - struct nk_rect total_space = nk_window_get_content_region(ctx); - f32 ratio[] = {0.05f, NK_UNDEFINED}; - nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 1, ratio + 1); + } + else if (Select_Sort_Area_Mode) + { + auto_curation_state.change_sort_mode(-1); + } + else AdjustColorMap(-1); + break; - nk_group_begin(ctx, "Content", 0); - { - nk_layout_row(ctx, NK_DYNAMIC, Screen_Scale.y * 35.0f, 2, ratio); + case GLFW_KEY_RIGHT_SHIFT: // num_cluster increase + if (Select_Sort_Area_Mode) + { + auto_curation_state.num_clusters = my_Min(auto_curation_state.num_clusters + 1, 200) ; + } + else keyPressed = 0; + break; - ForLoop(ArrayCount(Meta_Data->tags)) - { - char buff[4]; - stbsp_snprintf(buff, sizeof(buff), "%u:", index + 1); - nk_label(ctx, buff, NK_TEXT_LEFT); - if (nk_edit_string_zero_terminated(ctx, NK_EDIT_FIELD, (char *)Meta_Data->tags[index], sizeof(Meta_Data->tags[index]), 0) & NK_EDIT_ACTIVE) - { - if (!strlen((const char *)Meta_Data->tags[index])) + case GLFW_KEY_RIGHT: + if (MetaData_Edit_Mode) { - ForLoop2(Number_of_Pixels_1D) Map_State->metaDataFlags[index2] &= ~(1 << index); - u32 nextActive = index; - ForLoop2((ArrayCount(Meta_Data->tags) - 1)) + u32 nextActive = MetaData_Active_Tag; + ForLoop(ArrayCount(Meta_Data->tags)) { if (++nextActive == ArrayCount(Meta_Data->tags)) nextActive = 0; if (strlen((const char *)Meta_Data->tags[nextActive])) @@ -8740,112 +8914,107 @@ MetaTagsEditorRun(struct nk_context *ctx, u08 show) } } } - else if (!strlen((const char *)Meta_Data->tags[MetaData_Active_Tag])) MetaData_Active_Tag = index; - } - } - - nk_group_end(ctx); - } - } - nk_end(ctx); -} - -global_function -void -AboutWindowRun(struct nk_context *ctx, u32 show) -{ - struct nk_window *window = nk_window_find(ctx, "About"); - u32 doesExist = window != 0; - - if (!show && !doesExist) - { - return; - } - - if (show && doesExist && (window->flags & NK_WINDOW_HIDDEN)) - { - window->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; - } - - enum windowMode {showAcknowledgements, showLicence, showThirdParty}; - - static windowMode mode = showAcknowledgements; - - if (nk_begin_titled(ctx, "About", PretextView_Version, nk_rect(Screen_Scale.x * 50, Screen_Scale.y * 50, Screen_Scale.x * 870, Screen_Scale.y * 610), - NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE|NK_WINDOW_CLOSABLE)) - { - nk_menubar_begin(ctx); - { - nk_layout_row_dynamic(ctx, Screen_Scale.y * 35.0f, 3); - if (nk_button_label(ctx, "Acknowledgements")) - { - mode = showAcknowledgements; - } - if (nk_button_label(ctx, "Licence")) - { - mode = showLicence; - } - if (nk_button_label(ctx, "Third Party Software")) - { - mode = showThirdParty; - } - } - nk_menubar_end(ctx); - - struct nk_rect total_space = nk_window_get_content_region(ctx); - f32 one = NK_UNDEFINED; - nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 1, &one); - - nk_group_begin(ctx, "About_Content", 0); - { - if (mode == showThirdParty) - { - u08 text[] = R"text(PretextView was made possible thanks to the following third party libraries and -resources, click each entry to view its licence.)text"; - - nk_layout_row_static(ctx, Screen_Scale.y * 60, (s32)(Screen_Scale.x * 820), 1); - s32 len = sizeof(text); - nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)text, &len, len, 0); + else if (Select_Sort_Area_Mode) + { + auto_curation_state.change_sort_mode(1); + } + else AdjustColorMap(1); + break; - ForLoop(Number_of_ThirdParties) - { - u32 nameIndex = 2 * index; - u32 licenceIndex = nameIndex + 1; - s32 *sizes = ThirdParty_Licence_Sizes[index]; + case GLFW_KEY_UP: + if (Select_Sort_Area_Mode) + { + auto_curation_state.adjust_cut_threshold(1); + } + else NextColorMap(1); + break; - if (nk_tree_push_id(NK_Context, NK_TREE_TAB, (const char *)ThirdParty[nameIndex], NK_MINIMIZED, (s32)index)) + case GLFW_KEY_DOWN: + if (Select_Sort_Area_Mode) { - nk_layout_row_static(ctx, Screen_Scale.y * (f32)sizes[0], (s32)(Screen_Scale.x * (f32)sizes[1]), 1); - len = (s32)StringLength(ThirdParty[licenceIndex]); - nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)ThirdParty[licenceIndex], &len, len, 0); - nk_tree_pop(NK_Context); + auto_curation_state.adjust_cut_threshold(-1); } - } - } - else if (mode == showAcknowledgements) - { - nk_layout_row_static(ctx, Screen_Scale.y * 500, (s32)(Screen_Scale.x * 820), 1); - s32 len = sizeof(Acknowledgements); - nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)Acknowledgements, &len, len, 0); + else + NextColorMap(-1); + break; +#ifdef Internal + case GLFW_KEY_ESCAPE: + if (!mods) + { + glfwSetWindowShouldClose(window, GLFW_TRUE); + } + break; +#endif + case GLFW_KEY_ENTER: + if (mods == GLFW_MOD_ALT) + { + if (glfwGetWindowMonitor(window)) + { + glfwSetWindowMonitor(window, NULL, + Windowed_Xpos, Windowed_Ypos, + Windowed_Width, Windowed_Height, 0); + } + else + { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwGetWindowPos(window, &Windowed_Xpos, &Windowed_Ypos); + glfwGetWindowSize(window, &Windowed_Width, &Windowed_Height); + glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); + } + } + } + else + { + keyPressed = 0; + } + break; + + case GLFW_KEY_EQUAL: + if (mods == GLFW_MOD_CONTROL && action == GLFW_PRESS){ + text_box_size.increase_size(); + Redisplay = 1; + } + else keyPressed = 0; + break; + case GLFW_KEY_MINUS: + if (mods == GLFW_MOD_CONTROL && action == GLFW_PRESS){ + text_box_size.decrease_size(); + Redisplay = 1; + } + else keyPressed = 0; + break; + case GLFW_KEY_0: + if (mods == GLFW_MOD_CONTROL && action == GLFW_PRESS){ + text_box_size.defult_size(); + Redisplay = 1; + } + else keyPressed = 0; + break; + + default: + keyPressed = 0; } - else + + if (keyPressed) { - nk_layout_row_static(ctx, Screen_Scale.y * 500, (s32)(Screen_Scale.x * 820), 1); - s32 len = sizeof(Licence); - nk_edit_string(ctx, NK_EDIT_READ_ONLY | NK_EDIT_NO_CURSOR | NK_EDIT_SELECTABLE | NK_EDIT_MULTILINE, (char *)Licence, &len, len, 0); + Redisplay = 1; } - - nk_group_end(ctx); } } - - nk_end(ctx); } +global_function +void +ErrorCallback(s32 error, const char *desc) +{ + (void)error; + fprintf(stderr, "Error: %s\n", desc); +} -global_function void -UserSaveState(const char *, u08, char *); global_function void @@ -10289,6 +10458,98 @@ LoadState(u64 headerHash, char *path) return(0); } +/* + load the .agp file to restore the curated state + NOTE: please make sure to clear the cache before loading the agp file. + which means after clearing the cache, all edits will be lost. if you want + to keep the edits, please try to change the file name and reopen that. as + the cache is automatically named as the hash of the file name. +*/ +void Load_AGP(const std::string& agp_path) +{ + fmt::print("Load AGP file: {}\n", agp_path); + try + { + AssemblyAGP assembly_agp(agp_path, Original_Contigs, Number_of_Original_Contigs, Meta_Data); + fmt::println("{}", assembly_agp.__str__()); + + // do curations to achieve the map_state + // 1. split the original contigs according to the curation agp + std::vector> split_points_frags(Number_of_Original_Contigs); + for (int i = 0; i < assembly_agp.frags.size(); i++) + { + auto& frag = assembly_agp.frags[i]; + if (frag.start > 1) // skip the start bp of one contig + split_points_frags[frag.orig_contig_id].insert(frag.start); + } + // merge the split points + int ptr_bp = 0; + std::vector merged_split_points; + std::string contig_name_tmp; + for (int i = 0; i < Number_of_Original_Contigs; i ++ ) + { + for (auto it = split_points_frags[i].begin(); it != split_points_frags[i].end(); ++it) + merged_split_points.push_back((int)((double)(*it + ptr_bp) / assembly_agp.bp_per_pixel)); + contig_name_tmp = std::string((char*)Original_Contigs[i].name); + if (assembly_agp.original_contigs.find(contig_name_tmp) != assembly_agp.original_contigs.end()) + ptr_bp += assembly_agp.original_contigs[contig_name_tmp].len; + else + fmt::print("[Load AGP::error]: Original_Contig name ({}) not found in the assembly_agp.original_contigs.\n", contig_name_tmp); + } + // cut the at the split points + cut_frags( merged_split_points, false, false); + // check if the number of contigs between contigs_agp and contigs are the same + // after cutting, this should be the same + if (Contigs->numberOfContigs != assembly_agp.frags.size()) + { + fmt::print("[Load AGP::error]: The number of contigs between contigs_agp and contigs are not the same.\n"); + throw std::runtime_error(fmt::format("[Load AGP::error]: The number of contigs between Contigs({}) and contigs_agp({}) are not the same.\n", Contigs->numberOfContigs, assembly_agp.frags.size())); + } + + // vector to save frag's local order within orignal contig + std::vector global_frag_start_Contigs(Number_of_Original_Contigs, 0); + for (int i = 1; i < Number_of_Original_Contigs; i++) + global_frag_start_Contigs[i] = global_frag_start_Contigs[i-1] + Original_Contigs[i-1].nContigs; + + // 2. reorder them + // call the auto_curation func to reorder the contigs globally + std::vector contigs_order_agp(assembly_agp.frags.size()); // restore the order in agp + for (int i = 0; i < assembly_agp.frags.size(); i++) + { + auto& frag = assembly_agp.frags[i]; + contigs_order_agp[i] = ( global_frag_start_Contigs[frag.orig_contig_id] + frag.local_index + 1 ) * (frag.is_reverse?-1:1); + } + FragsOrder frags_order_agp(contigs_order_agp); + // curation globally + AutoCurationFromFragsOrder( &frags_order_agp, Contigs, Map_State, nullptr ); + + // add scaff id to restore the painted scaffID and meta tags + int pix_ptr = 0, contig_start_pix_ptr = 0; + for (int i =0 ; i < Contigs->numberOfContigs;i++) + { + auto& frag = assembly_agp.frags[i]; + while (pix_ptr < Number_of_Pixels_1D && pix_ptr < contig_start_pix_ptr + Contigs->contigs_arr[i].length) + { + Map_State->scaffIds[pix_ptr] = frag.is_painted? frag.scaff_id + 1 : 0; // scaff_id + Map_State->metaDataFlags[pix_ptr++] = frag.meta_data_flag ; // meta data flag + } + contig_start_pix_ptr += Contigs->contigs_arr[i].length; + } + UpdateContigsFromMapState(); + } + catch (const std::exception& e) + { + fmt::print("Exception: {}\n", e.what()); + fmt::print("[Load AGP]: Failed.\n"); + return; + } + + + + + return ; +} + // restore the state before curation global_function @@ -10616,8 +10877,9 @@ default_user_profile_settings() } + global_function void -UserSaveState(const char *headerHash = "userprofile", u08 overwrite = 1, char *path = 0) +UserSaveState(const char *headerHash, u08 overwrite , char *path) { if (!UserSaveState_Path) @@ -10724,6 +10986,22 @@ UserSaveState(const char *headerHash = "userprofile", u08 overwrite = 1, char *p fwrite(&invert_mouse_tmp, sizeof(bool), 1, file); } + // TODO (shaoheng) save default file browser directory + const char* placeholder = "fdpc"; // placeholder for File Directory Path Cache + for (int i = 0; i < 4; i ++ ) fwrite(&placeholder[i], sizeof(char), 1, file); + for (const file_browser& browser_tmp : { browser, saveBrowser, loadBrowser, saveAGPBrowser, loadAGPBrowser }) { + if (browser_tmp.directory[0]!='\0') { + u32 dir_len = strlen(browser_tmp.directory); + fwrite(&dir_len, sizeof(dir_len), 1, file); + fwrite(browser_tmp.directory, sizeof(char), dir_len, file); + } + else { + u32 dir_len = 0; + fwrite(&dir_len, sizeof(dir_len), 1, file); + } + } + + // END of SAVE user profile settings fclose(file); @@ -10868,6 +11146,24 @@ global_function } } + // TODO (shaoheng) LOAD default file browser directory + char lable[4]; + if (fread(lable, sizeof(char), 4, file) == 4 && strncmp(lable, "fdpc", 4) == 0) + { + std::vector browsers = { &browser, &saveBrowser, &loadBrowser, &saveAGPBrowser, &loadAGPBrowser }; + for (file_browser* browser_ptr : browsers) { + u32 dir_len; + if (fread(&dir_len, sizeof(dir_len), 1, file) == 1 && dir_len > 0) + { + char *dir = (char *)malloc(dir_len + 1); + if (fread(dir, sizeof(char), dir_len, file) == dir_len ) { + dir[dir_len] = '\0'; // Null-terminate the string + FileBrowserReloadDirectoryContent(browser_ptr, dir); + } + free(dir); + } + } + } fclose(file); @@ -10985,16 +11281,23 @@ GenerateAGP(char *path, u08 overwrite, u08 formatSingletons, u08 preserveOrder) u32 scaffId_unPainted = 0; if (preserveOrder) ForLoop(Contigs->numberOfContigs) scaffId_unPainted = my_Max(scaffId_unPainted, (Contigs->contigs_arr + index)->scaffId); - for ( u08 type = 0; - type < (preserveOrder ? 1 : 2); - ++type) + for ( u08 type = 0; + type < (preserveOrder ? 1 : 2); // two round: 1st round for painted scaffs, 2nd round for unpainted + ++type) { ForLoop(Contigs->numberOfContigs) { contig *cont = Contigs->contigs_arr + index; - u08 invert = IsContigInverted(index); + u08 invert = IsContigInverted( index ); u32 startCoord = cont->startCoord - (invert ? (cont->length - 1) : 0); - + + /* + set invert to 0 if: + - formatSingletons is False and + - contig is not painted + - contig is just 1 pixel long + - contig is the first or last contig in the genome + */ invert = (!formatSingletons && (!cont->scaffId || (index && (index < (Contigs->numberOfContigs - 1)) && (cont->scaffId != ((cont + 1)->scaffId)) && (cont->scaffId != ((cont - 1)->scaffId))) || (!index && (cont->scaffId != ((cont + 1)->scaffId))) || ((index == (Contigs->numberOfContigs - 1)) && (cont->scaffId != ((cont - 1)->scaffId))))) ? 0 : invert; u64 contRealStartCoord = (u64)((f64)startCoord / (f64)Number_of_Pixels_1D * (f64)Total_Genome_Length) + 1; @@ -11003,13 +11306,13 @@ GenerateAGP(char *path, u08 overwrite, u08 formatSingletons, u08 preserveOrder) char *contName = (char *)((Original_Contigs + cont->get_original_contig_id())->name); - if (cont->scaffId && !type) + if (cont->scaffId && !type) // painted scaff { if (cont->scaffId != scaffId) { scaffId = cont->scaffId; scaffCoord_Start = 1; - scaffPart = 0; + scaffPart = 0; // index within the scaffold } u64 scaffCoord_End; @@ -11030,7 +11333,7 @@ GenerateAGP(char *path, u08 overwrite, u08 formatSingletons, u08 preserveOrder) { ForLoop2(ArrayCount(Meta_Data->tags)) { - if (*cont->metaDataFlags & (1 << index2)) + if (*cont->metaDataFlags & (1ULL << index2)) { stbsp_snprintf(buffer, sizeof(buffer), "\t%s", (char *)Meta_Data->tags[index2]); fwrite(buffer, 1, strlen(buffer), file); @@ -11040,7 +11343,7 @@ GenerateAGP(char *path, u08 overwrite, u08 formatSingletons, u08 preserveOrder) fwrite("\n", 1, 1, file); scaffCoord_Start = scaffCoord_End + 1; } - else if (!cont->scaffId && (preserveOrder || type)) + else if (!cont->scaffId && (preserveOrder || type)) // unpainted { stbsp_snprintf(buffer, sizeof(buffer), "Scaffold_%u\t1\t%" PRIu64 "\t1\tW\t%s\t%" PRIu64 "\t%" PRIu64 "\t%s", (preserveOrder ? ++scaffId_unPainted : ++scaffId), contRealSize, contName, contRealStartCoord, contRealEndCoord, invert ? "-" : "+"); fwrite(buffer, 1, strlen(buffer), file); @@ -11048,7 +11351,7 @@ GenerateAGP(char *path, u08 overwrite, u08 formatSingletons, u08 preserveOrder) { ForLoop2(ArrayCount(Meta_Data->tags)) { - if (*cont->metaDataFlags & (1 << index2)) + if (*cont->metaDataFlags & (1ULL << index2)) // NOTE: please make sure the 1ULL, or after index2 >= 32 there will be unexpected behaviour { stbsp_snprintf(buffer, sizeof(buffer), "\t%s", (char *)Meta_Data->tags[index2]); fwrite(buffer, 1, strlen(buffer), file); @@ -11184,7 +11487,7 @@ MainArgs Edit_Pixels.editing = 0; Edit_Pixels.selecting = 0; Edit_Pixels.scaffSelecting = 0; - Edit_Pixels.snap = 0; + Edit_Pixels.snap = 1; // change the default to snap mode on Camera_Position.x = 0.0f; // 0.0f Camera_Position.y = 0.0f; // 0.0f @@ -11287,11 +11590,6 @@ MainArgs // } // } - // file browser - struct file_browser browser; - struct file_browser saveBrowser; - struct file_browser loadBrowser; - struct file_browser saveAGPBrowser; u32 showClearCacheScreen = 0; @@ -11307,6 +11605,7 @@ MainArgs FileBrowserInit(&saveBrowser, &media); FileBrowserInit(&loadBrowser, &media); FileBrowserInit(&saveAGPBrowser, &media); + FileBrowserInit(&loadAGPBrowser, &media); } { @@ -11320,9 +11619,6 @@ MainArgs Redisplay = 1; - char searchbuf[256] = {0}; - s32 caseSensitive_search_sequences = 0; - while (!glfwWindowShouldClose(window)) { if (Redisplay) @@ -11423,6 +11719,7 @@ MainArgs s32 showSaveStateScreen = 0; s32 showLoadStateScreen = 0; s32 showSaveAGPScreen = 0; + s32 showLoadAGPScreen = 0; s32 showMetaDataTagEditor = 0; s32 showUserProfileScreen = 0; static u32 currGroup1 = 0; @@ -11460,6 +11757,7 @@ MainArgs showSaveStateScreen = nk_button_label(NK_Context, "Save State"); showLoadStateScreen = nk_button_label(NK_Context, "Load State"); showSaveAGPScreen = nk_button_label(NK_Context, "Generate AGP"); + showLoadAGPScreen = nk_button_label(NK_Context, "Load AGP"); if (nk_button_label(NK_Context, "Clear Cache")) showClearCacheScreen = 1; // used to clear cache for current opened sample } showUserProfileScreen = nk_button_label(NK_Context, "User Profile"); @@ -12598,6 +12896,10 @@ MainArgs if (FileBrowserRun("Load State", &loadBrowser, NK_Context, (u32)showLoadStateScreen)) LoadState(headerHash, loadBrowser.file); + if (FileBrowserRun("Load AGP", &loadAGPBrowser, NK_Context, (u32)showLoadAGPScreen)) + Load_AGP(loadAGPBrowser.file); + + MetaTagsEditorRun(NK_Context, (u08)showMetaDataTagEditor); } diff --git a/src/Resources.cpp b/src/Resources.cpp index 2423fa8..39b2ea6 100755 --- a/src/Resources.cpp +++ b/src/Resources.cpp @@ -7,6 +7,7 @@ global_variable u08 Licence[] = R"lic(Copyright (c) 2021 Ed Harry, Wellcome Sanger Institute + 2024 Shaoheng Guan, Wellcome Sanger Institute Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/aisort.cpp b/src/aisort.cpp index 81a7c11..645ce6b 100644 --- a/src/aisort.cpp +++ b/src/aisort.cpp @@ -22,7 +22,7 @@ SOFTWARE. */ -#include +#include "aisort.h" /* diff --git a/src/auto_curation_state.h b/src/auto_curation_state.h index d01e8f4..297538f 100644 --- a/src/auto_curation_state.h +++ b/src/auto_curation_state.h @@ -100,7 +100,7 @@ struct AutoCurationState f32 link_score_threshold = 0.4f; f32 auto_cut_threshold = 0.05f; - u32 auto_cut_diag_window_for_pixel_mean= 8; + u32 auto_cut_diag_window_for_pixel_mean = 8; u32 auto_cut_smallest_frag_size_in_pixel = 8; // cluster according to the hap name diff --git a/src/frag_cut_calculation.h b/src/frag_cut_calculation.h index fa5906c..fe165db 100644 --- a/src/frag_cut_calculation.h +++ b/src/frag_cut_calculation.h @@ -114,7 +114,7 @@ class FragCutCal } } - std::vector get_cut_locs_pixel( + std::vector get_cut_locs_pixel( const AutoCurationState& auto_curation_state, const u32* pixel_rearrange_index_, const contigs* Contigs, @@ -129,7 +129,7 @@ class FragCutCal // 在 select_area 中进行cut this->frags->re_allocate_mem(Contigs, select_area, 1); - std::vector cut_locs_pixel; + std::vector cut_locs_pixel; for (u32 i = 0; i < this->frags->num; i++) { this->get_single_fragment_cut_locs(i, cut_locs_pixel); @@ -141,7 +141,7 @@ class FragCutCal void find_break_points( const u32 start_pixel, const std::vector& arr, - std::vector& break_points) + std::vector& break_points) { const auto& windows_size = this->smallest_frag_size_in_pixel; const auto& threshold = this->cut_threshold; @@ -185,7 +185,7 @@ class FragCutCal min_i = i; } } - break_points.push_back(min_i + start_pixel); + break_points.push_back((int)(min_i + start_pixel)); i += windows_size ; } } @@ -196,7 +196,7 @@ class FragCutCal void get_single_fragment_cut_locs( const u32& frag_id, - std::vector& cut_locs) + std::vector& cut_locs) { // copy orginal to local std::vector hic_pixel_density_tmp(this->hic_pixel_density.begin() + this->frags->startCoord[frag_id], @@ -225,7 +225,7 @@ class FragCutCal fmt::print("[Pixel Cut]: output hic_pixel_density to {}\n", filename); #endif // DEBUG_OUTPUT_PIXEL_CUT_FILE - std::vector break_points(0); + std::vector break_points(0); this->find_break_points( this->frags->startCoord[frag_id], hic_pixel_density_tmp, diff --git a/src/frags_order.h b/src/frags_order.h index 8aa3b0f..f49d231 100644 --- a/src/frags_order.h +++ b/src/frags_order.h @@ -32,6 +32,30 @@ struct FragsOrder return ; } + FragsOrder(std::vector order) // order start from 1, +/- represents the direction of the fragment + { + cleanup(); + num_frags = order.size(); + this->order.push_back(std::vector(num_frags)); + for (s32 i = 0; i < num_frags; i++) this->order[0][i] = order[i]; + return ; + } + + FragsOrder( // merge to build new fragsOrder + const std::vector& frags_order_list, + const std::vector>& clusters) + { + cleanup(); + for (u32 i = 0; i < frags_order_list.size(); i++) // 遍历所有的cluster + { + this->num_frags += clusters[i].size(); + auto tmp_order = frags_order_list[i].get_order_without_chromosomeInfor(); + for (auto& tmp:tmp_order) + tmp = (clusters[i][std::abs(tmp)-1] + 1) * (tmp>0?1:-1); + this->order.push_back(std::move(tmp_order)); + } + } + ~FragsOrder() { cleanup(); @@ -82,22 +106,6 @@ struct FragsOrder std::cout << std::endl; return ; } - - - FragsOrder( - const std::vector& frags_order_list, - const std::vector>& clusters) - { - cleanup(); - for (u32 i = 0; i < frags_order_list.size(); i++) // 遍历所有的cluster - { - this->num_frags += clusters[i].size(); - auto tmp_order = frags_order_list[i].get_order_without_chromosomeInfor(); - for (auto& tmp:tmp_order) - tmp = (clusters[i][std::abs(tmp)-1] + 1) * (tmp>0?1:-1); - this->order.push_back(std::move(tmp_order)); - } - } }; diff --git a/src/genomeData.h b/src/genomeData.h index 8bfb0a1..a0e09b6 100644 --- a/src/genomeData.h +++ b/src/genomeData.h @@ -54,8 +54,7 @@ struct contact_matrix }; -// one original contig -// which may be split into multiple fragments +// one original contig may be split into multiple fragments struct original_contig { u32 name[16]; // contig name @@ -130,6 +129,49 @@ struct map_state originalContigIds[i] = originalContigIds[i] % Max_Number_of_Contigs; } } + + + map_state(int num_pixel_1d) + { + contigIds = new u32[num_pixel_1d]; + originalContigIds = new u32[num_pixel_1d]; + contigRelCoords = new u32[num_pixel_1d]; + scaffIds = new u32[num_pixel_1d]; + metaDataFlags = new u64[num_pixel_1d]; + memset(contigIds, 0, num_pixel_1d * sizeof(u32)); + memset(originalContigIds, 0, num_pixel_1d * sizeof(u32)); + memset(contigRelCoords, 0, num_pixel_1d * sizeof(u32)); + memset(scaffIds, 0, num_pixel_1d * sizeof(u32)); + memset(metaDataFlags, 0, num_pixel_1d * sizeof(u64)); + } + ~map_state() + { + if (contigIds) + { + delete[] contigIds; + contigIds = nullptr; + } + if (originalContigIds) + { + delete[] originalContigIds; + originalContigIds = nullptr; + } + if (contigRelCoords) + { + delete[] contigRelCoords; + contigRelCoords = nullptr; + } + if (scaffIds) + { + delete[] scaffIds; + scaffIds = nullptr; + } + if (metaDataFlags) + { + delete[] metaDataFlags; + metaDataFlags = nullptr; + } + } }; diff --git a/src/grey_out_settings.h b/src/grey_out_settings.h index 4098ce7..7885e17 100644 --- a/src/grey_out_settings.h +++ b/src/grey_out_settings.h @@ -61,6 +61,22 @@ struct GreyOutSettings } return ""; } + + /* return: + 0: no grey out + 1: vertical grey out + 2: horizontal grey out + */ + u32 is_vert_horiz_grey_out(u64* meta_data_flags, meta_data* Meta_Data){ + for (s32 i = 0; i < ArrayCount(Meta_Data->tags) ; i ++ ) { + if ((*meta_data_flags & ((u64)1 << i)) ){ + if ( std::string((char*) Meta_Data->tags[i])=="vertPaint") return 1; + else if ( std::string((char*) Meta_Data->tags[i])=="horzPaint") return 2; + } + } + return 0; + } + }; diff --git a/src/parse_agp.h b/src/parse_agp.h new file mode 100644 index 0000000..bc75e5e --- /dev/null +++ b/src/parse_agp.h @@ -0,0 +1,377 @@ + + +#ifndef parse_agp_h +#define parse_agp_h + +#include +#include +#include +#include +#include +#include +#include +#include "genomeData.h" + + + +struct Date +{ + int year; + int month; + int day; + int hour; + int minute; + int second; + Date() + : year(-1), month(-1), day(-1), hour(-1), minute(-1), second(-1) {} + + Date(int y, int m, int d, int h, int min, int s) + : year(y), month(m), day(d), hour(h), minute(min), second(s) {} + + Date(int y, int m, int d) + : year(y), month(m), day(d), hour(-1), minute(-1), second(-1) {} + + Date(const Date& other) + : year(other.year), month(other.month), day(other.day), + hour(other.hour), minute(other.minute), second(other.second) {} + + std::string to_string() const + { + if (second>=0 && minute>=0 && hour>=0) + return fmt::format("{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}", year, month, day, hour, minute, second); + else if (year>=0 && month>=0 && day>=0) + return fmt::format("{:04d}-{:02d}-{:02d}", year, month, day); + else if (year>=0 && month>=0) + return fmt::format("{:04d}-{:02d}", year, month); + else if (year>=0) + return fmt::format("{:04d}", year); + else return "Unknown Date"; + } +}; + + +struct Original_Contig_agp +{ + int len; // len in bp + int num_frags; + std::vector starts; // record the start position of each fragment + + Original_Contig_agp() + : len(0), num_frags(0) {} + Original_Contig_agp(int l, int n) : len(l), num_frags(n) {} + +}; + +struct Scaff_agp +{ + int len; // len in bp + bool is_painted; + Scaff_agp( bool p) + : len(0), is_painted(p) {} +}; + + +struct Frag +{ + const int orig_contig_id; // start from 0 + const int scaff_id; // start from 0 + const int start; // start position (bp) in the original contig + const int len; // len in bp + int local_index = -1; // index within orignal contig + const bool is_reverse; + const bool is_painted ; + const uint64_t meta_data_flag = 0; + Frag(const int oci, const int si, int s, int l, bool r, bool p, const uint64_t& m) + : orig_contig_id(oci), scaff_id(si), start(s), len(l), is_reverse(r), is_painted(p), meta_data_flag(m) {} +}; + + +int get_scaff_id(const std::string& scaff_name) +{ + std::smatch match; + if (!std::regex_search(scaff_name, match, std::regex("^\\w+_(\\d+)$"))) + { + fmt::print(stderr, "Error: scaff name {} not match ^\\w+_(\\d+)$ \n", scaff_name); + assert(0); + } else + { + return std::stoi(match[1].str()) - 1; + } +} + + +class AssemblyAGP +{ +public: + std::string sample_name; + std::string agp_version; + std::string description; + Date date; + double bp_per_pixel; + + std::vector frags; + std::unordered_map original_contigs; + std::vector scaffs; + int total_bp = 0; + + + int get_num_painted_scaff() const + { + int num = 0; + for (const auto& scaff : scaffs) + { + if (scaff.is_painted) num++; + } + return num; + } + + void add_frag( + const original_contig* Original_Contigs, const int& num_original_contigs, + const std::string& scaff_name, + const std::string& original_contig_name, + const int& start_local, const int& end_local, + bool is_reverse, + bool is_painted, + const uint64_t& meta_tags ) + { + int original_contig_id = this->get_original_contig_id(Original_Contigs, num_original_contigs, original_contig_name); + if (original_contig_id < 0) + throw std::runtime_error(fmt::format("Error: original contig name {} not found in Original_Contigs. file: {}, line: {}\n", original_contig_name, __FILE__, __LINE__)); + int scaff_id = get_scaff_id(scaff_name); // start from 0 + + if (this->original_contigs.find(original_contig_name) == this->original_contigs.end()) + this->original_contigs[original_contig_name] = Original_Contig_agp(end_local, 1); + else { + this->original_contigs[original_contig_name].num_frags++; + this->original_contigs[original_contig_name].len = std::max(this->original_contigs[original_contig_name].len, end_local); + } + this->original_contigs[original_contig_name].starts.push_back(start_local); + this->frags.emplace_back(original_contig_id, scaff_id, start_local, std::abs(end_local- start_local) + 1, is_reverse, is_painted, meta_tags); + + if (this->scaffs.size() <= frags.back().scaff_id) + this->scaffs.emplace_back(is_painted); + + this->scaffs[this->frags.back().scaff_id].len += this->frags.back().len; + this->total_bp += this->frags.back().len; + } + + /* + restore the assembly from the + - UN-CORRECTED .agp file + */ + AssemblyAGP( + const std::string& agp_file, + const original_contig* Original_Contigs, + const int& num_original_contigs, + meta_data* Meta_Data // used to restore the meta data tag + ) + { + std::ifstream file(agp_file); + if (!file.is_open()) + { + fmt::print(stderr, "Failed to open AGP file: {}\n", agp_file); + return; + } + + sample_name = agp_file.substr(agp_file.find_last_of("/\\") + 1); + sample_name = sample_name.substr(0, sample_name.find('.')); + + std::string line; + while (std::getline(file, line)) + { + std::smatch match; + if (line.empty() ) continue; // Skip empty lines and comments + else if (std::regex_search(line, match, std::regex("^##agp-version\\s*(\\d+.\\d+)\\s*$"))) // version + { + this->agp_version = match[1].str(); + } + else if (std::regex_search(line, match, std::regex("^# DESCRIPTION:\\s*(.*)$"))) // descirption + { + this->description = match[1].str(); + } else if (std::regex_search(line, match, std::regex("^# HiC MAP RESOLUTION:\\s*(\\d+.\\d*)\\s+bp/texel\\s*$"))) + { + this->bp_per_pixel = std::stod(match[1].str()); + } else if (std::regex_search(line, match, std::regex("^([\\w_]+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\w+)\\s+([\\w_]+)\\s+(\\d+)\\s+(\\d+)\\s+([+-])\\s*([Painted]*)\\s*(.*)$"))) // genome line + { + std::string scaff_name = match[1].str(); + int scaff_start = std::stoi(match[2].str()); + int scaff_end = std::stoi(match[3].str()); + int scaff_frag_id = std::stoi(match[4].str()); + bool is_gap = match[5].str() == "U"; + std::string original_contig_name = match[6].str(); + int start_local = std::stoi(match[7].str()); + int end_local = std::stoi(match[8].str()); + bool is_reverse = match[9].str() == "-"; + bool is_painted = match[10].str() == "Painted"; + uint64_t meta_tags = parse_tags(match[11].str(), Meta_Data); + + this->add_frag( + Original_Contigs, num_original_contigs, + scaff_name, original_contig_name, + start_local, end_local, + is_reverse, is_painted, meta_tags + ); + } else if (std::regex_search(line, match, std::regex("^([\\w_]+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\w+)\\s+(\\d+)\\s+([\\w_]+)\\s+(\\w+)\\s+([\\w_]+)(.+)$"))) // gap line + { + std::string scaff_name = match[1].str(); + int scaff_start = std::stoi(match[2].str()); + int scaff_end = std::stoi(match[3].str()); + int scaff_frag_id = std::stoi(match[4].str()); + bool is_gap = match[5].str() == "U"; + int gap_length = std::stoi(match[6].str()); + } + } + file.close(); + this->sort_frags_local_index(Original_Contigs); + } + + uint64_t parse_tags(const std::string tags_str, meta_data* Meta_Data) + { + if (tags_str.empty()) return 0; + uint64_t tags_u64 = 0; + std::istringstream iss(tags_str); + std::string tag; + + // 使用流操作符按空格分割字符串 + while (iss >> tag) + { + for (int i = 0 ; i<64 ; i ++ ) + { + if (std::string((char*)Meta_Data->tags[i]).empty()) + { + memcpy(Meta_Data->tags[i], tag.data(), tag.size() + 1); + tags_u64 |= (1ULL << i); + break; + } + else if (tag == std::string((char*)(Meta_Data->tags[i]))) + { + tags_u64 |= (1ULL << i); + break; + } + } + } + return tags_u64; + } + + /* validate the number of base pairs */ + int cal_total_bp() + { + int total = 0; + for (const auto& frag : frags) + { + total += frag.len; + } + + int total_bp_scaff = 0; + for (const auto& scaff : scaffs) + total_bp_scaff += scaff.len; + if (total != total_bp_scaff) + { + fmt::print(stderr, "Warning: total bp in frags ({}) != total bp in scaffs ({})\n", total, total_bp_scaff); + assert(total == total_bp_scaff); + return -1; + } + return total; + } + + /* + restore the map_state from the assembly + - UN-CORRECTED .agp file + return: 0 if success, -1 if failed + */ + int restore_map_state_from_agp( + const int& num_pix_1d, + map_state* Map_State, + const original_contig* Original_Contigs, + const int& num_original_contigs + ) + { + int pixel_index; + double start_pixel_global = 0, len_pixel = 0, local_start_pixel; + for ( int i = 0 ; i < this->frags.size() ; i++ ) + { + const Frag& frag = frags[i]; + local_start_pixel = (double) frag.start / this->bp_per_pixel; + len_pixel = (double) frag.len / this->bp_per_pixel; + for (int j = 0; j < std::round(len_pixel); j ++ ) + { + pixel_index = std::round(start_pixel_global) + j; + if ( pixel_index >= num_pix_1d ) + { + fmt::print(stderr, "Warning: pixel index ({:.2f}) out of range [0, {}).\n", pixel_index, num_pix_1d); + assert(0); + return -1; + } + Map_State->contigIds[pixel_index] = i; + Map_State->originalContigIds[pixel_index] = frag.orig_contig_id; + Map_State->contigRelCoords[pixel_index] = frag.is_reverse ? + std::round(local_start_pixel + len_pixel) - j - 1: + std::round(local_start_pixel) + j; + Map_State->scaffIds[pixel_index] = frag.scaff_id; + pixel_index++; + } + start_pixel_global += len_pixel; + } + #ifdef DEBUG + int n = 2000; + fmt::print("[Load AGP::restore_map_state_from_agp]: Relative coordinates of the first {} pixels:\n", n); + for (int i = 0; i < n ; i++) + { + fmt::print("{}:{} ", Map_State->originalContigIds[i], Map_State->contigRelCoords[i]); + if (i % 40 == 39) fmt::print("\n"); + } + fmt::print("\n"); + + #endif // DEBUG + + + return 0; + } + + int get_original_contig_id( + const original_contig* Original_Contigs, const int& num_original_contigs, const std::string& name) + { + for (int i = 0; i < num_original_contigs; i++) + { + if (strcmp((char*)(Original_Contigs+i)->name, name.c_str()) == 0) + { + return i; + } + } + return -1; + } + + std::string __str__() const + { + std::string + str = fmt::format("Sample name: {}\n", sample_name); + str += fmt::format("AGP Version: {}\n", agp_version); + str += fmt::format("Description: {}\n", description); + str += fmt::format("Date: {}\n", date.to_string()); + str += fmt::format("BP per pixel: {}\n", bp_per_pixel); + str += fmt::format("Total BP: {}\n", total_bp); + return str; + } + + void sort_frags_local_index(const original_contig* Original_Contigs) + { + for (auto& it:this->original_contigs) std::sort(it.second.starts.begin(), it.second.starts.end()); + int tmp_index = 0 ; + for (int i = 0 ; i < frags.size(); i++) + { + std::string original_contig_name = std::string((char*)Original_Contigs[frags[i].orig_contig_id].name); + while (tmp_index < this->original_contigs[original_contig_name].starts.size() && + this->frags[i].start != this->original_contigs[original_contig_name].starts[tmp_index]) tmp_index++; + if (tmp_index >= this->original_contigs[original_contig_name].starts.size()) + { + throw std::runtime_error(fmt::format("Error: original contig name {} not found in Original_Contigs. file: {}, line: {}\n", original_contig_name, __FILE__, __LINE__)); + } + this->frags[i].local_index = tmp_index; + tmp_index = 0; + } + } +}; + +#endif // parse_agp_h + + diff --git a/src/utilsPretextView.h b/src/utilsPretextView.h index 84848b2..c01c04d 100644 --- a/src/utilsPretextView.h +++ b/src/utilsPretextView.h @@ -35,7 +35,7 @@ SOFTWARE. #include #include #include - +#include #include #include From 619a4fe4189c2e1a6f065c6b55e7f0ec77a8dd40 Mon Sep 17 00:00:00 2001 From: guanshaoheng Date: Tue, 3 Jun 2025 16:34:27 +0100 Subject: [PATCH 29/38] comment the test object --- CMakeLists.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ff137c..9b15866 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,13 +106,14 @@ endif() # test the parse_agp -add_executable(parse_agp_test src/parse_agp_test.cpp) -target_link_libraries(parse_agp_test PRIVATE ${lib_fmt}) -if (CMAKE_BUILD_TYPE STREQUAL "Debug") - target_compile_definitions(parse_agp_test PRIVATE DEBUG) +if (AGP_TEST_FLAG) + add_executable(parse_agp_test src/parse_agp_test.cpp) + target_link_libraries(parse_agp_test PRIVATE ${lib_fmt}) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(parse_agp_test PRIVATE DEBUG) + endif() endif() - find_library( lib_deflate_static NAMES deflate deflatestatic From e7730211b702f868b2ea935859a61ec1adc5d664 Mon Sep 17 00:00:00 2001 From: guanshaoheng Date: Tue, 3 Jun 2025 16:35:35 +0100 Subject: [PATCH 30/38] solve the glfw delete segmental fault --- .gitignore | 1 + PretextView.cpp | 29 ++++++++++++++++------------- src/copy_texture.cpp | 3 ++- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index cc14256..9a55a13 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ app/ ascii.dSYM/ build_cmake/ builddir/ +build/ *.DS_Store __pycache__/ *.pyc diff --git a/PretextView.cpp b/PretextView.cpp index 28127fb..ebb68a1 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -11006,7 +11006,9 @@ UserSaveState(const char *headerHash, u08 overwrite , char *path) fclose(file); - printf("[UserProfile]: Saved to: %s\n", path); + #ifdef DEBUG + printf("[UserProfile]: Saved to: %s\n", path); + #endif // DEBUG } global_function @@ -12943,18 +12945,6 @@ MainArgs } if (currFileName) SaveState(headerHash); - - ThreadPoolWait(Thread_Pool); - ThreadPoolDestroy(Thread_Pool); - glfonsDelete(FontStash_Context); - nk_font_atlas_clear(NK_Atlas); - nk_free(NK_Context); - nk_buffer_free(&NK_Device->cmds); - glfwDestroyWindow(window); - glfwTerminate(); - - // free the memory allocated for the shader sources - fprintf(stdout, "Memory freed for shader sources.\n"); // do we need to free anything else? // for example the allocated memory arena @@ -12996,6 +12986,19 @@ MainArgs } #endif // PYTHON_SCOPED_INTERPRETER + + ThreadPoolWait(Thread_Pool); + ThreadPoolDestroy(Thread_Pool); + glfonsDelete(FontStash_Context); + nk_font_atlas_clear(NK_Atlas); + nk_free(NK_Context); + nk_buffer_free(&NK_Device->cmds); + glfwDestroyWindow(window); + glfwTerminate(); + + // free the memory allocated for the shader sources + fprintf(stdout, "Memory freed for shader sources.\n"); + ResetMemoryArenaP(Loading_Arena); fprintf(stdout, "Memory freed for the arena.\n"); diff --git a/src/copy_texture.cpp b/src/copy_texture.cpp index 89dc344..b7110b1 100644 --- a/src/copy_texture.cpp +++ b/src/copy_texture.cpp @@ -268,10 +268,11 @@ TexturesArray4AI::~TexturesArray4AI() if (this->hic_shader_initilised) { - glDeleteShader(this->shaderProgram); + // glDeleteShader(this->shaderProgram); glDeleteBuffers(1, &this->vbo); glDeleteBuffers(1, &this->ebo); glDeleteVertexArrays(1, &this->vao); + glDeleteProgram(this->shaderProgram); } } From 3269c3b7889bea226cd5734b8c7e9056e4565e2f Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 4 Jun 2025 11:49:41 +0100 Subject: [PATCH 31/38] avoid return warning --- src/parse_agp.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/parse_agp.h b/src/parse_agp.h index bc75e5e..50a082c 100644 --- a/src/parse_agp.h +++ b/src/parse_agp.h @@ -93,10 +93,8 @@ int get_scaff_id(const std::string& scaff_name) { fmt::print(stderr, "Error: scaff name {} not match ^\\w+_(\\d+)$ \n", scaff_name); assert(0); - } else - { - return std::stoi(match[1].str()) - 1; - } + } + return std::stoi(match[1].str()) - 1; } From 8a2855f091ff3cdb8be77e4294261013e7bf6b1e Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Wed, 4 Jun 2025 11:50:04 +0100 Subject: [PATCH 32/38] remove the left click to run global sort or cutting --- PretextView.cpp | 168 ++++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/PretextView.cpp b/PretextView.cpp index ebb68a1..3417763 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -11148,7 +11148,6 @@ global_function } } - // TODO (shaoheng) LOAD default file browser directory char lable[4]; if (fread(lable, sizeof(char), 4, file) == 4 && strncmp(lable, "fdpc", 4) == 0) { @@ -11817,81 +11816,84 @@ MainArgs // Pixel Cut { bounds = nk_widget_bounds(NK_Context); - auto_cut_button = nk_button_label(NK_Context, "Pixel Cut"); + auto_cut_button = nk_button_label(NK_Context, "Pixel Cut") || auto_cut_button ; - if (auto_cut_button && currFileName) - { - auto_cut_state = 1; - auto_curation_state.clear(); // if click the button, the sort/cut will be applied globally - } // window to set the parameters for cut - if (nk_contextual_begin(NK_Context, 0, nk_vec2(Screen_Scale.x * 480, Screen_Scale.y * 800), bounds)) + if (auto_cut_button) { - nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 1); - - nk_label(NK_Context, "Cut threshold (default: 0.05)", NK_TEXT_LEFT); - nk_edit_string_zero_terminated( - NK_Context, - NK_EDIT_FIELD, - (char*)auto_curation_state.auto_cut_threshold_buf, - sizeof(auto_curation_state.auto_cut_threshold_buf), - nk_filter_float); - - nk_label(NK_Context, "Pixel_mean window size (default: 8)", NK_TEXT_LEFT); // 见鬼了,见鬼了,这个地方,如果我使用 "Pixel_mean windows (default: 8)" 点击弹出的窗口外面程序就会卡死,但是使用"Pixel_mean window size (default: 8)" 就不会卡死 - nk_edit_string_zero_terminated( - NK_Context, - NK_EDIT_FIELD, - (char*)auto_curation_state.auto_cut_diag_window_for_pixel_mean_buf, - sizeof(auto_curation_state.auto_cut_diag_window_for_pixel_mean_buf), - nk_filter_decimal); - - nk_label(NK_Context, "Smallest frag size (default: 8)", NK_TEXT_LEFT); - nk_edit_string_zero_terminated( - NK_Context, - NK_EDIT_FIELD, - (char*)auto_curation_state.auto_cut_smallest_frag_size_in_pixel_buf, - sizeof(auto_curation_state.auto_cut_smallest_frag_size_in_pixel_buf), - nk_filter_decimal); + static struct nk_rect popup_rect = nk_rect(Screen_Scale.x * 100, Screen_Scale.y * 100, Screen_Scale.x * 480, Screen_Scale.y * 300); + if (nk_popup_begin(NK_Context, NK_POPUP_STATIC, "Pixel Cut Parameters", + NK_WINDOW_CLOSABLE|NK_WINDOW_BORDER, popup_rect)) { + + nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 1); - nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 2); - if (nk_button_label(NK_Context, "Cancel")) - { - printf("[Pixel Cut] Cancel button clicked\n"); - auto_curation_state.set_buf(); - nk_contextual_close(NK_Context); - } - if (nk_button_label(NK_Context, "Apply")) - { - // Apply changes - // Convert text to integer and float - auto_curation_state.update_value_from_buf(); // todo 这里还有问题,因为设置了pixel_mean之后 并没有重新计算 pixel_density... + nk_label(NK_Context, "Cut threshold (default: 0.05)", NK_TEXT_LEFT); + nk_edit_string_zero_terminated( + NK_Context, + NK_EDIT_FIELD, + (char*)auto_curation_state.auto_cut_threshold_buf, + sizeof(auto_curation_state.auto_cut_threshold_buf), + nk_filter_float); - nk_contextual_close(NK_Context); + nk_label(NK_Context, "Pixel_mean window size (default: 8)", NK_TEXT_LEFT); // 见鬼了,见鬼了,这个地方,如果我使用 "Pixel_mean windows (default: 8)" 点击弹出的窗口外面程序就会卡死,但是使用"Pixel_mean window size (default: 8)" 就不会卡死 + nk_edit_string_zero_terminated( + NK_Context, + NK_EDIT_FIELD, + (char*)auto_curation_state.auto_cut_diag_window_for_pixel_mean_buf, + sizeof(auto_curation_state.auto_cut_diag_window_for_pixel_mean_buf), + nk_filter_decimal); + + nk_label(NK_Context, "Smallest frag size (default: 8)", NK_TEXT_LEFT); + nk_edit_string_zero_terminated( + NK_Context, + NK_EDIT_FIELD, + (char*)auto_curation_state.auto_cut_smallest_frag_size_in_pixel_buf, + sizeof(auto_curation_state.auto_cut_smallest_frag_size_in_pixel_buf), + nk_filter_decimal); - auto_curation_state.set_buf(); - fmt::print("[Pixel Cut] cut_threshold: {:.3f}\n", auto_curation_state.auto_cut_threshold); - fmt::print("[Pixel Cut] pixel mean window size: {}\n", auto_curation_state.auto_cut_diag_window_for_pixel_mean); - fmt::print("[Pixel Cut] smallest_frag_size_in_pixel: {}\n", auto_curation_state.auto_cut_smallest_frag_size_in_pixel); - } + nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 2); + if (nk_button_label(NK_Context, "Apply settings")) + { + // Apply changes + // Convert text to integer and float + auto_curation_state.update_value_from_buf(); // todo 这里还有问题,因为设置了pixel_mean之后 并没有重新计算 pixel_density... - nk_contextual_end(NK_Context); + auto_curation_state.set_buf(); + fmt::print("[Pixel Cut] cut_threshold: {:.3f}\n", auto_curation_state.auto_cut_threshold); + fmt::print("[Pixel Cut] pixel mean window size: {}\n", auto_curation_state.auto_cut_diag_window_for_pixel_mean); + fmt::print("[Pixel Cut] smallest_frag_size_in_pixel: {}\n", auto_curation_state.auto_cut_smallest_frag_size_in_pixel); - + auto_cut_button = 0; + nk_popup_close(NK_Context); + } + /* 关闭按钮 */ + if (nk_button_label(NK_Context, "Close")) { + auto_cut_button = 0; + auto_curation_state.set_buf(); + nk_popup_close(NK_Context); + } + + nk_popup_end(NK_Context); + } else { + /* 如果用户点击了窗口外的区域,也关闭弹出窗口 */ + auto_cut_button = 0; + #ifdef DEBUG + fmt::println("Outside of the popup window is clicked."); + #endif // debug + } } } // Pixel Sort button bounds = nk_widget_bounds(NK_Context); - auto_sort_button = nk_button_label(NK_Context, "Pixel Sort"); - { - if (auto_sort_button && currFileName) - { - auto_sort_state = 1; - auto_curation_state.clear(); // if click the button, the sort/cut will be applied globally - } + auto_sort_button = nk_button_label(NK_Context, "Pixel Sort") ||auto_sort_button ; + if (auto_sort_button) { // window to set the parameters for sort - if (nk_contextual_begin(NK_Context, 0, nk_vec2(Screen_Scale.x * 480, Screen_Scale.y * 400), bounds)) - { + static struct nk_rect popup_rect = nk_rect( + Screen_Scale.x * 200, Screen_Scale.y * 100, + Screen_Scale.x * 480, Screen_Scale.y * 260); + if (nk_popup_begin(NK_Context, NK_POPUP_STATIC, "Pixel Sort Parameters", + NK_WINDOW_CLOSABLE|NK_WINDOW_BORDER, popup_rect)) { nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 1); nk_label(NK_Context, "Smallest Frag Size (default: 2)", NK_TEXT_LEFT); nk_edit_string_zero_terminated( @@ -11911,45 +11913,34 @@ MainArgs nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 3); if (nk_option_label(NK_Context, "UnionFind", auto_curation_state.sort_mode == 0)) - { auto_curation_state.sort_mode = 0; - } if (nk_option_label(NK_Context, "Fuse", auto_curation_state.sort_mode == 1)) - { auto_curation_state.sort_mode = 1; - } if (nk_option_label(NK_Context, "Deep Fuse", auto_curation_state.sort_mode == 2)) - { auto_curation_state.sort_mode = 2; - } - - // if (nk_option_label(NK_Context, "YaHS", auto_curation_state.sort_mode == 3)) - // { - // auto_curation_state.sort_mode = 3; - // } nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 2); - if (nk_button_label(NK_Context, "Cancel")) - { - printf("[Pixel Sort] Cancel button clicked\n"); - auto_curation_state.set_buf(); - nk_contextual_close(NK_Context); - } - if (nk_button_label(NK_Context, "Apply")) + if (nk_button_label(NK_Context, "Apply settings")) { // Apply changes // Convert text to integer and float auto_curation_state.update_value_from_buf(); - - nk_contextual_close(NK_Context); - auto_curation_state.set_buf(); fmt::print("[Pixel Sort] smallest_frag_size_in_pixel: {}\n", auto_curation_state.smallest_frag_size_in_pixel); fmt::print("[Pixel Sort] link_score_threshold: {:.3f}\n", auto_curation_state.link_score_threshold); fmt::print("[Pixel Sort] Sort mode: {}\n", auto_curation_state.get_sort_mode_name()); + auto_sort_button = 0; + nk_popup_close(NK_Context); } + // 关闭按钮 + if (nk_button_label(NK_Context, "Close")) { + auto_sort_button = 0; + auto_curation_state.set_buf(); + nk_popup_close(NK_Context); + } + /* // redo all the changes if (!auto_curation_state.show_autoSort_redo_confirm_popup) { @@ -11998,7 +11989,16 @@ MainArgs auto_curation_state.show_autoSort_erase_confirm_popup=false; } } - nk_contextual_end(NK_Context); + */ + + nk_popup_end(NK_Context); + + } else { + /* 如果用户点击了窗口外的区域,也关闭弹出窗口 */ + auto_sort_button = 0; + #ifdef DEBUG + fmt::println("Outside of the popup window is clicked."); + #endif // debug } } pop_nk_style(NK_Context, 3); // pop the style for Pixel Cut and Pixel Sort button From 651e01b66627130501530fc4209728ff16b0623a Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Thu, 5 Jun 2025 11:46:19 +0100 Subject: [PATCH 33/38] select and paint frags with different paint tags --- PretextView.cpp | 159 +++++++++++++++++++++++++++------------- src/grey_out_settings.h | 7 ++ 2 files changed, 114 insertions(+), 52 deletions(-) diff --git a/PretextView.cpp b/PretextView.cpp index 3417763..5a000c4 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -831,6 +831,7 @@ Default_Tags[] = "Primary", "vertPaint", // paint vertically "horzPaint", // paint horizontally + "crossPaint", // paint with cross }; @@ -2804,26 +2805,26 @@ MouseMove(GLFWwindow* window, f64 x, f64 y) u32 contigId = Map_State->contigIds[pixel]; if (MetaData_Edit_State == 1) - Map_State->metaDataFlags[pixel] |= (1 << MetaData_Active_Tag); // set the active tag + Map_State->metaDataFlags[pixel] |= (1ULL << MetaData_Active_Tag); // set the active tag else - Map_State->metaDataFlags[pixel] &= ~(1 << MetaData_Active_Tag); // clear the active tag + Map_State->metaDataFlags[pixel] &= ~(1ULL << MetaData_Active_Tag); // clear the active tag u32 testPixel = pixel; while (testPixel && (Map_State->contigIds[testPixel - 1] == contigId)) { if (MetaData_Edit_State == 1) - Map_State->metaDataFlags[--testPixel] |= (1 << MetaData_Active_Tag); + Map_State->metaDataFlags[--testPixel] |= (1ULL << MetaData_Active_Tag); else - Map_State->metaDataFlags[--testPixel] &= ~(1 << MetaData_Active_Tag); + Map_State->metaDataFlags[--testPixel] &= ~(1ULL << MetaData_Active_Tag); } testPixel = pixel; while ((testPixel < (Number_of_Pixels_1D - 1)) && (Map_State->contigIds[testPixel + 1] == contigId)) { if (MetaData_Edit_State == 1) - Map_State->metaDataFlags[++testPixel] |= (1 << MetaData_Active_Tag); + Map_State->metaDataFlags[++testPixel] |= (1ULL << MetaData_Active_Tag); else - Map_State->metaDataFlags[++testPixel] &= ~(1 << MetaData_Active_Tag); + Map_State->metaDataFlags[++testPixel] &= ~(1ULL << MetaData_Active_Tag); } UpdateContigsFromMapState(); @@ -4579,7 +4580,6 @@ Render() { if (*cont->metaDataFlags) { u32 tmp = 0; // used to count the number of tags drawn - bool haplotigTagged = false; ForLoop2(ArrayCount(Meta_Data->tags)) { if (*cont->metaDataFlags & ((u64)1 << index2)) @@ -4607,38 +4607,36 @@ Render() { 0); } - // Check if the tag is "Haplotig" - if (strcmp((char *)Meta_Data->tags[index2], "Haplotig") == 0) - { - haplotigTagged = true; - } } } // draw the grey out mask - std::string grey_out_tag = Grey_Out_Settings->is_grey_out(cont->metaDataFlags, Meta_Data); - if (!grey_out_tag.empty()) - { - vert[0].x = ModelXToScreen(start_contig - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_contig); - vert[1].x = ModelXToScreen(start_contig - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_contig); - vert[2].x = ModelXToScreen(end_contig - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_contig); - vert[3].x = ModelXToScreen(end_contig - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_contig); - - ColourGenerator((u32)scaffId, (f32 *)barColour); + auto paint_func = [&](const void* vert_) { + ColourGenerator((u32)0, (f32 *)barColour); u32 colour = FourFloatColorToU32(*((nk_colorf *)barColour)); glUseProgram(Flat_Shader->shaderProgram); glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); + glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert_); glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); glUseProgram(UI_Shader->shaderProgram); fonsSetColor(FontStash_Context, colour); + }; + std::string grey_out_tag = Grey_Out_Settings->is_grey_out(cont->metaDataFlags, Meta_Data); + if (!grey_out_tag.empty()) + { + vert[0].x = ModelXToScreen(start_contig - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_contig); + vert[1].x = ModelXToScreen(start_contig - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_contig); + vert[2].x = ModelXToScreen(end_contig - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_contig); + vert[3].x = ModelXToScreen(end_contig - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_contig); + paint_func(vert); } else{ + int is_vert_horiz_grey_out = Grey_Out_Settings->is_vert_horiz_grey_out(cont->metaDataFlags, Meta_Data); if (is_vert_horiz_grey_out == 0) continue; // draw the vertical or horizontal grey out mask @@ -4652,28 +4650,42 @@ Render() { vert[1].y = ModelYToScreen(-0.5f); vert[2].y = ModelYToScreen(-0.5f); vert[3].y = ModelYToScreen( 0.5f); + paint_func(vert); } - else { // horizontal + else if (is_vert_horiz_grey_out == 2) { // horizontal vert[0].x = ModelXToScreen(-0.5f); vert[1].x = ModelXToScreen(-0.5f); vert[2].x = ModelXToScreen( 0.5f); vert[3].x = ModelXToScreen( 0.5f); + paint_func(vert); + } + else if (is_vert_horiz_grey_out == 3) { // corss + // paint the hrizontal + vert[0].x = ModelXToScreen(-0.5f); + vert[1].x = ModelXToScreen(-0.5f); + vert[2].x = ModelXToScreen( 0.5f); + vert[3].x = ModelXToScreen( 0.5f); + paint_func(vert); + + // paint the vertical uppon + vertex vert_v0[4]; + vert_v0[0].x = ModelXToScreen(start_contig - 0.5f); vert_v0[0].y = ModelYToScreen(0.5f); + vert_v0[1].x = ModelXToScreen(start_contig - 0.5f); vert_v0[1].y = ModelYToScreen(0.5f - start_contig); + vert_v0[2].x = ModelXToScreen(end_contig - 0.5f); vert_v0[2].y = ModelYToScreen(0.5f - start_contig); + vert_v0[3].x = ModelXToScreen(end_contig - 0.5f); vert_v0[3].y = ModelYToScreen(0.5f); + paint_func(vert_v0); + + // paint the vertical bottom + vertex vert_v1[4]; + vert_v1[0].x = ModelXToScreen(start_contig - 0.5f); vert_v1[0].y = ModelYToScreen(0.5f - end_contig); + vert_v1[1].x = ModelXToScreen(start_contig - 0.5f); vert_v1[1].y = ModelYToScreen(-0.5f); + vert_v1[2].x = ModelXToScreen(end_contig - 0.5f); vert_v1[2].y = ModelYToScreen(-0.5f); + vert_v1[3].x = ModelXToScreen(end_contig - 0.5f); vert_v1[3].y = ModelYToScreen(0.5f - end_contig); + paint_func(vert_v1); + } + else { + throw std::runtime_error(fmt::format("This part is unreachable! File: {}, line: {}\n", __FILE__, __LINE__)); } - - ColourGenerator((u32)scaffId, (f32 *)barColour); - u32 colour = FourFloatColorToU32(*((nk_colorf *)barColour)); - - glUseProgram(Flat_Shader->shaderProgram); - glUniform4fv(Flat_Shader->colorLocation, 1, (GLfloat *)&barColour); - - glBindBuffer(GL_ARRAY_BUFFER, Scaff_Bar_Data->vbos[ptr]); - glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(vertex), vert); - glBindVertexArray(Scaff_Bar_Data->vaos[ptr++]); - glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL); - - glUseProgram(UI_Shader->shaderProgram); - fonsSetColor(FontStash_Context, colour); - } } @@ -11334,7 +11346,9 @@ GenerateAGP(char *path, u08 overwrite, u08 formatSingletons, u08 preserveOrder) { ForLoop2(ArrayCount(Meta_Data->tags)) { - if (*cont->metaDataFlags & (1ULL << index2)) + if (*cont->metaDataFlags & (1ULL << index2) && // NOTE: please make sure the 1ULL, or after index2 >= 32 there will be unexpected behaviour + Grey_Out_Settings->paint_tags.count(std::string((char*)Meta_Data->tags[index2])) == 0 // make sure this is not tag used for vertical horizontal or cross painting + ) { stbsp_snprintf(buffer, sizeof(buffer), "\t%s", (char *)Meta_Data->tags[index2]); fwrite(buffer, 1, strlen(buffer), file); @@ -11352,7 +11366,9 @@ GenerateAGP(char *path, u08 overwrite, u08 formatSingletons, u08 preserveOrder) { ForLoop2(ArrayCount(Meta_Data->tags)) { - if (*cont->metaDataFlags & (1ULL << index2)) // NOTE: please make sure the 1ULL, or after index2 >= 32 there will be unexpected behaviour + if (*cont->metaDataFlags & (1ULL << index2) && // NOTE: please make sure the 1ULL, or after index2 >= 32 there will be unexpected behaviour + Grey_Out_Settings->paint_tags.count(std::string((char*)Meta_Data->tags[index2])) == 0 // make sure this is not tag used for vertical horizontal or cross painting + ) { stbsp_snprintf(buffer, sizeof(buffer), "\t%s", (char *)Meta_Data->tags[index2]); fwrite(buffer, 1, strlen(buffer), file); @@ -11851,21 +11867,36 @@ MainArgs sizeof(auto_curation_state.auto_cut_smallest_frag_size_in_pixel_buf), nk_filter_decimal); - nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 2); + nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 3); if (nk_button_label(NK_Context, "Apply settings")) { // Apply changes // Convert text to integer and float auto_curation_state.update_value_from_buf(); // todo 这里还有问题,因为设置了pixel_mean之后 并没有重新计算 pixel_density... + auto_curation_state.set_buf(); + fmt::print("[Pixel Cut] cut_threshold: {:.3f}\n", auto_curation_state.auto_cut_threshold); + fmt::print("[Pixel Cut] pixel mean window size: {}\n", auto_curation_state.auto_cut_diag_window_for_pixel_mean); + fmt::print("[Pixel Cut] smallest_frag_size_in_pixel: {}\n", auto_curation_state.auto_cut_smallest_frag_size_in_pixel); + // auto_cut_button = 0; + // nk_popup_close(NK_Context); + } + /* run cut 按钮 */ + if (nk_button_label(NK_Context, "Run") && currFileName) { + + // Convert text to integer and float + auto_curation_state.update_value_from_buf(); // todo 这里还有问题,因为设置了pixel_mean之后 并没有重新计算 pixel_density... auto_curation_state.set_buf(); fmt::print("[Pixel Cut] cut_threshold: {:.3f}\n", auto_curation_state.auto_cut_threshold); fmt::print("[Pixel Cut] pixel mean window size: {}\n", auto_curation_state.auto_cut_diag_window_for_pixel_mean); fmt::print("[Pixel Cut] smallest_frag_size_in_pixel: {}\n", auto_curation_state.auto_cut_smallest_frag_size_in_pixel); auto_cut_button = 0; + auto_cut_state = 1; + auto_curation_state.clear(); // click the button will run sort globally nk_popup_close(NK_Context); } + /* 关闭按钮 */ if (nk_button_label(NK_Context, "Close")) { auto_cut_button = 0; @@ -11873,6 +11904,7 @@ MainArgs nk_popup_close(NK_Context); } + nk_popup_end(NK_Context); } else { /* 如果用户点击了窗口外的区域,也关闭弹出窗口 */ @@ -11921,7 +11953,7 @@ MainArgs if (nk_option_label(NK_Context, "Deep Fuse", auto_curation_state.sort_mode == 2)) auto_curation_state.sort_mode = 2; - nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 2); + nk_layout_row_dynamic(NK_Context, Screen_Scale.y * 30.0f, 3); if (nk_button_label(NK_Context, "Apply settings")) { // Apply changes @@ -11931,7 +11963,22 @@ MainArgs fmt::print("[Pixel Sort] smallest_frag_size_in_pixel: {}\n", auto_curation_state.smallest_frag_size_in_pixel); fmt::print("[Pixel Sort] link_score_threshold: {:.3f}\n", auto_curation_state.link_score_threshold); fmt::print("[Pixel Sort] Sort mode: {}\n", auto_curation_state.get_sort_mode_name()); + // auto_sort_button = 0; + // nk_popup_close(NK_Context); + } + /* run sort 按钮 */ + if (nk_button_label(NK_Context, "Run") && currFileName) { + + // Convert text to integer and float + auto_curation_state.update_value_from_buf(); + auto_curation_state.set_buf(); + fmt::print("[Pixel Sort] smallest_frag_size_in_pixel: {}\n", auto_curation_state.smallest_frag_size_in_pixel); + fmt::print("[Pixel Sort] link_score_threshold: {:.3f}\n", auto_curation_state.link_score_threshold); + fmt::print("[Pixel Sort] Sort mode: {}\n", auto_curation_state.get_sort_mode_name()); + auto_sort_button = 0; + auto_sort_state = 1; + auto_curation_state.clear(); // click the button will run sort globally nk_popup_close(NK_Context); } // 关闭按钮 @@ -12310,7 +12357,8 @@ MainArgs if (*Meta_Data->tags[i] == 0) break; int tmp = ( MetaData_Active_Tag == i) ; tmp = nk_check_label(NK_Context, (char*)Meta_Data->tags[i], tmp); - if (tmp) MetaData_Active_Tag = i; + if (tmp) + MetaData_Active_Tag = i; } nk_tree_pop(NK_Context); } @@ -12322,12 +12370,19 @@ MainArgs for (u32 i = 0; i < ArrayCount(Meta_Data->tags); i ++ ) { if (*Meta_Data->tags[i] == 0) break; - s32 tmp = Grey_Out_Settings->grey_out_flags[i]; - nk_checkbox_label(NK_Context, (char*)Meta_Data->tags[i], Grey_Out_Settings->grey_out_flags+i); - if (tmp!=Grey_Out_Settings->grey_out_flags[i]) - { - fmt::print("[UserPofile]: Grey out tags changed\n"); - UserSaveState(); + if (Grey_Out_Settings->paint_tags.count(std::string((char*)Meta_Data->tags[i])) != 0 ){ + s32 non = 0; + nk_checkbox_label(NK_Context, (char*)Meta_Data->tags[i], &non); + *(Grey_Out_Settings->grey_out_flags+i) = 0; + } + else{ + s32 tmp = Grey_Out_Settings->grey_out_flags[i]; + nk_checkbox_label(NK_Context, (char*)Meta_Data->tags[i], Grey_Out_Settings->grey_out_flags+i); + if (tmp!=Grey_Out_Settings->grey_out_flags[i]) + { + fmt::print("[UserPofile]: Grey out tag {}({}) changed from {} to {}\n", (char*)Meta_Data->tags[i], i, tmp>0?"true":"false", *(Grey_Out_Settings->grey_out_flags+i)>0?"true":"false"); + UserSaveState(); + } } } nk_tree_pop(NK_Context); @@ -12731,7 +12786,7 @@ MainArgs contig *cont = Contigs->contigs_arr + index2; f32 contLen = (f32)((f64)cont->length / (f64)Number_of_Pixels_1D); - if (*cont->metaDataFlags & (1 << index)) + if (*cont->metaDataFlags & (1ULL << index)) { char buff[128]; u32 startCoord = cont->startCoord; @@ -12997,10 +13052,10 @@ MainArgs glfwTerminate(); // free the memory allocated for the shader sources - fprintf(stdout, "Memory freed for shader sources.\n"); + fprintf(stdout, "Memory freed: shader sources.\n"); ResetMemoryArenaP(Loading_Arena); - fprintf(stdout, "Memory freed for the arena.\n"); + fprintf(stdout, "Memory freed: the arena.\n"); EndMain; } diff --git a/src/grey_out_settings.h b/src/grey_out_settings.h index 7885e17..8945a9f 100644 --- a/src/grey_out_settings.h +++ b/src/grey_out_settings.h @@ -11,6 +11,11 @@ struct GreyOutSettings s32 n_flags = 64; s32 grey_out_flags[64]; std::unordered_set default_grey_out_tag = {"FalseDuplicate", "Primary"}; + std::unordered_set paint_tags = { + "vertPaint", // paint vertically + "horzPaint", // paint horizontally + "crossPaint", // paint with cross + }; GreyOutSettings() { @@ -66,12 +71,14 @@ struct GreyOutSettings 0: no grey out 1: vertical grey out 2: horizontal grey out + 3: corss grey out */ u32 is_vert_horiz_grey_out(u64* meta_data_flags, meta_data* Meta_Data){ for (s32 i = 0; i < ArrayCount(Meta_Data->tags) ; i ++ ) { if ((*meta_data_flags & ((u64)1 << i)) ){ if ( std::string((char*) Meta_Data->tags[i])=="vertPaint") return 1; else if ( std::string((char*) Meta_Data->tags[i])=="horzPaint") return 2; + else if ( std::string((char*) Meta_Data->tags[i]) == "crossPaint") return 3; } } return 0; From 840a1868c16f27df005ab4cbe497aadc3edb1c3c Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Mon, 9 Jun 2025 15:05:24 +0100 Subject: [PATCH 34/38] minor modifications --- src/copy_texture.cpp | 6 ++---- src/frag_for_compress.h | 6 ++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/copy_texture.cpp b/src/copy_texture.cpp index b7110b1..770c178 100644 --- a/src/copy_texture.cpp +++ b/src/copy_texture.cpp @@ -24,7 +24,7 @@ SOFTWARE. #include "copy_texture.h" #include "shaderSource.h" #include "utilsPretextView.h" -#include "cmath" +#include Show_State::Show_State() @@ -1055,9 +1055,7 @@ f32 TexturesArray4AI::cal_diagonal_mean_within_fragments(int shift, const contig return (f32)sum / (f32)std::max(cnt, 1u); } -/* todo -windows: 编译错误,尝试访问 d -*/ + void TexturesArray4AI::get_interaction_score( const u32& row, const u32& column, diff --git a/src/frag_for_compress.h b/src/frag_for_compress.h index 96903b9..77618b3 100644 --- a/src/frag_for_compress.h +++ b/src/frag_for_compress.h @@ -102,7 +102,8 @@ struct Frag4compress { } } length[0] = Contigs->contigs_arr[frag_id[0]].length; - metaDataFlags[0] = (Contigs->contigs_arr[frag_id[0]].metaDataFlags == nullptr)?0:*(Contigs->contigs_arr[frag_id[0]].metaDataFlags); + metaDataFlags[0] = (Contigs->contigs_arr[frag_id[0]].metaDataFlags == nullptr)? + 0 : *(Contigs->contigs_arr[frag_id[0]].metaDataFlags); total_length = length[0]; for (u32 i = 1; i < num; i++) @@ -111,7 +112,8 @@ struct Frag4compress { inversed[i] = false; // currently, this is not used startCoord[i] = startCoord[i-1] + length[i-1]; length[i] = Contigs->contigs_arr[frag_id[i]].length; - metaDataFlags[i] = (Contigs->contigs_arr[frag_id[i]].metaDataFlags == nullptr)?0:*(Contigs->contigs_arr[frag_id[i]].metaDataFlags); + metaDataFlags[i] = (Contigs->contigs_arr[frag_id[i]].metaDataFlags == nullptr)? + 0 : *(Contigs->contigs_arr[frag_id[i]].metaDataFlags); total_length += length[i]; } } From aaeaf74844771c4dd33d97e4e14e0b536a399732 Mon Sep 17 00:00:00 2001 From: shguanWHU Date: Mon, 9 Jun 2025 15:05:27 +0100 Subject: [PATCH 35/38] error of showing tags --- PretextView.cpp | 182 ++++++++++++++++++++++++++++++------------------ 1 file changed, 114 insertions(+), 68 deletions(-) diff --git a/PretextView.cpp b/PretextView.cpp index 5a000c4..de67a4f 100644 --- a/PretextView.cpp +++ b/PretextView.cpp @@ -102,6 +102,19 @@ SOFTWARE. #pragma clang diagnostic pop +#ifdef DEBUG_STUCK_PROBLEM + #include + #include + #define Stuck_Problem_Check_Debug { \ + fmt::println("Stuck problem check :: File: {}, line: {}, time: {:%H:%M:%S}", \ + __FILE__, __LINE__, \ + fmt::localtime(std::time(nullptr))\ + ); \ + } +#else + #define Stuck_Problem_Check_Debug +#endif //DEBUG_STUCK_PROBLEM + #include "Resources.cpp" // defines icons, fonts @@ -4606,7 +4619,6 @@ Render() { (char *)Meta_Data->tags[index2], 0); } - } } @@ -4638,53 +4650,54 @@ Render() { else{ int is_vert_horiz_grey_out = Grey_Out_Settings->is_vert_horiz_grey_out(cont->metaDataFlags, Meta_Data); - if (is_vert_horiz_grey_out == 0) continue; - // draw the vertical or horizontal grey out mask - vert[0].x = ModelXToScreen(start_contig - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_contig); - vert[1].x = ModelXToScreen(start_contig - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_contig); - vert[2].x = ModelXToScreen(end_contig - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_contig); - vert[3].x = ModelXToScreen(end_contig - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_contig); - - if (is_vert_horiz_grey_out == 1) { // vertical - vert[0].y = ModelYToScreen( 0.5f); - vert[1].y = ModelYToScreen(-0.5f); - vert[2].y = ModelYToScreen(-0.5f); - vert[3].y = ModelYToScreen( 0.5f); - paint_func(vert); - } - else if (is_vert_horiz_grey_out == 2) { // horizontal - vert[0].x = ModelXToScreen(-0.5f); - vert[1].x = ModelXToScreen(-0.5f); - vert[2].x = ModelXToScreen( 0.5f); - vert[3].x = ModelXToScreen( 0.5f); - paint_func(vert); - } - else if (is_vert_horiz_grey_out == 3) { // corss - // paint the hrizontal - vert[0].x = ModelXToScreen(-0.5f); - vert[1].x = ModelXToScreen(-0.5f); - vert[2].x = ModelXToScreen( 0.5f); - vert[3].x = ModelXToScreen( 0.5f); - paint_func(vert); - - // paint the vertical uppon - vertex vert_v0[4]; - vert_v0[0].x = ModelXToScreen(start_contig - 0.5f); vert_v0[0].y = ModelYToScreen(0.5f); - vert_v0[1].x = ModelXToScreen(start_contig - 0.5f); vert_v0[1].y = ModelYToScreen(0.5f - start_contig); - vert_v0[2].x = ModelXToScreen(end_contig - 0.5f); vert_v0[2].y = ModelYToScreen(0.5f - start_contig); - vert_v0[3].x = ModelXToScreen(end_contig - 0.5f); vert_v0[3].y = ModelYToScreen(0.5f); - paint_func(vert_v0); - - // paint the vertical bottom - vertex vert_v1[4]; - vert_v1[0].x = ModelXToScreen(start_contig - 0.5f); vert_v1[0].y = ModelYToScreen(0.5f - end_contig); - vert_v1[1].x = ModelXToScreen(start_contig - 0.5f); vert_v1[1].y = ModelYToScreen(-0.5f); - vert_v1[2].x = ModelXToScreen(end_contig - 0.5f); vert_v1[2].y = ModelYToScreen(-0.5f); - vert_v1[3].x = ModelXToScreen(end_contig - 0.5f); vert_v1[3].y = ModelYToScreen(0.5f - end_contig); - paint_func(vert_v1); + if (is_vert_horiz_grey_out != 0) { + // draw the vertical or horizontal grey out mask + vert[0].x = ModelXToScreen(start_contig - 0.5f); vert[0].y = ModelYToScreen(0.5f - start_contig); + vert[1].x = ModelXToScreen(start_contig - 0.5f); vert[1].y = ModelYToScreen(0.5f - end_contig); + vert[2].x = ModelXToScreen(end_contig - 0.5f); vert[2].y = ModelYToScreen(0.5f - end_contig); + vert[3].x = ModelXToScreen(end_contig - 0.5f); vert[3].y = ModelYToScreen(0.5f - start_contig); + + if (is_vert_horiz_grey_out == 1) { // vertical + vert[0].y = ModelYToScreen( 0.5f); + vert[1].y = ModelYToScreen(-0.5f); + vert[2].y = ModelYToScreen(-0.5f); + vert[3].y = ModelYToScreen( 0.5f); + paint_func(vert); + } + else if (is_vert_horiz_grey_out == 2) { // horizontal + vert[0].x = ModelXToScreen(-0.5f); + vert[1].x = ModelXToScreen(-0.5f); + vert[2].x = ModelXToScreen( 0.5f); + vert[3].x = ModelXToScreen( 0.5f); + paint_func(vert); + } + else if (is_vert_horiz_grey_out == 3) { // corss + // paint the hrizontal + vert[0].x = ModelXToScreen(-0.5f); + vert[1].x = ModelXToScreen(-0.5f); + vert[2].x = ModelXToScreen( 0.5f); + vert[3].x = ModelXToScreen( 0.5f); + paint_func(vert); + + // paint the vertical uppon + vertex vert_v0[4]; + vert_v0[0].x = ModelXToScreen(start_contig - 0.5f); vert_v0[0].y = ModelYToScreen(0.5f); + vert_v0[1].x = ModelXToScreen(start_contig - 0.5f); vert_v0[1].y = ModelYToScreen(0.5f - start_contig); + vert_v0[2].x = ModelXToScreen(end_contig - 0.5f); vert_v0[2].y = ModelYToScreen(0.5f - start_contig); + vert_v0[3].x = ModelXToScreen(end_contig - 0.5f); vert_v0[3].y = ModelYToScreen(0.5f); + paint_func(vert_v0); + + // paint the vertical bottom + vertex vert_v1[4]; + vert_v1[0].x = ModelXToScreen(start_contig - 0.5f); vert_v1[0].y = ModelYToScreen(0.5f - end_contig); + vert_v1[1].x = ModelXToScreen(start_contig - 0.5f); vert_v1[1].y = ModelYToScreen(-0.5f); + vert_v1[2].x = ModelXToScreen(end_contig - 0.5f); vert_v1[2].y = ModelYToScreen(-0.5f); + vert_v1[3].x = ModelXToScreen(end_contig - 0.5f); vert_v1[3].y = ModelYToScreen(0.5f - end_contig); + paint_func(vert_v1); + } } - else { - throw std::runtime_error(fmt::format("This part is unreachable! File: {}, line: {}\n", __FILE__, __LINE__)); + else { // not grey out vertically, horizontally or crossly. + // throw std::runtime_error(fmt::format("This part is unreachable! File: {}, line: {}\n", __FILE__, __LINE__)); } } @@ -5898,7 +5911,8 @@ LoadFile(const char *filePath, memory_arena *arena, char **fileName, u64 *header // 从内存池中分配存储原始 contig 的数组内存,类型为 original_contig,数组长度为 Number_of_Original_Contigs Original_Contigs = PushArrayP(arena, original_contig, Number_of_Original_Contigs); // 分配一个存储浮点数的数组 - f32 *contigFracs = PushArrayP(arena, f32, Number_of_Original_Contigs); + // f32 *contigFracs = PushArrayP(arena, f32, Number_of_Original_Contigs); + f32 *contigFracs = new f32[Number_of_Original_Contigs]; ForLoop(Number_of_Original_Contigs) // 读取 contigs fraction (f32) and name { @@ -5974,6 +5988,7 @@ LoadFile(const char *filePath, memory_arena *arena, char **fileName, u64 *header #endif } } + delete[] contigFracs; while (lastPixel < Number_of_Pixels_1D) // 处理数值计算导致的lastPixel小于Number_of_Pixels_1D的问题 { Map_State->originalContigIds[lastPixel] = (u32)(Number_of_Original_Contigs - 1); //假设其为最后一个contig的像素点 @@ -10998,7 +11013,7 @@ UserSaveState(const char *headerHash, u08 overwrite , char *path) fwrite(&invert_mouse_tmp, sizeof(bool), 1, file); } - // TODO (shaoheng) save default file browser directory + // finished (shaoheng) save default file browser directory const char* placeholder = "fdpc"; // placeholder for File Directory Path Cache for (int i = 0; i < 4; i ++ ) fwrite(&placeholder[i], sizeof(char), 1, file); for (const file_browser& browser_tmp : { browser, saveBrowser, loadBrowser, saveAGPBrowser, loadAGPBrowser }) { @@ -11015,7 +11030,6 @@ UserSaveState(const char *headerHash, u08 overwrite , char *path) // END of SAVE user profile settings - fclose(file); #ifdef DEBUG @@ -11131,43 +11145,45 @@ global_function // load selected area color f32 tmp_mask_color[4]; - if (fread(tmp_mask_color, sizeof(tmp_mask_color), 1, file) == 1) - { + if (fread(tmp_mask_color, sizeof(tmp_mask_color), 1, file) == 1) { auto_curation_state.set_mask_color(tmp_mask_color); } - else - { + else { auto_curation_state.set_mask_color(auto_curation_state.mask_color_default); + fmt::println("[UserLoadState::warning]: userProfile loading stops as there is no 1 byte left for tmp_mask_color. The left part will not be restored from the userProfile cache. File: {}, line: {}", __FILE__, __LINE__); + return 1; } // load grey out flags int32_t grey_out_flags[64]; - if (fread(grey_out_flags, sizeof(int32_t), 64, file) == 64) - { - if (Grey_Out_Settings) - { + if (fread(grey_out_flags, sizeof(int32_t), 64, file) == 64) { + if (Grey_Out_Settings) { memcpy(Grey_Out_Settings->grey_out_flags, grey_out_flags, 64 * sizeof(int32_t)); } } + else { + fmt::println("[UserLoadState::warning]: userProfile loading stops as there is no 64 byte left for grey_out_flags. The left part will not be restored from the userProfile cache. File: {}, line: {}", __FILE__, __LINE__); + return 1; + } // load user profile settings bool invert_mouse_tmp; - if (fread(&invert_mouse_tmp, sizeof(bool), 1, file) == 1) - { - if (user_profile_settings_ptr) - { - user_profile_settings_ptr->invert_mouse = invert_mouse_tmp; - } + if (fread(&invert_mouse_tmp, sizeof(bool), 1, file) == 1) { + if (user_profile_settings_ptr) user_profile_settings_ptr->invert_mouse = invert_mouse_tmp; + } + else { + if (user_profile_settings_ptr) user_profile_settings_ptr->invert_mouse = false; + fmt::println("[UserLoadState::warning]: userProfile loading stops as there is no 1 byte left for invertMouse setting. The left part will not be restored from the userProfile cache. File: {}, line: {}", __FILE__, __LINE__); + return 1; } + // file directory path cache char lable[4]; - if (fread(lable, sizeof(char), 4, file) == 4 && strncmp(lable, "fdpc", 4) == 0) - { + if (fread(lable, sizeof(char), 4, file) == 4 && strncmp(lable, "fdpc", 4) == 0) { std::vector browsers = { &browser, &saveBrowser, &loadBrowser, &saveAGPBrowser, &loadAGPBrowser }; for (file_browser* browser_ptr : browsers) { u32 dir_len; - if (fread(&dir_len, sizeof(dir_len), 1, file) == 1 && dir_len > 0) - { + if (fread(&dir_len, sizeof(dir_len), 1, file) == 1 && dir_len > 0) { char *dir = (char *)malloc(dir_len + 1); if (fread(dir, sizeof(char), dir_len, file) == dir_len ) { dir[dir_len] = '\0'; // Null-terminate the string @@ -11177,6 +11193,10 @@ global_function } } } + else { + fmt::println("[UserLoadState::warning]: userProfile loading stops as the 4 byte left is not \'fdpc\' for the file directory path cache. The left part will not be restored from the userProfile cache. File: {}, line: {}", __FILE__, __LINE__); + return 1; + } fclose(file); @@ -11472,6 +11492,8 @@ void SortMapByMetaTags(u64 tagMask) MainArgs { + Stuck_Problem_Check_Debug + #ifdef PYTHON_SCOPED_INTERPRETER // START Python interpreter py::scoped_interpreter guard{}; @@ -11489,6 +11511,8 @@ MainArgs printf("Read from file: %s\n", currFile); } + Stuck_Problem_Check_Debug + Mouse_Move.x = -1.0; // intialize the mouse position Mouse_Move.y = -1.0; @@ -11513,6 +11537,8 @@ MainArgs CreateMemoryArena(Working_Set, MegaByte(256)); Thread_Pool = ThreadPoolInit(&Working_Set, 3); + Stuck_Problem_Check_Debug + glfwSetErrorCallback(ErrorCallback); if (!glfwInit()) { @@ -11520,6 +11546,8 @@ MainArgs exit(EXIT_FAILURE); } + Stuck_Problem_Check_Debug + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); @@ -11536,12 +11564,16 @@ MainArgs exit(EXIT_FAILURE); } + Stuck_Problem_Check_Debug + glfwMakeContextCurrent(window); NK_Context = PushStruct(Working_Set, nk_context); glfwSetWindowUserPointer(window, NK_Context); gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); glfwSwapInterval(1); + Stuck_Problem_Check_Debug + glfwSetFramebufferSizeCallback(window, GLFWChangeFrameBufferSize); glfwSetWindowSizeCallback(window, GLFWChangeWindowSize); glfwSetCursorPosCallback(window, MouseMove); @@ -11579,8 +11611,12 @@ MainArgs GLFWcursor *arrowCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); GLFWcursor *crossCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR); + Stuck_Problem_Check_Debug + Setup(); + Stuck_Problem_Check_Debug + if (initWithFile) { UI_On = LoadFile((const char *)currFile, Loading_Arena, (char **)&currFileName, &headerHash) == ok ? 0 : 1; @@ -11607,6 +11643,8 @@ MainArgs // } // } + Stuck_Problem_Check_Debug + u32 showClearCacheScreen = 0; @@ -11625,6 +11663,8 @@ MainArgs FileBrowserInit(&loadAGPBrowser, &media); } + Stuck_Problem_Check_Debug + { f64 mousex, mousey; glfwGetCursorPos(window, &mousex, &mousey); @@ -11636,6 +11676,8 @@ MainArgs Redisplay = 1; + Stuck_Problem_Check_Debug + while (!glfwWindowShouldClose(window)) { if (Redisplay) @@ -11644,6 +11686,7 @@ MainArgs glfwSwapBuffers(window); Redisplay = 0; if (currFileName) SaveState(headerHash); + Stuck_Problem_Check_Debug } if (Loading) @@ -11684,6 +11727,8 @@ MainArgs GatheringTextInput = 1; glfwPollEvents(); + Stuck_Problem_Check_Debug + /*nk_input_key(NK_Context, NK_KEY_DEL, glfwGetKey(window, GLFW_KEY_DELETE) == GLFW_PRESS); nk_input_key(NK_Context, NK_KEY_ENTER, glfwGetKey(window, GLFW_KEY_ENTER) == GLFW_PRESS); nk_input_key(NK_Context, NK_KEY_TAB, glfwGetKey(window, GLFW_KEY_TAB) == GLFW_PRESS); @@ -12997,6 +13042,7 @@ MainArgs } if (!Redisplay) glfwWaitEvents(); + Stuck_Problem_Check_Debug } if (currFileName) SaveState(headerHash); From 1511e91fe9ac662470a20b612382cf8249793671 Mon Sep 17 00:00:00 2001 From: SH GUAN Date: Mon, 15 Sep 2025 10:45:36 +0800 Subject: [PATCH 36/38] 1.0.4 release --- AGPCorrect | 0 install | 0 install.cmake.sh | 0 install.sh | 0 mac_dmg_generate.sh | 0 make_macos_app_plist.sh | 0 python/autoCut/models/swin/setup.py | 0 src/Resources.cpp | 0 8 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 AGPCorrect mode change 100755 => 100644 install mode change 100755 => 100644 install.cmake.sh mode change 100755 => 100644 install.sh mode change 100755 => 100644 mac_dmg_generate.sh mode change 100755 => 100644 make_macos_app_plist.sh mode change 100755 => 100644 python/autoCut/models/swin/setup.py mode change 100755 => 100644 src/Resources.cpp diff --git a/AGPCorrect b/AGPCorrect old mode 100755 new mode 100644 diff --git a/install b/install old mode 100755 new mode 100644 diff --git a/install.cmake.sh b/install.cmake.sh old mode 100755 new mode 100644 diff --git a/install.sh b/install.sh old mode 100755 new mode 100644 diff --git a/mac_dmg_generate.sh b/mac_dmg_generate.sh old mode 100755 new mode 100644 diff --git a/make_macos_app_plist.sh b/make_macos_app_plist.sh old mode 100755 new mode 100644 diff --git a/python/autoCut/models/swin/setup.py b/python/autoCut/models/swin/setup.py old mode 100755 new mode 100644 diff --git a/src/Resources.cpp b/src/Resources.cpp old mode 100755 new mode 100644 From a4890796618dfb21f22e3fd0c27357af53b91758 Mon Sep 17 00:00:00 2001 From: SH GUAN Date: Mon, 15 Sep 2025 10:55:54 +0800 Subject: [PATCH 37/38] v1.0.4 install nsis while compile on Windows --- .github/workflows/build.yml | 8 ++++++++ .github/workflows/deploy.yml | 1 + 2 files changed, 9 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a987f19..9a233f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,14 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Install NSIS on Windows + if: runner.os == 'Windows' + shell: powershell + run: | + choco install nsis -y --no-progress + echo "C:\Program Files (x86)\NSIS" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + makensis -VERSION + - name: install GL and x11 for Linux if: runner.os == 'Linux' run: | diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bf88ad0..e6e8c63 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -37,6 +37,7 @@ jobs: if: runner.os == 'Windows' shell: cmd run: | + choco install nsis -y --no-progress install.cmake.bat - name: Run install script on Linux From 5b479b932983e4926c66de5027ac67dfe36fc49f Mon Sep 17 00:00:00 2001 From: SH GUAN Date: Mon, 15 Sep 2025 10:58:17 +0800 Subject: [PATCH 38/38] v1.0.4 install nsis on Windows --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a233f8..44a4d64 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,8 +39,6 @@ jobs: shell: powershell run: | choco install nsis -y --no-progress - echo "C:\Program Files (x86)\NSIS" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - makensis -VERSION - name: install GL and x11 for Linux if: runner.os == 'Linux'